文章目录
- 前置安装
- 初始化项目
- 生成 package.jsom
- 创建项目目录
- 在public目录下新建 index.html
- 在src目录下新建 index.js
- 在 src/components 目录下新建 home.js
- 修改 package.jsom
- 基础环境搭建
- 开发环境搭建
- 让 webpack-dev-server 支持热更新
- ESLint 代码校验
- CSS 引用图片
- CSS 分离打包
- PostCSS 处理 CSS 压缩去重自动前缀转换
- JS 压缩
- JS 按需加载
- CSS-Module
- SCSS/SASS/LESSe —— 暂时pass
- 生产环境 构建 —— 暂时pass
- 功能第三方库
- UI第三方库
- Router/Rudux/Hooks
- Other
前置安装
安装 cnpm
npm install cnpm -g --registry=https://registry.npm.taobao.org
安装 yarn
cnpm install -g yarn
初始化项目
生成 package.jsom
npm init -y
创建项目目录
项目根目录
├── assets //存放静态资源
│ ├── css //存放css
│ └── images //存放图片
├── public //存放html模板
├── src //代码
│ ├── common //公用类库
│ ├── components //组件 Dumb组件
│ │ ├── PostList //组件目录
│ │ │ └── index.js //采用 Saga 获取数据
│ │ ├── UserList //组件目录
│ │ │ └── index.js //采用 Thunk 获取数据
│ │ └── index.js //App.js 路由组件
│ ├── constant 公用常量
│ │ ├── actionTypes.js
│ │ └── url.js
│ └── redux
│ ├── reducer
│ └── sagas
└── package.json
在public目录下新建 index.html
填入以下内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<!-- 移动端全屏 -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0" />
<meta name="theme-color" content="#000000" />
<!-- 防止页面缓存 -->
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>React Demo</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
在src目录下新建 index.js
填入以下内容
'use strict';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components';
ReactDOM.render(<App />, document.getElementById('root'));
在 src/components 目录下新建 home.js
填入以下内容
import React from 'react';
function App() {
return (<div>Hello, World!</div>)
}
export default App;
修改 package.jsom
在 scripts 下填入以下内容
"scripts": {
"build": "webpack --mode=production",
"start": "webpack-dev-server --open --mode=development"
}
基础环境搭建
安装 Babel 7
包名 | 说明 |
---|---|
@babel/core | Bable核心包 |
@babel/preset-env | 根据配置的目标浏览器或运行环境,自动的将代码转为es5 |
@babel/preset-react | 支持React |
@babel/polyfill | 支持新的(ES6+)API |
安装
cnpm i -D @babel/core @babel/preset-env @babel/preset-react
cnpm i -S @babel/polyfill
新建配置 .babelrc.js
const presets = ["@babel/preset-env","@babel/preset-react"];
const plugins = [];
module.exports = { presets, plugins };
安装 Webpack 4
包名 | 说明 |
---|---|
webpack | webpack主包 |
webpack-cli | 支持命令行 |
babel-loader | 加载babel |
style-loader | 将处理结束的css代码存储在js中,运行时嵌入<style>后挂载到html页面上 |
css-loader | 使webpack可以识别css文件 |
clean-webpack-plugin | 清除之前的打包文件 |
html-webpack-plugin | 支持html |
webpack-dev-server | 开发服务器 |
loader的加载顺序是从右往左,从下往上
安装
//安装 webpack
cnpm i -D webpack webpack-cli
//安装loader
cnpm i -D babel-loader style-loader css-loader
//安装 插件
cnpm i -D clean-webpack-plugin html-webpack-plugin webpack-dev-server
配置 webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const ENTRYPATH = path.resolve(__dirname, './src/index.js');
const OUTPUTPATH = path.resolve(__dirname, './dist');
module.exports = {
//入口
entry: ENTRYPATH,
//出口
output: {
filename: "bundle.js",
path: OUTPUTPATH,
},
//加载器
module: {
rules: [
{
test: /\.js|jsx$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
minify: { // 压缩HTML文件
removeComments: true, // 移除HTML中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true// 压缩内联css
},
template: './public/index.html',
filename: './index.html',
hash: true,
showErrors: true,
})
],
devServer: {
contentBase: OUTPUTPATH,
historyApiFallback: true, // 该选项的作用所有的404都连接到index.html
compress:true //压缩
}
}
安装 Reactjs 16
包名 | 说明 |
---|---|
react | react主包 |
react-dom | react支持dom |
cnpm i -S react react-dom
调试/构建项目
npm run start //调试项目
npm run build //构建项目
//或者
yarn start
yarn build
开发环境搭建
让 webpack-dev-server 支持热更新
安装
cnpm i -D react-hot-loader
使用
- 修改 /src/components/index.js
import { hot } from 'react-hot-loader/root'; //引入hot
const App = () => <div>Hello World!</div>; //根组件
export default hot(App); //包裹根组件
- 修改 .babelrc.js
const plugins = ["react-hot-loader/babel"];
- 修改 package.json -> scripts 添加 --hot
"start": "webpack-dev-server --open --hot --mode=development"
- 执行 yarn start
ESLint 代码校验
包名 | 说明 |
---|---|
eslint | 包体 |
eslint-loader | webpack加载器 |
eslint-plugin-html | 用于检查在写在 script 标签中的代码 |
eslint-friendly-formatter | 规定报错时输入的信息格式 |
eslint-plugin-react | 用于React的ESLint规则 |
安装
cnpm i -D eslint eslint-loader eslint-friendly-formatter eslint-plugin-html eslint-plugin-react
使用
- 在console中执行命令,生成 .eslintrc.js
//window
node_modules\.bin\eslint --init
您想如何使用ESLint?
? How would you like to use ESLint? (Use arrow keys)
> To check syntax, find problems, and enforce code style
您的项目使用什么类型的模块?
? What type of modules does your project use? (Use arrow keys)
> JavaScript modules (import/export)
您的项目使用哪个框架?
? Which framework does your project use? (Use arrow keys)
> React
你的代码在哪里运行?(按<space>选择,<a>切换所有,<i>反转选择)
? Where does your code run? (Press <space> to select, <a> to toggle all, <i> to invert selection)
>(*) Browser
您想如何为您的项目定义一个样式?
? How would you like to define a style for your project? (Use arrow keys)
> Answer questions about your style
> JavaScript
> Tabs
> Single
> Unix
y
- 修改 .eslintrc.js
"extends": ["eslint:recommended", "plugin:react/recommended"],
//清空rules
'rules': { }
- 修改 webpack.config.js -> module -> rules
{
test: /\.js|jsx$/,
loader: 'eslint-loader',
enforce: "pre", // 编译前检查
exclude: /node_modules/, // 不检测的文件
include: [path.resolve(__dirname, 'src')], // 指定检查的目录
options: { // 这里的配置项参数将会被传递到 eslint 的 CLIEngine
formatter: require('eslint-friendly-formatter') // 指定错误报告的格式规范
}
}
CSS 引用图片
包名 | 说明 |
---|---|
file-loader | 可以解析项目中的url引入(不仅限于css) |
url-loader | Loads files as base64 encoded URL |
安装
cnpm i -D file-loader url-loader
使用
修改 webpack.config.js -> module -> rules 下添加
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'url-loader',
options: {
name: 'assets/[name]-[hash:5].[ext]',
limit: 8192 //超过限制会使用file-loader
}
}
]
}
CSS 分离打包
包名 | 说明 |
---|---|
extract-css-chunks-webpack-plugin | 分离css |
mini-css-extract-plugin | 分离css |
- extract-css-chunks-webpack-plugin
安装
修改 webpack.config.jscnpm i -D extract-css-chunks-webpack-plugin
//引入 const ExtractCssChunks = require("extract-css-chunks-webpack-plugin"); //module -> rules -> css里将 style-loader 替换为 { loader: ExtractCssChunks.loader, options: { hot: true, // if you want HMR reloadAll: true, // when desperation kicks in - this is a brute force HMR flag } } //plugins 里添加 new ExtractCssChunks( { filename: "[name].css", chunkFilename: "[id].css", orderWarning: true, } )
- mini-css-extract-plugin
安装
修改 webpack.config.jscnpm i -D mini-css-extract-plugin
//引入 const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //module -> rules -> css里将 style-loader 替换为 { loader: MiniCssExtractPlugin.loader, options: { // you can specify a publicPath here // by default it uses publicPath in webpackOptions.output publicPath: '../', hmr: process.env.NODE_ENV === 'development', }, }, //plugins 里添加 new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output // all options are optional filename: '[name].css', chunkFilename: '[id].css', ignoreOrder: false, // Enable to remove warnings about conflicting order }),
PostCSS 处理 CSS 压缩去重自动前缀转换
包名 | 说明 |
---|---|
postcss-loader | 加载器 |
precss | 转换现代CSS, 包含postcss-preset-env 包含Autoprefixer 自动前缀 |
cssnano | css 优化处理器,压缩去重 |
postcss-import | 让 css 不支持 http 等远程的 URL 链接的处理 |
postcss-scss | 支持现代css语法 |
postcss-normalize|Use the parts of normalize.css (or sanitize.css) you need from your browserslist
sass-loader|加载器,使webpack可以识别sass/scss文件,默认使用node-sass进行编译,
安装
cnpm i -D postcss-loader precss cssnano postcss-import postcss-scss
新建 postcss.config.js 填入以下内容
const precss = require('precss');
module.exports = {
loader: 'postcss-loader',
parser: 'postcss-scss',
plugins: [
precss(),
require('postcss-import'),
require('cssnano')({
preset: ['default', { discardComments: { removeAll: true } }]
})
]
}
修改 webpack.config.js -> module -> rules
{
test: /\.css$/,
use: [
'style-loader',
{ loader: 'css-loader', options: { importLoaders: 1 }},
'postcss-loader'
]
}
在vscode里按下ctrl + ,;搜索设置 files.associations,填入以下代码
"files.associations": {
"*.css": "scss"
}
JS 压缩
- uglifyjs-webpack-plugin:用来对js文件进行压缩,从而减小js文件的大小,加速load速度
cnpm i -D uglifyjs-webpack-plugin
使用 webpack.config.js
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
//...
{
plugins: [
new UglifyJSPlugin({
sourceMap: true //支持devtools
})
]
}
JS 按需加载
- webpack-bundle-analyzer,
这个插件会清晰的展示出打包后的各个bundle所依赖的模块:
引入:npm i webpack-bundle-analyzer -D
使用,在plugins数组中添加即可:const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
new BundleAnalyzerPlugin()
- require.ensure()
webpack 的遗留功能// 空参数 require.ensure([], function(require){ var = require('module-b'); }); // 依赖模块 "module-a", "module-b",会和'module-c'打包成一个chunk来加载 // 不同的分割点可以使用同一个chunkname,这样可以保证不同分割点的代码模块打包为一个chunk require.ensure(["module-a", "module-b"], function(require) { var a = require("module-a"); var b = require("module-b"); var c = require('module-c'); },"custom-chunk-name");
- import()
import() 会针对每一个读取到的module创建独立的chunk。import("./module").then(module => { return module.default; }).catch(err => { console.log("Chunk loading failed"); });
function route(path, query) { return import(`./routes/${path}/route`) .then(route => new route.Route(query)); }
- bundle-loader
用于分离代码和延迟加载生成的 bundle
默认普通模式wrapper:// 在require bundle时,浏览器会立即加载 var waitForChunk = require("bundle!./file.js"); // 使用lazy模式,浏览器并不立即加载,只在调用wrapper函数才加载 var waitForChunk = require("bundle?lazy!./file.js"); // 等待加载,在回调中使用 waitForChunk(function(file) { var file = require("./file.js"); });
lazy模式wrapper:var cbs = [],data; module.exports = function(cb) { if(cbs) cbs.push(cb); else cb(data); }, require.ensure([], function(require) { data = require('./file.js'); var callbacks = cbs; cbs = null; for(var i = 0, l = callbacks.length; i < l; i++) { callbacks[i](data); } });
使用bundle-loader在代码中require文件的时候只是引入了wrapper函数,而且因为每个文件都会产生一个分离点,导致产生了多个打包文件,而打包文件的载入只有在条件命中的情况下才产生,也就可以按需加载。module.exports = function (cb) { require.ensure([], function(require) { var app = require('./file.js'); cb(app); }); };
- 支持自定义Chunk名称:
require("bundle-loader?lazy&name=my-chunk!./file.js");
- 支持自定义Chunk名称:
- promise-loader
类似于 bundle-loader ,但是使用了 promise API
wrapper函数:// 使用Bluebird promise库 var load = require("promise?bluebird!./file.js"); // 使用全局Promise对象 var load = require("promise?global!./file.js"); load().then(function(file) { });
var Promise = require('bluebird'); module.exports = function (namespace) { return new Promise(function (resolve) { require.ensure([], function (require) { resolve(require('./file.js')[namespace])); }); }); }
js公共资源分割
wepack4采用了runtimeChunkPlugin,可以将每个entry chunk中的runtime部分的函数分离出来,只需要一个简单的配置
runtimeChunk: "single"
// 等价于
runtimeChunk: {
name: 'minifest'
}
css资源分割
按模块加载
按路由加载
点我返回目录
react-router的 标签有一个叫做getComponent的异步的方法去获取组件。他是一个function接受两个参数,分别是location和callback。当react-router执行回调函数 callback(null, ourComponent)时,路由只渲染指定组件ourComponent
- getComponent异步方法
<Router history={history}> <Route path="/" getComponent={(nextState, callback) => { callback(null, HomePage) }} /> <Route path="/faq" getComponent={(nextState, callback) => { callback(null, FAQPage); }} /> </Router>
CSS-Module
- 修改 webpack.config.js
module.exports = { module: { rules: [ { test: /\.css$/, exclude: /\.module\.css$/, //排除css模块 use: ['style-loader', 'css-loader'] }, { test: /\.module\.css$/, use: ['style-loader', { loader: 'css-loader', options: { importLoaders: 1, modules: { localIdentName: '[path][name]__[local]--[hash:base64:5]' }, } }] } ] }, }
- 修改 home.js
import React from 'react'; import '../../assets/css/home.css'; import css from '../../assets/css/test.module.css'; function App() { return (<div> <ul> <!-- 全局css --> <li className="global">Hello, World!</li> <!-- 模块css --> <li className={css.global}>I am Test!</li> </ul> </div>) } export default App;
SCSS/SASS/LESSe —— 暂时pass
"node-sass": "^4.5.3",
"sass-loader": "^4.0.2",
"less": "^2.6.1",
"less-loader": "^4.0.3",
生产环境 构建 —— 暂时pass
包名 | 说明 |
---|---|
webpack-merge | 合并webpack配置 |
安装
cnpm i -D webpack-merge
在webpack.common.conf.js
const path = require('path');
module.exports = {
//...配置
});
在webpack.dev/prod.conf.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
//...配置
});
webpack.common.js 公共配置
// webpack.base.conf.js
'use strict'
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// 入口
entry: {
app: './src/index.js',
},
// 输出
output: {
path: path.resolve(__dirname, '../dist'),
filename: "[name].js",
},
// 解析
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json','.jsx']
},
// loader
module: {
rules: [{
test: /\.js|jsx$/,
exclude: /node_modules/,// 屏蔽不需要处理的文件(文件夹)(可选)
loader: 'babel-loader'
},{
test: /\.css$/,
use: ['style-loader','css-loader']
}]
},
// 插件
plugins: [
new HtmlWebpackPlugin({
filename: './public/index.html',
template: './public/index.html',
inject: 'body'
})
]
}
webpack.dev.js 开发配置
//webpack.dev.conf.js
'use strict'
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
const path = require('path');
const webpack = require('webpack');
module.exports = merge(baseWebpackConfig, {
// 模式
mode: "development",
// 调试工具
devtool: 'inline-source-map',
// 开发服务器
devServer: {
contentBase: false,// 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录
historyApiFallback: true,// 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html
compress: true,// 启用gzip压缩
inline: true,// 设置为true,当源文件改变时会自动刷新页面
hot: true,// 模块热更新,取决于HotModuleReplacementPlugin
host: '127.0.0.1',// 设置默认监听域名,如果省略,默认为“localhost”
port: 8703// 设置默认监听端口,如果省略,默认为“8080”
},
// 插件
plugins: [
// 热更新相关
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
],
optimization: {
nodeEnv: 'development',
}
});
webpack.prod.js 生产配置
//webpack.prod.conf.js
'use strict'
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = merge(baseWebpackConfig, {
// 模式
mode: "production",
// 调试工具
devtool: '#source-map',
// 输出
output: {
path: path.resolve(__dirname, '../dist'),
filename: "js/[name].[chunkhash].js",
},
// 插件
plugins: [
new CleanWebpackPlugin(),
new webpack.HashedModuleIdsPlugin(),
],
// 代码分离相关
optimization: {
nodeEnv: 'production',
runtimeChunk: {
name: 'manifest'
},
splitChunks: {
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
name: false,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'initial',
}
}
}
}
});
功能第三方库
时间处理 moment.js
npm install moment --save
CSS 相关
包名 | 说明 |
---|---|
flex.css | 支持flex布局 |
normalize.css | 在默认的HTML元素样式上提供了跨浏览器的高度一致性 |
异步加载组件 跳过
react-loadable
SplitChunksPlugin
http请求 跳过
"axios": "^0.16.2", //http请求
视频播放
包名 | 说明 |
---|---|
video.js | 视频播放库 |
videojs-flash | rtmp需要flash播放 |
安装
cnpm i -S video.js videojs-flash
使用
import React, { Component } from 'react';
import Videojs from 'video.js';
require('!style-loader!css-loader!video.js/dist/video-js.css');
//import "video.js/dist/video-js.css";
import VideojsFlash from "videojs-flash";
class Video extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.cfg = {
autoplay: true,
controls: true,
sources: [{
// rtmp 格式流
'src': "rtmp://202.69.69.180:443/webcast/bshdlive-pc",
'type': 'rtmp/flv'
// hls 格式流
// 'src': "http://localhost:8000/live/1/index.m3u8",
// 'type': 'application/x-mpegURL'
}]
};
this.player = Videojs(this.videoNode, this.cfg, function onPlayerReady() {
console.log('onPlayerReady', this);
});
}
componentWillUnmount() {
if (this.player) {
this.player.dispose();
}
}
render() {
return (
<div className="Video">
<div data-vjs-player>
<video ref={node => this.videoNode = node} className="video-js Video-Player"></video>
</div>
</div>
);
}
}
export default Video;
修改 webpack.config.js module -> rules
{
test: /\.(woff|woff2|eot|ttf)$/,
loader: 'url-loader?limit=100000',
}
UI第三方库
轮播图支持
安装
cnpm i -S swiper
使用 参考swiper中文网
import React, { Component } from 'react';
import Swiper from 'swiper/dist/js/swiper.js';
import 'swiper/dist/css/swiper.min.css';
class Banner extends Component {
constructor(props) {
super(props);
this.state = { list: ['1', '2', '3', '4'] };
}
componentDidMount() {
var mySwiper = new Swiper('.swiper-container', {
effect : 'cube',
direction: 'horizontal', // 水平切换选项
autoplay: true,
loop: true,
speed: 300,
pagination: {
el: '.swiper-pagination',
clickable: true,
}
on: {
click: function (e) {
},
touchEnd: function(e){
e.activeLoopIndex //循环索引
e.activeIndex //非循环索引
}
},
})
}
render() {
return (
<div className="Banner">
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide">Slide 1</div>
<div class="swiper-slide">Slide 2</div>
<div class="swiper-slide">Slide 3</div>
</div>
<!-- 如果需要分页器 -->
<div class="swiper-pagination"></div>
<!-- 如果需要导航按钮 -->
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
<!-- 如果需要滚动条 -->
<div class="swiper-scrollbar"></div>
</div>
</div>
);
}
}
export default Banner;
Ant Design
安装
cnpm i -S antd
cnpm i -S antd-mobile
按需加载
babel-plugin-import: 在 Babel 配置中引入该插件,可以针对 antd, antd-mobile, lodash, material-ui等库进行按需加载.
cnpm i -D babel-plugin-import
修改 .babelrc.js
//在 plugins 中添加
[
"import",
{
"libraryName": "antd",
"libraryDirectory": "lib"
},
"ant"
],
[
"import",
{
"libraryName": "antd-mobile",
"libraryDirectory": "lib"
},
"antd-mobile"
]
Router/Rudux/Hooks
安装 Router
cnpm i -S react-router-dom
安装 Redux
安装
cnpm i -S redux react-redux
redux-thunk 异步处理
安装
cnpm i -S redux-thunk
redux-saga 异步处理
安装
cnpm i -S redux-saga
安装插件
cnpm i -D @babel/plugin-transform-runtime
//在转换 ES2015 语法为 ECMAScript 5 的语法时,babel 会需要一些辅助函数,例如 _extend。babel 默认会将这些辅助函数内联到每一个 js 文件里,这样文件多的时候,项目就会很大。
//所以 babel 提供了 transform-runtime 来将这些辅助函数“搬”到一个单独的模块 babel-runtime 中,这样做能减小项目文件的大小。
修改 .babelrc.js
//在 plugins 里添加 '@babel/plugin-transform-runtime'
const plugins = ["@babel/plugin-transform-runtime"];
redux-logger 日志工具
安装
cnpm i -S redux-logger
Hooks
安装
cnpm i -D eslint-plugin-react-hooks
修改 .eslintrc.js 在 rules 下填入
"plugins": [
//...
"react-hooks"
],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
},
数据唯一
包名 | 说明 |
---|---|
immutable | 数据唯一 |
redux-immutable | redux 数据唯一 |
安装
cnpm i -S immutable redux-immutable
使用 在 rules 下填入
// reducer.js下
import { fromJS } from 'immutable';
const defaultState = fromJS({
list: false
})
import { combineReducers } from 'redux-immutable';
Other
"es6-promise": "^4.1.0",
"query-string": "^4.3.4",
"body-parser": "^1.15.1",
"sugarss": "^1.0.0",
"get-next-page": "1.0.0",
"obj-merged": "^1.0.5",
yarn add webpack-bundle-analyzer //分析打包后的文件体积
yarn add compression-webpack-plugin //build生成gizp压缩文件
- webpack-bundle-analyzer 在yarn start后默认在浏览器localhost:8888可以查看打包分布图
- compression-webpack-plugin 在yarn build 可以直接在build文件夹中生成 .gz后缀的文件
npm install --save react-pullload