webpack 实践记录(v4.26.0)
- 初始化
npm init
- 安装webpack
npm install --save-dev webpack webpack-cli
- 安装本地开发环境
webpack-dev-server
npm install --save-dev webpack-dev-server
- 建立目录结构
zero
|-package.json
|-/js
|-/common
|-common.js
|-login.js
|-/imgs
|-mobile.png
|-captcha.png
|-username.png
|-317x70.png
|-/less
|-common.less
|-login.less
|-/pages
|-login.html
|-node_modules
|-webpack.config.js
复制代码
- 配置
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode:'development',
entry:{
login:'./js/login.js'
},
output:{
path:path.resolve(__dirname,'dist'),
filename:'js/[name].bundle.js'
},
devServer:{
contentBase:path.resolve(__dirname,'dist'),
open:true,
openPage:'pages',
index:'login.html'
},
module:{
rules:[{
test:/\.js$/,
exclude:/node_modules/,
use:[{
loader:'babel-loader',
options:{
presets:['@babel/preset-env'],
plugins:['@babel/plugin-syntax-dynamic-import',['@babel/plugin-transform-runtime',{
corejs:2
}]]
}
}]
},{
test:/\.html/,
use:[{
loader:'html-loader',
options:{
attrs:[':src']
}
}]
},{
test:/\.less$/,
use:['style-loader','css-loader','less-loader']
},{
test:/\.(png|jpg|svg|gif)$/,
use:[{
loader:'url-loader',
options:{
limit:1024,
name:'[name].[ext]',
publicPath: '',
outputPath:'imgs/'
}
}]
}]
},
plugins:[
new HtmlWebpackPlugin({
filename:'pages/login.html',
template:'./pages/login.html'
})
]
}
复制代码
login.js
import '../less/common.less'
import '../less/login.less'
import './common/common.js'
复制代码
- 修改
package.json
文件
{
...
scripts:{
"dev":"set NODE_ENV=development&& webpack-dev-server --config webpack.config.js",
"build":"set NODE_ENV=production&& webpack --progress --config webpack.config.js"
}
...
}
复制代码
开发:npm run dev
打包:修改 mode:'production'
然后执行 npm run build
dist
|-/imgs
|-*.png
|-/js
|-login.bundle.js
|-/pages
|-login.html
复制代码
打开 login.html
页面正常。查看 login.bundle.js
发现所有的 css
和 js
都被打包到了这一个文件中。这并不是我们想要的结果。并且针对开发环境和生产环境没有进行区分。我们同时拆分 webpack.config.js
使其更加灵活。
- 公共配置
webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
entry:{
login:'./js/login.js'
},
output:{
path:path.resolve(__dirname,'dist'),
filename:'js/[name].bundle.js'
},
module:{
rules:[{
test:/\.js$/,
exclude:/node_modules/,
use:[{
loader:'babel-loader',
options:{
presets:['@babel/preset-env'],
plugins:['@babel/plugin-syntax-dynamic-import',['@babel/plugin-transform-runtime',{
corejs:2
}]]
}
}]
},{
test:/\.html/,
use:[{
loader:'html-loader',
options:{
attrs:[':src']
}
}]
}]
},
plugins:[
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
filename:'pages/login.html',
template:'./pages/login.html'
})
]
}
复制代码
- 开发配置
webpack.dev.js
const path = require('path')
const merge = require('webpack-merge')
const common = require('./webpack.common.js')
module.exports = merge(common,{
mode:'development',
devServer:{
contentBase:path.resolve(__dirname,'dist'),
open:true,
openPage:'pages',
index:'login.html',
},
devtool:'inline-source-map',
module:{
rules:[{
test:/\.less$/,
use:['style-loader','css-loader','less-loader']
},{
test:/\.(png|jpg|svg|gif)$/,
use:[{
loader:'file-loader',
options:{
name:'[name].[ext]',
publicPath:'../imgs/',
outputPath:'imgs/'
}
}]
}]
}
})
复制代码
- 生产配置
webpack.prod.js
const path = require('path')
const merge = require('webpack-merge')
const common = require('./webpack.common.js')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = merge(common,{
mode:'production',
output:{
publicPath:'https://xyz.com/js/'
},
devtool:'source-map',
module:{
rules:[{
test:/\.less$/,
use:[MiniCssExtractPlugin.loader,'css-loader','less-loader']
},{
test:/\.(png|jpg|svg|gif)$/,
use:[{
loader:'url-loader',
options:{
limit:1024,
name:'[name].[ext]',
publicPath: 'https://xyz.com/img/',
outputPath:'imgs/'
}
}]
}]
},
plugins:[
new MiniCssExtractPlugin({
filename:'css/[name].css'
}),
new OptimizeCssAssetsPlugin({})
]
})
复制代码
同时修改 package.json
{
...
scripts:{
"dev":"set NODE_ENV=development&& webpack-dev-server --config webpack.dev.js",
"build":"set NODE_ENV=production&& webpack --progress --config webpack.prod.js"
}
...
}
复制代码
打包:
dist
|-/css
|-login.css
|-/img
|-*.png
|-/js
|-login.bundle.js
|-login.bundle.js.map
|-/pages
|-login.html
复制代码
至此基本满足需求。 HtmlWebpackPlugin
插件会把打包出来的资源文件自动插入到 html
模板当中。 但是我们往往还需要引入第三方文件 例如 jquery
。 安装 npm install --save jquery@1.11.3
公共类型的第三方文件使用方式:
webpack.common.js
{
plugins:[
...
new webpack.ProvidePlugin({
$:'jquery',
jQuery:'jquery'
})
...
]
}
复制代码
这样我们就可以直接使用 $
。 login.js
import './common/common.js'
import '../less/common.less'
import '../less/login.less'
$(document).ready(function(){ ... })
复制代码
其他非公共第三方文件安装后通过 import
引用。
代码拆分
第三方的公共文件我们不会轻易更换,所以一般要把第三方文件单独打包,这样便于缓存。使用 optimization.splitChunks
进行代码拆分
webpack.common.js
{
...
optimization:{
splitChunks:{
chunks:'all'
},
runtimeChunk:{
name:'runtime'
}
}
}
复制代码
打包:
dist
|-/css
|-login.css
|-/img
|-*.png
|-/js
|-login.bundle.js
|-runtime.bundle.js
|-vendors~login.bundle.js
|-*.js.map
|-/pages
|-login.html
复制代码
默认的代码拆分规则
- 新的被共享的代码块或者是
node_modules
中的模块 - 新的被共享的代码块在
min+gz
之前大于30kb
- 按需加载时,并行请求数量
<=5
- 页面初始化时,并行请求数量
<=3
{
...
optimization:{
splitChunks:{
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
}
复制代码
往往我们需要自定义代码拆分的规则。自定义配置
{
output:{
filename:'js/[name].bundle.js',
chunkFilename:'js/[name].bundle.js'
},
plugins:[
...
new MiniCssExtractPlugin({
filename:'css/[name].css',
chunkFilename:'css/[name].css'
})
],
...
optimization:{
splitChunks:{
chunks:'all',
cacheGroups:{
vendors:{
test:/node_modules/,
name:'vendors',
priority:10
},
common:{
test:/(common\.js|common\.less)/,
name:'common',
priority:1,
enforce:true
}
}
},
runtimeChunk:{
name:'runtime'
}
}
}
复制代码
打包:
dist
|-/css
|-login.css
|-common.css
|-/img
|-*.png
|-/js
|-common.bundle.js
|-login.bundle.js
|-runtime.bundle.js
|-vendors~login.bundle.js
|-*.bundle.js.map
|-/pages
|-login.html
复制代码
缓存
同时我们还希望当我们对文件进行了修改后可以让浏览器请求最新的文件。我们需要对输出文件的文件名进行控制
{
...
output:{
filename:'js/[name].[contenthash].js'
},
...
plugins:[{
new MiniCssExtractPlugin({
filename:'css/[name].[contenthash].css'
})
}],
optimization:{
namedChunks:true,
moduleIds:'hashed'
}
}
复制代码
这样当有文件发生变化时,打包后就只有对应修改的文件和 runtime.js
文件会发生变化。
多页面
当有多个页面时,我们需要多个入口文件
zero
|-package.json
|-/js
|-/common
|-common.js
|-login.js
|-company.js
|-/imgs
|-mobile.png
|-captcha.png
|-username.png
|-317x70.png
|-/less
|-common.less
|-login.less
|-company.less
|-/pages
|-login.html
|-company.html
|-node_modules
|-webpack.common.js
|-webpack.dev.js
|-webpack.prod.js
复制代码
webpack.common.js
{
entry:{
login:'./js/login.js',
company:'./js/company.js'
},
...
plugins:[{
new HtmlWebpackPlugin({
filename:'pages/login.html',
template:'./pages/login.html',
chunks:['runtime','vendors','common','login']
})
new HtmlWebpackPlugin({
filename:'pages/company.html',
template:'./pages/company.html',
chunks:['runtime','vendors','common','company']
}),
}]
}
复制代码
虽然这样可以达到目的,但是每当有新文件增加时就需要修改配置文件,可以通过读取文件目录自动生成入口配置和模板配置
const glob = require('glob')
let entry = {};
glob.sync('./js/*.js').forEach((file)=>{
let filename = path.basename(file,'.js');
entry[filename] = file;
});
let pages = [];
glob.sync('./pages/*.html').forEach((file)=>{
let filename = path.basename(file,'.html');
pages.push(new HtmlWebpackPlugin({
filename:`${filename}.html`,
template:file,
chunks:['runtime','vendors','common',filename]
}));
});
webpack.common.js
{
entry:entry,
...
plugins:[
...pages
]
}
复制代码
使用到的loader和plugin说明
loader的作用顺序是从后往前
-
babel-loader
ES6转ES5 -
style-loader
css-loader
解析css
并插入到模板style
标签中 -
less-loader
解析less -
file-loader
url-loader
解析资源文件 图片、字体等 -
html-loader
解析html
文件中的图片 -
html-webpack-plugin
生成模板文件把资源自动插入到模板中 -
clean-webpack-plugin
打包是清理上一次的打包文件 -
mini-css-extract-plugin
css
分离插件 -
optimize-css-assets-webpack-plugin
css
压缩插件 -
webpack.ProvidePlugin
自动加载模块 -
webpack.DefinePlugin
创建全局常量 -
SplitChunksPlugin
代码分割 -
webpack-bundle-analyzer
资源分析