原型链+instanceof+Vue底层原理

ops/2024/10/19 9:36:50/

一些重要的前端知识总结(基于笔面试题的扩展),包含原型链、instanceof、深度剖析Vue底层原理

目录

一、原型链

二、instanceof

1. instanceof

2. 用法

三、defineProperty和Proxy

1. vue架构-MVVM

2. render函数

1)render是什么

2)render的工作流程

3. Vue实例

1)Vue实例

2)Vue实例的选项

4.观察者机制(订阅者-发布者模式)

1)订阅者-发布者模式

2)Vue中的Watcher

5.diff算法

6.Vue渲染页面过程

1)初始化:

2)编译模板:

3)虚拟DOM:

4)挂载:

5)数据变化:

6)差异对比(diff):

7)更新真实DOM:

7.Vue的数据双向绑定机制(响应式原理)

1)Vue2的响应式机制:

2)Vue3的响应式机制:

3)双向数据绑定的简化流程:


一、原型链

原型链:每个对象都有与之相关的原型对象,原型对象自身也可以有原型,以此类推,层层嵌套实现原型链。

1. prototype是一个属性,每个构造函数(类函数)都有该属性,该属性是一个对象,用于作为通过构造函数创建的实例的原型
也就是通过这个构造函数(类函数)创建的实例(new创建)的原型指向构造函数的prototype属性

2. 两个类,如果要让一个类继承另一个类的属性和方法,就需要让这个类的原型指向另一个类的prototype属性,
比如一个类为A,另一个类为B,设置A.prptotype = Object.create(B.prototype) 这个方法利用Object.create()函数
将A的原型设置为一个对象,这个对象的原型是B.prototype

在设置prototype指向后,还需要修正A.prototype的construstor属性,使其指向A函数本身

例题:

// Animal构造函数
function Animal (name, age) {this.name = name;this.age = age;
}//Animal通过原型链定义方法
Animal.prototype.eat = function() {console.log(`${this.name} is eating.`);
}// Dog构造函数
function Dog(name, age, breed) {Animal.call(this, name, age);this.breed = breed;
}// 通过原型链将Dog的原型指向Animal.prototype,也就是设置Dog的原型为Animal的实例,
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;Dog.prototype.bark = function() {console.log(`${this.name} barks.`);
}const myDog = new Dog('Buddy', 3, 'Labrador');myDog.eat();
myDog.bark();
console.log(myDog instanceof Dog); // true
console.log(myDog instanceof Animal); // true

输出结果如图:

二、instanceof

1. instanceof

instanceof 操作符,用于检测构造函数的prototype属性是否存在于某个实例对象的原型链上(判断一个对象是否是其指定构造函数的实例
 

比如上面的Animal和Dog例子,myDog是Dog()的实例,也就是myDog的原型指向Dog.prototype,原型链中Dog.prototype又指向Animal.prototype,所以也属于Animal的实例,可以使用Animal中的属性和方法。

2. 用法

FormData是一个Web API提供的接口,用于构建一组键值对,代表表单字段和它们的值,以便可以使用XMLHttpRequest发送或使用fetch API发送数据。


要检查一个变量是否为FormData的实例:

// 创建一个FormData对象  
var formData = new FormData();  // 使用instanceof检查formData是否是FormData的实例  
if (formData instanceof FormData) {  console.log('The variable is a FormData instance.');  
} else {  console.log('The variable is not a FormData instance.');  
}

在实际开发中,接口请求时,如果传送的是file文件,那可以用:

const formData = new FormData();
formData.append('file', file);

然后将formData作为参数发送请求,但是由于传送的是FormData()实例,请求头需要添加'multipart/form-data',所以可以如下设置,添加请求拦截器,利用instanceof判断传递的data是否为FormData的实例,如果是,则添加对应请求头:

// 添加请求拦截器
instance.interceptors.request.use(config => {if (config.data instanceof FormData) {config.headers['Content-Type'] = 'multipart/form-data';}return config;},error => {return Promise.reject(error);}
);

三、defineProperty和Proxy

这个就要了解到vue的响应式原理。

1. vue架构-MVVM

以上是一个简单理解MVVM的原理图

MVVM:

M(Model):指在Vue实例的data、computed、methods等选项中定义的数据对象和方法,这些数据对象和方法是应用程序的业务逻辑和数据模型,可以是简单的变量、对象、数组,也可以是复杂的数据结构和逻辑处理函数。

V(View):Vue组件的模板部分,将数据模型通过render函数生成虚拟DOM树,渲染成用户界面

M(ViewModel):指Vue实例本身,View实例作为View和Model之间的桥梁,通过数据双向绑定机制实现了View和Model之间的通信和同步。当Model中的数据发生变化时,Vue会利用Watcher观察者机制监听这些变化,并自动更新虚拟DOM树,再利用diff算法更新真实DOM树。

以上又出现了几个问题:

1.render函数?

2.Vue实例?

3.观察者机制?

4.diff算法

5.Vue渲染页面过程?

一一解答:

2. render函数

1)render是什么

