内容简介:在关于DOM操作就不得不提到一个js库——JQuery。JQuery是成也DOM(强大的选择器,链式操作方式)败也DOM(数据绑定取代了DOM操作)。 业务代码中嵌入大量的DOM操作会带来一些问题:1.作用域。DOM操作没有作用域,也就是说可以被任何代码操作,这样导致变化不可追溯,出现问题难以调试。虽然shadow DOM具有一定的作用域,但其它代码也是可以操作的。 2.性能。频繁或大量地操作DOM通常容易引起渲染性能问题,原生操作DOM的方式优化起来需要一定的经验和技巧,所以容易导致不同水平的开发者写出性
在 《抛开 Vue、React、JQuery 这类第三方js,我们该怎么写代码?》 文章中提到了我不使用任何第三方js库来开发项目的经历。 从零开发项目确实很有挑战性,开发中碰到了一些比较麻烦的问题,这篇文章就来记录一下我在封闭DOM操作时碰到的问题以及解决方式。
主流框架与数据绑定
关于DOM操作就不得不提到一个js库——JQuery。JQuery是成也DOM(强大的选择器,链式操作方式)败也DOM(数据绑定取代了DOM操作)。 业务代码中嵌入大量的DOM操作会带来一些问题:
1.作用域。DOM操作没有作用域,也就是说可以被任何代码操作,这样导致变化不可追溯,出现问题难以调试。虽然shadow DOM具有一定的作用域,但其它代码也是可以操作的。 2.性能。频繁或大量地操作DOM通常容易引起渲染性能问题,原生操作DOM的方式优化起来需要一定的经验和技巧,所以容易导致不同水平的开发者写出性能不同的代码。 3.耦合度。JavaScript逻辑和DOM操作混合的代码耦合性很高,可读性低且难以测试。
所以封装DOM操作是必要的,借鉴现有的主流视图框架思想,可以采用数据绑定。 即建立一个数据模型,通过修改数据对象属性来操作视图。 数据绑定的实现形式主要有3种:
脏值检测
脏值检测的实现原理是建立一个待检测队列,在解析视图模板的时候,将需要进行绑定的数据模型属性放入队列中。代表框架:AngularJS。 在需要检测的时候遍历队列,当属性发生变化时修改视图。 那么什么时候进行检测呢? 大致可分为两类
- 同步操作,比如组件实例化的时候。
- 异步操作,包括ajax请求、事件监听、setTimeout、setInterval等。
这种方式缺陷很明显
- 需要对所有的可能引起数据变化的操作进行封装,而且在编写业务代码的时候必须使用封装后的函数。
- 每次检测会遍历整个队列,随着绑定属性增多,性能会受到影响。
状态提交
数据模型修改时(后),调用函数来触发视图修改。代表框架:React。 这种方式在进行批量操作的时候非常有优势,这就和 SQL 数据库中使用事务来提交批量操作有些类似。 缺陷也比较明显,就是每次修改数据都要进行提交,代码写起来略嫌麻烦。
数据劫持
数据劫持就监听数据模型属性的变动,然后触发对应的视图修改。代表框架:Vue。
可以通过 Object.defineProperty
或者在不考虑兼容的情况下使用 Proxy
。
但是在处理数组数据的时候有一些问题:调用数组函数如 push
、 pop
等不会触发属性监听事件。
所以需要一些hack手段将这些函数进行封装。
实现数据绑定
选择
个人的编程习惯比较偏向于“onDemand”,在编写代码的时候的体现为按需编写和调用代码,在编译后的代码中喜欢按需加载代码。
既然如此,AngularJS那种监听属性全部遍历的粗放做法肯定不是我的首选。
然后手动提交更新的方式一来会增加代码来进行提交操作,另一方面也容易忘记提交导致视图不更新产生bug,所以最后的选择只剩下数据劫持了。
思路
如果按照Vue的实现过程,需要解析视图模板,然后建立vdom树,同时对于需要绑定的数据进行监听,然后通过操作vdom树来更新视图。
鉴于项目本身并不复杂,而且也没有必要完全照搬其实现思路,所以精简一下实现思路:
- “解析”视图模板。
- 对需要绑定的数据进行监听。
- 在监听函数中执行对应的DOM操作。
“解析”模板
一般来说“解析”这种操作是会将原有的代码或数据进行转化,比如“词法解析”就会把源码转化成一个一个的token。
而这里“解析”模板的目的只是为了识别字符串中的需要数据绑定的语法(我们暂且称之“指令”)。所以可以在实例化之后直接使用选择器来进行操作。
比如要进行文本属性的绑定,使用了 x-bind
指令,那么我们可以直接在shadowDOM中进行查找
this.shadowRoot.querySelectorAll('[x-bind]')
找到这些DOM元素之后,可以通过 getAttribute('x-bind')
来获取需要绑定的属性。
数据监听
在建立数据监听之前我们需要建立一个数据模型,用来和视图建立映射关系,即当我们修改这个数据模型的时候能同步到视图上。
假设我们的数据模型变量名为 state
。然后通过 Object.defineProperty(this.state, 'xxx', ...)
对state变量的指定属性进行监听。
这时候需要注意的是,一个属性可能和多个视图元素进行绑定,但是我们监听数据属性只能编写一次,所以需要对监听属性建立一个队列,当数据模型数据发生变化时,遍历队列中的执行函数并调用。
操作DOM
在执行函数中我们传入其绑定的DOM,然后执行函数根据各个指令的功能来操作DOM了。
比如 x-bind
指令的执行逻辑会是这样:
this.textContent = undefined === value ? '' : value;
当然到这一步还只能算完成了一半,因为只实现了 数据 ==> 视图
的操作, 视图 ==> 数据
还没有完成。因此我们需要进行事件绑定。
事件绑定是不是也可以用指令的方式呢?比如绑定单击事件:
<button x-click="click">click me</button>
这样能满足一部分业务场景,但是更多的时候我们不仅要触发事件,而且还要 传入参数 。而被传入的参数有可能是变量名,也有可能是常量。比如:
<button x-click="click(name, true)">click me</button>
name
为数据模型上的属性名,而 true
为一个布尔值常量。所以需要对事件绑定进行简单的语法解析,并在调用对应函数的时候传入正确的参数。
优化
数据绑定
基于上面的实现,还可以将表单元素的事件绑定和数据绑定封装一下,实现双向数据绑定,这样能进一步减少业务代码。
假定这个指令的名称为 x-model
。那么在“解析”模板的时候要编写一个执行函数来同步DOM的 value
值和模型数据属性。同时建立事件监听来将数据模型属性同步到DOM中。
变化检测
因为数据监听是在数据被赋值的时候就会触发,为了减少更新DOM,可以在调用DOM更新的执行函数时进行判断:只有属性发生变化时才触发DOM更新。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 无依赖开发之封装DOM
- 通过剔除上下文依赖减弱封装的耦合性
- 封装JDBC—非框架开发必备的封装类
- SpringBlade 2.3.2 发布,增加 OSS 封装及单元测试封装
- SpringBlade 2.3.2 发布,增加 OSS 封装及单元测试封装
- 浅析依赖倒转、控制反转、IoC 容器、依赖注入。
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
游戏化思维
[美] 凯文·韦巴赫(Kevin Werbach)、[美] 丹·亨特(Dan Hunter) / 周逵、王晓丹 / 浙江人民出版社 / 2014-4 / 36.90
[内容简介] ●本书由开设了全世界第一个游戏化课程的沃顿商学院副教授凯文·韦巴赫和丹·亨特所著,第一次全面系统地介绍游戏化的理论,阐述了如何将游戏的理念应用到商业实践中。 ●作者指出,在商业竞争日益激烈的今天,传统的激励方式渐渐失效,未来的管理将更多地建立在员工和消费者的内在动机和自我激励上。这些制作精良、设计巧妙的游戏建立在多年来对人类动机和人类心理的研究基础之上,可以最大限度地激发......一起来看看 《游戏化思维》 这本书的介绍吧!