Vue双向数据绑定代码解读

news2024/9/21 19:50:42

 Vue核心基础-CSDN博客

数据双向绑定原理_哔哩哔哩_bilibili

原理示意图

 前置知识

reduce()方法

用于链式获取对象的属性值

Object.defineProperty()方法

Object.defineProperty(obj, prop, descriptor)

  • obj:要定义属性的对象。
  • prop:要定义或修改的属性的名称或 Symbol。
  • descriptor:将被定义或修改的属性描述符。

属性描述符(Descriptor)

属性描述符对象可以包含以下属性之一或多个:

  • value:属性的值(对于 getter 和 setter 属性,该属性会被忽略)。
  • writable:当且仅当该属性的值为 true 时,属性的值才可以被 [[Set]] 操作改变(即可以重新赋值)。默认为 false
  • enumerable:当且仅当该属性的值为 true 时,该属性才会出现在对象的枚举属性中(例如,通过 for...in 循环或 Object.keys() 方法)。默认为 false
  • configurable:当且仅当该属性的值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false
  • get:一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,会调用此 getter 方法,执行时不传入任何参数,但是会传入 this 对象(即该属性的宿主对象)。
  • set:一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,会调用此 setter 方法。该方法将接受唯一参数,即被赋予的新值。
const object1 = {};  
  
Object.defineProperty(object1, 'property1', {  
  value: 42,  
  writable: false  
});  
  
console.log(object1.property1); // 输出:42  
  
// 尝试修改属性值  
object1.property1 = 100;  
console.log(object1.property1); // 输出:42,因为 writable 为 false  
  
// 尝试删除属性  
delete object1.property1;  
console.log(object1.property1); // 输出:42,因为 configurable 为默认值 false,所以属性不能被删除
const object2 = {  
  _value: 42  
};  
 ------------------------------------------------------------------------------------------
Object.defineProperty(object2, 'value', {  
  get() {  
    return this._value;  
  },  
  set(newValue) {  
    if (newValue < 0) {  
      throw new Error('值必须大于或等于0');  
    }  
    this._value = newValue;  
  }  
});  
  
console.log(object2.value); // 输出:42  
  
object2.value = 100;  
console.log(object2.value); // 输出:100  
  
// 尝试设置无效值  
object2.value = -1; // 抛出错误:值必须大于或等于0

发布订阅者模式

Javascript常见设计模式-CSDN博客

流程

数据劫持(递归实现深层次)

1. 初始化过程

当 Vue 实例被创建时,它会通过 Vue.options.data 函数(或组件的 data 函数)获取到初始数据对象。然后,Vue 会遍历这个对象的所有属性,并使用 Object.defineProperty() 将它们转换为 getter/setter。

2. 递归转换

对于对象中的每个属性,Vue 会检查其值是否为对象或数组。如果是,Vue 会递归地调用一个内部函数(如 observe),以确保这个对象或数组中的所有属性也被转换为响应式。

  • 对象:对于对象,Vue 会遍历其所有属性,并对每个属性应用 Object.defineProperty()
  • 数组:对于数组,Vue 不能直接通过 Object.defineProperty() 拦截数组索引的访问,因为数组的长度是动态的。Vue 通过修改数组原型上的方法(如 pushpopshiftunshiftsplicesortreverse)来实现对数组操作的拦截。
function defineReactive(obj, key, val) {  
  // 递归地将对象属性转换为 getter/setter  
  observe(val);  
  
  // 使用 Object.defineProperty 拦截属性的访问  
  Object.defineProperty(obj, key, {  
    enumerable: true,  
    configurable: true,  
    get: function reactiveGetter() {  
      // 这里可以添加依赖收集的逻辑(省略)  
      return val;  
    },  
    set: function reactiveSetter(newVal) {  
      if (newVal === val) return;  
      // 这里可以添加派发更新的逻辑(省略)  
      // 如果 newVal 是对象或数组,则进行递归劫持  
      observe(newVal);  
      val = newVal;  
    }  
  });  
}  
  
function observe(value) {  
  if (!isObject(value) || value instanceof VNode) {  
    return;  
  }  
    
  // 数组需要特殊处理,因为不能拦截索引的访问  
  if (Array.isArray(value)) {  
    // 这里可以扩展为修改数组原型方法(省略)  
    // 或者使用 Object.defineProperty 对数组的长度进行劫持(但通常不推荐)  
    // 这里只是简单处理,不展开  
  } else {  
    // 遍历对象的所有属性  
    Object.keys(value).forEach(function (key) {  
      defineReactive(value, key, value[key]);  
    });  
  }  
}  
  
