背景

随着小程序业务的一直迭代,组件越来越多,导致组件布局不清晰、复用率较低、组件反复和代码凌乱,以及还有其余如UI交互一致性和晋升效率等需要。

对于组件库来说,实现性能重要,而清晰的文档则更加重要,不然因为团队沟通协调老本高,还是会造成各自为战的状况,不能很好的解决组件反复和代码凌乱的问题。

抽离封装电动车Taro组件库以及组件库在线演示文档解决上述问题。

计划抉择

在文档这一块,PC和H5都有比拟成熟的计划框架,如dumi、VitePress等,但Taro这一块还没有成熟的计划框架,即便taro官网提供的taro-ui,组件也不够丰盛,而且阐明和示例不对应,减少了使用者的学习老本。

所以咱们抉择本人手动搭建组件库。

手动搭建还有一个劣势,像dumi这种框架尽管成熟,但整体很重,外面封装了过多的性能,而且大部分代码是黑盒的。对于大多数团队,只须要应用其局部外围性能,然而做减法是艰难且容易出错的。框架代码的黑盒也导致后续保护艰难。而手动搭建尽管一开始工作比拟多,然而后续保护非常容易。

次要模块

技术栈对于一套组件库,次要模块包含组件、文档、示例三大部分:

组件:es模块输入和按需加载是必须,rollup是最佳抉择,组件编写方面,和业务我的项目保持一致,应用React,组件更适宜应用hooks语法,而后ts,less这种就是惯例项。

示例:这里是Taro组件库和惯例组件库最大的区别,文档是运行在浏览器环境里的,所以想要组件demo能够展现,则须要应用Taro框架的打包h5性能。所以独自起一个我的项目用来跑demo代码,应用Taro React。至于怎么把文档里的代码引入到demo我的项目运行,则是应用webpack-chain自定义md文件的loader,本人写一个loader去实现。

文档:文档就是个动态页面,构建工具就抉择vite,配置简略,编译速度快。markdown内容局部,应用react-markdown等插件渲染,依据路由读取不同文件即可。右侧预览局部应用iframe加载demo我的项目。

目录构造

├─config│   ├─vite // vite配置│   └─rollup // rollup配置├─dist // doc打包产物├─docs-dist // 文档打包产物├─packages│   ├─demo // 组件Taro demo我的项目│   ├─doc // api文档我的项目│   └─ui // 组件库├─tests├─index.html // doc入口文件└─package.json

开发dev流程&路由关系

以减少一个标签组件tag为例:在ui/components下新增tag文件夹,组件入口tag/index.tsx,文档文件tag/README.md,组件打包产物 /dist/tag/index.js;

doc我的项目减少对应的路由 /tag,路由渲染菜单到左侧,当路由切换到 /tag 时,通过路由组合出import url引入 tag/README.md 文件,通过vite框架的 ?raw引入形式,读取md文件中的内容为字符串传递给 react-markdown 组件,渲染成两头的文档页面;

demo我的项目独立运行,打包成h5页面,通过iframe嵌入到右侧,先减少与组件路由对应的页面文件,当doc的路由变动时,iframe的路由也同步变动,demo切换到对应的路由页面,而后就能够读取对应的 tag/README.md;

demo我的项目通过自定义markdwon-loader读取 tag/README.md 文件中写的示例代码,对于组件的援用,通过配置webpack门路别名的形式,把@hb/rent-taro-components 指向组件产物 /dist/tag/index.js;

demo我的项目通过自定义markdwon-loader 把以后文档中全副示例代码组合成一个可运行页面输入,即可实时预览到全副示例代码的运行。

这套架构的长处

  • 文档和demo在一个md文件输入,保护不便且灵便
  • 多个demo代码独自编写,互不影响,清晰简洁
  • 右侧demo我的项目独立,如有须要,可独自公布demo
  • 小程序各模块性能清晰无黑盒,后续保护容易

各模块阐明

doc

doc目录构造

├─src│   ├─components│   │   ├─markdown-render // md文件渲染组件│   │   └─... // 其余布局组件│   ├─guides // 指南文档│   ├─router│   │   ├─comp-doc.ts // 组件路由│   │   ├─guide-doc.ts // 指南路由│   │   └─index.ts // 对立导出│   ├─app.less│   └─App.tsx // 主页面└─main.tsx // 入口文件

router
comp-doc.ts:

import Button from '@ui/button/README.md?raw';import Tag from '@ui/tag/README.md?raw';const compRoutes = [  {    name: '根底组件',    path: '/basic',    children: [      {        name: '按钮',        path: '/button',        component: Button,      },      {        name: '标签',        path: '/tag',        component: Tag,      },    ],  },];export default compRoutes;

demo

demo目录构造
除了减少了一个自定义loader,其余文件构造和规范Taro我的项目完全一致。

├─config│   ├─dev.js│   ├─index.js // Taro webpack配置│   ├─markdownloader.js // 自定义md文件loader│   └─prod.js├─src│   ├─pages│   │   └─basic // 文件夹门路与doc路由保持一致│   │       ├─button│   │       │   ├─index.config.ts│   │       │   └─index.tsx│   │       └─tag│   ├─app.config.ts // 路由配置│   ├─app.less│   ├─app.ts│   └─index.html└─package.json

route&外围代码
app.config.ts:
除了 /index 局部,路由与doc路由一一对应。

export default {  pages: [    'pages/basic/button/index', // pages和index之间的局部对应doc路由    'pages/basic/tag/index'  ],  ...}

pages/basic/button/index.tsx:
只须要把 md文件当做组件引入并 export即可,md 经 markdown-loader 解析后就是一个可运行组件。

// 间接引入组件库中README.md作为demo组件,代码解析在自定义loader中实现import Demo from '@components/button/README.md';export default Demo;

config/index.js:
通过 webpackChain 自定义loader。

const config = {  h5: {    // ...    webpackChain (chain, webpack) {      chain.merge({        module: {          rule: {            mdLoader: {              test: /\.md$/,              use: [                {                  loader: 'babel-loader',                  options: {}                },                {                  // 引入自定义loader                  loader: `${path.join(__dirname, './markdownLoader.js')}`,                  options: {}                },              ]            }          }        }      })    }    // ...  }}

ui

ts文件配置
通过rollup-plugin-typescript2配置对应的config文件。

import RollupTypescript from 'rollup-plugin-typescript2';// ...plugins: [  // ...  RollupTypescript({    tsconfig: resolveFile('config/tsconfig.rollup.json'),  }),]

按需加载

  • 多入口打包
  • 通过rollup-plugin-postcss抽离款式文件
  • 通过rollup-plugin-copy间接挪动less文件到dist(因为对于业务我的项目,不须要打包成css)
import RollupCopy from 'rollup-plugin-copy';import RollupPostCss from 'rollup-plugin-postcss';const inputD = {};compFiles.forEach((item) => {  const val = item.replace(cwd, '').replace('/packages/ui/src/components/', '').replace('/index.tsx', '');  inputD[`${val}`] = item;});const config = {  input: inputD,  plugins: [    RollupPostCss({      use: [        [          'less',          {            javascriptEnabled: true,          },        ],      ],      extract: 'index.less',      extensions: ['.css', '.less'],      makeAbsoluteExternalsRelative: false,    }),    RollupCopy({      verbose: true,      targets: [{        src: resolveFile('/packages/ui/src/components/*/*.less'),        dest: resolveFile('/dist/styles'),      }],    }),  ]}

(本文作者:范翔宇)

本文系哈啰技术团队出品,未经许可,不得进行商业性转载或者应用。非商业目标转载或应用本文内容,敬请注明“内容转载自哈啰技术团队”。