Vue中fragment.js使用方法小结


Posted in Javascript onFebruary 17, 2020

createDocumentFragment

如果要在一个节点上一次性插入多个元素怎么办,比如说一次插入 10000 个节点?

最简单粗暴的方式就是:

var parent = document.getElementById(`'parent'`);

for`(`var i = 0; i < 10000; i++) {

var child = document.createElement(`'div'`);

var text = document.createTextNode(`'' + i);`

child.appendChild(text);

parent.appendChild(child);

}

不过众所周知的原因,对 DOM 反复操作会导致页面重绘、回流,效率非常低,而且页面可能会被卡死,这段代码基本是没人用的。

如果分段来进行 DOM 操作呢,这样就能避免卡死页面了,js 忍者秘籍里面提到过可以用 setTimeout 来改进:

var i = 0, max = 10000;

setTimeout(`function addNodes() {`

for`(`var step = i + 500; i < step; i++) {

var child = document.createElement(`'div'`);

child.appendChild(document.createTextNode(`'' + i));`

div.appendChild(child);

}

if`(i < max) {`

setTimeout(addNodes, 0);

}

}, 0);

当然,更多能想到的方式应该是,在内存中直接操作节点,所有节点都凑在一起之后再跟 DOM 树进行交互,把所有节点都串在一个 div 上,然后再把 div 挂到 DOM 树上:

var parent = document.getElementById(`'parent'`);

var div = document.createElement(`'div'`);

for`(`var i = 0; i < 10000; i++) {

var child = document.createElement(`'div'`);

var text = document.createTextNode(`'' + i);`

child.appendChild(text);

div.appendChild(child);

}

parent.appendChild(div);

如上,只跟 DOM 树交互一次,性能方面肯定是大有改善的,不过额外插入了一个 div,如果说不是跟div之类的节点进行交互呢,比如在 table 中插入 th、td?

这时候,createDocumentFragment 就该出马了,翻译过来叫“文档片段”,按MDN的描述:

DocumentFragments 是一些 DOM 节点。它们不是 DOM 树的一部分。通常的使用场景是创建一个文档片段,然后将创建的 DOM 元素插入到文档片段中,最后把文档片段插入到 DOM 树中。在 DOM 树中,文档片段会被替换为它所有的子元素。

因为文档片段存在与内存中,并不在 DOM 树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段 document fragments 通常会起到优化性能的作用。

简单来说,就是上面一个例子的不需要 div 中转版本,插入的时候,直接用其子元素替换其本身,非常完美。

虽然说,“好用的都不通用”(特别是针对某公司浏览器),不过这个好用的东西,甚至连 IE6 都支持。

具体代码大概就长这样:

var parent = document.getElementById(`'parent'`);

var frag = document.createDocumentFragment();

for`(`var i = 0; i < 10000; i++) {

var child = document.createElement(`'div'`);

var text = document.createTextNode(`'' + i);`

child.appendChild(text);

frag.appendChild(child);

}

parent.appendChild(frag);

具体性能方面的测试,有兴趣的可以把所有代码都跑一遍。

innerHTML

把一长串字符串转换为对应的 DOM 节点,正常而言,首先想到的肯定是 innerHTML。大概流程就是,先创建一个 div 节点,然后 div.innerHTML = str,根据需要把 div 的 children 取出来放到该放的地方去,div 本身给扔了。

如果想单独生成一个 th 节点呢?

试试上面的流程:

var div = document.createElement(`'div'`);

div.innerHTML = '<th>xxx</th>'`;`

console.log(div);

实际输出是(chrome 下):

<`div>xxx</div`>

并没有得到想要的:

<`div><th>xxx</th></div`>

对于这样的结果是可以理解的,毕竟一个 th 放到 div 里面,怎么看都不对,直接把外围的标签去掉,内容扔到 div 里面也是相当智能的。

不过架不住,有时候就是要获取一个 th 节点。

其实也好办,写全了不就得了:

var node = document.createElement(`'div'`);

node.innerHTML = '<table><tbody><tr><th>xxx</th></tr></tbody></table>'`;`

// 把外面的几层皮扒掉就是想要的 th 了

var depth = 3;

while`(depth--) {`

node = node.lastChild;

}

console.log(node.firstChild);

可以看出,结果正是所想要的。

fragment.js

