记录一下vue3+typescript开发公共组件的注意事项
项目结构
|-- examples 放置用于测试组件的代码
| |-- App.vue
| |-- main.ts
|-- packages 放置组件的代码
| |-- index.ts
| |-- shims-vue.d.ts
| |-- assets
| | |-- logo.png
| |-- components
| |-- helloWorld
| |-- HelloWorld.tsx 组件的定义
| |-- index.ts 组件暴露以及全局声明
|-- public
| |-- favicon.ico
| |-- index.html
|-- tests
| |-- unit
| |-- example.spec.ts
|-- .browserslistrc
|-- .eslintrc.js
|-- .gitignore
|-- babel.config.js
|-- jest.config.js
|-- package-lock.json
|-- package.json
|-- README.md
|-- tsconfig.json
|-- vue.config.js
组件编写
组件定义
// packages/components/helloWorld/HelloWorld.tsx
import { defineComponent, type ExtractPropTypes } from "vue";
const props = {
/** 姓名 */
name: String,
/** 年龄 */
age: Number,
};
// ExtractPropTypes:接受一个类型,返回vue3处理后的类型
export type HelloWorldProps = ExtractPropTypes<typeof props>;
export default defineComponent({
name: "HelloWorld",
props: props,
emits: ["click"],
setup(props, { emit }) {
const onClick = (event: MouseEvent) => {
emit("click", event);
};
return () => {
return (
<tag>
<div onClick={onClick}>
HelloWorld, {props.name}, {props.age}
</div>
</tag>
);
};
},
});
组件声明,暴露
// packages/components/helloWorld/index.ts
import HelloWorld from "./HelloWorld";
export { type HelloWorldProps } from "./HelloWorld";
export default HelloWorld;
// 组件调用时,提供代码提示
declare module "vue" {
export interface GlobalComponents {
HelloWorld: typeof HelloWorld;
}
}
组件注册
// packages/index.ts
import type { App } from "vue";
import HelloWorld from "./components/helloWorld";
// 定义install方法,供外部调用
const install = (Vue: App) => {
Vue.component("HelloWorld", HelloWorld);
};
export default { install };
编译插件
npm i -D vue-ts
配置文件
package.json
"scripts": {
"lib": "vue-cli-service build --target lib --name xxx --dest lib packages/index.ts && vue-tsc --declaration --emitDeclarationOnly",
}
tsconfig.json
{
"compilerOptions": {
"outDir": "lib/types", // 指定编译后的文件路径
"target": "esnext",
"module": "CommonJS",
"strict": true,
"jsx": "preserve",
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"useDefineForClassFields": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env",
"jest"
],
"paths": {
"@/*": [
"packages/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"packages/**/*.ts",
"packages/**/*.tsx"
],
"exclude": [
"node_modules"
]
}