webpack5-配置总结

近一段时间做了webpack的打包配置工作,从头到尾把打包配置又理了一遍,现在来记录一下。

前言

本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。---来自官网的介绍

webpack打包配置能做些什么?

具体处理功能:

  • css 处理
  • html使用模版,且实现自动引入js、css等资源
  • js的兼容性和压缩
  • 代码格式化开发 .eslintrc.js
  • 提升开发体验
  • 打包构建速度
  • 缓存
  • 开发服务器自动化

1. 核心概念

  • 入口entry:指定打包入口
  • 输出output:指定打包后的文件输出到哪里,怎么命名
  • 加载器loaderwebpack本省只能处理js,json等资源,其他资源需要借助loader webpack 才能解析
  • 插件plugins:扩展webpack的功能
  • 模式mode: development , production
  • 浏览器兼容性browser compatibility:webpack支持所有符合ES5标准的浏览器。webpackimport()需要Promise,如果你想要支持旧版本浏览器,在使用表达式之前,还需要提前加载polyfill。

2. css 处理

webpack不能处理样式资源,需要借助loader才能解析,这些loader配置在module.rules[]中,那css,styl需要哪些loader以及怎样配置?

2.1 css样式初步处理

下载:

npm i css-loader style-loader stylus-loader -D

配置

module:{
  rules[{
    test: /\.css$/,
    use:["style-loader", "css-loader", "stylus-loader"]
  }]
}

其中,
test用来表示需要匹配的内容,以.css结尾的文件,
use数组表示使用的加载器loader是哪些,以及loader的配置,从执行。

loader介绍:
stylus-loader:将styl资源编译成css资源
css-loader:将css文件编译成commonjs模块化到js中;
style-loader: 动态创建style标签,将js中的css引入到html中生效
注:其他的样式资源也是类似的配置,css再转style

2.2 css样式再处理-单独文件

随着后续项目增大,css资源增多,把这些文件都打入到一个js中,则js文件过大,可能会出现闪屏现在,用户体验不好,所以需要我们把文件打成单独的css,不再使用style-loader而是使用link标签引入资源来提高运行性能。

下载:

npm i mini-css-extract-plugin -D

配置:

