经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Vue.js » 查看文章
Vue3 企业级优雅实战 - 组件库框架 - 10 实现组件库 cli - 下
来源:cnblogs  作者:程序员优雅哥(公\/同)  时间:2023/2/10 11:04:25  对本文有异议

上文创建了一堆 utils、component-info,并实现了新组件模块相关目录和文件的创建。本文继续实现后面的内容。

1 组件样式文件并导入

src/service 目录中创建 init-scss.ts 文件,该文件导出 initScss 函数。

由于 .vue 类型的组件的样式就直接写在了 style 中,故首先判断组件类型是否是 tsx,tsx 类型的组件才进行这一步的操作:

  1. scss/components/ 目录下创建组件的 scss 文件 _xxx.module.scss
  2. scss/components/index.scss 中导入 _xxx.module.scss

1.1 init-scss.ts

代码实现如下:

  1. import { ComponentInfo } from '../domain/component-info'
  2. import path from 'path'
  3. import { scssTemplate } from '../util/template-utils'
  4. import fs from 'fs'
  5. import { g } from '../util/log-utils'
  6. const updateComponentScssIndex = (scssRootPath: string, lineName: string) => {
  7. const indexScssPath = path.resolve(scssRootPath, 'components/index.scss')
  8. const content = fs.readFileSync(indexScssPath).toString()
  9. const newContent = content.substring(0, content.length) + `@use "${lineName}.module";\n`
  10. fs.writeFileSync(indexScssPath, newContent)
  11. }
  12. /**
  13. * 创建组件库 scss 文件,并在 scss/components/index.scss 中引入该文件
  14. */
  15. export const initScss = (componentInfo: ComponentInfo) => new Promise((resolve, reject) => {
  16. // tsx 类型需要创建scss文件
  17. if (componentInfo.type === 'tsx') {
  18. const { parentPath, lineName, lineNameWithPrefix } = componentInfo
  19. // scss 根目录(packages/scss)
  20. const scssRootPath = path.resolve(parentPath, 'scss')
  21. // 1. 创建组件的 scss 文件
  22. const componentScssPath = path.resolve(scssRootPath, `components/_${lineName}.module.scss`)
  23. fs.writeFileSync(componentScssPath, scssTemplate(lineNameWithPrefix))
  24. // 2. 在组件库 scss 入口文件 (packages/components/index.scss)引入上面创建的文件
  25. updateComponentScssIndex(scssRootPath, lineName)
  26. g('component scss init success')
  27. }
  28. resolve(componentInfo)
  29. })

1.2 template-utils.ts

上面的 init-scss.ts 在创建 scss 文件时调用了 template-utils.ts 中的 scssTemplate 函数获取模板。故需要在 util/template-utils.ts 中添加该函数:

  1. /**
  2. * scss 文件模板
  3. */
  4. export const scssTemplate = (lineNameWithPrefix: string): string => {
  5. return `@import "../tools";
  6. @import "../acss/mp";
  7. @import "../base/var.module";
  8. @include b('${lineNameWithPrefix}') {
  9. }
  10. `
  11. }

2 添加到组件库入口模块

新组件和样式创建完成,接下来便是将新组件模块安装到组件库入口模块的依赖中。在 src/service 目录中创建 update-component-lib.ts 文件,该文件导出函数 updateComponentLib。该函数需要完成两件事:

  1. 在组件库入口模块中安装新组件为依赖;
  2. 更新组件库入口模块的 index.ts 文件,引入新组件。

代码实现如下:

  1. import { ComponentInfo } from '../domain/component-info'
  2. import { execCmd } from '../util/cmd-utils'
  3. import path from 'path'
  4. import { Config } from '../config'
  5. import fs from 'fs'
  6. import { g } from '../util/log-utils'
  7. const updateComponentLibIndex = (libPath: string, componentInfo: ComponentInfo) => {
  8. const indexPath = path.join(libPath, 'index.ts')
  9. const content = fs.readFileSync(indexPath).toString()
  10. const index1 = content.indexOf('// import component end')
  11. const index2 = content.indexOf('] // components')
  12. const result = `${content.substring(0, index1)}` +
  13. `import ${componentInfo.upCamelName} from '${componentInfo.nameWithLib}'\n` +
  14. content.substring(index1, index2 - 1) +
  15. `,\n ${componentInfo.upCamelName}\n` +
  16. content.substring(index2)
  17. fs.writeFileSync(indexPath, result)
  18. }
  19. /**
  20. * 更新组件库入口
  21. */
  22. export const updateComponentLib = async (componentInfo: ComponentInfo) => {
  23. // 组件库入口的路径
  24. const libPath = path.resolve(componentInfo.parentPath, Config.COMPONENT_LIB_NAME)
  25. // 1. 添加新创建的组件到依赖中
  26. await execCmd(`cd ${libPath} && pnpm install ${componentInfo.nameWithLib}`)
  27. // 2. 更新入口 index.ts
  28. updateComponentLibIndex(libPath, componentInfo)
  29. g('component library update success')
  30. }

3 组件库文档相关文件

3.1 init-doc.ts

src/service 目录中创建 init-doc.ts 文件,该文件导出函数 initDoc。该函数需要完成三件事:

  1. 创建组件的 MarkDown 文档;
  2. 创建组件 MD 文档中的 demo;
  3. 更新组件库文档菜单。

