Skip to content

Commit 53d0592

Browse files
committed
[weex] use factory pattern to create fresh Vue copies for each Weex instance
1 parent 3b34b02 commit 53d0592

File tree

7 files changed

+105
-93
lines changed

7 files changed

+105
-93
lines changed

build/config.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ const banner =
1313
' * Released under the MIT License.\n' +
1414
' */'
1515

16+
const weexFactoryPlugin = {
17+
intro () {
18+
return 'module.exports = function weexFactory (exports, renderer) {'
19+
},
20+
outro () {
21+
return '}'
22+
}
23+
}
24+
1625
const builds = {
1726
// Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
1827
'web-runtime-cjs': {
@@ -77,6 +86,14 @@ const builds = {
7786
format: 'cjs',
7887
external: ['stream', 'module', 'vm', 'he', 'de-indent']
7988
},
89+
// Weex runtime factory
90+
'weex-factory': {
91+
weex: true,
92+
entry: path.resolve(__dirname, '../src/entries/weex-factory.js'),
93+
dest: path.resolve(__dirname, '../packages/weex-vue-framework/factory.js'),
94+
format: 'cjs',
95+
plugins: [weexFactoryPlugin]
96+
},
8097
// Weex runtime framework (CommonJS).
8198
'weex-framework': {
8299
weex: true,
@@ -111,7 +128,7 @@ function genConfig (opts) {
111128
flow(),
112129
buble(),
113130
alias(Object.assign({}, require('./alias'), opts.alias))
114-
]
131+
].concat(opts.plugins || [])
115132
}
116133

117134
if (opts.env) {

src/entries/weex-factory.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// this entry is built and wrapped with a factory function
2+
// used to generate a fresh copy of Vue for every Weex instance.
3+
4+
import Vue from 'weex/runtime/index'
5+
6+
exports.Vue = Vue

src/platforms/weex/framework.js

Lines changed: 71 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
import Vue from 'weex/runtime/index'
2-
import renderer from 'weex/runtime/config'
1+
import TextNode from 'weex/runtime/text-node'
32

4-
Vue.weexVersion = '__WEEX_VERSION__'
5-
export { Vue }
3+
// this will be preserved during build
4+
const VueFactory = require('./factory')
65

7-
const {
6+
const instances = {}
7+
const modules = {}
8+
const components = {}
9+
10+
const renderer = {
11+
TextNode,
812
instances,
913
modules,
1014
components
11-
} = renderer
12-
13-
let activeId
14-
const oriIsReservedTag = (Vue && Vue.config && typeof Vue.config.isReservedTag === 'function') ? Vue.config.isReservedTag : function () {}
15+
}
1516

1617
/**
1718
* Prepare framework config, basically about the virtual-DOM and JS bridge.
@@ -35,7 +36,6 @@ export function reset () {
3536
delete renderer.Element
3637
delete renderer.Comment
3738
delete renderer.sendTasks
38-
Vue.config.isReservedTag = oriIsReservedTag
3939
}
4040

4141
/**
@@ -63,9 +63,6 @@ export function createInstance (
6363
data,
6464
env = {}
6565
) {
66-
// Set active instance id and put some information into `instances` map.
67-
activeId = instanceId
68-
6966
// Virtual-DOM object.
7067
const document = new renderer.Document(instanceId, config.bundleUrl)
7168

@@ -78,7 +75,7 @@ export function createInstance (
7875
// The latest callback id, incremental.
7976
const callbackId = 1
8077

81-
instances[instanceId] = {
78+
const instance = instances[instanceId] = {
8279
instanceId, config, data,
8380
document, callbacks, callbackId
8481
}
@@ -95,26 +92,13 @@ export function createInstance (
9592
}
9693
Object.freeze(weexInstanceVar)
9794

98-
// Each instance has a independent `Vue` variable and it should have
99-
// all top-level public APIs.
100-
const subVue = Vue.extend({})
101-
102-
// ensure plain-object components are extended from the subVue
103-
subVue.options._base = subVue
104-
105-
// expose global utility
106-
;['util', 'set', 'delete', 'nextTick', 'version', 'weexVersion', 'config'].forEach(name => {
107-
subVue[name] = Vue[name]
108-
})
109-
110-
// expose weex native module getter on subVue prototype so that
111-
// vdom runtime modules can access native modules via vnode.context
112-
subVue.prototype._requireWeexModule = moduleGetter
95+
// Each instance has a independent `Vue` mdoule instance
96+
const Vue = instance.Vue = createVueModuleInstance(instanceId, moduleGetter)
11397

11498
// The function which create a closure the JS Bundle will run in.
11599
// It will declare some instance variables like `Vue`, HTML5 Timer APIs etc.
116100
const instanceVars = Object.assign({
117-
Vue: subVue,
101+
Vue,
118102
weex: weexInstanceVar,
119103
__weex_require_module__: weexInstanceVar.requireModule // deprecated
120104
}, timerAPIs)
@@ -131,7 +115,7 @@ export function createInstance (
131115
*/
132116
export function destroyInstance (instanceId) {
133117
const instance = instances[instanceId] || {}
134-
if (instance.app instanceof Vue) {
118+
if (instance.app instanceof instance.Vue) {
135119
instance.app.$destroy()
136120
}
137121
delete instances[instanceId]
@@ -146,11 +130,11 @@ export function destroyInstance (instanceId) {
146130
*/
147131
export function refreshInstance (instanceId, data) {
148132
const instance = instances[instanceId] || {}
149-
if (!(instance.app instanceof Vue)) {
133+
if (!(instance.app instanceof instance.Vue)) {
150134
return new Error(`refreshInstance: instance ${instanceId} not found!`)
151135
}
152136
for (const key in data) {
153-
Vue.set(instance.app, key, data[key])
137+
instance.Vue.set(instance.app, key, data[key])
154138
}
155139
// Finally `refreshFinish` signal needed.
156140
renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'refreshFinish', args: [] }], -1)
@@ -162,7 +146,7 @@ export function refreshInstance (instanceId, data) {
162146
*/
163147
export function getRoot (instanceId) {
164148
const instance = instances[instanceId] || {}
165-
if (!(instance.app instanceof Vue)) {
149+
if (!(instance.app instanceof instance.Vue)) {
166150
return new Error(`getRoot: instance ${instanceId} not found!`)
167151
}
168152
return instance.app.$el.toJSON()
@@ -177,7 +161,7 @@ export function getRoot (instanceId) {
177161
*/
178162
export function receiveTasks (instanceId, tasks) {
179163
const instance = instances[instanceId] || {}
180-
if (!(instance.app instanceof Vue)) {
164+
if (!(instance.app instanceof instance.Vue)) {
181165
return new Error(`receiveTasks: instance ${instanceId} not found!`)
182166
}
183167
const { callbacks, document } = instance
@@ -229,68 +213,75 @@ export function registerModules (newModules) {
229213
* @param {array} newComponents
230214
*/
231215
export function registerComponents (newComponents) {
232-
const config = Vue.config
233-
const newConfig = {}
234216
if (Array.isArray(newComponents)) {
235217
newComponents.forEach(component => {
236218
if (!component) {
237219
return
238220
}
239221
if (typeof component === 'string') {
240222
components[component] = true
241-
newConfig[component] = true
242223
} else if (typeof component === 'object' && typeof component.type === 'string') {
243224
components[component.type] = component
244-
newConfig[component.type] = true
245225
}
246226
})
247-
const oldIsReservedTag = config.isReservedTag
248-
config.isReservedTag = name => {
249-
return newConfig[name] || oldIsReservedTag(name)
250-
}
251227
}
252228
}
253229

254-
// Hack `Vue` behavior to handle instance information and data
255-
// before root component created.
256-
Vue.mixin({
257-
beforeCreate () {
258-
const options = this.$options
259-
const parentOptions = (options.parent && options.parent.$options) || {}
260-
261-
// root component (vm)
262-
if (options.el) {
263-
// record instance info
264-
const instance = instances[activeId] || {}
265-
this.$instanceId = activeId
266-
options.instanceId = activeId
267-
this.$document = instance.document
268-
269-
// set external data of instance
270-
const dataOption = options.data
271-
const internalData = (typeof dataOption === 'function' ? dataOption() : dataOption) || {}
272-
options.data = Object.assign(internalData, instance.data)
273-
274-
// record instance by id
275-
instance.app = this
276-
277-
activeId = undefined
278-
} else {
279-
this.$instanceId = options.instanceId = parentOptions.instanceId
280-
}
281-
}
282-
})
283-
284230
/**
285-
* @deprecated Just instance variable `weex.config`
286-
* Get instance config.
287-
* @return {object}
231+
* Create a fresh instance of Vue for each Weex instance.
288232
*/
289-
Vue.prototype.$getConfig = function () {
290-
const instance = instances[this.$instanceId] || {}
291-
if (instance.app instanceof Vue) {
292-
return instance.config
233+
function createVueModuleInstance (instanceId, moduleGetter) {
234+
const exports = {}
235+
VueFactory(exports, renderer)
236+
const Vue = exports.Vue
237+
238+
const instance = instances[instanceId]
239+
240+
// patch reserved tag detection to account for dynamically registered
241+
// components
242+
const isReservedTag = Vue.config.isReservedTag || (() => false)
243+
Vue.config.isReservedTag = name => {
244+
return components[name] || isReservedTag(name)
245+
}
246+
247+
// expose weex-specific info
248+
Vue.prototype.$instanceId = instanceId
249+
Vue.prototype.$document = instance.document
250+
251+
// expose weex native module getter on subVue prototype so that
252+
// vdom runtime modules can access native modules via vnode.context
253+
Vue.prototype.$requireWeexModule = moduleGetter
254+
255+
// Hack `Vue` behavior to handle instance information and data
256+
// before root component created.
257+
Vue.mixin({
258+
beforeCreate () {
259+
const options = this.$options
260+
// root component (vm)
261+
if (options.el) {
262+
// set external data of instance
263+
const dataOption = options.data
264+
const internalData = (typeof dataOption === 'function' ? dataOption() : dataOption) || {}
265+
options.data = Object.assign(internalData, instance.data)
266+
// record instance by id
267+
instance.app = this
268+
}
269+
}
270+
})
271+
272+
/**
273+
* @deprecated Just instance variable `weex.config`
274+
* Get instance config.
275+
* @return {object}
276+
*/
277+
Vue.prototype.$getConfig = function () {
278+
const instance = instances[this.$instanceId] || {}
279+
if (instance.app instanceof Vue) {
280+
return instance.config
281+
}
293282
}
283+
284+
return Vue
294285
}
295286

296287
/**

src/platforms/weex/runtime/modules/transition.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ function enter (_, vnode) {
102102
enterHook && enterHook(el, cb)
103103

104104
if (needAnimation) {
105-
const animation = vnode.context._requireWeexModule('animation')
105+
const animation = vnode.context.$requireWeexModule('animation')
106106
animation.transition(el.ref, {
107107
styles: endState,
108108
duration: transitionProperties.duration || 0,
@@ -188,7 +188,7 @@ function leave (vnode, rm) {
188188
}
189189

190190
function performLeave () {
191-
const animation = vnode.context._requireWeexModule('animation')
191+
const animation = vnode.context.$requireWeexModule('animation')
192192
// the delayed leave may have already been cancelled
193193
if (cb.cancelled) {
194194
return

src/platforms/weex/runtime/node-ops.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import renderer from './config'
1+
/* globals renderer */
2+
// renderer is injected by weex factory wrapper
23

34
export const namespaceMap = {}
45

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
let latestNodeId = 1
22

3-
function TextNode (text) {
3+
export default function TextNode (text) {
44
this.instanceId = ''
55
this.nodeId = latestNodeId++
66
this.parentNode = null
77
this.nodeType = 3
88
this.text = text
99
}
10-
11-
export default {
12-
TextNode,
13-
instances: {},
14-
modules: {},
15-
components: {}
16-
}

src/platforms/weex/util/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1+
/* globals renderer */
2+
13
import { makeMap } from 'shared/util'
2-
import renderer from '../runtime/config'
34

45
export const isReservedTag = makeMap(
56
'div,img,image,input,switch,indicator,list,scroller,cell,template,text,slider,image'
67
)
8+
79
export function isUnaryTag () { /* console.log('isUnaryTag') */ }
810
export function mustUseProp () { /* console.log('mustUseProp') */ }
911
export function getTagNamespace () { /* console.log('getTagNamespace') */ }
1012
export function isUnknownElement () { /* console.log('isUnknownElement') */ }
13+
1114
export function query (el, document) {
15+
// renderer is injected by weex factory wrapper
1216
const placeholder = new renderer.Comment('root')
1317
placeholder.hasAttribute = placeholder.removeAttribute = function () {} // hack for patch
1418
document.documentElement.appendChild(placeholder)

0 commit comments

Comments
 (0)