> 文章列表 > vue原理理解记录

vue原理理解记录

vue原理理解记录

背景

我们在学习vue的时候,刚开始看到的是vue是一个渐进式的JavaScript框架。这里是我们很多同学能够很快上手vue开的一个原因。但是随着我们开发过程中遇到各种的问题,如果我们想要快速的解决问题和了解其原理的时候,那么就需要我们对vue原理进行学习和深入的研究。有时候我们使用v-for的时候要使用key,但是你知道为什么要使用key吗,我们知道data的数据发生变化时,页面上就会发生相应的变化这个又是因为什么呢,带着这样的疑问,我们学习一下,是什么能够帮助我们实现响应式和vue 的diff算法、双向绑定、mvvm设计模式。

一、vue响应式的原理:

在vue2中我们是使用Object.defineProperty,我们在MDN上查找其定义。

Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

语法:

Object.defineProperty(obj, prop, descriptor)参数
obj
要定义属性的对象。

prop
要定义或修改的属性的名称或 Symbol 。

descriptor
要定义或修改的属性描述符。

返回值
被传递给函数的对象。

示例:

var o = {}; // 创建一个新对象// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(o, "a", {value : 37,writable : true,enumerable : true,configurable : true
});// 对象 o 拥有了属性 a,值为 37// 在对象中添加一个设置了存取描述符属性的示例
var bValue = 38;
Object.defineProperty(o, "b", {// 使用了方法名称缩写(ES2015 特性)// 下面两个缩写等价于:// get : function() { return bValue; },// set : function(newValue) { bValue = newValue; },get() { console.log("调用了set方法") return bValue;},set(newValue) { console.log("调用了set方法") bValue = newValue; },enumerable : true,configurable : true
});o.b; // 38 打印调用了get方法
o.b = 14 //打印调用了set方法

下面我们通过一个例子来看看是如何进行页面更新的。

我们先定义一个视图更新的函数;

function updateView(){

    console.log('视图更新') 

}

考虑到defineProperty方法没有办法监听数组,因此我们需要重新定义数组原型,不能影响到原先的原型。

const arrProperty  = Array.prototype

//创建新的对象原型指向我们新定义的arrProperty,再扩展新的方法不会影响原型

const arrProtoTypy = Object.create(arrProperty);

['push','shift','unshift','pop','splice'].forEach(methodName=>{

    updateView()//触发视图更新

    arrProtoType.apply(this,...arguments)

}) 

//定义一个属性并监听起来

function defineReactive(target,key,value){

    //为了深度监听

   Object.defineProperty(target,key,{

       get(){
         return value

      } 

      set (newValue){

         if(newValue !== value){

           //不确定newValue是不是对象我们就要继续去深度的监听

          observer(newValue)

          value = newValue

          updateView()

         }    

    } 

   })

}

//监听对象属性

function observer(target){

    if(typeof target ! =='object' || target === null ){

       //不是对象或者数组,直接返回

       return target

     }

     if (Array.isArray(target)){

       target.__proto__ = arrPrototype

    }

  for( let key in target) {

     defineReactive(target,key,target[key])

  }

}

验证:

定义一个对象

const data = {

  name:'zhangsan',

  age:30,

  children:[

   son:{ name:'zhangsi',age:4}   ,

   ],

 wife:{

   name:"xiaotiantian",

   age:28

}

}

//监听数据

observer(data)

// 测试

data.name = 'zhangsanfeng'

 data.age = 35

// // console.log('age', data.age)

 data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.set

 delete wife.name // 删除属性,监听不到 —— 所有已 Vue.delet

 data.wife.age= 30 // 深度监听

data.children.push({name:"xiaokeai",age:1}) // 监听数组

二、上面看到的是vue2的,在vue3版本中实现响应式的是Proxy

Proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

术语
handler (en-US)
包含捕捉器(trap)的占位符对象,可译为处理器对象。

traps
提供属性访问的方法。这类似于操作系统中捕获器的概念。

target
被 Proxy 代理虚拟化的对象。它常被作为代理的存储后端。根据目标验证关于对象不可扩展性或不可配置属性的不变量(保持不变的语义)。

语法
const p = new Proxy(target, handler)
Copy to Clipboard
参数
target
要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

handler
一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

我们看一下是如何实现响应式的;

Reflect是一个映射的方法,将逐步代替Object

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 proxy handler (en-US) 的方法相同。Reflect 不是一个函数对象,因此它是不可构造的。

描述
与大多数全局对象不同 Reflect 并非一个构造函数,所以不能通过 new 运算符对其进行调用,或者将 Reflect 对象作为一个函数来调用。Reflect 的所有属性和方法都是静态的(就像 Math 对象)

// const data = {

//     name: 'zhangsan',

//     age: 20,

// }

const data = ['a', 'b', 'c']

const proxyData = new Proxy(data, {

    get(target, key, receiver) {

        // 只处理本身(非原型的)属性

        const ownKeys = Reflect.ownKeys(target)

        if (ownKeys.includes(key)) {

            console.log('get', key) // 监听

        }

        const result = Reflect.get(target, key, receiver)

        return result // 返回结果

    },

    set(target, key, val, receiver) {

        // 重复的数据,不处理

        if (val === target[key]) {

            return true

        }

        const result = Reflect.set(target, key, val, receiver)

        console.log('set', key, val)

        // console.log('result', result) // true

        return result // 是否设置成功

    },

    deleteProperty(target, key) {

        const result = Reflect.deleteProperty(target, key)

        console.log('delete property', key)

        // console.log('result', result) // true

        return result // 是否删除成功

    }

})

这篇文章中我们学习一个vue2和vue3事项响应式的核心API,下一章我们学习一个vue的diff算法

网页前端