In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-05 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
Today, I would like to share with you what are the differences between Node's CJS and ESM. The content is detailed and the logic is clear. I believe most people still know too much about this knowledge, so share this article for your reference. I hope you can get something after reading this article.
Imperfect ESM support
1.1 using ESM in Node
Node only supports CJS syntax by default, which means that you have written a js file of ESM syntax and will not be executed.
If you want to use ESM syntax in Node, there are two possible ways:
⑴ added "type": "module" configuration item to package.json.
⑵ changes the files you want to use ESM to the .mjs suffix.
For the first method, Node parses all modules in the same path as the package.json file as ESM.
The second method does not require modification. Package.json,Node automatically parses all xxx.mjs files as ESM.
Similarly, if you set "type": "commonjs" in the package.json file, it means that the module under this path is parsed in the form of CJS. If the file suffix is .cjs, Node automatically parses it as a CJS module (even if it is configured in ESM mode in package.json).
We can modify the package.json above so that all modules are executed in ESM form, and then the modules on the project are written using ESM syntax.
If there are more obsolete CJS modules that don't bother to modify, move them all to a folder and add a new package.json with the content {"type": "commonjs"} under the folder path.
When Node parses a referenced module (whether it is import or require), it parses the module based on the suffix name of the referenced module, or the corresponding package.json configuration.
1.2 issues with ESM referencing CJS modules
ESM can basically import CJS modules smoothly, but for named exports (Named exports, that is, module.exports assigned as a whole), it can only be introduced in the form of default export:
/ * * @ file cjs/a.js * * / / named exportsmodule.exports = {foo: () = > {console.log ("It's a foo function...")}} / * * @ file index_err.js * * / import {foo} from'. / cjs/a.js'; / / SyntaxError: Named export 'foo' not found. The requested module'. / cjs/a.js' is a CommonJS module, which may not support all module.exports as named exports.foo (); / * @ file index_err.js * * / import pkg from'. / cjs/a.js'; / / introduce pkg.foo () in the form of default export; / / execute normally
1.3 problems with CJS referencing ESM modules
Suppose you are developing an open source project for others to use and export the module in the form of ESM, then the problem arises-the require function of CJS cannot be directly introduced into the ESM package, and an error will be reported:
Let {foo} = require ('. / esm/b.js'); ^ Error [ERR_REQUIRE_ESM]: require () of ES Module BlogDemo3\ 220220\ test2\ esm\ b.js from BlogDemo3\ 220220\ test2\ require.js not supported.Instead change the require of b.js in BlogDemo3\ 220220\ test2\ require.js to a dynamic import () which is available in all CommonJS modules. At Object. (BlogDemo3\ 220220\ test2\ require.js:4:15) {code: 'ERR_REQUIRE_ESM'}
According to the above misstatement, we cannot and use require to introduce the ES module (the reason will be mentioned later). Instead, we should use the dynamic import method built into the CJS module:
Import ('. / esm/b.js') .then (({foo}) = > {foo ();}); / / or (async () = > {const {foo} = await import ('. / esm/b.js');}) ()
Of course, open source projects can not force users to change to this form of introduction, so you have to use tools such as rollup to compile the project into CJS modules.
It can be seen from the above that Node.js 's current support for ESM syntax is limited, and these restrictions can be very bad if they are not handled with the help of tools.
For beginners who want to get started, these troublesome rules and restrictions can also be confusing.
As of the time I wrote this article, the Node.js LTS version was 16.14.0, more than two years after the 13.2.0 version of ESM was supported.
So why can't Node.js get through to CJS and ESM yet?
The answer is not that Node.js is hostile to the ESM standard and therefore does not optimize, but because-- CJS and ESM, the two are so different.
Second, the differences between CJS and ESM
2.1 different loading logic
In the CJS module, require () is a synchronization interface that reads the dependent module directly from disk (or network) and executes the corresponding script immediately.
ESM standard module loader is completely different, it will not be directly executed after reading the script, but will first enter the compilation phase for module parsing, check where import and export are called on the module, and download the dependent modules asynchronously and in parallel one by one.
At this stage, the ESM loader does not execute any dependent module code, only does syntax error detection, determines module dependencies, and determines module input and output variables.
Finally, ESM enters the execution phase, executing the scripts of each module sequentially.
So we often say that the CommonJS module is loaded at run time and the ES6 module is the compile-time output interface.
In section 1.2 above, we mentioned that CJS named exports cannot be introduced in ESM by specifying dependency module attributes:
/ * * @ file cjs/a.js * * / / named exportsmodule.exports = {foo: () = > {console.log ("It's a foo function...")}} / * * @ file index_err.js * * / import {foo} from'. / cjs/a.js'; / / SyntaxError: Named export 'foo' not found. The requested module'. / cjs/a.js' is a CommonJS module, which may not support all module.exports as named exports.foo ()
This is because ESM acquires the specified dependency module attributes (the attributes inside the curly braces), which requires static analysis during the compilation phase, while CJS scripts cannot calculate their named exports values until the run time, which will cause ESM to fail to analyze at compile time.
2.2 different modes
ESM uses strict mode (use strict) by default, so the this in the ES module no longer points to the global object (but undefined), and variables cannot be used before they are declared.
This is why in a browser, if a tag is to enable the ability to natively introduce ES modules, it must be added with type= "module" to tell the browser that it should be treated differently from regular JS.
Check out more restrictions on ESM strict mode:
Https://es6.ruanyifeng.com/#docs/module#%E4%B8%A5%E6%A0%BC%E6%A8%A1%E5%BC%8F
2.3 ESM supports "top-level await", but not CJS.
ESM supports top-level await (top-level await), that is, in the ES module, await can be used directly without having to be inside the async function:
/ / index.mjsconst {foo} = await import ('. / c.js'); foo ()
Go to Github to get the sample code (test3):
Https://github.com/VaJoy/BlogDemo3/tree/main/220220/test3
There is no such capability in the CSJ module (even if a dynamic import interface is used), which is one of the reasons why require cannot load ESM.
Imagine that the require loader in a CJS module loads an ES module synchronously, the ES module asynchronously import a CJS module, and the CJS module loads an ES module synchronously. This complex nested logic can be tricky to deal with.
Check out the more discussion on "how to load ESM with require":
Https://github.com/nodejs/modules/issues/454
2.4 ESM lacks _ _ filename and _ _ dirname
In CJS, the execution of the module needs to be wrapped in functions and specify some commonly used values:
NativeModule.wrapper = ['(function (exports, require, module, _ _ filename, _ _ dirname) {','\ n});']
So we can use _ _ filename, _ _ dirname directly in the CJS module.
The ESM standard does not include an implementation of this aspect, that is, you cannot use _ _ filename and _ _ dirname in Node's ESM.
As you can see from the above, there are huge compatibility issues in Node.js if you want to switch from the default CJS to ESM.
This is also a protracted battle of module specification that is difficult for Node.js to solve at present and even for a long time in the future.
If you want to feel comfortable using ESM without tools and rules, try using Deno instead of Node, which defaults to ESM as the module specification (of course, the ecology is not as perfect as Node).
Third, realize the mixed writing of CJS and ESM with the help of tools
With the help of building tools, we can achieve the mixing of CJS module and ES module, and even mix two kinds of standard API in the same module at the same time, so that developers no longer need to care about the limitations of Node.js. In addition, the build tool can also make use of the static parsing feature of ESM in the compilation phase to achieve Tree-shaking effect and reduce the output of redundant code.
Here, let's take rollup as an example to do a global installation:
Pnpm I-g rollup
Then install the rollup-plugin-commonjs plug-in, which allows rollup to support the introduction of CJS modules (rollup itself does not support the introduction of CJS modules):
Pnpm I-- save-dev @ rollup/plugin-commonjs
We create a new rollup configuration file rollup.config.js in the project root directory:
Import commonjs from 'rollup-plugin-commonjs';export default {input:' index.js', / / entry file output: {file: 'bundle.js', / / target file format:' iife'}, plugins: [commonjs ({transformMixedEsModules: true, sourceMap: false,})]}
Plugin-commonjs skips all modules with import/export by default. If you want to support mixed writing such as import + require, you need to have the transformMixedEsModules attribute.
Then execute the rollup-- config instruction, and you can compile and package according to rollup.config.js.
Example
/ * * @ file a.js * * / export let func = () = > {console.log ("It's an an a.js deadCode function");} export let deadCode = () = > {console.log ("[a.js deadCode] function") } / * * @ file b.js * * / / named exportsmodule.exports = {func () {console.log ("It's a Never been called here...")}, deadCode () {console.log ("[b.js deadCode] functor");} / * * @ file c.js * * / module.exports.func = () = > {console.log ("It's a CCL functionary...")} Module.exports.deadCode = () = > {console.log ("[c.js deadCode] Never been called here");} / * @ file index.js * * / let a = require ('. / a'); import {func as bFunc} from'. / b.jsaccountimport {func as cFunc} from'. / c.jsaccounts.func (); bFunc (); cFunc ()
The packaged bundle.js file is as follows:
(function () {'use strict'; function getAugmentedNamespace (n) {if (n.__esModule) return n; var a = Object.defineProperty ({},' _ esModule', {value: true}); Object.keys (n) .forEach (function (k) {var d = Object.getOwnPropertyDescriptor (n, k)) Object.defineProperty (a, k, d.get? D: {enumerable: true, get: function () {return n [k];}});}); return a } let func$1 = () = > {console.log ("It's an a Never been called here...");}; let deadCode = () = > {console.log ("[a.js deadCode] function");} Var aqum1 = / * # _ _ PURE__*/Object.freeze ({_ _ proto__: null, func: func$1, deadCode: deadCode}); var require$$0 = / * @ _ _ PURE__*/getAugmentedNamespace (axi1) Var b = {func () {console.log ("It's a bmer function...");}, deadCode () {console.log ("[b.js deadCode] Never been called here");}} Var func = () = > {console.log ("It's a CMFunction...");}; let a = require$$0; a.func (); b.func (); func ();}) ()
As you can see, rollup removes the deadCode method of the c module that has never been called through Tree-shaking, but the deadCode code snippets in modules an and b are not removed because we use require when referencing a.js and CJS named exports in b.js, which makes rollup unable to use the features of ESM for static parsing.
In general, when developing a project, it is recommended to use the syntax of ESM to write all modules as much as possible, so that you can maximize the use of build tools to reduce the size of the final build file.
These are all the contents of this article entitled "what are the differences between Node's CJS and ESM". Thank you for reading! I believe you will gain a lot after reading this article. The editor will update different knowledge for you every day. If you want to learn more knowledge, please pay attention to the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.