笔记内容与视频内容和文档内容不完全相同,包含一些自己的见解,如有内容错误,请及时与我取得联系并进行修改。
笔记用语较为通俗,专业术语使用的较少请见谅。
笔记以 Vue 3 为主,Vue 2 内容较少。
学习建议
对于经验丰富的开发者或者学过 Vue 但是需要提升自己的同学:
视频:小满zs 的 《Vue3 + vite + Ts + pinia + 实战 + 源码 + electron》
文档:《Vue 官方文档》
对于只学过前端三剑客或是基础较薄弱的同学:
视频:《尚硅谷Vue3入门到实战,最新版vue3+TypeScript前端开发教程-哔哩哔哩》
文档: 《Vue 互动教程》
视频以及文档评价
视频
小满zs
内容丰富,涵盖实际开发中需要用到的大部分知识,包含项目实战、服务搭建等内容。
难度较高,适合有一定前端开发基础的同学学习,涉及部分源码内容讲解。
尚硅谷
涵盖常用语法,常用库的使用。
课程设计贴心,对新人友好。
缺少实战内容(如果对实战有需求,可以看看黑马的课程)。
文档
官方文档
内容完整,包含 Vue 的几乎所有语法以及运行原理等。
难度较高,适合已经学习过 Vue 或 React 的同学。
互动教程
涵盖 Vue 的大部分语法。
难度较低,适合只学了前端三剑客的同学学习。
环境搭建
操作步骤笔记中不做记录,详见视频。
环境需求
nodejs
npm(nodejs 自带)
nvm(管理 nodejs 版本)
pnpm(可选,替代 npm)
创建项目
采用 vite 脚手架进行项目创建:
pnpm create vite@latest
⚡MengChen ❯❯ pnpm create vite@latest
√ Project name: ... LearnVue
√ Package name: ... learnvue
√ Select a framework: » Vue
√ Select a variant: » TypeScript
Scaffolding project in E:\Projects\Learn\LearnVue...
Done. Now run:
cd LearnVue
pnpm install
pnpm run dev
当然也可采用以下方式创建项目:
pnpm create vue@latest
安装依赖:
pnpm install
⚡MengChen ❯❯ pnpm install
Packages: +47
+++++++++++++++++++++++++++++++++++++++++++++++
Progress: resolved 90, reused 32, downloaded 15, added 47, done
dependencies:
+ vue 3.5.13
devDependencies:
+ @vitejs/plugin-vue 5.2.1
+ @vue/tsconfig 0.7.0
+ typescript 5.7.3
+ vite 6.1.1
+ vue-tsc 2.2.2
Done in 2.3s
启动开发服务器:
pnpm run dev
⚡MengChen ❯❯ pnpm run dev
> learnvue@0.0.0 dev E:\Projects\Learn\LearnVue
> vite
VITE v6.1.1 ready in 1511 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
编辑器配置
本人采用的是 vscode 进行开发,对于初学者或喜欢偷懒的同学可以采用 WebStorm 进行开发。
vscode 推荐插件(根据字母顺序排序)
Atom One Dark Theme(主题插件)
Auto Import(自动导入)
Auto Rename Tag(自动重命名前后 Tag )
background(美化背景)
Error Lens(将 Error/Warning 信息直接显示到行内)
ESLint(规范编码习惯)
HTML CSS Support(语法支持)
JavaScript (ES6) code snippets(语法支持)
JavaScript and TypeScript Nightly(语法支持)
Material Icon Theme(图标主题)
Path Intellisense(路径补全)
Prettier - Code formatter(代码格式化)
Vue - Official(Vue语法支持)
浏览器配置
推荐使用 Chrome 或者 Edge 进行开发,个人习惯使用 Edge。
浏览器推荐插件
可在 Chrome 扩展商店、Microsoft Edge 扩展商店、极简插件 等完整下载安装。
Vue.js devtools(Vue 开发者工具)
Allow CORS: Access-Control-Allow-Origin(跨域请求插件)
项目结构
LearnVue
│ .gitignore
│ index.html // 入口文件
│ package.json
│ README.md
│ tsconfig.app.json
│ tsconfig.json // TS 配置文件
│ tsconfig.node.json
│ vite.config.ts // Vite 配置文件
│
├───.vscode
│ extensions.json
│
├───node_modules
│
├───public // 静态资源,不会被编译
│ vite.svg
│
└───src
│ App.vue // Vue 全局入口文件
│ main.ts // 全局 TS 文件
│ style.css // 项目自带,可修改 main.ts 文件内容后删除
│ vite-env.d.ts // Vite 声明文件
│
├───assets // 资源,例如图片等
│ vue.svg
│
└───components // 存放组件
HelloWorld.vue
Vue 语法
下面是一个基础的 *.vue
文件结构:
<script setup lang="ts">
// 写 JS 代码
// 带有 setup 属性的 script 标签只能出现一个
</script>
<template>
// 写标签,单文件组件只能出现一个 template
</template>
<style scoped>
// 样式
</style>
模板语法
建议采用 组合式 API +
setup
语法糖。
组合式 API (Composition API)
两种写法不能混用,例如在使用了语法糖的
script
标签内无法使用export
。
语法糖,可直接将 setup
写在 script
标签:
<script setup lang="ts">
const a = 1 // a 可直接在 template 中使用,无需 return
</script>
不使用语法糖:
<script lang="ts">
export default {
setup () {
const a = 1
return {
a
}
}
}
</script>
组件命名
组建默认以文件名命名,在不使用语法糖的条件下,可通过 export default { name: ''}
的形式设置名称。
<script lang="ts">
export default {
name: 'ComponentName',
setup () {
}
}
</script>
在使用语法糖的条件下,可使用 vite-plugin-vue-setup-extend 插件,通过在 script
标签添加 name
属性,实现命名。
注意这是一个开发包,需要加上 -D
标识:
pnpm add vite-plugin-vue-setup-extend -D
之后在 vite.config.ts
中添加插件:
// ...
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VueSetup from 'vite-plugin-vue-setup-extend'
export default defineConfig({
plugins: [
vue(),
VueSetup()
]
// ...
})
<script setup name="ComponentName" lang="ts"></script>
选项式 API (Options API)
<script lang="ts">
export default {
// 定义变量
data() {
return {
count: 0
}
},
// 定义方法(函数)
methods: {
increment() {
this.count++
}
},
// 生命周期钩子会在组件生命周期的各个不同阶段被调用
// 例如这个函数就会在组件挂载完成后被调用
mounted() {
console.log(`The initial count is ${this.count}.`)
}
}
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
响应式数据
响应式数据在 template
中使用时,当数据内容被修改后,能实时响应到视图中
ref
ref
用于定义基本类型或对象类型数据,例如 Number
、String
、Boolean
、Object
、Array
等,返回的是一个 RefImpl
对象,可直接打印查看对象内容,例如:
RefImpl: {
// ...
__v_isRef: true,
__v_isShallow: false,
_rawValue: 0,
_value: 0,
value: (...)
}
该对象包含一个 value
key,在 template
中引用变量不需要写 value
,例如,在下面的例子中,引用该变量应该写成 {{ count }}
而不是 {{ count.value }}
。
如果不想手动打
.value
,可通过配置 vscode Vue 插件,在设置中勾选Auto Insert: Dot Value
后会自动插入。
<script setup lang="ts">
import { ref } from 'vue'
let count = ref(0)
let timer = ref({ name: "计时器", time: 100 })
addCount = () => {
count.value++
}
resetTimer = () {
timer.value.time = 0
}
</script>
<template>
<div>
{{ count }}
</div>
<div>
{{ timer.name }} : {{ timer.time }}
<button @click="addCount">+1</button>
<button @click="resetTimer">重置计时器</button>
</template>
reactive
当
ref
定义的是一个对象类型数据时,其底层实际上是由reactive
实现的。如果对象的层级较深,建议使用
reactive
而不是ref
。
reactive
用于定义对象类型数据,例如 Object
、Array
等,返回的是一个 Proxy
对象,例如:
Proxy: {
[[Handler]]: Object,
[[Target]]: Object {
name: "计数器",
count: 0
}
}
变量内容可直接进行修改,无需特殊操作。
如果要重新分配对象,需要使用 Object.assign
,不能直接赋值新对象, 也不能重新使用 reactive
。如果该变量是由 ref
实现的,可以直接赋值,不能重新使用 ref
。
<script setup lang="ts">
import { reactive, ref } from 'vue'
let counter = reactive({ name: "计数器", count: 0 })
let timer = ref({ name: "计时器", time: 0 })
addCount = () => {
counter.count++
}
newCounter = () => {
Object.assign(counter, { name: "新计数器", count: 100 })
}
newTimer = () => {
timer.value = { name: "新计时器", time: 100 }
}
</script>
<template>
<div>
{{counter.name}} : {{ counter.count }}
</div>
<div>
{{timer.name}} : {{ timer.time }}
</div>
<button @click="addCount">+1</button>
<button @click="newCounter">New Counter</button>
<button @click="newTimer">New Timer</button>
</template>
toRefs
如果需要从 reactive
对象中解构出响应式内容,可使用 toRefs
。
toRefs
会返回含有 ObjectRefImpl
的 Object ,其结构与 RefImpl
类似,例如:
{
name: ObjectRefImpl,
age: ObjectRefImpl
}
在修改值的时候需要加上 .value
,原对象中的值与结构出的变量指向同一个数据,会同步修改:
import { reactive, toRefs } from 'vue'
let person = reactive({ name: "梦辰", age: 18 })
// let { name, age } = person // 能解构出值,但是不是响应式的
let { name, age } = toRefs(person)
name.value = "MengChen"
console.log(person.name, name.value) // 输出为 MengChen MengChen
toRef
toRef
功能与 toRefs
相同,只是一次性只能结构出一个值:
import { reactive, toRef } from 'vue'
let person = reactive({ name: "梦辰", age: 18 })
let age = toRef(person, 'age')
age.value = 19
console.log(person.age, age.value) // 输出为 19 19
Vue 指令
在下面的例子中 a
会以文字的形式显示到 div
中:
<script setup lang="ts">
const a = "我是一段文字"
</script>
<template>
<div v-text="a"></div>
</template>
v-bind
v-bind:
可简写为 :
,用于绑定属性。
类与样式绑定
条件渲染(v-if、v-else-if、v-else、v-show
列表渲染(v-for)
事件处理(v-on)
v-on
可简写为 @
,用于监听 DOM 事件。
@[variable]
可实现可变事件。
v-model
v-model
可将数据与元素双向绑定,一方数据发生变化,另一方也会同步变化。
例如将 v-model
用于 input
实现获取用户输入:
<template>
姓名:<input v-model="name">
<div>
你的姓名是:{{ name }}
</div>
</template>
<script setup>
import { ref } from 'vue'
let name = ref("梦辰")
</script>
计算属性( computed )
当渲染的数据需要进行计算时,例如拼接、比较等,可使用 computed
进行计算,返回一个响应式对象 ComputedRefImpl
。
多次调用同一个数据只会进行一次计算。
在下面的示例中,会显示 3 个 “你好 👋,梦辰!”,但是只有一次 log 输出:
<template>
姓名:<input v-model="name">
<div>
{{ welcome }}
</div>
<div>
{{ welcome }}
</div>
<div>
{{ welcome }}
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
let name = ref("梦辰")
let welcome = computed(() => {
console.log("正在拼接字符串")
return "你好 👋," + name + "!"
})
</script>
虽然直接定义一个
getWelcome
函数返回拼接后的字符串,并且在template
中使用{{ getwelcome() }}
也可实现相同的效果,但是会进行 3 次计算,对于性能敏感的场景有明显缺陷。
上面的示例中 welcome
是只读的,无法通过 welcome.value = xxx
的方式直接修改,如果需要改为可读可写,需要设置 getter 和 setter。
新的赋值会传递给 setter,例如在下面的例子中会输出 “MengChen”
let welcome = computed({
get() {
return "你好 👋," + name + "!"
},
set(val) {
console.log(val)
}
})
welcome.value = "MengChen"
侦听器( watch )
详见 侦听器 | Vue.js
watch
可监听 4 种类型的数据:
一个函数,返回一个值
一个 ref
一个响应式对象
...或是由以上类型的值组成的数组
下面是常见的几种情况情况:
ref 定义的基本类型数据
<template>
{{ count }}
<button @click="addCount">+1</button>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
let count = ref(0)
addCount = () => {
count.value++
}
const stopWatch = watch(count, (newValue, oldValue) => {
console.log("count 变化了")
console.log("变化前:", oldValue)
console.log("变化后:", newValue)
if (newValue >= 10) {
stopWatch()
console.log("计数器大于10,不再监听数据变化")
}
})
</script>
ref 定义的对象类型数据
对于 ref
定义的对象类型数据, watch
默认只监视对象的地址,如果需要监视里面的内容,需要传入 deep: true
开启深度监视:
<template>
{{ counter.count }}
<button @click="addCount">+1</button>
<button @click="newCounter">New</button>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
let counter = ref({ name: "计数器", count: 0 })
addCount = () => {
counter.value.count++
}
newCounter = () => {
counter.value = { name: "新计数器", count: 0 }
}
watch(counter, (newValue, oldValue) => {
console.log("Counter 发生了变化")
console.log("变化前:", oldValue)
console.log("变化后:", newValue)
// }) // 不传入 deep ,addCount 时无法监听到变化,只有在 newCounter 时能监听到变化,因为地址改变了
}, { deep: true }) // 传入 deep ,addCount 时可以监听到变化
</script>
上面的例子中存在一个小 bug ,在 addCount
时只是修改了 Counter 的属性的值,watch
监听传入的 newValue
和 oldValue
指向了同一个地址,导致打印出的都是新值而不是旧值。
reactive 定义的对象数据类型
对于 reactive
定义的对象类型数据, watch
默认开启深度监视,并且无法关闭:
<script setup lang="ts">
import { reactive, watch } from 'vue'
let counter = reactive({ name: "计数器", count: 0 })
addCount = () => {
counter.count++
}
newCounter = () => {
Object.assign(counter, { name: "新计数器", count: 0})
}
watch(counter, (newValue, oldValue) => {
console.log("Counter 发生了变化")
console.log("变化前:", oldValue)
console.log("变化后:", newValue)
})
</script>
<template>
<div>
{{counter.name}} : {{ counter.count }}
</div>
<button @click="addCount">+1</button>
<button @click="newCounter">New Counter</button>
</template>
上面的例子中所有的修改操作都没有对地址进行修改,都只是修改了 Counter 的属性,所以打印出的newValue
和 oldValue
依旧是相同的。
reactive 定义的对象的某个属性
如果只需要监听 reactive
定义的对象的某个属性,watch
函数传入的第一个参数需要改成一个函数,并且返回一个值
如果需要监听的属性也是一个对象,不写成函数可以监听到其内部的变化,但是无法监听到其本身地址的变化。最佳实践是写成函数,监听其地址的变化,再根据需求选择是否传入 deep: true
。
<script setup lang="ts">
import { reactive, watch } from 'vue'
let counter = reactive({
name: "计数器",
count: 0,
info: {
desc: "这是一个计数器"
}
})
addCount = () => {
counter.count++
}
changeDesc = () => {
counter.info.desc = "这是新的描述"
}
changeInfo= () => {
counter.info = { desc: "这是修改了 info 之后的 desc" }
}
watch(() => counter.count, (value) => {
console.log("Counter.count 发生了变化", value)
})
// 写成函数监视其地址变化,并加上 deep 深度监视
watch(() => counter.info, (value) => {
console.log("Counter.info 发生了变化", value)
}, { deep: true })
// 可以监听到 info 内部的变化,但是无法监听到 info 本身地址的变化
/* watch(counter.info, (value) => {
console.log("Counter.info 发生了变化", value)
}) */
</script>
<template>
<div>
{{counter.name}} : {{ counter.count }}
</div>
<button @click="addCount">+1</button>
<button @click="changeDesc">Change Desc</button>
<button @click="changeInfo">Change Info</button>
</template>
监听多个数据
将第一个参数改为数组即可实现同时监听多个数据,传入的 value 格式与数组格式相同:
<script setup lang="ts">
import { reactive, watch } from 'vue'
let counter = reactive({
name: "计数器",
count: 0,
info: {
desc: "这是一个计数器"
}
})
addCount = () => {
counter.count++
}
changeDesc = () => {
counter.info.desc = "这是新的描述"
}
// 上述的 4 种情况均可写到数组里面
watch([() => counter.count, () => counter.info], (newValue, oldValue) => {
console.log("Counter发生了变化", newValue, oldValue) // 输出 [count, info]
}, { deep: true })
</script>
<template>
<div>
{{counter.name}} : {{ counter.count }}
</div>
<button @click="addCount">+1</button>
<button @click="changeDesc">Change Desc</button>
</template>
自动监听(watchEffect)
watchEffect
可以自动监听数据的变化,无需指定需要监听的数据。
例如在第一个例子中,如果需要在 count >= 10
的时候进行某些操作,代码如下:
const stopWatch = watch(count, (value) => {
if (value >= 10) {
stopWatch()
console.log("计数器大于10,不再监听数据变化")
}
})
使用 watchEffect
简化代码如下:
const stopWatch = watchEffect(() => {
if (count.value >= 10) {
stopWatch()
console.log("计数器大于10,不再监听数据变化")
}
})
对于较少数据,简化效果不明显,但是对于较多需要同时监听的数据,简化效果更为明显,例如在
watch
传入数组的时候。
标签 ref
在开发中可能会遇到需要用 id 绑定元素或组件的情况,但是有可能遇到父组件与子组件用了同一个 id 导致一些通过 id 获取元素的操作出错,此时可使用 ref
来绑定元素或组件。
下面的示例中,由于 h1
比 h2
先加载,所以两个 log 输出都是 h1
:
父组件(Father)
<template>
<h1 id="title" @click="showLog">父标题</h1>
<Son />
</template>
<script setup lang="ts">
import Son from './Son.vue'
showLog = () => {
console.log(document.getElementById("title"))
}
</script>
子组件(Son)
<template>
<h2 id="title" @click="showLog">子标题</h1>
</template>
<script setup lang="ts">
showLog = () => {
console.log(document.getElementById("title"))
}
</script>
下面的示例中,两个 log 输出分别是 h1
和 h2
:
父组件(Father)
<template>
<h1 ref="title" @click="showLog">父标题</h1>
<Son />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Son from './Son.vue'
let title = ref()
showLog = () => {
console.log(title)
}
</script>
子组件(Son)
<template>
<h2 ref="title" @click="showLog">子标题</h2>
</template>
<script setup lang="ts">
import { ref } from 'vue'
let title = ref()
showLog = () => {
console.log(title)
}
</script>
通信
子传父(defineExpose)
当 ref
指向子组件时,可通过 ref
对象获取到子组件的一些属性。
defineExpose
可将子组件的 ref
对象暴露给父组件使用:
父组件(Father)
<template>
<h1>父组件</h1>
<button @click="add">+1</button>
<Son ref="son" />
</template>
<script setup lang="ts">
import Son from './Son.vue'
const add = () => {
son.value.addCount()
console.log("父组件调用了子组件的 addCount 方法")
console.log("Count: ", son.value.count)
}
</script>
子组件(Son)
<template>
<h2>子组件</h2>
Count: {{ count }}
</template>
<script setup lang="ts">
import { ref, defineExpose } from 'vue'
const count = ref(0)
const addCount = () => {
count.value++
}
defineExpose({count, addCount})
</script>
父传子(defineProps)
父组件向子组件传递数据通常通过 props
实现。
defineProps
用于定义 props
,子组件可接收从父组件传递过来的参数:
父组件(Father)
<template>
<h1>父组件</h1>
<button @click="updateCount">更新子组件的计数</button>
<Son :count="parentCount" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
import Son from './Son.vue';
const parentCount = ref(0); // 父组件的计数器
// 更新计数并传递给子组件
const updateCount = () => {
parentCount.value += 1;
console.log(`父组件的计数更新为:${parentCount.value}`);
};
</script>
子组件(Son)
<template>
<h2>子组件</h2>
<p>从父组件接收到的计数:{{ count }}</p>
</template>
<script setup lang="ts">
import { defineProps } from 'vue';
// 定义接收的 props
const props = defineProps<{
count: number; // 接收父组件传递的计数
}>();
</script>
字符串数组式声明这里不做过多介绍,因为不是很好用,并且不太符合 TS 开发习惯。
生命周期
对于初学者,该部分内容较难理解,建议看视频理解。
自定义 Hooks
使用自定义 Hooks 可以将一些功能封装起来,实现模块化开发。
通常会创建一个以 use 开头的 ts 文件来编写相关代码。
下面是一个简单的自定义 Hook:
App.ts
<template>
<div>{{ count }}</div>
<button @click:"addCount">+1</button>
<button @click:"subCount">-1</button>
<button @click:"resetCount">Reset</button>
</template>
<script setup lang="ts">
import useCounter from './useCounter'
const { count, addCount, subCount, resetCount } = useCounter()
</script>
useCounter.ts
import { ref } from 'vue'
export default useCounter = () => {
let count = ref(0)
addCount = () => {
count.value++
}
subCount = () => {
count.value--
}
resetCount = () => {
count.value = 0
}
return { count, addCount, subCount, resetCount }
}
路由(Vue Router)
官方文档写的非常详细,详见 Vue Router | Vue.js 的官方路由
安装路由库
pnpm add vue-router@4
基础路由
定义路由器(router.ts):
import { createRouter, createWebHistory } from 'vue-router'
import HomePage from './Home.Vue'
import AboutPage from './About.vue'
import ProfilePage from './Profile.vue'
export default const router = createRouter({
history: createWebHistory(), // 工作模式
routes: [ // 路由规则
{
path: "/home",
component: 'HomePage'
},
{
path: "/about",
component: 'AboutPage'
},
{
path: "/profile",
component: 'ProfilePage'
}
]
})
哈希模式为 createWebHashHistory。
修改入口(main.ts):
import { createApp } from 'vue'
import App from './app.vue'
import router from './router.ts' // 引入路由器
// createApp(App).mount("#app") //原先代码
const app = createApp(App)
app.use(router) // 使用路由器
app.mount("#app")
渲染路由视图(App.vue):
<template>
<div class="navigate">
<RouterLink to="/home" active-class="active">首页</RouterLink>
<RouterLink to="/about" active-class="active">关于</RouterLink>
<RouterLink to="/profile" active-class="active">我的</RouterLink>
</div>
<div class="main">
<RouterView></RouterView>
</div>
</template>
<script setup lang="ts">
import { RouterView, RouterLink } from 'vue'
</script>
<style>
.active {
color: green;
}
</style>
active-class 会在切换到对应页面时激活。
默认情况下,在切换页面后,之前渲染过的页面组件会被销毁而不是隐藏。
一般将路由(页面)组件放到 pages 或 views 目录下,普通组件放到 components 目录下。
Routerlink
to
有两种写法:
<RouterLink to="/home" />
<RouterLink :to={path: "/about"} />
路由页面可通过 path
和 name
去跳转:
import { createRouter, createWebHistory } from 'vue-router'
import HomePage from './Home.Vue'
import AboutPage from './About.vue'
export default const router = createRouter({
history: createWebHistory(),
routes: [
{
path: "/home",
component: 'HomePage'
},
{
name: "About",
path: "/about",
component: 'AboutPage'
}
]
})
<RouterLink :to={path: "/about"} />
<RouterLink :to={name: "About"} />
可带有 replace
属性
<RouterLink replace :to={path: "/about"} />
嵌套路由
详见
传参
query
Link :
<RouterLink to="/home?a=123" />
<RouterLink :to="`/home?a=${123}`" />
<RouterLink :to={path: "/home", query: { a: 123 }} />
Home:
<script setup lang="ts">
import { ref, useRoute } from 'vue-router'
const route = useRoute()
const { query } = ref(route)
console.log(query) // { a: 123 }
</script>
params
数组等类型不支持 params 传参
Router:
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: "/home/:id/:title?", // 带 ? 的为可选参数
component: HomePage,
}
]
})
Link:
<RouterLink to="/home/id123/title123" />
Home:
<script setup lang="ts">
import { ref, useRoute } from 'vue-router'
const route = useRoute()
const { params } = ref(route)
console.log(params) // { id: "id123", title: "title123" }
</script>
props
props 相当于组建传参
Router:
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: "/home/:id/:title",
component: HomePage,
// 写法1:将所有 params 传给 props
// props: true,
// 写法2:函数写法,自定义props,这里只做简单演示
props(route) {
return route.params
// return route.query
},
// 写法3:对象写法,不推荐
// props: { id: "id123" },
}
]
})
Link:
<RouterLink to="/home/id123/title123" />
Home:
<script setup lang="ts">
import { defineProps } from 'vue-router'
const props = defineProps<{
id: string,
title: string
}>();
console.log(props)
</script>
编程式路由
import { useRouter } from 'vue-router'
const router = useRouter()
onMounted(() => {
setTimeout(() => {
router.push("/home")
}, 3000)
})
与 React Router 的不同
children
的path
无需以/
开头