博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React性能优化之代码分割
阅读量:6982 次
发布时间:2019-06-27

本文共 4909 字,大约阅读时间需要 16 分钟。

代码分割从两方面来讲:React Lazy 和 webpack两方面

每次单页应用打包出的文件又臭又长,是最蛋疼的事,浏览器刷个半天才出来网页让人恨不得找个地洞钻进去,这种事很尴尬,让人怀疑你这前端不专业

所以正题来了,这篇文章主要讲解

  • 如何分模块拆分代码
  • 如何提取公共库,避免重复打包(两个模块都用到jQuery,如何只打包进一个chunk)
  • 如何打包需要的代码(比如一个ui库,你只用到一个Button,那么只打包Button)
  • React组件如何实现懒加载

先讲 React Lazy

先上个最简单的demo,App.js和Child.js

// App.jsimport React from 'react';import Child from './Child';function App() {	return (		
);}export default App;复制代码
// Child.jsimport React from 'react';class Child extends React.Component{    render(){        return 
我是子组件啊
}}export default Child;复制代码

就这个目录结构,然后执行打包,打包之后如下:

可以看到Child组件被打包到了main.chunk.js里面
那么Child是否可以做懒加载呢?

查找React文档,可以找到code split,那么用React lazy来对Child组件做一下懒加载,还是原来的组件App和Child

// App.jsimport React, { lazy, Suspense } from 'react';const Child = lazy(() => import('./Child'))function App() {	return (		
Loading...
}>
);}export default App;复制代码
// Child.jsimport React from 'react';class Child extends React.Component{    render(){        return 
我是子组件啊
}}export default Child;复制代码

其实api来说很简单,就两句代码,这就实现了懒加载

import React, { lazy, Suspense } from 'react';const Child = lazy(() => import('./Child'))复制代码

可是源码里可以注意到还有一个Suspense,而且不用Suspense的话,浏览器会给出一个提示:

从错误提示来看,lazy非要搭配Suspense使用了。那么Suspense是何方神圣呢?

可以试想一下,既然是懒加载,那么当Child还未加载完成之前,这个视图怎么办?

bingo!Suspense就是为了处理这个的,让视图更友好,为懒加载组件做优雅降级,它叫加载指示器

好的,那已经加上lazy了,执行打包吧

再刷新下页面,发现Child组件被打包到了2.chunk.js里面,说明已经运行成功了。而且在Child加载成功之前,有个loading在提示。good!效果还不错

可是问题来了。

阅读React lazy的官方文档。发现lazy传入一个函数,而且此函数需要返回一个Thenable,那么当没有返回Thenable的时候会怎么办呢?

发现浏览器挂了。。。这只是试下异常情况,所以最好还是按照React的官方文档去操作吧

再从webpack谈下

webpack的代码分离有三种方法:

  • 多个entry
  • 插件来防止重复:SplitChunksPlugin
  • 动态导入
先讲下最高端的,动态导入(以es6 import为?)

此章节讲解如何一个ui库,解构导入let { Button } = ui;只导入Button

这也是老生常谈的antd的import { Button } from 'antd',只导入Button问题

先上个反面教材吧

// index.jsimport { mainAdd } from './main';console.log(mainAdd(1, 2));复制代码
// main.jsconst mainAdd = (a, b) => {    return a + b;}const mainMultiple = (a, b) => {    return a * b;}export {    mainAdd,    mainMultiple}复制代码

webpack.config.js的配置entry就只配置index.js,来webpack打包一把

血崩啊,明明只是import了mainAdd,mainMultiple咋还打包进去了

好的,反面教材讲完了,现在教大家如何修改,先修改一下文件目录

// webpack.config.jsconst path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {	mode: 'development',	entry: {		index: './src/index.js'	},	output: {		filename: '[name].bundle.js',		path: path.resolve(__dirname, 'dist')	},	plugins: [		new HtmlWebpackPlugin()	],	module: {		rules: [			{                test: /\.js$/,				loaders: 'babel-loader',				include: path.join(__dirname, 'src'),				options: {					plugins: [						["import", { "libraryName": "main", "libraryDirectory": "../../src/main"}],					]				},			}		]	}};复制代码
//index.jsimport { mainAdd } from 'main';console.log(mainAdd(1, 2));复制代码
// main目录下的main-add.jsconst mainAdd = (a, b) => {    return a + b;}export default mainAdd;复制代码
// main目录下的main-multiple.jsconst mainMultiple = (a, b) => {    return a * b;}export default mainMultiple;复制代码

