导航
[[react] Hooks](https://juejin.im/post/684490…)
[[React 从零实际01-后盾] 代码宰割](https://juejin.im/post/687902…)
[[React 从零实际02-后盾] 权限管制](https://juejin.im/post/688148…)
[[React 从零实际03-后盾] 自定义hooks](https://juejin.im/post/688713…)
[[React 从零实际04-后盾] docker-compose 部署react+egg+nginx+mysql](https://juejin.im/post/689239…)
[[React 从零实际05-后盾] Gitlab-CI应用Docker自动化部署](https://juejin.cn/post/689788…)
[[源码-webpack01-前置常识] AST形象语法树](https://juejin.im/post/684490…)
[[源码-webpack02-前置常识] Tapable](https://juejin.im/post/684490…)
[[源码-webpack03] 手写webpack – compiler简略编译流程](https://juejin.im/post/684490…)
[[源码] Redux React-Redux01](https://juejin.im/post/684490…)
[[源码] axios ](https://juejin.im/post/684490…)
[[源码] vuex ](https://juejin.im/post/684490…)
[[源码-vue01] data响应式 和 初始化渲染 ](https://juejin.im/post/684490…)
[[源码-vue02] computed 响应式 – 初始化,拜访,更新过程 ](https://juejin.im/post/684490…)
[[源码-vue03] watch 侦听属性 – 初始化和更新 ](https://juejin.im/post/684490…)
[[源码-vue04] Vue.set 和 vm.$set](https://juejin.im/post/684490…)
[[源码-vue05] Vue.extend](https://juejin.im/post/684490…)
[[源码-vue06] Vue.nextTick 和 vm.$nextTick](https://juejin.im/post/684790…)
[[部署01] Nginx](https://juejin.im/post/684490…)
[[部署02] Docker 部署vue我的项目](https://juejin.im/post/684490…)
[[部署03] gitlab-CI](https://juejin.im/post/684490…)
[[深刻01] 执行上下文](https://juejin.im/post/684490…)
[[深刻02] 原型链](https://juejin.im/post/684490…)
[[深刻03] 继承](https://juejin.im/post/684490…)
[[深刻04] 事件循环](https://juejin.im/post/684490…)
[[深刻05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490…)
[[深刻06] 隐式转换 和 运算符](https://juejin.im/post/684490…)
[[深刻07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490…)
[[深刻08] 前端平安](https://juejin.im/post/684490…)
[[深刻09] 深浅拷贝](https://juejin.im/post/684490…)
[[深刻10] Debounce Throttle](https://juejin.im/post/684490…)
[[深刻11] 前端路由](https://juejin.im/post/684490…)
[[深刻12] 前端模块化](https://juejin.im/post/684490…)
[[深刻13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490…)
[[深刻14] canvas](https://juejin.im/post/684490…)
[[深刻15] webSocket](https://juejin.im/post/684490…)
[[深刻16] webpack](https://juejin.im/post/684490…)
[[深刻17] http 和 https](https://juejin.im/post/684490…)
[[深刻18] CSS-interview](https://juejin.im/post/684490…)
[[深刻19] 手写Promise](https://juejin.im/post/684490…)
[[深刻20] 手写函数](https://juejin.im/post/684490…)
[[深刻21] 算法 – 查找和排序](https://juejin.cn/post/690714…)
前置常识
一些单词
automatic:主动的
delimiter:分隔符
( automaticNameDelimiter )
lighthouse:灯塔
priority:优先级
vendor: 第三方
Suspense:悬念,悬停
fallback:进路
(1) 为什么要做代码宰割
- ( A文件 ) 宰割成 ( B文件,C文件 )
加载一个2MB的文件A,和加载两个1MB的文件B和C,因为存在异步加载并行加载,所以宰割后可能加载速度更快
当批改代码时,不做代码宰割,只批改一小部分就会从新打包整个文件A,生成新的文件A',用户端就得从新加载整个文件A';做代码宰割后,如果批改的代码在B文件,从新打包只须要打包B文件,同时用户端也只须要从新加载B文件,C文件会被缓存
还能够做按需加载,懒加载,路由懒加载,解决白屏等,最终晋升性能
(2) 代码宰割的三种角度
-
拆分成 ( 业务代码-常常变动 ) 和 ( 第三方依赖代码-简直不变 )
- 业务代码会随着需要迭代等一直变动,而第三方依赖包根本不变,所以能够把第三方依赖包独自拆分打包,比方叫vender.js这个包根本不变,代码公布后,在用户端不必从新加载,而是浏览器会主动缓存
-
依据路由进行切割,即路由懒加载
- 比方进入首页的路由时,只须要加载首页的那局部代码
- (
首页有依赖其余模块,同步引入其实也能够在拆分成粒度更细的包,动静引入的能够通过import()函数做动静加载拆分
)
-
依据组件进行切割
- 按路由形式进行代码切割,当A组件蕴含C组件,而B组件也蕴含C组件时,两个打包后的包,都会别离蕴含C组件的代码,造成冗余。
- 按组件形式进行代码切割,则能防止下面的问题,然而由此带来的问题就是包的数量会急剧减少,须要开发者本人掂量利弊。
(3) import(specifier) 函数
- import加载模块时,不能做到像require那样的
运行时
加载模块,所以有了import()函数
提案,动静加载模块 - 参数:模块的门路
- 返回值:返回一个 promise 对象
-
实用场合:
- 按需加载:在须要时在加载模块
- 条件加载:在if语句中做条件加载
- 动静模块门路:容许模块门路动静生成
-
留神点:
- import()返回的是一个promise实例对象,加载胜利后,
模块对象
作为.then()
办法的参数
,能够通过解构赋值
获取输入接口 - 如果模块有 default 输入接口,能够通过参数间接获取default接口,即
.then(moudle => module.default)
- 通过加载多个模块
- 当 Webpack 解析到import()语法时,会主动进行代码宰割。如果你应用 Create React App,该性能已开箱即用,你能够立即应用该个性。当然也能够本人配置webpack
- 当应用 Babel 时,你要确保 Babel 可能解析动静 import 语法而不是将其进行转换。对于这一要求你须要 @babel/plugin-syntax-dynamic-import
import(/* webpackChunkName: "AsyncTest" */'../../components/async-test') .then(({ default: AsyncTest }) => { ... }) .catch(err => console.log(err))
- import()返回的是一个promise实例对象,加载胜利后,
-
代码拆分如何命名包名
- / webpackChunkName: “AsyncTest” /
- 应用插件 @babel/plugin-syntax-dynamic-import 就能够下面的 魔法正文 写法
-
通过 create-react-app新建的我的项目中
=> babel-preset-react-app 依赖=> @babel/preset-env 依赖=> @babel/plugin-syntax-dynamic-import
同步加载( import )
-
代码拆分如何命名包名
- 通过设置 optimization => splitchunks => cashGroups 来配置包名
(4) webpack => optimization
-
对于用webpack构建的我的项目
同步形式引入的模块( import ),做代码宰割须要配置 optimization.splitchunks
异步形式引入的模块( import() ),不须要做任何配置
-
optimization.splitchunks
-
automaticNameDelimiter:
- 指定拆分进去的包的连接符,
起源组名称 连接符 入口名称
(例如vendors~main.js) - 默认是
~
- 指定拆分进去的包的连接符,
-
maxAsyncRequests
- 按需加载时最大的并行申请数,默认30
-
maxInitialRequests
- 入口最大并行申请数,默认30
-
chunks:
- string 或者 function
- string时,有效值为
all
,async
和initial
,all示意同步和异步模块都进行拆分 - function时,能够无效的指定具体的哪些模块须要进行拆分
- chunks 须要配合 cashGroups
-
cacheGroups
-
priority:定义每个组的优先级
- 当一个模块满足多个组规定时,该模块将被打包到 priority 高的文件中
- number越大优先级越高,默认组的默认值是正数,自定义组的默认值是0
- filename:打包后模块的名字
-
reuseExistingChunk:boolean
- 如果在之前的模块中引入过该模块A,并打包了,当初又引入了模块A,就复用之前曾经打包好的A
-
-
minChunks(maxChunks)
- 模块是否进行拆分的最小援用次数,即至多该模块被援用多少次才进行拆分
-
minSize(maxSize)
- 模块是否进行拆分的最小大小(以字节为单位)
-
- 官网阐明
-
SplitChunksPlugin
optimization.splitchunks默认配置项如下: module.exports = { //... optimization: { splitChunks: { chunks: 'all', // 对同步引入模块 和 异步引入模块都做代码宰割,all async initial minSize: 20000, // 当引入的模块大小大于 20KB 时,对该模块进行代码宰割 minRemainingSize: 0, maxSize: 0, // 超过值后,对模块进行二次拆分 minChunks: 1, // 引入的模块被援用一次时就进行代码宰割 maxAsyncRequests: 30, // 最大的按需(异步)加载次数,整个我的项目最多进行30个代码宰割 maxInitialRequests: 30, // 最大的初始化加载次数,首页最多进行30个代码宰割 automaticNameDelimiter: '~', // 打包后的名字中的连接符,组名+连接符+入口文件名 enforceSizeThreshold: 50000, cacheGroups: { defaultVendors: { // 组名称 test: /[\\/]node_modules[\\/]/, // 匹配的范畴是 node_modules priority: -10 // 优先级,当一个模块满足多个组规定时,该模块将被打包到 priority 高的文件中 // filename: 'vender.js' // 指定打包后模块的名字 }, default: { // 引入的模块,如果不满足下面的defaultVendors组规定的模块,就会进行default组的规定匹配 minChunks: 2, priority: -20, reuseExistingChunk: true, // 之前曾经打包过该模块,就间接复用 } } } } };
(5) 谬误边界
局部 UI 中的 JavaScript 谬误不应该毁坏整个应用程序。 为了解决这个问题,React引入了 “谬误边界(Error Boundaries)”
react中的代码宰割实现
(1) React.lazy 和 Suspense 实现代码宰割
-
- React.lazy(() => import()) 参数是一个函数,函数返回值必须是一个promsie对象,React.lazy 目前仅反对默认导出
-
-
Suspense组件,fallback 属性承受任何在组件加载过程中你想展现的 React 元素。你能够将 Suspense 组件置于懒加载组件之上的任何地位。你甚至能够用一个 Suspense 组件包裹多个懒加载组件。
import React, { useState, Suspense } from 'react' import { Button } from 'antd'; const Home = (props: any) => { console.log(props); const [AsyncTest, setAsyncTest] = useState<any>() const [AsyncTest2, setAsyncTest2] = useState<any>() // import()形式代码宰割 const asyncLoad1 = () => { import(/* webpackChunkName: "AsyncTest" */'../../components/async-test') .then(({ default: AsyncTest }) => { setAsyncTest((element: any) => element = AsyncTest) }) .catch(err => console.log(err)) } // React.lazy() + Suspense 形式代码宰割 const asyncLoad2 = () => { const Test2 = React.lazy(() => import(/* webpackChunkName: "AsyncTest2" */'../../components/async-test2')) setAsyncTest2((component: any) => component = Test2) } return ( <div> <header>home page bigscreen</header> <Button onClick={() => { asyncLoad1(); asyncLoad2() }}>异步加载</Button> {AsyncTest ? AsyncTest() : null} {/* {AsyncTest ? <AsyncTest />: null} */} <Suspense fallback={<div>Loading...</div>}> {AsyncTest2 ? <AsyncTest2 /> : null} </Suspense> </div> ) } export default Home
-
(2) 基于路由的代码宰割(React.laze)(Suspense)(react-router-config)
-
和vue相似
React.lazy Suspense react-router-config routes.js------------------ const Login = lazy(() => import(/* webpackChunkName: 'Login' */'../pages/login')) const HomeBigScreen = lazy(() => import(/* webpackChunkName: 'HomeBigScreen' */'../pages/home/bigscreen.home')) const HomeAdmin = lazy(() => import(/* webpackChunkName: 'HomeAdmin' */'../pages/home/admin.home')) const Layout = lazy(() => import(/* webpackChunkName: 'Layout' */'../pages/layout')) const routes: RouteModule[] = [ { path: '/login', component: Login, }, { path: '/', component: Layout, routes: [ // -------------------------------------------------------- 嵌套路由 { path: '/home-bigscreen', exact: true, component: HomeBigScreen, }, { path: '/home-admin', exact: true, component: HomeAdmin, }, ] } ] router.js------------------ import { renderRoutes } from 'react-router-config' //--------------------- react-router-config集中式路由解决方案 const Router = () => { return ( <Suspense fallback={<div>loading...</div>}> //------------------------ Suspense包裹lazy,Suspense.fallback <Switch> {renderRoutes(routes)} </Switch> </Suspense> ) } layout.js---------------- const render = () => { if (systemType === SYSTEMTYPE.ADMIN) { return ( <div className={styles.layoutAdmin}> <header className={styles.header}>layout page admin</header> {renderRoutes(props.route.routes)} //--------------------------- renderRoutes(props.router.routes) </div> ) } else { return ( <div className={styles.layoutBigScreen}> {renderRoutes(props.route.routes)} </div> ) } }
(3) 基于路由的代码宰割(第三方库 react-loadable)
- https://github.com/jamiebuild…
我的项目源码
- 我的项目源码
- 部署成果预览地址
材料
官网教程: https://www.html.cn/create-re…
react中做代码分片:https://juejin.im/post/684490…
发表回复