兵哥vue3

一、Vue3啟程

1. 初始Vue3

<div id="app">

? ? <h2>姓名:{{name}}</h2>

? ? <h2>年齡:{{age}}</h2>

? ? <button @click="updateData">修改數(shù)據(jù)</button>

</div>

// Vue2中--創(chuàng)建實例的方式

new Vue({

? ? //指定掛載容器

? ? // el:'#app',

? ? //定義屬性

? ? data() {

? ? ? ? return {

? ? ? ? ? ? name:'張三',

? ? ? ? ? ? age:20

? ? ? ? }

? ? },

? ? //定義方法

? ? methods: {

? ? ? ? updateData(){

? ? ? ? ? ? this.name = '李四'

? ? ? ? ? ? this.age = 25

? ? ? ? }

? ? },

}).$mount('#app') ?//指定當(dāng)前vue實例掛載的容器

// Vue3中--創(chuàng)建實例的方式

Vue.createApp({

? ? //注意:這個配置對象里面除了不能寫el選項,之前怎么寫,現(xiàn)在還可以怎么寫

? ? //定義屬性

? ? data() {

? ? ? ? return {

? ? ? ? ? ? name:'張三',

? ? ? ? ? ? age:20

? ? ? ? }

? ? },

? ? //定義方法

? ? methods: {

? ? ? ? updateData(){

? ? ? ? ? ? this.name = '李四'

? ? ? ? ? ? this.age = 25

? ? ? ? }

? ? },

}).mount('#app') ? //只能通過mount方法指定掛載的容器,不用通過el選項指定

2. Vue2和Vue3的響應(yīng)式

<div id="app">

? ? <h2>學(xué)生:{{stu}}</h2>

? ? <h2>食物:{{foods}}</h2>

? ? <div>

? ? ? ? <button @click="updateStuName">修改學(xué)生姓名</button>

? ? ? ? <button @click="addStuSex">添加學(xué)生性別</button>

? ? ? ? <button @click="delStuAge">刪除學(xué)生年齡</button>

? ? ? ? <button @click="updateFoods2">修改第二個食物</button>

? ? </div>

</div>

// Vue2

new Vue({

? ? data() {

? ? ? ? return {

? ? ? ? ? ? //學(xué)生對象

? ? ? ? ? ? stu:{

? ? ? ? ? ? ? ? name:'張三',

? ? ? ? ? ? ? ? age:20

? ? ? ? ? ? },

? ? ? ? ? ? //食物數(shù)組

? ? ? ? ? ? foods:['榴蓮','葡萄','香蕉']

? ? ? ? }

? ? },

? ? methods: {

? ? ? ? updateStuName(){

? ? ? ? ? ? this.stu.name = '李四'

? ? ? ? },

? ? ? ? addStuSex(){

? ? ? ? ? ? // 直接給對象添加的屬性,不具備響應(yīng)式

? ? ? ? ? ? // this.stu.sex = '男'

? ? ? ? ? ? // 如果要給對象添加屬性,并且添加的屬性也要具備響應(yīng)式,要使用$set方法

? ? ? ? ? ? // 方法的第一個參數(shù)是指定的對象,第二個參數(shù)是屬性名,第三個參數(shù)是屬性值。

? ? ? ? ? ? this.$set(this.stu,'sex','男')

? ? ? ? },

? ? ? ? delStuAge(){

? ? ? ? ? ? // 直接刪除對象身上的屬性,是不具備響應(yīng)式的

? ? ? ? ? ? // delete this.stu.age

? ? ? ? ? ? // 如果要刪除對象身上的屬性,并且還要具備響應(yīng)式,要使用$delete方法

? ? ? ? ? ? // 方法的第一個參數(shù)是指定的對象,第二個參數(shù)是屬性名

? ? ? ? ? ? this.$delete(this.stu,'age')

? ? ? ? },

? ? ? ? updateFoods2(){

? ? ? ? ? ? // 直接根據(jù)索引修改數(shù)組元素,不具備響應(yīng)式

? ? ? ? ? ? // this.foods[1] = '西瓜'

? ? ? ? ? ? // 操作數(shù)組中的元素,并且還要具備響應(yīng)式,只能使用數(shù)組的以下方法:

? ? ? ? ? ? // push unshift pop shift splice reverse sort

? ? ? ? ? ? // this.foods.splice(1,1,'西瓜')

? ? ? ? ? ? // 如果就是想通過下標(biāo)去操作數(shù)組,還要具備響應(yīng)式,使用$set方法

? ? ? ? ? ? this.$set(this.foods,1,'西瓜')

? ? ? ? }

? ? },

}).$mount('#app')

// 總結(jié)Vue2的響應(yīng)式:不能直接給對象添加屬性,刪除對象的屬性,不能直接操作數(shù)組的下標(biāo),

// 但是,Vue2同時也提供了解決這些問題的方案。

// Vue3

Vue.createApp({

? ? data() {

? ? ? ? return {

? ? ? ? ? ? //學(xué)生對象

? ? ? ? ? ? stu:{

? ? ? ? ? ? ? ? name:'張三',

? ? ? ? ? ? ? ? age:20

? ? ? ? ? ? },

? ? ? ? ? ? //食物數(shù)組

? ? ? ? ? ? foods:['榴蓮','葡萄','香蕉']

? ? ? ? }

? ? },

? ? methods: {

? ? ? ? updateStuName(){

? ? ? ? ? ? this.stu.name = '李四'

? ? ? ? },

? ? ? ? addStuSex(){

? ? ? ? ? ? // 在Vue3中,直接給對象添加屬性,新的屬性依然具備響應(yīng)式

? ? ? ? ? ? this.stu.sex = '男'

? ? ? ? },

? ? ? ? delStuAge(){

? ? ? ? ? ? // 在Vue3中,直接刪除對象的屬性,依然具備響應(yīng)式

? ? ? ? ? ? delete this.stu.age

? ? ? ? },

? ? ? ? updateFoods2(){

? ? ? ? ? ? // 在Vue3中,根據(jù)下標(biāo)操作數(shù)組,依然具備響應(yīng)式

? ? ? ? ? ? this.foods[1] = '西瓜'

? ? ? ? }

? ? },

}).mount('#app')

// 總結(jié)Vue3的響應(yīng)式:解決了再Vue2中的所有問題。

3. Vue2和Vue3的響應(yīng)式原理

<h2 id="name"></h2>

<h2 id="age"></h2>

// Vue2的響應(yīng)式原理:

// 這里的obj是源對象

let obj = {

? ? name:'張三',

? ? age:20

}

// 在頁面中顯示姓名和年齡

document.getElementById('name').innerText = obj.name

document.getElementById('age').innerText = obj.age

// 這里的obj2代理對象---由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是源對象

let obj = {

? ? name:'張三',

? ? age:20

}

// 在頁面中顯示姓名和年齡

document.getElementById('name').innerText = obj.name

document.getElementById('age').innerText = obj.age

// 這里的obj2代理對象---由obj2代理obj

// new Proxy(源對象,{...})的方式,創(chuàng)建代理對象

