原生JS实现轻量Vue+指令解析+变异数组监听

本章涉及知识点

1、前言

2、ZVue架构设计

3、框架的功能列表

4、ZVue类的属性定义

5、_observer方法定义

6、_complie方法定义

7、v-model和v-bind指令解析

8、v-for循环指令解析

9、v-click点击指令解析

10、Watcher类的属性定义

11、Watcher动态批量删除DOM方法

12、Watcher动态批量添加DOM方法

13、Watcher动态更新DOM方法

14、变异数组类的属性定义

15、变异数组切面调用回调函数

16、案例页面引用ZVue框架

17、案例结果的展示分析

18、后记

一、前言

写完了几篇数学算法的推导,突发奇想来换个角度写一点关于前端的框架原理算法,于是就尝试写了这个轻量的框架—ZVue,大致上模拟了Vue渐进式框架的MVVM逻辑流程,也是自己对Vue框架原理的一些感悟和心得体会

该框架可以实现动态的data劫持监听和指令解析、增删改查整个数组字面量或者若干数组项字面量的时候,同时刷新view对应的视图、事件的动态绑定,以及data和view的双向绑定。在page层实例ZVue的语法,html指令模板语法,包括渲染到浏览器端后的dom的结构和原生Vue基本上相同

二、ZVue整体架构设计

梳理一下MVVM流程,需要动态劫持,解析指令,更新视图,变异数组处理等,设计出的架构图如下

ZVue架构设计图

根据架构图,类和函数的设计清单如下:

1:ZVue类

(1)constructor:初始化ZVue实例的数据集合、方法集合、template映射表和render映射表等

(2)_toNewArray:实例变异数组及注入回调函数

(3)_defineProperty:属性劫持到变化,通知相应的watcher对象刷新view

(4)_observer:深度遍历所有属性,维护render映射表,并分派劫持监听

(5)_complie:遍历dom结构的所有指令,维护template映射表,分派到各自的解析函数

(6)_parsing_v_model:解析v-model指令,绑定input事件和watcher对象

(7)_parsing_v_click:解析v-click指令,绑定click事件,解析事件的参数列表

(8)_parsing_v_bind:解析v-bind指令和对应的watcher对象

(9)_parsing_v_for:解析v-for指令,作用于整个数组,循环生成动态dom,为父节点绑定watcher对象

(10)_parsing_v_for_item:遍历v-for下的动态dom,绑定watcher对象

(11)_deleteArrayWatcher:动态维护删除render的映射表

(12)_maintainTemplate:动态维护template映射表,并为真实dom设置v-data唯一标志

二、Watcher类

(1)constructor:初始化data绑定的视图片段,包含真实的dom,dom的属性,data的key,ZVue的指针等

(2)getTemplateDom:根据dom字符串,动态组装出包含标签、类名、内容以及自定义指令的dom模板

(3)deleteBatchDom:动态批量删除dom,并同步维护render映射表

(4)addBatchDom:动态批量添加dom,并同步劫持监听新的属性值以及解析新的指令

(5)update_Array:更新数组类型的视图片段

(6)update_Text:更新普通标签中的视图片段

(7)update:set钩子通知触发,负责通知属性对应的更新函数来刷新视图

三、NewArray类(继承Array类)

(1)constructor:接受参数列表和ZVue注入的回调函数,调用父级的构造函数

(2)push:重载Array类的push方法,并在切面调用ZVue的回调函数

(3)pop:重载Array类的pop方法,并在切面调用ZVue的回调函数

(4)splice:重载Array类的splice方法,并在切面调用ZVue的回调函数

三、架构的功能列表

有了架构图和函数设计列表,此架构的功能列表为:

(1)页面ZVue对象的data和methods接管

(2)页面的模板节点结构、插值表达式的正确渲染和节点事件的绑定

(3)实现v-model、v-bind、v-click、v-for四个指令的正确解析渲染

(4)Input输入测试v-model双向绑定,页面的值随着Input的输入值变化而同步变化

(5)dl列表测试v-for循环生成若干个dd(包含class和若干指令),且插值表达式包含常量+索引变量+数组项数值变量

(6)button点击测试更新整个数组的字面量,页面的列表同步变化

(7)button点击测试局部更新数组的某一项字面量,页面的列表同步变化

(8)button点击测试push数组,页面的列表同步变化

(9)button点击测试pop数组,页面的列表同步变化

(10)button点击测试splice数组,页面的列表同步变化

四、ZVue类的属性定义

ZVue类的属性定义

需要注意原始数组类型需要赋值为变异数组,才能进行数组API的切面编程

五、_observer方法定义

_observer方法

需要注意遍历到数组类型的时候,需要初始化整个数组指针的render映射表并深度遍历,到遍历到数组项的时候,render映射表需要额外deep一层,显然普通类型、数组指针和数组任意一项都被劫持,渲染后data结果为

数组整体和局部的劫持

六、_complie方法定义

_complie方法

分派到各自的指令函数解析后,render映射表为

render映射表

七、v-model和v-bind指令解析

