一、初始Vue3
1、導(dǎo)入Vue3
<script src="https://unpkg.com/vue@next"></script>
2秕脓、創(chuàng)建vue實例
Vue3和Vue2創(chuàng)建vue實例的區(qū)別:
① 在Vue3里面Vue是一個對象,通過該對象的createApp()方法,創(chuàng)建一個Vue實例悯姊。在Vue2里面的Vue是一個構(gòu)造函數(shù),通過該構(gòu)造函數(shù)創(chuàng)建一個Vue實例贩毕。
② Vue3中取消了el選項悯许,只能通過mount方法指定掛載的容器,不用通過el選項指定辉阶。Vue2可以通過el選項指定一個掛載的容器先壕,也可以通過$mount()方法指定掛載的容器。
③ Vue3中谆甜,無論是組件還是vue實例垃僚,data選項都必須是一個方法,由方法返回對象规辱。而在Vue2中谆棺,data選項可以是對象,也可以是方法返回一個對象罕袋。
<div id="app">
<div>
<p>name:{{name}}</p>
<p>age:{{age}}</p>
</div>
</div>
// vue2創(chuàng)建一個vue實例
// 在vue2里面的Vue是一個構(gòu)造函數(shù)改淑,通過該構(gòu)造函數(shù)創(chuàng)建一個Vue實例
// vue2可以通過el選項指定一個掛載的容器,也可以通過$mount()方法指定掛載的容器
/* new Vue({
// el:'#app',
// 在vue2中浴讯,data選項可以是對象朵夏,也可以是方法返回一個對象
data:{
name:'Vue2',
age:'6'
}
}).$mount('#app') */
// vue3創(chuàng)建一個vue實例
// 在vue3里面Vue是一個對象,通過該對象的createApp()方法榆纽,創(chuàng)建一個Vue實例
Vue.createApp({
// 注意:這個配置對象里面除了不能寫el選項仰猖,之前怎么寫捏肢,現(xiàn)在還可以怎么寫
// 注意:vue3中,無論是組件和是vue實例饥侵,data選項都必須是一個方法鸵赫,由方法返回對象。
data(){
return {
name:'Vue3',
age:'2'
}
}
}).mount('#app')
二爆捞、Vue2和Vue3的響應(yīng)式
1奉瘤、Vue2的響應(yīng)式
在Vue2中,Vue實例在初始化的時候煮甥,會將obj對象身上的所有數(shù)據(jù)做響應(yīng)式處理盗温。所謂響應(yīng)式,指的是數(shù)據(jù)發(fā)生變化后成肘,頁面自動更新卖局。
Vue2的響應(yīng)式:不能直接給對象添加屬性、刪除對象的屬性双霍,不能直接操作數(shù)組的下標(biāo)砚偶。 但是,Vue2同時也提供了解決這些問題的方案:可以通過Vue實例的$set方法洒闸,更新指定的對象屬性或數(shù)組成員染坯;$delete方法,刪除指定對象的屬性或數(shù)組的成員丘逸。
<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:['魚翅','松茸','魚子醬','帝王蟹','熊掌']
},
methods: {
//添加性別
addSex(){
//后添加的屬性是非響應(yīng)式的
// this.student.sex='男'
//可以通過$forceUpdate()強制頁面更新一次
// this.$forceUpdate()
//推薦使用$set方法給對象添加新的屬性单鹿,確保新添加的屬性同樣具備響應(yīng)式
this.$set(this.student,'sex','男')
},
//刪除姓名
delName(){
// 直接使用delete方式刪除對象的屬性后,不具備響應(yīng)式
// delete this.student.name
//使用$delete方法深纲,刪除對象的屬性后仲锄,繼續(xù)具備響應(yīng)式
this.$delete(this.student,'name')
},
//添加食物
addFood(){
// 操作數(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)
}
},
})
2怀愧、Vue3的響應(yīng)式
Vue3修復(fù)了Vue2中響應(yīng)式的所有缺陷。在Vue3中余赢,直接給對象添加屬性掸驱、直接刪除對象的屬性、根據(jù)下標(biāo)操作數(shù)組没佑,都依然具備響應(yīng)式。
<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>
// vue3的響應(yīng)式
Vue.createApp({
data() {
return {
student:{
name:'張三',
age:20
},
foods:['魚翅','松茸','魚子醬','帝王蟹','熊掌']
}
},
methods: {
addSex(){
// 在Vue3中温赔,直接給對象添加屬性蛤奢,新的屬性依然具備響應(yīng)式
this.student.sex = '男'
},
delName(){
// 在Vue3中,直接刪除對象的屬性,依然具備響應(yīng)式
delete this.student.name
},
addFood(){
// 在Vue3中啤贩,根據(jù)下標(biāo)操作數(shù)組待秃,依然具備響應(yīng)式
this.foods[5] = '佛跳墻'
},
delFood(){
delete this.foods[3]
}
},
}).mount("#app")
三、Vue2和Vue3的響應(yīng)式原理
1痹屹、Vue2的響應(yīng)式原理
Vue2在實例化時章郁,會將data里面的所有數(shù)據(jù)采用 Object.defineProperty 進行處理,從而實現(xiàn)響應(yīng)式功能志衍。但是你之后往data里面添加的數(shù)據(jù)暖庄,由于沒有采用 Object.defineProperty 進行處理,所以不具備響應(yīng)式楼肪。$set()方法培廓,內(nèi)部就是對單個屬性重新采用 Object.defineProperty 進行處理,從而具備響應(yīng)式春叫。
<div>
<h2 id="name"></h2>
<h2 id="age"></h2>
<h2 id="sex"></h2>
</div>
// vue2的響應(yīng)式原理
//源對象
let obj = {
name:'張三',
age:20,
sex:'男'
}
document.querySelector('#name').innerHTML = obj.name
document.querySelector('#age').innerHTML = obj.age
document.querySelector('#sex').innerHTML = obj.sex
//定義一個obj2對象肩钠,作為obj的代理對象
let obj2 = {}
//通過Object.defineProperty方法,給obj2添加屬性
Object.defineProperty(obj2,'name',{
//讀取屬性的值暂殖,調(diào)用get方法
get(){
return obj.name
},
//設(shè)置屬性的值价匠,調(diào)用set方法
set(val){
obj.name = val
document.querySelector('#name').innerHTML = obj.name
}
})
Object.defineProperty(obj2,'age',{
//讀取屬性的值,調(diào)用get方法
get(){
return obj.age
},
//設(shè)置屬性的值呛每,調(diào)用set方法
set(val){
obj.age = val
document.querySelector('#age').innerHTML = obj.age
}
})
Object.defineProperty(obj2,'sex',{
//讀取屬性的值踩窖,調(diào)用get方法
get(){
return obj.sex
},
//設(shè)置屬性的值,調(diào)用set方法
set(val){
obj.sex = val
document.querySelector('#sex').innerHTML = obj.sex
}
})
2莉给、Vue3的響應(yīng)式原理
通過new Proxy(源對象,{...})的方式毙石,創(chuàng)建代理對象。通過Proxy(代理) 攔截對象中任意屬性的變化颓遏,包括:屬性值的讀寫徐矩、屬性的添加、屬性的刪除等叁幢。
通過Reflect(反射): 對源對象的屬性進行操作滤灯。對于vue3而言,底層使用reflect會比較健壯曼玩,不容易出現(xiàn)有個錯誤就導(dǎo)致程序阻塞鳞骤。
vue3 使用proxy代理對象和Reflect反射對象組合實現(xiàn)響應(yīng)式數(shù)據(jù),在性能方面黍判,比vue2更加有優(yōu)勢豫尽。
// new Proxy()創(chuàng)建一個代理對象
let obj4 = new Proxy(obj3,{
// 讀取屬性,參數(shù)分別是:源對象顷帖,屬性名
get(target,property){
// 直接返回源對象身上的指定的屬性值
// return target[property]
// 通過反射對象美旧,從指定的對象身上反射出指定的屬性值
return Reflect.get(target,property)
},
// 設(shè)置屬性渤滞,參數(shù)分別是:源對象,屬性名榴嗅,屬性值
set(target,property,value){
// target[property] = value
Reflect.set(target,property,value)
document.querySelector(`#${property}2`).innerHTML = Reflect.get(target,property)
},
// 刪除屬性妄呕,參數(shù)分別是:源對象,屬性名
deleteProperty(target,property){
// return delete target[property]
document.querySelector(`#${property}2`).remove()
return Reflect.deleteProperty(target,property)
}
})
四嗽测、Vue3新推出的組合式API
Vue3中绪励,無論是Vue實例,還是組件唠粥,data選項都必須是一個方法疏魏。我們之前習(xí)慣將所有的數(shù)據(jù)放在data選項中定義,所有的方法放在methods選項中定義厅贪,所有的計算屬性放在computed選項中定義蠢护,所有的偵聽器放在watch選項中定義。這樣就會導(dǎo)致一個業(yè)務(wù)的代碼會拆分到多個結(jié)構(gòu)中去寫养涮,如果一個頁面中要操作很多個業(yè)務(wù)葵硕,代碼后期維護成本會很高。所以贯吓,Vue3引入了組合式API懈凹,簡化之前繁瑣的過程,將相同業(yè)務(wù)的代碼靠在一起寫悄谐。
組合式API(Composition API):就是Vue推出的一些新的方法介评,這個方法在setup中使用。組合式api的作用是將原來分散開來定義的數(shù)據(jù)爬舰、方法们陆、計算屬性、監(jiān)聽器等情屹,組合起來定義一個完整的業(yè)務(wù)坪仇。
ref對象:在setup中,直接定義的數(shù)據(jù)是不具備響應(yīng)式的垃你。如果要使數(shù)據(jù)具備響應(yīng)式椅文,需要使用ref組合式API對數(shù)據(jù)進行包裝,包裝后返回的是ref對象惜颇。ref對象的value屬性保存的是值皆刺。
<div id="app">
<div>
<p>{{carName}}--{{carPrice}}</p>
<button @click="updateCar">修改汽車信息</button>
</div>
<div>
<p>{{planeName}}--{{planePrice}}</p>
<button @click="updatePlane">修改飛機信息</button>
</div>
</div>
// ref用于定義響應(yīng)式數(shù)據(jù)
let {ref} = Vue
Vue.createApp({
// setup方法是所有組合式API的入口,所有的組合式api都要在setup里面使用
setup() {
//定義汽車相關(guān)數(shù)據(jù)
// 使用ref()方法凌摄,定義一個響應(yīng)式對象
let carName=ref('保時捷')
let carPrice=ref('100W')
//定義汽車相關(guān)方法
function updateCar(){
//修改對象的值羡蛾,要通過value屬性
carName.value = '特斯拉'
carPrice.value = '80W'
}
//定義飛機相關(guān)數(shù)據(jù)
let planeName=ref('波音747')
let planePrice=ref('10Y')
//定義飛機相關(guān)方法
function updatePlane(){
planeName.value = 'B52轟炸機',
planePrice.value = '30Y'
}
return{
//返回汽車相關(guān)數(shù)據(jù)
carName,
carPrice,
updateCar,
//返回飛機相關(guān)數(shù)據(jù)
planeName,
planePrice,
updatePlane
}
},
// vue2中只能這樣寫代碼,vue3也可以這樣寫
/* data() {
return {
carName:'保時捷',
carPrice:'100W',
planeName:'波音747',
planePrice:'10Y'
}
},
methods: {
updateCar(){
this.carName = '特斯拉'
this.carPrice = '80W'
},
updatePlane(){
this.planeName = 'B52轟炸機',
this.planePrice = '30Y'
}
}, */
}).mount('#app')
五锨亏、ref和reactive
ref 和 reactive 都是用于定義響應(yīng)式數(shù)據(jù)林说。通常情況下煎殷,基本類型的數(shù)據(jù),選擇用ref定義腿箩;引用類型的數(shù)據(jù),選擇用reactive定義劣摇。
ref方法:返回的是ref對象珠移,ref對象的value屬性是一個代理對象(Proxy)。使用ref既可以定義基本類型數(shù)據(jù)末融,也可以定義引用類型數(shù)據(jù)钧惧。注意:修改ref對象的值,必須要先.value再.具體的屬性勾习。
reactive方法:直接返回一個代理對象(Proxy)浓瞪。reactive只能定義引用類型數(shù)據(jù)。
<div id="app">
<ul>
<li>姓名:{{name}}</li>
<li><button @click="updateName">修改姓名</button></li>
</ul>
<ul>
<li>車名:{{car.name}}</li>
<li>車價:{{car.price}}</li>
<li><button @click="updateCar">修改汽車</button></li>
</ul>
<ul>
<li>機名:{{plane.name}}</li>
<li>機價:{{plane.price}}</li>
<li><button @click="updatePlane">修改汽車</button></li>
</ul>
</div>
// ref 和 reactive 用于定義響應(yīng)式數(shù)據(jù)
let {ref,reactive} = Vue
Vue.createApp({
// 所有的組合式api巧婶,要在setup方法里面使用
setup() {
// 使用ref定義基本類型數(shù)據(jù)
let name = ref('張三')
let updateName = ()=>{
// 修改值時乾颁,必須要點value
name.value = '李四'
}
// 使用ref定義引用類型數(shù)據(jù)
let car = ref({
name:'奔馳',
price:30
})
let updateCar = ()=>{
// 修改值時,必須要點value
car.value.name = '奧迪'
car.value.price = 40
}
// 使用reactive定義引用類型數(shù)據(jù)
// 注意:reactive只能定義引用類型數(shù)據(jù)
let plane = reactive({
name:'長城',
price:300
})
let updatePlane = ()=>{
// Proxy對象艺栈,不需要先點value
plane.name = '東方'
plane.price = 400
}
//setup方法英岭,返回出去的對象里面的成員,可以在模板中使用
return{
name,
updateName,
car,
updateCar,
plane,
updatePlane
}
}
}).mount('#app')