1. Vue3簡介
-
2020年9月18日,
Vue.js
發布版3.0
版本,代號:One Piece
(n -
經歷了:4800+次提交、40+個RFC、600+次PR、300+貢獻者
-
官方發版地址:Release v3.0.0 One Piece · vuejs/core
-
截止2023年10月,最新的公開版本為:
3.3.4
1.1. 【性能的提升】
-
打包大小減少
41%
。 -
初次渲染快
55%
, 更新渲染快133%
。 -
內存減少
54%
。
1.2.【 源碼的升級】
-
使用
Proxy
代替defineProperty
實現響應式。 -
重寫虛擬
DOM
的實現和Tree-Shaking
。
1.3. 【擁抱TypeScript】
-
Vue3
可以更好的支持TypeScript
。
1.4. 【新的特性】
-
Composition API
(組合API
):-
setup
-
ref
與reactive
-
computed
與watch
......
-
-
新的內置組件:
-
Fragment
-
Teleport
-
Suspense
......
-
-
其他改變:
-
新的生命周期鉤子
-
data
選項應始終被聲明為一個函數 -
移除
keyCode
支持作為v-on
的修飾符......
-
2. 創建Vue3工程
2.1. 【基于 vue-cli 創建】
點擊查看官方文檔
備注:目前
vue-cli
已處于維護模式,官方推薦基于Vite
創建項目。
## 查看@vue/cli版本,確保@vue/cli版本在4.5.0以上
vue --version
?
## 安裝或者升級你的@vue/cli
npm install -g @vue/cli
?
## 執行創建命令
vue create vue_test
?
## 隨后選擇3.x
## Choose a version of Vue.js that you want to start the project with (Use arrow keys)
## > 3.x
## ? 2.x
?
## 啟動
cd vue_test
npm run serve
2.2. 【基于 vite 創建】(推薦)
vite
是新一代前端構建工具,官網地址:https://vitejs.cn,vite
的優勢如下:
-
輕量快速的熱重載(
HMR
),能實現極速的服務啟動。 -
對
TypeScript
、JSX
、CSS
等支持開箱即用。 -
真正的按需編譯,不再等待整個應用編譯完成。
-
具體操作如下(點擊查看官方文檔)
## 1.創建命令
npm create vue@latest
?
## 2.具體配置
## 配置項目名稱
√ Project name: vue3_test
## 是否添加TypeScript支持
√ Add TypeScript? ?Yes
## 是否添加JSX支持
√ Add JSX Support? ?No
## 是否添加路由環境
√ Add Vue Router for Single Page Application development? ?No
## 是否添加pinia環境
√ Add Pinia for state management? ?No
## 是否添加單元測試
√ Add Vitest for Unit Testing? ?No
## 是否添加端到端測試方案
√ Add an End-to-End Testing Solution? ? No
## 是否添加ESLint語法檢查
√ Add ESLint for code quality? ?Yes
## 是否添加Prettiert代碼格式化
√ Add Prettier for code formatting? ?No
自己動手寫一個app.vue
<template><div class="app"><h1>你好啊!</h1></div>
</template><script lang="ts">export default {name:'App' //組件名}
</script><style>.app {background-color: #ddd;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;}
</style>
總結:
-
Vite
項目中,index.html
是項目的入口文件,在項目最外層。 -
加載
index.html
后,Vite
解析<script type="module" src="xxx">
指向的JavaScript
。 -
Vue3
中是通過createApp
函數創建一個應用實例。
2.3. 【一個簡單的效果】基于Vue2選項式編程
Vue3
向下兼容Vue2
語法,且Vue3
中的模板中可以沒有根標簽
我們自己創建一個vue來呈現自己的效果
在src包的component創建Person.vue
<template><div class="person"><h2>姓名:{{name}}</h2><h2>年齡:{{age}}</h2><button @click="changeName">修改名字</button><button @click="changeAge">年齡+1</button><button @click="showTel">點我查看聯系方式</button></div></template><script lang="ts">export default {name:'App',data() {return {name:'張三',age:18,tel:'13888888888'}},methods:{changeName(){this.name = 'zhang-san'},changeAge(){this.age += 1},showTel(){alert(this.tel)}},}</script>
然后在app.vue導入這個vue
<template><div class="app"><h1>你好啊!</h1></div><Person/> #使用剛剛我們創建的vue
</template><script lang="ts">import Person from './components/Person.vue'; //importexport default {name:'App', //組件名components:{Person} //注冊組件}
</script><style>.app {background-color: #ddd;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;}
</style>
效果
3. Vue3核心語法
3.1. 【OptionsAPI 與 CompositionAPI】
-
Vue2
的API
設計是Options
(配置)選項式編程風格的。 -
Vue3
的API
設計是Composition
(組合)組合式風格的。
Options API 的弊端
Options
類型的 API
,數據、方法、計算屬性等,是分散在:data
、methods
、computed
中的,若想新增或者修改一個需求,就需要分別修改:data
、methods
、computed
,不便于維護和復用。
Composition API 的優勢
可以用函數的方式,更加優雅的組織代碼,讓相關功能的代碼更加有序的組織在一起。
3.2. 【拉開序幕的 setup】
setup 概述
setup
是Vue3
中一個新的配置項,值是一個函數,它是 Composition API
“表演的舞臺”,組件中所用到的:數據、方法、計算屬性、監視......等等,均配置在setup
中。
特點如下:
-
setup
函數返回的對象中的內容,可直接在模板中使用。 -
setup
中訪問this
是undefined
。所以不能用this -
setup
函數會在beforeCreate
之前調用,它是“領先”所有鉤子執行的。
<template><div class="person"><h2>姓名:{{name}}</h2><h2>年齡:{{age}}</h2><button @click="changeName">修改名字</button><button @click="changeAge">年齡+1</button><button @click="showTel">點我查看聯系方式</button></div>
</template>
?
<script lang="ts">export default {name:'Person',setup(){// 數據,原來寫在data中(注意:此時的name、age、tel數據都不是響應式數據)let name = '張三'let age = 18let tel = '13888888888'
?// 方法,原來寫在methods中function changeName(){name = 'zhang-san' //注意:此時這么修改name頁面是不變化的console.log(name)}function changeAge(){age += 1 //注意:此時這么修改age頁面是不變化的console.log(age)}function showTel(){alert(tel)}
?// 返回一個對象,對象中的內容,模板中可以直接使用return {name,age,tel,changeName,changeAge,showTel}}}
</script>
setup 的返回值
-
若返回一個對象:則對象中的:屬性、方法等,在模板中均可以直接使用(重點關注)。
-
若返回一個函數:則可以自定義渲染內容,代碼如下:
setup(){return ()=> '你好啊!'
}
setup 與 Options API 的關系
-
Vue2
的配置(data
、methos
......)中可以訪問到setup
中的屬性、方法。 -
但在
setup
中不能訪問到Vue2
的配置(data
、methos
......)。 -
如果與
Vue2
沖突,則setup
優先。
setup 語法糖
setup
函數有一個語法糖,就是用來簡化代碼的。這個語法糖,可以讓我們把setup
獨立出去,這樣我們就不用手動寫return將數據交到前端。代碼如下:
<template><div class="person"><h2>姓名:{{name}}</h2><h2>年齡:{{age}}</h2><button @click="changName">修改名字</button><button @click="changAge">年齡+1</button><button @click="showTel">點我查看聯系方式</button></div>
</template>
?
<script lang="ts">export default {name:'Person',}
</script>
?
<!-- 下面的寫法是setup語法糖 -->
<script setup lang="ts">console.log(this) //undefined// 數據(注意:此時的name、age、tel都不是響應式數據)let name = '張三'let age = 18let tel = '13888888888'
?// 方法function changName(){name = '李四'//注意:此時這么修改name頁面是不變化的}function changAge(){console.log(age)age += 1 //注意:此時這么修改age頁面是不變化的}function showTel(){alert(tel)}
</script>
擴展:上述代碼,還需要編寫一個不寫setup
的script
標簽,去指定組件名字,比較麻煩,我們可以借助vite
中的插件簡化
第一步:npm i vite-plugin-vue-setup-extend -D第二步:vite.config.tsimport { defineConfig } from 'vite'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
?
export default defineConfig({plugins: [ VueSetupExtend() ]
})
-
第三步:將原來文件里面的setup的script改成
<script setup lang="ts" name="Person">
3.3. 【ref 創建:基本類型的響應式數據】
-
作用:定義響應式變量。
-
語法:
let xxx = ref(初始值)
。 -
返回值:一個
RefImpl
的實例對象,簡稱ref對象
或ref
,ref
對象的value
屬性是響應式的。 -
注意點:
-
JS
中操作數據需要:xxx.value
,但模板中不需要.value
,直接使用即可。 -
對于
let name = ref('張三')
來說,name
不是響應式的,name.value
是響應式的。
-
<template><div class="person"><h2>姓名:{{name}}</h2><h2>年齡:{{age}}</h2><button @click="changeName">修改名字</button><button @click="changeAge">年齡+1</button><button @click="showTel">點我查看聯系方式</button></div>
</template>
?
<script setup lang="ts" name="Person">import {ref} from 'vue' //第一步要引入ref// name和age是一個RefImpl的實例對象,簡稱ref對象,它們的value屬性是響應式的。let name = ref('張三')let age = ref(18)// tel就是一個普通的字符串,不是響應式的let tel = '13888888888'
?function changeName(){// JS中操作ref對象時候需要.valuename.value = '李四'console.log(name.value)
?// 注意:name不是響應式的,name.value是響應式的,所以如下代碼并不會引起頁面的更新。// name = ref('zhang-san')}function changeAge(){// JS中操作ref對象時候需要.valueage.value += 1 console.log(age.value)}function showTel(){alert(tel)}
</script>
3.4. 【reactive 創建:對象類型的響應式數據】
-
作用:定義一個響應式對象(基本類型不要用它,要用
ref
,否則報錯) -
語法:
let 響應式對象= reactive(源對象)
。 -
返回值:一個
Proxy
的實例對象,簡稱:響應式對象。 -
注意點:
reactive
定義的響應式數據是“深層次”的。
<template><div class="person"><h2>汽車信息:一臺{{ car.brand }}汽車,價值{{ car.price }}萬</h2><h2>游戲列表:</h2><ul><li v-for="g in games" :key="g.id">{{ g.name }}</li></ul><h2>測試:{{obj.a.b.c.d}}</h2><button @click="changeCarPrice">修改汽車價格</button><button @click="changeFirstGame">修改第一游戲</button><button @click="test">測試</button></div>
</template>
?
<script lang="ts" setup name="Person">
import { reactive } from 'vue'
?
// 數據
let car = reactive({ brand: '奔馳', price: 100 })
let games = reactive([{ id: 'ahsgdyfa01', name: '英雄聯盟' },{ id: 'ahsgdyfa02', name: '王者榮耀' },{ id: 'ahsgdyfa03', name: '原神' }
])
let obj = reactive({a:{b:{c:{d:666}}}
})
?
function changeCarPrice() {car.price += 10
}
function changeFirstGame() {games[0].name = '流星蝴蝶劍'
}
function test(){obj.a.b.c.d = 999
}
</script>
3.5. 【ref 創建:對象類型的響應式數據】
-
其實
ref
接收的數據可以是:基本類型、對象類型。 -
若
ref
接收的是對象類型,內部其實也是調用了reactive
函數。ref里面的value就是生成的proxy代理對象。所以我們用ref實現對象類型的響應式數據,要去訪問對象的value,然后才是具體成員變量。
<template><div class="person"><h2>汽車信息:一臺{{ car.brand }}汽車,價值{{ car.price }}萬</h2><h2>游戲列表:</h2><ul><li v-for="g in games" :key="g.id">{{ g.name }}</li></ul><h2>測試:{{obj.a.b.c.d}}</h2><button @click="changeCarPrice">修改汽車價格</button><button @click="changeFirstGame">修改第一游戲</button><button @click="test">測試</button></div>
</template>
?
<script lang="ts" setup name="Person">
import { ref } from 'vue'
?
// 數據
let car = ref({ brand: '奔馳', price: 100 })
let games = ref([{ id: 'ahsgdyfa01', name: '英雄聯盟' },{ id: 'ahsgdyfa02', name: '王者榮耀' },{ id: 'ahsgdyfa03', name: '原神' }
])
let obj = ref({a:{b:{c:{d:666}}}
})
?
console.log(car)
?
function changeCarPrice() {car.value.price += 10
}
function changeFirstGame() {games.value[0].name = '流星蝴蝶劍'
}
function test(){obj.value.a.b.c.d = 999
}
</script>
3.6. 【ref 對比 reactive】
宏觀角度看:
ref
用來定義:基本類型數據、對象類型數據;
reactive
用來定義:對象類型數據。
-
區別:
ref
創建的變量必須使用.value
(可以使用volar
插件自動添加.value
)。
reactive
重新分配一個新對象,會失去響應式(可以使用Object.assign
去整體替換)。
-
使用原則:
若需要一個基本類型的響應式數據,必須使用
ref
。若需要一個響應式對象,層級不深,
ref
、reactive
都可以。若需要一個響應式對象,且層級較深,推薦使用
reactive
。
3.7. 【toRefs 與 toRef】
-
作用:將一個響應式對象中的每一個屬性,轉換為
ref
對象。就是說,我可以將reactive包括的對象里面的每一組變量都變成ref對象。 -
如果沒有toRefs,那么我們直接let {age, name} = Person,那么實際上是執行了let age = Person.age, let name = Person.name,此時的age和name不是響應式的。
-
備注:
toRefs
與toRef
功能一致,但toRefs
可以批量轉換。 -
語法如下:
<template><div class="person"><h2>姓名:{{person.name}}</h2><h2>年齡:{{person.age}}</h2><h2>性別:{{person.gender}}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年齡</button><button @click="changeGender">修改性別</button></div>
</template>
?
<script lang="ts" setup name="Person">import {ref,reactive,toRefs,toRef} from 'vue'
?// 數據let person = reactive({name:'張三', age:18, gender:'男'})// 通過toRefs將person對象中的n個屬性批量取出,且依然保持響應式的能力let {name,gender} = ?toRefs(person)// 通過toRef將person對象中的gender屬性取出,且依然保持響應式的能力let age = toRef(person,'age')
?// 方法function changeName(){name.value += '~'}function changeAge(){age.value += 1}function changeGender(){gender.value = '女'}
</script>
效果
3.8. 【computed】
作用:根據已有數據計算出新數據(和Vue2
中的computed
作用一致)。
我們輸入firstName和LastName就可以通過conputed得到fullName
<template><div class="person">姓:<input type="text" v-model="firstName"> <br>名:<input type="text" v-model="lastName"> <br><button @click="changeFullName">將全名改為li-si</button> <br>全名:<span>{{fullName}}</span> <br>全名:<span>{{fullName}}</span> <br>全名:<span>{{fullName}}</span> <br>全名:<span>{{fullName}}</span> <br>全名:<span>{{fullName}}</span> <br>全名:<span>{{fullName}}</span> <br></div>
</template><script lang="ts" setup name="Person">import {ref,computed} from 'vue'let firstName = ref('zhang')let lastName = ref('san')// fullName是一個計算屬性,且是只讀的/* let fullName = computed(()=>{console.log(1)return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value}) */// fullName是一個計算屬性,可讀可寫let fullName = computed({// 當fullName被讀取時,get調用get(){return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value},// 當fullName被修改時,set調用,且會收到修改的值set(val){const [str1,str2] = val.split('-')firstName.value = str1lastName.value = str2}})function changeFullName(){fullName.value = 'li-si'}
</script><style scoped>.person {background-color: skyblue;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;}button {margin: 0 5px;}li {font-size: 20px;}
</style>
3.9.【watch】
-
作用:監視數據的變化(和
Vue2
中的watch
作用一致) -
特點:
Vue3
中的watch
只能監視以下四種數據:
ref
定義的數據。
reactive
定義的數據。函數返回一個值(
getter
函數)。一個包含上述內容的數組。
我們在Vue3
中使用watch
的時候,通常會遇到以下幾種情況:
情況一
監視ref
定義的【基本類型】數據:直接寫數據名即可,監視的是其value
值的改變。注意,不是ref定義的數據的value
注意watch的參數和函數的書寫,他的返回值是停止監視函數。
<template><div class="person"><h1>情況一:監視【ref】定義的【基本類型】數據</h1><h2>當前求和為:{{sum}}</h2><button @click="changeSum">點我sum+1</button></div>
</template>
?
<script lang="ts" setup name="Person">import {ref,watch} from 'vue'// 數據let sum = ref(0)// 方法function changeSum(){sum.value += 1}// 監視,情況一:監視【ref】定義的【基本類型】數據const stopWatch = watch(sum,(newValue,oldValue)=>{console.log('sum變化了',newValue,oldValue)if(newValue >= 10){stopWatch()}})
</script>
情況二
監視ref
定義的【對象類型】數據:直接寫數據名,監視的是對象的【地址值】,若想監視對象內部的數據,要手動開啟深度監視,就是watch配置第三個參數,這個參數就是用來配置配置選項的。
注意:
若修改的是
ref
定義的對象中的屬性,newValue
和oldValue
都是新值,因為它們是同一個對象。因為地址一樣,地址里面的屬性也就一樣若修改整個
ref
定義的對象,newValue
是新值,oldValue
是舊值,因為不是同一個對象了。原因就是對象的地址變化了,所以檢測地址里面的屬性前后就不一樣
<template><div class="person"><h1>情況二:監視【ref】定義的【對象類型】數據</h1><h2>姓名:{{ person.name }}</h2><h2>年齡:{{ person.age }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年齡</button><button @click="changePerson">修改整個人</button></div>
</template>
?
<script lang="ts" setup name="Person">import {ref,watch} from 'vue'// 數據let person = ref({name:'張三',age:18})// 方法function changeName(){person.value.name += '~'}function changeAge(){person.value.age += 1}function changePerson(){person.value = {name:'李四',age:90}}/* 監視,情況一:監視【ref】定義的【對象類型】數據,監視的是對象的地址值,若想監視對象內部屬性的變化,需要手動開啟深度監視watch的第一個參數是:被監視的數據watch的第二個參數是:監視的回調watch的第三個參數是:配置對象(deep、immediate等等.....) */watch(person,(newValue,oldValue)=>{console.log('person變化了',newValue,oldValue)},{deep:true})</script>
情況三
監視reactive
定義的【對象類型】數據,且默認開啟了深度監視。
<template><div class="person"><h1>情況三:監視【reactive】定義的【對象類型】數據</h1><h2>姓名:{{ person.name }}</h2><h2>年齡:{{ person.age }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年齡</button><button @click="changePerson">修改整個人</button><hr><h2>測試:{{obj.a.b.c}}</h2><button @click="test">修改obj.a.b.c</button></div>
</template>
?
<script lang="ts" setup name="Person">import {reactive,watch} from 'vue'// 數據let person = reactive({name:'張三',age:18})let obj = reactive({a:{b:{c:666}}})// 方法function changeName(){person.name += '~'}function changeAge(){person.age += 1}function changePerson(){Object.assign(person,{name:'李四',age:80})}function test(){obj.a.b.c = 888}
?// 監視,情況三:監視【reactive】定義的【對象類型】數據,且默認是開啟深度監視的watch(person,(newValue,oldValue)=>{console.log('person變化了',newValue,oldValue)})watch(obj,(newValue,oldValue)=>{console.log('Obj變化了',newValue,oldValue)})
</script>
情況四
監視ref
或reactive
定義的【對象類型】數據中的某個屬性,注意點如下:
-
若該屬性值不是【對象類型】,需要寫成函數形式。官方說是getter函數,實際上就寫一個函數return要監聽的字段
-
若該屬性值是依然是【對象類型】,可直接編,也可寫成函數,建議寫成函數。
結論:監視的要是對象里的屬性,那么最好寫函數式,注意點:若是對象監視的是地址值,需要關注對象內部,需要手動開啟深度監視。
<template><div class="person"><h1>情況四:監視【ref】或【reactive】定義的【對象類型】數據中的某個屬性</h1><h2>姓名:{{ person.name }}</h2><h2>年齡:{{ person.age }}</h2><h2>汽車:{{ person.car.c1 }}、{{ person.car.c2 }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年齡</button><button @click="changeC1">修改第一臺車</button><button @click="changeC2">修改第二臺車</button><button @click="changeCar">修改整個車</button></div>
</template>
?
<script lang="ts" setup name="Person">import {reactive,watch} from 'vue'
?// 數據let person = reactive({name:'張三',age:18,car:{c1:'奔馳',c2:'寶馬'}})// 方法function changeName(){person.name += '~'}function changeAge(){person.age += 1}function changeC1(){person.car.c1 = '奧迪'}function changeC2(){person.car.c2 = '大眾'}function changeCar(){person.car = {c1:'雅迪',c2:'愛瑪'}}
?// 監視,情況四:監視響應式對象中的某個屬性,且該屬性是基本類型的,要寫成函數式/* watch(()=> person.name,(newValue,oldValue)=>{console.log('person.name變化了',newValue,oldValue)}) */
?// 監視,情況四:監視響應式對象中的某個屬性,且該屬性是對象類型的,可以直接寫,也能寫函數,更推薦寫函數watch(()=>person.car,(newValue,oldValue)=>{console.log('person.car變化了',newValue,oldValue)},{deep:true})
</script>
情況五
監視上述的多個數據
<template><div class="person"><h1>情況五:監視上述的多個數據</h1><h2>姓名:{{ person.name }}</h2><h2>年齡:{{ person.age }}</h2><h2>汽車:{{ person.car.c1 }}、{{ person.car.c2 }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年齡</button><button @click="changeC1">修改第一臺車</button><button @click="changeC2">修改第二臺車</button><button @click="changeCar">修改整個車</button></div>
</template>
?
<script lang="ts" setup name="Person">import {reactive,watch} from 'vue'
?// 數據let person = reactive({name:'張三',age:18,car:{c1:'奔馳',c2:'寶馬'}})// 方法function changeName(){person.name += '~'}function changeAge(){person.age += 1}function changeC1(){person.car.c1 = '奧迪'}function changeC2(){person.car.c2 = '大眾'}function changeCar(){person.car = {c1:'雅迪',c2:'愛瑪'}}
?// 監視,情況五:監視上述的多個數據watch([()=>person.name,person.car],(newValue,oldValue)=>{console.log('person.car變化了',newValue,oldValue)},{deep:true})
?
</script>
3.10. 【watchEffect】
-
官網:立即運行一個函數,同時響應式地追蹤其依賴,并在依賴更改時重新執行該函數。
-
watch
對比watchEffect
-
都能監聽響應式數據的變化,不同的是監聽數據變化的方式不同
-
watch
:要明確指出監視的數據 -
watchEffect
:不用明確指出監視的數據(函數中用到哪些屬性,那就監視哪些屬性)。
-
-
示例代碼:
<template><div class="person"><h1>需求:水溫達到50℃,或水位達到20cm,則聯系服務器</h1><h2 id="demo">水溫:{{temp}}</h2><h2>水位:{{height}}</h2><button @click="changePrice">水溫+1</button><button @click="changeSum">水位+10</button></div>
</template>
?
<script lang="ts" setup name="Person">import {ref,watch,watchEffect} from 'vue'// 數據let temp = ref(0)let height = ref(0)
?// 方法function changePrice(){temp.value += 10}function changeSum(){height.value += 1}
?// 用watch實現,需要明確的指出要監視:temp、heightwatch([temp,height],(value)=>{// 從value中獲取最新的temp值、height值const [newTemp,newHeight] = value// 室溫達到50℃,或水位達到20cm,立刻聯系服務器if(newTemp >= 50 || newHeight >= 20){console.log('聯系服務器')}})
?// 用watchEffect實現,不用const stopWtach = watchEffect(()=>{// 室溫達到50℃,或水位達到20cm,立刻聯系服務器if(temp.value >= 50 || height.value >= 20){console.log(document.getElementById('demo')?.innerText)console.log('聯系服務器')}// 水溫達到100,或水位達到50,取消監視if(temp.value === 100 || height.value === 50){console.log('清理了')stopWtach()}})
</script>
3.11. 【標簽的 ref 屬性】
作用:用于注冊模板引用。我們獲取模塊可以用id,class,標簽本身等等,這種就是模塊的引用,ref和他們的功能相當
用在普通
DOM
標簽上,獲取的是DOM
節點。用在組件標簽上,獲取的是組件實例對象。
用在普通DOM
標簽上:
<template><div class="person"><h1 ref="title1">尚硅谷</h1><h2 ref="title2">前端</h2><h3 ref="title3">Vue</h3><input type="text" ref="inpt"> <br><br><button @click="showLog">點我打印內容</button></div>
</template>
?
<script lang="ts" setup name="Person">import {ref} from 'vue'let title1 = ref()let title2 = ref()let title3 = ref()
?function showLog(){// 通過id獲取元素const t1 = document.getElementById('title1')// 打印內容console.log((t1 as HTMLElement).innerText)console.log((<HTMLElement>t1).innerText)console.log(t1?.innerText)/************************************/// 通過ref獲取元素console.log(title1.value)console.log(title2.value)console.log(title3.value)}
</script>
用在組件標簽上:
<!-- 父組件App.vue -->
<template><Person ref="ren"/><button @click="test">測試</button>
</template>
?
<script lang="ts" setup name="App">import Person from './components/Person.vue'import {ref} from 'vue'
?let ren = ref()
?function test(){console.log(ren.value.name)console.log(ren.value.age)}
</script>
?
?
<!-- 子組件Person.vue中要使用defineExpose暴露內容 -->
<script lang="ts" setup name="Person">import {ref,defineExpose} from 'vue'// 數據let name = ref('張三')let age = ref(18)/****************************//****************************/// 使用defineExpose將組件中的數據交給外部defineExpose({name,age})
</script>
3.12. 【props】
// 定義一個接口,限制每個Person對象的格式
export interface PersonInter {
id:string,
name:string,age:number
}
?
// 定義一個自定義類型Persons
export type Persons = Array<PersonInter>
App.vue中代碼:<template><Person :list="persons"/>
</template><script lang="ts" setup name="App">
import Person from './components/Person.vue'
import {reactive} from 'vue'import {type Persons} from './types'let persons = reactive<Persons>([{id:'e98219e12',name:'張三',age:18},{id:'e98219e13',name:'李四',age:19},{id:'e98219e14',name:'王五',age:20}])
</script>
Person.vue中代碼:<template>
<div class="person">
<ul><li v-for="item in list" :key="item.id">{{item.name}}--{{item.age}}</li></ul>
</div>
</template>
?
<script lang="ts" setup name="Person">
import {defineProps} from 'vue'
import {type PersonInter} from '@/types'
?
// 第一種寫法:僅接收
// const props = defineProps(['list'])
?
// 第二種寫法:接收+限制類型
// defineProps<{list:Persons}>()
?
// 第三種寫法:接收+限制類型+指定默認值+限制必要性
let props = withDefaults(defineProps<{list?:Persons}>(),{list:()=>[{id:'asdasg01',name:'小豬佩奇',age:18}]
})
console.log(props)
</script>
3.13. 【生命周期】
概念:Vue
組件實例在創建時要經歷一系列的初始化步驟,在此過程中Vue
會在合適的時機,調用特定的函數,從而讓開發者有機會在特定階段運行自己的代碼,這些特定的函數統稱為:生命周期鉤子
規律:
生命周期整體分為四個階段,分別是:創建、掛載、更新、銷毀,每個階段都有兩個鉤子,一前一后。
Vue2
的生命周期
創建階段:
beforeCreate
、created
掛載階段:
beforeMount
、mounted
更新階段:
beforeUpdate
、updated
銷毀階段:
beforeDestroy
、destroyed
Vue3
的生命周期
創建階段:
setup
掛載階段:
onBeforeMount
、onMounted
更新階段:
onBeforeUpdate
、onUpdated
卸載階段:
onBeforeUnmount
、onUnmounted
常用的鉤子:onMounted
(掛載完畢)、onUpdated
(更新完畢)、onBeforeUnmount
(卸載之前)
<template><div class="person"><h2>當前求和為:{{ sum }}</h2><button @click="changeSum">點我sum+1</button></div>
</template>
?
<!-- vue3寫法 -->
<script lang="ts" setup name="Person">import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'
?// 數據let sum = ref(0)// 方法function changeSum() {sum.value += 1}console.log('setup')// 生命周期鉤子onBeforeMount(()=>{console.log('掛載之前')})onMounted(()=>{console.log('掛載完畢')})onBeforeUpdate(()=>{console.log('更新之前')})onUpdated(()=>{console.log('更新完畢')})onBeforeUnmount(()=>{console.log('卸載之前')})onUnmounted(()=>{console.log('卸載完畢')})
</script>
4. 路由
對路由的理解
Vue 3 中的路由(Vue Router)主要用途是實現單頁面應用(SPA)的頁面切換功能,同時提升用戶體驗和開發效率。以下是它的一些主要作用:
1. 實現頁面切換而無需重新加載頁面
-
在傳統的多頁面應用(MPA)中,每次點擊鏈接都會重新加載整個頁面,這會導致頁面閃爍、加載時間變長,并且用戶體驗較差。
-
Vue Router 通過在前端攔截鏈接跳轉,動態切換頁面內容,而無需重新加載整個頁面。這樣可以顯著提升頁面的響應速度和用戶體驗。
2. 支持動態路由和嵌套路由
-
動態路由:允許在路徑中傳遞參數。例如,
/user/:id
可以通過路徑動態傳遞用戶 ID,方便實現用戶詳情頁等功能。 -
嵌套路由:可以定義組件內部的子路由,實現更復雜的頁面結構。例如,一個用戶頁面可以包含多個子頁面(如用戶資料、用戶訂單等),通過嵌套路由可以方便地管理這些子頁面。
3. 支持多種路由模式
-
History 模式:使用 HTML5 的 History API,URL 更友好(如
/about
),適合需要更美觀 URL 的場景。 -
Hash 模式:通過 URL 的哈希值(
#
)來實現路由切換(如/#/about
)。這種模式兼容性更好,適合一些老舊瀏覽器或搜索引擎優化要求不高的場景。 -
Abstract 模式:主要用于非瀏覽器環境(如 Node.js),適合一些特殊場景。
總的來說,Vue Router 是 Vue.js 中實現單頁面應用的核心工具,它不僅提供了強大的路由功能,還提升了用戶體驗和開發效率。
一個路由切換的實現
先決條件,我們要去安裝vue-router,要準備好!!!
最終實現的效果
我們有一個導航區,有一個展示區,還有一個標題頭。導航區有三個組件,分別點擊有高亮效果并實現切換。
子組件的定義
就是普通定義組件一樣,這里面不涉及路由的知識
關于組件
<template><div class="about"><h2>大家好,歡迎來到尚硅谷直播間</h2></div>
</template><script setup lang="ts" name="About"></script><style scoped>
.about {display: flex;justify-content: center;align-items: center;height: 100%;color: rgb(85, 84, 84);font-size: 18px;
}
</style>
首頁組件
<template><div class="home"><img src="http://www.atguigu.com/images/index_new/logo.png" alt=""></div>
</template><script setup lang="ts" name="Home"></script><style scoped>.home {display: flex;justify-content: center;align-items: center;height: 100%;}
</style>
新聞組件
<template><div class="news"><ul><li><a href="#">新聞001</a></li><li><a href="#">新聞002</a></li><li><a href="#">新聞003</a></li><li><a href="#">新聞004</a></li></ul></div>
</template><script setup lang="ts" name="News"></script><style scoped>
/* 新聞 */
.news {padding: 0 20px;display: flex;justify-content: space-between;height: 100%;
}
.news ul {margin-top: 30px;list-style: none;padding-left: 10px;
}
.news li>a {font-size: 18px;line-height: 40px;text-decoration: none;color: #64967E;text-shadow: 0 0 1px rgb(0, 84, 0);
}
.news-content {width: 70%;height: 90%;border: 1px solid;margin-top: 20px;border-radius: 10px;
}
</style>
App.vue
<RouterLink>
和 <RouterView>
-
<RouterLink>
:-
用于創建導航鏈接,類似于HTML中的
<a>
標簽,但專門用于Vue Router。 -
在代碼中,
<RouterLink>
用于定義導航菜單,用戶可以通過點擊這些鏈接在不同的路由之間切換。 -
to
屬性指定了鏈接的目標路由路徑,例如to="/home"
表示導航到/home
路徑。 -
active-class
屬性用于自定義激活狀態的鏈接的CSS類名。在這個例子中,當鏈接被激活時,會應用xiaozhupeiqi
類。
-
-
<RouterView>
:-
用于顯示當前路由對應的組件內容,也就是給對應組件顯示占個位,告訴vue組件在這里顯示
-
在代碼中,
<RouterView>
位于<div class="main-content">
中,用于展示當前路由匹配的組件。
-
<template><div class="app"><h2 class="title">Vue路由測試</h2><!-- 導航區 --><div class="navigate"><RouterLink to="/home" active-class="xiaozhupeiqi">首頁</RouterLink><RouterLink to="/news" active-class="xiaozhupeiqi">新聞</RouterLink><RouterLink to="/about" active-class="xiaozhupeiqi">關于</RouterLink></div><!-- 展示區 --><div class="main-content"><RouterView></RouterView></div></div></template><script lang="ts" setup name="App">import {RouterView,RouterLink} from 'vue-router'</script><style>/* App */.title {text-align: center;word-spacing: 5px;margin: 30px 0;height: 70px;line-height: 70px;background-image: linear-gradient(45deg, gray, white);border-radius: 10px;box-shadow: 0 0 2px;font-size: 30px;}.navigate {display: flex;justify-content: space-around;margin: 0 100px;}.navigate a {display: block;text-align: center;width: 90px;height: 40px;line-height: 40px;border-radius: 10px;background-color: gray;text-decoration: none;color: white;font-size: 18px;letter-spacing: 5px;}.navigate a.xiaozhupeiqi {background-color: #64967E;color: #ffc268;font-weight: 900;text-shadow: 0 0 1px black;font-family: 微軟雅黑;}.main-content {margin: 0 auto;margin-top: 30px;border-radius: 10px;width: 90%;height: 400px;border: 1px solid;}</style>
index.ts
我們在src包下創建一個router包,然后下面創建一個index.ts文件。在這里我們配置路由的信息
直接看下面的代碼,看看是怎么創建路由的
包括創建路由實例,設置路由模式,配置路由規則,最后將路由暴露出去
// 創建一個路由器,并暴露出去// 第一步:引入createRouter
import {createRouter,createWebHistory} from 'vue-router'
// 引入一個一個可能要呈現組件
import Home from '@/components/Home.vue'
import News from '@/components/News.vue'
import About from '@/components/About.vue'// 第二步:創建路由器
const router = createRouter({history:createWebHistory(), //路由器的工作模式(稍后講解)routes:[ //一個一個的路由規則{path:'/home',component:Home},{path:'/news',component:News},{path:'/about',component:About},]
})// 暴露出去router
export default router
main.ts
這里我們引入并使用路由
// 引入createApp用于創建應用
import {createApp} from 'vue'
// 引入App根組件
import App from './App.vue'
// 引入路由器
import router from './router'// 創建一個應用
const app = createApp(App)
// 使用路由器
app.use(router)
// 掛載整個應用到app容器中
app.mount('#app')
這樣,一個路由切換的實例就做好了
路由器的工作模式
1. 哈希模式(Hash Mode)
特點
-
URL 中包含一個
#
符號,例如:http://example.com/#/home
。 -
路由的變化不會導致頁面重新加載,因為瀏覽器不會將
#
后面的內容發送到服務器。 -
適合需要兼容舊瀏覽器的應用,因為這種模式不需要服務器支持。
實現
默認情況下,Vue Router 使用哈希模式。可以通過以下代碼顯式指定:
import { createRouter, createWebHashHistory } from 'vue-router';
?
const router = createRouter({history: createWebHashHistory(),routes: [{ path: '/home', component: Home },{ path: '/news', component: News },{ path: '/about', component: About },],
});
優點
-
無需服務器配置:因為 URL 中的
#
郜部分不會發送到服務器,所以不需要服務器支持。 -
兼容性好:幾乎所有瀏覽器都支持哈希模式。
缺點
-
URL 不夠美觀:URL 中包含
#
符號,可能會影響用戶體驗。 -
搜索引擎優化(SEO):雖然現代搜索引擎可以處理哈希模式的 URL,但 HTML5 歷史模式的 URL 更友好。
2. HTML5 歷史模式(History Mode)
特點
-
URL 看起來更像傳統的 Web 應用,例如:
http://example.com/home
。 -
路由的變化不會導致頁面重新加載,但需要服務器支持。
-
適合現代的單頁應用(SPA),因為 URL 更友好,用戶體驗更好。
實現
可以通過以下代碼指定使用 HTML5 歷史模式:
import { createRouter, createWebHistory } from 'vue-router';
?
const router = createRouter({history: createWebHistory(),routes: [{ path: '/home', component: Home },{ path: '/news', component: News },{ path: '/about', component: About },],
});
優點
-
URL 更美觀:沒有
#
符號,URL 更符合傳統 Web 應用的風格。 -
搜索引擎優化(SEO):更友好的 URL 結構有助于搜索引擎優化。
-
用戶體驗更好:用戶更習慣沒有
#
的 URL。
缺點
-
需要服務器支持:服務器需要配置,以便正確處理路由變化。如果服務器沒有正確配置,可能會導致 404 錯誤。
-
配置復雜:需要額外的服務器配置,增加了開發和部署的復雜性。
to的兩種寫法
<!-- 第一種:to的字符串寫法 -->
<router-link active-class="active" to="/home">主頁</router-link><!-- 第二種:to的對象寫法 -->
<router-link active-class="active" :to="{path:'/home'}">Home</router-link>
命名路由
作用:可以簡化路由跳轉及傳參(后面就講)。
給路由規則命名:
routes:[{name:'zhuye',path:'/home',component:Home},{name:'xinwen',path:'/news',component:News,},{name:'guanyu',path:'/about',component:About}
]
跳轉路由:
<!--簡化前:需要寫完整的路徑(to的字符串寫法) -->
<router-link to="/news/detail">跳轉</router-link><!--簡化后:直接通過名字跳轉(to的對象寫法配合name屬性) -->
<router-link :to="{name:'guanyu'}">跳轉</router-link>
嵌套路由
預期實現
我們想實現這樣的功能:當點擊新聞頁面的時候,左側是新聞,右側是內容。簡單的來說,我們想新聞下面再嵌套一個vue組件,加一個路由
定義Detail組件
<template><ul class="news-list"><li>編號:xxx</li><li>標題:xxx</li><li>內容:xxx</li></ul>
</template><script setup lang="ts" name="About"></script><style scoped>.news-list {list-style: none;padding-left: 20px;}.news-list>li {line-height: 30px;}
</style>
重新編寫news.vue
原本全是展示區的news現在有一半是導航區一半是展示區。記得RouterView和RouterLink
<template><div class="news"><!-- 導航區 --><ul><li v-for="news in newsList" :key="news.id"><RouterLink to="/news/detail">{{news.title}}</RouterLink></li></ul><!-- 展示區 --><div class="news-content"><RouterView></RouterView></div></div>
</template><script setup lang="ts" name="News">import {reactive} from 'vue'import {RouterView,RouterLink} from 'vue-router'const newsList = reactive([{id:'asfdtrfay01',title:'很好的抗癌食物',content:'西藍花'},{id:'asfdtrfay02',title:'如何一夜暴富',content:'學IT'},{id:'asfdtrfay03',title:'震驚,萬萬沒想到',content:'明天是周一'},{id:'asfdtrfay04',title:'好消息!好消息!',content:'快過年了'}])</script><style scoped>
/* 新聞 */
.news {padding: 0 20px;display: flex;justify-content: space-between;height: 100%;
}
.news ul {margin-top: 30px;list-style: none;padding-left: 10px;
}
.news li>a {font-size: 18px;line-height: 40px;text-decoration: none;color: #64967E;text-shadow: 0 0 1px rgb(0, 84, 0);
}
.news-content {width: 70%;height: 90%;border: 1px solid;margin-top: 20px;border-radius: 10px;
}
</style>
router包下的index.js
引入Detail,并定義在news下面,注意語法,記得子組件前面不要? ? "? ?/? ?"
// 創建一個路由器,并暴露出去// 第一步:引入createRouter
import {createRouter,createWebHistory} from 'vue-router'
// 引入一個一個可能要呈現組件
import Home from '@/components/Home.vue'
import News from '@/components/News.vue'
import About from '@/components/About.vue'
import Detail from '@/components/Detail.vue'// 第二步:創建路由器
const router = createRouter({history:createWebHistory(), //路由器的工作模式(稍后講解)routes:[ //一個一個的路由規則{name:'home',path:'/home',component:Home},{path:'/news',component:News,children:[{path:'detail',component:Detail}]},{path:'/about',component:About},]
})// 暴露出去router
export default router
路由傳參
query參數
我們在上面的案例的基礎上加碼,我們要實現,點擊新聞都每一個新聞,右邊的編號那些都會顯示對應的信息
改裝news
注意傳參的寫法,query是參數,路徑也是參數
<template><div class="news"><!-- 導航區 --><ul><li v-for="news in newsList" :key="news.id"><!-- 第一種寫法 --><!-- <RouterLink :to="`/news/detail?id=${news.id}&title=${news.title}&content=${news.content}`">{{news.title}}</RouterLink> --><!-- 第二種寫法 --><RouterLink :to="{path:'/news/detail',query:{id:news.id,title:news.title,content:news.content}}">{{news.title}}</RouterLink></li></ul><!-- 展示區 --><div class="news-content"><RouterView></RouterView></div></div>
</template><script setup lang="ts" name="News">import {reactive} from 'vue'import {RouterView,RouterLink} from 'vue-router'const newsList = reactive([{id:'asfdtrfay01',title:'很好的抗癌食物',content:'西藍花'},{id:'asfdtrfay02',title:'如何一夜暴富',content:'學IT'},{id:'asfdtrfay03',title:'震驚,萬萬沒想到',content:'明天是周一'},{id:'asfdtrfay04',title:'好消息!好消息!',content:'快過年了'}])</script><style scoped>
/* 新聞 */
.news {padding: 0 20px;display: flex;justify-content: space-between;height: 100%;
}
.news ul {margin-top: 30px;/* list-style: none; */padding-left: 10px;
}
.news li::marker {color: #64967E;
}
.news li>a {font-size: 18px;line-height: 40px;text-decoration: none;color: #64967E;text-shadow: 0 0 1px rgb(0, 84, 0);
}
.news-content {width: 70%;height: 90%;border: 1px solid;margin-top: 20px;border-radius: 10px;
}
</style>
改裝detail
接受父組件傳來的參數,記得不要直接賦值,要先轉換成動態的
<template><ul class="news-list"><li>編號:{{query.id}}</li><li>標題:{{query.title}}</li><li>內容:{{query.content}}</li></ul>
</template><script setup lang="ts" name="About">import {useRoute} from 'vue-router'import {toRefs} from 'vue'let route = useRoute()let {query} = toRefs(route)console.log(query)</script><style scoped>.news-list {list-style: none;padding-left: 20px;}.news-list>li {line-height: 30px;}
</style>
params參數
我們還原到query的前面,這次我們換一個參數來傳參,同樣實現query的效果
修改news
傳遞params
參數時,若使用to
的對象寫法,必須使用name
配置項,不能用path!!!要用在路由中路徑對應起的名字
<template><div class="news"><!-- 導航區 --><ul><li v-for="news in newsList" :key="news.id"><!-- 第二種寫法 --><RouterLink :to="{name:'xijie',params:{id: news.id,title: news.title,content: news.content}}">{{news.title}}</RouterLink></li></ul><!-- 展示區 --><div class="news-content"><RouterView></RouterView></div></div>
</template><script setup lang="ts" name="News">import {reactive} from 'vue'import {RouterView,RouterLink} from 'vue-router'const newsList = reactive([{id:'asfdtrfay01',title:'很好的抗癌食物',content:'西藍花'},{id:'asfdtrfay02',title:'如何一夜暴富',content:'學IT'},{id:'asfdtrfay03',title:'震驚,萬萬沒想到',content:'明天是周一'},{id:'asfdtrfay04',title:'好消息!好消息!',content:'快過年了'}])</script><style scoped>
/* 新聞 */
.news {padding: 0 20px;display: flex;justify-content: space-between;height: 100%;
}
.news ul {margin-top: 30px;/* list-style: none; */padding-left: 10px;
}
.news li::marker {color: #64967E;
}
.news li>a {font-size: 18px;line-height: 40px;text-decoration: none;color: #64967E;text-shadow: 0 0 1px rgb(0, 84, 0);
}
.news-content {width: 70%;height: 90%;border: 1px solid;margin-top: 20px;border-radius: 10px;
}
</style>
修改index.ts
傳遞params
參數時,需要提前在規則中占位。記得起名字,也就是配置name屬性
// 創建一個路由器,并暴露出去// 第一步:引入createRouter
import {createRouter,createWebHistory} from 'vue-router'
// 引入一個一個可能要呈現組件
import Home from '@/components/Home.vue'
import News from '@/components/News.vue'
import About from '@/components/About.vue'
import Detail from '@/components/Detail.vue'// 第二步:創建路由器
const router = createRouter({history:createWebHistory(), //路由器的工作模式(稍后講解)routes:[ //一個一個的路由規則{name:'home',path:'/home',component:Home},{path:'/news',component:News,children:[{name:'xijie',path:'detail/:id/:title/:content',component:Detail}]},{path:'/about',component:About},]
})// 暴露出去router
export default router
修改detail
注意在news中已經將參數放到route里面了
<template><ul class="news-list"><li>編號:{{ route.params.id }}</li><li>標題:{{ route.params.title }}</li><li>內容:{{ route.params.content }}</li></ul>
</template><script setup lang="ts" name="About">import {useRoute} from 'vue-router'import {toRefs} from 'vue'let route = useRoute();console.log(route.params);</script><style scoped>.news-list {list-style: none;padding-left: 20px;}.news-list>li {line-height: 30px;}
</style>
路由的props配置
再次代碼恢復到query的時候,我還是想實現query的需求。這是第三種方法。傳參的方法。
既然props是路由的配置,那么就是在路由上做文章的。
注意props有三種寫法,這里演示兩種
寫法1
修改index.ts
第一種就是直接讓props為true,那么他就是默認的傳法,傳的是params。
// 創建一個路由器,并暴露出去// 第一步:引入createRouter
import { createRouter, createWebHistory } from 'vue-router'
// 引入一個一個可能要呈現組件
import Home from '@/components/Home.vue'
import News from '@/components/News.vue'
import About from '@/components/About.vue'
import Detail from '@/components/Detail.vue'// 第二步:創建路由器
const router = createRouter({history: createWebHistory(), //路由器的工作模式(稍后講解)routes: [ //一個一個的路由規則{name: 'home',path: '/home',component: Home},{path: '/news',component: News,children: [{name: 'xijie',path: 'detail/:id/:title/:content',component: Detail,props: true// props(route){// return route.query// }}]},{path: '/about',component: About},]
})// 暴露出去router
export default router
detail
<template><ul class="news-list"><li>編號:{{ id }}</li><li>標題:{{ title }}</li><li>內容:{{ content }}</li></ul>
</template><script setup lang="ts" name="About">defineProps(['id','title','content'])</script><style scoped>.news-list {list-style: none;padding-left: 20px;}.news-list>li {line-height: 30px;}
</style>
news
將路由傳參設置為params
<template><div class="news"><!-- 導航區 --><ul><li v-for="news in newsList" :key="news.id"><!-- 第二種寫法 --><RouterLink :to="{name:'xijie',params:{id: news.id,title: news.title,content: news.content}}">{{news.title}}</RouterLink></li></ul><!-- 展示區 --><div class="news-content"><RouterView></RouterView></div></div>
</template><script setup lang="ts" name="News">import {reactive} from 'vue'import {RouterView,RouterLink} from 'vue-router'const newsList = reactive([{id:'asfdtrfay01',title:'很好的抗癌食物',content:'西藍花'},{id:'asfdtrfay02',title:'如何一夜暴富',content:'學IT'},{id:'asfdtrfay03',title:'震驚,萬萬沒想到',content:'明天是周一'},{id:'asfdtrfay04',title:'好消息!好消息!',content:'快過年了'}])</script><style scoped>
/* 新聞 */
.news {padding: 0 20px;display: flex;justify-content: space-between;height: 100%;
}
.news ul {margin-top: 30px;/* list-style: none; */padding-left: 10px;
}
.news li::marker {color: #64967E;
}
.news li>a {font-size: 18px;line-height: 40px;text-decoration: none;color: #64967E;text-shadow: 0 0 1px rgb(0, 84, 0);
}
.news-content {width: 70%;height: 90%;border: 1px solid;margin-top: 20px;border-radius: 10px;
}
</style>
寫法2
修改index.js
第二種就是將props寫成函數,函數的形參就是route,那么我們就可以返回route里面的東西,包括params和query
// 創建一個路由器,并暴露出去// 第一步:引入createRouter
import { createRouter, createWebHistory } from 'vue-router'
// 引入一個一個可能要呈現組件
import Home from '@/components/Home.vue'
import News from '@/components/News.vue'
import About from '@/components/About.vue'
import Detail from '@/components/Detail.vue'// 第二步:創建路由器
const router = createRouter({history: createWebHistory(), //路由器的工作模式(稍后講解)routes: [ //一個一個的路由規則{name: 'home',path: '/home',component: Home},{path: '/news',component: News,children: [{name: 'xijie',path: 'detail',component: Detail,// props: trueprops(route){return route.query}}]},{path: '/about',component: About},]
})// 暴露出去router
export default router
修改news
將路由傳參設置為query
<template><div class="news"><!-- 導航區 --><ul><li v-for="news in newsList" :key="news.id"><!-- 第二種寫法 --><RouterLink :to="{name:'xijie',query:{id: news.id,title: news.title,content: news.content}}">{{news.title}}</RouterLink></li></ul><!-- 展示區 --><div class="news-content"><RouterView></RouterView></div></div>
</template><script setup lang="ts" name="News">import {reactive} from 'vue'import {RouterView,RouterLink} from 'vue-router'const newsList = reactive([{id:'asfdtrfay01',title:'很好的抗癌食物',content:'西藍花'},{id:'asfdtrfay02',title:'如何一夜暴富',content:'學IT'},{id:'asfdtrfay03',title:'震驚,萬萬沒想到',content:'明天是周一'},{id:'asfdtrfay04',title:'好消息!好消息!',content:'快過年了'}])</script><style scoped>
/* 新聞 */
.news {padding: 0 20px;display: flex;justify-content: space-between;height: 100%;
}
.news ul {margin-top: 30px;/* list-style: none; */padding-left: 10px;
}
.news li::marker {color: #64967E;
}
.news li>a {font-size: 18px;line-height: 40px;text-decoration: none;color: #64967E;text-shadow: 0 0 1px rgb(0, 84, 0);
}
.news-content {width: 70%;height: 90%;border: 1px solid;margin-top: 20px;border-radius: 10px;
}
</style>