ไทย

คู่มือที่ครอบคลุมเกี่ยวกับการแก้ปัญหาโมดูล TypeScript ครอบคลุมกลยุทธ์การแก้ปัญหาโมดูลแบบคลาสสิกและโหนด baseUrl เส้นทาง และแนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการเส้นทาง Import ในโครงการที่ซับซ้อน

TypeScript Module Resolution: ไขความลับกลยุทธ์เส้นทาง Import

ระบบการแก้ปัญหาโมดูลของ TypeScript เป็นส่วนสำคัญของการสร้างแอปพลิเคชันที่ปรับขนาดและบำรุงรักษาได้ การทำความเข้าใจวิธีที่ TypeScript ค้นหาโมดูลตามเส้นทาง Import เป็นสิ่งสำคัญสำหรับการจัดระเบียบโค้ดเบสของคุณและหลีกเลี่ยงข้อผิดพลาดทั่วไป คู่มือฉบับสมบูรณ์นี้จะเจาะลึกถึงความซับซ้อนของการแก้ปัญหาโมดูล TypeScript ครอบคลุมกลยุทธ์การแก้ปัญหาโมดูลแบบคลาสสิกและโหนด บทบาทของ baseUrl และ paths ใน tsconfig.json และแนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการเส้นทาง Import อย่างมีประสิทธิภาพ

Module Resolution คืออะไร

Module Resolution คือกระบวนการที่คอมไพเลอร์ TypeScript กำหนดตำแหน่งของโมดูลตามคำสั่ง Import ในโค้ดของคุณ เมื่อคุณเขียน import { SomeComponent } from './components/SomeComponent'; TypeScript จำเป็นต้องหาว่าโมดูล SomeComponent อยู่ที่ใดในระบบไฟล์ของคุณ กระบวนการนี้อยู่ภายใต้ชุดของกฎและการกำหนดค่าที่กำหนดวิธีที่ TypeScript ค้นหาโมดูล

การแก้ปัญหาโมดูลที่ไม่ถูกต้องอาจนำไปสู่ข้อผิดพลาดในการคอมไพล์ ข้อผิดพลาดรันไทม์ และความยากลำบากในการทำความเข้าใจโครงสร้างของโปรเจ็กต์ ดังนั้นความเข้าใจที่มั่นคงเกี่ยวกับการแก้ปัญหาโมดูลจึงเป็นสิ่งสำคัญสำหรับนักพัฒนา TypeScript ทุกคน

กลยุทธ์ Module Resolution

TypeScript มีกลยุทธ์ Module Resolution หลักสองประการ ซึ่งกำหนดค่าผ่านตัวเลือกคอมไพเลอร์ moduleResolution ใน tsconfig.json:

Classic Module Resolution

กลยุทธ์ Module Resolution classic เป็นวิธีที่ง่ายกว่าในสองวิธีนี้ โดยจะค้นหาโมดูลด้วยวิธีตรงไปตรงมา โดยการข้ามขึ้นไปบนโครงสร้างไดเรกทอรีจากไฟล์ที่ Import

วิธีการทำงาน:

  1. เริ่มต้นจากไดเรกทอรีที่มีไฟล์ที่ Import
  2. TypeScript มองหาไฟล์ที่มีชื่อและนามสกุลที่ระบุ (.ts, .tsx, .d.ts)
  3. หากไม่พบ จะย้ายขึ้นไปยังไดเรกทอรีหลักและทำซ้ำการค้นหา
  4. กระบวนการนี้ดำเนินต่อไปจนกว่าจะพบโมดูลหรือถึงรูทของระบบไฟล์

ตัวอย่าง:

พิจารณาโครงสร้างโปรเจ็กต์ต่อไปนี้:


project/
├── src/
│   ├── components/
│   │   ├── SomeComponent.ts
│   │   └── index.ts
│   └── app.ts
├── tsconfig.json

