We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
之前做过的项目在通讯录成员有1000个时,网页加载要30s才能加载完全。
想到可能的原因有两种
打开Network面板,刷新页面重新加载后发现接口的响应时间只有1.43秒,所以排除是接口响应时间过长导致的问题。
Network
排除掉接口响应时间长的问题后,用Performance面板分析是否是脚本执行时间过长导致的问题。
Performance
点击Record按钮后重新加载通讯录,待通讯录加载完毕后结束录制,查看。
Record
其中黄色部分是脚本执行的时间,发现脚本执行时间占了大部分。
至此,确定是脚本执行时间过长阻塞了浏览器渲染。
查看在此期间的函数调用栈,发现createNodeCallback函数执行时间长达29.75秒,而这个函数调用了数次apply方法和addDiyDom方法。
createNodeCallback
apply
addDiyDom
找到函数的定义位置,发现函数执行了1000次的循环,循环调用addDiyDom方法。如果setting.callback.onNodeCreated存在还会触发1000次NODECREATED事件。而这两项都是挂在setting,也就是用户的配置上。
setting.callback.onNodeCreated
NODECREATED
setting
由于项目的配置中没有onNodeCreated项,所以在代码中注释掉addDiyDom方法,再次用Performance面板分析
onNodeCreated
可以看到脚本的执行时长大大减少。
查看addDiyDom方法
该方法查找了页面上的DOM元素,并直接对页面上的DOM元素进行了修改;而在JS中对DOM的操作最是损耗性能。
至此,问题原因定位完毕,由于zTree插件连续调用1000次对页面上的dom节点直接进行修改的addDiyDom方法,造成了脚本执行时间过长,页面卡顿的性能问题。
zTree
查看源码发现createNodeCallback方法是在appendParentULDom方法之后调用,而appendParentULDom方法所做的正是把zTree插件生成的dom树插入到页面中。所以,用户定义的addDiyDom方法就只能对页面上的DOM直接进行操作了。
appendParentULDom
由于业务需求,需要在人员节点上增加两个按钮。可不可以在生成节点的时候就吧自定义按钮插入进去呢?这样就可以避免直接操作页面上DOM的性能问题
DOM
在官网的API中也没有找到可以在生成节点的时候把用户自定义的节点插入的方法。只好再翻一下源码。
找到生成dom节点的位置
在这些方法中一个个找可以插入节点的地方,zTree的节点结构是固定的,外层是一个li元素,第一个子节点span元素用作(展开/收起)的按钮,接下来是一个a标签。a标签中第一个元素是节点名称前的图标,第二个是展示用户配置数据的节点名称data[i].name
li
span
a
data[i].name
源码中第一个方法生成li元素的开始标签部分;第二个方法生成span.switch元素;第三个方法中查询用户配置,如果用户配置了setting.check.enable为true,则会在a元素前插入一个选择框;第四个方法生成a标签的开始部分;第五个方法从名称上看是向a元素前插入节点,前一个方法刚好生成了a标签的开始部分,如果这个方法可以插入节点,问题就能解决。似乎看到了一丝希望。
setting.check.enable为true
断点打到getInnerBeforeA内部
getInnerBeforeA
啊嘞嘞,这个函数要想起作用,必须_init.innerBeforeA数组中有方法才行,那有什么办法可以向_init.innerBeforeA数组push函数?
_init.innerBeforeA
push
在文件中查找innerBeforeA
innerBeforeA
看来要想往_init.innerBeforeA数组push函数得调用data.addInnerBeforeA才行,那data这个对象有没有暴露给用户呢?
data.addInnerBeforeA
data
通过VS Code查找这个对象的引用
VS Code
果然是有暴露给用户的~
这样就可以在zTree初始化前自定义一个方法,将我需要定制的节点插入a元素中。
在该函数中打断点查看调用栈,找到传入的三个参数setting、node、html。setting是用户的配置。用户传入的对应数据上的属性会挂在node对象上。html是前面几个方法生成的html数组,正好到a标签的开始部分(最后zTree会调用.join方法转成字符串,再调用jquery的append方法,插入到页面中)。这时只要向a标签中push进我需要定制的节点就可以了。
node
html
$.fn.zTree._z.data.addInnerBeforeA(function (setting, node, html) { if (setting.view.enableDiyDom && node.dataType === 'orgMember') { html.push('<button class="mem-btn-video" btntype="yy" title="云眼"></button>'); } });
由于该方法可以拿到用户配置,所以可以自定义配置。在这里自定义配置项:setting.view.enableDiyDom。只有当它为true,且节点类型是成员(orgMember)才会添加自定义按钮。如果页面中多个地方用到了zTree插件,那么必须用自定义配置项去做限制,否则自定义的addInnerBeforeA方法会污染所有用到zTree插件的地方。
setting.view.enableDiyDom
true
addInnerBeforeA
添加之后查看元素面板,自定义的元素在其中,且是a元素的第一个子元素。
打开Performance面板进行分析
可以看到脚本的执行的时长仍在1秒左右,至此,问题解决
zTree插件提供的addDiyDom方法在大数据量的加载时会导致显著的性能问题,因为该方法添加自定义节点时只能直接对页面上的DOM直接进行操作。
如果用户配置了onNodeCreated方法,且在该方法中进行了DOM操作,在大数据量的加载中也会导致显著的性能问题,原因与addDiyDom方法相同。
添加自定义节点可以使用zTree的内部方法
addInnerAfterA
addAfterA
如果是在鼠标悬浮时才显示用户的自定义元素,可使用setting.view.addHoverDom和setting.view.removeHoverDom配置项
setting.view.addHoverDom
setting.view.removeHoverDom
加载1000个节点,js脚本的执行时间已经达到了1秒,如果要求加载的数据更多,或有进一步的优化要求就只能考虑分页加载了
The text was updated successfully, but these errors were encountered:
No branches or pull requests
背景
之前做过的项目在通讯录成员有1000个时,网页加载要30s才能加载完全。
想到可能的原因有两种
定位问题原因
打开
Network
面板,刷新页面重新加载后发现接口的响应时间只有1.43秒,所以排除是接口响应时间过长导致的问题。排除掉接口响应时间长的问题后,用
Performance
面板分析是否是脚本执行时间过长导致的问题。点击
Record
按钮后重新加载通讯录,待通讯录加载完毕后结束录制,查看。其中黄色部分是脚本执行的时间,发现脚本执行时间占了大部分。
至此,确定是脚本执行时间过长阻塞了浏览器渲染。
查看在此期间的函数调用栈,发现
createNodeCallback
函数执行时间长达29.75秒,而这个函数调用了数次apply
方法和addDiyDom
方法。找到函数的定义位置,发现函数执行了1000次的循环,循环调用
addDiyDom
方法。如果setting.callback.onNodeCreated
存在还会触发1000次NODECREATED
事件。而这两项都是挂在setting
,也就是用户的配置上。由于项目的配置中没有
onNodeCreated
项,所以在代码中注释掉addDiyDom
方法,再次用Performance
面板分析可以看到脚本的执行时长大大减少。
查看
addDiyDom
方法该方法查找了页面上的DOM元素,并直接对页面上的DOM元素进行了修改;而在JS中对DOM的操作最是损耗性能。
至此,问题原因定位完毕,由于
zTree
插件连续调用1000次对页面上的dom节点直接进行修改的addDiyDom
方法,造成了脚本执行时间过长,页面卡顿的性能问题。寻找解决方式
查看源码发现
createNodeCallback
方法是在appendParentULDom
方法之后调用,而appendParentULDom
方法所做的正是把zTree插件生成的dom树插入到页面中。所以,用户定义的addDiyDom
方法就只能对页面上的DOM直接进行操作了。由于业务需求,需要在人员节点上增加两个按钮。可不可以在生成节点的时候就吧自定义按钮插入进去呢?这样就可以避免直接操作页面上
DOM
的性能问题在官网的API中也没有找到可以在生成节点的时候把用户自定义的节点插入的方法。只好再翻一下源码。
找到生成dom节点的位置
在这些方法中一个个找可以插入节点的地方,
zTree
的节点结构是固定的,外层是一个li
元素,第一个子节点span
元素用作(展开/收起)的按钮,接下来是一个a
标签。a
标签中第一个元素是节点名称前的图标,第二个是展示用户配置数据的节点名称data[i].name
源码中第一个方法生成
li
元素的开始标签部分;第二个方法生成span.switch元素;第三个方法中查询用户配置,如果用户配置了setting.check.enable为true
,则会在a元素前插入一个选择框;第四个方法生成a
标签的开始部分;第五个方法从名称上看是向a
元素前插入节点,前一个方法刚好生成了a
标签的开始部分,如果这个方法可以插入节点,问题就能解决。似乎看到了一丝希望。断点打到
getInnerBeforeA
内部啊嘞嘞,这个函数要想起作用,必须
_init.innerBeforeA
数组中有方法才行,那有什么办法可以向_init.innerBeforeA
数组push
函数?在文件中查找
innerBeforeA
看来要想往
_init.innerBeforeA
数组push
函数得调用data.addInnerBeforeA
才行,那data
这个对象有没有暴露给用户呢?通过
VS Code
查找这个对象的引用果然是有暴露给用户的~
这样就可以在
zTree
初始化前自定义一个方法,将我需要定制的节点插入a
元素中。在该函数中打断点查看调用栈,找到传入的三个参数
setting
、node
、html
。setting
是用户的配置。用户传入的对应数据上的属性会挂在node
对象上。html
是前面几个方法生成的html
数组,正好到a
标签的开始部分(最后zTree会调用.join方法转成字符串,再调用jquery的append方法,插入到页面中)。这时只要向a
标签中push
进我需要定制的节点就可以了。由于该方法可以拿到用户配置,所以可以自定义配置。在这里自定义配置项:
setting.view.enableDiyDom
。只有当它为true
,且节点类型是成员(orgMember)才会添加自定义按钮。如果页面中多个地方用到了zTree
插件,那么必须用自定义配置项去做限制,否则自定义的addInnerBeforeA
方法会污染所有用到zTree
插件的地方。添加之后查看元素面板,自定义的元素在其中,且是
a
元素的第一个子元素。打开
Performance
面板进行分析可以看到脚本的执行的时长仍在1秒左右,至此,问题解决
总结
zTree
插件提供的addDiyDom
方法在大数据量的加载时会导致显著的性能问题,因为该方法添加自定义节点时只能直接对页面上的DOM
直接进行操作。如果用户配置了
onNodeCreated
方法,且在该方法中进行了DOM操作,在大数据量的加载中也会导致显著的性能问题,原因与addDiyDom
方法相同。添加自定义节点可以使用
zTree
的内部方法addInnerBeforeA
插入的节点作为a
元素的第一个子元素addInnerAfterA
插入的节点作为a
元素的最后一个子元素addAfterA
插入的节点作为a
元素后的兄弟元素如果是在鼠标悬浮时才显示用户的自定义元素,可使用
setting.view.addHoverDom
和setting.view.removeHoverDom
配置项加载1000个节点,js脚本的执行时间已经达到了1秒,如果要求加载的数据更多,或有进一步的优化要求就只能考虑分页加载了
The text was updated successfully, but these errors were encountered: