/
entry-framework.js
226 lines (187 loc) · 5.47 KB
/
entry-framework.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/**
* Created by zhiyuan.huang@rdder.com on 17/6/5.
*/
'use strict'
const VueFactory = require('./factory.js').VueFactory
const instances = {}
const components = {}
const modules = {}
const renderer = {
instances,
modules,
components
}
export const version = '__DDDER_VERSION__';
export function init (cfg) {
renderer.Document = cfg.Document
renderer.Element = cfg.Element
renderer.Anime = cfg.Anime
}
export function reset () {
clear(instances)
clear(modules)
clear(components)
delete renderer.Document
delete renderer.Element
delete renderer.Comment
}
function clear (obj) {
for (const key in obj) {
delete obj[key]
}
}
/**
* Create an instance with id, code, config and external data.
* @param {string} instanceId
* @param {string} appCode
* @param {object} config
* @param {object} data
* @param {object} env { info, config, services }
*/
export function createInstance (
instanceId,
appCode = '',
config = {},
data,
env = {},
parentInstanceId
) {
// Virtual-DOM object.
const document = new renderer.Document(instanceId)
const instance = instances[instanceId] = { instanceId, config, data, document, parentInstanceId }
const ddderInstanceVar = {
config,
document,
env
}
Object.freeze(ddderInstanceVar)
// Each instance has a independent `Vue` module instance
const Vue = instance.Vue = createVueModuleInstance(instanceId)
// The function which create a closure the JS Bundle will run in.
// It will declare some instance variables like `Vue`, HTML5 Timer APIs etc.
const instanceVars = Object.assign({
Vue,
ddder: ddderInstanceVar
})
// todo: ddder 暂时没有 bundle 为一个文件,所以,应该不是以string 类型的appCode 来作为入口调用的
// callFunction(instanceVars, appCode)
return instanceVars
}
/**
* Destroy an instance with id. It will make sure all memory of
* this instance released and no more leaks.
* @param {string} instanceId
*/
export function destroyInstance (instanceId) {
const instance = instances[instanceId]
if (instance && instance.app instanceof instance.Vue) {
instance.app.$destroy()
}
delete instances[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.
* @param {string} instanceId
* @param {object} data
*/
export function refreshInstance (instanceId, data) {
const instance = instances[instanceId]
if (!instance || !(instance.app instanceof instance.Vue)) {
return new Error(`refreshInstance: instance ${instanceId} not found!`)
}
for (const key in data) {
instance.Vue.set(instance.app, key, data[key])
}
}
/**
* Get the JSON object of the root element.
* @param {string} instanceId
*/
export function getRoot (instanceId) {
const instance = instances[instanceId]
if (!instance || !(instance.app instanceof instance.Vue)) {
return new Error(`getRoot: instance ${instanceId} not found!`)
}
return instance.app.$el.toJSON()
}
/**
* Register native components information.
* @param {array} newComponents
*/
export function registerComponents (newComponents) {
if (Array.isArray(newComponents)) {
newComponents.forEach(component => {
if (!component) return
if (typeof component === 'string') {
components[component] = true
} else if (typeof component === 'object' && typeof component.type === 'string') {
components[component.type] = component
}
})
}
}
/**
* Create a fresh instance of Vue for each Weex instance.
*/
function createVueModuleInstance (instanceId) {
const instance = instances[instanceId];
const parentInstance = instance.parentInstanceId && instances[instance.parentInstanceId];
let Vue;
if (parentInstance && parentInstance.document.Vue) {
const SuperVue = parentInstance.document.Vue
Vue = SuperVue.extend()
} else {
const exports = {}
VueFactory(exports, renderer)
Vue = exports.Vue
}
Vue.version = version
// patch reserved tag detection to account for dynamically registered
// components
const isReservedTag = Vue.config.isReservedTag || (() => false)
Vue.config.isReservedTag = name => {
return components[name] || isReservedTag(name)
}
// expose ddder-specific info
instance.document.Vue = Vue
Vue.document = instance.document
Vue.prototype.$instanceId = instanceId
Vue.prototype.$document = instance.document
// 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
}
}
})
return Vue
}
/**
* Call a new function body with some global objects.
* @param {object} globalObjects
* @param {string} code
* @return {any}
*/
/* eslint-disable no-unused-vars */
function callFunction (globalObjects, body) {
const globalKeys = []
const globalValues = []
for (const key in globalObjects) {
globalKeys.push(key)
globalValues.push(globalObjects[key])
}
globalKeys.push(body)
const result = new Function(...globalKeys)
return result(...globalValues)
}