// 需要单独处理的一些特殊节点
var map = {
 legend : [1, '<fieldset>', '</fieldset>'],
 tr : [2, '<table><tbody>', '</tbody></table>'],
 col : [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'],
 
 _default : [0, '', '']
};
map.td = map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>'];
map.option = map.optgroup = [1, '<select multiple="multiple">', '</select>'];
map.thead = map.tbody = map.colgroup = map.caption = map.tfoot = [1, '<table>', '</table>']
map.text = map.circle = map.ellipse = map.line = map.path = map.polygon = map.polyline = map.rect = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>'];
 
var TAG_RE = /<([\w:]+)/;
 
module.exports = function(templateString) {
 var frag = document.createDocumentFragment(),
 m = TAG_RE.exec(templateString);
 // 单纯字符串的情况
 if(!m) {
 frag.appendChild(document.createTextNode(templateString);
 return frag;
 }
 
 var tag = m[1],
 wrap = map[tag] || map._default,
 depth = wrap[0],
 prefix = wrap[1],
 suffix = wrap[2],
 node = document.createElement('div');
 // 拼接节点字符串
 node.innerHTML = prefix + templateString.trim() + suffix;
 // 去除外包裹层,只留字符串转化的节点
 while(depth--) node = node.lastChild;
 // 只有一个节点的情况
 if(node.firstChild === node.lastChild) { 
 frag.appendChild(node.firstChild);
 return frag;
 }
 // 多个节点,依序添加到 frag
 var child;
 while(child = node.firstChild) {
 frag.appendChild(child);
 }
 return frag;
}
Javascript 相关文章推荐
childNodes.length与children.length的区别
May 14 Javascript
EditPlus注册码生成器(js代码实现)
Mar 25 Javascript
JS判断客户端是手机还是PC的2个代码
Apr 12 Javascript
JS获取图片lowsrc属性的方法
Apr 01 Javascript
Javascript中的包装类型介绍
Apr 02 Javascript
Javascript 调用 ActionScript 的简单方法
Sep 22 Javascript
Bootstrap在线电子商务网站实战项目5
Oct 14 Javascript
在vue项目中使用element-ui的Upload上传组件的示例
Feb 08 Javascript
vue利用v-for嵌套输出多层对象,分别输出到个表的方法
Sep 07 Javascript
浅析vue插槽和作用域插槽的理解
Apr 22 Javascript
JQuery中的常用事件、对象属性与使用方法分析
Dec 23 jQuery
vue 解决uglifyjs-webpack-plugin打包出现报错的问题
Aug 04 Javascript
javascript实现倒计时效果
Feb 17 #Javascript
JavaScript将数组转换为链表的方法
Feb 16 #Javascript
javascript canvas API内容整理
Feb 16 #Javascript
vue props 单项数据流实例分享
Feb 16 #Javascript
卸载vue2.0并升级vue_cli3.0的实例讲解
Feb 16 #Javascript
vue中axios防止多次触发终止多次请求的示例代码(防抖)
Feb 16 #Javascript
Vue简单封装axios之解决post请求后端接收不到参数问题
Feb 16 #Javascript
You might like
PHP时间戳和日期相互转换操作实例小结
2018/12/18 PHP
JQuery 简便实现页面元素数据验证功能
2007/03/24 Javascript
JS鼠标事件大全 推荐收藏
2011/11/01 Javascript
js循环改变div颜色具体方法
2013/06/25 Javascript
只需一行代码,轻松实现一个在线编辑器
2013/11/12 Javascript
Javascript 拖拽的一些简单的应用(逐行分析代码,让你轻松了拖拽的原理)
2015/01/23 Javascript
JavaScript获取一个范围内日期的方法
2015/04/24 Javascript
jQuery+ajax实现文章点赞功能的方法
2015/12/31 Javascript
AngularJS验证信息框架的封装插件用法【w5cValidator扩展插件】
2016/11/03 Javascript
JS实现搜索框文字可删除功能
2016/12/28 Javascript
bootstrap侧边栏圆点导航
2017/01/11 Javascript
原生js实现商品放大镜效果
2017/01/12 Javascript
Node.js利用debug模块打印出调试日志的方法
2017/04/25 Javascript
vue-loader教程介绍
2017/06/14 Javascript
JavaScript闭包和回调详解
2017/08/09 Javascript
关于JS与jQuery中的文档加载问题
2017/08/22 jQuery
Angular实现下拉框模糊查询功能示例
2018/01/03 Javascript
Vue 设置axios请求格式为form-data的操作步骤
2019/10/29 Javascript
微信小程序实现上拉加载功能
2019/11/20 Javascript
javascript 内存模型实例详解
2020/04/18 Javascript
Python探索之修改Python搜索路径
2017/10/25 Python
python中dir()与__dict__属性的区别浅析
2018/12/10 Python
详细整理python 字符串(str)与列表(list)以及数组(array)之间的转换方法
2019/08/30 Python
用python打开摄像头并把图像传回qq邮箱(Pyinstaller打包)
2020/05/17 Python
浅谈Python3中print函数的换行
2020/08/05 Python
python利用文件时间批量重命名照片和视频
2021/02/09 Python
html5开发之viewport使用
2013/10/17 HTML / CSS
Stefania Mode美国:奢华设计师和时尚服装
2018/01/07 全球购物
音乐教学案例
2014/01/30 职场文书
乡镇一岗双责责任书
2015/01/29 职场文书
老公写给老婆的检讨书
2015/05/06 职场文书
运动会观后感
2015/06/09 职场文书
幼儿园开学温馨提示
2015/07/15 职场文书
go语言使用Casbin实现角色的权限控制
2021/06/26 Golang
JavaScript原型链详解
2021/11/07 Javascript
SQL CASE 表达式的具体使用
2022/03/21 SQL Server