动手搭建react开发环境二
2019.07.12
hzzly
版本
本篇主要使用针对上篇的webpack配置进行优化
webpack优化
为每个引入 CSS 的 JS 文件创建一个 CSS 文件,提高首页加载速度
- 把 style-loader 替换成 MiniCssExtractPlugin.loader
- 新增 plugins
1
| yarn add mini-css-extract-plugin -D
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { module: { rules: [ { test: /\.(sc|sa|c)ss$/, use: [ MiniCssExtractPlugin.loader, ], }, ], }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].[hash].css', chunkFilename: '[id].[hash].css', }), ] }
|
2、压缩CSS(OptimizeCssAssetsWebpackPlugin)
1
| yarn add optimize-css-assets-webpack-plugin -D
|
1 2 3 4 5 6 7 8
| const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'); module.exports = { plugins: [ new OptimizeCssAssetsWebpackPlugin(), ] }
|
3、压缩JS
webpack 4只要在生产模式下, 代码就会自动压缩
4、代码分割(SplitChunksPlugin)
代码分割,单独打包,可以有效避免所有页面只生成一个js文件,首屏加载很慢的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| module.exports = { optimization: { splitChunks: { chunks: 'all', } } };
|
5、配置全局变量
1 2 3 4 5 6 7 8 9 10
| const webpack = require("webpack"); module.exports = { plugins: [ new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('development'), }), ] }
|
🔥Tip1 问题:当用 DefinePlugin 来配置全局变量时,只给依赖中注入了环境变量,也就是src文件夹下面的和依赖的模块。当我们在webpack配置文件中去取 process.env.NODE_ENV 依然是 undefined。
解决:在package.json命令中注入
1 2 3
| "scripts": { "start": "webpack-dev-server NODE_ENV=development --config ./build/webpack.config.js", }
|
6、CSS Tree Shaking
去除项目代码中用不到的 CSS 样式,仅保留被使用的样式代码
🔥Tip2 问题:当使用 CSS Tree Shaking 的时候,需要把 css-modules 关闭,不然 css 会被全部清除掉。
1
| yarn add glob-all purify-css purifycss-webpack -D
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const PurifyCSS = require("purifycss-webpack"); const glob = require("glob-all");
module.exports = { plugins: [ new PurifyCSS({ paths: glob.sync([ path.resolve(__dirname, "../public/*.html"), path.resolve(__dirname, "../src/*.js") ]) }) ] }
|
7、JS Tree Shaking
清除到代码中无用的js代码,只支持import方式引入,不支持commonjs的方式引入
webpack 4只要在生产模式下, tree shaking就会生效。
8、resolve(解析)
能设置模块如何被解析。
- extension: 指定extension之后可以不用在require或是import的时候加文件扩展名,会依次尝试添加扩展名进行匹配
- alias: 配置别名可以加快webpack查找模块的速度
1 2 3 4 5 6 7 8 9
| module.exports = { resolve: { extensions: ['.js', '.jsx'], alias: { '@': path.join(__dirname, '../src'), }, }, }
|
9、模块热替换HMR
模块热替换也称为HMR,代码更新时只会更新被修改部分都显示。有如下有点
- 针对于样式调试更加方便
- 只会更新被修改代码的那部分显示,提升开发效率
- 保留在完全重新加载页面时丢失的应用程序状态。
这里我们采用Node.js的方式实现
1 2 3 4
| yarn add express webpack-dev-middleware webpack-hot-middleware react-hot-loader cross-env -D cd build touch dev-server.js
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| const path = require('path'); const express = require('express'); const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); const webpackHotMiddleware = require("webpack-hot-middleware") const config = require('./webpack.config.js'); const complier = webpack(config); const app = express(); const DIST_DIR = path.resolve(__dirname, '../', 'dist'); const port = parseInt(process.env.PORT, 10) || 8586; const host = process.env.HOST || 'localhost'; const devMiddleware = webpackDevMiddleware(complier, { quiet: true, noInfo: true, stats: 'minimal' }) const hotMiddleware = webpackHotMiddleware(complier, { log: false, heartbeat: 2000 }) app.use(devMiddleware) app.use(hotMiddleware)
app.use(express.static(DIST_DIR))
app.listen(port, () => { console.log(`App running at: http://${host}:${port}`); })
|
修改webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| const isDev = process.env.NODE_ENV === 'development'; module.exports = { entry: { main: [ 'webpack-hot-middleware/client?noInfo=true&reload=true', './src/index.js' ] }, module: { rules: [ { test: /\.(sc|sa|c)ss$/, use: [ isDev ? 'style-loader' : MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { modules: { localIdentName: '[local]_[hash:base64:5]', }, sourceMap: !isDev && true, }, }, 'postcss-loader', 'sass-loader', ], }, ], }, plugins: [ new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin(), ] }
|
修改入口文件index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import React from 'react'; import ReactDOM from 'react-dom'; import { AppContainer } from 'react-hot-loader'; import { BrowserRouter } from 'react-router-dom'; import Router from './router';
function render() { ReactDOM.render( <AppContainer> <BrowserRouter> <Router /> </BrowserRouter> </AppContainer>, document.getElementById('root') ); }
render();
if (module.hot) { module.hot.accept('./router/index.js', () => { render(); }); }
|
修改script命令行
1
| "start": "cross-env NODE_ENV=development node ./build/dev-server.js",
|
ok,当我们修改代码时,页面就不需要刷新了,而是直接更新变化的部分。
10、BundleAnalyzerPlugin
使用交互式可缩放树形图可视化webpack输出文件的大小,可以方便我们针对代码依赖的大小进行优化。
1
| yarn add webpack-bundle-analyzer -D
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = { plugins: [ new BundleAnalyzerPlugin({ analyzerMode: 'server', analyzerHost: '127.0.0.1', analyzerPort: 8889, reportFilename: 'report.html', defaultSizes: 'parsed', openAnalyzer: true, generateStatsFile: false, statsFilename: 'stats.json', statsOptions: null, logLevel: 'info' }) ] }
|
1 2 3 4 5 6
| { "script": { "analyz": "cross-env NODE_ENV=production npm_config_report=true npm run build" } }
|
webpack分离配置文件
针对开发环境和发布环境配置对应的webpack,公共的部分提取出来,再使用 webpack-merge 来将不同环境下的配置合并起来
1 2 3 4 5 6
| cd build touch webpack.base.conf.js touch webpack.dev.conf.js touch webpack.prd.conf.js yarn add webpack-merge -D
|
1、提取公共配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const isDev = process.env.NODE_ENV === 'development';
module.exports = { entry: ['./src/index.js'], output: { path: path.resolve(__dirname, '../dist'), }, module: { }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ filename: 'index.html', template: path.join(__dirname, '../public/index.html'), }), ], resolve: { extensions: ['.js', '.jsx'], alias: { '@': path.join(__dirname, '../src'), }, }, optimization: { splitChunks: { chunks: 'all', }, }, performance: false, };
|
2、配置开发环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| const path = require("path"); const webpack = require("webpack"); const merge = require('webpack-merge'); const commonConfig = require('./webpack.base.conf.js');
module.exports = merge(commonConfig, { mode: "development", devtool: 'cheap-module-eval-soure-map', entry: { main: [ 'webpack-hot-middleware/client?noInfo=true&reload=true', './src/index.js' ] }, output: { path: path.resolve(__dirname, "../dist"), filename: "bundle.[name].[hash].js", chunkFilename: '[name].[hash].js' }, plugins: [ new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin(), ], devServer: { hot: true, contentBase: path.resolve(__dirname, "../dist"), host: "localhost", port: 8586, historyApiFallback: true, proxy: { } } });
|
3、配置发布环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| const path = require('path'); const merge = require('webpack-merge'); const commonConfig = require('./webpack.base.conf.js');
module.exports = merge(commonConfig, { mode: 'production', devtool: 'cheap-module-source-map', output: { publicPath: '/', path: path.resolve(__dirname, '../dist'), filename: 'bundle.[name].[hash].js', chunkFilename: '[name].[hash].js', }, optimization: { usedExports: true, splitChunks: { chunks: 'all', cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, name: 'vendors', }, }, }, }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].[hash].css', chunkFilename: '[id].[hash].css', }), ], });
|
4、修改script命令行
1 2
| "start": "cross-env NODE_ENV=development node ./build/dev-server.js", "build": "cross-env NODE_ENV=production webpack --config ./build/webpack.prod.conf.js",
|
写到这里,一个基本的React开发环境也就搭起来了,接下来就可以针对代码或者开发效率进行优化。