/* @flow */
|
|
// this will be preserved during build
|
// $flow-disable-line
|
const VueFactory = require('./factory')
|
|
const instanceOptions: { [key: string]: WeexInstanceOption } = {}
|
|
/**
|
* Create instance context.
|
*/
|
export function createInstanceContext (
|
instanceId: string,
|
runtimeContext: WeexRuntimeContext,
|
data: Object = {}
|
): WeexInstanceContext {
|
const weex: Weex = runtimeContext.weex
|
const instance: WeexInstanceOption = instanceOptions[instanceId] = {
|
instanceId,
|
config: weex.config,
|
document: weex.document,
|
data
|
}
|
|
// Each instance has a independent `Vue` module instance
|
const Vue = instance.Vue = createVueModuleInstance(instanceId, weex)
|
|
// DEPRECATED
|
const timerAPIs = getInstanceTimer(instanceId, weex.requireModule)
|
|
const instanceContext = Object.assign({ Vue }, timerAPIs)
|
Object.freeze(instanceContext)
|
return instanceContext
|
}
|
|
/**
|
* Destroy an instance with id. It will make sure all memory of
|
* this instance released and no more leaks.
|
*/
|
export function destroyInstance (instanceId: string): void {
|
const instance = instanceOptions[instanceId]
|
if (instance && instance.app instanceof instance.Vue) {
|
try {
|
instance.app.$destroy()
|
instance.document.destroy()
|
} catch (e) {}
|
delete instance.document
|
delete instance.app
|
}
|
delete instanceOptions[instanceId]
|
}
|
|
/**
|
* Refresh an instance with id and new top-level component data.
|
* It will use `Vue.set` on all keys of the new data. So it's better
|
* define all possible meaningful keys when instance created.
|
*/
|
export function refreshInstance (
|
instanceId: string,
|
data: Object
|
): Error | void {
|
const instance = instanceOptions[instanceId]
|
if (!instance || !(instance.app instanceof instance.Vue)) {
|
return new Error(`refreshInstance: instance ${instanceId} not found!`)
|
}
|
if (instance.Vue && instance.Vue.set) {
|
for (const key in data) {
|
instance.Vue.set(instance.app, key, data[key])
|
}
|
}
|
// Finally `refreshFinish` signal needed.
|
instance.document.taskCenter.send('dom', { action: 'refreshFinish' }, [])
|
}
|
|
/**
|
* Create a fresh instance of Vue for each Weex instance.
|
*/
|
function createVueModuleInstance (
|
instanceId: string,
|
weex: Weex
|
): GlobalAPI {
|
const exports = {}
|
VueFactory(exports, weex.document)
|
const Vue = exports.Vue
|
|
const instance = instanceOptions[instanceId]
|
|
// patch reserved tag detection to account for dynamically registered
|
// components
|
const weexRegex = /^weex:/i
|
const isReservedTag = Vue.config.isReservedTag || (() => false)
|
const isRuntimeComponent = Vue.config.isRuntimeComponent || (() => false)
|
Vue.config.isReservedTag = name => {
|
return (!isRuntimeComponent(name) && weex.supports(`@component/${name}`)) ||
|
isReservedTag(name) ||
|
weexRegex.test(name)
|
}
|
Vue.config.parsePlatformTagName = name => name.replace(weexRegex, '')
|
|
// expose weex-specific info
|
Vue.prototype.$instanceId = instanceId
|
Vue.prototype.$document = instance.document
|
|
// expose weex native module getter on subVue prototype so that
|
// vdom runtime modules can access native modules via vnode.context
|
Vue.prototype.$requireWeexModule = weex.requireModule
|
|
// Hack `Vue` behavior to handle instance information and data
|
// before root component created.
|
Vue.mixin({
|
beforeCreate () {
|
const options = this.$options
|
// root component (vm)
|
if (options.el) {
|
// set external data of instance
|
const dataOption = options.data
|
const internalData = (typeof dataOption === 'function' ? dataOption() : dataOption) || {}
|
options.data = Object.assign(internalData, instance.data)
|
// record instance by id
|
instance.app = this
|
}
|
},
|
mounted () {
|
const options = this.$options
|
// root component (vm)
|
if (options.el && weex.document && instance.app === this) {
|
try {
|
// Send "createFinish" signal to native.
|
weex.document.taskCenter.send('dom', { action: 'createFinish' }, [])
|
} catch (e) {}
|
}
|
}
|
})
|
|
/**
|
* @deprecated Just instance variable `weex.config`
|
* Get instance config.
|
* @return {object}
|
*/
|
Vue.prototype.$getConfig = function () {
|
if (instance.app instanceof Vue) {
|
return instance.config
|
}
|
}
|
|
return Vue
|
}
|
|
/**
|
* DEPRECATED
|
* Generate HTML5 Timer APIs. An important point is that the callback
|
* will be converted into callback id when sent to native. So the
|
* framework can make sure no side effect of the callback happened after
|
* an instance destroyed.
|
*/
|
function getInstanceTimer (
|
instanceId: string,
|
moduleGetter: Function
|
): Object {
|
const instance = instanceOptions[instanceId]
|
const timer = moduleGetter('timer')
|
const timerAPIs = {
|
setTimeout: (...args) => {
|
const handler = function () {
|
args[0](...args.slice(2))
|
}
|
|
timer.setTimeout(handler, args[1])
|
return instance.document.taskCenter.callbackManager.lastCallbackId.toString()
|
},
|
setInterval: (...args) => {
|
const handler = function () {
|
args[0](...args.slice(2))
|
}
|
|
timer.setInterval(handler, args[1])
|
return instance.document.taskCenter.callbackManager.lastCallbackId.toString()
|
},
|
clearTimeout: (n) => {
|
timer.clearTimeout(n)
|
},
|
clearInterval: (n) => {
|
timer.clearInterval(n)
|
}
|
}
|
return timerAPIs
|
}
|