大白话Vue 源码

news2025/3/6 22:22:24

大白话Vue 源码

整体介绍

Vue 源码就像是一个超厉害的“魔法工具箱”,它能帮咱们轻松地做出各种漂亮又好用的网页。这个工具箱里有好几个关键的“魔法道具”,分别是响应式系统、虚拟 DOM、模板编译、组件系统和生命周期钩子,下面咱一个一个详细说说。

响应式系统

想象一下,你有一个神奇的盒子,盒子里装着一个宝贝(数据)。每当有人去看这个宝贝(访问数据)或者去换这个宝贝(修改数据)的时候,盒子都会自动知道。这就是 Vue 里的响应式系统干的事儿。

代码解释
// Dep 类,就像是一个小本子,专门用来记录哪些人对这个盒子里的宝贝感兴趣
class Dep {
    constructor() {
        // 这个数组就是小本子的页面,用来记录感兴趣的人(Watcher 实例)
        this.subs = [];
    }

    // 把对宝贝感兴趣的人记到小本子上
    addSub(sub) {
        this.subs.push(sub);
    }

    // 如果有人正在关注这个盒子,就把他记下来
    depend() {
        if (Dep.target) {
            Dep.target.addDep(this);
        }
    }

    // 当盒子里的宝贝换了,就告诉所有记在小本子上的人
    notify() {
        this.subs.forEach(sub => sub.update());
    }
}

// 这个全局变量就像是一个小旗子,用来标记当前正在关注盒子的人
Dep.target = null;

// Watcher 类,就像是那些对盒子里的宝贝感兴趣的人
class Watcher {
    constructor(vm, expOrFn, cb) {
        this.vm = vm;
        this.cb = cb;
        // 把小旗子插到自己身上,表示自己正在关注盒子
        Dep.target = this;
        // 去看一眼盒子里的宝贝,这样盒子就知道自己被关注了
        this.getter = expOrFn;
        this.value = this.get();
        // 看完了,把小旗子拿下来
        Dep.target = null;
    }

    // 去看盒子里的宝贝
    get() {
        return this.getter.call(this.vm);
    }

    // 把自己记到小本子上
    addDep(dep) {
        dep.addSub(this);
    }

    // 当盒子里的宝贝换了,就执行自己的任务
    update() {
        const oldValue = this.value;
        this.value = this.get();
        this.cb.call(this.vm, this.value, oldValue);
    }
}

// 这个函数就是给盒子施魔法,让它能知道什么时候有人来看宝贝,什么时候宝贝被换了
function defineReactive(obj, key, val) {
    // 给这个盒子配一个小本子
    const dep = new Dep();

    // 用魔法让盒子能感知到有人来看宝贝或者换宝贝
    Object.defineProperty(obj, key, {
        get() {
            // 如果有小旗子插着,就把这个人记到小本子上
            if (Dep.target) {
                dep.depend();
            }
            return val;
        },
        set(newVal) {
            if (newVal !== val) {
                val = newVal;
                // 当宝贝换了,就告诉小本子上所有的人
                dep.notify();
            }
        }
    });
}

// 咱们来试试这个魔法
const obj = {
    name: 'John'
};

// 给 obj 的 name 属性施魔法
defineReactive(obj, 'name', obj.name);

// 有个人对这个盒子里的 name 宝贝感兴趣,给他安排个任务
const watcher = new Watcher(obj, () => obj.name, (newVal, oldVal) => {
    console.log(`Name changed from ${oldVal} to ${newVal}`);
});

// 把盒子里的 name 宝贝换了,看看会发生什么
obj.name = 'Jane'; 

虚拟 DOM

想象一下,你要盖一座房子(网页),但是每次修改房子的一个小地方都要重新把整个房子拆了再盖一遍,那可太麻烦了。虚拟 DOM 就像是房子的一个模型,你可以先在模型上改来改去,改好了再一次性把这些改动应用到真正的房子上。这样就不用每次改一点就去动真正的房子,能省好多时间和力气。

