Vue3
Vue3簡介
1.Vue3簡介
? ? .2020年9月18日,Vue.js發(fā)布3.0版本,代號: One Piece(海賊王)。耗時2年多、2600+次提交、30+個RFC、600+次PR、99位貢獻者
? ? . github上的tags地址: https://github.com/vuejs/vue-next/releases/tag/v3.0.0
2.Vue3帶來了什么
? ? .性能的提升
? ? .打包大小減少41%
? ? .初次渲染快55%,更新渲染快133%。內(nèi)存減少54%
3.擁抱TypeScript
? ? . vue3可以更好的支持TypeScript
4.新的特性
? ? 1.Composition API(組合API)
? ? ? ? . setup配置
? ? ? ? . ref與reactive
? ? ? ? . watch與watchEffect
? ? ? ? . provide與inject
? ? ? ? . ...
? ? 2.新的內(nèi)置組件
? ? ? ? . Fragment
? ? ? ? . Teleport
? ? ? ? . suspense
? ? 3.其他改變
? ? ? ? . 新的生命周期鉤子
? ? ? ? . data選項應(yīng)始終被聲明為一個函數(shù)
? ? ? ? . 移除keyCode支持作為v-on的修飾符
? ? ? ? . ...
使用vue-cli創(chuàng)建工程
1.使用vue-cli 創(chuàng)建
官方文檔: https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create
## 查看@vue/cli版本,確保@vue/cli版本在4.5.o以上
vue --version
## 安裝或者升級你的@vue/cli
npm install -g @vue/cli
## 創(chuàng)建
vue create vue_test
## 啟動
cd vue_test
npm run serve
使用vite創(chuàng)建工程
⒉使用vite 創(chuàng)建
官方文檔: https://v3.cn.vuejs.org/guide/installation.html#vite
vite官網(wǎng): https://vitejs.cn
.什么是vite? ——相對于webpack,新一代前端構(gòu)建工具。
.優(yōu)勢如下:
? ? 。開發(fā)環(huán)境中,無需打包操作,可快速的冷啟動。
? ? 。輕量快速的熱重載(HMR)。
? ? 。真正的按需編譯,不再等待整個應(yīng)用編譯完成。
##創(chuàng)建工程
npm init vite-app <project-name>
npm init vite-app vue3_test_vite
##進入工程目錄
cd <project-name>
##安裝依賴
npm install
##運行
npm run dev
分析Vue3的工程結(jié)構(gòu)
分析vue-cli所創(chuàng)建出的vue3工程
關(guān)閉語法檢查:vue3自帶的vue.config.js中的內(nèi)容直接替換為vue2的寫法也行,如下:
module.exports = {
? lintOnSave:false,//關(guān)閉語法檢查
}
App.vue中vue3中template標(biāo)簽中可以沒有根標(biāo)簽括起來
main.js
// 引入的不再是Vue構(gòu)造函數(shù)了,引入的是一個名為createApp的工廠函數(shù)
import { createApp } from 'vue'
//引入App組件
import App from './App.vue'
// createApp(App).mount('#app')
// 創(chuàng)建應(yīng)用實例對象--app(類似于之前Vue2中的vm,但app比vm更“輕”)
const app = createApp(App)
app.mount('#app')
setTimeout(() => {
app.unmount('#app')
}, 1000);
/* 類似vue2中的如下方式的創(chuàng)建vm掛載Vue到app掛載點,但main.js中不兼容vue2的寫法,把vue2的main.js拿來替換不能用,因為無法引入Vue
const vm = new VueElement({
render:h =>h(App)
})
vm.$mount('app') */
vue3的開發(fā)者工具
Vue3開發(fā)者工具,可在極簡插件中下載,選那個vue圖標(biāo)右下角帶圓角矩形的
極簡插件_Chrome擴展插件商店_優(yōu)質(zhì)crx應(yīng)用下載
別使用谷歌瀏覽器下載,會自動刪除下載的文件
初識setup
是常用Composition API? ?組合式API
拉開序幕的setup
? ? 1.理解: vue3.0中一個新的配置項,值為一個函數(shù)。
? ? 2.setup是所有Composition API(組合API)“表演的舞臺”。
? ? 3.組件中所用到的:數(shù)據(jù)、方法等等,均要配置在setup中。
? ? 4.setup函數(shù)的兩種返回值:
? ? ? ? 1.若返回一個對象,則對象中的屬性、方法,在模板中均可以直接使用。(重點關(guān)注! )
? ? ? ? 2.若返回一個渲染函數(shù):則可以自定義渲染內(nèi)容。(了解)
? ? 5.注意點:
? ? ? ? 1.盡量不要與Vue2.x配置混用
? ? ? ? ? ? . Vue2.x配置(data、methos、computed...)中可以訪問到setup中的屬性、方法。
? ? ? ? ? ? ·但在setup中不能訪問到Vue2.x配置(data、methos、computed...)。
? ? ? ? ? ? ·如果有重名, setup優(yōu)先。
? ? ? ? 2.setup不能是一個async函數(shù),因為返回值不再是return的對象,而是promise,模板看不到return對象中的屬性。(后期也可以返回一個Promise實例,但需要Suspense和異步組件的配合)
App.vue
<template>
<h1>一個人的信息</h1>
<h2>姓名:{{name}}</h2>
<h2>年齡:{{age}}</h2>
<h2>性別:{{sex}}</h2>
<h2>vue2和vue3的數(shù)據(jù)a沖突的時候的以vue3的setup為主{{a}}</h2>
<button @click="sayHello">說話(Vue3配置的sayHello方法)</button><br/><br/>
<button @click="sayWelcome">說話(Vue2配置的sayWelcome方法)</button><br/><br/>
<button @click="test1">測試一下在vue2中讀取vue3中的數(shù)據(jù)和方法</button><br/><br/>
<button @click="test2">測試一下在vue3中讀取vue2中的數(shù)據(jù)和方法</button>
</template>
<script>
import {h} from "vue"
export default {
name: 'App',
data() {
return {
sex:"男" ,
a:100
}
},
methods:{
sayWelcome(){
alert('歡迎來到尚硅谷學(xué)習(xí)');
},
test1(){//在vue2中讀取vue3是可以的
console.log(this.sex)
console.log(this.name)
console.log(this.age)
console.log(this.sayHello)
}
},
setup(){ // setup是所有Composition API(組合API)“表演的舞臺”。組件中所用到的:數(shù)據(jù)、方法等等,均要配置在setup中。
//data直接往這里面寫 ,變量用let,常量用const
let name = "張三";
let age = 18;
let a = 200;
//方法也直接寫function
function sayHello(){
alert(`我叫${name},我${age}歲了,你好啊`)//使用``加${}傳數(shù)據(jù)
};
function test2(){//在vue3中讀取vue2,讀取不到
console.log(this.name)
console.log(this.age)
console.log(this.sex) // undefined
console.log(this.sayWelcome) // undefined
}
//setup的return若返回一個對象,則對象中的屬性、方法,在模板中均可以直接使用。(重點關(guān)注! )
return{
name,
age,
sayHello,
test2,
a,
}
// 若返回一個渲染函數(shù):則可以自定義渲染內(nèi)容。(了解) 會覆蓋掉template中的所有內(nèi)容
// return()=>{ return h("h1","尚硅谷")}
/* return()=> h("h1","尚硅谷") */
}
}
</script>
ref函數(shù)處理基本數(shù)據(jù)類型
?除了html中標(biāo)簽中ref屬性,vue3多了一個ref函數(shù)。
ref加工后的數(shù)據(jù)就是一個對象RefImpl,reference implement引用實現(xiàn),引用實現(xiàn)的對象簡稱引用對象。里面有個value屬性,為實現(xiàn)數(shù)據(jù)響應(yīng)式的,通過還是get和set做的響應(yīng)式在__proto__原型對象中的。像vue2中的_data中的東西放在了vm上。
App.vue
<template>
<h1>一個人的信息</h1>
<!-- ref引用對象,在模板中不用name.value,模板自動會解析加上.value -->
<h2>姓名:{{name}}</h2>
<h2>年齡:{{age}}</h2>
<button @click="changeInfo">修改人的信息</button>
</template>
<script>
import {h} from "vue"
import {ref} from 'vue'
export default {
name: 'App',
setup(){
/* let name = "張三";
let age = 18; */
// 把普通數(shù)據(jù)變成一個響應(yīng)式的數(shù)據(jù)
let name = ref("張三")
let age = ref(18)
function changeInfo(){
/* name = '李四'
age = 48 */
// 由于name和age為ref函數(shù)得到的引用對象,通過get和set做的響應(yīng)式,所以我們要通過.vaue
name.value = '李四'
age.value = 48
}
return{
name,
age,
changeInfo
}
}
}
</script>
ref函數(shù)處理對象類型的數(shù)據(jù)
對于ref函數(shù)處理的對象類型,沒有直接變?yōu)镽efIMpl應(yīng)用對象,而是變?yōu)榱薖roxy代理對象。是內(nèi)部求助的reactive函數(shù),這個函數(shù)封裝了proxy。
App.vue
<template>
<h1>一個人的信息</h1>
<!-- ref引用對象,在模板中不用name.value,模板自動會解析加上.value -->
<h2>姓名:{{name}}</h2>
<h2>年齡:{{age}}</h2>
<h3>手機品牌:{{phone.brand}}</h3>
<h3>手機顏色:{{phone.color}}</h3>
<button @click="changeInfo">修改人的信息</button>
</template>
<script>
import {h} from "vue"
import {ref} from 'vue'
export default {
name: 'App',
setup(){
/* let name = "張三";
let age = 18; */
// 把普通數(shù)據(jù)變成一個響應(yīng)式的數(shù)據(jù)
let name = ref("張三")
let age = ref(18)
// ref函數(shù)處理對象類型
let phone = ref({
brand:"redmi",
color:"red"
})
function changeInfo(){
/* name = '李四'
age = 48 */
// 由于name和age為ref函數(shù)得到的引用對象,通過get和set做的響應(yīng)式,所以我們要通過.vaue
name.value = '李四'
age.value = 48
phone.value.brand="xiaomi" //ref函數(shù)對象類型的屬性修改數(shù)據(jù)
phone.value.color="green"
}
return{
name,
age,
changeInfo,
phone
}
}
}
</script>
reactive函數(shù)
reactive函數(shù)
。作用:定義一個對象類型的響應(yīng)式數(shù)據(jù)(基本類型不要用它,要用ref函數(shù))
。語法: const 代理對象= reactive(源對象)接收一個對象(或數(shù)組),返回一個代理對象〈Proxy的實例對象,簡稱proxy對象)
。 reactive定義的響應(yīng)式數(shù)據(jù)是"深層次的”。
。內(nèi)部基于ES6的Proxy實現(xiàn),通過代理對象操作源對象內(nèi)部數(shù)據(jù)進行操作。
App.vue
<template>
<h1>一個人的信息</h1>
<!-- ref引用對象,在模板中不用name.value,模板自動會解析加上.value -->
<h2>姓名:{{name}}</h2>
<h2>年齡:{{age}}</h2>
<h3>手機品牌:{{phone.brand}}</h3>
<h3>手機顏色:{{phone.color}}</h3>
<h3>hobby:{{hobby}}</h3>
<h3>測試的數(shù)據(jù)d:{{a.b.c.d}}</h3>
<button @click="changeInfo">修改數(shù)據(jù)</button>
</template>
<script>
import {h} from "vue"
import {ref,reactive} from 'vue'
export default {
name: 'App',
setup(){
// 把普通數(shù)據(jù)變成一個響應(yīng)式的數(shù)據(jù)
let name = ref("張三")
let age = ref(18)
// reactive將對象變?yōu)轫憫?yīng)式的
let phone = reactive({
brand:"redmi",
color:"red"
})
let a = reactive({
b:{
c:{
d:666
}
}
})
let hobby = reactive(['看喜羊羊','看灰太狼','看光頭強'])
function changeInfo(){
name.value = '李四'
age.value = 48
phone.brand="xiaomi" //active函數(shù)對象類型的屬性修改數(shù)據(jù)
phone.color="green"
a.b.c.d=999
hobby[0] = '看熊出沒'
}
return{
name,
age,
changeInfo,
phone,
a,
hobby
}
}
}
</script>
Vue2的響應(yīng)式原理的回顧
vue2.x的響應(yīng)式
. 實現(xiàn)原理:
? ? 。對象類型:通過object.defineProperty()對屬性的讀取、修改進行攔截(數(shù)據(jù)劫持)。
? ? 。數(shù)組類型:通過重寫更新數(shù)組的一系列方法來實現(xiàn)攔截。(對數(shù)組的變更方法進行了包裹)。
? ? ? ? object.defineProperty(data, 'count ', {
? ? ? ? ? ? get(){},
? ? ? ? ? ? set(){}
? ? ? ? })
. 存在問題:
? ? 。新增屬性、刪除屬性,界面不會更新。
? ? 。直接通過下標(biāo)修改數(shù)組,界面不會自動更新。
Vue2項目中的App.vue,來看vue2的響應(yīng)式存在的問題
<template>
<div>
<h1>我是Vue2的寫法</h1>
<h2 v-show="person.name">姓名:{{person.name}}</h2>
<h2>年齡:{{person.age}}</h2>
<h2>愛好:{{person.hobby}}</h2>
<h2 v-show="person.sex">性別:{{person.sex}}</h2>
<button @click="addSex">添加一個sex屬性</button>
<button @click="deleteName">添加一個name屬性</button>
<button @click="updateHobby">修改第一個愛好的名字</button>
</div>
</template>
<script>
import Vue from 'vue'
export default {
name:"App",
data() {
return {
person:{
name:'張三',
age:18,
hobby:["看喜羊羊",'看灰太狼','看光頭強']
}
}
},
methods:{
addSex(){
/* console.log(this.person.sex) //undefined
this.person.sex='女'
console.log(this.person.sex) //'女' 確實新增了sex屬性了,但頁面不更新,defineProperties監(jiān)測不到 */
this.$set(this.person,"sex",'女') //可通過這種和下面這種方式加上這個屬性
// Vue.set(this.person,"sex",'女')
},
deleteName(){
/* console.log(this.person.name) // '張三'
delete this.person.name
console.log(this.person.name)// undefined 確實刪除了name,但頁面中監(jiān)測不到,頁面還是不變 */
//this.$delete(this.person,"name")
Vue.delete(this.person,"name")
},
updateHobby(){
// this.person.hobby[0] = '看大大怪' // 這樣修改確實能修改,但頁面監(jiān)測不到,頁面上的數(shù)據(jù)不變
//this.$set(this.person.hobby,0,"看大大怪")
// 或使用數(shù)組的方法,如push shift splice等,這些方法為vue對普通方法的封裝,可以監(jiān)測到
this.person.hobby.splice(0,1,'看小小怪')
}
}
}
</script>
Vue3的響應(yīng)式原理Proxy
Vue3中不存在vue2中的添加或?qū)傩曰蛐薷臄?shù)組元素的不是響應(yīng)式的頁面不變的問題
Vue3中的App.vue
<template>
<h1>一個人的信息</h1>
<!-- ref引用對象,在模板中不用name.value,模板自動會解析加上.value -->
<h2 v-show="person.name">姓名:{{person.name}}</h2>
<h2>年齡:{{person.age}}</h2>
<h2 v-show="person.sex">性別:{{person.sex}}</h2>
<h3>手機品牌:{{person.phone.brand}}</h3>
<h3>手機顏色:{{person.phone.color}}</h3>
<h3>hobby:{{person.hobby}}</h3>
<button @click="changeInfo">修改數(shù)據(jù)</button>
<button @click="addSex">添加一個sex屬性</button>
<button @click="deleteName">刪除一個name屬性</button>
</template>
<script>
import {ref,reactive} from 'vue'
export default {
name: 'App',
setup(){
let person = reactive({
name:"張三",
age:18,
phone:{
brand:"redmi",
color:"red"
},
hobby:['看喜羊羊','看灰太狼','看光頭強']
})
function changeInfo(){
person.name = '李四'
person.age = 48
person.phone.brand="xiaomi" //active函數(shù)對象類型的屬性修改數(shù)據(jù)
person.phone.color="green"
person.hobby[0] = '看熊出沒' //直接通過數(shù)組下標(biāo)修改元素也是可以響應(yīng)式的,頁面也會發(fā)生變化
}
function addSex(){
person.sex = '男' // 添加一個不存在的屬性,頁面可以發(fā)生變化,做了響應(yīng)式,reactive的原因。
}
function deleteName(){
delete person.name
}
return{
person,
changeInfo,
addSex,
deleteName
}
}
}
</script>
Object.defineProperties、new Proxy()模擬vue2和vue3的響應(yīng)式原理
響應(yīng)式原理.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
//源數(shù)據(jù)
let person = {
name : "張三",
age : 18
}
//模擬vue2中實現(xiàn)響應(yīng)式
//#region
/* let p = {}
Object.defineProperties(p,"name",{
configurable:true, //聲明為可配置的,就允許被刪除了
get(){ //有人讀取的時候調(diào)用
return person.name
},
set(value){
console.log("有人修改了name屬性,發(fā)現(xiàn)了,要去更新界面!")
person.name = value
}
})
Object.defineProperties(p,"age",{
get(){ //有人讀取的時候調(diào)用
return person.age
},
set(value){
console.log("有人修改了age屬性,發(fā)現(xiàn)了,要去更新界面!")
person.age = value
}
})
// 然后再f12控制臺中,person.name = '李四' person.sex = '男' delete person.name 之類操作查看結(jié)果 */
//#endregion
//模擬vue3中實現(xiàn)響應(yīng)式
const p = new Proxy(person,{ //就會進行代理,person中的屬性都會有g(shù)et和set了,下面代碼可以里面的api做響應(yīng)式
//有人讀取p的某個屬性式調(diào)用
get(target,propName){
console.log(`有人讀取了p身上的${propName}屬性`)
return target[propName]
},
// 有人修改了p的某個屬性,或給p追加某個屬性時調(diào)用
set(target,propName,value){
console.log(`有人修改了p身上的${propName}屬性,我要去更新頁面了!`)
target[propName] = value
},
//有人刪除p的某個屬性時調(diào)用
deleteProperty(target,propName){
console.log(`有人刪除了p身上的${propName}屬性,我要去更新頁面了!`)
return delete target[propName]
}
})
// f12中person.name='李四'等操作即可
</script>
</body>
</html>
Vue3響應(yīng)式原理Reflect
?Reflect反射的方式,容錯性更好,我們修改上面的Vue3的模擬中的操作
Vue3的響應(yīng)式.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
//源數(shù)據(jù)
let person = {
name : "張三",
age : 18
}
//模擬vue2中實現(xiàn)響應(yīng)式
//#region
/* let p = {}
Object.defineProperties(p,"name",{
configurable:true, //聲明為可配置的,就允許被刪除了
get(){ //有人讀取的時候調(diào)用
return person.name
},
set(value){
console.log("有人修改了name屬性,發(fā)現(xiàn)了,要去更新界面!")
person.name = value
}
})
Object.defineProperties(p,"age",{
get(){ //有人讀取的時候調(diào)用
return person.age
},
set(value){
console.log("有人修改了age屬性,發(fā)現(xiàn)了,要去更新界面!")
person.age = value
}
})
// 然后再f12控制臺中,person.name = '李四' person.sex = '男' delete person.name 之類操作查看結(jié)果 */
//#endregion
//模擬vue3中實現(xiàn)響應(yīng)式
//#region
const p = new Proxy(person,{ //就會進行代理,person中的屬性都會有g(shù)et和set了,下面代碼可以里面的api做響應(yīng)式
//有人讀取p的某個屬性式調(diào)用
get(target,propName){
console.log(`有人讀取了p身上的${propName}屬性`)
// return target[propName]
return Reflect.get(target,propName)
},
// 有人修改了p的某個屬性,或給p追加某個屬性時調(diào)用
set(target,propName,value){
console.log(`有人修改了p身上的${propName}屬性,我要去更新頁面了!`)
// target[propName] = value
Reflect.set(target,propName,value)
},
//有人刪除p的某個屬性時調(diào)用
deleteProperty(target,propName){
console.log(`有人刪除了p身上的${propName}屬性,我要去更新頁面了!`)
// return delete target[propName]
return Reflect.deleteProperty(target,propName)
}
})
//#endregion
//#region
/* //Reflect.definedProperty反射對象去操作
let obj = {a:1,b:2}
//除了obj.a = 666 還能Reflect.set(obj,'a',666)的方式修改a Reflect.deleteProperty(obj,'a')的方式刪除a
//通過Object.defineProperty去操作
Object.defineProperty(obj,'c',{
get(){
return 3
}
})
Object.defineProperty(obj,'c',{
get(){
return 4
}
})
//如上Object.defineProperty對obj定義追加了c重復(fù)了,就會報錯,這樣容錯性不太好,需要try-catch包裹起來
//但使用如下Reflect.definedProperty反射對象去操作后,重復(fù)后就不會報錯
const x1 = Reflect.defineProperty(obj,'c',{
get(){
return 3
}
})
const x2 = Reflect.defineProperty(obj,'c',{
get(){
return 4
}
})
//可通過if-else判斷
if(x2){
console.log("某某操作成功了")
}else{
console.log("某某操作失敗了")
} */
//#endregion
</script>
</body>
</html>
實現(xiàn)原理:
? ? 。通過Proxy(代理)︰攔截對象中任意屬性的變化,包括:屬性值的讀寫、屬性的添加、屬性的刪除等。
? ? 。通過Reflect(反射)︰對源對象的屬性進行操作。
? ? 。MDN文檔中描述的Proxy與Reflect:
? ? ? ? .Proxy: https:/ldeveloper.mozilla.org/zh-CN/docs/WeblJavaScript/Reference/Global_Objects/Proxy
? ? ? ? .Reflect: https:/ldeveloper.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
reactive和ref的對比
. 從定義數(shù)據(jù)角度對比:
? ? 。ref用來定義:基本類型數(shù)據(jù)。
? ? 。 reactive用來定義:對象(或數(shù)組)類型數(shù)據(jù)。
? ? 。備注: ref也可以用來定義對象(或數(shù)組)類型數(shù)據(jù),它內(nèi)部會自動通過reactive轉(zhuǎn)為代理對象。
. 從原理角度對比:
? ? 。ref通過 object.definePropertv()的get與set來實現(xiàn)響應(yīng)式(數(shù)據(jù)劫持)。
? ? 。reactive通過使用Proxy來實現(xiàn)響應(yīng)式(數(shù)據(jù)劫持)﹐并通過Reflect操作源對象內(nèi)部的數(shù)據(jù)。
. 從使用角度對比:
? ? 。ref定義的數(shù)據(jù):操作數(shù)據(jù)需要.value,讀取數(shù)據(jù)時模板中直接讀取不需要.value 。
? ? 。reactive定義的數(shù)據(jù):操作數(shù)據(jù)與讀取數(shù)據(jù):均不需要.value。
setup的兩個注意點
?setup的兩個注意點
. setup執(zhí)行的時機
? ? 。在beforeCreate之前執(zhí)行一次,this是undefined。
. setup的參數(shù)
? ? 。props:值為對象,包含:組件外部傳遞過來,且組件內(nèi)部聲明接收了的屬性。
? ? 。context: 上下文對象
? ? ? ? ` attrs:值為對象,包含:組件外部傳遞過來,但沒有在props配置中聲明的屬性,相當(dāng)于[this .$attrs 。
? ? ? ? ` slots: 收到的插槽內(nèi)容,相當(dāng)于this.$slots 。
? ? ? ? ` emit:分發(fā)自定義事件的函數(shù),相當(dāng)于this.$emit 。
vue2中的props傳參,父組件寫的參數(shù),子組件props:[]沒聲明的參數(shù),就會被$atts撿漏。vue2中slots父組件中寫的額插槽的內(nèi)容,會放在$slot中作為虛擬節(jié)點。
set第一個參數(shù)需要props聲明,第二個參數(shù)用到的emit需要在上面聲明給emits
vue3中使用具名插槽盡量用v-slot
App.vue
<template>
<Demo msg='你好' school='尚硅谷' @hello='showHelloMsg'>
<span>尚硅谷</span>
</Demo>
</template>
<script>
import Demo from "./components/Demo.vue"
export default {
name: 'App',
components:{
Demo
},
setup(){
function showHelloMsg(value){
alert(`你好啊,你觸發(fā)了hello事件,我收到的參數(shù)是:${value}`)
}
return {
showHelloMsg
}
}
}
</script>
Demo.vue
<template>
<h1>一個人的信息</h1>
<h2>姓名:{{person.name}}</h2>
<h2>年齡:{{person.age}}</h2>
<button @click="test">測試觸發(fā)一下Demo組件的Hello事件</button>
</template>
<script>
import { reactive } from 'vue'
export default {
name:'Demo',
props:['msg','school'],
emits:['hello'],
setup(props,context) {
// console.log("-----setup-----",props)
// console.log("---setup---",context)
// console.log("---setup---",context.attrs)//相當(dāng)于vue2中的$attrs
// console.log("---setup---",context.emit) // 觸發(fā)自定義事件的
console.log("---setup---",context.slots) //插槽
//數(shù)據(jù)
let person = reactive({
name:'張三',
age:18
})
function test(){
context.emit('hello',666)
}
return {
person,
test
}
}
}
</script>
<style>
</style>
Vue3computed計算屬性
App.vue
<template>
<Demo/>
</template>
<script>
import Demo from "./components/Demo.vue"
export default {
name: 'App',
components:{
Demo
},
}
</script>
Demo.vue
<template>
<h1>一個人的信息</h1>
姓:<input type="text" v-model="person.firstName">
<br/>
名:<input type="text" v-model="person.lastName">
<br/>
<!-- <span>全名:{{fullName}}</span> -->
<span>全名:{{person.fullName}}</span>
<br/>
全名:<input type="text" v-model="person.fullName">
</template>
<script>
import { reactive ,computed} from 'vue'
export default {
name:'Demo',
/* computed:{ // 在vue3中是可以像vue2中寫計算屬性的。但不推薦。
fullName(){
return this.person.firstName + '-' + this.person.lastName
}
}, */
setup() {
let person = reactive({
firstName:'張',
lastName:"三"
})
// 計算屬性-簡寫(沒有考慮計算屬性被修改的情況)
/* let fullName = computed(()=>{
return person.firstName + "-" + person.lastName
}) */
/* person.fullName = computed(()=>{
return person.firstName + "-" + person.lastName
}) */
// 計算屬性的完整寫法(考慮讀和寫)
person.fullName = computed({
get(){
return person.firstName + '-' +person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName=nameArr[0],
person.lastName=nameArr[1]
}
})
return {
person
}
}
}
</script>
<style>
</style>
watch監(jiān)視ref定義的數(shù)據(jù)
Demo.vue
<template>
<h2>當(dāng)前求和為:{{sum}}</h2>
<button @click="sum++">點我+1</button>
<hr>
<h2>當(dāng)前的信息為:{{msg}}</h2>
<button @click="msg+='!'">修改信息</button>
</template>
<script>
import { ref, watch } from 'vue'
export default {
name:'Demo',
/* watch:{// vue3中也可使用vue2中的watch,不過不推薦
sum(newValue,oldValue){//簡寫的方式,詳細寫法見vue2中的watch
console.log('sum的值變化了',newValue,oldValue)
}
}, */
setup() {
let sum = ref(0)
let msg = ref('你好啊')
//情況1,監(jiān)視ref所定義的一個響應(yīng)式數(shù)據(jù)
/* watch(sum,(newValue,oldValue)=>{
console.log("sum變化了",newValue,oldValue)
}) */
//情況2,監(jiān)視ref所定義的多個響應(yīng)式數(shù)據(jù) 可寫多個watch在vue3中
/* watch(sum,(newValue,oldValue)=>{
console.log("sum變化了",newValue,oldValue)
})
watch(msg,(newValue,oldValue)=>{
console.log("msg變化了",newValue,oldValue)
}) */
/* watch([sum,msg],(newValue,oldValue)=>{ // newValue,oldValue分別為數(shù)組
console.log("sum或msg變化了",newValue,oldValue)
}) */
watch(sum,(newValue,oldValue)=>{
console.log("sum變化了",newValue,oldValue)
},{immediate:true})//可加第三個參數(shù)為配置 但deep:true在vue3中有小問題
return {
sum,
msg
}
}
}
</script>
<style>
</style>
App.vue中引用注冊并渲染這個Demo.vue
watch監(jiān)視reactive定義的數(shù)據(jù)
watch函數(shù)
? ? 。與Vue2.x中watch配置功能一致。
兩個小“坑":
? ? 。監(jiān)視reactive定義的響應(yīng)式數(shù)據(jù)時: oldValue無法正確獲取、強制開啟了深度監(jiān)視(deep配置失效)。
? ? 。監(jiān)視reactive定義的響應(yīng)式數(shù)據(jù)中某個屬性時: deep配置有效。
Demo.vue
<template>
<h2>姓名:{{person.name}}</h2>
<h2>年齡:{{person.age}}</h2>
<h2>薪水:{{person.job.j1.salary}}</h2>
<button @click="person.name+='~'">修改姓名</button>
<button @click="person.age++">增長年齡</button>
<button @click="person.job.j1.salary++">增長薪資</button>
</template>
<script>
import { reactive, ref, watch } from 'vue'
export default {
name:'Demo',
setup() {
let person = reactive({
name:'張三',
age:18,
job:{
j1:{
salary:20
}
}
})
//情況三:監(jiān)視reactive所定義的一個響應(yīng)式數(shù)據(jù)的全部屬性,注意,此處無法正確的獲取oldValue,這是vue3的小bug
watch(person,(newValue,oldValue)=>{ //vue3中默認就開啟了深度監(jiān)視了,所以salary修改也能顯示,且關(guān)不掉這個深度監(jiān)視
console.log('person發(fā)生變化了',newValue,oldValue);
})
//情況四:監(jiān)視reactive所定義的一個響應(yīng)式數(shù)據(jù)中的某個屬性
watch(()=>person.age,(newValue,oldValue)=>{ //vue3中默認就開啟了深度監(jiān)視了,所以salary修改也能顯示,且關(guān)不掉這個深度監(jiān)視
console.log('person的age發(fā)生變化了',newValue,oldValue);
})
//情況五:監(jiān)視reactive所定義的一個響應(yīng)式數(shù)據(jù)中的某些屬性
watch([()=>person.age,()=>person.name],(newValue,oldValue)=>{ //vue3中默認就開啟了深度監(jiān)視了,所以salary修改也能顯示,且關(guān)不掉這個深度監(jiān)視
console.log('person的age或name發(fā)生變化了',newValue,oldValue);
})
//特殊情況 ()=>person.job 如果改job下的子屬性,需要開啟深度監(jiān)視deep:true
watch(()=>person.job,(newValue,oldValue)=>{ //vue3中默認就開啟了深度監(jiān)視了,所以salary修改也能顯示,且關(guān)不掉這個深度監(jiān)視
console.log('person的job發(fā)生變化了',newValue,oldValue);
},{deep:true})
return {
person
}
}
}
</script>
<style>
</style>
App.vue中引用注冊并渲染這個Demo.vue
watch時的value問題
ref()對基本類型,.value就是基本類型,但ref對對象類型,.value的是proxy代理對象。
所以watch為ref普通屬性直接寫,但watch為ref的對象時,修改person里面的屬性,用person.value,還可在后面加上deep:true屬性
watchEffect函數(shù)
watchEffect函數(shù)
. watch的套路是:既要指明監(jiān)視的屬性,也要指明監(jiān)視的回調(diào)。
. watchEffect的套路是:不用指明監(jiān)視哪個屬性,監(jiān)視的回調(diào)中用到哪個屬性,那就監(jiān)視哪個屬性。
. watchEffect有點像computed:
? ? 。但computed注重的計算出來的值(回調(diào)函數(shù)的返回值),所以必須要寫返回值。
? ? 。而watchEffect更注重的是過程(回調(diào)函數(shù)的函數(shù)體),所以不用寫返回值。
Demo.vue
<template>
<h2>當(dāng)前求和為:{{sum}}</h2>
<button @click="sum++">點我+1</button>
<hr>
<h2>當(dāng)前的信息為:{{msg}}</h2>
<button @click="msg+='!'">修改信息</button>
<hr>
<h2>姓名:{{person.name}}</h2>
<h2>年齡:{{person.age}}</h2>
<h2>薪水:{{person.job.j1.salary}}</h2>
<button @click="person.name+='~'">修改姓名</button>
<button @click="person.age++">增長年齡</button>
<button @click="person.job.j1.salary++">增長薪資</button>
</template>
<script>
import { ref, reactive, watch, watchEffect } from 'vue'
export default {
name:'Demo',
setup() {
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'張三',
age:18,
job:{
j1:{
salary:20
}
}
})
/* watch(sum,(newValue,oldValue)=>{
console.log("sum變化了",newValue,oldValue)
},{immediate:true}) */
watchEffect(()=>{//里面用到了哪些屬性就監(jiān)視誰,而且還默認第一次就加載
const x1 = sum.value
const x2 = person.job.j1.salary
console.log("watchEffect所指定的回調(diào)執(zhí)行了");
})
return {
sum,
msg,
person
}
}
}
</script>
<style>
</style>
App.vue中引用注冊并渲染這個Demo.vue
Vue3生命周期鉤子
vue2生命周期鉤子前6個都一樣,最后beforeDestroy和destroyed與vue3的beforeUnmount和unmounted不一樣.vue2中即使沒寫el,也能執(zhí)行beforeCreate和created這兩步,但vue3得都有才能開始執(zhí)行beforeCreate。vue3也能通過配置項的形式引用生命周期鉤子,也可通過組合式api的形式去使用生命周期鉤子
Vue3.0也提供了Composition API形式的生命周期鉤子,與Vue2.x中鉤子對應(yīng)關(guān)系如下:
? ? 。beforeCreate ===> setup()
? ? 。created =======> setup()
? ? 。beforeMount =======> onBeforeMount
? ? 。mounted =======> onMounted
? ? 。beforeUpdate ===> onBeforeUpdate
? ? 。updated =======> onUpdated
? ? 。beforeUnmount ==> onBeforeUnmount
? ? 。unmounted =====> onUnmounted
Demo.vue
<template>
<h2>當(dāng)前求和為:{{sum}}</h2>
<button @click="sum++">點我+1</button>
</template>
<script>
import { ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
export default {
name:'Demo',
setup() {
let sum = ref(0)
//通過組合式api的形式去使用生命周期鉤子 優(yōu)先級setup的高于配置項的
onBeforeMount(()=>{
console.log("---onBeforeMount---");
})
onMounted(()=>{
console.log("---onMounted---");
})
onBeforeUpdate(()=>{
console.log("---onBeforeUpdate---");
})
onUpdated(()=>{
console.log("---onUpdated---");
})
onBeforeUnmount(()=>{
console.log("---onBeforeUnmount---");
})
onUnmounted(()=>{
console.log("---onUnmounted---");
})
return {
sum,
}
},
//#region
// 通過配置項的形式引用生命周期鉤子
/* beforeCreate() {
console.log('beforeCreate')
},
created() {
console.log('created')
},
beforeMount() {
console.log('beforeMount')
},
mounted() {
console.log('mounted')
},
beforeUpdate() {
console.log('beforeUpdate')
},
updated() {
console.log('updated')
},
beforeUnmount() {
console.log("beforeUnmount")
},
unmounted() {
console.log("unmounted")
}, */
//#endregion
}
</script>
<style>
</style>
App.vue
<template>
<button @click="isShowDemo = !isShowDemo">切換隱場顯示</button>
<Demo v-if="isShowDemo"/>
</template>
<script>
import { ref } from 'vue'
import Demo from "./components/Demo.vue"
export default {
name: 'App',
components:{
Demo
},
setup(){
let isShowDemo = ref(true)
return {
isShowDemo
}
},
}
</script>
自定義hook
.什么是hook?——本質(zhì)是一個函數(shù),把setup函數(shù)中使用的Composition APlI進行了封裝。
·類似于vue2.x中的mixin。
·自定義hook的優(yōu)勢:復(fù)用代碼,讓setup中的邏輯更清楚易懂。
本來在Demo.vue中寫的鼠標(biāo)打點的方法,但可能其他組件也需要使用改鼠標(biāo)打點的功能,要么將這段代碼復(fù)制粘貼,或者我們可以在src下建一個hooks文件夾,創(chuàng)建userPoint.js文件,然后在這里寫那些代碼,然后返回出去,然后Demo.vue中再引入
usePoint.js
import {reactive,onMounted, onBeforeUnmount} from 'vue'
// function savePoint(){
export default function (){
//實現(xiàn)鼠標(biāo)打點的數(shù)據(jù)
let point = reactive({
x:0,
y:0
})
//實現(xiàn)鼠標(biāo)打點的方法
function savePoint(event){
point.x = event.pageX
point.y = event.pageY
console.log(event.pageX,event.pageY);
}
//實現(xiàn)鼠標(biāo)打點相關(guān)的生命周期鉤子
onMounted(()=>{
window.addEventListener("click",savePoint)
})
onBeforeUnmount(()=>{
window.removeEventListener("click",savePoint)
})
return point
}
// export default savePoint
Demo.vue
<template>
<h2>當(dāng)前求和為:{{sum}}</h2>
<button @click="sum++">點我+1</button>
<hr>
<h2>當(dāng)前點擊時鼠標(biāo)的坐標(biāo)為:x:{{point.x}},y:{{point.y}}</h2>
</template>
<script>
import usePoint from '../hooks/usePoint'
import { ref,reactive,onMounted, onBeforeUnmount} from 'vue'
export default {
name:'Demo',
setup() {
let sum = ref(0)
/* let point = reactive({
x:0,
y:0
})
function savePoint(event){
point.x = event.pageX
point.y = event.pageY
console.log(event.pageX,event.pageY);
}
onMounted(()=>{
window.addEventListener("click",savePoint)
})
onBeforeUnmount(()=>{
window.removeEventListener("click",savePoint)
}) */
//然后復(fù)用
let point = usePoint()
return {
sum,point
}
},
}
</script>
<style>
</style>
toRef與toRefs
toRef:將一個不是ref的變成ref
. 作用:創(chuàng)建一個ref對象,其value值指向另一個對象中的某個屬性。
· 語法:const name = toRef(person , ' name ')
. 應(yīng)用:要將響應(yīng)式對象中的某個屬性單獨提供給外部使用時。
. 擴展: toRefs 與toRef功能一致,但可以批量創(chuàng)建多個ref對象,語法: toRefs(person)
Demo.vue
<template>
<h2>姓名:{{name}}</h2>
<h2>年齡:{{age}}</h2>
<h2>薪水:{{job.j1.salary}}</h2>
<button @click="name+='~'">修改姓名</button>
<button @click="person.age++">增長年齡</button>
<button @click="job.j1.salary++">增長薪資</button>
</template>
<script>
import {reactive, toRef, toRefs} from 'vue'
export default {
name:'Demo',
setup() {
let person = reactive({
name:'張三',
age:18,
job:{
j1:{
salary:20
}
}
})
/* const name1 = person.name
console.log('%%%%%%',name1);
const name2 = toRef(person,'name') // 這里并不是又復(fù)制了一份,而是這里讀取的指向的還是上面的let person里面的
console.log('@@@@@',name2); */
const x = toRefs(person) //將person中所有屬性變成ref引用對象
console.log(x);
return {
/* person,
name:toRef(person,'name'),
age:toRef(person,'age'),
salary:toRef(person.job.j1,'salary'), */
//name:ref(person.name) //用ref的話,之后修改的就不是上面的person中的name了,而是新的ref,所以不能這么寫
...toRefs(person)
}
}
}
</script>
<style>
</style>
?App.vue中引入即可
其他組合式API
?不常用
shallowReactive與shallowRef
??App.vue中導(dǎo)入注冊渲染Demo.vue. shallowReactive:只處理對象最外層屬性的響應(yīng)式(淺響應(yīng)式)。
. shallowRef:只處理基本數(shù)據(jù)類型的響應(yīng)式,不進行對象的響應(yīng)式處理。
. 什么時候使用?
? ? 。如果有一個對象數(shù)據(jù),結(jié)構(gòu)比較深,但變化時只是外層屬性變化===> shallowReactive。
? ? 。如果有一個對象數(shù)據(jù),后續(xù)功能不會修改該對象中的屬性,而是生新的對象來替換===>shallowRef。
Demo.vue
<template>
<h4>當(dāng)前x的值是:{{x.y}}</h4>
<button @click="x.y++">點我x+1</button>
<h2>姓名:{{name}}</h2>
<h2>年齡:{{age}}</h2>
<h2>薪水:{{job.j1.salary}}</h2>
<button @click="name+='~'">修改姓名</button>
<button @click="person.age++">增長年齡</button>
<button @click="job.j1.salary++">增長薪資</button>
</template>
<script>
import {ref,reactive, toRef, toRefs,shallowReactive,shallowRef} from 'vue'
export default {
name:'Demo',
setup() {
//let person = shallowReactive({ //淺層次的reactive,只有第一層可響應(yīng)式,即下面的name和age有響應(yīng)式,但salary沒有響應(yīng)式
let person = reactive({
name:'張三',
age:18,
job:{
j1:{
salary:20
}
}
})
// let x = shallowRef({ //shallowRef和ref區(qū)別為,當(dāng)值為對象時,shallowRef只能修改一層,如下x的y屬性就沒有響應(yīng)式
let x = ref({
y:0
})
return {
...toRefs(person),
x
}
}
}
</script>
<style>
</style>
readOnly與shallowReadOny
readonly:讓一個響應(yīng)式數(shù)據(jù)變?yōu)橹蛔x的(深只讀)。
shallowReadonly:讓一個響應(yīng)式數(shù)據(jù)變?yōu)橹蛔x的(淺只讀)
應(yīng)用場景:不希望數(shù)據(jù)被修改時。
Demo.vue
<template>
<h4>當(dāng)前求和為:{{sum}}</h4>
<button @click="sum++">點我+1</button>
<h2>姓名:{{name}}</h2>
<h2>年齡:{{age}}</h2>
<h2>薪水:{{job.j1.salary}}</h2>
<button @click="name+='~'">修改姓名</button>
<button @click="age++">增長年齡</button>
<button @click="job.j1.salary++">增長薪資</button>
</template>
<script>
import {ref,reactive, toRef, toRefs,shallowReactive,shallowRef, readonly, shallowReadonly} from 'vue'
export default {
name:'Demo',
setup() {
let person = reactive({
name:'張三',
age:18,
job:{
j1:{
salary:20
}
}
})
let sum = ref(0)
// person = readonly(person) //不讓任何人修改這個數(shù)據(jù),就可以用readonly,并且用person接收覆蓋了原來的person
person = shallowReadonly(person) //底有第一層年齡和姓名改不了,但薪資可改的
// sum = readonly(sum)
// sum = shallowReadonly(sum)
return {
...toRefs(person),
sum
}
}
}
</script>
<style>
</style>
toRaw與markRaw
. toRaw:
? ? 。作用:將一個由reactive生成的響應(yīng)式對象轉(zhuǎn)為普通對象。
? ? 。使用場景:用于讀取響應(yīng)式對象對應(yīng)的普通對象,對這個普通對象的所有操作,不會引起頁面更新.
. markRaw":
? ? 。作用:標(biāo)記一個對象,使其永遠不會再成為響應(yīng)式對象。
? ? 。應(yīng)用場景:
? ? ? ? 1.有些值不應(yīng)被設(shè)置為響應(yīng)式的,例如復(fù)雜的第三方類庫等。
? ? ? ? 2.當(dāng)渲染具有不可變數(shù)據(jù)源的大列表時,跳過響應(yīng)式轉(zhuǎn)換可以提高性能。
Demo.vue
<template>
<h4>當(dāng)前求和為:{{sum}}</h4>
<button @click="sum++">點我+1</button>
<h2>姓名:{{name}}</h2>
<h2>年齡:{{age}}</h2>
<h2>薪水:{{job.j1.salary}}</h2>
<h2>手機:{{person.phone}}</h2>
<button @click="name+='~'">修改姓名</button>
<button @click="age++">增長年齡</button>
<button @click="job.j1.salary++">增長薪資</button>
<button @click="showRawPerson">輸出最原始的person</button>
<button @click="addPhone">給人添加一個手機</button>
<button @click="person.phone.brand+='!'">換手機品牌</button>
<button @click="person.phone.color+='@'">換手機顏色</button>
</template>
<script>
import {markRaw,ref,reactive, toRef, toRefs,shallowReactive,shallowRef, toRaw} from 'vue'
export default {
name:'Demo',
setup() {
let person = reactive({
name:'張三',
age:18,
job:{
j1:{
salary:20
}
}
})
let sum = ref(0)
function showRawPerson(){
const p = toRaw(person) // 將將一個由reactive生成的響應(yīng)式對象轉(zhuǎn)為普通對象。ref的行不通。
console.log(person);
console.log(p);
}
function addPhone(){
/* let phone = {brand:"小米",color:"red"}
person.phone = phone //追加的內(nèi)容為響應(yīng)式的 */
let phone = {brand:"小米",color:"red"}
person.phone = markRaw(phone) //使用markRaw標(biāo)記后這個數(shù)據(jù)永遠不做響應(yīng)式了,修改時數(shù)據(jù)確實改了,但頁面不變化
}
return {
...toRefs(person),
sum,
person,//把person也交出去,后期添加person屬性可直接用person.的方式獲取值
showRawPerson,
addPhone
}
}
}
</script>
<style>
</style>
App.vue中引用Demo.vue
customRef自定義ref
作用:創(chuàng)建一個自定義的ref,并對其依賴項跟蹤和更新觸發(fā)進行顯式控制。
通過一個防抖的案例:即輸入框中輸入內(nèi)容,顯示的h4標(biāo)簽過1秒再顯示。
App.vue
<template>
<input type="text" v-model="keyWord">
<h3>{{keyWord}}</h3>
</template>
<script>
import { ref ,customRef} from 'vue'
export default {
name: 'App',
setup(){
//自定義一個ref—名為:myRef
function myRef(value,delay){
let timer;
return customRef((track,trigger)=>{ //要返回一個對象
return{
get(){
console.log(`有人從myRef這個容器中讀取數(shù)據(jù)了,我把${value}給他了`);
track() // 2 通知vue追蹤value的變化,(提前和get商量一下,讓他認為這個value是有用的)
return value
},
set(newValue){
console.log(`有人把myRef這個容器中數(shù)據(jù)改為了${newValue}`);
clearTimeout(timer) //防抖
//value = newValue
// trigger() // 1 通知vue去重新解析模板
timer = setTimeout(() => {
value = newValue
trigger() // 1 通知vue去重新解析模板
}, delay);
}
}
})
}
// let keyWord = ref("hello")// 使用vue提供的ref
let keyWord = myRef("hello",500)// 使用vue提供的ref
return {
keyWord
}
},
}
</script>
provide與inject
。作用:實現(xiàn)祖與后代組件間通信
。套路:父組件有一個provide選項來提供數(shù)據(jù),后代組件有一個inject選項來開始使用這些數(shù)據(jù) ?
App.vue
<template>
<div class="app">
<h3>我是App組件(祖)</h3>
<Child/>
</div>
</template>
<script>
import { provide, reactive, toRefs } from 'vue'
import Child from "./components/Child.vue"
export default {
name: 'App',
components:{
Child
},
setup(){
let phone = reactive({brand:"小米",color:"red"})
provide('phone',phone)
return {...toRefs(phone)}
}
}
</script>
<style>
.app{
background-color: grey;
padding: 10px;
}
</style>
Child.vue
<template>
<div class="child">
<h3>我是Child組件(子),{{phone.brand}}--{{phone.color}}</h3>
<Sun/>
</div>
</template>
<script>
import { inject } from 'vue'
import Sun from './Sun.vue'
export default {
name: 'Child',
components:{
Sun
},
setup(){
let phone = inject('phone')
return{phone}
}
}
</script>
<style>
.child{
background-color: skyblue;
padding: 10px;
}
</style>
Sun.vue
<template>
<div class="sun">
<h3>我是Sun組件(孫),{{phone.brand}}--{{phone.color}}</h3>
</div>
</template>
<script>
import { inject } from 'vue'
export default {
name: 'Sun',
setup(){
let phone = inject('phone')
return{phone}
}
}
</script>
<style>
.sun{
background-color: orange;
padding: 10px;
}
</style>
響應(yīng)式數(shù)據(jù)的判斷
響應(yīng)式數(shù)據(jù)的判斷API
? ? isRef:檢查一個值是否為一個ref 對象
? ? isReactive:檢查一個對象是否是由reactive創(chuàng)建的響應(yīng)式代理
? ? isReadonly:檢查一個對象是否是由readonly 創(chuàng)建的只讀代理
? ? isProxy:檢查一個對象是否是由reactive或者readonly方法創(chuàng)建的代理
App.vue
<template>
<h3>我是App組件(祖)</h3>
</template>
<script>
import { isProxy, isReactive, isReadonly, isRef, provide, reactive, readonly, ref, toRefs } from 'vue'
export default {
name: 'App',
setup(){
let phone = reactive({brand:"小米",color:"red"})
let sum = ref(0)
let phone2 = readonly(phone)
console.log(isRef(sum));//true
console.log(isReactive(phone));//true
console.log(isReadonly(phone2));//true
console.log(isProxy(phone));//true
console.log(isProxy(phone2));//true readOnly處理后的也是proxy
provide('phone',phone)
return {...toRefs(phone)}
}
}
</script>
<style>
</style>
組合式API的優(yōu)勢
Composition API組合式API,Options API配置式API
Options API存在的問題:使用傳統(tǒng)OptionsAPI中,新增或者修改一個需求,就需要分別在data,methods,computed里修改。
2.Composition API的優(yōu)勢:我們可以更加優(yōu)雅的組織我們的代碼,函數(shù)。讓相關(guān)功能的代碼更加有序的組織在一起。還可借助hook函數(shù)發(fā)揮組合式API的威力。
新的組件
Fragment與Teleport
1.Fragment
? ? 。在Vue2中:組件必須有一個根標(biāo)簽
? ? 。在Vue3中:組件可以沒有根標(biāo)簽,內(nèi)部會將多個標(biāo)簽包含在一個Fragment虛擬元素中。好處:減少標(biāo)簽層級,減小內(nèi)存占用
2.Teleport。什么是Teleport?
? ? Teleport是一種能夠?qū)⑽覀兊慕M件html結(jié)構(gòu)移動到指定位置的技術(shù)。
? ? 案例:傳統(tǒng)的方式寫彈窗會將外幾層的組件全都撐開。使用teleport指定to='html'或to='body'就可將這段代碼的東西傳送到html標(biāo)簽或body標(biāo)簽中,也可加遮罩層
?App.vue
<template>
<div class="app">
<h3>我是App組件</h3>
<Child/>
</div>
</template>
<script>
import Child from "./components/Child.vue"
export default {
name: 'App',
components:{
Child
}
}
</script>
<style>
.app{
background-color: grey;
padding: 10px;
}
</style>
Child.vue
<template>
<div class="child">
<h3>我是Child組件</h3>
<Sun/>
</div>
</template>
<script>
import Sun from './Sun.vue'
export default {
name: 'Child',
components:{
Sun
},
}
</script>
<style>
.child{
background-color: skyblue;
padding: 10px;
}
</style>
Son.vue
<template>
<div class="sun">
<h3>我是Sun組件</h3>
<Dialog/>
</div>
</template>
<script>
import Dialog from './Dialog.vue'
export default {
name: 'Sun',
components:{Dialog}
}
</script>
<style>
.sun{
background-color: orange;
padding: 10px;
}
</style>
Dialog.vue
<template>
<div>
<button @click="isShow = true">點我彈個窗</button>
<!-- <div v-if="isShow" class="dialog">
<h3>我是一個彈窗</h3>
<h4>一些內(nèi)容</h4>
<h4>一些內(nèi)容</h4>
<h4>一些內(nèi)容</h4>
<button @click="isShow = false">關(guān)閉彈窗</button>
</div> -->
<teleport to="body">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一個彈窗</h3>
<h4>一些內(nèi)容</h4>
<h4>一些內(nèi)容</h4>
<h4>一些內(nèi)容</h4>
<button @click="isShow = false">關(guān)閉彈窗</button>
</div>
</div>
</teleport>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
name:"Dialog",
setup(){
let isShow = ref(false)
return {isShow}
}
}
</script>
<style>
.dialog{
width: 300px;
height: 300px;
background-color: green;
position: absolute;
top:50%;
left:50%;
transform: translate(-50%,-50%);
}
.mask{/* 遮罩層樣式,點被其遮住的元素的點不了 */
position: absolute;
top: 0 ;bottom: 0;left: 0;right: 0;
background-color: rgba(0, 0, 0, 0.5);
}
</style>
Suspense組件
網(wǎng)速快的時候,靜態(tài)引入和動態(tài)引入一樣,但網(wǎng)速慢的時候,靜態(tài)引入是App和Child組件一起出來(最后引入取決于組件套娃中最后出來的那個組件出來后才顯示),動態(tài)引入是App和Child誰加載完誰出來。但動態(tài)引入當(dāng)某個先出來展示,某個組件加載慢1秒中后才出來就會用戶體驗不好,頁面有抖動,可以用Suspense將子標(biāo)簽Child標(biāo)簽包起來,然后再Child組件還未加載好的時候先用一個loading替換
?App.vue
<template>
<div class="app">
<h3>我是App組件</h3>
<!-- <Child/> -->
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>稍等,加載中......</h3>
</template>
</Suspense>
</div>
</template>
<script>
// import Child from "./components/Child.vue" //靜態(tài)引入
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue')) //動態(tài)引入 異步引入
export default {
name: 'App',
components:{
Child
}
}
</script>
<style>
.app{
background-color: grey;
padding: 10px;
}
</style>
Child.vue
<template>
<div class="child">
<h3>我是Child組件</h3>
{{sum}}
</div>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'Child',
async setup(){ //因為App.vue中使用了Suspense,且引入Child組件時為異步引入,所以這里的setup可返回Promise對象
let sum = ref(0)
let p = new Promise((resolve,reject)=>{
setTimeout(() => {
resolve({sum})
}, 3000);
})
return await p
}
}
</script>
<style>
.child{
background-color: skyblue;
padding: 10px;
}
</style>
其他
全局API的轉(zhuǎn)移
. Vue 2.x有許多全局API和配置。
? ? 。例如:注冊全局組件、注冊全局指令等。
? ? ? ? //注冊全局組件
? ? ? ? Vue. component( ' MyButton ', {
? ? ? ? ? ? data: () =>({
? ? ? ? ? ? count: 0}),
? ? ? ? ? ? template: '<button @click="count++">clicked {{ count }} times.</button>'
? ? ? ? ? ? })
? ? ? ? //注冊全局指令
? ? ? ? Vue.directive( 'focus ', {
? ? ? ? ? ? inserted : el => el.focus()
? ? ? ? ? ? }
. Vue3.0中對這些API做出了調(diào)整:
? ? 。將全局的API,即:Vue.xxx調(diào)整到應(yīng)用實例(app ) 上
????????2.x全局APl (vue ) ?=> ? 3.x 實例APl( app)
????????Vue.config.xxxX ?=> ?app.config.xxXx
? ? ? ? Vue.config.productionTip ?=> ?移除
????????Vue.component ?=> ?app.component
????????Vue.directive ?=> ?app.directive
? ? ? ? Vue.mixin ?=> ?app.mixin????????
????????Vue.use ?=> ?app.use
????????Vue.prototype ?=> ?app.config.globalProperties
2.其他改變
? ? . data選項應(yīng)始終被聲明為一個函數(shù)。
? ? 。過度類名的更改:
? ? ? ? o Vue2.x寫法
? ? ? ? .v-enter,
? ? ? ? .v-leave-to {
? ? ? ? ? ? opacity: 0;
? ? ? ? ? ? }
? ? ? ? .v-leave,
? ? ? ? .v-enter-to {
? ? ? ? ? ? opacity: 1;
? ? ? ? ? ? }
? ? ? ? o Vue3.x寫法
? ? ? ? .v-enter-from,
? ? ? ? .v-leave-to {
? ? ? ? ? ? opacity: 0;
? ? ? ? ? ? }
? ? ? ? .v-leave-from,
? ? ? ? .v-enter-to {
? ? ? ? ? ? opacity: 1;
? ? ? ? ? ? }
? ? 移除keyCode作為v-on 的修飾符,同時也不再支持config.keyCodes
? ? 移除v-on.native修飾符
? ? 移除了過濾器filter文章來源:http://www.zghlxwxcb.cn/news/detail-675371.html
? ? . . . . . .?文章來源地址http://www.zghlxwxcb.cn/news/detail-675371.html
到了這里,關(guān)于Vue3筆記——(尚硅谷張?zhí)煊鞻ue筆記)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!