v-model指令
v-bind指令

可以看到指令解析里,注入了与data对应的watcher对象负责更新对应的view

八、v-for循环指令解析

v-for指令

需要注意的是_parsing_v_for需要解析参数列表(可能包含数组索引或者数值,或者同时都包含),最后是给节点的父节点绑定了watcher对象,该对象只负责删除原来旧列表和生成新列表,而生成的列表节点还没有被劫持和解析指令,所以需要重新劫持和解析新的节点

v_for_item方法

可以看到_parsing_v_for_item给每一个新节点注入了对应的watcher对象,同时还检测了新节点是否包含v-click指令,如果包含则调用其的解析函数,这也就是架构图中_parsing_v_for_item需要在watcher动态添加节点之后调用

九、v-click点击指令解析

v-click指令-1
v-click指令-2

可以看到v-click指令需要先解析待调用的函数名字和参数列表,同时要注意向上检测父节点来判断当前节点是否是循环生成的这种情况,因为如果是循环生成的节点,需要二次从v-for指令中解析转义参数列表为迭代数组的索引和数组项,最后在调用原生的方法并传入解析转义的参数列表

十、Watcher类的属性定义

Watcher类

需要注意repalce是保存了迭代数组的key名称集合,来更新替换循环dom的插值表达式

十一、Watcher动态批量删除DOM方法

动态批量删除dom

该方法是配合v-for指令使用,注意需要在删除旧dom的同时,需要维护ZVue的render映射表

十二、Watcher动态批量添加DOM方法

动态批量添加dom

该方法是配合v-for指令使用,注意需要先从template映射表中读取到dom的字符串模板,在动态组装成dom节点,并根据新数组的长度循环生成新dom,而这些新dom此刻并没有被ZVue劫持属性和解析指令,所以在循环结束后需要同步劫持新的属性值和解析新dom的指令

其中动态根据dom字符串来正则解析组装dom的方法定义为

getTemplateDom方法

十三、Watcher动态更新DOM方法

dom更新方法

需要判断当数据属于数组类型,而数组类型的更新需要动态删除、添加、维护render映射表、重新劫持和解析指令,所以单独封装出数组的更新方法为

dom数组更新方法

十四、变异数组类的属性定义

变异数组属性

需要注意到如splice方法的底层实现也许使用了中间数组来桥接原数组,这样会导致重复执行回调函数,而变异数组的声明是在ZVue的构造函数或者set钩子里,所以才可以带上具名的回调函数,为此,需要判断如果是变异数组内部构造了另一个变异数组,则不会带上回调函数的情况

十五、变异数组切面调用回调函数

切面调用回调函数

这样调用变异数组的push、pop、splice方法都会在执行完原生数组的这些方法之后调用我们的回调函数,而这个回调函数要做的,就是更新ZVue中对应的数组,并且是数组本身字面量的直接覆盖,才能让数组本身发生变化,从而进入其set钩子里去通知相应的watcher对象刷新其视图片段

下面是我们ZVue回调函数的定义

回调函数-1
回调函数-2

我们只希望数组本身被注入set和get钩子,为此我们将data的名称当做变量作为回调函数的名称,使用eval来声明具名回调函数,并为该回调函数注入静态变量—ZVue实例指针,这样就可以在回调函数内部访问到ZVue的属性

十六、案例页面引用ZVue框架

下面我们编写案例页面来引用ZVue

案例页面代码-1

页面html代码部分和Vue非常相似~可以写不同的指令和插值表达式

下面我们在页面内引入ZVue并实例

案例页面代码-2
案例页面代码-3

可以看到,页面的js代码部分和原生Vue非常相似~其中我们用随机数来作为数组项来模拟测试增删改查

十七、案例结果的展示分析

页面渲染结果

观察渲染的dom结构为

页面的dom渲染结构

可以看到渲染后dom结构和Vue非常接近。下面我们输入input或者点击增加减少按钮来测试页面的双向绑定

测试双向绑定

下面我们来测试数组的变化,随意push三个随机值到数组中,观察数组的set和render映射表

push三个数组项
新数组项被劫持
新数组项加入映射表

可以看到新加入的数组项都被劫持和映射到render表中,下面我们点击局部更新按钮来更新数组的第二项

局部更新数组的第二项

下面我们pop出四个值,得到

pop四次

下面我们splice删除数组第二项,得到

splice删除数组第二项

最后我们更新整个数组字面量为一个长度为4的新数组,得到

更新整个数组字面量
更新整个数组后的data和render

可以看到我们更新覆盖完整个数组字面量后,data中list里又动态注入了新数据的set钩子且删除了旧数据的set钩子,render映射表也动态维护了新节点对应的watcher对象且删除了旧节点的watcher对象

十八、后记

写完ZVue,主要目的是从原理出发用原生JS来实现数据的劫持和指令的解析,以及数组的动态CURD映射到视图的动态变化,并没有加入虚拟dom的diff算法,同时也是向Vue的作者尤雨溪致敬!

案例代码见:原生JS实现轻量Vue+指令解析+变异数组监听

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容