指令添加组件【补充】

前面有介绍使用资源加载的方式自动注册组件,这次我们来介绍一下使用创建指令来添加组件。

这里为完整代码:

相关内容:

目录介绍

|-- auto-register-components
    |-- src
    |   |-- App.vue
    |   |-- main.ts
    |   |-- style.css
    |   |-- vite-env.d.ts
    |   |-- assets
    |   |   |-- vue.svg
    |   |   |-- iconfont  			  // 字体图标资源		
    |   |   |   |-- iconfont.css      // 普通版本,当前使用的
    |   |   |   |-- index.scss		  // 自定义前缀版本
    |   |   |-- images				  // 图片资源
    |   |       |-- index.ts
    |   |       |-- icons
    |   |       |   |-- language.svg
    |   |       |   |-- moon.svg
    |   |       |   |-- sun.svg
    |   |       |-- logo
    |   |           |-- index.svg
    |   |-- constant				  // 常量文件
    |   |   |-- images.ts	
    |   |-- globalComponents		  // 需要全局注册的组件
    |       |-- FontIcon              // 字体图标组件
    |       |   |-- index.vue 
    |       |   |-- README.md
    |       |-- ImgIcon               // 图片组件
    |           |-- index.vue
    |           |-- README.md
    |       |-- importComponent.ts	  // 引入全局组件【*】
    |       |-- index.ts		      // 全局注册的主要文件
    |-- tools						  // 指令实现部分
    |   |-- component.js			  // 指令主体
    |   |-- template				  // 文件模板内容
    |   |   |-- components.js	
    |   |-- utils					  // 工具类
    |       |-- file.js
    |       |-- index.js
    |       |-- util.js
    |-- typings						  // 其他的ts类型,主要用做全局组件类型提示
    |   |-- components.d.ts 		  // 全局组件类型文件【*】
    |-- .gitignore
    |-- index.html 
    |-- package.json 
    |-- README.md
    |-- tsconfig.json
    |-- tsconfig.node.json
    |-- vite.config.ts 
  • 以上【*】的文件都需要使用node修改文件
  • 全局组件需要统一注册,所以单独新增【globalComponents】文件夹放公共组件
    • 因为要使用node修改文件,所以尽量抽出不需要改动的逻辑为一个文件
      • 添加一个importComponent.ts作为所有的公共组件引入,后面使用指令添加的时候就自动为该文件添加一行引入代码即可
      • 抽出一个index.ts,作为注册使用。这个文件导入importComponent.ts导出的组件,然后进行注册
  • typings: 全局组件没有参数提示,所以需要使用这个来进行添加
    • 上文中我们使用shims-vue.d.ts文件进行注册全局组件,但是因为这个可能全局使用,后面修改有所影响,所以我们换了一个components.d.ts作为自动导入全局组件的类型注册。

思路介绍

  • 添加指令。运行components.js文件。
    • globalComponents目录下创建组件
    • importComponent.ts文件中导入组件【这里写的越简单,对文件的修改就越简单】
    • 修改typings/components.d.ts文件,添加组件类型

思路实现

恢复主要文件 + 添加指令 + 补充工具函数

src\globalComponents\index.ts

import { App } from "vue";
import * as components from "./importComponent";
 


const install = (app: App) => {
  // 转一下类型
  let componentList: {[key:string]: any} = components;
  for (const key in componentList) {
    // 标记:1 
    const element = componentList[key].default;

    if (element.name) {
      app.component(element.name, element);
    }
  }
}


export default { install }

标记1:后面有对这里说明

入口文件main.ts


import { createApp } from "vue";
import App from "./App.vue";
import GlobalComponents from "./globalComponents/main";
import "@/assets/iconfont/iconfont.css";



const app = createApp(App);
// 注册全局组件 
app.use(GlobalComponents); 

// 挂载
app.mount("#app");

package.json

{ 
  // ....
  "scripts": { 
     // ....
    "component": "ts-node  ./tools/component.js"
  }, 
}

会发现,这两个文件又回到了以前的样子,那是因为我们不在需要异步处理了,所以恢复了以前的方式,接下来主要看看我们的主体tools\component.js

该指令为:npm run component 组件名称

tools\utils

import fs from 'fs';
import path from "path";   
  

// 递归创建目录 同步方法
export function mkdirSync(dirname) {
    if (!fs.existsSync(dirname)) {
        if (mkdirSync(path.dirname(dirname))) {
            fs.mkdirSync(dirname);
            return true;
        }
    }
} 

/**
 * 创建输出目录
 * @param outputPath 输出目录
 */
export function mkdirFileDirectory(outputPath) {
    if (!fs.existsSync(outputPath)) {
        fs.mkdirSync(outputPath);
    }
}

实现指令

第一步:创建组件

添加模板 tools\template\components.js

export const componentTemplateFunc = (name) => {


   const componentTemplate = `
<template>
   <div>${name}</div>
</template>

<script setup lang="ts"> 

defineOptions({ name: "${name}" });

// 逻辑代码部分


</script>
`
   return { componentTemplate };
}

组件名称这个参数后面所有导入,类型等都有相关,所以我们这里导出一个函数,接受一个组件名称,然后返回模板内容

创建 组件文件 tools\component.js


import path from "path";
import fs from "fs";
import { mkdirFileDirectory } from "./utils/index.js";
import { componentTemplateFunc } from "./template/components.js";


// 获取组件名称
const componentName = process.argv[2];
// 获取组件文件夹根目录
const componentRoot = path.resolve(process.cwd(), "./src/globalComponents");



// 获取组件模板
const { componentTemplate } = componentTemplateFunc(componentName);



