本文是自学webpack的全过程,能够结合Vue、React框架直接使用,并且总结了关于webpack的代码优化内容,希望对您有帮助。
附上个人git仓库地址:https://gitee.com/zhaosir1/webpack_code.git
开发环境配置
-
在根目录创建config文件夹,里面分别创建webpack.dev.js(开发环境)和webpack.prod.js(生产环境)配置文件。
-
文件目录结构(仅用于测试webpack的配置)。
-
webpack.dev.js文件配置。
const ESLintPlugin = require('eslint-webpack-plugin'); //语法检查插件
const HtmlWebpackPlugin = require('html-webpack-plugin'); //处理html文件的插件
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); //解析css的插件
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");//压缩css的插件
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");//压缩图片的插件
const path = require('path') //处理路径
const os = require("os"); //获取cpu的内核数量,
const TerserPlugin = require("terser-webpack-plugin");//压缩js的插件
// 核数
const threads = os.cpus().length;
// 获取处理样式的loder (由于很多css的loader用法很相似,因此提取出公共部分代码)
function getStyleLoder(pre) {
return [ //执行顺序从下到上
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
pre //如需使用其他loader可以通过传参的方式引入
].filter(Boolean) //过滤掉undefined
}
module.exports = {
// 打包入口文件
entry: './src/main.js',
// 文件输出配置
output: {
// 输出路径
// path: path.resolve(__dirname, 'dist'),
// 开发环境没有输出-可以配置 undefined
path: undefined,
// 输出的文件名
filename: 'static/js/[name].js',
// 自动清空上传打包的资源
// 开发环境没有输出打包资源所以不用配置
// clean:true
// 打包输出其他文件命名
chunkFilename:'static/js/[name].chunk.js',
// 图片 字体等通过type:asset处理资源命名方式
assetModuleFilename:'static/image/[hash:10][ext][query]'
},
// 加载器
module: {
rules: [ //loder配置
{
oneOf: [{ //处理css文件
test: /\.css$/,
use: getStyleLoder()
},
{//处理less文件
test: /\.less$/,
use: getStyleLoder("less-loader")
},
{//处理sass scss文件
test: /\.s[ac]ss$/,
use: getStyleLoder("sass-loader")
},
{//处理styl文件
test: /\.styl$/,
use: getStyleLoder("stylus-loader")
},
{//处理图片资源
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 //小于10kb转base64格式 减少请求次数
}
},
// generator: { //图片输出位置--开发环境无需配置
// filename: 'static/image/[hash:10][ext][query]'
// }
},
{ //处理字体图标音视频等其他资源文件
test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
type: "asset/resource",
// generator: { //输出位置--开发环境无需配置
// filename: 'static/media/[hash:10][ext][query]'
// }
},
{ //处理js文件 解决低版本浏览器对js的兼容问题
test: /\.m?js$/,
// exclude: /(node_modules)/, //排除node_modules文件夹
include: path.resolve(__dirname, '../src'), //只处理src文件夹下面的js文件(exclude|include 两者选择其一即可)
use: [{
loader: 'thread-loader', //开启多进程
options: {
works: threads //进程数量
}
},
{
loader: 'babel-loader',
options: {
// presets: ['@babel/preset-env'] //babel预设也可抽离出单独文件进行配置(babel.config.js)文件
cacheDirectory: true, //开启babel缓存
cacheCompression: false, //关闭缓存文件压缩
plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积
},
}
]
}
]
}
]
},
// 插件
plugins: [
new ESLintPlugin({ //代码检查
context: path.resolve(__dirname, '../src'),
exclude: 'node_modules', //默认值,排除node_modules文件夹
cache:true, //开启缓存
cacheLocation:path.resolve(__dirname,'../node_modules/.cache/eslintcache'),
threads //开启多进程
}),
new HtmlWebpackPlugin({ //处理html文件 自动引入资源
template: path.resolve(__dirname, '../public/index.html')
}),
new MiniCssExtractPlugin(),//解析css的插件
// new CssMinimizerPlugin(),
// new TerserPlugin({
// parallel:threads//开启多进程
// })
],
//webpack5之后 官方建议把处理文件压缩的插件放在optimization配置项里面
optimization: { //压缩的操作也可以放着
minimizer: [
// 压缩css
new CssMinimizerPlugin(),
// 压缩js
new TerserPlugin({
parallel: threads //开启多进程
}),
// 压缩图片-按照官方写即可
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
[
"svgo",
{
plugins: [
"preset-default",
"prefixIds",
{
name: "sortAttrs",
params: {
xmlnsOrder: "alphabetical",
},
},
],
},
],
],
},
},
}),
],
// 代码分割配置
splitChunks: {
chunks: 'all'
}
},
// 开发服务器: 内存中运行
devServer: {
host: "localhost", // 启动服务器域名
port: "3000", // 启动服务器端口号
open: true, // 是否自动打开浏览器
hot: true, //开启HDM(热更新)
},
// 模式
mode: 'development',
// 用于配置soutceMap(源文件映射),
// 当代码报错时,能够清晰的显示出是具体对于源文件的哪一行报错
devtool: "cheap-module-source-map",
}
- 由于使用到了eslint语法检查,所以还要在根目录创建.eslintignore文件(忽略不用检查的文件夹)和.eslintrc.js文件(语法检查配置项)
- .eslinignore文件,直接把需要忽略的文件夹名称放进去即可,和git的.ignore文件用法一样
- .eslintrc.js文件配置内容:
module.exports = {
// 继承 Eslint 规则
// 如需使用vue或者react 此处直接继承官方的写法即可 (vue-app|react-app)
extends: ["eslint:recommended"],
env: {
node: true, // 启用node中全局变量
browser: true, // 启用浏览器中全局变量
},
parserOptions: {
ecmaVersion: 6, //es6
sourceType: "module", //es module
},
// 如需重构,在下面配置即可覆盖
rules: {
"no-var": 2, // 不能使用 var 定义变量
},
plugins:['import'] //允许使用import关键词引入文件
};
- babel智能预设配置,在根目录创建babel.config.js文件:
module.exports = {
presets: [
//智能预设,能够编译ES6语法
// 如果使用vue和react可以直接使用方法预设 presets:['react-app'] | presets:['vue-app']
['@babel/preset-env', {
//usage 会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加。
useBuiltIns: 'usage',
corejs: 3 //确定corejs的版本
}],
]
}
- 由于配置了浏览器的兼容,为了摒弃一些不常用的浏览器,要在package.json里面添加配置:
"browserslist": [
"last 2 version",
"> 1%",
"not dead"
],
“last 2 version":兼容到浏览器最新的两个版本
“> 1%” :兼容99%的浏览器
“not dead”:摒弃已经废弃的浏览器
到此为止开发环境的webpack配置已经完成可通过命令:webpack serve --config ./config/webpack.dev.js进行开发环境的打包操作。
生产环境配置
- 在config文件夹创建webpack.prod.js配置文件:
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin');
const os = require("os");
const WorkboxPlugin = require('workbox-webpack-plugin');
const TerserPlugin = require("terser-webpack-plugin");
// 核数
const threads = os.cpus().length;
const path = require('path')
// 获取处理样式的loder
function getStyleLoder(pre) {
return [ //执行顺序从下到上
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
pre
].filter(Boolean)
}
module.exports = {
// 入口
entry: './src/main.js',
// 输出
output: {
// 输出路径
path: path.resolve(__dirname, '../dist'),
// 入口文件打包输出文件名
filename: 'static/js/[name].js',
// 自动清空上传打包的资源
clean: true,
// 打包输出其他文件命名
chunkFilename:'static/js/[name].chunk.js',
// 图片 字体等通过type:asset处理资源命名方式
assetModuleFilename:'static/image/[hash:10][ext][query]'
},
// 加载器
module: {
rules: [ //loder配置
{
oneOf: [{
test: /\.css$/,
use: getStyleLoder()
},
{
test: /\.less$/,
use: getStyleLoder("less-loader")
},
{
test: /\.s[ac]ss$/,
use: getStyleLoder("sass-loader")
},
{
test: /\.styl$/,
use: getStyleLoder("stylus-loader")
},
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 //小于10kb转base64 减少请求次数
}
},
// generator: { //图片输出位置
// filename: 'static/image/[hash:10][ext][query]'
// }
},
{ //字体图标音视频等其他资源文件
test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
type: "asset/resource",
// generator: { //输出位置
// filename: 'static/media/[hash:10][ext][query]'
// }
},
{ //处理js文件 兼容 todo
test: /\.m?js$/,
// exclude: /(node_modules)/, //排除node_modules
include: path.resolve(__dirname, '../src'), //只处理src文件夹下面的js文件
use: [{
loader: 'thread-loader', //开启多进程
options: {
works: threads //进程数量
}
},
{
loader: 'babel-loader',
options: {
// presets: ['@babel/preset-env']
cacheDirectory: true, //开启babel缓存
cacheCompression: false, //关闭缓存文件压缩
plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积
},
}
]
}
]
}
]
},
// 插件
plugins: [
new ESLintPlugin({ //代码检查
context: path.resolve(__dirname, '../src'),
exclude: 'node_modules', //默认值,排除node_modules文件夹
cache: true, //开启缓存
cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'),
threads //开启多进程
}),
new HtmlWebpackPlugin({ //处理html文件 自动引入资源
template: path.resolve(__dirname, '../public/index.html')
}),
new MiniCssExtractPlugin({ //提取css成单独的文件
filename: 'static/css/[name].[contenthash:10].css',
chunkFilename: 'static/css/[name].chunk.[contenthash:10].css',
}),
// new CssMinimizerPlugin(),
// new TerserPlugin({
// parallel:threads//开启多进程
// })
// js按需加载 缓存
new PreloadWebpackPlugin({
rel:'preload',
as:'script'
// rel:'prefetch'
}),
new WorkboxPlugin.GenerateSW({
// 这些选项帮助快速启用 ServiceWorkers
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true,
}),
],
optimization: { //压缩的操作也可以放着
minimizer: [
// 压缩css
new CssMinimizerPlugin(),
// 压缩js
new TerserPlugin({
parallel: threads //开启多进程
}),
// 压缩图片
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
["gifsicle", {
interlaced: true
}],
["jpegtran", {
progressive: true
}],
["optipng", {
optimizationLevel: 5
}],
[
"svgo",
{
plugins: [
"preset-default",
"prefixIds",
{
name: "sortAttrs",
params: {
xmlnsOrder: "alphabetical",
},
},
],
},
],
],
},
},
})
],
// 代码分割配置
splitChunks: {
chunks: 'all'
},
runtimeChunk: {
name:(entrypoint)=>`runtuime~${entrypoint.name}.js`,
}
},
// 开发服务器: 内存中运行
// 生产模式不需要
// devServer: {
// host: "localhost", // 启动服务器域名
// port: "3000", // 启动服务器端口号
// open: true, // 是否自动打开浏览器
// },
// 模式
mode: 'production',
devtool: "source-map",
}
- 生产环境配置和开发环境配置大部分内容都是一样的。运行打包命令:webpack --config ./config/webpack.prod.js即可进行生产环境的打包操作
- 为了更方便打包,可在package.json文件进行命令的配置:
"scripts": {
"start": "npm run dev",
"dev": "webpack serve --config ./config/webpack.dev.js",
"build": "webpack --config ./config/webpack.prod.js"
},
即开发环境打包命令:npm start 或者 yarn start
生产环境打包命令:npm run build 或者 yarn run build
总结
1. 提升开发体验
- 使用 Source Map 让开发或上线时代码报错能有更加准确的错误提示。
2. 提升 webpack 打包构建速度
- 使用 HotModuleReplacement 让开发时只重新编译打包更新变化了的代码,不变的代码使用缓存,从而使更新速度更快。
- 使用 OneOf 让资源文件一旦被某个 loader 处理了,就不会继续遍历了,打包速度更快。
- 使用 Include/Exclude 排除或只检测某些文件,处理的文件更少,速度更快。
- 使用 Cache 对 eslint 和 babel 处理的结果进行缓存,让第二次打包速度更快。
- 使用 Thead 多进程处理 eslint 和 babel 任务,速度更快。(需要注意的是,进程启动通信都有开销的,要在比较多代码处理时使用才有效果)
3. 减少代码体积
- 使用 Tree Shaking 剔除了没有使用的多余代码,让代码体积更小。
- 使用 @babel/plugin-transform-runtime 插件对 babel 进行处理,让辅助代码从中引入,而不是每个文件都生成辅助代码,从而体积更小。
- 使用 Image Minimizer 对项目中图片进行压缩,体积更小,请求速度更快。(需要注意的是,如果项目中图片都是在线链接,那么就不需要了。本地项目静态图片才需要进行压缩。)
4. 优化代码运行性能
- 使用 Code Split 对代码进行分割成多个 js 文件,从而使单个文件体积更小,并行加载 js 速度更快。2. 并通过 import 动态导入语法进行按需加载,从而达到需要使用时才加载该资源,不用时不加载资源。
- 使用 Preload / Prefetch 对代码进行提前加载,等未来需要使用时就能直接使用,从而用户体验更好。
- 使用 Network Cache 能对输出资源文件进行更好的命名,将来好做缓存,从而用户体验更好。
- 使用 Core-js 对 js 进行兼容性处理,让我们代码能运行在低版本浏览器。
- 使用 PWA 能让代码离线也能访问,从而提升用户体验。
特别感谢:尚硅谷
本人webpack初学者,如有大神有高见随时求打扰,如有不解也可在评论区相互探讨相互进步。
给个小赞谢谢🤞🤞🤞🤞🤞