webpack5的使用详解

236 阅读12分钟

一、不能被浏览器识别的一些内容

我们最常用的就是ES6的模块化语法,那么我们来实验一下,确实是会报错的

二、使用编译工具编译成浏览器能识别的语法

初始化我们的代码成为包管理项目,使用npm init -y,会生成package.json(包描述文件),特别注意这里的包名不能和我们需要下载的任意包名重复;注意package文件中的main需要和自己的入口文件匹配;然后下载webpack和webpack-cli

在我们还没有配置webpack.config文件的时候可以使用npx去运行node_modules中运用程序(将bin添加到环境变量),使用 npx webpack ./src/main.js --mode=development;先使用开发者模式打包;打包还是很快的;这时在index.html中引入打包后的文件,浏览器就不会报错了

三、开发者模式和生产模式的代码比较

开发者模式:会发现每一个模块都被eval函数包裹,并且ES6的一些语法是没有被转化的

生产模式:发现代码非常简介,使用一个匿名函数防止变量冲突,我们的减法模块直接就给个结果,能压缩的就压缩;加法直接就变成了一个运算

四、webpack只认识JS和JSON

尝试引入css打包试一下,webpack中没有被依赖和引入的文件是不会打包的,确实是不认识的

五、使用配置文件配置css-loader和style-loader

配置文件必须叫webpack.config.js,位置和package.json文件一个层级;我们的webpack是在node环境下运行的,所以需要采用commonJS的写法,webpack的五大核心概念就是配置文件中的基石,然后在loader中配置css-loader,参照官方文档,文档中只安装css-loader是不够的还需要style-loader

webpack在读取loader时是从右往左读,css-loader是将css样式变成一个JS模块,style-loader是动态创建style标签,在public下面的index.html中是没有的,是动态创建

