06月22, 2019

UI组件按需加载实战

之前有分享过关于UI组件按需加载的思路,但个人还没怎么实战过,上周实战了一把。

可能有些人会觉得这不是很容易么,但看结合怎样的背景了,像我司的那个react ui库,结合了:

  • antd2.x的ui
  • element ui的table组件
  • 自研的一套

构建用的是webpack。(我在考虑后期改成rollup似乎更合适一些)

一些原因

可能有人比较好奇,已经有了antd的ui,为什么还要用elementtable

这是因为业务组的童鞋之前用的是element react来开发项目,然后后来统一用dpl-react(我司的UI组件)后,发现antdTable没有element的好用。

当然可能我没有深入踩过业务,感知性不强。

不过elementtable,也有一些坑,其实也不能叫坑,就是它没有集成pagination

翻了一下他的源码实现,发现实现真分页的集成,还是比较容易的。但要实现假分页(即客户端分页),改造起来还是有一定的成本的。

另外如果自定义滚动条的话,需要将下面的宽高设成一样:

alt

否则在有固定列时,会造成底部的滚动条两边不一样的结果:

alt

生成es代码

由于这边antd代码使用的就是es代码,所以这个基本上不用修改。

然而在测试的过程中,发现同事有些代码改了,但用了类似这样的写法:

<Loading  />

我表示非常的服气,当然要把这一块改成es5的写法即可。

步骤一

element的代码是需要生成es的,因为同事拷贝的是源代码,结构如下:

- components
- lib
    - antd2
    - element-react
        - libs
        - locale
        - style
        - src
            - table

table组件会依赖libslocalestyle这几个文件夹。要生成es目录的话,那么尽量保持文件夹干净。

所以我的打算是element-react只有一个src目录和es目录。

生成es的方式倒是容易,有一个babel配置:

{
  "env": {
    "production": {
      "presets" : [
        ["@babel/preset-env", { "modules": false, "loose": true }], 
        "@babel/preset-react"
      ],
      "plugins" : [
        "@babel/plugin-proposal-class-properties",
        "@babel/plugin-transform-runtime"
      ]
    }
  }
}
{
    "scripts": {
        "es": "rimraf ./es && cross-env BABEL_ENV=production babel ./src --out-dir ./es"
    }
}

步骤二

接下去就是生成components的es目录了。

和步骤一的生成方式差不多,但我用了一个新的babel配置(里面去掉了环境)。

// npmBabel.js
module.exports = {
  "presets" : [
    ["@babel/preset-env", { "modules": false, "loose": true }], 
    "@babel/preset-react"
  ],
  "plugins" : [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-transform-runtime"
  ]
}

下意识地以为这样写就行了:

{
    "scripts": {
        "es": "rimraf ./npm && babel ./src/components --out-dir ./npm/components --config-file ./npmBabel.js"
    }
}

踩了两个坑:

  • 生成出来的代码居然没有import
  • 生成出来的组件文件里面没有less等其他文件

解决方法:

  • 要带上--no-babelrc,才能正常
  • 要带上--copy-files,不然最终的目录里面只会有生成的编译js文件

测试

在生成es目录之后,我们就可以在package.json里面声明一个module,然后指向es目录里面的index.js即可。

那么来测试一下吧,我用create-react-app创建了一个项目,写了以下的几行代码:

import React from 'react';
import {Button} from 'dpl-react';

export default () => {
  return (
    <div>
      <Button>default</Button>&nbsp;
      <Button type='primary'>primary</Button>&nbsp;
      <Button type="primary-bordered">bordered</Button>&nbsp;
      <Button disabled>disabled</Button>
    </div>
  )
}

一打包,发现体积有点大,似乎不太对。那么我们来分析一下吧:

{
    scripts: {
        "analyze": "source-map-explorer 'build/static/js/*.js'",
    }
}

alt

我尼玛一口老血差点吐出来,这是全量加载了呀!!

查了一下tree shanking没有生效的原因,可能是babel这货最终编译的代码产生了副作用。

你的Tree-Shaking并没什么卵用

摊手,那只能采用babel插件来变相的tree-shaking

最终方案如下:

配置.babelrc

{
  "plugins": [
    // ...
    ["transform-modules", {
      "dpl-react": {
        "transform": "dpl-react/npm/components/${member}",
        "preventFullImport": true,
        "camelCase": true
      }
    }]
  ]
}

alt

alt

说个趣事

公司有个私有npm库,然后里面有一个servyou-rc-table,是基于rc-table的改造。

然后我这边之前为了本地能测试,也给自己本机的私服上传了这个servyou-rc-table,当时也没怎么测试能不能走通。

这一次在跑测试时,发现报错:cannot resolve "servyou-rc-table"

我一开始怀疑是node_modules里面没有,但检查了一下有这个文件夹(但没深入去看)。于是开始怀疑人生。。。

晚上睡着的时候,突然想到了,妈蛋的,可能是package.json里面的main或者module字段没有找到相应的文件。

第二天醒来,果然是这个问题,原来是我之前push的版本忘记build了。。。

感慨一句:有时候脑子用久了,应该让它休息休息,一味地死嗑,是行不通的。

另外,就我个人来讲,我觉得还是有必须要package.json加一个prepublishOnly的,即在发布前先要进行构建。

本文链接:www.my-fe.pub/post/ui-demand-load-combat.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。