let obj2 = new Proxy(obj,{

? ? //讀取屬性,參數(shù)分別是:源對象,屬性名

? ? get(target, property){

? ? ? ? // 直接根據(jù)源對象返回源對象身上的屬性

? ? ? ? // return target[property]

? ? ? ? // 通過發(fā)射對象,發(fā)射輸出源對象身上的屬性

? ? ? ? return Reflect.get(target,property)

? ? },

? ? //設(shè)置屬性,參數(shù)分別是:源對象,屬性名,屬性值

? ? set(target, property,value){

? ? ? ? // target[property] = value

? ? ? ? if(Reflect.has(target,property)){

? ? ? ? ? ? Reflect.set(target, property,value)

? ? ? ? ? ? document.getElementById(`${property}`).innerText = value

? ? ? ? }

? ? },

? ? //刪除屬性,參數(shù)分別是:源對象,屬性名

? ? deleteProperty(target, property){

? ? ? ? // delete target[property]

? ? ? ? Reflect.deleteProperty(target, property)

? ? }

})

4. 引出Vue3新推出的組合式API

<div id="app">

? ? <div>

? ? ? ? <h2>學(xué)生信息</h2>

? ? ? ? <!-- 注意:ref對象在模板只不需要.value的方式獲取里面的值 -->

? ? ? ? <h4>姓名:{{stuName}}</h4>

? ? ? ? <h4>年齡:{{stuAge}}</h4>

? ? ? ? <button @click="updateStu">修改學(xué)生信息</button>

? ? </div>

? ? <div>

? ? ? ? <h2>汽車信息</h2>

? ? ? ? <h4>車名:{{carName}}</h4>

? ? ? ? <h4>車價:{{carPrice}}</h4>

? ? ? ? <button @click="updateCar">修改汽車信息</button>

? ? </div>

? ? <div>

? ? ? ? <h2>手機信息</h2>

? ? ? ? <h4>名稱:{{phoneName}}</h4>

? ? ? ? <h4>顏色:{{phoneColor}}</h4>

? ? ? ? <button @click="updatePhone">修改手機信息</button>

? ? </div>

? ? <div>

? ? ? ? <h2>食物信息</h2>

? ? ? ? <h4>名稱:{{foodName}}</h4>

? ? ? ? <h4>價格:{{foodPrice}}</h4>

? ? ? ? <button @click="updateFood">修改食物信息</button>

? ? </div>

</div>

// 什么是組合式API(Composition API),就是Vue推出的一些新的方法,這個方法在setup中使用

// 從Vue身上獲取ref組合式API函數(shù)

let {ref} = Vue

Vue.createApp({

? ? // 注意:Vue2中,Vue實例的data選項可以是一個對象,也可以是一個方法,由方法返回一個對象

? ? // 但是,組件中data選項必須是一個方法。

? ? // Vue3中,無論是Vue實例,還是組件,data選項都必須是一個方法。

? ? // 我們之前習(xí)慣將所有的數(shù)據(jù)放在data選項中定義,所有的方法放在methods選項中定義,

? ? // 所有的計算屬性放在computed選項中定義,所有的偵聽器放在watch選項中定義,

? ? // 這樣就會導(dǎo)致一個業(yè)務(wù)的代碼會拆分到多個結(jié)構(gòu)中去寫,如果一個頁面中要操作很多個業(yè)務(wù),代碼后期維護成本會很高。

? ? // 所以,Vue3引入了組合式API,簡化之前繁瑣的過程,將相同業(yè)務(wù)的代碼靠在一起寫。

? ? /* data: function () {

? ? ? ? return {

? ? ? ? ? ? //定義學(xué)生數(shù)據(jù)

? ? ? ? ? ? stuName: '張三',

? ? ? ? ? ? stuAge: '20',

? ? ? ? ? ? //汽車信息

? ? ? ? ? ? carName: '奔馳',

? ? ? ? ? ? carPrice: '50W',

? ? ? ? ? ? //手機信息

? ? ? ? ? ? phoneName: 'iphone',

? ? ? ? ? ? phoneColor: '白色',

? ? ? ? ? ? //食物信息

? ? ? ? ? ? foodName: '漢堡',

? ? ? ? ? ? foodPrice: '¥20'

? ? ? ? }

? ? },

? ? methods: {

? ? ? ? //修改學(xué)生的方法

? ? ? ? updateStu(){

? ? ? ? ? ? this.stuName = '李四'

? ? ? ? ? ? this.stuAge = 30

? ? ? ? },

? ? ? ? //修改汽車的方法

? ? ? ? updateCar(){

? ? ? ? ? ? this.carName = '寶馬'

? ? ? ? ? ? this.carPrice = '40W'

? ? ? ? },

? ? ? ? //修改手機的方法

? ? ? ? updatePhone(){

? ? ? ? ? ? this.phoneName = '華為'

? ? ? ? ? ? this.phoneColor = '藍(lán)色'

? ? ? ? },

? ? ? ? updateFood(){

? ? ? ? ? ? this.foodName = '蛋糕'

? ? ? ? ? ? this.foodPrice = '¥30'

? ? ? ? }

? ? }, */

? ? // setup方法是所有組合式API的入口

? ? setup() {

? ? ? ? // 定義學(xué)生的信息

? ? ? ? // 在setup中,直接定義的數(shù)據(jù)是不具備響應(yīng)式的,

? ? ? ? // 如果要使數(shù)據(jù)具備響應(yīng)式,需要使用ref組合式API對數(shù)據(jù)進行包裝,包裝后返回的是ref對象

? ? ? ? let stuName = ref('張三')

? ? ? ? let stuAge = ref('20')

? ? ? ? let updateStu = () => {

? ? ? ? ? ? //ref對象的value屬性保存的是值

? ? ? ? ? ? stuName.value = '李四'

? ? ? ? ? ? stuAge.value = 30

? ? ? ? }

? ? ? ? // 定義汽車的信息

? ? ? ? let carName = ref('奔馳')

? ? ? ? let carPrice = ref('50W')

? ? ? ? let updateCar = () => {

? ? ? ? ? ? carName.value = '寶馬'

? ? ? ? ? ? carPrice.value = '40W'

? ? ? ? }

? ? ? ? // 定義手機的信息

? ? ? ? let phoneName = ref('iphone')

? ? ? ? let phoneColor = ref('白色')

? ? ? ? let updatePhone = () => {

? ? ? ? ? ? phoneName.value = '華為'

? ? ? ? ? ? phoneColor.value = '藍(lán)色'

? ? ? ? }

? ? ? ? // 定義食物的信息

? ? ? ? let foodName = ref('漢堡')

? ? ? ? let foodPrice = ref('¥20')

? ? ? ? let updateFood = () => {

? ? ? ? ? ? foodName.value = '蛋糕'

? ? ? ? ? ? foodPrice.value = '¥30'

? ? ? ? }

? ? ? ? //返回模板中需要使用的數(shù)據(jù)

? ? ? ? return{

? ? ? ? ? ? stuName,

? ? ? ? ? ? stuAge,

? ? ? ? ? ? updateStu,

? ? ? ? ? ? carName,

? ? ? ? ? ? carPrice,

? ? ? ? ? ? updateCar,

? ? ? ? ? ? phoneName,

? ? ? ? ? ? phoneColor,

? ? ? ? ? ? updatePhone,

? ? ? ? ? ? foodName,

? ? ? ? ? ? foodPrice,

? ? ? ? ? ? updateFood

? ? ? ? }

? ? }

}).mount('#app')

