头部背景图片
小畅的学习笔记 |
小畅的学习笔记 |

webpack基本原理

Webpack引入原因

1.在网页中会引用哪些常见的静态资源?

  • JS
    .js .jsx .coffee .ts(TypeScript 类 C# 语言)
  • CSS
    .css .less .sass .scss
  • Images
    .jpg .png .gif .bmp .svg
  • 字体文件(Fonts)
    .svg .ttf .eot .woff .woff2
  • 模板文件
    .ejs .jade .vue【这是在webpack中定义组件的方式,推荐这么用】

2.网页中引入的静态资源多了以后有什么问题???

  • 网页加载速度慢, 因为 我们要发起很多的二次请求;
  • 要处理错综复杂的依赖关系(比如bootstrap依赖于jQuery)

3.如何解决上述两个问题

  • 合并、压缩、精灵图、图片的Base64编码
  • 可以使用之前学过的requireJS、也可以使用webpack可以解决各个包之间的复杂依赖关系;

webpack是一个js打包工具,不一个完整的前端构建工具。它的流行得益于模块化和单页应用的流行。webpack提供扩展机制,在庞大的社区支持下各种场景基本它都可找到解决方案。

webpack原理

  • webpack只是一个打包模块的机制,只是把依赖的模块转化成可以代表这些包的静态文件。并不是什么commonjs或者amd之类的模块化规范。
  • webpack就是识别你的 入口文件。识别你的模块依赖,来打包你的代码。至于你的代码使用的是commonjs还是amd或者es6的import。webpack都会对其进行分析。来获取代码的依赖。
  • webpack做的就是分析代码。转换代码,编译代码,输出代码。webpack本身是一个node的模块,所以webpack.config.js是以commonjs形式书写的(node中的模块化是commonjs规范的)
  • webpack中每个模块有一个唯一的id,是从0开始递增的。整个打包后的bundle.js是一个匿名函数自执行。参数则为一个数组。数组的每一项都为个function。function的内容则为每个模块的内容,并按照require的顺序排列。

核心思想:

  • 一切皆模块:
    正如js文件可以是一个“模块(module)”一样,其他的(如css、image或html)文件也可视作模 块。因此,你可以require(‘myJSfile.js’)亦可以require(‘myCSSfile.css’)。这意味着我们可以将事物(业务)分割成更小的易于管理的片段,从而达到重复利用等的目的。
  • 按需加载:
    传统的模块打包工具(module bundlers)最终将所有的模块编译生成一个庞大的bundle.js文件。但是在真实的app里边,“bundle.js”文件可能有10M到15M之大可能会导致应用一直处于加载中状态。因此Webpack使用许多特性来分割代码然后生成多个“bundle”文件,而且异步加载部分代码以实现按需加载。

webpack核心概念

  • entry 一个可执行模块或库的入口文件。
  • chunk 多个文件组成的一个代码块,例如把一个可执行模块和它所有依赖的模块组合和一个 chunk 这体现了webpack的打包机制。
  • loader 文件转换器,例如把es6转换为es5,scss转换为css。
  • plugin 插件,用于扩展webpack的功能,在webpack构建生命周期的节点上加入扩展hook为webpack加入功能。

webpack构建流程

从启动webpack构建到输出结果经历了一系列过程,它们是:

  1. 解析webpack配置参数,合并从shell传入和webpack.config.js文件里配置的参数,生产最后的配置结果。
  2. 注册所有配置的插件,好让插件监听webpack构建生命周期的事件节点,以做出对应的反应。
  3. 从配置的entry入口文件开始解析文件构建AST语法树,找出每个文件所依赖的文件,递归下去。
  4. 在解析文件递归的过程中根据文件类型和loader配置找出合适的loader用来对文件进行转换。
  5. 递归完后得到每个文件的最终结果,根据entry配置生成代码块chunk。输出所有chunk到文件系统。
  6. 需要注意的是,在构建生命周期中有一系列插件在合适的时机做了合适的事情,比如UglifyJsPlugin会在loader转换递归完后对结果再使用UglifyJs压缩覆盖之前的结果。

如何实现一个简单的webpack

  1. 读取文件分析模块依赖
  2. 对模块进行解析执行(深度遍历)
  3. 针对不同的模块使用相应的loader
  4. 编译模块,生成抽象语法树AST。
  5. 循环遍历AST树,拼接输出js。

打包原理

把所有依赖打包成一个bundle.js文件,通过代码分割成单元片段并按需加载。
Image1.png

如图,entry.js是入口文件,调用了util1.js和util2.js,而util1.js又调用了util2.js。

打包后的bundle.js例子

******/ ([
/* 0 */        //模块id
/***/ function(module, exports, __webpack_require__) {

    __webpack_require__(1);        //require资源文件id
    __webpack_require__(2);

/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
    //util1.js文件
    __webpack_require__(2);
    var util1=1;
    exports.util1=util1;

/***/ },
/* 2 */
/***/ function(module, exports) {
    //util2.js文件
    var util2=1;
    exports.util2=util2;

/***/ }
...
...
/******/ ]);
  1. bundle.js是以模块 id 为记号,通过函数把各个文件依赖封装达到分割效果,如上代码 id 为 0 表示 entry 模块需要的依赖, 1 表示 util1模块需要的依赖
  2. require资源文件 id 表示该文件需要加载的各个模块,如上代码_webpack_require__(1) 表示 util1.js 模块,webpack_require(2) 表示 util2.js 模块
  3. exports.util1=util1 模块化的体现,输出该模块

webpack-dev-serve常用命令参数

修改package.json的script节点如下,其中

--open表示自动打开浏览器
--port 4321表示打开的端口号为4321
--hot表示启用浏览器热更新
--contentBase src指定启动的根目录

"dev": "webpack-dev-server --contentBase src --hot --port 4321 --open"

注意:热更新用来进行减少不必要的代码更新,每次重新编译只会更新修改过的文件(相当于打补丁)热更新在JS中表现的不明显,可以从一会儿要讲到的CSS身上进行介绍说明
Image2.png

使用webpack处理第三方类型

在解析对于文件,会自动去调用响应的loaderloader 本质上是一个函数,输入参数是一个字符串,输出参数也是一个字符串。当然,输出的参数会被当成是 JS 代码,从而被 esprima 解析成 AST,触发进一步的依赖解析。webpack会按照从右到左的顺序执行loader。

webpack默认只能处理js类型的文件,无法处理其他的非js类型的文件,如果需要非JS类型的文件,我们需要手动安装一些合适的第三方loader加载器

webpack处理第三方文件类型的过程:

  • 发现这个要处理的文件不是js文件,然后就去配置文件中,查找有没有对应的第三方loader规则
  • 如果能找到对应的规则,就会调用相应的loader处理这种文件类型
  • 在调用loader的时候,是从后往前调用的
  • 当最后一个loader调用完毕,会把处理结果,交给webpack进行打包合并,最终输出到bundle.js中去

如使用webpack打包css文件

  1. 新建index.css文件
  2. 在main.js引入index.css

    import './css/index.css'
    
  3. 如果要打包处理css文件,需要安装cnpm i style-loader css-loader -D
  4. 打开webpack.config.js这个配置文件,在里面新增一个配置节点叫module,它是一个对象,在这个module对象上有一个rules属性,是个数组,这个数组中存放了所有第三方文件的匹配和处理规则

    module: { // 用来配置第三方loader模块的
        rules: [ // 文件的匹配规则
        { test: /.css$/, use: ['style-loader', 'css-loader'] }//处理css文件的规则
        ]
    }
    

    注意:use表示使用哪些模块来处理test所匹配到的文件;use中相关loader模块的调用顺序是从后向前调用的

Lililich's Blog