webpack| 核心概念、loader、plugin🤞✌

167 阅读8分钟

webpack是什么?

webpack是前端一个有效的打包工作,主要就是处理依赖,形成模块,压缩文件,管理插件。

webpack的5个核心概念

module.exports = {
   mode: '',
   entry: '',
   output: '',
   plugins: [],
   module: [],
   resolve: '',
   devServer: {}
}

entry: 指示wepack从哪个文件为入口开始打包,分析构建内部的依赖图

ouput: 指示webpack打包后的资源输出到哪里去,以及如何命名

loader: loader能够使得wbpack处理非js文件,比如:css、图片、文件

plugin: plugin可以使得webpack的功能更加强大,从打包优化到压缩

mode: 指示webpack使用相应的模块配置 development:开发环境,代码在本地运行的环境 production:生产环境,代码优化后的上线环境 development打包后,一些没有依赖的方法、变量、文件会保留,production则会移除。 production打包后,代码会进行压缩,比development的文件小

常用的loader

style-loader:创建style标签,将css-loader解析后的css引入style中

css-loader:可以解析import、require、@import、url引入的内容

less-loader:处理less语法

sass-loader:处理sass语法

postcss-loader:用postcss来处理css

file-loader:将以import和requrie()方式的资源解析成url,并且把文件放到输出的文件夹下

url-loader:和file-loader类似,处理图片文件,当文件在限制内,可以把文件转成base64,当超过限制,会自动交给file-loader处理

html-minify-loader:压缩html

babel-loader:用babel来将ES6转换成ES5

raw-loader:加载文件原始内容(utf-8)

常用的plugin

image.png

plugin和loader区别

loader 加载器

loader使得webpack能够处理除了js以外的文件(webpack默认能够处理js文件和json文件),比如css、图片、html等。其他类型loader举例:比如加载css(css-loader)、图片和字体文件(file-loader)、加载html(html-loader)、最新的js语法转成ES5(babel-loader)

//test定义了需要转换的文件或者文件类型,use定义了对该文件进行转换的loader类型,
该配置是告诉webpack,当遇到txt类型的文件,先使用raw-loader转换一下该文件在把他打包进bundle
module.exports = {
  module: {
    rules: [
      { 
        test: /.txt$/, 
        use: 'raw-loader' 
      }
    ]
  }
};

plugin 插件

plugin使得webpack的功能更加强大,plugin是组成webpack的关键,webpack自身也是基于plugin搭建的,webpack有丰富的内置插件和外置插件,同时要想使用,必须先引入插件,插件举例:new HotModuleReplacementPlugin(),new HtmlWebpackPlugin()

区别

loader是转换和加载特定类型的文件,所有loader的执行层面是单个源文件

plugin可以监听webpack处理过程中的关键事件,深入集成到webpack的编译器,可以说plugin的执行层面是整个构建过程。

实战体会

css-loader

css-loader会处理 import/require() @import/url 引入的内容。

// style.css
html,
body {
  color: red;
}


// index.js
require("./assets/style.css");

css-loader处理之后导出的是

20200228174101926.png

这个是没有办法满足需求的,所以需要借助style-loader

style-loader

1、创建style标签

2、 将css引入标签内

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"], // use底层是一个栈结构,后进先出,后面的loader先执行
      },
    ],
  },
};

最后css文件打包才会成功

image.png

file-loader

// index.js
const src = require("./test.jpg");
// 图片
const app = document.getElementById("imgContent");
const img = new Image();
img.src = src;
app.appendChild(img);

  module: {
    rules: [
      {
        test: /\.(jpg|png)$/,
        loader: "file-loader",
        options: {
          esModule: false,
        },
      },
    ],
  },


Snipaste_2022-08-03_19-43-34.png

HtmlWebpackPlugin

官方回复简单创建一个html,服务器端访问,说白了,看不懂

每次打包完成,dist文件夹中都有一个index.html,其实这个就是HtmlWebpackPlugin功劳了