代码解释
// 这个函数就是用来做房子模型的
function createVNode(tag, props, children) {
    return {
        tag, // 房子的类型,比如是别墅还是公寓
        props, // 房子的一些属性,比如有几个窗户
        children // 房子里的小房间
    };
}

// 咱们来做一个房子模型试试
const vnode = createVNode('div', { id: 'app' }, [
    createVNode('p', null, 'Hello, Vue!')
]);

console.log(vnode);

模板编译

模板编译就像是一个翻译官,它能把咱们写的 HTML 模板(就像是一份盖房子的图纸)翻译成 JavaScript 代码(就像是一份工人能看懂的施工方案)。这样计算机就能按照这个方案来盖房子(渲染网页)了。

代码解释
// 这个简单的翻译官,只是把图纸简单地变成了一个施工方案
function compile(template) {
    // 这里只是简单模拟,实际的翻译过程要复杂得多
    return function render() {
        return createVNode('div', null, [
            createVNode('p', null, template)
        ]);
    };
}

// 咱们来试试这个翻译官
const template = 'Hello, Vue!';
const renderFunction = compile(template);
const vnode = renderFunction();
console.log(vnode);

组件系统

组件系统就像是一个积木盒子,里面有各种各样的积木(组件),你可以用这些积木拼出不同的造型(网页)。每个积木都有自己的功能和样子,而且可以重复使用。这样咱们就不用每次都从头开始做一个完整的东西,能提高效率。

代码解释
// 组件类,就像是积木的制作说明书
class VueComponent {
    constructor(options) {
        this.$options = options;
        this.$data = options.data();
        // 给这个积木的内部数据施魔法,让它能响应变化
        this.observe(this.$data);
        this.render = options.render;
    }

    // 给数据施魔法的函数
    observe(obj) {
        if (typeof obj !== 'object' || obj === null) {
            return;
        }
        Object.keys(obj).forEach(key => {
            defineReactive(obj, key, obj[key]);
        });
    }

    // 把这个积木拼到房子里
    mount() {
        const vnode = this.render();
        console.log('Rendering component with vnode:', vnode);
    }
}

// 咱们来做一个积木试试
const componentOptions = {
    data() {
        return {
            message: 'Hello, Component!'
        };
    },
    render() {
        return createVNode('div', null, [
            createVNode('p', null, this.$data.message)
        ]);
    }
};

const component = new VueComponent(componentOptions);
component.mount();

生命周期钩子

生命周期钩子就像是一个人的成长过程中的重要时刻,比如出生、上学、工作、退休等等。在 Vue 里,每个组件也有自己的生命周期,我们可以在这些重要时刻做一些自己想做的事情。

代码解释
class Vue {
    constructor(options) {
        this.$options = options;
        this.$data = options.data();
        // 在组件刚出生的时候,做一些事情
        this.callHook('beforeCreate');
        // 给组件的内部数据施魔法
        this.observe(this.$data);
        // 组件出生后,做一些事情
        this.callHook('created');
        this.render = options.render;
        // 在组件要被放到网页上之前,做一些事情
        this.callHook('beforeMount');
        this.mount();
        // 组件放到网页上之后,做一些事情
        this.callHook('mounted');
    }

    // 给数据施魔法的函数
    observe(obj) {
        if (typeof obj !== 'object' || obj === null) {
            return;
        }
        Object.keys(obj).forEach(key => {
            defineReactive(obj, key, obj[key]);
        });
    }

    // 把组件放到网页上
    mount() {
        const vnode = this.render();
        console.log('Rendering with vnode:', vnode);
    }

    // 调用生命周期钩子函数的函数
    callHook(hook) {
        const hooks = this.$options[hook];
        if (hooks) {
            hooks.forEach(hook => hook.call(this));
        }
    }
}

// 咱们来创建一个组件试试
const vmOptions = {
    data() {
        return {
            message: 'Hello, Vue!'
        };
    },
    render() {
        return createVNode('div', null, [
            createVNode('p', null, this.$data.message)
        ]);
    },
    beforeCreate() {
        console.log('Before create');
    },
    created() {
        console.log('Created');
    },
    beforeMount() {
        console.log('Before mount');
    },
    mounted() {
        console.log('Mounted');
    }
};

