手撕Vue-编译指令数据

vue · 浏览次数 : 60

小编点评

**Chapter 5. Build Element** **Methods:** - buildElement **Purpose:** - Handles the compilation of directive data into the build process. **Details:** - This chapter focuses on the `buildElement` method, which performs the following steps: - Analyzes the directive data to determine its type. - Calls appropriate processing functions based on the directive name. - Concatenates and renders the final output based on the directive type. **Code:** ```javascript // CompilerUtil class handles directive type classification let CompilerUtil = { model: function (node, value, vm) { // ... }, html: function (node, value, vm) { // ... }, text: function (node, value, vm) { // ... }, }; // Build element method function buildElement(node, value, vm) { // Split name and directive from the name const [name, directive] = node.name.split('-'); // Call the appropriate processing function CompilerUtil[directive](node, value, vm); } ``` **Example Usage:** ```html
123
``` **Explanation:** The code first splits the `name` attribute into `name` and `directive` parts. Then, it uses the `CompilerUtil` object to call the corresponding processing function based on the `directive` name. **Data Structure Handling:** - When handling object data structures, we need to perform specific processing to access the nested properties. - The `getValue` method is used to handle nested objects and arrays. - It iterates through the properties and builds the final output string accordingly. **Conclusion:** The `buildElement` method handles directive compilation by identifying the directive type, calling appropriate processing functions, and rendering the final output based on the directive type.

正文

经过上一篇的分析,完成了查找指令和模板的功能,接下来就是编译指令的数据了。

所以本章节主要处理的方法则是 buildElement 方法,我们先分析一下我们所拿到的数据在进行编码,这样会更加清晰一些。

我将 name, value 打印出来,分别对应的值是 name: v-model, value: name,在今后我们的命令中可不止只有 v-model,还有 v-text、v-html、v-on 等等,所以我们需要对这些指令进行分类,然后再进行编译。

所以我这里特意定义了一个工具类叫 CompilerUtil,用来处理指令的分类,代码如下:

let CompilerUtil = {
    /**
     * 处理 v-model 指令
     * @param node 当前元素
     * @param value 指令的值
     * @param vm Nue 的实例对象
     */
    model: function (node, value, vm) {
    },
    html: function (node, value, vm) {
    },
    text: function (node, value, vm) {
    }
}

然后我们在 buildElement 方法中调用这个方法,代码如下:

// 解构 name
let [, directive] = name.split('-');
// v-model -> [v, model]

// 2.根据指令名称, 调用不同的处理函数
CompilerUtil[directive](node, value, this.vm);

这样我们就可以根据指令的名称,调用不同的处理函数了。

接下来我们就来处理 v-model 指令,代码如下:

/**
 * 处理 model 指令
 * @param node 当前元素
 * @param value 指令的值
 * @param vm Nue 的实例对象
 */
model: function (node, value, vm) {
    node.value = vm.$data[value];
},

这样我们就可以将数据渲染到页面上了,打开浏览器,可以看到效果如下:

image-20231015171034901

v-model 指令已经可以正常使用了,但是还有问题,就是我们的数据结构目前是比较简单的,那么如果我们的数据是一个对象呢,例如:

image-20231015172334067

time: {
    h: 10,
    m: 10,
    s: 10
}

在用 input 绑定 v-model 进行渲染发现,只有第一个 input 能够正常渲染,其他的 input 都是 undefined,这是为什么呢?

<input type="text" v-model="time.h">
<input type="text" v-model="time.m">
<input type="text" v-model="time.s">

那么这里就要去看一下我们 model 方法的实现了,如果是 time.h,value 等于的值为 time.h, 然后我们在执行 vm.$data[value] 就变为了 vm.$data[time.h], 正常的获取这种数据结构的方式应该是先 vm.$data[time] 拿到 time 对象,然后再 time[h] 拿到 h 的值,所以我们需要对这种数据结构进行处理,为了已维护,我这里单独抽离了一个方法出来进行处理获取 value,方法名字叫做 getValue,代码如下:

getValue(vm, value) {
    // time.h --> [time, h]
    return value.split('.').reduce((data, currentKey) => {
        // 第一次执行: data=$data, currentKey=time
        // 第二次执行: data=time, currentKey=h
        return data[currentKey];
    }, vm.$data);
},

reduce 方法被用于迭代这个字符串数组。它接受一个回调函数,这个回调函数在每次迭代中被调用。在这个回调函数中,data 是上一次迭代的结果,而 currentKey 是当前迭代的数组元素(键路径中的一个部分)在每次迭代中,回调函数通过 data[currentKey] 的方式访问嵌套对象的属性,然后将这个属性的值作为下一次迭代的 data, 最终,reduce 方法将遍历整个键路径,直到达到最深层的属性,然后返回该属性的值。这样我们就可以正常的获取到数据了,最后在改造一下之前 model 方法获取值的地方,调用下刚刚编写的 getValue 方法即可:

model: function (node, value, vm) {
    node.value = this.getValue(vm, value);
},

再次打开浏览器,可以看到效果如下:

image-20231015172812038

