vue3.0打造企业级组件库 大圣(Vue3 企业级优雅实战 – 组件库框架 – 10 实现组件库 cli – 下)
上文创建了一堆 utils 、component-info ,并实现了新组件模块相关目录和文件的创建 。本文继续实现后面的内容 。
1 组件样式文件并导入
在 src/service 目录中创建 init-scss.ts 文件 ,该文件导出 initScss 函数。
由于 .vue 类型的组件的样式就直接写在了 style 中 ,故首先判断组件类型是否是 tsx ,tsx 类型的组件才进行这一步的操作:
在 scss/components/ 目录下创建组件的 scss 文件 _xxx.module.scss; 在 scss/components/index.scss 中导入 _xxx.module.scss 。1.1 init-scss.ts
代码实现如下:
import { ComponentInfo } from ../domain/component-info import path from path import { scssTemplate } from ../util/template-utils import fs from fs import { g } from ../util/log-utils const updateComponentScssIndex = (scssRootPath: string, lineName: string) => { const indexScssPath = path.resolve(scssRootPath, components/index.scss) const content = fs.readFileSync(indexScssPath).toString() const newContent = content.substring(0, content.length) + `@use "${lineName}.module";\n` fs.writeFileSync(indexScssPath, newContent) } /** * 创建组件库 scss 文件 ,并在 scss/components/index.scss 中引入该文件 */ export const initScss = (componentInfo: ComponentInfo) => new Promise((resolve, reject) => { // tsx 类型需要创建scss文件 if (componentInfo.type === tsx) { const { parentPath, lineName, lineNameWithPrefix } = componentInfo // scss 根目录(packages/scss) const scssRootPath = path.resolve(parentPath, scss) // 1. 创建组件的 scss 文件 const componentScssPath = path.resolve(scssRootPath, `components/_${lineName}.module.scss`) fs.writeFileSync(componentScssPath, scssTemplate(lineNameWithPrefix)) // 2. 在组件库 scss 入口文件 (packages/components/index.scss)引入上面创建的文件 updateComponentScssIndex(scssRootPath, lineName) g(component scss init success) } resolve(componentInfo) })1.2 template-utils.ts
上面的 init-scss.ts 在创建 scss 文件时调用了 template-utils.ts 中的 scssTemplate 函数获取模板 。故需要在 util/template-utils.ts 中添加该函数:
/** * scss 文件模板 */ export const scssTemplate = (lineNameWithPrefix: string): string => { return `@import "../tools"; @import "../acss/mp"; @import "../base/var.module"; @include b(${lineNameWithPrefix}) { } ` }2 添加到组件库入口模块
新组件和样式创建完成 ,接下来便是将新组件模块安装到组件库入口模块的依赖中 。在 src/service 目录中创建 update-component-lib.ts 文件 ,该文件导出函数 updateComponentLib 。该函数需要完成两件事:
在组件库入口模块中安装新组件为依赖; 更新组件库入口模块的 index.ts 文件 ,引入新组件 。代码实现如下:
import { ComponentInfo } from ../domain/component-info import { execCmd } from ../util/cmd-utils import path from path import { Config } from ../config import fs from fs import { g } from ../util/log-utils const updateComponentLibIndex = (libPath: string, componentInfo: ComponentInfo) => { const indexPath = path.join(libPath, index.ts) const content = fs.readFileSync(indexPath).toString() const index1 = content.indexOf(// import component end) const index2 = content.indexOf(] // components) const result = `${content.substring(0, index1)}` + `import ${componentInfo.upCamelName} from ${componentInfo.nameWithLib}\n` + content.substring(index1, index2 - 1) + `,\n ${componentInfo.upCamelName}\n` + content.substring(index2) fs.writeFileSync(indexPath, result) } /** * 更新组件库入口 */ export const updateComponentLib = async (componentInfo: ComponentInfo) => { // 组件库入口的路径 const libPath = path.resolve(componentInfo.parentPath, Config.COMPONENT_LIB_NAME) // 1. 添加新创建的组件到依赖中 await execCmd(`cd ${libPath} && pnpm install ${componentInfo.nameWithLib}`) // 2. 更新入口 index.ts updateComponentLibIndex(libPath, componentInfo) g(component library update success) }3 组件库文档相关文件
3.1 init-doc.ts
在 src/service 目录中创建 init-doc.ts 文件 ,该文件导出函数 initDoc 。该函数需要完成三件事:
创建组件的 MarkDown 文档; 创建组件 MD 文档中的 demo; 更新组件库文档菜单 。代码实现如下:
import { ComponentInfo } from ../domain/component-info import { g } from ../util/log-utils import path from path import fs from fs import { demoTemplate, mdTemplate } from ../util/template-utils /** * 创建组件文档 、demo及更新菜单 */ export const initDoc = (componentInfo: ComponentInfo) => { // 组件库文档根路径 const docRootPath = path.resolve(componentInfo.parentPath, ../docs) const { lineName, lineNameWithPrefix, upCamelName, zhName } = componentInfo // 1. 创建组件的 MD 文档 fs.writeFileSync(path.resolve(docRootPath, `components/${lineName}.md`), mdTemplate(componentInfo)) // 2. 创建组件文档中的 Demo fs.mkdirSync(path.resolve(docRootPath, `demos/${lineName}`)) fs.writeFileSync(path.resolve(docRootPath, `demos/${lineName}/${lineName}-1.vue`), demoTemplate(lineNameWithPrefix)) // 3. 更新组件库文档菜单 const menuPath = path.resolve(docRootPath, components.ts) const content = fs.readFileSync(menuPath).toString() const index = content.indexOf(] // end) const result = content.substring(0, index - 1) + `,\n { text: ${upCamelName} ${zhName}, link: /components/${lineName} }\n` + content.substring(index) fs.writeFileSync(menuPath, result) g(component document init success) }3.2 template-utils.ts
上面的 init-doc.ts 调用了 mdTemplate 和 demoTemplate 两个函数,在 template-utils.ts 中添加这两个函数:
export const mdTemplate = (componentInfo: ComponentInfo) => { return ` # ${componentInfo.upCamelName} ${componentInfo.zhName} ## 基本使用 <preview path="../demos/${componentInfo.lineName}/${componentInfo.lineName}-1.vue" title="基本使用" description=" "></preview> ## 组件 API ### Attributes 属性 | 参数 | 说明 | 类型 | 可选值 | 默认值 | | ---- | ---- | ---- | ---- | ---- | | | | | | | ### Methods 方法 | 方法名 | 说明 | 参数 | 返回值 | | ---- | ---- | ---- | ---- | | | | | | ### Events 事件 | 事件名 | 说明 | 参数 | 返回值 | | ---- | ---- | ---- | ---- | | | | | | ### Slots 插槽 | 插槽名 | 说明 | 参数 | | ---- | ---- | ---- | | | | | ` } export const demoTemplate = (lineNameWithPrefix: string) => { return `<template> <${lineNameWithPrefix}></${lineNameWithPrefix}> </template> <script lang="ts" setup> </script> <style scoped lang="scss"> </style> ` }这两个函数的模板可以自己去定义 。
4 create-component.ts
四个步骤都已实现 ,最后需要在 src/command/create-component.ts 文件中的 createNewComponent 函数中完成上面四个 service 的调用 。
4.1 import
导入四个service及使用到的其他函数:
import { ComponentInfo } from ../domain/component-info import { closeLoading, showLoading } from ../util/loading-utils import { g, r } from ../util/log-utils import { initComponent } from ../service/init-component import { initScss } from ../service/init-scss import { updateComponentLib } from ../service/update-component-lib import { initDoc } from ../service/init-doc4.2 createNewComponent
该函数首先根据用户输入 ,构造 ComponentInfo 对象,然后依次调用引入的四个 service ,完成组件创建的全部流程:
const createNewComponent = async (componentName: string, description: string, componentType: string) => { console.log(componentName, description, componentType) showLoading(Generating, please wait...) try { // 1. 构造 ComponentInfo 对象 const componentInfo = new ComponentInfo(componentName, description, componentType) // 2. 创建组件目录及文件 await initComponent(componentInfo) // 3. 创建样式 await initScss(componentInfo) // 4. 更新组件库入口 await updateComponentLib(componentInfo) // 5. 组件库文档 initDoc(componentInfo) closeLoading() g(`component [${componentInfo.lineName} ${componentInfo.zhName}] created done!`) } catch (e: any) { closeLoading() r(e.message) } }组件库 cli 就这样完成了。运行 pnpm run gen ,依次输入组件名、组件中文名,选择组件类型 ,便自动完成组件的创建 、注册 、文档的创建了 。优雅哥花了大量篇幅介绍 cli 的开发 ,不仅仅可以在这里使用 ,通过本案例的实现 ,希望大家可以将这种方式移植到其他地方 ,如从 github 拉取代码模板 、自动化 CI/CD 等 。
下一篇文章将介绍组件库的打包构建和发布。
感谢阅读本文 ,如果本文给了你一点点帮助或者启发 ,还请三连支持一下 ,了解更多内容工薇号“程序员优雅哥 ” 。
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!