webpack配置:
const path = require('path') // 用来找到文件夹路径
const HtmlWebpackPlugin = require(`html-webpack-plugin`) // 用来设置打包的html文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 引入css从style引入 变成 link引入。
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') // 优化打包css插件,对css进行压缩
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
entry: './src/index.js', // 入口文件
output:{ // 输出文件
filename: 'bundle.js',
path: path.resolve(__dirname,'./dist'),
clean: true, // 每次打包后,删除上次的文件
assetModuleFilename: 'images/[contenthash][ext]', // 设置资源模块的文件夹和文件名
publicPath: 'http://localhost:8080/', //配置公共路径
},
mode: 'production', // 编译模式
devtool: 'inline-source-map', // 代码报错时,会在浏览器控制台显示多少代码多少行错误
plugins: [
new HtmlWebpackPlugin({ // 打包html文件,会自动在html文件中引入打包好后的js
template: './index.html', // 要打包的文件
filename: 'app.html',
inject: 'body' // script引入标签的地址
}),
new MiniCssExtractPlugin({
filename: 'styles/[contenthash].css' // 打包css文件设置目录和文件名
})
],
devServer:{
static: './dist' // 启动服务器
},
module: { // 配置模块
rules: [ // 文件解析规则
{
test: /\.png$/, // 以png结尾的文件
type: 'asset/resource', // 获取文件的url地址
generator: {
filename: 'images/[contenthash][ext]' // 打包好后的文件路径和名字,contenthash-- 文件名会自动编译, ext -- 会打包对应的图片名字,优先级会比output高
}
},
{
test: /\.svg$/, // 以svg结尾
type: 'asset/inline' // 导出一个资源的data URI,不会打包出文件,64位字符
},
{
test: /\.text$/,
type: 'asset/source' // 导出资源的源代码,不会打包出文件
},
{
test: /\.jpg$/,
type: 'asset' // 文件大小大于8k时生成'asset/resource', 小于8k时使用 'asset/inline'
parser: {
dataUrlCondition: { // 转换成64位
maxSize: 4 * 1024 * 1024 // 表示超过4M为
}
}
},
{
test: /\.(css|less)$/,
use: ['MiniCssExtractPlugin.loader', 'css-loader', 'less-loader'] // style-loader会将css放到header标签里。注意顺序,webpack支持链式调用,顺序从后往前解析,
},
{
test: /\.(csv|tsv)$/, // 会将数据转换成数组
use: 'csv-loader'
},
{
test: /\.xml$/, // 会将数据转换成对象
use: 'xml-loader'
},
{
test: /\.toml$/,
type: 'json',
parser: {
parse: toml.parse
}
},
{
test: /\.yaml$/,
type: 'json',
parser: {
parse: yaml.parse
}
},
{
test: /\.json5$/,
type: 'json',
parser: {
parse: json5.parse
}
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader', // webpack里babel解析es6的桥梁
options: {
presets: ['@babel/preset-env'], // babel的预设,一组插件的集合
plugins: [
[
'@babel/plugin-transform-runtime' // @babel/preset-env
]
]
}
}
}
]
},
optimization: { // 优化项
minimizer: [
new CssMinimizerPlugin(), // 优化打包css插件,减少文件打包体积,使用插件需要将模式改成生产模式
new TerserPlugin() //将生产环境代码进行压缩
],
splitChunks: {
cacheGroups: { // 缓存第三方库
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
},
performance: {
hints: false // 去掉打包文件体积性能提示
}
}
一些插件:
npm i webpack-dev-server -D // 用来热更新,启动后npm webpack-dev-server,进入对应打包好后文件夹,会热更新
npm i css-loader -D // 解析引入css
npm i style-loader -D // 让css渲染到界面
npm i less-loader less -D
npm i mini-css-extract-plugin -D // webpack5环境才有,合并style标签代码,生成一个css文件,变成link标签引入
npm i css-minimizer-webpack-plugin -D // 优化css打包插件,对css进行压缩
npm i csv-loader xml-loader -D // 解析csv、xml的loader
npm i toml yaml json5 -D // 解析toml、yaml、json文件
npm i babel-loader @babel/core @babel/preset-env -D
// babel-loader webpack里babel解析es6的桥梁
// @babel/core babel的核心模块
// @babel/preset-env babel的预设,一组插件的集合
regeneratorRuntime //
npm i @babel/runtime -D // regenerator运行时需要的内容
npm i @babel/plugin-transform-runtime -D // 会在需要regenerator时候自动导包
npm install terser-webpack-plugin -D // webpack开箱即用的打包压缩插件,但在optimization中配置了minimizer: new CssMinimizerPlugin() 插件时候,terser这个插件就会失效,因此需要重新下载。
npm i webpack-merge -D // 合并webpack配置文件,
一些命令:
npm webpack --watch // 主动修改代码后,自动重新打包
npx webpack-dev-server --open
npx webpack --env production // 编译在生产环境
npx webpack -c ./config/webpack.config.dev.js // 指定webpack调用打包配置文件
npx webpack serve -c ./config/webpack.config.dev.js // 指定打包配置文件启动。
打包时代码分离方法:
- 入口起点:使用entry配置手动分离代码。缺点:公共代码会分别在每个包里边去重复打包。
- 防止重复:使用Entry dependencies 或者 SplitChunksPlugin 去重和分离代码。
- 动态导入:通过模块的内联函数调用来分离代码。
入口起点配置:
entry: {index: './src/index.js',another: './src/another-module.js'},
output:{
filename: '[name].bundle.js', // 会更具文件名自动生成打包好的文件
path: path.resolve(__dirname,'./dist'),
clean: true,
assetModuleFilename: 'images/[contenthash][ext]'
},
防止重复: 这样配置公共代码会重复打包,入口文件可以修改成下面代码解决问题: 第一种方式:
entry: {
index: {
import: './src/index.js',
dependOn: 'shared'
},
another: {
import: './src/another-module.js',
dependOn: 'shared'
},
shared: 'lodash' // 将的lodash提成公共的
},
第二种方式:在optimization中使用splitChunks(webpack内置),对公共文件进行抽离成单独的chunks打包。
entry: {index: './src/index.js',another: './src/another-module.js'},
optimization: {
splitChunks: {
chunks: 'all'
}
}
动态导入:动态导入公共文件会自动分离,但是如果动态导入和静态导入一起时会失效,需要splitChunks进行分离。
动态加载文件
function getComponent(){
return import('lodash').then(({default: _})=>{
const ele = document.createElement('div')
ele.innerHTML= _.join(['Hello','webpack'],' ')
return ele
})
}
getComponent().then((ele)=>{
document.body.appendChild(ele)
})
import './async-module'
懒加载文件: 在js文件中通过 点击事件 实现懒加载。
const button = document.createElement('button')
button.textContent = '点击执行加法'
button.addEventListener('click',()=>{
import(/* webpackChunkName: 'math' */'./math').then(({add})=>{ // 可以使用魔法注释修改打包后的文件名
console.log(add(4,5));
})
})
document.body.appendChild(button)
预获取、预加载模块
webpack在4.6.0+增加了对预获取和预加载的支持。 在声明import时,使用下面这些内置指令,可以让webpack输出“resoure hint(资源提示)”,来告知浏览器:
- prefetch(预获取):将来某些导航下可能需要的资源
- preload (预加载): 当前导航下可能需要的资源
prefetch: 魔法注释里 webpackPrefetch: true ,浏览器会通过link标签prefetch形式,网络空闲时候去加载打包文件
const button = document.createElement('button')
button.textContent = '点击执行加法'
button.addEventListener('click',()=>{
import(/* webpackChunkName: 'math', webpackPrefetch: true */'./math').then(({add})=>{
console.log(add(4,5));
})
})
document.body.appendChild(button)
preload: 魔法注释里 webpackPreload: true ,和上面点击懒加载类似
const button = document.createElement('button')
button.textContent = '点击执行加法'
button.addEventListener('click',()=>{
import(/* webpackChunkName: 'math', webpackPreload: true */'./math').then(({add})=>{
console.log(add(4,5));
})
})
document.body.appendChild(button)
prefetch和preload区别:
- preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
- preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
- preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
输出文件名并将js放在一个文件夹:
部署时候,浏览器会在文件名没更新时会使用缓存版本,因此部署时候需要对输出文件名进行更改。
output:{
filename: 'scripts/[name].[contenthash].js', // contenthash会根据内容动态生成hash文件名
path: path.resolve(__dirname,'./dist'),
clean: true,
assetModuleFilename: 'images/[contenthash][ext]'
},
缓存第三方库
将第三方库(library)提取到单独的vendor chunk文件中,他们很少像本地的源代码频繁修改,因此通过实现以上步骤,client的长效缓存机制,浏览器命中缓存来消除请求,并减少向server获取资源,同时还能保证client代码和server代码版本一致(将代码打包缓存到浏览器里,这样只有我们改变的代码会更新,第三方库始终使用浏览器缓存的内容)
optimization: {
minimizer: [
new CssMinimizerPlugin()
],
splitChunks: {
cacheGroups: {
vendor:{
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
环境变量
暴露方式变成函数,传入形参env,用户在使用下面打包命令时候,就可以识别打包环境
npx webpack --env production // 编译在生产环境运行
npx webpack --env production --env goal=local // 编译在生产环境运行,并切携带参数{goal:local}
module.exports = (env) => {
console.log(env.goal);
return {
mode: env.production ? 'production' : 'development', // 可以根据用户输入命令来识别打包环境
}
}
生产环境和开发环境配置分离,提取公共环境配置
webpack配置文件分离后,注意需要把output中打包路径修改一下,提取公共环境配置文件时候需要使用merge插件
const path = require('path')
const HtmlWebpackPlugin = require(`html-webpack-plugin`)
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const toml = require('toml')
const yaml = require('yaml')
const json5 = require('json5')
module.exports = {
entry: {
index: './src/index.js',
another: './src/another-module.js'
},
output: {
filename: 'scripts/[name].[contenthash].js',
path: path.resolve(__dirname, '../dist'),
clean: true,
assetModuleFilename: 'images/[contenthash][ext]',
publicPath: 'http://localhost:8080/'
},
devtool: 'inline-source-map',
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
filename: 'app.html',
inject: 'body'
}),
new MiniCssExtractPlugin({
filename: 'styles/[contenthash].css'
})
],
devServer: {
static: './dist'
},
module: {
rules: [
{
test: /\.png$/,
type: 'asset/resource',
generator: {
filename: 'images/[contenthash][ext]'
}
},
{
test: /\.svg$/,
type: 'asset/inline'
},
{
test: /\.(css|less)$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
type: 'asset/resource'
},
{
test: /\.(csv|tsv)$/,
use: 'csv-loader'
},
{
test: /\.xml$/,
use: 'xml-loader'
},
{
test: /\.toml$/,
type: 'json',
parser: {
parse: toml.parse
}
},
{
test: /\.yaml$/,
type: 'json',
parser: {
parse: yaml.parse
}
},
{
test: /\.json5$/,
type: 'json',
parser: {
parse: json5.parse
}
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: [
[
'@babel/plugin-transform-runtime'
]
]
}
}
}
]
},
optimization: {
minimizer: [
new CssMinimizerPlugin(),
new TerserPlugin()
],
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
}