最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 微信小程序组件生命周期的坑

    正文概述 掘金(楼东)   2021-03-02   761

    组件生命周期,通常是我们业务逻辑开始的地方。 如果业务场景比较复杂,组件生命周期有不符合预期的表现时, 可能会导致一些诡异的业务bug,它们极难复现和修复。

    组件 attached 生命周期执行次数

    按照通常的理解,除moved/show/hide等生命周期可能多次执行外, 严格意义上与组件加载相关的生命周期,如:createdattachedready等, 每个组件实例应该只执行一次。但是事实真的如此吗?

    背景

    这个问题的发现,源于我们在小程序的报错日志中, 收到大量类似Cannot redefine property: isComponent的报错。

    微信小程序组件生命周期的坑

    原因分析

    通过变量名可以追溯到我们在代码中的定义方式为:

    Component({
      lifetimes: {
        attached() {
          Object.defineProperty(this, 'isComponent', {
            enumerable: true,
            get() { return true },
          });
        },
      },
    });
    

    很容易理解,这种错误的起因在于试图给对象重新定义一个不可配置的属性, 具体可以查看MDN上的说明。

    可是这个定义是写在attached生命周期当中的,难道说,组件的attached生命周期被触发了两次?

    天呐,这怎么可能?

    是的,就是这么神奇!

    场景还原

    该问题并不容易复现,但是通过不断删繁就简、抽丝剥茧,最终还是找到了问题的根源:

    微信小程序组件生命周期的坑

    可以通过以下代码复现该场景,或者直接访问小程序代码片段。

    页面

    // page.js
    Page({
      data: {
        showChild2: false,
      },
      onChild1Attached() {
        this.setData({ showChild2: true });
      },
    });
    
    
    <!-- page.wxml -->
    <child1 bind:attached="onChild1Attached"></child1>
    <child2 wx:if="{{ showChild2 }}"></child2>
    

    子组件1

    与页面一同渲染,并在attached的时候,通过triggerEvent,通知页面更新状态并渲染子组件2。

    // child1.js
    Component({
      lifetimes: {
        attached() {
          this.triggerEvent('attached');
        },
      },
    });
    
    
    <!-- child1.wxml -->
    <view>child1</view>
    

    子组件2

    执行了两次attached生命周期,导致报错。

    // child2.js
    Component({
      lifetimes: {
        attached() {
          Object.defineProperty(this, 'isComponent', {
            enumerable: true,
            get() { return true },
          });
        },
      },
    });
    
    <!-- child2.wxml -->
    <view>child2</view>
    

    组件 ready 生命周期的执行时机

    小程序官方文档没有明确给出组件生命周期的执行顺序, 不过通过打印日志我们可以很容易地发现:

    • 在加载阶段,会依次执行:created -> attached -> ready
    • 在卸载阶段,会依次执行:detached

    所以,看起来这个顺序貌似应该是:created -> attached -> ready -> detached。 但是实际情况果真如此吗?

    背景

    有段时间,客服经常反馈,我们的小程序存在串数据的现象。 例如:A商家的直播展示了B商家的商品。

    原因分析

    串数据发生在多个场景,考虑到数据是通过消息推送到小程序端上的, 最终怀疑问题出在WebSocket通信上。

    在小程序端,我们封装了一个WebSocket通信组件,核心逻辑如下:

    // socket.js
    Component({
      lifetimes: {
        ready() {
          this.getSocketConfig().then(config => {
            this.ws = wx.connectSocket(config);
            this.ws.onMessage(msg => {
              const data = JSON.parse(msg.data);
              this.onReceiveMessage(data);
            });
          });
        },
        detached() {
          this.ws && this.ws.close({});
        },
      },
      methods: {
        getSocketConfig() {
          // 从服务器请求 socket 连接配置
          return new Promise(() => {});
        },
        onReceiveMessage(data) {
          event.emit('message', data);
        },
      },
    });
    

    简单说,就是在组件ready时,初始化一个WebSocket连接并监听消息推送, 然后在detached阶段关闭连接。

    看起来并没有什么问题,那么就只能从结果倒推可能不符合常理的情况了。

    数据串了 -> WebSocket 消息串了 -> WebSocket 没有正常关闭 -> close有问题/detached未执行/ready在detached之后执行

    场景还原

    此处的实际业务逻辑较为复杂,因此只能通过简化的代码来验证。 通过不断试验,最终发现:

    微信小程序组件生命周期的坑

    可以通过以下代码复现该场景,或者直接访问小程序代码片段。

    页面

    // page.js
    Page({
      data: {
        showChild: true,
      },
      onLoad() {
        this.setData({ showChild: false });
      },
    });
    
    
    <!-- page.wxml -->
    <child wx:if="{{ showChild }}" />
    

    组件

    组件未ready的时候销毁组件,会先同步执行detached,然后异步执行ready

    // child.js
    Component({
      lifetimes: {
        created() {
          console.log('created');
        },
        attached() {
          console.log('attached');
        },
        ready() {
          console.log('ready');
        },
        detached() {
          console.log('detached');
        }
      },
    });
    

    拓展

    即便是将初始化的工作从ready前置到attached阶段,只要有异步操作,仍然可能存在detached先于异步回调执行的情况。

    因此,请不要完全信任在组件detached阶段的销毁操作。


    起源地下载网 » 微信小程序组件生命周期的坑

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元