vue
Version:
Reactive, component-oriented view layer for modern web interfaces.
188 lines (168 loc) • 5.44 kB
JavaScript
/* @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
}