最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • webpack 工作流程

    正文概述 掘金(柔小鱼)   2021-07-28   693

    说在前面

    这篇文章没有贴webpack源码,更多的是作为自己学习的一个小总结,欢迎大佬们指正(✿◠‿◠)。希望给萌新们一点帮助 (=•ω<=)☆。也希望自己以后可以更加深入源码,把每一步的关键源码真正看懂(ง •_•)ง!
    进入正题啦 !!了解总体的工作流程,首先要了解 webpack 中的一些核心概念。

    核心概念

    module

    模块。
    module 是 webpack 的中的核心实体,要加载的一切和所有的依赖都是 module,可以理解为一个文件就是一个 module,我们写的代码都是 moudle。

    entry

    入口、起点。
    webpack 会对我们写的代码进行编译、查找依赖。entry 就是编译过程中第一个进行编译的文件。如 main.js

    loader

    模块转换器。
    把浏览器中不能直接运行的代码转换成浏览器中可以直接运行的代码。举例,
    比如我们开发的时候使用了 ES6 新的特性语法,那么就需要 webpack 在打包流程中用 babel-loader 把浏览器不理解的 ES6 语法转换为浏览器理解的 ES5 语法。一般我们日常业务开发无需自己写 loader,都是直接使用类似babel-loader这样的第三方库。

    常见的loader

    • babel-loader 把 ES6 转换成 ES5
    • ts-loader 把 typescript 转换成 js
    • less-loader 把 less 转换成 css
    • css-loader 对 css 样式进行处理
    • style-loader 把打包的样式插入到 HTML 结构中

    plugin

    如果把 webpack 打包构建的流程比作一条生产线,那么插件就像是插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。
    webpack 在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。 webpack 通过 Tapable 来组织这条复杂的生产线。 webpack 的事件流机制保证了插件的有序性,使得整个系统扩展性很好。

    chunk、bundle 与 moudle

    webpack 工作流程 module、chunk、bundle 这三个都可以理解为文件,区别在于:我们直接写出来的是 module,webpack 处理时是 chunk,最后生成浏览器可以直接运行的是 bundle。也可以这样理解,module,chunk 和 bundle 其实就是同一份逻辑代码在不同转换场景下的三个名字。

    用一个具体例子表现 module、chunk 和 bundle 的关系

    • src 目录结构
      src
      ├── index.css
      ├── index.html # 这个是 HTML 模板代码
      ├── index.js
      ├── common.js
      └── utils.js
    
    • 代码
    // mian.js
    import "./example.js";
    import "./index.css";
    // example.js
    console.log("a module file for example");
    // util.js
    export function square(a) {
      return a * a;
    }
    
    /* index.css */
    body {
      background-color: green;
    }
    
    // webpack 配置
    moudle.exports={
        entry: {
            index: "../src/main.js",
            utils: '../src/utils.js',
        },
        output: {
            filename: "[name].bundle.js", // 两个 bundle 文件名与两个 entry 对应
        },
        module: {
            rules: [
                {
                    test: /\.css$/,
                    use: [
                        MiniCssExtractPlugin.loader, // MiniCssExtractPlugin 创建一个 link 标签
                        'css-loader', // css-loader 负责解析 CSS 代码, 处理 CSS 中的依赖
                    ],
                },
            ]
        }
        plugins: [
    
            new MiniCssExtractPlugin({ // MiniCssExtractPlugin 抽离出 css 文件,以 link 标签的形式引入样式文件
                filename: 'index.bundle.css' // 输出的 css 文件名为 index.bundle.css
            }),
        ]
    }
    
    • 关系图 webpack 工作流程

    前面说到的是面向使用者的重要概念,下面说一下 webpack 打包构建中(webpack 源码中)的重要概念。

    compiler、complication

    • compiler
      可以理解为 webpack 编译的调度中心,是一个编译器实例,在 compiler 对象中记录了完整的 webpack 环境信息,在 webpack 的每个进程中,compiler 只会生成一次。(举例,npm run dev 一次只有一个 compiler)
    • complication
      是 compiler 的生命周期内一个核心对象,它包含了一次构建过程中所有的数据(modules、chunks、assets)。也就是说一次构建过程对应一个 complication 实例。(举例,比如热更新的时候,webpack 会监听本地文件改变然后重新生成一个 complication)

    compiler 几个重要的钩子:

    • make?:在钩子的回调中有开始读取 webpack 配置文件的逻辑
    • emit?:在钩子的回调中执行生成文件(也就是 bundle)的逻辑
    • done?:文件已写入文件系统后触发这个钩子

    AST

    抽象语法树。通过它来提取出各个模块的依赖关系,进而形成依赖树?。

    主线剧情

    webpack 工作流程

    webpack 就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。 这条生产线上的每个处理流程都是“单一职责”的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。
    webpack 源码非常复杂,想完全看明白源码太困难,对于应用它的人来说,只要看懂主线剧情就可以了。主线剧情就是从读取配置文件中的 entry 开始,到最后输出 bundle 的过程。

    具体是解开哪些疑问呢?

    • webpack 编译我们的源码过程中具体都做了些什么?(贯穿1-4小节)
    • loader 是在哪个过程起作用的?(第2小节)
    • 依赖树是怎么形成的?(第2小节)
    • chunk 是如何划分的?(第2-3小节)
    • 最后怎么形成 bundle 的?(第4小节)

    1.准备工作阶段

    1. 初始化 compiler 对象,注册内置插件和配置文件中的插件。
    2. 执行 compiler.run()-->compiler.compile() 开始构建流程。
    3. compiler.compile 中创建此次编译的 complication 对象,触发 compiler 的 make 钩子
    4. compiler 的 make 钩子中,主要做两件事。
      一是执行 compiler.hooks.make.callAsync()负责接入各个 plugin 然后应用它们(执行订阅了 make 钩子的 plugin 的回调函数)。
      二是执行 complication.addEntry() 进入主线剧情。

    2.modules 的生成阶段

    webpack 工作流程

    1. addEntry
      addEntry 方法会开始第一批 module 的解析,即我们配置的入口文件(例如 main.js)。addEntry 主要执行_addModuleChain(),_addModuleChain 中调用addModule

    2. addModule
      使用对应的工厂 NormalModuleFactory (具体方法是 create)生成一个空的 webpack 模块化 对象。把它存入 complication.modules 中,由于当前是入口文件,所以也会存入 complication.entries 中。随后执行 buildModule->doBuild

    3. doBuild 读取 module 中内容并进行处理。用对应的 loaders ,把我们的源码 module 转成标准的 JS 模块对象。然后执行 Parse.parse()

    4. parse
      将 JS 解析为 AST。然后遍历 AST

    5. 遍历 AST
      找到入口中的所有依赖 module,加入 dependencies 数组。再依次对 dependencies 中的依赖 module 进行processModuleDependencies->2->-3->4->5 的过程,这是一个递归遍历过程,会把所有的原来的 module 源码都聚合到一个对象(后面称为“chunk 初始态对象” )中。
      如果发现动态引入例如 import(),那么就开启新的一轮_addModuleChain。直到每一轮的递归结束,执行 seal()
      此时调用多少次_addModuleChain,就形成了多少个依赖树,就有多少个 chunk 初始态对象。

    概括

    根据配置中的 entry 找出所有的入口文件,从 entry 开始,使用到 loader 先完成模块转换,然后解析形成 AST,接着遍历 AST 把所有 require 找出来形成 依赖树。递归遍历依赖树节点对应的文件,做相同的模块转换、形成依赖树的处理,直到递归结束。这样就形成了一个 chunk 初始态对象。 如果遇到动态的 import,那么就和上面对 entry 的处理一样,最后形成另一个或多个独立的 chunk 初始态对象。

    3.chunks 生成阶段

    下面图片包含了 chunks 和 bundles 生成流程。 webpack 工作流程

    1. seal
      执行createChunkAssets()
    2. createChunkAssets
      主要调用 MainTemplate.render()或者 ChunkTemplate.render()。MainTemplate.render 对入口形成的 chunk 对象进行处理,ChunkTemplate.render 对动态引入形成的 chunk 对象进行处理。处理知之后的对象可以理解为一个有固定结构的字符串 。然后执行 ModuleTemplate.render()
    3. ModuleTemplate.render
      保存了最终需要输出到文件的 js 代码字符串。然后执行 emitAsset()
    4. emitAsset
      将生成好的字符串保存到 complication.assetes 中。assets 就是最终要生成的文件列表。

    概括

    complication.seal 方法用于生成 chunks,对 chunks 进行一系列的优化操作,形成的 chunk 保存在 complication.assets 中。

    4.生成 bundle 文件阶段

    1. assets 准备好后, complication 实例的工作到此也全部结束。接着触发 compiler 的 emit 钩子
    2. emit 钩子,主要做两件事。
      一是 compiler.hooks.emit.callAsync() 接入各个 plugin(执行订阅了 emit 钩子的 plugin 的回调函数,这是我们修改最终文件的最后一个机会。)
      二是 遍历 complication.assets 生成 bundle 们写入文件系统,写入后触发 compiler 的 done 钩子
    3. 到了 done 钩子,就代表结束构建流程了。

    参考

    github·aasailan-webpack 运行流程

    github·lihongxun945-Webpack 源码解析

    webpack 官网·https://webpack.docschina.org/


    起源地下载网 » webpack 工作流程

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元