前言
这一篇文章主要是续着上篇的 ——「webpack4 从零搭建 vue 项目开发环境」。强烈推荐先看上一篇文章 。
文章主要说一下不同环境的区分和一些可以优化的点
文章 demo 已经提交到 github , 请自取 ———— 「webpack 4 demo」
区分「开发环境」和「生产环境」
- 开发环境:
热更新
、多线程
、sourceMap
等等 - 生产环境:
js压缩
、提取css
、css压缩
、多线程
、sourceMap
、目录清理
等等
由于「开发环境」和「生产环境」的需求不同,所以我们要区分不同环境来构建我们的项目。
配置 webpack.common.js
、webpack.dev.js
和 webpack.prod.js
新建 build
目录,并新建下面三个文件
webpack.common.js
公用配置webpack.dev.js
开发环境配置webpack.prod.js
生成环境配置
安装 webpack-merge
用于合并 webpack
公用配置到指定环境
npm i webpack-merge@5.8.0 -D
配置 webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
entry: path.join(__dirname, "../src/main.js"),
output: {
path: path.join(__dirname, "../dist"),
filename: "bundle.js"
},
module: {
rules: [
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192, //小于 8K ,用 url-loader 转成 base64 ,否则使用 file-loader 来处理文件
fallback: {
loader: 'file-loader',
options: {
name: '[name].[hash:8].[ext]',
outputPath: '../dist/images/', //打包之后文件存放的路径, dist/images
}
},
}
}
]
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
fallback: {
loader: 'file-loader',
options: {
name: '[name].[hash:8].[ext]',
outputPath: '../dist/media/',
}
},
}
}
]
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 1,
fallback: {
loader: 'file-loader',
options: {
name: '[name].[hash:8].[ext]',
outputPath: '../dist/fonts/',
}
},
}
}
]
},
{
test: /\.(css)$/,
use: [
"style-loader",
"css-loader",
"postcss-loader"
]
},
{
test: /\.(less)$/,
use: [
"style-loader",
"css-loader",
"postcss-loader",
"less-loader",
]
},
{
test: /\.vue$/,
use: ["vue-loader"]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, '../src/index.html')
}),
new VueLoaderPlugin()
]
};
配置 webpack.dev.js
const webpack = require("webpack");
const { merge } = require('webpack-merge')
const webpackCommon = require('./webpack.common.js')
module.exports = merge(webpackCommon, {
mode: "development",
devServer: {
contentBase: path.join(__dirname, "../dist"),
compress: true,
open: true,
quiet: false,
hot: true, //开启热更新
port: 3000,
clientLogLevel: 'none', //关闭浏览器控制台输出的热更新信息
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
});
配置 webpack.prod.js
const { merge } = require('webpack-merge')
const webpackCommon = require('./webpack.common.js')
module.exports = merge(webpackCommon, {
mode: "production"
})
修改 package.json
npm 脚本
指定 webpack
不同环境执行不同的 配置文件
。
"scripts": {
"serve": "webpack-dev-server --config build/webpack.dev.js", //运行环境
"build": "webpack --config build/webpack.prod.js",//开发环境
},
其实也可以通过 --mode
来设置 process.env.NODE_ENV
,并判断是「开发环境」还是「生产环境」,执行不同的配置文件内容。这里就不演示了。网上也很多例子,感兴趣自己去搜一下啊
CleanWebpackPlugin 自动删除 dist 上次打包的内容
npm i clean-webpack-plugin -D
// webpack.prod.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
plugins: [
new CleanWebpackPlugin()
],
DefinePlugin 设置环境全局变量
DefinePlugin
是 webpack
内置的插件,不需要安装
配置 webpack.dev.js
和 webpack.prod.js
这里只演示 webpack.dev.js
,webpack.prod.js
设置也是一样的。
plugins: [
new webpack.DefinePlugin({
'process.env': {
//设置全局变量可以在整个项目中调用
BASE_URL: JSON.stringify('http://localhost:3000/dev')
}
})
]
调用全局变量
console.log('BASE_URL:', process.env.BASE_URL)
设置 sourceMap
便于调试
webpack
打包过的 bundle.js
都是压缩过的。没法正常调试。而sourceMap
就是用来帮助我们调试。如果你之前用过 vue-cli
,打包的时候 ,会在 dist
目录下生存后缀为 .map
的文件,这个就是源码的映射文件。
我们可以通过设置devtool
来生成不同类型的 sourceMap
。不同模式生成的代码体积、构建速度、安全性等等都有差异,可以参考「webpack
官网」。
个人推荐,开发环境推荐使用带eval
字样的生成模式,速度会比较快。至于是cheap
和module
根据自己的需要选择就好。生产环境,为了安全性,建议使用把sourceMap
放在本地
或者内网
,可使用SourceMapDevToolPlugin
插件来配置。
- 开发环境
devtool: "cheap-module-eval-source-map",
- 生产环境
个人测试了一下,发现SourceMapDevToolPlugin
和devtool
是存在冲突的,使用SourceMapDevToolPlugin
来配置时需要设置 devtool: false
。
webpack
默认情况下会在bundle.js
最后面追加一行 //# sourceMappingURL=源代码对应的 sourcemaps 文件的路径
的注释,使用浏览器调试的时候(F12),就会自动去查找这个路径的 sourceMap
。我们可以在 append
里自定义这个路径,支持 url
和 本地地址
// webpack.prod.js
devtool: false,
plugins: [
// 默认情况下生成的模式是 `source-map`
new webpack.SourceMapDevToolPlugin({
//sourcemap 内网地址
append: '\n//# sourceMappingURL=http://localhost:1888/sourcemaps/[name].[hash:8].map',
// 生成 sourcemap 的存放路径和名称
filename: 'sourcemaps/[name].[hash:8].map',
// module:true // 相当于 devtool 的 module
// columns:false //相当于 devtool 的 cheap
// noSources:true //相当于 devtool 的 nosources
})
],
这里顺便提一下,
vue-cli「生产环境」默认生成为:
devtool:source-map 也就是生成的
map文件包含了未压缩的源代码,个人觉得不太安全!如果项目比较打的时候构建速度也会特别慢
如果你是第一次接触 sourceMap
相信看完也是懵逼的,可以看一下下面的文章,应该对你有帮助
sourceMap
推荐阅读文章:
优化 (前言)
一个项目是否需要优化,要根据自己的项目情况来决定的。比如某些优化只在大项目时候才会体现出它的优势,小项目使用反而会适得其反。如果你感觉自己的项目构建速度和项目体积都可以接受,那也没有必要去优化。
下面是一些可以优化的点,但不要都一股脑的加上。根据自己项目情况来尝试和选择,查看效果
优化前准备工作
speed-measure-webpack-plugin
用于查看 loader
和 插件
的处理时间
webpack-bundle-analyzer
用于查看打包后包的体积大小。
减少文件查找范围
指定 loader 处理范围 ———— exclude/include
exclude
是排除不在某个目录下查找
include
是限定在哪个目录下查找
同时配置的话,exclude
的优先级高于 include
,推荐只使用include
。
// webpack.commom.js
{
test: /\.js$/,
// exclude: /node_modules/,
include: path.join(__dirname, "../src"),
use: ["babel-loader"]
},
resolve.alias 别名配置
用别名来代替,编写更简洁,也可以起到减少查找范围的作用
// webpack.commom.js
resolve: {
alias: {
"@": path.join(__dirname, "../src")
}
},
resolve.extensions 指定文件后缀匹配规则
webpack
默认配置为 extensions: ['.js', '.json']
,假设文件导入的时候没用写后缀名,会根据这个规则来匹配。
例如:import router from "@/router/index"
, 先找js
,再找 json
。
extensions
可以修改匹配的顺序,可以跳转它的优先级,不常用的文件不建议写进入
// webpack.commom.js
resolve: {
extensions: [".js", ".vue",".json"]
// extensions:[".ts",".js", ".vue",".json"] 如果你的项目中有使用到 `typescript` 推荐把 `.ts` 放在前面
}
缓存
cache-loader
在一些开销比较大的loader
前面使用cache-loader
,开销比较小的 loader
不需要使用,用了反而会慢。它会把结果缓存在磁盘上,node_modueles/.cache/cache-loader
默认存放路径。第一次构建的时间时间会比较长,后面再次构建就会比较快了
npm install cache-loader -D
// webpack.commom.js
{
test: /\.js$/,
include: path.join(__dirname, "../src"),
use: ["cache-loader", "babel-loader"]
}
babel-loader
其实 babel-loader
本身也具备缓存的功能。 我听说 cache-loader
会更快一点。我自己试了一下,差距不大,cache-loader
稍微快一点点。
// webpack.commom.js
{
test: /\.js$/,
include: path.join(__dirname, "../src"),
use: [{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}]
},
hard-source-webpack-plugin
HardSourceWebpackPlugin
为模块提供中间缓存, 第一次构建,花费正常时间。第二个构建速度快60% ~ 90%
。不推荐 DllPlugin
和 DllReferencePlugin
一起使用
注意:开发环境,使用 HardSourceWebpackPlugin 时,不要使用 speed-measure-webpack-plugin 会报错
npm install hard-source-webpack-plugin -D
// webpack.commom.js
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
plugins: [
// 为模块提供中间缓存,不能和 SpeedMeasurePlugin 插件同时使用
new HardSourceWebpackPlugin(),
// 排除缓存 MiniCssExtractPlugin 插件 ,不然会报错
new HardSourceWebpackPlugin.ExcludeModulePlugin([
{
test: /mini-css-extract-plugin[\\/]dist[\\/]loader/,
},
]),
]
多线程
大家知道 JavaScript
是单线程的,我们可以使用 web worker
来现实多线程执行JavaScript
。如果你没听过 web worker
推荐看一下这篇文章——「Web Worker 使用教程」
而在 webpack
打包构建项目的时候,我们也可以使用 worker
来实现。有两个基于worker
实现的插件 thread-loader
和 happypack
,happypack
貌似被作者抛弃了,推荐使用thread-loader
来现实。
请只在耗时比较长的 loader 上使用!
npm install thread-loader -D
// webpack.commom.js
{
test: /\.js$/,
include: path.join(__dirname, "../src"),
use: ["thread-loader", "cache-loader", "babel-loader"]
// 项目大,loader 花费时间长时用
}
压缩
JS压缩,删除注释和 console
—— terser-webpack-plugin
webpack 4
需要安装,webpack 5
默认支持
npm i terser-webpack-plugin@4 -D
// webpack.prod.js
const TerserPlugin = require('terser-webpack-plugin');
optimization: {
minimizer: [
// 压缩 JS
new TerserPlugin({
parallel: true, //开启多线程
// include: path.join(__dirname, "../src"),
sourceMap: true, // 如果使用了 SourceMapDevToolPlugin ,需要设置为 true
extractComments: false, // 默认打包会生成 LICENSE.txt 文件,这里设置禁止生成
terserOptions: {
output: {
comments: false, //删除注释
},
compress: {
drop_console: true //删除 console
// drop_debugger: false //默认为 true, 会删除 debugger
},
},
}),
],
},
css 提取
如果打包生成的JS
太大,提取css
有利于减少JS
的体积以及页面的快速渲染(link 引入样式不会堵塞DOM渲染和JS渲染),
npm i mini-css-extract-plugin@1.6.2 -D
// webpack.prod.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module: {
rules: [
{
test: /\.(css)$/,
include: path.join(__dirname, "../src"),
use: [
MiniCssExtractPlugin.loader,
// "thread-loader", // 项目大,loader 花费时间长时用
"css-loader",
"postcss-loader"
]
},
{
test: /\.(less)$/,
include: path.join(__dirname, "../src"),
use: [
MiniCssExtractPlugin.loader,
// "thread-loader", // 项目大,loader 花费时间长时用
"css-loader",
"postcss-loader",
"less-loader",
]
},
]
},
// 提取 css
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[hash:8].css'
}),
]
css 压缩
目的就是减少项目体积
npm i optimize-css-assets-webpack-plugin -D
// webpack.prod.js
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
plugins: [
// 压缩 css
new OptimizeCssAssetsPlugin()
]
减少打包文件
Tree Shaking
Tree Shaking
用于剔除一些引入了却没有使用的代码。
//package.json
// 需要排除 「.css」和「.vue」文件,样式才能生效
"sideEffects": [
"*.css",
"*.vue"
],
externals
打包的时候把这些 externals
配置的包剔除掉,减少体积。然后使用 CDN 的方式引入这些包。
// webpack.prod.js
externals: {
"vue": "Vue",
"vue-router":"VueRouter"
}
<!-- index.html -->
<!-- cdn 引入 externals 排除的依赖 -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.runtime.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.5.2/vue-router.min.js"></script>
依赖版本
由于最新版本是 webpack5
部分插件也会同步更新,插件的最新版本在webpack 4
上不适用,可以参考下面依赖版本进行安装!
"devDependencies": {
"@babel/core": "^7.4.4",
"@babel/preset-env": "^7.14.8",
"autoprefixer": "^10.3.1",
"babel-loader": "^8.2.2",
"cache-loader": "^4.1.0",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^5.2.7",
"file-loader": "^6.2.0",
"hard-source-webpack-plugin": "^0.13.1",
"html-webpack-plugin": "^4.5.2",
"less": "^3.5.0",
"less-loader": "^7.3.0",
"mini-css-extract-plugin": "^1.6.2",
"optimize-css-assets-webpack-plugin": "^6.0.1",
"postcss-loader": "^4.3.0",
"speed-measure-webpack-plugin": "^1.5.0",
"style-loader": "^2.0.0",
"terser-webpack-plugin": "^4.2.3",
"thread-loader": "^3.0.4",
"url-loader": "^4.1.1",
"vue-loader": "^15.9.6",
"vue-template-compiler": "^2.6.14",
"webpack": "^4.46.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.2",
"webpack-merge": "^5.8.0"
},
文章 demo 已经提交到 github , 请自取 ———— 「webpack 4 demo」
觉得还不错记得 「点个赞」!谢谢