# 底层原理
# require的简易实现
- commonjs
// a.js
module.exports = 'abc'
// commonjs.js
const fs = require('fs')
const path = require('path')
function req(modulePath) {
const content = fs.readFileSync(path.resolve(__dirname, modulePath), 'utf8')
const fn = new Function('exports', 'module', '__dirname', '__filename', `${content}\r\nreturn module.exports`)
const module = {
exports: {}
}
return fn(module.exports, module, __dirname, __filename)
}
let str = req('./a.js')
console.log(str) // abc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- AMD
const factories = {
}
const define = (moduleName, dependencies, factory) => {
factory.dependencies = dependencies || []
factories[moduleName] = factory
}
function require(mods, factory) {
const result = mods.map(mod => {
const fn = factories[mod]
const dependencies = fn.dependencies
let exports = ''
require(dependencies, function() {
exports = fn.apply(null, arguments)
})
return exports
})
factory.apply(null, result)
}
define('a', [], () => {
return 'a'
});
define('b', [], () => {
return 'b'
})
define('c', ['b'], (b) => {
return b + 'c'
})
// ab
require(['a', 'b'], (a, b) => {
console.log(a + b)
})
// abc
require(['a', 'c'], (a, c) => {
console.log(a + c)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# webpack的简易实现
# 主要内容
npm link
读取配置文件
构建模块
- 获取源码
getSource(modulePath) {
return fs.readFileSync(modulePath, 'utf8')
}
1
2
3
2
3
- 解析模块
parse(source, parentPath) {
const ast = babylon.parse(source) // 生成ast
let dependencies = []
traverse(ast, {
CallExpression(p) {
let node = p.node
if (node.callee.name === 'require') {
node.callee.name = '__webpack_require__'
let moduleName = node.arguments[0].value
moduleName = moduleName + (path.extname(moduleName) ? '' : '.js')
moduleName = './' + path.join(parentPath, moduleName)
dependencies.push(moduleName)
node.arguments = [t.stringLiteral(moduleName)]
}
}
})
let sourceCode = generator(ast).code
return {
sourceCode,
dependencies
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- 递归构建依赖模块
const { sourceCode, dependencies } = this.parse(source, path.dirname(moduleName))
this.modules[moduleName] = sourceCode
dependencies.forEach(dependency => {
this.buildModule(path.join(this.root, dependency), false)
})
1
2
3
4
5
2
3
4
5
- 模板合并,发射文件
const main = path.resolve(this.config.output.path, this.config.output.filename)
const templateStr = this.getSource(path.join(__dirname, 'template.ejs'))
const code = ejs.render(templateStr, {
entryId: this.entryId,
modules: this.modules
})
fs.writeFileSync(main, code)
1
2
3
4
5
6
7
2
3
4
5
6
7
← tapable