vue3官網(wǎng):https://v3.cn.vuejs.org/guide/introduction.html
1. 初始Vue3
導(dǎo)入vue3
<script src="https://unpkg.com/vue@next"></script>
vue3創(chuàng)建一個(gè)vue實(shí)例
在vue3里面Vue是一個(gè)對(duì)象,通過(guò)該對(duì)象的createApp()方法,創(chuàng)建一個(gè)Vue實(shí)例
Vue.createApp({
// 注意:vue3中,取消了el選項(xiàng)
// el:'#app',
// 注意:vue3中,無(wú)論是組件和是vue實(shí)例,data選項(xiàng)都必須是一個(gè)方法,由方法返回對(duì)象。
data(){
return {
name:'Vue3',
age:'2'
}
}
}).mount('#app')
在vue2里面的Vue是一個(gè)構(gòu)造函數(shù),通過(guò)該構(gòu)造函數(shù)創(chuàng)建一個(gè)Vue實(shí)例
new Vue({
// el:'#app',
// 在vue2中,data選項(xiàng)可以是對(duì)象,也可以是方法返回一個(gè)對(duì)象
data:{
name:'Vue2',
age:'6'
}
}).$mount('#app')
在vue2可以通過(guò)el選項(xiàng)指定一個(gè)掛載的容器,也可以通過(guò)$mount()方法指定掛載的容器
在vue3中,創(chuàng)建完一個(gè)組件實(shí)例,我們需要調(diào)用mount()方法將組件實(shí)例掛載到頁(yè)面中
2. Vue2和Vue3的響應(yīng)式
在Vue2中,Vue實(shí)例在初始化的時(shí)候,會(huì)將obj對(duì)象身上的所有數(shù)據(jù)做響應(yīng)式處理。所謂響應(yīng)式,指的是數(shù)據(jù)發(fā)生變化后,頁(yè)面自動(dòng)更新。
Vue2的響應(yīng)式:不能直接給對(duì)象添加屬性,刪除對(duì)象的屬性,不能直接操作數(shù)組的下標(biāo),但是,Vue2同時(shí)也提供了解決這些問(wèn)題的方案。
<div id="app" v-cloak>
<div>學(xué)生信息:{{student}}</div>
<button @click="student.name+='!'">修改學(xué)生姓名</button>
<button @click="student.age++">修改學(xué)生年齡</button>
<button @click="addSex">添加性別</button>
<button @click="delName">刪除姓名</button>
<div>食物:{{foods}}</div>
<button @click="addFood">添加食物</button>
<button @click="delFood">刪除帝王蟹</button>
</div>
// vue2的響應(yīng)式
new Vue({
el:'#app',
data:{
student:{
name:'張三',
age:20
},
foods:['魚(yú)翅','松茸','魚(yú)子醬','帝王蟹','熊掌']
},
methods: {
//添加性別
addSex(){
//后添加的屬性是非響應(yīng)式的
// this.student.sex='男'
//可以通過(guò)$forceUpdate()強(qiáng)制頁(yè)面更新一次
// this.$forceUpdate()
//推薦使用$set方法給對(duì)象添加新的屬性,確保新添加的屬性同樣具備響應(yīng)式
this.$set(this.student,'sex','男')
},
//刪除姓名
delName(){
// 直接使用delete方式刪除對(duì)象的屬性后,不具備響應(yīng)式
// delete this.student.name
//使用$delete方法,刪除對(duì)象的屬性后,繼續(xù)具備響應(yīng)式
this.$delete(this.student,'name')
},
//添加食物
addFood(){
// 操作數(shù)組后同時(shí)要具有響應(yīng)式,必須要使用下面的方法:
// push pop unshift shift sort reverse splice
// this.foods.push('佛跳墻')
// this.foods[5] = '佛跳墻'
// this.$forceUpdate()
//推薦使用$set方法根據(jù)下標(biāo)添加數(shù)組元素,確保新添加的元素同樣具備響應(yīng)式
this.$set(this.foods,5,'佛跳墻')
},
//刪除食物
delFood(){
// this.foods.splice(3,1)
//直接根據(jù)下標(biāo)刪除數(shù)組元素,不具備響應(yīng)式
// this.foods[3] = null
//使用$delete方法,刪除數(shù)組中指定位置的元素,繼續(xù)具備響應(yīng)式
this.$delete(this.foods,3)
}
},
})
vue3修復(fù)了vue2中響應(yīng)式的所有缺陷。在Vue3中,直接給對(duì)象添加屬性、直接刪除對(duì)象的屬性、根據(jù)下標(biāo)操作數(shù)組,都依然具備響應(yīng)式。
Vue.createApp({
data() {
return {
student:{
name:'張三',
age:20
},
foods:['魚(yú)翅','松茸','魚(yú)子醬','帝王蟹','熊掌']
}
},
methods: {
addSex(){
this.student.sex = '男'
},
delName(){
delete this.student.name
},
addFood(){
this.foods[5] = '佛跳墻'
},
delFood(){
delete this.foods[3]
}
},
}).mount("#app")
3. Vue2和Vue3的響應(yīng)式原理
Vue2在實(shí)例化時(shí),會(huì)將data里面的所有數(shù)據(jù)采用 Object.defineProperty 進(jìn)行處理,從而實(shí)現(xiàn)響應(yīng)式功能。但是你之后往data里面添加的數(shù)據(jù),由于沒(méi)有采用 Object.defineProperty 進(jìn)行處理,所以不具備響應(yīng)式。$set()方法,內(nèi)部就是對(duì)單個(gè)屬性重新采用 Object.defineProperty 進(jìn)行處理,從而具備響應(yīng)式。
<h2 id="name"></h2>
<h2 id="age"></h2>
// Vue2的響應(yīng)式原理:
// 這里的obj是源對(duì)象
let obj = {
name:'張三',
age:20
}
// 在頁(yè)面中顯示姓名和年齡
document.getElementById('name').innerText = obj.name
document.getElementById('age').innerText = obj.age
// 這里的obj2代理對(duì)象---由obj2代理obj
let obj2 = {}
// 給obj2定義name屬性
Object.defineProperty(obj2,'name',{
get(){
return obj.name
},
set(value){
obj.name = value
document.getElementById('name').innerText = obj.name
}
})
// 給obj2定義age屬性
Object.defineProperty(obj2,'age',{
get(){
return obj.age
},
set(value){
obj.age = value
document.getElementById('age').innerText = obj.age
}
})
// Vue3的響應(yīng)式原理:
// 這里的obj是源對(duì)象
let obj = {
name:'張三',
age:20
}
// 在頁(yè)面中顯示姓名和年齡
document.getElementById('name').innerText = obj.name
document.getElementById('age').innerText = obj.age
// 這里的obj2代理對(duì)象---由obj2代理obj
// new Proxy(源對(duì)象,{...})的方式,創(chuàng)建代理對(duì)象
let obj2 = new Proxy(obj,{
//讀取屬性,參數(shù)分別是:源對(duì)象,屬性名
get(target, property){
// 直接根據(jù)源對(duì)象返回源對(duì)象身上的屬性
// return target[property]
// 通過(guò)發(fā)射對(duì)象,發(fā)射輸出源對(duì)象身上的屬性
return Reflect.get(target,property)
},
//設(shè)置屬性,參數(shù)分別是:源對(duì)象,屬性名,屬性值
set(target, property,value){
// target[property] = value
if(Reflect.has(target,property)){
Reflect.set(target, property,value)
document.getElementById(`${property}`).innerText = value
}
},
//刪除屬性,參數(shù)分別是:源對(duì)象,屬性名
deleteProperty(target, property){
// delete target[property]
Reflect.deleteProperty(target, property)
}
})
4. 引出Vue3新推出的組合式API
什么是組合式API(Composition API),就是Vue推出的一些新的方法,這個(gè)方法在setup中使用
setup方法是所有組合式API的入口
Vue3中,無(wú)論是Vue實(shí)例,還是組件,data選項(xiàng)都必須是一個(gè)方法。
我們之前習(xí)慣將所有的數(shù)據(jù)放在data選項(xiàng)中定義,所有的方法放在methods選項(xiàng)中定義,
所有的計(jì)算屬性放在computed選項(xiàng)中定義,所有的偵聽(tīng)器放在watch選項(xiàng)中定義,
這樣就會(huì)導(dǎo)致一個(gè)業(yè)務(wù)的代碼會(huì)拆分到多個(gè)結(jié)構(gòu)中去寫(xiě),如果一個(gè)頁(yè)面中要操作很多個(gè)業(yè)務(wù),代碼后期維護(hù)成本會(huì)很高。
所以,Vue3引入了組合式API,簡(jiǎn)化之前繁瑣的過(guò)程,將相同業(yè)務(wù)的代碼靠在一起寫(xiě)。
// 組合式api的作用是:將原來(lái)分散開(kāi)來(lái)定義的數(shù)據(jù),方法,計(jì)算屬性,監(jiān)聽(tīng)器,組合起來(lái)定義一個(gè)完整的業(yè)務(wù)。
// ref用于定義響應(yīng)式數(shù)據(jù)
let {ref} = Vue
Vue.createApp({
// setup是組合式api的舞臺(tái),所有的組合式api都要在setup里面使用
setup() {
// 在setup中,直接定義的數(shù)據(jù)是不具備響應(yīng)式的,
// 如果要使數(shù)據(jù)具備響應(yīng)式,需要使用ref組合式API對(duì)數(shù)據(jù)進(jìn)行包裝,包裝后返回的是ref對(duì)象
//定義汽車(chē)相關(guān)數(shù)據(jù)
// 使用ref()方法,定義一個(gè)響應(yīng)式對(duì)象
let carName=ref('保時(shí)捷')
let carPrice=ref('100W')
//定義汽車(chē)相關(guān)方法
function updateCar(){
//修改對(duì)象的值,要通過(guò)value屬性
carName.value = '特斯拉'
carPrice.value = '80W'
}
//定義飛機(jī)相關(guān)數(shù)據(jù)
let planeName=ref('波音747')
let planePrice=ref('10Y')
//定義飛機(jī)相關(guān)方法
function updatePlane(){
planeName.value = 'B52轟炸機(jī)',
planePrice.value = '30Y'
}
//手表
let watchName=ref('勞力士')
let watchPrice=ref('10W')
function updateWacth(){
watchName.value = '歐米伽',
watchPrice.value = '4W'
}
//手機(jī)
let phoneName=ref('iphone13')
let phonePrice=ref('5999')
function updatePhone(){
phoneName.value = '華為',
phonePrice.value = '6999'
}
return{
//返回汽車(chē)相關(guān)數(shù)據(jù)
carName,
carPrice,
updateCar,
//返回飛機(jī)相關(guān)數(shù)據(jù)
planeName,
planePrice,
updatePlane,
//返回手表相關(guān)數(shù)據(jù)
watchName,
watchPrice,
updateWacth,
//返回手機(jī)相關(guān)數(shù)據(jù)
phoneName,
phonePrice,
updatePhone
}
},
}).mount('#app')
5. ref和reactive
ref 和 reactive 都是用于定義響應(yīng)式數(shù)據(jù)。通常情況下,基本類(lèi)型的數(shù)據(jù),選擇用ref定義;引用類(lèi)型的數(shù)據(jù),選擇用reactive定義。
ref方法:返回的是ref對(duì)象,ref對(duì)象的value屬性是一個(gè)代理對(duì)象(Proxy)。使用ref既可以定義基本類(lèi)型數(shù)據(jù),也可以定義引用類(lèi)型數(shù)據(jù)。注意:修改ref對(duì)象的值,必須要先.value再.具體的屬性。
reactive方法:直接返回一個(gè)代理對(duì)象(Proxy)。reactive只能定義引用類(lèi)型數(shù)據(jù).
<div id="app">
<h4>姓名:{{name}}</h4>
<h4>學(xué)生:{{stu}}</h4>
<button @click="updateName">修改姓名</button>
<button @click="updateStu">修改學(xué)生</button>
</div>
let {ref,reactive} = Vue
Vue.createApp({
setup() {
let name = ref('張三')
let updateName = ()=>{
name.value = '張杰'
}
/* let stu = ref({
name:'李四',
age:20
})
let updateStu = ()=>{
// 注意:修改ref對(duì)象的值,每次都要先點(diǎn)value
stu.value.name = '李明'
stu.value.age = 30
} */
// reactive組合式API方法,根據(jù)源對(duì)象返回一個(gè)代理對(duì)象(Proxy對(duì)象)
let stu = reactive({
name:'李四',
age:20
})
let updateStu = ()=>{
// Proxy對(duì)象,不需要先點(diǎn)value
stu.name = '李明'
stu.age = 30
}
return {
name,
updateName,
stu,
updateStu
}
}
}).mount('#app')