renger是vue2.x中新增的一个函数,用来提升节点的性能。它是基于JavaScript计算,使用render函数将Template里面的节点解析成虚拟的DOM树

当使用模板HTML语法组建页面时,Vue会将其编译成VNode(虚拟节点)的函数。而当使用render函数构建DOM时,Vue就免去了转译的步骤,直接生成VNode

2)render的工作流程

render(h) {// h 是 createElement 的别名return h('div', /* props */, /* children */)
}

        (1)定义:在 Vue 组件中,render 函数是一个选项,它返回一个虚拟节点 (VNode),或者是一个包含多个 VNode 的数组。

        (2) createElement函数:render函数通常接收一个createElement函数作为参数,通常简写为h。createElement函数用于创建VNodes。

// 示例:创建一个简单的 VNode
createElement('div', { /* 属性 */ }, [ /* 子节点 */ ])

        (3) 创建VNodes:createElement接受三个参数:一个标签名、一个包含属性的对象,以及子节点(可以是字符串、数组或另一个VNode)。

        (4) 编译模板:如果在组建中定义了template,Vue的编译器会将模板编译成render函数,这个编译过程是在构建时完成的,不是在运行时。

        (5) 更新机制:当组件的状态(数据)发生变化时,Vue会调用render函数生成一个新的VNode树,并与旧的VNode树进行比较(diff算法),计算出需要对真实DOM进行的最小更新。

3. Vue实例

1)Vue实例

Vue中,一个Vue实例是通过构造函数Vue创建的一个对象,它是使用Vue构建应用程序的起点。Vue实例通常包含了应用程序的根组件,并负责管理应用的声明周期、数据状态、事件处理以及与DOM的交互。

(可以将一个.vue页面视为一个Vue实例)

2)Vue实例的选项

  • data:包含Vue实例响应式数据的基本对象
  • methods:定义可以通过Vue实例访问的方法
  • computed:定义计算属性,这些属性是基于它们的依赖进行缓存的(即在第一次得出结果后,如果它依赖的数据没有发生变化,则会利用缓存机制,不会再重新计算计算属性的值)
  • watch:提供一个对象,键是数据属性名,值是对应的回调函数,用于响应数据的变化。
  • components:注册组件
  • filters:定义过滤器,用于模板中的数据处理
  • directives:定义自定义指令,用于扩展HTML的行为
  • props:当实例用作组件时,props定义了组件可以接收的属性
  • template:提供一个字符串模板作为Vue实例的HTML
  • render:提供一个函数,用于生成虚拟节点(VNode),替代模板。
  • el:提供一个在页面上已存在的DOM元素作为Vue实例的挂载目标。(挂载元素是指Vue实例将要管理的DOM元素,‘el’是‘element’的缩写。当指定一个DOM元素时,Vue会将该元素作为挂载元素然后将该元素的内部内容作为Vue实例的模板进行解析和渲染

比如:

new Vue({

        el:'#app',

        data:{ 

                message:'Hello World!'

                }

  })

以上el:'#app',指定了挂载元素为id为app的DOM元素)

4.观察者机制(订阅者-发布者模式)

1)订阅者-发布者模式

一种设计模式,用于定义对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知并被自动更新。在Vue.js中,这种模式被用于实现数据的响应式系统。

发布者(Subject):

  • 发布者维护一个观察者列表,并提供方法来移除观察者。
  • 当发布者的状态发生变化时,它会通知所有注册的观察者。

订阅者(Observer):

  • 订阅者是一个对象,它监视发布者的状态
  • 当发布者的状态改变时,订阅者会收到通知,并执行一些操作。

2)Vue中的Watcher

(1)数据绑定:在Vue中如果模板中有多个地方使用了同一个数据属性,那么每个使用该属性的地方都会有一个观察者。当属性值发生变化时,所有依赖于该属性的地方都需要更新。

(2)依赖收集:组件渲染计算属性被计算时,Vue会创建一个Watcher实例。这个Watcher实例会在其构造函数中访问数据属性,从而触发数据的getter方法。在getter方法中,Watcher实例会被添加到数据属性的依赖列表中。