const vm = new Vue(vmOptions);

通过这些“魔法道具”,Vue 就能帮咱们轻松地做出各种漂亮又好用的网页啦!

Vue的模板编译

啥是 Vue 的模板编译

咱先打个比方,你要给远方的朋友描述一个超级酷炫的机器人长啥样,要是用嘴说,可能说半天都说不清楚。但你要是画个详细的图纸,朋友一看就明白了。在 Vue 里,咱们写的那些 HTML 模板就像是描述机器人样子的语言,可计算机不咋懂这些,它更擅长理解 JavaScript 代码。所以模板编译就像是一个“翻译官”,把咱们写的 HTML 模板翻译成计算机能懂的 JavaScript 代码。

模板编译的步骤

1. 把模板字符串解析成 AST(抽象语法树)

这就好比你拿到一份复杂的建筑设计说明书,第一步得把它拆分成一个个小的零件说明,并且整理出它们之间的关系。在 Vue 里,就是把 HTML 模板字符串拆分成一个树形结构的对象,这个对象就是 AST。每个节点代表模板里的一个元素、属性或者文本。

比如说,有这样一个简单的 Vue 模板:

<div id="app">
    <p>Hello, {{ message }}</p>
</div>

经过解析后,AST 可能就会像这样(简化示意):

{
    tag: 'div',
    attrs: [
        { name: 'id', value: 'app' }
    ],
    children: [
        {
            tag: 'p',
            children: [
                {
                    type: 2, // 表示文本插值
                    expression: 'message',
                    text: 'Hello, {{ message }}'
                }
            ]
        }
    ]
}

这里面,每个对象就是一个节点,tag 表示标签名,attrs 是标签的属性,children 是子节点。

2. 对 AST 进行优化

这一步就像是你盖房子,设计图纸出来后,要检查一下有没有可以优化的地方,比如某些结构可以简化,某些材料可以节省。在 Vue 里,就是找出 AST 里那些静态的节点(也就是不会随着数据变化而变化的节点),给它们做个标记。这样在后续的渲染过程中,就不用每次都重新处理这些静态节点了,能提高效率。

比如上面的模板里,<div><p> 标签本身不会变,只有 {{ message }} 会根据数据变化,所以 <div><p> 标签对应的 AST 节点就可以标记为静态节点。

3. 把优化后的 AST 生成渲染函数

这就好比根据优化后的建筑图纸,写出一份详细的施工方案,工人(计算机)拿到这个方案就能开始盖房子(渲染页面)了。在 Vue 里,就是把 AST 转换成 JavaScript 函数,这个函数返回的就是虚拟 DOM。

还是上面的例子,生成的渲染函数可能类似这样(简化示意):

function render() {
    return createVNode('div', { id: 'app' }, [
        createVNode('p', null, [
            createTextVNode('Hello, ' + this.message)
        ])
    ]);
}

这里的 createVNodecreateTextVNode 是用来创建虚拟 DOM 节点的函数。

模板编译的好处

  • 提高性能:通过把模板编译成 JavaScript 代码,计算机执行起来更快。而且优化 AST 能减少不必要的计算,提高渲染效率。
  • 方便维护:咱们写 HTML 模板的时候更符合人的习惯,而计算机执行的是编译后的 JavaScript 代码,分工明确,代码更易于维护。

总结

Vue 的模板编译就像是一个桥梁,把咱们写的 HTML 模板和计算机能懂的 JavaScript 代码连接起来,让我们可以更方便地开发网页,同时也让网页的渲染更高效。

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

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

相关文章

【手撕算法】支持向量机(SVM)从入门到实战:数学推导与核技巧揭秘

摘要 支持向量机&#xff08;SVM&#xff09;是机器学习中的经典算法&#xff01;本文将深入解析最大间隔分类原理&#xff0c;手撕对偶问题推导过程&#xff0c;并实战实现非线性分类与图像识别。文中附《统计学习公式手册》及SVM调参指南&#xff0c;助力你掌握这一核心算法…

