Open 404cat opened 5 years ago
公司老项目的技术配置为react+dva+roadhog,到我接手时已经迭代开发了好几年,项目文件和依赖都十分巨大,据交接给我的开发人员说可能由于第三方升级的原因,导致编译速度十分缓慢;开发时的热更新速度一般都是10-20s+,线上打包速度更是六七分钟以上,线上打包也不是经常操作,所以时间长点忍忍也就过去了, 但是开发时的热更新速度这么慢简直不能忍受;
package.json:"roadhog": "^1.1.2" 根目录:.roadhogrc.js文件(暂时先不删删除,因为需要兼容项目,所以这里面的一些配置要copy到webpack当中);
"roadhog": "^1.1.2"
.roadhogrc.js
首先安装webpack相关:
"webpack": "^4.8.1", "webpack-cli": "^3.3.5", "webpack-merge": "^4.2.1",
接着在项目根目录创建
webpack.common.js, // 用来放置一些公用的配置 webpack.dev.js // 用来放置开发配置 webpack.prod.js // 放置线上打包配置
具体可以查看webpack并结合自身项目来进行配置,这里贴一下我的配置:
common.js
const path = require('path') const webpack = require('webpack') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') // const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin // let bundleConfig = require('./bundle-config.json') const theme = require('../theme.config.js') const config = require('../src/utils/config') const nodeEnv = process.env.NODE_ENV || 'development' const isDev = nodeEnv !== 'production' console.log('webpack NODE_ENV CONFIG_ENV config.publicPath', process.env.NODE_ENV, process.env.CONFIG_ENV, config.publicPath, config.baseURL) module.exports = { cache: isDev, entry: { index: ['babel-polyfill', 'es6-symbol/polyfill', path.resolve(__dirname, '..', 'src', 'index.js')], }, output: { filename: isDev ? '[name].js' : '[name].[chunkhash:8].js', path: path.join(__dirname, '..', 'dist'), publicPath: config.publicPath, chunkFilename: isDev ? '[name].chunk.js' : '[name].[chunkhash:8].chunk.js', }, resolve: { alias: { src: path.resolve(__dirname, '..', 'src/'), '@config': path.resolve(__dirname, '..', './src/utils/config'), '@assets': path.resolve(__dirname, '..', './src/assets'), '@srcRoot': path.resolve(__dirname, '..', './src'), '@components': path.resolve(__dirname, '..', './src/components'), '@utils': path.resolve(__dirname, '..', './src/utils'), '@themes': path.resolve(__dirname, '..', './src/themes'), '@services': path.resolve(__dirname, '..', './src/services'), '@models': path.resolve(__dirname, '..', './src/models'), '@routes': path.resolve(__dirname, '..', './src/routes'), '@shared': path.resolve(__dirname, '..', './src/shared'), }, extensions: ['.js', '.jsx'], // 导入没有带后缀的文件 }, devtool: isDev ? 'inline-source-map' : 'hidden-source-map', module: { rules: [ { test: /\.(js|mjs|jsx|ts|tsx)$/, include: [path.resolve(__dirname, '..', 'src')], exclude: /node_modules/, 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: { net: 'empty', fs: 'empty', module: 'empty', child_process: 'empty', tls: 'empty', dgram: 'empty', }, optimization: { splitChunks: { chunks: 'async', minSize: 30000, minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, automaticNameDelimiter: '~', name: true, cacheGroups: { styles: { name: 'styles', test: /\.(css|less)/, chunks: 'async', enforce: true, }, commons: { test: /[\\/]node_modules[\\/]/, name: 'vendors', chunks: 'async', }, }, }, }, // 提取公共代码 plugins: [ new webpack.DefinePlugin({ 'process.env': {}, 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), 'process.env.CONFIG_ENV': JSON.stringify(process.env.CONFIG_ENV), 'process.env.THEME': JSON.stringify(theme), 'process.env.PUBLICPATH': JSON.stringify(config.publicPath), }), new MiniCssExtractPlugin({ filename: '[name].css', }), // new webpack.DllReferencePlugin({ // context: __dirname,'..', // // eslint-disable-next-line global-require // manifest: require('./dll/manifest.json'), // }), new HtmlWebpackPlugin({ template: path.resolve(__dirname, '..', 'src', 'index.ejs'), // 模板 filename: 'index.html', // vendorJsName: bundleConfig.vendor.js, // 加载dll文件 hash: true, // 防止缓存 minify: false, }), new CleanWebpackPlugin({ verbose: true, }), new CopyWebpackPlugin([ { from: path.resolve(__dirname, '..', 'public'), }, ]), // new BundleAnalyzerPlugin(), ], }
dev.js
const path = require('path') const webpack = require('webpack') const merge = require('webpack-merge') const common = require('./webpack.common') const { baseURL } = require('../src/utils/config') // const { publicPath, NODE_ENV } = require('../src/utils/config') // const theme = require('../theme.config.js') // const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin module.exports = merge(common, { devServer: { contentBase: path.resolve(__dirname, 'dist'), // 提供静态文件内容 host: 'localhost', port: 8008, open: true, inline: true, hot: true, publicPath: '/', historyApiFallback: true, clientLogLevel: 'none', overlay: { errors: true, }, proxy: { [baseURL]: { target: 'http://192.168.1.87:9003/', changeOrigin: true, secure: false, pathRewrite: { [baseURL]: '/aek-mspp', }, }, }, }, devtool: 'eval-source-map', stats: { children: false, warningsFilter: warn => warn.indexOf('Conflicting order between:') > -1, }, performance: { hints: 'warning', }, plugins: [ new webpack.HotModuleReplacementPlugin(), // new BundleAnalyzerPlugin(), ], })
prod.js
// const UglifyJsPlugin = require('uglifyjs-webpack-plugin') const merge = require('webpack-merge') const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin') const common = require('./webpack.common') module.exports = merge(common, { devtool: false, optimization: { minimizer: [ new ParallelUglifyPlugin({ // 多进程压缩 cacheDir: '.cache/', uglifyJS: { output: { comments: false, beautify: false, }, compress: { // warnings: false, drop_console: true, collapse_vars: true, reduce_vars: true, }, }, }), // new UglifyJsPlugin({ // 这个不用是因为上面的ParallelUglifyPlugin也可以起到压缩代码的作用 // exclude: /\.min\.js$/, // cache: true, // parallel: true, // 开启并行压缩,充分利用cpu // sourceMap: false, // extractComments: false, // 移除注释 // uglifyOptions: { // compress: { // unused: true, // warnings: false, // drop_debugger: true, // }, // output: { // comments: false, // }, // }, // }), // 压缩代码 ], }, })
babel配置 在根目录配置 .babelrc 文件
{ "presets": ["env", "react", "stage-0"], "plugins": [ "dva-hmr", "transform-decorators-legacy", ["import", { "libraryName": "antd", "style": true }], "transform-class-properties", "transform-runtime", "add-module-exports", "recharts", "lodash" ], "env": { "development": { "plugins": ["dynamic-import-node"] }, "production": { "plugins": ["transform-remove-console"] } } }
package.json
{ "private": true, "dependencies": { "antd": "^2.13.6", "axios": "^0.19.0", "babel-polyfill": "^6.23.0", "braft-editor": "^2.3.7", "braft-extensions": "0.0.18", "braft-polyfill": "0.0.2", "classnames": "^2.2.5", "countup.js": "^1.9.2", "decimal.js-light": "^2.3.0", "dva": "^2.0.1", "dva-loading": "^1.0.2", "dva-model-extend": "^0.1.1", "es6-symbol": "^3.1.1", "form-data": "^2.3.1", "lodash": "^4.17.4", "md5": "^2.2.1", "nprogress": "^0.2.0", "parse-domain": "^1.1.0", "path-to-regexp": "^3.0.0", "prop-types": "^15.5.10", "qrcode.react": "^0.8.0", "qs": "^6.2.0", "rc-tween-one": "^1.0.0", "react": "^15.5.4", "react-barcode": "^1.2.0", "react-color": "^2.17.3", "react-copy-to-clipboard": "^5.0.0", "react-dom": "^15.5.4", "react-helmet": "^5.0.0", "react-loadable": "^5.5.0", "react-modal": "^3.0.0", "react-pdf": "^2.2.0", "react-scroll": "^1.5.5", "recharts": "^1.0.0-alpha.1", "sockjs-client": "1.0.0", "stompjs": "^2.3.3" }, "devDependencies": { "@hot-loader/react-dom": "^16.8.6", "@webassemblyjs/ast": "^1.3.1", "@webassemblyjs/wasm-edit": "^1.3.1", "address": "^1.0.3", "assets-webpack-plugin": "^3.9.10", "babel-core": "^6.26.3", "babel-eslint": "^8.2.3", "babel-loader": "^7.1.4", "babel-plugin-add-module-exports": "^1.0.2", "babel-plugin-dev-expression": "^0.2.1", "babel-plugin-dva-hmr": "^0.3.2", "babel-plugin-dynamic-import-node": "^2.3.0", "babel-plugin-import": "^1.7.0", "babel-plugin-lodash": "^3.3.2", "babel-plugin-module-resolver": "^2.7.1", "babel-plugin-recharts": "^1.1.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", "better-npm-run": "^0.1.1", "body-parser": "^1.18.3", "clean-webpack-plugin": "^3.0.0", "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-import-resolver-babel-module": "^4.0.0-beta.3", "eslint-plugin-compat": "^2.2.0", "eslint-plugin-import": "^2.7.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": "^3.0.0", "less": "^3.0.4", "less-loader": "^4.1.0", "less-vars-to-js": "^1.1.2", "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", "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.3.5", "webpack-dev-server": "^3.7.2", "webpack-merge": "^4.2.1", "webpack-parallel-uglify-plugin": "^1.1.0", "window-size": "^1.1.1" }, "pre-commit": [ "lint" ], "scripts": { "start": "cross-env CONFIG_ENV=development better-npm-run start-dev", "startOnline": "cross-env CONFIG_ENV=production CONFIG_PROXY_ONLINE_ENV=development better-npm-run start-dev", "build:production": "cross-env CONFIG_ENV=production better-npm-run build", "build:test": "cross-env CONFIG_ENV=test better-npm-run build", "build:preTest": "cross-env CONFIG_ENV=preTest better-npm-run build", "dll": "cross-env webpack --config webpack.dll.config.js --colors --display-modules" }, "betterScripts": { "start-dev": { "command": "webpack-dev-server --profile --inline --display-modules --progress --color --config=./script/webpack.dev.js", "env": { "NODE_ENV": "development" } }, "build": { "command": "webpack --profile --inline --display-modules --progress --color --config=./script/webpack.prod.js", "env": { "NODE_ENV": "production" } } }, "theme": "./src/theme.config.js" }
entry:入口文件,webpack会从该文件遍历寻找出所依赖到的文件并打包成bundle,入口文件可以配置一个或者多个;
output:出口,打包好的模块的输出配置;output的常见配置:
filename: // 每个输出bundle的文件名,如果有多个bundle的话应该使用`id , name , hash, chunkhash`等来对文件名进行唯一标识 path: path.join(__dirname, '..', 'dist'), // 输出文件的目标文件夹,应该是 publicPath: config.publicPath, // 需要引用外部资源时配置这个选项,比如说线上都是把js 和 css文件放在云服务器上,这时候就要配置这个publicPath,在打包的时候会被解析插入到相对应的HTML文件中,比如说我们项目线上的源代码中能看到:<script src="**//js.xxx.com/**index.70b799cc.js?bb88a66d5cfeb59190a8"></script> 其中加粗的地方就是配置的publicPath,后面跟着的是服务器上的js文件,这个地址如果没有配置好的话,那么在加载这些js 或者其他资源的时候会报404错误; chunkFilename: // 需要异步加载的文件名称,可以在这里统一配置,也可以在使用代码切割时指定切割文件的chunkname;
关于filename和chunkfilename 的配置可选:一般会采用hash值来对前端资源实现增量更新,参考Webpack中hash与chunkhash的区别,以及js与css的hash指纹解耦方案
resolve 设置打包的模块如何被解析,具体配置可参考webpack官网~~
路由按需加载是用的dva封装的dynamic,webpack在打包的时候会根据路由表的配置进行代码切割 或者使用 react 提供的loadable来进行代码切割; 但是有个坑就是在开发环境下进行代码切割在编译Module and chunk tree optimization阶段会巨慢, 然后搜索到参考1 这个issues,答案提及的babel-plugin-dynamic-import-node这个插件,可以把import转换为require,从而在编译的时候不进行切割; 但是当时忘了只能在Dev环境下使用这个插件,然后prod打包时代码不会进行切割了,然后各种排查。。。 代码切割参考webpack import
Module and chunk tree optimization
babel-plugin-dynamic-import-node
开发和线上配置区分没有做好。把new webpack.HotModuleReplacementPlugin(),plugin放在了common中,这样在打包prod时也启用了热更新,热更新插件会把输出到dist的文件移除。
new webpack.HotModuleReplacementPlugin(),
配置完之后首次打开项目12s,热更新编译的速度1-2s,跟之前的速度项目简直爽的不要不要的;
本来觉得webpack配置很复杂,一不小心就是各种报错,所以也不太想了解;但是花了时间把这套配置搭建出来后,对开发效率有了质的提升,在解决各种报错和玄学问题的过程中也收货颇大,现在对webpack的了解虽然还只是冰山一角,但是现在看到它不会再去害怕它!
package.json 里面的scripts分享一下呢
已更新
然后搜索到参考1 这个issues,答案提及的babel-plugin-dynamic-import-node这个插件,可以把import转换为require,从而在编译的时候不进行切割; 什么地方的import转换为require???
1、为什么要自己动手搭建webpack?
公司老项目的技术配置为react+dva+roadhog,到我接手时已经迭代开发了好几年,项目文件和依赖都十分巨大,据交接给我的开发人员说可能由于第三方升级的原因,导致编译速度十分缓慢;开发时的热更新速度一般都是10-20s+,线上打包速度更是六七分钟以上,线上打包也不是经常操作,所以时间长点忍忍也就过去了, 但是开发时的热更新速度这么慢简直不能忍受;
2、如何干掉roadhog&配置webpack?
2.1、首先把项目中的关于roadhog的配置全部删除:
package.json:
"roadhog": "^1.1.2"
根目录:.roadhogrc.js
文件(暂时先不删删除,因为需要兼容项目,所以这里面的一些配置要copy到webpack当中);2.2、webpack配置:
首先安装webpack相关:
接着在项目根目录创建
具体可以查看webpack并结合自身项目来进行配置,这里贴一下我的配置:
common.js
dev.js
prod.js
babel配置 在根目录配置 .babelrc 文件
package.json
2.3、采坑点和需要注意的点
webpack基础
entry:入口文件,webpack会从该文件遍历寻找出所依赖到的文件并打包成bundle,入口文件可以配置一个或者多个;
output:出口,打包好的模块的输出配置;output的常见配置:
关于filename和chunkfilename 的配置可选:一般会采用hash值来对前端资源实现增量更新,参考Webpack中hash与chunkhash的区别,以及js与css的hash指纹解耦方案
resolve 设置打包的模块如何被解析,具体配置可参考webpack官网~~
路由按需加载
路由按需加载是用的dva封装的dynamic,webpack在打包的时候会根据路由表的配置进行代码切割 或者使用 react 提供的loadable来进行代码切割; 但是有个坑就是在开发环境下进行代码切割在编译
Module and chunk tree optimization
阶段会巨慢, 然后搜索到参考1 这个issues,答案提及的babel-plugin-dynamic-import-node
这个插件,可以把import转换为require,从而在编译的时候不进行切割; 但是当时忘了只能在Dev环境下使用这个插件,然后prod打包时代码不会进行切割了,然后各种排查。。。 代码切割参考webpack import打包成功但是dist文件夹内没有生产出文件
开发和线上配置区分没有做好。把
new webpack.HotModuleReplacementPlugin(),
plugin放在了common中,这样在打包prod时也启用了热更新,热更新插件会把输出到dist的文件移除。before & after
配置完之后首次打开项目12s,热更新编译的速度1-2s,跟之前的速度项目简直爽的不要不要的;
结语
本来觉得webpack配置很复杂,一不小心就是各种报错,所以也不太想了解;但是花了时间把这套配置搭建出来后,对开发效率有了质的提升,在解决各种报错和玄学问题的过程中也收货颇大,现在对webpack的了解虽然还只是冰山一角,但是现在看到它不会再去害怕它!