如今作的項目仍是基於早期的ant-design-pro
那套東西,技術棧是 react + antd + dva + roadhog。隨着項目的迭代,不知從什麼時候起,發現項目打包很慢,每次jenkins
上部署都要五、6分鐘的樣子。正好最近項目需求較少,正好有時間能夠搗鼓一下這個打包慢的問題。javascript
去roadhog的github下面搜索issues,發現有好多人和我遇到一樣的問題,解決方法大概就是把roadhog換成原汁原味的webpack4,因而我就開始着手改造了。css
刪除roadhog相關依賴html
"roadhog": "^2.5.0-beta.1",
"roadhog-api-doc": "^1.0.3",
複製代碼
在devDependencies添加webpack4須要用到相關依賴java
"webpack": "^4.8.1",
"webpack-cli": "^3.1.1",
"webpack-dev-server": "^3.1.4"
複製代碼
我完整的devDependencies是這樣的node
"@hot-loader/react-dom": "^16.8.6",
"@webassemblyjs/ast": "^1.3.1",
"@webassemblyjs/wasm-edit": "^1.3.1",
"address": "^1.0.3",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
"babel-loader": "^7.1.4",
"babel-plugin-import": "^1.7.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-plugin-transform-remove-console": "^6.9.2",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"babel-runtime": "^6.26.0",
"body-parser": "^1.18.3",
"clean-webpack-plugin": "^0.1.19",
"copy-webpack-plugin": "^4.5.1",
"cross-env": "^5.1.1",
"cross-port-killer": "^1.0.1",
"css-hot-loader": "^1.4.4",
"css-loader": "^0.28.11",
"cssnano": "^3.10.0",
"eslint": "^4.19.1",
"eslint-config-airbnb": "^16.1.0",
"eslint-plugin-compat": "^2.2.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.7.0",
"estraverse": "^4.2.0",
"file-loader": "^1.1.11",
"happypack": "^5.0.0-beta.4",
"hard-source-webpack-plugin": "^0.8.0",
"html-webpack-plugin": "^4.0.0-beta.5",
"husky": "^1.0.0-rc.4",
"less": "^3.0.4",
"less-loader": "^4.1.0",
"mini-css-extract-plugin": "^0.4.1",
"mockjs": "^1.0.1-beta3",
"optimize-css-assets-webpack-plugin": "^4.0.1",
"pro-download": "^1.0.1",
"react-hot-loader": "^4.8.4",
"redbox-react": "^1.5.0",
"redux-devtools": "^3.4.1",
"redux-devtools-dock-monitor": "^1.1.3",
"redux-devtools-log-monitor": "^1.4.0",
"regenerator-runtime": "^0.11.1",
"sass-loader": "^7.0.1",
"serve-index": "^1.9.1",
"style-loader": "^0.21.0",
"stylelint": "^9.2.0",
"stylelint-config-standard": "^18.2.0",
"type-is": "^1.6.15",
"uglifyjs-webpack-plugin": "^1.2.5",
"url-loader": "^1.0.1",
"webpack": "^4.8.1",
"webpack-bundle-analyzer": "^2.11.2",
"webpack-cli": "^3.1.1",
"webpack-dev-server": "^3.1.4"
複製代碼
修改scripts
裏的啓動、打包命令react
本地啓動命令:webpack
"start": "cross-env ESLINT=none webpack-dev-server --config=webpack.config.development.js --mode development"
複製代碼
打包命令:git
"build": "cross-env ESLINT=none webpack --config=webpack.config.production.js --mode production"
複製代碼
剛剛應該有注意到我上面的腳本里有用到webpack.config.development.js
和webpack.config.production.js
這兩個文件。github
這兩個文件是須要咱們手動新增到根目錄下面的。web
webpack.config.development.js
是用來給本地啓動用的,下面是這個文件裏的完整內容const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const theme = require('./src/theme');
module.exports = {
entry: path.resolve(__dirname, 'src', 'index.js'),
devServer: {
contentBase: path.resolve(__dirname, 'dist'),
host: 'localhost', // 主機地址
port: 8000, // 端口號
open: true,
inline: true,
openPage: 'ioc/#/user/login',
hot: true,
publicPath: '/ioc/',
historyApiFallback: true,
overlay: {
errors: true,
},
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
publicPath: './',
chunkFilename: '[name].async.js',
},
resolve: {
alias: {
src: path.resolve(__dirname, 'src/'),
},
},
stats: {
children: false,
warningsFilter: warn => warn.indexOf('Conflicting order between:') > -1,
},
module: {
rules: [
{
test: /\.js$/,
include: [path.resolve(__dirname, 'src')],
loader: 'babel-loader?cacheDirectory',
},
{
test: /\.css$/,
use: [
'css-hot-loader',
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
],
},
{
test: /\.less$/,
use: [
'css-hot-loader',
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true,
localIdentName: '[name]_[local]-[hash:base64:5]',
},
},
{
loader: 'less-loader',
options: {
javascriptEnabled: true,
modifyVars: theme,
},
},
],
exclude: /node_modules/,
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
{
loader: 'less-loader',
options: {
javascriptEnabled: true,
modifyVars: theme,
},
},
],
exclude: /src/,
},
{
test: /\.(png|svg|jpg|gif|ttf)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
outputPath: './assets/',
},
},
],
},
],
},
node: {
fs: 'empty',
module: 'empty',
},
devtool: false,
optimization: {
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.(css|less)/,
chunks: 'all',
enforce: true,
},
},
},
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'src', 'index.ejs'), // 模板
filename: 'index.html',
hash: true, // 防止緩存
}),
new CleanWebpackPlugin(['dist']),
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, 'public'),
},
]),
new webpack.HotModuleReplacementPlugin(),
],
};
複製代碼
webpack.config.production.js
是用來給打包用的,下面是這個文件裏的完整內容const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HappyPack = require('happypack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
const theme = require('./src/theme');
module.exports = {
entry: path.resolve(__dirname, 'src', 'index.js'),
output: {
filename: '[name].[chunkhash:8].js',
path: path.resolve(__dirname, 'dist'),
publicPath: './',
chunkFilename: '[name].[chunkhash:8].async.js',
},
resolve: {
alias: {
src: path.resolve(__dirname, 'src/'),
},
},
module: {
rules: [
{
test: /\.js$/,
include: [path.resolve(__dirname, 'src')],
use: ['happypack/loader?id=babel'],
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
],
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true,
localIdentName: '[name]_[local]-[hash:base64:5]',
},
},
{
loader: 'less-loader',
options: {
javascriptEnabled: true,
modifyVars: theme,
},
},
],
exclude: /node_modules/,
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
{
loader: 'less-loader',
options: {
javascriptEnabled: true,
modifyVars: theme,
},
},
],
exclude: /src/,
},
{
test: /\.(png|svg|jpg|gif|ttf)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
outputPath: './assets/',
},
},
],
},
],
},
stats: {
children: false,
warningsFilter: warn => warn.indexOf('Conflicting order between:') > -1,
},
node: {
fs: 'empty',
module: 'empty',
},
optimization: {
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.(css|less)/,
chunks: 'all',
enforce: true,
},
commons: {
name: 'commons',
chunks: 'initial',
minChunks: 2,
},
vendors: {
name: 'vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
},
},
},
runtimeChunk: true,
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'src', 'index.ejs'), // 模板
filename: 'index.html',
hash: true, // 防止緩存
}),
new CleanWebpackPlugin(['dist']),
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, 'public'),
},
]),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano'),
cssProcessorOptions: { discardComments: { removeAll: true } },
canPrint: true,
}),
new HappyPack({
id: 'babel',
loaders: ['babel-loader?cacheDirectory'],
threadPool: happyThreadPool,
}),
new webpack.HashedModuleIdsPlugin(),
],
};
複製代碼
別小看上面這段配置,這但是我百度了一些webpack的配置模板,而後再去研究webpack4的接口文檔,再結合咱們的這個實際項目,不斷調試報錯,花了大半天時間搞出來的。
能夠看到development
的配置文件比production
多了devServer
和hmr
相關的配置,可是production
的比development
多了代碼壓縮、以及HappyPack
相關配置。因此我以爲分紅兩個配置文件仍是頗有必要的,這樣就能夠根據本地調試和線上打包具體需求的差別修改不一樣的配置文件。
找到根目錄下的.babelrc文件,稍做修改
{
"presets": ["env", "react", "stage-0"],
"plugins": [
"dva-hmr",
"transform-decorators-legacy",
["import", { "libraryName": "antd", "libraryDirectory": "es", "style": true }],
"transform-class-properties",
"transform-runtime"
],
"env": {
"production": {
"plugins": ["transform-remove-console"]
}
}
}
複製代碼
主要是添加了dva-hmr
這個熱更新插件。
刪除node_modules
。因爲 package.json
中依賴改的比較多,因此建議先把原來項目中的node_modules
文件夾刪掉,以避免形成沒必要要的衝突。
安裝依賴
npm i
複製代碼
或者
cnpm i
複製代碼
本地啓動
npm start
複製代碼
不出意外的話,本地應該能夠啓動成功,而且會自動打開瀏覽器頁面
打包
npm run build
複製代碼
本地啓動成功,再試着打一個線上環境的包,根目錄下會多出一個dist
文件夾,裏面就是打包好的文件。
打包時間明顯縮短了,這一點無論是本地打包或者jenkins
打包,都明顯提高。
遷移以前時間
遷移以後時間
node
版本最好升級到v8.11.1
以上。一開始我本地能夠打包成功,可是jenkens
上打包失敗了,看了一下log
npm ERR! node v6.16.0
npm ERR! npm v3.10.10
npm ERR! code ELIFECYCLE
npm ERR! green-town-ioc@0.3.0 build: `cross-env ESLINT=none webpack --config=webpack.config.development.js --mode development`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the green-town-ioc@0.3.0 build script 'cross-env ESLINT=none webpack --config=webpack.config.development.js --mode development'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
複製代碼
發現我本地電腦用的node
是v8.11.1
,可是jenkins
服務器的版本是v6.16.0
的,而後我讓咱們的運維童鞋把jenkins
上的node
版本升級了一下,就打包成功了。
關於HMR
,用的是dva
提供的babel插件dva-hmr
,這樣本地修改代碼,頁面就會自動刷新了。一開始我dev
配置把sourceMap
功能打開了,而後每次修改完代碼,就會隔好久頁面才能刷新,後來直接去掉了,熱更新就快了不少
DllReferencePlugin
插件。hmr
換成react-hot-loader
,這樣本地開發更新了代碼後就能夠保存react組件狀態。當時也試過用它,可是始終沒有成功,因此被迫無奈用了dva-hmr
。有遷移想法的小夥伴歡迎在評論區交流。