手写webpack打包基础实现

2019-04-19  本文已影响0人  huk

实现了最基础打包,源码可以查看https://github.com/huker/webpack-demo

整体思路

写个简单的有引入的文件,然后npx webpack打包一下看下打包出来的内容是什么。

(function (modules) { 
    var installedModules = {};
    function __webpack_require__(moduleId) {
        //....太长了 省略一些代码 
        //这边就是webpack自己实现的一个require方法
        return module.exports;
    }
    //"./src/index.js"入口文件
    return __webpack_require__(__webpack_require__.s = "./src/index.js");
})
    //传入的模块依赖关系
({
    "./src/a.js": (function (module, exports, __webpack_require__) {
        eval("var b = __webpack_require__(\"./src/b.js\");\n\nvar a = 'a' + b;\nmodule.exports = a;\n\n//# sourceURL=webpack:///./src/a.js?");
    }),
    "./src/b.js": (function (module, exports) {
        eval("b = 'b';\nmodule.exports = b\n\n//# sourceURL=webpack:///./src/b.js?");
    }),
    "./src/index.js": (function (module, exports, __webpack_require__) {
        eval("var a = __webpack_require__(\"./src/a.js\");\n\nconsole.log(a)\n\n//# sourceURL=webpack:///./src/index.js?");
    })
});

把打包出来的内容缩减了一下,重点是3个地方

那就来尝试达到上面这个打包的效果,新建个项目叫my-pack好了,初始化后在package.json中添加bin,来指定命令行执行的路径。在执行文件头部加上如下的代码,来指定这代码是在node环境下跑的。

接着我们把my-pack链接到npm全局,这样才能在别的项目中命令行中使用,npm link 即可。最后在要打包的项目中,link下我们全局的my-pack,npm link my-pack 这样my-pack就安装到项目中了,可以npx my-pack来执行。

//package.json
"bin": {
    "my-pack":"./bin/index.js"
}

// bin/index.js
#! /usr/bin/env node
console.log("hello")

分析和处理

创建依赖关系

这一步就是buildModules的内容。

思路是调用buildModules首先传入的是入口文件,解析后得到模块源码和模块依赖,如果有依赖就继续解析,形成递归。

//构建模块 生成模块的依赖关系
//modulePath绝对路径 isEntry是否主入口
buildModal(modulePath, isEntry) {
        //拿到模块内容  getSource只是读取文件的方法,拎出来写了
        let source = this.getSource(modulePath);
        //拿到模块id "./src/a.js"这样 是相对路径 其实就是modulePath-root路径
        let moduleName = './' + path.relative(this.root, modulePath);
        //如果是主入口的话存起来
        if (isEntry) {
            this.entryId = moduleName;
        }
        //接下来要把文件中的require替换成__webpack_require__ 把比如"./a.js"变成"./src/a.js"
        //解析源码进行改造 返回一个依赖列表(看继续有没有要遍历下去的)
        let { sourceCode, dependencies } = this.parse(source, path.dirname(moduleName)); // "./src"
        //获得改造后的源码 放进this.modules中 (把相对路径和模块中的内容对应起来)
        this.modules[moduleName] = sourceCode;
        //有依赖 继续递归
        if (dependencies && dependencies.length > 0) {
            dependencies.forEach((dep) => {
                this.buildModal(path.resolve(this.root, dep), false)
            })
        }
}

AST解析语法树

这一步就是parse的内容,解析读取到的模块的内容。

用到了几个库来做这件事:

  1. babylon 主要是把源码解析成AST
  2. @babel/traverse 遍历节点(遍历到对应的节点)
  3. @babel/types 替换遍历到的节点
  4. @babel/generator 替换好的结果生成
    (traverse和generator是es6模块 引用的时候要require('@babel/traverse').default 不然默认导出的是一个对象)

https://astexplorer.net/可以在这个网站直观的查看AST节点。

打包结果生成

添加loader

思路是在读取每个依赖模块的时候,获取所有的rules,modulePath正则匹配下每个rule的test,匹配到就通过loader转化这个模块。

添加plugins

在实例化的时候apply所有plugins

上一篇 下一篇

猜你喜欢

热点阅读