(3)触发更新:当属性值通过setter改变时,Vue会通知所有依赖于该属性的Watcher实例(所有注册的观察者),每个Watcher实例都会调用其回调函数,这通常会导致组件的重新渲染计算属性的重新计算

补充:

Watcher:Watcher是一个类,它不是一个简单的函数,而是一个构造函数,它创建了一个可以跟踪数据变化并执行毁掉的实例。

5.diff算法

当Vue中的数据发生变化时,Vue会根据新数据生成一颗新的虚拟DOM树,然后使用diff算法来比较两颗虚拟DOM树的差异,找出需要更新的节点,将变化的部分更新到真实DOM中,从而最小化对真实DOM的操作

Vue的diff算法是基于同级比较的,即只会比较同级节点,不会跨级比较。

在比较过程中,Vue会先比较Tag和Key,如果Tag不同,直接删除旧节点,创建新节点;如果Tag相同,则继续比较Key;如果Key也相同,则认为是同一个节点,否则认为是不同的节点。对于静态节点,Vue会在初始化时就创建一个静态的虚拟DOM树,并在后续的更新过程中直接复用这棵树,从而避免了不必要的比较和更新操作。

6.Vue渲染页面过程

结合上述知识,总结Vue渲染页面的过程:

1)初始化:

  • 创建Vue实例时,它会接收一个选项对象,包括数据、模板、方法、计算属性等
  • Vue会遍历选项对象中的数据属性,使用Object.defineProperty()Proxy(Vue3)将它们转换为getter/setter,以便实现响应式。

2)编译模板:

  • Vue使用HTML模板字符串或<template>标签中的内容作为输入
  • 模板编译器会将模板字符串转换成一个渲染函数,这个渲染函数会返回虚拟DOM树(VNodes)
new Vue({el: '#app',template: `<div><h1>{{ message }}</h1><p>{{ description }}</p></div>`,data: {message: 'Hello Vue!',description: 'This is a Vue instance.'}
});

补充:

  • 模板字符串:

HTML模板字符串是指一段用双引号(“”)或反引号(``)包围的字符串,其中包含了HTML代码。

  • 模板编译器:

一个将模板字符串转换成渲染函数的工具。

(1)解析:模板编译器首先解析模板字符串,将其转换成抽象语法树(AST)。AST 是源代码的抽象表示,它以树状结构表示代码的结构。

(2)优化:Vue 的编译器会遍历 AST,并找出静态节点和静态根节点。这些信息将被用来优化渲染过程,因为静态节点不需要在每次重新渲染时都重新生成。

(3)代码生成:编译器的最后一步是将优化后的 AST 转换成渲染函数代码。这个渲染函数是一个返回虚拟 DOM 树的 JavaScript 函数。

3)虚拟DOM:

  • 虚拟DOM是真实DOM的内存表示。它是一个轻量级的JavaScript对象,描述了应该渲染的DOM结构
  • 渲染函数的输出是一个虚拟节点树,Vue将使用这个树来更新浏览器中的真实DOM

4)挂载:

  • 在Vue实例的mounted生命周期钩子被调用之前,Vue会将虚拟DOM转换为真实DOM,并将其挂载到指定的DOM元素上(通常是#app)

5)数据变化:

  • 当数据变化时,由于数据响应式系统,Vue找到依赖了这些数据的虚拟节点进行更新
  • Vue重新调用渲染函数,生成一个新的虚拟DOM树

6)差异对比(diff):

  • Vue通过diff算法对比新旧虚拟DOM树,找出差异部分并更新节点

7)更新真实DOM:

  • Vue最小化地更新真实DOM,反应数据的变化

7.Vue的数据双向绑定机制(响应式原理)

1)Vue2的响应式机制:

(1)数据劫持:Vue使用Object.definePropert()对每个对象的每个属性进行劫持(数据绑定),为每个属性添加setter和getter,以便在数据读取和修改时执行特定的逻辑。

(2)依赖收集:当组件进行渲染时,会访问模板中用到的数据属性,这时属性的getter会被调用,Vue将会记录这次访问的依赖关系,当数据变化时,setter会被触发,Vue会通知那些依赖这个数据属性的地方进行更新。

(3)发布-订阅模式:Vue2使用了发布-订阅模式,每个属性都有一个或多个观察者(Watcher),当数据变化时,会通知所有订阅了该数据的观察者。