1、可以生成创建html入口文件,比如单页面可以生成一个html文件入口,配置N个html-webpack-plugin可以生成N个页面入口

2、为html文件中引入的外部资源如script、link动态添加每次compile后的hash,防止引用缓存的外部文件问题


const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin"); // 主要多了这一行

module.exports = {
  entry: "./src/index.js", // 入口文件
  output: {
    path: path.resolve(__dirname, "dist"), // 输出到哪个文件夹
    filename: "output.js", // 输出的文件名
  },
  mode: "development", // 开发模式
  module: {
    rules: [{ test: /\.css$/, use: ["style-loader", "css-loader"] }],
  },
  plugins: [
    //  主要多了这一行
    new HtmlWebpackPlugin({
      filename: "webpack.html", // 打包出去的文件名
      template: "./src/index.html", // 依赖哪个文件做模板
    }),
  ],
};

HotModuleReplacementPlugin

传送门webpack|体会webpack热更新和热更新原理🔥🔥

RemoveCommentsPlugin

封装一个去掉打包文件中注释的插件

思路:webpack提供了各种钩子函数,选择合适的钩子函数进行操作,emit:输出 asset 到 output 目录之前执行。这个钩子不会被复制到子编译器;在输出之前,把js文件中的注释去掉 ,返回去掉注释的文件

const pluginName = "RemoveCommentsPlugin";

module.exports = class RemoveCommentsPlugin {
  apply(compiler) {
    compiler.hooks.emit.tap(pluginName, (compilation) => {
      //compilation是本次打包的执行上下文,它包含所有打包中产生的结果
      // assets 包含打包要输出的所有文件
      for (const name in compilation.assets) {
        // 找到后缀是js文件
        if (name.endsWith("js")) {
          // source()可以获取文件内容
          let content = compilation.assets[name].source();
          // 匹配注释替换成空字段串
          /**
           *   \/ => /
           *   \* => *
           *   {2,} 至少匹配2次
           *   \/ => /
           *   \s => 	匹配任何空白字符
           *   ? => 匹配前面的子表达式零次或一次
           *   /g => 查找所有匹配项
           * 综上所述这个正则的意思是 /**....../
           */
          let noComments = content.replace(/\/\*{2,}\/\s?/g, "");
          compilation.assets[name] = {
            source: () => noComments,
            // 去掉注释,文件大小肯定会有变化
            size: () => noComments.length,
          };
        }
      }
    });
  }
};

webapck引入自己封装的插件即可

const RemoveCommentsPlugin = require("./remove-comments-plugin");

module.exports = {
  plugins: [
    new RemoveCommentsPlugin(),
  ],
};

可以看一下打包前后的对比

Snipaste_2022-08-03_16-06-28.png

webpack编译原理

初始化: 解析配置参数,主要是shell命令(npm install 类似的命令)和webpack.config.js文件的配置信息,进行融合,输出最终的配置信息

编译:

1.创建chunk

chunk 代码块 bundle 打包 modules 模块

chunk表示通过入口找到所有依赖的总称,简单总结来说,webpack的编译是基于入口的,通过入口文件找出每个文件的依赖,形成chunk(块),比方说:入口模块(./src/index.js)依赖一种模块(./src/a.js),a模块又依赖b模块(./src/b.js),通过一个入口模块分析依赖关系,可以找到三个模块,那么【index.js、a.js、b.js】这三个统称为一个chunk,根据入口模块,创建一个chunk,每一个块都有自己的名字。 chunk至少有2个属性{name:'main(默认)',id:'唯一编号'}

2.构建所有依赖模块

(1)先检查模块是否加载过,去模块记录表中查看

(2)如果有记录,说明加载过,如果没有,进行下一步

(3)读取模块中的内容

(4)对内容进行分析树形结构遍历,找到所有依赖,生成AST抽象语法树

(5)将分析出来的依赖加到dependencies数组中

(6)替换依赖函数

