一、开发环境搭建:基石奠定
- Node 环境安装:
- Vue 3 的开发离不开 Node.js,它为项目提供了运行时环境与丰富的包管理能力。
- 开发编辑工具:
- Visual Studio Code(Vscode)是当下热门且功能强大的前端开发工具。
- 浏览器环境:
- 谷歌浏览器(Google Chrome)凭借出色的调试功能与对前端技术的良好支持,成为 Vue 开发的首选浏览器。
二、初窥 Vue 3:创建首个应用
- 通过 CDN 使用 Vue:
- 在 HTML 文件中引入 Vue 3 的 CDN 链接,如:
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">{{ message }}</div>
<script type="module">
import { createApp, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
setup() {
const message = ref('Hello Vue!')
return {
message
}
}
}).mount('#app')
</script>
- 这里先引入 Vue 全局脚本,然后利用
createApp
函数创建 Vue 应用实例,在setup
函数中定义响应式数据message
,最后通过.mount('#app')
将应用挂载到指定 DOM 节点(即id
为app
的div
)上,页面便能展示出Hello Vue!
。 - 还可以创建交互组件,如:
<div id="app">
<button @click="count++">{{ count }}</button>
</div>
<script type="module">
import { createApp, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
setup() {
const count = ref(0)
return {
count
}
}
}).mount('#app')
</script>
- 点击按钮,
count
值会自动更新并在页面显示,展示了 Vue 的响应式特性。
- 通过 Vite 或者官方的命令行创建 Vue 3 项目:
- 使用 Vite:
- 首先确保已安装 Node.js,在终端执行
npm init vite@latest my-vue3-app -- --template vue
(my-vue3-app
为项目名称,可自定义),这会创建一个基于 Vue 3 和 Vite 的初始项目结构。 - 进入项目目录
cd my-vue3-app
,执行npm install
安装依赖,然后npm run dev
启动开发服务器,即可在浏览器访问http://localhost:3000
查看初始页面,后续可在此基础上进行组件开发、路由配置等操作。
- 首先确保已安装 Node.js,在终端执行
- 使用官方命令行工具(需先全局安装
@vue/cli
:npm install -g @vue/cli
):- 执行
vue create my-vue3-project
(my-vue3-project
为自定义项目名),按提示选择 Vue 3 相关预设,如默认配置或手动选择功能插件(如路由、状态管理等),完成项目创建。同样进入项目目录后npm run serve
启动开发服务器,开启开发流程。
- 执行
- 使用 Vite:
三、核心知识剖析:语法与特性
- 模板语法:
- 文本插值:使用双大括号
{{}}
,如<span>Message: {{ msg }}</span>
,msg
为 Vue 实例中的响应式数据,能实时将数据渲染到页面。 - 插入 HTML:
- 插值语法插入 HTML 字符串时,会以文本形式显示,如
<p>插值语法插入html: {{ <span style="color: red">drogen</span> }}</p>
,只会显示 HTML 代码文本。 - 若要正确渲染 HTML,需使用
v-html
指令,如<p>v-html指令: <span v-html="rawHtml"></span></p>
,其中rawHtml
应为包含合法 HTML 标签的字符串变量,要注意避免 XSS 攻击,谨慎使用。
- 插值语法插入 HTML 字符串时,会以文本形式显示,如
- 文本插值:使用双大括号
- Attribute 绑定:
- 使用
v-bind
指令,可简写为:
,如<div v-bind:id="cxy"></div>
或<div :id="cxy"></div>
,cxy
为 Vue 实例中的数据,能动态绑定元素属性。常用于按钮禁用状态绑定<button :disabled="isDisabled"> 按钮 </button>
,或链接跳转地址绑定<a :href="https://chenxuyuan.net"> drogen官网 </a>
。
- 使用
- 响应式原理:
- ref():通过
ref
函数创建响应式数据,如const count = ref(0)
,在 JavaScript 中访问其值需使用.value
,即count.value
,但在模板中使用时无需加.value
,Vue 会自动处理。当count.value
发生变化,使用该数据的 DOM 区域会自动更新。 - DOM 更新时机:Vue 采用异步更新策略,修改响应式状态时,DOM 不会立即更新,而是在“next tick”更新周期中缓冲所有状态修改,确保每个组件只更新一次。若需等待 DOM 更新完成后执行额外代码,可使用
nextTick
全局 API,如:
import { nextTick } from 'vue' async function increment() { count.value++ await nextTick() // 现在 DOM 已经更新了 }
- reactive():用于创建响应式对象,如
const state = reactive({ count: 0 })
,但它有一定局限性:- 只能用于对象类型(对象、数组和如 Map、Set 这样的集合类型),不能持有如 string、number 或 boolean 这样的原始类型。
- 不能替换整个对象,否则响应性连接将丢失,如
state = reactive({ count: 1 })
会导致原state
的响应性失效。 - 对解构操作不友好,解构后的数据与原响应式对象断开连接,如
const { count } = state
,修改count
不会影响原state.count
,若要保持响应性,需传入整个对象。
- ref():通过
四、进阶特性:提升开发效能
-
计算属性:
- 无计算属性:在模板中直接进行复杂逻辑运算,如:
<script setup> import { ref } from "vue"; const list = ref({ books: [ "语文", "数学", "英语", ], }); </script> <template> <p>是否拥有书籍:</p> <span>{{ list.books.length > 0? "Yes" : "No" }}</span> </template>
- 但这样会使模板逻辑复杂,不易维护。
- 有计算属性:使用
computed
函数创建计算属性,如:
<script setup> import { ref, computed } from "vue"; const list = ref({ books: [ "语文", "数学", "英语", ], }); // 一个计算属性 ref const booksLength = computed(() => { return list.value.books.length > 0? "Yes" : "No"; }); </script> <template> <p>拥有书籍的个数:</p> <span>{{ booksLength }}</span> </template>
- 计算属性会基于其依赖的数据自动缓存结果,当依赖数据不变时,不会重复计算,提升性能,且在模板中使用如同普通数据,简洁清晰。
- 可写计算属性:
<script setup> import { ref, computed } from "vue"; const firstName = ref("老"); const lastName = ref("王"); const fullName = computed({ // getter get() { return firstName.value + " " + lastName.value; }, // setter set(newValue) { // 注意:我们这里使用的是解构赋值语法 [firstName.value, lastName.value] = newValue.split(" "); }, }); // fullName.value = "范 冰冰"; </script> <template> <p>Has published books:</p> <span>{{ fullName }}</span> </template>
- 通过定义
get
和set
方法,既能像普通属性读取,又能在赋值时执行自定义逻辑,实现数据双向关联。
-
类与样式绑定:
- 类绑定:
- 绑定 HTML class:使用
:class
指令,可绑定对象或计算属性,如:
- 绑定 HTML class:使用
<script setup> import { ref, computed } from "vue"; const isActive = ref(true); const isTitle = ref(true); const computedClass = computed(() => ({ active: isActive, title: isTitle })); </script> <template> <div :class="computedClass">drogen</div> </template>
- 绑定数组:可传递数组元素作为类名,如:
<script setup> import { ref } from "vue"; const active = ref("active"); const title = ref("title"); const isActive = ref(true); </script> <template> <div :class="[isActive? active : '', title]">drogen</div> </template>
- 绑定内联样式:使用
:style
指令,可绑定对象,对象属性为 CSS 样式名与对应值,如:
<script setup> import { ref } from "vue"; const color = ref("red"); const fontSize = ref(40); const styleObject = ref({ color: "red", fontSize: "40px", }); </script> <template> <div :style="styleObject">drogen</div> </template>
- 类绑定:
-
条件渲染与列表渲染:
- 条件渲染:
v-if+v-else
:根据条件决定元素是否渲染,如:
<script setup> import { ref } from "vue"; const title = ref(true); </script> <template> <h1 v-if="title">drogen</h1> <h2 v-else>chenxuyuan.net</h2> </template>
v-else-if
:实现多条件分支,如:
<script setup> import { ref } from "vue"; const type = ref(1); </script> <template> <h1 v-if="type === 1">drogen</h1> <h2 v-else-if="type === 2">html</h2> <h2 v-else-if="type === 3">css</h2> <h2 v-else-if="type === 4">js</h2> <h2 v-else>chenxuyuan.net</h2> </template>
- `v-show`:元素始终渲染,通过 `display` 属性控制显示隐藏,如:
<script setup> import { ref } from "vue"; const title = ref(true); </script> <template> <h1 v-show="title">drogen</h1> </template>
- 列表渲染:
v-for
:遍历数组或对象,如:
<script setup> import { ref } from "vue"; const courseList = ref(["html", "css", "js"]); </script> <template> <ul> <li v-for="item in courseList">{{ item }}</li> </ul> </template>
- 也可使用
of
关键字,如<li v-for="item of courseList">{{ item }}</li>
,效果相同。 - 嵌套遍历:
<script setup> import { ref } from "vue"; const courseList = ref([ { name: "html", list: ["h1", "h2", "h3"] }, { name: "css", list: ["color", "fontSize", "display"] }, { name: "js", list: ["var", "const", "let"] }, ]); </script> <template> <ul> <li v-for="item in courseList"> {{ item.name }} <div v-for="childItem in item.list">{{ childItem }}</div> </li> </ul> </template>
- 注意:不推荐
v-if
和v-for
同时用在同一标签,因为v-if
优先级高于v-for
,会导致v-if
条件无法访问v-for
作用域内变量别名,如:
<script setup> import { ref } from "vue"; const courseObject = ref({ front: "js", back: "java" }); </script> <template> <ul> <li v-for="(value, key, index) in courseObject" v-if="!value"> {{ value }} - {{ key }} -{{ index }} </li> </ul> </template>
- 条件渲染:
-
事件处理与表单输入绑定:
- 事件处理:
- 监听事件:使用
v-on:click
或简写@click
,如:
- 监听事件:使用
<script setup> import { ref } from "vue"; const count = ref(0); const addClick = () => { count.value++; }; </script> <template> <div @click="addClick()">计数器:{{ count }}</div> </template>
- 事件修饰符:如
.stop
阻止冒泡、.prevent
阻止默认行为、.self
仅当事件目标是自身时触发、.capture
使用捕获模式、.once
只触发一次、.passive
提升移动端滚动性能;按键修饰符如.enter
、.tab
等用于特定按键监听,系统按键修饰符.ctrl
、.alt
等可组合使用。 - 表单输入绑定:
- 未使用 Vue 时,需手动处理双向数据绑定,如:
<input :value="text" @input="event => text = event.target.value" >
- 使用
v-model
指令简化,如:
<input v-model="text">
- 不同表单元素用法:
- 文本:
<p>Message is: {{ message }}</p> <input v-model="message" placeholder="edit me" />
- **复选框**:
<input type="checkbox" id="checkbox" v-model="checked" /> <label for="checkbox">{{ checked }}</label>
- **单选按钮**:
<div>Picked: {{ picked }}</div> <input type="radio" id="one" value="One" v-model="picked" /> <label for="one">One</label> <input type="radio" id="two" value="Two" v-model="picked" /> <label for="two">Two</label>
- **选择器**:
<div>Selected: {{ selected }}</div> <select v-model="selected"> <option disabled value="">Please select one</option> <option>A</option> <option>B</option> <option>C</option> </select>
- 事件处理:
-
侦听器:
- 使用
watch
侦听器:
<script setup> import { ref, watch } from 'vue'; const question = ref(''); const answer = ref('答案'); const loading = ref(false); // 可以直接侦听一个 ref watch(question, (newQuestion) => { if (newQuestion.includes('?')) { loading.value = true; answer.value = 'Thinking...'; setTimeout(() => { answer.value = '是的'; }, 1000); } }); </script> <template> <p> 提问问题 <input v-model="question" :disabled="loading" /> </p> <p>{{ answer }}</p> </template>
- 这里侦听
question
数据变化,当输入包含?
时,更新loading
和answer
状态模拟异步获取答案过程。 - 侦听数据源类型:
- 可侦听
ref
(包括计算属性)、响应式对象、getter 函数、多个数据源组成的数组。注意不能直接侦听响应式对象的属性值,需通过 getter 函数,如:
- 可侦听
const obj = ref({ count: 0 }); // 错误,因为 watch() 得到的参数是一个 number watch(obj.value, (count) => { console.log(`count is: ${count}`); }); // 提供一个 getter 函数 watch( () => obj.value.count, (count) => { console.log(`count is: ${count}`); } );
- 深层侦听器:
- 侦听响应式对象:
const obj = ref({ count: 0 }); watch(obj.value, (newValue, oldValue) => { // 在嵌套的属性变更时触发 // 注意:`newValue` 此处和 `oldValue` 是相等的 // 因为它们是同一个对象! }); obj.count++;
- 侦听返回响应式对象的 getter 函数(开销大,慎用):
watch( () => state.someObject, (newValue, oldValue) => { // 注意:`newValue` 此处和 `oldValue` 是相等的 // *除非* state.someObject 被整个替换了 }, { deep: true } );
- 即时回调侦听器:
watch( source, (newValue, oldValue) => { // 立即执行,且当 `source` 改变时再次执行 }, { immediate: true } );
- 一次性侦听器:
watch( source, (newValue, oldValue) => { // 当 `source` 变化时,仅触发一次 }, { once: true } );
watchEffect()
:会在副作用发生期间追踪依赖,自动追踪所有能访问到的响应式属性,代码简洁但响应性依赖关系有时不明确,如:
import { ref } from 'vue'; const count = ref(0); WatchEffect(() => { console.log(`当前 count 的值为 ${count.value}`); });
- 使用
-
模板引用:
- 模板引用 ref:
<script setup> import { ref, onMounted } from 'vue'; // 声明一个 ref 来存放该元素的引用 // 必须和模板里的 ref 同名 const input = ref(null); onMounted(() => { input.value.focus(); }); </script> <template> <input ref="input" /> </template>
- 这里获取输入框元素引用,在组件挂载后使其获取焦点。
- 函数的模板引用:
<input :ref="(el) => { /* 将 el 赋值给一个数据属性或 ref 变量 */ }">
- v-for 模板引用:
<script setup> import { ref, onMounted } from 'vue'; const list = ref([ /*... */ ]); const itemRefs = ref([]); onMounted(() => console.log(itemRefs.value)); </script> <template> <ul> <li v-for="item in list" ref="itemRefs"> {{ item }} </li> </ul> </template>
- 组件上的 ref:
<script setup> import { ref, onMounted } from 'vue'; import Child from './Child.vue'; const child = ref(null); onMounted(() => { // child.value 是 <Child /> 组件的实例 }); </script> <template> <Child ref="child" /> </template>
五、生命周期:组件的生命轨迹
- 认识 Vue 3 当中的生命周期函数:
<script setup> import { ref, onMounted } from 'vue'; const data = ref(''); // 挂载时机生命周期函数; onMounted(() => { setTimeout(() => { data.value = 'drogen'; }, 100); }); </script> <template> <div>接口 100ms 后请求数据:</div> <span>{{ data }}</span> </template>
- 这里利用
onMounted
在组件挂载完成后,通过定时器模拟异步请求数据并更新data
展示。 onMounted
:常用于执行定时器、绑定事件、订阅消息等操作,此时组件已挂载到 DOM,可访问真实 DOM 元素。onUnMounted
:组件卸载之后,用于清除定时器、取消订阅消息等,防止内存泄漏。
- 这里利用
- 概览 Vue 3 的所有生命周期函数流程:
- 组件生命周期图示清晰展示各阶段,从初始化开始,历经挂载、更新、卸载等环节,各阶段有对应的生命周期函数被调用。
- 详解生命周期的初始化流程:组件加载前进行基础数据初始化等操作,为后续挂载做准备。
- 详解生命周期的挂载流程:
onBeforeMount
:在组件即将挂载到 DOM 之前被调用,模板已编译完成但未渲染,可在此预处理数据。onMounted
:组件挂载到 DOM 后立即调用,常用于 DOM 操作。
- 详解生命周期的更新 + 销毁流程:
onBeforeUpdate
:组件数据更新前调用,可保存状态等。onUpdated
:数据更新并重新渲染完成后调用,可检查更新后的 DOM。onBeforeUnmount
:组件即将卸载前调用,清理资源。onUnmounted
:组件从 DOM 中卸载后调用,标志生命周期结束。
六、组件开发:构建 Vue 应用基石
- Vue 3 单页面组件开发基础 - 组件定义 + 使用:
- 组件定义:
<script setup> import { ref } from 'vue'; const count = ref(0); </script> <template> <button @click="count++">You clicked me {{ count }} times.</button> </template>
- 定义一个简单的按钮点击计数组件。
- 使用组件:
<script setup> import ButtonCounter from './ButtonCounter.vue'; </script> <template> <h1>Here is a child component!</h1> <ButtonCounter /> </template>
- 在父组件中导入并使用子组件,可多次重用。
- 闭合写法:使用自定义标签名调用组件,如
<button-counter></button-counter>
,需在父组件正确注册。 - 传递 props:
<script setup> defineProps(['title']); </script> <template> <h4>{{ title }}</h4> </template> - 在子组件通过 `defineProps` 接收父组件传入属性,父组件使用时: <ChildComponent title="drogen" />
- Vue 3 单页面组件开发基础 - 监听事件:
- 父组件传递函数给子组件:
- $emit + defineEmits:
- 子组件:
- $emit + defineEmits:
defineEmits(["childClick"]); @click="$emit('childClick')"
- 父组件: ```html @childClick="()=>{}" ```
- 传递参数:
- 子组件:
defineEmits(["childClick"]); @click="$emit('childClick','chenxuyuan.net')"
- 父组件:
@childClick="(n)=>{console.log(n)}"
- 还可在子组件通过
emit
函数触发:<script setup> import { ref } from 'vue'; const emit = defineEmits(["childClick"]); onMounted(() => { emit("childClick", "声明式触发事件"); }); </script>
- 父组件传递函数给子组件:
- Vue 3 单页面组件开发基础 - 插槽:
- defineExpose:
- 子组件:
<script setup> const cxy = ref(0); defineExpose({ cxy }); </script>
- 父组件:
<script setup> const childRef = ref(''); <child-component ref='childRef' /> <span>{{ childRef.cxy }}<span/> </script>
- 插槽 slot:
- 子组件:
<template> <slot></slot> </template>
- 父组件:
<child-component> drogen </<child-component>
- defineExpose:
七、组件进阶:优化与拓展组件功能
- Vue 3 单页面组件开发进阶 - 组件注册 + 命名:
- 组件注册:
- 全局注册:
// main.js import { createApp } from 'vue'; import App from './App.vue'; import ChildComponent from './ChildComponent.vue'; const app = createApp(App); app.component('ChildComponent', ChildComponent); app.mount('#app');
- 全局注册虽方便,但存在问题:不必要的文件体积,未使用组件无法 tree - shaking;难维护,依赖关系不明确。
- 局部注册:在使用组件的父组件中通过
import
导入并在components
选项注册,更利于大型项目维护。 - 组件名格式:推荐大驼峰(
ChildComponent
)或连字符格式(child-component
),遵循统一规范利于代码可读性与维护性。
- 组件注册:
- Vue 3 单页面组件开发进阶 - props:
- props 声明:
<script setup> const props = defineProps(['cxy']); console.log(props.cxy); </script> <script setup> defineProps({ cxy: String, name: String }); </script>
- 通过
defineProps
声明接收父组件传入属性,指定类型利于开发时错误排查。 - props 名字格式:建议小驼峰命名,符合 JavaScript 变量命名习惯。
- 静态和动态:
- 静态:
<ChildComponent title="drogen" />
。 - 动态:
<ChildComponent :title="cxy" />
,通过:
绑定动态数据。
- 静态:
- 单项数据流:
prop
是只读的,不能直接修改,如:
const props = defineProps(['cxy']); // ❌ 警告!prop 是只读的! props.cxy = 'chenxuyuan.net';
- 更改用法:
- 用于初始值:
const props = defineProps(['cxy']); // 计数器只是将 props.cxy 作为初始值 // 像下面这样做就使 prop 和后续更新无关了 const counter = ref(props.cxy);
- 进一步处理/转换:
const props = defineProps(['cxy']); // 该 prop 变更时计算属性也会自动更新 const newcxy = computed(() => props.cxy + '老王');
- Vue 3 单页面组件开发进阶 - defineModel:
- 实现父子组件数据双向绑定的新语法糖:
- 子组件:
<script setup> const model = defineModel(); </script> <template> <h2>子组件</h2> <input v-model="model" /> </template>
- 父组件:
<script setup> import ChildComponent from "./ChildComponent.vue"; import { ref } from "vue"; const cxy = ref("drogen"); </script> <template> <h1>父组件</h1> <span>{{ cxy }}</span> <ChildComponent v-model="cxy"></ChildComponent> </script>
- 还可多属性双向绑定:
- 父组件:
<ChildComponent v-model:cxy="cxy" v-model:aa="aa"></ChildComponent>
- 子组件:
const cxy = defineModel("cxy"); const aa = defineModel("aa"); <input type="text" v-model="cxy" /> <input type="text" v-model="aa" />
- 实现父子组件数据双向绑定的新语法糖:
- Vue 3 单页面组件开发进阶 - 依赖注入:
- 依赖注入:用于跨组件层级传递数据,无需层层传递
props
。 - provide + inject:
- 供给方组件:
<script setup> import { provide } from 'vue'; provide(/* 注入名 */ 'message', /* 值 */ 'hello!'); </script>
- 注入方组件:
<script setup> import { inject } from 'vue'; const message = inject('message'); </script>
- 和响应式数据配合:
- 供给方组件:
<script setup> import { provide, ref } from 'vue'; const cxy = ref('drogen'); function updatecxy() { cxy.value = 'chenxuyuan.net'; } provide('cxy', { cxy, updatecxy }); </script>
- 注入方组件:
<script setup> import { inject } from 'vue'; const { cxy, updatecxy } = inject('cxy'); </script> <template> <button @click="updatecxy">{{ cxy }}</button> </template>
- 供给方组件:
- 注意:注入的数据默认只读,防止意外修改导致数据混乱。
- 依赖注入:用于跨组件层级传递数据,无需层层传递
- Vue 3 单页面组件开发进阶 - KeepAlive:
- 是内置组件,用于在多个组件间动态切换时缓存被移除的组件实例:
<script setup> import ChildComponent1 from "./ChildComponent1.vue"; import ChildComponent2 from "./ChildComponent2.vue"; import { ref } from "vue"; const cxy = ref(true); </script> <template> <h1>父组件</h1> <button @click="() => (cxy =!cxy)">切换组件</button> <KeepAlive><ChildComponent1 v-if="cxy" /></KeepAlive> <ChildComponent2 v-if="!cxy" /> </template>
- 如切换回缓存组件,其状态(如计数器值)保持不变,提升用户体验。
八、路由应用:构建单页面应用导航
-
Vue 3 单页面实现规模化 Vue Router 基础 - 入门使用:
- 安装:执行
pnpm install vue-router@4
安装 Vue Router 4。 - 使用:
import { createRouter, createWebHashHistory } from 'vue-router'; import ChildComponent1 from './ChildComponent1.vue'; import ChildComponent2 from './ChildComponent2.vue'; const routes = [ { path: '/one', component: ChildComponent1 }, { path: '/two', component: ChildComponent2 }, ]; const router = createRouter({ history: createWebHashHistory(), routes, }); export default router;
<script setup> import { ref } from 'vue'; const cxy = ref(true); </script> <template> <h1>父组件</h1> <p> <!-- 通过传递 `to` 来指定链接 --> <router-link to="/one">去组件1</router-link> <br /> <router-link to="/two">去组件2</router-link> </p> <div> <!-- 路由出口 --> <router-view></router-view> </div> </template>
- 这里创建路由实例,定义路由路径与对应组件,在父组件通过
<router-link>
实现导航链接,<router-view>
展示当前路由匹配组件内容。
- 安装:执行
-
Vue 3 单页面实现规模化 Vue Router 基础 - 嵌套路由 + 编程式导航:
- 嵌套路由:
import { createRouter, createWebHashHistory } from 'vue-router'; import ChildComponent1 from './ChildComponent1.vue'; import ChildComponent2 from './ChildComponent2.vue'; import ChildComponent3 from './ChildComponent3.vue'; const routes = [ { path: '/one', component: ChildComponent1, }, { path: '/two', 'two', component: ChildComponent2, children: [ { path: 'aa', component: ChildComponent3, }, ], }, ]; const router = createRouter({ history: createWebHashHistory(), routes, }); export default router;
- 上述代码定义了嵌套路由,当访问
/two/aa
时,会在ChildComponent2
的<router-view>
子路由出口中展示ChildComponent3
组件,实现更复杂的页面布局与导航结构。 - 编程式导航:
<script setup> import router from "./router.js"; const routerChange = (n) => { router.push(n); }; </script> <template> <h1>父组件</h1> <p> <button @click="routerChange('/one')">打开组件1</button> <br /> <button @click="routerChange('/two')">打开组件2</button><br /> <button @click="routerChange('/two/aa')">打开组件3</button> </p> <div> <router-view></router-view> </div> </template>
- 通过
router.push
方法实现编程式导航,可在 JavaScript 代码中根据业务逻辑灵活切换路由,比如点击按钮时触发相应路由跳转。
-
Vue 3 单页面实现规模化 Vue Router 基础 - 命名 + 重定向:
import { createRouter, createWebHashHistory } from 'vue-router'; import ChildComponent1 from './ChildComponent1.vue'; import ChildComponent2 from './ChildComponent2.vue'; import ChildComponent3 from './ChildComponent3.vue'; const routes = [ { path: '/', redirect: '/one', }, { path: '/one', name: 'one', component: ChildComponent1, }, { path: '/two', name: 'two', component: ChildComponent2, children: [ { path: 'three', name: 'three', component: ChildComponent3, }, ], }, ]; const router = createRouter({ history: createWebHashHistory(), routes, }); export default router;
- 为路由命名方便后续编程式导航引用,如
router.push({ name: 'one' })
。设置重定向可在用户访问根路径"/"
时自动跳转到指定路由,优化用户初始访问体验。
- 为路由命名方便后续编程式导航引用,如
-
Vue 3 单页面实现规模化 Vue Router 基础 - 路由传参:
- query:
<router-link :to="{ path: '/one', query: { cxy: 'drogen', state: 'chenxuyuan' } }" >打开组件1</router-link >
- 通过
query
参数传递数据,在目标组件可通过$route.query
获取,如this.$route.query.cxy
,常用于携带一些非关键、临时性的搜索条件等信息,参数会显示在 URL 中。 - params:
<router-link :to="{ name: 'one', params: { cxy: 'drogen' } }" >打开组件1</router-link > path: "/one/:cxy"
- 先在路由定义中声明参数占位符
:cxy
,然后通过params
传递,在目标组件通过$route.params
获取,参数不会显示在 URL 中,适合传递一些敏感或对 URL 美观有要求的数据,如用户 ID 等。 - 编程式导航传参:
const routerChange = (n, obj) => { router.push({ path: n, query: obj }); };
- 以编程方式跳转路由并传参,灵活实现业务逻辑中的数据传递需求。
-
Vue 3 单页面实现规模化 Vue Router 进阶 - 历史记录模式 + 导航守卫:
- 不同的历史记录模式:
- 哈希模式:
createWebHashHistory()
,此模式 URL 带有#
符号,如http://localhost:3000/#/one
,兼容性好,服务器无需特殊配置,是推荐的入门模式。 - html5 模式:
createWebHistory()
,使用 HTML5 History API,URL 更简洁美观,如http://localhost:3000/one
,但需要服务器配合进行 URL 重写,防止刷新页面出现 404 错误。
- 哈希模式:
- 导航守卫:
router.beforeEach((to, from, next) => { if (to.meta.isAuth) { if (localStorage.getItem("token") === "1") { next(); } else { alert("请先登录"); } } else { next(); } });
- 在路由跳转前进行权限验证等操作,
to
是即将进入的目标路由,from
是当前离开的路由,next
用于控制导航流程,决定是否继续跳转、重定向或中断导航,保障应用安全性与业务逻辑正确性。
- 不同的历史记录模式:
-
Vue 3 单页面实现规模化 Vue Router 进阶 - 路由懒加载:
component: () => import("./ChildComponent1.vue"),
- 采用路由懒加载技术,将组件的加载延迟到路由被访问时,而非一次性加载所有组件,优化初始加载性能,尤其在大型项目中有大量组件时,能显著减少初始包体积,加快页面首次加载速度,提升用户体验。