Vue2源码 —— 数据响应式实现

Vue2源码 —— 数据响应式实现

配置项

//package.json
{"name": "vue","version": "1.0.0","main": "index.js","scripts": {"dev": "rollup -cw"},"author": "","license": "ISC","description": "","devDependencies": {"@babel/core": "^7.24.3","@babel/preset-env": "^7.24.3","rollup": "^2.79.1","rollup-plugin-babel": "^4.4.0"},"dependencies": {"@rollup/plugin-node-resolve": "^15.2.3"}
}
// rollup.config.js
import babel from 'rollup-plugin-babel'
import resolve from '@rollup/plugin-node-resolve'
export default {input: './src/index.js', //入口output: {file: './dist/vue.js',//出口name:'Vue',format: 'umd' , sourcemap:true // 希望可以调试源代码},plugins: [babel({exclude: 'mode_modules/**' //排除node_modules的所有文件}),resolve()]
}
  • 在rollup.config.js文件中, format 配置项用于指定输出的模块格式。其可选的类型有以下几种:

    • “amd”: Asynchronous Module Definition, 适用于浏览器环境和 RequireJS 等 AMD 模块加载器。
    • “cjs”: CommonJS 格式, 适用于 Node.js 环境。
    • “es” 或 “esm”: ES module 格式,用于支持 ECMAScript 模块。
    • “iife”: Immediately Invoked Function Expression, 适用于浏览器环境。
    • “umd”: Universal Module Definition, 可同时用于浏览器和 Node.js 环境。
  • 配置文件写完之后,就可以写接下来的方法和属性了

    文件

  1. 文件目录

    • dist
      • test.html // 测试文件
    • node_modules
    • src
      • index.js
  2. index.js
    index.js作为入口文件,作为rollup.config.js的入口。在这个文件中创建一个Vue的函数,然后将这个函数抛出去,作为构造文件,在这个文件中,会引入外部文件。这些外部文件是对这个Vue的原型进行修改的

    这是文件的最开始的项目准备

    响应式数组实现

    在vue2中,新建一个Vue的实例,命名为vm,在控制台中输出vm.$data === vm._data输出结果为true。说明在Vue2中,私有方法和公有方法的data实现都是一个对象,只不过是挂载的对象不同

  3. 下面的代码实现的功能

    1. 将Vue创建的时候传入的参数,捕获,并且传递到init属性上

    2. 将实例保存到$options属性上

    3. 将el挂载到$mount属性上
      index.html

      <html>
      <head>
      <meta charset="UTF-8">
      <meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      </head>
      <body>
      <script src="vue.js"></script>
      <script>
      const vm = new Vue({data() {return {name: 'zf',age: 20,address: {num: 30,content: 'xiaokeai'},hobby: ['slepp', {a: 1}]}},// el: "#app"
      })
      </script>
      </body>
      </html>
      
    init.js
    ```javascript
    export  function initMixin(Vue) {Vue.prototype._init = function (options) {const vm = thisvm.$options = optionsif (options.el) {vm.$mount(options.el)}}
    }
    

    index.js

    import { initMixin } from "./init"
    function Vue (options) { // options用户的选项console.log(options)this._init(options)
    }
    initMixin(Vue)
    export default Vue
    
    • 在官方文档中,如果在vue实例化的时候,没有收到el选项,则会处于未挂载状态,没有关联的dom,可以使用vm.$mount()手动的挂在一个未挂载的实例,这里有el选项,所以,会自动挂载。后续手动挂载,在后面实现
  4. 下面代码实现的功能

    1. 将第一层数据绑定了get和set方法(文件转换太频繁,最后放完整的zip代码包吧,这里仅仅将一些关键代码罗列)

      class Observe {constructor(data) {this.walk(data)}walk(data) {Object.keys(data).forEach(key => {definedReactive(data,key,data[key])})}observeArray(data) {data.forEach(item => observe(item))}
      }
      export function definedReactive(target, key, value) {Object.defineProperty(target, key, {get() {return value},set(newValue) {if(value === newValue) returnvalue = newValue}})
      }
      export function observe(data) {return new Observe(data)
      }
      

      在上面的代码中,仅仅是将第一层数据加上了get和set方法,如果想要给多层加上get和set方法,还需要对代码进行改写

    2. 下属的代码实现了的功能

      • 将_data的get和set方法挂在到Vue实例上。 (之前的时候,将_data方法)

      • 对数组进行监听,不使用遍历监听每个数据,对性能有很大的浪费。

      • 将observer放到__ob__属性中。实现了响应式

        // array.js
        let oldArrayProto = Array.prototype
        export let newArrayProto = Object.create(oldArrayProto)
        let methods = ['push','pop','shift','unshift','reverse','sort','splice'
        ]
        console.log(newArrayProto)
        methods.forEach(method => {newArrayProto[method] = function (...args) {const result = oldArrayProto[method].call(this, ...args)let insertedlet ob = this.__ob__switch (method) {case 'push':case'unshift':inserted = argscase 'splice' :inserted = args.slice(2)default:break}if (inserted) {ob.observeArray(inserted)}return result}
        })
        
        // index.js
        import {newArrayProto} from "./array";
        

      class Observer {
      constructor(data) {

       data.__ob__ = thisObject.defineProperty(data, '__ob__', {value:this,enumerable:false})if (Array.isArray(data)) {data.__proto__ = newArrayProto} else{this.walk(data)}
      

      }
      walk(data) {

       Object.keys(data).forEach(key => {definedReactive(data,key,data[key])})
      

      }
      observeArray(data) {

       data.forEach(item => observe(item))
      

      }
      }
      export function definedReactive(target, key, value) {
      observe(value)
      Object.defineProperty(target, key, {

       get() {return value},set(newValue) {if(value === newValue) returnobserve(newValue)value = newValue}
      

      })
      }
      export function observe(data) {
      if (typeof data !== ‘object’ || data == null) {

       return
      

      }
      if (data.ob instanceof Observer) {

       return data.__ob__
      

      }
      return new Observer(data)
      }

    • 下面代码实现了
      1. 对data的类型进行判断,只有符合为object类型,才能对object进行挂载,将object中的数据都挂载到_data上

        import { observe} from "./observe"
        export function initState(vm) {const ops = vm.$optionsif(ops.data) {initData(vm)}
        }
        function proxy(vm, target, key) {Object.defineProperty(vm, key, {get() {return vm[target][key]},set(value) {vm[target][key] = value}})
        }
        function initData(vm) {let data = vm.$options.datadata = typeof data === 'function' ? data.call(vm):data  // 转换为对象类型vm._data = data //挂在到实例上observe(data)for (const dataKey in data) {proxy(vm, '_data', dataKey)}
        }
        

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

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

相关文章

Linux非root用户安装mysql5.7

1、下载安装包MySQL :: Download MySQL Community Server 点击Archives 我下载的是5.7.27版本&#xff0c;linux主机直接选择linux-Generic即可&#xff0c;选择第一个包下载即可 2、安装mysql 解压 shell> tar xzvf mysql-5.7.31-linux-glibc2.12-x86_64.tar.gz shell&g…

VUE2实现元素抖动的指令

指令代码 要实现Vue2的指令&#xff0c;可以按照以下步骤进行&#xff1a; 创建一个指令对象。 export default {inserted: (el, binding) > {// 触发抖动效果if (!binding.value) return el.classList.remove(shake-animation)el.classList.add(shake-animation)const a…

抽象类和接口的简单认识

目录 一、抽象类 1.什么是抽象类 2.抽象类的注意事项 3.抽象类与普通类的对比 二、接口 1.接口的简单使用 2.接口的特性 3.接口的使用案例 4.接口和抽象类的异同 一、抽象类 所谓抽象类&#xff0c;就是更加抽象的类&#xff0c;也就是说&#xff0c;这个类不能具体描…

等级保护测评无补偿因素的高风险安全问题判例(共23项需整改)

层面 控制点 要求项 安全问题 适用范围 充分条件 整改建议简要 安全物理环境 基础设施位置 应保证云计算基础设施位于中国境内 1.云计算基础设施物理位置不当 二级及以上 相关基础设施不在中国境内 云平台相关基础设施在中国境内部署 安全通信网络 网络架构 应…

NFC RC522开发记录

文章目录 一、ID卡、IC卡(M1卡、CPU卡)的区别二、RC522读写操作1. 数据读写流程三、RC522驱动代码1. RC522 与 STM32 的接线图2. RC522.c3. RC522.h4. main.c一、ID卡、IC卡(M1卡、CPU卡)的区别 ID卡 :只存储了ID号,设备识别ID号,没有算法可言,容易复制,安全性低IC卡包含了…

ALPHA开发板上PHY网络芯片LAN8720:常用的几个寄存器功能

一. 简介 正点原子的开发板 ALPHA开发板&#xff0c;有线网络硬件方案所使用的也是最常用的一种方案&#xff0c;IMX6ULL芯片内部是自带 MAC网络芯片的&#xff0c;所以&#xff0c;也就是采用 "SOC内部集成网络MAC外设 PHY网络芯片方案"。 前面一篇文章简单了解了…

Asp.net Core 中一键注入接口

Asp.net Core 中一键注入接口 前言准备开始使用 前言 在之前开发Asp.Net Core程序时遇到接口需要一个一个的注入到Services中,当有非常多的接口需要注入时会显得代码成为了一座山,这里记录一下如何通过接口的命名一键自动注入. 准备 IDE: Visual studio 2022 .Net版本:.Net …

机器学习——最优化模型

最优化模型的概述&#xff1a; 从某种程度上说&#xff0c;我们的世界是由最优化问题组成的。每一天&#xff0c;我们的生活都面临无数的最优化问题&#xff1a;上班怎么选择乘车路线&#xff0c;才能舒服又快速地到达公司&#xff1b;旅游如何选择航班和宾馆&#xff0c;既省…

不可变集合及Stream流

若希望某个数据是不可修改的&#xff0c;就可以考虑使用不可变集合&#xff0c;以提高安全性&#xff1b;&#xff08;JKD9之后才有&#xff09; List不可变集合&#xff1a; public static void main(String[] args) {/*创建不可变的List集合"张三", "李四&q…

conda 创建 python3.10.12 环境

conda 创建 python3.10.12 环境 介绍使用前置条件&#xff1a;安装 conda配置环境变量验证 Conda 安装结果创建环境&#xff1a;python激活 Anaconda 环境 验证 Python 版本。 介绍 Conda是一个开源的包管理和环境管理系统&#xff0c;由Continuum Analytics公司开发。它可以安…

python之@overload

from typing import overloadoverload def repeat(s: str, count: int) -> str:...overload def repeat(s: bytes, count: int) -> bytes:...这段代码是在定义一个名为repeat的函数&#xff0c;其中使用了Python的类型注解和装饰器overload来进行重载&#xff08;Overloa…

批量爬取招聘网站【Boss直聘】上工作岗位的招聘信息

不管是学生还是工作的小伙伴&#xff0c;估计都对不同岗位工作几年的薪酬水平比较感兴趣。本文提供爬取招聘网站&#xff0c;获取某类工作招聘信息的实现逻辑和代码。具体的实施步骤是&#xff1a;明确爬取的招聘网站—确定爬取的工作城市—确定爬取的岗位—获取岗位的招聘子链…

学习Python渗透第14天:用python实现sql注入

sql注入这个漏洞一直位于owasp列出的漏洞排行榜的前几位&#xff0c;虽然这个漏洞的使用难度很低&#xff0c;但是破坏力却极其的大。sql注入的原理是将原本的sql语句与用户可控的参数进行了拼接&#xff0c;形成了非预期的sql语句的执行&#xff0c;从而可能造成信息泄露或者如…

Sy6 编辑器vi的应用(+shell脚本3例子)

实验环境&#xff1a; 宿主机为win11&#xff0c;网络&#xff1a;10.255.50.5 6389 WSL2 ubuntu 目标机的OS&#xff1a;Ubuntu 内核、版本如下&#xff1a; linuxpeggy0223:/$ uname -r 5.15.146.1-microsoft-standard-WSL2 linuxpeggy0223:/$ cat /proc/version Linux vers…

【Blockchain】区块 | 节点 | 共识机制 | 公链 | 联盟链 | 以太坊 | DApp | 智能合约

Blockchain 初识区块链区块链是什么什么是区块什么是节点什么是分布式什么是共识机制工作量证明(Proof of Work, PoW)权益证明(Proof of Stake, PoS)历史证明(Proof of History, PoH)权威证明(Proof of Authority, PoA)信誉共识(Proof of Reputation, PoR)存储证明(Proof of St…

【3DsMax+Pt】练习案例

目录 一、在3DsMax中展UV 二、在Substance 3D Painter中绘制贴图 一、在3DsMax中展UV 1. 首先创建如下模型 2. 选中如下三条边线作为接缝 重置剥 发现如下部分还没有展开 再选一条边作为接缝 再次拨开 拨开后的UV如下 二、在Substance 3D Painter中绘制贴图 1. 新建项目&am…

C++AVL树拓展之红黑树原理及源码模拟

前言&#xff1a;我们之前已经从零开始掌握AVL树http://t.csdnimg.cn/LaVCChttp://t.csdnimg.cn/LaVCC 现在我们将继续学习红黑树的原理并且实现插入等功能&#xff0c;学习本章的前提要求是掌握排序二叉树和AVL树&#xff0c;本章不再提及一些基础知识&#xff0c;防止本文结…

国产数据库中统计信息自动更新机制

数据库中统计信息描述的数据库中表和索引的大小数以及数据分布状况&#xff0c;统计信息的准确性对优化器选择执行计划时具有重要的参考意义。本文简要整理了下传统数据库和国产数据库中统计信息的自动更新机制&#xff0c;以加深了解。 1、数据库统计信息介绍 优化器是数据库…

纯小白蓝桥杯备赛笔记--DAY5(竞赛常用库函数)

文章目录 大小写转换islower和isupper&#xff1a;检查一个字符是否是小写或者大写。Tolower和toupper函数&#xff1a;ASCII码&#xff1a; 二分查找二分查找的前提&#xff1a;库函数只能对数组进行二分查找&#xff0c;且数组中的元素是单调的。binary_search函数&#xff1…

【C++第五课-C/C++内存管理】C/C++的内存分布、new/delete、new和delete的实现原理

目录 C/C的内存分布new/deletenew内置类型使用new自定义类型使用newnew失败 delete内置类型使用delete自定义类型使用delete new和delete的实现原理new[] 和delete[]的补充知识 定位new&#xff08;了解&#xff09;常见面试题 C/C的内存分布 频繁的new/delete堆容易产生内存碎…