vue3-typescript-existed
2024年04月29日
前言
基于 Vite+Vue3.0+TypeScript 搭建项目
构建项目
yarn create vite 项目名称
编辑器配置
settings.json 文件配置
{
//自动换行
"editor.wordWrap": "on",
//开启 保存自动格式化
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.options": {
"extensions": [".js", ".vue", ".jsx", ".ts", ".tsx"]
},
"eslint.validate": [
"javascript",
"javascriptreact",
"html",
"vue",
"vue-html",
"typescript",
"typescriptreact"
]
}
editorconfig 文件配置
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 4
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 200
代码规范配置
Eslint 规范配置
-
安装
eslint
依赖yarn add @typescript-eslint/eslint-plugin@4.29.1 @typescript-eslint/parser@4.29.1 @vue/eslint-config-airbnb@6.0.0 @vue/eslint-config-prettier@6.0.0 @vue/eslint-config-typescript@7.0.0 eslint@7.32.0 eslint-config-prettier@8.3.0 eslint-import-resolver-alias@1.1.2 eslint-plugin-import@2.24.0 eslint-plugin-prettier@3.4.2 @eslint-plugin-vue@7.16.0 -D
-
配置
.eslintrc.js
文件module.exports = {
root: true,
env: {
node: true,
},
extends: [
'plugin:vue/vue3-recommended',
'airbnb-base',
'@vue/typescript/recommended',
'@vue/prettier',
'@vue/prettier/@typescript-eslint',
],
parserOptions: {
ecmaVersion: 2020,
project: './tsconfig.json',
},
rules: {
'prettier/prettier': 0,
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
// indent: ['error', 4],
// 'import/extensions': ['off'],
'import/extensions': [
'error',
{
ignorePackages: true,
pattern: {
js: 'never',
jsx: 'never',
json: 'always',
ts: 'never',
tsx: 'never',
scss: 'never',
vue: 'always',
},
},
],
'import/no-extraneous-dependencies': ['error', {devDependencies: true}],
'import/no-unresolved': [
2,
{
ignore: ['^@/'], // @ 是设置的路径别名
},
],
'vue/html-self-closing': [
'error',
{
html: {
void: 'always',
normal: 'never',
component: 'always',
},
svg: 'always',
math: 'always',
},
],
'vue/attribute-hyphenation': ['error', 'never'],
// '@typescript-eslint/no-floating-promises': [
// 'error',
// {
// ignoreVoid: true,
// ignoreIIFE: false
// }
// ],
'no-void': ['error', {allowAsStatement: true}],
'no-undef': 'off',
'no-restricted-syntax': [
'error',
'WithStatement',
"BinaryExpression[operator='in']",
],
'no-restricted-globals': ['error', 'event', 'fdescribe'],
},
settings: {
'import/extensions': ['.js', '.jsx', '.ts', '.tsx', '.scss'],
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
alias: {
map: [['/@', './src']],
extensions: ['.ts', '.js', '.jsx', '.json', '.scss'],
},
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
}; -
配置
.eslintignore
文件vite.config.ts
tsconfig.json
vite.config.ts
tsconfig.json
*.sh
node_modules
*.md
*.woff
*.ttf
.vscode
.idea
dist
/public
.husky
.local
/bin
Dockerfile
Stylelint 规范配置
-
安装
stylelint
依赖yarn add stylelint@14.0.1 stylelint-config-prettier@9.0.3 stylelint-config-recess-order@2.6.0 stylelint-config-recommended-vue@1.0.0 stylelint-config-standard-scss@2.0.1 stylelint-scss@4.0.0 -D
-
配置
.stylelintrc.js
文件module.exports = {
root: true,
plugins: ['stylelint-scss'],
extends: [
'stylelint-config-standard',
'stylelint-config-recess-order',
'stylelint-config-prettier',
'stylelint-config-recommended-vue',
],
rules: {
'declaration-colon-space-before': 'never',
'declaration-colon-space-after': 'always-single-line',
'at-rule-no-unknown': [
true,
{
ignoreAtRules: ['include', 'mixin'],
},
],
'rule-empty-line-before': ['never'],
},
};
Prettier 格式化配置
-
安装
prettier
依赖yarn add prettier -D
-
配置
.prettierrc.js
文件module.exports = {
printWidth: 120,
tabWidth: 4,
singleQuote: true,
semi: true,
useTabs: false,
}; -
配置
.prettierignore
文件/dist/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh
/public/*
TypeScript 配置
安装相关类型依赖
-
安装
node
类型依赖问题- 如有报错:
Cannot find type definition file for ‘node’
,需安装@types/node
依赖
yarn add @types/node -D
- 如有报错:
描述文件配置
-
Vue 类型文件配置
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
declare module 'slash2';
declare module '*.css';
declare module '*.less';
declare module '*.scss';
declare module '*.sass';
declare module '*.svg';
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.gif';
declare module '*.md';
declare module '*.json';
declare module '*.woff';
declare module '*.ttf';
declare interface CustomEleProps {
style: {
[key: string]: string
} | string
className: string
}
declare interface W {
style: {
[key: string]: string
} | string
className: string
}
interface Window {
hljs: {
highlightBlock: () => void
}
} -
自定义类型文件配置
type UpmIds =
| ''
| '596'
| 'gs-prado'
| 'pradox'
| 'dongjian'
| 'hmmis'
| 'fenghuo'
| 'carbo'
| 'didiCarbo'
| 'htwMarketing'
| 'jiuxiao'
| 'anxing'
| 'didiIotPlatform';
interface MenuItemTypes {
id: string; // 随机生成,同一个工作台不重复
key: string;
title: string;
level: number;
name: string; // 菜单名称
uniqKey: string; // 唯一key
pid: string; // 父级菜单id
icon: string; // 展示icon
remark: string; // 菜单描述
hasAuth: boolean; // 是否有菜单权限
bizType: string[]; // 是否有菜单权限
authConfig: {
auth: boolean; // 是否权限校验
upmId: UpmIds; // 子系统id
featureId: number; // 权限点
};
menuConfig: {
type: 'iframe' | 'mapp' | 'qiankun' | 'none'; // 接入类型:iframe,mfe
url: string; // 页面URL
openTab: boolean; // 打开新的窗口
mappPath: string; // 微前端路径
mappName: string; // 微前端应用id
};
creator?: string; // 创建人
updator?: string; // 修改人
createdAt?: number; // 创建时间
updatedAt?: number;
index: (string | number)[];
children: MenuItemTypes[];
}
interface BizTypeList {
label: string;
value: string;
}
type MenuListTypes = MenuItemTypes[];
type MenuMapTypes = {
[k: string]: MenuItemTypes;
};
type MenuActiveKeys = (string | number)[];
type FeatureBizTypeMapTypes = {
[k: string]: MenuListTypes;
};
interface WorkbenchRes {
menuList: MenuListTypes;
bizTypeList: BizTypeList[];
}
interface UserInfoTypes {
avatar: string;
dept: string;
email: string;
id: number;
job: string;
permissionCount: null;
phone: string;
roleCount: null;
status: number;
username: string;
usernameZh: string;
}
type CollectMenuMapTypes = {
[k: string]: MenuMapTypes;
};
interface GlobalState {
activeAppName: string;
operation: string;
onMountApps?: string[];
}
interface StateTypes {
menuMap: MenuMapTypes | null;
bizType?: string;
allMenuList: MenuListTypes;
topActiveKey: string;
menuList: MenuListTypes;
bizTypeList: BizTypeList[];
menuBizTypeMap: FeatureBizTypeMapTypes;
leftActiveKeys: MenuActiveKeys;
leftOpenKeys: MenuActiveKeys;
noBizTypeMenu: number[];
userInfo: UserInfoTypes;
collectMenuMap: CollectMenuMapTypes;
env: string;
containerType: 'RightMenu' | 'MicroApp';
activeMenu: MenuItemTypes | null;
globalState: GlobalState;
panes: MenuListTypes;
activeTabKey: string;
setGlobalState: (state: GlobalState) => void;
}
interface RolesTypes {
id: number;
name: string;
nameZh: string;
description: string;
labelName: null;
appId: number;
status: number;
lang: null;
labelId: null;
type: null;
labels: null;
langNameList: null;
businessId: number;
businessName: null;
userCountBound: null;
featureCountBound: null;
permissionCountBound: null;
commonName: null;
riskLevel: null;
minRiskLevel: null;
availableRiskLevel: [];
isApplicable: number;
}
interface ResponseTypes<T = unknown> {
code: number;
data: T;
message: string;
}
tsconfig.json 文件配置
{
"compilerOptions": {
"plugins": [
{
"name": "typescript-lit-html-plugin"
}
],
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"noImplicitAny": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"types": ["vite/client", "node"],
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx",
".eslintrc.js",
"*.ts"
],
"exclude": ["node_modules"]
}
vite.config.ts 配置
基础配置
-
安装所需依赖
yarn add @vitejs/plugin-vue @vitejs/plugin-vue-jsx -D
-
配置
vite.config.ts
说明- 如报
Internal server error: Failed to parse source for import analysis because the content contain
错误,需要配置plugins:[vue(),vueJsx()]
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
export default ({ mode }) => {
return defineConfig({
plugins: [vue(), vueJsx()],
resolve: {},
optimizeDeps: {},
build: {},
server: {},
});
}; - 如报
代理配置
使用server.proxy
为开发服务器配置自定义代理规则,以下是两种配置写法:
-
选项写法
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
export default ({ mode }) => {
return defineConfig({
plugins: [
vue(),
vueJsx(),
],
css: {},
resolve: {},
optimizeDeps: {},
build: {},
server: {
proxy: {
'/add': {
target: 'http://localhost:4000',
changeOrigin: true,
},
},
},
});
};import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
export default ({ mode }) => {
return defineConfig({
plugins: [
vue(),
vueJsx(),
],
css: {},
resolve: {},
optimizeDeps: {},
build: {},
server: {
proxy: {
'/api': {
target: 'http://localhost:4000',
changeOrigin: true,
rewrite: (path) => {
return path.replace(/\/api/, '');
},
},
},
},
});
}; -
正则表达式写法
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
export default ({ mode }) => {
return defineConfig({
plugins: [
vue(),
vueJsx(),
],
css: {},
resolve: {},
optimizeDeps: {},
build: {},
server: {
proxy: {
'^/api2/(api3|api4).*': {
target: 'http://localhost:4000',
changeOrigin: true,
},
},
},
});
};import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
export default ({ mode }) => {
return defineConfig({
plugins: [
vue(),
vueJsx(),
],
css: {},
resolve: {},
optimizeDeps: {},
build: {},
server: {
proxy: {
'/api': {
target: 'http://localhost:4000',
changeOrigin: true,
rewrite: (path) => {
return path.replace(/\/api/, '');
},
},
'^/api2/(api3|api4).*': {
target: 'http://localhost:4000',
changeOrigin: true,
rewrite: (path) => {
return path.replace(/\/api2\/(api3|api4)/, '');
},
},
},
},
});
};
导入扩展名配置
使用resolve.extensions
来配置导入文件省略的扩展名列表
import { defineConfig } from 'vite';
import Path from 'path';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
export default ({ mode }) => {
return defineConfig({
plugins: [
vue(),
vueJsx(),
],
css: {},
resolve: {
alias: { '@': Path.resolve(__dirname, './src') },
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.scss'],
},
optimizeDeps: {},
build: {},
server: {}
});
};
导入文件路径别名配置
使用resolve.alias
来配置文件路径别名
import { defineConfig } from 'vite';
import Path from 'path';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
export default ({ mode }) => {
return defineConfig({
plugins: [
vue(),
vueJsx(),
],
css: {},
resolve: {
alias: { '@': Path.resolve(__dirname, './src') },
},
optimizeDeps: {},
build: {},
server: {}
});
};
UI 组件按需引入配置
-
AntdResign UI
库组件自动按需引入配置说明- UI库组件自动按需引入配置: 配置好这个后,后续开发使用
UI库组件
无需手动引入,且为按需加载
-
安装所需依赖
yarn add ant-design-vue@2.2.8 -S
yarn add vite-plugin-style-import unplugin-vue-components less -D -
具体配置
注意- 样式按需引入
- 通过
vite-plugin-style-import
来实现按需引入样式 - 通过
css选项
来支持使用JavaScript
变量
- 通过
- 组件按需引入
- 通过
unplugin-vue-components
来实现
- 通过
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import StyleImport from 'vite-plugin-style-import';
import ViteComponents from 'unplugin-vue-components/vite';
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
export default ({ mode }) => {
return defineConfig({
plugins: [
vue(),
vueJsx(),
StyleImport({
libs: [
{
libraryName: 'ant-design-vue',
esModule: true,
resolveStyle: (name) => `ant-design-vue/es/${name}/style/index`,
},
],
}),
ViteComponents({
dirs: ['src'],
dts: true,
resolvers: [AntDesignVueResolver({ importStyle: 'less', resolveIcons: true })],
}),
],
css: {
postcss: '',
preprocessorOptions: {
less: {
modifyVars: {
'primary-color': '#1db5ad',
},
javascriptEnabled: true,
},
},
},
resolve: {},
optimizeDeps: {},
build: {},
server: {},
});
}; - 样式按需引入
- UI库组件自动按需引入配置: 配置好这个后,后续开发使用
-
ElementUI
组件库自动按需引入说明- UI库组件自动按需引入配置: 配置好这个后,后续开发使用
UI库组件
无需手动引入,且为按需加载
-
安装所需依赖
yarn add element-plus -S
yarn add unplugin-vue-components vite-plugin-style-import -D -
具体配置
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import StyleImport from 'vite-plugin-style-import';
import ViteComponents from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
export default ({ mode }) => {
return defineConfig({
plugins: [
vue(),
vueJsx(),
StyleImport({
libs: [
{
libraryName: 'element-plus',
esModule: true,
resolveStyle: (name) => {
return `element-plus/lib/theme-chalk/${name}.css`;
},
},
],
}),
ViteComponents({
dirs: ['src'],
dts: true,
resolvers: [ElementPlusResolver()],
}),
],
css: {},
resolve: {},
optimizeDeps: {},
build: {},
server: {},
});
};
- UI库组件自动按需引入配置: 配置好这个后,后续开发使用
自定义组件自动按需引入配置
-
安装所需依赖
yarn add unplugin-auto-import -D
-
具体配置
注意- 对于
ant-design-vue
弹窗而言,自动引入的话,样式缺失。所以我们需要在入口文件手动引入样式文件- message 样式:
import 'ant-design-vue/es/message/style/index';
- modal 样式:
import 'ant-design-vue/es/modal/style/index';
- message 样式:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import AutoImport from 'unplugin-auto-import/vite';
export default ({ mode }) => {
return defineConfig({
plugins: [
vue(),
vueJsx(),
AutoImport({
include: [/\.[tj]sx?$/, /\.vue\??/],
imports: [
'vue',
'vue-router',
'vuex',
{
'ant-design-vue': ['message'],
},
],
dts: true,
}),
],
resolve: {},
optimizeDeps: {},
build: {},
server: {},
});
};import { createApp } from 'vue';
import App from './App.vue';
import store from './store/index';
import router from './router/index';
import 'ant-design-vue/es/message/style/index';
import 'ant-design-vue/es/modal/style/index';
const Instance = createApp(App);
Instance.use(router);
Instance.use(store);
Instance.mount('#app');<template>
<div>
<h3>首页</h3>
<!-- 无需引入 a-button 组件,直接使用即可 -->
<a-button @click="handleShowMessage">提示</a-button>
</div>
</template>
<script lang="ts">
export default defineComponent({
name: 'App',
components: {},
setup() {
// 直接使用 useRouter
const route = useRouter();
console.log(route);
// 直接使用 useStore
const store = useStore();
console.log(store);
const methods = {
handleShowMessage: () => {
// 直接使用 message 即可(需要在 main.ts 中引入相关样式)
message.success('操作成功!');
},
};
return {
...methods,
};
},
});
</script>
<style></style> - 对于