หาก app.ts มีคำสั่ง Import import { SomeComponent } from './components/SomeComponent'; กลยุทธ์ Module Resolution classic จะ:

  1. มองหา ./components/SomeComponent.ts, ./components/SomeComponent.tsx หรือ ./components/SomeComponent.d.ts ในไดเรกทอรี src
  2. หากไม่พบ จะย้ายขึ้นไปยังไดเรกทอรีหลัก (รูทของโปรเจ็กต์) และทำซ้ำการค้นหา ซึ่งไม่น่าจะสำเร็จในกรณีนี้เนื่องจากคอมโพเนนต์อยู่ในโฟลเดอร์ src

ข้อจำกัด:

ควรใช้เมื่อใด:

โดยทั่วไปแล้วกลยุทธ์ Module Resolution classic เหมาะสำหรับโปรเจ็กต์ขนาดเล็กมากที่มีโครงสร้างไดเรกทอรีที่เรียบง่ายและไม่มี Dependency ภายนอก โปรเจ็กต์ TypeScript สมัยใหม่ควรใช้กลยุทธ์ Module Resolution node เกือบตลอดเวลา

Node Module Resolution

กลยุทธ์ Module Resolution node เลียนแบบอัลกอริทึม Module Resolution ที่ใช้โดย Node.js ทำให้เป็นตัวเลือกที่ต้องการสำหรับโปรเจ็กต์ที่กำหนดเป้าหมาย Node.js หรือใช้แพ็กเกจ npm เนื่องจากให้ลักษณะการทำงาน Module Resolution ที่สอดคล้องและคาดการณ์ได้

วิธีการทำงาน:

กลยุทธ์ Module Resolution node เป็นไปตามชุดกฎที่ซับซ้อนกว่า โดยจัดลำดับความสำคัญของการค้นหาภายใน node_modules และการจัดการนามสกุลไฟล์ที่แตกต่างกัน:

  1. Non-relative Import: หากเส้นทาง Import ไม่ได้ขึ้นต้นด้วย ./, ../ หรือ / TypeScript จะถือว่าเป็นการอ้างอิงถึงโมดูลที่อยู่ใน node_modules โดยจะค้นหาโมดูลในตำแหน่งต่อไปนี้:
    • node_modules ในไดเรกทอรีปัจจุบัน
    • node_modules ในไดเรกทอรีหลัก
    • ...และอื่นๆ ขึ้นไปจนถึงรูทของระบบไฟล์
  2. Relative Import: หากเส้นทาง Import ขึ้นต้นด้วย ./, ../ หรือ / TypeScript จะถือว่าเป็นเส้นทางแบบสัมพันธ์และค้นหาโมดูลในตำแหน่งที่ระบุ โดยพิจารณาถึงสิ่งต่อไปนี้:
    • ขั้นแรก จะมองหาไฟล์ที่มีชื่อและนามสกุลที่ระบุ (.ts, .tsx, .d.ts)
    • หากไม่พบ จะมองหาไดเรกทอรีที่มีชื่อที่ระบุและไฟล์ชื่อ index.ts, index.tsx หรือ index.d.ts ภายในไดเรกทอรีนั้น (เช่น ./components/index.ts หาก Import คือ ./components)

ตัวอย่าง:

พิจารณาโครงสร้างโปรเจ็กต์ต่อไปนี้ที่มี Dependency ในไลบรารี lodash:


project/
├── src/
│   ├── utils/
│   │   └── helpers.ts
│   └── app.ts
├── node_modules/
│   └── lodash/
│       └── lodash.js
├── tsconfig.json

หาก app.ts มีคำสั่ง Import import * as _ from 'lodash'; กลยุทธ์ Module Resolution node จะ:

  1. รับรู้ว่า lodash เป็น Non-relative Import
  2. ค้นหา lodash ในไดเรกทอรี node_modules ภายในรูทของโปรเจ็กต์
  3. ค้นหาโมดูล lodash ใน node_modules/lodash/lodash.js

