Vue中fragment.js使用方法详解


Posted in Javascript onMarch 09, 2017

 大部分内容源自 jQuery,当然,同时也参考了 component/domify ,如果有兴趣去这翻阅原始的代码,可以到 jQuery 中查找 wrapMap;至于 domify,直接到 github 搜索即可,相关项目类容很少,直接看 index.js 就行了。

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 相关文章推荐
发布一个基于javascript的动画类 Fx.js
Nov 05 Javascript
window.open打开页面居中显示的示例代码
Dec 27 Javascript
如何将php数组或者对象传递给javascript
Mar 20 Javascript
对比分析AngularJS中的$http.post与jQuery.post的区别
Feb 27 Javascript
Bootstrap中文本框的宽度变窄并且加入一副验证码图片的实现方法
Jun 23 Javascript
js 上传文件预览的简单实例
Aug 16 Javascript
jQuery实现字符串全部替换的方法
Dec 12 Javascript
echarts实现词云自定义形状的示例代码
Feb 20 Javascript
js中async函数结合promise的小案例浅析
Apr 14 Javascript
jQuery提示框插件SweetAlert用法分析
Aug 05 jQuery
nginx部署多个vue项目的方法示例
Sep 06 Javascript
vue中选中多个选项并且改变选中的样式的实例代码
Sep 16 Javascript
JavaScript编写一个贪吃蛇游戏
Mar 09 #Javascript
canvas压缩图片转换成base64格式输出文件流
Mar 09 #Javascript
Bootstrap笔记之缩略图、警告框实例详解
Mar 09 #Javascript
Bootstrap学习笔记之进度条、媒体对象实例详解
Mar 09 #Javascript
bootstrap实现的自适应页面简单应用示例
Mar 09 #Javascript
js实现3D图片环展示效果
Mar 09 #Javascript
Vue监听数组变化源码解析
Mar 09 #Javascript
You might like
基于asp+ajax和数据库驱动的二级联动菜单
2010/05/06 PHP
PHP获取表单textarea数据中的换行问题
2010/09/10 PHP
php文件缓存方法总结
2016/03/16 PHP
浅谈PHP无限极分类原理
2019/03/14 PHP
麦鸡的TAB切换功能结合了javascript和css
2007/12/17 Javascript
完美解决AJAX跨域问题
2013/11/01 Javascript
获得Javascript对象属性个数的示例代码
2013/11/21 Javascript
jquery获得option的值和对option进行操作
2013/12/13 Javascript
jQuery插件开发的两种方法及$.fn.extend的详解
2014/01/16 Javascript
js返回前一页刷新本页重载页面
2014/07/29 Javascript
JS判断是否360安全浏览器极速内核的方法
2015/01/29 Javascript
后台获取ZTREE选中节点的方法
2015/02/12 Javascript
Javascript基础之数组的使用
2016/05/13 Javascript
JS 组件系列之Bootstrap Table的冻结列功能彻底解决高度问题
2017/06/30 Javascript
Vue学习笔记进阶篇之单元素过度
2017/07/19 Javascript
webpack3+React 的配置全解
2017/08/21 Javascript
jQuery菜单实例(全选,反选,取消)
2017/08/28 jQuery
vue获取dom元素注意事项
2017/12/28 Javascript
vue中如何让子组件修改父组件数据
2018/06/14 Javascript
vue + typescript + 极验登录验证的实现方法
2019/06/27 Javascript
微信小程序实现张图片合成为一张并下载
2019/07/16 Javascript
vue+element搭建后台小总结 el-dropdown下拉功能
2020/04/10 Javascript
Vue.js计算机属性computed和methods方法详解
2019/10/12 Javascript
关于angular浏览器兼容性问题的解决方案
2020/07/26 Javascript
在Vue中使用Echarts可视化库的完整步骤记录
2020/11/18 Vue.js
[07:49]2014DOTA2国际邀请赛 Newbee夺冠后采访xiao8坦言奖金会上交
2014/07/23 DOTA
[02:22]2018DOTA2亚洲邀请赛VG赛前采访
2018/04/03 DOTA
[00:10]DOTA2全国高校联赛 以DOTA2会友
2018/05/30 DOTA
python使用beautifulsoup从爱奇艺网抓取视频播放
2014/01/23 Python
opencv设置采集视频分辨率方式
2019/12/10 Python
python反扒机制的5种解决方法
2021/02/06 Python
新加坡最受追捧的体验平台:Hapz
2018/01/01 全球购物
沃达丰英国有限公司:Vodafone英国
2019/04/16 全球购物
美国滑板店:Tactics
2020/11/08 全球购物
学校党委副书记个人对照检查材料思想汇报
2014/09/28 职场文书
win10+RTX3050ti+TensorFlow+cudn+cudnn配置深度学习环境的方法
2022/06/25 Servers