开发环境
const webpack = require("webpack");
const path = require('path')
module.exports = {
// entry: {
// a: './src/0706/a.js',
// c: './src/0706/c.js',
// },
entry: "./src/0707/reactDemo.js",
output: {
filename: '[name]_dist.js',
path: path.resolve(__dirname, 'dist3'),
},
mode: 'development',
devtool: 'source-map',
module: {
rules: [
{
test:/.js$/,
use: 'babel-loader',
},
{
test: /.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test:/.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
},
{
test: /.(png|jpg|gif|jpeg)$/,
use: 'file-loader'
},
{
test: /.(png|jpg|jpeg|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 10240 * 10
}
}
},
{
test: /.(woff|woff2|eot|ttf|otf)$/,
use: 'file-loader'
}
],
},
// plugins: [
// new webpack.HotModuleReplacementPlugin()
// ],
// 在使用devServer的时候,如果hot为true的话,会自动帮我们添加HotModuleReplacementPlugin
// 如果使用自己实现的服务器,就需要自己添加
devServer: {
contentBase: './dist3',
hot: true
}
}
生产环境
const webpack = require("webpack");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// minicssextractplugin 推荐使用cssminimizerwebpackplugin来压缩css
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
// 根据模板生产html,并插入相应的chunk,同时也可以压缩html
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 清除构建产物的插件,注意这里的引入方式
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const path = require('path');
module.exports = {
// entry: {
// a: './src/0706/a.js',
// c: './src/0706/c.js',
// },
entry: "./src/0707/reactDemo.js",
output: {
// 文件指纹 chunkhash chunk改变就会重新生成
// hash 整个项目有文件改变就会重新生成
// contenthash 文件内容改变才会重新生成
filename: '[name]_[chunkhash:8].js',
path: path.resolve(__dirname, 'dist3'),
},
mode: 'production',
optimization: {
minimizer: [
// 压缩CSS
new CssMinimizerPlugin(),
// webpack5内置了terser-plugin,但是上面的插件会覆盖掉默认的terser-plugin,所以通过下面的一行来将默认的插件加回去
'...'
]
},
module: {
rules: [
{
test:/.js$/,
use: 'babel-loader',
},
{
test: /.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
test:/.less$/,
use: [
// 使用miniCssExtractPlugin提取css后,这里需要替换成它的loader
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader'
]
},
{
test: /.(png|jpg|gif|jpeg)$/,
use: {
loader: 'file-loader',
options: {
name: '[name]_[hash:8].[ext]'
}
}
},
{
test: /.(png|jpg|jpeg|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 10240 * 10
}
}
},
{
test: /.(woff|woff2|eot|ttf|otf)$/,
use: 'file-loader'
}
],
},
plugins: [
new MiniCssExtractPlugin({
// 使用contenthash 这样如果只改变了js的话css也无需重新生成
filename: '[name]_[contenthash:8].css'
}),
new HtmlWebpackPlugin({
// 模板所在路径
template: path.resolve(__dirname, 'src/index.html'),
// 生成的html的名字
filename: 'index2.html',
// 用到了哪个chunk
// chunks: ['a']
// 压缩选项
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: true
}
})
]
}
自动添加CSS前缀
- 使用postcss-loader + autoprefixer
- 添加postcss.config.js 新版本直接在webpack配置文件里添加会报错,所以需要写到一个独立的配置文件里
module.exports = {
plugins: [
require('autoprefixer')({
overrideBrowserslist: ['last 2 version', '>1%', 'ios 7']
})
]
}
- 添加loader
{
test: /.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader' // 这里为新加的loader
]
},
移动端适配 css px自动转rem
- 使用手淘lib-flexible 动态计算font-size
// 将lib-flexible静态内联到html上,因为要最先执行计算
// 在头部加入如下代码
// 使用了raw-loader,相当于在对应的位置是插入字符串
// 需注意raw-loader新老版本引入的差异
- 使用px2rem-loader 将px转成rem
{
test: /.less$/
use: [
'style-loader',
'css-loader',
'less-loader',
{
loader: 'px2rem-loader',
options: {
// 以设计稿宽度750px为例,1rem = 75px
remUnit: 75,
// 转换后的小数点后保留位数
remPrecision: 8,
}
}
]
}
- 代码里面直接按设计稿一样写px
// 下面的px最后会被转成em,如果有些特殊的地方不想转,可写成大写PX
.box {
width: 100px;
height: 100px;
// 写成大写则不会被转换
border: 1PX;
}
代码分割
- 利用splitChunks plugin将公共代码抽离
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
chunks: 'all',
name: 'vendors',
// 将react和react-dom提取出一个包
test: /(react|react-dom)/
},
common: {
name: 'common',
chunks: 'all',
minSize: 0,
// 被引用两次以上的提取出一个包
minChunks: 2
}
}
}
}
参考 前端进阶面试题详细解答
动态import 懒加载
- 通过ES6的动态import + babel插件@babel/plugin-syntax-dynamic-import
//babel配置里增加
plugins: [
'@babel/plugin-syntax-dynamic-import'
]
// 代码里按需引入
import('xxx').then(res => res.default);
webpack结合eslint
- 以react为例,用到几个插件eslint eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y
- 安装解析器babel-eslint
- 用airbnb的规则,需安装eslint-config-airbnb
- 安装eslint-loader
- 增加eslint配置 eslintrc.js
module.exports = {
// 使用babel-eslint作为解析器
"parser": "babel-eslint",
// 继承airbnb的规则
"extends": ["airbnb"],
// 指定环境,这样使用全局变量的时候不会报错
"env": {
"browser": true,
"node": true
},
// 自定义规则覆盖默认规则
"rules": {
// 使用4个空格缩进,否则error
"indent": ["error", 4]
}
}
webpack打包库
- 代码写好后,webpack配置如下
const path = require('path');
module.exports = {
// 同时提供未压缩和压缩的版本
entry: {
'mylibrary': './src/entry.js',
'mylibrary.min': './src/entry.js'
},
output: {
path: path.resolve(__dirname, 'lib'),
// mylibrary.js mylibrary.min.js
filename: '[name].js',
// 对外暴露的库的名称
library: 'mylibrary',
// 支持cjs, ejs, script脚本等引入方式
libraryTarget: 'umd',
// 不加这个的话,使用的时候可能需要mylibrary.default
libraryExport: 'default'
}
}
- 添加terser-webpack-plugin进行压缩
const TerserPlugin = require(‘terser-webpack-plugin’);
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
// 只对min版本压缩
test: /.min.js/
})
]
}
- package.json 指定入口文件
"main": "index.js"
- index.js里面做环境判断
if(process.env.NODE_EVN === 'production') {
module.exports = require('./lib/mylibrary.min.js');
} else {
module.exports = require('./lib/mulibrary.js');
}
主动捕获异常
- 通过插件主动捕获异常
plugins: [
function() {
this.hooks.done.tap('done', (stats) => {
if(stats.compilation && stats.compilation.errors.length > 1) {
console.log('error')
}
})
}
]
构建优化
速度优化:
- speed-measure-webpack-plugin分析构建速度
const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin');
const spm = new SpeedMeasureWebpackPlugin();
module.exports = spm.wrap({...});
- thread-loader开启多进程,放在需要的loader上面
module: {
rules: [
{
test: /.js$/
use: [
{
loader: 'thread-loader',
options: {
workers: 3
}
}
]
}
]
}
- include exclude缩小构建目标
- resolve减少文件搜索范围
modules.exports = {
...
resolve: {
// 指定node_modules的路径,减少模块搜索层级
modules: [path.resolve(__dirname, 'node_modules')],
// import react的时候直接从指定的路径去找
alias: {
react: path.resolve(__dirname, './node_modules/react/dist/react.min.js')
},
// import xx from 'a'的时候,只找.js后缀的
// 高频文件后缀名放前面
extensions: ['.js'],
// 指定入口,避免不必要的分析
mainFields: ['main']
}
}
- 开启babel-loader缓存
// 仅需加个url参数
module: {
rules: [
{
test: /.js$/,
use: ['babel-loader?cacheDirectory=true'
}
]
}
- terser-webpack-plugin开启缓存
// webpack5之后不再用这种方式
new TerserWebpackPlugin({
cache: true
})
- cache-loader缓存
- hard-source-webpack-plugin缓存,减少二次构建时间
plugins: [new HardSourceWebpackPlugin()]
- terser-webpack-plugin默认开启了JS多进程压缩
optimization: {
minimizer: [
new TerserWebpackPlugin({
// 指定进程数量
parallel: 4
})
]
}
- 使用DLLPlugin进行分包
先构建出单独的包
// 单独的配置文件用于生成包
module.exports = {
entry: {
// 将react react-dom抽离出单独的包
library: ['react', 'react-dom']
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, 'dist3/lib')
library: '[name]'
},
plugins: [
// 使用DLLPlugin抽离,生成manifest
new webpack.DllPlugin({
name: '[name]_2',
path: path.resolve(__dirname, 'dist3/lib/[name].json'),
}),
// new CleanWebpackPlugin(),
]
}
再通过manifest关联抽离的包
// webpack.prod.config.js
new webpack.DllReferencePlugin({
manifest: require('./dist3/lib/library.json')
})
最后将抽离的包插入html模板中
- noParse 对完全不需要解析的库进行忽略 (不去解析但仍会打包到 bundle 中,注意被忽略掉的文件里不应该包含 import、require、define 等模块化语句)
体积优化
- webpack-bundle-analyzer分析体积
plugins: [
new WebpackBundleAnalyzer()
]
- 图片压缩
使用image-webpack-loader
rules: [{
test: /.(gif|png|jpe?g|svg)$/i,
use: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
},
// optipng.enabled: false will disable optipng
optipng: {
enabled: false,
},
pngquant: {
quality: [0.65, 0.90],
speed: 4
},
gifsicle: {
interlaced: false,
},
// the webp option will enable WEBP
webp: {
quality: 75
}
}
},
],
}]
- 对CSS 进行tree shaking
使用purgecss-webpack-plugin,要配合mini-css-extract-plugin一起使用
const purgecssPath = path.join(__dirname, 'src');
const glob = require('glob');
new PurgecssPlugin({
paths:
glob.sync(`${purgecssPath}/**/*`, { nodir: true }),
}),
- 动态polyfill
根据浏览器的user agent 动态下发polyfill
或者可以自建CDN
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net