5. ref和reactive

<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對象的值,每次都要先點value

? ? ? ? ? ? stu.value.name = '李明'

? ? ? ? ? ? stu.value.age = 30

? ? ? ? } */

? ? ? ? // reactive組合式API方法,根據(jù)源對象返回一個代理對象(Proxy對象)

? ? ? ? let stu = reactive({

? ? ? ? ? ? name:'李四',

? ? ? ? ? ? age:20

? ? ? ? })

? ? ? ? let updateStu = ()=>{

? ? ? ? ? ? // Proxy對象,不需要先點value

? ? ? ? ? ? stu.name = '李明'

? ? ? ? ? ? stu.age = 30

? ? ? ? }

? ? ? ? return {

? ? ? ? ? ? name,

? ? ? ? ? ? updateName,

? ? ? ? ? ? stu,

? ? ? ? ? ? updateStu

? ? ? ? }

? ? }

}).mount('#app')

二、腳手架

1. Vue-Cli

Vue CLI 4.x以上,Node.js版本 8.9以上

npm install -g @vue/cli

# OR

yarn global add @vue/cli

# 查看版本

vue --version

# 創(chuàng)建項目

vue create hello-world

# 運行

npm run serve

main.js

// vue2

/* import Vue from 'vue'

import App from './App.vue'

new Vue({

? ? render:h=>h(App)

}).$mount("#app") */

// vue3

// 從vue中導(dǎo)入createApp方法,通過這個方法,創(chuàng)建vue實例

import { createApp } from 'vue'

// 導(dǎo)入App組件

import App from './App.vue'

// 通過createApp方法創(chuàng)建一個vue實例,渲染App組件,并將結(jié)果掛載到#app容器中。

createApp(App).mount('#app')

2. Vite

Vite 需要 Node.js版本 12.0以上

npm init @vitejs/app

# OR

yarn create @vitejs/app

# 然后按照提示操作即可

# 安裝依賴

npm install

# 運行

npm run dev

3. 計算屬性

<h2>計算屬性</h2>

<div>姓:<input type="text" v-model="firstName">名:<input type="text" v-model="lastName"></div>

<div>姓名:{{fullName}}<input type="text" v-model="fullName"></div>

// 在Vue3中,定義計算屬性,需要引入computed組合式API

import {ref,computed} from 'vue'

export default {

? ? // Vue2中的計算屬性

? ? // 數(shù)據(jù)

? ? /* data() {

? ? ? ? return {

? ? ? ? ? ? firstName:'張',

? ? ? ? ? ? lastName:'杰'

? ? ? ? }

? ? },

? ? // 計算屬性

? ? computed:{

? ? ? ? // 只讀的計算屬性

? ? ? ? // fullName(){

? ? ? ? // ? ? return this.firstName+'.'+this.lastName

? ? ? ? // }

? ? ? ? // 讀寫計算屬性

? ? ? ? fullName:{

? ? ? ? ? ? //返回計算機屬性的結(jié)果

? ? ? ? ? ? get(){

? ? ? ? ? ? ? ? return this.firstName+'.'+this.lastName

? ? ? ? ? ? },

? ? ? ? ? ? //修改計算屬性的值

? ? ? ? ? ? set(val){

? ? ? ? ? ? ? ? let arr = val.split('.')

? ? ? ? ? ? ? ? this.firstName = arr[0]

? ? ? ? ? ? ? ? this.lastName = arr[1]

? ? ? ? ? ? }

? ? ? ? }

? ? } */

? ? // Vue3中的計算屬性

? ? setup() {

? ? ? ? let firstName = ref('張')

? ? ? ? let lastName = ref('杰')

? ? ? ? //computed()函數(shù)的參數(shù)是一個回調(diào)函數(shù),回調(diào)函數(shù)的返回值,就是計算屬性的返回值

? ? ? ? // 定義只讀的計算屬性

? ? ? ? // let fullName = computed(()=>{

? ? ? ? // ? ? return firstName.value + '.' + lastName.value

? ? ? ? // })

? ? ? ? // 定義讀寫計算屬性

? ? ? ? let fullName = computed({

? ? ? ? ? ? get(){

? ? ? ? ? ? ? ? return firstName.value + '.' + lastName.value

? ? ? ? ? ? },

? ? ? ? ? ? set(val){

? ? ? ? ? ? ? ? let arr = val.split('.')

? ? ? ? ? ? ? ? firstName.value = arr[0]

? ? ? ? ? ? ? ? lastName.value = arr[1]

? ? ? ? ? ? }

? ? ? ? })

? ? ? ? return{

? ? ? ? ? ? firstName,

? ? ? ? ? ? lastName,

? ? ? ? ? ? fullName

? ? ? ? }

? ? }

};

4. 偵聽器

<h2>偵聽器</h2>

<div>薪資:{{money}}

? ? <button @click="money+=1000">加薪</button>

</div>

<div>學(xué)生:{{stu}}

? ? <button @click="stu.name='李四',stu.age=25">修改學(xué)生</button>

? ? <button @click="stu.car.name='寶馬',stu.car.price=40">學(xué)生換車</button>

</div>

// 引入組合式API watch 和 watchEffect

import {ref,reactive, watch, watchEffect} from 'vue'

