ESmodule的用法全解析,以及在node中与CommonJS规范模块化的交互用法。

1,009 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

ES6导入导出的注意事项

// moudles.js
let name = "jack", age = 18;
let obj = {
    name, 
    age
}
export { name, age }
// app.js
import { name, age } from "./module.js"

// 1. 导出后面的花括号 虽然看着和对象的字面量 一样 但是export 后面的 花括号 是导出的特定的语法, import后面的{  }也不是对象中解构的意思 也是 一种固定的用法
// 导出的 name age是一个引用关系  即导入到app.js中的name 与 moudles.js中的是一个
export default obj   //{ name, age }
// 用 export default { name, age } 导出的对象 假如 用 import { name, age } 去导入的话 会报错可见,import后面的{  }也不是对象中解构的意思 是 一种固定的用法

//2. 假如要导出一个对象 ,则要使用 export default 这时 后面的 { }含义与对象字面量中的{ } 一样,export default 后面可以导出一个值

ES Modules导入用法

// app.js
import { name, age } from "./module.js"
// 1. from 后面不能省略.js的扩展名 与 commonJs是有区别的

// 2. commonJs中导入一个文件目录 会自动导入下面的index.js  EsModules中是不可以的 必须要补全路径  但是 再用到打包工具后就会 可以省略 index 这个默认路径 和 .js的扩展名了。
// 3."./module.js" 中  ./是不能省略; 可以用/开头的绝对路径 ; 也可以使用完整的url所以可以导入cdn
import {  } from "./module.js"
// 简写  import "./module.js"  加载该模块,而不提取它
// 4.假如 import后的{}为空 则会执行该模块,而不会提取任何成员
import * as mod from "./module.js"
// 5.假如导出的成员很多,我们需要全部都导入则可以 使用 * 用as 取一个 别名 这是导入的则会是一个 对象  可以通过mod.属性名 来拿到 对应的成员
import("./module.js").then(module => {
    console.log(module); 
})
// 6. import这个关键词 只能出现在最顶层
// 假如需要动态导入 则要使用 import() 这个函数 返回值是一个promise
// moudles.js
let name = "jack", age = 18, title = "hello";
let obj = {
    name, 
    age
}
export { name, age }

export default title
// app.js
import { name, age, default as title } from "./module.js"
// 或者
import title, { name, age } from "./module.js" //通过都好分割 ,左边的是默认成员 名字可以随便起

ES Modules 导出导入成员

将导入的结果直接作为结果导出

// app.js
export { foo, bar } from "./module.js"

// 将 import关键词直接修改为export 此时 在当前文件 就不能访问这些成员了

ES Modules 在浏览器中 Polyfill 兼容方案

//生产阶段 不推荐使用 ES Module Loader

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>ES Module 浏览器环境 Polyfill</title>
</head>
<body>
  <!-- nomodule这个属性可以判断 该浏览器是否支持ES Module 如果没有该属性在支持ES Module的浏览器中将会执行两边代码 -->
  <script nomodule src="https://unpkg.com/promise-polyfill@8.1.3/dist/polyfill.min.js"></script>
  <script nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/babel-browser-build.js"></script>
  <script nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/browser-es-module-loader.js"></script>
  <script type="module">
    import { foo } from './module.js'
    console.log(foo)
  </script>
</body>
</html>

ES Modules in Node.js

// 第一,将文件的扩展名由 .js 改为 .mjs;
// 第二,启动时需要额外添加 `--experimental-modules` 参数; node --experimental-modules index.mjs

import { foo, bar } from './module.mjs'

console.log(foo, bar)

// 此时我们也可以通过 esm 加载内置模块了
import fs from 'fs'
fs.writeFileSync('./foo.txt', 'es module working')

// 也可以直接提取模块内的成员,内置模块兼容了 ESM 的提取成员方式
import { writeFileSync } from 'fs'
writeFileSync('./bar.txt', 'es module working')

// 对于第三方的 NPM 模块也可以通过 esm 加载
import _ from 'lodash'
console.log(_.camelCase('ES Module'));

// 不支持,因为第三方模块都是导出默认成员 必须使用默认导入的方式导入
// import { camelCase } from 'lodash'
// console.log(camelCase('ES Module'))

ES Modules in Node.js 与 CommonJS交互

  • ESM 导入 commonJs
// commonjs.js 

// commonjs 模块始终只会导出一个默认成员
module.exports = {
    foo: "CommonJS exports value"
}
//CommonJs 中exports就是 module.exports的一个别名 
exports.foo = "CommonJS exports value"

// es-module.mjs

import mod from "./commonjs.js"
console.log(mod)   // { foo: "commonjs export value"}

// 1.Es Module 中可以导入CommonJS 模块

  • commonJs导入EsM
// es-module.mjs

export const foo = "es module export value"


// commonjs.js 

const mod = require("./es-module.mjs")
console.log(mod)  //直接载入报错

// 不能在CommonJS模块中通过require载入ES Module

小结

  • ES Module中可以导入 CommonJS模块
  • CommonJS中不能导入ES Module模块
  • 注意!import不是解构导出对象
  • CommonJS 模块始终只会导出一个默认成员

ES Modules in Node.js 与 CommonJS的差异

// cjs.js

// 加载模块函数
console.log(require)

// 模块对象
console.log(module)

// 导出对象别名
console.log(exports)

// 当前文件的绝对路径
console.log(__filename)

// 当前文件所在目录
console.log(__dirname)



// esm.mjs
// ESM 中没有模块全局成员了
// // 加载模块函数
// console.log(require)

// // 模块对象
// console.log(module)

// // 导出对象别名
// console.log(exports)

// // 当前文件的绝对路径
// console.log(__filename)

// // 当前文件所在目录
// console.log(__dirname)

// -------------

// require, module, exports 自然是通过 import 和 export 代替

// __filename 和 __dirname 通过 import 对象的 meta 属性获取
// const currentUrl = import.meta.url
// console.log(currentUrl)

// 通过 url 模块的 fileURLToPath 方法转换为路径
import { fileURLToPath } from 'url'
import { dirname } from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
console.log(__filename)
console.log(__dirname)

ES Modules in Node.js新版本的支持

  1. 在package.json中设置,便不需要将后缀改为 .mjs了 但是commonJs模块的文件 则需要把后缀改为.cjs 才可以使用CommoonJs规范
{
    "type": "module"
}

ES Modules in Node.js 兼容方案

//安装  yarn add @babel/node @babel/core @babel/preset-env --dev
// 运行 yarn babel-node index.js --presets=@babel/preset-env 

// .babelrc
{
    "presets":["@babel/preset-env"]
}
// 运行时不用再加参数  yarn babel-node index.js

// preset 是指一组插件  具体转化是由每一个插件完成的
// 例如只安装 @babel/plugin-transform-modules-commonjs  这个插件
// yarn add @babel/plugin-transform-modules-commonjs --dev

// .babelrc
{
    "plugins":["@babel/plugin-transform-modules-commonjs"]
}