一、项目初始化
1.1 使用vite创建项目
# pnpm
pnpm create vite
# npm
npm init vite@latest
# yarn
yarn create vite
Need to install the following packages:
create-vite@latest
Ok to proceed? (y) y
√ Project name: ... vite-vue3-admin-ts
√ Select a framework: » vue
√ Select a variant: » vue-ts
1.2 代码规范和eslint
1.2.1 安装eslint
npm install eslint --save-dev
1.2.2 初始化eslint配置
//初始化命令
npx eslint --init
// 提示信息
You can also run this command directly using 'npm init @eslint/config'.
Need to install the following packages:
@eslint/create-config
Ok to proceed? (y) y
√ How would you like to use ESLint? · style
√ What type of modules does your project use? · esm
√ Which framework does your project use? · vue
√ Does your project use TypeScript? · No / Yes
√ Where does your code run? · browser
√ How would you like to define a style for your project? · guide
√ Which style guide do you want to follow? · standard
√ What format do you want your config file to be in? · JavaScript
Checking peerDependencies of eslint-config-standard@latest
The config that you've selected requires the following dependencies:
eslint-plugin-vue@latest @typescript-eslint/eslint-plugin@latest eslint-config-standard@latest eslint@^8.0.1 eslint-plugin-import@^2.25.2 eslint-plugin-n@^15.0.0 eslint-plugin-promise@^6.0.0 @typescript-eslint/parser@latest
√ Would you like to install them now? · No / Yes
√ Which package manager do you want to use? · npm
Installing eslint-plugin-vue@latest, @typescript-eslint/eslint-plugin@latest, eslint-config-standard@latest, eslint@^8.0.1, eslint-plugin-import@^2.25.2, eslint-plugin-n@^15.0.0, eslint-plugin-promise@^6.0.0, @typescript-eslint/parser@latest
added 109 packages in 16s
Successfully created .eslintrc.js file in D:\vscode\gitee-pro\vite-vue3-admin-ts
1.2.3 在package.json文件中添加lint命令
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"lint":"eslint ./src/**/*.{js,jsx,vue,ts,tsx} --fix"
},
1.2.3 eslint配置文件
module.exports = {
env: {
browser: true,
es2021: true
},
extends: [
// 使用vue3规则
'plugin:vue/vue3-strongly-recommended',
'standard'
],
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module'
},
plugins: [
'vue',
'@typescript-eslint'
],
rules: {
}
}
1.2.3 配置git commit hook
- 安装所需包
npx mrm@2 lint-staged
//提示信息
Need to install the following packages:
mrm@2
Ok to proceed? (y) y
Running lint-staged...
Update package.json
Installing lint-staged and husky...
husky - Git hooks installed
husky - created .husky/pre-commit
- 在
package.json文件中,配置git commit钩子函数
"lint-staged": {
"*.{js,jsx,vue,ts,tsx}":[
"npm run lint",
"git add"
]
}
1.2.4 在开发和构建的时候进行验证
- 安装所需包
npm install vite-plugin-eslint --save-dev
- 在
vite.config.ts中配置plugins
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import eslintPlugin from 'vite-plugin-eslint'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
eslintPlugin({
exclude: ['./node_modules/**'],
cache: false
})
]
})
1.2.5 gitcommit规范
1.2.5.1 validate-commit-msg github
validate-commit-msg 用于检查 Node 项目的 Commit message 是否符合格式
- 安装所需包
# Install commitlint cli and conventional config(mac/linux)
npm install --save-dev @commitlint/{config-conventional,cli}
# For Windows:
npm install --save-dev @commitlint/config-conventional @commitlint/cli
- 根目录下创建
commitlint.config配置文件
module.exports = {
extends: [
'@commitlint/config-conventional'
]
}
- To lint commits before they are created you can use Husky's
commit-msghook:
之前安装
lint-staged时,已安装并激活Husky,步骤3可以省略
# Install Husky v6
npm install husky --save-dev
# or
yarn add husky --dev
# Activate hooks
npx husky install
# or
yarn husky install
- Add hook
// 创建配置文件
npx husky add .husky/commit-msg ''
配置commit-msg文件
#!/bin/sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no-install commitlint --edit "$1"
1.3 JSX支持
- 安装
@vitejs/plugin-vue-jsx
npm install @vitejs/plugin-vue-jsx -D
- 在
vite.config.js配置JSX
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import eslintPlugin from 'vite-plugin-eslint'
// 引入JSX支持
import vueJsx from '@vitejs/plugin-vue-jsx'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
eslintPlugin({
exclude: ['./node_modules/**'],
cache: false
}),
vueJsx()
]
})
1.4 初始化vue-router
- 安装
vue-router
npm install vue-router@4
- 在
src下新建views/home/index.vue
<template>
<h1>首页</h1>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
- 在
src下新建router/index.ts
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'home',
component: () => import('../views/home/index.vue')
},
{
path: '/login',
name: 'login',
component: () => import('../views/login/index.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
- 在
main.ts引入router
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
- 修改
App.vue
<template>
<router-view />
</template>
<script setup lang="ts">
</script>
- 遇到的问题
Eslint 报错 error Component name "index" should always be multi-word vue/multi-word-component-names
解决方案
- 按照规则,使用驼峰命名,例如 AppHeader.vue
- 在 .eslintrc.js 文件中关闭命名规则
module.exports = {
env: {
browser: true,
es2021: true
},
extends: [
'plugin:vue/vue3-strongly-recommended',
'standard'
],
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module'
},
plugins: [
'vue',
'@typescript-eslint'
],
rules: {
'vue/multi-word-component-names': [
'error',
{
ignores: ['index'] // 需要忽略的组件名
}
]
}
}
1.5 初始化pinia
- 安装
pinia包.
npm install pinia
- 在
src下新建store/index.ts
import { createPinia } from "pinia";
const pinia = createPinia()
export default pinia
- 在
main.ts引入vuex
import { createApp } from 'vue'
import pinia from '@/store'
const app = createApp(App)
app.use(pinia)
1.6 路径别名
- 在
vite.config.ts配置别名
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import eslintPlugin from 'vite-plugin-eslint'
// 引入JSX支持
import vueJsx from '@vitejs/plugin-vue-jsx'
import { resolve } from 'path'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
eslintPlugin({
exclude: ['./node_modules/**'],
cache: false
}),
vueJsx()
],
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
comps: resolve(__dirname, 'src/components'),
api: resolve(__dirname, 'src/api'),
views: resolve(__dirname, 'src/views'),
utils: resolve(__dirname, 'src/utils'),
routes: resolve(__dirname, 'src/routes'),
styles: resolve(__dirname, 'src/styles'),
layouts: resolve(__dirname, 'src/layouts'),
plugins: resolve(__dirname, 'src/plugins')
}
}
})
- 路径声明变量
{
"compilerOptions": {
...
"paths": {
"@/*":[
"./src/*"
]
}
},
}
1.7 自动导入
- 安装
unplugin-auto-import
npm install unplugin-auto-import -D
- 配置
vite.config.ts
...
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [AutoImport()],
...
});
1.8 Sass/Scss 预处理器
以前用vuecli的时候,还要安装sass-loader、node-sass什么的,但是vite其实安装sass就可以了。
- 安装
sass依赖
npm install -D sass
2.配置全局的scss文件,在src下新建styles/index.scss、styles/common.scss
styles/index.scss
@import './common.scss'; // 全局公共样式
- 在
vite.config.ts里配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import eslintPlugin from 'vite-plugin-eslint'
// 引入JSX支持
import vueJsx from '@vitejs/plugin-vue-jsx'
import { resolve } from 'path'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
eslintPlugin({
exclude: ['./node_modules/**'],
cache: false
}),
vueJsx()
],
...
css: {
preprocessorOptions: {
// 引入公用的样式
scss: {
additionalData: '@import "@/styles/index.scss";'
}
}
}
})
1.9 封装axios
- 安装
axios
npm install axios
- 封装
axios(参照诸葛小愚实例)
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
import { ElMessage } from 'element-plus'
// 数据返回的接口
// 定义请求响应参数,不含data
interface Result {
code: number;
msg: string
}
// 请求响应参数,包含data
interface ResultData<T = any> extends Result {
data?: T;
}
const URL: string = ''
enum RequestEnums {
TIMEOUT = 20000,
OVERDUE = 600, // 登录失效
SUCCESS = 200, // 请求成功
}
const config = {
// 默认地址
baseURL: URL as string,
// 设置超时时间
timeout: RequestEnums.TIMEOUT as number,
// 跨域时候允许携带凭证
withCredentials: true
}
class RequestHttp {
// 定义成员变量并指定类型
service: AxiosInstance
public constructor (config: AxiosRequestConfig) {
// 实例化axios
this.service = axios.create(config)
/**
* 请求拦截器
* 客户端发送请求 -> [请求拦截器] -> 服务器
* token校验(JWT) : 接受服务器返回的token,存储到vuex/pinia/本地储存当中
*/
this.service.interceptors.request.use(
(config: AxiosRequestConfig) => {
const token = localStorage.getItem('token') || ''
return {
...config,
headers: {
'x-access-token': token // 请求头中携带token信息
}
}
},
(error: AxiosError) => {
// 请求报错
Promise.reject(error)
}
)
/**
* 响应拦截器
* 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
*/
this.service.interceptors.response.use(
(response: AxiosResponse) => {
const { data } = response // 解构
if (data.code === RequestEnums.OVERDUE) {
// 登录信息失效,应跳转到登录页面,并清空本地的token
localStorage.setItem('token', '')
// router.replace({
// path: '/login'
// })
return Promise.reject(data)
}
// 全局错误信息拦截(防止下载文件得时候返回数据流,没有code,直接报错)
if (data.code && data.code !== RequestEnums.SUCCESS) {
ElMessage.error(data) // 此处也可以使用组件提示报错信息
return Promise.reject(data)
}
return data
},
(error: AxiosError) => {
const { response } = error
if (response) {
this.handleCode(response.status)
}
if (!window.navigator.onLine) {
ElMessage.error('网络连接失败')
// 可以跳转到错误页面,也可以不做操作
// return router.replace({
// path: '/404'
// })
}
}
)
}
handleCode (code: number):void {
switch (code) {
case 401:
ElMessage.error('登录失败,请重新登录')
break
default:
ElMessage.error('请求失败')
break
}
}
// 常用方法封装
get<T> (url: string, params?: object): Promise<ResultData<T>> {
return this.service.get(url, { params })
}
post<T> (url: string, params?: object): Promise<ResultData<T>> {
return this.service.post(url, params)
}
put<T> (url: string, params?: object): Promise<ResultData<T>> {
return this.service.put(url, params)
}
delete<T> (url: string, params?: object): Promise<ResultData<T>> {
return this.service.delete(url, { params })
}
downFile<T> (url: string, params?: object): Promise<ResultData<T>> {
return this.service.get(url, { params, responseType: 'blob' })
}
}
// 导出一个实例对象
export default new RequestHttp(config)
- 调用封装
axiossrc/api/user.ts
import axios from '@/utils/request'
namespace Login {
// 用户登录表单
export interface LoginReqForm {
username: string;
password: string;
}
// 登录成功后返回的token
export interface LoginResData {
token: string;
}
}
// 用户登录
export const login = (params: Login.LoginReqForm) => {
// 返回的数据格式可以和服务端约定
return axios.post<Login.LoginResData>('/user/login', params)
}
import.meta.globEager自动加载接口函数src/api/api.ts
const requireComponent = import.meta.globEager('/src/api/**/*.ts')
const modules: any = {}
Object.entries(requireComponent).forEach(([k, v]) => {
const pattern = /\/(\w*)\.ts/
const matched = k.match(pattern)
if (matched && matched[1] !== 'api') {
Object.assign(modules, v)
}
})
export default modules
- 页面调用
import api from '@/api/api'
console.dir(api.login({ admin: 'admin',password:'123456' }))
2.0 elementPlus按需加载
- 安装
unplugin-vue-components和unplugin-auto-import
npm install -D unplugin-vue-components unplugin-auto-import
- 配置
vite.config.ts
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
})
- 国际化
App.vue
<template>
<el-config-provider
:locale="zhCn"
:z-index="zIndex"
size="small"
>
<router-view />
</el-config-provider>
</template>
// :size="size"
<script setup lang="ts">
import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/lib/locale/lang/zh-cn'
const zIndex = 2000
</script>