一、vue中 key 值的作用
在 Vue 中,
key
是一个特殊的属性,主要用于优化虚拟 DOM 的更新过程。它的核心作用是 帮助 Vue 识别哪些节点是可复用的,哪些节点需要重新创建,从而提高渲染性能并避免不必要的 DOM 操作。
1. key
的作用
(1)标识节点的唯一性
-
key
用于标识每个节点的唯一性。 -
当 Vue 更新虚拟 DOM 时,会通过
key
来判断新旧节点是否相同。
(2)优化列表渲染
-
在列表渲染(如
v-for
)中,key
可以帮助 Vue 更高效地更新 DOM。 -
如果没有
key
,Vue 会默认使用“就地更新”策略,可能导致错误的渲染结果或性能问题。
(3)触发组件的生命周期
-
当
key
发生变化时,Vue 会销毁旧组件并重新创建新组件,从而触发完整的生命周期钩子(如mounted
、updated
等)。
2. key
的使用场景
(1)列表渲染(v-for
)
在 v-for
中,key
是必须的,用于标识每个列表项的唯一性。
<template><ul><li v-for="item in items" :key="item.id">{{ item.name }}</li></ul>
</template><script>
export default {data() {return {items: [{ id: 1, name: "Apple" },{ id: 2, name: "Banana" },{ id: 3, name: "Orange" },],};},
};
</script>
为什么需要 key
?
-
如果没有
key
,Vue 会默认使用索引(index
)作为标识。 -
当列表顺序发生变化时(如插入、删除、排序),Vue 可能会错误地复用节点,导致渲染问题。
(2)动态组件
在动态组件中,key
可以强制组件重新渲染。
<template><component :is="currentComponent" :key="componentKey" /><button @click="toggleComponent">Toggle Component</button>
</template><script>
export default {data() {return {currentComponent: "ComponentA",componentKey: 0,};},methods: {toggleComponent() {this.currentComponent =this.currentComponent === "ComponentA" ? "ComponentB" : "ComponentA";this.componentKey++; // 强制重新渲染},},
};
</script>
为什么需要 key
?
-
当
key
发生变化时,Vue 会销毁旧组件并重新创建新组件,确保组件状态被正确重置。
3. key
的注意事项
(1)key
必须是唯一的
-
在同一个父节点下,
key
必须是唯一的。 -
如果
key
重复,Vue 会抛出警告。
(2)不要使用索引作为 key
-
在列表渲染中,避免使用索引(
index
)作为key
,因为索引无法唯一标识节点。 -
当列表顺序发生变化时,使用索引作为
key
会导致错误的节点复用。
(3)key
的变化会触发重新渲染
-
当
key
发生变化时,Vue 会销毁旧节点并重新创建新节点。 -
如果需要强制重新渲染组件,可以通过修改
key
实现。
4. key
的工作原理
(1)虚拟 DOM 的 Diff 算法
-
Vue 通过虚拟 DOM 的 Diff 算法比较新旧节点。
-
key
是 Diff 算法的重要依据,用于判断节点是否相同。
(2)节点的复用
-
如果新旧节点的
key
相同,Vue 会复用该节点,只更新其属性和子节点。 -
如果
key
不同,Vue 会销毁旧节点并创建新节点。
二、$route和$router的区别
1. $route
(1)定义
-
$route
是一个 当前路由信息对象,包含了当前 URL 解析后的信息。 -
它是只读的,不能直接修改。
(2)包含的属性
-
path
:当前路由的路径(如/home
)。 -
params
:动态路由参数(如/user/:id
中的id
)。 -
query
:URL 查询参数(如?name=John
中的name
)。 -
hash
:URL 的哈希值(如#section
中的section
)。 -
fullPath
:完整的 URL 路径(包括路径、查询参数和哈希)。 -
name
:路由的名称(如果定义了路由的name
属性)。 -
matched
:当前路由匹配的所有路由记录(数组)。
(3)使用场景
-
获取当前路由的信息,如路径、参数、查询参数等。
-
在组件中根据当前路由动态渲染内容。
(4)示例
// 路由配置
const routes = [{ path: "/user/:id", component: User },
];// 在组件中访问 $route
export default {mounted() {console.log(this.$route.path); // 输出当前路径,如 "/user/123"console.log(this.$route.params.id); // 输出动态参数,如 "123"},
};
2. $router
(1)定义
-
$router
是 路由实例,提供了路由的导航方法。 -
它是可操作的,可以通过它进行路由跳转、前进、后退等操作。
(2)常用的方法
-
push
:导航到一个新路由,会向历史记录中添加一条记录。this.$router.push("/home"); this.$router.push({ path: "/home" }); this.$router.push({ name: "user", params: { id: 123 } });
-
replace
:导航到一个新路由,但不会向历史记录中添加记录。this.$router.replace("/home");
-
go
:在历史记录中前进或后退。this.$router.go(-1); // 后退一步 this.$router.go(1); // 前进一步
-
back
:后退到上一个路由。this.$router.back();
-
forward
:前进到下一个路由。this.$router.forward();
(3)使用场景
-
在代码中动态导航到不同的路由。
-
控制路由的历史记录(前进、后退等)。
(4)示例
export default {methods: {navigateToHome() {this.$router.push("/home"); // 跳转到首页},navigateBack() {this.$router.go(-1); // 返回上一页},},
};
3. $route
和 $router
的区别
特性 | $route | $router |
---|---|---|
类型 | 当前路由信息对象 | 路由实例 |
是否可修改 | 只读 | 可操作 |
主要用途 | 获取当前路由的信息(路径、参数等) | 进行路由导航(跳转、前进、后退等) |
常用属性/方法 | path 、params 、query 、hash | push 、replace 、go 、back |
4. 总结
-
$route
:用于获取当前路由的信息,如路径、参数、查询参数等。 -
$router
:用于操作路由,如跳转、前进、后退等。
三、路由的两种模式(Hash 模式和History 模式)
Vue Router 是 Vue.js 官方的路由管理器,支持两种路由模式:
Hash 模式(默认模式)
History 模式
这两种模式的主要区别在于 URL 的表现形式 和 实现原理。
1. Hash 模式
(1)特点
-
URL 中带有
#
符号,例如:http://example.com/#/home
。 -
#
后面的内容不会发送到服务器,因此不需要服务器额外配置。
(2)实现原理
-
基于浏览器的
hashchange
事件。 -
当 URL 的哈希部分(
#
后面的内容)发生变化时,页面不会重新加载,但会触发路由更新。
(3)优点
-
兼容性好,支持所有浏览器(包括 IE9 及以下)。
-
不需要服务器额外配置。
(4)缺点
-
URL 中带有
#
,不够美观。
(5)配置
const router = new VueRouter({mode: "hash", // 默认模式,可以不写routes: [{ path: "/home", component: Home },{ path: "/about", component: About },],
});
2. History 模式
(1)特点
-
URL 是干净的,没有
#
,例如:http://example.com/home
。 -
基于 HTML5 的 History API(
pushState
、replaceState
)。
(2)实现原理
-
使用
history.pushState
和history.replaceState
来修改浏览器的历史记录。 -
当用户点击浏览器的前进/后退按钮时,会触发
popstate
事件,Vue Router 会根据当前 URL 更新路由。
(3)优点
-
URL 更美观,没有
#
。 -
更适合需要 SEO 的场景。
(4)缺点
-
需要服务器支持,否则刷新页面会返回 404。
-
兼容性较差,不支持 IE9 及以下浏览器。
(5)配置
const router = new VueRouter({mode: "history", // 使用 History 模式routes: [{ path: "/home", component: Home },{ path: "/about", component: About },],
});
(6)服务器配置
由于 History 模式的 URL 是虚拟的,刷新页面时服务器会尝试查找对应的文件,导致 404 错误。因此,需要在服务器端配置一个 回退路由,将所有请求重定向到 index.html
。
-
Nginx 配置:
location / {try_files $uri $uri/ /index.html; }
3. Hash 模式 vs History 模式
特性 | Hash 模式 | History 模式 |
---|---|---|
URL 表现形式 | 带有 # ,如 http://example.com/#/home | 无 # ,如 http://example.com/home |
实现原理 | 基于 hashchange 事件 | 基于 History API (pushState 、replaceState ) |
兼容性 | 兼容所有浏览器 | 不兼容 IE9 及以下浏览器 |
服务器配置 | 不需要 | 需要配置回退路由 |
SEO 支持 | 不支持 | 支持 |
美观性 | URL 带有 # ,不够美观 | URL 干净,更美观 |
4. 总结
-
Hash 模式:简单易用,兼容性好,适合不需要 SEO 的场景。
-
History 模式:URL 更美观,支持 SEO,但需要服务器配置。
四、v-if和v-show的区别
1. v-if
的特点
-
实现原理:
v-if
是 惰性渲染 的,当条件为false
时,元素不会渲染到 DOM 中;当条件为true
时,元素才会被创建并插入 DOM。 -
性能开销:
-
切换时,会销毁和重新创建 DOM 元素。
-
如果元素包含子组件,子组件的生命周期钩子(如
mounted
、destroyed
)也会被触发。
-
-
适用场景:
-
元素或组件的切换频率较低。
-
初始条件为
false
时,避免渲染不必要的 DOM 元素。
-
<div v-if="isVisible">Visible Content</div>
2. v-show
的特点
-
实现原理:
v-show
是通过 CSS 的display: none
来控制元素的显示和隐藏,元素始终会渲染到 DOM 中。 -
性能开销:
-
切换时,只是修改 CSS 的
display
属性,不会销毁和重新创建 DOM 元素。 -
适合频繁切换的场景,性能开销较小。
-
-
适用场景:
-
元素或组件的切换频率较高。
-
初始条件为
true
时,元素已经渲染到 DOM 中。
-
<div v-show="isVisible">Visible Content</div>
3. 为什么频繁切换使用 v-show
?
-
性能优势:
v-show
只是切换 CSS 的display
属性,不会触发 DOM 的销毁和重建,性能开销更小。 -
适合高频切换:例如,选项卡切换、模态框显示/隐藏等场景。
4. 为什么不频繁切换使用 v-if
?
-
减少初始渲染开销:如果初始条件为
false
,v-if
不会渲染元素,减少初始 DOM 的复杂度。 -
避免不必要的 DOM 存在:如果元素不需要频繁切换,使用
v-if
可以避免 DOM 中一直存在不必要的元素。
5. 对比总结
特性 | v-if | v-show |
---|---|---|
实现原理 | 条件为 true 时渲染 DOM | 始终渲染 DOM,通过 display 控制显示/隐藏 |
性能开销 | 切换时销毁和重建 DOM,开销较大 | 切换时只修改 CSS,开销较小 |
适用场景 | 不频繁切换,初始条件为 false | 频繁切换,初始条件为 true |
生命周期钩子 | 切换时触发组件的生命周期钩子 | 不触发组件的生命周期钩子 |
注意点:
当设置
display: none
时:
元素会从 渲染树 中移除,因此不会显示在页面上,也不会占据布局空间。
但是,元素仍然存在于 DOM 树 中,只是不会被渲染到页面上。
元素的状态(如事件监听器、子元素等)仍然保留。
当修改
display
属性为其他值(如block
、inline
等)时:
元素会被重新添加到 渲染树 中,并显示在页面上。
由于元素始终存在于 DOM 树中,因此不需要重新创建 DOM 元素。
6. DOM 树与渲染树的区别(扩展点)
-
DOM 树:
-
DOM 树是浏览器根据 HTML 文档解析生成的树形结构,表示页面的结构和内容。
-
DOM 树中的每个节点都对应一个 HTML 元素。
-
DOM 树是持久的,除非显式地通过 JavaScript 删除或添加节点,否则 DOM 元素不会被销毁或重新创建。
-
-
渲染树(Render Tree):
-
渲染树是浏览器根据 DOM 树和 CSS 样式生成的树形结构,用于实际渲染页面。
-
渲染树只包含需要显示的元素(例如,
display: none
的元素不会包含在渲染树中)。 -
渲染树是动态的,会根据 CSS 样式和 DOM 变化而更新。
-
五、vue.extend和vue.component
1. Vue2 中的 Vue.extend
和 Vue.component
(1)Vue.extend
-
作用:创建一个组件构造器。
-
使用场景:
-
动态创建组件实例(如弹窗、通知等)。
-
手动控制组件的生命周期。
-
(2)Vue.component
-
作用:注册全局组件。
-
使用场景:
-
在多个地方复用组件。
-
全局范围内使用组件。
-
(3) Vue2 中的示例
// Vue.extend 创建组件构造器
const MyComponent = Vue.extend({template: '<div>Hello, {{ name }}</div>',data() {return { name: 'Vue2' };},
});// Vue.component 注册全局组件
Vue.component('my-component', MyComponent);// 动态创建组件实例
const instance = new MyComponent().$mount();
document.body.appendChild(instance.$el);
2. Vue3 中的变化
在 Vue3 中,Vue.extend
和 Vue.component
仍然可以使用,但它们的推荐用法发生了变化。
(1) Vue.extend
的替代
-
Vue3 推荐使用
defineComponent
来定义组件。 -
defineComponent
提供了更好的 TypeScript 支持和更清晰的组件定义方式。
(2) Vue.component
的替代
-
Vue3 推荐使用
app.component
来注册全局组件。 -
app.component
是 Vue3 应用实例的方法,更加模块化。
(3)Vue3 中的示例
import { createApp, defineComponent } from 'vue';// 使用 defineComponent 定义组件
const MyComponent = defineComponent({template: '<div>Hello, {{ name }}</div>',data() {return { name: 'Vue3' };},
});// 创建应用实例并注册全局组件
const app = createApp({});
app.component('my-component', MyComponent);
app.mount('#app');
3. Vue3 中的推荐用法
(1)局部组件
-
使用
defineComponent
定义组件,然后在父组件中通过components
选项注册。
import { defineComponent } from 'vue';const ChildComponent = defineComponent({template: '<div>Child Component</div>',
});export default defineComponent({components: { ChildComponent },template: '<child-component />',
});
(2)全局组件
-
使用
app.component
注册全局组件。
import { createApp } from 'vue';
import MyComponent from './MyComponent.vue';const app = createApp({});
app.component('my-component', MyComponent);
app.mount('#app');
(3)动态组件
-
使用
h
函数(渲染函数)动态创建组件。
import { defineComponent, h } from 'vue';const DynamicComponent = defineComponent({props: ['componentName'],render() {return h(this.componentName);},
});
4. 现在还会使用 Vue.extend
和 Vue.component
吗?
-
Vue2:仍然会使用
Vue.extend
和Vue.component
,尤其是在动态创建组件或注册全局组件时。 -
Vue3:不推荐使用
Vue.extend
和Vue.component
,而是推荐使用defineComponent
和app.component
。
5. 总结
特性 | Vue2 中的用法 | Vue3 中的替代方案 |
---|---|---|
组件定义 | Vue.extend | defineComponent |
全局组件注册 | Vue.component | app.component |
动态组件创建 | Vue.extend + new Component() | h 函数(渲染函数) |
-
Vue2:继续使用
Vue.extend
和Vue.component
。 -
Vue3:推荐使用
defineComponent
和app.component
,避免使用Vue.extend
和Vue.component
。