หาก helpers.ts มีคำสั่ง Import import { SomeHelper } from './SomeHelper'; กลยุทธ์ Module Resolution node จะ:

  1. รับรู้ว่า ./SomeHelper เป็น Relative Import
  2. มองหา ./SomeHelper.ts, ./SomeHelper.tsx หรือ ./SomeHelper.d.ts ในไดเรกทอรี src/utils
  3. หากไม่มีไฟล์เหล่านั้น จะมองหาไดเรกทอรีชื่อ SomeHelper จากนั้นค้นหา index.ts, index.tsx หรือ index.d.ts ภายในไดเรกทอรีนั้น

ข้อดี:

ควรใช้เมื่อใด:

กลยุทธ์ Module Resolution node เป็นตัวเลือกที่แนะนำสำหรับโปรเจ็กต์ TypeScript ส่วนใหญ่ โดยเฉพาะอย่างยิ่งโปรเจ็กต์ที่กำหนดเป้าหมาย Node.js หรือใช้แพ็กเกจ npm ให้ระบบ Module Resolution ที่ยืดหยุ่นและมีประสิทธิภาพมากกว่าเมื่อเทียบกับกลยุทธ์ classic

การกำหนดค่า Module Resolution ใน tsconfig.json

ไฟล์ tsconfig.json เป็นไฟล์กำหนดค่าส่วนกลางสำหรับโปรเจ็กต์ TypeScript ของคุณ ช่วยให้คุณระบุตัวเลือกคอมไพเลอร์ รวมถึงกลยุทธ์ Module Resolution และปรับแต่งวิธีที่ TypeScript จัดการโค้ดของคุณ

นี่คือไฟล์ tsconfig.json พื้นฐานที่มีกลยุทธ์ Module Resolution node:


{
  "compilerOptions": {
    "moduleResolution": "node",
    "target": "es5",
    "module": "commonjs",
    "esModuleInterop": true,
    "strict": true,
    "outDir": "dist",
    "sourceMap": true
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules"
  ]
}

compilerOptions หลักที่เกี่ยวข้องกับ Module Resolution:

baseUrl และ paths: การควบคุมเส้นทาง Import

ตัวเลือกคอมไพเลอร์ baseUrl และ paths มีกลไกที่มีประสิทธิภาพสำหรับการควบคุมวิธีที่ TypeScript แก้ปัญหาเส้นทาง Import สามารถปรับปรุงความสามารถในการอ่านและความสามารถในการบำรุงรักษาโค้ดของคุณได้อย่างมาก โดยอนุญาตให้คุณใช้ Import แบบสัมบูรณ์และสร้างการแมปเส้นทางแบบกำหนดเอง

baseUrl

ตัวเลือก baseUrl ระบุไดเรกทอรีฐานสำหรับการแก้ปัญหาชื่อโมดูลที่ไม่ใช่แบบสัมพันธ์ เมื่อตั้งค่า baseUrl แล้ว TypeScript จะแก้ปัญหาเส้นทาง Import ที่ไม่ใช่แบบสัมพันธ์ที่สัมพันธ์กับไดเรกทอรีฐานที่ระบุ แทนที่จะเป็นไดเรกทอรีการทำงานปัจจุบัน

ตัวอย่าง:

พิจารณาโครงสร้างโปรเจ็กต์ต่อไปนี้:


project/
├── src/
│   ├── components/
│   │   ├── SomeComponent.ts
│   │   └── index.ts
│   └── app.ts
├── tsconfig.json

หาก tsconfig.json มีสิ่งต่อไปนี้:


{
  "compilerOptions": {
    "moduleResolution": "node",
    "baseUrl": "./src"
  }
}

จากนั้น ใน app.ts คุณสามารถใช้คำสั่ง Import ต่อไปนี้:


import { SomeComponent } from 'components/SomeComponent';

แทนที่จะ:


import { SomeComponent } from './components/SomeComponent';

TypeScript จะแก้ปัญหา components/SomeComponent ที่สัมพันธ์กับไดเรกทอรี ./src ที่ระบุโดย baseUrl

ประโยชน์ของการใช้ baseUrl:

paths

