webpck
- 使用webpack需要先下载
webpack
、webpack-cli
包 - webpck可以处理
js
、json
资源
命令打包
webpck 文件路径 -o 输出路径 --moode=production
webpck 文件路径 -o 输出路径 --moode=development
配置打包
- 需要创建
webpck.config.js
- 如果需要使用dll,需要创建
webpck.dll.js
,不用重复打包dll - 项目中使用的是
ES6 import export
- webpack中使用的是
nodeJs commonJs require module.exports
// webpack.config.js
// resolve 用来拼接绝对路径
const { resolve } = require("path");
const fs = require('fs');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const WorkboxWebpackPlugin = require("workbox-webpack-plugin");
const webpack = require("webpack");
const AddAssetHtmlWebpackPlugin = require("add-asset-html-webpack-plugin");
const dll = path.resolve(__dirname, 'dll');
const files = fs.readdirSync(dll);
if (files && files.length) {
files.forEach((file) => {
if (/.*\.manifest.json/.test(file)) {
plugins.push(
// 告诉webpack那些库不打包,同时使用名称也用 manifest.json 中映射后的
new webpack.DllReferencePlugin({
manifest: path.resolve(dll, file)
})
);
}
if (/.*\.dll.*.js/.test(file)) {
plugins.push(
// 因为忽略了一些库不打包,所以在 html 中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(dll, file)
})
);
}
});
}
// 公用 css loader
const commonCssLoader = [
// 使用需下载 style-loader 包
// 创建 style 标签,将 js 中样式字符串插入,添加到 head 中生效
// 问题:由于构建后css是在js中,并且js体积变大引入较慢,会闪屏
// "style-loader",
// 解决:将构建后的 css 提取出来
MiniCssExtractPlugin.loader,
// 使用需下载 css-loader 包
// 将 css 文件转成 commonJs 模块加载到js中,内容是样式字符串
"css-loader",
// 使用需下载 postcss-loader postcss-preset-env 包
// css 兼容性处理
// "postcss-loader",
{
loader: "postcss-loader",
options: {
ident: "postcss",
plugins: () => [
// 帮 postcss 去 package.json 中找 browserslist 配置,通过配置加载指定的css兼容性样式
// browserslist 看下一个代码块
require("postcss-preset-env")()
]
}
}
]
module.exports = {
// 代码分割 方法1:单页面->单入口 多页面->多入口,需要经常修改,太笨重
// 代码分割 方法2: optimization.splitChunks
// 代码分割 方法3: 使用 import.then 语法动态导入,能叫该文件单独打包
// import(/* webpackChunkName: "test" */"./test")
// .then({ add } => {})
// entry: "./src/index.js",
entry: {
main: "./src/index.js",
test: "./src/test.js"
}
output: {
// name 取入口的文件名 main、test
filename: "js/[name].[contenthash:10].js",
// __dirname 当前文件的目录的绝对路径
path: resolve(__dirname, "build")
},
module: {
rules: [
{
// js 语法检查
// 检查规则在 package.json 中 eslintConfig 配置
// eslintConfig 看下一个代码块
// 项目代码中可以使用 // eslint-disable 等注释不对某行某文件不进行检查
test: /\.js$/,
exclude: /node_modules/,
// 优先执行,这个执行完后才执行 js 兼容性
enforce: "pre",
loader: "eslint-loader",
options: {
// 自动修复 eslint 错误
fix: true
}
},
// 以下 loader 只会匹配一个,不能有多个配置处理同一种类型文件
oneOf: [
{
// js 兼容性,ES6 -> ES5
// 使用需下载 babel-loader
test: /\.js$/,
exclude: /node_modules/,
use: [
{
// 开启多线程打包
loader: "thread-loader",
options: {
// 开启2个进程,如果不设置默认就是cpu进程-1
workers: 2
}
},
// === options.cacheDirectory = true,babel缓存
"babel-loader?cacheDirectory"
]
},
{
test: /\.css$/,
user: [...commonCssLoader, "css-loader"]
},
{
test: /\.css$/,
use: [
[...commonCssLoader],
// 使用需下载 less less-loader 包
// 将 less 文件编译成 css 文件
"less-loader"
]
},
{
// 问题:默认处理不了 html 中的图片
test: /\.(jpg|png|gif)$/,
// 使用需下载 file-loader url-loader,因为 url-loader 依赖 file-loader
// 使用一个 loader 不用写 use 数组
loader: "url-loader",
options: {
// 当图片小于8KB,就会base64处理
// 优点减少请求数量(减轻服务器压力),缺点文件体积会变大(请求速度变慢)
limit: 8 * 1024,
// 重命名图片名称
// [hash:10] 取十位 hash 值,ext 文件原来扩展名
name: "[hash:10].[ext]",
// 问题:html-loader 引入后的是 commonJs 模块, url-loader 默认使用 ES6模块解析
// 解析时会出现 [object Module]
// 解决:关闭 url-loader 的ES6模块化,使用 commonJs 解析
esModule: false,
// 修改构建后的图片路径
outputPath: "imgs"
}
},
{
test: /\.html$/,
// 使用需下载 html-loader
// 处理 html 中的图片(负责引入img,从而给 url-loader 处理)
loader: "html-loader"
},
{
// 除了css、js、html、图片外的其他资源
exclude: /\.(css|less|js|html|jpg|png|gif)$/,
loader: "file-loader",
options: {
name: "[hash:10].[ext]",
outputPath: "media"
}
}
]
]
},
plugins: [
// 复制 "./src/index.html" 文件,自动引入打包输出后的所有资源
new HtmlWebpackPlugin({
template: "./src/index.html",
// 压缩 html
minify: {
// 折叠空白区域
collapseWhitespace: true,
// 移除注释
removeComments: true,
userShortDoctype: true,
// 移除空元素
removeEmptyElements: true,
// 移除空属性
removeEmptyAttributes: true,
// 压缩文内 css
minifyCSS: true,
// 压缩文内 js
minifyJS: true,
// 压缩文内 网址
minifyURLs: true,
}
}),
new MiniCssExtractPlugin({
// 对输出的 css 指定目录重新命名
filename: "css/built.[contenthash:10].css"
}),
// 压缩 css
new OptimizeCssAssetsWebpackPlugin(),
// 压缩 js,如果只是单纯压缩代码也可以使用 mode:production
new TerserWebpackPlugin({
// 启用文件缓存
cache: true,
// 使用多进程并行执行,提升构建效率
parallel: true,
// 将错误信息位置映射到模块
sourceMap: true,
terserOptions: {
// 打包时剔除 console.log
drop_console: true,
// 打包时剔除 debugger
drop_debugger: true
}
}),
// js HMR 功能
new webpack.HotModuleReplacementPlugin(),
// PWA 离线可访问
// 帮助 serviceWorker 快速启动,删除旧的 serviceWorker,生成一个 service-worker.js 配置文件
// 需要在入口文件处理兼容性并注册serviceWorker
// if("serviceWorker" in navigator){
// window.addEventListener("load", () => {
// navigator.serviceWorker
// .register("/service-worker.js")
// .then(() => {})
// .catch(() => {})
// })
// }
// 下一次访问该服务器资源,就会从浏览器的 serviceWorker 读取上次的资源
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
skiipWaiting: true
}),
],
// mode: "prodution",
mode: "development",
// 开发服务器,自动编译、自动打开浏览器、自动刷新浏览器
// 特点:在内存中编译打包,不会有输出
// 使用需下载 webpack-dev-server
// 启动指令: 本地安装 npx webpack-dev-server、全局安装 webpack-dev-server
devServer: {
// 运行的项目的目录(构建后的)
contentBase: resolve(__dirname, "./build"),
// 启动 Gzip 压缩
compress: true,
// 端口
port: 3000,
// 自动打开浏览器
open: true,
// 开启 HMR 功能,如果修改了webpack配置,必须重启服务
// 问题:只要修改了一个模块,所有模块都会重新编译打包
// 解决: 使用HMR(只更新修改的模块)
// 样式文件:
// devServer.hot 能生效;
// 原因是 style-loader 内部实现,所以开发环境使用 style-loader,而不把 css 抽取出来;
// js文件:
// 默认不生效;
// 需要添加插件 webpack.HotModuleReplacementPlugin
// html文件:
// 默认不生效;
// 需要将 entry 修改为 ["./src/index.js", "./src/index.html"],将 html 引入;
// 修改会导致所有都更新,但不用处理因为就只有一个 html 文件;
hot: true
},
// 1. 不管单入口还是多入口,会将 node_modules 中代码单独打包成一个 chunk 输出
// 2. 自动分析多入口 chunk 是否有公共文件,如果有也会打包成单独chunk
optimization: {
splitChunks: {
chunks: "all"
}
},
// 不打包第三方包,在入口文件使用 cdn 引入
externals: {
// 忽略库名: npm包名
jquery: "jQuery"
}
}
// package.json 中 browserslist 配置
// 默认会使用 production ,与 webpack.config.js 中的 mode 无关,与 process.env.NODE_ENV 有关
browserslist: {
"development": [
// 兼容最新版本谷歌浏览器
"last chrome version",
],
"produciton": [
// 兼容99.8%的浏览器 && 不要没人用的浏览器
">0.2%",
"not dead",
"not op_mini all"
]
},
eslintConfig: {
// 这里是使用的airbnb-base规则,可以使用别的规则
// 使用需下载 eslint-config-airbnb-base eslint-plugin-import eslint 包
"extend": "airbnb-base",
"env": {
// 支持浏览器的 window navigator 等变量
"browser": true,
// 支持 node 的变量
"node": true
}
}
// webpack.dll.js
const { resolve } = require("path");
const webpack = require("webpack");
const venders = [
'axios',
'decimal.js',
'immutable',
'lodash',
'moment',
];
module.exports = {
mode: "production",
entry: {
venter: venders
},
output: {
filename: "[name].js",
path: resolve(__dirname, "dll"),
// 构建后的包里往外抛出的内容名称,类似 export jquery_12345
library:"[name]_[hash:5]"
},
plugins: [
// 映射构建后的包内容
new webpack.DllPlugin({
name: "[name]_[hash:5]",
path: resolve(__dirname, "dll/manifest.json")
})
]
}
懒加载 & 预加载
// 1. 将引入放到异步中就是懒加载,使用的时候才加载
// 2. 加上 webpackPrefetch 就是预加载,还未使用就加载了,使用的时候就是使用的缓存
// 预加载与正常加载的区别,预加载是等其他资源都加载完毕,浏览器空闲后才加载,但是兼容性不太好
document.getElement("btn").onclick = function() {
import(/* webpackChunkName: "test", webpackPrefetch: true */"./test").then({ add } => {})
}
优化
-
开发环境
- 打包构建速度
开启 HMR 热更新
- 调试
devtool: "eval-source-map"
-
生产环境
- 打包构建速度
oneOf & exclude & include 打包体积优化 压缩html、css、js 小图片转base64 合理配置hash值 开启Gzip 树摇(tre shaking) -> 去除无用代码 前提:1.使用ES6、2.mode为production 注意:package.json 设置不需要树摇的 sideEffects: ["*.css", "*.less"] 多进程打包 externals dll
- 运行性能
缓存 babel缓存 -> 让第二次打包速度更快 cacheDirectory: true 文件资源缓存 -> 让代码上线运行更快 hash、chunkhash、contenthash // 问题:如果服务器强缓存,引用的包做修改不会重新请求 // 解决:给文件名加上 hash 值,如果重新打包文件名称会改变就会重新请求 // 问题:每一次webpack构建时都会生成一个唯一的hash,改了一个文件重新打包所有的文件都会更改hash,文件名就都变了 // chunkhash 根据chunk生成的hash,但是也不行因为css是在js中被引用的,属于同一个chunk // contenthash 根据文件内容生成的hash,不同文件的一定不一样 code split 代码分割 懒加载预加载 PWA 离线访问