Skip to content

Commit 0d6ad12

Browse files
Hanks10100yyx990803
authored andcommitted
refactor(weex): sync recent changes of Weex (vuejs#6028)
* compile bundle on native side if 'compileBundle()' is available on native side. * refactor sendTasks * reset renderer.compileBundle * v2.2.2-weex.1 * v2.2.2-weex.2 && fixed memory leak * call C++ timer instead of WxTimerModule in weex-vue-framwork * v2.2.2-weex.4 * v2.2.2-weex.5 * v2.2.6-weex.1 * style(weex): fix eslint * test(weex): fix test case for weex callback manager
1 parent 306997e commit 0d6ad12

File tree

2 files changed

+124
-106
lines changed

2 files changed

+124
-106
lines changed

src/platforms/weex/entry-framework.js

+120-102
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function init (cfg) {
2222
renderer.Document = cfg.Document
2323
renderer.Element = cfg.Element
2424
renderer.Comment = cfg.Comment
25-
renderer.sendTasks = cfg.sendTasks
25+
renderer.compileBundle = cfg.compileBundle
2626
}
2727

2828
/**
@@ -35,7 +35,7 @@ export function reset () {
3535
delete renderer.Document
3636
delete renderer.Element
3737
delete renderer.Comment
38-
delete renderer.sendTasks
38+
delete renderer.compileBundle
3939
}
4040

4141
/**
@@ -66,18 +66,9 @@ export function createInstance (
6666
// Virtual-DOM object.
6767
const document = new renderer.Document(instanceId, config.bundleUrl)
6868

69-
// All function/callback of parameters before sent to native
70-
// will be converted as an id. So `callbacks` is used to store
71-
// these real functions. When a callback invoked and won't be
72-
// called again, it should be removed from here automatically.
73-
const callbacks = []
74-
75-
// The latest callback id, incremental.
76-
const callbackId = 1
77-
7869
const instance = instances[instanceId] = {
7970
instanceId, config, data,
80-
document, callbacks, callbackId
71+
document
8172
}
8273

8374
// Prepare native module getter and HTML5 Timer APIs.
@@ -102,11 +93,16 @@ export function createInstance (
10293
weex: weexInstanceVar,
10394
// deprecated
10495
__weex_require_module__: weexInstanceVar.requireModule // eslint-disable-line
105-
}, timerAPIs)
106-
callFunction(instanceVars, appCode)
96+
}, timerAPIs, env.services)
97+
98+
if (!callFunctionNative(instanceVars, appCode)) {
99+
// If failed to compile functionBody on native side,
100+
// fallback to 'callFunction()'.
101+
callFunction(instanceVars, appCode)
102+
}
107103

108104
// Send `createFinish` signal to native.
109-
renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'createFinish', args: [] }], -1)
105+
instance.document.taskCenter.send('dom', { action: 'createFinish' }, [])
110106
}
111107

112108
/**
@@ -117,6 +113,7 @@ export function createInstance (
117113
export function destroyInstance (instanceId) {
118114
const instance = instances[instanceId]
119115
if (instance && instance.app instanceof instance.Vue) {
116+
instance.document.destroy()
120117
instance.app.$destroy()
121118
}
122119
delete instances[instanceId]
@@ -138,7 +135,7 @@ export function refreshInstance (instanceId, data) {
138135
instance.Vue.set(instance.app, key, data[key])
139136
}
140137
// Finally `refreshFinish` signal needed.
141-
renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'refreshFinish', args: [] }], -1)
138+
instance.document.taskCenter.send('dom', { action: 'refreshFinish' }, [])
142139
}
143140

144141
/**
@@ -153,41 +150,51 @@ export function getRoot (instanceId) {
153150
return instance.app.$el.toJSON()
154151
}
155152

153+
const jsHandlers = {
154+
fireEvent: (id, ...args) => {
155+
return fireEvent(instances[id], ...args)
156+
},
157+
callback: (id, ...args) => {
158+
return callback(instances[id], ...args)
159+
}
160+
}
161+
162+
function fireEvent (instance, nodeId, type, e, domChanges) {
163+
const el = instance.document.getRef(nodeId)
164+
if (el) {
165+
return instance.document.fireEvent(el, type, e, domChanges)
166+
}
167+
return new Error(`invalid element reference "${nodeId}"`)
168+
}
169+
170+
function callback (instance, callbackId, data, ifKeepAlive) {
171+
const result = instance.document.taskCenter.callback(callbackId, data, ifKeepAlive)
172+
instance.document.taskCenter.send('dom', { action: 'updateFinish' }, [])
173+
return result
174+
}
175+
156176
/**
157-
* Receive tasks from native. Generally there are two types of tasks:
158-
* 1. `fireEvent`: an device actions or user actions from native.
159-
* 2. `callback`: invoke function which sent to native as a parameter before.
160-
* @param {string} instanceId
161-
* @param {array} tasks
177+
* Accept calls from native (event or callback).
178+
*
179+
* @param {string} id
180+
* @param {array} tasks list with `method` and `args`
162181
*/
163-
export function receiveTasks (instanceId, tasks) {
164-
const instance = instances[instanceId]
165-
if (!instance || !(instance.app instanceof instance.Vue)) {
166-
return new Error(`receiveTasks: instance ${instanceId} not found!`)
167-
}
168-
const { callbacks, document } = instance
169-
tasks.forEach(task => {
170-
// `fireEvent` case: find the event target and fire.
171-
if (task.method === 'fireEvent') {
172-
const [nodeId, type, e, domChanges] = task.args
173-
const el = document.getRef(nodeId)
174-
document.fireEvent(el, type, e, domChanges)
175-
}
176-
// `callback` case: find the callback by id and call it.
177-
if (task.method === 'callback') {
178-
const [callbackId, data, ifKeepAlive] = task.args
179-
const callback = callbacks[callbackId]
180-
if (typeof callback === 'function') {
181-
callback(data)
182-
// Remove the callback from `callbacks` if it won't called again.
183-
if (typeof ifKeepAlive === 'undefined' || ifKeepAlive === false) {
184-
callbacks[callbackId] = undefined
185-
}
182+
export function receiveTasks (id, tasks) {
183+
const instance = instances[id]
184+
if (instance && Array.isArray(tasks)) {
185+
const results = []
186+
tasks.forEach((task) => {
187+
const handler = jsHandlers[task.method]
188+
const args = [...task.args]
189+
/* istanbul ignore else */
190+
if (typeof handler === 'function') {
191+
args.unshift(id)
192+
results.push(handler(...args))
186193
}
187-
}
188-
})
189-
// Finally `updateFinish` signal needed.
190-
renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'updateFinish', args: [] }], -1)
194+
})
195+
return results
196+
}
197+
return new Error(`invalid instance id "${id}" or tasks`)
191198
}
192199