function isObject(value) {  
  // 简单的类型检查  
  return value !== null && typeof value === 'object';  
}  
  

代理数据劫持

Object.keys(this.$data).forEach((key)=>{
    Object.defineProperty(this,key,{
        enumerable:true,
        configurable:true,
        get(){
           return this.$data[key]
        },
        set(vaue){
           this.$data[key]=value
        }
})
})

模板编译

Vue 2 的模板编译流程是一个复杂但有序的过程,它涉及到将 Vue 模板(通常是 HTML 字符串,可能包含 Vue 特有的指令和插值表达式)转换成高效的渲染函数(render function)。这个过程主要在 Vue 的内部实现,特别是通过 vue-template-compiler 包来完成。以下是 Vue 2 模板编译的大致流程:

  1. 解析模板(Parse)
    • 将模板字符串转换为抽象语法树(AST)。AST 是一种树状的数据结构,用于表示源代码的语法结构。
    • 在这一步,Vue 会识别出模板中的所有元素、属性、指令(如 v-bindv-model)、插值表达式(如 {{ message }})等。
  2. 优化 AST(Optimize)
    • 对 AST 进行静态分析,标记出哪些部分是静态的(在多次渲染中不会改变),哪些部分是动态的。
    • Vue 会利用这些静态信息来优化渲染过程,比如通过静态提升(hoisting)来避免不必要的DOM操作。
  3. 生成渲染函数(Generate)
    • 将优化后的 AST 转换成 JavaScript 渲染函数。这个函数是一个纯 JavaScript 函数,它接收组件的上下文(如 props、data、computed、methods 等)作为参数,并返回一个虚拟 DOM(VNode)树。
    • 渲染函数是 Vue 组件渲染过程的核心,它会在组件的每次更新时被调用,并生成新的 VNode 树。
  4. 挂载或更新 DOM
    • Vue 的运行时(runtime)会接收渲染函数生成的 VNode 树,并将其与实际的 DOM 进行比较(使用虚拟 DOM 的 diff 算法)。
    • 根据比较结果,Vue 会最小化地进行 DOM 更新,以达到高效渲染的目的。

需要注意的是,这个过程是在 Vue 组件的编译阶段完成的,而不是在运行时。当你使用 Vue 的单文件组件(.vue 文件)或直接在 JavaScript 中定义模板时,Vue 的构建工具(如 webpack、Vue CLI)会在构建过程中调用 vue-template-compiler 来编译模板。

简化版的编译过程

下面是一个非常简化的模拟过程,说明Vue是如何处理{{ }}插值表达式的:

  1. 解析模板:首先,模板(HTML字符串)会被解析成一个抽象语法树(AST)。在这个过程中,Vue会识别出模板中的所有Vue特有的指令和{{ }}插值表达式。

  2. 转换AST:然后,Vue会遍历这个AST,并将{{ }}插值表达式转换为特定的代码块。对于每个{{ }}插值,Vue会生成一个JavaScript表达式,该表达式在组件的渲染过程中会被计算,并用于替换原始的{{ }}文本。

  3. 生成渲染函数:最后,Vue会将转换后的AST转换成一个JavaScript渲染函数。这个渲染函数会基于组件的状态(如数据、计算属性等)来生成最终的HTML字符串。

示例:模拟处理{{ }}插值

虽然Vue的内部实现要复杂得多,但我们可以模拟一个非常简单的处理过程:

function compileTemplate(template) {  
  // 假设template是一个简单的字符串,我们手动替换{{ }}内的内容  
  // 实际应用中,你会使用正则表达式或更复杂的解析器来解析模板  
  let code = template.replace(/\{\{ (.*?)\}\}/g, (_, expr) => {  
    // 这里expr是`{{ }}`内的表达式  
    // 在Vue中,这个表达式会被转换成类似`_s(this.expr)`的JavaScript代码  
    // 这里我们简单地返回表达式本身,实际应用中你需要根据组件状态计算这个值  
    return `_s(${expr})`; // 假设_s是一个将值转换为字符串的函数  
  });  
  
  // 这里的code只是一个字符串示例,并不是真正的渲染函数  
  // 在Vue中,这个字符串会被转换成JavaScript代码,并生成渲染函数  
  console.log(code);  
  // 注意:这里只是为了演示,并没有真正执行任何渲染逻辑  
}  
  
// 示例模板  
let template = `<div>{{ message }}</div>`;  
  
// 编译模板  
compileTemplate(template);  
// 输出: "<div>_s(message)</div>"  
// 注意:这里的输出只是为了说明如何替换{{ }},并不是Vue实际生成的渲染函数

 正则表达式-CSDN博客