function createComponents() {
    const componentPath = path.join(componentRoot, componentName);
    // 获取组件目录
    const componentFilePath = path.join(componentPath, "./index.vue");
    // 创建组件
    console.log("开始创建组件");
    mkdirFileDirectory(componentPath);
    fs.writeFileSync(componentFilePath, componentTemplate);
    console.log("组件创建完成");
}
 
 

function beginCreateComponent() {
    createComponents(); 
}


beginCreateComponent();
  • 使用process.argv[2]获取组件名称
  • 获取组件存放位置目录地址
  • 使用mkdirFileDirectory方法进行递归创建文件夹
  • 创建文件,并写入内容

注意:这里文件一定要带后缀,不然可能会找不到模块

第二步:引入组件

上面有提到,我们引入组件越简单,那么对这个文件的编辑也就越简单,最好能一行搞定,不然可能有多处修改,所以导出的方法,我采用的是以下格式

export * as 组件名称 from "./组件名称/index.vue";

这里在使用的时候要加上default才能访问到组件对象,也就是为什么标记1要加上default

添加模板 tools\template\components.js

export const componentTemplateFunc = (name) => {

   // 略
    
   const componentExportTemplate = `\nexport * as ${name} from "./${name}/index.vue";`
   
   
   return { componentTemplate, componentExportTemplate };
}

这里\n是为了换行

引入组件文件添加组件 tools\component.js

// 略

// 获取组件模板
const { componentTemplate, componentExportTemplate } = componentTemplateFunc(componentName);


// 略createComponents


function importComponent() {
    // 获取组件导入的文件
    const fileImportFilePath = path.join(componentRoot, "./importComponent.ts");
    // 组件导入
    console.log("开始组件导入");
    // 查找是否有该导出文件 
    try {
        fs.appendFileSync(fileImportFilePath, componentExportTemplate);
    } catch (error) {
        fs.writeFileSync(fileImportFilePath, componentExportTemplate);
    }
    console.log("组件导入完成")

}

function beginCreateComponent() {
    createComponents();
    importComponent();
}


beginCreateComponent();
  • 获取到导入组件的文件
  • 直接进行文件内容追加,没有文件就会报错,报错就进行创建并写入

到这里运行完成指令,组件其实就可以使用了,但是并没有添加类型

第三步:添加类型

这一步稍微难一点,前面引入都在一个点,可以直接尾部添加。但是类型文件修改点两处,还有在文件中间的内容,所以需要准确定位到位置。

看以往的文件,我们发现主要有两个位置,一个是引入组件,一个是添加类型。所以我们使用标记,标记哪里是需要添加类型,哪里需要引入文件,然后进行文件替换即可

添加模板 tools\template\components.js

export const componentTemplateFunc = (name) => {

   // 略
    
   const addTypeTemplate = `\nimport ${name} from "@/globalComponents/${name}/index.vue";\n`;
   const registerTypeTemplate = `\n        ${name}: typeof ${name};\n        `;
   const importComponentFlag = "/** 组件导入位置 **/";
   const registerComponentFlag = "/** 类型申明添加位置 **/";
   const typeReplaceFlagList = [{
      flagText: importComponentFlag,
      replaceText: addTypeTemplate + importComponentFlag
   }, {
      flagText: registerComponentFlag,
      replaceText: registerTypeTemplate + registerComponentFlag
   }];

   const initTypeTemplate = `
/**
 * 全局组件类型
 */
${addTypeTemplate}
${importComponentFlag}
   
    
   
declare module 'vue' {
    interface GlobalComponents {
        ${registerTypeTemplate}
        ${registerComponentFlag}
    } 
 }
   
   
export { }
`

   return { componentTemplate, componentExportTemplate, typeReplaceFlagList, initTypeTemplate }
}
  • 这里返回一个数组【typeReplaceFlagList】,然后数组里记录了标记文案和替换文案,后面循环将文档进行内容替换即可

  • 并且还有一个初始化模板,当发现没有该文件的时候可以直接使用

  • 一定要记得替换文案一定要带上标识符,否则只能创建一个组件,后面要是再创建组件文件中就没有标记了,那么就没有办法添加相关说明了

引入组件文件添加组件 tools\component.js

// 略
 
// 获取组件模板
const { componentTemplate, componentExportTemplate, initTypeTemplate, typeReplaceFlagList } = componentTemplateFunc(componentName);




// 略createComponents  importComponent


function addComponentType() {
    const typePath = path.resolve(process.cwd(), "./typings/components.d.ts");
    // 添加类型
    console.log("开始添加组件类型");
    try {
        let value = fs.readFileSync(typePath, "utf-8");
        let resultText = typeReplaceFlagList.reduce((pre, { replaceText, flagText }) => pre.replace(flagText, replaceText), value);
        fs.writeFileSync(typePath, resultText);
    } catch (error) {
        fs.writeFileSync(typePath, initTypeTemplate);
    }
    console.log("组件类型添加完成")

}

function beginCreateComponent() {
    createComponents();
    importComponent();
    addComponentType();
}


beginCreateComponent();
  • 获取类型文件
  • 读取文件,然后使用typeReplaceFlagList数组进行替换,最后写回文件中
  • 如果发现没有该文件,那么就写入初始化模板到文件里去

最后

到此,组件导入相关内容就完全结束了,后续还可以优化一下内容,比如说:

  • 添加同名组件提示错误
  • 添加指令移除组件
  • 添加指令自动引入未注册的全局组件

原文链接:https://juejin.cn/post/7261090843393343545 作者:懒懒的天空

(0)
上一篇 2023年7月30日 上午10:15
下一篇 2023年7月30日 上午10:25

相关推荐

发表回复

登录后才能评论