export default {

? ? // Vue2中的偵聽器

? ? /* //數(shù)據(jù)

? ? data() {

? ? ? ? return {

? ? ? ? ? ? money:10000,

? ? ? ? ? ? stu:{

? ? ? ? ? ? ? ? name:'張三',

? ? ? ? ? ? ? ? age:20,

? ? ? ? ? ? ? ? car:{

? ? ? ? ? ? ? ? ? ? name:'奔馳',

? ? ? ? ? ? ? ? ? ? price:50

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? },

? ? //偵聽器

? ? watch:{

? ? ? ? //根據(jù)數(shù)據(jù)的名稱定義一個方法,用于該方法去偵聽該屬性值是否發(fā)生變化(參數(shù)1是新值,參數(shù)2是舊值)

? ? ? ? // 注意:默認(rèn)情況下,偵聽器一上來不會立刻執(zhí)行,必須要偵聽到值重新發(fā)生變化后,才執(zhí)行。

? ? ? ? // money(nval,oval){

? ? ? ? // ? ? console.log(nval,oval);

? ? ? ? // }

? ? ? ? // 完整寫法,偵聽器定義成一個對象

? ? ? ? money:{

? ? ? ? ? ? //表示偵聽器默認(rèn)執(zhí)行一次

? ? ? ? ? ? immediate:true,

? ? ? ? ? ? //定義偵聽器的方法

? ? ? ? ? ? handler(nval,oval){

? ? ? ? ? ? ? ? console.log(nval,oval);

? ? ? ? ? ? }

? ? ? ? },

? ? ? ? // 監(jiān)聽學(xué)生數(shù)據(jù),注意:只有整個學(xué)生對象變化了才會監(jiān)聽到,如果只是修改對象身上的屬性,監(jiān)聽不到。

? ? ? ? // stu(nval,oval){

? ? ? ? // ? ? console.log(nval,oval);

? ? ? ? // }

? ? ? ? // 解決方案:監(jiān)聽器改成一個對象

? ? ? ? stu:{

? ? ? ? ? ? //表示偵聽器開啟深度監(jiān)視

? ? ? ? ? ? deep:true,

? ? ? ? ? ? handler(nval,oval){

? ? ? ? ? ? ? ? console.log(nval,oval);

? ? ? ? ? ? }

? ? ? ? }

? ? } */

? ? // Vue3中的偵聽器

? ? setup() {

? ? ? ? let money = ref(10000)

? ? ? ? let stu = reactive({

? ? ? ? ? ? name:'張三',

? ? ? ? ? ? age:20,

? ? ? ? ? ? car:{

? ? ? ? ? ? ? ? name:'奔馳',

? ? ? ? ? ? ? ? price:50

? ? ? ? ? ? }

? ? ? ? })

? ? ? ? // watch函數(shù)有三個參數(shù):1.偵聽誰,2.回調(diào)函數(shù),3.配置對象(可以省略)

? ? ? ? // 簡單用法:一上來沒有立刻執(zhí)行

? ? ? ? // watch(money,(nval,oval)=>{

? ? ? ? // ? ? console.log(nval,oval);

? ? ? ? // })

? ? ? ? // 完整用法:加上第三個參數(shù),配置對象

? ? ? ? watch(money,(nval,oval)=>{

? ? ? ? ? ? console.log(nval,oval);

? ? ? ? },{

? ? ? ? ? ? //立刻執(zhí)行

? ? ? ? ? ? immediate:true,

? ? ? ? })

? ? ? ? // 監(jiān)視reactive的數(shù)據(jù),默認(rèn)就開啟深度監(jiān)視,并且無法關(guān)閉

? ? ? ? // watch(stu,(nval,oval)=>{

? ? ? ? // ? ? console.log(nval,oval);

? ? ? ? // })

? ? ? ? // 對于reactive的數(shù)據(jù),可以采用監(jiān)視部分屬性

? ? ? ? watch(()=>stu.name,(nval,oval)=>{

? ? ? ? ? ? console.log(nval,oval);

? ? ? ? })

? ? ? ? // 如果監(jiān)視的是reactive里面的對象屬性,默認(rèn)是不開啟深度監(jiān)視的,需要手動開啟

? ? ? ? watch(()=>stu.car,(nval,oval)=>{

? ? ? ? ? ? console.log(nval,oval);

? ? ? ? },{

? ? ? ? ? ? deep:true

? ? ? ? })

? ? ? ? //watchEffect監(jiān)聽器,只有一個回調(diào)函數(shù)參數(shù),并且沒有參數(shù)

? ? ? ? // 特點:

? ? ? ? // 1.默認(rèn)會執(zhí)行一次

? ? ? ? // 2.不需要明確監(jiān)視誰,回調(diào)函數(shù)里面用到了誰,誰變了,就會重新執(zhí)行回調(diào)函數(shù)。

? ? ? ? watchEffect(()=>{

? ? ? ? ? ? console.log('我是watchEffect');

? ? ? ? ? ? let m = money.value

? ? ? ? ? ? ?let name = stu.name

? ? ? ? })

? ? ? ? return{

? ? ? ? ? ? money,

? ? ? ? ? ? stu

? ? ? ? }

? ? }

};

5. 過濾器

<h2>過濾器</h2>

<div>薪資:{{toFixed2(money)}}</div>

<div>薪資:{{toFixed2Money}}</div>

export default {

? ? data() {

? ? ? ? return {

? ? ? ? ? ? money:10000.12345

? ? ? ? }

? ? },

? ? // 注意:在Vue2中可以定義過濾器,但是在Vue3中已經(jīng)取消了過濾器。

? ? /* filters:{

? ? ? ? toFixed2(val){

? ? ? ? ? ? return val.toFixed(2)

? ? ? ? }

? ? } */

? ? // Vue3推薦我們使用方法 或 計算屬性的方式,實現(xiàn)之前過濾器的效果。

? ? methods: {

? ? ? ? toFixed2(val){

? ? ? ? ? ? return val.toFixed(2)

? ? ? ? }

? ? },

? ? computed:{

? ? ? ? toFixed2Money(){

? ? ? ? ? ? return this.money.toFixed(2)

? ? ? ? }

? ? }

};

6. 響應(yīng)式

<h2>響應(yīng)式</h2>

<div>薪資:{{money}} <button @click="updateMoney">漲薪</button></div>

<div>汽車:{{car}} <button @click="updateCar">換車</button></div>

<div>學(xué)生:{{stu}} <button @click="updateStu">修改學(xué)生</button></div>

//Vue3中的所有組合式API,都要采用按需引入的方式導(dǎo)入

import {ref,reactive} from 'vue'

export default {

? ? //setup是所有組合式API的入口

? ? setup() {

? ? ? ? //使用ref定義基本類型數(shù)據(jù)

? ? ? ? let money = ref(10000)

? ? ? ? let updateMoney = ()=>{

? ? ? ? ? ? money.value += 1000

? ? ? ? }

? ? ? ? //使用ref定義引用類型數(shù)據(jù)

? ? ? ? // ref方法,返回的是ref對象,ref對象的value屬性是一個代理對象(Proxy)

? ? ? ? let car = ref({

? ? ? ? ? ? name:'奔馳',

? ? ? ? ? ? price:'50W'

? ? ? ? })

? ? ? ? let updateCar = ()=>{

? ? ? ? ? ? // 注意:這里每次修改數(shù)據(jù)時,必須要先.value再.具體的屬性

? ? ? ? ? ? car.value.name = '寶馬',

? ? ? ? ? ? car.value.price = '40W'

? ? ? ? }

? ? ? ? // 注意:reactive只能定義引用類型(對象和數(shù)組)

? ? ? ? // reactive方法,直接返回一個代理對象(Proxy)

? ? ? ? let stu = reactive({

? ? ? ? ? ? name:'張三',

? ? ? ? ? ? age:20

? ? ? ? })

? ? ? ? let updateStu = ()=>{

? ? ? ? ? ? stu.name = '李四'

? ? ? ? ? ? stu.age = 25

? ? ? ? }

? ? ? ? // 總結(jié):通常情況下:

? ? ? ? // 1.基本類型的數(shù)據(jù),選擇用ref定義

? ? ? ? // 2.引用類型的數(shù)據(jù),選擇用reactive定義


? ? ? ? //setup方法里面返回出去的成員,在模板可以使用

? ? ? ? return{

? ? ? ? ? ?money,

? ? ? ? ? ?updateMoney,

? ? ? ? ? ?car,

? ? ? ? ? ?updateCar,

? ? ? ? ? ?stu,

? ? ? ? ? ?updateStu

? ? ? ? }

? ? }

}

