js服务
weex中注册js服务,会在当前运行的js环境中注入一个对应service的create方法返回的对象,这个对象有两种返回形式:
- 在返回对象的instance上声明一个实例服务;
- 直接在返回对象对象上声明一个服务。
如下面代码中的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.createif (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.instanceObject.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] = bundleTypeconst framework = runtimeConfig.frameworks[bundleType]if (!framework) {return new Error(`[JS Framework] Invalid bundle type "${bundleType}".`)}// prepare js serviceconst services = createServices(id, {weex,config: options,created: Date.now(),framework: bundleType,bundleType}, runtimeConfig)Object.freeze(services)// prepare runtime contextconst 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 contextconst 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>
理解了service的原理和使用方法,我们就可以利用其实现外部js的加载和执行了,
serviceMap是在instance的create之前服务的create获取的,因此,要想使用service,需要在实例创建之前完成服务的注册。
