澳门威尼斯官网loadable原理浅析,我决定放弃dva并自己搭一个基于TypeScript的脚手架

时间: 2019-05-13阅读: 479标签: dva

react-loadable

最近在学习react,之前做的一个项目首屏加载速度很慢,便搜集了一些优化方法,react-loadable这个库是我在研究路由组件按需加载的过程中发现的,其实react-router v4官方也有code splitting的相关实践,但是在现在的webpack 3.0版本下已经不行了,因为需要使用以下相关语法

import loadSomething from 'bundle-loader?lazy!./Something'

有兴趣的同学可以自行研究。

react-router-v4:code-splitting

后面就发现了react-loadable这个库可以帮助我们按需加载,其实和react-router-v4官方实现的原理差不太多,基本使用方法如下:

第一步:先准备一个Loding组件,这个是官方的,你自己写一个更好:

const MyLoadingComponent = ({ isLoading, error }) => {
    // Handle the loading state
    if (isLoading) {
        return <div>Loading...</div>;
    }
    // Handle the error state
    else if (error) {
        return <div>Sorry, there was a problem loading the page.</div>;
    }
    else {
        return null;
    }
};

第二步:引入react-loadable

import Loadable from 'react-loadable';

const AsyncHome = Loadable({
    loader: () => import('../containers/Home'),
    loading: MyLoadingComponent
});

第三步:替换我们原本的组件

 <Route path="/" exact component={AsyncHome} />

这样,你就会发现只有路由匹配的时候,组件才被import进来,达到了code splitting的效果,也就是我们常说的按需加载,代码分块,而不是一开始就将全部组件加载。

澳门威尼斯官网 1

chunk

可以观察到,点击不同的路由都会加载一个chunk.js,这就是我们所分的块。

老实说,dva真的很好用,下载下来过后直接写业务逻辑代码就可以了,基本不用理会webpack如何配置,项目结构怎么设计,甚至我可以不用知道redux的数据流是怎样的、saga如何处理异步、react-router路由是什么,怎么按需加载,都可以开发,只要知道react语法就成功了一半,更开心的是,我可能用到的插件,在node_modules下一搜索,基本都有了,简直不要太方便,然而,我还是有不得不放弃的理由。

dva现在是构建在umi基础上,由于项目的原因,我并没有采用umi架构,而是自己使用webpack4来进行打包,只用dva负责数据流的处理,dva原来的dynamic在webpack4上编译会有一堆错误。具体就不贴上来了,这里主要是利用webpack4采用import的动态加载原理进行改造。

核心:import()

不要把 import关键字和import()方法弄混了,该方法是为了进行动态加载才被引入的。

import关键字的引入是静态编译且存在提升的,这就对我们按需加载产生了阻力(画外音:require是可以动态加载的),所以才有了import(),而react-loadable便是利用了import()来进行动态加载。

阮一峰:Module的加载实现

tc39/proposal-dynamic-import

而且这个方法不能传变量,只能使用字符串和字符串模板,原本想将那一堆生成组件的代码进行抽象,结果死活不行,才发现坑在这里。

突然想用TypeScript重构一下项目,怕以后业务越来复杂,项目越来越大,之前写的会忘记。dva是支持TypeScript的,但是配置路由中,因为使用了dva/dynamic,总会有一些奇奇怪怪的报错信息,最后找到了最佳的实践方法,就是把Umi引入进来,之前看官方说的类nextjs的框架,果断放弃了,因为我用不到,用了之后才知道,嗯……类似nextjs结构吧,和nextjs好像没有太多相同之处,因为dva+umi+roadhog才是一个完整的页面+路由+服务的最佳实践。我的项目感觉越来越大了,我想我要放弃了。

首先,dva依赖与react-router,所以最初的想法是采用react-router结合webpack4的方式进行改写。这里我用到一个库,就是@loadable/component,这个在react-router的动态加载案例里也有采纳,但是大家都知道,dva原来的dynamic.js在动态加载react-router的component时还要使用registerModel来进行model注册。这个过程实际是是redux的动态加载。在这里我参考了webpack4的动态加载案例,使用import().then(module)的方式来进行动态加载。说了这么多,把代码放上来:

Loadable

react-loadable有5k star,内部机制已经十分完善了,看现在的源码我肯定看不懂,于是误打误撞地看了其initial commit的源码。

我们上面对Loadable函数的用法是这样的:

const AsyncHome = Loadable({
    loader: () => import('../containers/Home'),
    loading: MyLoadingComponent
});

接收一个配置对象为参数,第一个属性名为loader,是一个方法,用于动态加载我们所需要的模块,第二个参数就是我们的Loading组件咯,在动态加载还未完成的过程中会有该组件占位。

{
  loader: () => import('../containers/Home'),
  loading: MyLoadingComponent
}

这个方法的返回值是一个react component,我们Route组件和url香匹配时,加载的就是这个component,该component通过loader方法进行异步加载组件以及错误处理。其实就这么多,也有点高阶组件的意思。

然后来看看源码吧(源码参数部分使用了ts进行类型检查)。

import React from "react";