7. fragment組件

在vue3的模板中,不再需要根標(biāo)簽,它內(nèi)部有一個fragment的組件作為模板的根標(biāo)簽

三、Vue3高階

1. Hook函數(shù)

useCar

import {ref,computed} from 'vue'

//導(dǎo)出去一個函數(shù)

export default function(){

? ? //汽車數(shù)據(jù)

? ? let carName = ref('保時捷')

? ? let carPrice = ref(100)

? ? //汽車的計算屬性

? ? let carPrice2 = computed(()=>{

? ? ? ? return (carPrice.value*0.8).toFixed(2)

? ? })

? ? //操作汽車的方法

? ? let updateCar = ()=>{

? ? ? ? carName.value = '賓利'

? ? ? ? carPrice.value = 300

? ? }

? ? //返回暴露給外界的內(nèi)容

? ? return {

? ? ? ? carName,

? ? ? ? carPrice,

? ? ? ? carPrice2,

? ? ? ? updateCar

? ? }

}

usePhone

import {ref,computed} from 'vue'

export default function(){

? ? //手機數(shù)據(jù)

? ? let phoneName = ref('華為')

? ? let phonePrice = ref(5000)

? ? //手機的計算屬性

? ? let phonePrice2 = computed(()=>{

? ? ? ? return (phonePrice.value*0.5).toFixed(2)

? ? })

? ? //操作手機的方法

? ? let updatePhone = ()=>{

? ? ? ? phoneName.value = '蘋果'

? ? ? ? phonePrice.value = 9000

? ? }

? ? //返回暴露給外界的內(nèi)容

? ? return {

? ? ? ? phoneName,

? ? ? ? phonePrice,

? ? ? ? phonePrice2,

? ? ? ? updatePhone

? ? }

}

組件

<h1>Hook函數(shù)</h1>

<div class="car">

? ? <h2>汽車信息</h2>

? ? <ul>

? ? ? ? <li>汽車名稱:{{carName}}</li>

? ? ? ? <li>汽車價格:{{carPrice}}萬</li>

? ? ? ? <li>優(yōu)惠價格:{{carPrice2}}萬</li>

? ? ? ? <li>

? ? ? ? ? ? <button @click="updateCar">修改汽車信息</button>

? ? ? ? </li>

? ? </ul>

</div>

<div class="phone">

? ? <h2>手機信息</h2>

? ? <ul>

? ? ? ? <li>手機名稱:{{phoneName}}</li>

? ? ? ? <li>手機價格:{{phonePrice}}</li>

? ? ? ? <li>優(yōu)惠價格:{{phonePrice2}}</li>

? ? ? ? <li>

? ? ? ? ? ? <button @click="updatePhone">修改手機信息</button>

? ? ? ? </li>

? ? </ul>

</div>

// 導(dǎo)入hook函數(shù)

import useCar from '../hooks/useCar'

import usePhone from '../hooks/usePhone'

export default {

? ? setup() {

? ? ? ? // 返回模板中需要使用的數(shù)據(jù)

? ? ? ? return {

? ? ? ? ? ? //返回汽車信息

? ? ? ? ? ? ...useCar(),

? ? ? ? ? ? //返回手機信息

? ? ? ? ? ? ...usePhone()

? ? ? ? }

? ? }

}

2. 生命周期

<h1>生命周期</h1>

<h3>

? ? 數(shù)量:{{count}}

? ? <button @click="count++">數(shù)量++</button> ? ?

</h3>

// 組合式API生命周期函數(shù)

