生命周期
Vue2 vue3
beforeCreate -> setup()
created -> setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
使用 vite 快速创建脚手架
Vite 需要 Node.js 版本 >= 12.0.0
# npm 6.x
npm init @vitejs/app 项目名 --template
# npm 7+, 需要额外的双横线:
npm init @vitejs/app 项目名 -- --template
# yarn
yarn create @vitejs/app 项目名 --template
cd 项目名
# 安装依赖
yarn
# 启动
yarn dev
prettier 安装&&使用
npm install prettier --save-dev
新建 prettier.js
module.exports = {
tabWidth: 2,
jsxSingleQuote: true,
jsxBracketSameLine: true,
printWidth: 100,
singleQuote: true,
semi: false,
overrides: [
{
files: '*.json',
options: {
printWidth: 200,
},
},
],
arrowParens: 'always',
}
项目下新建 .prettierignore
# 忽略格式化文件 (根据项目需要自行添加)
node_modules
dist
package.json 配置:
{
"script": {
"lint": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx",
"prettier": "prettier --write ."
}
}
配置预处理器 scss
npm install sass-loader --dev
npm install dart-sass --dev
npm install sass --dev
路由
新建router文件夹,新建router.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'Login',
component: () => import('@/pages/login/Login.vue'),
},
]
const router = createRouter({
history: createWebHistory(),
routes,
})
export default router
main.ts
import router from './router/index'
app.use(router)
vue-router4.x 支持 typescript,配置路由的类型是 RouteRecordRaw,这里 meta 可以让我们有更多的发挥空间,这里提供一些参考:
title:string; 页面标题,通常必选。
icon?:string; 图标,一般配合菜单使用。
auth?:boolean; 是否需要登录权限。
ignoreAuth?:boolean; 是否忽略权限。
roles?:RoleEnum[]; 可以访问的角色
keepAlive?:boolean; 是否开启页面缓存
hideMenu?:boolean; 有些路由我们并不想在菜单中显示,比如某些编辑页面。
order?:number; 菜单排序。
frameUrl?:string; 嵌套外链。
请求封装
yarn add axios
新建 service 文件夹,service 下新增 http.ts 文件以及 api 文件夹
import axios, { AxiosRequestConfig } from "axios";
// 设置请求头和请求路径
axios.defaults.baseURL = "/api";
axios.defaults.timeout = 10000;
axios.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
axios.interceptors.request.use(
(config): AxiosRequestConfig<any> => {
const token = window.sessionStorage.getItem("token");
if (token) {
//@ts-ignore
config.headers.token = token;
}
return config;
},
(error) => {
return error;
}
);
// 响应拦截
axios.interceptors.response.use((res) => {
if (res.data.code === 111) {
sessionStorage.setItem("token", "");
// token过期操作
}
return res;
});
interface ResType<T> {
code: number;
data?: T;
msg: string;
err?: string;
}
interface Http {
get<T>(url: string, params?: unknown): Promise<ResType<T>>;
post<T>(url: string, params?: unknown): Promise<ResType<T>>;
upload<T>(url: string, params: unknown): Promise<ResType<T>>;
download(url: string): void;
}
const http: Http = {
get(url, params) {
return new Promise((resolve, reject) => {
axios
.get(url, { params })
.then((res) => {
resolve(res.data);
})
.catch((err) => {
reject(err.data);
});
});
},
post(url, params) {
return new Promise((resolve, reject) => {
axios
.post(url, JSON.stringify(params))
.then((res) => {
resolve(res.data);
})
.catch((err) => {
reject(err.data);
});
});
},
upload(url, file) {
return new Promise((resolve, reject) => {
axios
.post(url, file, {
headers: { "Content-Type": "multipart/form-data" },
})
.then((res) => {
resolve(res.data);
})
.catch((err) => {
reject(err.data);
});
});
},
download(url) {
const iframe = document.createElement("iframe");
iframe.style.display = "none";
iframe.src = url;
iframe.onload = function () {
document.body.removeChild(iframe);
};
document.body.appendChild(iframe);
},
};
export default http;
login.ts
import http from '@/service/http'
import * as T from './types'
const loginApi: T.ILoginApi = {
login(params){
return http.post('/login', params)
}
}
export default loginApi
types.ts
export interface ILoginParams {
userName: string
passWord: string | number
}
export interface ILoginApi {
login: (params: ILoginParams)=> Promise<any>
}
状态管理 pinia
# 安装
yarn add pinia@next
main.ts
# 引入
import { createPinia } from "pinia"
# 创建根存储库并将其传递给应用程序
app.use(createPinia())
创建store文件夹,index.ts
import { defineStore } from 'pinia'
export const store = defineStore({
id: 'mian',
state: () => ({
name: '超级管理员',
}),
getters: {
nameLength: (state) => state.name.length,
},
actions: {
async insertPost(data:string){
// 可以做异步
// await doAjaxRequest(data);
this.name = data;
}
},
})
组件内使用
<template>
<div>{{ mainStore.name }}<br />{{ mainStore.nameLength }}</div>
<button @click="updateName">修改</button>
</template>
<script setup lang="ts">
import {store} from '@/store/mian'
const stores= store()
const updateName = ()=>{
// $patch 修改 store 中的数据
stores.$patch({
name: '名称被修改了'
})
// action使用方法
stores.insertPost('名称被修改了')
}
</script>
环境变量配置
项目根目录新建:.env.development
NODE_ENV=development
VITE_APP_WEB_URL= 'http://192.168.3.11:8000'
项目根目录新建:.env.production
NODE_ENV=production
VITE_APP_WEB_URL= 'http://192.168.3.11:8000'
配置 package.json
"build:dev": "vue-tsc --noEmit && vite build --mode development",
"build:pro": "vue-tsc --noEmit && vite build --mode production",
Vite 常用基础配置
运行 代理 和 打包 配置
server: {
host: '0.0.0.0',
port: 3000,
open: true,
https: false,
proxy: {}
},
生产环境去除 console debugger
build:{
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
}
element-plus
npm install element-plus --save
yarn add element-plus
pnpm install element-plus
main.ts
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
app.use(ElementPlus)