什么是模块化
- 模块化是一种最主流的代码组织方式,它通过把我们的复杂代码按照功能的不同划分为不同的模块,单独维护的方式去提高开发效率降低维护成本
- 模块化只是思想
模块化历程
- CommonJS 规范
模块化的操作都是同步完成的,不适合在浏览器使用。因为在后端运行js模块化加载都是可以直接在磁盘中读取的,速度一般不会受到影响 - AMD规范
异步操作的加载规范,提供了require
和default
关键字实现模块化的操作 - CMD规范
整合了CommonJS
和AMD规范
的特点专门实现浏览器异步模板化的加载(代表cjs) - ES modules 规范
ES6
中将模块化纳入标准规范 - 当下常用的规范是
CommonJS
和ESM
,一个用于node平台下的开发,一个用于浏览器平台下的开发
CommonJS(超集,模块化只是里面的一种)
- CommonJs可以动态加载语句,代码发生在运行时
- CommonJs混合导出,还是一种语法,只不过不用声明前面对象而已,当我导出引用对象时之前的导出就被覆盖了
- CommonJs导出值是拷贝,可以修改导出的值,这在代码出错时,不好排查引起变量污染
导出
module属性
1. 模块导出
const a = { name:'cx' }
const b = '字符串'
module.exports = {a, b, c:'c'}
我们可以直接在 exports 对象上添加方法,表示对外输出的接口,如同在module.exports上添加一样
exports.callback = done =>{
console.log('callback task~')
done()
}
// module.exports 和 exports. 的形式同时导出时,导入该文件对象为module.exports
// 想要module.exports
和exports
的形式同时导出可以用以下方式👇
exports = module.exports = {a: 'a', b: 'b', c: 'c'} // 默认导出的必须是一个对象(函数)
exports.callback = done => {}
导出共用
exports.callback2 = exports.callback
导入
require属性
1. 导入系统模块
const fs = require('fs');
2. 导入js文件或第三方库
const my = require('./my.js')
3. 导入 exports 对象
const callback = require('./a.js') // a 是一个空对象
模块加载速度
- 核心模块:node源码编译时写入到二进制文件中
- 文件模块:代码运行时,动态加载
文件定位
缓存优化原则
ES Modules
通过给 script 添加 type = “module”,就可以以ES Module的标准执行其中的Js代码
特性
- ESM自动采用严格模式,忽略 ‘use strict’(不能在全局范围使用this,为undefined)
- 每个ES Module 都是运行在单独的私有作用域中
- ESM是通过CORS的方式请求外部JS模块的(src,服务端必须支持cors,cors不支持文件的形式访问)
- ESM的script标签会延迟执行脚本(等待渲染完后再执行脚本)
注意
- 以下这种方式的导入导出并不是以字面量的形式,是一种固定的语法
export { name , age }; import { name , age } from ‘./modules.js’- export导出不是把值复制一份给你,而是把存放值的地址给你(拿到的值会受内部值修改的影响)
- 在外部导入的成员是一个只读的成员
- 导入的成员必须要有完整的文件名称,相对路径中的./不能省略,
可以使用绝对路径或完整的url( /xx/modules.js 或 http:// )
导出
1. 命名导出
export const a = () => {}
2. 默认导出(每个模块只能有一个,可以是一个对象)
const a = {
fn(){},
b:18
}
export default a
导入
1. 导入多个接口(可用as起别名)
import { xx, xx } form ''
2. 默认导入(可用as起别名)
import xx from ''
导入默认模块重命名
import { default as title } from './modules.js'
只执行模块, 不提取它
import './modules.js'
3.混合导入
import title { name, age } form './modules.js' // 左边是默认,右边是具名
4. 导入css
import 'xx/xx.css'
5. 导入图片
import img form 'xx/xx.png'
<img :src={img} alt="" />
6. 动态导入
if(true){
import('./modules.js').then((res)=>{}) // 可不在最顶级的地方导入
}
7.直接导出导入成员
引入导出
export { Button } from './Button.js'
export { Avatar } from './Avatar.js'
引入
import { Button, Avatar } from 'index.js'
CommonJS 与 ES Modules交互
以ES Modules方式运行模块
node --experimental-modules esm.mjs
以ES Modules方式实现CommonJS的 filename 和dirname
import { fileURLToPath } from 'url'
import { dirname} from 'path'
const __filename = fileURLToPath(import.meta.url)
console.log(__filename) // \differences\esm.mjs 当前运行文件的路径
const __dirname = dirname(__filename)
console.log(__dirname) // \differences 当前项目路径
- ES Modules可以导入CommonJS模块
- CommonJS不能导入ES Modules模块
- CommonJS始终只会导出一个默认成员
- 注意import不是解构导出对象
在package.json中配置{“type”:“module”},在项目下的所有的文件会以ES Modules的方式工作了,就不需要修改为(mjs)👇
在node12版本中,将文件名修改为.cjs就可以使用CommonJS的规范了
如果你使用的是早期的nodejs版本可以使用Babel做es的兼容
Babel将一些使用了新特性的代码编译成当前环境支持的代码
yarn @babel/node @babel/core @babel/preset-env --dev
preset-env是一个插件的集合
yarn babel-node index.js --presets=@babel/preset-env
可配置在.babelrc
{“presets”:[“@babel/preset-env”]}