Webpack
webpack的五个核心概念:
- entry,入口配置,设定webpack以那个文件为入口起点开始打包,分析构建内部依赖图
- output,输出配置,设定打包后的资源bundles输出位置以及命名
- loader,处理非js文件,webpack只识别js和json文件,因此需要loader将非js文件翻译解析
- plugins,执行范围更广的任务,功能扩展,功能强化
- mode,分为两种:
- development,开发模式,设置代码本地调试运行的环境
- production,生产模式,设置代码优化上线运行的环境
初接触
运行指令:
准备工作:
npm init npm i webpack webpack-cli -D
开发环境:
webpack ./src/index.js -o ./build/built.js --mode=development //解读:webpack以当前路径下src文件夹下index.js文件为入口文件,打包输出生成到当前路径下build文件夹下built.js文件,打包环境为开发环境
生产环境:
webpack ./src/index.js -o ./build/built.js --mode=production //解读:webpack以当前路径下src文件夹下index.js文件为入口文件,打包输出生成到当前路径下build文件夹下built.js文件,打包环境为生产环境
引入时注意需引入打包后的文件
1.webpack能处理js/json文件,不能处理css/img等其他资源 2.生产环境和开发环境将es6模块化编译成浏览器能识别的模块化 3.生产环境比开发环境多一个压缩js代码
样式打包
webpack.config.js — webpack配置文件 — 配置webpack指令运行时的操作 — commonjs的语法
所有构建工具都是基于nodejs平台运行的 — 模块化默认采用commonjs语法
webpack配置语法:
module.exports = {
//webpack配置
}
注意:项目模块用的ES6模块化,配置用的commonjs
示例
const { resolve } = require('path')
module.exports = {
entry : './src/index.js',
output : {
filename : 'built.js',
//__dirname --- nodejs变量 --- 表示当前文件的目录的绝对路径
path : resolve(__dirname, 'build')
},
//loader配置
module : {
rules : [
//详细loader配置
//不同文件必须配置不同loader处理
{
//匹配那些文件
test : /\.css$/,
//使用哪些loader配置
use : [
//use数组中从右到左,从下到上 依次执行
//创建style标签,将js种的样式资源插入进行,添加到head中生效
'style-loader',
//将css文件变成commonjs模块加载再js种,内容为样式字符串
'css-loader'
]
},
{
test : /\.less$/,
//使用哪些loader配置
use : [
//use数组中从右到左,从下到上 依次执行
//创建style标签,将js种的样式资源插入进行,添加到head中生效
'style-loader',
//将css文件变成commonjs模块加载再js种,内容为样式字符串
'css-loader',
//将less文件编译成css文件
//需要less和less-loader两个包
'less-loader'
]
},
{
test : /\.scss$/,
//使用哪些loader配置
use : [
//use数组中从右到左,从下到上 依次执行
//创建style标签,将js种的样式资源插入进行,添加到head中生效
'style-loader',
//将css文件变成commonjs模块加载再js种,内容为样式字符串
'css-loader',
//将scss文件编译成css文件
//需要scss和scss-loader两个包
'scss-loader'
]
}
]
},
//plugins配置
plugins : [
//详细配置
],
//模式选择 当前为开发模式
mode : 'development'
//mode : 'production'
}
html打包
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
//注意下载插件
module.exports = {
entry : './src/index.js',
output : {
filename : 'built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
//详细loader配置
]
},
plugins : [
//html-webpack-plugin
//默认创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS)
new HtmlWebpackPlugin({
//复制 './src/index.html'文件 并自动引入打包输出的所有资源
template : './src/index.html'
})
],
mode : 'development'
}
图片资源打包
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/index.js',
output : {
filename : 'built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
{
test : /\.less$/,
//多个loader处理用use
use : [
'style-loader',
'css-loader',
'less-loader'
]
},
{
//处理图片资源
test : /\.(jpg|png|gif)/,
//注:url-loader依赖于file-loader,因此此处要下载两个依赖
loader : 'url-loader',
options : {
//图片小于8kb,就会被base64处理
//优点:减少请求数量,减轻服务器压力
//缺点:图片体积会更大,导致文件请求速度更慢
limit : 8 * 1024
//注:由于url-oader默认使用es6模块化解析,而html-loader引入图片是commonjs
//解析时地址会出问题:[object Module]
//因此,需要关闭url-loader的es6模块化,使用commonjs格式
esModule : false,
//图片重命名
//[hash:10]图片的hash的前10位
//[ext]取文件原来扩展名
name : '[hash:10].[ext]'
}
},
{
test : /\.html$/,
//处理html文件的img文件(负责引入img,从而能被url-loader进行处理)
loader : 'html-loader',
}
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html'
})
],
mode ; 'development'
}
其他资源打包
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/index.js',
output : {
filename : 'built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
{
test : /\.css$/,
//多个loader处理用use
use : [
'style-loader',
'css-loader',
]
},
//打包其他资源
{
//排除css/js/html资源
exclude : /\.(css|js|html)/
}
]
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html',
loader : 'file-loader',
options : {
name : '[hash:10].[ext]'
}
})
],
mode ; 'development'
}
devServer
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/index.js',
output : {
filename : 'built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
{
test : /\.css$/,
use : [
'style-loader',
'css-loader',
]
},
{
exclude : /\.(css|js|html)/
}
]
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html',
loader : 'file-loader',
options : {
name : '[hash:10].[ext]'
}
})
],
mode ; 'development',
//开发服务器 devServer : 用来自动化(自动编译,自动打开浏览器,自动刷新浏览器)
//特点:只会在内存中编译打包,不会有任何输出
//启动指令: webpack-dev-server
devServer : {
contentBase : resolve(__dirname, 'build'),
//启动gzip压缩
compress : true,
//端口号
port : 3000,
//默认打开
open : true
}
}
开发环境基本配置
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/js/index.js',
output : {
filename : 'js/built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
{
//处理less资源
test : /\.less$/,
use : [
'style-loader',
'css-loader',
'less-loader'
]
},
{
//处理scss资源
test : /\.scss$/,
use : [
'style-loader',
'css-loader',
'scss-loader'
]
},
{
//处理css资源
test : /\.css$/,
use : [
'style-loader',
'css-loader'
]
},
{
//处理图片资源
test : /\.(jpg|png|gif)/,
loader : 'url-loader',
options : {
limit : 8 * 1024,
name : '[hash:10].[ext]',
esModule : false,
outputPath : 'img'
}
},
{
//处理html中图片资源
test : /\.html$/,
loader : 'html-loader'
},
{
//处理其他资源
exlude : /\.(js|html|css|css|less|scss|jpg|png|gif)/,
loader ; 'file-loader',
options : {
name : '[hash:10].[ext]',
outputPath : 'media'
}
}
]
},
plugins : [
//plugins的配置
new HtmlWebpackPlugin({
template : './src/index.html'
})
],
mode : 'development',
devServer : {
contentBase : resolve(__dirname, 'build'),
compress : true,
port : 3000,
open : true
}
}
提取css成单独文件
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
entry : './src/js/index.js',
output : {
filename : 'js/built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
{
//处理less资源
test : /\.less$/,
use : [
'style-loader',
'css-loader',
'less-loader'
]
},
{
//处理scss资源
test : /\.scss$/,
use : [
'style-loader',
'css-loader',
'scss-loader'
]
},
{
//处理css资源
test : /\.css$/,
use : [
//取代style-loader,提取js中的css成单独文件
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
//处理图片资源
test : /\.(jpg|png|gif)/,
loader : 'url-loader',
options : {
limit : 8 * 1024,
name : '[hash:10].[ext]',
esModule : false,
outputPath : 'img'
}
},
{
//处理html中图片资源
test : /\.html$/,
loader : 'html-loader'
},
{
//处理其他资源
exlude : /\.(js|html|css|css|less|scss|jpg|png|gif)/,
loader ; 'file-loader',
options : {
name : '[hash:10].[ext]',
outputPath : 'media'
}
}
]
},
plugins : [
//plugins的配置
new HtmlWebpackPlugin({
template : './src/index.html'
}),
new MiniCssExtractPlugin({
//对输出css文件进行重命名
filename : 'css/main.css'
})
],
mode : 'development',
devServer : {
contentBase : resolve(__dirname, 'build'),
compress : true,
port : 3000,
open : true
}
}
css兼容性处理
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
//设置nodejs环境变量
process.env.NODE_ENV = 'development'
module.exports = {
entry : './src/js/index.js',
output : {
filename : 'js/built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
{
//处理css资源
test : /\.css$/,
use : [
//取代style-loader,提取js中的css成单独文件
MiniCssExtractPlugin.loader,
'css-loader',
//css兼容性处理:postcss --> postcss-loader postcss-preset-env
//帮助postcss找到package.json中browserslist里面的配置
//通过配置加载指定的css兼容性样式
//修改loader的配置
{
loader : 'postcss-loader',
options : {
ident : 'postcss',
plugins : () => [
//postcss的插件
require('postcss-preset-env')()
]
}
}
]
},
{
//处理图片资源
test : /\.(jpg|png|gif)/,
loader : 'url-loader',
options : {
limit : 8 * 1024,
name : '[hash:10].[ext]',
esModule : false,
outputPath : 'img'
}
},
{
//处理html中图片资源
test : /\.html$/,
loader : 'html-loader'
},
{
//处理其他资源
exlude : /\.(js|html|css|css|less|scss|jpg|png|gif)/,
loader ; 'file-loader',
options : {
name : '[hash:10].[ext]',
outputPath : 'media'
}
}
]
},
plugins : [
//plugins的配置
new HtmlWebpackPlugin({
template : './src/index.html'
}),
new MiniCssExtractPlugin({
//对输出css文件进行重命名
filename : 'css/main.css'
})
],
mode : 'development',
devServer : {
contentBase : resolve(__dirname, 'build'),
compress : true,
port : 3000,
open : true
}
}
package.json配置
"browserslist" : {
"development" : [
//兼容最近一个版本的浏览器
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production" : [
//大于99.8%的浏览器
">0.2%",
//不要已经'死'的浏览器
"not dead",
//不要oprea mini
"not op_mini all"
]
}
压缩CSS
const { resolve } = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
entry : './src/js/index.js',
output : {
filename : 'js/built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
{
//处理css资源
test : /\.css$/,
use : [
MiniCssExtractPlugin.loader,
'css-loader',
]
},
]
},
plugins : [
new MiniCssExtractPlugin({
filename : 'css/main.css'
}),
//压缩css
new optimizeCssAssetsWebpackPlugin()
],
mode : 'development',
devServer : {
contentBase : resolve(__dirname, 'build'),
compress : true,
port : 3000,
open : true
}
}
js语法检查Eslint
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/js/index.js',
output : {
filename : 'js/built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
{
//语法检查:eslint-loader eslint
//注:只检查开发人员自身写的代码,无需检查第三方库
//设置检查规则:
//package.json中配置eslintConfig选项
// airbnb --> eslint-config-airbnb-base eslint eslint-plugin-import
test : /\.js$/,
exclude : /node_modules/
loader : exlint-loader,
options : {
//自动修复
fix : true
}
}
]
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html'
}),
],
mode : 'development'
}
package.json中配置
"browserslist" : {
"development" : [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production" : [
">0.2%",
"not dead",
"not op_mini all"
]
},
"eslintConfig" : {
"extends" ; "airbnb-base"
}
js的兼容性处理
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/js/index.js',
output : {
filename : 'js/built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
//js兼容性处理:babel-loader
{
test : /\.js$/,
exclude : /node_modules/
loader : 'babel-loader',
options : {
//预设,配置babel如何进行兼容性处理
//babel @babel/preset-env @babel/core
//基本js兼容性处理 : @babel/preset-env
//全部js兼容性处理 : @babel/polyfill --> 部分兼容性问题却引入了所有兼容性代码,体积太大
//需求:按需加载 --> core-js
presets : [
[
'@babel/preset-env',
{
//按需加载
useBuiltIns : 'usage',
//指定core-js版本
corejs : {
version : 3
},
targets : {
chrome : '60',
firefox : '60',
ie : '9',
safari : '10',
edge : '17'
}
}
]
]
}
}
]
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html'
}),
],
mode : 'development'
}
压缩js和html
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/js/index.js',
output : {
filename : 'js/built.js',
path : resolve(__dirname, 'build')
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html',
//压缩html代码配置
minify : {
//移除空格
collapesWhitespace : true,
//移除注释
removeComents : true
}
}),
],
mode : 'production'
//生产环境自动压缩js代码
}
生产环境基本配置
const { resolve } = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
process.env.NODE_ENV = 'production'
//复用loader
const commonCssLoader = [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader : 'postcss-loader',
options : {
ident : 'postcss',
plugins : () => [
require('postcss-preset-env')()
]
}
}
]
module.exports = {
entry : './src/js/index.js',
output : {
filename : 'js/built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
{
test : /\.css$/,
use : [...commonCssLoader]
},
{
test : /\.less$/,
use : [...commonCssLoader,'less-loader']
},
{
test : /\.scss$/,
use : [...commonCssLoader,'scss-loader']
},
//一般来说,一个文件只能被一个loader处理,当出现多个loader处理一个文件时
//loader执行顺序不能出错
//先执行eslint,再执行babel
{
test : /\.js$/,
exclude : /node_modules/,
loader : 'eslint-loader',
//优先执行
enforce : 'pre'
options : {
fix : true
}
},
{
test : /\.js$/,
exclude : /node_modules/,
loader : 'babel-loader',
options : {
presets : [
[
'@babel/preset-env',
{
useBuiltIns : 'usage',
corejs : { version : 3 },
targets : {
chrome : '60',
firefox : '60',
ie : '9',
safari : '10',
edge : '17'
}
}
]
]
}
},
{
test : /\.(jps|png|gif)/,
loader : 'url-loader',
options : {
limit : 8 * 1024,
name : '[hash:10].[ext]',
outputPath : 'img',
esModule : false
}
},
{
test : /\.html$/,
loader : 'html-loader'
},
{
exclude : /\.(js|css|html|less|scss|jpg|png|gif)/,
loader : 'file-loader',
options : {
outputPath : 'media'
}
}
]
}
plugins : [
new MiniCssExtractPlugin({
filename : 'css/main.css'
}),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template : './src/index.html',
minify : {
//压缩空格
collapseWhitespace : true,
//去除注释
removeComments : true
}
})
],
mode : 'production'
}
package.json中配置
"browserslist" : {
"development" : [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production" : [
">0.2%",
"not dead",
"not op_mini all"
]
},
"eslintConfig" : {
"extends" ; "airbnb-base"
}
性能优化
性能优化需求:
开发环境:
1.优化打包构建速度
2.优化代码调试
生产环境:
1.优化打包构建速度
2.优化代码运行的性能
优化开发环境的打包构建速度—HMR
HMR --- hot module replacement 热模块替换
一个模块发生变化,只会重新打包这一个模块
const { resolve } = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
//entry : './src/js/index.js',
//解决html文件由于HMR导致不能热更新问题
entry : ['./src/js/index.js','./src/index.html'],
output : {
filename : 'js/built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
{
test : /\.css$/,
use : [
MiniCssExtractPlugin.loader,
'css-loader',
]
},
]
},
plugins : [
new MiniCssExtractPlugin({
filename : 'css/main.css'
}),
],
mode : 'development',
devServer : {
contentBase : resolve(__dirname, 'build'),
compress : true,
port : 3000,
open : true,
//开启HMR功能
//当修改webpack配置,新配置要想生效,必须重新webpack服务
hot : true
}
}
注意:
样式文件:可以使用HMR功能,因为style-loader内部实现了
js文件:默认不使用HMR功能
解决:修改js代码,添加支持HMR功能的代码
html文件:默认不使用HMR功能,同时会导致html文件不能热更新
解决:修改入口文件的配置
html文件不需要做HMR功能
//js代码支持HMR
//module.hot存在则说明开启了HMR功能
if(module.hot){
module.hot.accept('需要监听变化的文件路径',() => print())
//方法会监听print.js文件的变化,一旦发生变化,其他模块不会重新打包构建
//会执行后面的回调函数
}
//监听几个文件,就写几个?
//可抽离优化
优化开发环境的代码调试—source-map
source-map
提供源代码到构建后代码映射的一种技术
功能:如果构建后代码出错了,通过映射可以追踪源代码错误
source-map配置
[inline-|hidden-|eval-][nosources-][cheap-[modlue-]]source-map
source-map 外部
错误代码准确信息 和 源代码的错误位置
inline-source-map 内联
inline 内联只生成一个内联的source-map
错误代码准确信息 和 源代码的错误位置
hidden-source-map 外部
错误代码错误原因但是没有错误位置
不能追踪代码错误,只能提示到构建后代码的错误位置
eval-source-map 内联
eval 内联每个文件都生成对应的source-map,都在eval
错误代码准确信息 和 源代码的错误位置
nosources-source-map 外部
错误代码准确信息
没有任何源代码信息
hidden和nosources就是为了隐藏源代码而诞生的
cheap-source-map 外部
错误代码准确信息 和 源代码的错误位置
但是只精确到行错误,不精确到列
cheap-modlue-source-map 外部
错误代码准确信息 和 源代码的错误位置
内联和外部的区别:
1.外部生成了文件,内联没有
2.内联构建速度更快
开发环境: 速度快,调试更友好
速度:eval>inline>cheap>...
eval-cheap-source-map
eval-source-map
调试更友好:
source-map
cheap-module-source-map
cheap-source-map
综合方案:eval-source-map(调试更友好加module,速度更快加cheap)
生产环境: 源代码是否隐藏,调试是不是要更友好
source-map
nosources-source-map 全部隐藏
hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
由于内联会让代码体积更大,因此生产环境不使用内联
综合方案 : source-map / cheap-module-source-map
const { resolve } = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
entry : ['./src/js/index.js','./src/index.html'],
output : {
filename : 'js/built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
{
test : /\.css$/,
use : [
MiniCssExtractPlugin.loader,
'css-loader',
]
},
]
},
plugins : [
new MiniCssExtractPlugin({
filename : 'css/main.css'
}),
],
mode : 'development',
devServer : {
contentBase : resolve(__dirname, 'build'),
compress : true,
port : 3000,
open : true,
hot : true
},
//代码调试优化
devtool : 'eval-source-map'
}
优化生产环境中module/rules/loader配置项—oneOf
const { resolve } = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
entry : ['./src/js/index.js','./src/index.html'],
output : {
filename : 'js/built.js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
//以下loader只会匹配一个
//注意:不能有两个配置处理同一个文件
//因此当有eslint-loader时需提到oneOf外面
oneOf : [
{
test : /\.css$/,
use : [
MiniCssExtractPlugin.loader,
'css-loader',
]
},
]
]
},
plugins : [
new MiniCssExtractPlugin({
filename : 'css/main.css'
}),
],
mode : 'production',
devServer : {
contentBase : resolve(__dirname, 'build'),
compress : true,
port : 3000,
open : true,
hot : true
},
devtool : 'eval-source-map'
}
优化生产环境—缓存
1.babel缓存 --- 类比HMR,只编译js代码更改的文件 --- 第二次打包速度更快
2.文件资源缓存 --- 上线代码性能优化
hash:每次webpack构建时会生成一个唯一的hash值
问题:js和css同时使用一个hash值
如果重新打包,会导致所有缓存失效(可是只改动一个文件)
chunkhash:根据chunk生成的hash值,如果打包来源于同一个chunk,那么hash值就一样
问题:js和css的hash值还是一样的
因为css是在js文件中被引入的,所以同属于一个chunk
contenthash : 根据文件内容生成hash值,不同文件hash值一定不一样
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/js/index.js',
output : {
//添加hash:解决强制缓存期间js代码改变却不更新的问题
filename : 'js/built.[contenthash:10].js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
{
test : /\.js$/,
exclude : /node_modules/
loader : 'babel-loader',
options : {
presets : [
[
'@babel/preset-env',
{
useBuiltIns : 'usage',
corejs : {
version : 3
},
targets : {
chrome : '60',
firefox : '60',
ie : '9',
safari : '10',
edge : '17'
}
}
]
],
//开启babel缓存
//第二次构建时,会读取之前的缓存
cacheDirectory : true,
}
}
]
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html'
}),
],
mode : 'production',
devtool : 'source-map'
}
生产环境优化 — tree shaking摇树
tree shaking --- 去除无用代码
前提1 : 必须使用es6模块
前提2 : production环境
减少代码体积
package.json中配置
"sideEffects" : false 所有代码都没有副作用(都可以进行tree shaking)
问题 : 会把css / @babel/polifill文件当作无用文件清理掉
解决: "sideEffects" : ["*.css"]
生产环境优化 — 代码分割 code-split
code-split --- 分割代码 实现 并行加载 按需加载
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
//单入口
//entry : './src/js/index.js',
//需求:js文件分别输出
//多入口
entry : {
main : './src/js/index.js',
test : './src/js/test.js'
},
output : {
//[name] : 取文件名
filename : 'js/[name].[contenthash:10].js',
path : resolve(__dirname, 'build')
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html',
minify : {
collapseWhitespace : true,
removeComments : true
}
}),
],
mode : 'production',
}
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/js/index.js',
output : {
filename : 'js/built.[contenthash:10].js',
path : resolve(__dirname, 'build')
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html',
minify : {
collapseWhitespace : true,
removeComments : true
}
}),
],
//可以讲node_modules中代码单独打包成一个chunk输出
//会自动分析,多入口文件中是否存在公共文件,如果有会打包成一个单独的chunk
optimization : {
splitChunks : {
chunks : 'all'
}
}
mode : 'production',
}
//通过js代码,让某个文件被单独打包成一个chunk
//import 动态导入语法,能将文件单独打包
//给打包文件取固定名字
import(/* webpackChunkName : 'test' */'文件路径')
.then(result) => {
//文件加载成功
}
.catch(() => {
//文件加载失败
})
优化—js文件的懒加载和预加载
//import动态导入语法
//注意:懒加载也会进行代码分割
//调用js文件时再加载
document.getElementById('btn').onclick = function(){
import(/* webpackChunkName : 'test' */'文件路径')
.then(result) => {
result.fn()
}
}
//预加载
//import动态语法中添加注释字段 webpackPrefetch : true
//预先加载js文件,但是不执行
/*
与正常加载的区别:
正常加载为并行加载(同一时间加载多个文件)
预加载为等其他资源加载完毕,浏览器空闲了再加载资源
注意:预加载的兼容性问题
*/
document.getElementById('btn').onclick = function(){
import(/* webpackChunkName : 'test', webpackPrefetch : true */'文件路径')
.then(result) => {
result.fn()
}
}
优化 — PWA
PWA --- 渐进式网络开发应用程序(离线可访问网页)
plugin : workbox --> workbox-webpack-plugin
const { resolve } = require('path')
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
module.exports = {
entry : {
main : './src/js/index.js',
test : './src/js/test.js'
},
output : {
filename : 'js/[name].[contenthash:10].js',
path : resolve(__dirname, 'build')
},
plugins : [
new WorkboxWebpackPlugin.GenerateSW({
/*
1.帮助serviceworker快速启动
2.删除旧的serviceworker
生成一个servicerworker配置文件
注意: 在js文件中住粗serviceworker,且注意兼容性问题
*/
clientsClaim : true,
skipWaiting : true
})
],
mode : 'production',
}
//注意:eslint不认识window、navigator全局变量
//解决:修改packange.json中配置
if('serviceworker' in navigator) {
window.addEventListener('load',() => {
navigator.serviceworker.register('/service-worker.js')
.then(() => {
//注册成功
})
.catch(() => {
//注册失败
})
})
}
package.json中配置
"browserslist" : {
"development" : [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production" : [
">0.2%",
"not dead",
"not op_mini all"
]
},
"eslintConfig" : {
"extends" : "airbnb-base",
"env" : {
"browser" : true //支持浏览器端全局变量
//"node" : true 支持node全局变量
}
}
多进程打包
thread-loader
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/js/index.js',
output : {
filename : 'js/built.[contenthash:10].js',
path : resolve(__dirname, 'build')
},
module : {
rules : [
{
test : /\.js$/,
exclude : /node_modules/
use : [
//开启多进程打包
//进程启动时间大概为600ms,进程通信也有开销
//工作时间消耗较长,才需要多进程打包
'thread-loader',
{
'babel-loader',
options : {
presets : [
[
'@babel/preset-env',
{
useBuiltIns : 'usage',
corejs : {
version : 3
},
targets : {
chrome : '60',
firefox : '60',
ie : '9',
safari : '10',
edge : '17'
}
}
]
],
cacheDirectory : true,
}
}
]
}
]
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html'
}),
],
mode : 'production',
devtool : 'source-map'
}
externals — 防止某些包被打包
const { resolve } = require('path')
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
module.exports = {
entry : {
main : './src/js/index.js',
test : './src/js/test.js'
},
output : {
filename : 'js/[name].[contenthash:10].js',
path : resolve(__dirname, 'build')
},
plugins : [
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim : true,
skipWaiting : true
})
],
mode : 'production',
externals : {
//拒绝jqury被打包
jquery : 'jQuery'
}
}
动态连接库 – dll
//webpack.dll.js
/*
使用dil技术,对某些库(第三方库,react,vue...)进行单独打包
运行指令 webpack --config webpack.dll.js
*/
const { resolve } = require('path')
const webpack = require('webpack')
module.exports = {
//最终打包生成的[name] --> jquery
//['jquery'] --> 要打包的库是jquery
entry : {
jquery : ['jquery']
},
output : {
filename : '[name].js',
path : resolve(__dirname, 'dll'),
library : '[name]_[hash]', //打包的库里面向外暴露的内容叫的什么名字
},
plugins : [
new webpack.DllPlugin({
name : '[name]_[hash]', //映射库的暴露的内容名称
path : resolve(__dirname, 'dll/manifest.json') //输出文件路径
})
],
mode : 'production'
}
//webpack.config.js配置
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
module.exports = {
entry : './src/js/index.js',
output : {
filename : 'js/built.[contenthash:10].js',
path : resolve(__dirname, 'build')
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html'
}),
//选择不参与打包的库,同时使用时的名称也改变
new webpack.DllReferencePlugin({
manifest : resolve(__dirname, 'dll/manifest.json')
}),
//将某个文件打包输出,并在html中自动引入该资源文件
new AddAssetHtmlWebpackPlugin({
filepath : resolve(__dirname, 'dll/jquery.js')
})
],
mode : 'production',
}
webpack配置详解
entry详解
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/index.js',
output : {
filename : 'built.js',
path : resolve(__dirname, 'build')
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html'
})
],
mode : 'development'
}
entry :
1.string ---> 单入口文件 打包形成一个chunk, 输出一个bundle chunk名称默认为main
2.array ---> 多入口文件 所有入口文件最终只会形成一个chunk,输出一个bundle 默认为main
注 : 2一般用于开发环境中HMR功能中让html更新生效
3.object ---> 多入口文件,key 名称,value 路径
几个文件几个chunk,对应输出几个bundle chunk名称为key
--> 特殊用法 entry : {
index : ['./src/index.js','./src/test.js'],
add : ['./src/add.js']
}
需求:多入口文件中将多个文件打包成一个chunk时使用
output详解
//const { resolve } = require('path')
output : {
//指定名称加目录
filename : 'js/[name].js',
//输出文件目录(将所有资源输出的公共目录)
path : resolve(__dirname, 'build'),
//所有资源引入公共路径前缀 --> 'imgs/a.jpg' --> '/imgs/a.jpg'
//一般用于生产环境
publicPath : '/',
//非入口文件的chunk名称 --> 非entry的额外chunk --> js的import动态语法/optimization配置
chunkFilename : '[name]_chunk.js',
//整个库向外暴露的变量名
library : '[name]',
//变量名添加的目标属性 --> 此处绑定在browser上
libraryTarget : 'window'
//libraryTarget : 'commonjs' 意为以commonjs格式暴露
}
module详解
//const { resolve } = require('path')
module : {
rules : [
//loader配置
{
test : /\.css$/,
//多个loader用use
use : ['style-loader','css-loader']
},
{
test : /\.js$/,
//单个loader用loader
//排除node_modules下的js文件
exclude: /node_modules/,
//只检查src下的js文件
include: resolve(__dirname,'src'),
//优先执行
//enforce : 'pre',
//延后执行
enforce : 'post'
loader : 'eslint-loader'
},
{
//以下配置只会生效一个
oneOf : []
}
]
}
resolve详解
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/index.js',
output : {
filename : 'built.js',
path : resolve(__dirname, 'build')
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html'
})
],
mode : 'development',
//解析模块的配置
resolve : {
//配置解析模块的路径别名
allas : {
//变量名,代表resolve中的绝对路径
//优点:简写路径, 缺点: 路径没有提示
$css : resolve(__dirname,'src/css')
},
//配置省略文件路径的后缀名
extensions : ['.js', '.jsx', '.tsx'],
//声明解析模块的目录
modules : [resolve(__dirname,'../../node_modules'),'node_modules']
}
}
dev Server详解
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/index.js',
output : {
filename : 'built.js',
path : resolve(__dirname, 'build')
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html'
})
],
mode : 'development',
resolve : {
allas : {
$css : resolve(__dirname,'src/css')
},
extensions : ['.js', '.jsx', '.tsx'],
modules : [resolve(__dirname,'../../node_modules'),'node_modules']
},
devServer : {
//运行代码的目录
contentBase : resolve(__dirname, 'build'),
//监视contentBase目录下的所有文件,文件变化则reload
watchContentBase : true,
watchOptions : {
//忽略文件
ignored : /node_modules/
}
//启动gzip压缩
compress : true,
//开启端口号
port : 5000,
//域名
host : 'localhost',
//自动打开浏览器
open: 'true',
//开启HMR功能
hot : true,
//不显示启动服务器的日志信息
clientLogLevel : 'none',
//除了基本启动信息外,不显示其他内容
quiet : true,
//如果出错了,不要全屏提示
overlay : false,
//服务器代理 --> 解决开发环境跨域问题
proxy: {
//devServer(5000)接收到/api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
'/api' : {
target : 'http://localhost:3000',
pathRewrite : {
//发送请求时,请求路径重写 : 将/api/xxx --> /xxx(去掉/api)
'^/api' : ..
}
}
}
}
}
optimization详解
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
entry : './src/js/index.js',
output : {
filename : 'js/built.[contenthash:10].js',
path : resolve(__dirname, 'build')
},
plugins : [
new HtmlWebpackPlugin({
template : './src/index.html',
minify : {
collapseWhitespace : true,
removeComments : true
}
}),
],
optimization : {
splitChunks : {
chunks : 'all',
//以下结尾默认值,一般不做修改,可以不写
//分割的chunk最小为30kb
miniSize : 30 * 1024,
//最大无限制
maxSize : 0,
//要提取的chunk最少被引用一次
minChunks : 1,
//按需加载时,并行加载的文件的最大数量为5
maxAsyncRequests : 5,
//入口js文件最大并行请求数量
maxInitialRequests : 3,
//名称连接符
automaticNameDelimiter : '~',
//可以使用命名规则
name ; true,
//分割chunk的组
cacheGroups : {
//node_modules中文件被打包到vendors 组的chunk中, --> vendors~xxx.js
//满足上方的公共规则,如 : 大小超过30kb,至少被引用一次
vendors : {
test : /[\\/]node_modules[\\/]/,
//优先级
priority : -10
},
default : {
//要提取的chunk最少被引用2次
minChunks : 2,
//优先级
priority : -20,
//如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
reuseExistingChunk : true,
}
}
},
//将当前模块的记录其他模块的hash单独打包为一个文件 runtime
//解决问题 : 修改a文件导致b文件的contenthash变化
runtimeChunk : {
name : entrypoint => `runtime-$(entrypoint.name)`
},
minimizer : {
//生产环境的压缩方案 : js和css
new TerserWebpackPlugin({
//开启缓存
cache : true,
//开启多进程打包
parallel : true
//启用source-map
sourceMap : true
})
}
}
mode : 'production',
}