这个搞定之后,我们紧接着把 v-html 和 v-text 也搞定,代码基本上都是一样的,只是渲染的方式不一样,代码如下:

/**
 * 处理 html 指令
 * @param node 当前元素
 * @param value 指令的值
 * @param vm Nue 的实例对象
 */
html: function (node, value, vm) {
    node.innerHTML = this.getValue(vm, value);
},
/**
 * 处理 text 指令
 * @param node 当前元素
 * @param value 指令的值
 * @param vm Nue 的实例对象
 */
text: function (node, value, vm) {
    node.innerText = this.getValue(vm, value);
}

编写测试代码:

image-20231015173151943

html: `<div>我是div</div>`,
text: `<div>我是div</div>`

编写HTML代码:

<div v-html="html">abc</div>
<div v-text="text">123</div>

打开浏览器,可以看到效果如下:

image-20231015173252270

与手撕Vue-编译指令数据相似的内容:

手撕Vue-编译指令数据

经过上一篇的分析,完成了查找指令和模板的功能,接下来就是编译指令的数据了。 所以本章节主要处理的方法则是 buildElement 方法,我们先分析一下我们所拿到的数据在进行编码,这样会更加清晰一些。 我将 name, value 打印出来,分别对应的值是 name: v-model, value:

『手撕Vue-CLI』添加帮助和版本号

前言 经过上一篇『手撕Vue-CLI』编码规范检查之后,手撕 Vue-CLI 已经进阶到了代码规范检查这一步,已经将基本的工程搭建好了,然后代码规范约束也已经加入了,并且将 nue-cli 指令绑定到了全局当中,可以在任何地方使用了。 正文 接下来这篇文章呢,就要来实现一下大多数的命令行工具都会有的

『手撕Vue-CLI』编码规范检查

前言 这篇为什么是编码规范检查呢?因为这是一个很重要的环节,一个好的编码规范可以让代码更加清晰易读,在官方的 VUE-CLI 也是有着很好的编码规范的,所以我也要加入这个环节。 其实不管在哪个项目中,编码规范都是很重要的,像我们平日里的项目开发当中,我们也会使用 ESLint 来进行代码规范检查。

『手撕Vue-CLI』完善提示信息

前言 经过『手撕Vue-CLI』自动安装依赖,已经实现了自动安装依赖的功能。 然而,虽然项目已复制并安装依赖,但其提示信息并不够友好,于是我试着去运行了一下vue create,发现其提示信息是这样的: 于是我决定完善提示信息,也借此机会完善一下项目的代码,变量命名等。 完善提示信息 完善变量命名

『手撕Vue-CLI』自动安装依赖

开篇 经过『手撕Vue-CLI』拷贝模板,实现了自动下载并复制指定模板到目标目录。然而,虽然项目已复制,但其依赖并未自动安装,可能需要用户手动操作,这并不够智能。 正如前文所述,我们已经了解了业务需求和背景。那么,接下来我们将直接深入探讨核心实现细节。 自动安装依赖 在前文中,我们已经将模板文件复制

『手撕Vue-CLI』拷贝模板

开篇 经过上篇文章的介绍,实现了可以根据用户选择的模板名称加上对应的版本号,可以下载对应的模板,并且将下载之后的文件存放在用户目录下的 .nue-template文件夹中。 接下来这篇文章主要实现内容是将下载的模板文件拷贝到当前所执行命令的目录下。 拷贝模板 例如我现在在终端当中输入 nue-cli

『手撕Vue-CLI』下载指定模板

开篇 经上篇文章的介绍,实现了获取下载目录地址,接下来实现下载指定模板的功能。 背景 通过很多章节过后,已经可以拿到模板名称,模板版本号,下载目录地址,这些信息都是为了下载指定模板做准备的。 实现 如何从 GitHub 下载模板 可以借助 download-git-repo 这个库来下载 GitHu

『手撕Vue-CLI』获取下载目录

开篇 在上一篇文章中,简单的对 Nue-CLI 的代码通过函数柯里化优化了一下,这一次来实现一个获取下载目录的功能。 背景 在 Nue-CLI 中,我现在实现的是 create 指令,这个指令本质就是首先拿到模板名称和版本号之后,然后去进行下载对应的模板,关于下载那么肯定要面临的问题就是如何下载?下

『手撕Vue-CLI』函数柯里化优化代码

开篇 在上一篇文章中,给 nue-cli 添加了拉取版本号的功能,这一次来优化一下代码,使用函数柯里化的方式来优化代码。 实现 函数柯里化 函数柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。 在进行改写之前,首先给大家简单介绍一下函数柯里化,写一个简单的例子来演示一下。

『手撕Vue-CLI』拉取版本号

开篇 在上一篇文章中,给 nue-cli 添加了与用户终端交互的功能,这一次来实现一个拉取版本号的功能。 这个功能的背景是,有时候我们在使用脚手架的时候,不同版本的脚手架可能会有不同的功能,所以用户有可能会根据自己的需求选择不同的版本,所以这里将会实现一个根据用户选择的版本号,拉取对应的版本号的模板