一道关于Vue的数据绑定和依赖收集的面试题

news2024/11/26 16:30:36

概要

分享一道Vue的面试题,该题涉及Vue的响应式数据绑定和依赖收集,希望可以加深大家对Vue原理的理解。

题面

有如下html和JS,要求使用Vue的响应式数据原理和依赖收集原理,实现createApps和ref方法。只能使用原生JS,不得使用已有的Vue2.0或3.0框架。

  1. 要求实现响应式数据title和description与DOM节点的数据绑定;
  2. 要求可以通过changeTitle和changeContent,更新响应式数据,绑定的DOM可以随着响应式数据的更新而自动更新内容。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <h1>{{title}}</h1>
        <span>{{description}}</span>
        <h1>{{title}}</h1>
        <span>{{description}}</span>
        <p>
            <button @click="changeTitle">Change Title</button>
            <button @click="changeContent">Change Description</button>
        </p>
    </div>
</body>
createApps({
    refs: {
        title: ref('default title'),
        description: ref('default description')
    },
    methods:{
        changeTitle(){
            this.title.value = "new title";
        },
        changeContent(){
            this.description.value = "new description";
        }
    }
});

解决方法

思路分析

首先我们先分析createApps的参数,该对象包含两个代码段:

  • refs段中包含所有需要转换成响应式数据的内容,需要通过调用ref方法,实现数据换换;
  • methods段中包含两个方法,我们需要将该方法通过click事件真正绑定到DOM元素上,该方法的this不再指向DOM节点,而是指向当前的响应数据。

基于上面的分析,createApps方法应该包含以下功能:

  1. 为title和description生成响应式数据;
  2. 将响应式数据绑定到指定的DOM元素上;
  3. 为DOM元素绑定Click事件处理函数,并更改函数中this的指向。

代码实现

ref方法,根据指定数据生成响应式数据:

function ref(content){
    const refObject = {
        _value: content,
        deps: new Set()
    };
    Object.defineProperty(refObject, "value", {
        get: () => refObject._value,
        set: (newValue) => {
            refObject._value = newValue;
        }
    });
    return refObject;
}
  1. 根据题目要求,通过value属性可以获取和修改响应式数据的值,所以通过Object.defineProperty方法,为value属性设置getter和setter;
  2. deps为响应式数据的依赖收集的DOM节点,初始为一个空Set。

createRefs方法用于实现响应式数据的依赖收集

function createRefs(options, nodes){   
    const reg = /\{\{\s*(?<refKey>.+?)\s*\}\}/;
    [...nodes].forEach(el => {
        if (reg.test(el.textContent)){
            const {refKey} = el.textContent.match(reg).groups;
            options.refs[refKey].deps.add(el);
        }
    });
    return options.refs;
}
  1. 该方法包含两个参数:options是题面传入的参数,nodes是题面中app节点先的所有DOM元素;
  2. 通过正则表达式找到包含{{}}的DOM节点,通过大括号内数据内容,从options.refs中找到对应的响应式数据;
  3. 将包含{{}}的DOM节点放入响应式数据的deps中,作为依赖收集项;
  4. 返回当前app的所有响应式数据。

render方法根据已经生成的响应式数据和依赖收集的DOM节点信息,更新DOM元数,实现数据绑定。

function render(refs){
    const keys = Object.keys(refs);
    keys.forEach(key => {
        _render(refs[key]);
    });
}
function _render({deps, value}){
    deps.forEach(node => {
        node.textContent = value;
    });
}
function update({deps, value}){
    _render({deps, value});
}

render方法的参数是当前app的全部响应式数据对象,数据结构和内容如下所示:
在这里插入图片描述

  1. 获取上述对象的所有key值,然后进行遍历;
  2. 调用_render,传入当前响应式数据,更新DOM元素;
  3. update方法作为_render方法的别名, 添加到ref方法中,实现数据到UI的联动更新。
function ref(content){
    const refObject = {
        _value: content,
        deps: new Set()
    };
    Object.defineProperty(refObject, "value", {
        get: () => refObject._value,
        set: (newValue) => {
            refObject._value = newValue;
            update(refObject);
        }
    });
    return refObject;
}

bindEvent方法将options参数中题目指定的DOM事件和方法进行绑定

