創建項目

基礎語法
定義data
- script標簽上lang="ts"
- 定義一個類型
type或者接口interface來約束data - 可以使用
ref或者toRefs來定義響應式數據 - 使用
ref在setup讀取的時候需要獲取xxx.value,但在template中不需要 - 使用
reactive時,可以用toRefs解構導出,在template就可以直接使用了
<script lang="ts"> import { defineComponent, reactive, ref, toRefs } from 'vue'; type Todo = { id: number, name: string, completed: boolean } export default defineComponent({ const data = reactive({ todoList: [] as Todo[] }) const count = ref(0); console.log(count.value) return { ...toRefs(data) } }) </script>
定義props
props需要使用PropType泛型來約束
<script lang="ts"> import { defineComponent, PropType} from 'vue'; interface UserInfo = { id: number, name: string, age: number } export default defineComponent({ props: { userInfo: { type: Object as PropType<UserInfo>, // 泛型類型 required: true } }, }) </script>
定義methods
<script lang="ts"> import { defineComponent, reactive, ref, toRefs } from 'vue'; type Todo = { id: number, name: string, completed: boolean } export default defineComponent({ const data = reactive({ todoList: [] as Todo[] }) // 約束輸入和輸出類型 const newTodo = (name: string):Todo => { return { id: this.items.length + 1, name, completed: false }; } const addTodo = (todo: Todo): void => { data.todoList.push(todo) } return { ...toRefs(data), newTodo, addTodo } }) </script>
vue-router
createRouter創建router實例router的模式分為:createWebHistory-- history模式createWebHashHistory-- hash模式routes的約束類型是RouteRecordRaw
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import Home from '../views/Home.vue';
const routes: Array< RouteRecordRaw > = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
});
export default router;
擴展路由額外屬性
在實際項目開發中,常常會遇到這么一個場景,某一個路由是不需要渲染到側邊欄導航上的,此時我們可以給該路由添加一個hidden屬性來實現。
在ts的強類型約束下,添加額外屬性就會報錯,那么我們就需要擴展RouteRecordRaw類型
// 聯合類型 type RouteConfig = RouteRecordRaw & {hidden?: boolean}; //hidden 是可選屬性 const routes: Array<RouteConfig> = [ { path: '/', name: 'Home', component: Home, hidden: true, meta: { permission: true, icon: '' } } ];
在setup中使用
需要導入useRouter創建一個router實例
<script lang="ts"> import { useRouter } from 'vue-router'; import { defineComponent } from 'vue'; export default defineComponent({ setup () { const router = useRouter(); goRoute(path) { router.push({path}) } } }) </script>
vuex
使用this.$store
import { createStore } from 'vuex';
export type State = {
count: number
}
export default createStore({
state: {
count: 0
}
});
需要創建一個聲明文件vuex.d.ts
// vuex.d.ts import {ComponentCustomProperties} from 'vue'; import {Store} from 'vuex'; import {State} from './store' declare module '@vue/runtime-core' { interface ComponentCustomProperties { $store: Store<State> } }
在setup中使用
-
定義InjecktionKey -
在安裝插件時傳入key -
在使用useStore時傳入
import { InjectionKey } from 'vue';
import { createStore, Store } from 'vuex';
export type State = {
count: number
}
// 創建一個injectionKey
export const key: InjectionKey<Store<State>> = Symbol('key');
// main.ts import store, { key } from './store'; app.use(store, key);
<script lang="ts"> import { useStore } from 'vuex'; import { key } from '@/store'; export default defineComponent({ setup () { const store = useStore(key); const count = computed(() => store.state.count); return { count } } }) </script>
模塊
新增一個todo模塊。導入的模塊,需要是一個vuex中的interface Module的對象,接收兩個泛型約束,第一個是該模塊類型,第二個是根模塊類型
// modules/todo.ts import { Module } from 'vuex'; import { State } from '../index.ts'; type Todo = { id: number, name: string, completed: boolean } const initialState = { todos: [] as Todo[] }; export type TodoState = typeof initialState; export default { namespaced: true, state: initialState, mutations: { addTodo (state, payload: Todo) { state.todos.push(payload); } } } as Module<TodoState, State>; //Module<S, R> S 該模塊類型 R根模塊類型
// index.ts export type State = { count: number, todo?: TodoState // 這里必須是可選,不然state會報錯 } export default createStore({ state: { count: 0 } modules: { todo } });
setup () { console.log(store.state.todo?.todos); }
elementPlus
yarn add element-plus
完整引入
import { createApp } from 'vue'
import ElementPlus from 'element-plus';import 'element-plus/lib/theme-chalk/index.css';import App from './App.vue';
import 'dayjs/locale/zh-cn'
import locale from 'element-plus/lib/locale/lang/zh-cn'
const app = createApp(App)
app.use(ElementPlus, { size: 'small', zIndex: 3000, locale })
app.mount('#app')
按需加載
需要安裝babel-plugin-component插件
yarn add babel-plugin-component -D // babel.config.js plugins: [ [ 'component', { libraryName: 'element-plus', styleLibraryName: 'theme-chalk' } ] ]
import 'element-plus/lib/theme-chalk/index.css'; import 'dayjs/locale/zh-cn'; import locale from 'element-plus/lib/locale'; import lang from 'element-plus/lib/locale/lang/zh-cn'; import { ElAside, ElButton, ElButtonGroup, } from 'element-plus'; const components: any[] = [ ElAside, ElButton, ElButtonGroup, ]; const plugins:any[] = [ ElLoading, ElMessage, ElMessageBox, ElNotification ]; const element = (app: any):any => { // 國際化 locale.use(lang); // 全局配置 app.config.globalProperties.$ELEMENT = { size: 'small' }; components.forEach(component => { app.component(component.name, component); }); plugins.forEach(plugin => { app.use(plugin); }); }; export default element;
// main.ts import element from './plugin/elemment' const app = createApp(App); element(app);
axios
axios的安裝使用和vue2上沒有什么大的區別,如果需要做一些擴展屬性,還是需要聲明一個新的類型
type Config = AxiosRequestConfig & {successNotice? : boolean, errorNotice? : boolean}
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios';
import { ElMessage } from 'element-plus';
const instance = axios.create({
baseURL: process.env.VUE_APP_API_BASE_URL || '',
timeout: 120 * 1000,
withCredentials: true
});
// 錯誤處理
const err = (error) => {
if (error.message.includes('timeout')) {
ElMessage({
message: '請求超時,請刷新網頁重試',
type: 'error'
});
}
if (error.response) {
const data = error.response.data;
if (error.response.status === 403) {
ElMessage({
message: 'Forbidden',
type: 'error'
});
}
if (error.response.status === 401) {
ElMessage({
message: 'Unauthorized',
type: 'error'
});
}
}
return Promise.reject(error);
};
type Config = AxiosRequestConfig & {successNotice? : boolean, errorNotice? : boolean}
// 請求攔截
instance.interceptors.request.use((config: Config) => {
config.headers['Access-Token'] = localStorage.getItem('token') || '';
return config;
}, err);
// 響應攔截
instance.interceptors.response.use((response: AxiosResponse) => {
const config: Config = response.config;
const code = Number(response.data.status);
if (code === 200) {
if (config && config.successNotice) {
ElMessage({
message: response.data.msg,
type: 'success'
});
}
return response.data;
} else {
let errCode = [402, 403];
if (errCode.includes(response.data.code)) {
ElMessage({
message: response.data.msg,
type: 'warning'
});
}
}
}, err);
export default instance;
setup script
官方提供了一個實驗性的寫法,直接在script里面寫setup的內容,即:setup script。
之前我們寫組件是這樣的
<template> <div> {{count}} <ImgReview></ImgReview > </div> </template> <script lang="ts"> import { ref, defineComponent } from "vue"; import ImgReview from "./components/ImgReview.vue"; export default defineComponent({ components: { ImgReview, }, setup() { const count = ref(0); return { count }; } }); </script>
啟用setup script后:在script上加上setup
<template> <div> {{count}} <ImgReview></ImgReview> </div> </template> <script lang="ts" setup> import { ref } from "vue"; import ImgReview from "./components/ImgReview.vue"; const count = ref(0); </script>
是不是看起來簡潔了很多,組件直接導入就行了,不用注冊組件,數據定義了就可以用。其實我們可以簡單的理解為script包括的內容就是setup中的,并做了return
導出方法
<script lang="ts" setup> const handleClick = (type: string) => { console.log(type); } </script>
定義props
使用props需要用到defineProps來定義,具體用法跟之前的props寫法類似:
基礎用法
<script lang="ts" setup> import { defineProps } from "vue"; const props = defineProps(['userInfo', 'gameId']); </script>
構造函數進行檢查 給props定義類型:
const props = defineProps({ gameId: Number, userInfo: { type: Object, required: true } });
使用類型注解進行檢查
defineProps<{
name: string
phoneNumber: number
userInfo: object
tags: string[]
}>()
可以先定義好類型:
interface UserInfo { id: number, name: string, age: number } defineProps<{ name: string userInfo: UserInfo }>()
defineEmit
<script lang="ts" setup> import { defineEmit } from 'vue'; // expects emits options const emit = defineEmit(['kk', 'up']); const handleClick = () => { emit('kk', '點了我'); }; </script>
<Comp @kk="handleClick"/> <script lang="ts" setup> const handleClick = (data) => { console.log(data) } </script>
獲取上下文
在標準組件寫法里,setup 函數默認支持兩個入參:
| 參數 | 類型 | 含義 |
| props | object | 由父組件傳遞下來的數據 |
| context | object | 組件的執行上下文 |
在setup script 中使用useContext獲取上下文:
<script lang="ts" setup> import { useContext } from 'vue' const { slots, attrs } = useContext(); </script>
獲取到的slots, attrs跟setup里面的是一樣的。
關于本文
作者:FinGet
https://juejin.cn/post/6980267119933931551
浙公網安備 33010602011771號