const path = require('path');
module.exports = {
  // 入口需要使用相对路径
  entry:'./src/main.js',
  output:{
    // path需要使用绝对路径,__dirname是当前目录,所以这里是当前目录下的dist文件
    path:path.resolve(__dirname,'dist'),
    // 输出文件名是main.js
    filename:'main.js',
    // 自动清空上次打包目录
    clean:true
  },
  // 配置loader
  module:{
    rules: [
      {
        // 只检查css文件
        test: /.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  // 配置插件
  plugins:[],
  // 打包模式
  mode:'production',
}

六、使用mini-css-extract-plugin提取css样式

使用style-loader是利用js创建style标签去引入css样式的,这样就需要将js解析完才可以出样式,会很容易形成闪屏现象,将我们浏览器的网速调慢就会出现,所以需要使用mini-css-extract-plugin提取css单独的文件,然后手动引入即可,参照官网配置webpack.docschina.org/plugins/min…;这里我们使用生产环境进行打包,发现样式是没有进行压缩的

七、使用css-minimizer-webpack-plugin进行css压缩

上面提取了样式但是没有压缩,所以这里使用一个插件进行压缩,使用参照官网webpack.docschina.org/plugins/css…

mini-css-extract-plugin需要引入、在plugins中new、然后在loader中替换style-loader

css-minimizer-webpack-plugin只需要引入和在plugins中new即可

还可以使用postcss-loader给css加上前缀来保证兼容性

八、清空打包内容

webpack4清空打包内容需要使用clean-webpack-plugin;webpack5只需要在输出中配置clean即可

九、处理图片资源;音视频、字体图标都是使用资源配置可以解决

webpack4中需要使用file-loader和url-loader处理图片资源,webpack5已经将两个loader内置到webpack中了,不需要下载可以直接使用,不用配置也可以直接使用,但是如果想让转换base64才需要配置;base64的体积比原图的体积会大,所以一般将小图转换成base64,一般小于8k才转换;配置方式在官网中搜索资源模块下面去找一下;

默认打包出来的内容目录是平铺的,想要对目录进行划分,需要修改输出路径,在配置loader的地方配置filename

{
  // ?表示可以有可以没有
  test: /.(png|jpe?g|svg|webp|gif)/,
  type: 'asset',
  parser: {
    dataUrlCondition: {
      maxSize: 8 * 1024 // 小于8kb转base64,减少请求但是体积变大
    }
  },
  generator: {
    // hash是生成的hash值,价格:是表示取几位,相当于根据内容计算,ext是后缀,query是后缀之后的内容一般不用
    filename: 'images/[hash:8][ext][query]'
  }
}

十、处理JS中的新增语法——Eslint和babel的配置

Eslint

可以用来检查JS和JSX语法,也就是可以检查react而不能检查vue,要检查vue需要添加其他的loader,具体的Eslint规则可以参照官网eslint.cn/docs/rules/;自己写的规则比继承的规则优先级高;webpack4是使用loader进行处理,webpack5使用的是插件进行处理

使用方式参照官网,webpack.docschina.org/plugins/esl…,主要的步骤是安装eslint以及eslint插件,在webpack.config中配置插件,然后在webpack.config.js同级目录添加eslint的配置文件,配置文件的命名规则是.eslintrc.,后面的可以不带,也可以是js文件或者json文件,我们是使用js文件比较多

module.exports = {
  // 继承eslint的规则
  extends:["eslint:recommended"],
  // 指定环境变量
  env:{
    node:true, //启用node中的全局变量
    browser:true // 启用浏览器中的全局变量比如console
  },
  // 语法环境,默认支持ES5想要支持ES6需要配置一下
  parserOptions:{
    ecmaVersion:6,
    sourceType:"module"
  },
  // 这里可以配置自己的一些规则
  rules:{
    "no var":2
  }
}

Babel

babel-loader在不加预设(插件)的情况下是没有什么作用的,所以可以按需去下载预设再配置,可以参照webpack.docschina.org/loaders/bab…官网配置

@babel/preset-env:用于将ES6语法转换成ES5语法,是一个智能预设

@babel/preset-react:用来编译JSX语法

@babel/preset-typescript:用来编译TS语法

特别注意:使用了智能预设只能转换简单的es6语法,如let、const、箭头函数,不能转换promise等;所以需要使用core-js,使用参照juejin.cn/post/716841…

可以看到promise语法还在,扩展运算符不在,下面是使用了core-js的打包截图

可以看到promise是被core-js重写了并且引入,也可以看到babel为每个文件注入一些辅助代码,默认情况下会添加到每个文件中,这样如果处理的文件特别多那么打包体积会增加,所以可以将这些辅助代码提成一个独立的模块,哪里需要引入就可以了,所以需要借助@babel/plugin-transform-runtime

十一、处理html资源

解决手动引入打包好的文件如css、js,需要使用html-webpack-plugin,他会生成一个html,并引入我们打包好的js,如果只有一个js我们可以手动加,但是一个项目打包完一般都不会只有一个js,所以我们最好是使用这个插件来动态的插入打包好的文件,官网参照webpack.docschina.org/plugins/htm…

直接new HTMLWebpackPlugin的话他会新建一个html然后引入我们打包之后的文件,所以我们需要加个template参数,其次我们发现打包好的js是放到head中引入的,但是加了defer

new HTMLWebpackPlugin({
  template:resolve(__dirname,'public/index.html'),
})

十二、添加dev-server实现自动更新

在开发过程中,如果我们修改了内容就需要去打包的话是很费时的,所以有了热更新;dev-server并没有将我们的代码实时打包到本地,而是打包到内存中,这样我们读取更快,在开启dev-server的时候其实是启动了一个服务器,并且sockjs在浏览器与服务器之间是建立了一个长连接的,以便将webpack编译和打包的各阶段状态告诉浏览器,webpack-dev-server调用webpack api监听了complie的done事件将打包后的hash值传到浏览器进行比较,参照zhuanlan.zhihu.com/p/30669007,官网配置webpack.docschina.org/api/webpack…;开启devserver后启动需要使用npx webpack serve

在配置dev-server的时候有一个属性是hot,是模块热更新,他可以只更新我们修改的文件,而不需要全部的打包,但是他不是配置了就生效的,css样式可以实现热更新是因为使用了style-loader,如果想要js也热更新需要自己写一些代码,或者使用vue-loader或者react-hot-loader

devServer: {
  host: 'localhost',
  port: 3000,
  open: true,
  hot:true // 模块热更新,默认值就是true
},

// 如果没有使用vue-loader或者react-hot-loader,可以按照下面的方式写
if(module.hot){
  module.hot.accept('xxx.js')
}

十三、区分开发环境和生产环境

开发环境和生产环境还是有很大的不一样的,比如生产环境不需要自动更新、eslint,开发环境不需要压缩、clean等,最重要的是模式配置不一样,所以我们需要区分生产环境和开发环境,建一个config文件夹,里面配置生产环境和开发环境两个js,外面的webpack.config.js就不需要了,因为改了文件位置,所以里面的路径配置需要改一下,相对路径不需要修改,他是根据执行命令的位置去找的,绝对路径就要往上找一层,执行命令npx webpack serve --config ./config/webapck.dev.js

现在输入的命令变长了,我们可以在package.json的scripts中配置短命令,这里就不需要加npx了,运行就使用npm run +短命令

十四、提升开发体验sourceMap

我们使用webpack的话,运行的代码都是进行编译之后的,所以和源代码会存在差异,所以如果代码报错是很难定位的,sourceMap是在编译的时候生成一个.map的文件,用来记录编译之后的代码和源代码的一个映射关系;source map是不影响网页的性能的,因为他只有在打开了开发者工具的情况下才会下载;具体配置项看官网:webpack.docschina.org/configurati…

一般来说都是在source-map前面加上前缀

cheap:不映射列名,但是会告诉在哪行,映射的代码是编译之后的

module:映射的代码是编译之后的,一般和cheap配合使用,就会告诉在几行,会显示源代码

在开发环境一般就使用cheap加上module就可以了即cheap-module-source-map,在生产环境使用source-map即可

十五、提高打包构建速度

在开发模式下前面提到的热更新是可以提高我们的构建速度的

oneOf

我们的loader解析文件的时候,他其实是会遍历rules中所有的loader看是否能够解析我们的文件,就算前已经匹配到了,也会往后再去查找;所以可以配置oneOf,每个文件只被一个loader解析,找到能够解析文件的loader之后就不再往后找了

我的demo的文件很少,js只有两个文件,也是可以看到打包的时间变少了的

exclede/include

排除或者包含某些文件,比如node_modules下的文件我们是不需要再编译了的或者说我们只需要编译src下面的文件,这两个配置只能写一个,一般都是针对js文件做处理,配置babel的时候有写过

cache

缓存打包结果,第二次打包速度就更快了,一般也是针对js,缓存的位置一般是node_modules下面

开启缓存之后效果还是很明显的

开启多进程打包thread-loader

多进程打包一般用于特别耗时的操作中,因为开启一个进程也是需要耗时的大约是600ms,我们启动进程的数量就是我们的CPU核数,所以开启过多的进程是没有用的,官网webpack.docschina.org/loaders/thr…

获取我们电脑的CPU核数,使用node运行一下就可以获得

一般我们的多进程只用于Eslint和Babel以及webpack内置的js压缩插件,如果需要对内置的js压缩插件做处理就需要引入

const os = require('os');
const threads = os.cpus().length;
// webpack内置的js压缩插件,因为需要修改配置所以需要单独引入
const TerserWebpackPlugin = require('terser-webpack-plugin');

{
  ...
  plugins:[
    new TerserWebpackPlugin({
      parallel:threads
    })
  ]
}

可以看到开启多进程之后打包速度是变慢了的,因为开启进程需要时间,而且我们的js本来就很少,所以时间反倒是变慢了的

十六、代码分割

如果我们项目庞大,将所有的js都打包到一个文件中,那么我们首屏渲染的时候会加载很多其他模块的js,这样首屏渲染就很慢;所以我们可以将代码进行分割,这样我们渲染哪个页面就只加载某个js,代码分割为我们所做的事情就是:1、将打包文件进行分割生成多个打包文件,2、按需加载,需要哪个文件就加载哪个文件

如果是多页应用,我们可以设置多入口就可以实现文件的拆分,只需要将entry改为对象即可

现在我们基本都是单页应用,一般来说只有一个入口文件,那么我们可以通过import动态引入的方式去打包成多个chunk,要想分割代码还需要配置splitChunks,splitChunks里面还有很多配置,还能将公共的代码分割成单独的文件等等,还有一点需要注意就是node_modules里面的文件也会被单独分割

最后:更多配置详情