基础知识

模块化:增强代码可读性和维护性
传统的网页开发转变成 Web Apps 开发 
代码复杂度在逐步增高 
分离的 JS文件/模块,便于后续代码的维护性 部
署时希望把代码优化成几个 HTTP 请求
常见的几种模块化方式
1.ES module
import * as largeNumber from 'large-number';
// ...
largeNumber.add('999', '1');

2.CJS
const largeNumbers = require('large-number');
// ...
largeNumber.add('999', '1');

3.AMD
require(['large-number'], function (large-number) {
//...
largeNumber.add('999', '1');
});
AST 基础知识
抽象语法树(abstract syntax tree 或者缩写为 AST),或者语法树(syntax tree),是 源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。树上的每个节点都 表示源代码中的一种结构。

ast

Webpack 模块机制

modules
· 打包出来的是一个 IIFE (匿名闭包)
· modules 是一个数组,每一项是一个模块初始化函数
· __webpack_require 用来加载模块,返回 module.exports
· 通过 WEBPACK_REQUIRE_METHOD(0) 启动程序

简易webpack

1. 可以将 ES6 语法转换成 ES5 的语法
    ·通过 babylon 生成AST
    ·通过 babel-core 将AST重新生成源码
2. 可以分析模块之间的依赖关系
    ·通过 babel-traverse 的 ImportDeclaration 方法获取依赖属性
3. 生成的 JS 文件可以在浏览器中运行
目录结构

catalog

package.json
    {
        "name": "spack",
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
            "test": "echo \"Error: no test specified\" && exit 1"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "dependencies": {
            "@babel/preset-env": "^7.16.11",
            "babel-core": "^6.26.3",
            "babel-preset-env": "^1.7.0",
            "babel-traverse": "^6.26.0",
            "babylon": "^6.18.0"
        }
    }
src/index.js
    import { greeting } from './greeting.js'
    import {hh} from './hh.js'
    const a = greeting('baishu')
    const b = hh()
    document.write(`${a} ${b}`)
src/greeting.js
    export function greeting(name){
        return 'hello ' + name
    }
src/hh.js
    export function hh () {
        return 'hai webpack'
    }
lib/index.js
    const Compiler = require('./compiler.js')
    const options = require('../simplepack.config')
    new Compiler(options).run();
simplepack.config.js
'use strict';

const path = require('path')

module.exports = {
    entry: path.join(__dirname,'./src/index.js'),
    output:{
        path: path.join(__dirname,'./dist'),
        filename:'main.js'
    }
}
.babelrc
{
    "presets": [
        "@babel/preset-env"
    ]
}
dist/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <script type="text/javascript" src="./main.js"></script>
    </body>
    </html>
lib/compiler.js
    const fs = require('fs')
    const { getAST, getDependencies, transform } = require('./parser')
    const path = require('path')
    module.exports = class Compiler {
        constructor(options){
            const { entry, output } = options
            this.entry = entry
            this.output = output
            this.modules = []
        }

        run(){
            const entryModule = this.buildModule(this.entry,true)
            this.modules.push(entryModule)
            this.modules.map((_module)=>{
                _module.dependencies.map((dependency) => {
                    this.modules.push(this.buildModule(dependency))
                })
            })
            this.emitFiles()
        }

        buildModule(filename, isEntry){
            let ast
            if(isEntry){
                ast = getAST(filename)
            }else{
                let absolutePath = path.join(process.cwd(), './src', filename);
                ast = getAST(absolutePath)
            }

            return {
                filename,
                dependencies: getDependencies(ast),
                source:transform(ast)
            }

        }

        // 输出内容输出到哪里
        emitFiles(){
            const outputPath = path.join(this.output.path, this.output.filename)
            let modules = '';
            this.modules.map((_module) => {
                modules += `'${ _module.filename }': function (require, module, exports) { ${ _module.source } },`
            });
            
            const bundle = `
                (function(modules) {
                    function require(fileName) {
                        const fn = modules[fileName];
            
                        const module = { exports : {} };
            
                        fn(require, module, module.exports);
            
                        return module.exports;
                    }

                    require('${this.entry}');
                })({${modules}})
            `;
            // console.log(bundle,'bundle')
        
            fs.writeFileSync(outputPath,bundle,'utf-8')
        }
    }
lib/parser.js
    const fs = require('fs')
    const babylon = require('babylon')
    const traverse = require('babel-traverse').default
    const { transformFromAst } = require('babel-core')
    module.exports = {
        getAST:(path) => {
            const source = fs.readFileSync(path,'utf-8')

            return babylon.parse(source,{
                sourceType:'module'
            })
        },

        getDependencies:(ast) => {
            const dependencies = []
            traverse(ast,{
                ImportDeclaration: ({ node })=>{
                    dependencies.push(node.source.value)
                }
            })

            return dependencies
        },

        transform:(ast) => {
            const {code} = transformFromAst(ast,null,{
                presets:['env']
            })
            return code
        }
    }
lib/test.js
    const { getAST, getDependencies, transform } = require('./parser')
    const path = require('path')

    const ast = getAST(path.join(__dirname,'../src/index.js'))
    const dependences = getDependencies(ast)

    if(process.argv[2] === 'ast'){
        console.log(ast)
        return
    }
    if(process.argv[2] === 'source'){
        const source = transform(ast)
        console.log(source)
        return
    }
    if(process.argv[2] === 'd'){
        const dependences = getDependencies(ast)
        console.log(dependences)
        return
    }
  • 测试parser功能, node lib/test.js d
浏览器中运行

html