发布(set中)与订阅(get中)

// 定义一个函数来观察一个对象,使其属性变为响应式  
function observe(data) {    
    // 遍历对象的所有键  
    Object.keys(data).forEach(key => {    
        let internalValue = data[key];  // 获取当前属性的值  
        const dep = new Dep();  // 为每个属性创建一个依赖实例  
    
        // 使用Object.defineProperty来定义属性的getter和setter  
        Object.defineProperty(data, key, {    
            enumerable: true,  // 属性可枚举  
            configurable: true,  // 属性可配置  
            get() {    
                dep.depend();  // 访问属性时,收集依赖  
                return internalValue;  // 返回属性的值  
            },    
            set(newVal) {    
                if (newVal === internalValue) return;  // 如果新值等于旧值,则不执行任何操作  
                internalValue = newVal;  // 更新属性值  
                dep.notify();  // 通知所有依赖此属性的观察者,属性已更改  
            }    
        });    
    });    
}    
  
// 定义一个依赖类,用于收集依赖和通知观察者  
class Dep {    
    constructor() {    
        this.subscribers = [];  // 存储所有依赖此属性的观察者  
    }    
    
    // 依赖收集方法,将当前活动的观察者添加到依赖的订阅者列表中  
    depend() {    
        if (Dep.target) {    
            this.subscribers.push(Dep.target);    
        }    
    }    
    
    // 通知所有订阅者(观察者)更新  
    notify() {    
        this.subscribers.forEach(sub => sub.update());    
    }    
    
    // 静态属性,用于存储当前活动的观察者  
    static target = null;    
}    
  
// 定义一个观察者构造函数,用于观察Vue实例上的表达式  
function watcher(vm, exp, cb) {    
    // 设置当前活动的观察者为当前watcher实例  
    Dep.target = this;    
    this.vm = vm;  // 绑定Vue实例  
    this.exp = exp;  // 绑定要观察的表达式  
    this.cb = cb;  // 绑定回调函数  
    // 触发getter,进行依赖收集  
    this.value = vm[exp];   
    // 清除当前活动的观察者,避免污染后续操作  
    Dep.target = null;    
}    
  
// 定义watcher实例的update方法,用于在数据变化时执行回调函数  
watcher.prototype.update = function() {    
    const newValue = this.vm[this.exp];  // 获取最新值  
    if (newValue !== this.value) {  // 如果新值不等于旧值  
        this.cb(newValue);  // 执行回调函数,并传入新值  
        this.value = newValue;  // 更新旧值  
    }    
};  
  
// 注意:上述代码仅为演示Vue响应式系统的一部分,并未完全模拟Vue的全部功能。  
// 在Vue中,watcher的创建和管理、以及Dep的target的设置和清除通常是通过Vue的内部机制来完成的。

输入框中数据改变

(涉及到解析模板指令v-model)

<template>  
  <div>  
    <input :value="message" @input="updateMessage" placeholder="edit me">  
    <p>Message is: {{ message }}</p>  
  </div>  
</template>  
  
<script>  
export default {  
  data() {  
    return {  
      message: ''  
    }  
  },  
  methods: {  
    updateMessage(event) {  
      // 你可以从event.target.value获取到输入框的值  
      this.message = event.target.value;  
    }  
  }  
}  
</script>

不是很了解的:模板的编译解析

在Vue.js 2.x中,模板编译和解析是Vue内部的一个复杂过程,它主要负责将Vue模板(HTML字符串或模板文件)转换成渲染函数(render function)。这个过程并不是直接通过DOM操作完成的,而是利用了JavaScript的字符串处理、正则表达式以及Vue的编译系统内部逻辑。然而,理解这个过程涉及到的一些基本概念和工具是有帮助的。

模板编译与解析的概述

Vue模板编译主要发生在Vue的初始化阶段,它涉及到以下几个步骤:

  1. 解析模板:将模板字符串转换成AST(抽象语法树)。
  2. 优化AST:静态内容提升等优化操作。
  3. 生成代码:将AST转换成渲染函数代码字符串。
  4. 编译成函数:使用new Function()将渲染函数代码字符串编译成可执行函数。

常用到的DOM操作方法(间接相关)

虽然Vue的模板编译过程不直接操作DOM,但Vue的渲染函数和虚拟DOM系统最终会操作DOM。不过,在模板编译阶段,我们讨论的是字符串处理和JavaScript操作,而非直接的DOM操作。然而,了解Vue如何与DOM交互是有帮助的:

  • createElementappendChildremoveChild等(Vue内部通过虚拟DOM模拟这些操作)。

