关于VUE的一些总结

一、数据与方法

1、当一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
2、值得注意的是只有当实例被创建时就已经存在于 data 中的 property 才是响应式的。
3、组件的复用 data 必须是一个函数
4、Vue组件可能存在多个实例,如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的。采用函数形式定义,在initData时会将其作为工厂函数返回全新的data对象,有效规避多实例之间状态污染问题。
而在Vue根实例创建过程中则不存在该限制,也是因为根实例只能有一个,不需要担心这种情况。

二、VUE双向绑定原理

vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

1、Object.defineProperty()

Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性;

Object.defineProperty(obj, prop, desc)

obj
    要定义属性的对象。
prop
    要定义或修改的属性的名称或 Symbol 。
descriptor
    要定义或修改的属性描述符。 
	var temperature = null;
  	Object.defineProperty(this, 'temperature', {
    get: function() {
      console.log('get!');
      return temperature;
    },
    set: function(value) {
      temperature = value;
    }
  });
  	存取描述符 --是由一对 getter、setter 函数功能来描述的属性

	get:一个给属性提供getter的方法,如果没有getter则为undefined。该方法返回值被用作属性值。默认为undefined。
	set:一个给属性提供setter的方法,如果没有setter则为undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认值为undefined。

通过Object.defineProperty( )对属性设置set函数,当数据改变了就会来触发这个函数,所以我们只要将一些需要更新的方法放在这里面就可以实现data更新view了。

2、实现细节

实现数据的双向绑定:
1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。

(1) Observer 监听器

首先需要对定义的数据进行监听,其核心使用Object.defineProperty( ) 对设置的属性进行设置set get 函数,如果属性变化就通知订阅者Watcher是否需要更新。由于订阅者是多个,我们使用消息订阅器Dep;监听器Observer和订阅者Watcher之间进行统一管理;

(2) 消息订阅器Dep
用来容纳所有的“订阅者”。订阅器Dep主要负责收集订阅者,然后当数据变化的时候后执行对应订阅者的更新函数。就是典型的“发布订阅者”模式,数据变化为“发布者”,依赖对象为“订阅者”,

(3) 订阅者Watcher

订阅者Watcher在初始化的时候需要将自己添加进订阅器Dep中,已经知道监听器Observer是在get函数执行了添加订阅者Wather的操作的,所以我们只要在订阅者Watcher初始化的时候出发对应的get函数去执行添加订阅者操作即可;

(4) 解析器Compile

解析器Compile来做解析和绑定工作。解析器Compile实现步骤:
1.解析模板指令,并替换模板数据,初始化视图
2.将模板指令对应的节点绑定对应的更新函数,初始化相应的订阅器

实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。

三、 生命周期

1、beforeCreate
在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。

2、created
在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el property 目前尚不可用。

3、beforeMount
在挂载开始之前被调用:相关的 render 函数首次被调用。

4、mounted
实例被挂载后调用,这时 el 被新创建的 vm. e l 替 换 了 。 如 果 根 实 例 挂 载 到 了 一 个 文 档 内 的 元 素 上 , 当 m o u n t e d 被 调 用 时 v m . el 替换了。如果根实例挂载到了一个文档内的元素上,当 mounted 被调用时 vm. elmountedvm.el 也在文档内。

注意 mounted 不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在 mounted 内部使用
vm.$nextTick:

5、beforeUpdate
数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。

6、updated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。

当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。

注意 updated 不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在 updated 里使用
vm.$nextTick:

updated: function () { this.$nextTick(function () {
// Code that will run only after the
// entire view has been re-rendered }) }

7、activated
被 keep-alive 缓存的组件激活时调用。

8、deactivated
被 keep-alive 缓存的组件停用时调用。

9、beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用。

10、destroyed
实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。

11、errorCaptured
当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。

你可以在此钩子中修改组件的状态。因此在捕获错误时,在模板或渲染函数中有一个条件判断来绕过其它内容就很重要;不然该组件可能会进入一个无限的渲染循环

四、VUE通信


    1、父子组件互相传值props和$emit
    2、父组件和子孙组件传值 $attrs 和 $listeners
    3、父组件 $children 操作子组件 和  子组件 $parent 访问父组件
    4、非父子组件通信 中央事件总线 $emit / $on
    5、provide / inject 跨级组件(非响应式,provide改变inject不变)
    6、ref
    7VUEX

1、父子组件互相传值props和$emit

父组件向子组件传递数据是通过prop传递的,子组件传递数据给父组件是通过$emit触发事件来做到的.

1).父组件传递了message数据给子组件,并且通过v-on绑定了一个getChildData事件来监听子组件的触发事件;
2).子组件通过props得到相关的message数据,最后通过this.$emit触发了getChildData事件

2、父组件和子孙组件传值 $attrs 和 $listeners

当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。

<!-- 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) -->
 <C v-bind="$attrs" v-on="$listeners"></C>

3、父组件 $children 操作子组件 和 子组件 $parent 访问父组件

使用 $children 操作子组件

  this.$children.forEach(child => {
     child.$data.colored = !child.$data.colored // 逐一控制子组件的 $data
    })

子组件可通过 $parent 来修改父组件的 $data

 this.$parent.$data.colored = !this.$parent.$data.colored

4、非父子组件通信 中央事件总线 $emit / $on

新建一个Vue事件bus对象,然后通过bus.$emit触发事件,bus. $on 监听触发的事件

具体步骤是创建一个 Vue 实例,然后 $on 监听事件,$emit 来派发事件

// src/eventBus.js
import Vue from 'vue'
let EventBus = new Vue();

export const $bus = EventBus;


首先创建并导出一个 Vue 实例

import bus from '@/eventbus'

export default {
    // ...
  methods: {
    handleClick (e) {
      bus.$emit('change-color')
    }
  }
}
后代元素 $emit 触发 eventBus 的事件

import bus from '@/eventbus'

export default {
    // ...
  mounted () {
    bus.$on('change-color', () => {
      this.colored = !this.colored
    })
  }
}

祖先元素 $on 方法监听 eventBus 的事件

5、provide/inject 依赖注入

父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。

在这里插入代码片
export default { 
  provide: { 
    name: '浪里行舟' 
  } 
} 
export default { 
  inject: ['name'], 
  mounted () { 
    console.log(this.name);  // 浪里行舟 
  } 
} 

6、ref

如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例

7、 Vuex

vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。

五、Vue性能优化方法

1、路由懒加载

const router = new VueRouter({
  routes: [
    {path: '/foo',component: ()=>import('./Foo.vue')}
  ]
})

2、keep-alive缓存页面

<template>
  <div id="app">
    <keep-alive>
      <router-view />
    </keep-alive>
  </div>
</template>

3、使用v-show 复用DOM

4、利用Object.freeze()提升性能

纯粹的数据展示,不会有任何变化,就不需要做响应化

5、使用单文件组件预编译模板

六、vue中的diff算法

渲染真实DOM的开销是很大的,比如有时候我们修改了某个数据,如果直接渲染到真实dom上会引起整个dom树的重绘和重排,有没有可能我们只更新我们修改的那一小块dom而不要更新整个dom

根据真实DOM生成一颗virtual DOM,当virtual DOM某个节点的数据改变后会生成一个新的Vnode,然后Vnode和oldVnode作对比,发现有不一样的地方就直接修改在真实的DOM上,然后使oldVnode的值为Vnode。
diff的过程就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁。

1、virtual DOM是将真实的DOM的数据抽取出来,以对象的形式模拟树形结构

var Vnode = {
    tag: 'div',
    children: [
        { tag: 'p', text: '123' }
    ]
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值