背景
随着小程序业务的一直迭代,组件越来越多,导致组件布局不清晰、复用率较低、组件反复和代码凌乱,以及还有其余如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'), }], }), ]}
(本文作者:范翔宇)
本文系哈啰技术团队出品,未经许可,不得进行商业性转载或者应用。非商业目标转载或应用本文内容,敬请注明“内容转载自哈啰技术团队”。