v-if/v-if-else/v-else 中使用 key
<div v-if="flag">
正确内容
</div>
<div v-else>
错误内容
</div>
vue会尽可能的高效更新DOM. 相同类型的元素之间切换时,会修补已存在的元素,而不是将旧的元素移除后,同一位置添加一个新元素。
解决路由切换组件不变的问题
cosnt routes = [
{
path: 'detail/:id',
name: 'detail',
component: Detail
}
]
当页面切换到同一个路由但 不同的参数地址时, 组件的生命周期钩子不会重复处触发。
① 路由导航守卫 beforeRouteUpdate
vue-router提供了 导航守卫 beforeRouteUpdate, 当前路由改变 并 组件 复用 时调用。
所有只需要 把每次 切换路由时 需要 执行的 逻辑 放到 beforeRouteUpdate守卫中即可
② 观察 $route对象的变化
通过 watch 可以监听若有对象中的变化,从而对 路由变化做出响应
const User = {
watch: {
'$route' (to, from){
// 对 路由变化做出 响应
}
}
}
③ 为 router-view 组件添加属性 key
<router-view :key="$route.fullPath"></router-view>
有点事简单粗暴,改动小。 缺点是 ,每次切换 都会 被销毁并重新创建,很消耗性能。
为所有路由添加 query
如果路由上 的query 中有 一些 参数需要一直 传递下去,在每个跳转路由的地方设置会很麻烦。
①使用全局守卫 beforeEach
事实上,beforeEach 不具备修改query的方法,但可以使用next方法 中断当前导航,并切换到新导航,添加一些query进去。
刚进入新导航后,依然会被全局守卫 beforeEach拦截,然后再次开启新导航,从而导致死循环。 解决办法是 判断这个 全局添加的参数在路由中是否存在,如果存在,则不开启新导航
const query = {referer: 'test-hehehe'}
router.beforeEach(to, from, next) => {
to.query.referer ? next() : next({...to, query: {...to.query, ...query}})
}
②使用函数劫持
原理是 通过拦截 router.history.transitionTo方法 ,在vue-router内切换路由之前 将参数 添加到query 中。
const query = {referer: 'test-hehehe'}
const transitioinTo = router.history.transitionTo
router.history.transitionTo = function(location, onComplete, onAbort){
location = typeof location === 'object' ?
{...location, query: {...location.query, ...query} }
: {path: location, query}
transitionTo.call(router.history, location, onComplete, onAbort)
}
代码中先将 vue-router内部的 router.history.transitionTo方法 缓存到变量中。 然后使用新函数重新router.history.transitionTo方法, 通过在函数中修改参数达到全局 添加 query 参数的目的。当执行缓存的原始方法时,将修改后的参数传递进去。
vue中 key的作用和原理
src\core\vdom\patch.js 中的 updateChildren函数(遍历当前节点的所有 子节点更新)通过 key 判断是不是同一个节点。
可以知道
key的作用是: 为了高效的更新虚拟DOM
原理是: vue在patch过程中通过key可以精准判断两个节点是不是同一个,从而避免频繁更新不同的元素, 使得整个patch 过程更加高效,减少DOM操作量,提高性能
diff
必要性: lificycle.js - mountComponent()
在$mount的时候回调用 ,一个组件 会 有 一个 watcher.
组件中 可能存在很多个data中 key的使用, 为了区分,使用diff执行 patch.js --- patchVnode()
patchVnode是diff 发生的地方,整体策略试试:深度优先,同层比较
高效性 patch.js --- updateChildren()
diff算法是虚拟DOM技术的产品: 通过 新旧虚拟DOM对比(即diff),将变化的地方更新在真实DOM上,另外, 也需要diff高效的执行对比过程, 从而降低时间复杂度为O(n).
vue2为了降低 watcher粒度, 每个组件只有一个watcher与之对应,引入diff才能精确的查找发生变化的地方。
vue中执行diff的时刻是 组件实例执行 更新函数时,它会对比上一次渲染结果 oldVnode和新的渲染结果 newVnode,此过程称为 patch.
diff 过程整体遵循深度优先、同层比较的策略。 两个 节点之间会根据它们时候拥有子节点或者文本节点做不同的操作。 比较两组子节点是 算法的重点。借助key通常可以精准的找到相同的节点,因此patch过程是高效的。
组件化的理解
vue实例化时自上而下的,但是 挂载是自下而上的。
- 组件 是独立可 复用的 代码组织单元。 组件系统是 vue 核心特性之一, 它使 开发者使用小型、独立和通常可以复用的 组件 构建大型应用。
- 组件化开发能大幅提高应用开发效率、测试性、复用性等;
- 组件使用按分类有: 页面组件、 业务组件、 通用组件
- vue的组件是基于配置的,同名通常编写的组件配置 而非组件, 框架 后续会生产其 构造函数,它们基于 VueComponent,扩展Vue;
- vue中常见的组件化技术有: 属性prop,自定义事件、插槽等,它们主要用于组件通信、扩展等;
- 合理的划分组件,有利于提升应用性能(比如某一块经常更新,把它提为组件,组件有单独的watcher。这样更新,就不会影响其他的)
- 组件应该高内聚、低耦合
- 遵循单向数据流的原则
vue性能优化方法
- 路由懒加载
routes:[
{path: '/foo', component: ()=> import('./Foo.vue')}
]
- keep-alive 缓存页面
- v-show 复用DOM (频繁显示,组件比较重的情况,渲染时间比较长)
- v-for 遍历避免同时 使用 v-if (用computed计算属性处理)
- 长列表性能优化。
如果列表是纯粹的展示,不需要改变,就不需要做响应式
data: () =>({ user: []}),
async cteated(){
const user = await axios.get('/api/users');
this.user = Object.freeze(user) // 冻结 数据 ,不会变成响应式
}
如果是大数据列表,采用 虚拟滚动,只渲染少部分区域的内容。
vue-virtual-scroller
只显示 可视区的内容,滚动后加载其他
- 事件的销毁
vue组件销毁时, 会自动解绑它的全部指令及事件监听器。但仅限于组件本身的事件。 我们自己比如setInterval的定时器,需要我们在beforeDestroy的时候手动clearInterval - 图片懒加载 vue-lazyload
- 第三方插件按需引入
比如element-ui这样第三方组件库 按需引入, 避免体积太大。 - 无状态的展示型组件 ,使用函数式组件 (没有自己的状态实例)
<template functional>
<div>
<p v-for="(item,index) in props.items" :key="index" @click="props.itemClick(item)" />
</div>
</template>
- 子组件分割 : 比较复杂的组件,切割出来,自己管自己。不会影响其他。避免整个页面都重新渲染
- SSR
vuex的使用与理解
定义:
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式
。它采用集中式
存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测
的方式发生变化
解决的问题:
解决不同组件之间状态共享问题。 利用各个组件通信方式,虽然也能做到状态共享,当时旺旺需要在多个组件之间保持状态的一致性。这种模式很容易出问题,程序逻辑也更复杂。 vuex将组件的共享状态抽取出来,以全局单例模式管理,这样 任何组件都能用一致的方式获取和修改状态, 响应式的数据也能保证简洁的单向数据流动,我们的代码也变得更结构化且易于维护。
使用场景:vuex 并非必须的, 它帮我们管理共享状态,当却带来更多的概念和框架。 如果我们不打算开发大型单页应用,或者我们的应用并没有大量全局的状态需要维护,完全没有用vuexd 必要。
vuex的使用:
首先对核心概念的理解和应用,
将全局状态放入state对象中,它本身是一棵状态树, 组件中使用store实例的state访问这些状态; 然后又配套的mutation方法修改这些状态, 并且只能用mutation修改状态, 在组件中调用commit 方法提交mutation;
如果应用中有异步操作或复杂逻辑组合,我们需要编写action,执行结束如果有状态修改仍然需要提交mutation, 组件中调用action 使用dispatch方法派发。
最后模块化, 通过modules 选项组织 拆分出去各个 子模块, 在访问状态时注意添加子模块名称, 如果子模块 有设置 namespace,那么提交mutation和派发action时 还需要额外的 命名空间前缀。
原理:
https://www.jianshu.com/p/86dc8b5d004d
vuex实现单项数据流时需要做到数据的响应式, 借用了vue的数据响应式特性实现的, 它会利用vue将state 作为data 对其进行响应式处理,从而使得这些状态发生改变,能够导致组件重新渲染。
vue中组件之间的通信方式
- props
$emit/$on
$children/$parent
$attrs/$listeners
- ref
$root
- eventbus (事件总线)
- vuex
父子组件之间通信
- props
$emit/$on
$children/$parent
$attrs/$listeners
- ref
兄弟组件 - $parent
- $root
- eventbus
- vuex
跨层级关系 - eventbus
- vuex
- provide/inject
EventBus 又称为事件总线。在Vue中可以使用 EventBus 来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以上下平行地通知其他组件,但也就是太方便所以若使用不慎,就会造成难以维护的“灾难”,因此才需要更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次。https://zhuanlan.zhihu.com/p/72777951