function bindEvent(options, nodes){
    [...nodes].forEach(el => {
        let methodKey = el.getAttribute("@click");
        if (!methodKey){
            return;
        }
        methodKey = methodKey.trim();
        el.addEventListener("click", function(e){
            options.methods[methodKey].apply(options.refs,[e]);
        }, false);
    });
}
  1. 该方法包含两个参数:options是题面传入的参数,nodes是题面中app节点先的所有HTML元素;
  2. 遍历所有app下的节点,找到包含@click属性的节点;
  3. 将click事件绑定到题目指定的DOM上;
  4. 将事件的处理函数中的this强制指定为当前app的响应式数据。

createApps代码实现如下:

function createApps(options){
    const root = document.querySelector("#app");
    const nodes = root.querySelectorAll("*");
    const refs = createRefs(options, nodes);
    render(refs);
    bindEvent(options, nodes);
}
  1. 获取app的所有DOM节点;
  2. 调用createRefs生成响应式数据;
  3. 利用生成的响应式数据,调用render方法,渲染页面;
  4. 绑定题目指定的Click事件处理函数。

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

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

相关文章

教育的本质——采用不同学习方式,学习者在两周后还能记住的内容有多少

目录 一、学习金字塔模型 二、学习曲线 三、左右脑交替学习法 一、学习金字塔模型 “学习金字塔模型”&#xff0c;人们学习的效率一共分为七个层次&#xff1a; 第一层 ~ 第四层&#xff1a;这是我们最熟悉不过的形式&#xff0c;在学生时代&#xff0c;老师在上面讲课、…

【Leetcode】225. 用队列实现栈、232. 用栈实现队列

作者&#xff1a;一个喜欢猫咪的的程序员 专栏&#xff1a;《Leetcode》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 目录 225. 用队列实现栈 232. 用栈实现队列 225. 用队列实现栈 225. 用队…

91-143-Scala-集合模式泛型等

91-Scala-集合模式泛型等&#xff1a; Scala 的集合有三大类&#xff1a;序列 Seq、集 Set、映射 Map&#xff0c;所有的集合都扩展自 Iterable特质。 2&#xff09;对于几乎所有的集合类&#xff0c;Scala 都同时提供了可变和不可变的版本&#xff0c;分别位于以下两个包 不…

人工智能(AI)背后人工的力量——数据标注

尽管随着AI的普及&#xff0c;我们在生活中越来越依赖于人工智能&#xff0c;但“人工智障”的相关调侃也从来没有消失过。 相信大家都知道&#xff0c;如果我们想要让AI准确识别出图中的鸟&#xff0c;我们需要在数据集中手动将这些照片标记为鸟&#xff0c;然后让算法和图像…

c3p0数据库连接池的使用

c3p0数据库连接池的使用 c3p0的官网&#xff1a;c3p0 - JDBC3 c3p0数据库连接池有两种方法 导入jar包 首先两种方法都需要导入jar包 图中打钩的&#xff0c;第一个是c3p0的包&#xff0c;第二个是连接数据库的 在WEB-INF下新建lib包&#xff0c;将所需要的jar导入 右键添加…

【Java技术专题】「OpenJDK专题」想不想编译属于你自己的JDK呢?(Windows环境)

Win10下编译OpenJDK8 编译环境 Windows10专业版64位&#xff1b; 编译前准备 Tip&#xff1a; 以下软件的安装和解压目录尽量不要包含中文或空格&#xff0c;不然可能会出现问题 安装 Visual Studio 2010 Professional 在windows下编译JDK需要使用Visual Studio 2010 Profes…

反抗与反噬:亚马逊被迫结束封号神秘主义,不再粗暴关店

“每一天醒来&#xff0c;都要看一眼自己的店铺是否还在。”、“账号被封之后&#xff0c;自己也曾第一时间向平台申诉&#xff0c;经历过一次、两次甚至是多次申诉无果后&#xff0c;才意识到账号是真的回不来了。”、“过去传言大卖有保护伞&#xff0c;这一回才发现做亚马逊…

B站有哪些值得Java初学者看的视频,Java学习路线

我的读者中有很大一部分学生读者&#xff0c;以前也分享过一些Java学习路线&#xff0c;但是我收到的反馈并不好&#xff0c;因为学习路线包含的内容太多了&#xff0c;对于初学者来说难度太大&#xff0c;时间也不够用&#xff0c;根本学不完。今天我将结合B站优秀视频整理一期…

英特尔着眼系统工艺协同优化理念,推进摩尔定律新浪潮

Ann Kelleher介绍了晶体管诞生75年之后的新进展 在IEDM 2022&#xff08;2022 IEEE国际电子器件会议&#xff09;全体会议上发表演讲之前&#xff0c;英特尔副总裁兼技术开发总经理Ann Kelleher接受了《IEEE Spectrum》的采访&#xff0c;她表示&#xff0c;摩尔定律的下一波浪…

