日常开发的注意事项,针对大屏开发的遇到的常见问题做的一次简单分享……
针对大屏开发的遇到的常见问题做的一次简单培训,内容与演示效果在 realization 项目里的 demo 目录(暂不开源)。
一 销毁定时器
有创建定时器的行为,就该有销毁定时器的行为,setInterval 与 clearInterval 需要成对出现。否则组件销毁时定时器依然存在,继续运行着对应的方法,占用内存;而且当组件重建时,定时器再次激活,导致定时器内的行为重复发生。
示例代码:
setup() {
const doSetInterval = () => {
console.log(new Date())
}
let interval
onMounted(() => {
interval = setInterval(doSetInterval, 10000)
})
onUnmounted(() => {
clearInterval(interval)
})
return {}
}
二 事件委托与销毁自定义事件
添加到页面上的事件处理程序越多,内存中的对象就越多,造成页面渲染和就绪时间延迟、事件不灵敏等性能问题。可以通过“事件委托”和适时移除事件的方式减少内存消耗,优化性能。
1 事件委托
利用事件冒泡的机制,指定一个事件处理程序来管理同一类型的所有事件,一般情况下就是把子元素的相同的事件都挂到父元素上,通过单个父元素代理多个子元素的事件。
示例代码:
<ul id="links">
<li id="doSomething">doSomething</li>
<li id="goSomewhere">goSomewhere</li>
<li id="sayHi">say hi</li>
</ul>
var list = document.getElementById('links');
EventUtil.addHandler(list, 'click', function(e){
e = EventUtil.getEvent(e);
var target = EventUtil.getTarget(e);
switch(target.id) {
case 'doSomething':
document.title = 'the document’s title was changed';
break;
case 'goSomewhere':
location.href = 'http://www.baidu.com';
break;
case 'sayHi':
alert('hi');
break;
}
})
2 销毁自定义事件
Vue 原生的事件会随着组件的卸载而自动销毁,但是通过 addEventListener 或类似方式自定义的事件并不会随着组件的卸载而销毁,需要手动销毁,否则事件会继续占用着内存。
示例代码:
setup() {
const eventDom = ref(null)
const handleClick = (e) => {
e.target.style.color = 'red'
}
onMounted(() => {
if(eventDom.value) eventDom.value.addEventListener('click', handleClick)
})
onUnmounted(() => {
if(eventDom.value) eventDom.value.removeEventListener('click', handleClick)
})
return {}
}
三 z-index
z-index 不是越大越好,不要动不动就设置为 999999!z-index 是 兄弟之间的比较,如果父级容器的z-index 小,那设置再高也遮盖不住堂兄弟的节点,详细说明可以参考 层叠样式表。realization 项目中有简单的演示。
以前看过一篇 blog,说 z-index 取 -1、0、1 三个值就可以满足所有情况,当然我们对 dom 的设计可能比不过人家的设计,但我觉得取到 10 已经足够用了。
| 取值 | 说明 |
|---|---|
| auto | 盒子不会创建一个新的本地堆叠上下文。在当前堆叠上下文中生成的盒子的堆叠层级和父级盒子相同。 |
| 整型数字 | 生成的盒子在当前堆叠上下文中的堆叠层级。此盒子也会创建一个堆叠层级为 0 的本地堆叠上下文。这意味着后代(元素)的 z-indexes 不与此元素的外部元素的 z-indexes 进行对比。 |
四 防抖与节流
当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。这防止事件的高频触发,避免消耗大量又不必要的性能。
代码示例:
setup() {
function debounce(fn,delay){
let timer = null //借助闭包
return function() {
if(timer){
clearTimeout(timer)
}
timer = setTimeout(fn,delay) // 简化写法
}
}
const consoleLog = () => {
console.log(e.pageX)
}
onMounted(() => {
window.addEventListener('keyup', debounce(consoleLog, 100))
})
onUnmounted(() => {
window.removeEventListener('keyup', debounce(consoleLog, 100))
})
return {}
}
节流:当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
function throttle(fn,delay){
let valid = true
return function() {
if(!valid){
//休息时间
return false
}
// 工作时间
valid = false
setTimeout(() => {
fn()
valid = true;
}, delay)
}
}
五 滚动距离
开发大屏时,对于滚动元素,比如 parent.scrollLeft += 1, 每次滚动 1px ,但通常我们会缩小分辨率来开发,如果缩小为原分辨率的 50%,那么 parent.scrollLeft 自增的是 0.5,这种情况下浏览器对小于 1 的距离默认设为 0,所以实际是看不到滚动效果的,这时可以用 parent.scrollLeft += 2或更大的值来调试。
六 Eventloop
七 销毁 echarts
有 echartsInstance(echarts 实例) 的地方,必有 echartsInstance.dispose()。否则来回切换组件实例会产生不必要的错误渲染。
let chart
onMounted(() => {
chart = echarts.init(document.getElementById('id'))
})
onUnmounted(() => {
chart.dispose()
})