AORO P9000 PRO三防平板携手RTK高精度定位,电力巡检效率倍增

电网系统覆盖幅员辽阔&#xff0c;每年因设备故障导致的巡检耗时超过百万工日。传统巡检模式受限于定位误差、设备防护不足和作业效率低下三大核心痛点&#xff0c;亟需智能化工具的突破性革新。为了满足这一需求&#xff0c;遨游通讯推出AORO P9000 PRO三防平板&#xff0c;以…

游戏引擎学习第135天

仓库:https://gitee.com/mrxiao_com/2d_game_3 回顾 game_asset.cpp 的创建 在开发过程中&#xff0c;不使用任何现成的游戏引擎或第三方库&#xff0c;而是直接基于 Windows 进行开发&#xff0c;因为 Windows 目前仍然是游戏的标准平台&#xff0c;因此首先在这个环境中进行…

关联封号率降70%!2025最新IP隔离方案实操手册

高效运营安全防护&#xff0c;跨境卖家必看的风险规避指南 跨境账号管理的核心挑战&#xff1a;关联封号风险激增 2024年&#xff0c;随着全球电商平台对账号合规的审查日益严苛&#xff0c;“关联封号”已成为跨境卖家最头疼的问题之一。无论是同一IP登录多账号、员工操作失误…

【深度学习CV】【图像分类】从CNN(卷积神经网络)、ResNet迁移学习到GPU高效训练优化【案例代码】详解

摘要 本文分类使用的是resNet34,什么不用yolo v8&#xff0c;yolo v10系列,虽然他们也可以分类&#xff0c;因为yolo系列模型不纯粹&#xff0c;里面包含了目标检测的架构&#xff0c;所以分类使用的是resNet 本文详细介绍了三种不同的方法来训练卷积神经网络进行 CIFAR-10 图…

如何排查服务器内存泄漏问题

服务器内存泄漏是一种常见的问题&#xff0c;可能导致系统性能下降甚至系统崩溃。以下是一般情况下用于排查服务器内存泄漏问题的步骤&#xff1a; 排查服务器内存泄漏问题的步骤&#xff1a; 监控系统资源&#xff1a; 使用系统监控工具&#xff08;如top、htop、free&#x…

Ubuntu20.04双系统安装及软件安装(九):谷歌浏览器

Ubuntu20.04双系统安装及软件安装&#xff08;九&#xff09;&#xff1a;谷歌浏览器 打开终端&#xff0c;下载谷歌浏览器软件包&#xff1a; wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb下载完成后直接在原终端执行&#xff1a; sudo…

有关Java中的集合(1):List<T>和Set<T>

学习目标 核心掌握List集合了解Set集合 1.List<T> ● java.util.List。有序列表。 ● List集合元素的特点&#xff1a;有序表示存取有序&#xff08;因为有索引&#xff09;而且可以重复 ● List常用实现类&#xff1a; ArrayList、LinkedList、Vector等 1.1 常用方法…

【C++STL之vector】vector容器浅析

文章目录 &#x1f31f; 深入探索C vector&#xff1a;从青铜到王者的动态数组进阶指南 &#x1f31f;&#x1f680; 开篇&#xff1a;为什么vector是C程序员的瑞士军刀&#xff1f;&#x1f50d; 一、vector的本质解密&#xff1a;不只是智能数组那么简单1.1 动态数组的华丽蜕…

Redis的持久化-RDBAOF

文章目录 一、 RDB1. 触发机制2. 流程说明3. RDB 文件的处理4. RDB 的优缺点 二、AOF1. 使用 AOF2. 命令写⼊3. 文件同步4. 重写机制5 启动时数据恢复 一、 RDB RDB 持久化是把当前进程数据生成快照保存到硬盘的过程&#xff0c;触发 RDB 持久化过程分为手动触发和自动触发。 …

Redis 的几个热点知识

