概述
Vue3出來已經(jīng)有一段時(shí)間了,在團(tuán)隊(duì)中驱负,也進(jìn)行了大量的業(yè)務(wù)實(shí)踐,也有了一些自己的思考患雇。
總的來說跃脊,Vue3無論是在底層的原理上,還是在業(yè)務(wù)的實(shí)際開發(fā)中苛吱,都有了長足的進(jìn)步匾乓。
使用 proxy 代替之前的 Object.defineProperty 的API,性能更加優(yōu)異又谋,也解決了之前vue在處理對象拼缝、數(shù)組上的缺陷;在diff算法上彰亥,使用了靜態(tài)標(biāo)記的方式咧七,大大提升了Vue的執(zhí)行效率。
在使用的層面任斋,我們從options Api继阻,變成了composition Api,慢慢的在實(shí)際的業(yè)務(wù)中废酷,我們拋棄了原本的data瘟檩、methods、computed那種隔離式的寫法澈蟆。compositon Api墨辛,它更加聚焦,它講究的是相關(guān)業(yè)務(wù)的聚合性趴俘。
同時(shí)睹簇,在composition Api中,為了防止過于重的業(yè)務(wù)邏輯寥闪,它提供了一種關(guān)注點(diǎn)分離的方式太惠,大大的提升了我們代碼的可讀性。
完全良好的支持了TypeScript疲憋,類型校驗(yàn)也成為了以后Vue3進(jìn)行大型項(xiàng)目開發(fā)的質(zhì)量保障凿渊,同時(shí)這也是面向了趨勢 -- 前端的未來就是TypeScript!
1缚柳、compositon Api
compositon Api的本質(zhì)埃脏,體現(xiàn)在代碼里面,也就是一個(gè)setup函數(shù)喂击,在這個(gè)setup函數(shù)中剂癌,返回的數(shù)據(jù),會用到該組件的模板中翰绊。return的這個(gè)對象佩谷,一定程度上,代表了之前vue2中的data屬性监嗜。
import { defineComponent, ref } from 'vue';
export default defineComponent({
name: 'Gift',
setup() {
const counter = ref(0);
return {
counter
}
}
})
這時(shí)候谐檀,對于大多數(shù)初學(xué)者來說,可能存在的疑惑就是裁奇,那么我能不能定義options Api的寫法桐猬,比如data、computed刽肠、watch溃肪、methods等等免胃。
這里我需要明確的是,Vue3是完全兼容Vue2的這種options Api的寫法惫撰,但是從理念上來說羔沙,更加推薦setup的方式,來寫我們的組件厨钻。
原因如下:Vue3的存在扼雏,本身是為了解決Vue2的問題的,Vue2的問題就是在于夯膀,聚合性不足诗充,會導(dǎo)致代碼越來越臃腫!setup的方式诱建,能夠讓data蝴蜓、方法邏輯、依賴關(guān)系等聚合在一塊涂佃,更方便維護(hù)励翼。
也就是說,以后我們盡量不要寫單獨(dú)的data辜荠、computed汽抚、watch、methods等等伯病,不是Vue3不支持造烁,而是和Vue3的理念違背。
components屬性午笛,也就是一個(gè)組件的子組件惭蟋,這個(gè)配置在Vue2和3的差異不大,Vue2怎么用药磺,Vue3依然那么用告组。
1、ref 和 reactive的區(qū)別癌佩?
在功能方面木缝,ref 和 reactive,都是可以實(shí)現(xiàn)響應(yīng)式數(shù)據(jù)围辙!
在語法層面我碟,兩個(gè)有差異。ref定義的響應(yīng)式數(shù)據(jù)需要用[data].value的方式進(jìn)行更改數(shù)據(jù)姚建;reactive定義的數(shù)據(jù)需要[data].[prpoerty]的方式更改數(shù)據(jù)矫俺。
const actTitle: Ref<string> = ref('活動名稱');
const actData = reactive({
list: [],
total: 0,
curentPage: 1,
pageSize: 10
});
actTitle.value = '活動名稱2';
actData.total = 100;
但是在應(yīng)用的層面,還是有差異的,通常來說:單個(gè)的普通類型的數(shù)據(jù)厘托,我們使用ref來定義響應(yīng)式友雳。表單場景中,描述一個(gè)表單的key:value這種對象的場景催烘,使用reactive沥阱;在一些場景下,某一個(gè)模塊的一組數(shù)據(jù)伊群,通常也使用reactive的方式,定義數(shù)據(jù)策精。
那么舰始,對象是不是非要使用reactive來定義呢?其實(shí)不是的咽袜,都可以丸卷,根據(jù)自己的業(yè)務(wù)場景,具體問題具體分析询刹!ref他強(qiáng)調(diào)的是一個(gè)數(shù)據(jù)的value的更改谜嫉,reactive強(qiáng)調(diào)的是定義的對象的某一個(gè)屬性的更改。
2凹联、周期函數(shù)
周期函數(shù)沐兰,在Vue3中,是被單獨(dú)使用的蔽挠,使用方式如下:
import { defineComponent, ref, onMounted } from 'vue';
export default defineComponent({
name: 'Gift',
setup() {
const counter = ref(0);
onMounted(() => {
// 處理業(yè)務(wù)住闯,一般進(jìn)行數(shù)據(jù)請求
})
return {
counter
}
}
})
3、store使用
在Vue2中澳淑,其實(shí)可以直接通過this.$store進(jìn)行獲取比原,但是在Vue3中,其實(shí)沒有this這個(gè)概念杠巡,使用方式如下:
import { useStore } from "vuex";
import { defineComponent, ref, computed } from 'vue';
export default defineComponent({
name: 'Gift',
setup() {
const counter = ref(0);
const store = useStore();
const storeData = computed(() => store); // 配合computed量窘,獲取store的值。
return {
counter,
storeData
}
}
})
4氢拥、router的使用
在Vue2中蚌铜,是通過this.$router的方式,進(jìn)行路由的函數(shù)式編程兄一,但是Vue3中厘线,是這么使用的:
import { useStore } from "vuex";
import { useRouter } from "vue-router";
import { defineComponent, ref, computed } from 'vue';
export default defineComponent({
name: 'Gift',
setup() {
const counter = ref(0);
const router = useRouter();
const onClick = () => {
router.push({ name: "AddGift" });
}
return {
counter,
onClick
}
}
})
2、關(guān)注點(diǎn)分離
關(guān)注點(diǎn)分離出革,應(yīng)該分兩層意思:第一層意思就是造壮,Vue3的setup,本身就把相關(guān)的數(shù)據(jù),處理邏輯放到一起耳璧,這就是一種關(guān)注點(diǎn)的聚合成箫,更方便我們看業(yè)務(wù)代碼。
第二層意思旨枯,就是當(dāng)setup變的更大的時(shí)候蹬昌,我們可以在setup內(nèi)部,提取相關(guān)的一塊業(yè)務(wù)攀隔,做到第二層的關(guān)注點(diǎn)分離皂贩。
import { useStore } from "vuex";
import { useRouter } from "vue-router";
import { defineComponent, ref, computed } from 'vue';
import useMerchantList from './merchant.js';
export default defineComponent({
name: 'Gift',
setup() {
const counter = ref(0);
const router = useRouter();
const onClick = () => {
router.push({ name: "AddGift" });
}
// 在該示例中,我們把獲取商家列表的相關(guān)業(yè)務(wù)分離出去昆汹。也就是下面的merchant.ts
const {merchantList} = useMerchantList();
return {
counter,
onClick,
merchantList
}
}
})
merchant.ts
import { getMerchantlist } from "@/api/rights/gift";
import { ref, onMounted } from "vue";
export default function useMerchantList(): Record<string, any> {
const merchantList = ref([]);
const fetchMerchantList = async () => {
let res = await getMerchantlist({});
merchantList.value = res?.data?.child;
};
onMounted(fetchMerchantList);
return {
merchantList
};
}
3明刷、TypeScript支持
這一部分內(nèi)容,準(zhǔn)確的來說满粗,是TS的內(nèi)容辈末,不過它與Vue3項(xiàng)目開發(fā),息息相關(guān)映皆,所以真的想用Vue3挤聘,我們還是得了解TS的使用。
不過這一部分捅彻,我不會介紹TS的基礎(chǔ)語法组去,主要是在業(yè)務(wù)場景中,如何組織TS沟饥。
使用TS進(jìn)行業(yè)務(wù)開發(fā)添怔,一個(gè)核心的思維是,先關(guān)注數(shù)據(jù)結(jié)構(gòu)贤旷,再根據(jù)數(shù)據(jù)結(jié)構(gòu)進(jìn)行頁面開發(fā)广料。以前的前端開發(fā)模式是,先寫頁面幼驶,后關(guān)注數(shù)據(jù)艾杏。
比如要寫一個(gè)禮品列表的頁面,我們可能要定義這么一些interface盅藻」荷#總而言之,我們需要關(guān)注的是:頁面數(shù)據(jù)的interface氏淑、接口返回的數(shù)據(jù)類型勃蜘、接口的入?yún)㈩愋偷鹊取?/p>
// 禮品創(chuàng)建、編輯假残、列表中的每一項(xiàng)缭贡,都會是這個(gè)數(shù)據(jù)類型炉擅。
interface IGiftItem {
id: string | number;
name: string;
desc: string;
[key: string]: any;
}
// 全局相應(yīng)的類型定義
// 而且一般來說,我們不確認(rèn)阳惹,接口返回的類型到底是什么(可能是null谍失、可能是對象、也可能是數(shù)組)莹汤,所以使用范型來定義
interface IRes<T> {
code: number;
msg: string;
data: T
}
// 接口返回?cái)?shù)據(jù)類型定義
interface IGiftInfo {
list: Array<IGiftItem>;
pageNum: number;
pageSize: number;
total: number;
}
在一個(gè)常見的接口請求中快鱼,我們一般使用TS這么定義一個(gè)數(shù)據(jù)請求,數(shù)據(jù)請求的req類型纲岭,數(shù)據(jù)請求的res類型抹竹。
export const getGiftlist = (
params: Record<string, any>
): Promise<IRes<IGiftInfo>> => {
return Http.get("/apis/gift/list", params);
};