前端-Vue3

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. 【新的特性】

  1. Composition API(組合API):

    • setup

    • refreactive

    • computedwatch

      ......

  2. 新的內置組件:

    • Fragment

    • Teleport

    • Suspense

      ......

  3. 其他改變:

    • 新的生命周期鉤子

    • 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),能實現極速的服務啟動。

  • TypeScriptJSXCSS 等支持開箱即用。

  • 真正的按需編譯,不再等待整個應用編譯完成。

  • 具體操作如下(點擊查看官方文檔)

## 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】

  • Vue2API設計是Options(配置)選項式編程風格的。

  • Vue3API設計是Composition(組合)組合式風格的。

Options API 的弊端

Options類型的 API,數據、方法、計算屬性等,是分散在:datamethodscomputed中的,若想新增或者修改一個需求,就需要分別修改:datamethodscomputed,不便于維護和復用。

Composition API 的優勢

可以用函數的方式,更加優雅的組織代碼,讓相關功能的代碼更加有序的組織在一起。

3.2. 【拉開序幕的 setup】

setup 概述

setupVue3中一個新的配置項,值是一個函數,它是 Composition API “表演的舞臺,組件中所用到的:數據、方法、計算屬性、監視......等等,均配置在setup中。

特點如下:

  • setup函數返回的對象中的內容,可直接在模板中使用。

  • setup中訪問thisundefined。所以不能用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 的配置(datamethos......)中可以訪問到 setup中的屬性、方法。

  • 但在setup不能訪問到Vue2的配置(datamethos......)。

  • 如果與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>

擴展:上述代碼,還需要編寫一個不寫setupscript標簽,去指定組件名字,比較麻煩,我們可以借助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() ]
})
  1. 第三步:將原來文件里面的setup的script改成<script setup lang="ts" name="Person">

3.3. 【ref 創建:基本類型的響應式數據】

  • 作用:定義響應式變量。

  • 語法:let xxx = ref(初始值)

  • 返回值:一個RefImpl的實例對象,簡稱ref對象refref對象的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】

宏觀角度看:

  1. ref用來定義:基本類型數據對象類型數據

  2. reactive用來定義:對象類型數據

  • 區別:

  1. ref創建的變量必須使用.value(可以使用volar插件自動添加.value)。

  2. reactive重新分配一個新對象,會失去響應式(可以使用Object.assign去整體替換)。

  • 使用原則:

  1. 若需要一個基本類型的響應式數據,必須使用ref

  2. 若需要一個響應式對象,層級不深,refreactive都可以。

  3. 若需要一個響應式對象,且層級較深,推薦使用reactive

3.7. 【toRefs 與 toRef】

  • 作用:將一個響應式對象中的每一個屬性,轉換為ref對象。就是說,我可以將reactive包括的對象里面的每一組變量都變成ref對象。

  • 如果沒有toRefs,那么我們直接let {age, name} = Person,那么實際上是執行了let age = Person.age, let name = Person.name,此時的age和name不是響應式的。

  • 備注:toRefstoRef功能一致,但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只能監視以下四種數據

  1. ref定義的數據。

  2. reactive定義的數據。

  3. 函數返回一個值(getter函數)。

  4. 一個包含上述內容的數組。

我們在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定義的對象中的屬性,newValueoldValue 都是新值,因為它們是同一個對象。因為地址一樣,地址里面的屬性也就一樣

  • 若修改整個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>

情況四

監視refreactive定義的【對象類型】數據中的某個屬性,注意點如下:

  1. 若該屬性值不是【對象類型】,需要寫成函數形式。官方說是getter函數,實際上就寫一個函數return要監聽的字段

  2. 若該屬性值是依然是【對象類型】,可直接編,也可寫成函數,建議寫成函數。

結論:監視的要是對象里的屬性,那么最好寫函數式,注意點:若是對象監視的是地址值,需要關注對象內部,需要手動開啟深度監視。

<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

    1. 都能監聽響應式數據的變化,不同的是監聽數據變化的方式不同

    2. watch:要明確指出監視的數據

    3. 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的生命周期

創建階段:beforeCreatecreated

掛載階段:beforeMountmounted

更新階段:beforeUpdateupdated

銷毀階段:beforeDestroydestroyed

Vue3的生命周期

創建階段:setup

