dva+roadhog打包优化
标签: dva+roadhog打包优化 Webpack博客 51CTO博客
2023-04-27 18:23:53 269浏览
背景
公司有个老项目,使用还是dva+roadhog的打包构建工具,随着项目越来越大,代码越来越多,引入的包也越来越多,工程构件速度很慢,该项目用的是roadhog打包工具,roadhog引用的是af-webpack,而af-webpack又是对webpack3的封装,导致不能进行一些自定义的扩展,目前roadhog作者已经不维护了,所以想着在换掉roadhog包直接使用webpack3进行打包优化。
思路
删掉roadhog包,安装webpack3,webpack-dev-server,同时配置对应的plugin和loader
plugin和loader版本推荐
webpack包如下
webpack@3.5.6 webpack-dev-server@2.9.4
引入其他的plugin和loader
html-webpack-plugin@2.30.1 //生成html插件
html-webpack-externals-plugin@3.8.0 //外部链接引入包插件
happypack@4.0.0 //并发打包,thread-loader只支持webpack4,所以使用happypack
babel-loader@8.2.2 //解析js,jsx的loader
style-loader@0.20.3 //将编译完的css插入html的loader
css-loader@0.28.7 //将css代码编译的loader
less-loader@4.0.5 //将less转译成css的loader
url-loader@0.6.2 //处理图片资源的loader
file-loader@4.0.0 //处理字体/svg资源的loader
clean-webpack-plugin@2.0.0 //清理打包目录的plugin
uglifyjs-webpack-plugin@1.3.0 //并发压缩js的plugin
copy-webpack-plugin@4.4.1 //拷贝文件的plugin,比如静态图片资源
extract-text-webpack-plugin@3.0.0 //将css单独抽离为文件的
为啥想替换掉roadhog,主要是想用到happypack的并发打包能力,能大大加快打包速度。
项目目录