(4)局限:Vue2的响应式系统无法检测到对象属性的添加或删除也无法检测到数组索引和长度的变化

2)Vue3的响应式机制:

Vue3引入了一个全新的响应式系统,基于ES6的Proxy特性。

(1)Proxy:Vue3使用Proxy来代理整个对象,而不是像Vue2一样对对象的每个属性进行劫持。Proxy可以拦截对象的任意操作,包括属性的读取、设置、枚举、函数调用等。

(2)优势:

  • 代理整个对象,不需要为每个属性都设置setter和getter,减少内存使用,提高性能;
  • 支持数组的索引和长度变化,以及对象属性的添加和删除。
  • 允许更精细的变更检测,如单独跟踪数组变化,而不是对整个数组进行重新处理。
  • 支持可中断更新。

3)双向数据绑定的简化流程:

(1)初始化:Vue 实例在初始化时会遍历 data 对象中的所有属性,并使用 Object.defineProperty()(Vue 2)或 Proxy(Vue 3)将它们转换为 getter/setter,以便于追踪变化。

(2)视图渲染:当视图被渲染时,v-model 指令会告诉 Vue 将指定的数据属性绑定到表单元素的值上。

(3)用户输入:用户在输入框中输入内容时,会触发输入框的 input 事件。

(4)更新模型:v-model 指令监听到 input 事件后,会将输入框的最新值赋给绑定的数据属性。

(5)视图更新:由于数据属性现在是响应式的,一旦它被修改,Vue 将自动更新依赖于该属性的视图部分


http://www.ppmy.cn/ops/126681.html

相关文章

Leetcode 二叉搜索树的第 K 个元素

复习一下二叉搜索树 二叉搜索树 (Binary Search Tree, 简称 BST) 是一种特殊的二叉树(可以为空)&#xff0c;其中每个节点都有一个值&#xff0c;并且满足以下特点&#xff1a; 定义&#xff1a; 左子树节点的值小于根节点的值&#xff1a;对于每个节点&#xff0c;左子树中所…

利用 Direct3D 绘制几何体—6.常量缓冲区

1. 创建常量缓冲区 常量缓冲区 (constant buffer) 也是一种 GPU 资源 (ID3D12Resource)&#xff0c;其数据内容可供着色器程序所引用。顶点着色器实例中&#xff1a; cbuffer cbPerObject : register(b0) {float4x4 gWorldViewProj; }; 在这段代码中&#xff0c;cbuffer 对…

Java异常体系

1. 概述 以下是详细内容 2. 什么是异常 编程的错误分为语法错误、逻辑错误、异常三种&#xff0c;其中语法错误和逻辑错误不属于异常。因为如果发生语法错误&#xff0c;Java程序根本无法运行&#xff0c;而如果发生逻辑错误&#xff0c;Java程序也不可能得到正确的结果。 我…

repo 命令大全详解(第十一篇 repo init)

repo forall 命令用于在指定的项目上执行给定的命令&#xff0c;非常适合批量操作。 参数分类及解释 基本参数 [<project>...]: 可选&#xff0c;指定要操作的项目。如果不指定&#xff0c;则对所有项目执行命令。 示例: repo forall my_project -c "git status&q…

目标检测最新SOTA模型D-FINE

2024年10月18号&#xff0c;中科大推出了 D-FINE&#xff0c;这是一款功能强大的实时物体检测器&#xff0c;通过重新定义 DETR 模型中的边界框回归任务实现了出色的定位精度。 摘要 D-FINE 包含两个关键组件&#xff1a;细粒度分布细化 (FDR) 和全局最优定位自蒸馏 (GO-LSD)…

eggjs sequelize egg-sequelize-auto自动从零生成一个数据表 自动创建model

sequelize egg-sequelize-auto整个过程还是有一些坑 包括兼容性问题 依赖安装问题 需要注意 缺少一个条件 包跑不起来 或使用体验很差 1. 全局安装插件 pnpm install -g sequelize-cli sequelize mysql2 egg-sequelize-auto 2. 执行命令创建 migrate迁移文件 以及 mod…

HDU RSA

翻译成中文后&#xff1a; 思路&#xff1a;由题易得&#xff0c;d * e y * f ( n ) 1 ,且gcd ( e , f ( n ) ) 1,所以用扩展欧几里得求出 d &#xff0c;但要保证 d 是非负的&#xff0c;最有用快速幂求出每个字符即可。 #include<bits/stdc.h> using namespace std;…

【计算机网络 - 基础问题】每日 3 题(四十二)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞…