Vue3 エンタープライズ レベルの優雅さの実践 - コンポーネント ライブラリ フレームワーク - 10 コンポーネント ライブラリ cli の実装 - パート 2

上記は、一連のユーティリティとコンポーネント情報を作成し、新しいコンポーネント モジュールに関連するディレクトリとファイルの作成を実装します。この記事では引き続き以下の内容を実装していきます。

1 コンポーネント スタイル ファイルとインポート

initScss関数をエクスポートするinit-scss.tsファイルをsrc/serviceディレクトリに作成します

.vue タイプのコンポーネントのスタイルはstyleに直接記述されているため、最初にコンポーネントのタイプがtsxであるかどうかが判断されます。このステップは tsx タイプのコンポーネントに対してのみ実行されます。

  1. コンポーネントの scss ファイル_xxx.module.scssをscss/components/ディレクトリに作成します
  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 テンプレート-utils.ts

上記のinit-scss.ts は、 scss ファイルの作成時にtemplate-utils.tsscssTemplate関数を呼び出してテンプレートを取得します。したがって、この関数を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をエクスポートします。この関数は次の 2 つのことを行う必要があります。

  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をエクスポートします。この関数は次の 3 つのことを行う必要があります。

  1. コンポーネントの MarkDown ドキュメントを作成します。
  2. コンポーネント MD ドキュメントでデモを作成します。
  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 テンプレート-utils.ts

上記の init-doc.ts は2 つの関数mdTemplatedemoTemplateを呼び出します。これらの 2 つの関数を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>
`
}

これら 2 つの関数のテンプレートは自分で定義できます。

4 コンポーネントの作成.ts

4 つのステップが実装され、最後に、src/command/create-component.tsファイルのcreateNewComponent関数で上記の 4 つのサービス呼び出しを完了する必要があります。

4.1 インポート

使用される 4 つのサービスとその他の機能をインポートします。

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 新しいコンポーネントの作成

この関数は、まずユーザー入力に基づいて ComponentInfo オブジェクトを構築し、次に導入された 4 つのサービスを順番に呼び出して、コンポーネント作成のプロセス全体を完了します。

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を実行し、コンポーネント名、コンポーネントの中国語名を入力し、コンポーネントの種類を選択すると、コンポーネントの作成、登録、ドキュメントの作成が自動的に完了します。Yaya 兄弟は cli の開発について多くのスペースを割いて紹介しましたが、これはここでのみ使用できるものではありません。この事例の実装を通じて、この方法を github からのコード テンプレートのプルや自動 CI/ など、他の場所にも移植できることを願っています。 CDなど

次の記事では、コンポーネント ライブラリのパッケージ化、構築、リリースについて紹介します。

この記事をお読みいただきありがとうございます。この記事が少しでも助けやインスピレーションを与えた場合は、Gongwei アカウント「Brother Elegant Programmer」についてさらに詳しく知るために 3 回サポートしてください。

おすすめ

転載: blog.csdn.net/youyacoder/article/details/128936988