笔记内容与视频内容和文档内容不完全相同,包含一些自己的见解,如有内容错误,请及时与我取得联系并进行修改。

笔记用语较为通俗,专业术语使用的较少请见谅。

笔记以 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)

创建项目

详见 快速上手 | Vue.js

采用 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>

模板语法

详见 模板语法 | Vue.js

建议采用 组合式 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>

响应式数据

详见 响应式基础 | Vue.js

响应式数据在 template 中使用时,当数据内容被修改后,能实时响应到视图中

ref

ref 用于定义基本类型或对象类型数据,例如 NumberStringBooleanObjectArray等,返回的是一个 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 用于定义对象类型数据,例如 ObjectArray 等,返回的是一个 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 指令

详见 内置指令 | Vue.js

在下面的例子中 a 会以文字的形式显示到 div 中:

<script setup lang="ts">
  const a = "我是一段文字"
</script>

<template>
  <div v-text="a"></div>
</template>

v-bind

详见 内置指令 | Vue.js

v-bind: 可简写为 : ,用于绑定属性。

类与样式绑定

详见 Class 与 Style 绑定 | Vue.js

条件渲染(v-if、v-else-if、v-else、v-show

详见 条件渲染 | Vue.js

列表渲染(v-for)

详见 列表渲染 | Vue.js

事件处理(v-on)

详见 事件处理 | Vue.js

v-on 可简写为 @ ,用于监听 DOM 事件。

@[variable] 可实现可变事件。

v-model

详见 组件 v-model | Vue.js

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 )

详见 计算属性 | Vue.js

当渲染的数据需要进行计算时,例如拼接、比较等,可使用 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 监听传入的 newValueoldValue 指向了同一个地址,导致打印出的都是新值而不是旧值。

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 的属性,所以打印出的newValueoldValue 依旧是相同的。

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

详见 内置的特殊 Attributes | Vue.js

在开发中可能会遇到需要用 id 绑定元素或组件的情况,但是有可能遇到父组件与子组件用了同一个 id 导致一些通过 id 获取元素的操作出错,此时可使用 ref 来绑定元素或组件。

下面的示例中,由于 h1h2 先加载,所以两个 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 输出分别是 h1h2

父组件(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)

详见 模板引用 | Vue.js

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 | Vue.js

父组件向子组件传递数据通常通过 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 开发习惯。

生命周期

详见 生命周期钩子 | Vue.js

对于初学者,该部分内容较难理解,建议看视频理解。

自定义 Hooks

详见 组合式函数 | Vue.js

使用自定义 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 目录下。

to 有两种写法:

<RouterLink to="/home" />
<RouterLink :to={path: "/about"} />

路由页面可通过 pathname 去跳转:

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 的不同

  • childrenpath 无需以 / 开头