常用到的正则表达式

在Vue模板编译的上下文中,正则表达式通常用于解析模板字符串,提取指令(如v-bindv-model等)、插值表达式({{ }})等。不过,Vue的源代码中这些正则表达式是高度定制的,且随着版本更新而变化。这里提供一个简化的例子,说明如何可能使用正则表达式来识别插值表达式:

// 简化的正则表达式,用于匹配插值表达式 
const interpolationRE = /\{\{([^}]+)\}\}/g; 


let template = 'Hello, {{ name }}!'; 
let matches = template.match(interpolationRE); 
if (matches) { 
console.log(matches[1]); // 输出: name 
}

常用到的JavaScript方法

在Vue模板编译过程中,会大量使用JavaScript的字符串和数组方法,以及对象操作:

  • 字符串方法replace()split()trim()indexOf()substring()等,用于处理和转换模板字符串。
  • 数组方法map()filter()reduce()forEach()等,用于遍历和处理AST节点。
  • 对象方法hasOwnProperty()Object.keys()Object.assign()Object.create()等,用于操作对象属性和原型链。
  • 正则表达式相关方法exec()test()match()等,用于在模板字符串中查找和匹配特定的模式。

总结

Vue的模板编译和解析是一个复杂的过程,它涉及到JavaScript的多个方面,包括字符串处理、正则表达式、数组和对象操作等。然而,这个过程主要发生在Vue内部,开发者通常不需要直接处理。理解Vue模板编译的基本原理和目的,有助于更好地理解和使用Vue框架。

 

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

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

相关文章

睿考网:中级经济师报名条件中的相关工作怎么定义?

在中级经济师报考条件中&#xff0c;明确不同学历需要满足相关工作经验的要求&#xff0c;怎么看我的工作是否为相关工作呢&#xff1f; 1.从事经济类相关的工作岗位 也就是说考生在本单位从事的工作性质需要与经济行业相关&#xff0c;如本单位的财务、会计、审计、出纳等相…

原地旋转数组--189. 轮转数组

189. 轮转数组 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转 3 步:…

传授大伙如何用ai工具优化自己的代码》》拥抱机器时代》》提示词工程

之前在做数据开发时候遇到的问题数据表格批量生成建表的宏出问题了&#xff0c;此时只好硬着头皮写vba代码,此时您想我不会vba也不知道怎么使用但是sql语句建表格式总还是知道的那么此刻开始我们的提示词开发代码时刻 先进行具体提示词开发>>>>>>>>>…

SpringCloud nacos

**************************** 准备工作 首先准备号nacos的镜像 根据镜像创建nacos容器 nacos:container_name: nacosimage: nacos/nacos-server:v2.1.0-slimports: #需要监听三个端口- "8848:8848"- "9848:9848"- "9849:9849"privileged: tr…

Axure高效打造大屏可视化BI数据展示

在使用AxureRP软件设计大屏可视化BI数据显示模板时&#xff0c;我们可以遵循一系列高效的方法和步骤来确保设计的质量和效率。以下是一个详细的教程&#xff0c;指导如何高效地使用AxureRP进行大屏界面设计。 一、确定设计标准与分辨率 通常&#xff0c;大屏可视化设计以标准的…

MySQL事务【后端 13】

MySQL事务 在数据库管理系统中&#xff0c;事务&#xff08;Transaction&#xff09;是一个非常重要的概念&#xff0c;它确保了数据库操作的完整性和一致性。MySQL作为一种流行的关系型数据库管理系统&#xff0c;自然也支持事务处理。本文将深入探讨MySQL事务的基本概念、特性…

基于YOLOv8的PCB缺陷检测算法,加入一种基于内容引导注意力(CGA)的混合融合方案(一)

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文内容&#xff1a;针对基于YOLOv8的PCB缺陷检测算法进行性能提升&#xff0c;加入各个创新点做验证性试验。 1&#xff09;提出了一种基于内容引导注意力(CGA)的混合融合方案&#xff0c;mAP0.5由原始的0.966提升至0.975 1.PCB缺陷…

Django 如何全局捕获异常和DEBUG

DEBUG 默认&#xff1a;False 一个开启、关闭调试模式的布尔值。 永远不要在 DEBUG 开启的情况下将网站部署到生产中。 调试模式的主要功能之一是显示详细的错误页面。如果你的应用程序在 DEBUG 为 True 时引发了异常&#xff0c;Django 会显示一个详细的回溯&#xff0c;包…

AplPost使用

