Vue3.0的優(yōu)勢(shì)
- 性能比Vue2.x快1.2~2倍
- 按需編譯军俊,體積比Vue2.x更小
- 組合API(類似React Hooks)
- 更好的TS支持
- 暴露自定義渲染API
Vue3.0如何變快得微谓?
- diff方法優(yōu)化
- Vue2中得虛擬dom是進(jìn)行全量得對(duì)比
- Vue3新增了靜態(tài)標(biāo)記
- 在與上次虛擬節(jié)點(diǎn)進(jìn)行對(duì)比時(shí)候,只對(duì)比帶有patch flag得節(jié)點(diǎn)去扣,并且可以通過flag(例如:1就代表動(dòng)態(tài)文本節(jié)點(diǎn))得信息得知當(dāng)前節(jié)點(diǎn)要對(duì)比得具體內(nèi)容
- hoistStatic 靜態(tài)提升
- Vue2中無論元素是否參與更新蔚携,每次都會(huì)重新創(chuàng)建
- Vue3中對(duì)于不參與更新得元素,只會(huì)被創(chuàng)建一次银亲,之后會(huì)在每次渲染得時(shí)候被不停得復(fù)用
- cacheHandlers 事件偵聽器緩存
- 默認(rèn)情況下onClick會(huì)被視為動(dòng)態(tài)綁定艺骂,所以每次都會(huì)去追蹤它得變化汞舱,但是因?yàn)槭峭粋€(gè)函數(shù)伍纫,所以沒有追蹤變化(也是通過flag),直接緩存起來復(fù)用即可
- SSR渲染
- 當(dāng)有大量靜態(tài)得內(nèi)容得時(shí)候昂芜,這些內(nèi)容會(huì)被當(dāng)做純字符串推進(jìn)一個(gè)buffer里面莹规,即使存在動(dòng)態(tài)得綁定,會(huì)通過模板插值嵌入進(jìn)去泌神,這樣會(huì)比通過虛擬dom
渲染得快上很多很多 - 靜態(tài)內(nèi)容大到一定量級(jí)時(shí)候良漱,會(huì)用_createStaticVNode方法在客戶端去生成一個(gè)static node,這些靜態(tài)node,會(huì)被直接innerHTML,就不需要?jiǎng)?chuàng)建對(duì)象欢际,然后根據(jù)對(duì)象渲染了
- 當(dāng)有大量靜態(tài)得內(nèi)容得時(shí)候昂芜,這些內(nèi)容會(huì)被當(dāng)做純字符串推進(jìn)一個(gè)buffer里面莹规,即使存在動(dòng)態(tài)得綁定,會(huì)通過模板插值嵌入進(jìn)去泌神,這樣會(huì)比通過虛擬dom
Vue3.0快速上手
當(dāng)然也有腳手架和webpack的方式母市,但是此時(shí)只使用這個(gè) Vite打包使用的是rollup
-
什么是Vite?
Vite是Vue作者開發(fā)的一款意圖取代webpack的工具,其實(shí)現(xiàn)原理是利用ES6的import會(huì)發(fā)送請(qǐng)求去加載文件的特性损趋,攔截這些請(qǐng)求患久,做一些預(yù)編譯,省去webpack冗長的打包時(shí)間
-
安裝Vite
npm i -g create-vite-app
-
創(chuàng)建項(xiàng)目
create-vite-app projectName
安裝依賴然后運(yùn)行
腳手架形式
- npm install -g @vue/cli
- vue create hello-vue3
知識(shí)點(diǎn)補(bǔ)充
<template>
<div>
<!-- 注意此處,本來bind里面綁定的應(yīng)該是變量蒋失,但是此時(shí)傳遞固定值則需要加'' -->
<a v-bind:[myHref]="'https://www.baidu.com'">跳轉(zhuǎn)百度</a>
<!-- 對(duì)象也可遍歷 -->
<li v-for="(value, name, index) in myObject" :key="index">
{{ name }}--{{ value }}---{{ index }}
</li>
<button @click="one($event), two($event)">多事件處理</button>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
infos: "嘿嘿",
infos1: "哈哈",
myHref: "href",
// myHref:'title',
myObject: {
title: "ahdhasa",
name: "zq",
},
};
},
methods: {
one(e) {
console.log(e);
},
two(e) {
console.log(e);
},
},
};
</script>
自定義事件
<template>
<div>
<!-- 自定義事件 -->
<Child @send="getChild" />
</div>
</template>
<script>
import Child from "./components/Child";
export default {
name: "App",
components: {
Child
},
methods: {
getChild(data) {
console.log("子組件觸發(fā)了該方法", data);
}
},
};
</script>
<template>
<div>
<p>Child</p>
<button @click="test">執(zhí)行父組件的自定義事件</button>
</div>
</template>
<script>
export default {
//自定義事件還可以添加上校驗(yàn)返帕,但是一般不需要
emits:{
//這個(gè)send需要和自定義事件名稱保持一致
send:({msg})=>{
//注意:雖然驗(yàn)證,但是還是會(huì)把數(shù)據(jù)傳遞到父組件的
if (msg.length>10) {
return true
} else {
console.log(111,"數(shù)據(jù)長度不足");
return false
}
}
},
//建議定義的所有發(fā)出的事件都記錄如下篙挽,雖然不記錄也可以
// emits:["xxx-xxx"],
data(){
return{
msg:'子組件數(shù)據(jù)'
}
},
methods:{
test(){
//send就是組件上面的自定義函數(shù)名
// this.$emit('send','子組件數(shù)據(jù)')
this.$emit('send',{msg:this.msg})
}
}
}
</script>
自定義輸入框?qū)崿F(xiàn)雙向數(shù)據(jù)綁定
- 默認(rèn)情況下荆萤,組件上的v-model使用modelvalue作為prop和update:modelValue作為事件。
- 可以通過v-model傳遞參數(shù)來修改這些名稱
<template>
<div>
<!-- 自定義輸入框組件铣卡,實(shí)現(xiàn)雙向數(shù)據(jù)綁定,infos是自定義屬性名稱链韭,不定的 -->
<MyComponent v-model:infos="infos" v-model:infos1="infos1" />
</div>
</template>
<script>
import MyComponent from "./components/MyComponent";
export default {
name: "App",
components: {
MyComponent
},
data() {
return {
infos: "嘿嘿",
infos1: "哈哈",
};
}
};
</script>
<template>
<!-- update是固定的,infos的app.vue中的關(guān)鍵字是自定義的煮落,對(duì)應(yīng)起來就行 -->
<input
type="text"
:value="infos"
@input="$emit('update:infos', $event.target.value)"
/>
<input
type="text"
:value="infos1"
@input="$emit('update:infos1', $event.target.value)"
/>
</template>
<script>
export default {
props: ["infos","infos1"],
};
</script>
非prop的attribute繼承
- 一個(gè)非prop的attribute是指?jìng)飨蛞粋€(gè)組件敞峭,但是該組件并沒有相應(yīng)的props或emits定義的attribute。
- 常見的示例包含class style id屬性
<template>
<div>
<!-- 非prop的attribute繼承 -->
<Date class="datecss" />
</div>
</template>
- Date.vue
<template>
<!-- 其實(shí)可以發(fā)現(xiàn)州邢,此處div被添加了datecss的樣式 -->
<div>
hello world
</div>
</template>
自定義attribute繼承
- 如果你不希望組件的根元素繼承attribute儡陨,可以在組件的選項(xiàng)中設(shè)置inheritAttrs:false;
- 例如:禁用attribute繼承的常見情況是需要將attribute應(yīng)用于根節(jié)點(diǎn)之外的其他元素
- 通過設(shè)置inheritAttrs:false,可以訪問組件的$attr的property量淌,該property包括組件props和emits property中未包含的所有屬性(例如:class style v-on監(jiān)聽器等
<template>
<div>
<!-- 自定義attribute繼承 -->
<DatePicker data-time="2020-11-11" />
</div>
</template>
- DatePicker.vue
<template>
<!-- 而且vue3允許多個(gè)根節(jié)點(diǎn),即使默認(rèn)繼承如果出現(xiàn)多個(gè)根節(jié)點(diǎn)也會(huì)報(bào)警告嫌褪,所以多個(gè)根節(jié)點(diǎn)也需要指定哪個(gè)繼承,例如 v-bind="$attrs" -->
<!-- 本來data數(shù)據(jù)屬于attribute呀枢,默認(rèn)是給了當(dāng)面的最外層div,可以通過查看dom確定 -->
<div class="datapicker">
<!-- 但是此時(shí)需要讓input使用笼痛,而不是上層的div -->
<!-- 通過v-bind="$attrs"實(shí)現(xiàn)裙秋,前提是inhertAttrs:false,//禁用默認(rèn)繼承 -->
<input type="date" v-bind="$attrs" />
</div>
</template>
<script>
export default {
inhertAttrs:false,//禁用默認(rèn)繼承
};
</script>
自定義dialog內(nèi)部關(guān)閉dialog
<template>
<div>
<button @click="isVisible=true">彈出彈窗</button>
<Dialog :visible="isVisible" @close-modal="isVisible=false"/>
<Home3/>
</div>
</template>
<script>
import Dialog from "./components/Dialog";
export default {
name: "App",
components: {
Dialog
},
data() {
return {
isVisible:false
};
}
};
</script>
- Dialog.vue
<template>
<teleport to="body">
<div v-if="visible">
我是彈窗,樣式不計(jì)缨伊,只說明功能
<button @click="$emit('close-modal')">關(guān)閉</button>
</div>
</teleport>
</template>
<script>
export default {
props: ["visible"],
};
</script>
teleport
- Vue3中的組件模板屬于該組件摘刑,有時(shí)候想把模板的內(nèi)容移動(dòng)到當(dāng)前組件之外的dom中,這個(gè)時(shí)候就可以使用Teleport了
- 表示把teleport內(nèi)包含的內(nèi)容顯示到body中
<teleport to="body">
內(nèi)容
</teleport>
同理可以 to="#app"這種形式
例如:彈窗組件刻坊,點(diǎn)擊彈出枷恕,內(nèi)部一定通過css樣式確定該組件的位置,但是彈窗組件所有位置谭胚,可能外部有定位樣式徐块,影響了本身彈窗的樣式,
導(dǎo)致顯示混亂灾而,這時(shí)候就可以通過teleport然后指定顯示插入的節(jié)點(diǎn)位置胡控,
例如放到body節(jié)點(diǎn),則基本上無影響了旁趟。雖然代碼形式上可能彈窗再某個(gè)組件內(nèi)
部昼激,但是實(shí)際顯示時(shí)候卻是再body根節(jié)點(diǎn)上面
vue3生命周期
- beforeCreate:實(shí)例剛被創(chuàng)建
- created:實(shí)例已經(jīng)創(chuàng)建完成
- beforeMount:模板編譯之前
- mounted:模板編譯完成
- beforeUpdate:數(shù)據(jù)更新之前
- updated:數(shù)據(jù)更新完畢
- actived: keep-alive緩存的組件激活時(shí)調(diào)用
- deactived: keep-alive緩存的組件停用時(shí)調(diào)用
- beforeUnmount:對(duì)應(yīng)2.x的beforeDestory,頁面銷毀的時(shí)候要保存一些數(shù)據(jù),就在此處
- unmounted:2.x的destoryed 實(shí)例銷毀完成
import {onMounted,oUnmounted} from 'vue';
set(){
onMounted(()=>{
console.log('onMounted');
});
}
Compositon API介紹
reactive
作用:創(chuàng)建響應(yīng)式對(duì)象橙困,非包裝對(duì)象敛劝,可以認(rèn)識(shí)是模板中的狀態(tài)
- template可以放兄弟節(jié)點(diǎn)
- reactive類似useState,如果參數(shù)是字符串纷宇,數(shù)字夸盟,會(huì)報(bào)警告,value cannot be made reactive,所以應(yīng)該設(shè)置對(duì)象像捶,這樣可以數(shù)據(jù)驅(qū)動(dòng)頁面
<div>
{{countobj.count}}-<button @click="add()">Add</button>
</div>
setup(){
const countobj =reactive({count:0});
const add=() => {
countobj.count++;
}
return {countobj,add}
}
<template>
<div>child-hooks-{{ mytitle }}-{{ mytext }}</div>
<div>
navbar-<button @click="handleClick">navbar-click</button>
</div>
</template>
<script>
import { ref } from "vue";
export default {
props: ["mytitle"],
//setup內(nèi)部沒有this上陕,可以這樣獲取prosp;
setup(props, { emit }) {
// console.log(props.mytitle)
const mytext = ref(props.mytitle + "11111111111111111");
const handleClick = () => {
emit('kerwinevent')
}
return {
mytext,
handleClick
};
},
};
</script>
- 下面是父級(jí)接受emit
<template>
<div>
<navbar @kerwinevent="handleChange"/>
<sidebar v-if="state.isShow"/>
</div>
</template>
ref
作用:創(chuàng)建一個(gè)包裝式對(duì)象,含有一個(gè)響應(yīng)式屬性value拓春。它和reactive的差別释簿,就說前者沒有包裝屬性value
const count =ref(0); 可以接收普通數(shù)據(jù)類型,count.value++
<div>
{{count}}-<button @click="add()">Add</button>
</div>
setup(){
const count =ref(0);
const add=() => {
count.count++;
}
return {count,add}
}
ref嵌套在reactive中
<template>
<div class="home">
home-{{count}}--{{state.count}}
<button @click="add">click</button>
</div>
</template>
<script>
import {reactive,ref} from 'vue';
export default{
name:'Home',
setup(){
const count = ref(0);
const state = reactive({count})
const add=() => {
state.count++
//state.count跟ref count都會(huì)更新硼莽;
}
return{ count,add,state}
}
}
</script>
toRefs
默認(rèn)直接展開state庶溶,那么此時(shí)reactive數(shù)據(jù)變成普通數(shù)據(jù),通過toRefs,可以把reactive里的每個(gè)屬性懂鸵,轉(zhuǎn)化為ref對(duì)象偏螺,這樣展開后,就會(huì)變成多個(gè)ref對(duì)象匆光,以然具有響應(yīng)式特性
<template>
<div class="home">
home-{{count}}
<button @click="add">click</button>
</div>
</template>
<script>
import {reactive,ref} from 'vue';
export default{
name:'Home',
setup(){
const state = reactive({count})
const add=() => {
state.count++
}
return{ add,...toRefs(state)}
}
}
</script>
ref訪問dom或者組件
<input type="text" ref="myinput">
const myinput =ref(null);
console.log(myinput.value.value);
計(jì)算屬性
set(){
const mytext = ref('');
const computedSum=computed(()=>mytext.value.substring(0,1).toUpperCase()+mytext.value.substring(1)+mytext.value.substring(1));
return {mytext,computedSum}
}
watch
監(jiān)聽器watch是一個(gè)方法套像,它包含兩個(gè)參數(shù)
const reactivedata=reactive({count:1});
const text=ref("");
watch(()=>reactivedata.count,val=>{
});
watch(text,val=>{
})
//注意:reactive和ref的監(jiān)聽有細(xì)微區(qū)別
第一個(gè)參數(shù)是監(jiān)聽的值,count.value表示當(dāng)count.value發(fā)生變化就會(huì)觸發(fā)監(jiān)聽器的回調(diào)函數(shù)终息,即第二個(gè)參數(shù)夺巩,底二個(gè)參數(shù)可以執(zhí)行監(jiān)聽時(shí)候的回調(diào)
vue3組合api的使用
通俗來說,option API在中小型項(xiàng)目沒問題周崭,但是大型項(xiàng)目柳譬,建議使用composition API,方便邏輯復(fù)用
<template>
<div>
<!-- vue3組合api的使用 -->
<Home3/>
</div>
</template>
- Home3.vue
<template>
<div>------{{ title }}----------</div>
<button @click="getTitle">getTitle</button>
<button @click="getInfo">getInfo</button>
<button @click="setTitle">setTitle</button>
<p>我是分割線----------------</p>
{{ descrip }}--{{ auth }}
<p>{{ fullName }}</p>
<p>watchEffect功能演示</p>
<p>{{ data.num }}</p>
</template>
<script>
import { ref, reactive, toRefs, computed, watchEffect } from "vue";
export default {
setup() {
//定義響應(yīng)式數(shù)據(jù)-如果不用這些定義,則只是普通數(shù)據(jù)续镇,非響應(yīng)式的(原始對(duì)象)
//ref:基本數(shù)據(jù)類型 reactive:對(duì)象
let title = ref("我是一個(gè)標(biāo)題");
let info = reactive({ info: "infossss" });
let article = reactive({ descrip: "描述", auth: "作者" });
// title=readonly(title); //只讀
let getTitle = () => {
console.log(title.value); //注意此處美澳,是title.value
};
let setTitle = () => {
title.value = "修改的";
};
let getInfo = () => {
console.log(info.info);
};
const user = reactive({
fn: "1",
ln: "2",
});
//計(jì)算屬性
const fullName = computed(() => {
return user.fn + "------" + user.ln;
});
//watchEffect功能
let data = reactive({
num: 1,
});
watchEffect(() => {
//和watch不同,不論改不改變監(jiān)聽得值磨取,都會(huì)執(zhí)行該函數(shù)人柿,即最開始時(shí)候也會(huì)執(zhí)行一次的,也算作更新
//而且watch必須監(jiān)聽data即大對(duì)象才能監(jiān)聽忙厌,但是watchEffect可以監(jiān)聽具體的data.num,
//也就是說watchEffect可以監(jiān)聽當(dāng)前回調(diào)里面所有數(shù)據(jù)變化凫岖,即使只是打印一個(gè)log
console.log(`num=${data.num}`);
});
// watch(num1,(newValue,oldValue)=>{
// })
setInterval(() => {
data.num++;
}, 1000);
return {
title,
info,
getTitle,
getInfo,
setTitle,
data,
// ...article //三點(diǎn)運(yùn)算符相當(dāng)于把a(bǔ)rticle內(nèi)部對(duì)象解構(gòu)到這里,但是不是響應(yīng)式數(shù)據(jù),解決方案toRefs
...toRefs(article),
fullName,
};
},
};
</script>
- watchEffect
在響應(yīng)式的跟蹤其依賴項(xiàng)時(shí)立即運(yùn)行一個(gè)函數(shù)逢净,并在更改依賴項(xiàng)時(shí)重新運(yùn)行它.
- watch和watchEffect的區(qū)別
- 懶執(zhí)行哥放,也就是說僅在偵聽的源變更時(shí)才執(zhí)行回調(diào)
- 更明確哪些狀態(tài)的改變會(huì)觸發(fā)偵聽器重新運(yùn)行
- 訪問偵聽狀態(tài)變化前后的值
Provider Inject
通常需要將數(shù)據(jù)從父組件傳遞到子組件時(shí)歼指,使用props。但是甥雕,有一些深嵌套的組件踩身,需要來自深嵌套子組件中父組件的某些內(nèi)容;這種情況社露,相當(dāng)不方便
可以使用provide和inject對(duì)父組件可以作為其子組件的依賴項(xiàng)提供程序挟阻,而不管組件層次結(jié)構(gòu)有多深。
這個(gè)特性有兩部分峭弟,父組件provide選項(xiàng)來提供數(shù)據(jù)附鸽,子組件有一個(gè)inject選項(xiàng)來開始使用這個(gè)數(shù)據(jù)
事件總線
- vue3.x從實(shí)例移除了off $once
- 但是$emit仍然是現(xiàn)有api的一部分,可是只能實(shí)現(xiàn)子組件觸發(fā)父組件的方法
- 實(shí)現(xiàn)非父子組件通信瞒瘸,可以使用第三方
npm install --save mitt
- 新建model\event.js
import mitt from 'mitt'
const VueEvent=mitt();
export default VueEvent;
混入
- 混入和當(dāng)前都有同一個(gè)屬性坷备,則會(huì)應(yīng)用當(dāng)前組件的屬性
- 也可以全局配置mixin,這樣就不需要再每個(gè)組件里面配置了
hooks對(duì)應(yīng)的生命周期
setup函數(shù)說明
setup函數(shù)情臭,是在beforecreate鉤子之前完成的省撑,所以data中數(shù)據(jù)和methods中函數(shù)是無法使用的,setup中的this被vue修改成了underfined俯在;
并且setup函數(shù)必須是同步的竟秫,不能是異步的(async)
- beforeCreate:組件剛剛被創(chuàng)建出來,組件的data和methods還沒有初始化好
- created: 組件剛剛被創(chuàng)建出來朝巫,并且組件的data和methods已經(jīng)初始化好了
- 什么是reactive鸿摇?
- reactive是Vue3中提供的實(shí)現(xiàn)響應(yīng)式數(shù)據(jù)的方法
- 而在Vue3中響應(yīng)式數(shù)據(jù)是通過ES6的Proxy實(shí)現(xiàn)的
- 什么是ref?
- ref和reactive一樣,也是用來實(shí)現(xiàn)響應(yīng)式數(shù)據(jù)的方法
- 由于reactive必須傳遞一個(gè)對(duì)象劈猿,所以導(dǎo)致在開發(fā)中如果只想讓某個(gè)變量實(shí)現(xiàn)響應(yīng)式的時(shí)候會(huì)非常麻煩,所以提供了ref
- ref本質(zhì)還是reactive潮孽,ref(xx)=>reactive({value:xx})
- ref注意點(diǎn):Vue中使用ref的值不用通過value獲取揪荣,但是js中使用的話必須使用value獲取
- Vue是如果判斷數(shù)據(jù)是ref還是reactive的?通過當(dāng)前數(shù)據(jù)的__v_ref來判斷的往史,如果是true就代表是ref數(shù)據(jù)仗颈,reactvie沒有這個(gè)私有屬性
- 可以通過isRef和isReactive來判斷數(shù)據(jù)類型
<template>
<div>
<p>{{ count }}</p>
<button @click="fn">按鈕</button>
<li v-for="stu in state.stus" :key="stu.id">{{ stu.name }}-{{ stu.id }}</li>
</div>
</template>
<script>
import { reactive, ref } from "vue";
export default {
name: "App",
//setup函數(shù)是組合API得入口函數(shù)
setup() {
//通過這個(gè)可知,組合API內(nèi)部邏輯可以是其他函數(shù)椎例,甚至其他js文件導(dǎo)出的邏輯
let { fn, count } = ctrlBtn();
//監(jiān)聽復(fù)雜對(duì)象,reactive的參數(shù)必須是對(duì)象或者數(shù)組
let state = reactive({
stus: [
{
id: 1,
name: "zq",
},
],
});
return {
state,
fn,
count,
};
},
};
function ctrlBtn() {
//定義名稱叫做count得變量挨决,這個(gè)變量得初始值是0
//ref函數(shù)只能監(jiān)聽簡(jiǎn)單類型的變化,不能監(jiān)聽復(fù)雜類型變化订歪,例如數(shù)組脖祈,對(duì)象
let count = ref(0);
function fn() {
count.value += 1;
}
return { count, fn };
}
</script>
監(jiān)聽
- 默認(rèn)情況下,無論是通過ref還是reactive都是遞歸監(jiān)聽
- 遞歸監(jiān)聽數(shù)據(jù)量比較大刷晋,消耗性能
- 非遞歸監(jiān)聽(shallowReactive,shallowRef),shallowReactive非遞歸監(jiān)聽是只監(jiān)聽第一層變化盖高,只生成第一層的proxy對(duì)象(可以打印說明)慎陵;但是如果修改第一層數(shù)據(jù),同時(shí)內(nèi)部的數(shù)據(jù)也被修改的話喻奥,可以發(fā)現(xiàn)UI界面都變化了席纽,即使沒被監(jiān)聽的也變化了,這是因?yàn)榈谝粚有薷牧擞|發(fā)了render撞蚕,如果不修改第一層數(shù)據(jù)润梯,發(fā)現(xiàn)UI無變化。當(dāng)然不論是遞歸還是不遞歸監(jiān)聽甥厦,數(shù)據(jù)都被變化纺铭,只是界面UI更新與否的差別
- 注意點(diǎn):如果是通過shallowRef創(chuàng)建數(shù)據(jù),那么vue監(jiān)聽的是.value的變化矫渔,并不是第一層數(shù)據(jù)的變化彤蔽,即例如let state=shallowRef();其實(shí)是state被包裝成了proxy。
- triggerRef:傳入的參數(shù)會(huì)更新UI,一般用于庙洼,有的是非遞歸監(jiān)聽顿痪,但是我就想更新某個(gè)數(shù)據(jù)時(shí)候使用
- 本質(zhì):shallowRef(xx)=>shallowRef({value:xx}),監(jiān)聽的是.value的變化油够,因?yàn)関alue其實(shí)才是第一層
let state=shallowRef({
a:'a',
b:{
c:'c'
}
});
//a變化是不會(huì)更新界面的
//如下才行
state.value={....}
//triggerRef:主動(dòng)更新蚁袭,但是沒有triggerReactive,如果是reactive類型數(shù)據(jù)是無法主動(dòng)觸發(fā)UI更新的
triggerRef(state);
toRaw
- 相當(dāng)于reactive的逆向石咬,獲取的是傳入的參數(shù)
- 應(yīng)用場(chǎng)景是揩悄,不需要更新UI,但是想操作數(shù)據(jù)的時(shí)候
- 注意:如果是ref的數(shù)據(jù)鬼悠,則需要.value形式才能拿到原始數(shù)據(jù)
let state = reactive({
name:'zq'
});
//state其實(shí)是把傳入的對(duì)象包裝成了一個(gè)proxy
//獲取類型的原始數(shù)據(jù)
toRaw(state);//其實(shí)就是reactive傳入的對(duì)象
//ref也是可以傳遞對(duì)象的
let obj={name:'zq'}
let ha=ref(obj);
toRaw(ha.value);
markRaw
- 作用是避免響應(yīng)式删性,UI不會(huì)再更新了
let obj={name:'zq'}
obj=markRaw(obj);
let state=reactive(obj);
//后面再怎么更新,UI也不會(huì)有變化
toRef
把一個(gè)響應(yīng)式對(duì)象轉(zhuǎn)換成普通對(duì)象焕窝,該普通對(duì)象的每個(gè)property都是一個(gè)ref蹬挺,和響應(yīng)式對(duì)象property--對(duì)應(yīng)
- 效果和ref類似,但是更多的應(yīng)用場(chǎng)景是讓局部數(shù)據(jù)變成響應(yīng)式
- 不會(huì)觸發(fā)UI界面的更新
- ref 復(fù)制它掂,修改響應(yīng)式數(shù)據(jù)不會(huì)影響以前的數(shù)據(jù)巴帮,數(shù)據(jù)變化UI更新
- toRef 引用,修改響應(yīng)式數(shù)據(jù)會(huì)影響以前的數(shù)據(jù)虐秋,數(shù)據(jù)變化UI不更新
let obj={name:'zq',id:1}
let state=ref(obj.name);
function myFn(){
/*
如果利用ref將某一個(gè)對(duì)象中的屬性變成響應(yīng)式數(shù)據(jù)
如果修改響應(yīng)式的數(shù)據(jù)是不會(huì)影響到原始數(shù)據(jù)的榕茧,即ref其實(shí)是復(fù)制的關(guān)系
*/
state.value='zs';
// obj 發(fā)現(xiàn)壓根沒變
//state 響應(yīng)式對(duì)象是變化了
}
let obj={name:'zq',id:1}
let state=toRef(obj,'name');
function myFn(){
state.value='zs';
// obj/state 數(shù)據(jù)都變化了
/**
* 利用toRef將某一個(gè)對(duì)象中的屬性變成響應(yīng)式的數(shù)據(jù),修改響應(yīng)式的數(shù)據(jù)是會(huì)修改原始數(shù)據(jù)的
* 但是不會(huì)觸發(fā)UI界面的更新
* /
}
toRefs
- 類似于toRef客给,但是是可以添加多個(gè)響應(yīng)的
- toRef如果響應(yīng)多個(gè)用押,是需要多次調(diào)用的
let obj={name:'zq',id:1}
let state=toRefs(obj);
function myFn(){
//注意此處value的位置
state.name.value='zs';
state.id.value=16;
}
customRef
- 自定義ref
- 應(yīng)用場(chǎng)景:setup函數(shù)內(nèi)部必須是同步操作,網(wǎng)絡(luò)請(qǐng)求一般都是異步的起愈,雖然回調(diào)可以使用只恨,但是不優(yōu)雅译仗,這時(shí)候自定義ref的場(chǎng)景(最起碼使用起來是同步的)就出現(xiàn)了
function myRef(value) {
return customRef((track,trigger)=>{
return {
get(){
track();//告訴vue,這個(gè)數(shù)據(jù)需要追蹤變化
return value;
},
set(val){
value=val;
trigger();//告訴vue觸發(fā)界面更新
}
}
})
}
setup() {
let age = myRef(18);
function myFn() {
age.value += 1;
}
return { age, myFn };
},
- 案例:網(wǎng)絡(luò)請(qǐng)求
function myRef(value) {
return customRef((track, trigger) => {
fetch(value)
.then((res) => {
return res.json();
})
.then((data) => {
value = data;
trigger();
})
.catch((err) => {
console.log(err);
});
return {
get() {
track(); //告訴vue官觅,這個(gè)數(shù)據(jù)需要追蹤變化
//注意點(diǎn):不能再get方法中發(fā)送網(wǎng)絡(luò)請(qǐng)求纵菌,因?yàn)殇秩窘缑?>調(diào)用get->發(fā)送網(wǎng)絡(luò)請(qǐng)求->保存數(shù)據(jù)->更新界面->調(diào)用get;形成死循環(huán)
return value;
},
set(val) {
value = val;
trigger(); //告訴vue觸發(fā)界面更新
},
};
});
}
setup() {
//使用的時(shí)候,同步使用即可
let data = myRef("../public/data.json");
return { data };
},
組合API中監(jiān)聽生命周期
<template>
<div>
<button ref="btn">添加</button>
</div>
</template>
<script>
import { onMounted, ref } from "vue";
export default {
name: "HelloWorld",
setup() {
let btn=ref(null);
//組合API中監(jiān)聽生命周期
onMounted(()=>{
console.log('onMounted',btn.value); //<button>添加</button>
})
return { btn };
},
};
</script>
readonly
傳入一個(gè)對(duì)象(響應(yīng)式或普通)或ref,返回一個(gè)原始對(duì)象的只讀代理休涤,這個(gè)只讀代理是深層的咱圆,對(duì)象內(nèi)部任何嵌套的屬性也都是只讀的。
- 只讀數(shù)據(jù)
- isReadonly:判斷數(shù)據(jù)類型是不是readonly/shallowReadonly類型
- const :賦值保護(hù)功氨,不能給變量重新賦值序苏,但是對(duì)象內(nèi)部修改可以修改
- readonly:屬性保護(hù),不能給屬性重新賦值
- 注意: shallowReadonly :創(chuàng)建一個(gè)只讀的數(shù)據(jù)捷凄,第一層只讀
setup() {
//用于創(chuàng)建一個(gè)只讀的數(shù)據(jù)忱详,并且是遞歸只讀(全部只讀)
let state = readonly({ name: "zq" });
// state.name='asfsa' //警告:Set operation on key "name" failed: target is readonly
return { state };
},
手寫Reactive/Ref等等
function shallowReactive(obj) {
return new Proxy(obj, {
get(ojb, key) {
return obj[key];
},
set(obj, key, val) {
obj[key] = val;
return true;
}
})
}
function shallowReadonly(obj) {
return new Proxy(obj, {
get(ojb, key) {
return obj[key];
},
set(obj, key, val) {
console.log(`${key}是只讀的,不能賦值`);
}
})
}
//同理readonly其實(shí)就是把reactive的set改為輸出即可
function shallowRef(val) {
return shallowReactive({ value: val });
}
function ref(val) {
return reactive({value:val})
}
function reactive(obj) {
if (typeof obj === 'object') {
if (obj instanceof Array) {
//如果是數(shù)組跺涤,取出數(shù)組中每一個(gè)元素匈睁,判斷每個(gè)元素是否是對(duì)象
//如果又是對(duì)象也需要包裝成proxy
obj.forEach(item => {
if (typeof item === 'object') {
obj[index] = reactive(item);
}
})
} else {
//如果是對(duì)象,取出對(duì)象屬性的取值桶错,判斷對(duì)象屬性的取值是否又是對(duì)象航唆,如果還是對(duì)象則包裝成proxy
for (const key in obj) {
let item = obj[key];
if (typeof item === 'object') {
obj[key] = reactive(item);
}
}
}
return new Proxy(obj, {
get(ojb, key) {
return obj[key];
},
set(obj, key, val) {
obj[key] = val;
return true;
}
})
} else {
console.log(`${obj} is not a object`);
}
}
TS搭建vue3.0開發(fā)環(huán)境
- 方案一:初始化項(xiàng)目時(shí)候選擇自定義
- 方案二:進(jìn)入已有項(xiàng)目,執(zhí)行`vue add typescript
主要變化
- script標(biāo)簽添加ts
- export default后添加
defineComponent
<script lang="ts">
import { defineComponent } from 'vue';
import Home from "./components/Home.vue";
import News from "./components/News.vue";
export default defineComponent({
name: 'App',
components: {
Home,
News
},
data(){
return{
aaa:'ada'
}
}
});
</script>
基本使用
<template>
<p>{{title}}</p>
<button @click="setTitle">修改</button>
</template>
<script lang="ts">
import { defineComponent } from "vue";
let title:string="hahahh";
export default defineComponent({
data(){
return {
title
}
},
methods:{
setTitle(){
this.title="修改呢";
}
}
})
</script>
使用接口限制數(shù)據(jù)類型
<template>
<p>----{{username}}----</p>
<button @click="setUserName">改變</button>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from "vue";
interface User{
username:string,
age:number,
setUserName():void;
getUserName?():void;
}
export default defineComponent({
setup(){
//實(shí)現(xiàn)接口的第一種寫法
let user:User=reactive({
age:20,
username:"張三",
setUserName(){
this.username="修改名字"
}
})
//實(shí)現(xiàn)接口的第二種寫法
// let user=reactive<User>({
// age:20,
// username:"張三",
// setUserName(){
// this.username="修改名字"
// }
// })
//實(shí)現(xiàn)接口的第三種寫法
// let user=reactive({
// age:20,
// username:"張三",
// setUserName(){
// this.username="修改名字"
// }
// }) as User
//普通寫法
// let user=reactive({
// age:20,
// username:"張三",
// setUserName(){
// this.username="修改名字"
// }
// })
//泛型
// let count=ref<number|string>("20");
return{
...toRefs(user)
}
}
})
</script>
路由
npm install vue-router@next -S
- 新建src/routes.ts配置路由
import { createRouter,createWebHashHistory,createWebHistory } from "vue-router";
import Home from "./components/Home.vue";
import News from "./components/News.vue";
import NewsContent from "./components/NewsContent.vue";
const router=createRouter({
history:createWebHashHistory(),//hash模式
// history:createWebHistory(),//history模式
routes:[
{path:'/',component:Home},
{path:'/news',component:News},
{path:'/newscontent/:aid',component:NewsContent},//動(dòng)態(tài)路由
]
})
export default router;
獲取$router
import {getCurrentInstance} from 'vue'
setup(){
//const router=useRouter();//vue-router中的useRouter直接獲取router對(duì)象
const {ctx}=getCurrentInstance();
ctx.$router.push('/about);
//或者直接:router.push('/about);
}
//獲取動(dòng)態(tài)路由參數(shù)
setup(){
const router=useRouter();
//或者ctx形式也行
console.log(router.currentRoute.value.params.id);
}
- main.ts
import { createApp } from 'vue'
import App from './App.vue'
import route from "./routes";
const app=createApp(App);
app.use(route);
app.mount('#app');
<template>
<router-link to="/">首頁</router-link>
<router-link to="/news">新聞</router-link>
<router-view></router-view>
</template>
- News.vue
<template>
<ul>
<li v-for="(item,index) in list" :key="index">
<!--路由跳轉(zhuǎn)-->
<router-link :to="`/newscontent/${index}`">{{item}}</router-link>
</li>
</ul>
</template>
<script lang="ts">
import { defineComponent} from "vue";
interface User{
list:string[]
}
export default defineComponent({
data(){
return {
list:[]
} as User;
},
mounted(){
for (let i = 0; i < 10; i++) {
this.list.push(`我是第${i}個(gè)新聞`)
}
}
})
</script>
- NewsContent.vue
<template>
<p>NewContents組件</p>
</template>
<script lang="ts">
import { defineComponent} from "vue";
export default defineComponent({
mounted(){
//獲取動(dòng)態(tài)路由傳值
console.log(this.$route.params);
}
})
</script>
- Get獲取參數(shù)
get參數(shù)傳值:http://localhost:8080/#/newscontent?aid=1230
獲取: this.$route.query
- 編程式路由
this.$router.push({path:'news'})
this.$router.push({path:'/newscontent/495'})
Vuex
setup(){
//const store =useStore();//vuex中的useStore直接獲取store對(duì)象
const {ctx}=getCurrentInstance();
const storeCount=computed(()=>ctx.$store.state.count);
add(){
ctx.$store.commit('addMutation');
}
return{ storeCount}
}
//store/index.js
export default Vuex.createStore({
state:{count:1},
mutations:{
addMutation(state){
state.count++;
}
},
actions:{
},
modules:{
}
})
不能使用mapMutations,mapState....,因?yàn)橐蕾囉趖his.$store;