当import/export、require/module.ex
ES6的模块化: import
export default
common.js的模块化: require
module.exports
node.js使用该规范
为什么有模块概念
理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。
但是,Javascript不是一种模块化编程语言,在es6以前,它是不支持”类”(class),所以也就没有”模块”(module)了。同时以前也没有webpack这样的工具,可以使用node去操作文件,读取文件内容, 然后达到引入文件内容的目的.(也就是模块化)--在不同的文件中放着不同的功能模块
Common.js
首先需要明白的在node中每个文件都是一个自执行函数(只不过我们肉眼凡胎看不出来,哈啊哈)
(function (exports, require, module, __filename, __dirname) {
module.exports = exports = this
return module.exports
})()
为了说明module.exports = exports = this
请看下面例子
a.js
this.a = 123 // 第一种
exports.a = 123 //第二种
module.exports.a = 123 // 第三种
b.js
let a = require('./a')
console.log(a)
效果:
image
ES6中的模块化
import/export
export default
和 export
的区别
- 在一个文件或模块中
export
可以有多个,但export default
仅有一个 - 通过 export 方式导出,在导入时要加 { },而 export default 则不需要
1.export
//a.js
export const str = "小生方勤";
//b.js
import { str } from 'a'; // 导入的时候需要花括号
2.export default
//a.js
const str = "小生方勤";
export default str;
//b.js
import str from 'a'; // 导入的时候无需花括号
export default const a = 1;
这样写是会报错的哟。
es6 的导出模块写法有
export default 123;
export const a = 123;
const b = 3;
const c = 4;
export { b, c };
虽然我们一般在像Vue一些框架中都可以使用Es6的语法进行导入导出,但是babel 会将这些统统转换成 commonjs 的 exports。
exports.default = 123;
exports.a = 123;
exports.b = 3;
exports.c = 4;
exports.__esModule = true;
babel 转换 es6 的模块输出逻辑非常简单,即将所有输出都赋值给 exports,并带上一个标志 __esModule
表明这是个由 es6 转换来的 commonjs 输出。
babel将模块的导出转换为commonjs规范后,也会将引入 import 也转换为 commonjs 规范。即采用 require 去引用模块,再加以一定的处理,符合es6的使用意图。
也就是所有最后通过webpack打包后都将转化为common.js的规范
引入 default
对于最常见的
import a from './a.js';
在es6中 import a from './a.js' 的本意是想去引入一个 es6 模块中的 default 输出。
通过babel转换后得到 var a = require(./a.js) 得到的对象却是整个对象,肯定不是 es6 语句的本意,所以需要对 a 做些改变。
我们在导出提到,default 输出会赋值给导出对象的default属性。
exports.default = 123;
所以这里最后的 a 变量就是 require 的值的 default 属性。如果原先就是commonjs规范的模块,那么就是那个模块的导出对象。
引入 * 通配符
我们使用 import * as a from './a.js'
es6语法的本意是想将 es6 模块的所有命名输出以及defalut输出打包成一个对象赋值给a变量。
已知以 commonjs 规范导出:
exports.default = 123;
exports.a = 123;
exports.b = 3;
exports.__esModule = true;
那么对于 es6 转换来的输出通过 var a = require('./a.js') 导入这个对象就已经符合意图。
import { a } from './a.js'
import { a } from './a.js'
直接转换成 require('./a.js').a
即可。
总结
经过上面的转换分析,我们得知即使我们使用了 es6 的模块系统,如果借助 babel 的转换,es6 的模块系统最终还是会转换成 commonjs 的规范。所以我们如果是使用 babel 转换 es6 模块,混合使用 es6 的模块和 commonjs 的规范是没有问题的,因为最终都会转换成 commonjs。
问题
为何有的地方使用 require 去引用一个模块时需要加上 default?
require('xx').default
我们在上文 babel 对导出模块的转换提到,es6 的 export default 都会被转换成 exports.default,即使这个模块只有这一个输出。
我们经常会使用 es6 的 export default 来输出模块,而且这个输出是这个模块的唯一输出,我们会误以为这种写法输出的是模块的默认输出。
// a.js
export default 123;
// b.js 错误
var foo = require('./a.js')
在使用 require 进行引用时,我们也会误以为引入的是a文件的默认输出。
结果这里需要改成 var foo = require('./a.js').default
这个场景在写 webpack 代码分割逻辑时经常会遇到。
require.ensure([], (require) => {
callback(null, [
require('./src/pages/profitList').default,
]);
});
模块依赖的优化
模块依赖的优化
我们在使用各大 UI 组件库时都会被介绍到为了避免引入全部文件,请使用 babel-plugin-component
等babel 插件。
import { Button, Select } from 'element-ui'
由前文可知 import 会先转换为 commonjs, 即
var a = require('element-ui');
var Button = a.Button;
var Select = a.Select;
var a = require('element-ui');
这个过程就会将所有组件都引入进来了。
所以 babel-plugin-component
就做了一件事,将 import { Button, Select } from 'element-ui'
转换成了
import Button from 'element-ui/lib/button'
import Select from 'element-ui/lib/select'
即使转换成了 commonjs 规范,也只是引入自己这个组件的js,将引入量减少到最低。
所以我们会看到几乎所有的UI组件库的目录形式都是
|-lib
||--component1
||--component2
||--component3
|-index.common.js
index.common.js
给 import element from 'element-ui'
这种形式调用全部组件
给个element的链接去看看吧 :https://element.eleme.cn/#/zh-CN/component/quickstart