01月28, 2019

react组件实现按需加载及doc编写

17年底写过一篇文章:组件打包小记(2),里面其实已经有关于按需加载的方案了。现在想详细展开说说。

UI组件的统一

先说点其他的。像我司,前年有开发了一个UI组件,叫zcmui(名字有点垃圾)。之所以有这个,是因为我司搞了一个DPL,有些组件antd满足不了,或者对antd的已有组件再次包装。

但问题在于,业务开发人员用了两套UI:antdzcmui,这样很容易造成一些困扰。

解决的方案其实很简单,可以将antd的组件放到zcmui里面,对外只提供zcmui

比如Button

import React from 'react';
import AntdButton from 'antd/lib/button';

class Button extends React.Component {
    render() {
        return (
            <AntdButton {...this.props} />
        );
    }
}

export default Button;

当然在用这个的时候,也要小心,比如说同事使用了Button.Group,那就GG了。不过也可以改造:

import React from 'react';
import AntdButton from 'antd/lib/button';

class Group extends React.Component {
    render() {
        return (
            <AntdButton.Group {...this.props} />
        )
    }
}

class Button extends React.Component {
    render() {
        return (
            <AntdButton {...this.props} />
        );
    }
}

Button.Group = Group;

export default Button;

当然有些坑可能一开始并不知道,但是我觉得先去尝试了,出错了总会有堆栈信息,我们可以根据堆栈信息来排查出问题。

私以为,这样最大的好处在于,将来想自己写Button或者用其他三方的库,都可以换掉底层。

当然还有style的部分,这里先按下不表,下面会提及。

按需加载

其实就是babel-import-plugin这样的功能。

然而我个人觉得这个插件,从现在的角度来看,未必是对的,当然它的做法可能是向下兼容。

现在的打包工具,拿webpack来说,有一个配置:resolve.mainFields

alt

也就是说在package.json中,有mainmodule同时存在的情况下,会优先考虑module

那么module是啥,其实也是babel编译出来的,但是没有编译import,它可以用来实现tree shaking

我在前面之所以说babel-import-plugin有问题,是因为它的编译结果指向的是lib目录,而不是es目录。

假设我们未引用babel-import-plugin,直接这样书写代码:

import { Button } from "antd";

是的,你可能猜到了。上面打包的结果就是es目录里面的,也就是说它会自动tree-shanking,但需要注意的是上面的Button并没有样式。

antd的样式是写在component/style文件夹里面。

这个babel-import-plugin帮我们按需加载了相应模块的js和css。但说到css,它默认又会把它的那一套reset样式加载进来,即全局的样式。

在同用一套UI的实施过程中,我的思路是这样的,不再使用antd的样式,直接先copy到自己的UI样式中。但后期要修改,起码得实现两个东西:

  • 变量定义
  • 皮肤(在一个公司里面,可能不同的团队会用不同的skin)

但老实讲,我觉得css比js难多了。。

es目录的生成方案

rollup

import babel from 'rollup-plugin-babel'
import commonjs from 'rollup-plugin-commonjs'
import external from 'rollup-plugin-peer-deps-external'
import postcss from 'rollup-plugin-postcss'
import resolve from 'rollup-plugin-node-resolve'
import url from 'rollup-plugin-url'
import svgr from '@svgr/rollup'

import pkg from './package.json'

export default {
  input: 'src/index.js',
  output: [
    {
      file: pkg.main,
      format: 'cjs',
      sourcemap: true
    },
    {
      file: pkg.module,
      format: 'es',
      sourcemap: true
    }
  ],
  plugins: [
    external(),
    postcss({
      modules: true
    }),
    url(),
    svgr(),
    babel({
      exclude: 'node_modules/**',
      plugins: [ 'external-helpers' ]
    }),
    resolve(),
    commonjs()
  ]
}

简单地说:就是通过format来区分是打包commonjs还是es

这个写法问题不大,但对于css不能写在js里面加载,如果写在js里面的话。那么抽css会变成这样的结果:

alt

gulp/node+ babel

其实问题的本质就是babel的配置不同。

我抄了一份vantbabel配置:

module.exports = function (api) {
  const { BABEL_MODULE, NODE_ENV } = process.env;
  const useESModules = BABEL_MODULE !== 'commonjs' && NODE_ENV !== 'test';

  api && api.cache(false);

  return {
    presets: [
      [
        '@babel/preset-env',
        {
          loose: true,
          modules: useESModules ? false : 'commonjs'
        }
      ],
      [
        '@vue/babel-preset-jsx',
        {
          functional: false
        }
      ]
    ],
    plugins: [
      [
        '@babel/plugin-transform-runtime',
        {
          corejs: false,
          helpers: true,
          regenerator: false,
          useESModules
        }
      ],
      '@babel/plugin-syntax-dynamic-import',
      '@babel/plugin-transform-object-assign'
    ]
  };
};

看代码不难发现有一个BABEL_MODULE参数,再来看一下它的构建吧:

alt

文档

可选择的方案其实挺多的,比如bishengdoczdoc-scripts

我试用了docz,但代码显示那一块不好。它没法像antd的文档那样,在代码编辑器内出现import。针对那种情况,只能通过写注释啥的来处理。

doc-scripts没试用,不敢说难用啥的。

antd来说,其实它的代码区域(预览)是借助自己写了一些class component来实现的,所以也不能怪工具。

前阵子同事给推荐了一款开源的产品,叫:storybook。地址:storybook

然后这几天,我翻了一下,本来觉得没什么亮点,直到我看到这个项目:wix-style-react

这文档真的写的很6,而且就是基于storybook的,但它的代码预览使用了自己的代码,但似乎可以抄过来,那尝试一下吧。

alt

// index.stories.js
import React from 'react';

import { storiesOf } from '@storybook/react';

import Markdown from 'wix-storybook-utils/Markdown';
import CodeExample from 'wix-storybook-utils/CodeExample';
import Button from './examples/button';
import ButtonRaw from '!raw-loader!./examples/button';

import ButtonReadme from './examples/README.md';

storiesOf('Button', module)
  .add('test', () => (
    <div>
      <Markdown source={ButtonReadme} />
      <CodeExample title="Button" code={ButtonRaw}>
        <Button />
      </CodeExample>
    </div>
  ));

expamle的button.js代码如下:

alt

在尝试的过程中,发现了几个问题:

  • 需要在.storybook使用自定义的webpack,不然scss编译会报错,Markdown组件有依赖scss文件
  • Markdown的scss写法,是需要用module的,不然最终解析出来的css会带有:global
  • 只有CodeExample组件,那么样式会丢失,代码的相关高亮都在Markdown组件中,不过一般这两个组件都会用到

关于第二点,就是配置中的这个:

alt

OK。。那我们来看一下效果吧:

alt

alt

个人感觉相当地赞。。。

本文链接:www.my-fe.pub/post/react-component-demand-load-and-doc.html

-- EOF --

Comments

评论加载中...

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