在Vue组件库中新增组件的流程 + 脚本实现
简介
要在vue组件库中新增一个组件,通常会创建这几个文件:
MyComponent.vueindex.tsMyComponent.types.tsMyComponent.scss
文件内容是固定的,制作一个模板用来生成它们可以减少重复的工作。
基本文件内容
MyComponent.vue
<template>
<div class="my-component">
</div>
</template>
<script setup lang="ts">
import { myComponentProps } from './MyComponent.types';
const props = defineProps(myComponentProps);
const emits = defineEmits<MyComponentEmits>();
</script>
index.ts
import type { App } from 'vue';
import MyComponent from './MyComponent.vue';
import './MyComponent.scss';
MyComponent.install = (app: App) => {
app.component('PrefixMyComponent', MyComponent);
};
export { MyComponent };
MyComponent.types.ts
export const myComponentProps = {
}
export interface MyComponentEmits {
}
MyComponent.scss
可以在头部导入全局样式的文件
.my-component {
}
整理为模板
命名格式
注意到文件中出现了多种命名:
MyComponentupperCamelmy-componentkebabmyComponentlowerCamelPrefix会遇到项目的组件需要前缀的情况,如组件名为Input,但是导出叫McInput
所以需要编写函数获取多种命名
function getNames(upperCase) {
const kebabCase = upperCase.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
const lowerCase = upperCase.charAt(0).toUpperCase() + upper.slice(1);
return {
upper: upperCase,
kebab: kebabCase,
lower: lowerCase,
};
}
模板
将MyComponent替换为模板内容即可
MyComponent.vue -> vue.template
<template>
<div class="{{kebab}}">
</div>
</template>
<script setup lang="ts">
import { {{lower}}Props } from './{{upper}}.types';
const props = defineProps({{lower}}Props);
const emits = defineEmits<{{upper}}Emits>();
</script>
index.ts -> index.template
import type { App } from 'vue';
import {{upper}} from './{{upper}}.vue';
import './{{upper}}.scss';
{{upper}}.install = (app: App) => {
app.component('{{prefix}}{{upper}}', {{upper}});
};
export { {{upper}} };
MyComponent.types.ts -> types.template
export const {{lower}}Props = {
}
export interface {{upper}}Emits {
}
MyComponent.scss -> scss.template
可以在头部导入全局样式的文件
.{{kebab}} {
}
脚本文件
这里的add-component.js文件放在项目根目录中的scripts文件夹中,模板文件的路径为root/scripts/templates/*.template,输出路径为root/src/components
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
import { join } from 'path';
const templatesDir = join(__dirname, './templates');
const componentsDir = join(__dirname, '../src/components');
function getNames(upperCase) {
const kebabCase = upperCase.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
const lowerCase = upperCase.charAt(0).toUpperCase() + upper.slice(1);
return {
upper: upperCase,
kebab: kebabCase,
lower: lowerCase,
};
}
function generateTemplates(upper, kebab, lower){
const files = [
['vue.template', `${upper}.vue`],
['index.template', 'index.ts'],
['scss.template', `${upper}.scss`],
['types.template', `${upper}.types.ts`],
];
for (const [template, target] of files){
const src = join(templatesDir, template);
const dst = join(componentsDir, target);
const content = readFileSync(src, 'utf8')
.replace(/\{\{pascal\}\}/g, pascal)
.replace(/\{\{kebab\}\}/g, kebab)
.replace(/\{\{lower\}\}/g, lower);
writeFileSync(dst, content, 'utf8');
console.log(`创建文件${dst}`);
}
}
function createComponent(componentName){
const {upper, kebab, lower} = getNames(componentName);
const outputDir = join(componentsDir, upper);
if (existsSync(outputDir)) {
console.error(`组件${upper}已存在!`);
process.exit(1);
}
mkdirSync(outputDir, { recursive: true });
generateTemplates(upper, kebab, lower);
}
function main(){
const args = process.argv.slice(2);
if (args.length === 0) {
console.error('❌ 请提供组件名称!');
process.exit(1);
}
const componentName = args[0];
createComponent(componentName);
}
main();
全局使用
不太可能为所有项目都配置一个这样的脚本和模板文件夹,毕竟有的项目不是自己的,所以在全局中配置一个这样的脚本很有必要。编写自己的javascript脚本库辅助开发讲述了如何配置项目,下面给出全局的创建vue组件的代码部分。
./index.js
#!/usr/bin/env node
import yargs from 'yargs/yargs';
import { hideBin } from 'yargs/helpers';
yargs(hideBin(process.argv))
.command(
'add-vue-component <componentName>',
'Create a new Vue component',
(yargs) => {
return yargs.positional('componentName', {
describe: 'Name of the Vue component',
type: 'string',
});
},
async (argv) => {
const { default: createVueComponent } = await import('./create-vue-component/index.js');
createVueComponent(argv.componentName, process.cwd());
}
)
.alias('add-vue-component', 'vc')
.demandCommand(1, 'You need at least one command before moving on')
.help()
.argv;
./create-vue-component/index.js
import fs from 'fs';
import path from 'path';
import getVueComponentTemplate from './template.js';
/**
* Creates a Vue component file in a specified directory.
*
* @param {string} componentName - The name of the component (e.g., 'my-component').
* @param {string} targetDir - The directory where the component should be created.
*/
function createVueComponent(componentName, targetDir) {
if (!componentName) {
console.error('Error: Component name is required.');
process.exit(1);
}
const componentDir = path.join(targetDir, componentName);
const componentFilePath = path.join(componentDir, 'index.vue');
if (fs.existsSync(componentDir)) {
console.error(`Error: Directory '${componentDir}' already exists.`);
return;
}
try {
fs.mkdirSync(componentDir, { recursive: true });
const componentTemplate = getVueComponentTemplate(componentName);
fs.writeFileSync(componentFilePath, componentTemplate);
console.log(`Successfully created component '${componentName}' at: ${componentFilePath}`);
} catch (error) {
console.error(`Error creating component '${componentName}':`, error);
process.exit(1);
}
}
export default createVueComponent;
./create-vue-component/template.js
/**
* Generates the content for a new Vue component file.
* @param {string} componentName - The name of the component.
* @returns {string} The Vue component template string.
*/
function getVueComponentTemplate(componentName) {
return `<template>
<div class="${componentName}">
<h1>${componentName} Component</h1>
</div>
</template>
<script>
export default {
name: '${componentName}',
props: {},
data() {
return {};
},
mounted() {},
methods: {}
};
</script>
<style scoped>
.${componentName} {
/* Add your component styles here */
}
</style>
`;
}
export default getVueComponentTemplate;
./create-vue-component/templates/
文件夹中的模板内容与上一节中的模板内容一样