前言 Redis 是一款内存级的数据库&#xff0c;凭借其卓越的性能&#xff0c;几乎成为每位开发者的标配工具。 虽然 Redis 包含大量需要掌握的知识&#xff0c;但其中的热点知识并不多。今天&#xff0c;『知行』就和大家分享一些 Redis 中的热点知识。 Redis 数据结构 Redis…

靶场之路-VulnHub-DC-6 nmap提权、kali爆破、shell反连

靶场之路-VulnHub-DC-6 一、信息收集 1、扫描靶机ip 2、指纹扫描 这里扫的我有点懵&#xff0c;这里只有两个端口&#xff0c;感觉是要扫扫目录了 nmap -sS -sV 192.168.122.128 PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.4p1 Debian 10deb9u6 (protoc…

机器视觉开发教程——封装Halcon通用模板匹配工具【含免费教程源码】

目录 引言前期准备Step1 设计可序列化的输入输出集合【不支持多线程】Step2 设计程序框架1、抽象层【IProcess】2、父类【HAlgorithm】3、子类【HFindModelTool】 Step3 设计UI结果展示 引言 通过仿照VisionPro软件二次开发Halcon的模板匹配工具&#xff0c;便于在客户端软件中…

【3DMAX室内设计】2D转3D平面图插件2Dto3D使用方法

【一键筑梦】革新性2Dto3D插件&#xff0c;轻松实现2D平面图向3D空间的华丽蜕变。这款专为3DMAX室内设计师设计的神器&#xff0c;集一键式墙体、门、窗自动生成功能于一身&#xff0c;能够将2D图形无缝转化为3D网格对象&#xff08;3D平面图、鸟瞰图&#xff09;&#xff0c;一…

vscode 查看3d

目录 1. vscode-3d-preview obj查看ok 2. vscode-obj-viewer 没找到这个插件&#xff1a; 3. 3D Viewer for Vscode 查看obj失败 1. vscode-3d-preview obj查看ok 可以查看obj 显示过程&#xff1a;开始是绿屏&#xff0c;过了1到2秒&#xff0c;后来就正常看了。 2. vsc…

自动驾驶---不依赖地图的大模型轨迹预测

1 前言 早期传统自动驾驶方案通常依赖高精地图&#xff08;HD Map&#xff09;提供道路结构、车道线、交通规则等信息&#xff0c;可参考博客《自动驾驶---方案从有图迈进无图》&#xff0c;本质上还是存在问题&#xff1a; 数据依赖性高&#xff1a;地图构建成本昂贵&#xf…

perl初试

我手头有一个脚本&#xff0c;用于从blastp序列比对的结果文件中&#xff0c;进行文本处理&#xff0c; 获取序列比对最优的hit记录 #!/usr/bin/perl -w use strict;my ($blast_out) ARGV; my $usage "This script is to get the best hit from blast output file wit…

VS Code C++ 开发环境配置

VS Code 是当前非常流行的开发工具. 本文讲述如何配置 VS Code 作为 C开发环境. 本文将按照如下步骤来介绍如何配置 VS Code 作为 C开发环境. 安装编译器安装插件配置工作区 第一个步骤的具体操作会因为系统不同或者方案不同而有不同的选择. 环境要求 首先需要立即 VS Code…

Web Snapshot 网页截图 模块代码详解

本文将详细解析 Web Snapshot 模块的实现原理和关键代码。这个模块主要用于捕获网页完整截图&#xff0c;特别优化了对动态加载内容的处理。 1. 模块概述 snapshot.py 是一个功能完整的网页截图工具&#xff0c;它使用 Selenium 和 Chrome WebDriver 来模拟真实浏览器行为&am…

Windows 10 下 SIBR Core (i.e. 3DGS SIBR Viewers) 的编译

本文针对在 Windows 10 上从源码编译安装3DGS &#xff08;3D Gaussian Splatting&#xff09;的Viewers 即SIBR Core及外部依赖库extlibs&#xff08;预编译的版本直接在页面https://sibr.gitlabpages.inria.fr/download.html下载&#xff09; &#xff0c;参考SIBR 的官方网站…