vue2源码解析之第一步(对数据进行劫持)

###环境搭建

第一步 创建项目:
        npm init -y 

第二步 安装对应的插件:
        npm i rollup rollup-plugin-babel @babel/core @babel/preset-env --save-dev

第三步 全局下创建rollup配置文件 rollup.config.js

import babel from 'rollup-plugin-babel'
export default {input:'./src/index.js', // 入口文件output:{file:'./dist/vue.js', //出口文件name:'Vue', // global.Vueformat:'umd', // umd格式sourcemap:true, // 调式代码 debug}, plugins:[babel({excludes:'node_modules/**'}) // 忽略文件]}

第四步 修改package.js文件的配置:

将代码修改成
"script":{"dev":"rollup -c -w" // 启动rollup的命令 }

第五步 创建.babelrc文件

{"presets":["@babel/preset-env"]
}
### 搭建基本的目录结构

项目根目录下src文件夹创建index.js文件, 项目根目录创建dist文件夹创建vue.js文件和index.html文件。

这时候index.js 文件中随便输入代码, 运行npm run dev将会把打包的代码,同步在dist/vue.js文件中。

dist文件下的index.html代码引入vue.js文件,并创建vue的实例对象传递参数,参数是一个对象,有data el methods coputed等方法 

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><script src="./vue.js"></script><script>const vm = new Vue({data(){return { name:'zs',age:20 }}})</script>
</body>
</html>

src/index.js 文件代码如下: 需要创建一个Vue的构造函数

function Vue(options){// 挂载一个初始化数据的方法_init()this._init(options) // this执行创建出来的Vue的实例对象 也就是dist/index.js中的vm
}
export default Vue //将这个构造函数导出

此刻项目结构如下 


###初始化数据 对数据做劫持
 

首先创建initMiXin的方法接受一个Vue作为参数,给Vue原型添加一个初始化数据的_init的方法 

import {initMiXin} from './init' // 导入一个方法 在init.js文件中
function Vue(options){this._init(options) // this执行创建出来的Vue的实例对象 也就是dist/index.js中的vm
}
initMiXin(Vue) //将Vue实例作为参数 传递出去
export default Vue //将这个构造函数导出

在与src/index.js文件 同级目录下创建一个state.js初始化数据的方法
 

import {observe} from './observe/index' // 这里先引入后期再下面要创建的方法export const initMiXin = (Vue){Vue.prototype._init(options) {  // 这里接收的是src/index.js中传递的参数const vm = this // 这里的this是Vuevm.$options = options // 把数据挂载在vm.$options的属性上面initState(vm) // 初始化数据} }function initState(vm){const ops =  vm.$optionsif(ops.data){  // 如果有data这个属性initData(vm) // 初始化Data}}function initData(vm){ let data = vm.$options.data data = typeof data === 'function'? data() : data // 判断data类型如果是函数的话就执行vm._data= data //再往Vue上面挂载一个_data的属性observe(data) // 这里对数据进行劫持
}

在src目录下创建observe文件夹创建index.js文件

export const observe = (data)=>{if(typeof data !=='object' || data=null){ // 对data数据进行处理return false // 后面的代码不用执行因为data返回值需要是一个对象} return new Observer(data) // 创建一个Observer的类 来对数据进行处理
}class Observer{constructor(data){this.wark(data)}wark(data){ // 挂载在Observe原型上面的方法// 循环每一项 创建defineReactive 劫持对象中每一个属性Object.keys(data).forEach(key=>defineReactive(data,key,data[key]))}
}export function defineReactive(data,key,value) {observe(value) // 如果属性值或者数据也要遍历进行劫持Object.defineProperty(data,key,{get(){return value},set(newValue){if(newValue === value) return  // 不相同的时候再重新赋值observe(newValue) // 对设置的新的属性值也要劫持value = newValue}})
}

如果此刻我们访问vm实例对象中的数据的时候,还需要使用vm._data.name vm._data.age才能访问到,此刻我们实现vue中 只需要this.name  this.age就可了 。我们需要在state.js文件中实现

import {observe} from './observe/index' // 这里先引入后期再下面要创建的方法export const initMiXin = (Vue){Vue.prototype._init(options) {  // 这里接收的是src/index.js中传递的参数const vm = this // 这里的this是Vuevm.$options = options // 把数据挂载在vm.$options的属性上面initState(vm) // 初始化数据} }function initState(vm){const ops =  vm.$optionsif(ops.data){  // 如果有data这个属性initData(vm) // 初始化Data}}function initData(vm){ let data = vm.$options.data data = typeof data === 'function'? data() : data // 判断data类型如果是函数的话就执行vm._data= data //再往Vue上面挂载一个_data的属性observe(data) // 这里对数据进行劫持// ++++++++++++++++++++++增加的代码for(let key in data){  //  +++++ 增加的代码 使用vm来代理就可以proxy(vm,_data,key) +++++}  ++++++
} proxy(vm,target,key){ // 以下都是增加的代码 ++++++++Object.defineProperty(vm,key,{ // 这里相当于后期执行 vm.name  vm.age get(){return vm[target][key] // vm._data.name   vm._data.age},set(newValue){vm[target][key] = newValue // vm.name='ls' === vm._data.name = 'ls'}})
}

此刻 我们如果对象中的某个属性值不再是对象 而是 数组的话 我们就需要重新写数组的方法了。
我们在observe/index.js文件下继续修改代码 增加后期修改的属性值是对象的判断。

export const observe = (data)=>{if(typeof data !=='object' || data=null){ // 对data数据进行处理return false // 后面的代码不用执行因为data返回值需要是一个对象} return new Observer(data) // 创建一个Observer的类 来对数据进行处理
}class Observer{constructor(data){++++ 这里我们需要对data数据进行判断是否为数组类型了if(Array.isArray(data)){ +++ 如果是数组 重写数组方法修改数组本身this.observeArray(data)    }else { +++ 不是数组继续执行下面数据劫持的方法this.wark(data)}}wark(data){ // 挂载在Observe原型上面的方法// 循环每一项 创建defineReactive 劫持对象中每一个属性Object.keys(data).forEach(key=>defineReactive(data,key,data[key]))}obseerveArray(data){ +++ 观察数据 如果数组中有对象的话会被劫持没有对象就不会被劫持data.forEach(item=>observe(item))}
}export function defineReactive(data,key,value) {observe(value) // 如果属性值或者数据也要遍历进行劫持Object.defineProperty(data,key,{get(){return value},set(newValue){if(newValue === value) return  // 不相同的时候再重新赋值observe(newValue) // 对设置的新的属性值也要劫持value = newValue}})
}

接下来我们重写数组的方法:observe文件夹下创建array.js文件

let oldArrayProto = Array.prototype //将array原型上面的所有属性和方法赋值一份export let newArrayProto = Object.create(oldArrayProto) // 给newArrayProto创建原型prototypelet methods = ['push','pop','shift','unshift','reverse','sort','splice'] // 重写数组方法methods.forEach(method=>{newArrayProto[method] = function (...ags) { // 重写了数组的方法const result = oldArrayproto[method].call(this,...ags) let inserted;let ob = this.__ob__;switch(method){case 'push':case 'unshift':inserted = ags;break;case 'splice':inserted = args.slice(2)default:brack;}if(inserted) {ob.observeArray(inserted)}return  result }
})

接下来将数组重写的方法 挂载在data的__proto__的属性上面; 在observe/index.js文件下
 

import {newArrayProto} from './array.js'
class Observer{constructor(data){data.__ob__= this // 给数据增加一个标识 如果有__ob__说明数据被观察过了if(Array.isArray(data)){ //  将这里的代码修改如下     data.__proto__ = newArrayProto // +++++增加代码this.observeArray(data)}else { this.wark(data)}}

在observe./index.js文件中可以增加判断数据是否被检测过了

export const observe = (data)=>{if(typeof data !=='object' || data=null){ // 对data数据进行处理return false // 后面的代码不用执行因为data返回值需要是一个对象} if(data.__ob__ instanceof Observer) {  +++++  // 判断数据是否被检测过了吗return data.__ob__  }return new Observer(data) // 创建一个Observer的类 来对数据进行处理
}

将__ob__变为不可枚举的这样在遍历的时候就不会遍历到了 observe/index.js文件下

import {newArrayProto} from './array.js'class Observer{constructor(data){Oject.defineProperty(data,'__ob__',{ ++++++++++++++++++++value:this,enumerable:false // 不可枚举})// ------------- 替换这行代码  data.__ob__= this if(Array.isArray(data)){ //  将这里的代码修改如下     data.__proto__ = newArrayProto // +++++增加代码this.observeArray(data)}else { this.wark(data)}}


 

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

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

相关文章

【开源】基于JAVA、微信小程序的音乐平台

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示 四、核心代码4.1 查询单首音乐4.2 新增音乐4.3 新增音乐订单4.4 查询音乐订单4.5 新增音乐收藏 五、免责说明 一、摘要 1.1 项目介绍 基于微信小程序JAVAVueSpringBootMySQL的音乐平台&#xff0c;包含了音乐…

用通俗易懂的方式讲解:大模型 RAG 在 LangChain 中的应用实战

Retrieval-Augmented Generation&#xff08;RAG&#xff09;是一种强大的技术&#xff0c;能够提高大型语言模型&#xff08;LLM&#xff09;的性能&#xff0c;使其能够从外部知识源中检索信息以生成更准确、具有上下文的回答。 本文将详细介绍 RAG 在 LangChain 中的应用&a…

外汇天眼:

随着年关将近&#xff0c;各种电信诈骗也层出不穷&#xff0c;令人防不胜防。 上个月&#xff0c;台北市警方就接到辖区内银行的诈骗通报&#xff0c;得知竟有民众被骗走新台币1亿元以上&#xff0c;这究竟是怎么回事呢&#xff1f; 根据警方掌握到的情报&#xff0c;受害者是…

最新Python安装和pycharm使用

1,进入python官网,点击download下载 2,双击默认下一步进行安装,并勾选添加环境变量 3,全选安装 ,4,安装完成,关闭即可 5,使用命令窗口测试版本 =======================================

JavaScript日期和时间处理手册

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》 ​ ​ ✨ 前言 日期和时间在应用开发中是非常常用的功能。本文将全面介绍JavaScript中处理日期和时间的方…

MT36291 2.5A 高效的1.2MHz电流模式升压转换器 DCDC管理芯片 航天民芯

描述 MT36291是一个恒定频率、6引脚SOT23电流模式升压转换器&#xff0c;旨在用于小型、低功耗的应用。MT36291的开关频率为1.2MHz&#xff0c;并允许使用2mm或更低高度的微小、低成本的电容器和电感器。内部软启动导致注入电流小&#xff0c;延长电池寿命。MT36291的特点是在光…

openssl3.2 - 自己构建openssl.exe的VS工程(在编译完的源码版本上)

文章目录 openssl3.2 - 自己构建openssl.exe的VS工程(在编译完的源码版本上)概述笔记备注END openssl3.2 - 自己构建openssl.exe的VS工程(在编译完的源码版本上) 概述 将openssl3.2编译出来了(openssl3.2 - 编译) 安装后的openssl.exe可以干openssl3.2所有的事情, 用openssl.…

在VSCode中安装使用Copilot

在VSCode里找到扩展 安装好后登录github&#xff0c;授权 在VSCode里初步尝试 创建一个js文件 写一个函数名 在括号里回车&#xff0c;会出现可能的代码 如果觉得可以&#xff0c;按一下tab后&#xff0c;代码变亮

探索 OceanBase 中图数据的实现

在数据管理和处理的现代环境中&#xff0c;对能够处理复杂数据结构的复杂数据模型和方法的需求从未如此迫切。图数据的出现以其自然直观地表示复杂关系的独特能力&#xff0c;开辟了数据分析的新领域。 虽然 Neo4j 等成熟的图形数据库为处理图形数据提供了强大的解决方案&…

BabylonJS 6.0文档 Deep Dive 摄像机(三):自定义摄像机输入

1. 如何自定义摄像机输入 当你调用摄像机的attachControl方法之后&#xff0c;摄像机都会自动为您处理输入。可以使detachControl方法撤消该控件。大多数Babylon.js专家使用两步流程来激活和连接相机&#xff1a; //First, set the scenes activeCamera... to be YOUR camera…

python_数据可视化_pandas_导入excel数据

目录 1.1导入库 1.2读取excel文件 1.3读取excel&#xff0c;指定sheet2工作表 1.4指定行索引 1.5指定列索引 1.6指定导入列 案例速览&#xff1a; 1.1导入库 import pandas as pd 1.2读取excel文件 pd.read_excel(文件路径) data pd.read_excel(D:/desktop/TestExcel…

【竞技宝jjb.lol】LOL:ale分析新版本 战士只剩锐雯能玩

北京时间2024年1月10日,随着新年的来临,英雄联盟赛事也开启了全新的篇章,如今距离春季赛开启的时间已经越来越近了。为了让选手结束休赛期后恢复到正常的竞技水平,LPL在前不久刚刚进行了德玛西亚杯的比赛,最终BLG决赛横扫宿敌JDG拿下冠军。而新赛季官方也会推出新版本,那么职业…