vue在混合开发中应用的思考

2019-07-24  本文已影响0人  GrasFish

一、前言😃

我目前在做的一个项目,前端这块虽然说使用的是CS架构,但其实是C#提供了一层Chrome内核来运行app的。也就是混合开发的一种。现在的工作是将以前的jquery一把梭的开发模式转移到vue上来,这就带来了vue在混合开发中应用的思考。

二、混合开发

Android,WPFIOS等等这些原生(或桌面)应用都会提供一个WebView的控件来支持原生web交互。

而交互的方式分为两种:

一般情况都是原生容器提供一个对象,js去调用这个对象的方法,若为同步处理,直接返回值;若是异步的处理,原生容器会在处理结束后再调用window上的回调函数。
总的就是

// 原生容器提供的对象
window.OBJ.callback("123");
// js提供回调给原生容器调用
window.onOBJCallback = function (data){
    console.log(data);
}

三、vue应用于混合开发

1. 一代

可以看到js和原生交互都在window上,而vue这种内部处理数据的方式和这种window交互其实不是很搭的,要写也可以,但是极为别扭。如:

export default {
  data() {
    return {
      text: ""
    };
  },
  mounted() {
    // 挂载到window上
    window.onCallback = this.onCallback;
  },
  methods: {
    onCallback(data) {
      this.text = data;
    }
  }
};

这样虽然直观,但如果回调太多,代码写的多而易读性差不说还丑😐。

2. 二代⚡

混入(mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

由于混入是在已有组件的调用前调用的,所以在使用恰当的方式下不影响已有组件的开发。

设计思路
export class Callackable {

  // 用于缓存每个页面的callbacks
  callbacks = {};
  install(Vue, options) {
    // 让Vue的组件能访问到这个实例里
    const self = this;

    // 开始混入
    Vue.mixin({
      beforeCreate() {
        if (this.$options.callbacks && !(this.$route.name in self.callbacks)) {
          self.installCallback(this, this.$options.callbacks)
        }
      },
      mounted() {
        const name = this.$route.name;
        if (self.callbacks[name]) {
          self.callbacks[name].forEach(callback => {
            // 挂载
            window[callback.name] = callback.func
          });
        }
      }
    })
  }

  installCallback(vm, callbacks) {
    const routeName = vm.$options.name
    Object.keys(callbacks).forEach(name => {
      (this.callbacks[routeName] || (this.callbacks[routeName] = [])).push({
        name,
        // 将回调方法的this指向绑定到自己的组件实例上
        func: callbacks[name].bind(vm)
      })
    })
  }
}

主要思路就是每次组件加载时,在beforeCreated中获取callbacks,然后根据组件名称缓存起来,然后再在mounted时挂载组件。
重点主要在:绑定this。如果直接挂载到winodw上,那么调用这个回调函数时,this指向的就是window,所以通过bind方法来绑定到当前组件上

然后在main.js中通过Vue.use(new Callbackable())就可以了。

开发时如下:

export default {
  data() {
    return {
      text: ""
    };
  },
  callbacks: {
    onCallback(data) {
      this.text = data;
    }
  }
};
然而这其实是bug版本!!!👾

3. 二代改良版🔥

后面开发中发现,如果回调里处理data数据,切换路由后到其他页面再切换回来就会出现不能绑定到template上的情况!经过各种调试,发现虽然会触发data数据的改变,但并不会触发该组件内的watch去监听,自然就没有渲染。奇怪的是this也是组件本身。。。🙉

偶然地机会,🤔发现了两次this._uid不一样,恍然大悟下知道了vue-router其实每次进入路由,若没有通过<keep-alive>包裹<router-view>,那么都会新建一个组件,这就是解释了之前的问题,也就是this指向的是之前的那个组件,所以回调改变的其实也是之前的组件的data。所以每次路由切换都要重新绑定一次this才行。代码如下:

export class Callackable {

  callbacks = [];
  install(Vue, options) {
    const self = this;
    Vue.mixin({
      beforeCreate() {
        /**
         * 不能通过缓存来取callback。
         * 因为路由切换会导致组件实例的重新创建,this便指向过去的组件实例,而不是当前新创建的实例
         */
        if (this.$options.callbacks) {
          self.installCallback(this, this.$options.callbacks)
        }
      },
      mounted() {
        self.callbacks.forEach(callback => {
          window[callback.name] = callback.func
        });
      }
    })
  }

  installCallback(vm, callbacks) {
    Object.keys(callbacks).forEach(name => {
      this.callbacks.push({
        name,
        func: callbacks[name].bind(vm)
      })
    })
  }
}

这次就完美地解决了之前的问题。

四、总结

vue在混合开发中的应用就暂时告一段落了,大概设计了回调的使用方式和了解了vue-router的机制,还是要多读源码啊:P。

上一篇下一篇

猜你喜欢

热点阅读