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.Vue
        format:'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是Vue
    
        vm.$options = options // 把数据挂载在vm.$options的属性上面

        initState(vm) // 初始化数据

    } 

    
}

function initState(vm){
    
   const ops =  vm.$options

    if(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是Vue
    
        vm.$options = options // 把数据挂载在vm.$options的属性上面

        initState(vm) // 初始化数据

    } 

    
}

function initState(vm){
    
   const ops =  vm.$options

    if(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创建原型prototype

let 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)
        
        }


    }


 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mysql的导入导出

mysql的导入导出 1.使用navicat导入导出1.1导入1.2导出 2.使用.mysqldump命令导入导出2.1导出表结构和数据2.2导出表结构2.3导入 3..LOAD DATA INFILE命令导入导出3.1在mysqlini 文件的[mysqld] 代码下增加 secure_file_privE:/TEST 再重启 mysql3.2导出3.3导入 4.远程备份导入…

Ubuntu20二进制方式安装nginx

文章目录 1.下载nginx安装包2.安装nginx3.安装出现的问题及解决方案错误1&#xff1a;错误2&#xff1a;错误3&#xff1a; 4.常用命令5.知识扩展&#xff1a; 1.下载nginx安装包 nginx官网&#xff1a;http://nginx.org/en/download.html 选择稳定的nginx版本下载。 2.安装ngi…

[redis] redis主从复制,哨兵模式和集群

一、redis的高可用 1.1 redis高可用的概念 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)。 高可用的计算公式是1-&#xff08;宕机时间&#xff09;/&#xff08;宕机时…

WPS或word中英文字母自动调整大小写,取消自动首字母大写,全部英文单词首字母大小写变换方法

提示&#xff1a;写英文论文时&#xff0c;如何实现英文字母大小写的自动切换&#xff0c;不用再傻傻的一个字母一个字母的编辑了&#xff0c;一篇文章搞定WPS与Word中字母大小写切换 文章目录 一、WPS英文单词大小写自动修改与首字母大写调整英文字母全部由大写变成小写 或 小…

Python进阶之元类

Python进阶之元类 目录 什么是元类&#xff1f; 元类的调用流程 根据类自定义元类 __new__方法以及参数 ----------cls ----------name ----------bases ----------attrs __call__方法 生成对象的完整代码 什么是元类&#xff1f; 在python面向对象中&#xff0c;我们知道所有…

【AI】Pytorch 系列:预训练模型使用

1. 模型下载 import re import os import glob import torch from torch.hub import download_url_to_file from torch.hub import urlparse import torchvision.models as modelsdef download_

jenkins构建git项目timeout

问题点&#xff1a; Started by user unknown or anonymous Running as SYSTEM Building in workspace /var/jenkins_home/workspace/test-one using credential f28d956-8ee1-4f20-a32b-06879b487c70 Cloning the remote Git repository Cloning repository http://git.cc.co…

TextDiffuser-2:超越DALLE-3的文本图像融合技术

概述 近年来&#xff0c;扩散模型在图像生成领域取得了显著进展&#xff0c;但在文本图像融合方面依然存在挑战。TextDiffuser-2的出现&#xff0c;标志着在这一领域的一个重要突破&#xff0c;它成功地结合了大型语言模型的能力&#xff0c;以实现更高效、多样化且美观的文本…

HarmonyOS中的@ohos.promptAction 模块中弹框

在各种APP中会根据不同的业务场景显示不同的弹框情况&#xff0c;针对这些场景API中提示了那些弹框呢&#xff1f;今天就看下&#xff1a; 首先弹框分为模态弹框和非模态弹框&#xff0c;模态弹框必须用户点击反馈后进行下一步操作&#xff0c;非模态弹框只是告知用户信息&…

arduino安装DHT11库

步骤操作如下&#xff1a; 打开Arduino IDE。 在菜单栏中选择“项目” -> “加载库” -> “管理库…”。 在库管理器的搜索框中&#xff0c;输入“DHT sensor library”。 找到“DHT sensor library by Adafruit”&#xff0c;点击“安装”。 安装完成后&#xff0c;…

FineBI实战项目一(2):案例架构说明及数据准备

