天天看點

Weex中js服務的了解

js服務

weex中注冊js服務,會在目前運作的js環境中注入一個對應service的create方法傳回的對象,這個對象有兩種傳回形式:

  1. 在傳回對象的instance上聲明一個執行個體服務;
  2. 直接在傳回對象對象上聲明一個服務。

如下面代碼中的InstanceService和NormalService

service.register(SERVICE_NAME /* same string with native */, {
  /**
    * JSService lifecycle. JSService `create` will before then each instance lifecycle `create`. The return param `instance` is Weex protected param. This object will return to instance global. Other params will in the `services` at instance.
    *
    * @param  {String} id  instance id
    * @param  {Object} env device environment
    * @return {Object}
    */
  create: function(id, env, config) {
    return {
      instance: {
        InstanceService: function(weex) {
          var modal = weex.requireModule('modal')
          return {
            toast: function(title) {
              modal.toast({ message: title })
            }
          }
        }
      },
      NormalService: function(weex) {
        var modal = weex.requireModule('modal')
        return {
          toast: function(title) {
            modal.toast({ message: title })
          }
        }
      }
    }
  },

  /**
    * JSService lifecycle. JSService `refresh` will before then each instance lifecycle `refresh`. If you want to reset variable or something on instance refresh.
    *
    * @param  {String} id  instance id
    * @param  {Object} env device environment
    */
  refresh: function(id, env, config){

  },

  /**
    * JSService lifecycle. JSService `destroy` will before then each instance lifecycle `destroy`. You can deleted variable here. If you doesn't detete variable define in JSService. The variable will always in the js runtime. It's would be memory leak risk.
    *
    * @param  {String} id  instance id
    * @param  {Object} env device environment
    * @return {Object}
    */
  destroy: function(id, env) {

  }
})
           

源碼了解

我們從源碼的角度了解service的注冊過程,我們可以在runtime的代碼中找到建立服務的方法:

function createServices (id, env, config) {
  // Init JavaScript services for this instance.
  const serviceMap = Object.create(null)
  serviceMap.service = Object.create(null)
  services.forEach(({ name, options }) => {
    if (process.env.NODE_ENV === 'development') {
      console.debug(`[JS Runtime] create service ${name}.`)
    }
    const create = options.create
    if (create) {
      try {
        const result = create(id, env, config)
        Object.assign(serviceMap.service, result)
        Object.assign(serviceMap, result.instance)
      }
      catch (e) {
        console.error(`[JS Runtime] Failed to create service ${name}.`)
      }
    }
  })
  delete serviceMap.service.instance
  Object.freeze(serviceMap.service)
  return serviceMap
}
           

我們看到在第3行和第4行分别建立了一個serviceMap對象和一個serviceMap上的service對象,這是用來将來儲存我們通過兩種方式建立的服務的;

接下來我們看第10行,如果注冊的服務有create方法,那麼我們取它的傳回值為result,接下來分别把result上的屬性複制到serviceMap.service,把result.instance上的屬性複制到serviceMap上,最後傳回這個serviceMap。

那麼這個serviceMap是怎麼放到全局的呢,我們繼續查找源碼,可以跟蹤到createInstanceContext方法,

function createInstanceContext (id, options = {}, data) {
  const weex = new WeexInstance(id, options, data)

  const bundleType = options.bundleType || 'Vue'
  instanceTypeMap[id] = bundleType
  const framework = runtimeConfig.frameworks[bundleType]
  if (!framework) {
    return new Error(`[JS Framework] Invalid bundle type "${bundleType}".`)
  }

  // prepare js service
  const services = createServices(id, {
    weex,
    config: options,
    created: Date.now(),
    framework: bundleType,
    bundleType
  }, runtimeConfig)
  Object.freeze(services)

  // prepare runtime context
  const runtimeContext = Object.create(null)
  Object.assign(runtimeContext, services, {
    weex,
    getJSFMVersion,
    requireModule: (...args) => weex.requireModule(...args),
    __WEEX_CALL_JAVASCRIPT__: receiveTasks,
    services // Temporary compatible with some legacy APIs in Rax
  })
  Object.freeze(runtimeContext)

  // prepare instance context
  const instanceContext = Object.assign({}, runtimeContext)
  if (typeof framework.createInstanceContext === 'function') {
    Object.assign(instanceContext, framework.createInstanceContext(id, runtimeContext, data))
  }
  Object.freeze(instanceContext)
  return instanceContext
}
           

這個方法是weex的runtime用來建立一個執行個體的上下文運作環境的,我們看到代碼的第12行,調用了createServices來建立services對象,而這個services就是我們上面說的serviceMap對象了。

接下來我們看到在第21行建立了一個空的對象作為運作時的上下文環境,緊接着在22行,把services等對象上的屬性複制到這個上下文對象上了,而上下文環境上的屬性我們可以當做全局變量來使用。

不好了解的話我們可以結合上面注冊的服務的例子,想象一下createInstanceContext最後傳回這樣一個對象:

{
  service:{
    NormalService:function(weex){
      //...
    }
  },
  InstanceService:function(weex){
    //...
  }
  weex:weex,
  getJSFMVersion:getJSFMVersion,
  __WEEX_CALL_JAVASCRIPT__  
}
           

這個對象上的屬性會被weex的底層注入到全局上下文運作環境中,即這個對象上的屬性是全局變量。

使用

是以我們就知道service如何在weex中使用了

<script>
var _InstanceService = new InstanceService(weex)
var _NormalService = new service.NormalService(weex)

module.exports = {
  created: fucntion() {
    // called modal module to toast something
    _InstanceService.toast('Instance JSService')
    _NormalService.toast('Normal JSService')
  }
}
</script>
           

serviceMap是在instance的create之前服務的create擷取的,是以,要想使用service,需要在執行個體建立之前調用原生平台的代碼執行js服務的注冊代碼完成服務的注冊。

Android和IOS原生平台的注冊代碼這裡不再贅述,參見下面的官方文檔即可。

參考文檔:js服務官方文檔

繼續閱讀