vue中nextTick使用引起的一个小问题

问题描述:

两个页面之间进行快速切换会报错元素找不到

问题原因:

该方法的调用是在nextTick中,也就是放在微任务队列中,当切换到该页面后主任务开始执行,(主任务执行完成后,更新dom元素,然后才是下一个tick),

然后快速切出该页面,组件销毁,此时dom元素处于尴尬位置,(可能是压根没有加载,也可能是加载后销毁)总之会元素消失,然而微任务队列还在等待执行,然后执行。此时代码中存在寻找dom元素的方法。dom已经销毁,这样就会引出错误。

1. 画布 

<canvas :id="canvasId"></canvas>

2.画图方法

   draw() {
      // 因为该函数是放在微任务队列中,可能会存在元素销毁后该方法才执行,所以要判断一下元素是否存在,如果不存在则退出方法
      const idEle = document.getElementById(this.canvasId)
      if (!idEle && this.antData.length === 0) {
        return
      }
      let height = this.antData.length >= 20 ? 390 : 270
      this.antData.reverse()
      this.chart = new F2.Chart({
        id: this.canvasId,
        width: this.screenWidth,
        height: height,
        pixelRatio: window.devicePixelRatio,
        padding: [0, "auto", 20, "auto"],
      })
      let range = [0, 0.6]
      if (this.judgeDataExitNegative(this.antData)) {
        range = [0.1, 0.6]
      }
      this.chart.source(this.antData, {
        label: {
          type: "cat",
        },
        value: {
          range: range,
        },
      })

      this.chart.coord({
        transposed: true,
      })
      this.chart.tooltip({
        custom: true, // 自定义 tooltip 内容框
        showTooltipMarker: true,
        tooltipMarkerStyle: {
          fill: "#F9EEDE",
          fillOpacity: 0.99,
        },
      })
      this.chart.legend(false)
      // 添加文本标注
      let offsetY2021 = 0
      let offsetY2020 = 0
      this.antData.forEach(item => {
        if (item.name === moment().format("YYYY")) {
          if (item.value < 0) {
            this.chart.guide().html({
              position: [item.label, item.value],
              html: `<div style="line-height:14px;font-weight: bold;display:flex;font-size:12px;"><span style="display:inline-block;line-height:14px;color:${
                item.disValue > 0 ? "#D04C63" : "#2e7065"
              };padding:0 2px;font-weight: bold;">${item.disValue > 0 ? "+" : ""}${item.disValue ? item.disValue.toLocaleString() : "0"}</span><span style="color: #000000;"> ${
                item.value ? item.value.toLocaleString() : "0"
              }</span> </div>`,
              offsetY: offsetY2021,
              offsetX: 0,
              alignX: "right",
              alignY: "bottom",
            })
          } else {
            this.chart.guide().html({
              position: [item.label, item.value],
              html: `<div style="font-weight: bold;display:flex;font-size:12px;align-items:center"><span style="color: #000000;"> ${
                item.value ? item.value.toLocaleString() : "0"
              }</span> <span style="display:inline-block;line-height:14px;color:${
                item.disValue > 0 ? "#D04C63" : item.disValue === 0 ? "gray" : "#2e7065"
              };padding:0 2px;margin-left: 4px;">${item.disValue > 0 ? "+" : ""}${item.disValue ? item.disValue.toLocaleString() : "0"}</span></div>`,
              offsetY: 1,
              offsetX: 2,
              alignX: "left",
              alignY: "bottom",
            })
          }
        } else if (item.name === moment().subtract(1, "year").format("YYYY")) {
          let alignX = "left"
          if (item.value < 0) {
            alignX = "right"
          }
          this.chart.guide().html({
            position: [item.label, item.value],
            html: `<span style="display:inline-block;font-weight: bold;font-size: 12px;text-align: center;color: #000000;height: 14px;line-height:14px">${
              item.value ? item.value.toLocaleString() : "0"
            }</span>`,
            offsetY: offsetY2020,
            offsetX: 2,
            alignX: alignX,
            alignY: "top",
          })
        }
      })
      this.chart.axis("label", {
        line: { top: true },
        // grid: {
        //   lineDash: true,
        //   stroke: "#e8e8e8",
        //   lineWidth: 1,
        //   fillOpacity: 0.6,
        // },
      })
      this.chart.axis("value", {
        line: { top: true },
      })
      this.chart.guide().line({
        start: ["max", 0],
        end: ["min", 0],
        style: {
          stroke: "gray",
          lineWidth: 1,
          lineCap: "round",
          opacity: 0.3,
        },
      })
      if (this.antData.length > 7) {
        let { size, marginRatio } = this.getSizeAndRatio(this.antData.length)
        // this.chart.interval().position("label*value").color("red")
        this.chart
          .interval()
          .position("label*value")
          .color("name", value => {
            if (value === "2021") {
              return "#89503c"
            } else {
              return "#CBAA7B"
            }
          })
          .adjust({
            type: "dodge",
            marginRatio: marginRatio, // 设置分组间柱子的间距
          })
          .size(size)
        // .style({
        //   radius: radius,
        // })
      } else {
        this.chart
          .interval()
          .position("label*value")
          .color("name", value => {
            if (value === "2021") {
              return "#9B5F4A"
            } else {
              return "#CBAA7B"
            }
          })
          .adjust({
            type: "dodge",
            marginRatio: 0.02, // 设置分组间柱子的间距
          })
      }
      this.chart.axis("label", {
        label: {
          fill: "#606266",
          fontFamily: "PingFangSC-Medium, PingFang SC",
          fontWeight: "500",
          fontSize: "12",
        },
      })
      this.chart.scale("label", { tickInterval: 0.11 })
      this.chart.render()
      this.chart.interaction("interval-select", {
        startEvent: "tap", // 触发事件,默认为 tap 事件
        selectAxis: true, // 是否高亮坐标轴文本
        selectAxisStyle: {
          fill: "#731E00",
          fontWeight: "800",
        },
        unSelectStyle: { fillOpacity: 0.9 },
        defaultSelected: {
          ...this.antData.reverse()[0],
        },
        cancelable: false,
        onStart: e => {
          this.$emit("interactionClick", e)
        }, // 事件触发后的回调
        mode: "range", // 选中策略,默认为 'shape', 即击中柱子才会触发交互
      })
    },

3. 方法的使用

init() {
      this.clear()
      this.$nextTick(() => {
        this.draw()
      })
    }

4. 修改之前的代码片段

 5. 修改后的代码片段

 6. 问题解决

 7. 感想:

    在开发过程中使用一个官方方法时要知其所以然,多思考该方法可能存在的问题。就比如该处出现的问题,就应该综合vue的dom元素更新策略,watcher的执行时机,宏观任务和微观任务的区别等等......

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好喝的西北风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值