Vue2源码分析-day1

初始化数据

vue中最核心的我们都知道那就是响应式数据,数据的变化视图自动更新。那么我们来new一个我们自己的vue

index.html文件下加入如下代码,这也是vue最常见的基本结构。data已经有了下面我们来获取data的数据

 <script src="./vue.js"></script><script>const vm=new MVue({data(){return{name:"zhangsan",age:"16"}}})</script>

首先我们在src/main.js文件下加入如下代码

// 创建MVue构造函数
function MVue(options) {      // options就是我们接受到用户的选项,选项式api}// 将构造函数导出
export default MVue

为了更能的扩展功能和方便维护,我们需要一个专门做初始化的方法。所以我们在src下创建init.js文件加入如下代码

export function init(MVue){// 给MVue原型对象上挂载_init()方法MVue.prototype._init = function(options){}
}

main.js文件下如果这个方法,并且调用

import { init } from "./init"// 创建MVue构造函数
function MVue(options) {      // options就是我们接受到用户的选项,选项式api// 当vue被new的时候调用_init()方法this._init(options)
}init(MVue)// 将构造函数导出
export default MVue

下面我们就可以写我们的初始化方法了,init.js文件下加入如下代码。可能你会好奇这个init_state()方法是那里的。由于用户选项有很多比如data,watch,computed…所以我们最好创建一个文件统一管理。在src文件下创建state.js文件

import { init_state } from "./state"export function init(MVue){// 给MVue原型对象上挂载_init()方法MVue.prototype._init = function(options){// 在用户选项挂载到实例上const vm=thisvm.$options=options// 初始化状态init_state(vm)}
}

state.js文件下加入如下代码

export function init_state(vm){// 获取实例上挂载的选项const opts=vm.$options;// 选项上如果存在data初始化dataif(opts.data){init_data(vm);}
}
function init_data(vm){let data=vm.$options.data;// 判断data是对象还是函数并且重新赋值data=typeof data==='function'?data.call(vm):data;console.log(data);
}

到这里,输入打包命令打开index.html如果在游览器下看到如下图所示,我们就成功获取到了data里面的数据

pnpm run build

在这里插入图片描述


实现对象的响应式原理

下面我们首先来实现只有一层对象的数据劫持。state.js文件下加入如下代码

import { observe } from "./observe/index";export function init_state(vm){// 获取实例上挂载的选项const opts=vm.$options;// 选项上如果存在data初始化dataif(opts.data){init_data(vm);}
}
function init_data(vm){let data=vm.$options.data;// 判断data是对象还是函数并且重新赋值data=typeof data==='function'?data.call(vm):data;// 将data劫持重新定义dataobserve(data)
}

src文件下创建observe/index.js文件并且加入如下代码

class Observer {constructor(data) {/* 这里注意,Object.defineProperty只能劫持已经存在的属性,新增或删除的无法劫持这也是为什么vue2中新增了,$set和$delete方法这类方法的原因*/this.walk(data)}walk(data) {// 循环对象keys依次劫持,重新定义属性Object.keys(data).forEach(key => {define_reactive(data, key, data[key])})// 这里重新定义data后我们打印看下data是否都加入了get和set方法console.log(data);}}// 这里会存在一个闭包,get和set方法存放了函数外部的变量value
export function define_reactive(data, key, value) {Object.defineProperty(data, key, {get() {return value},set(newValue){if(value!=newValue){value=newValue}}})
}export function observe(data) {// 对对象劫持if (typeof data !== 'object' || data === null) {return;}// 如果一个对象被劫持过就不需要再次劫持,我们可以通过一个观察类判断是否被劫持过return new Observer(data)
}

如果你的游览器打印了如下图所示,那么恭喜你只有一层对象的data你重新定义完成了

在这里插入图片描述
说起data,vue2中一直有个属性_data属性。这上面挂载了我们所有在data中定义的属性。下面我们来实现它

state.js文件下修改如下代码。没错就是这么简单要加入vm._data=data这行代码就完成了。由于这是引用值。所有对data的修改也会影响到_data

function init_data(vm){let data=vm.$options.data;// 判断data是对象还是函数并且重新赋值data=typeof data==='function'?data.call(vm):data;// 实例上挂载_datavm._data=data// 将data劫持重新定义dataobserve(data)
}

下面我们在index.html下打印vm看看是否有我们的_data属性

在这里插入图片描述

到这里,其实我们的_data属性并不完美,你通过vm获取属性的时候发现每次都要加上_data,
vm._data.name。但是vue2中并不是这样的而是直接vm.name就可以获取并且修改。下面我们来解决这个问题

state.js文件下加入如下代码。我们将将vm._data代理给vm就可以解决这个问题

function proxy(vm,source,key){Object.defineProperty(vm,key,{get(){return vm[source][key]},set(newValue){if(vm[source][key]!=newValue){vm[source][key]=newValue;}}})
}function init_data(vm){let data=vm.$options.data;// 判断data是对象还是函数并且重新赋值data=typeof data==='function'?data.call(vm):data;// 实例上挂载_datavm._data=data// 将data劫持重新定义dataobserve(data)// 将vm._data代理给vmfor(let key in data){proxy(vm,'_data',key)}
}

下面我们在看我们的vm实例会发现多了name和age属性。并且可以通过vm访问到和修改

在这里插入图片描述
到这里不知你们还记得我们最早说过的,这只是一个只有一层对象的数据劫持。如果多层就代理不到了
我们修改index.html代码如下

       const vm=new MVue({data(){return{name:"zhangsan",age:"16",address:{city:"地球村    "}}}})console.log(vm);

在游览器上。我们会看到address并且没有get和set方法。下面我们来解决这个问题

在这里插入图片描述

observe/index.js文件下修改如下代码

// 这里会存在一个闭包,get和set方法存放了函数外部的变量value
export function define_reactive(data, key, value) {observe(value)		// 递归调用我们的observe方法Object.defineProperty(data, key, {get() {return value},set(newValue){if(value!=newValue){value=newValue}}})
}

再次看我们的address就会发现get和set方法已经出现了。这说明我们的多级对象代理已经完成了

在这里插入图片描述

用户可能会存在这样的赋值,现在我们是没办法办法代理到的

const vm = new MVue({data() {return {name: "zhangsan",age: "16",}}
})
vm.address = {city: "地球村"
}

所以我们在observe/index.js文件下define_reactive方法中的set修改如下。如果直接通过vm赋值对象,给这些属性同样代理

set(newValue){if(value!=newValue){observe(newValue)value=newValue}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/57219.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AWS 中文入门开发教学 49- S3 - 区域间复制

知识点 S3 存储桶内容在全球区域间进行复制官网 https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/replication.html 实战演习 在东京区建立存储桶 Name: woyaofuzhi启用版本控制(完成区域间复制必须开启版本控制) 在新加坡区建立存储桶 Name: woyaofuzhibac…

CountDownLatch 使用详情

CountDownLatch 是 Java.util.concurrent 包下的一个类&#xff0c;它可以用来实现一个或多个线程等待其他线程完成后再继续执行的场景。 CountDownLatch 类中有一个计数器&#xff0c;每次调用 countDown() 方法计数器的值减1&#xff0c;当计数器的值变为0时&#xff0c;调用…

从零开始学习 Java:简单易懂的入门指南之API、String类(八)

常用API 1.API1.1API概述1.2如何使用API帮助文档 2.String类2.1String类概述2.2String类的特点2.3String类的构造方法2.4创建字符串对象两种方式的区别2.5字符串的比较2.5.1号的作用2.5.2equals方法的作用 2.6用户登录案例2.6.1案例需求2.6.2代码实现 2.7遍历字符串案例2.7.1案…

【MySQL】sql字段约束

在MySQL中&#xff0c;我们需要存储的数据在特定的场景中需要不同的约束。当新插入的数据违背了该字段的约束字段&#xff0c;MySQL会直接禁止插入。 数据类型也是一种约束&#xff0c;但数据类型这个约束太过单一&#xff1b;比如我需要存储的是一个序号&#xff0c;那就不可…

IP核之fifo

一.FIFO简介 FIFO (First In First Out&#xff0c;即先入先出&#xff09;&#xff0c;是一种数据缓冲器&#xff0c;用来实现数据先入先出的读写方式。 二&#xff0c;FIFO实现原理 FIFO是采用一种先入先出的实现原理 就如图按照D1到D10的顺序输入那么读取的时候也是按照D…

el-table合并表头、动态合并列、合并尾部合计

在有些情况下&#xff0c;我们会有合并表头、合并列、合并尾部合计的需求&#xff0c;这篇文章是为了记录&#xff0c;如何进行合并&#xff0c;方便日后翻阅。 效果图 el-table合并表头 el-table合并列&#xff08;动态合并&#xff09; el-table合并尾部合计 el-table合并表…

看门狗文章

1. iwdg.c #include "stm32f4xx.h" #include "iwdg.h"//prer&#xff1a;预分频值 //rlr&#xff1a;自动重装载值 void IWDG_Init(unsigned char prer,unsigned int rlr)//IWDG初始化 {IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);…

知网期刊《中阿科技论坛》简介及投稿须知

知网期刊《中阿科技论坛》简介及投稿须知 主管单位&#xff1a;宁夏回族自治区科学技术厅 主办单位&#xff1a;宁夏回族自治区对外科技交流中心(中国一阿拉伯国家技术转移中心) 刊  期&#xff1a;月刊 国际刊号&#xff1a;ISSN 2096-7268 国内刊号&#xff1a;CN 64-…

SystemVerilog scheduler

文章目录 简介调度器simulation regionPreponed regionActive regionInactive regionNBA(Non-blocking Assignment Events region)Observed regionReactive regionRe-Inactive Events regionRe-NBA RegionPostponed Region PLI region:Pre-active regionPre-NBA regionPost-NBA…

空地协同智能消防系统——无人机、小车协同

1 题目 1.1 任务 设计一个由四旋翼无人机及消防车构成的空地协同智能消防系统。无人机上安装垂直向下的激光笔&#xff0c;用于指示巡逻航迹。巡防区域为40dm48dm。无人机巡逻时可覆盖地面8dm宽度区域。以缩短完成全覆盖巡逻时间为原则&#xff0c;无人机按照规划航线巡逻。发…

面试热题(字符串相加)

给定两个字符串形式的非负整数 num1 和num2 &#xff0c;计算它们的和并同样以字符串形式返回。 你不能使用任何內建的用于处理大整数的库&#xff08;比如 BigInteger&#xff09;&#xff0c; 也不能直接将输入的字符串转换为整数形式。 输入&#xff1a;num1 "11"…

Linux下C/C++的gdb工具与Python的pdb工具常见用法之对比

1、gdb和pdb分别是什么&#xff1f; 1.1、gdb GDB&#xff08;GNU Debugger&#xff09;是一个功能强大的命令行调试工具&#xff0c;由GNU项目开发&#xff0c;用于调试C、C等编程语言的程序。它在多个操作系统中都可以使用&#xff0c;包括Linux、MacOS和Windows&#xff0…