Nodesjs - Require chưa bao giờ hết thắc mắc
TIL
590
@100daysTIL
72
White

Minh-Trung Nguyễn viết ngày 15/02/2018

Nodesjs - Require chưa bao giờ hết thắc mắc

Day22

Tham khảo:

Require(something) có gì?

Tạo 2 file, index.jshello.js trong cùng 1 folder, với nội dung như sau:

// hello.js
console.log("Hello Trung!");

// index.js
const hello = require('./hello');
console.log(module);

Chạy node index.js, rồi quan sát kết quả trong terminal:

Hello Trung
Module {
  id: '.',
  exports: {},
  parent: null,
  filename: '/backend/app-echo-server/index.js',
  loaded: false,
  children:
   [ Module {
       id: '/backend/app-echo-server/hello.js',
       exports: {},
       parent: [Circular],
       filename: '/backend/app-echo-server/hello.js',
       loaded: true,
       children: [],
       paths: [Array] } ],
  paths:
   [ '/backend/app-echo-server/node_modules',
     '/backend/node_modules',
     '/node_modules' ] }

Có thể thấy là:

  • "Hello Trung" là kết quả của const hello = require('./hello'):
    • Bạn require này bản thân cũng là 1 module, nhưng là global object nên ta không phải khai báo require('require'). Object này không có gì đặc biệt, nó chỉ đóng vai trò nhận vào tên hoặc đường dẫn của module ta muốn nhúng vào, sau đó trả về object module.exports.
    • Bạn require này nhúng code của hello.js vào index.js, nên dòng console.log("Hello Trung") trong hello.js mới được thực thi. Nhưng không phải nhúng tay bo mà còn được gói lại trong 1 lớp code nữa. Nói sau.
  • Bạn module cũng là 1 module (module tên "module"), là global object, không cần require('module').
    • Bạn module này giống như 1 ông quản gia ngầm, chứa hết các thông tin hầm bà làng về id,, exports, parent, filename, loaded, children, và path của các modules liên quan.
    • Cho dù có không gọi module qua console.log() thế kia, thì nó vẫn tồn tại ngầm phía sau.
    • Với module index.js:
    • id bằng '.' (folder gốc), trỏ đường dẫn đến module index.js
    • exports bằng {} (rỗng) -> index.js không export gì cả.
    • children thể hiện thông tin về module hello.js. Tức là Node.js coi module hello.jscon của module index.js. Bên trong module hello.js lại chứa 1 loạt các thuộc tính như id, exports, ... như trên.
    • path chứa rất hiều folder -> không hiểu tại sao lại lắm folder như thế.

Gọi module.exports hiện hình

Bây giờ thay đổi hello.js một chút, gọi bạn module.exports ra xem:

console.log("Hello Trung");

module.exports = function salute() {
    console.log("Bonjour, Trung!");
};

Trong đoạn code trên, ta đã gán hàm salute() vào module.exports. Lúc này đây, khi gọi lại node index.js, mọi thứ xuất ra trong terminal vẫn thế, trừ phần children:

 children:
   [ Module {
       id: '/backend/app-echo-server/hello.js',
       exports: [Function: salute],
       parent: [Circular],
       filename: '/backend/app-echo-server/hello.js',
       loaded: true,
       children: [],
       paths: [Array] } ],

À, vậy là Module.children.exports của hello.js không còn là rỗng {} nữa, mà đã thành [Function: salute].

Nghĩa là hàm salute() từ hello.js đã được nhúng thành công vào index.js, ta có thể sử dụng salute() trong index.js một cách tự nhiên. Công lớn thuộc về ai? Chắc là bạn object module. Cái này là thứ mà Lê Minh Tuấn trong bài NodeJS - require, exports, module.exports không nhắc đến.

Để làm tình hình phức tạp hơn, có thể:

  • require() thêm nhiều module khác
  • Không chỉ gán hàm vào module.exports, mà còn object chẳng hạn

để xem global object module nó hiện ra cái gì.

Điều gì xảy ra đằng sau require(something)?

Để "nhúng" được các module phụ vào module chính, Node.js đã thực hiện các bước sau:

# Bước Phiên âm Giải thích
1 Resolving /rəˈzälviŋ/ Đi tìm đường dẫn tuyệt đối của file
2 Loading /ˈləʊdɪŋ/ Xác định type của file
3 Wrapping /ˈræpɪŋ/ Bọc file lại, cho file 1 private scope. Biến object requiremodule thành dạng local.
4 Evaluating /ɪ'væljueɪt/ what the VM eventually does with the loaded code (không hiểu)
5 Caching /kæʃɪŋ/ cơ chế caching, để khi require một file nào một lần nữa, thì Node.js sẽ không chạy lại file đó từ đầu.

