熊熊出没 发表于 2025-1-8 21:30:29

Vue 3 详解

一、开辟环境搭建:基石奠定


[*]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 检察初始页面,后续可在此底子上进行组件开辟、路由配置等操纵。

[*]使用官方命令行工具(需先全局安装 @vue/cli:npm install -g @vue/cli):

[*]实行 vue create my-vue3-project(my-vue3-project 为自定义项目名),按提示选择 Vue 3 相关预设,如默认配置或手动选择功能插件(如路由、状态管理等),完成项目创建。同样进入项目目录后 npm run serve 启动开辟服务器,开启开辟流程。


三、核心知识剖析:语法与特性


[*]模板语法:

[*]文本插值:使用双大括号 {{}},如 <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 攻击,审慎使用。


[*]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,若要保持相应性,需传入整个对象。


四、进阶特性:提拔开辟效能


[*] 盘算属性:

[*]无盘算属性:在模板中直接进行复杂逻辑运算,如:
<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) {
    // 注意:我们这里使用的是解构赋值语法
    = newValue.split(" ");
},
});
// fullName.value = "范 冰冰";
</script>
<template>
<p>Has published books:</p>
<span>{{ fullName }}</span>
</template>
   

[*]通过定义 get 和 set 方法,既能像平凡属性读取,又能在赋值时实行自定义逻辑,实现数据双向关联。

[*] 类与样式绑定:

[*]类绑定:

[*]绑定 HTML class:使用 :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="">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:

[*]子组件:


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>

七、组件进阶:优化与拓展组件功能


[*]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"),


[*]采用路由懒加载技术,将组件的加载延迟到路由被访问时,而非一次性加载全部组件,优化初始加载性能,尤其在大型项目中有大量组件时,能明显减少初始包体积,加快页面初次加载速率,提拔用户体验。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Vue 3 详解