# 底层原理

# 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
  • 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

# webpack的简易实现

# 主要内容

  1. npm link

  2. 读取配置文件

  3. 构建模块

  • 获取源码
getSource(modulePath) {
    return fs.readFileSync(modulePath, 'utf8')
}
1
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
  1. 递归构建依赖模块
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
  1. 模板合并,发射文件
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