关于小程序闭包插件在遇到页面回退的时候this指向的bug
发布于 6 年前 作者 xia01 16622 次浏览 来自 问答

1,一般做小程序的插件我都会在一个js写一个闭包,例如这样子:

        var plugin = function(tempOption){

             this.option = tempOption;

             this.pageThis =  tempOption.pageThis;        //传入页面Page的this

         };

         plugin.prototype.init = function(){             

             this.pageThis.aaa = this.aaa;                        //往页面注入方法

         };

         plugin.prototype.aaa = function(){

         };

         module.exports.Plugin = function(tempOption){

             var tempPlugin = new plugin(tempOption);

              tempPlugin.init();

              return tempPlugin;

         };

2,一般插件都涉及到对页面交互的操作,所以需要操作到Page闭包里面的data数据,或者需要往页面注入一些方法方便模板的bindtap调用。所以一般插件我都会在页面调用的时候把页面的this当做参数传入到插件中供调用。例如:

        var pluginJs = require(’…/plugin.js’);

        Page({

            onLoad:function(){

                plugin = pluginJs.Plugin({pageThis:this});            //把页面的this传入到插件中

            }

        })

3,一般情况下是没有任何问题的,但是当在页面A实例化插件时this指向的是页面A的this,然后A页面跳转到B页面实例化插件时this指向的是页面B的this。到这个地方依然没有任何问题,但如果这时候我点击顶部的回退按钮撤退到页面A,这时候插件的this指向的是还是页面B的this。所以就只能在onShow再做一次实例化做弥补,因为不少插件是要在数据读取之前先实例化的,所以没办法规避在onLoad里面做实例化。其它的编程语言也没有这种说写一个插件给别人调用还要写2次实例化这种道理的。

    

        

        

4 回复

你好,小程序使用标准的JS引擎,小程序本身也不对JS语法相关的问题进行任何特殊处理。

我没完全明白你的问题。但这里可以说明的是,按照你说明中第2点的逻辑,应该是每次页面load的时候会执行一次插件构造器,并传入Page对象。当A、B两个页面构造的时候,会分别创建两个对象,分别拥有两个页面的this。你这里似乎没有表述清楚plugin变量的作用范围,若plugin变量是文件级的,那如果A、B两个页面都对应到这个Page上的话,那确实是会有相互覆盖的情况(类似于普通的require模式,Page所在的JS文件永远只会被执行一次)。这是符合JS语法的,你需要用别的方法规避这个问题。

        比如我们现在如果要做一个比较成型的插件肯定是不止写一个js而已,肯定会包括wxml,wxss,plugin.js这三个文件的配套,用户用的时候直接include wxml,@import wxss,require plugin.js,然后实例化构造函数,搞定。

        打个比方,现在如果要写一个简单的对话框,那么至少要有一个关掉对话框的事件,例如下面是它的wxml:

        <view class=“dialog” bindtap=“closeDialog”>这里是对话框</view>

        这里面有个事件closeDialog,所以在Page()里面肯定要有一个对应的closeDialog()方法,对于相对比较大型插件来说bindtap的方法肯定是非常多,总不能用户要用你的插件还要自己去Page()里面自己写一堆方法吧,所以这些乱七八糟的方法就需要往plugin.js内部封装进去。就拿这个简单的对话框来说吧,他的plugin.js如下:

(function(){

        var plugin = function(tempOption){

             this.option = tempOption;

             this.pageThis =  tempOption.pageThis; 

        };

        plugin.prototype.init = function(){

             this.pageThis.closeDIalog = this.closeDIalog;        //把plugin的closeDialog注入到Page()   

        }

        plugin.prototype.closeDIalog = function(){

            //事件具体执行的操作代码写这里

        };

        module.exports.Plugin = function(tempOption){

             var tempPlugin = new plugin(tempOption);

              tempPlugin.init();

              return tempPlugin;

         };

})();

        这样子插件的wxml里面哪怕有100个方法用户也都可以不需要理会,只要配置实例化一下这100个方法就会绑到Page()给wxml的bandtap调用。说到这里你应该可以明白Page里面的this一定要传到plugin.js里面了吧,因为没有this我就没办法往Page()里面注入方法,就没办法跟wxml里面的bindtap的方法匹配起来。

        那天是一个插件老是发生了一些不明不白的bug,后面我调试后发现点击返回按钮的时候plugin.js里面的pageThis指向的是上一个页面Page()的this,而不是当前页面Page()的this。

        小生不才,也还没有多少工作经验,原本以为简单描述一下你们这群身经百战的老司机都应该知道怎么回事,所以也就没有写得那么多,我邮箱[email protected],如果还没说清楚问题我就发一封邮件给你吧。

首先我还是没有完全明白你的问题。我就说说如果让我写我会怎么写吧。

假定插件是在文件plugin.js里面的:

var createPluginInstance = require('plugin.js')
 
Page({
    onLoad: function(){
        this.plugin = createPluginInstance() // 创建一个插件实例
    },
    someFunction: function(){
        this.plugin.xxx() // 调用插件
    }
})

这样不就可以了吗?

        一般要写成一个插件确实都会写在单独一个js给不用页面进行调用,我在web端写plugin构造函数外面会套上一个(function(){})();,小程序一般也是require进去的所以外面那层闭包一般就被我偷懒了。我前期也料到可能会有不可控的因素,所以也要求几个同事把页面调用的所有插件的实例化都写在Page的一个固定的方法initPlugin()里面,其实现在相当于在onLoad和onShow都需要去执行这个方法。

        我在公司是可以让同事都按照这种模式写,但是有一天我想把我写的插件开源出去的话我总不能告诉人们因为微信小程序的机制是这样的所以就得这么写吧。小程序有比require更好的引入js机制,或者有更好的传入Page对象的this的机制麻烦赐教。

回到顶部