我正在参加「掘金·启航计划」
序
- 从最基础配置开始, 逐步理解 webpack 是如何工作的
- 可以直接用作 webpack 配置项的参考,按需在项目中添加
- 加深前端开发对工程化项目的认知
基础配置
要安装最新版本或特定版本,请运行以下命令之一:
npm install --save-dev webpack
npm install --save-dev webpack@<version>
如果你使用 webpack 4+ 版本,你还需要安装 CLI。
npm install --save-dev webpack-cli
默认的配置文件是当前目录下的webpack.config.js
const path = require('path')
module.exports = {
// 选择打包模式 development 或者 production 后者会对代码进行压缩编译
mode: 'development',
// 入口文件
entry: './src/main.js',
// 出口文件
output: {
filename: 'bundle.js', // 打包后的文件名字
path: path.resolve(__dirname, 'dist') // 储存路径 用nodejs path模块 转换成绝对路径传入
}
}
-
我们可以为 出口的js 设置hash 来防止总是覆盖 旧版js
module.exports = { ... output: { filename: 'bundle.[hash:8].js' // 打包后的文件名字 path: path.resolve(__dirname, 'dist') } }
-
在
package.json
中 可以简化 我们在终端输入的命令 并且 为这条命令绑定 一些参数 (–config 可以手动指定webpack的配置文件)"scripts": { "build": "webpack --config webpack.config.js" },
终端运行如下代码 等同于运行了
webpack --config webpack.config.js
npm run build
最初始的webpack只能帮助我们打包编译 js文件 我们可以为它配置更多的东西 让它变得很酷o(∩_∩)o
html 插件
webpack-dev-server
我们把 打包好的 js 放在 dist中 创建一个html 导入它 点击这个html在浏览器进行试调。然而我们更希望,把dist部署在一个 开发服务端上 让我们去请求 。
webpack内置了一个这样的开发服务 webpack-dev-server
,它的内部是通过express
实现的,它不会真实的打包文件,而是在内存中进行编译。
通过npm
安装
npm i webpack-dev-server -D
在 package.json
中配置
"scripts": {
"dev": "webpack-dev-server"
}
在终端运行
npm run dev
webpack-dev-server 默认将当前目录映射到了本机的8080服务器中,当然我们可以在webpack中配置它
module.exports = {
devServer: {
port: 3001, // 设置端口号
contentBase: path.join(__dirname, "dist"), // 指向的文件目录
progress: true, // 显示启动进度
open: false // 直接打开浏览器
},
...
}
html-webpack-plugin
webpack-dev-server 帮我们实现了把dist 映射到了开发服务器,但我们不希望自己手动去创建 dist、html ,手动去引入bundle.js,我们需要一个插件 html-webpack-plugin
来帮助我们
npm i html-webpack-plugin -D
在webpack中引入并配置它
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...
plugins: [ // 这里面存放了所有的webpack的插件
// 借助 html-webpack-plugin 自动在dist下 创建 html 并引入 bundle.js
new HtmlWebpackPlugin({
template: './src/index.html', // 需要的模版html
filename: 'index.html', // 编译之后文件名
minify:{ // 压缩 html
removeAttributeQuotes: true, // 删除 属性双引号
collapseWhitespace: true // 折叠 空行
},
hash: true // 在引用bundle.js时 添加 hash (解决缓存问题)
})
]
}
加载样式
在模版html中用link引入css,但是在打包到dist中的时候,不会动态的获取css的路径。但是我们可以在index.js
(入口js)中,把css当做模块引入 require('css文件路径')
,此时我们需要一个合适的 loader
去解析这个文件。
module.exports = {
...
module: { // 模块
rules: [ // 规则
{ test: /正则匹配/, use: 'loader' }
]
}
}
-
关于
loader
每个loader的功能单一
在use中传递一个数组,可使用多个loader
loader 默认的执行顺序是 从右到左,从下到上
如果需要为某个loader传入参数,写成对象形式
css
npm i style-loader css-loader -D
css-loader
负责解析 @ import 这种语法 和 路径
style-loader
把css插入到head标签中
rules: [
{ test: /\.css$/, use: ['style-loader','css-loader'] }
]
-
默认
style-loader
把css插入到了head标签的底部如果我们想要html中 书写的style标签不被覆盖 可以对
style-loader
设置参数,将css插入到顶部{ test: /\.css$/, use: [ { loader:'style-loader', options:{ insertAt: 'top' } }, 'css-loader' ] }
scss
npm i node-sass sass-loader -D
sass-loader
会调用 node-sass
编译scss文件
{
test: /\.scss$/,
use: [
{
loader:'style-loader',
options:{
insertAt: 'top'
}
},
'css-loader',
'sass-loader'
]
}
全局引入 scss 变量
借助sass-resources-loader
插件
npm install sass-resources-loader -D
webpack.config.js
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',·
'postcss-loader',
'sass-loader',
{
loader: 'sass-resources-loader',
options: {
resources: './src/style/common.scss' // 全局变量资源的路径
}
}
]
}
在vue-cli3.0中引入全局
vue.config.js
module.exports = {
...
css: {
loaderOptions: {
sass: {
data: `
@import "@/assets/css/common.scss";
`
}
}
}
}
less
npm i less less-loader -D
stylus
npm i stylus stylus-loader -D
图片处理
引入图片
引入图片的几种方式
- js创建图片
- css引入
- html img标签
js中引入
let image = new Image()
image.src = './images/test.jpg'
document.body.appendChild(image)
以 './images/test.jpg'
引入,webpack在打包的过程中会认为它只是一个普通的字符串。
需要建立路径与资源的关系,使用 import 或 require 引入
import test from './images/test.jpg'
let image = new Image()
image.src = test
document.body.appendChild(image)
css中引入
在css中同样需要建立路径与资源的关系,但 css-loader
默认帮我们做了处理。
所以只需要用原来的方式书写css即可
background: url('../images/test.jpg');
html中引入
在html中使用相对路径引入图片,在打包到dist文件夹后,路径就不有效了。
使用 html-withimg-loader
解析html,帮助编译图片
npm i html-withimg-loader -D
webpack.config.js
{
test: /\.html$/,
use: 'html-withimg-loader'
},
file-loader
需要 file-loader
帮助我们处理 将要用到的资源文件
npm i file-loader -D
webpack.config.js
{
test: /\.(png|jpg|gif)$/,
use: 'file-loader'
},
url-loader
url-loader可以将图片转为base64字符串,能更快的加载图片,一旦图片过大,
就需要使用file-loader的加载本地图片,故url-loader可以设置图片超过多少字节时,使用file-loader加载图片。
npm i url-loader -D
webpack.config.js
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
loader: 'url-loader',
options: {
limit: 200*1024,
outputPath: 'img/' // 输出目录
}
},
- url-loader依赖file-loader (两个都安装)
- 当使用url-loader加载图片,图片大小小于上限值,则将图片转base64字符串;否则使用file-loader加载图片,都是为了提高浏览器加载图片速度。
- 使用url-loader加载图片比file-loader更优秀,所以我们通常用 url-loader 的配置替换 file-loader的配置 使用(两者取一)
- 可以为图片资源设置输出目录
公共路径
webpack.config.js
在出口配置中添加publicPath
, 可以为所有的url添加上这个公共路径
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: 'http://www.test.cn/'
},
如果我们只需要在部分url上添加,则可以单独为相应loader添加配置
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
loader: 'url-loader',
options: {
limit: 200*1024,
outputPath: 'img/', // 输出目录
publicPath: 'http://www.test.cn/' // 单独为图片资源添加公共路径
}
},
抽离样式
通过模块化的方式加载样式,在loader的处理下,最终会以style标签的形式写入到html的head中。随着样式增多,可能会导致堵塞,因此我们更希望用 link 的方式引入它们。
mini-css-extract-plugin
npm i mini-css-extract-plugin -D
mini-css-extract-plugin
可以将编译好的css,抽离到一个指定的文件。
webpack中的配置
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
plugins: [ // 这里面存放了所有的webpack的插件
new MiniCssExtractPlugin({ // 配置抽离css插件的相关属性
filename: 'style/main.css' // 生成的文件路径和文件名
})
],
module: { // 模块
rules: [ // 规则
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,// 替换 style-loader
'css-loader'
]
}
]
}
}
-
如果我们想要生成多个css文件,不妨引入 多个
mini-css-extract-plugin
分别进行配置const MiniCssExtractPlugin1 = require('mini-css-extract-plugin') const MiniCssExtractPlugin2 = require('mini-css-extract-plugin')
autoprefixer
什么是浏览器前缀(www.fly63.com/article/det…
为了兼容低版本浏览器,我们要为css3的属性添加不同浏览器的前缀。但是,我们不希望自己手动的去添加,可以使用autoprefixer
插件自动添加,让我们在编程的过程中忘记浏览器前缀这回事。在webpack中,如果你想使用它还需要借助相应的loader postcss-loader
将它调用。
npm i postcss-loader autoprefixer -D
postcss-loader
需要一个postcss.config.js
的配置文件,可以将我们想要使用的 autoprefixer
插件 声明在这个配置里,以便loader去调用。
postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')
]
}
webpack.config.js
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader', // 在css-loader之前处理 postcss-loader
'sass-loader'
]
}
压缩样式
webpack 可以在production
模式下,默认启动js的压缩,但对css文件无能为力。如果我们需要将css压缩。可以为webpack设置优化项。
npm i optimize-css-assets-webpack-plugin terser-webpack-plugin -D
webpack.config.js
const TerserJSPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
optimization: {
minimizer: [ new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({}) ],
},
...
}
- 设置
optimization
时,webpack默认启动的js压缩会被取消,需要手动的重新配置terser-webpack-plugin
js的压缩插件
抽离后图片路径问题
在引入MiniCssExtractPlugin.loader时使用对象方式,并在options目录下添加 publicPath: ‘../’ 配置,这样就只会在css文件中引入的资源中添加 “../”,就不会影响其他文件中的路径
{
test: /\.scss$/,
use: [
{
loader:MiniCssExtractPlugin.loader,
options: {
publicPath: '../'
}
},
'css-loader',
'postcss-loader',
'sass-loader'
]
}
babel7
如果想要对js进行处理,将 ECMAScript 2015+ 版本的代码向后兼容,以便能够运行在当前和旧版本的浏览器或其他环境中。可以使用 babel
,同样我们需要相应loader去加载它。
基础配置
npm i babel-loader @babel/core @babel/preset-env -D
babel-loader
加载器
@babel/core
babel 核心模块
@babel/preset-env
babel 转化语法模块 es6 -> es5
webpack.config.js
{
test: /\.js$/,
exclude: /node_modules/,
include: path.resolve(__dirname, './src'),
use: [
{
loader: 'babel-loader',
options: {
presets: [ // 预设
'@babel/preset-env'
]
}
}
]
}
更高级的语法
想要兼容更高级的语法,需要额外的添加一些babel的插件,并配置
class
npm i @babel/plugin-proposal-class-properties -D
webpack.config.js
use: [
{
loader: 'babel-loader',
options: {
presets: [ // 预设
'@babel/preset-env'
],
plugins: [ // 插件
'@babel/plugin-proposal-class-properties'
]
}
}
]
装饰器(decorators)
介绍(zhuanlan.zhihu.com/p/20139834)
npm i @babel/plugin-proposal-decorators -D
webpack.config.js
options: {
...
plugins: [ // 插件
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }]
]
}
@babel/plugin-transform-runtime
一个插件,可以重复使用Babel注入的帮助程序代码来节省代码。
它在运行时取决于我们部署的代码,所以还要为他安装生生产依赖 @babel/runtime
.
npm i @babel/plugin-transform-runtime -D
npm i @babel/runtime -S
webpack.config.js
options: {
...
plugins: [ // 插件
...
'@babel/plugin-transform-runtime'
]
}
@babel/polyfill
填充工具,模拟完整的ES2015 +环境。有了这个模块,可以使用新的内置函数(如Promise或WeakMap),静态方法(如Array.from或Object.assign),实例方法(如Array.prototype.includes)和生成器函数(假设使用的是再生器插件)
npm i @babel/polyfill -S
在需要使用的 js 文件中
import "@babel/polyfill"
.babelrc
也可以把 options 的配置 直接写在 .babelrc
文件中, babel-loader 会去这个文件中读取配置。
{
"presets": [ // 预设
"@babel/preset-env"
],
"plugins": [ // 插件
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }],
"@babel/plugin-transform-runtime"
]
}
ts-loader
搭建 typescript 的运行环境
npm install ts-loader typescript --save-dev
webpack.config.js
module.exports = {
...
resolve: { // 添加解析 后缀
extensions: [".ts", ".js"]
},
module: {
rules: [ // 处理 ts 或 tsx 文件
{ test: /\.tsx?$/, loader: "ts-loader" }
]
}
}
新建 tsconfig.json 可以为解析运行时添加相应配置
{
"compilerOptions": {
"sourceMap": true // 开启试调 映射
},
"include": [ // 解析目录
"src/**/*"
],
"exclude": [ // 排除目录
"node_modules",
"**/*.spec.ts"
]
}
vue-loader
npm i vue-loader vue-template-compiler -D
Vue Loader 的配置和其它的 loader 不太一样。除了通过一条规则将 vue-loader
应用到所有扩展名为 .vue
的文件上之外,要确保在 webpack 配置中添加 Vue Loader 的插件:
webpack.base.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
module: {
rules: [
// ... 其它规则
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin()
]
}
VueLoaderPlugin
将你定义过的其它规则复制并应用到.vue
文件里相应语言的块
代码校验(eslint)
如需在 webpack 中 使用eslint ,需要加载器eslint-loader
npm i eslint eslint-loader -D
在 eslint.cn/demo/ 选择相应规则,并下载json,将文件名改为 .eslintrc.json
放入开发根目录
webpack.config.js
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'eslint-loader',
options: {
enforce: 'pre' // 将 eslint-loader 提前执行
}
}
]
},
loader的类型
- pre 提前执行的loader
- post 后置的loader
- normal 普通的loader
- 内联loader
插件全局引入
为了解决一些插件不支持commonJs引入的问题(如:bootstrap.js,它只允许jQuery暴露为全局变量才可用)
全局暴露(expose-loader)
npm i expose-loader -S
该加载器可以将模块添加到全局对象,以内联loader使用
**入口 js 直接使用 **
import $ from 'expose-loader?$!jquery'
或在webpack.config.js中配置
webpack.config.js
{
test: require.resolve('jquery'),
use: 'expose-loader?$'
},
入口js
import $ from 'jquery'
将变量注入每个模块
webpack
内置了一个 webpack.ProvidePlugin
插件,可以为每一个模块都提供需要的插件
webpack.config.js
const webpack = require('webpack')
module.exports = {
...
plugins: [
...
new webpack.ProvidePlugin({
$: 'jquery' // 将 jquery 提供给 $
})
]
}
- 配置完成后则无需在各个模块中再次引入
- 只是为每个模块注入了变量,并没有挂载到全局上,window.$ 是 undefined
在html中引入,但不打包
index.html
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
webpack.config.js
如果我们想外部引用一个库,但是又不想让webpack打包,并且又不影响我们在程序中以CMD、AMD或者window/global全局等方式进行使用,那就可以通过配置externals。
module.exports = {
...
externals: {
jquery: '$'
},
...
}
则我们在模块中 使用 import $ from 'jquery'
时,webpack会忽略这条语句的打包处理
多入口
多入口配置
多个入口js
// 入口文件
entry: {
index: './src/index.js',
other: './src/other.js'
},
// 出口文件
output: {
filename: '[name].js', // 打包后的文件名字 [name]相当于变量储存了 入口文件的键名 index other
path: path.resolve(__dirname, 'dist') // 储存路径
},
多个 html,创建多个 HtmlWebpackPlugin
并配置
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...
plugins: [ // 这里面存放了所有的webpack的插件
// 借助 html-webpack-plugin 自动在dist下 创建 html 并引入 指定js
new HtmlWebpackPlugin({
template: './src/index.html', // 需要的模版html
filename: 'index.html', // 编译之后文件名
chunks: ['index'], // 需要引入的js块 可同时引入多个
minify:{ // 压缩 html
removeAttributeQuotes: true, // 删除 属性双引号
collapseWhitespace: true // 折叠 空行
},
hash: true // 在引用js时 添加 hash (解决缓存问题)
}),
// 多个 html
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'other.html',
chunks: ['other'],
minify:{
removeAttributeQuotes: true,
collapseWhitespace: true
},
hash: true
})
]
}
抽离公共模块
当多入口同时引用一个模块的时候,我们希望这个模块在引用的时候被缓存,避免重复加载
配置优化项
webpack.config.js
module.export = {
optimization: {
...
splitChunks: { // 分割 代码块
cacheGroups: { // 缓存组
common: { // 公共模块
chunks: 'initial' // 从初始模块 开始
minSize: 0, // 超过 0 字节的
minChunk: 2 // 使用 超过 2次的
}
}
}
}
...
}
单独抽离第三方模块
webpack.config.js
module.export = {
optimization: {
...
splitChunks: { // 分割 代码块
cacheGroups: { // 缓存组
...
vender: { // 第三方模块
priority: 1, // 权重 优先在 模块抽离前抽离
test: /node_modules/, // 在 node_modules 目录下
chunks: 'initial'
minSize: 0,
minChunks: 2
}
}
}
}
...
}
调试(source-map)
文件压缩打包之后,在浏览器中出现错误,却无法定位bug出现的具体位置。
我们需要的将源码映射出来,以供试调。
webpack.config.js
module.exports = {
...
devtool: 'source-map',
...
}
source-map
会生成一个 map 文件,大而全,出错时会标识,显示行和列
此外,也可以使用:
eval-source-map
不会生成单独的文件,但是可以显示行和列cheap-module-source-map
会生成单独的文件,但不显示列 (没有调试功能,但是可以保留)cheap-module-eval-source-map
不会生成单独的文件,也不显示列
实时打包
如果我们需要实时查看打包之后的文件,可以为 webpack 配置 watch 监控 及 watchOptions 监控选项,
实时打包
webpack.config.js
module.exports = {
...
watch: true,
watchOptions: {
poll: 1000, // 1s 轮询
aggregateTimeout: 600, // 防抖
ignored: /node_modules/ // 忽略文件
},
...
}
其他小插件
clean-webpack-plugin
在每一次打包之前帮我们清空出口文件夹
npm i clean-webpack-plugin -D
webpack.config.js
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
...
plugins: [
...
new CleanWebpackPlugin()
],
...
}
copy-webpack-plugin
直接拷贝文件到出口文件夹
npm i copy-webpack-plugin -D
webpack.config.js
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
...
plugins: [
...
new CopyWebpackPlugin([
{
from: 'doc', // 从 根目录下的什么文文件
to: 'doc' // 到 入口文件夹 的哪里
}
])
],
...
}
webpack.BannerPlugin
webpack内置插件,在打包好的 js 文件开头标注信息, 通常标注作者和版权信息
webpack.config.js
const webpack = require('webpack')
module.exports = {
...
plugins: [
...
new webpack.BannerPlugin('2019 by cxsl')
],
...
}
处理跨域
ajax
js
// 创建对象
let xhr = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")
// 初始化一个请求
xhr.open('GET', '/api/user', true)
// 事件触发
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.response)
}
}
// 发送 HTTP 请求
xhr.send()
配置代理
在xhr初始化一个请求,存在请求的url域名、端口号不一样时,会产生跨域报错。为解决这个问题,可以使用代理的方式,原理是将请求进行转发。可以通过 http-proxy
这个模块实现。
WebPack中devServer的proxy代理其实是集成了http-proxy-middleware,可以直接配置。
webpack.config.js
module.exports = {
...
devServer: {
proxy: {
'/api':'http://localhost:3000'
}
}
...
}
更多配置
proxy: {
'/api': {
target: 'https://www.runoob.com',
secure: false,
changeOrigin: true,
pathRewrite: { '/api': '' }
}
}
模拟数据
如果我们前端只是想单纯的模拟数据,devServer本身就是通过 express 实现的,可以直接在配置中模拟服务端响应
webpack.config.js
module.exports = {
...
devServer: {
...
proxy: {
'/api':'http://localhost:3000'
}
}
...
}
服务端启动 webpack
自己有服务端,可以将前端和服务端启动在一起
需要express中间件webpack-dev-middleware
webpack提供
npm i webpack-dev-middleware -D
server.js
const express = require('express')
const app = express()
const webpack = require('webpack') // 引入webpack模块
const webpackDevMiddleware = require('webpack-dev-middleware') // 引入webpackdev中间件
const config = require('./webpack.config') // 引入配置文件
let compiler = webpack(config) // 处理配置文件,返回结果
app.use(webpackDevMiddleware(compiler)) // 使用中间件webpackDevMiddleware 传入配置
app.get('/user', (req, res) => res.json({
message: 'hello world'
}))
app.listen(3000, () => console.log('Example app listening on port 3000!'))
第三方包解析配置(resolve)
nodejs提供了默认的第三方包查找规则,webpack 配置 resolve 可以覆盖一些解析规则。
别名
webpack.config.js
module.exports = {
...
resolve: {
modules: [ path.resolve('node_modules') ], // 指明第三方包路径,可以多个
alias:{ // 别名
// 在js中 import 'bootstrap' 会根据配置路径查找文件
bootstrap: 'bootstrap/dist/css/bootstrap.css'
}
}
...
}
主入口
webpack.config.js
module.exports = {
...
resolve: {
modules: [ path.resolve('node_modules') ], // 指明第三方包路径,可以多个
// 主入口字段 在第三方的 package.json 中先找 'style' 指向 后找 'main'
mainFields: [ 'style', 'main' ]
// 主入口文件
// mainFiles: []
}
...
}
添加扩展名
webpack.config.js
module.exports = {
...
resolve: {
modules: [ path.resolve('node_modules') ], // 指明第三方包路径,可以多个
// 主入口字段 在第三方的 package.json 中先找 'style' 指向 后找 'main'
extensions: [ '.js','.scss','css','.vue' ] // 在引入模块时 从左到右依次尝试添加扩展名
}
...
}
生产与开发环境分离
webpack.DefinePlugin
定义全局变量
webpack.config.js
plugins: [ // 这里面存放了所有的webpack的插件
new webpack.DefinePlugin({
DEV: JSON.stringify('dev') // 定义全局变量 DEV为 字符串dev
}),
...
]
则可以根据这个变量判断环境
js
if (DEV === 'dev') {
url = 'http://www.dev.com'
} else {
url = 'http://www.prod.com'
}
分离配置文件
生产相关配置写入 webpack.prod.js
开发相关配置写入 webpack.dev.js
公共部分写入 webpack.base.js
通过webpack-merge
合并
npm i webpack-merge -D
webpack.dev.js
const { smart } = require('webpack-merge')
const base = require('./webpack.base.js')
const webpack = require('webpack')
...
module.exports = smart(base,{
mode: 'development',
...
plugins: [ // 这里面存放了所有的webpack的插件
new webpack.DefinePlugin({
DEV: JSON.stringify('dev') // 定义全局变量 DEV为 字符串dev
}),
...
]
})
webpack.prod.js
const { smart } = require('webpack-merge')
const base = require('./webpack.base.js')
const webpack = require('webpack')
...
module.exports = smart(base,{
mode: 'production',
...
plugins: [ // 这里面存放了所有的webpack的插件
new webpack.DefinePlugin({
DEV: JSON.stringify('prod') // 定义全局变量 DEV为 字符串dev
}),
...
]
})
页面自动更新
启用 devServer hot
当hot 为true 会开启强制刷新,当文件保存时,会强制刷新页面
webpack.dev.js
devServer: {
hot: true,
...
}
配置热更新
在开启 hot 的基础上,我们还需要webpack.NamedModulesPlugin
和 webpack.HotModuleReplacementPlugin()
的辅助
plugins: [
...
new webpack.NamedModulesPlugin(), // 打印更新的文件路径
new webpack.HotModuleReplacementPlugin() // 热更新插件
]
样式表
借助于 style-loader
的帮助,CSS 的模块热替换实际上是相当简单的。当更新 CSS 依赖模块时,此 loader 在后台使用 module.hot.accept
来修补(patch) <style>
标签。
模块
打包优化项
noParse
这是module中的一个属性,作用:
不去解析属性值代表的库的依赖
webpack解析引入的第三方包的时候,会去解析它是否还存在其他依赖,如果我们明确引入的包,不再有其他依赖,例如,jquery。
可以在webpack的配置中增加noParse属性,来提高解析效率
webpack.base.js
...
module:{
noParse:/jquery/,// 不去解析jquery中的依赖库
...
}
webpack.IgnorePlugin
例如 moment.js
引入了完整的语言包, webpack.IgnorePlugin
可以忽略掉某些引入的内容。
webpack.base.js
const webpack = require('webpack')
...
module.exports = {
...
plugins: [
// 忽略 moment 中的 ./locale 引入
new webpack.IgnorePlugin(/\.\/locale/, /moment/),
...
]
}
则我们需要手动引用相应的语言包
js
import moment from 'moment'
import 'moment/locale/zh-cn'
动态链接库
一些较大的模块,每次打包都要重新编译。我们可以单独的先将这些模块打包完成,再作引入。
新建webpack 配置
webpack.vue.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode: 'development',
// 入口文件
entry: {
vue: ['vue','vue-router']
},
// 出口文件
output: {
filename: '_dll_[name].js', // 打包后的文件名字 [name]相当于变量储存了 入口文件的键名 index other
path: path.resolve(__dirname, 'dist'), // 储存路径
library: '_dll_[name]', // 将结果赋值给变量
libraryTarget: 'var' // 赋值形式 默认为 var
},
plugins: [
// 使用插件生成任务清单
new webpack.DllPlugin({ // name == library
name: '_dll_[name]',
// 需要保存到一个json文件中
path: path.resolve(__dirname,'dist','manifest.json')
})
]
}
在html中引入打包好的模块
index.html
<script src="/_dll_vue.js"></script>
告诉webpack主配置,当我们引入这些模块的时候先去任务清单(动态链接库)上查找
webpack.base.js
plugins: [
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname,'dist','manifest.json')
}),
...
]
Happypack
多线程打包
npm i happypack -D
webpack.base.js
const Happypack = require('happypack')
...
module.exports = {
...
module: {
rules:[
{
test: /\.js$/,
exclude: /node_modules/,
include: path.resolve(__dirname, './src'),
use: 'Happypack/loader?id=js' // js 文件开启多线程打包
}
]
},
plugins: [
new Happypack({
id: 'js',
use: ['babel-loader'] // 原有配置 转移至此处
}),
...
]
}
懒加载
有的时候,我们不需要在一开始的时候就加载资源,而是通过需求,在特定的时候加载。
js
loadA () {
// es6 草案中的语法 底层是通过jsonp实现动态加载文件
// 引入的资源会被编译成 一个 promise
import('./a.js').then( (data) => {
// 原始资源 被放在 data.default 中
console.log(data.default)
})
}
需要借助 @babel/plugin-syntax-dynamic-import
插件
npm i @babel/plugin-syntax-dynamic-import -D
.babelrc
"plugins": [ // 插件
...
"@babel/plugin-syntax-dynamic-import"
]