(7)将替换后的模块代码,保存到模块记录表中

(8)比如index.js模块处理完成,由于index.js依赖其它模块,所以递归循环保存在dependencies数组中的依赖,开始分析./src/a.js模块,假设a模块依赖./src/b.js模块,那么等a模块处理完成后,再处理a模块所依赖的b模块,再最后处理index模块所依赖的b模块,此时会发现b模块在处理a模块所依赖的b模块已经加载过了,那么index模块所依赖的b模块是不会进行下一步处理,直接结束。 最后的目的就是为了形成一个模块记录表

3.产生chunk assets

chunk中的模块记录表,webpack会根据配置为chunk生成一个资源列表,就是chunk assets

image.png

4.合并chunk assets

将多个chunk assets合成一个,并生成一个总的hash ,将生成的总的assets,通过nodejs的fs模块,生成相应的文件

image.png

总结

当敲下webapck打包命令时,文件开始初始化,各个参数进行融合,形成最终的配置对象,然后把配置对象交给编译器进行编译,通过入口模块找到互相依赖的模块形成模块列表,接下来webpack根据配置为chunk生成资源列表,然后将每一个chunk生成的资源合并成一个完整的资源,并且生成一个完成的hash值,最终根据完成的资源列表输出到文件。

image.png

思考题

思考💡:vue.config.js 和webpack.config.js 的关系?区别?

webpack.config.js是webpack的配置文件,vue和react都可以使用

vue.config.js是基于vue的webpack的配置文件

vue-cli 2.0时代,webpack的配置文件写在config/index.js 文件中

vue-cli 3.0时代,没有了config文件夹,vue.config.js写在根目录下

vue-cli3创建的时候并不会自动创建vue.config.js,因为这个是可选项,所以一般都是修改webpack的时候才会自己创建一个vue.config.js

再然后因为vue-cli3内部高度集成了webpack,一般来说使用者不需要再去webpack做了什么,所以没有暴露webpack的配置文件,但你依然可以创建vue.config.js去修改默认的webpack

思考💡:vue和vue-cli的关系区别?

vue是一个渐进式框架,可以在html引入vue.js 使用vue的数据绑定,也可以基于vue使用vue的全家桶,构建一个前端项目

vue-cli是一个脚手架,也就是一个代码生成器,vue-cli = vue + 一堆js 代码

vue-cli 4.5以下对应的是vue2, 4.5以上对应的是vue3

思考💡:vue-cli3.0高度集成的webpack怎么理解?

vue-cli3.0 里面自带webpack,已经具有一些基本的loader,可以通过vue inspect > output.js查看webpack的配置,这个命令会在根目录下生成一个js文件

简单理解就是vue-cli提供了一个inspect命令,可以把webpack配置、包括链式访问规则和插件的提示打印到一个根目录下的output.js文件中,我们只需要在项目根目录下命令行中输入该命令,即可在根目录下得到output.js,该文件中即是webpack的所有配置,方便我们参考,但是仅仅是方便我们看的,我们对其进行更改不会生效。

思考💡:如果需要配置webpack,怎么做 ?

调整 webpack 配置最简单的方式就是在 vue.config.js(项目中没有的话,在根目录下创建一个)中的 configureWebpack 选项提供一个对象


// vue.config.js
module.exports = {
  configureWebpack: {
    plugins: [
      //你想加的配置写在下面,以一个对象的形式写到参数里。
      //例如:new MyWebpackPlugin({template: './src/index.html'})
      //随便举的例子,实际开发请写自己需要加的配置!
      new MyAwesomeWebpackPlugin()
    ]
  }
}


该对象将会被 webpack-merge 合并入最终的 webpack 配置。

参考👀

前端性能优化-Vue-cli 3.0项目中webpack配置

css-loader和style-loader的区别和使用

html-webpack-plugin详解

超详细webpack的plugin讲解,看不懂算我输,案例>原理>实践

url-loader和file-loader的区别和使用