import {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'

export default {

? ? // beforeCreate() {

? ? // ? ? console.log('創(chuàng)建之前');

? ? // },

? ? // created() {

? ? // ? ? console.log('創(chuàng)建完成');

? ? // },

? ? // beforeMount() {

? ? // ? ? console.log('掛載之前1');

? ? // },

? ? // mounted() {

? ? // ? ? console.log('掛載完成1');

? ? // },

? ? // beforeUpdate() {

? ? // ? ? console.log('更新之前1');

? ? // },

? ? // updated() {

? ? // ? ? console.log('更新完成1');

? ? // },

? ? //注意:在vue3中,對beforeDestroy和destroyed這兩個生命周期函數(shù),進行了重命名

? ? /* beforeDestroy() {

? ? ? ? console.log('銷毀之前');

? ? },

? ? destroyed() {

? ? ? ? console.log('銷毀完成'); ? ?

? ? }, */

? ? // 在vue3中,beforeUnmount 替換了 beforeDestroy;unmounted 替換了 destroyed

? ? // beforeUnmount() {

? ? // ? ? console.log('卸載之前1');

? ? // },

? ? // unmounted() {

? ? // ? ? console.log('卸載完成1');

? ? // },

? ? data() {

? ? ? ? return {

? ? ? ? ? ? count:1

? ? ? ? }

? ? },

? ? // setup()函數(shù),可以替代beforeCreate 和 created 這兩個生命周期函數(shù)

? ? setup() {

? ? ? ? console.log('setup');

? ? ? ? //組合式API生命周期函數(shù),會先與傳統(tǒng)的生命周期函數(shù)執(zhí)行

? ? ? ? onBeforeMount(()=>{

? ? ? ? ? ? console.log('掛載之前2');

? ? ? ? })

? ? ? ? onMounted(()=>{

? ? ? ? ? ? console.log('掛載完成2');

? ? ? ? })

? ? ? ? onBeforeUpdate(()=>{

? ? ? ? ? ? console.log('修改之前2');

? ? ? ? })

? ? ? ? onUpdated(()=>{

? ? ? ? ? ? console.log('修改完成2');

? ? ? ? })

? ? ? ? onBeforeUnmount(()=>{

? ? ? ? ? ? console.log('卸載之前2');

? ? ? ? })

? ? ? ? onUnmounted(()=>{

? ? ? ? ? ? console.log('卸載完成2');

? ? ? ? })

? ? }

}

3. toRef和toRefs

<h1>toRef和toRefs</h1>

<div class="stu">

? ? <h2>學(xué)生信息</h2>

? ? <ul>

? ? ? ? <li>姓名:{{name}}</li>

? ? ? ? <li>姓名:{{age}}</li>

? ? ? ? <li>車名:{{car.name}}</li>

? ? ? ? <li>車價:{{car.price}}</li>

? ? </ul>

</div>

import { reactive,toRef,toRefs } from 'vue'

export default {

? ? setup() {

? ? ? ? // 定義數(shù)據(jù)

? ? ? ? let stuData = reactive({

? ? ? ? ? ? name:'張三',

? ? ? ? ? ? age:20,

? ? ? ? ? ? car:{

? ? ? ? ? ? ? ? name:'大眾',

? ? ? ? ? ? ? ? price:'20W'

? ? ? ? ? ? }

? ? ? ? })

? ? ? ? return{

? ? ? ? ? ? // toRef()函數(shù),可以用來為一個 reactive 對象的屬性創(chuàng)建一個 ref

? ? ? ? ? ? // 這樣做的好處是,簡化了模板中的表達(dá)式。

? ? ? ? ? ? // toRef()函數(shù),需要傳兩個參數(shù):1.reactive 對象,2.具體的屬性名

? ? ? ? ? ? // name:toRef(stuData,'name'),

? ? ? ? ? ? // age:toRef(stuData,'age'),

? ? ? ? ? ? // car:toRef(stuData,'car')

? ? ? ? ? ? // 假如 reactive 對象中,有100個屬性,上面的操作要寫100次,所以,一般都直接用toRefs函數(shù)

? ? ? ? ? ? // toRefs函數(shù),把一個響應(yīng)式對象轉(zhuǎn)換成普通對象,該普通對象的每個 屬性 都是一個 ref

? ? ? ? ? ? ...toRefs(stuData)

? ? ? ? }

? ? }

}

4. 其他的組合式API

<h1>其他的組合式API</h1>

<div>

? ? 學(xué)生信息:{{stuData}}

? ? <br>

? ? <button @click="stuData.age++">修改年齡</button>

? ? <button @click="stuData.car.price++">修改車價</button>

</div>

<div>

? ? num3的值:{{num3}}

</div>

<div>

? ? 汽車信息:{{car}}

? ? <button @click="updateCar">修改汽車</button>

</div>

<div>

? ? 手機信息:{{phone}}

? ? <button @click="updatePhone">修改手機</button>

</div>

<div>

? ? 年齡:{{age}}

? ? <button @click="age++">年齡++</button>

</div>

import {ref,reactive,readonly,isRef,unref, shallowRef, isReactive, shallowReactive,customRef,toRaw, markRaw} from 'vue'

export default {

? ? setup() {

? ? ? ? // 定義數(shù)據(jù)

? ? ? ? // readonly()函數(shù),返回一份只讀數(shù)據(jù),這個只讀是“深層的”,內(nèi)部任何嵌套的屬性也都是只讀的

? ? ? ? let stuData = readonly({

? ? ? ? ? ? name:'張三',

? ? ? ? ? ? age:20,

? ? ? ? ? ? car:{

? ? ? ? ? ? ? ? name:'大眾',

? ? ? ? ? ? ? ? price:20

? ? ? ? ? ? }

? ? ? ? })

? ? ? ? let num1 = ref(100)

? ? ? ? let num2 = 200

? ? ? ? // isRef()函數(shù),檢查一個值是否為一個 ref 對象

? ? ? ? // isProxy()函數(shù),檢查一個對象是否是由 reactive 或者 readonly 方法創(chuàng)建的代理

? ? ? ? // isReactive()函數(shù),檢查一個對象是否是由 reactive 創(chuàng)建的響應(yīng)式代理

? ? ? ? // isReadonly()函數(shù),檢查一個對象是否是由 readonly 創(chuàng)建的只讀代理

? ? ? ? // let num3 = (isRef(num1)?num1.value:num1) + (isRef(num2)?num2.value:num2)

? ? ? ? // unref()函數(shù),如果參數(shù)是一個 ref 則返回它的 value,否則返回參數(shù)本身

? ? ? ? let num3 = unref(num1) + unref(num2)

? ? ? ? // ref() 返回的對象 value 屬性值是 reactive對象(代理對象)

? ? ? ? // shallowRef() 返回的對象 value 屬性值是 object對象(普通對象),不再具備任何響應(yīng)式了

? ? ? ? let car = shallowRef({

? ? ? ? ? ? name:'大眾',

? ? ? ? ? ? type:{

? ? ? ? ? ? ? ? typeName:'SUV'

? ? ? ? ? ? }

? ? ? ? })

? ? ? ? let updateCar = ()=>{

? ? ? ? ? ? // 由于value返回的是object對象,所以,這里不再具有響應(yīng)式

? ? ? ? ? ? car.value.name = '奔馳'

? ? ? ? ? ? car.value.type.typeName = '跑車'

? ? ? ? }

? ? ? ? // shallowReactive() 返回一個淺層的響應(yīng)式代理,只對對象的第一層屬性創(chuàng)建響應(yīng)式

? ? ? ? let phone = shallowReactive({

? ? ? ? ? ? name:'華為',

? ? ? ? ? ? type:{

? ? ? ? ? ? ? ? typeName:'滑蓋手機'

? ? ? ? ? ? }

? ? ? ? })

? ? ? ? // toRaw() 將代理對象轉(zhuǎn)為一個普通對象返回

? ? ? ? let phone2 = toRaw(phone)

? ? ? ? console.log(phone2);

? ? ? ? console.log('--------------------');

? ? ? ? //定義了一份數(shù)據(jù)

? ? ? ? // markRaw() 記一個對象為“永遠(yuǎn)不會轉(zhuǎn)為響應(yīng)式代理”

? ? ? ? let food = markRaw({

? ? ? ? ? ? name:'面包'

? ? ? ? })

? ? ? ? // 注意:food2就是一個普通對象

? ? ? ? let food2 = reactive(food)

? ? ? ? console.log(food2);

? ? ? ? let updatePhone = ()=>{

? ? ? ? ? ? //修改name,會觸發(fā)頁面更新

? ? ? ? ? ? // phone.name += "!"

? ? ? ? ? ? //修改type里面的屬性,不會觸發(fā)頁面更新

? ? ? ? ? ? phone.type.typeName += "!"

? ? ? ? }

? ? ? ? //自定義一個ref

? ? ? ? function useDebouncedRef(value, delay = 200) {

? ? ? ? ? ? let timeout

? ? ? ? ? ? // customRef(),用于自定義一個 ref

? ? ? ? ? ? return customRef((track, trigger) => {

? ? ? ? ? ? ? ? return {

? ? ? ? ? ? ? ? ? ? get() {

? ? ? ? ? ? ? ? ? ? ? ? track()

? ? ? ? ? ? ? ? ? ? ? ? return value

? ? ? ? ? ? ? ? ? ? },

? ? ? ? ? ? ? ? ? ? set(newValue) {

? ? ? ? ? ? ? ? ? ? ? ? clearTimeout(timeout)

? ? ? ? ? ? ? ? ? ? ? ? ? ? timeout = setTimeout(() => {

? ? ? ? ? ? ? ? ? ? ? ? ? ? value = newValue

? ? ? ? ? ? ? ? ? ? ? ? ? ? trigger()

? ? ? ? ? ? ? ? ? ? ? ? }, delay)

? ? ? ? ? ? ? ? ? ? },

? ? ? ? ? ? ? ? }

? ? ? ? ? ? })

? ? ? ? }

? ? ? ? let age = useDebouncedRef(20,2000)

? ? ? ? return {

? ? ? ? ? ? stuData,

? ? ? ? ? ? num3,

? ? ? ? ? ? car,

? ? ? ? ? ? updateCar,

? ? ? ? ? ? phone,

? ? ? ? ? ? updatePhone,

? ? ? ? ? ? age

? ? ? ? }

? ? }

}

四、組件傳值

1. 父子組件傳值

<Son1 :name="name" :age="age" :sex="sex"

? ? ? @updateName="name=$event" @updateAge="age=$event" @updateSex="sex=$event">

? ? <!-- 通過template組件指定具體的插槽 -->

? ? <template v-slot:one>

? ? ? ? <div>

? ? ? ? ? ? <button>按鈕1</button>

? ? ? ? </div>

? ? ? ? <p>HelloWorld</p>

? ? </template>

? ? <!-- #是v-slot:的簡寫 -->

? ? <template #two>

? ? ? ? <div>

? ? ? ? ? ? <button>按鈕2</button>

? ? ? ? </div>

? ? ? ? <p>你好世界</p>

? ? </template>

</Son1>

<h2>Son1</h2>

<div>

? ? 姓名:{{myName}}

? ? 年齡:{{myAge}}

? ? 性別:{{mySex}}

? ? <button @click="updateData">修改信息</button>

</div>

<!-- 插槽,定義多個插槽時,需要給插槽定義名稱:具名插槽 -->

<slot name="one"></slot>

<slot name="two"></slot>

<GrandSon1 />

import { ref } from 'vue';

import GrandSon1 from './GrandSon1.vue'

export default {

? name: "Son1",

? components:{

? ? ? GrandSon1

? },

? // 接收父組件傳過來的數(shù)據(jù)

? props:['name','age'],

? // Vue2中的方式 ?

? /* data() {

? ? ? return {

? ? ? ? ? myName:this.name,

? ? ? ? ? myAge:this.age,

? ? ? ? ? mySex:this.sex

? ? ? }

? },

? methods: {

? ? ? updateData(){

? ? ? ? ? this.myName = '李四'

? ? ? ? ? this.myAge = 30

? ? ? ? ? this.mySex = '女'

? ? ? ? ? this.$emit('updateName',this.myName)

? ? ? ? ? this.$emit('updateAge',this.myAge)

? ? ? ? ? this.$emit('updateSex',this.mySex)

? ? ? }

? }, */

? //setup函數(shù)中通過參數(shù)props接收父組件傳遞進來的參數(shù)

? //注意:props參數(shù)中,只會接收props選項中接收的參數(shù)

? //context參數(shù)里面有三個對象:attrs,emit,slots

? //attrs用于獲取沒有采用props選項接收的參數(shù)

? //emit用于觸發(fā)自定義事件

? //slots用于獲取插槽信息

? setup(props,{attrs,emit,slots}) {

? ? ? //slots對象返回的是插槽里面所有內(nèi)容的虛擬DOM

? ? ? console.log(slots.one()[0].children);

? ? ? //獲取姓名和年齡

? ? ? let myName = ref(props.name)

? ? ? let myAge = ref(props.age)

? ? ? //獲取性別

? ? ? let mySex = ref(attrs.sex)

? ? ? //修改數(shù)據(jù)的方法

? ? ? let updateData = ()=>{

? ? ? ? ? //修改自身數(shù)據(jù)

? ? ? ? ? myName.value = '李四'

? ? ? ? ? myAge.value = 30,

? ? ? ? ? mySex.value = '女'

? ? ? ? ? //觸發(fā)自定義事件,將最新數(shù)據(jù)回傳給父組件

? ? ? ? ? emit('updateName',myName.value)

? ? ? ? ? emit('updateAge',myAge.value)

? ? ? ? ? emit('updateSex',mySex.value)

? ? ? }

? ? ? return{

? ? ? ? ? myName,

? ? ? ? ? myAge,

? ? ? ? ? mySex,

? ? ? ? ? updateData

? ? ? }

? }

};

2. 祖孫組件傳值

import {reactive, provide} from 'vue'

let phone = reactive({

? ? name:'華為',

? ? price:5000

})

//provide將指定的數(shù)據(jù)添加為依賴數(shù)據(jù),讓后代組件可以直接使用

provide('phone',phone)

<h3>GrandSon1</h3>

<div>

? ? 手機信息:{{phone}}

? ? <button @click="updatePhone">修改手機</button>

</div>

import {inject} from 'vue'

// inject注入祖級組件中設(shè)置為依賴的數(shù)據(jù)

let phone = inject('phone')

let updatePhone = ()=>{

? ? phone.name = '蘋果'

? ? phone.price = 8000

}

return {

? ? phone,

? ? updatePhone,

}

3. v-model

<!-- Vue3可以通過v-model指令實現(xiàn)對多個數(shù)據(jù)的雙向綁定 -->

<Son2 v-model:name="name" v-model:age="age" v-model:sex="sex"

@click="testClick" />

<h2>Son2</h2>

<div>

? ? 姓名:{{myName}}

? ? 年齡:{{myAge}}

? ? 性別:{{mySex}}

? ? <button @click="updateData">修改信息</button>

? ? <button @click="emitClick">觸發(fā)一個click事件</button>

</div>

let testClick = (e)=>{

? ? alert(e)

}

import { ref } from 'vue';

export default {

? name: "Son2",

? //props選項接收父組件參數(shù)

? props:['name','age','sex'],

? //emits選項確定父組件可以觸發(fā)哪些事件

? //注意:因為click跟原生事件同名,如果不在emits里面配置的話,父組件會觸發(fā)兩次click事件

? emits:['click'],

? setup(props,{emit}) {

? ? ? let myName = ref(props.name)

? ? ? let myAge = ref(props.age)

? ? ? let mySex = ref(props.sex)

? ? ? let updateData = ()=>{

? ? ? ? ? myName.value = '謝娜'

? ? ? ? ? myAge.value = 35,

? ? ? ? ? mySex.value = '女'

? ? ? ? ? //注意:自定義事件名稱必須命名為update:屬性名

? ? ? ? ? //就可以實現(xiàn)對父組件中指定屬性的雙向綁定 ?

? ? ? ? ? emit('update:name',myName.value)

? ? ? ? ? emit('update:age',myAge.value)

? ? ? ? ? emit('update:sex',mySex.value)

? ? ? }

? ? ? let emitClick = ()=>{

? ? ? ? ? //觸發(fā)一個click事件

? ? ? ? ? emit('click','哈哈')

? ? ? }

? ? ? return{

? ? ? ? ? myName,

? ? ? ? ? myAge,

? ? ? ? ? mySex,

? ? ? ? ? updateData,

? ? ? ? ? emitClick

? ? ? }

? }

};

4. 異步組件

定義

<h2>Son3</h2>

<div>姓名:{{name}},年齡:{{age}}</div>

import {ref} from 'vue'

export default {

? name: "Son3",

? setup() {

? ? ? let name = ref('周杰倫')

? ? ? let age = ref(20)

? ? ? //注意:通常情況下,setup方法直接返回對象,不要返回Promise對象。

? ? ? return new Promise((resolve,reject)=>{

? ? ? ? ? setTimeout(() => {

? ? ? ? ? ? resolve({

? ? ? ? ? ? ? ? name,

? ? ? ? ? ? ? ? age

? ? ? ? ? ? })

? ? ? ? ? }, 2000);

? ? ? })

? }

使用

<!-- suspense用于在渲染異步組件時,顯示Loading... -->

<!-- 注意:異步加載的組件可以用suspense,也可以不用;

但是,如果組件中setup的返回值是一個Promise對象,該組件必須要用suspense -->

<suspense>

? ? <template #default>

? ? ? ? <Son3/>

? ? </template>

? ? <template #fallback>

? ? ? ? Loading...

? ? </template>

</suspense>

// defineAsyncComponent組合式API,用于定義異步組件

import {defineAsyncComponent} from 'vue'

// 異步導(dǎo)入組件

let Son3 = defineAsyncComponent(()=>import('./components/Son3.vue'))

5. teleport組件

<h2>Son4</h2>

<button @click="show=true">彈出</button>

<!-- teleport組件:官方起的名稱:瞬移。通過to屬性確定里面的元素移動到哪 -->

<teleport to="body">

? ? <div v-show="show" class="box">

? ? ? ? <button @click="show=false">關(guān)閉</button>

? ? </div>

</teleport>

五、vuex4 & vue-router4

1. 創(chuàng)建router對象

// createRouter方法,用于創(chuàng)建路由器對象

// createWebHashHistory方法,用于生成hash模式的路由,路由地址中包含一個#

// createWebHistory方法,用于生成history模式的路由

import {createRouter,createWebHashHistory} from 'vue-router'

// 創(chuàng)建當(dāng)前項目中的路由器對象

let router = createRouter({

? ? //定義路由模式

? ? history:createWebHashHistory(),

? ? //定義具體的路由信息

? ? routes:[

? ? ? ? //每一條路由信息,配置一個對象

? ? ? ? {

? ? ? ? ? ? path:'/',

? ? ? ? ? ? name:'home',

? ? ? ? ? ? component:()=>import('../views/Home.vue')

? ? ? ? },

? ? ? ? {

? ? ? ? ? ? path:'/store',

? ? ? ? ? ? name:'store',

? ? ? ? ? ? component:()=>import('../views/Store.vue')

? ? ? ? },

? ? ? ? {

? ? ? ? ? ? path:'/list/:id',

? ? ? ? ? ? props:true,

? ? ? ? ? ? name:'list',

? ? ? ? ? ? component:()=>import('../views/List.vue')

? ? ? ? },

? ? ? ? {

? ? ? ? ? ? path:'/news',

? ? ? ? ? ? name:'news',

? ? ? ? ? ? component:()=>import('../views/News.vue')

? ? ? ? },

? ? ? ? {

? ? ? ? ? ? path:'/page1',

? ? ? ? ? ? name:'page1',

? ? ? ? ? ? component:()=>import('../views/Page1.vue')

? ? ? ? },

? ? ? ? {

? ? ? ? ? ? // 注意:不可以寫通配符*

? ? ? ? ? ? // path:'*',

? ? ? ? ? ? path:'/:pathMatch(.*)*',

? ? ? ? ? ? name:'error404',

? ? ? ? ? ? component:()=>import('../views/Error404.vue')

? ? ? ? }

? ? ]

})

export default router

2. 使用router

//useRouter方法,返回當(dāng)前項目中的路由器對象

//useRoute方法,返回當(dāng)前路由信息對象

import {useRouter,useRoute} from 'vue-router'

//返回當(dāng)前項目中的路由器對象

let $router = useRouter()

//獲取當(dāng)前路由信息

let $route = useRoute()

//通過props,也能獲取都路由參數(shù)

props:['id']

//監(jiān)聽路由參數(shù)id

watch(()=>$route.params.id,(nval)=>{

? ? //清空數(shù)組

? ? showList.splice(0)

? ? //向數(shù)組中添加最新的數(shù)據(jù)

? ? showList.push(...list.filter(r=>r.typeId==$route.params.id))

},{

? ? //一上來,先執(zhí)行一次

? ? immediate:true

})

3. 創(chuàng)建store對象

// 從vuex中導(dǎo)入createStore方法,該方法,用于創(chuàng)建全局狀態(tài)管理對象

import { createStore } from 'vuex'

// 導(dǎo)入汽車模塊

import car from './modules/car.js'

// 創(chuàng)建一個全局狀態(tài)管理對象

let store = createStore({

? ? //定義狀態(tài)

? ? state:{

? ? ? ? firstName:'張',

? ? ? ? lastName:'三'

? ? },

? ? //定義圍繞狀態(tài)的計算屬性

? ? getters:{

? ? ? ? fullName(state){

? ? ? ? ? ? return state.firstName+'.'+state.lastName

? ? ? ? }

? ? },

? ? //定義同步方法

? ? mutations:{

? ? ? ? updateFirstName(state,val){

? ? ? ? ? ? state.firstName = val

? ? ? ? },

? ? ? ? updateLastName(state,val){

? ? ? ? ? ? state.lastName = val

? ? ? ? }

? ? },

? ? //定義異步方法

? ? actions:{

? ? ? ? updateFirstName(store,val){

? ? ? ? ? ? setTimeout(() => {

? ? ? ? ? ? ? ? store.commit('updateFirstName',val)

? ? ? ? ? ? }, 1000);

? ? ? ? },

? ? ? ? updateLastName(store,val){

? ? ? ? ? ? setTimeout(() => {

? ? ? ? ? ? ? ? store.commit('updateLastName',val)

? ? ? ? ? ? }, 1000);

? ? ? ? }

? ? },

? ? //模塊

? ? modules:{

? ? ? ?car

? ? }

})

//導(dǎo)出全局狀態(tài)管理對象

export default store

4. 使用store

//useStore方法,返回當(dāng)前項目中的全局狀態(tài)管理對象

import { useStore } from "vuex";

// 獲取全局狀態(tài)管理對象

let $store = useStore();

let firstName = computed(() => {

? ? return $store.state.firstName;

});

let lastName = computed(() => {

? ? return $store.state.lastName;

});

let fullName = computed(() => {

? ? return $store.getters.fullName;

});

let carName = computed(() => {

? ? return $store.state.car.carName;

});

let address = computed(() => {

? ? return $store.state.car.address;

});

let carInfo = computed(() => {

? ? return $store.getters["car/carInfo"];

});

function updateFirstName() {

? ? //調(diào)用mutations里面的方法,修改姓

? ? $store.commit("updateFirstName", "李");

}

function updateLastName() {

? ? //調(diào)用actions里面的方法,修改名

? ? $store.dispatch("updateLastName", "四");

}

function updateCarName() {

? ? //調(diào)用mutations里面的方法,修改車名

? ? $store.commit("car/updateCarName", "賓利");

}

function updateCarAddress() {

? ? //調(diào)用actions里面的方法,修改地址

? ? $store.dispatch("car/updateCarAddress", "英國");

}

5. 注冊

// 導(dǎo)入當(dāng)前項目中創(chuàng)建的全局狀態(tài)管理對象

import store from './store'

// 導(dǎo)入當(dāng)前項目中創(chuàng)建的路由器對象

import router from './router'

// 使用createApp方法創(chuàng)建一個Vue實例,該方法的參數(shù)是App組件,表示渲染App組件

// use方法,用于給當(dāng)前vue實例添加功能

// mount方法,用于將渲染后的內(nèi)容,掛載到指定的容器中

createApp(App).use(store).use(router).mount('#app')

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容