const MiniCssExtractPlugin = require(''mini-css-extract-plugin")
module.exports = {
entry:xxx,
output: {path: xxx, filename:xxx},
module: {
  rules: [
    test: /\.css$/,
    use:[MiniCssExtractPlugin.loader, css-loader, stylues-loader]
  ]
},
plugins: [
  new MiniCssExtractPlugin({filename: "static/css/main.css"})
]
}

其中,
①通过MiniCssExtractPlugin.loader可以使用link标签引入css
②使用MiniCssExtractPlugin插件,并配置css输出的路径以及文件名

2.3 css样式再处理-兼容性

css存在兼容性问题(eg:flex),可通过Can I Use查看兼容性问题

下载:

npm i postcss-loader postcss postcss-preset-env

配置:

const MiniCssExtractPlugin = require(''mini-css-extract-plugin")
module.exports = {
entry:xxx,
output: {path: xxx, filename:xxx},
module: {
  rules: [
    test: /\.css$/,
    use:[
      MiniCssExtractPlugin.loader,
      css-loader, 
      {
        loader: "postcss-loader",
        options: {
          postcssOptioins: {
            plugins: ["postcss-preset-env"]
          }
        }
      },
      stylues-loader]
  ]
},
plugins: [
  new MiniCssExtractPlugin({filename: "static/css/main.css"})
]
}

postcss-loader 依赖于postcss
postcss-preset-env智能预设,用来解决大多数兼容性问题。

2.4 css样式再处理-压缩

下载:

npm I css-minimizer-webpack-plugin -D

配置:

const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")

module.exports = {
entry:xxx,
output: {path: xxx, filename:xxx},
module: {
  rules: [
    test: /\.css$/,
    use:[
      MiniCssExtractPlugin.loader,
      css-loader, 
      {
        loader: "postcss-loader",
        options: {
          postcssOptioins: {
            plugins: ["postcss-preset-env"]
          }
        }
      },
      stylues-loader]
  ]
},
plugins: [
  new MiniCssExtractPlugin({filename: "static/css/main.css"})
],
optimization: {
  minimize: true,
  minimizer: {
    new CssMinimizerWebpackPlugin()
  }
}

optimization压缩和优化处理都会写在这里
css-minimizer-webpack-plugin用于压缩css资源

3. html 处理

3.1 html使用模版

每次都需要在html文件中自己手动写link引入css,比较繁琐,并且打包生产的html文件内容并非自己内容,这样处理
下载:

npm i html-webpack-plugin -D

配置:

const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
entry:xxx,
output: {path: xxx, filename:xxx},
module: {
  rules: [
    test: /\.css$/,
    use:[
      MiniCssExtractPlugin.loader,
      css-loader, 
      {
        loader: "postcss-loader",
        options: {
          postcssOptioins: {
            plugins: ["postcss-preset-env"]
          }
        }
      },
      stylues-loader]
  ]
},
plugins: [
  new MiniCssExtractPlugin({filename: "static/css/main.css"}),
  new HtmlWebpackPlugin({
    template: path.resolve(__dirname, "public/index.html")
  })
],
optimization: {
  minimize: true,
  minimizer: {
    new CssMinimizerWebpackPlugin()
  }
}

HtmlWebpackPlugin会将public/index.html作为模版输出,并且自动引入打包生成的js css等资源

3.2 html压缩

生产环境下默认对htmljs都进行了压缩

4. js处理

webpack只能编译jsES模块化语法,不能编译其他语法,例如箭头函数,Promise,所以还要再做一些兼容性处理

4.1 js兼容性处理-babel

babel:将ES6语法编写的代码转换为向后兼容的js语法,使在旧浏览器上可以运行。
下载:

npm i babel-loader @babel/core @babel/preset-env -D

创建配置文件:
babel.config.js
配置babel.config.js:

module.exports = {
  presets: ["@babel/preset-env"]
}

presets一组babel插件,扩展babel功能
@babel/preset-env:智能预设,允许使用最新的JavaScript
配置webpack.config.js

const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
entry:xxx,
output: {path: xxx, filename:xxx},
module: {
  rules: [
  {
    test: /\.css$/,
    use:[
      MiniCssExtractPlugin.loader,
      css-loader, 
      {
        loader: "postcss-loader",
        options: {
          postcssOptioins: {
            plugins: ["postcss-preset-env"]
          }
        }
      },
      stylues-loader]
    ]
  },
  {
    test: /\.js$/,
    exclude: /node_modules/,
    use:[loader: "babel-loader"]
  },
},
plugins: [
  new MiniCssExtractPlugin({filename: "static/css/main.css"}),
  new HtmlWebpackPlugin({
    template: path.resolve(__dirname, "public/index.html")
  })
],
optimization: {
  minimize: true,
  minimizer: {
    new CssMinimizerWebpackPlugin()
  }
}

exclude: 排除node_module不用编译
② 使用babel-loader处理js兼容性问题

4.2 js兼容性处理-core-js

发现使用babel处理js的兼容性不够完善,它能够处理箭头函数,点点点运算符等,但是如果是async函数,promise对象,数组的便易方法等,它没有办法处理,一旦遇到低版本的浏览器还是会出现兼容性问题。
core-js是用来做ES6以上API的补丁(社区上提供的代码段)
下载:

npm i core-js -D

配置babel.config.js

module.exports = {
  presets: [
    "@babel/preset-env",
    { useBuiltIns: "usage", corejs: {version: "3", proposals: true }}
  ]
}

这样就会按照我们代码中使用的语法,自动按需引入相应的补丁了

4.3 babel处理兼容性后体积过大-runtime

babel对一些公共方法添加了辅助代码,默认会为每隔一需要他的文件都引入进去,导致项目多大,可以将这些辅助代码作为一个独立模块,避免重复引入。

@babel/plugin-transform-runtime禁用了babel对每个文件的runtime注入,而是通过@babel/plugin-transform-runtime从这里引用。

下载:

npm i @babel/plugin-transform-runtime -D

配置:

module.exports = {
  presets: [
    "@babel/preset-env",
    { useBuiltIns: "usage", corejs: {version: "3", proposals: true }}
  ],
  plugins: ["@babel/plugin-transform-runtime"]
}
4.4 js单独成文件&按需加载

将所有的js文件打包到一个文件中,体积太大了,如果我们只渲染首页,应该只加载首页的js,不应该把其他的也加进来。这样就需要把js按文件分割,生成多个js文件

Code Split主要做的两件事:
①分割文件:将打包生成的文件进行分割,生成多个js
②按需加载:需要哪个文件就加载哪个文件

分割文件

optimization: {
  minimize: true,
  minimizer: {
    new CssMinimizerWebpackPlugin(),
    splitChunks: {
      chunks: "all", // 对所有模块进行分割
    }
  }
}

动态导入&给动态导入文件命名

import (/* webpackChunkName: "detail" */ "./page/detail/detail.js")
.then((res)=> {
  console.log(res)
})
.catch(()=> {
  console.log("导入失败")
})

其中,
webpackChunkName: "detail" 是webpack动态导入模块命名方式,"detail"会作为[name]的值显示

4.4 按需加载卡顿问题

使用Code Split技术按需加载也带了一定的问题,比如用户某个点击操作之后进行资源加载,此时网速不佳,资源过大,那么用户明显会感觉到卡顿的感觉,此时需要用到preload或是prefetch技术
preload:告知浏览器立即加载资源
prefetch:告知浏览器空闲时开始加载资源
共同点:仅加载,不执行,有缓存,但兼容性差
不同点:preload执行级别高于prefetchpreload只能加载当前页面资源,而prefetch可以加载下一个页面的资源
下载:

npm i @vue/preload-webpack-plugin -D

配置:

const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin")
module.exports = {
  ...
  plugins: [
      new PreloadWebpakcPlugin({
        rel: "preload",
        as: "script"
      })
  ]
}
4.5 引用函数库的某些方法

开发时会定义些工具库,如果没有做处理的话会将整个工具库引入进来,但实际上只用了极小的部分功能,这样打包进来体积太大,webpack默认开了一个功能Tree Shaking,用于移除js中没有用的代码,需要注意的是它依赖于ES Module

5. 代码格式化开发

Eslint 用来检测js的语法工具,使用Eslint关键是编写配置文件里面的各种rules规则,运行Eslint时就会以写的规则对代码进行检查。
编写配置.eslintrc.js

module.exports = {
  parserOptions:{
    ecmaVersion: 6, // ES 语法版本
    sourceType: "module", // ES 模块化
  },
  rules: {
    semi: "error", // 禁止使用分号
    eqeqeq: [
      'warn', // 强制使用 === 和 !==,否则警告
    ],
  },// rules 优先级别高于extends,三种级别off,warn,error
  extends: [],//继承其它规则
}

Eslint 官方的规则
Vue Cli官方规则
配置webpack.config.js

const EslintWebpackPlugin = require('eslint-webpack-plugin')
module.exports = {
  ...
  plugins:[
    new ESLintWebpackPlugin({
      context: path.resolve(__dirname, "src")
    })
]
}
import问题

在给js动态导入文件import(()=> ()).then().catch(),在eslint检查语法规范时是不合格的,所以我们需要再引入一个eslint插件

npm i eslint-plugin-import -D

配置:

module.exports = {
  parserOptions:{
    ecmaVersion: 6, // ES 语法版本
    sourceType: "module", // ES 模块化
  },
  plugins:["import"],// 解决动态导入import语法报错问题,实际使用eslint-plugin-import 的规则解决
  rules: {
    semi: "error", // 禁止使用分号
    eqeqeq: [
      'warn', // 强制使用 === 和 !==,否则警告
    ],
  },// rules 优先级别高于extends,三种级别off,warn,error
  extends: [],//继承其它规则
}

6. 提升开发体验

SourceMap(源代码映射)将生成的源代码与构建后代码的一一映射的文件,生成一个xxx.map文件,从构建后代码出错位置找到源代码出错的位置,从而让浏览器提示源代码文件出错的位置,帮助我们很快找到错误根源。
配置

module.exports= {
  devtool: "cheap-module-source-map"// 开发环境
  // devtool: "source-map"// 生产环境
}

7. 提升打包构建速度

7.1 HMR(HotModuleRelaceMent热模替换)

开始时如果修改了一个模块代码,webpack默认会将所有模块打包重新编译,速度较慢,所以如果修改某个模块代码,只打包改模块代码其他模块不变,这样打包速度就能快很多了。

HMR(热模替换)在程序运行中,替换、添加或删除模块,而无需重新加载整个页面。

css样式经过style-loader处理具备了HMR功能,如果是vue项目的话,使用vue-loader来解决HMR问题

7.2 OneOf

默认打包时每个文件都会经历所有的loader处理,即使已经匹配上了某一个loader,还要继续向下匹配,做了无用功。OneOf只要匹配上一个loader剩下的就不匹配了
配置

module.exports = {
  module: {
    rules:[
      { oneof: [{test: /\.js$/,exclude: /node_module/,use["babel-loader"]},{}] },
      {}
    ]
  }
}
7.3 Include/Exclude(包含/排除)

include:包含哪些文件
exclude:排除哪些文件

7.4 Thread开始多进程

当项目越来越大时,打包速度也越来越慢,主要是js文件,而处理jseslint, babel, terser三个工具,开启多进程处理js文件
注:如果项目比较大,适合开启多进程,因为每个进程启动需要大约600ms的开销
下载:

npm i thread-loader -D

配置

import os  = require("os")
const threads = os.cpus().length;
module.exports = {
...
  module: {
    rules:[
      { oneof: [
        {
          test: /\.js$/,
          exclude: /node_module/,
          use[
              {loader: "babel-loader",options:{works:threads}},// 开启进程数量
           ]
        },
    ]
  }
}

以上以babel举例,具体如何配置,查看webpack官网配置即可。

8. 缓存

8.1 eslint,babel缓存

每次打包js都要经过eslint检查,babel编译,速度比较慢。如果缓存之前eslint检查,babel编译结果,那第二次打包速度将会更快
配置

import os  = require("os")
const threads = os.cpus().length;
module.exports = {
...
  module: {
    rules:[
      { oneof: [
        {
          test: /\.js$/,
          exclude: /node_module/,
          use[
              { loader: "babel-loader",
                options:{
                  works:threads,
                  cacheDirectory: true,// 开启babel编译缓存
                  cacheCompress:false //缓存文件不要压缩
              }},// 开启进程数量
           ]
        },
    ]
  }
},
plugins:[
  new EslintWebpackPlugin({
     context: path.resolve(__dirname, "../src"),
     exclude: /node_module/,
     cache: true,//开启缓存
     cachelocation: path.resolve(__dirname, "../node_module/.cache/.eslintcache")
  })
]
8.2 Network Cache

将来开发时我们对静态资源会使用缓存来优化,这样浏览器第二次请求资源就能读取缓存了,速度很快。

但是这样的话就会有一个问题, 因为前后输出的文件名是一样的,都叫 main.js,一旦将来发布新版本,因为文件名没有变化导致浏览器会直接读取缓存,不会加载新资源,项目也就没法更新了。

所以我们从文件名入手,确保更新前后文件名不一样,这样就可以做缓存了。
它们都会生成一个唯一的 hash 值。

  • fullhash
    每次修改任何一个文件,所有文件名的 hash 至都将改变。所以一旦修改了任何一个文件,整个项目的文件缓存都将失效。

  • chunkhash
    根据入口文件(Entry)进行依赖文件解析、构建对应的 chunk,生成对应的哈希值。js 和 css 是同一个引入,会共享一个 hash 值。

  • contenthash
    根据文件内容生成 hash 值,只有文件内容变化了,hash 值才会变化。所有文件 hash 值是独享且不同的。

filename: "static/js/[name].[contenthash:8].js"
chunkFilename: "static/js/[name].[contenthash:8].chunk.js" // 动态导入输出资源的命名方式

但是这样也会引发一个问题,修改了一个js文件,因为contenthash的原因,则相应的js文件名会改变,那和改js相依赖的文件也会发生变化。

这时需要把hash值单独保管在一个runtime文件中,如果A文件发生变化,最终修改的只是A文件和runtime,其他的保持不变。

// 提取runtime文件
    runtimeChunk: {
      name: (entrypoint) => `runtime~${entrypoint.name}`, // runtime文件命名规则
    },
8.3 离线加载

一种类似原生应用程序的技术,在离线时应用程序能够继续运行。内部通过Service Workers技术实现,使用workbox-webpack-plugin插件完成

9. 开发服务器自动化

每次写完代码都需要手动输入指令才能编译代码,太麻烦了,怎么一切自动化呢?

npm I webpack-dev-server -D

配置

devServer: {
  host: "localhost", // 启动服务器域名
  port: "3000", // 服务器端口号
  open: true // 自动打开浏览器
}

后续再补充。。。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容