掛載階段:onBeforeMountonMounted

更新階段:onBeforeUpdateonUpdated

卸載階段:onBeforeUnmountonUnmounted

常用的鉤子: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>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/76518.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/76518.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/76518.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[ctfshow web入門] web39

信息收集 題目發生了微妙的變化&#xff0c;只過濾flag&#xff0c;include后固定跟上了.php。且沒有了echo $flag;&#xff0c;雖說本來就沒什么用 if(isset($_GET[c])){$c $_GET[c];if(!preg_match("/flag/i", $c)){include($c.".php");} }else{…

【動手學深度學習】LeNet:卷積神經網絡的開山之作

【動手學深度學習】LeNet&#xff1a;卷積神經網絡的開山之作 1&#xff0c;LeNet卷積神經網絡簡介2&#xff0c;Fashion-MNIST圖像分類數據集3&#xff0c;LeNet總體架構4&#xff0c;LeNet代碼實現4.1&#xff0c;定義LeNet模型4.2&#xff0c;定義模型評估函數4.3&#xff0…

代碼隨想錄第15天:(二叉樹)

一、二叉搜索樹的最小絕對差&#xff08;Leetcode 530&#xff09; 思路1 &#xff1a;中序遍歷將二叉樹轉化為有序數組&#xff0c;然后暴力求解。 class Solution:def __init__(self):# 初始化一個空的列表&#xff0c;用于保存樹的節點值self.vec []def traversal(self, r…

計算機操作系統-【死鎖】

文章目錄 一、什么是死鎖&#xff1f;死鎖產生的原因&#xff1f;死鎖產生的必要條件&#xff1f;互斥條件請求并保持不可剝奪環路等待 二、處理死鎖的基本方法死鎖的預防摒棄請求和保持條件摒棄不可剝奪條件摒棄環路等待條件 死鎖的避免銀行家算法案例 提示&#xff1a;以下是…

vue拓撲圖組件

vue拓撲圖組件 介紹技術棧功能特性快速開始安裝依賴開發調試構建部署 使用示例演示截圖組件源碼 介紹 一個基于 Vue3 的拓撲圖組件&#xff0c;具有以下特點&#xff1a; 1.基于 vue-flow 實現&#xff0c;提供流暢的拓撲圖展示體驗 2.支持傳入 JSON 對象自動生成拓撲結構 3.自…

go 通過匯編分析函數傳參與返回值機制

文章目錄 概要一、前置知識二、匯編分析2.1、示例2.2、匯編2.2.1、 寄存器傳值的匯編2.2.2、 棧內存傳值的匯編 三、拓展3.1 了解go中的Duff’s Device3.2 go tool compile3.2 call 0x46dc70 & call 0x46dfda 概要 在上一篇文章中&#xff0c;我們研究了go函數調用時的棧布…

python-1. 找單獨的數

問題描述 在一個班級中&#xff0c;每位同學都拿到了一張卡片&#xff0c;上面有一個整數。有趣的是&#xff0c;除了一個數字之外&#xff0c;所有的數字都恰好出現了兩次。現在需要你幫助班長小C快速找到那個拿了獨特數字卡片的同學手上的數字是什么。 要求&#xff1a; 設…

算法學習C++需注意的基本知識

文章目錄 01_算法中C需注意的基本知識cmath頭文件一些計算符ASCII碼表數據類型長度運算符cout固定輸出格式浮點數的比較max排序自定義類型字符的大小寫轉換與判斷判斷字符是數字還是字母 02_數據結構需要注意的內容1.stringgetline函數的使用string::findsubstr截取字符串strin…

從零開始寫android 的智能指針

Android中定義了兩種智能指針類型&#xff0c;一種是強指針sp&#xff08;strong pointer&#xff09;&#xff0c;源碼中的位置在system/core/include/utils/StrongPointer.h。另外一種是弱指針&#xff08;weak pointer&#xff09;。其實稱之為強引用和弱引用更合適一些。強…

【leetcode hot 100 152】乘積最大子數組

錯誤解法&#xff1a;db[i]表示以i結尾的最大的非空連續&#xff0c;動態規劃&#xff1a;dp[i] Math.max(nums[i], nums[i] * dp[i - 1]); class Solution {public int maxProduct(int[] nums) {int n nums.length;int[] dp new int[n]; // db[i]表示以i結尾的最大的非空連…

