React基于路由进行代码分割 | 您所在的位置:网站首页 › react代码生成器 › React基于路由进行代码分割 |
theme: fancy
highlight: agate
你好,我是南一。这是我在准备面试八股文的笔记,如果有发现错误或者可完善的地方,还请指正,万分感谢🌹 这两天整理项目经历,看到这一个知识点,重新实现了一遍,顺便记录一下。 一、为什么要做代码分割和懒加载?背景: 随着项目开发,业务功能增加,代码量随着增长,代码包体积日渐肥胖,尤其是整合了多种第三方库,导致代码包体积过大,加载时间长,性能下降。 对策: WebPack 等打包工具早有代码分离的特性来应对这种问题,将代码分离到不同的 bundle 中,需要时按需加载就可以极大改善加载时间长的问题。常见的代码分离方法有三种: 入口起点:使用 entry 配置手动地分离代码。 防止重复:使用 Entry dependencies 或者 SplitChunksPlugin 去重和分离 chunk。 动态导入:通过模块的内联函数调用来分离代码。今天我们就是采用动态导入来实现分包。 决定在哪引入代码分割需要一些技巧。需要确保选择的位置能够均匀地分割代码包而不会影响用户体验。 一个不错的选择是从路由开始。大多数网络用户习惯于页面之间能有个加载切换过程。 实现将代码按照路由进行分割,只在访问该路由的时候才加载该页面内容,可以提高首屏加载速度。 二、知识预知 1、import()import :ES6语法,使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。 import()ES6语法,可用于动态引入模块,返回一个 Promise 对象。 WebPack解析代码时,遇到import()会作为一个分割点,将导入的模块作为一个单独的bundle打包。如果是使用脚手架 Create React App 搭建的项目,可直接使用此功能。 import("./a").then(res => { console.log(res); });这里我花了很多时间试错,经测试发现,import()语法如果是包含在函数或者循环内,webpack的代码分割会失效,所以后面我用了路由表配置的方式去实现,如果有更优雅的实现方式可以在评论区分享。 2、React.lazyReact.lazy 函数能让你像渲染常规组件一样处理动态引入(的组件)。React.lazy 接受一个函数,这个函数需要动态调用 import()。它必须返回一个 Promise,该 Promise 需要 resolve 一个 default export 的 React 组件。 const OtherComponent = React.lazy(() => import('./OtherComponent')); 3、Suspense然后应在 Suspense 组件中渲染 lazy 组件,如此使得我们可以使用在等待加载 lazy 组件时做优雅降级(如 loading 指示器等)。 import React, { useState, lazy, Suspense } from 'react' import Loading from '@/component/Loading'; function App() { const [RouteRouter] = useState(() => { return lazy(() => import('@/routes/RouteRouterSplit')) }) return } 三、具体实现路由表设计,我选择了最笨的方式实现 export const routerConfig = [ { path: '/', component: lazy(() => import('@/pages')) }, { path: '/Login', component: lazy(() => import('@/pages/Login')), }, { path: '/Home', component: lazy(() => import('@/pages/Home')) } { path: '/Render', component: lazy(() => import('@/pages/Render')) }, { path: '/Test', component: lazy(() => import('@/pages/Test')) } ]为了更好用我还做了路由拦截和路由鉴权, 路由鉴权:采用 context 将路由权限向下传递,用 useContext 获取权限,并做筛选。 路由拦截: 用高阶组件对页面组件进行包裹,在页面加载前后调用处理函数 import React, { useState, useLayoutEffect, lazy, Suspense, useMemo } from 'react' import Loading from '@/component/Loading'; export const Permission = React.createContext() function App() { const [rootPermission, setRootPermission] = useState([]) const [RouteRouter] = useState(() => { return lazy(() => import('@/routes/RouteRouterSplit')) }) useLayoutEffect(() => { setRootPermission([ '/', '/NoPermission', '/WriteDoc', '/Home', '/Login', ]) }, []) const config = useMemo(() => ({ before: function () { // console.log('before'); }, after: function () { // console.log('after'); }, }), []) return } export default App import { lazy, useContext, useLayoutEffect } from 'react'; import { Route, Routes } from 'react-router-dom' import { Permission } from '@/App' import { routerConfig } from './routerConfig' const NoFound = lazy(() => import('@/component/NoFound')) /** * 鉴权函数,判断此组件是否在权限范围内 (不同的鉴权方式可在此函数中修改) * @param {Array} permissionList * @param {string} componentName */ function authentication(permissionList, componentName) { return permissionList.indexOf(componentName) >= 0 } /** * 路由拦截 * @param {*} Component * @param {*} config * @returns */ function RouteInterception(Component, config) { const { before, after } = config || {} return function ProRouteComponent(props) { // const ref = useRef() // 进入路由前触发 before && before() // 路由挂载之后触发 useLayoutEffect(() => { after && after() }, []) return } } export default function RouteRouter(props) { // 获取权限数组 const permissionList = useContext(Permission) const routes = routerConfig.filter(({ path }) => { // 权限筛选 return authentication(permissionList, path) }).map(({ path, component: Component }) => { // 路由拦截 Component = RouteInterception(Component, props.config) return }) return ( {routes} ) }打包完就是这样效果 本文正在参加「金石计划 . 瓜分6万现金大奖」 温馨提示:本文最后更新于2022-12-12 00:49:59,某些文章具有时效性,若有错误或已失效,请在下方留言或联系烟雨网。 |
CopyRight 2018-2019 实验室设备网 版权所有 |