当前位置: 首页 > Web前端 > JavaScript

【React从零实践01-背景】代码拆分

时间:2023-03-27 16:46:21 JavaScript

导航【[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-composedeployreact+egg+nginx+mysql](https:///juejin.im/post/689239...)[[React从零实践05-背景]Gitlab-CI使用Docker自动化部署](https://juejin.cn/post/689788...)[[来源code-webpack01-pre-knowledge]AST抽象语法树](https://juejin.im/post/684490...)[[source-webpack02-pre-knowledge]Tapable](https://juejin.im/post/684490...)[[源码-webpack03]手写webpack-编译器简单编译过程](https://juejin.im/post/684490...)[[源码]ReduxReact-Redux01](https://juejin.im/post/684490...)://juejin.im/post/684490...)[[源码]ReduxReact-Redux01](https://juejin.im/post/684490...)[[源码]axios](https://juejin.im/post/684490...)[[源码]vuex](https://juejin.im/post/684490...)[[源码-vue01]数据响应和初始化渲染](https://juejin.im/post/684490...)[[源码-vue02]计算响应式-初始化、访问、更新过程](https://juejin.im/post/684490...)[[source-vue03]watchlistenerproperty-初始化和更新](https://juejin.im/post/684490...)[[source-vue04]Vue.setandvm.$set](https://juejin.im/post/684490...://juejin.im/post/684490...)[[source-vue05]Vue.extend](https://juejin.im/post/684490...)[[source-vue06]Vue.nextTickandvm.$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]]CurrypartialFunction函数记忆](https://juejin.im/post/684490...)[[深入06]隐式转换与运算符](https://juejin.im/post/684490...)[[深入07]浏览器缓存机制(httpcaching机制)](https://juejin.im/post/684490...)[[深入08]前端安全](https://juejin.im/post/684490...)[[Deep09]DebounceThrottle](https://juejin.im/post/684490...)[[Deep10]DebounceThrottle](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]手写承诺](https://juejin.im/post/684490...)[[深入20]手写功能](https://juejin.im/post/684490...)[[深入21]算法-搜索排序](https://juejin.cn/post/690714...)预知一些词automatic:automaticdelimiter:delimiter(automaticNameDelimiter)lighthouse:lighthousepriority:priorityvendor:third-partySuspense:悬念,hoverfallback:fallback(1)为什么代码拆分(A文件)拆分成(B文件,C文件)加载一个2MB的文件A,加载两个1MB的文件B和C,由于异步加载和并行加载,加载速度可能会分裂后更快。修改代码时,不做代码拆分,只修改一小部分,重新??打包整个文件A,生成新的文件A',客户端需要重新加载整个文件A';代码拆分后,如果修改后的代码在B文件中,只需要打包B文件重新打包,客户端只需要重新加载B文件,C文件会被缓存,也可以压入需要加载、懒加载、路由懒加载、解决白屏等,最终提升性能(2)代码切分的三个角度分为(业务代码-经常变动)和(第三方依赖代码-差不多)不变)业务代码会随着需求的迭代等不断变化,但是第三方依赖包基本不变,所以可以将第三方依赖包拆分单独打包。例如,名为vender.js的包基本没有变化。代码发布后,不需要重新加载,但是浏览器会根据路由自动缓存和切分,也就是路由懒加载的时候,比如进入首页的路由时,只需要加载首页上的部分代码(首页依赖其他模块,同步导入也可以拆分粒度更细的包,动态导入的可以通过import()function)Cuttingaccordingtocomponents代码切割是根据路由进行的。当A组件包含C组件,B组件又包含C组件时,两个包最终的包会分别包含C组件的代码,造成冗余。按组件切割代码可以避免上述问题,但由此带来的问题是包的数量会急剧增加,开发者需要自己权衡利弊。.(3)import(specifier)函数导入模块时,不能像require一样在运行时加载模块,所以有提议import()函数动态加载模块参数:模块的路径返回value:返回一个promise对象applicableOccasion:On-demandloading:在需要的时候加载模块Conditionalloading:在if语句中有条件地加载动态模块路径:允许动态生成模块路径注意:import()返回一个promise实例对象,之后加载成功,模块对象作为.then()方法的参数,可以通过解构赋值得到输出接口。如果模块有默认输出接口,可以直接通过参数获取默认接口,即.then(moudle=>module.default)加载多个模块作为Webpackimport()语法解析时,代码拆分会被自动执行。如果您使用CreateReactApp,此功能是开箱即用的,您可以立即使用此功能。当然你也可以自己配置webpack。使用Babel时,需要保证Babel能够解析动态导入语法,而不是对其进行转换。对于这个要求,你需要@babel/plugin-syntax-dynamic-importimport(/*webpackChunkName:"AsyncTest"*/'../../components/async-test').then(({default:AsyncTest})=>{...}).catch(err=>console.log(err))代码拆分如何命名包名/webpackChunkName:"AsyncTest"/使用插件@babel/plugin-syntax-dynamic-import来做上面的魔术注解是通过create-react-app=>babel-preset-react-appdependency=>@babel/preset-envdependency=>@babel/plugin-syntax-dynamic-import写在新建的项目中同步加载(导入)代码拆分包名如何命名通过设置optimization=>splitchunks=>cashGroups配置包名(4)webpack=>optimization对于用webpack构建的项目同步导入的模块(import),需要optimization.splitchunks为代码拆分配置异步引入的模块(import())不需要任何配置。optimization.splitchunksautomaticNameDelimiter:指定拆分包的连接器,源组名连接器入口名(如vendors~main.js)默认为~maxAsyncRequests最大并行请求加载数,默认为30maxInitialRequests,最大数量entry的并行请求数,默认为30chunks:string或functionstring,有效值为all,async和initial,all表示在function拆分时同步和异步模块都可以有效拆分指定具体需要哪些模块被拆分成chunk需要配合cashGroupscacheGroupspriority:定义一个模块满足多个组时各组的优先级当使用该规则时,该模块将被打包成一个优先级高的文件。数字越大,优先级越高。默认组默认值为负数,自定义组默认值为0引入模块A,重用之前封装好的AminChunks(maxChunks)模块。SplitChunksPluginoptimization.splitchunks默认配置项如下:module.exports={//...optimization:{splitChunks:{chunks:'all',//同步导入模块和异步导入模块的代码拆分,都是异步初始minSize:20000,//当导入的模块大小大于20KB时,对该模块进行代码拆分minRemainingSize:0,maxSize:0,//超过该值后,模块将被拆分两次minChunks:1,//导入模块被引用一次就会进行代码拆分maxAsyncRequests:30,//按需(异步)加载的最大次数,整个项目最多30次代码拆分maxInitialRequests:30,//初始化加载的最大次数,主页最多30个codesplitsautomaticNameDelimiter:'~',//打包名称中的Connectors,组名+connector+入口文件名enforceSizeThreshold:50000,cacheGroups:{defaultVendors:{//组名test:/[\\/]node_modules[\\/]/,//匹配范围为node_modulespriority:-10//优先级,当一个模块满足多个组规则时,模块将被打包成一个优先级高的文件//filename:'vender.js'//指定打包模块的名称},default:{//如果导入的模块不满足上述defaultVendors组规则,则匹配default组的规则minChunks:2,priority:-20,reuseExistingChunk:true,//模块之前已经打包好了,可以直接复用}}}}};(5)错误边界部分UI中的JavaScript错误不应该破坏整个应用为了解决这个问题,React引入了“ErrorBoundaries”react中代码拆分的实现(1)React.lazy和Suspense实现代码拆分React。lazy(()=>import())参数是一个函数,函数的返回值必须是一个promsie对象,React.lazy目前默认只支持导出Suspense组件,fallback属性接受你的任何React元素想要在组件加载期间显示。您可以将Suspense组件放置在惰性组件之上的任何位置。您甚至可以用单个Suspense组件包装多个惰性组件。importReact,{useState,Suspense}from'react'import{Button}from'antd';constHome=(props:any)=>{console.log(props);const[AsyncTest,setAsyncTest]=useState<任何>()const[AsyncTest2,setAsyncTest2]=useState()//import()方法代码分割constasyncLoad1=()=>{import(/*webpackChunkName:"AsyncTest"*/'../../components/async-test').then(({default:AsyncTest})=>{setAsyncTest((element:any)=>element=AsyncTest)}).catch(err=>console.log(err))}//React.lazy()+Suspense方法代码分割constasyncLoad2=()=>{constTest2=React.lazy(()=>import(/*webpackChunkName:"AsyncTest2"*/'../../components/async-test2'))setAsyncTest2((component:any)=>component=Test2)}return(

主页大屏
{asyncLoad1();asyncLoad2()}}>异步加载{AsyncTest?AsyncTest():null}{/*{AsyncTest?:null}*/}Loading...
}>{AsyncTest2?:null}
)}exportdefaultHome(2)基于路径的代码分割(React.laze)(Suspense)(react-router-config)和vue类似React.lazySuspensereact-router-configroutes.js-----------------constLogin=lazy(()=>import(/*webpackChunkName:'Login'*/'../pages/login'))constHomeBigScreen=lazy(()=>import(/*webpackChunkName:'HomeBigScreen'*/'../pages/home/bigscreen.home'))constHomeAdmin=lazy(()=>import(/*webpackChunkName:'HomeAdmin'*/'../pages/home/admin.home'))constLayout=lazy(()=>import(/*webpackChunkName:'Layout'*/'../pages/layout'))constroutes: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-----------------从'导入{renderRoutes}react-router-config'//--------------------react-router-config集合中式路径由解决方案constRouter=()=>{return(loading...
}>//------------------------Suspense包装lazy,Suspense.fallback{renderRoutes(routes)})}layout.js----------------constrender=()=>{if(systemType===SYSTEMTYPE.ADMIN){return(layoutpageadmin{renderRoutes(props.route.routes)}//------------------------renderRoutes(props.router.routes)
)}else{return({renderRoutes(props.route.routes)}
)}}(3)基于路由的代码分割(第三方库react-loadable)https://github.com/jamiebuild...项目源码项目源码部署效果预览地址资料官网教程:https://www.html.cn/create-re...在react中做代码分片:https://juejin.im/post/684490...