开发环境
下面贴一下开发环境下的webpack配置
let path = require('path')
let webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin');// 2.30.1
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
let HappyPack = require('happypack');
// const DIST_PATH = path.resolve(__dirname, '../dist'); // 生产目录
const APP_PATH = path.resolve(__dirname, '../src'); // 源文件目录
const theme = require('../src/theme');
// 主题颜色
module.exports = {
// bundle入口
entry: {
index: './src/index.js',
},
devtool: 'eval-cheap-module-source-map',
// bundle输出
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].async.js',
publicPath: '/',
},
plugins: [
new HtmlWebpackExternalsPlugin({
externals: [
{
// 引入的模块
module: 'lodash',
// cdn的地址
entry: 'https://xxxxxxxxx/lodash.js/4.17.21/lodash.min.js',
// 挂载到了window上的名称
global: '_',
},
{
// 引入的模块
module: 'ali-oss',
// cdn的地址
entry: 'https://xxxxxxxxx/xxx/xxx/aliyun-oss-sdk.min.1208.js',
// 挂载到了window上的名称
global: 'OSS',
},
],
}),
// 处理html
new HtmlWebpackPlugin({
template:
process.env.API_ENV == 'prod'
? './src/index-prod.ejs'
: process.env.API_ENV == 'staging'
? './src/index-staging.ejs'
: process.env.API_ENV == 'test'
? './src/index-test.ejs'
: process.env.API_ENV == 'dev'
? './src/index-dev.ejs'
: './src/index.ejs',
inject: 'body',
minify: {
html5: true,
},
hash: false,
chunksSortMode: 'manual',
chunks: ['manifest', 'vendor', 'antd', 'react', 'pdf', 'components', 'index'],
}),
new HappyPack({
id: 'babel',
loaders: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
],
}),
new HappyPack({
id: 'css1',
loaders: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
sourceMap: false,
},
},
],
}),
new HappyPack({
id: 'less1',
loaders: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true, // true -> import styles from 'xxx.less' false -> import 'xxx.less'
sourceMap: false,
localIdentName:
process.env.NODE_ENV === 'production' ? '[hash:base64]' : '[path][name]__[local]', // 指定样式名,加快本地开发编译速度,
},
},
{
loader: 'less-loader',
options: { javascriptEnabled: true, modifyVars: theme, sourceMap: false },
},
],
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity,
}),
],
module: {
noParse: /jquery/,
rules: [
{
test: /\.(js|jsx)?$/,
exclude: /node_modules/,
include: APP_PATH,
use: ['happypack/loader?id=babel'],
},
{
test: /\.css$/,
use: ['happypack/loader?id=css1'],
},
{
test: /\.less$/,
use: ['happypack/loader?id=less1'],
},
{
test: /\.(jpg|png|gif|bmp|jpeg)$/,
use: "url-loader",
},
{
test: /\.(woff|svg)$/,
use: 'file-loader',
exclude: /node_modules/,
},
],
/*
*解决Critical dependency: require function is used in a way in which dependencies cannot be statically extracted的问题
*unknownContextCritical : false,
*解决the request of a dependency is an expression
*/
exprContextCritical: false,
},
resolve: {
alias: {
components: path.resolve(__dirname, '../src/components/'),
services: path.resolve(__dirname, '../src/services/'),
common: path.resolve(__dirname, '../src/common/'),
utils: path.resolve(__dirname, '../src/utils/'),
routes: path.resolve(__dirname, '../src/routes/'),
assets: path.resolve(__dirname, '../src/assets/'),
'@': path.resolve(__dirname, '../src/'),
},
extensions: ['.js', '.jsx'], // 解析文件后缀
},
devServer: {
overlay: {
errors: true,
},
/*
* 用来编译完成之后最终控制台显示的输出
*stats: "errors-only",
*/
port: 8009,
host: 'localhost', // '0.0.0.0',
compress: true, // 启用 gzip
historyApiFallback: true,
hot: true, // 开启
https: false,
inline: true,
open: true,
// publicPath: '/',
headers: {
'Access-Control-Allow-Origin': '*',
},
proxy: {
// 连后端对应开发者电脑
'/zhApi': {
target: 'http://xxx.xx.xx.xxx:7014',
changeOrigin: true,
pathRewrite: {
'^/zhApi': '',
},
},
},
},
}
配合开发环境的hmr,要把.babelrc.js也配置一下
@babel/preset-env
@babel/preset-react
@babel/plugin-proposal-class-properties@7.17.12
@babel/plugin-proposal-decorators@7.18.2
@babel/plugin-proposal-private-methods@7.17.12
@babel/plugin-proposal-private-property-in-object@7.17.12
@babel/plugin-transform-runtime@7.4.4
babel-plugin-dva-hmr //这个是hmr的关键,其他的都是针对js新特性的一些编译包
babel-plugin-dynamic-import-node@2.3.3 //这个能加速开发时编译速度
const path = require('path');
module.exports = {
presets: [
['@babel/preset-env'],
['@babel/preset-react', {runtime: 'automatic'}],
],
plugins: [
[
'module-resolver',//个人感觉不需要
{
alias: {
components: path.join(__dirname, './src/components'),
},
},
],
[
'import',
{
libraryName: 'antd',
libraryDirectory:"es",
style: "css", // or 'css'
},
],
["@babel/plugin-proposal-decorators", { legacy: true}],
["@babel/plugin-proposal-class-properties", { "loose" : true }],
["@babel/plugin-proposal-private-property-in-object", { "loose": true }],
["@babel/plugin-proposal-private-methods", { "loose": true }],
'@babel/plugin-transform-runtime',
'dva-hmr',
'@babel/plugin-syntax-dynamic-import'
],
'env': {
'development': {
'plugins': ['dynamic-import-node']
}
}
};
生产环境
let path = require('path')
let webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin');// 2.30.1
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
let HappyPack = require('happypack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); // 单独打包css
const DIST_PATH = path.resolve(__dirname, '../dist'); // 生产目录
const APP_PATH = path.resolve(__dirname, '../src'); // 源文件目录
let Webpack = require('webpack');
const SRC_PUBLIC_PATH = path.resolve(__dirname, '../public');
const theme = require('../src/theme');
// 主题颜色
module.exports = {
// bundle入口
entry: {
index: './src/index.js',
},
devtool: 'hidden-source-map',
output: {
path: path.join(__dirname, '../dist'),
filename: '[name].[contentHash].js',
chunkFilename: '[name].[contentHash].async.js',
publicPath: '/',
pathinfo: false,
},
plugins: [
new HtmlWebpackExternalsPlugin({
externals: [
{
// 引入的模块
module: 'lodash',
// cdn的地址
entry: 'https://xxxxxxx/xxx/xxxx/lodash.js/4.17.21/lodash.min.js',
// 挂载到了window上的名称
global: '_',
},
{
// 引入的模块
module: 'ali-oss',
// cdn的地址
entry: 'https://xxxxxx/xxxx/xxx/aliyun-oss-sdk.min.1208.js',
// 挂载到了window上的名称
global: 'OSS',
},
],
}),
// 处理html
new HtmlWebpackPlugin({
template:
process.env.API_ENV == 'prod'
? './src/index-prod.ejs'
: process.env.API_ENV == 'staging'
? './src/index-staging.ejs'
: process.env.API_ENV == 'test'
? './src/index-test.ejs'
: process.env.API_ENV == 'dev'
? './src/index-dev.ejs'
: './src/index.ejs',
favicon: `${SRC_PUBLIC_PATH}/favicon.png`,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true,
},
hash: false,
chunksSortMode: 'manual',
chunks: ['manifest', 'vendor', 'antd', 'react', 'pdf', 'components', 'index'],
}),
new UglifyJsPlugin({
sourceMap: false,
cache: false, // 是否启用文件缓存,默认缓存在node_modules/.cache/uglifyjs-webpack-plugin.目录
parallel: true, // 使用多进程并行运行来提高构建速度
extractComments: false,
uglifyOptions: {
warnings: false,
compress: {
drop_console: true,
drop_debugger: true,
unused: false,
},
},
}),
new HappyPack({
id: 'babel',
loaders: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
],
}),
new HappyPack({
id: 'css1',
loaders: [
// { loader: 'style-loader' },
{ loader: 'css-loader',
options: { sourceMap: false },
},
],
}),
new HappyPack({
id: 'less1',
loaders: [
// { loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true, // true -> import styles from 'xxx.less' false -> import 'xxx.less'
sourceMap: false,
localIdentName:
process.env.NODE_ENV === 'production' ? '[hash:base64]' : '[path][name]__[local]', // 指定样式名,加快本地开发编译速度,
},
},
{
loader: 'less-loader',
options: { javascriptEnabled: true, modifyVars: theme, sourceMap: false },
},
],
}),
new webpack.optimize.CommonsChunkPlugin({
names: ['vendor'], /* 对应上面的提取的公共文件模块 */
chunks: ['index'],
minChunks(module) {
return (
module.resource && /\.(js|jsx)$/.test(module.resource) && module.resource.includes('node_modules')
)
},
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'antd',
chunks: ['vendor'],
minChunks(module) {
return module.resource && /(\/antd|\/antd-mobile|\/dva|\/dva-loading)/.test(module.resource)
},
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'react',
chunks: ['vendor'],
minChunks(module) {
const tests = /(\/react-redux\/)|(\/react\/)|(\/react-router-dom\/)|(\/react-lazy-load\/)|(\/react-router-redux\/)|(\/react-router\/)|(\/react-is\/)|(\/react-dom\/)|(\/react-lifecycles-compat\/)/
return module.context && tests.test(module.context)
},
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'pdf',
chunks: ['vendor'],
minChunks(module) {
return module.context && /(react-pdf)|(pdfjs-dist)/.test(module.context)
},
}),
new webpack.optimize.CommonsChunkPlugin({
names: ['components'],
chunks: ['index'],
minChunks(module) {
const flag = module.resource && (/src\/(components|common|layouts|models|services|utils)/.test(module.resource));
return (
module.resource && /\.(js|jsx)$/.test(module.resource) && flag
)
},
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity,
}),
new CleanWebpackPlugin(),
new CopyWebpackPlugin([
{
from: `${SRC_PUBLIC_PATH}`,
to: `${DIST_PATH}/static`,
},
]),
new ExtractTextPlugin({
filename: '[name].[contenthash:8].css',
allChunks: true,
}),
new Webpack.IgnorePlugin(/\.\/locale/, /moment/), // moment这个库中,如果引用了./locale/目录的内容,就忽略掉,不会打包进去
],
module: {
noParse: /jquery/,
rules: [
{
test: /\.(js|jsx)?$/,
exclude: /node_modules/,
include: APP_PATH,
use: ['happypack/loader?id=babel'],
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: `${path.resolve(__dirname, '../node_modules', 'happypack/loader')}?id=css1`,
}
),
},
{
test: /\.less$/, // ['happypack/loader?id=less1']
include: APP_PATH,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: `${path.resolve(__dirname, '../node_modules', 'happypack/loader')}?id=less1`,
}),
},
{
test: /\.(jpg|png|gif|bmp|jpeg)$/,
use: {
loader: "url-loader",
options: {
esModule: false, // 这里设置为false
// outputPath:'static/',//输出**文件夹
publicPath: '/',
name: '[name].[ext]',
limit: 8 * 1024, // 是把小于8 kb的文件打成Base64的格式,写入JS
},
},
},
{
test: /\.(woff|svg)$/,
use: 'file-loader',
exclude: /node_modules/,
},
],
/*
*解决Critical dependency: require function is used in a way in which dependencies cannot be statically extracted的问题
*unknownContextCritical : false,
*解决the request of a dependency is an expression
*/
exprContextCritical: false,
},
resolve: {
alias: {
components: path.resolve(__dirname, '../src/components/'),
services: path.resolve(__dirname, '../src/services/'),
common: path.resolve(__dirname, '../src/common/'),
utils: path.resolve(__dirname, '../src/utils/'),
routes: path.resolve(__dirname, '../src/routes/'),
assets: path.resolve(__dirname, '../src/assets/'),
'@': path.resolve(__dirname, '../src/'),
},
extensions: ['.js', '.jsx'], // 解析文件后缀
},
}
问题
1.HardSourceWebpackPlugin 这个缓存插件好像跟happypack不兼容,不然开发环境二次编译会更加快速
2.webpack3还是比较老了,很多新特性和新插件都不支持,所以后续考虑升级到webpack5试试
好博客就要一起分享哦!分享海报
此处可发布评论
评论(0)展开评论
展开评论
您可能感兴趣的博客


