关于前端:Vue3-企业级优雅实战-组件库框架-10-实现组件库-cli-下

32次阅读

共计 6252 个字符,预计需要花费 16 分钟才能阅读完成。

上文创立了一堆 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

代码实现如下:

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。该函数须要实现两件事:

  1. 在组件库入口模块中装置新组件为依赖;
  2. 更新组件库入口模块的 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。该函数须要实现三件事:

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

代码实现如下:

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 调用了 mdTemplatedemoTemplate 两个函数,在 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-doc'

4.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 等。

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

感激浏览本文,如果本文给了你一点点帮忙或者启发,还请三连反对一下,理解更多内容工薇号“程序员优雅哥”。

正文完
 0