Tại sao lại cần bước resolving để đi tìm đường dẫn tuyệt đối của file chứa module?

Cần có bước này để đảm bảo Nodejs tìm và chỉ trỏ đến 1 và chỉ 1 file ứng với 1 module cho dù người dùng có đặt cái gì vào something trong require(something) đi chăng nữa. something có thể là:

  • core module/ package, ví dụ const filesystem = require('fs')
  • module/package của bên thứ ba, cài bằng npm, ví dụ: const express = require('express')
  • file JS đơn lẻ tự nghĩ ra, ví dụ: const server = require('./boot/server.js')
  • file JSON, ví dụ const databaseConfigs = require('./configs/database.json')
  • thậm chí cả 1 folder const routes = require('./routes') (gọi tắt thay vì gọi './routes/index.js'.

Vậy tìm ở đâu? ta chỉ nói chung chung là require(something) thì resolving sẽ tìm ở những folder nào? Nó tìm trong phần Module.path ở bên trên kia kìa. Theo thứ tự từ trên xuống dưới. Nếu để ý, bạn Node.js luôn chỉ tìm ở những folder "node_modules".

  paths:
   [ '/backend/app-echo-server/node_modules',
     '/backend/node_modules',
     '/node_modules' ] }

Điều đó có nghĩa là, nếu trong index.js, ta bỏ đường dẫn của hello.js đi, nghĩa là để const hello = require('hello.js'); thay vì const hello = require('./hello'); thì nó sẽ báo lỗi "Error: Cannot find module 'hello.js'" ngay tắp lự. Đơn giản là vì Module.path không chứa thư mục gốc.

Bây giờ, vẫn để const hello = require('hello.js'); (không cho đường dẫn), rồi tạo folder "node_modules" bên trong thư mục gốc, rồi nhét vào trong đó cũng 1 file hello.js với nội dung:

console.log("Tôi là hello.js giả mạo, đặt trong folder con 'node_modules', dưới folder chứa index.js một bậc");

gọi node index.js, ta sẽ thấy code chạy ngon lành, terminal hiện lên chữ:

Tôi là hello.js giả mạo, đặt trong folder con 'node_modules', dưới folder chứa index.js một bậc

Waaaa, như vậy đúng là một khi không để đường dẫn đến file module một cách cụ thể, thì bạn Node.js chỉ mò mẫn trong đống paths nhà bạn ý thôi.

Lưu ý:

  1. Nếu không require() đến file, mà đến folder, thì Node.js sẽ gọi file index.js chứa trong folder được gọi.
  2. Đôi khi không muốn load file hoặc package, mà chỉ kiểm tra xem file hoặc package đó đã tồn tại (được cài hay chưa), thì có thể chỉ cần dùng require.resolve(something) là đủ.
  3. Đường dẫn có 2 loại:
    • Tương đối (relative path):
    • Bắt đầu với ./ --> tính từ folder hiện tại
    • Bắt đầu với ../--> tính từ folder trên folder hiện tại 1 bậc
    • Tuyệt đối (absolute path):
    • Bắt đầu với /

Về loading

Chưa rõ

Về wrapping, tại sao lại cần wrapping (đóng gói)?

Chưa hiểu hết. Tính sau

Về caching, tại sao lại cần caching?

Cũng chưa hiểu. Nhưng là vấn đề quan trọng. Đọc lại sau.

ngminhtrung 16-02-2018

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Minh-Trung Nguyễn

58 bài viết.
72 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
44 15
Đăng lại một bài đã viết từ cách đây mấy tháng. Chủ đề này đã có không ít, nhưng chẳng hiểu sao lượng bài tự viết của dân lập trình người Việt ta v...
Minh-Trung Nguyễn viết 3 tháng trước
44 15
White
27 5
Ghi chú: Tiêu đề hoàn toàn mang tính câu view. Bài copy từ blog của tác giả :) Tại sao lại có bài viết này? Một ngày đẹp giời tôi cần kiểm t...
Minh-Trung Nguyễn viết 7 tháng trước
27 5
White
16 4
Về bước tìm và xử lý dữ liệu của Việt Nam phục vụ Data Visualization nền web Làm việc với D3js được nửa năm, một trong những điều bận lòng là chưa...
Minh-Trung Nguyễn viết 2 tháng trước
16 4
Bài viết liên quan
White
2 1
Javascript inititalValue trong reduce() có quan trọng không? Day 41: Đọc code mẫu về hàm reduce() trong (Link), thấy hàm reduce() khá "đơn giản"....
Minh-Trung Nguyễn viết 3 tháng trước
2 1
White
0 2
fCC: Technical Documentation Page note So I have finished the HTML part of this exercise and I want to come here to lament about the lengthy HTML ...
HungHayHo viết 28 ngày trước
0 2
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
58 bài viết.
72 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!