当前位置: 首页 > article >正文

《Vue3 十》Vue 底层原理

命令式编程和声明式编程:

以计时器为例:

// 原生 JavaScript 实现计数器,是命令式编程
<div>
    <h1>当前数字:<span class="count"></span></h1>
    <button class="add" @click="handleAdd">+1</button>
    <button class="sub" @click="handleDelete">-1</button>
</div>

const countEl = document.querySelector('.count')
const addEl = document.querySelector('.add')
const subEl = document.querySelector('.sub')

let count  = 0
countEl.textContent = count

addEl.onclick =function () {
    count++;
    countEl.textContent = count
}
subEl.onclick =function () {
    count--;
    countEl.textContent = count
}
// Vue 实现计时器,是声明式编程
<div id="app">
    <h1>当前数字:{{count}}</h1>
    <button @click="handleAdd">+1</button>
    <button @click="handleSub">-1</button>
</div>

const app = Vue.createApp({
    data() {
        return {
            count: 0,
        }
    },
    methods: {
        handleAdd: function() {
            this.count++;

        },
        handleSub: function() {
            this.count--;
            
        },
    }
})
app.mount('#app')
  1. 命令式编程:需要将要做的每一个步骤都编写一条代码,转化成一个个的命令告知浏览器。关注的是 how to do,自己完成整个 how 的过程。

    原生 JavaScript、jQuery 都是命令式编程。

  2. 声明式编程:需要哪些东西就声明哪些东西,具体如何实现不需要管,框架会帮助完成。关注的是 what to do,由框架完成 how 的过程。

    目前,Vue、React、Angular、小程序等都是声明式编程。

MVC 模型和 MVVM 模型:

MVC:

MVC,即 Mode l- View - Controller。

  1. Model:数据模型。负责保存应用数据,与后端数据进行同步。
  2. View:UI 视图。负责视图展示,将 Model 中的数据可视化出来。
  3. Controller:控制器。负责业务逻辑,根据用户行为对 Model 数据进行更改。
    MVC 中所有的通信都是单向的。View 传送指令到 Controller;Controller 完成业务逻辑后,要求 Model 改变状态;Model 将新的数据发送到 View,用户得到反馈。
    在这里插入图片描述

MVVM:

MVVM ,即 Model - View - ViewModel 。

  1. Model:数据模型。
  2. View:UI 视图。
  3. ViewModel:View 和 Model 的连接器。View 和 Model 通过 ViewModel 实现双向绑定,ViewModel 在底层做了大量的事情来实现数据变化更新视图,视图变化更新数据。

React、Vue、Angular 都是 MVVM 框架。

在这里插入图片描述

单向数据流:

数据的流动是单向的。父组件传递给子组件数据,子组件只能使用不能修改。

双向数据绑定:

双向数据绑定:即数据变化更新视图,视图变化更新数据。

Vue2 的响应式原理:

Vue2 的响应式原理基于 Object.defineProperty() 实现,利用 Object.defineProperty() 中的 set() 方法对对象的属性进行劫持,监听到对象属性的改变,就自动执行依赖的代码。

手动实现 Vue2 的响应式:
// 1. 定义一个 watchFn 函数,接收一个回调函数,当其内部依赖到的数据发生变化时,自动执行该回调函数
let reactiveFn = null
function watchFn(fn) {
    reactiveFn = fn
    fn()
    reactiveFn = null
}

// 2. 定义一个收集依赖的类
class Depend {
    constructor() {
        // 一个数据可能会被多次依赖,使用 Set 的目的是为了防止添加的依赖的函数重复
        this.reactiveFns = new Set()
    }

    addDepend(fn) {
        if (!fn) return
        this.reactiveFns.add(fn)
    }

    notify() {
        this.reactiveFns.forEach(fn => {
            fn()
        })
    }
}

// 3. 定义一个通过对象的属性名获取对应的 depend 依赖的函数。每一个对象的每一个属性对应一个 depend 依赖对象;通过一个 map 对象存储一个对象中所有属性与其 depend 依赖对象的映射关系;再通过一个 objMap 对象存储所有对象与其 map 对象的映射关系
const objMap = new WeakMap() 
function getDepend(obj, key) {
    // 根据对象,找到对应的 map
    let map = objMap.get(obj)
    if (!map) {
        map = new Map()
        objMap.set(obj, map)
    }

    // 根据属性名,找到对应的 depend
    let dep = map.get(key)
    if(!dep) {
        dep = new Depend()
        map.set(key, dep)
    }

    return dep
}

// 4. 监听对象中数据的访问,自动收集其依赖;监听对象中数据的改变,自动执行依赖的代码
function reactive(obj) {
    Object.keys(obj).forEach(key => {
        let value = obj[key]
        Object.defineProperty(obj, key, {
            get: function() {
                // 找到对象的属性名对应的 depend,收集依赖
                const dep = getDepend(obj ,key)
                dep.addDepend(reactiveFn)
                return value
            },
            set: function(newValue) {
                value = newValue
                // 找到对象的属性名对应的 depend,自动执行
                const dep = getDepend(obj ,key)
                dep.notify()
            }
        })
    })

    return obj
}

// ------------上面的是封装的实现响应式的代码,下面的是业务代码------------------ //

