Javascript 不是一种模块化编程语言,在 ES6 以前,它是不支持类 class,所以也就没有模块 module。因此社区一直在寻找如何实现模块化效果的最佳可行性方案。
var module = (function() {
var _count = 0
var m1 = function() {
alert(_count)
}
var m2 = function() {
alert(_count + 1)
}
return {
m1: m1,
m2: m2
}
})()
有了服务器端模块以后,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。但是,由于一个重大的局限,使得 CommonJS 规范不适用于浏览器环境。
CommonJS 采用的是同步加载。在服务器端所有的模块都存放在本地硬盘,可以同步加载完成。而浏览器则取决于网速问题不可能等待文件被加载。因此只能采用异步加载。
采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。同时期出现两种规范:AMD (Asynchronous Module Definition)(代表:requirejs) 和 CMD (Common Module Definition) (代表:seajs)。
// CMD
define(function(require, exports, module) {
var a = require('./a') // 依赖在使用的时候引入
a.doSomething()
var b = require('./b')
b.doSomething()
})
// AMD
define(['./a', './b'], function(a, b) {
// 依赖在刚开始的时候定义
a.doSomething()
b.doSomething()
})
ES6 标准发布后,module 成为标准,标准使用是以 export 指令导出接口,以 import 引入模块。
在前端应用中我们使用新的标准,但是在开发 node 应用使用我们一贯的使用 CommonJS 规范。
module 的语法参考之前写的文章 ”ES6 Module 多种用法“
// 根据规则 1
// exports === module.exports
// exports 是 module.exports 一个引用
// 因此下边两个是等价的
exports.name = ''
module.exports.name = ''
// 如果直接给 exports 赋值
// 这种写法改变了 exports 的引用给他新分配了一个引用
// 因此:
// exports === { name: '' }
// exports !== module.exports
// module.exports !== { name: '' }
// module.exports === undefined
// 因此导出的模块是空的,原因是上述规则的第二条
exports = { name: '' }
require.ensure()是 webpack 用来加载异步模块的方式,import()是 ES 提案。
虽然 require.ensure()任然可以使用但是建议使用 import()。
require.ensure() 接收四个参数,依赖数组、回调函数、错误回掉函数(可选)和 chunk 块名
const A = r => require.ensure([], () => r(require('@views/make/A')), 'A')
const B = () => import(/* webpackChunkName: "B" */ '@views/make/B')
官网地址
在 ES 规范中提出允许 import 函数使用变量去加载文件,但是 webpack 目前还不完全支持。例如 import(foo),这样完全动态的加载方式将会失败,因为 webpack 需要一些文件位置信息。因为变量 foo 可能是系统或项目中任何文件的路径。import()必须至少包含关于模块所在位置的一些信息,因此让捆绑可以局限于特定的目录或文件夹。因此需要如下使用方式:
// 必须由三部分组成
// 1、文件的目录至少一级
// 2、文件路径 + 文件名
// 3、文件后缀
import('./' + path + '.js')
require('./' + path + '.js')
这样做 webpack 会根据 1 和 3 将所有符合条件的文件都打包到一起,然后根据结果返回想要的内容。这意味着 webpack 能够支持动态地 require,但会导致所有可能用到的模块都包含在 bundle 中。
通过 require.context() 函数来创建自己的 context。可以给这个函数传入三个参数:一个要搜索的目录,一个标记表示是否还搜索其子目录, 以及一个匹配文件的正则表达式。
require.context(directory, (useSubdirectories = false), (regExp = /^\.\//))