Webpack5正式发布也有很长长长一段时间了,上手了一段时候后发现真香。webpack5的新特性使得我们在配置上比以往版本更加方便了,构建速度也有了质的飞跃。本文着重为大家讲解从 0 到 1 搭建 vue3 + webpack 5开发环境的过程中遇到的疑问。
项目地址: webpack5-vue3
demo地址: https://zhouyupeng.github.io/webpack5-vue3/#/
先看一下配置好的项目目录结构
├─build
│ ├─webpack.base.conf.js
│ ├─webpack.dev.conf.js
│ ├─webpack.prod.conf.js
│ ├─webpack.rules.conf.js
├─node_modules
├─public
| |-index.html
└─src
| ├─api
| ├─assets
| ├─components
| ├─filters
| ├─plugins
| ├─router
| ├─store
| ├─style
| ├─utils
| ├─views
| |-App.vue
| |-main.ts
|-.env.dev
|-.env.test
|-.env.prod
|-.gitigore
|-babel.config.js
|-package.json
|-postcss.config.js
接下来,我们使用 Webpack5
从0搭建一个完整的 Vue3
的开发环境!
环境(environment)
webpack 5 运行于 Node.js v10.13.0+ 的版本。
本文涉及到依赖的版本号
├── webpack 5.43
├── webpack-cli 4.7.2
├── node 14.17.0
第一步: 创建目录并且初始化 package.json
mkdir webpack5-vue3 && cd webpack5-vue3
yarn init -y
第二步: 安装webpack三件套
yarn add webpack webpack-cli webpack-dev-server -D
注意:
第三步:初始化目录和文件
./build/webpack.config.js
文件和./src/main.js
文件并且写上webpack打包配置代码// webpack.config.js
const path = require('path');
module.exports = {
entry: path.resolve(__dirname, '../src/main.js'), // 入口
output: {
path: path.resolve(__dirname, '../dist'),
filename: './js/[name].[chunkhash].js',
publicPath: './',
},
}
"build": "webpack --config ./build/webpack.config.js --mode production --progress --color -w"
参数详解
运行打包脚本yarn build
看到webpack运行并且打包成功了。
webpack5 + webpack-cli4 启动开发服务器命令与之前有所变化,从 webpack-dev-server 转变为 webpack serve,
"build": "webpack serve --config ./build/webpack.config.js --mode development --progress --color",
运行起来后出现了另外的问题。改了代码控制台重新编译,但是热更新无效。
看一下无效的启动和有效启动之后的 network 截图对比:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QOTs8jF9-1627564441599)(https://card.h5551.com/gPONlR.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vQQTHdw2-1627564441603)(https://card.h5551.com/gPxobq.png)]
网上找了一圈,说删除 package.json 里的 browserslist可以热更,截止我写这篇文章安装的依赖版本时不行,
最后在issues看到解决办法,按照官方给出的解释好像只会在[email protected]中修复这个问题,这里安装beta最新版本yarn add webpack-dev-server@lastest -D
,运行代码发现热更新成功。
安装webpack-dev-server 4.X版本后配置发生了很多改变,废弃了很多以前的配置
4.x配置点我点我或者看这里
在我们平时项目开发中,一般都会有:开发环境、测试环境、预发布环境和生产环境。现在来对 webpack 的配置文件进行环境拆分。
├─build
│ ├─webpack.base.conf.js //公共配置
│ ├─webpack.dev.conf.js //mode为development配置
│ ├─webpack.prod.conf.js //mode为production配置
│ ├─webpack.rules.conf.js //loader配置
使用dotenv
来按需加载不同的环境变量,VUE CLI3的环境变量也是使用的这个插件
dotenv
插件yarn add dotenv -D
webpack.base.conf.js
//...
const envMode = process.env.envMode
require('dotenv').config({ path: `.env.${envMode}` })
// 正则匹配以 VUE_APP_ 开头的 变量
const prefixRE = /^VUE_APP_/
let env = {}
// 只有 NODE_ENV,BASE_URL 和以 VUE_APP_ 开头的变量将通过 webpack.DefinePlugin 静态地嵌入到客户端侧的代码中
for (const key in process.env) {
if (key == 'NODE_ENV' || key == 'BASE_URL' || prefixRE.test(key)) {
env[key] = JSON.stringify(process.env[key])
}
}
plugins: [
//...
new webpack.DefinePlugin({ // 定义环境变量
'process.env': {
...env
}
})
]
package.json
"scripts": {
"dev": "cross-env envMode=dev webpack serve --config ./build/webpack.dev.conf.js --color",
"build": "cross-env envMode=prod webpack --config build/webpack.prod.conf.js --color",
"build:test": "cross-env envMode=test webpack --config build/webpack.prod.conf.js --color"
},
.env.dev
.env.test
.env.prod
配置完成后可以像VUE CLI3一样使用环境变量了点我点我
由于有些浏览器无法解析 ES6+ 等高级语法,故需要将其转化为浏览器能够解析的低版本语法
yarn add @babel/core @babel/preset-env babel-loader -D
yarn add core-js -S
loader配置
// webpack.rules.conf.js
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
}
},
]
Babel 配置文件
Babel的配置文件是Babel执行时默认会在当前目录寻找的文件,主要有.babelrc,.babelrc.js,babel.config.js和package.json。它们的配置项都是相同,作用也是一样的,只需要选择其中一种。
推荐使用后缀名是js配置文件,因为可以使用js做一些逻辑处理,适用性更强。
// babel.config.js
const presets = [
["@babel/preset-env", {
"useBuiltIns": 'usage', // 这里配置usage 会自动根据你使用的方法以及你配置的浏览器支持版本引入对于的方法。
"corejs": "3.11.0" // 指定 corejs 版本
}]
]
const plugins = [
]
module.exports = {
plugins,
presets
}
安装 html-webpack-plugin
插件处理 index.html 文件,此插件的功能是根据提供的模板文件,自动生成正确的项目入口文件,并把 webpack 打包的 js 文件自动插入其中
yarn add html-webpack-plugin -D
plugins配置
webpack.base.conf.js
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html'),
filename: 'index.html',
title: 'webpack5+vue3',
minify: {
html5: true, // 根据HTML5规范解析输入
collapseWhitespace: true, // 折叠空白区域
preserveLineBreaks: false,
minifyCSS: true, // 压缩文内css
minifyJS: true, // 压缩文内js
removeComments: false // 移除注释
},
files: prodMode ? cdn.prod : cdn.dev //CDN引入文件配置
}),
这里的index.html源文件放在../public/
文件夹里。
注意: 配置动态网页标题时,需将模板中的
标签里的内容改成 <%= htmlWebpackPlugin.options.title %>
CDN引入js
<% for (var i in
htmlWebpackPlugin.options.files&&htmlWebpackPlugin.options.files.js) { %>
<% } %>
yarn add style-loader css-loader -D
yarn add node-sass sass-loader -D
yarn add autoprefixer postcss-loader -D
loader配置
//webpack.rules.conf.js
{
test: /\.(css|scss|sass)$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader'
]
}
loader 从右到左(或从下到上)地取值(evaluate)/执行(execute)。在上面的示例中,从 sass-loader 开始执行,最后以 style-loader 为结束。
创建 import 或 require 的别名,来确保模块引入变得更简单
// webpack.base.conf.js
resolve: {
alias: {
"@": path.resolve(__dirname, "../src"),
assets: path.resolve(__dirname, '../src/assets/'),
img: path.resolve(__dirname, '../src/assets/img'),
utils: path.resolve(__dirname, '../src/utils'),
api: path.resolve(__dirname, '../src/api'),
},
},
Webpack5 之前我们处理静态资源比如PNG 图片、SVG 图标等等,需要用到url-loader,file-loader,raw-loader。Webpack5 提供了内置的静态资源构建能力,我们不需要安装额外的 loader,仅需要简单的配置就能实现静态资源的打包和分目录存放。这三个loader在github上也停止了更新。
webpack5使用四种新增的资源模块(Asset Modules)替代了这些loader的功能。
asset/resource 将资源分割为单独的文件,并导出url,就是之前的 file-loader的功能.
asset/inline 将资源导出为dataURL(url(data:))的形式,之前的 url-loader的功能.
asset/source 将资源导出为源码(source code). 之前的 raw-loader 功能.
asset 自动选择导出为单独文件或者 dataURL形式(默认为8KB). 之前有url-loader设置asset size limit 限制实现。
//webpack.rules.conf.js
{
test: /\.(png|jpg|svg|gif)$/,
type: 'asset/resource',
generator: {
// [ext]前面自带"."
filename: 'assets/[hash:8].[name][ext]',
},
}
更多配置点我点我
webpack5.20以下版本清除dist文件内容一般使用插件 clean-webpack-plugin, 5.20版本以后output新增特性clean,用于清除dist文件
//webpack.prod.conf.js
module.exports = {
//...
output: {
clean: true, // Clean the output directory before emit.
},
};
更多配置点我点我
yarn add copy-webpack-plugin -D
当某些文件不需要经过webpack打包处理而直接使用,这里我们可以使用 copy-webpack-plugin
这个插件,在构建的时候,将 public/ 的静态资源直接复制到 dist根目录下
//webpack.prod.conf.js
new copyWebpackPlugin({
patterns: [{
from: path.resolve(__dirname, "../public"),
to: './',
globOptions: {
dot: true,
gitignore: true,
ignore: ["**/index.html*"],
}
}]
}),
本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。
yarn add mini-css-extract-plugin -D
//webpack.prod.conf.js
plugins: [
//...
new miniCssExtractPlugin({
filename: './css/[name].[contenthash].css',
chunkFilename: './css/[id].[contenthash].css',
})
],
修改webpack.rules.conf.js
{
test: /\.(css|scss|sass)$/,
use: [
!prodMode ? 'style-loader'
: {
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../',
},
},
'css-loader',
'postcss-loader',
'sass-loader',
],
},
yarn add vue-loader@next @vue/compiler-sfc -D
yarn add vue@next -S
注意:
// webpack.rules.conf.js
rules: [
{
test: /\.vue$/,
use: [
'vue-loader'
]
}
]
//webpack.base.conf.js
const { VueLoaderPlugin } = require('vue-loader/dist/index');
plugins: [
new VueLoaderPlugin()
]
添加App.vue
{{title}}
修改main.js
import { createApp } from 'vue' // Vue 3.x 引入 vue 的形式
import App from './App.vue' // 引入 APP 页面组建
const app = createApp(App) // 通过 createApp 初始化 app
app.mount('#root') // 将页面挂载到 root 节点
跑一遍代码,运行成功。
####安装Vue全家桶
yarn add vue-router@4 vuex@next axios -S
yarn add postcss-loader postcss-px-to-viewport -D
新建postcss.config.js文件
'postcss-px-to-viewport': {
unitToConvert: 'px', // 需要转换的单位,默认为"px"
viewportWidth: 750, // 设计稿的视口宽度
unitPrecision: 5, // 单位转换后保留的精度
propList: ['*'], // 能转化为vw的属性列表
viewportUnit: 'vw', // 希望使用的视口单位
fontViewportUnit: 'vw', // 字体使用的视口单位
selectorBlackList: ['.ignore', '.hairlines', '.ig-'], // 需要忽略的CSS选择器
minPixelValue: 1, // 最小的转换数值,如果为1的话,只有大于1的值会被转换
mediaQuery: false, // 媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换,例如只转换 'src/mobile' 下的文件 (include: /\/src\/mobile\//)
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
landscapeUnit: 'vw', // 横屏时使用的单位
landscapeWidth: 568 // 横屏时使用的视口宽度
}
使用TerserWebpackPlugin来进行去除console.log
,压缩JS,webpack5之后自带最新的terser-webpack-plugin
,无需再重新安装原文点我点我
//webpack.prod.conf.js
minimizer: [
new TerserPlugin({
// 多进程
parallel: true,
//删除注释
extractComments: false,
terserOptions: {
compress: { // 生产环境去除console
drop_console: true,
drop_debugger: true,
},
},
})
],
Webpack5 内置 FileSystem Cache
能力加速二次构建,可以通过以下配置来实现
cache: {
type: 'filesystem',
// 可选配置
buildDependencies: {
config: [__filename], // 当构建依赖的config文件(通过 require 依赖)内容发生变化时,缓存失效
},
name: '', // 配置以name为隔离,创建不同的缓存文件,如生成PC或mobile不同的配置缓存
...,
},
配置好后第二次构建速度快的飞起。
注意事项:
cache 的属性 type 会在开发模式下被默认设置成 memory
,而且在生产模式中被禁用,所以如果想要在生产打包时使用缓存需要显式的设置。
为了防止缓存过于固定,导致更改构建配置无感知,依然使用旧的缓存,默认情况下,每次修改构建配置文件都会导致重新开始缓存。当然也可以自己主动设置 version
来控制缓存的更新。
yarn add vant@next -S
yarn add babel-plugin-import -D
// babel.config.js
const plugins = [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
修改postcss.config.js
const path = require('path');
module.exports = ({file}) => {
const designWidth = file.includes(path.join('node_modules', 'vant')) ? 375 : 750;
return {
plugins: {
'postcss-px-to-viewport': {
unitToConvert: 'px', // 需要转换的单位,默认为"px"
viewportWidth: designWidth, // 设计稿的视口宽度
unitPrecision: 5, // 单位转换后保留的精度
propList: ['*'], // 能转化为vw的属性列表
viewportUnit: 'vw', // 希望使用的视口单位
fontViewportUnit: 'vw', // 字体使用的视口单位
selectorBlackList: ['.ignore', '.hairlines', '.ig-'], // 需要忽略的CSS选择器
minPixelValue: 1, // 最小的转换数值,如果为1的话,只有大于1的值会被转换
mediaQuery: false, // 媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换,例如只转换 'src/mobile' 下的文件 (include: /\/src\/mobile\//)
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
landscapeUnit: 'vw', // 横屏时使用的单位
landscapeWidth: 568 // 横屏时使用的视口宽度
}
}
}
}
yarn add webpack-bundle-analyzer -D
用来分析哪些模块引入了哪些代码,进行有目的性的优化代码
在打包脚本后面加--analyze
如yarn build --analyze
,无需另外配置。新版本webpack-cli已经支持了。原文
不知道大家看完这篇文章,学废了吗。文章中若是有错误或者不准确的地方,欢迎大家指出讨论。