请求get 方法 1&#xff0c;添加token 2&#xff0c;填写get 的参数 2,post方法 把对象的形式直接复制到row里面 3&#xff0c;delete方法 可以直接后面拼接参数

CTK框架(八):服务追踪

目录 1.简介 2.实现方式 3.具体实现 3.1.新建插件PluginA​​ 3.2.新建插件PluginB 4.服务追踪的优势 5.应用场景 6.总结 1.简介 CTK服务追踪是一种机制&#xff0c;用于在CTK插件框架中追踪和管理插件提供的服务。当一个插件注册了一个服务到服务注册中心后&#xff0…

SCADA|KingIOServer数据存入KingHistorian的过程记录

哈喽,你好啊,我是雷工! KingIOServer是采集数据的软件,KingHistorian是工业数据库; 最近用到KingIOServer采集到的数据存入到KingHistorian工业库,然后KingFusion再连接KingHistorian获取历史数据并曲线展示的应用功能; 以下为测试笔记; 01 软件安装 KingIOServer软…

动态代理IP池设计:打造高效网络工具

在互联网飞速发展的今天&#xff0c;动态代理IP池成为了网络世界中的一大法宝。无论是数据采集、网络营销还是电商运营&#xff0c;动态代理IP池都能提供极大的便利。今天&#xff0c;我们来深入探讨一下如何设计一个高效的动态代理IP池。 什么是动态代理IP池&#xff1f; 动…

高性能多目标进化优化算法求解DTLZ1-DTLZ9,MATLAB代码

DTLZ&#xff08;Deb-Thiele-Laumanns-Zitzler&#xff09;测试函数系列是多目标优化领域中一组广泛使用的基准测试问题。这些测试问题由Kalyanmoy Deb、Lothar Thiele、Marco Laumanns和Eckart Zitzler于2002年提出&#xff0c;旨在评估和比较多目标优化算法的性能。以下是DTL…

HTML5+CSS+JS制作中秋佳节页面

HTML5CSSJS制作中秋佳节页面 中秋节&#xff0c;是中国民间的传统节日。每年农历八月十五庆祝。 在中秋节这一天&#xff0c;人们会通过各种方式庆祝&#xff0c;其中最重要的活动之一就是赏月。家人团聚在一起&#xff0c;共同欣赏明亮的月亮。同时&#xff0c;吃月饼也是中秋…

【Go】-Gin框架

目录 Gin框架简介 简单示例 Gin渲染 HTML渲染 自定义模板函数 静态文件处理 使用模板继承 JSON渲染和XML渲染 获取参数 获取querystring参数 获取form参数 获取Path参数 参数绑定 文件上传 单个文件上传 参数 多个文件上传 重定向 HTTP重定向 路由重定向 …

【Tourism】Xianyang

文章目录 1、陕西省咸阳市2、清渭楼3、古渡廊桥4、古渡遗址博物馆5、文庙6、窦家BingBing面7、凤凰台8、安国寺9、福园巷子10、参考 1、陕西省咸阳市 咸阳市&#xff0c;陕西省辖地级市&#xff0c;地处陕西省关中平原中部&#xff0c;是中国大地原点所在地&#xff0c;其境东…

iPhone 16系列发布:硬件升级但创新乏力?iPhone还值得入手吗?

2024年苹果发布会如约而至&#xff0c;重磅推出了iPhone 16系列。然而&#xff0c;发布会结束后&#xff0c;许多科技评论员和用户不禁质疑&#xff1a;苹果的创新精神是否逐渐消退&#xff1f;从发布会的内容来看&#xff0c;iPhone 16在设计、性能上有所提升&#xff0c;但和…

软件测试之单元测试与unittest框架详解

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 单元测试的定义 1. 什么是单元测试&#xff1f; 单元测试是指&#xff0c;对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作&…

快来看,图书《人工智能怎么学》电子书上线啦!

图书《人工智能怎么学》出版后&#xff0c;受到了读者们的喜爱&#xff0c;被读者们亲切的称为“人工智能宝典”。不少读者留言说书籍具有很强的检索功能&#xff0c;想学人工智能的某些知识时&#xff0c;通过查阅此书&#xff0c;很方便就能够找到相关的著名教材、精品课程、…

【网易低代码】第3课,页面表格删除功能

你好&#xff01; 这是一个新课程 CodeWave网易低代码 通过自然语言交互式智能编程&#xff0c;同时利用机器学 习&#xff0c;帮助低代码开发者进一步降低使用门槛、提高应用开发效率 【网易低代码】第3课&#xff0c;页面表格删除功能 1.拖拽组件link链接到表格中&#xff0c…