一、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')