|
|
@@ -22,7 +22,7 @@ export function init (cfg) {
|
|
|
renderer.Document = cfg.Document
|
|
|
renderer.Element = cfg.Element
|
|
|
renderer.Comment = cfg.Comment
|
|
|
- renderer.sendTasks = cfg.sendTasks
|
|
|
+ renderer.compileBundle = cfg.compileBundle
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -35,7 +35,7 @@ export function reset () {
|
|
|
delete renderer.Document
|
|
|
delete renderer.Element
|
|
|
delete renderer.Comment
|
|
|
- delete renderer.sendTasks
|
|
|
+ delete renderer.compileBundle
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -66,18 +66,9 @@ export function createInstance (
|
|
|
// Virtual-DOM object.
|
|
|
const document = new renderer.Document(instanceId, config.bundleUrl)
|
|
|
|
|
|
- // All function/callback of parameters before sent to native
|
|
|
- // will be converted as an id. So `callbacks` is used to store
|
|
|
- // these real functions. When a callback invoked and won't be
|
|
|
- // called again, it should be removed from here automatically.
|
|
|
- const callbacks = []
|
|
|
-
|
|
|
- // The latest callback id, incremental.
|
|
|
- const callbackId = 1
|
|
|
-
|
|
|
const instance = instances[instanceId] = {
|
|
|
instanceId, config, data,
|
|
|
- document, callbacks, callbackId
|
|
|
+ document
|
|
|
}
|
|
|
|
|
|
// Prepare native module getter and HTML5 Timer APIs.
|
|
|
@@ -102,11 +93,16 @@ export function createInstance (
|
|
|
weex: weexInstanceVar,
|
|
|
// deprecated
|
|
|
__weex_require_module__: weexInstanceVar.requireModule // eslint-disable-line
|
|
|
- }, timerAPIs)
|
|
|
- callFunction(instanceVars, appCode)
|
|
|
+ }, timerAPIs, env.services)
|
|
|
+
|
|
|
+ if (!callFunctionNative(instanceVars, appCode)) {
|
|
|
+ // If failed to compile functionBody on native side,
|
|
|
+ // fallback to 'callFunction()'.
|
|
|
+ callFunction(instanceVars, appCode)
|
|
|
+ }
|
|
|
|
|
|
// Send `createFinish` signal to native.
|
|
|
- renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'createFinish', args: [] }], -1)
|
|
|
+ instance.document.taskCenter.send('dom', { action: 'createFinish' }, [])
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -117,6 +113,7 @@ export function createInstance (
|
|
|
export function destroyInstance (instanceId) {
|
|
|
const instance = instances[instanceId]
|
|
|
if (instance && instance.app instanceof instance.Vue) {
|
|
|
+ instance.document.destroy()
|
|
|
instance.app.$destroy()
|
|
|
}
|
|
|
delete instances[instanceId]
|
|
|
@@ -138,7 +135,7 @@ export function refreshInstance (instanceId, data) {
|
|
|
instance.Vue.set(instance.app, key, data[key])
|
|
|
}
|
|
|
// Finally `refreshFinish` signal needed.
|
|
|
- renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'refreshFinish', args: [] }], -1)
|
|
|
+ instance.document.taskCenter.send('dom', { action: 'refreshFinish' }, [])
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -153,41 +150,51 @@ export function getRoot (instanceId) {
|
|
|
return instance.app.$el.toJSON()
|
|
|
}
|
|
|
|
|
|
+const jsHandlers = {
|
|
|
+ fireEvent: (id, ...args) => {
|
|
|
+ return fireEvent(instances[id], ...args)
|
|
|
+ },
|
|
|
+ callback: (id, ...args) => {
|
|
|
+ return callback(instances[id], ...args)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function fireEvent (instance, nodeId, type, e, domChanges) {
|
|
|
+ const el = instance.document.getRef(nodeId)
|
|
|
+ if (el) {
|
|
|
+ return instance.document.fireEvent(el, type, e, domChanges)
|
|
|
+ }
|
|
|
+ return new Error(`invalid element reference "${nodeId}"`)
|
|
|
+}
|
|
|
+
|
|
|
+function callback (instance, callbackId, data, ifKeepAlive) {
|
|
|
+ const result = instance.document.taskCenter.callback(callbackId, data, ifKeepAlive)
|
|
|
+ instance.document.taskCenter.send('dom', { action: 'updateFinish' }, [])
|
|
|
+ return result
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
- * Receive tasks from native. Generally there are two types of tasks:
|
|
|
- * 1. `fireEvent`: an device actions or user actions from native.
|
|
|
- * 2. `callback`: invoke function which sent to native as a parameter before.
|
|
|
- * @param {string} instanceId
|
|
|
- * @param {array} tasks
|
|
|
+ * Accept calls from native (event or callback).
|
|
|
+ *
|
|
|
+ * @param {string} id
|
|
|
+ * @param {array} tasks list with `method` and `args`
|
|
|
*/
|
|
|
-export function receiveTasks (instanceId, tasks) {
|
|
|
- const instance = instances[instanceId]
|
|
|
- if (!instance || !(instance.app instanceof instance.Vue)) {
|
|
|
- return new Error(`receiveTasks: instance ${instanceId} not found!`)
|
|
|
- }
|
|
|
- const { callbacks, document } = instance
|
|
|
- tasks.forEach(task => {
|
|
|
- // `fireEvent` case: find the event target and fire.
|
|
|
- if (task.method === 'fireEvent') {
|
|
|
- const [nodeId, type, e, domChanges] = task.args
|
|
|
- const el = document.getRef(nodeId)
|
|
|
- document.fireEvent(el, type, e, domChanges)
|
|
|
- }
|
|
|
- // `callback` case: find the callback by id and call it.
|
|
|
- if (task.method === 'callback') {
|
|
|
- const [callbackId, data, ifKeepAlive] = task.args
|
|
|
- const callback = callbacks[callbackId]
|
|
|
- if (typeof callback === 'function') {
|
|
|
- callback(data)
|
|
|
- // Remove the callback from `callbacks` if it won't called again.
|
|
|
- if (typeof ifKeepAlive === 'undefined' || ifKeepAlive === false) {
|
|
|
- callbacks[callbackId] = undefined
|
|
|
- }
|
|
|
+export function receiveTasks (id, tasks) {
|
|
|
+ const instance = instances[id]
|
|
|
+ if (instance && Array.isArray(tasks)) {
|
|
|
+ const results = []
|
|
|
+ tasks.forEach((task) => {
|
|
|
+ const handler = jsHandlers[task.method]
|
|
|
+ const args = [...task.args]
|
|
|
+ /* istanbul ignore else */
|
|
|
+ if (typeof handler === 'function') {
|
|
|
+ args.unshift(id)
|
|
|
+ results.push(handler(...args))
|
|
|
}
|
|
|
- }
|
|
|
- })
|
|
|
- // Finally `updateFinish` signal needed.
|
|
|
- renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'updateFinish', args: [] }], -1)
|
|
|
+ })
|
|
|
+ return results
|
|
|
+ }
|
|
|
+ return new Error(`invalid instance id "${id}" or tasks`)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -288,9 +295,7 @@ function createVueModuleInstance (instanceId, moduleGetter) {
|
|
|
* Generate native module getter. Each native module has several
|
|
|
* methods to call. And all the behaviors is instance-related. So
|
|
|
* this getter will return a set of methods which additionally
|
|
|
- * send current instance id to native when called. Also the args
|
|
|
- * will be normalized into "safe" value. For example function arg
|
|
|
- * will be converted into a callback id.
|
|
|
+ * send current instance id to native when called.
|
|
|
* @param {string} instanceId
|
|
|
* @return {function}
|
|
|
*/
|
|
|
@@ -300,12 +305,20 @@ function genModuleGetter (instanceId) {
|
|
|
const nativeModule = modules[name] || []
|
|
|
const output = {}
|
|
|
for (const methodName in nativeModule) {
|
|
|
- output[methodName] = (...args) => {
|
|
|
- const finalArgs = args.map(value => {
|
|
|
- return normalize(value, instance)
|
|
|
- })
|
|
|
- renderer.sendTasks(instanceId + '', [{ module: name, method: methodName, args: finalArgs }], -1)
|
|
|
- }
|
|
|
+ Object.defineProperty(output, methodName, {
|
|
|
+ enumerable: true,
|
|
|
+ configurable: true,
|
|
|
+ get: function proxyGetter () {
|
|
|
+ return (...args) => {
|
|
|
+ return instance.document.taskCenter.send('module', { module: name, method: methodName }, args)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ set: function proxySetter (val) {
|
|
|
+ if (typeof val === 'function') {
|
|
|
+ return instance.document.taskCenter.send('module', { module: name, method: methodName }, [val])
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
}
|
|
|
return output
|
|
|
}
|
|
|
@@ -328,15 +341,17 @@ function getInstanceTimer (instanceId, moduleGetter) {
|
|
|
const handler = function () {
|
|
|
args[0](...args.slice(2))
|
|
|
}
|
|
|
+
|
|
|
timer.setTimeout(handler, args[1])
|
|
|
- return instance.callbackId.toString()
|
|
|
+ return instance.document.taskCenter.callbackManager.lastCallbackId.toString()
|
|
|
},
|
|
|
setInterval: (...args) => {
|
|
|
const handler = function () {
|
|
|
args[0](...args.slice(2))
|
|
|
}
|
|
|
+
|
|
|
timer.setInterval(handler, args[1])
|
|
|
- return instance.callbackId.toString()
|
|
|
+ return instance.document.taskCenter.callbackManager.lastCallbackId.toString()
|
|
|
},
|
|
|
clearTimeout: (n) => {
|
|
|
timer.clearTimeout(n)
|
|
|
@@ -368,50 +383,53 @@ function callFunction (globalObjects, body) {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Convert all type of values into "safe" format to send to native.
|
|
|
- * 1. A `function` will be converted into callback id.
|
|
|
- * 2. An `Element` object will be converted into `ref`.
|
|
|
- * The `instance` param is used to generate callback id and store
|
|
|
- * function if necessary.
|
|
|
- * @param {any} v
|
|
|
- * @param {object} instance
|
|
|
- * @return {any}
|
|
|
+ * Call a new function generated on the V8 native side.
|
|
|
+ *
|
|
|
+ * This function helps speed up bundle compiling. Normally, the V8
|
|
|
+ * engine needs to download, parse, and compile a bundle on every
|
|
|
+ * visit. If 'compileBundle()' is available on native side,
|
|
|
+ * the downloding, parsing, and compiling steps would be skipped.
|
|
|
+ * @param {object} globalObjects
|
|
|
+ * @param {string} body
|
|
|
+ * @return {boolean}
|
|
|
*/
|
|
|
-function normalize (v, instance) {
|
|
|
- const type = typof(v)
|
|
|
-
|
|
|
- switch (type) {
|
|
|
- case 'undefined':
|
|
|
- case 'null':
|
|
|
- return ''
|
|
|
- case 'regexp':
|
|
|
- return v.toString()
|
|
|
- case 'date':
|
|
|
- return v.toISOString()
|
|
|
- case 'number':
|
|
|
- case 'string':
|
|
|
- case 'boolean':
|
|
|
- case 'array':
|
|
|
- case 'object':
|
|
|
- if (v instanceof renderer.Element) {
|
|
|
- return v.ref
|
|
|
- }
|
|
|
- return v
|
|
|
- case 'function':
|
|
|
- instance.callbacks[++instance.callbackId] = v
|
|
|
- return instance.callbackId.toString()
|
|
|
- default:
|
|
|
- return JSON.stringify(v)
|
|
|
+function callFunctionNative (globalObjects, body) {
|
|
|
+ if (typeof renderer.compileBundle !== 'function') {
|
|
|
+ return false
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-/**
|
|
|
- * Get the exact type of an object by `toString()`. For example call
|
|
|
- * `toString()` on an array will be returned `[object Array]`.
|
|
|
- * @param {any} v
|
|
|
- * @return {string}
|
|
|
- */
|
|
|
-function typof (v) {
|
|
|
- const s = Object.prototype.toString.call(v)
|
|
|
- return s.substring(8, s.length - 1).toLowerCase()
|
|
|
+ let fn = void 0
|
|
|
+ let isNativeCompileOk = false
|
|
|
+ let script = '(function ('
|
|
|
+ const globalKeys = []
|
|
|
+ const globalValues = []
|
|
|
+ for (const key in globalObjects) {
|
|
|
+ globalKeys.push(key)
|
|
|
+ globalValues.push(globalObjects[key])
|
|
|
+ }
|
|
|
+ for (let i = 0; i < globalKeys.length - 1; ++i) {
|
|
|
+ script += globalKeys[i]
|
|
|
+ script += ','
|
|
|
+ }
|
|
|
+ script += globalKeys[globalKeys.length - 1]
|
|
|
+ script += ') {'
|
|
|
+ script += body
|
|
|
+ script += '} )'
|
|
|
+
|
|
|
+ try {
|
|
|
+ const weex = globalObjects.weex || {}
|
|
|
+ const config = weex.config || {}
|
|
|
+ fn = renderer.compileBundle(script,
|
|
|
+ config.bundleUrl,
|
|
|
+ config.bundleDigest,
|
|
|
+ config.codeCachePath)
|
|
|
+ if (fn && typeof fn === 'function') {
|
|
|
+ fn(...globalValues)
|
|
|
+ isNativeCompileOk = true
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error(e)
|
|
|
+ }
|
|
|
+
|
|
|
+ return isNativeCompileOk
|
|
|
}
|