好的,再来执行一次打包,发现只打包进了mainAdd,而mainMultiple没有打包进去。

总结一下,主要用到一个babel的知识点

  • 需要用到babel-plugin-import插件。顾名思义,就是个动态import的plugin,然后配置babel-loader的options
options: {	plugins: [		["import", { "libraryName": "main", "libraryDirectory": "../../src/main"}],	]},复制代码

libraryName指代的是import { mainAdd } from 'main'这里的main -> 库名,libraryDirectory是main下面的哪个文件夹。比如antd/lib/button,这里libraryDirectory配置的是lib,这里默认是指向node_modules下的。我是放在src/main目录下,所以这里配置为"../../src/main"

webpack Entry多入口

这个就不多说了,大家用的都比较多

webpack 公共库拆分打包

还是老习惯,先来个反面教材

// index.jsimport _ from 'lodash';import { each } from 'jquery';console.log(    _.join(['index', 'module', 'loaded!'], ' '));复制代码
// main.jsimport { each } from 'jquery';let arr = ['zhangsan', 'lisi'];each(arr, (idx, item) => {    console.log(item);})复制代码
// webpack.config.jsconst path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {	mode: 'development',	entry: {		index: './src/index.js',		another: './src/main.js'	},	output: {		filename: '[name].bundle.js',		path: path.resolve(__dirname, 'dist')	},	plugins: [		new HtmlWebpackPlugin()	],	module: {		rules: [			{                test: /\.js$/,				loaders: ['babel-loader'],				include: path.join(__dirname, 'src')			}		]	}};复制代码

两个入口,打包一把

又血崩了,发现index.bundle.js打包进了jquery,another.bundle.js也打包进了jquery,可以打开两个bundle.js进去查找下jquery看看是否打包进去。

那么理想状态下,应该是一份jquery,一份index,一份main,三份代码。用官方文档SplitChunksPlugin来试一把,index.js main.js webpack.config.js代码几乎不变。

// webpack.config.js中加上一项配置optimization: {	splitChunks: {		chunks: 'all'	}}复制代码

再打包一下

发现公共库被提取出来了,index和another两个文件只包含的业务代码

看一下chunk的里面的代码,发现只有verdors~other~index.bundle.js里面有require("jquery"),而vendors~index.bundle.js里面是直接用jquery的,并没有引入jquery源码

说明我们的效果已经达到了。

至此四个功能已经讲完了,自己写个demo来演练下吧,有问题可以加我微信demo0808

  • React的lazy懒加载
  • webpack的entry多入口
  • SplitChunksPlugin提取公共库代码
  • babel-plugin-import动态导入antd下的Button

转载于:https://juejin.im/post/5cdd28e551882520365abee3

你可能感兴趣的文章
如何在Kali Linux中安装Google Chrome浏览器
查看>>
勒索软件防不胜防? 要先从了解它开始
查看>>
大数据精准医疗解读遗传密码 未来医疗健康的变革
查看>>
神经网络基础:七种网络单元,四种层连接方式
查看>>
2014末,Surface Pro 3叫好不叫座只是价格问题?
查看>>
Arimo利用Alluxio的内存能力提升深度学习模型的结果效率(Time-to-Result)
查看>>
代号“沙尘暴”:黑客剑指日本关键基础设施
查看>>
光纤光缆市场需求高于预期 我国将迎来流量经济
查看>>
晶科能源与森源电气签订300MW光伏组件供货协议
查看>>
中国电信发布转型升级战略:构建一横四纵生态圈
查看>>
全渠道的核心是渠道协同和数据整合
查看>>
“小会话,大学问” - 如何让聊天机器人读懂对话历史?| 论文访谈间 #03
查看>>
让问答更自然 - 基于拷贝和检索机制的自然答案生成系统研究 | 论文访谈间 #02...
查看>>
首航节能:光热行业刚起步 子公司处于亏损状态
查看>>
《PHP精粹:编写高效PHP代码》——第1章面向对象编程
查看>>
美国智能家居止步不前 原因是产品过于碎片化
查看>>
大数据到底是不是“算命”?技术大牛们这样说
查看>>
让智能家居产品操控更简单 快捷键来了
查看>>
《面向对象分析与设计》一3.2 参与者
查看>>
WCF 性能基准报告
查看>>