代码实现如下:

  1. import { ComponentInfo } from '../domain/component-info'
  2. import { g } from '../util/log-utils'
  3. import path from 'path'
  4. import fs from 'fs'
  5. import { demoTemplate, mdTemplate } from '../util/template-utils'
  6. /**
  7. * 创建组件文档、demo及更新菜单
  8. */
  9. export const initDoc = (componentInfo: ComponentInfo) => {
  10. // 组件库文档根路径
  11. const docRootPath = path.resolve(componentInfo.parentPath, '../docs')
  12. const { lineName, lineNameWithPrefix, upCamelName, zhName } = componentInfo
  13. // 1. 创建组件的 MD 文档
  14. fs.writeFileSync(path.resolve(docRootPath, `components/${lineName}.md`), mdTemplate(componentInfo))
  15. // 2. 创建组件文档中的 Demo
  16. fs.mkdirSync(path.resolve(docRootPath, `demos/${lineName}`))
  17. fs.writeFileSync(path.resolve(docRootPath, `demos/${lineName}/${lineName}-1.vue`), demoTemplate(lineNameWithPrefix))
  18. // 3. 更新组件库文档菜单
  19. const menuPath = path.resolve(docRootPath, 'components.ts')
  20. const content = fs.readFileSync(menuPath).toString()
  21. const index = content.indexOf('] // end')
  22. const result = content.substring(0, index - 1) +
  23. `,\n { text: '${upCamelName} ${zhName}', link: '/components/${lineName}' }\n` +
  24. content.substring(index)
  25. fs.writeFileSync(menuPath, result)
  26. g('component document init success')
  27. }

3.2 template-utils.ts

上面的 init-doc.ts 调用了 mdTemplatedemoTemplate 两个函数,在 template-utils.ts 中添加这两个函数:

  1. export const mdTemplate = (componentInfo: ComponentInfo) => {
  2. return `
  3. # ${componentInfo.upCamelName} ${componentInfo.zhName}
  4. ## 基本使用
  5. <preview path="../demos/${componentInfo.lineName}/${componentInfo.lineName}-1.vue" title="基本使用" description=" "></preview>
  6. ## 组件 API
  7. ### Attributes 属性
  8. | 参数 | 说明 | 类型 | 可选值 | 默认值 |
  9. | ---- | ---- | ---- | ---- | ---- |
  10. | | | | | |
  11. ### Methods 方法
  12. | 方法名 | 说明 | 参数 | 返回值 |
  13. | ---- | ---- | ---- | ---- |
  14. | | | | |
  15. ### Events 事件
  16. | 事件名 | 说明 | 参数 | 返回值 |
  17. | ---- | ---- | ---- | ---- |
  18. | | | | |
  19. ### Slots 插槽
  20. | 插槽名 | 说明 | 参数 |
  21. | ---- | ---- | ---- |
  22. | | | |
  23. `
  24. }
  25. export const demoTemplate = (lineNameWithPrefix: string) => {
  26. return `<template>
  27. <${lineNameWithPrefix}></${lineNameWithPrefix}>
  28. </template>
  29. <script lang="ts" setup>
  30. </script>
  31. <style scoped lang="scss">
  32. </style>
  33. `
  34. }

这两个函数的模板可以自己去定义。

4 create-component.ts

四个步骤都已实现,最后需要在 src/command/create-component.ts 文件中的 createNewComponent 函数中完成上面四个 service 的调用。

4.1 import

导入四个service及使用到的其他函数:

  1. import { ComponentInfo } from '../domain/component-info'
  2. import { closeLoading, showLoading } from '../util/loading-utils'
  3. import { g, r } from '../util/log-utils'
  4. import { initComponent } from '../service/init-component'
  5. import { initScss } from '../service/init-scss'
  6. import { updateComponentLib } from '../service/update-component-lib'
  7. import { initDoc } from '../service/init-doc'

4.2 createNewComponent

该函数首先根据用户输入,构造 ComponentInfo 对象,然后依次调用引入的四个 service,完成组件创建的全部流程:

  1. const createNewComponent = async (componentName: string, description: string, componentType: string) => {
  2. console.log(componentName, description, componentType)
  3. showLoading('Generating, please wait...')
  4. try {
  5. // 1. 构造 ComponentInfo 对象
  6. const componentInfo = new ComponentInfo(componentName, description, componentType)
  7. // 2. 创建组件目录及文件
  8. await initComponent(componentInfo)
  9. // 3. 创建样式
  10. await initScss(componentInfo)
  11. // 4. 更新组件库入口
  12. await updateComponentLib(componentInfo)
  13. // 5. 组件库文档
  14. initDoc(componentInfo)
  15. closeLoading()
  16. g(`component [${componentInfo.lineName} ${componentInfo.zhName}] created done!`)
  17. } catch (e: any) {
  18. closeLoading()
  19. r(e.message)
  20. }
  21. }

组件库 cli 就这样完成了。运行 pnpm run gen,依次输入组件名、组件中文名,选择组件类型,便自动完成组件的创建、注册、文档的创建了。优雅哥花了大量篇幅介绍 cli 的开发,不仅仅可以在这里使用,通过本案例的实现,希望大家可以将这种方式移植到其他地方,如从 github 拉取代码模板、自动化 CI/CD 等。

下一篇文章将介绍组件库的打包构建和发布。

感谢阅读本文,如果本文给了你一点点帮助或者启发,还请三连支持一下,了解更多内容工薇号“程序员优雅哥”。

原文链接:https://www.cnblogs.com/youyacoder/p/17105747.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号