介绍
Webpack 是一个模块打包器(module bundler),提供了一个核心,核心提供了很多开箱即用的功能,同时它可以用 loader 和 plugin 来扩展。webpack 本身结构精巧,基于 tapable 的插件架构,扩展性强,众多的 loader 或者 plugin 让 Webpack 稍显复杂。
环境
本案例所依赖的 npm 包默认都是最新版,vue 使用的是 2.0 版本
- node 15.2.0
- webpack 5.52.0
- vue 2.6.14
准备
初始化项目
mkdir webpack-vue-app
cd webpack-vue-app
npm init -y # -y 是指遇到确认自动选择 yes
安装 webpack
- npm
- yarn
npm i webpack webpack-cli -D
yarn add webpack webpack-cli -D
webpack-cli 是 Webpack 的命令行工具包
创建目录
webpack-vue-app
|- package.json
+ |- public
+ |- index.html
+ |- /src
+ |- index.js
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>webpack-vue-app</title>
</head>
<body>
<!-- ../dist/main.js 是 webpack 默认打包的相对路径 -->
<script src="../dist/main.js"></script>
</body>
</html>
const element = document.createElement('h1');
element.innerHTML = 'Hello Webpack';
document.body.appendChild(element);
为 package.json
的 scripts
脚本增加一行
本案例所有的代码修改都会以 背景色 突出为准
{
"name": "vue-webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.52.0",
"webpack-cli": "^4.8.0"
}
}
启动
执行 npm start
,就可以开始编译我们的入口文件了,然后把 html 文件在浏览器中打开
修改 js 文件中的信息,重新执行 npm start
,再刷新浏览器
就这?啥也不是!
别急,这只是完成了我们最基本的一个 webpack 配置
进阶
配置文件
根目录下创建 webpack.config.js
const path = require('path');
/** @type {(import('webpack').Configuration} */
const config = {
entry: './src/index.js', // 默认入口,如果一样可以不设置
output: {
// 出口
path: path.resolve(__dirname, 'dist'), // 默认打包文件夹,如果一样可以不设置
filename: 'main.js' // 默认打包目录文件名,如果一样可以不设置
},
mode: 'development' // 指定当前运行环境,必须设置:development":开发, "production":构建
};
module.exports = config;
开发服务器
webpack-dev-server 可以让我们启动一个本地服务器
- npm
- yarn
npm i webpack-dev-server -D
yarn add webpack-dev-server -D
-D 参数是 --dev 的简写,所有加了 -D 的安装依赖包,最终都不被打包进生产环境中
一般一些编译库,如:类型库,测试库,格式化库(eslint,prettier) 等会添加 -D 参数
不加 -D 的包都是会在生产环境用到的,如:主框架库,工具库
const path = require('path');
/** @type {(import('webpack').Configuration} */
const config = {
entry: './src/index.js', // 默认入口,如果一样可以不设置
output: {
// 出口
path: path.resolve(__dirname, 'dist'), // 默认打包文件夹,如果一样可以不设置
filename: 'main.js' // 默认打包目录文件名,如果一样可以不设置
},
mode: 'development', // 指定当前运行环境,必须设置:development":开发, "production":构建
devServer: {
port: 8000, // 指定端口
open: true, // 自启浏览器
hot: true, // 热更新
historyApiFallback: true, // 支持 history 模式路由
client: {
logging: 'none' // 清除浏览器 webpack 输出日志
}
// proxy: { // 代理跨域 如果需要的话
// "/api": {
// target:
// "https://www.baidu.com/api", // 目标代理接口地址
// changeOrigin: true, // 是否跨域
// pathRewrite: {
// "^/api": "/",
// },
// },
// },
},
stats: 'minimal' // 清除控制台 webpack 杂乱信息,仅在警告、错误和重新编译时输出
};
module.exports = config;
修改 package.json
启动脚本
"scripts": {
- "start": "webpack",
+ "start": "webpack serve",
+ "build": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
},
html-webpack-plugin
html-webpack-plugin 是一个 Webpack 插件,可以让我们的 html
文件自动去获取开发环境入口文件的位置
- npm
- yarn
npm install html-webpack-plugin -D
yarn add html-webpack-plugin -D
配置插件
const config = {
// ...
plugins: [
new HtmlWebpackPlugin({ template: 'public/index.html' }),
];
};
module.exports = config;
同时,我们可以删除掉 public/index.html
中的 script 标签了
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>webpack-vue-app</title>
</head>
<body>
<!-- <script src="../dist/main.js"></script> -->
</body>
</html>
重新执行 npm start
即可,然后任意修改 src/index.js
入口文件的打印,同时观察浏览器控制台的输出内容,会自动刷新
Vue 开发环境
安装 Vue
npm i vue
修改入口文件全部替换为
import Vue from 'vue';
new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
template: '<div>{{message}}</div>'
});
因为我们挂载了 Vue
实例到 id 为 app 的元素上,所以我们要在 html 中创建这个元素
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>webpack-vue-app</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
再运行 npm start
,观察控制台应该会发现一个警告:
[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
大致意思是 Vue 的包默认导出的是运行时版本,这个版本无法使用模板语法,我们需要去包含能够编译模板的版本
alias 别名
所以我们可以将 import Vue from 'vue';
调整为 import Vue from 'vue/dist/vue.js';
,但是这样每次写起来很麻烦并且不够清晰,所以接下来用到 Webpack 别名
修改配置文件
const config = {
// ...
resolve: {
alias: {
vue: 'vue/dist/vue.js',
'@': path.resolve(__dirname, 'src') // 同时配置 '@' 符号作为 src 的绝对路径别名方便后续开发
}
}
};
module.exports = config;
这样所以导入 vue
的地方都变成了 vue/dist/vue.js
这个路径
再次运行 npm start,页面上应该能看到 Hello Vue! 字样了
单文件组件支持
这个模板是纯字符串,没有任何提示也不好看,Vue
支持一种叫 SFC
的单文件组件支持,配合编辑器能够高效的写出更优雅的代码
在 src 下添加 App.vue 文件
webpack-vue-app
|- package.json
|- public
|- index.html
- |- /src
+ |- App.vue
|- index.js
添加以下代码
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
message: 'Hello Vue!'
};
}
};
</script>
同时修改入口文件,引入 App.vue
import Vue from 'vue';
import App from '@/App.vue';
new Vue({
el: '#app',
render: h => h(App)
});
重新执行 npm start
,观察网页会发现又出了新的报错:
Module parse failed: Unexpected token (1:0) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
意思是 Webpack 无法解析 .vue
的文件(默认只能处理 .js
文件),我们需要全装对应的 loader 来处理
loader
什么是 loader ?
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。
用白话来说 loader 的作用就是借助 Webpack 的拓展能力,让第三方去处理 Webpack 自身无法处理的文件
Vue
同样为其单文件组件准备了对应的 vue-loader
vue-loader
npm i vue-loader vue-template-compiler css-loader -D
css-loader
处理.css
结尾的文件vue-template-compiler
是vue
的模板解析器
vue-template-compiler
需要独立安装的原因是你可以单独指定其版本。
每个 vue
包的新版本发布时,一个相应版本的 vue-template-compiler
也会随之发布。编译器的版本必须和基本的 vue
包保持同步,这样 vue-loader
就会生成兼容运行时的代码。这意味着你每次升级项目中的 vue
包时,也应该匹配升级 vue-template-compiler
。
修改配置文件
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const config = {
// ...
module: {
rules: [
{
test: /\.vue$/, // 通过正则匹配所有 .vue 结尾的文件使用
loader: 'vue-loader', // 使用 vue-loader
},
// 它会应用到普通的 `.css` 文件
// 以及 `.vue` 文件中的 `<style>` 块
{
test: /\.css$/,
use: [ // 多个 loader 以数组形式并使用 use 作为 key
'vue-style-loader', // 这个 loader 是 vue-loader 已经集成的
'css-loader',
],
},
],
},
plugins: [
new HtmlWebpackPlugin({ template: 'public/index.html' }),
new VueLoaderPlugin(),
];
};
module.exports = config;
Vue Loader 的配置和其它的 loader 不太一样。除了通过一条规则将 vue-loader
应用到所有扩展名为 .vue
的文件上之外,还需要在 webpack 配置中添加 Vue Loader
vue-loader
的作用是将你定义过的其它规则复制并应用到 .vue
文件里相应语言的块。例如,如果有一条匹配 /\.js$/
的规则,那么它会应用到 .vue
文件里的 <script>
里面
重新执行 npm start
,.vue
文件可以正常通过编译,并且可以使用 style
标签添加 css 属性了
less
上述也仅支持原生 css 而已,原生 css 已经无法满足我们的正常需求了。嵌套、变量、函数、混合等功能也越来越变得刚需了。
如 less/scss/stylus/css-module 等众多 css 库已经成为了开发的标配,所以我们还需要对其配置一下
本文以 less 为例 添加 less 安装包,less 和 less-loader 就像 vue 和 vue-loader 的关系,一个是本体,一个是加载工具
npm i less less-loader -D
修改配置文件
const config = {
// ...
module: {
rules: [
// ...
{
test: /\.(css|less)$/,
use: ['vue-style-loader', 'css-loader', 'less-loader']
}
]
}
};
module.exports = config;
style-loader
loader 的顺序很重要,如果 loader 有多个,那 执行的顺序是从后往前相反的,所以编译规则是:
less-loader
: 使用 Less loader 把 less 转化为普通 csscss-loader
: 解决 css 中所有带import
和url(...)
的地方(浏览器并不不支持这些方法,就像 js 解析import/require()
一样)style-loader
:把 css 处理成 style 标签并插入到 DOM 中
可以看到我们上述用了 vue-style-loader
而不是 常规的 style-loader
,这里其实是 Vue 把 style-loader
复制了一份,然后解决了一些服务端渲染的问题
如何测试能正常使用 less?替换 App.vue 为以下代码,当嵌套发生作用时,说明配置成功
<template>
<div class="container">
<h1>{{ message }}</h1>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
message: 'Hello Vue!'
};
}
};
</script>
<style lang="less">
.container {
h1 {
background-color: #ccc;
}
}
</style>
图片/字体
Assetmodules 是一种 webpack 模块类型,允许使用资产文件(字体、图标等)而不需要配置其他 loader。
webpack4 及之前处理图片、字体文件需要引用到 file-loader
、url-loader
、raw-loader
等插件
修改配置文件
const config = {
entry: './src/index.js', // 默认入口,如果一样可以不设置
output: {
// 出口
path: path.resolve(__dirname, 'dist'), // 默认打包文件夹,如果一样可以不设置
filename: 'js/[name].bundle.js', // 默认打包目录文件名,如果一样可以不设置
assetModuleFilename: 'images/[name].[ext][query]', // 图片文件需要存放的位置,如果不设置会全部打包进根目录下
clean: true // 打包时每次清空目录
},
// ...
module: {
rules: [
// ...
{
test: /\.(png|jpe?g|gif)$/,
type: 'asset/resource'
},
{
test: /\.(woff(2)?|eot|ttf|otf|svg|)$/,
type: 'asset/inline'
}
]
}
};
module.exports = config;
可以正常加载图片了
eslint
eslint 可以帮助我们在开发过程中即时检测一些语法错误拼写错误,甚至部分能够帮我们自动修复
npm i eslint eslint eslint-plugin-vue eslint-webpack-plugin -D
- eslint eslint 核心库,对基础语法的校验和修复功能
- eslint-plugin-vue vue 官方开发的针对 vue 语法 的 eslint 插件
- eslint-webpack-plugin webpack 的 eslint 插件,用于开发时控制台提示出语法错误信息
在根目录下新建 .eslintrc
配置文件,我们的配置可以完全继承于 eslint-plugin-vue
{
"env": {
// 指定我们的开发宿主环节有哪些
"node": true // eslint-plugin-vue 中已经包含了 "browser" 和 "es6",添加 node 是为了让我们正常使用 node 方法比如 require
},
"extends": ["eslint:recommended", "plugin:vue/recommended"] // 继承 eslint 的推荐配置(常规 js 文件) 和 vue 的推荐配置(.vue 文件)
}
编辑器提示
- Visual Studio Code
- Sublime Text
- IntelliJ IDEA
确保安装了微软官方的 vscode eslint 插件,在项目下新建 .vsocde
目录并在里面新建 .settings.json
文件,配置如下代码
{
"eslint.validate": ["javascript", "javascriptreact", "vue"]
}
如果安装了 Vetur 插件还需要设置 "vetur.validation.template": false
避免冲突
使用 Package Control 安装 SublimeLinter 和 它的 ESLint 插件 SublimeLinter-eslint
打开菜单 Preferences > Package Settings > SublimeLinter > Settings
粘贴以下代码:
{
"linters": {
"eslint": {
"selector": "text.html.vue, source.js - meta.attribute-with-value"
}
}
}
打开设置/首选项(/Cmd+,/Ctrl+Alt+S/
),在 Languages and Frameworks 中选择 JavaScript,然后在 Code Quality Tools 选择 ESLint。在打开的 ESLint 页面上,选择 Enable 复选框。
详情可以去看 JetBrains - ESLint
配置好以后编辑器中的报错应该都消失了
eslintignore
设置 eslint 忽略文件以排除不必要的文件校验,在根目录下新建 .eslintignore
!**/.eslintrc*
node_modules*
dist
public
*.svg
*.ico
*.json
.gitignore
*.md
*.log
*.lock
eslint-webpack-plugin
上述只是编辑器的一个提示,控制台或浏览器还不能正常提示(但其实对大部分人来说也足够,并且控制台的报错影响开发体验),不需要的可以跳过
修改 webpack 配置文件
// ...
const EslintWebpackPlugin = require('eslint-webpack-plugin');
const config = {
// ...
plugins: [
// ...
new EslintWebpackPlugin({
fix: true, // fix 参数能够自动修复部分错误
extensions: ['js', 'vue'], // 需要检测的文件类型
// 更多参数可以去查看 https://webpack.docschina.org/plugins/eslint-webpack-plugin/#root
}),
],
module.exports = config;
extensions 后缀解析
extensions(解析)功能会尝试按顺序解析特定后缀名。如果有多个文件有相同的名字,但后缀名不同,webpack 会解析列在数组首位的后缀的文件 并跳过其余的后缀
我们目前的路径还需要输入完整的后缀名, extensions 配置能帮我们省去
// ...
const config = {
// ...
resolve: {
alias: {
vue: 'vue/dist/vue.js',
'@': path.resolve(__dirname, 'src')
},
extensions: ['.vue', '.js', '.json']
}
};
module.exports = config;
babel
babel
的作用是把我们的 es6
或更高的代码转为 es5
,使得能够兼容低版本的浏览器,如 箭头函数、Promise
、class
类等
安装 babel 依赖
npm i @babel/core @babel/preset-env babel-loader -D
修改配置文件
//...
const config = {
// ...
module: {
rules: [
// ...
{
test: /\.js$/, // 对所有 .vue 结尾的文件使用 vue-loader
exclude: /node_modules/, // 排除 node_modules 目录
use: [
{
loader: 'babel-loader'
}
]
}
]
}
};
module.exports = config;
同时还需要配置 babel,虽然也可以写在 webpack 配置文件 babel-loader
的 options 中,为了更加清爽直观,我们选择在单独的配置文件中配置它
根目录下创建 .babelrc
文件,babel-loader 会自动找到这个路径读取 babel 配置
{
"presets": ["@babel/env", "@vue/babel-preset-jsx"]
}
jsx
jsx 在灵活性和智能提示上有时候比模板更好用,vue 官方也对 jsx 专门出了一个对应的 babel 插件 @vue/babel-preset-jsx
npm i @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props -D
因为是 babel 插件,所以也要修改下 babel 的配置
{
"presets": ["@babel/env", "@vue/babel-preset-jsx"]
}
为了 eslint 能够正常识别 jsx 语法,因此也要对应的设置一下
{
"env": {
"node": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true // 开启 jsx
}
},
"extends": ["eslint:recommended", "plugin:vue/recommended"],
"rules": {
"no-unused-vars": "warn",
"semi": "warn"
}
}
现在,App.vue
中就可以正常使用 jsx 语法了
<script>
export default {
name: 'App',
data() {
return {
message: 'Hello Vue!'
};
},
render() {
return (
<div class="container">
<h1>{this.message}</h1>
<img src={require('./assets/logo.png')} />
</div>
);
}
};
</script>
<style lang="less" scoped>
.container {
h1 {
background-color: #ccc;
}
}
</style>
sourcemap
尝试 在 src/App.vue
文件中打任意打印,然后在浏览器控制台显示的文件名和行号都不对,这是因为运行代码是源代码编译或者加密混淆之后的代码,所以不管是开发时还是线上时调试的文件名称和行号等信息,都无法准确定位。
source-map 的基本原理是,在编译处理的过程中,在生成产物代码的同时生成产物代码中被转换的部分与源代码中相应部分的映射关系表。
Webpack 中,通过设置 devtool 属性来选择 source map 的类型,包含了 "eval" "cheap" "module" "inline" "hidden" "nosource" "source-map" 等关键字的组合
修改 webpack 配置文件
// ...
const config = {
// ...
devtool: 'source-map'
// ...
};
module.exports = config;
重新执行 npm start
就可以清楚的看到打印信息来源了,报错信息也如此
生产环境
目前所有的配置都是基于开发环境,在构建生产环境的时候,我们需要一系列的优化。
配置文件抽离
所以开发环境和生产环境的配置也有所不同,为了便于维护,我们将对开发环境和生产环境的配置文件单独维护
webpack-vue-app
|- config
|- webpack.common.config.js
|- webpack.dev.config.js
|- webpack.prod.config.js
webpack.common.config.js
: 提取公共的配置webpack.dev.config.js
: 开发环境的配置webpack.prod.config.js
: 生产环境的配置
修改后的代码如下
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const EslintWebpackPlugin = require('eslint-webpack-plugin');
/** @type {(import('webpack').Configuration} */
const config = {
entry: path.resolve(__dirname, '../src/index.js'), // 默认入口,如果一样可以不设置
output: {
// 出口
path: path.resolve(__dirname, '../dist'), // 默认打包文件夹,如果一样可以不设置
filename: 'js/[name].bundle.js', // 默认打包目录文件名,如果一样可以不设置,
assetModuleFilename: 'images/[name].[ext][query]', // 图片文件需要存放的位置,如果不设置会全部打包进根目录下
clean: true // 打包时每次清空目录
},
module: {
rules: [
{
test: /\.vue$/, // 对所有 .vue 结尾的文件使用 vue-loader
loader: 'vue-loader'
},
{
test: /\.js$/, // 对所有 .vue 结尾的文件使用 vue-loader
exclude: /node_modules/, // 排除 node_modules 目录
use: [
{
loader: 'babel-loader'
}
]
},
{
test: /\.(css|less)$/,
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true // 启用 css sourceMap
}
},
'less-loader'
]
},
{
test: /\.(png|jpe?g|gif|svg)$/,
type: 'asset/resource'
},
{
test: /\.(woff(2)?|eot|ttf|otf|svg|)$/,
type: 'asset/inline'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html'
}),
new VueLoaderPlugin(),
new EslintWebpackPlugin({
fix: true,
extensions: ['js', 'vue']
})
],
resolve: {
alias: {
vue: 'vue/dist/vue.js',
'@': path.resolve(__dirname, '../src') // 同时配置 src 路径别名方便后续开发
},
extensions: ['.vue', '.js', '.json']
},
stats: 'minimal'
};
module.exports = config;
const { merge } = require('webpack-merge');
const common = require('./webpack.common.config');
module.exports = merge(common, {
mode: 'development',
devtool: 'source-map',
devServer: {
port: 8000, // 指定端口
open: false, // 自启浏览器
hot: true, // 局部热更新
historyApiFallback: true, // 支持 history 模式路由
client: {
logging: 'none'
}
// proxy: { // 代理跨域 如果需要的话
// "/api": {
// target:
// "https://www.baidu.com/api", // 目标代理接口地址
// changeOrigin: true, // 是否跨域
// pathRewrite: {
// "^/api": "/",
// },
// },
// },
}
});
const { merge } = require('webpack-merge');
const common = require('./webpack.common.config');
module.exports = merge(common, {
mode: 'production',
devtool: false, // 生产环境不需要 source-map
output: {
filename: 'js/[name].[contenthash].bundle.js', // 文件名哈希
assetModuleFilename: 'images/[name].[contenthash][ext][query]' // 图片文件需要存放的位置,如果不设置会全部打包进根目录下
}
});
可以看出 生产环境中移除了 devServer 和 devtool 选项
webpack-merge
为了避免耦合,我们对公共代码部分进行了抽离,并且引入了一个新的插件 webpack-merge
,作用就是合并 webpack 配置
npm i webpack-merge -D
现在我们没有使用默认的配置文件 webpack.config.js
,webpack 自然也加载不出来,因此在 package.json
webpack 脚本中添加 --config
参数参数以修改配置文件路径,
{
"scripts": {
"start": "webpack serve --config config/webpack.dev.config.js",
"build": "webpack --config config/webpack.prod.config.js",
"lint": "eslint \"src/**/*.{js,vue}\"",
"test": "echo \"Error: no test specified\" && exit 1"
}
}
JS 代码压缩
为了生产环境的优化,js 一般会压缩所有的换行和不必要的空格,变成一行,以及代码混淆(没错就是变量名变成 a,b,c 那种)
js 代码压缩要用到 terser-webpack-plugin
,但 webpack 5 内置了这个插件可一键开启(如果是 webpack 4 则需要单独安装)
修改生产环境的配置文件
const { merge } = require('webpack-merge');
const common = require('./webpack.common.config');
module.exports = merge(common, {
mode: 'production',
devtool: false, // 生产环境不需要 source-map
optimization: {
minimize: true
// minimizer: [...] 如果想要自定压缩的话
}
});
如果想要自定义压缩的话,仍需引入 terser-webpack-plugin
Webpack v5 comes with the latest
terser-webpack-plugin
out of the box. If you are using Webpack v5 or above and wish to customize the options, you will still need to installterser-webpack-plugin
. Using Webpack v4, you have to installterser-webpack-plugin
v4.
CSS 代码抽离
目前的 css 代码都是含在 js 中通过 js 动态插入到 html 中,生产环境 不应该如此。
mini-css-extract-plugin 插件会将 css 提取到单独的文件中,为每个包含 css 的 js 文件创建一个 css 文件。
npm i mini-css-extract-plugin -D
将 config/webpack.common.config.js
中的 css 规则迁移到 config/webpack.dev.config.js
中,修改 config/webpack.prod.config.js
单独为 生产环境制定 css 优化
// ...
module.exports = merge(common, {
// ...
module: {
rules: [
{
test: /\.(css|less)$/,
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true // 启用 css sourceMap
}
},
'less-loader'
]
}
]
}
});
// ...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = merge(common, {
// ...
module: {
rules: [
{
test: /\.(css|less)$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
}
]
},
plugins: [
// 启用 css 压缩插件
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash].css'
})
]
});
CSS 压缩
js 压缩了换行和空合,那 css 自然也少不了css-minimizer-webpack-plugin
用于 css 的文件压缩
npm i css-minimizer-webpack-plugin -D
// ...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = merge(common, {
// ...
optimization: {
minimize: true,
minimizer: [
`...`, // 在 webpack@5 中,使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`)
new CssMinimizerPlugin()
]
}
});
部署
publicPath
默认情况下,webpack 会假设你的应用是被部署在一个域名的根路径上,例如 https://www.my-app.com。 如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.my-app.com/my-app/ ,则设置 publicPath 为 /my-app/
// ...
module.exports = merge(common, {
// ...
output: {
publicPath: '/子路径/'
}
});
其他
文件名 hash 值
如果每次构建输出的文件名称是一样的,会给在客户端做长缓存带来很大的麻烦。因此,我们需要在输出的文件名中添加一些 hash 值,使得每次构建输出的文件名称是不一样的。
假设有如下配置:
const config = {
output: {
path: PATHS.build,
filename: '[name].[contenthash].js'
}
};
Webpack 构建输出的文件名为:
main.d587bbd6e38337f5accd.jsvendor.dc746a5db4ed650296e1.js
此时,如果文件内容发生变化,则 [contenthash] 也会相应的变化,此时,浏览器缓存就会失效,浏览器就会重新发起一个请求来加载变化了的文件。即,如果仅仅只要 main.js 发生了变化,那么浏览器只会重新请求加载 main.js。
我们还可以将 hash 值加载请求参数中,比如 main.js?d587bbd6e38337f5accd, 这样的话,输出文件名不变,通过查询参数的变化来使缓存失效。
react 环境
如果你是一个 react 开发者,参考如下配置
- 将 vue-loader 移除,并删除对应规则
- 添加
@babel/preset-react
插件到 babel 配置中,babel 的规则修改为test: /\.jsx?$/
vue-style-loader
替换成style-loader
并安装- 所有涉及
.vue
后缀的地方替换成.jsx
- eslint 安装
eslint-plugin-react
,并将 eslint 配置文件中的 "plugin:vue/recommended" 替换成 "plugin:react/recommended"
然后就是正常的安装 react
react-dom
等依赖了