# 1. Vue 介绍

Vue 是一个渐进式 JS 框架,用于构建用户界面。 它的核心特点是​​响应式数据绑定​​​和​​​组件化开发​​​,同时提供了从视图层到全栈生态的渐进式解决方案。

Vue 的设计借鉴了 MVVM 的思想,尤其是​数据驱动视图​和​双向绑定​的特性,但它的组件化架构和灵活性超出了传统 MVVM 的严格定义。

  • ​数据绑定机制
    • ​ViewModel层​​:Vue 的响应式系统(如 data()reactive())充当 ViewModel,自动同步 ViewModel
    • ​模板语法​​:{{}}v-model 实现了双向绑定(类似 MVVM 的 Data Binding
  • 与经典 MVVM 的区别
    • 灵活性​​:Vue 允许直接操作 DOM(如 ref),而经典 MVVM 要求完全通过 ViewModel 交互。
    • ​职责范围​​:Vue 的组件系统包含逻辑(Methods)和视图(Template),比 MVVM 的 ViewModel 更复杂。

# Vue 和 React 的区别:

相同点
 使用 Virtual DOM
 中心思想相同:一切都是组件,组件实例之间可以嵌套;都提供合理的钩子函数
 都支持服务器端渲染
 都有支持native的方案:Vue的weex、React的React native
 都有自己的构建工具:Vue的vue-cli、React的Create React App

不同点

特性 React Vue (MVVM)
数据绑定 单向 双向
状态更新 显式调用 setState 自动响应式
组件通信 Props + 回调 Props + 事件 + v-model
语法 JSX 语法 .vue文件

 在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树

# MVVM 与 MVC 区别:

 MVVM 与 MVC 两者之间最大的区别就是:MVVM 实现了对 View 和 Model 的自动同步,也就是当 Model 的属性改变时,我们不用再自己手动操作 Dom 元素来改变 View 的变化,而是改变其属性后,该属性对应的 View 层数据会自动改变。

# 2. Vue 指令

# 一、数据绑定指令

# 1、v-bind(属性绑定)

作用​​:动态绑定 HTML 属性或组件 props 简写为一个冒号【 :】

  • Q​​: v-bind 和普通属性有什么区别?
  • ​A​​: v-bind 动态绑定属性值(响应式更新),普通属性是静态字符串。

<1>对象语法:

<div :class="{'is-active':isActive, 'text-danger':hasError}"></div>
<!-- data: { isActive: true, hasError: false } -->
1
2

<2>数组语法:

<p :class="[{'is-active':activeClass},errorClass]">12345</p>
<!-- data: { activeClass: false, errorClass: 'text-danger' } -->
1
2
// 三目运算符绑定
<div :class="[loginActive ? 'activeTab' : '','tabpane']"></div>
1
2

<3>直接绑定数据对象:

<div :class="classObject">12345</div>
<!-- data: { classObject:{ 'is-active': false, 'text-danger':true } } -->
1
2

<4>样式三元运算:

:style="{'color': countdown.status ? 'blue' : '#FCB11C'}" ;
1

# 2、v-model(双向绑定)

表单输入双向绑定(语法糖)。 v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值,因为它选择 Vue 实例数据做为具体的值。

  • 作用​​:表单输入双向绑定(语法糖)。
  • 原理​​:v-bind + v-on 的组合。
<input v-model="message">  
<select v-model="selected">
  <option value="A">A</option>
</select>
1
2
3
4
  • 修饰符​​:
    • .lazy:转为 change 事件后更新
    • .trim:自动去除首尾空格

# 二、条件渲染指令

# 1、v-if 和 v-show 的区别

相同点:
 v-if 与 v-show 都可以动态控制 dom 元素显示隐藏
不同点:
 v-if 显示隐藏是将 dom 元素整个添加或删除,会触发完整的生命周期钩子
 而 v-show 隐藏则是为该元素添加 css--display:none,此时 dom 元素还在

TIP

  • 如果需要非常频繁地切换,则使用 v-show 较好
  • 如果在运行时条件很少改变,则使用 v-if 较好

# 三、列表渲染指令

​作用​​:遍历数组或对象。 ​​语法​​:(item, index) in items 或 (value, key) in object。

有下面两种遍历形式

<!-- 使用in,index是一个可选参数,表示当前项的索引 -->
<div v-for="(item,index) in items" :key="item.id"></div>
1
2
<!-- 使用of -->
<div v-for="item of items"></div>
1
2
  • ​关键点​​:
    • ​必须加 :key​​:帮助 Vue 高效更新虚拟 DOM(唯一标识)。
    • ​避免 v-forv-if 同级使用​​:优先级问题(Vue 2 中 v-for 优先,Vue 3 中 v-if 优先)。

当 v-for 和 v-if 处于同⼀个节点时, v-for 的优先级⽐ v-if 更⾼,这意味着 v-if 将分别重复 运⾏于每个 v-for 循环中。如果要遍历的数组很⼤,⽽真正要展示的数据很少时,这将造成很⼤的性 能浪费。这种场景建议使⽤ computed ,先对数据进⾏过滤

# 四、事件处理指令

v-on 主要用来监听 dom 事件,以便执行一些代码块。表达式可以是一个方法名
简写为:【 @ 】

<button @click="handleClick">点击</button>
<input @input="onInput">
1
2
  • 修饰符​​:
    • .stop:阻止事件冒泡
    • .prevent:阻止默认行为
    • .once:只触发一次
<form @submit.prevent="onSubmit"></form>
1

# 五、特殊指令

# 1. v-slot(插槽)

​作用​​:定义具名插槽或作用域插槽。
​简写​​:#

<!-- 子组件 -->
<slot name="header" :user="user"></slot>

<!-- 父组件 -->
<template #header="{ user }">
  <div>用户名: {{ user.name }}</div>
</template>
1
2
3
4
5
6
7

# 2. v-pre

​作用​​:跳过该元素及其子元素的编译。

<div v-pre>{{ 这里不会编译 }}</div>
1

核心应用场景

  • 静态内容性能优化
<template>
  <!-- 包含大量静态内容的区块 -->
  <article v-pre>
    <h1>用户协议</h1>
    <p>本协议是您与...(数千字静态文本)</p>
    <!-- 节省编译开销 -->
  </article>
</template>
1
2
3
4
5
6
7
8
  • 性能影响​​:
    • 编译时间减少 30-50%(对于大型静态内容)
    • 虚拟DOM diff 时直接跳过比较
  • 第三方模板集成
<template>
  <!-- 集成 Handlebars 模板 -->
  <script type="text/x-handlebars-template" v-pre>
    {{#each users}}
      <div class="user">{{name}}</div>
    {{/each}}
  </script>
</template>
1
2
3
4
5
6
7
8
  • 优势​​:
    • 避免 Vue 尝试编译其他模板语法
    • 保持第三方模板的原始结构

# 3. v-cloak

​作用​​:解决初始化时模板闪烁问题。
配合 CSS​​:

[v-cloak] { display: none; }
1
<div v-cloak>{{ message }}</div>
1

# 4. v-once

​作用​​:使元素及其子元素只渲染一次​​,后续数据变化时跳过更新。

核心应用场景

  • 大型列表的基准项
<template>
  <ul>
    <li v-for="item in list" :key="item.id">
      <!-- 列表项标题永不变化 -->
      <h3 v-once>{{ item.title }}</h3>
      <!-- 可能变化的内容 -->
      <p>{{ item.dynamicContent }}</p>
    </li>
  </ul>
</template>
1
2
3
4
5
6
7
8
9
10

# 其他指令

# 1. v-text

<span v-text="msg"></span>
1

v-text 主要用来更新 textContent,可以等同于 JS 的 text 属性

{{}}是它的另一种写法, v-text与{{}}等价,{{}}叫模板插值,v-text叫指令

区别:在渲染数据比较多的时候,{{}}可能会把大括号显示出来,俗称屏幕闪动

# 2. v-html

双大括号的方式会将数据解释为纯文本,而非 HTML 为了输出真正的 HTML,可以用 v-html 指令 它等同于 JS 的 innerHtml 属性

<div v-html="rawHtml"></div>
1

这个 div 的内容将会替换成属性值 rawHtml,直接作为 HTML 进行渲染

  • 在⽹站上动态渲染任意 HTML,很容易导致 XSS 攻击。所以只能在可信内容上使⽤ v-html,且永远不 能⽤于⽤户提交的内容上

# 自定义指令:

有的情况下,需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令

directives: {  // 局部注册指令
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus()
    }
  }
}

<input v-focus> // 在元素上使用指令
1
2
3
4
5
6
7
8
9
10

指令注意事项:

WARNING

  • 1、当 v-for 和 v-if 处于同一节点,v-for 的优先级比 v-if 更高,永远不要把 v-if 和 v-for 同时用在同一个元素上
  • 2、如果一组 v-if + v-else 的元素类型相同,最好使用 key

# 3.父子组件间通信

# 3-1、 父组件 传数据 子组件

parent.vue:

<children v-bind:toChildData="parentSourceData"></children>
1

children.vue:

props: ["toChildData"];
1

# 3-2、 父组件 调用 子组件 方法

parent.vue:

<children ref="refChildrenName"></children>
1

method:

this.$refs.refChildrenName.子组件的方法名();
1

# 3-3、 子组件 传数据 父组件

parent.vue:

<children v-on:fromChild="parentMethod"></children>
1

children.vue:

this.$emit("fromChild", args);
1

# 3-4、 子组件 调用 父组件方法

parent.vue:

<children></children>
1

children.vue:

this.$parent.parentMethod(args);
1

或者在子组件里用$emit 向父组件触发一个事件,父组件监听这个事件就行了

<child @fatherMethod="fatherMethod"></child>

childMethod() { this.$emit('fatherMethod'); }
1
2
3

# 3-5、 子组件使用.sync 修饰符

# 4.组件上总是必须用 key

key 的作用是为了在 diff 算法执行时更快的找到对应的节点,提高 diff 速度。
key 一般用每个元素对应固定不变的值,如 id,对于数组索引 index 由于在插入数据时会变化,不推荐使用
注意:有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误,常见于 v-for 循环渲染。

# 5.Proxy 与 Object.defineProperty 对比

Object.defineProperty 虽然已经能够实现双向绑定了,但是他还是有缺陷的

1、只能对属性进行数据劫持,所以需要深度遍历整个对象
2、对于数组不能监听到数据的变化

# Proxy 优势

1、Proxy 的第二个参数可以有 13 种拦截方法,比 Object.defineProperty() 要更加丰富
2、Proxy 能够原生支持监听数组变化,并且可以直接对整个对象进行拦截

# Proxy 缺点

1、Proxy 的兼容性不如 Object.defineProperty(),不能使用 polyfill 来处理兼容性

所以在 Vue3.0 中使用 Proxy 代替 Object.defineProperty

# 备注:vue2 对数组的监听:

Vue 包含一组观察数组的变异方法(改变原数组),所以它们也将会触发视图更新。这些方法如 push()等 7 种:
对于非变异方法如 slice(),可以用新数组替换旧数组 arr = arr.slice(1)
由于 JavaScript 的限制,Vue 不能检测以下变动的数组
1、直接设置一个项时,例如:vm.items[indexOfItem] = newValue
2、修改数组的长度时,例如:vm.items.length = newLength 或者 vm.items.length++
解决方法:
对于第一种:Vue.set(vm.items, indexOfItem, newValue) 或者 vm.$set(vm.items, indexOfItem, newValue)
对于第二种:vm.items.splice(newLength)

# 6.组件的 data 必须是一个函数

这是为了 ​​确保每个组件实例维护独立的数据副本​ ,​当 data 是对象时,所有组件实例将共享同一个数据对象,导致状态污染。

# 注: 在 Vue 的根实例上 data 直接使用对象是可以的

因为只存在一个这样的实例,根实例不会被复用,不存在共享问题。

new Vue({
  data: {
    foo: "bar"
  }
});
1
2
3
4
5

# 常见问题

    1. 避免箭头函数​​:
// ❌ 避免使用箭头函数
data: () => ({ count: 0 })

// ✅ 使用普通函数
data() {
  return { count: 0 }
}
1
2
3
4
5
6
7

原因​​:箭头函数没有自己的 this,会继承父级上下文。

    1. 复杂数据初始化​​
data() {
  return {
    complexData: this.initComplexData()
  }
},
methods: {
  initComplexData() {
    // 复杂的初始化逻辑
    return { /*...*/ }
  }
}
1
2
3
4
5
6
7
8
9
10
11
    1. 为什么 React 组件可以用对象,而Vue 必须用函数?

React 组件状态存储在 this.state 中,由 React 内部管理
Vue 的响应式系统需要确保每个实例有独立的数据引用

# 7.keep-alive

keep-alive 是 Vue 内置的抽象组件,用于​​缓存不活动的组件实例​​,避免重复渲染和销毁带来的性能损耗。

<!-- 只缓存指定组件 -->
<keep-alive include="CompA,CompB">
  <component :is="currentView"></component>
</keep-alive>

<!-- 排除特定组件 -->
<keep-alive exclude="CompC">
  <component :is="currentView"></component>
</keep-alive>

<!-- 正则匹配 -->
<keep-alive :include="/CompA|CompB/">
  <component :is="currentView"></component>
</keep-alive>

<!-- 最大缓存数限制 -->
<keep-alive :max="5">
  <router-view></router-view>
</keep-alive>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  • 主要特性
  • ​组件缓存​​​:保留组件状态(data)和 DOM 结构
  • ​​生命周期钩子​​​:激活/停用时触发特定钩子
  • 条件缓存​​​:可控制哪些组件需要缓存

​​首次渲染​​:正常触发 created/mounted/activated 等钩子
activated: 首次加载组件 或者 keep-alive 组件激活时调用,(数据刷新、事件监听恢复)
deactivated: keep-alive 组件停用时调用(清除定时器、暂停动画)

# 完整生命周期调用顺序

# 首次加载会触发 activated

  • 初始化逻辑放置​​:

    • 只与DOM相关的初始化 → mounted
    • 需要每次显示都执行的逻辑 → activated
    • 只需执行一次的初始化 → created
  • 注意事项

    • ​不缓存大量组件​​:可能引起内存问题
    • ​不缓存包含定时器的组件​​:需在 deactivated 中清除
    • ​不缓存具有全局状态的组件​​:可能导致状态混乱
    • ​需要唯一key​​:确保正确匹配缓存

# 8.方法、计算属性、侦听属性

特性 方法(methods) 计算属性(computed) 侦听属性(watch)
​​定义方式​ 函数集合 基于依赖的缓存属性 侦听特定数据变化
​​调用方式​ 需要主动调用 像属性一样访问 自动触发
缓存 有(依赖不变不重新计算)
异步操作​ 支持 不支持 支持
​​适用场景​ 事件处理/需要主动触发的逻 依赖其他数据的派生数据 数据变化时需要执行复杂操作/异步

# 一、方法(methods)详解

  • 基本特性

    • ​定义​​:包含业务逻辑的函数集合
    • ​调用​​:必须通过 this.methodName() 调用
    • ​无缓存​​:每次调用都会重新执行
  • 注意事项

    • 避免箭头函数​​:会丢失 this 绑定
    • ​模板中使用​​:可以传递参数 @click="methodName(param)"
    • 性能考虑​​:频繁调用的复杂逻辑不适合放在 methods
    • 避免在模板中直接调用复杂方法

# 二、计算属性(computed)详解

  • 核心机制

    • ​响应式依赖追踪​​:自动收集依赖的响应式数据
    • ​惰性求值​​:只有依赖变化时才重新计算
    • ​缓存机制​​:依赖未变化时直接返回缓存值
  • 注意事项

    • 避免在计算属性中进行昂贵操作

# 三、侦听器(watch)详解

  • 核心机制

    • ​响应数据变化​​:执行副作用操作
    • ​深度监听​​:可监听对象内部变化
    • ​立即执行​​:配置 immediate: true
    • ​异步支持​​:适合执行异步操作
  • 注意事项

    • 对频繁变化的数据使用防抖(debounce)

# 9.过滤器

过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:

<!-- 在双花括号中 -->
{{ message | capitalize }}

<!--`v-bind`-->
<div v-bind:id="rawId | formatId"></div>

filters: {
  capitalize: function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

Vue3移除了过滤器,可以用过 方法(Methods)​、​计算属性(Computed)​​app.config.globalProperties自定义指令(Directives)​来实现。

# 10.Vue ref

ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;
如果用在子组件上,引用就指向组件实例。

<p ref="p">hello</p>

<child-component ref="child"></child-component>
1
2
3

TIP

  • 当 v-for 用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。
  • 除了可以获取dom元素,还能获取子组件中的data和去调用子组件中的方法

# 11.Vue hooks

# 1、内部监听生命周期函数

mounted() {
  // 监听窗口发生变化,resize组件
  window.addEventListener('resize', this.$_handleResizeChart)
  // 通过hook监听组件销毁钩子函数,并取消监听事件
  this.$once('hook:beforeDestroy', () => {
    window.removeEventListener('resize', this.$_handleResizeChart)
  })
}
1
2
3
4
5
6
7
8

# 2、外部监听生命周期函数

<template>
  <!--通过@hook:updated监听组件的updated生命钩子函数-->
  <!--组件的所有生命周期钩子都可以通过@hook:钩子函数名 来监听触发-->
  <custom-select @hook:updated="$_handleSelectUpdated" />
</template>

<script>
import CustomSelect from '../components/custom-select'
export default {
  components: {
    CustomSelect
  },
  methods: {
    $_handleSelectUpdated() {
      console.log('custom-select组件的updated钩子函数被触发')
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 12. Vue.nextTick 的原理和用途

# 核心用途​

# ​(1) 获取更新后的 DOM​​

Vue 的数据更新是 ​​异步​​ 的,直接修改数据后立即访问 DOM,可能获取的是 ​​旧值​​。使用 nextTick 可以确保在 ​​DOM 更新后​​ 再执行操作:

this.message = "更新后的消息"; // 修改数据
this.$nextTick(() => {
  console.log(this.$el.textContent); // 获取更新后的 DOM 内容
});
1
2
3
4

# (2) 在组件渲染完成后操作​

例如,在 mounted 钩子中访问 DOM 元素:

mounted() {
  this.$nextTick(() => {
    const el = document.getElementById("my-element");
    console.log(el.offsetHeight); // 确保 DOM 已渲染
  });
}
1
2
3
4
5
6

# (3) 解决 v-if 切换后的 DOM 操作​

v-if 切换显示时,DOM 不会立即更新,nextTick 可以确保操作在 DOM 更新后执行:

this.showModal = true; // 显示模态框
this.$nextTick(() => {
  this.$refs.modal.focus(); // 确保模态框已渲染
});
1
2
3
4

# 实现原理​

# ​(1) Vue 的异步更新机制​​

Vue 的数据更新是 ​​批量异步​​ 的(类似 Promise.thensetTimeout),目的是优化性能(避免频繁 DOM 操作)。 当数据变化时,Vue 不会立即更新 DOM,而是把更新任务推入 ​​异步队列​​,并在下一个 ​​事件循环(Event Loop)​​ 中执行。

# ​(2) nextTick 的实现方式​​

Vue 内部使用 ​​微任务(Microtask)​​ 或 ​​宏任务(Macrotask)​​ 来执行回调:

  • ​优先使用 Promise.then(微任务)​​(现代浏览器支持)
  • 如果不支持 Promise,则降级到 MutationObserver(微任务)
  • 再不支持,则使用 setImmediate(Node.js)setTimeout(宏任务)

# ​(3) nextTick 和 setTimeout(fn, 0) 的区别?​

​​nextTick setTimeout(fn, 0)
优先使用 ​微任务​(Promise/MutationObserver) 使用 ​​宏任务
确保在 ​​Vue DOM 更新后​​ 执行 不保证在 Vue 更新后执行​
更高效,适合 Vue 内部更新 可能比 nextTick 慢​

# ​(4) Vue3 的$nextTick() 能结合 async/await​

<template>
  <div>
    <button @click="addItem">添加一项并打印列表高度</button>
    <ul ref="list">
      <li v-for="item in items" :key="item">{{ item }}</li>
    </ul>
  </div>
</template>

<script setup>
import { ref, nextTick } from 'vue';
const items = ref(['苹果', '香蕉', '橙子']);
const list = ref(null); // 获取列表 DOM 的引用

const addItem = async () => {
  // 1. 添加新项(触发异步 DOM 更新)
  items.value.push('芒果');
  // 2. 👇 关键对比点:尝试直接读取高度(可能错误!)
  console.log('无 nextTick 高度:', list.value.scrollHeight); // ❌ 可能是旧值
  // 3. 等待 DOM 更新完成
  await nextTick();
  // 4. 此时读取正确高度
  console.log('有 nextTick 高度:', list.value.scrollHeight); // ✅ 保证是新值
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 13. Vue单向数据流

子组件不可以修改父组件传递的Prop

  • 定义​​:
    • 数据从父组件流向子组件,子组件不能直接修改父组件传递的数据,必须通过​​事件通知​​父组件自行更新(通过$emit 或者 通过.sync修饰符 )。
  • ​类比​​:
    • ​父组件​​:像公司的老板(拥有数据)。
    • ​子组件​​:像员工(接收老板分发的任务,但不能直接修改老板的决策,只能反馈建议)。

# 为什么 Vue 要这样设计?

  • 可维护性​​
    • 数据变更源头唯一(父组件),避免子组件随意修改导致的混乱。
    • 数据流清晰,便于调试(变更来源可追溯)。
  • ​防止意外副作用​​
    • 如果子组件能直接修改父组件数据,多个子组件同时修改时可能引发冲突。
  • ​与 React/Angular 的统一​​
    • 主流框架均采用单向数据流,降低开发者跨框架学习成本。

# 常见误区与纠正

# 误区 1:v-model 是双向绑定,违反单向数据流?

纠正​​:v-model 是语法糖,本质仍是单向数据流 + 事件通知

<input v-model="message">
<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value">
1
2
3

# 误区 2:子组件修改对象类型的 Prop 属性不算破坏单向流?

纠正​​:直接修改对象属性(如 props.user.name = 'new')​​技术上可行,但违背设计原则​​。正确做法:

// 子组件中
emit('update-user', { ...props.user, name: 'new' }); // 通知父组件更新
1
2

# 14. Vue的性能优化

    1. 尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
    1. v-if和v-for不能连⽤
<!-- 错误用法:同时使用v-if和v-for -->
<ul>
  <li v-for="item in list" v-if="item.active" :key="item.id">
    {{ item.name }}
  </li>
</ul>
1
2
3
4
5
6
    1. key保证唯⼀
<!-- 动态业务数据使用唯一ID -->
<div v-for="item in items" :key="item.id"></div>
<!-- 简单列表可用index -->
<div v-for="(item, index) in simpleList" :key="index"></div>
1
2
3
4
    1. 使⽤路由懒加载、异步组件
// 路由懒加载
const User = () => import('./User.vue')

// 条件懒加载
components: {
  HeavyComponent: () => import('./HeavyComponent.vue')
}

1
2
3
4
5
6
7
8
    1. 合理使用 keep-alive
<keep-alive :include="['Home', 'User']" :max="5">
  <router-view></router-view>
</keep-alive>
1
2
3
    1. 大型数据优化-虚拟滚动
import { RecycleScroller } from 'vue-virtual-scroller'
export default {
  components: { RecycleScroller }
}
1
2
3
4
    1. 图⽚懒加载

# 其他

  • Vue 不能挂载在 body、html 这样的根节点上

  • router.push 只能跳转路由内部的页面,要想跳转到外部页面,可以使用 window.location.href

lastUpdate: 5/26/2025, 5:49:23 PM