bug:

vue项目中,窗口大小不变(排除window监听resize),侧边菜单栏折叠展开,导致右边内容区域宽度大小变化,echarts图表的宽度超出或者小于操作后的 原图表容器区域的宽度;

常规解决办法的是监听窗口的变化window.addEventListener(“resize”),但当前bug是窗口不变,内部元素的宽度变化,所有window监听不生效。

在这里插入图片描述
抽取的调用echarts图表resize方法

	methods:{
		//调用echarts的resize()
		onResize(){
                this.charts.messagePie && this.charts.messagePie.resize();
                this.charts.onLineChart && this.charts.onLineChart.resize();
                this.charts.activeUserLine && this.charts.activeUserLine.resize();
		}
	}

思考:

1.是否可以类似监听窗口的resize一样来监听元素,发现元素没有这个resize;
2.监听侧边栏折叠的状态来调用抽取的onResize()方法,发现不生效;

		watch:{
		    //监听菜单栏折叠
	        collapsed() {
	       		 this.onResize();
	        },
        }

3.网上查了资料说是可以使用MutationObserver来监听侧边栏元素的属性变化,测试发现仍然不可行,此时过渡动画没有完成(第4点),加个setTimeout(onResize(),200)即可;

我查到的:MutationObserver不能监听css变化,这里只能监听内联属性style变化。(如果有误,请告知,谢谢!)

 let MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
            let element = document.querySelector('.ant-layout-sider');//侧边栏元素
            this.observer = new MutationObserver(()=>{
				setTimeout(()=>{this.onResize()},200)
			});
            this.observer.observe(element, {
                attributes: true,
                attributeFilter: ['style'],
                attributeOldValue: true,
            });

4.无意间发现侧边栏操作时有个transition:all 0.2s;过渡属性,是不是可以过渡完再执行? 发现可行,但是设置定时器0.2s着实有点不可取,继续查资料;

	 //监听菜单栏折叠,过渡动作结束后执行
        collapsed(nVal) {
            //侧边栏折叠时有0.2s过渡动画执行时间,过渡动画结束后再执行onResize
             setTimeout(() => {
                 this.onResize();
             }, 200);
        },

5.根据第4点,可以监听元素过渡完 transitionend / webkitTransitionEnd,但是会触发多次因为过渡css属性有多个;

 			let _sider = document.querySelector('.app-sider');
            _sider.addEventListener('transitionend',this.onResize,false);

可以通过触发元素是否为监听元素的width和防抖策略来过滤,然后在beforeDestory生命周期时取消监听;

removeEventListener在移除监听时,回调函数必须使用外部函数,类似下面的this.siderTransitionendListener,而不能是function(){ this.siderTransitionendListener },否则无法移除监听;

		mounted(){
			let _sider = document.querySelector('.app-sider'); //侧边栏
	          	_sider.addEventListener('transitionend',this.siderTransitionendListener,false);
	           this.$once('hook:beforeDestroy', () => {
                _sider.removeEventListener('transitionend', this.siderTransitionendListener, false);
            });
          },
          methods:{
				 //侧边栏width过渡监听
		        siderTransitionendListener(e) {
		            let _sider = document.querySelector('.app-sider');
		            if (e.target == _sider && e.propertyName == 'width') {
		                // 防止transitionend 多次触发执行
		                commonUtil.debounce(this.onResize, 300)();
		            }
		        },
			}
		debounce(fn, wait = 200, _timer = null) {
	        let timer = _timer;
	        return function() {
	            let args = arguments;
	            if (timer) {
	                clearTimeout(timer);
	                timer = null;
	            }
	            timer = setTimeout(() => {
	                fn.apply(this, args);
	            }, wait);
	        };
	    }

解决:

  1. 使用插件 element-resize-detector ,具体使用教程网上较多不再赘述;
  2. 监听元素属性变化MutationObserver (思考第3点)
  3. 手动设置过渡时间后执行onResize(思考第4点)
  4. 监听过渡动画结束+防抖策略执行onResize (思考第5点,推荐)

监听window.resize时遇到了无法移除监听的问题:

方法1

	mounted(){
		 	window.addEventListener('resize', this.windowResizeListener, false);
		 	this.$once('hook:beforeDestroy', () => {
                window.removeEventListener('resize', this.windowResizeListener, false);
            });
	},
	methods: {
	        //窗口resize监听,不用windowResizeListener: commonUtil.debounce(this.onResize, 300) 此时this为undefined
	        windowResizeListener: commonUtil.debounce(function () {
	            this.onResize();
	        }, 300),
    }

方法2

	mounted(){
		window.onresize = commonUtil.debounce(this.onResize, 300);
		this.$once('hook:beforeDestroy', () => {
             window.onresize = null;
        });
	}
Logo

前往低代码交流专区

更多推荐