1 系统架构 基于MySQL搭建数据仓库基于Kettle进行数据处理帆软FineBI基于MySQL搭建的数据仓库进行数据分析 2 数据流程图 通过Kettle将MySQL业务系统数据库中&#xff0c;将数据抽取出来&#xff0c;然后装载到MySQL数据仓库中。编写SQL脚本&#xff0c;对MySQL数据仓库中的数…

<windows>Edge浏览器侧边栏无法关闭问题的解决方法

1 问题现象 如图&#xff0c;Edge浏览器默认安装后&#xff0c;打开了侧边栏 但是&#xff0c;关闭按钮却是灰色无法点击。 2 解决方法 在该设置页面&#xff0c;右键选择检查 在右侧的代码区域&#xff0c;选中一行便会在左侧有蓝色的标记&#xff0c;能够看到对应的页面位…

使用 CSS : 伪元素:after、过渡动画transition实现过渡效果(鼠标悬浮或点击 标签时,底部边框从左到右过渡)

首先&#xff0c;给 <span> 标签添加一个父元素&#xff0c;定义属性类名&#xff1a;nav-wrapper &#xff0c;父级设置相对定位。然后&#xff0c;使用 ::after 伪元素来创建一个与底部边框相同宽度的元素&#xff0c;初始时宽度为 0&#xff0c;通过过渡动画transitio…

IBM DS5020硬盘状态Impending failure(reported by controller)

这个状态说明硬盘还没有完全坏掉&#xff0c;但是也需要注意更换新硬盘了 磁盘状态详细信息&#xff1a; 报错信息 按照恢复步骤&#xff1a; 选中该硬盘&#xff0c;手动failed&#xff0c;之后可以将该硬盘拔掉&#xff0c;重新插入新硬盘 此时&#xff0c;会有一块热备盘启…

Angular - 笔记

文章目录 语法属性绑定引用模板变量组件绑定父组件传子组件 input子组件传父组件 outputEventEmitter ViewChildViewChildren获取子组件对象列表 管道常用模块 参考文档 语法 属性绑定 Angular 的双向绑定语法是方括号和圆括号的组合 [()]。[] 进行属性绑定&#xff0c;() 进行…

x-cmd pkg | grex - 用于生成正则表达的命令行工具

目录 简介首次用户生成的正则表达式与 perl 和 rust 兼容支持 Unicode 符号友好的用户体验进一步阅读 简介 grex 是一个旨在简化创作正则表达式的复杂且繁琐任务的库和命令行程序。这个项目最初是 Devon Govett 编写的 JavaScript 工具 regexgen 的 Rust 移植。但 regexgen 在…

RT-Thread基于AT32单片机的CAN应用

1 硬件电路 2 RT-Thread驱动配置 RT-Studio中没有CAN相关的图形配置&#xff0c;需要手动修改board.h。在board.h的末尾&#xff0c;增加相关的BSP配置。 #define RT_CAN_USING_HDR #define BSP_USING_CAN13 IO配置 at32_msp.c中的IO配置是PB9和PB10&#xff0c;掌上实验室V…

松鼠目标检测数据集VOC格式400张

松鼠&#xff0c;一种小巧玲珑、活泼可爱的啮齿类动物&#xff0c;以其蓬松的大尾巴和机敏的动作而广受欢迎。 松鼠通常体型小巧&#xff0c;四肢灵活&#xff0c;尾巴蓬松。它们的耳朵大而直立&#xff0c;眼睛明亮&#xff0c;给人留下了深刻的印象。松鼠的毛色因种类而异&a…

OLED显示模块的驱动和使用方法(附资料分享)

一、引言 本文旨在分享单片机对OLED显示模块的驱动方法和驱动代码。与此同时&#xff0c;本文还会分享如何使用取模软件进行取模&#xff0c;包括汉字取模、图片取模、英文取模。文末附驱动的代码工程和模块资料链接。 二、模块概述 1、OLED显示屏模块 2、引脚定义 3、原理图 …

npm安装vue,添加淘宝镜像

如果是第一次使用命令栏可能会遇到权限问题。 解决vscode无法运行npm和node.js命令的问题-CSDN博客 安装 在vscode上面的导航栏选择terminal打开新的命令栏 另外可能会遇到网络或者其他的问题&#xff0c;可以添加淘宝镜像 npm install -g cnpm --registryhttps://registry.…