// 1. 定义一个要收集依赖的对象
const user = reactive({
    name: 'Lee',
    age: 18
})

// 2. 如果对象中的数据发生变化,自动执行传入的回调函数
watchFn(function() {
    console.log('name 发生变化:' + user.name)
})
watchFn(function() {
    console.log('age 发生变化:' + user.age)
})

// 3. 改变对象中的数据,依赖这个数据的代码将会自动执行
user.name='Mary'

在这里插入图片描述
在这里插入图片描述

Vue3 的响应式原理:

Vue3 的响应式原理基于 Proxy 实现。

手动实现 Vue3 的响应式:

其他部分的代码和手动实现 Vue2 的响应式中一样,只有监听数据的改变对其进行劫持的方式有变化。

// 4. 监听对象中数据的访问,自动收集其依赖;监听对象中数据的改变,自动执行依赖的代码
function reactive(obj) {
    const objProxy = new Proxy(obj, {
        get: function(target, key) {
            const dep = getDepend(target, key)
            dep.addDepend(reactiveFn)
            return target[key]
        },
        set: function(target, key, newValue) {
            target[key] = newValue
            const dep = getDepend(target, key)
            dep.notify()
        }
    })

    return objProxy
}

虚拟 DOM 和 Diff 算法:

虚拟节点 VNode:

VNode:全程是 Virtual Node,虚拟节点。VNode 本质上就是一个 JavaScript 对象。Vue 会先将模板中的元素解析为一个个 VNode,然后再将一个个 VNode 渲染为真实 DOM 节点。
在这里插入图片描述

虚拟 DOM VDOM:

VDOM:全称是 Virtual DOM,虚拟 DOM。一堆的元素就会形成一个虚拟节点树,也就是一棵以 JavaScript 对象为基础的树,它是一层对真实 DOM 的抽象,最终可以通过一系列操作使这棵树映射到真实环境上。
在这里插入图片描述
VDOM 的优势有:

  1. 可以进行 Diff 算法:运用 Diff 算法来计算出真正需要更新的节点,最大限度地减少 DOM 操作,从而显著提高性能。
  2. 方便进行跨平台:本质上只是 JavaScript 对象,只要对其进行不同的解析,就可以方便地进行跨平台。

Diff 算法:

虚拟DOM的最终目标是将虚拟节点渲染到视图上。但是如果直接使用虚拟节点覆盖旧节点的话,会有很多不必要的DOM操作(例如:一个ul标签下很多个li标签,其中只有一个li有变化,这种情况下如果使用新的ul去替代旧的ul,会因为这些不必要的DOM操作而造成了性能上的浪费)。为了避免不必要的DOM操作,虚拟DOM在虚拟节点映射到视图的过程中,将虚拟节点与上一次渲染视图所使用的旧虚拟节点(oldVnode)做对比,找出真正需要更新的节点来进行DOM操作,从而避免操作其他无需改动的DOM。虚拟DOM在Vue.js主要做了两件事:

  1. 提供与真实DOM节点所对应的虚拟节点vnode;
  2. 将虚拟节点vnode和旧虚拟节点oldVnode进行对比,然后更新视图;
    在这里插入图片描述
    Vue的diff算法是基于snabbdom改造过来的,仅在同级的vnode间做diff,递归地进行同级vnode的diff,最终实现整个DOM树的更新。

diff 算法包括几个步骤:

  1. 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中;
  2. 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异;
  3. 把所记录的差异应用到所构建的真正的DOM树上,视图就更新了;

http://www.kler.cn/a/512530.html

相关文章:

  • 成就与远见:2024年技术与思维的升华
  • [Qt]事件-鼠标事件、键盘事件、定时器事件、窗口改变事件、事件分发器与事件过滤器
  • 为什么相关性不是因果关系?人工智能中的因果推理探秘
  • 【json_object】mysql中json_object函数过长,显示不全
  • ElasticSearch DSL查询之高亮显示
  • LeetCode hot 力扣热题100 排序链表
  • [gpu驱动] H200 nvidia-fabricmanager-550升级到nvidia-fabricmanager-565报错,升级步骤
  • Android 13 动态显示隐藏 HomeButton,RecentsButton
  • 100条Linux命令汇总
  • 微信小程序之 如何使用全局变量将openid传到其他页面
  • 【Tortoise-ORM】 高级特性与实战
  • Linux内核编程(二十一)USB驱动开发
  • 【Java数据结构】Java对象的比较
  • python如何导出数据到excel文件
  • 京东api接口获得JD商品详情接口PHP调用演示示例
  • 12 分布式事务
  • 深入 Flutter 和 Compose 的 PlatformView 实现对比,它们是如何接入平台控件
  • Pandas 数据重命名:列名与索引
  • Linux容器(初学了解)
  • Chromium 132 编译指南 Mac 篇(三)- 配置 depot_tools
  • Bash语言的数据库交互
  • 电气防火保护器为高校学生宿舍提供安全保障
  • Git 分支策略
  • CentOS9 安装Docker+Dpanel+onlyoffice(https、更改字体、字号、去除限制)的避坑笔记
  • [论文阅读] (36)CS22 MPSAutodetect:基于自编码器的恶意Powershell脚本检测模型
  • Open3D 裁剪任意指定区域的点【2025最新版】