Tcp/Udp(网络套接字,服务器) -- Linux/Windows

目录 网络传输本质 认识端口号 认识协议 认识TCP协议 认识UDP协议 网络字节序 socket编程接口 socket 常见API sockaddr结构 sockaddr 结构 sockaddr_in 结构 in_addr结构 编写代码前的小tip&#xff08;重点&#xff09; UDP套接字&#xff08;网络通信&#xf…

算法竞赛入门【码蹄集进阶塔335题】(MT2101-2125)

算法竞赛入门【码蹄集进阶塔335题】(MT2201-2225&#xff09; 文章目录算法竞赛入门【码蹄集进阶塔335题】(MT2201-2225&#xff09;前言为什么突然想学算法了&#xff1f;为什么选择码蹄集作为刷题软件&#xff1f;目录1. MT2101 竹鼠发瓜子2. MT2102 竹鼠发瓜子&#xff08;二…

Raft协议

Raft协议先行了解 总体过程速览 假设我们只使用一个节点&#xff0c;可以很容易的达成协议或者共识。 但是现在我们思考&#xff0c;假如有多个节点呢&#xff1f; 多个节点之间达成协议或者共识就叫做分布式共识。 而Raft就是一个实现分布式共识的协议。 一个节点可以有3…

数字化转型的十大好处

前言&#xff1a; 在过去的几年中&#xff0c;“适者生存”对企业来说是至关重要的。不能适应环境变化的企业&#xff0c;也将会加速被淘汰的进程。只有从数字化转型中受益的企业才能更好的参与管理和快速调整&#xff0c;这样一来&#xff0c;员工便能够在更高效、更安全的状…

黑盒测试用例设计 - 判定表法

什么是判定表&#xff1f; 判定表法也叫判定驱动法&#xff0c;是分析和表达多逻辑条件下执行不同操作的情况的工作。 应用场合&#xff1a;只要适用于多条件的内容组合与结果分析 它由以下几个内容组成&#xff1a; 条件桩&#xff08;condition stub&#xff09;&#xff1…

LwIP带操作系统的移植

目录 LwIP移植前期准备 LwIP移植流程 修改lwipopts.h 修改lwip_comm.c文件 修改ethernetif.c/h文件 修改ethernetif_input函数 修改ethernet.c文件 添加应用程序 LwIP是支持操作系统的&#xff0c;在操作系统的支持下我们可以使用LwIP提供的另外两种API编程接口编程。没…

使用动态代理+Netty+Zookeeper+Protobuff手撸一个RPC框架

RPC是什么 RPC&#xff08;Remote Procedure Call&#xff09;远程过程调用&#xff0c;一种计算机之间的远程调用技术&#xff0c;客户端能够在不知道服务器底层的通信架构的情况下调用服务器端的方法&#xff0c;就像调用自身的方法一样。 举个例子&#xff1a; 老婆自己去…

Uni-app 实现md5加密

写下这篇文章&#xff0c;记录自己走过的坑 第一次尝试&#xff1a;参照博客uniapp使用md5_清雨小竹的博客-CSDN博客_uniapp md5 引入md5.js后&#xff0c;在main.js中import后&#xff0c;无法使用md5.hex_md5("需要加密的字符串")&#xff0c;vue页面无法打开&…

【捕风捉影】Vue项目报错,点击浏览器报错信息定位不到报错代码,该如何优雅地调试代码?

【捕风捉影】Vue项目如何优雅地调试代码一、背景二、调试时开启productionSourceMap三、devtool几种模式一、背景 通过vue-cli服务运行项目&#xff0c;项目运行一切正常。但打包后&#xff0c;通过nginx部署运行&#xff0c;大屏展示模块报echarts typeError 错误。但是点击浏…

如何使用Docker创建自定义网络

目录 网络模式 1.bridge模式(默认模式--桥接模式) 初识网络模式 查看桥接模式的特点 2.host模式&#xff08;仅主机模式&#xff09; 使用守护进程的方式创建并启动且进入容器 查看仅主机模式下的网络配置 端口映射 &#xff1a;​ 3.如何创建自定义网络 网络模式 Docker…

启发式算法 之 模拟退火原理及实践

一、初窥其貌 1.1 启发式算法和元启发式算法 启发式算法是求解优化问题的一类方法&#xff0c;因为经典优化方法存在局限性&#xff0c;有时无法得到最优解&#xff0c;只能得到一个可以接受的近似最优解&#xff0c;启发式算法就适合求解这类问题。启发式算法就是专家的推测…