vite-v3-ts-0到0.8
从0到0.8
vite 创建项目
npm init @vitejs/app demo-name
eslint
- 安装 eslint:
npm install eslint --save-dev
- 初始化 eslint 文件:
npx eslint --init
自动生成.eslintrc.js 文件- 配置 rules、settings,.eslintrc.js 文件:
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'plugin:vue/essential',
'airbnb-base',
],
parserOptions: {
ecmaVersion: 12,
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
plugins: [
'vue',
'@typescript-eslint',
],
rules: {
'import/no-unresolved': 'off',
'import/extensions': 'off',
'import/no-extraneous-dependencies': 'off',
},
settings: {
'import/resolver': {
alias: {
map: [
['@', './src'],
],
},
},
},
};
- vite.config.ts 配置了别名之后,tsconfig 也需要配置 paths/baseUrl
// vite.config.ts
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"baseUrl": "./",
"paths": {
"@/*": ["./src/*"],
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
- 可建.eslintignore 忽略不需要 eslint 检测的文件
/node_modules/
prettier
- 安装
npm install --save-dev eslint-plugin-prettier
npm install --save-dev --save-exact prettier
npm install --save-dev eslint-config-prettier
- plugin:prettier/recommended 的配置需要注意的是,一定要放在最后。因为 extends 中后引入的规则会覆盖前面的规则。
- eslint 的插件 eslint-config-prettier,这个插件的作用是禁用所有与格式相关的 eslint 规则,也就是说把所有格式相关的校验都交给 prettier 处理。
- 修改.eslintrc.js:
// .eslintrc.js
extends: [
"plugin:vue/vue3-recommended",
"airbnb-base",
"plugin:prettier/recommended",
],
rules: {
"prettier/prettier": "off",
},
- 新建.prettierrc.js 自定义 prettier 规则:
// .prettierrc.js
module.exports = {
useTabs: false,
tabWidth: 2,
printWidth: 100,
singleQuote: true,
trailingComma: 'es5',
bracketSpacing: true,
semi: true,
};
- ps: 项目中的 prettier 配置生效前提是有动作促使它执行,如 vscode 的 settings.json 中"editor.formatOnSave": true,保存格式化会执行项目中的 prettier 文件,项目中没有该文件会执行编辑器自带的 prettier。
EditorConfig
- vscode 安装 EditorConfig for VS Code 插件
- 根目录下新建.editorconfig 文件:
// .editorconfig
# Editor configuration, see http://editorconfig.org
# 表示是最顶层的 EditorConfig 配置文件
root = true
[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格(tab | space)
indent_size = 2 # 缩进大小
end_of_line = lf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行尾的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行
curly_bracket_next_line = true # 大括号不另起一行
spaces_around_operators = true # 运算符两遍都有空格
[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off
trim_trailing_whitespace = false
提交代码检测:husky && lint-staged
- husky,主要用于关联 git 的钩子函数,在执行相关 git hooks 时候进行自定义操作,如提交前进行 eslint 校验,提交时检验 commit message 等;,
npx husky-init && npm install
,- v7 版本执行命令后会生成.husky 文件夹,pre-commit 文件便是在 git 提交前会执行的操作,想要在提交前执行 eslint 校验代码,因此修改 husky 的 pre-commit 文件即可:
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# npm test
eslint . --ext .js, .ts, .vue --fix # 校验所有的js-ts-vue文件并修复可自动修复的问题
git add . # 用于将自动修复后盖板的文件添加到暂存区
exit 1 # 终止命令,用来测试钩子
- lint-staged:只对暂存区的文件执行 lint,可以实现每次提交时只校验自己修改的文件,
npm install lint-staged --save-dev
- 修改 package.json 文件
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"serve": "vite preview",
"prepare": "husky install",
"lint-staged": "lint-staged"
},
"lint-staged": {
"*.{ts,js,vue}": [
"eslint --fix",
"git add ."
]
},
- 修改 husky:添加 lint-staged 配置后,husky 就不在需要直接调用 eslint 了,修改.husky/pre-commit 文件:
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# npm test
# eslint . --ext .js, .ts, .vue --fix # 校验所有的js-ts-vue文件并修复可自动修复的问题
# git add . # 用于将自动修复后盖板的文件添加到暂存区
# exit 1 # 终止命令,用来测试钩子
npm run lint-staged
proxy && port && alias
- 新建 compile/proxy.ts 文件
module.exports = {
'/api': {
target: 'https://*.**.com/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
};
- 修改 vite.config.ts 文件
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
const proxy = require('./compile/proxy');
export default defineConfig({
plugins: [
vue(),
],
server: {
port: 6688,
open: true,
cors: true,
// 为开发服务器配置自定义代理规则
proxy,
},
// 别名
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
});
- 修改 eslintrc.js 文件:
settings: {
'import/resolver': {
alias: {
map: [['@', './src']],
},
},
},
- 修改 tsconfig.json 文件:
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"],
}
}
}
env-development 环境变量
- src/.env.development 文件:
VITE_BASE_URL = /api
- 对应类型声明文件 src/.env.d.ts:
// 建议类型声明文件,方便.提醒,.出存在字段
// 环境变量提醒
// eslint-disable-next-line no-unused-vars
interface ImportMetaEnv {
VITE_BASE_URL: string;
}
- ps:这里定义的 VITE_BASE_URL 可用于封装 axios 时的 baseURL 取值;
引入 vuex@next
npm i vuex@next -S
- 引入四步骤
- 创建 store 文件夹,并创建 index.ts 文件
- 在根目录下创建 vuex 的类型声明文件 vuex.d.ts
- 模块化,store/modules/user.ts
- main.ts 中引入 Store
- 组件中使用 state
- useStore()类型化:setup 中使用 useStore 时要类型化,三步骤
- 定义
InjectionKey
store/index.ts 中- app 安装时提供
InjectionKey
main.ts- 传递
InjectionKey
给useStore
组件中使用如 CompVuex.vue
// store/index.ts
import { InjectionKey } from 'vue';
import { createStore, Store } from 'vuex';
import users, { UserState} from './modules/todolist';
export type State = {
ac: number;
users?: UserState;
};
// 泛型 核心目的 约束State
// 1. 创建一个injectionKey
export const key: InjectionKey<Store<State>> = Symbol('description');
export default createStore({
state: {
ac: 0,
},
mutations: {
add(state) {
Object.assign(state, {
ac: state.ac + 1,
});
},
},
// 声明子模块
modules: {
users,
},
});
// modules/list.ts
import { Module } from 'vuex';
import { State } from '../index';
import { Item} from '../../types';
import http from '../../api/request';
const initialState = {
list: [] as Item[],
};
// 从已有值中推断出类型
export type UserState= typeof initialState;
// Module<S, R>泛型约束 S 子模块的类型 R根模块的类型
export default {
namespaced: true,
// 子模块state
state: initialState,
mutations: {
initItem(state, payload: Item[]) {
// eslint-disable-next-line no-param-reassign
state.list = payload;
},
addItem(state, payload: Item) {
state.list.push(payload);
},
},
actions: {
getList() {
http.get('/*').then((res) => {
console.log('res: ', res);
});
},
init({ commit }) {
// 使用相对路径,从而可以被我们的代理所接管,在vite.config.ts中配置代理
// <Item>请求返回的类型
http.get<Item>('/*').then((resp) => {
commit('add', resp.data);
});
},
add({commit, state }, payload: string) {
commit('add', {
id: state.list.length + 1,
title: payload,
completed: false,
} as Item);
},
},
} as Module<UserState, State>;
// main.ts
import store, { key } from './store/index';
// 2. App注入key
createApp(App)use(store, key).mount('#app');
// 组件中使用
<script lang="ts" setup>
import { computed, defineProps, PropType } from 'vue';
import { useStore } from 'vuex';
import { key } from '../store';
import { TitleInfo } from '../types';
// 有了useStore之后所有的值都被认为计算属性处理
// 3.传递`InjectionKey`给`useStore` 方便于属性提醒
const store = useStore(key);
// 全局state
const cc = computed(() => store.state.c);
// 子模块state
console.log(computed(() => store.state.users?.list));
// users初始化 类似于请求接口 store/index.ts加了命名空间,请求接口时候需要带上
store.dispatch('users/init', 'kk');
// 定义属性
defineProps({
titleInfo: {
type: Object as PropType<TitleInfo>,
required: true,
},
});
</script>
// 根目录types.d.ts
export type TitleInfo = {
value: string;
color: string;
};
export type Item= {
id: number;
title: string;
completed: boolean;
};
引入 vue-router@next
- 安装
npm i vue-router@next -S
- 引入 src/router/index.ts 创建实例
- App.vue 中设置路由出口,可能还有路由导航
- main.ts 中 use 路由
// router/index.ts
// 路由实例,ts默认支持路由,则不需要额外添加类型声明文件
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
// 对路由定义对象进行扩展,如增加新的字段hidden
export type AppRouteRecordRaw = RouteRecordRaw & {
hidden?: boolean;
};
const router = createRouter({
history: createWebHashHistory(),
// 路由 类型断言
routes: [
{
path: '/',
component: () => import('../pages/Home.vue'),
meta: {
title: 'kkk',
icon: 'el-icon-edit',
},
},
{
path: '/add',
hidden: true,
component: () => import('../pages/About.vue'),
},
] as AppRouteRecordRaw[],
});
// 路由守卫
router.beforeEach((to, from, next) => {
console.log('to, from, next: ', to, from, next);
return next();
});
export default router;
// App.vue
<!--路由出口-->
<router-link to="/">todo</router-link>
<router-link to="/add">add</router-link>
<router-view></router-view>
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router';
// 2. app安装时提供`InjectionKey`
createApp(App).use(router).mount('#app')
axios
- 安装
npm i axios -S
- 新建 src/api/request.ts
- 创建 axios 实例,统一处理、统一配置
按需引入 element-plus
- 安装
npm install element-plus --save
- main.ts 中完整引入
import ElementPlus from 'element-plus';
import 'element-plus/lib/theme-chalk/index.css';
createApp(App).use(ElementPlus)
- vite.config.ts 配置按需引入
import styleImport from 'vite-plugin-style-import';
export default defineConfig({
plugins: [
vue(),
styleImport({
libs: [
{
libraryName: 'element-plus',
esModule: true,
ensureStyleFile: true,
resolveStyle: (name) => {
const nameTemp = name.slice(3);
return `element-plus/packages/theme-chalk/src/${nameTemp}.scss`;
},
resolveComponent: (name) => `element-plus/lib/${name}`,
},
],
}),
],
});
- 组件页面中使用 ElementPlus
<el-button type="primary">主要按钮</el-button>
引入 scss 及初始化样式
npm install sass -D
npm i normalize.css -S
- 新建variables.scss,element-theme.scss,helpers.scss,reset.scss
参考 && 感谢 各路大神:
editorconfig
Vue3+Vite+TS+Eslint-搭建项目,配置 ESLint
Vue3+Vite+TS+Eslint-配置 husky 和 lint-staged
Vue3+Vite+TS+Eslint-引入 Element-plus,解决字体文件 404 问题
Vue3+Vite+TS+Eslint-引入 vuex、vuex 源码类型声明推导
体验 vite + vue3 + ts 搭建项目的全过程
ElementPlus
杨村长-Vue3+Typescript 从整合到项目实战
宝剑锋从磨砺出,梅花香自苦寒来。