js服务

weex中注册js服务,会在当前运行的js环境中注入一个对应service的create方法返回的对象,这个对象有两种返回形式:

  1. 在返回对象的instance上声明一个实例服务;
  2. 直接在返回对象对象上声明一个服务。

如下面代码中的InstanceService和NormalService

  1. service.register(SERVICE_NAME /* same string with native */, {
  2. /**
  3. * 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.
  4. *
  5. * @param {String} id instance id
  6. * @param {Object} env device environment
  7. * @return {Object}
  8. */
  9. create: function(id, env, config) {
  10. return {
  11. instance: {
  12. InstanceService: function(weex) {
  13. var modal = weex.requireModule('modal')
  14. return {
  15. toast: function(title) {
  16. modal.toast({ message: title })
  17. }
  18. }
  19. }
  20. },
  21. NormalService: function(weex) {
  22. var modal = weex.requireModule('modal')
  23. return {
  24. toast: function(title) {
  25. modal.toast({ message: title })
  26. }
  27. }
  28. }
  29. }
  30. },
  31. /**
  32. * JSService lifecycle. JSService `refresh` will before then each instance lifecycle `refresh`. If you want to reset variable or something on instance refresh.
  33. *
  34. * @param {String} id instance id
  35. * @param {Object} env device environment
  36. */
  37. refresh: function(id, env, config){
  38. },
  39. /**
  40. * 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.
  41. *
  42. * @param {String} id instance id
  43. * @param {Object} env device environment
  44. * @return {Object}
  45. */
  46. destroy: function(id, env) {
  47. }
  48. })

源码理解

我们从源码的角度理解service的注册过程,我们可以在runtime的代码中找到创建服务的方法:

  1. function createServices (id, env, config) {
  2. // Init JavaScript services for this instance.
  3. const serviceMap = Object.create(null)
  4. serviceMap.service = Object.create(null)
  5. services.forEach(({ name, options }) => {
  6. if (process.env.NODE_ENV === 'development') {
  7. console.debug(`[JS Runtime] create service ${name}.`)
  8. }
  9. const create = options.create
  10. if (create) {
  11. try {
  12. const result = create(id, env, config)
  13. Object.assign(serviceMap.service, result)
  14. Object.assign(serviceMap, result.instance)
  15. }
  16. catch (e) {
  17. console.error(`[JS Runtime] Failed to create service ${name}.`)
  18. }
  19. }
  20. })
  21. delete serviceMap.service.instance
  22. Object.freeze(serviceMap.service)
  23. return serviceMap
  24. }

我们看到在第3行和第4行分别创建了一个serviceMap对象和一个serviceMap上的service对象,这是用来将来保存我们通过两种方式创建的服务的;
接下来我们看第10行,如果注册的服务有create方法,那么我们取它的返回值为result,接下来分别把result上的属性复制到serviceMap.service,把result.instance上的属性复制到serviceMap上,最后返回这个serviceMap。
那么这个serviceMap是怎么放到全局的呢,我们继续查找源码,可以跟踪到createInstanceContext方法,

  1. function createInstanceContext (id, options = {}, data) {
  2. const weex = new WeexInstance(id, options, data)
  3. const bundleType = options.bundleType || 'Vue'
  4. instanceTypeMap[id] = bundleType
  5. const framework = runtimeConfig.frameworks[bundleType]
  6. if (!framework) {
  7. return new Error(`[JS Framework] Invalid bundle type "${bundleType}".`)
  8. }
  9. // prepare js service
  10. const services = createServices(id, {
  11. weex,
  12. config: options,
  13. created: Date.now(),
  14. framework: bundleType,
  15. bundleType
  16. }, runtimeConfig)
  17. Object.freeze(services)
  18. // prepare runtime context
  19. const runtimeContext = Object.create(null)
  20. Object.assign(runtimeContext, services, {
  21. weex,
  22. getJSFMVersion,
  23. requireModule: (...args) => weex.requireModule(...args),
  24. __WEEX_CALL_JAVASCRIPT__: receiveTasks,
  25. services // Temporary compatible with some legacy APIs in Rax
  26. })
  27. Object.freeze(runtimeContext)
  28. // prepare instance context
  29. const instanceContext = Object.assign({}, runtimeContext)
  30. if (typeof framework.createInstanceContext === 'function') {
  31. Object.assign(instanceContext, framework.createInstanceContext(id, runtimeContext, data))
  32. }
  33. Object.freeze(instanceContext)
  34. return instanceContext
  35. }

这个方法是weex的runtime用来创建一个实例的上下文运行环境的,我们看到代码的第12行,调用了createServices来创建services对象,而这个services就是我们上面说的serviceMap对象了。
接下来我们看到在第21行创建了一个空的对象作为运行时的上下文环境,紧接着在22行,把services等对象上的属性复制到这个上下文对象上了,而上下文环境上的属性我们可以当做全局变量来使用。
不好理解的话我们可以结合上面注册的服务的例子,想象一下createInstanceContext最后返回这样一个对象:

  1. {
  2. service:{
  3. NormalService:function(weex){
  4. //...
  5. }
  6. },
  7. InstanceService:function(weex){
  8. //...
  9. }
  10. weex:weex,
  11. getJSFMVersion:getJSFMVersion,
  12. __WEEX_CALL_JAVASCRIPT__
  13. }

这个对象上的属性会被weex的底层注入到全局上下文运行环境中,即这个对象上的属性是全局变量。

使用

因此我们就知道service如何在weex中使用了

  1. <script>
  2. var _InstanceService = new InstanceService(weex)
  3. var _NormalService = new service.NormalService(weex)
  4. module.exports = {
  5. created: fucntion() {
  6. // called modal module to toast something
  7. _InstanceService.toast('Instance JSService')
  8. _NormalService.toast('Normal JSService')
  9. }
  10. }
  11. </script>

理解了service的原理和使用方法,我们就可以利用其实现外部js的加载和执行了,

serviceMap是在instance的create之前服务的create获取的,因此,要想使用service,需要在实例创建之前完成服务的注册。