193200
/**
@@ -288,9 +295,7 @@ function createVueModuleInstance (instanceId, moduleGetter) {
288295
* Generate native module getter. Each native module has several
289296
* methods to call. And all the behaviors is instance-related. So
290297
* this getter will return a set of methods which additionally
291-
* send current instance id to native when called. Also the args
292-
* will be normalized into "safe" value. For example function arg
293-
* will be converted into a callback id.
298+
* send current instance id to native when called.
294299
* @param {string} instanceId
295300
* @return {function}
296301
*/
@@ -300,12 +305,20 @@ function genModuleGetter (instanceId) {
300305
const nativeModule = modules[name] || []
301306
const output = {}
302307
for (const methodName in nativeModule) {
303-
output[methodName] = (...args) => {
304-
const finalArgs = args.map(value => {
305-
return normalize(value, instance)
306-
})
307-
renderer.sendTasks(instanceId + '', [{ module: name, method: methodName, args: finalArgs }], -1)
308-
}
308+
Object.defineProperty(output, methodName, {
309+
enumerable: true,
310+
configurable: true,
311+
get: function proxyGetter () {
312+
return (...args) => {
313+
return instance.document.taskCenter.send('module', { module: name, method: methodName }, args)
314+
}
315+
},
316+
set: function proxySetter (val) {
317+
if (typeof val === 'function') {
318+
return instance.document.taskCenter.send('module', { module: name, method: methodName }, [val])
319+
}
320+
}
321+
})
309322
}
310323
return output
311324
}
@@ -328,15 +341,17 @@ function getInstanceTimer (instanceId, moduleGetter) {
328341
const handler = function () {
329342
args[0](...args.slice(2))
330343
}
344+
331345
timer.setTimeout(handler, args[1])
332-
return instance.callbackId.toString()
346+
return instance.document.taskCenter.callbackManager.lastCallbackId.toString()
333347
},
334348
setInterval: (...args) => {
335349
const handler = function () {
336350
args[0](...args.slice(2))
337351
}
352+
338353
timer.setInterval(handler, args[1])
339-
return instance.callbackId.toString()
354+
return instance.document.taskCenter.callbackManager.lastCallbackId.toString()
340355
},
341356
clearTimeout: (n) => {
342357
timer.clearTimeout(n)
@@ -368,50 +383,53 @@ function callFunction (globalObjects, body) {
368383
}
369384

370385
/**
371-
* Convert all type of values into "safe" format to send to native.
372-
* 1. A `function` will be converted into callback id.
373-
* 2. An `Element` object will be converted into `ref`.
374-
* The `instance` param is used to generate callback id and store
375-
* function if necessary.
376-
* @param {any} v
377-
* @param {object} instance
378-
* @return {any}
386+
* Call a new function generated on the V8 native side.
387+
*
388+
* This function helps speed up bundle compiling. Normally, the V8
389+
* engine needs to download, parse, and compile a bundle on every
390+
* visit. If 'compileBundle()' is available on native side,
391+
* the downloding, parsing, and compiling steps would be skipped.
392+
* @param {object} globalObjects
393+
* @param {string} body
394+
* @return {boolean}
379395
*/
380-
function normalize (v, instance) {
381-
const type = typof(v)
382-
383-
switch (type) {
384-
case 'undefined':
385-
case 'null':
386-
return ''
387-
case 'regexp':
388-
return v.toString()
389-
case 'date':
390-
return v.toISOString()
391-
case 'number':
392-
case 'string':
393-
case 'boolean':
394-
case 'array':
395-
case 'object':
396-
if (v instanceof renderer.Element) {
397-
return v.ref
398-
}
399-
return v
400-
case 'function':
401-
instance.callbacks[++instance.callbackId] = v
402-
return instance.callbackId.toString()
403-
default:
404-
return JSON.stringify(v)
396+
function callFunctionNative (globalObjects, body) {
397+
if (typeof renderer.compileBundle !== 'function') {
398+
return false
405399
}
406-
}
407400

408-
/**
409-
* Get the exact type of an object by `toString()`. For example call
410-
* `toString()` on an array will be returned `[object Array]`.
411-
* @param {any} v
412-
* @return {string}
413-
*/
414-
function typof (v) {
415-
const s = Object.prototype.toString.call(v)
416-
return s.substring(8, s.length - 1).toLowerCase()
401+
let fn = void 0
402+
let isNativeCompileOk = false
403+
let script = '(function ('
404+
const globalKeys = []
405+
const globalValues = []
406+
for (const key in globalObjects) {
407+
globalKeys.push(key)
408+
globalValues.push(globalObjects[key])
409+
}
410+
for (let i = 0; i < globalKeys.length - 1; ++i) {
411+
script += globalKeys[i]
412+
script += ','
413+
}
414+
script += globalKeys[globalKeys.length - 1]
415+
script += ') {'
416+
script += body
417+
script += '} )'
418+
419+
try {
420+
const weex = globalObjects.weex || {}
421+
const config = weex.config || {}
422+
fn = renderer.compileBundle(script,
423+
config.bundleUrl,
424+
config.bundleDigest,
425+
config.codeCachePath)
426+
if (fn && typeof fn === 'function') {
427+
fn(...globalValues)
428+
isNativeCompileOk = true
429+
}
430+
} catch (e) {
431+
console.error(e)
432+
}
433+
434+
return isNativeCompileOk
417435
}

test/weex/runtime/framework.spec.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ describe('framework APIs', () => {
248248
{ method: 'fireEvent', args: [textRef, 'click'] }
249249
])
250250
expect(result instanceof Error).toBe(true)
251-
expect(result).toMatch(/receiveTasks/)
252-
expect(result).toMatch(/not found/)
251+
expect(result).toMatch(/invalid\sinstance\sid/)
252+
expect(result).toMatch(instance.id)
253253
done()
254254
})
255255
})
@@ -341,8 +341,8 @@ describe('framework APIs', () => {
341341
{ method: 'callback', args: [callbackId] }
342342
])
343343
expect(result instanceof Error).toBe(true)
344-
expect(result).toMatch(/receiveTasks/)
345-
expect(result).toMatch(/not found/)
344+
expect(result).toMatch(/invalid\sinstance\sid/)
345+
expect(result).toMatch(instance.id)
346346
done()
347347
})
348348
})

0 commit comments

Comments
 (0)