手写Vue (1) 对象劫持

1.引入我们手写的Vue 拿到配置数据

import Vue from ‘../source/src/index‘;
let vm = new Vue({
  el: ‘#app‘,
  data() {
    return {
      msg: ‘hello‘,
      school: {
        name: ‘zf‘,
        age: 10
      },
      arr: [1, 2, 3]
    }
  },
  computed: {

  },
  watch: {

  }
})
console.log(vm)
// setTimeout(() => {
//   vm.arr[0].push(100)
//   console.log(vm)
// }, 1000)

2. 新建文件    source/src/index

在这个文件中 我们利用 ——init 这个方法 对 用户传入的数据进行初始化 传入配置信息

function Vue(options) {
  // console.log(options)
  // 初始化vue
  this._init(options)
}

3. 在_init 方法中 我们可以先将  options 挂载到实例  再进行初始化数据操作

Vue.prototype._init = function (options) {
  // vue 的初始化
  let vm = this;
  // 将 options 挂载到实例
  vm.$options = options;
  //需要数据重新初始化
  initState(vm)
}

5. 新建 文件  observe/index 编写  initState  方法并且导出

export function initState(vm) {
  console.log(vm)
}

在这里我们可以拿到 vue 的整个实例方法

6. 在initState  方法中 我们进行初始化  比如初始化 数据 初始化 计算属性 初始化 watch

export function initState(vm) {
  // 拿到option  存储起来
  let options = vm.$options;
  if (options.data) {
    // 初始化数据
    initData(vm)
  }
  if (options.computed) {
    // 初始化计算属性
    initComputed()
  }
  if (options.watch) {
    // 初始化watch
    initWatch()
  }
}

7. 初始化数据 在    initData  方法中 我们通过 传入实例的option 拿到数据 再判断 数据 是 函数 还是 对象 如果是函数 调用call 方法 拿到函数 返回值 如何不是直接返回数据 或者 空数据

 let data = vm.$options.data
  // 判断是否是函数 取返回值
  data = vm._data = typeof data === ‘function‘ ? data.call(vm) : data || {}

8. 拿到数据后 我们要对数据进行 监听 编写   observe  方法 监听数据 、

function initData(vm) {
  let data = vm.$options.data
  // 判断是否是函数 取返回值
  data = vm._data = typeof data === ‘function‘ ? data.call(vm) : data || {}
  observe(vm._data)
}

10 在  observe 方法中 要进行判断 看看数据是不是对象或者为空 如果不是 直接返回 如果是 返回一个  Observe 对象

export function observe(data) {
  // 不是对象或者是null
  if (typeof data !== ‘object‘ || data === null) {
    return
  }
  return new Observe(data)
}

11. 编写这个  Observe 对象   新建文件 Observe.js

这个文件里面最主要的是   Object.defineProperty 方法 里面传入 data , 还有key  key 代表属性 所以 我们需要遍历数据 拿到 所有的 key value 传入

class Observe {
  constructor(data) {
    // 数组 重写push 方法
    if (Array.isArray()) {
    } else {
      // 对象
      // data 就是我们 定义的 vm._data 的数据
      this.walk(data)
    }
  }
  //将对象的数据使用 defineProperty 重新定义
  walk(data) {
    let keys = Object.keys(data);
    for (let i = 0; i < keys.length; i++) {
      let key = keys[i];  // key
      let value = data[keys[i]];  // value
      defineReactive(data, key, value)
    }
  }
}
export function defineReactive(data, key, value) {
  Object.defineProperty(data, key, {
    get() {
      // 有值
      return value;
    },
    set(newValue) {
      if (newValue !== value) return;
      value = newValue
    }
  })
}
export default Observe;

11.  因为 数据里面 的对象 可能嵌套 一个对象 所以我们应该在 数据里面的对象再进行 深度监听


import { observe } from ‘./index‘

export function defineReactive(data, key, value) {
  observe(value);
  Object.defineProperty(data, key, {
    get() {
      // 有值
      return value;
    },
    set(newValue) {
      if (newValue !== value) return;
      value = newValue
    }
  })
}

12. 测试代码

import Vue from ‘../source/src/index‘;
let vm = new Vue({
  el: ‘#app‘,
  data() {
    return {
      msg: ‘hello‘,
      school: {
        name: ‘zf‘,
        age: 10
      },
      arr: [1, 2, 3]
    }
  },
  computed: {

  },
  watch: {

  }
})
console.log(vm._data.msg)  // hello

打印出100

13.  这里每次取值都需要 挂在 _data 上  很 不方便 比如

vm._data.msg  我们简化为 vm.msg

所以 我们可以 利用  proxy 代理 _data  代理应该在 初始化数据 的操作中 监听数据前

function initData(vm) {
  let data = vm.$options.data
  // 判断是否是函数 取返回值
  data = vm._data = typeof data === ‘function‘ ? data.call(vm) : data || {}
  for (let key in data) {
    proxy(vm, "_data", key)
  }
  observe(vm._data)
}

14, 编写 proxy  方法