ตัวเลือก paths ช่วยให้คุณกำหนดค่าการแมปเส้นทางแบบกำหนดเองสำหรับโมดูล ให้วิธีการที่ยืดหยุ่นและมีประสิทธิภาพมากขึ้นในการควบคุมวิธีที่ TypeScript แก้ปัญหาเส้นทาง Import ช่วยให้คุณสร้าง Alias สำหรับโมดูลและเปลี่ยนเส้นทาง Import ไปยังตำแหน่งต่างๆ

ตัวเลือก paths เป็นออบเจ็กต์ โดยที่แต่ละคีย์แสดงถึงรูปแบบเส้นทาง และแต่ละค่าคืออาร์เรย์ของการแทนที่เส้นทาง TypeScript จะพยายามจับคู่เส้นทาง Import กับรูปแบบเส้นทาง และหากพบการจับคู่ จะแทนที่เส้นทาง Import ด้วยเส้นทางการแทนที่ที่ระบุ

ตัวอย่าง:

พิจารณาโครงสร้างโปรเจ็กต์ต่อไปนี้:


project/
├── src/
│   ├── components/
│   │   ├── SomeComponent.ts
│   │   └── index.ts
│   └── app.ts
├── libs/
│   └── my-library.ts
├── tsconfig.json

หาก tsconfig.json มีสิ่งต่อไปนี้:


{
  "compilerOptions": {
    "moduleResolution": "node",
    "baseUrl": "./src",
    "paths": {
      "@components/*": ["components/*"],
      "@mylib": ["../libs/my-library.ts"]
    }
  }
}

จากนั้น ใน app.ts คุณสามารถใช้คำสั่ง Import ต่อไปนี้:


import { SomeComponent } from '@components/SomeComponent';
import { MyLibraryFunction } from '@mylib';

TypeScript จะแก้ปัญหา @components/SomeComponent เป็น components/SomeComponent ตามการแมปเส้นทาง @components/* และ @mylib เป็น ../libs/my-library.ts ตามการแมปเส้นทาง @mylib

ประโยชน์ของการใช้ paths:

กรณีการใช้งานทั่วไปสำหรับ paths:

แนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการเส้นทาง Import

การจัดการเส้นทาง Import ที่มีประสิทธิภาพเป็นสิ่งสำคัญสำหรับการสร้างแอปพลิเคชัน TypeScript ที่ปรับขนาดและบำรุงรักษาได้ ต่อไปนี้คือแนวทางปฏิบัติที่ดีที่สุดที่ควรปฏิบัติตาม:

การแก้ไขปัญหา Module Resolution

ปัญหา Module Resolution อาจทำให้หงุดหงิดในการดีบัก ต่อไปนี้คือปัญหาและวิธีแก้ไขทั่วไป:

ตัวอย่างในโลกแห่งความเป็นจริงในเฟรมเวิร์กต่างๆ

หลักการของ TypeScript Module Resolution ใช้ได้กับเฟรมเวิร์ก JavaScript ต่างๆ นี่คือวิธีที่ใช้กันทั่วไป:

สรุป

ระบบ Module Resolution ของ TypeScript เป็นเครื่องมือที่มีประสิทธิภาพสำหรับการจัดระเบียบโค้ดเบสของคุณและการจัดการ Dependency อย่างมีประสิทธิภาพ โดยการทำความเข้าใจกลยุทธ์ Module Resolution ที่แตกต่างกัน บทบาทของ baseUrl และ paths และแนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการเส้นทาง Import คุณสามารถสร้างแอปพลิเคชัน TypeScript ที่ปรับขนาดได้ บำรุงรักษาได้ และอ่านง่าย การกำหนดค่า Module Resolution อย่างถูกต้องใน tsconfig.json สามารถปรับปรุงเวิร์กโฟลว์การพัฒนาของคุณได้อย่างมาก และลดความเสี่ยงของข้อผิดพลาด ทดลองกับการกำหนดค่าต่างๆ และค้นหาวิธีที่เหมาะสมที่สุดสำหรับความต้องการของโปรเจ็กต์ของคุณ