圖論整理復習

回溯&#xff1a; 模板&#xff1a; void backtracking(參數) {if (終止條件) {存放結果;return;}for (選擇&#xff1a;本層集合中元素&#xff08;樹中節點孩子的數量就是集合的大小&#xff09;) {處理節點;backtracking(路徑&#xff0c;選擇列表); // 遞歸回溯&#xff…

uniapp離線打包提示未添加videoplayer模塊

uniapp中使用到video標簽&#xff0c;但是離線打包放到安卓工程中&#xff0c;運行到真機中時提示如下&#xff1a; 解決方案&#xff1a; 1、把media-release.aar、weex_videoplayer-release.aar放到工程的libs目錄下; 文檔&#xff1a;https://nativesupport.dcloud.net.cn/…

打包構建替換App名稱

方案適用背景 一套代碼出多個安裝包&#xff0c;且安裝包的應用名稱、圖標都不一樣考慮三語名稱問題 通過 Gradle 腳本實現 gradle.properties 里面定義標識來區分應用&#xff0c;如下文里的 APP_TYPEAAA 、APP_TYPEBBB// 定義 groovy 替換方法 def replaceAppName(String …

DrissionPage移動端自動化:從H5到原生App的跨界測試

一、移動端自動化測試的挑戰與機遇 移動端測試面臨多維度挑戰&#xff1a; 設備碎片化&#xff1a;Android/iOS版本、屏幕分辨率差異 混合應用架構&#xff1a;H5頁面與原生組件的深度耦合 交互復雜性&#xff1a;多點觸控、手勢操作、傳感器模擬 性能監控&#xff1a;內存…

達夢數據庫用函數實現身份證合法校驗

達夢數據庫用函數實現身份證合法校驗 拿走不謝~ CREATE OR REPLACE FUNCTION CHECK_IDCARD(A_SFZ IN VARCHAR2) RETURN VARCHAR2 IS TYPE WEIGHT_TAB IS VARRAY(17) OF NUMBER; TYPE CHECK_TAB IS VARRAY(11) OF CHAR; WEIGHT_FACTOR WEIGHT_TAB : WEIGHT_TAB(7,9,10,5,8,4,…

3dmax的python通過普通的攝像頭動捕表情

1、安裝python 進入cdm&#xff0c;打python要能顯示版本號 >>>&#xff08;進入python提示符模式&#xff09; import sys sys.path顯示python的安裝路徑&#xff0c; 進入到python.exe的路徑 在python目錄中安裝(ctrlz退出python交互模式) 2、pip install mediapipe…

國產Linux統信安裝mysql8教程步驟

系統環境 uname -a Linux FlencherHU-PC 6.12.9-amd64-desktop-rolling #23.01.01.18 SMP PREEMPT_DYNAMIC Fri Jan 10 18:29:31 CST 2025 x86_64 GNU/Linux下載離線安裝包 瀏覽器下載https://downloads.mysql.com/archives/get/p/23/file/mysql-test-8.0.33-linux-glibc2.28…

Vite 權限繞過導致任意文件讀取(CVE-2025-32395)(附腳本)

免責申明: 本文所描述的漏洞及其復現步驟僅供網絡安全研究與教育目的使用。任何人不得將本文提供的信息用于非法目的或未經授權的系統測試。作者不對任何由于使用本文信息而導致的直接或間接損害承擔責任。如涉及侵權,請及時與我們聯系,我們將盡快處理并刪除相關內容。 前言…

poi-tl

官網地址 Poi-tl Documentationword模板引擎https://deepoove.com/poi-tl github 地址 https://github.com/Sayi/poi-tl/tree/master gitcode 加速地址 GitCode - 全球開發者的開源社區,開源代碼托管平臺GitCode是面向全球開發者的開源社區,包括原創博客,開源代碼托管,代碼…

操作系統 4.1-I/O與顯示器

外設工作起來 操作系統讓外設工作的基本原理和過程&#xff0c;具體來說&#xff0c;它概括了以下幾個關鍵步驟&#xff1a; 發出指令&#xff1a;操作系統通過向控制器中的寄存器發送指令來啟動外設的工作。這些指令通常是通過I/O指令&#xff08;如out指令&#xff09;來實現…