本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-05-23
在更新子节点时,需要从旧虚拟节点列表中查找与新虚拟节点相同的节点进行更新,如果这个查找过程中设置了属性key,那么查找速度会快很多,所以建议尽可能在v-for时提供key。 参考
vue在渲染元素时,处于效率考虑, 会尽量地复用已有的元素而非重新渲染。
比如上面的示例,如果先在输入框内输入内容,在点击切换按钮,虽然DOM变了,但是之前在输入框键入的内容并没有改变,只是替换了placeholder的内容,说明input元素被复用了,如果不希望这样做,可以使用vue.js提供的key属性,它可以让你自己决定是否要复用元素,key的值必须是唯一的。
以上示例,可以发现当赋予了key值,我们在输入之后再切换是完全的删除和插入,而不是原地复用。
const routes = [
{
path: '/detail/:id',
name: 'detail',
component: Detail
}
]
当我们从路由 /detail/1
切换到 /detail/2
时,组件是不会发生任何变化的,因为 vue-router 会识别出两个路由使用的是同一个组件从而进行复用,并不会重新创建组件,因此组件的生命周期钩子函数也不会触发,vue-router 提供了 beforeRouteUpdate
钩子函数,会在当前路由改变且组件复用时调用,所以可以在组件内定义这个组件内的导航守卫来解决问题。
第二种方案是 watch $route 的变化:
export default {
// ...
watch: {
'$route': (to, from) => {
// 对路由变化作出响应
},
// 或者更细致的观察,针对对应的参数做对应的逻辑处理
'$route.query.id': () => {
},
'$route.query.page': () => {
},
}
}
第三种方案是为 router-view
组件添加属性 key,利用虚拟DOM在渲染时通过key来对比两个节点是否是相同的原理。
通过给 router-view
组件设置 key,可以使每次切换路由时key都不一样,让虚拟DOM认为 router-view 组件是一个新的节点,从而销毁组件,然后重新创建新组件。
<router-view :key="$route.fullPath"></router-view>
这样,如果 url 变了,key 就变了,Vuejs会重新创建这个组件,所以组件内的生命周期会被重复触发,但是这种方式的坏处非常明显,非常浪费性能。优点就是简单粗暴
如果路由上的 query 中有一些是从上游链路上传下来的,那么需要在应用的任何路由中携带,但是在所有跳转路由的地方都设置以便会非常麻烦,理想状态是在全局统一配置一个基础的 query,它会在应用的所有路由中携带,并且不影响应用中的各个路由的切换,也无须在切换路由时进行任何特殊处理。
全局守卫 beforeEach 并不具备修改 query 的能力,但可以在其中使用 next 方法来中断当前导航,并切换到新导航,添加一些新的 query 进去。
单单这样做会出问题,因为在进入新导航后,依然会被全局守卫 beforeEach 拦截,然后再次开启新导航,从而导致无限循环。解决办法是 beforeEach 中判断这个全局添加的参数在路由对象中是否存在,如果存在,则不开启新导航。
const query = { referer: 'abc.com' }
router.beforeEach((to, from, next) => {
to.query.referer
? next()
: next({
...to,
query: {
...to.query,
...query
}
})
})
这种方案的优点是可以统一配置全局公共的 query 参数,并且在组件内切换时无需进行特殊处理。缺点是,全局守卫 beforeEach 会执行两次,每次切换路由实际是切换两次。
通过拦截 router.history.transitionTo 方法,在 vue-router 内部在切换路由之前将参数添加到 query 中。
const query = { referer: 'abc.com' }
const transitionTo = router.history.transitionTo
router.history.transitionTo = function (location, onComplete, onAbort) {
location = typeof location === 'object'
? { ...location, query: { ...location.query, ...query } }
: { path: location, query }
transitionTo.call(route.history, location, onComplete, onAbort)
}
优点是全局添加 query 参数并且不会导致路由切换两次,缺点是通过修改 vue-router 内部方法实现目的,这是一种很危险的操作。
Vuejs 官方建议不要把 v-if 和 v-for 同时用在同一个元素上。
Vuejs官方给出的解释是:当Vuejs处理指令时,v-for 比 v-if 具有更高的优先级,所以即使我们只渲染出列表中的一小部分元素,也得在每次重渲染的时候遍历整个列表,而不考虑需要渲染的项是否发生了变化,
css 的规则都是全局的,任何一个组件的样式都对整个页面有效。因此,我们很容易在一个组件中写了某个样式,而不小心影响了另一个组件的样式,或者自己的组件被第三方库的css影响了。
在 Vuejs 中,可以通过 scoped 特性或 CSS Modules 来设置组件样式作用域。
1、使用 scoped
:
<template>
<button class="button button-close"></button>
</template>
<style scoped>
.button {
border: none;
border-radius: 2px;
}
.button-close {
background-color: red;
}
</style>
2、使用 CSS Modules
<template>
<button :class="[$style.button, $style.buttonClose]"></button>
</template>
<style module>
.button {
border: none;
border-radius: 2px;
}
/* 类名不能再用 - 分割 */
.buttonClose {
background-color: red;
}
</style>
单文件组件的文件名应该始终是单词首字母大写(PascalCase),或者始终是横线连接的(kebab-case)。