vue2-部分优化与理解

v-if/v-if-else/v-else 中使用 key

正确内容
错误内容

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


有点事简单粗暴,改动小。 缺点是 ,每次切换 都会 被销毁并重新创建,很消耗性能。

为所有路由添加 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()

  1. diff算法是虚拟DOM技术的产品: 通过 新旧虚拟DOM对比(即diff),将变化的地方更新在真实DOM上,另外, 也需要diff高效的执行对比过程, 从而降低时间复杂度为O(n).

  2. vue2为了降低 watcher粒度, 每个组件只有一个watcher与之对应,引入diff才能精确的查找发生变化的地方。

  3. vue中执行diff的时刻是 组件实例执行 更新函数时,它会对比上一次渲染结果 oldVnode和新的渲染结果 newVnode,此过程称为 patch.

  4. diff 过程整体遵循深度优先、同层比较的策略。 两个 节点之间会根据它们时候拥有子节点或者文本节点做不同的操作。 比较两组子节点是 算法的重点。借助key通常可以精准的找到相同的节点,因此patch过程是高效的。

组件化的理解

vue实例化时自上而下的,但是 挂载是自下而上的。

  1. 组件 是独立可 复用的 代码组织单元。 组件系统是 vue 核心特性之一, 它使 开发者使用小型、独立和通常可以复用的 组件 构建大型应用。
  2. 组件化开发能大幅提高应用开发效率、测试性、复用性等;
  3. 组件使用按分类有: 页面组件、 业务组件、 通用组件
  4. vue的组件是基于配置的,同名通常编写的组件配置 而非组件, 框架 后续会生产其 构造函数,它们基于 VueComponent,扩展Vue;
  5. vue中常见的组件化技术有: 属性prop,自定义事件、插槽等,它们主要用于组件通信、扩展等;
  6. 合理的划分组件,有利于提升应用性能(比如某一块经常更新,把它提为组件,组件有单独的watcher。这样更新,就不会影响其他的)
  7. 组件应该高内聚、低耦合
  8. 遵循单向数据流的原则

vue性能优化方法

  1. 路由懒加载
routes:[
  {path: '/foo', component: ()=> import('./Foo.vue')}
]
  1. keep-alive 缓存页面
  2. v-show 复用DOM (频繁显示,组件比较重的情况,渲染时间比较长)
  3. v-for 遍历避免同时 使用 v-if (用computed计算属性处理)
  4. 长列表性能优化。
    如果列表是纯粹的展示,不需要改变,就不需要做响应式
data: () =>({ user: []}),
async cteated(){
 const user = await axios.get('/api/users');
 this.user = Object.freeze(user) // 冻结 数据 ,不会变成响应式
}

如果是大数据列表,采用 虚拟滚动,只渲染少部分区域的内容。
vue-virtual-scroller
只显示 可视区的内容,滚动后加载其他

  1. 事件的销毁
    vue组件销毁时, 会自动解绑它的全部指令及事件监听器。但仅限于组件本身的事件。 我们自己比如setInterval的定时器,需要我们在beforeDestroy的时候手动clearInterval
  2. 图片懒加载 vue-lazyload
  3. 第三方插件按需引入
    比如element-ui这样第三方组件库 按需引入, 避免体积太大。
  4. 无状态的展示型组件 ,使用函数式组件 (没有自己的状态实例)

  1. 子组件分割 : 比较复杂的组件,切割出来,自己管自己。不会影响其他。避免整个页面都重新渲染
  2. 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

你可能感兴趣的:(vue2-部分优化与理解)