export default function Loadable(
  loader: () => Promise<React.Component>,
  LoadingComponent: React.Component,
  ErrorComponent?: React.Component | null,
  delay?: number = 200
) {
  // 有时组件加载很快(<200ms),loading 屏只在屏幕上一闪而过。

  // 一些用户研究已证实这会导致用户花更长的时间接受内容。如果不展示任何 loading 内容,用户会接受得更快, 所以有了delay参数。

  let prevLoadedComponent = null;

  return class Loadable extends React.Component {
    state = {
      isLoading: false,
      error: null,
      Component: prevLoadedComponent
    };

    componentWillMount() {
      if (!this.state.Component) {
        this.loadComponent();
      }
    }

    loadComponent() {
      // Loading占位
      this._timeoutId = setTimeout(() => {
        this._timeoutId = null;
        this.setState({ isLoading: true });
      }, this.props.delay);

      // 进行加载
      loader()
        .then(Component => {
          this.clearTimeout();
          prevLoadedComponent = Component;
          this.setState({
            isLoading: false,
            Component
          });
        })
        .catch(error => {
          this.clearTimeout();
          this.setState({
            isLoading: false,
            error
          });
        });
    }

    clearTimeout() {
      if (this._timeoutId) {
        clearTimeout(this._timeoutId);
      }
    }

    render() {
      let { error, isLoading, Component } = this.state;

      // 根据情况渲染Loading 所需组件 以及 错误组件
      if (error && ErrorComponent) {
        return <ErrorComponent error={error} />;
      } else if (isLoading) {
        return <LoadingComponent />;
      } else if (Component) {
        return <Component {...this.props} />;
      }
      return null;
    }
  };
}

安利一下我正在练习的项目react-music

参考资料:

React Loadable 介绍

webpack v3 结合 react-router v4 做 dynamic import — 按需加载(懒加载)

阮一峰:Module的加载实现

tc39/proposal-dynamic-import

在react-router4中进行代码拆分(基于webpack)

react-router-v4:code-splitting

React-Router v4真的是将前端路由发挥的很好,可以很灵活的配置,包括按需加载、事件监听等等,dva也很好,把路由的核心都放在了一起,但是类如我想在Route里面写render好像变难了,不能把models注入里面。一些看似鸡肋,有时候很好用的功能在dva里面变得更难了,我想我要放弃了。

import loadable from '@loadable/component'/*dva官方代码*/const cached = {}function registerModel(app, model) { model = model.default || model if( !cached[model.namespace] ) { app.model(model) cached[model.namespace] = 1 }}/*动态加载封装*/const AsyncPage = loadable(props = { import(`your/path/${props.component}/model`).then((module) = { registerModel(props.app, module.default) }) return import(`your/path/${props.component}`)})

webpack零配置啊,这点真的是省了很多查看各种loader和插件版本问题的时间,按需加载这种也不用去翻文档了,一个true就可以,但是越是封装的好,可灵活配置的就越少.webpackrc文件可以配置的也不多。打包dist下生成0.async.js ..... 31.async.js,几十个文件真的逼死强迫症。我想我又要放弃了。

使用起来就是像dva原来的dynamic上一样,建立一个routes对象数组

这个我就觉得很无奈了,roadhog也很好,为什么放到docker上运行npm run build就挂起了呢,网上提了问题,最后还是自问自答,发现2.3.0就会这样,还是要降到2.2.0。我想还是放弃把。

[{ path:'your/path', key:'yourkey', component: props = { return AsyncPage component="your/path/component" app={app} {..props} / }}]

幸幸苦苦项目算是跑起来了,可以感觉打包运行时间好长啊,果然做开发还是要配一个好点的电脑,不过,我决定还是放弃了。

然后map一下就OK了。

代码在这里

webpack4:不是看中它宣传的零配置,真的打包速度比前几个版本快好多,记得单独安装webpack-cli。

mobx:TypeScript兼容上比较好,可以感觉更轻一点。

react-router v4:看看文档配置起来真的很好用。

axios:fetch也不用啦,哪个小用哪个。

async/await:暂且拿它代替一下saga,感觉够用。

各种版本先升到最新的,一般来说新版本很多会做性能优化。

webpack:webpack4放弃了CommonsChunkPlugin,引入了optimization.splitChunks,还有optimization.runtimeChunk,搭配HtmlWebpackPlugin 用比较好。

TypeScript的加载器有两个,awesome-typescript-loaderts-loader,我比较倾向于前一个,更快。但是我的项目中用了ts-loader,因为引入了antd,为了不用bable,我用ts-import-plugin加载样式,在各种版本的尝试下,用最新版的awesome-typescript-loader加上最新版的antd并不能很好的加载antd的样式。

antd的版本如果3.0以前要在tsconfig.json中加"allowSyntheticDefaultImports": true

tsconfig.json:主要是一些ts语法转换的配置,有点像bable所做的事情,不过我不想用bable,在处理async/await的时候,要加上"lib": ["es6", "dom"]

发一个包,方便用。如果你想尝试一下

  • npm install -g rwt-cli
  • rwt init <name>
  • cd <name>
  • npm install
  • npm start查看版本 rwt -v or rwt --version 目前是1.1.6

没什么好说的,还是看代码吧,如果你觉得可以优化也可以github:

本文由澳门威斯尼人平台登录发布于Web前端,转载请注明出处:澳门威尼斯官网loadable原理浅析,我决定放弃dva并自己搭一个基于TypeScript的脚手架

相关阅读