一、Native
每一个页面都是一个 instance,framework 是全局唯一的,js 和 native 交互,第一个参数都是 instanceID,依靠 instanceID 来去区分不同的页面直接的调用。
在 controller 中创建 instance
_instance = [[WXSDKInstance alloc] init];
1、renderURL
以 iOS 为例:每一个 controller 都持有一个 WXSDKInstance,加载到 JSBundle 代码后,客户端通过 renderURL 的方式来进行加载。
[_instance renderWithURL:[NSURL URLWithString:randomURL] options:@{@"bundleUrl":URL.absoluteString} data:nil];
WXSDKInstance 在初始化方法里面生成 一个 唯一 instanceID,这个 instanceID 就是和 js 交互时 第一个参数,
在 Weex 架构里面,每一个页面都是一个 instance,通过 instanceID 来区分不同的页面。
renderWithURL
WXResourceRequest *request = [WXResourceRequest requestWithURL:url resourceType:WXResourceTypeMainBundle referrer:@"" cachePolicy:NSURLRequestUseProtocolCachePolicy];
[self _renderWithRequest:request options:options data:data];
renderWithURL
生成 request 去加载 jsBundle,里面会判断是否是 本地 file 还是需要从服务器下载,下载成功后调用
- (void)_renderWithMainBundleString:(NSString *)mainBundleString
。
_renderWithMainBundleString
WXPerformBlockOnMainThread(^{
_rootView = [[WXRootView alloc] initWithFrame:self.frame];
_rootView.instance = self;
if(self.onCreate) {
self.onCreate(_rootView);
}
});
// ensure default modules/components/handlers are ready before create instance
[WXSDKEngine registerDefaults];
[self _handleConfigCenter];
[[WXSDKManager bridgeMgr] createInstance:self.instanceId template:mainBundleString options:dictionary data:_jsData];
- create WXRootView 作为 instance 的跟 view
- 确保 components,module 注册。
- 是否需要替换 使用 CoreText 和 Slider
- 调用 js 的
createInstance
。
2、createInstance
renderURL 最终会调用到 WXBridgeContext 的 createInstance 方法:
- (void)createInstance:(NSString *)instance
template:(NSString *)temp
options:(NSDictionary *)options
data:(id)data
{
。。。。。。
NSArray *args = nil;
if (data){
args = @[instance, temp, options ?: @{}, data];
} else {
args = @[instance, temp, options ?: @{}];
}
WX_MONITOR_INSTANCE_PERF_START(WXPTJSCreateInstance, [WXSDKManager instanceForID:instance]);
[self callJSMethod:@"createInstance" args:args];
WX_MONITOR_INSTANCE_PERF_END(WXPTJSCreateInstance, [WXSDKManager instanceForID:instance]);
}
传递的 args是一个 array:
instance: instanceID
temp: js 代码
options:包含 bundleUrl 和 debug
options: {
bundleUrl = "file:///Users/yangshichang/Library/Developer/CoreSimulator/Devices/D5993070-3351-4E74-AEC5-40D97FEC8440/data/Containers/Bundle/Application/5919B561-0EC0-4927-89D8-B2895256D9CF/OperatorWeex.app/bundlejs/index.js";
debug = 1;
}
js
1、crateInstance
native 调起的 crateInstance
会转发到 runtime/init.js 中的 createInstance
function createInstance (id, code, config, data) {
let info = instanceMap[id]
if (!info) {
// Init instance info.
info = checkVersion(code) || {}
if (!frameworks[info.framework]) {
info.framework = 'Weex'
}
// Init instance config.
config = JSON.parse(JSON.stringify(config || {}))
config.bundleVersion = info.version
config.env = JSON.parse(JSON.stringify(global.WXEnvironment || {}))
console.debug(`[JS Framework] create an ${info.framework}@${config.bundleVersion} instance from ${config.bundleVersion}`)
const env = {
info,
config,
created: Date.now(),
framework: info.framework
}
env.services = createServices(id, env, runtimeConfig)
instanceMap[id] = env
return frameworks[info.framework].createInstance(id, code, config, data, env)
}
return new Error(`invalid instance id "${id}"`)
}
这里判断 instance 是否存在,避免重复创建。读取环境信息,判断是哪一个 framework,调用对应 framework 的 createInstance。
生成的 env 对象:
Object = $1
config: {debug: true, bundleUrl: "file:///Users/yangshichang/Library/Developer/CoreS…8-4BB8A4747BC4/OperatorWeex.app/bundlejs/hello.js", bundleVersion: undefined, env: Object}
created: 1500520452139
framework: "Vue"
info: {framework: "Vue"}
services: {service: {}, BroadcastChannel: function}
“Object”原型
因为使用 Vue 写的,所以这里会调用到 weex-vuew-framework.js
function createInstance (
instanceId,
appCode,
config,
data,
env
) {
if ( appCode === void 0 ) appCode = '';
if ( config === void 0 ) config = {};
if ( env === void 0 ) env = {};
// 1. create Document
var document = new renderer.Document(instanceId, config.bundleUrl);
// 2. create instance
var instance = instances[instanceId] = {
instanceId: instanceId, config: config, data: data,
document: document
};
// 3. create 获取 Module 对象的函数,需要 instance 来获取 instance.document.taskCenter。
var moduleGetter = genModuleGetter(instanceId);
// 4. create timerAPIs module
var timerAPIs = getInstanceTimer(instanceId, moduleGetter);
// 5. create weex module
var weexInstanceVar = {
config: config,
document: document,
requireModule: moduleGetter
};
Object.freeze(weexInstanceVar);
// 6. 给 instance 创建 Vue module。
var Vue = instance.Vue = createVueModuleInstance(instanceId, moduleGetter);
// 7. create instanceVars,把 上面创建的对象,打包传递给 callFunction, 生成 执行 JSBundle 的匿名函数。
// The function which create a closure the JS Bundle will run in.
// It will declare some instance variables like `Vue`, HTML5 Timer APIs etc.
var instanceVars = Object.assign({
Vue: Vue,
weex: weexInstanceVar,
// deprecated
__weex_require_module__: weexInstanceVar.requireModule // eslint-disable-line
}, timerAPIs, env.services);
// 8. callFunction(instanceVars, appCode),生成 执行 JSBundle 的匿名函数
if (!callFunctionNative(instanceVars, appCode)) {
// If failed to compile functionBody on native side,
// fallback to 'callFunction()'.
callFunction(instanceVars, appCode);
}
// Send `createFinish` signal to native.
instance.document.taskCenter.send('dom', { action: 'createFinish' }, []);
}
- create Document
- create instance
- create 获取 Module 对象的函数,需要 instance 来获取 instance.document.taskCenter。
- create timerAPIs module
- create weex 对象,Vue 中写的 weex.requireModue 就是调用这里的 genModuleGetter 的这个方法
- 给 instance 创建 Vue module。
- create instanceVars,把 上面创建的对象,打包传递给 callFunction, 生成 执行 JSBundle 的匿名函数。
- callFunction(instanceVars, appCode),生成 执行 JSBundle 的匿名函数
- 发送 instance createFinish 事件。
createInstance(1),创建 Document 对象
Document 在 vdom/document.js
export default function Document (id, url, handler) {
id = id ? id.toString() : ''
this.id = id
this.URL = url
// 1. 将生成的 doc 添加到 `docMap` 中
addDoc(id, this)
// 2. create nodeMap
this.nodeMap = {}
// 3. 读取 Listener,Listener 已经废弃掉了,
const L = Document.Listener || Listener
// 4. 初始化 Listener,这里 handler 是 undefined。没有值的
this.listener = new L(id, handler || createHandler(id, Document.handler)) // deprecated
// 5. create TaskCenter。每一个 Document 都持有一个 TaskCenter。这里 handler 为 undefined,用的是 Document.handler
this.taskCenter = new TaskCenter(id, handler ? (id, ...args) => handler(...args) : Document.handler)
// 6. create 一个 document element。也是一个 Element 的对象,把 role 设置为 'documentElement',
this.createDocumentElement()
}
1、Doucument.handler config.js
中 赋值为 sendTasks
,调用 global.callNative
。
2、每一个 Document 都持有一个 TaskCenter,作为 业务代码和 native 交互的通道。TaskCenter 发送的消息 第一个参数都会带上 instanceID。
TaskCenter
的代码在 runtime/task-center.js
。这里 handler 为 nil,所以用的是 Document.handler
taskCenter 的初始化只是设置 instanceId 和 callbackManager,fallback
(1)callbackManager 的作用就是 在 js 调用 native 时,如果参数带有回调函数,则将这个 callback 暂存在 callbackManager 中,将 生成的 callback id 发送给 native,
当 native callback 给 js 时,根据 callback id,取出对应的 callback 执行。
(2)fallback 赋值给 sendTasks
。
3、documentElement 是作为 根节点 body 的父节点而存在的。
在一个 document 被创建时会自动生成一个 documentElement
,并且 body
应该手动创建并添加到 documentElement
才能工作。body
的 type
必须是一个 div
,list
或 scroller
。
documentElement
另外添加了 2 个 函数,appendChild
和insertBefore
。
Object.defineProperty(el, 'appendChild', {
configurable: true,
enumerable: true,
writable: true,
value: (node) => {
appendBody(this, node)
}
})
Object.defineProperty(el, 'insertBefore', {
configurable: true,
enumerable: true,
writable: true,
value: (node, before) => {
appendBody(this, node, before)
}
})
createInstance(2)创建 instance
var instance = instances[instanceId] = {
instanceId: instanceId, config: config, data: data, document: document
};
生成的 instance 示例:
config: Object
bundleUrl: "file://…",bundleVersion: undefined,debug: true,
env: {scale: 3, appVersion: "1.8.3", deviceModel: "x86_64", appName: "OperatorWeex", platform: "iOS", …}
“Object”原型
data: undefined
document: Document
URL: "file:///…"
documentElement: Element
appendChild: function(node)
attr: {}
children: [] (0)
classStyle: {}
depth: 0
docId: "2"
event: {}
insertBefore: function(node, before)
nodeId: "139"
nodeType: 1
ownerDocument: Document {id: "2", URL: "file:///Users/yangshichang/Library/Developer/CoreS…8-4BB8A4747BC4/OperatorWeex.app/bundlejs/hello.js", nodeMap: Object, listener: Listener, taskCenter: TaskCenter, …}
pureChildren: [] (0)
ref: "_documentElement"
role: "documentElement"
style: {}
type: "document"
“Element”原型
id: "2"
listener: Listener
batched: false
handler: function(tasks)
id: "2"
updates: [] (0)
“Listener”原型
nodeMap: Object
_documentElement: Element {nodeType: 1, nodeId: "139", ref: "_documentElement", type: "document", attr: {}, …}
“Object”原型
taskCenter: TaskCenter
callbackManager: CallbackManager
callbacks: [] (0)
instanceId: undefined
lastCallbackId: 0
“CallbackManager”原型
instanceId: "2"
“TaskCenter”原型
“Document”原型
instanceId: "2"
createInstance(3)生成 获取 module 的方法
genModuleGetter
: 根据 module name,遍历 native 暴露出来的 methodName,生成 js 对应的 module 。调用对应的方法 都是 转发到 taskCenter 去做转发。
genModuleGetter
会作为 weex.requieModule 传递到 JSBundle 里面,JSBundle 中执行的 weex.requireModule
就是这个函数。
function genModuleGetter (instanceId) {
var instance = instances[instanceId];
return function (name) {
var nativeModule = modules[name] || [];
var output = {};
var loop = function ( methodName ) {
Object.defineProperty(output, methodName, {
enumerable: true,
configurable: true,
get: function proxyGetter () {
return function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
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])
}
}
});
};
for (var methodName in nativeModule) loop( methodName );
return output
}
}
createInstance(4)创建 timer
超时 等 回调方法。
createInstance(5)创建 Vue
var Vue = instance.Vue = createVueModuleInstance(instanceId, moduleGetter);
创建一个 Vue 函数,作用就是 JSBundle 的解析器。发送指令到 native 进行界面渲染。
function createVueModuleInstance (instanceId, moduleGetter) {
// 1. create Vue 对象
var exports = {};
VueFactory(exports, renderer);
var Vue = exports.Vue;
var instance = instances[instanceId];
// 2. 创建一个函数,判断 element type 是否保留 element
// patch reserved tag detection to account for dynamically registered
// components
var isReservedTag = Vue.config.isReservedTag || (function () { return false; });
Vue.config.isReservedTag = function (name) {
return components[name] || isReservedTag(name)
};
// 3. 给 Vue 添加 instanceID 和 document 属性
// expose weex-specific info
Vue.prototype.$instanceId = instanceId;
Vue.prototype.$document = instance.document;
// 4. 给 Vue 添加 requireModule 属性
// expose weex native module getter on subVue prototype so that
// vdom runtime modules can access native modules via vnode.context
Vue.prototype.$requireWeexModule = moduleGetter;
// 5. 添加一个 beforeCreate 钩子函数,在 root component 创建之前,将 外部 传入的 data 和 $options 中的 data 合并。
// data 是 createInstance 的时候,从 native 传过来的。
// 把根 vm 设置为 instance 的 app 属性。
// Hack `Vue` behavior to handle instance information and data
// before root component created.
Vue.mixin({
beforeCreate: function beforeCreate () {
var options = this.$options;
// root component (vm)
if (options.el) {
// set external data of instance
var dataOption = options.data;
var internalData = (typeof dataOption === 'function' ? dataOption() : dataOption) || {};
options.data = Object.assign(internalData, instance.data);
// record instance by id
instance.app = this;
}
}
});
/**
* @deprecated Just instance variable `weex.config`
* Get instance config.
* @return {object}
*/
Vue.prototype.$getConfig = function () {
if (instance.app instanceof Vue) {
return instance.config
}
};
return Vue
}
- create Vue 函数
- 创建一个函数,判断 element type 是否保留 element
- 给 Vue 添加 instanceID 和 document 属性
- 给 Vue 添加 requireModule 属性
- 添加一个 beforeCreate 钩子函数,在 root component 创建之前,将 外部 传入的 data 和 $options 中的 data 合并。
data 是 createInstance 的时候,从 native 传过来的。
VueFactory(exports, renderer);
VueFactory 定义在 weex-vue-framework factory.js。 是打包好的 weex 平台的 vue 核心库。
初始化一个 Vue 函数。
'use strict';
module.exports = function weexFactory (exports, renderer) {
。。。。。。
function initMixin (Vue) {
Vue.prototype._init = function (options) {
。。。。。。。
}
}
。。。。。
function Vue$2 (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue$2)) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
this._init(options);
}
initMixin(Vue$2);
stateMixin(Vue$2);
eventsMixin(Vue$2);
lifecycleMixin(Vue$2);
renderMixin(Vue$2);
。。。。。
/* */
// install platform specific utils
Vue$2.config.mustUseProp = mustUseProp;
Vue$2.config.isReservedTag = isReservedTag;
Vue$2.config.isUnknownElement = isUnknownElement;
// install platform runtime directives and components
Vue$2.options.directives = platformDirectives;
Vue$2.options.components = platformComponents;
// install platform patch function
Vue$2.prototype.__patch__ = patch;
// wrap mount
Vue$2.prototype.$mount = function (
el,
hydrating
) {
return mountComponent(
this,
el && query(el, this.$document),
hydrating
)
};
// this entry is built and wrapped with a factory function
// used to generate a fresh copy of Vue for every Weex instance.
exports.Vue = Vue$2;
}
isReservedTag
判断是否是 保留 tag,根据 components 里面的类型 和 Vue.config.isReservedTag
:
var isReservedTag = makeMap(
'template,script,style,element,content,slot,link,meta,svg,view,' +
'a,div,img,image,text,span,richtext,input,switch,textarea,spinner,select,' +
'slider,slider-neighbor,indicator,trisition,trisition-group,canvas,' +
'list,cell,header,loading,loading-indicator,refresh,scrollable,scroller,' +
'video,web,embed,tabbar,tabheader,datepicker,timepicker,marquee,countdown',
true
);
components 就是 native 注册的 组件。
createInstance callFunction
解析 JSBundle 代码
callFunction 的作用就是解析执行 JSBundle 的代码。
function callFunction (globalObjects, body) {
var globalKeys = [];
var globalValues = [];
for (var key in globalObjects) {
globalKeys.push(key);
globalValues.push(globalObjects[key]);
}
globalKeys.push(body);
var result = new (Function.prototype.bind.apply( Function, [ null ].concat( globalKeys) ));
return result.apply(void 0, globalValues)
}
globalKeys
:
Array (10) = $2
0 "Vue"
1 "weex"
2 "__weex_require_module__"
3 "setTimeout"
4 "setInterval"
5 "clearTimeout"
6 "clearInterval"
7 "service"
8 "BroadcastChannel"
9 "// { \"framework\": \"Vue\" }↵/******/ (function(modules) { // webpackBootstrap↵/******/ // The module cache↵/******/ var installedModules …"
“Array”原型
var result = new (Function.prototype.bind.apply( Function, [ null ].concat( globalKeys) ));
生成一个匿名函数,参数为 Vue….BroadcastChannel
。函数体为 body
。
result.apply(void 0, globalValues)
执行 这个匿名函数
function anonymous(Vue, weex, __weex_require_module__, setTimeout, setInterval, clearTimeout, clearInterval, service, BroadcastChannel) {
// { "framework": "Vue" }
/******/ (function(modules){ // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId){}
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
})
({
/***/ 0:
/***/ (function(module, exports, __webpack_require__) {
.........
var __vue_exports__, __vue_options__
var __vue_styles__ = []
/* styles */
__vue_styles__.push(__webpack_require__(133)
)
/* script */
__vue_exports__ = __webpack_require__(134)
/* template */
var __vue_template__ = __webpack_require__(135)
.........
module.exports = __vue_exports__
module.exports.el = 'true'
new Vue(module.exports)
}),
/***/ 133:
/***/ (function(module, exports) {}),
/***/ 134:
/***/ (function(module, exports) {}),
/***/ 135:
/***/ (function(module, exports) {})
});
}
入口就是 moduleId==0的 module。里面会 把所有的 module 都执行起来,
一般 最后三个就是 Vue 代码中的 template
、script
和style
。具体可在0的函数体中看到
如果 script
中有 require
则 调用 moduleGetter 读取 Module 的方法,并生成 Module。
例如 var event = weex.requireModule('event');
就会 生成 Event
module 对象。
最后一句 new Vue(module.exports)
用解析好的对象作为参数 实例化一个 Vue
对象。
new Vue(exports)
从上面的 Vue 函数可以看出 new Vue()
会调用到 Vue._init 方法
function initMixin (Vue) {
Vue.prototype._init = function (options) {
// 1. create vm, Vue 的实例化对象。
var vm = this;
// 2. 设置 uid
// a uid
vm._uid = uid++;
var startTag, endTag;
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = "vue-perf-init:" + (vm._uid);
endTag = "vue-perf-end:" + (vm._uid);
mark(startTag);
}
// a flag to avoid this being observed
vm._isVue = true;
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options);
} else {
// 3. 将传入的这些options选项挂载到vm.$options属性上
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
);
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm);
} else {
vm._renderProxy = vm;
}
// expose real self
vm._self = vm; // 自身的实例
// 接下来所有的操作都是在这个实例上添加方法
// lifecycle初始化
initLifecycle(vm);
// events初始化 vm._events, 主要是提供vm实例上的$on/$emit/$off/$off等方法
initEvents(vm);
// 初始化渲染函数,在vm上绑定$createElement方法
initRender(vm);
// 执行钩子函数, beforeCreate
callHook(vm, 'beforeCreate');
initInjections(vm); // resolve injections before data/props
// Observe data添加对data的监听, 将data中的 key 转化为getters/setters
initState(vm);
initProvide(vm); // resolve provide after data/props
// 执行钩子函数, created
callHook(vm, 'created');
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false);
mark(endTag);
measure(((vm._name) + " init"), startTag, endTag);
}
// vm挂载的根元素, el 在 JSBundle 中设置为 true
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
};
}
options 就是 解析好的 JSBundle 的对象。
Vue.init(options) (1) mergeOptions
merge options 的作用就是 把 连个 对象合成一个。这里 就是 把 Vue 的方法 合并到 vm.$options 上。
function mergeOptions (
parent,
child,
vm
) {
。。。。。。
var options = {};
var key;
for (key in parent) {
mergeField(key);
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key);
}
}
function mergeField (key) {
var strat = strats[key] || defaultStrat;
options[key] = strat(parent[key], child[key], vm, key);
}
return options
}
这里的 parent 就是 Vue 的构造函数,是:
parent:
Object = $5
_base: function(options)
beforeCreate: [function] (1)
components: {Transition: Object, TransitionGroup: Object}
directives: {}
filters: {}
“Object”原型
Vue.init(options)(2)initProxy(vm)
代理 vm 的 get 方法,拦截 vm 属性的 读取操作。如果 vm 的某一个属性不存在,则抛出一个warning。
Vue.init(options)(3)initLifecycle(vm)
初始化 vm 生命周期的关键属性值。
vm.$parent = parent;
vm.$root = parent ? parent.$root : vm;
vm.$children = [];
vm.$refs = {};
vm._watcher = null;
vm._inactive = null;
vm._directInactive = false;
vm._isMounted = false;
vm._isDestroyed = false;
vm._isBeingDestroyed = false;
Vue.init(options)(4)initEvents(vm)
function initEvents (vm) {
vm._events = Object.create(null);
vm._hasHookEvent = false;
// init parent attached events
var listeners = vm.$options._parentListeners;
if (listeners) {
updateComponentListeners(vm, listeners);
}
}
初始化 节点上绑定的 on 事件。这里先初始化一个空的 event对象,判断如果 判断 vm 是否有 _parentListeners,有才会更新。
其实 每一个独立文件的 component 都会创建一个 vm。这里初始化,就是指更新 该 vm 作为 子节点时候,是否绑定有事件。
Vue.init(options)(5)initRender(vm)
function initRender (vm) {
......
// bind the createElement fn to this instance
// so that we get proper render context inside it.
// args order: tag, data, children, normalizationType, alwaysNormalize
// internal version is used by render functions compiled from templates
vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };
// normalization is always applied for the public version, used in
// user-written render functions.
vm.$createElement = function (a, b, c, d) { return createElement(vm, a, b, c, d, true); };
}
绑定 createElement
方法到 vm
,渲染 js 都是调用这里的 createElement。
这两个绑定 最后一个参数不同,alwaysNormalize
Vue.init(options)(6)callHook(vm, 'beforeCreate')
beforeCreate
在 createVueModuleInstance
方法中进行了设置,主要是为了 把 vm 中的 data 和 native 传过来的 data 合并。
beforeCreate:
var internalData = (typeof dataOption === 'function' ? dataOption() : dataOption) || {};
如果 dataOption 是 函数,则只需,否则直接读取。
Vue.init(options)(3)initInjections(vm)
Vue.init(options)(3)initProvide(vm)
provide
和inject
主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。如果你熟悉 React,这与 React 的上下文特性很相似。
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // -> "bar"
}
// ...
}
Vue.init(options)(3)initState(vm)
这里面初始化 vm 的data,method 等等,非常重要
function initState (vm) {
// 1. 在vm上初始化一个_watchers数组,缓存这个vm上的所有watcher
vm._watchers = [];
// 2. 读取 vm 的 options。
var opts = vm.$options;
// 下面这些都对应