function proxy(vm, source, key) {
  Object.defineProperty(vm, key, {
    get() {
      return vm[source][key]
    },
    set(newValue) {
      vm[source][key] = newValue
    }
  })
}

15。测试成功

import Vue from ‘../source/src/index‘;
let vm = new Vue({
  el: ‘#app‘,
  data() {
    return {
      msg: ‘hello‘,
      school: {
        name: ‘zf‘,
        age: 10
      },
      arr: [1, 2, 3]
    }
  },
  computed: {

  },
  watch: {

  }
})
console.log(vm.msg)  // hello

原文地址:https://www.cnblogs.com/guangzhou11/p/12650360.html

时间: 2024-10-30 03:01:11

手写Vue (1) 对象劫持的相关文章

学习手写vue,理解原理

class Compiler{ constructor(el,vm){ // 判断el属性 是不是 一个元素, 如果不是就获取 this.el = this.isElementNode(el)?el:document.querySelector(el); // console.log(this.el); this.vm = vm; // 把当前节点放到内存中 let fragment = this.node2fragment(this.el); // console.log(fragment,"

手写vue中v-bind:style效果的自定义指令

自定义指令 什么是自定义指令 以 v- 为前缀,然后加上自己定义好的名字组成的一个指令就是自定义指令.为什么要有自定义指令呢?在有些时候,你仍然需要对普通的DOM元素进行底层的操作,这个时候就可以用到自定义指令. 自定义指令的语法 全局自定义指令 // 注册一个全局自定义指令 `v-focus` Vue.directive('focus', { // 当被绑定的元素插入到 DOM 中时-- inserted: function (el) { // 聚焦元素 el.focus() } }) 局部自

手写vue双向绑定数据

来一张原理图: 实现思路: (1)绑定data 种的数据,为每个数据添加指令.通过Object,defineProperty() 来通知属性是否更改 (2) 找到每个DOM节点的指令.绑定事件.并绑定watcher (3)  实现DOM事件改变之后, 响应data数据,实现视图更新 <!DocType> <html> <title>vue 的双向绑定事件</title> <body id="app"> <input ty

手写Vue (1) 准备工作

1.安装插件 "devDependencies": { "html-webpack-plugin": "^4.0.4", "webpack": "^4.42.1", "webpack-cli": "^3.3.11", "webpack-dev-server": "^3.10.3" } 2.配置 项目 的基本结果 (1)根目录下新

基于vue手写tree插件那点事

目录 iview提供的控件 手写控件 手写控件扩展 手写控件总结 # 加入战队 微信公众号 主题 Tree树形控件在前端开发中必不可少,对于数据的展示现在网站大都采取树形展示.因为大数据全部展示出来对于用户来说是不友好的.今天我们自己手写一个Tree插件. iview提供的控件 iview已经很成熟了,如果说我写的控件和iview提供的控件谁更好,那肯定是选择iview , 手写控件只是为了更好的了解vue父子组件之间的通信的. 请读者还是不要拿我的控件和iview或者其他第三方的去对比.下面我

iOS开发UI基础—手写控件,frame,center和bounds属性

一.手写控件 1.手写控件的步骤 (1)使用相应的控件类创建控件对象 (2)设置该控件的各种属性 (3)添加控件到视图中 (4)如果是button等控件,还需考虑控件的单击事件等 (5)注意:View Contollor和view的关系 2.注意点 在OC开发中,Storyboard中的所有操作都可以通过代码实现,程序员一定要熟练掌握代码布局界面的能力! 设置控件监听方法的示例代码如下: [btn addTarget:self action:@selector(click:) forContro

使用Caffe进行手写数字识别执行流程解析

之前在 http://blog.csdn.net/fengbingchun/article/details/50987185 中仿照Caffe中的examples实现对手写数字进行识别,这里详细介绍下其执行流程并精简了实现代码,使用Caffe对MNIST数据集进行train的文章可以参考  http://blog.csdn.net/fengbingchun/article/details/68065338 : 1.   先注册所有层,执行layer_factory.hpp中类LayerRegis

IOS基础UI之(三)手写UI和storyboard方式实现图片移动和缩放

手写UI是最早进行UI界面布局的方法,优点是灵活自由,缺点是使代码看起来比较长.平时学习的时候可以多尝试手写ui,这样会更深入熟悉控件.storyboard开发效率相对比较高.实际开发中看情况而定!! 下面用这两种方式分别实现图片移动和缩放. 功能描述: 1. 界面布局 2.点击相应的按钮,对显示的图片移动.缩放. 效果如下:    掌握点: 一:熟悉代码的描述UIButton属性 1.UIButton状态 UIControlStateNormal          // 正常状态 UICont

学习笔记TF024:TensorFlow实现Softmax Regression(回归)识别手写数字

TensorFlow实现Softmax Regression(回归)识别手写数字.MNIST(Mixed National Institute of Standards and Technology database),简单机器视觉数据集,28X28像素手写数字,只有灰度值信息,空白部分为0,笔迹根据颜色深浅取[0, 1], 784维,丢弃二维空间信息,目标分0~9共10类.数据加载,data.read_data_sets, 55000个样本,测试集10000样本,验证集5000样本.样本标注信