@@ -22,7 +22,7 @@ export function init (cfg) {
22
22
renderer . Document = cfg . Document
23
23
renderer . Element = cfg . Element
24
24
renderer . Comment = cfg . Comment
25
- renderer . sendTasks = cfg . sendTasks
25
+ renderer . compileBundle = cfg . compileBundle
26
26
}
27
27
28
28
/**
@@ -35,7 +35,7 @@ export function reset () {
35
35
delete renderer . Document
36
36
delete renderer . Element
37
37
delete renderer . Comment
38
- delete renderer . sendTasks
38
+ delete renderer . compileBundle
39
39
}
40
40
41
41
/**
@@ -66,18 +66,9 @@ export function createInstance (
66
66
// Virtual-DOM object.
67
67
const document = new renderer . Document ( instanceId , config . bundleUrl )
68
68
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
-
78
69
const instance = instances [ instanceId ] = {
79
70
instanceId, config, data,
80
- document, callbacks , callbackId
71
+ document
81
72
}
82
73
83
74
// Prepare native module getter and HTML5 Timer APIs.
@@ -102,11 +93,16 @@ export function createInstance (
102
93
weex : weexInstanceVar ,
103
94
// deprecated
104
95
__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
+ }
107
103
108
104
// Send `createFinish` signal to native.
109
- renderer . sendTasks ( instanceId + ' ', [ { module : 'dom' , method : ' createFinish', args : [ ] } ] , - 1 )
105
+ instance . document . taskCenter . send ( 'dom ', { action : 'createFinish' } , [ ] )
110
106
}
111
107
112
108
/**
@@ -117,6 +113,7 @@ export function createInstance (
117
113
export function destroyInstance ( instanceId ) {
118
114
const instance = instances [ instanceId ]
119
115
if ( instance && instance . app instanceof instance . Vue ) {
116
+ instance . document . destroy ( )
120
117
instance . app . $destroy ( )
121
118
}
122
119
delete instances [ instanceId ]
@@ -138,7 +135,7 @@ export function refreshInstance (instanceId, data) {
138
135
instance . Vue . set ( instance . app , key , data [ key ] )
139
136
}
140
137
// Finally `refreshFinish` signal needed.
141
- renderer . sendTasks ( instanceId + ' ', [ { module : 'dom' , method : ' refreshFinish', args : [ ] } ] , - 1 )
138
+ instance . document . taskCenter . send ( 'dom ', { action : 'refreshFinish' } , [ ] )
142
139
}
143
140
144
141
/**
@@ -153,41 +150,51 @@ export function getRoot (instanceId) {
153
150
return instance . app . $el . toJSON ( )
154
151
}
155
152
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
+
156
176
/**
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`
162
181
*/
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 ) )
186
193
}
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` )
191
198
}
192
199
193
200
/**
@@ -288,9 +295,7 @@ function createVueModuleInstance (instanceId, moduleGetter) {
288
295
* Generate native module getter. Each native module has several
289
296
* methods to call. And all the behaviors is instance-related. So
290
297
* 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.
294
299
* @param {string } instanceId
295
300
* @return {function }
296
301
*/
@@ -300,12 +305,20 @@ function genModuleGetter (instanceId) {
300
305
const nativeModule = modules [ name ] || [ ]
301
306
const output = { }
302
307
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
+ } )
309
322
}
310
323
return output
311
324
}
@@ -328,15 +341,17 @@ function getInstanceTimer (instanceId, moduleGetter) {
328
341
const handler = function ( ) {
329
342
args [ 0 ] ( ...args . slice ( 2 ) )
330
343
}
344
+
331
345
timer . setTimeout ( handler , args [ 1 ] )
332
- return instance . callbackId . toString ( )
346
+ return instance . document . taskCenter . callbackManager . lastCallbackId . toString ( )
333
347
} ,
334
348
setInterval : ( ...args ) => {
335
349
const handler = function ( ) {
336
350
args [ 0 ] ( ...args . slice ( 2 ) )
337
351
}
352
+
338
353
timer . setInterval ( handler , args [ 1 ] )
339
- return instance . callbackId . toString ( )
354
+ return instance . document . taskCenter . callbackManager . lastCallbackId . toString ( )
340
355
} ,
341
356
clearTimeout : ( n ) => {
342
357
timer . clearTimeout ( n )
@@ -368,50 +383,53 @@ function callFunction (globalObjects, body) {
368
383
}
369
384
370
385
/**
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 }
379
395
*/
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
405
399
}
406
- }
407
400
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
417
435
}
0 commit comments