小二助手-react.js分块加载
小二助手在线演示地址:http://118.25.217.253:8000? 账号test 密码123 小二助手是用material-ui开发的,感觉国内使用的人数不是特别多,所以创建了一个qq交流群,欢迎加入:836719189 ?在网上找了多种方案,这种采纳了下面的方案,简单有效。 一、为什么需要代码分片 Facebook 的?create-react-app?是一款非常优秀的开发脚手架。它为我们生成了 React 开发环境,自带 webpack 默认配置。 它会通过 webpack 打包我们的应用,产生一个?bundle.js?文件。随着我们的项目越写越复杂,bundle.js?文件会随之增大。? 由于该文件是唯一的,所以不管用户查看哪个页面、使用哪个功能,都必须先下载所有的功能代码。? 当?bundle.js?大到一定程度,就会明显影响用户体验。 此时,我们就需要 code splitting ,将代码分片,实现按需异步加载,从而优化应用的性能。 二、代码分片的原理 但是我们可以通过?dynamic import()?来实现动态加载的功能。?dynamic import()?是 stage 3 中的一个提案。这是一个?运算符?operator?而非函数?function?。 我们把模块的名字作为参数传入,它会返回一个 Promise ,当模块加载完成后,该 Promise 就会 fulfilled。 当你在代码中新增了一个?import()?,用它动态导入模块时, Webpack 2 会自动据此完成代码分片,不需要任何额外的手动配置。 三、以路由为中心进行代码分片 此处,我们以其最新版 React Router v4 为例。 分片前 import {requireAuthentication} from ‘./CheckToken‘ import Home from ‘../components/Home/Home‘ import Login from ‘./LoginContainer‘ import Signup from ‘./SignupContainer‘ import Profile from ‘./ProfileContainer‘ ... ... <Router> <Switch> <Route exact path=‘/‘ component={Home} /> <Route path=‘/login‘ component={Login} /> <Route path=‘/signup‘ component={Signup} /> <Route path=‘/profile‘ component={requireAuthentication(Profile)} /> ? 分片后 新增?AsyncComponent,它将接受一个函数作为参数,实现异步地动态加载组件。例如: const AsyncLogin = asyncComponent(() => import(‘./LoginContainer‘)) 这么写看起来啰嗦,但可以让我们控制生成多少个?.chunk.js?这样的分片文件。 代码: import React,{ Component } from ‘react‘ export default function asyncComponent(importComponent) { class AsyncComponent extends Component { constructor(props) { super(props) this.state = { component: null } } async componentDidMount() { const { default: component } = await importComponent() this.setState({ component: component }) } render() { const C = this.state.component return C ? <C {...this.props} /> : null } } return AsyncComponent } 路由 import {requireAuthentication} from ‘./CheckToken‘ import asyncComponent from ‘./AsyncComponent‘ const AsyncHome = asyncComponent(() => import(‘../components/Home/Home‘)) const AsyncLogin = asyncComponent(() => import(‘./LoginContainer‘)) const AsyncSignup = asyncComponent(() => import(‘./SignupContainer‘)) const AsyncProfile = asyncComponent(() => import(‘./ProfileContainer‘)) ... ... <Router> <Switch> <Route exact path=‘/‘ component={AsyncHome} /> <Route path=‘/login‘ component={AsyncLogin} /> <Route path=‘/signup‘ component={AsyncSignup} /> <Route path=‘/profile‘ component={requireAuthentication(AsyncProfile)} /> ? 此时再运行?npm run build,看编译的log,以及?build/static/js/?目录下的 js 文件,会发现多出了若干文件名?.chunk.js?结尾的文件。 ??npm start?把项目跑起来,在 chrome 的 devTool 中,打开?Network?,查看?JS?,就可以看到异步动态按需加载分片文件的效果了。 四、以组件为中心进行代码分片 即,路由本身并没有什么特别的,它们也是组件。 如果以组件为中心进行代码分片,会带来额外的好处: 除了路由此外,还有很多地方可以进行代码分片。广阔天地,大有作为。 通过它,我们可以用使用 React?高阶组件?(Higher Order Component / HOC)实现异步加载 React 组件的功能,同时处理操作失败、网络错误等等边缘情况。 注:一个高阶组件,简言之就是一个函数,它接受的参数是 React 组件,返回的结果也是 React 组件。 React Loadable 可以通过 npm 安装?react-loadable。 首先,我们用 React Loadable 来重构刚才的代码 处理边缘情况的组件 import React from ‘react‘ const MyLoadingComponent = ({isLoading,error}) => { // 加载中 if (isLoading) { return <div>Loading...</div> } // 加载出错 else if (error) { return <div>Sorry,there was a problem loading the page.</div> } else { return null } } export default LoadingComponent 路由 import {requireAuthentication} from ‘./CheckToken‘ import Loadable from ‘react-loadable‘ import LoadingComponent from ‘../components/common/Loading‘ const AsyncHome = Loadable({ loader: () => import(‘../components/Home/Home‘),loading: LoadingComponent }) const AsyncSignup = Loadable({ loader: () => import(‘./SignupContainer‘),loading: LoadingComponent }) const AsyncLogin = Loadable({ loader: () => import(‘./LoginContainer‘),loading: LoadingComponent }) const AsyncProfile = Loadable({ loader: () => import(‘./ProfileContainer‘),loading: LoadingComponent }) <Router> <Switch> <Route exact path=‘/‘ component={AsyncHome} /> <Route path=‘/login‘ component={AsyncLogin} /> <Route path=‘/signup‘ component={AsyncSignup} /> <Route path=‘/profile‘ component={requireAuthentication(AsyncProfile)} /> 五、进一步优化 重新运行项目,发现了可以进一步改进的地方。 防止 Loading 组件闪现 我们添加该组件的初衷,是在网络差的时候,给用户一个提示:“应用运行正常,只是正在加载中,请稍等。” 显然,如果网络良好,跳转足够快,LoadingComponent 组件根本没有必要出现。 React Loadable 可以很容易地实现这个功能。 LoadingComponent 组件接收一个 pastDelay 属性,该属性仅仅在延迟超过一个规定的值后才为 true 。 默认的延迟是 200ms,我们也可以自己指定别的时长。操作如下,我们将其设置为 300ms。 const AsyncLogin = Loadable({ loader: () => import(‘./LoginContainer‘),loading: LoadingComponent,delay: 300 }) ? LoadingComponent 组件做相应调整。同时增加一些简单的样式。 import React from ‘react‘ import Footer from ‘../Footer/Footer‘ import styled from ‘styled-components‘ const Wrap = styled.div` min-height: 100vh; display: flex; flex-direction: column; justify-content: space-between; background-color: #B2EBF2; text-align: center; ` const LoadingComponent = (props) => { if (props.error) { return ( <Wrap> <div>Error!</div> <Footer /> </Wrap> ) } else if (props.pastDelay) { // 300ms 之后显示 return ( <Wrap> <div>信息请求中...</div> <Footer /> </Wrap> ) } else { return null } } export default LoadingComponent 同一个组件中,延迟加载不急着显示的内容 例如这个组件,TopHeader 是优先显然的内容,Notification 是不一定显示的内容。我们可以推迟后者的加载。 ... ... import TopHeader from ‘../components/Header/TopHeader‘ import Notification from ‘./NotificationContainer‘ class TopHeaderContainer extends Component { ... ... return ( <div> <TopHeader sideButtons={tempIsAuthenticated} logout={this.logout} /> <Notification /> </div> ) } ... ... export default connect(mapStateToProps,{ logout })(TopHeaderContainer) 优化后 ... ... import TopHeader from ‘../components/Header/TopHeader‘ import Loadable from ‘react-loadable‘ import LoadingComponent from ‘../components/common/Loading‘ const AsyncNotification = Loadable({ loader: () => import(‘./NotificationContainer‘),delay: 300 }) ... ... class TopHeaderContainer extends Component { ... ... return ( <div> <TopHeader sideButtons={tempIsAuthenticated} logout={this.logout} /> <AsyncNotification /> </div> ) } } ... ... export default connect(mapStateToProps,{ logout })(TopHeaderContainer) ... ... ? 此外, 还可以实现 预加载(如 click 按钮显示某组件,那么在 hover 事件时就预先加载之)、服务端渲染 等等。 在此就不多做介绍了。--------------------- 作者:Beijiyang999 来源:CSDN 原文:https://blog.csdn.net/beijiyang999/article/details/78591398? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- playframework – sbt:子项目的动态聚合
- c – 我想在不使用字符串的情况下向字符声明指针数组
- React 项目初始化
- Oracle热备份-联机热备
- ruby-on-rails – Stripe Ruby – 按ID查找现有客户并添加计
- [CXF REST标准实战系列] 一、JAXB xml与javaBean的转换
- c# – 使用MVC,IMG标记,Url.Action和TempData的IE9意外行为
- postgresql – PgAdmin III – 密码为空时如何连接数据库?
- ruby-on-rails – 在rails上的ruby中选择复选框传递数组
- c# – Nancy:从“/”提供静态内容(例如index.html)?