提交 5bef921d 编写于 作者: fxy060608's avatar fxy060608

feat(mp): refactor

上级 63fa0d5e
......@@ -36,7 +36,8 @@ module.exports = {
plugins: [
alias({
'uni-shared': path.resolve(__dirname, '../src/shared/util.js'),
'uni-platform': path.resolve(__dirname, '../src/platforms/' + process.env.UNI_PLATFORM)
'uni-platform': path.resolve(__dirname, '../src/platforms/' + process.env.UNI_PLATFORM),
'uni-wrapper': path.resolve(__dirname, '../src/core/runtime/wrapper')
}),
replace({
__GLOBAL__: platform.prefix,
......
......@@ -40,7 +40,7 @@ const camelize = cached((str) => {
return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
});
const SYNC_API_RE = /subNVue|requireNativePlugin|upx2px|hideKeyboard|canIUse|^create|Sync$|Manager$/;
const SYNC_API_RE = /subNVue|requireNativePlugin|upx2px|hideKeyboard|canIUse|^create|Sync$|Manager$|base64ToArrayBuffer|arrayBufferToBase64/;
const CONTEXT_API_RE = /^create|Manager$/;
......@@ -89,15 +89,17 @@ function promisify (name, api) {
fail: reject
}), ...params);
/* eslint-disable no-extend-native */
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
}))
}
}
......@@ -371,69 +373,18 @@ Component = function (options = {}) {
return MPComponent(options)
};
const mocks = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__'];
function initBehavior (options) {
return Behavior(options)
}
function initRefs (vm) {
const mpInstance = vm.$scope;
Object.defineProperty(vm, '$refs', {
get () {
const $refs = {};
const components = mpInstance.selectAllComponents('.vue-ref');
components.forEach(component => {
const ref = component.dataset.ref;
$refs[ref] = component.$vm || component;
});
const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for');
forComponents.forEach(component => {
const ref = component.dataset.ref;
if (!$refs[ref]) {
$refs[ref] = [];
}
$refs[ref].push(component.$vm || component);
});
return $refs
}
});
}
function triggerLink (mpInstance, vueOptions) {
mpInstance.triggerEvent('__l', mpInstance.$vm || vueOptions, {
bubbles: true,
composed: true
});
}
function handleLink (event) {
if (event.detail.$mp) { // vm
if (!event.detail.$parent) {
event.detail.$parent = this.$vm;
event.detail.$parent.$children.push(event.detail);
event.detail.$root = this.$vm.$root;
}
} else { // vueOptions
if (!event.detail.parent) {
event.detail.parent = this.$vm;
}
}
}
function initPage$1 (pageOptions) {
return initComponent$1(pageOptions)
}
const PAGE_EVENT_HOOKS = [
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap'
];
function initComponent$1 (componentOptions) {
componentOptions.methods.$getAppWebview = function () {
return plus.webview.getWebviewById(`${this.__wxWebviewId__}`)
};
return Component(componentOptions)
}
function initMocks (vm, mocks$$1) {
function initMocks (vm, mocks) {
const mpInstance = vm.$mp[vm.mpType];
mocks$$1.forEach(mock => {
mocks.forEach(mock => {
if (hasOwn(mpInstance, mock)) {
vm[mock] = mpInstance[mock];
}
......@@ -443,12 +394,46 @@ function initMocks (vm, mocks$$1) {
function initHooks (mpOptions, hooks) {
hooks.forEach(hook => {
mpOptions[hook] = function (args) {
return this.$vm.__call_hook(hook, args)
return this.$vm && this.$vm.__call_hook(hook, args)
};
});
}
function getData (vueOptions, context) {
function initVueComponent (Vue$$1, vueOptions) {
vueOptions = vueOptions.default || vueOptions;
let VueComponent;
if (isFn(vueOptions)) {
VueComponent = vueOptions;
vueOptions = VueComponent.extendOptions;
} else {
VueComponent = Vue$$1.extend(vueOptions);
}
return [VueComponent, vueOptions]
}
function initSlots (vm, vueSlots) {
if (Array.isArray(vueSlots) && vueSlots.length) {
const $slots = Object.create(null);
vueSlots.forEach(slotName => {
$slots[slotName] = true;
});
vm.$scopedSlots = vm.$slots = $slots;
}
}
function initVueIds (vueIds, mpInstance) {
vueIds = (vueIds || '').split(',');
const len = vueIds.length;
if (len === 1) {
mpInstance._$vueId = vueIds[0];
} else if (len === 2) {
mpInstance._$vueId = vueIds[0];
mpInstance._$vuePid = vueIds[1];
}
}
function initData (vueOptions, context) {
let data = vueOptions.data || {};
const methods = vueOptions.methods || {};
......@@ -490,7 +475,7 @@ function createObserver (name) {
}
}
function getBehaviors (vueOptions) {
function initBehaviors (vueOptions, initBehavior) {
const vueBehaviors = vueOptions['behaviors'];
const vueExtends = vueOptions['extends'];
const vueMixins = vueOptions['mixins'];
......@@ -519,7 +504,7 @@ function getBehaviors (vueOptions) {
if (isPlainObject(vueExtends) && vueExtends.props) {
behaviors.push(
initBehavior({
properties: getProperties(vueExtends.props, true)
properties: initProperties(vueExtends.props, true)
})
);
}
......@@ -528,7 +513,7 @@ function getBehaviors (vueOptions) {
if (isPlainObject(vueMixin) && vueMixin.props) {
behaviors.push(
initBehavior({
properties: getProperties(vueMixin.props, true)
properties: initProperties(vueMixin.props, true)
})
);
}
......@@ -545,9 +530,13 @@ function parsePropType (key, type, defaultValue, file) {
return type
}
function getProperties (props, isBehavior = false, file = '') {
function initProperties (props, isBehavior = false, file = '') {
const properties = {};
if (!isBehavior) {
properties.vueId = {
type: String,
value: ''
};
properties.vueSlots = { // 小程序不能直接定义 $slots 的 props,所以通过 vueSlots 转换到 $slots
type: null,
value: [],
......@@ -798,23 +787,14 @@ function handleEvent (event) {
const hooks = [
'onHide',
'onError',
'onPageNotFound',
'onUniNViewMessage'
'onPageNotFound'
];
function initVm (vm) {
if (this.$vm) { // 百度竟然 onShow 在 onLaunch 之前?
return
}
this.$vm = vm;
this.$vm.$mp = {
app: this
};
}
function createApp (vm) {
function parseBaseApp (vm, {
mocks,
initRefs
}) {
Vue.prototype.mpHost = "app-plus";
Vue.mixin({
beforeCreate () {
......@@ -838,183 +818,182 @@ function createApp (vm) {
initRefs(this);
initMocks(this, mocks);
}
},
created () { // 处理 injections
this.__init_injections(this);
this.__init_provide(this);
}
});
const appOptions = {
onLaunch (args) {
initVm.call(this, vm);
this.$vm = vm;
this.$vm.$mp = {
app: this
};
this.$vm.$scope = this;
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('mounted', args);
this.$vm.__call_hook('onLaunch', args);
},
onShow (args) {
initVm.call(this, vm);
this.$vm.__call_hook('onShow', args);
}
};
// 兼容旧版本 globalData
appOptions.globalData = vm.$options.globalData || {};
initHooks(appOptions, hooks); // 延迟执行,因为 App 的注册在 main.js 之前,可能导致生命周期内 Vue 原型上开发者注册的属性无法访问
App(appOptions);
initHooks(appOptions, hooks);
return vm
return appOptions
}
const hooks$1 = [
'onShow',
'onHide',
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap',
'onBackPress',
'onNavigationBarButtonTap',
'onNavigationBarSearchInputChanged',
'onNavigationBarSearchInputConfirmed',
'onNavigationBarSearchInputClicked'
];
const mocks = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__'];
function initVm$1 (VueComponent) { // 百度的 onLoad 触发在 attached 之前
if (this.$vm) {
return
function findVmByVueId (vm, vuePid) {
const $children = vm.$children;
// 优先查找直属
let parentVm = $children.find(childVm => childVm.$scope._$vueId === vuePid);
if (parentVm) {
return parentVm
}
// 反向递归查找
for (let i = $children.length - 1; i >= 0; i--) {
parentVm = findVmByVueId($children[i], vuePid);
if (parentVm) {
return parentVm
}
}
}
this.$vm = new VueComponent({
mpType: 'page',
mpInstance: this
});
this.$vm.__call_hook('created');
this.$vm.$mount();
function initBehavior (options) {
return Behavior(options)
}
function createPage (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
let VueComponent;
if (isFn(vueOptions)) {
VueComponent = vueOptions;
vueOptions = VueComponent.extendOptions;
} else {
VueComponent = Vue.extend(vueOptions);
}
const pageOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions, Vue.prototype),
lifetimes: { // 当页面作为组件时
attached () {
initVm$1.call(this, VueComponent);
},
ready () {
this.$vm.__call_hook('beforeMount');
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
},
detached () {
this.$vm.$destroy();
}
},
methods: { // 作为页面时
onLoad (args) {
initVm$1.call(this, VueComponent);
this.$vm.$mp.query = args; // 又要兼容 mpvue
this.$vm.__call_hook('onLoad', args); // 开发者可能会在 onLoad 时赋值,提前到 mount 之前
},
onUnload () {
this.$vm.__call_hook('onUnload');
},
__e: handleEvent,
__l: handleLink
}
};
function isPage () {
return !!this.route
}
initHooks(pageOptions.methods, hooks$1);
function initRelation (detail) {
this.triggerEvent('__l', detail);
}
return initPage$1(pageOptions, vueOptions)
}
function initVm$2 (VueComponent) {
if (this.$vm) {
return
}
function initRefs (vm) {
const mpInstance = vm.$scope;
Object.defineProperty(vm, '$refs', {
get () {
const $refs = {};
const components = mpInstance.selectAllComponents('.vue-ref');
components.forEach(component => {
const ref = component.dataset.ref;
$refs[ref] = component.$vm || component;
});
const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for');
forComponents.forEach(component => {
const ref = component.dataset.ref;
if (!$refs[ref]) {
$refs[ref] = [];
}
$refs[ref].push(component.$vm || component);
});
return $refs
}
});
}
const properties = this.properties;
function handleLink (event) {
const {
vuePid,
vueOptions
} = event.detail || event.value; // detail 是微信,value 是百度(dipatch)
const options = {
mpType: 'component',
mpInstance: this,
propsData: properties
};
// 初始化 vue 实例
this.$vm = new VueComponent(options);
let parentVm;
// 处理$slots,$scopedSlots(暂不支持动态变化$slots)
const vueSlots = properties.vueSlots;
if (Array.isArray(vueSlots) && vueSlots.length) {
const $slots = Object.create(null);
vueSlots.forEach(slotName => {
$slots[slotName] = true;
});
this.$vm.$scopedSlots = this.$vm.$slots = $slots;
if (vuePid) {
parentVm = findVmByVueId(this.$vm, vuePid);
}
// 性能优先,mount 提前到 attached 中,保证组件首次渲染数据被合并
// 导致与标准 Vue 的差异,data 和 computed 中不能使用$parent,provide等组件属性
this.$vm.$mount();
}
function createComponent (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
let VueComponent;
if (isFn(vueOptions)) {
VueComponent = vueOptions; // TODO form-field props.name,props.value
vueOptions = VueComponent.extendOptions;
} else {
VueComponent = Vue.extend(vueOptions);
if (!parentVm) {
parentVm = this.$vm;
}
const behaviors = getBehaviors(vueOptions);
vueOptions.parent = parentVm;
}
function parseApp (vm) {
return parseBaseApp(vm, {
mocks,
initRefs
})
}
const hooks$1 = [
'onUniNViewMessage'
];
function parseApp$1 (vm) {
const appOptions = parseApp(vm);
const properties = getProperties(vueOptions.props, false, vueOptions.__file);
initHooks(appOptions, hooks$1);
return appOptions
}
function createApp (vm) {
App(parseApp$1(vm));
return vm
}
function parseBaseComponent (vueComponentOptions, {
isPage: isPage$$1,
initRelation: initRelation$$1
} = {}) {
let [VueComponent, vueOptions] = initVueComponent(Vue, vueComponentOptions);
const componentOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions, Vue.prototype),
behaviors,
properties,
data: initData(vueOptions, Vue.prototype),
behaviors: initBehaviors(vueOptions, initBehavior),
properties: initProperties(vueOptions.props, false, vueOptions.__file),
lifetimes: {
attached () {
initVm$2.call(this, VueComponent);
const properties = this.properties;
const options = {
mpType: isPage$$1.call(this) ? 'page' : 'component',
mpInstance: this,
propsData: properties
};
initVueIds(properties.vueId, this);
// 处理父子关系
initRelation$$1.call(this, {
vuePid: this._$vuePid,
vueOptions: options
});
// 初始化 vue 实例
this.$vm = new VueComponent(options);
// 处理$slots,$scopedSlots(暂不支持动态变化$slots)
initSlots(this.$vm, properties.vueSlots);
// 触发首次 setData
this.$vm.$mount();
},
ready () {
initVm$2.call(this, VueComponent); // 目前发现部分情况小程序 attached 不触发
triggerLink(this); // 处理 parent,children
// 补充生命周期
this.$vm.__call_hook('created');
this.$vm.__call_hook('beforeMount');
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
// 当组件 props 默认值为 true,初始化时传入 false 会导致 created,ready 触发, 但 attached 不触发
// https://developers.weixin.qq.com/community/develop/doc/00066ae2844cc0f8eb883e2a557800
if (this.$vm) {
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
} else {
this.is && console.warn(this.is + ' is not attached');
}
},
detached () {
this.$vm.$destroy();
......@@ -1022,7 +1001,7 @@ function createComponent (vueOptions) {
},
pageLifetimes: {
show (args) {
this.$vm.__call_hook('onPageShow', args);
this.$vm && this.$vm.__call_hook('onPageShow', args);
},
hide () {
this.$vm && this.$vm.__call_hook('onPageHide');
......@@ -1032,12 +1011,93 @@ function createComponent (vueOptions) {
}
},
methods: {
__e: handleEvent,
__l: handleLink
__l: handleLink,
__e: handleEvent
}
};
return initComponent$1(componentOptions, vueOptions)
if (isPage$$1) {
return componentOptions
}
return [componentOptions, VueComponent]
}
function parseComponent (vueComponentOptions) {
return parseBaseComponent(vueComponentOptions, {
isPage,
initRelation
})
}
function parseComponent$1 (vueComponentOptions) {
const componentOptions = parseComponent(vueComponentOptions);
componentOptions.methods.$getAppWebview = function () {
return plus.webview.getWebviewById(`${this.__wxWebviewId__}`)
};
return componentOptions
}
const hooks$2 = [
'onShow',
'onHide',
'onUnload'
];
hooks$2.push(...PAGE_EVENT_HOOKS);
function parseBasePage (vuePageOptions, {
isPage,
initRelation
}) {
const pageOptions = parseComponent$1(vuePageOptions, {
isPage,
initRelation
});
initHooks(pageOptions.methods, hooks$2);
pageOptions.methods.onLoad = function (args) {
this.$vm.$mp.query = args; // 兼容 mpvue
this.$vm.__call_hook('onLoad', args);
};
return pageOptions
}
function parsePage (vuePageOptions) {
return parseBasePage(vuePageOptions, {
isPage,
initRelation
})
}
const hooks$3 = [
'onBackPress',
'onNavigationBarButtonTap',
'onNavigationBarSearchInputChanged',
'onNavigationBarSearchInputConfirmed',
'onNavigationBarSearchInputClicked'
];
function parsePage$1 (vuePageOptions) {
const pageOptions = parsePage(vuePageOptions);
initHooks(pageOptions.methods, hooks$3);
return pageOptions
}
function createPage (vuePageOptions) {
{
return Component(parsePage$1(vuePageOptions))
}
}
function createComponent (vueOptions) {
{
return Component(parseComponent$1(vueOptions))
}
}
todos.forEach(todoApi => {
......
......@@ -88,16 +88,18 @@ function promisify (name, api) {
success: resolve,
fail: reject
}), ...params);
/* eslint-disable no-extend-native */
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
}))
}
}
......@@ -469,65 +471,15 @@ Component = function (options = {}) {
return MPComponent(options)
};
function initBehavior (options) {
return Behavior(options)
}
function initRefs (vm) {
const mpInstance = vm.$scope;
Object.defineProperty(vm, '$refs', {
get () {
const $refs = {};
const components = mpInstance.selectAllComponents('.vue-ref');
components.forEach(component => {
const ref = component.dataset.ref;
$refs[ref] = component.$vm || component;
});
const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for');
forComponents.forEach(component => {
const ref = component.dataset.ref;
if (!$refs[ref]) {
$refs[ref] = [];
}
$refs[ref].push(component.$vm || component);
});
return $refs
}
});
}
const mocks$1 = ['nodeId'];
function initPage$1 (pageOptions) {
return initComponent$1(pageOptions)
}
function initComponent$1 (componentOptions) {
componentOptions.messages = {
'__l': handleLink$1
};
return Component(componentOptions)
}
function triggerLink$1 (mpInstance, vueOptions) {
mpInstance.dispatch('__l', mpInstance.$vm || vueOptions);
}
function handleLink$1 (event) {
const target = event.value;
if (target.$mp) {
if (!target.$parent) {
target.$parent = this.$vm;
target.$parent.$children.push(target);
const PAGE_EVENT_HOOKS = [
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap'
];
target.$root = this.$vm.$root;
}
} else {
if (!target.parent) {
target.parent = this.$vm;
}
}
}
function initMocks (vm, mocks) {
const mpInstance = vm.$mp[vm.mpType];
mocks.forEach(mock => {
......@@ -540,12 +492,46 @@ function initMocks (vm, mocks) {
function initHooks (mpOptions, hooks) {
hooks.forEach(hook => {
mpOptions[hook] = function (args) {
return this.$vm.__call_hook(hook, args)
return this.$vm && this.$vm.__call_hook(hook, args)
};
});
}
function getData (vueOptions, context) {
function initVueComponent (Vue$$1, vueOptions) {
vueOptions = vueOptions.default || vueOptions;
let VueComponent;
if (isFn(vueOptions)) {
VueComponent = vueOptions;
vueOptions = VueComponent.extendOptions;
} else {
VueComponent = Vue$$1.extend(vueOptions);
}
return [VueComponent, vueOptions]
}
function initSlots (vm, vueSlots) {
if (Array.isArray(vueSlots) && vueSlots.length) {
const $slots = Object.create(null);
vueSlots.forEach(slotName => {
$slots[slotName] = true;
});
vm.$scopedSlots = vm.$slots = $slots;
}
}
function initVueIds (vueIds, mpInstance) {
vueIds = (vueIds || '').split(',');
const len = vueIds.length;
if (len === 1) {
mpInstance._$vueId = vueIds[0];
} else if (len === 2) {
mpInstance._$vueId = vueIds[0];
mpInstance._$vuePid = vueIds[1];
}
}
function initData (vueOptions, context) {
let data = vueOptions.data || {};
const methods = vueOptions.methods || {};
......@@ -587,7 +573,7 @@ function createObserver (name) {
}
}
function getBehaviors (vueOptions) {
function initBehaviors (vueOptions, initBehavior) {
const vueBehaviors = vueOptions['behaviors'];
const vueExtends = vueOptions['extends'];
const vueMixins = vueOptions['mixins'];
......@@ -616,7 +602,7 @@ function getBehaviors (vueOptions) {
if (isPlainObject(vueExtends) && vueExtends.props) {
behaviors.push(
initBehavior({
properties: getProperties(vueExtends.props, true)
properties: initProperties(vueExtends.props, true)
})
);
}
......@@ -625,7 +611,7 @@ function getBehaviors (vueOptions) {
if (isPlainObject(vueMixin) && vueMixin.props) {
behaviors.push(
initBehavior({
properties: getProperties(vueMixin.props, true)
properties: initProperties(vueMixin.props, true)
})
);
}
......@@ -658,9 +644,13 @@ function parsePropType (key, type, defaultValue, file) {
return type
}
function getProperties (props, isBehavior = false, file = '') {
function initProperties (props, isBehavior = false, file = '') {
const properties = {};
if (!isBehavior) {
properties.vueId = {
type: String,
value: ''
};
properties.vueSlots = { // 小程序不能直接定义 $slots 的 props,所以通过 vueSlots 转换到 $slots
type: null,
value: [],
......@@ -916,42 +906,19 @@ function handleEvent (event) {
});
}
});
}
function baiduComponentDestroy ($vm) {
$vm.$children.forEach(childVm => {
childVm.$mp.component.detached();
});
$vm.$mp.component.detached();
}
function baiduPageDestroy ($vm) {
$vm.$destroy();
$vm.$children.forEach(childVm => {
baiduComponentDestroy(childVm);
});
}
const hooks = [
'onHide',
'onError',
'onPageNotFound',
'onUniNViewMessage'
'onPageNotFound'
];
function initVm (vm) {
if (this.$vm) { // 百度竟然 onShow 在 onLaunch 之前?
return
}
this.$vm = vm;
this.$vm.$mp = {
app: this
};
}
function createApp (vm) {
function parseBaseApp (vm, {
mocks,
initRefs
}) {
Vue.prototype.mpHost = "mp-baidu";
Vue.mixin({
beforeCreate () {
......@@ -973,195 +940,172 @@ function createApp (vm) {
if (this.mpType !== 'app') {
initRefs(this);
initMocks(this, mocks$1);
initMocks(this, mocks);
}
},
created () { // 处理 injections
this.__init_injections(this);
this.__init_provide(this);
}
});
const appOptions = {
onLaunch (args) {
initVm.call(this, vm);
this.$vm = vm;
this.$vm.$mp = {
app: this
};
this.$vm.$scope = this;
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('mounted', args);
this.$vm.__call_hook('onLaunch', args);
},
onShow (args) {
initVm.call(this, vm);
this.$vm.__call_hook('onShow', args);
}
};
// 兼容旧版本 globalData
appOptions.globalData = vm.$options.globalData || {};
initHooks(appOptions, hooks); // 延迟执行,因为 App 的注册在 main.js 之前,可能导致生命周期内 Vue 原型上开发者注册的属性无法访问
App(appOptions);
initHooks(appOptions, hooks);
return vm
return appOptions
}
const hooks$1 = [
'onShow',
'onHide',
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap',
'onBackPress',
'onNavigationBarButtonTap',
'onNavigationBarSearchInputChanged',
'onNavigationBarSearchInputConfirmed',
'onNavigationBarSearchInputClicked'
];
function initVm$1 (VueComponent) { // 百度的 onLoad 触发在 attached 之前
if (this.$vm) {
return
function findVmByVueId (vm, vuePid) {
const $children = vm.$children;
// 优先查找直属
let parentVm = $children.find(childVm => childVm.$scope._$vueId === vuePid);
if (parentVm) {
return parentVm
}
this.$vm = new VueComponent({
mpType: 'page',
mpInstance: this
});
{
this.$vm.$baiduComponentInstances = Object.create(null);
// 反向递归查找
for (let i = $children.length - 1; i >= 0; i--) {
parentVm = findVmByVueId($children[i], vuePid);
if (parentVm) {
return parentVm
}
}
}
this.$vm.__call_hook('created');
this.$vm.$mount();
function initBehavior (options) {
return Behavior(options)
}
function createPage (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
let VueComponent;
if (isFn(vueOptions)) {
VueComponent = vueOptions;
vueOptions = VueComponent.extendOptions;
} else {
VueComponent = Vue.extend(vueOptions);
}
const pageOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions, Vue.prototype),
lifetimes: { // 当页面作为组件时
attached () {
initVm$1.call(this, VueComponent);
},
ready () {
this.$vm.__call_hook('beforeMount');
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
},
detached () {
this.$vm.$destroy();
}
},
methods: { // 作为页面时
onLoad (args) {
initVm$1.call(this, VueComponent);
{ // 百度当组件作为页面时 pageinstancce 不是原来组件的 instance
this.pageinstance.$vm = this.$vm;
}
this.$vm.$mp.query = args; // 又要兼容 mpvue
this.$vm.__call_hook('onLoad', args); // 开发者可能会在 onLoad 时赋值,提前到 mount 之前
},
onUnload () {
this.$vm.__call_hook('onUnload');
{ // 百度组件不会在页面 unload 时触发 detached
baiduPageDestroy(this.$vm);
function initRefs (vm) {
const mpInstance = vm.$scope;
Object.defineProperty(vm, '$refs', {
get () {
const $refs = {};
const components = mpInstance.selectAllComponents('.vue-ref');
components.forEach(component => {
const ref = component.dataset.ref;
$refs[ref] = component.$vm || component;
});
const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for');
forComponents.forEach(component => {
const ref = component.dataset.ref;
if (!$refs[ref]) {
$refs[ref] = [];
}
},
__e: handleEvent,
__l: handleLink$1
$refs[ref].push(component.$vm || component);
});
return $refs
}
};
initHooks(pageOptions.methods, hooks$1);
return initPage$1(pageOptions, vueOptions)
}
function initVm$2 (VueComponent) {
if (this.$vm) {
return
}
});
}
const properties = this.properties;
function handleLink (event) {
const {
vuePid,
vueOptions
} = event.detail || event.value; // detail 是微信,value 是百度(dipatch)
const options = {
mpType: 'component',
mpInstance: this,
propsData: properties
};
// 初始化 vue 实例
this.$vm = new VueComponent(options);
let parentVm;
// 处理$slots,$scopedSlots(暂不支持动态变化$slots)
const vueSlots = properties.vueSlots;
if (Array.isArray(vueSlots) && vueSlots.length) {
const $slots = Object.create(null);
vueSlots.forEach(slotName => {
$slots[slotName] = true;
});
this.$vm.$scopedSlots = this.$vm.$slots = $slots;
if (vuePid) {
parentVm = findVmByVueId(this.$vm, vuePid);
}
// 性能优先,mount 提前到 attached 中,保证组件首次渲染数据被合并
// 导致与标准 Vue 的差异,data 和 computed 中不能使用$parent,provide等组件属性
this.$vm.$mount();
}
function createComponent (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
let VueComponent;
if (isFn(vueOptions)) {
VueComponent = vueOptions; // TODO form-field props.name,props.value
vueOptions = VueComponent.extendOptions;
} else {
VueComponent = Vue.extend(vueOptions);
if (!parentVm) {
parentVm = this.$vm;
}
const behaviors = getBehaviors(vueOptions);
vueOptions.parent = parentVm;
}
const mocks$1 = ['nodeId'];
function isPage$1 () {
return !this.ownerId
}
const properties = getProperties(vueOptions.props, false, vueOptions.__file);
function initRelation$1 (detail) {
this.dispatch('__l', detail);
}
function parseApp (vm) {
return parseBaseApp(vm, {
mocks: mocks$1,
initRefs
})
}
function createApp (vm) {
App(parseApp(vm));
return vm
}
function parseBaseComponent (vueComponentOptions, {
isPage: isPage$$1,
initRelation: initRelation$$1
} = {}) {
let [VueComponent, vueOptions] = initVueComponent(Vue, vueComponentOptions);
const componentOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions, Vue.prototype),
behaviors,
properties,
data: initData(vueOptions, Vue.prototype),
behaviors: initBehaviors(vueOptions, initBehavior),
properties: initProperties(vueOptions.props, false, vueOptions.__file),
lifetimes: {
attached () {
initVm$2.call(this, VueComponent);
const properties = this.properties;
const options = {
mpType: isPage$$1.call(this) ? 'page' : 'component',
mpInstance: this,
propsData: properties
};
initVueIds(properties.vueId, this);
// 处理父子关系
initRelation$$1.call(this, {
vuePid: this._$vuePid,
vueOptions: options
});
// 初始化 vue 实例
this.$vm = new VueComponent(options);
// 处理$slots,$scopedSlots(暂不支持动态变化$slots)
initSlots(this.$vm, properties.vueSlots);
// 触发首次 setData
this.$vm.$mount();
},
ready () {
initVm$2.call(this, VueComponent); // 目前发现部分情况小程序 attached 不触发
triggerLink$1(this); // 处理 parent,children
// 补充生命周期
this.$vm.__call_hook('created');
this.$vm.__call_hook('beforeMount');
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
// 当组件 props 默认值为 true,初始化时传入 false 会导致 created,ready 触发, 但 attached 不触发
// https://developers.weixin.qq.com/community/develop/doc/00066ae2844cc0f8eb883e2a557800
if (this.$vm) {
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
} else {
this.is && console.warn(this.is + ' is not attached');
}
},
detached () {
this.$vm.$destroy();
......@@ -1169,7 +1113,7 @@ function createComponent (vueOptions) {
},
pageLifetimes: {
show (args) {
this.$vm.__call_hook('onPageShow', args);
this.$vm && this.$vm.__call_hook('onPageShow', args);
},
hide () {
this.$vm && this.$vm.__call_hook('onPageHide');
......@@ -1179,12 +1123,114 @@ function createComponent (vueOptions) {
}
},
methods: {
__e: handleEvent,
__l: handleLink$1
__l: handleLink,
__e: handleEvent
}
};
if (isPage$$1) {
return componentOptions
}
return [componentOptions, VueComponent]
}
function parseComponent (vueOptions) {
const componentOptions = parseBaseComponent(vueOptions, {
isPage: isPage$1,
initRelation: initRelation$1
});
const oldAttached = componentOptions.lifetimes.attached;
componentOptions.lifetimes.attached = function attached () {
oldAttached.call(this);
if (isPage$1.call(this)) { // 百度 onLoad 在 attached 之前触发
// 百度 当组件作为页面时 pageinstancce 不是原来组件的 instance
this.pageinstance.$vm = this.$vm;
this.$vm.$mp.query = this.pageinstance._$args; // 兼容 mpvue
this.$vm.__call_hook('onLoad', this.pageinstance._$args);
}
};
return initComponent$1(componentOptions, vueOptions)
componentOptions.messages = {
'__l': componentOptions.methods['__l']
};
delete componentOptions.methods['__l'];
return componentOptions
}
const hooks$1 = [
'onShow',
'onHide',
'onUnload'
];
hooks$1.push(...PAGE_EVENT_HOOKS);
function parseBasePage (vuePageOptions, {
isPage,
initRelation
}) {
const pageOptions = parseComponent(vuePageOptions, {
isPage,
initRelation
});
initHooks(pageOptions.methods, hooks$1);
pageOptions.methods.onLoad = function (args) {
this.$vm.$mp.query = args; // 兼容 mpvue
this.$vm.__call_hook('onLoad', args);
};
return pageOptions
}
function detached ($vm) {
$vm.$children.forEach(childVm => {
childVm.$scope.detached();
});
$vm.$scope.detached();
}
function onPageUnload ($vm) {
$vm.$destroy();
$vm.$children.forEach(childVm => {
detached(childVm);
});
}
function parsePage (vuePageOptions) {
const pageOptions = parseBasePage(vuePageOptions, {
isPage: isPage$1,
initRelation: initRelation$1
});
pageOptions.methods.onLoad = function onLoad (args) {
// 百度 onLoad 在 attached 之前触发,先存储 args, 在 attached 里边触发 onLoad
this.pageinstance._$args = args;
};
pageOptions.methods.onUnload = function onUnload () {
this.$vm.__call_hook('onUnload');
onPageUnload(this.$vm);
};
return pageOptions
}
function createPage (vuePageOptions) {
{
return Component(parsePage(vuePageOptions))
}
}
function createComponent (vueOptions) {
{
return Component(parseComponent(vueOptions))
}
}
todos.forEach(todoApi => {
......
......@@ -88,16 +88,18 @@ function promisify (name, api) {
success: resolve,
fail: reject
}), ...params);
/* eslint-disable no-extend-native */
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
}))
}
}
......@@ -514,77 +516,15 @@ Component = function (options = {}) {
return MPComponent(options)
};
function initBehavior (options) {
return Behavior(options)
}
const instances = Object.create(null);
const mocks$1 = ['__route__', '__webviewId__', '__nodeid__'];
function initPage$1 (pageOptions) {
return initComponent$1(pageOptions)
}
function initComponent$1 (componentOptions) {
if (componentOptions.properties) { // ref
componentOptions.properties.vueRef = {
type: String,
value: ''
};
}
return Component(componentOptions)
}
function initRefs$1 (vm) {
const mpInstance = vm.$scope;
mpInstance.selectAllComponents('.vue-ref', (components) => {
components.forEach(component => {
const ref = component.data.vueRef; // 头条的组件 dataset 竟然是空的
vm.$refs[ref] = component.$vm || component;
});
});
mpInstance.selectAllComponents('.vue-ref-in-for', (forComponents) => {
forComponents.forEach(component => {
const ref = component.data.vueRef;
if (!vm.$refs[ref]) {
vm.$refs[ref] = [];
}
vm.$refs[ref].push(component.$vm || component);
});
});
}
function triggerLink$1 (mpInstance) {
const nodeId = mpInstance.__nodeid__ + '';
const webviewId = mpInstance.__webviewId__ + '';
instances[webviewId + '_' + nodeId] = mpInstance.$vm;
mpInstance.triggerEvent('__l', {
nodeId,
webviewId
}, {
bubbles: true,
composed: true
});
}
// TODO 目前有 bug,composed 不生效
function handleLink$1 (event) {
const nodeId = event.detail.nodeId;
const webviewId = event.detail.webviewId;
const childVm = instances[webviewId + '_' + nodeId];
if (childVm) {
childVm.$parent = this.$vm;
childVm.$parent.$children.push(event.detail);
const PAGE_EVENT_HOOKS = [
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap'
];
childVm.$root = this.$vm.$root;
delete instances[webviewId + '_' + nodeId];
}
}
function initMocks (vm, mocks) {
const mpInstance = vm.$mp[vm.mpType];
mocks.forEach(mock => {
......@@ -597,12 +537,46 @@ function initMocks (vm, mocks) {
function initHooks (mpOptions, hooks) {
hooks.forEach(hook => {
mpOptions[hook] = function (args) {
return this.$vm.__call_hook(hook, args)
return this.$vm && this.$vm.__call_hook(hook, args)
};
});
}
function getData (vueOptions, context) {
function initVueComponent (Vue$$1, vueOptions) {
vueOptions = vueOptions.default || vueOptions;
let VueComponent;
if (isFn(vueOptions)) {
VueComponent = vueOptions;
vueOptions = VueComponent.extendOptions;
} else {
VueComponent = Vue$$1.extend(vueOptions);
}
return [VueComponent, vueOptions]
}
function initSlots (vm, vueSlots) {
if (Array.isArray(vueSlots) && vueSlots.length) {
const $slots = Object.create(null);
vueSlots.forEach(slotName => {
$slots[slotName] = true;
});
vm.$scopedSlots = vm.$slots = $slots;
}
}
function initVueIds (vueIds, mpInstance) {
vueIds = (vueIds || '').split(',');
const len = vueIds.length;
if (len === 1) {
mpInstance._$vueId = vueIds[0];
} else if (len === 2) {
mpInstance._$vueId = vueIds[0];
mpInstance._$vuePid = vueIds[1];
}
}
function initData (vueOptions, context) {
let data = vueOptions.data || {};
const methods = vueOptions.methods || {};
......@@ -644,7 +618,7 @@ function createObserver (name) {
}
}
function getBehaviors (vueOptions) {
function initBehaviors (vueOptions, initBehavior) {
const vueBehaviors = vueOptions['behaviors'];
const vueExtends = vueOptions['extends'];
const vueMixins = vueOptions['mixins'];
......@@ -673,7 +647,7 @@ function getBehaviors (vueOptions) {
if (isPlainObject(vueExtends) && vueExtends.props) {
behaviors.push(
initBehavior({
properties: getProperties(vueExtends.props, true)
properties: initProperties(vueExtends.props, true)
})
);
}
......@@ -682,7 +656,7 @@ function getBehaviors (vueOptions) {
if (isPlainObject(vueMixin) && vueMixin.props) {
behaviors.push(
initBehavior({
properties: getProperties(vueMixin.props, true)
properties: initProperties(vueMixin.props, true)
})
);
}
......@@ -699,9 +673,13 @@ function parsePropType (key, type, defaultValue, file) {
return type
}
function getProperties (props, isBehavior = false, file = '') {
function initProperties (props, isBehavior = false, file = '') {
const properties = {};
if (!isBehavior) {
properties.vueId = {
type: String,
value: ''
};
properties.vueSlots = { // 小程序不能直接定义 $slots 的 props,所以通过 vueSlots 转换到 $slots
type: null,
value: [],
......@@ -952,23 +930,14 @@ function handleEvent (event) {
const hooks = [
'onHide',
'onError',
'onPageNotFound',
'onUniNViewMessage'
'onPageNotFound'
];
function initVm (vm) {
if (this.$vm) { // 百度竟然 onShow 在 onLaunch 之前?
return
}
this.$vm = vm;
this.$vm.$mp = {
app: this
};
}
function createApp (vm) {
function parseBaseApp (vm, {
mocks,
initRefs
}) {
Vue.prototype.mpHost = "mp-toutiao";
Vue.mixin({
beforeCreate () {
......@@ -989,186 +958,230 @@ function createApp (vm) {
delete this.$options.mpInstance;
if (this.mpType !== 'app') {
initRefs$1(this);
initMocks(this, mocks$1);
initRefs(this);
initMocks(this, mocks);
}
},
created () { // 处理 injections
this.__init_injections(this);
this.__init_provide(this);
}
});
const appOptions = {
onLaunch (args) {
initVm.call(this, vm);
this.$vm = vm;
this.$vm.$mp = {
app: this
};
this.$vm.$scope = this;
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('mounted', args);
this.$vm.__call_hook('onLaunch', args);
},
onShow (args) {
initVm.call(this, vm);
this.$vm.__call_hook('onShow', args);
}
};
// 兼容旧版本 globalData
appOptions.globalData = vm.$options.globalData || {};
initHooks(appOptions, hooks); // 延迟执行,因为 App 的注册在 main.js 之前,可能导致生命周期内 Vue 原型上开发者注册的属性无法访问
App(appOptions);
initHooks(appOptions, hooks);
return vm
return appOptions
}
const hooks$1 = [
'onShow',
'onHide',
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap',
'onBackPress',
'onNavigationBarButtonTap',
'onNavigationBarSearchInputChanged',
'onNavigationBarSearchInputConfirmed',
'onNavigationBarSearchInputClicked'
];
function initVm$1 (VueComponent) { // 百度的 onLoad 触发在 attached 之前
if (this.$vm) {
return
function findVmByVueId (vm, vuePid) {
const $children = vm.$children;
// 优先查找直属
let parentVm = $children.find(childVm => childVm.$scope._$vueId === vuePid);
if (parentVm) {
return parentVm
}
// 反向递归查找
for (let i = $children.length - 1; i >= 0; i--) {
parentVm = findVmByVueId($children[i], vuePid);
if (parentVm) {
return parentVm
}
}
}
this.$vm = new VueComponent({
mpType: 'page',
mpInstance: this
});
this.$vm.__call_hook('created');
this.$vm.$mount();
function initBehavior (options) {
return Behavior(options)
}
function createPage (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
let VueComponent;
if (isFn(vueOptions)) {
VueComponent = vueOptions;
vueOptions = VueComponent.extendOptions;
} else {
VueComponent = Vue.extend(vueOptions);
function handleLink (event) {
const {
vuePid,
vueOptions
} = event.detail || event.value; // detail 是微信,value 是百度(dipatch)
let parentVm;
if (vuePid) {
parentVm = findVmByVueId(this.$vm, vuePid);
}
const pageOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions, Vue.prototype),
lifetimes: { // 当页面作为组件时
attached () {
initVm$1.call(this, VueComponent);
},
ready () {
this.$vm.__call_hook('beforeMount');
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
},
detached () {
this.$vm.$destroy();
}
},
methods: { // 作为页面时
onLoad (args) {
initVm$1.call(this, VueComponent);
this.$vm.$mp.query = args; // 又要兼容 mpvue
this.$vm.__call_hook('onLoad', args); // 开发者可能会在 onLoad 时赋值,提前到 mount 之前
},
onUnload () {
this.$vm.__call_hook('onUnload');
},
__e: handleEvent,
__l: handleLink$1
}
};
initHooks(pageOptions.methods, hooks$1);
if (!parentVm) {
parentVm = this.$vm;
}
return initPage$1(pageOptions, vueOptions)
vueOptions.parent = parentVm;
}
function initVm$2 (VueComponent) {
if (this.$vm) {
const mocks$1 = ['__route__', '__webviewId__', '__nodeid__', '__nodeId__'];
function isPage$1 () {
return this.__nodeid__ === 0 || this.__nodeId__ === 0
}
function initRefs$1 (vm) {
const mpInstance = vm.$scope;
mpInstance.selectAllComponents('.vue-ref', (components) => {
components.forEach(component => {
const ref = component.dataset.ref;
vm.$refs[ref] = component.$vm || component;
});
});
mpInstance.selectAllComponents('.vue-ref-in-for', (forComponents) => {
forComponents.forEach(component => {
const ref = component.dataset.ref;
if (!vm.$refs[ref]) {
vm.$refs[ref] = [];
}
vm.$refs[ref].push(component.$vm || component);
});
});
}
const instances = Object.create(null);
function initRelation$1 ({
vuePid,
mpInstance
}) {
// 头条 triggerEvent 后,接收事件时机特别晚,已经到了 ready 之后
const nodeId = (mpInstance.__nodeId__ || mpInstance.__nodeid__) + '';
const webviewId = mpInstance.__webviewId__ + '';
instances[webviewId + '_' + nodeId] = mpInstance.$vm;
this.triggerEvent('__l', {
vuePid,
nodeId,
webviewId
});
}
function handleLink$1 ({
detail: {
vuePid,
nodeId,
webviewId
}
}) {
const vm = instances[webviewId + '_' + nodeId];
if (!vm) {
return
}
const properties = this.properties;
let parentVm;
const options = {
mpType: 'component',
mpInstance: this,
propsData: properties
};
// 初始化 vue 实例
this.$vm = new VueComponent(options);
if (vuePid) {
parentVm = findVmByVueId(this.$vm, vuePid);
}
// 处理$slots,$scopedSlots(暂不支持动态变化$slots)
const vueSlots = properties.vueSlots;
if (Array.isArray(vueSlots) && vueSlots.length) {
const $slots = Object.create(null);
vueSlots.forEach(slotName => {
$slots[slotName] = true;
});
this.$vm.$scopedSlots = this.$vm.$slots = $slots;
if (!parentVm) {
parentVm = this.$vm;
}
// 性能优先,mount 提前到 attached 中,保证组件首次渲染数据被合并
// 导致与标准 Vue 的差异,data 和 computed 中不能使用$parent,provide等组件属性
this.$vm.$mount();
}
function createComponent (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
vm.$parent = parentVm;
vm.$root = parentVm.$root;
parentVm.$children.push(vm);
let VueComponent;
if (isFn(vueOptions)) {
VueComponent = vueOptions; // TODO form-field props.name,props.value
vueOptions = VueComponent.extendOptions;
} else {
VueComponent = Vue.extend(vueOptions);
}
vm.__call_hook('created');
vm.__call_hook('beforeMount');
vm._isMounted = true;
vm.__call_hook('mounted');
vm.__call_hook('onReady');
}
function parseApp (vm) {
Vue.prototype._$fallback = true; // 降级(调整原 vue 的部分生命周期,如 created,beforeMount,inject,provide)
Vue.mixin({
created () { // 处理 injections,头条 triggerEvent 是异步,且触发时机很慢,故延迟 relation 设置
if (this.mpType !== 'app') {
initRefs$1(this);
const behaviors = getBehaviors(vueOptions);
this.__init_injections(this);
this.__init_provide(this);
}
}
});
const properties = getProperties(vueOptions.props, false, vueOptions.__file);
return parseBaseApp(vm, {
mocks: mocks$1,
initRefs: function () {} // attached 时,可能查询不到
})
}
function createApp (vm) {
App(parseApp(vm));
return vm
}
function parseBaseComponent (vueComponentOptions, {
isPage: isPage$$1,
initRelation: initRelation$$1
} = {}) {
let [VueComponent, vueOptions] = initVueComponent(Vue, vueComponentOptions);
const componentOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions, Vue.prototype),
behaviors,
properties,
data: initData(vueOptions, Vue.prototype),
behaviors: initBehaviors(vueOptions, initBehavior),
properties: initProperties(vueOptions.props, false, vueOptions.__file),
lifetimes: {
attached () {
initVm$2.call(this, VueComponent);
const properties = this.properties;
const options = {
mpType: isPage$$1.call(this) ? 'page' : 'component',
mpInstance: this,
propsData: properties
};
initVueIds(properties.vueId, this);
// 处理父子关系
initRelation$$1.call(this, {
vuePid: this._$vuePid,
vueOptions: options
});
// 初始化 vue 实例
this.$vm = new VueComponent(options);
// 处理$slots,$scopedSlots(暂不支持动态变化$slots)
initSlots(this.$vm, properties.vueSlots);
// 触发首次 setData
this.$vm.$mount();
},
ready () {
initVm$2.call(this, VueComponent); // 目前发现部分情况小程序 attached 不触发
triggerLink$1(this); // 处理 parent,children
// 补充生命周期
this.$vm.__call_hook('created');
this.$vm.__call_hook('beforeMount');
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
// 当组件 props 默认值为 true,初始化时传入 false 会导致 created,ready 触发, 但 attached 不触发
// https://developers.weixin.qq.com/community/develop/doc/00066ae2844cc0f8eb883e2a557800
if (this.$vm) {
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
} else {
this.is && console.warn(this.is + ' is not attached');
}
},
detached () {
this.$vm.$destroy();
......@@ -1176,7 +1189,7 @@ function createComponent (vueOptions) {
},
pageLifetimes: {
show (args) {
this.$vm.__call_hook('onPageShow', args);
this.$vm && this.$vm.__call_hook('onPageShow', args);
},
hide () {
this.$vm && this.$vm.__call_hook('onPageHide');
......@@ -1186,12 +1199,113 @@ function createComponent (vueOptions) {
}
},
methods: {
__e: handleEvent,
__l: handleLink$1
__l: handleLink,
__e: handleEvent
}
};
if (isPage$$1) {
return componentOptions
}
return [componentOptions, VueComponent]
}
function parseComponent (vueOptions) {
const [componentOptions, VueComponent] = parseBaseComponent(vueOptions);
componentOptions.lifetimes.attached = function attached () {
const properties = this.properties;
const options = {
mpType: isPage$1.call(this) ? 'page' : 'component',
mpInstance: this,
propsData: properties
};
initVueIds(properties.vueId, this);
// 初始化 vue 实例
this.$vm = new VueComponent(options);
// 处理$slots,$scopedSlots(暂不支持动态变化$slots)
initSlots(this.$vm, properties.vueSlots);
// 处理父子关系
initRelation$1.call(this, {
vuePid: this._$vuePid,
mpInstance: this
});
// 触发首次 setData
this.$vm.$mount();
};
// ready 比 handleLink 还早,初始化逻辑放到 handleLink 中
delete componentOptions.lifetimes.ready;
componentOptions.methods.__l = handleLink$1;
return componentOptions
}
const hooks$1 = [
'onShow',
'onHide',
'onUnload'
];
hooks$1.push(...PAGE_EVENT_HOOKS);
function parseBasePage (vuePageOptions, {
isPage,
initRelation
}) {
const pageOptions = parseComponent(vuePageOptions, {
isPage,
initRelation
});
initHooks(pageOptions.methods, hooks$1);
pageOptions.methods.onLoad = function (args) {
this.$vm.$mp.query = args; // 兼容 mpvue
this.$vm.__call_hook('onLoad', args);
};
return pageOptions
}
function parsePage (vuePageOptions) {
const pageOptions = parseBasePage(vuePageOptions, {
isPage: isPage$1,
initRelation: initRelation$1
});
// 页面需要在 ready 中触发,其他组件是在 handleLink 中触发
pageOptions.lifetimes.ready = function ready () {
if (this.$vm && this.$vm.mpType === 'page') {
this.$vm.__call_hook('created');
this.$vm.__call_hook('beforeMount');
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
} else {
this.is && console.warn(this.is + ' is not ready');
}
};
return initComponent$1(componentOptions, vueOptions)
return pageOptions
}
function createPage (vuePageOptions) {
{
return Component(parsePage(vuePageOptions))
}
}
function createComponent (vueOptions) {
{
return Component(parseComponent(vueOptions))
}
}
todos.forEach(todoApi => {
......
......@@ -40,7 +40,7 @@ const camelize = cached((str) => {
return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
});
const SYNC_API_RE = /subNVue|requireNativePlugin|upx2px|hideKeyboard|canIUse|^create|Sync$|Manager$/;
const SYNC_API_RE = /subNVue|requireNativePlugin|upx2px|hideKeyboard|canIUse|^create|Sync$|Manager$|base64ToArrayBuffer|arrayBufferToBase64/;
const CONTEXT_API_RE = /^create|Manager$/;
......@@ -89,15 +89,17 @@ function promisify (name, api) {
fail: reject
}), ...params);
/* eslint-disable no-extend-native */
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
}))
}
}
......@@ -371,66 +373,18 @@ Component = function (options = {}) {
return MPComponent(options)
};
const mocks = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__'];
function initPage (pageOptions) {
return initComponent(pageOptions)
}
function initComponent (componentOptions) {
return Component(componentOptions)
}
function initBehavior (options) {
return Behavior(options)
}
function initRefs (vm) {
const mpInstance = vm.$scope;
Object.defineProperty(vm, '$refs', {
get () {
const $refs = {};
const components = mpInstance.selectAllComponents('.vue-ref');
components.forEach(component => {
const ref = component.dataset.ref;
$refs[ref] = component.$vm || component;
});
const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for');
forComponents.forEach(component => {
const ref = component.dataset.ref;
if (!$refs[ref]) {
$refs[ref] = [];
}
$refs[ref].push(component.$vm || component);
});
return $refs
}
});
}
function triggerLink (mpInstance, vueOptions) {
mpInstance.triggerEvent('__l', mpInstance.$vm || vueOptions, {
bubbles: true,
composed: true
});
}
function handleLink (event) {
if (event.detail.$mp) { // vm
if (!event.detail.$parent) {
event.detail.$parent = this.$vm;
event.detail.$parent.$children.push(event.detail);
const PAGE_EVENT_HOOKS = [
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap'
];
event.detail.$root = this.$vm.$root;
}
} else { // vueOptions
if (!event.detail.parent) {
event.detail.parent = this.$vm;
}
}
}
function initMocks (vm, mocks$$1) {
function initMocks (vm, mocks) {
const mpInstance = vm.$mp[vm.mpType];
mocks$$1.forEach(mock => {
mocks.forEach(mock => {
if (hasOwn(mpInstance, mock)) {
vm[mock] = mpInstance[mock];
}
......@@ -440,12 +394,46 @@ function initMocks (vm, mocks$$1) {
function initHooks (mpOptions, hooks) {
hooks.forEach(hook => {
mpOptions[hook] = function (args) {
return this.$vm.__call_hook(hook, args)
return this.$vm && this.$vm.__call_hook(hook, args)
};
});
}
function getData (vueOptions, context) {
function initVueComponent (Vue$$1, vueOptions) {
vueOptions = vueOptions.default || vueOptions;
let VueComponent;
if (isFn(vueOptions)) {
VueComponent = vueOptions;
vueOptions = VueComponent.extendOptions;
} else {
VueComponent = Vue$$1.extend(vueOptions);
}
return [VueComponent, vueOptions]
}
function initSlots (vm, vueSlots) {
if (Array.isArray(vueSlots) && vueSlots.length) {
const $slots = Object.create(null);
vueSlots.forEach(slotName => {
$slots[slotName] = true;
});
vm.$scopedSlots = vm.$slots = $slots;
}
}
function initVueIds (vueIds, mpInstance) {
vueIds = (vueIds || '').split(',');
const len = vueIds.length;
if (len === 1) {
mpInstance._$vueId = vueIds[0];
} else if (len === 2) {
mpInstance._$vueId = vueIds[0];
mpInstance._$vuePid = vueIds[1];
}
}
function initData (vueOptions, context) {
let data = vueOptions.data || {};
const methods = vueOptions.methods || {};
......@@ -487,7 +475,7 @@ function createObserver (name) {
}
}
function getBehaviors (vueOptions) {
function initBehaviors (vueOptions, initBehavior) {
const vueBehaviors = vueOptions['behaviors'];
const vueExtends = vueOptions['extends'];
const vueMixins = vueOptions['mixins'];
......@@ -516,7 +504,7 @@ function getBehaviors (vueOptions) {
if (isPlainObject(vueExtends) && vueExtends.props) {
behaviors.push(
initBehavior({
properties: getProperties(vueExtends.props, true)
properties: initProperties(vueExtends.props, true)
})
);
}
......@@ -525,7 +513,7 @@ function getBehaviors (vueOptions) {
if (isPlainObject(vueMixin) && vueMixin.props) {
behaviors.push(
initBehavior({
properties: getProperties(vueMixin.props, true)
properties: initProperties(vueMixin.props, true)
})
);
}
......@@ -542,9 +530,13 @@ function parsePropType (key, type, defaultValue, file) {
return type
}
function getProperties (props, isBehavior = false, file = '') {
function initProperties (props, isBehavior = false, file = '') {
const properties = {};
if (!isBehavior) {
properties.vueId = {
type: String,
value: ''
};
properties.vueSlots = { // 小程序不能直接定义 $slots 的 props,所以通过 vueSlots 转换到 $slots
type: null,
value: [],
......@@ -795,28 +787,14 @@ function handleEvent (event) {
const hooks = [
'onHide',
'onError',
'onPageNotFound',
'onUniNViewMessage'
'onPageNotFound'
];
function initVm (vm) {
if (this.$vm) { // 百度竟然 onShow 在 onLaunch 之前?
return
}
{
if (!wx.canIUse('nextTick')) { // 事实 上2.2.3 即可,简单使用 2.3.0 的 nextTick 判断
console.error('当前微信基础库版本过低,请将 微信开发者工具-详情-项目设置-调试基础库版本 更换为`2.3.0`以上');
}
}
this.$vm = vm;
this.$vm.$mp = {
app: this
};
}
function createApp (vm) {
function parseBaseApp (vm, {
mocks,
initRefs
}) {
Vue.prototype.mpHost = "mp-weixin";
Vue.mixin({
beforeCreate () {
......@@ -840,183 +818,175 @@ function createApp (vm) {
initRefs(this);
initMocks(this, mocks);
}
},
created () { // 处理 injections
this.__init_injections(this);
this.__init_provide(this);
}
});
const appOptions = {
onLaunch (args) {
initVm.call(this, vm);
{
if (!wx.canIUse('nextTick')) { // 事实 上2.2.3 即可,简单使用 2.3.0 的 nextTick 判断
console.error('当前微信基础库版本过低,请将 微信开发者工具-详情-项目设置-调试基础库版本 更换为`2.3.0`以上');
}
}
this.$vm = vm;
this.$vm.$mp = {
app: this
};
this.$vm.$scope = this;
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('mounted', args);
this.$vm.__call_hook('onLaunch', args);
},
onShow (args) {
initVm.call(this, vm);
this.$vm.__call_hook('onShow', args);
}
};
// 兼容旧版本 globalData
appOptions.globalData = vm.$options.globalData || {};
initHooks(appOptions, hooks); // 延迟执行,因为 App 的注册在 main.js 之前,可能导致生命周期内 Vue 原型上开发者注册的属性无法访问
App(appOptions);
initHooks(appOptions, hooks);
return vm
return appOptions
}
const hooks$1 = [
'onShow',
'onHide',
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap',
'onBackPress',
'onNavigationBarButtonTap',
'onNavigationBarSearchInputChanged',
'onNavigationBarSearchInputConfirmed',
'onNavigationBarSearchInputClicked'
];
const mocks = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__'];
function initVm$1 (VueComponent) { // 百度的 onLoad 触发在 attached 之前
if (this.$vm) {
return
function findVmByVueId (vm, vuePid) {
const $children = vm.$children;
// 优先查找直属
let parentVm = $children.find(childVm => childVm.$scope._$vueId === vuePid);
if (parentVm) {
return parentVm
}
// 反向递归查找
for (let i = $children.length - 1; i >= 0; i--) {
parentVm = findVmByVueId($children[i], vuePid);
if (parentVm) {
return parentVm
}
}
}
this.$vm = new VueComponent({
mpType: 'page',
mpInstance: this
});
this.$vm.__call_hook('created');
this.$vm.$mount();
function initBehavior (options) {
return Behavior(options)
}
function createPage (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
let VueComponent;
if (isFn(vueOptions)) {
VueComponent = vueOptions;
vueOptions = VueComponent.extendOptions;
} else {
VueComponent = Vue.extend(vueOptions);
}
const pageOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions, Vue.prototype),
lifetimes: { // 当页面作为组件时
attached () {
initVm$1.call(this, VueComponent);
},
ready () {
this.$vm.__call_hook('beforeMount');
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
},
detached () {
this.$vm.$destroy();
}
},
methods: { // 作为页面时
onLoad (args) {
initVm$1.call(this, VueComponent);
this.$vm.$mp.query = args; // 又要兼容 mpvue
this.$vm.__call_hook('onLoad', args); // 开发者可能会在 onLoad 时赋值,提前到 mount 之前
},
onUnload () {
this.$vm.__call_hook('onUnload');
},
__e: handleEvent,
__l: handleLink
}
};
function isPage () {
return !!this.route
}
initHooks(pageOptions.methods, hooks$1);
function initRelation (detail) {
this.triggerEvent('__l', detail);
}
return initPage(pageOptions, vueOptions)
}
function initVm$2 (VueComponent) {
if (this.$vm) {
return
}
function initRefs (vm) {
const mpInstance = vm.$scope;
Object.defineProperty(vm, '$refs', {
get () {
const $refs = {};
const components = mpInstance.selectAllComponents('.vue-ref');
components.forEach(component => {
const ref = component.dataset.ref;
$refs[ref] = component.$vm || component;
});
const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for');
forComponents.forEach(component => {
const ref = component.dataset.ref;
if (!$refs[ref]) {
$refs[ref] = [];
}
$refs[ref].push(component.$vm || component);
});
return $refs
}
});
}
const properties = this.properties;
function handleLink (event) {
const {
vuePid,
vueOptions
} = event.detail || event.value; // detail 是微信,value 是百度(dipatch)
const options = {
mpType: 'component',
mpInstance: this,
propsData: properties
};
// 初始化 vue 实例
this.$vm = new VueComponent(options);
let parentVm;
// 处理$slots,$scopedSlots(暂不支持动态变化$slots)
const vueSlots = properties.vueSlots;
if (Array.isArray(vueSlots) && vueSlots.length) {
const $slots = Object.create(null);
vueSlots.forEach(slotName => {
$slots[slotName] = true;
});
this.$vm.$scopedSlots = this.$vm.$slots = $slots;
if (vuePid) {
parentVm = findVmByVueId(this.$vm, vuePid);
}
// 性能优先,mount 提前到 attached 中,保证组件首次渲染数据被合并
// 导致与标准 Vue 的差异,data 和 computed 中不能使用$parent,provide等组件属性
this.$vm.$mount();
}
function createComponent (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
let VueComponent;
if (isFn(vueOptions)) {
VueComponent = vueOptions; // TODO form-field props.name,props.value
vueOptions = VueComponent.extendOptions;
} else {
VueComponent = Vue.extend(vueOptions);
if (!parentVm) {
parentVm = this.$vm;
}
const behaviors = getBehaviors(vueOptions);
const properties = getProperties(vueOptions.props, false, vueOptions.__file);
vueOptions.parent = parentVm;
}
function parseApp (vm) {
return parseBaseApp(vm, {
mocks,
initRefs
})
}
function createApp (vm) {
App(parseApp(vm));
return vm
}
function parseBaseComponent (vueComponentOptions, {
isPage: isPage$$1,
initRelation: initRelation$$1
} = {}) {
let [VueComponent, vueOptions] = initVueComponent(Vue, vueComponentOptions);
const componentOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions, Vue.prototype),
behaviors,
properties,
data: initData(vueOptions, Vue.prototype),
behaviors: initBehaviors(vueOptions, initBehavior),
properties: initProperties(vueOptions.props, false, vueOptions.__file),
lifetimes: {
attached () {
initVm$2.call(this, VueComponent);
const properties = this.properties;
const options = {
mpType: isPage$$1.call(this) ? 'page' : 'component',
mpInstance: this,
propsData: properties
};
initVueIds(properties.vueId, this);
// 处理父子关系
initRelation$$1.call(this, {
vuePid: this._$vuePid,
vueOptions: options
});
// 初始化 vue 实例
this.$vm = new VueComponent(options);
// 处理$slots,$scopedSlots(暂不支持动态变化$slots)
initSlots(this.$vm, properties.vueSlots);
// 触发首次 setData
this.$vm.$mount();
},
ready () {
initVm$2.call(this, VueComponent); // 目前发现部分情况小程序 attached 不触发
triggerLink(this); // 处理 parent,children
// 补充生命周期
this.$vm.__call_hook('created');
this.$vm.__call_hook('beforeMount');
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
// 当组件 props 默认值为 true,初始化时传入 false 会导致 created,ready 触发, 但 attached 不触发
// https://developers.weixin.qq.com/community/develop/doc/00066ae2844cc0f8eb883e2a557800
if (this.$vm) {
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
} else {
this.is && console.warn(this.is + ' is not attached');
}
},
detached () {
this.$vm.$destroy();
......@@ -1024,7 +994,7 @@ function createComponent (vueOptions) {
},
pageLifetimes: {
show (args) {
this.$vm.__call_hook('onPageShow', args);
this.$vm && this.$vm.__call_hook('onPageShow', args);
},
hide () {
this.$vm && this.$vm.__call_hook('onPageHide');
......@@ -1034,12 +1004,68 @@ function createComponent (vueOptions) {
}
},
methods: {
__e: handleEvent,
__l: handleLink
__l: handleLink,
__e: handleEvent
}
};
return initComponent(componentOptions, vueOptions)
if (isPage$$1) {
return componentOptions
}
return [componentOptions, VueComponent]
}
function parseComponent (vueComponentOptions) {
return parseBaseComponent(vueComponentOptions, {
isPage,
initRelation
})
}
const hooks$1 = [
'onShow',
'onHide',
'onUnload'
];
hooks$1.push(...PAGE_EVENT_HOOKS);
function parseBasePage (vuePageOptions, {
isPage,
initRelation
}) {
const pageOptions = parseComponent(vuePageOptions, {
isPage,
initRelation
});
initHooks(pageOptions.methods, hooks$1);
pageOptions.methods.onLoad = function (args) {
this.$vm.$mp.query = args; // 兼容 mpvue
this.$vm.__call_hook('onLoad', args);
};
return pageOptions
}
function parsePage (vuePageOptions) {
return parseBasePage(vuePageOptions, {
isPage,
initRelation
})
}
function createPage (vuePageOptions) {
{
return Component(parsePage(vuePageOptions))
}
}
function createComponent (vueOptions) {
{
return Component(parseComponent(vueOptions))
}
}
todos.forEach(todoApi => {
......
import 'uni-platform/runtime/index'
import Vue from 'vue'
import {
mocks,
initRefs
} from 'uni-platform/runtime/wrapper/index'
import {
initHooks,
initMocks
} from './util'
const hooks = [
'onHide',
'onError',
'onPageNotFound',
'onUniNViewMessage'
]
function initVm (vm) {
if (this.$vm) { // 百度竟然 onShow 在 onLaunch 之前?
return
}
if (__PLATFORM__ === 'mp-weixin') {
if (!wx.canIUse('nextTick')) { // 事实 上2.2.3 即可,简单使用 2.3.0 的 nextTick 判断
console.error('当前微信基础库版本过低,请将 微信开发者工具-详情-项目设置-调试基础库版本 更换为`2.3.0`以上')
}
}
this.$vm = vm
this.$vm.$mp = {
app: this
}
}
import 'uni-platform/runtime/index'
import parseApp from 'uni-platform/runtime/wrapper/app-parser'
export function createApp (vm) {
// 外部初始化时 Vue 还未初始化,放到 createApp 内部初始化 mixin
if (__PLATFORM__ === 'mp-alipay') {
Object.defineProperty(Vue.prototype, '$slots', {
get () {
return this.$scope && this.$scope.props.$slots
},
set () {
}
})
Object.defineProperty(Vue.prototype, '$scopedSlots', {
get () {
return this.$scope && this.$scope.props.$scopedSlots
},
set () {
}
})
}
Vue.mixin({
beforeCreate () {
if (!this.$options.mpType) {
return
}
this.mpType = this.$options.mpType
this.$mp = {
data: {},
[this.mpType]: this.$options.mpInstance
}
this.$scope = this.$options.mpInstance
delete this.$options.mpType
delete this.$options.mpInstance
if (this.mpType !== 'app') {
initRefs(this)
initMocks(this, mocks)
}
},
created () { // 处理 injections
this.__init_injections(this)
this.__init_provide(this)
}
})
const appOptions = {
onLaunch (args) {
initVm.call(this, vm)
this.$vm._isMounted = true
this.$vm.__call_hook('mounted')
this.$vm.__call_hook('onLaunch', args)
},
onShow (args) {
initVm.call(this, vm)
this.$vm.__call_hook('onShow', args)
}
}
// 兼容旧版本 globalData
appOptions.globalData = vm.$options.globalData || {}
initHooks(appOptions, hooks) // 延迟执行,因为 App 的注册在 main.js 之前,可能导致生命周期内 Vue 原型上开发者注册的属性无法访问
App(appOptions)
App(parseApp(vm))
return vm
}
import Vue from 'vue'
import {
isFn
} from 'uni-shared'
import {
handleLink,
triggerLink,
initComponent
} from 'uni-platform/runtime/wrapper/index'
import {
getData,
handleEvent,
getBehaviors,
getProperties
} from './util'
function initVm (VueComponent) {
if (this.$vm) {
return
}
const properties = __PLATFORM__ === 'mp-alipay'
? this.props
: this.properties
const options = {
mpType: 'component',
mpInstance: this,
propsData: properties
}
// 初始化 vue 实例
this.$vm = new VueComponent(options)
// 处理$slots,$scopedSlots(暂不支持动态变化$slots)
const vueSlots = properties.vueSlots
if (Array.isArray(vueSlots) && vueSlots.length) {
const $slots = Object.create(null)
vueSlots.forEach(slotName => {
$slots[slotName] = true
})
this.$vm.$scopedSlots = this.$vm.$slots = $slots
}
// 性能优先,mount 提前到 attached 中,保证组件首次渲染数据被合并
// 导致与标准 Vue 的差异,data 和 computed 中不能使用$parent,provide等组件属性
this.$vm.$mount()
}
import parseComponent from 'uni-platform/runtime/wrapper/component-parser'
export function createComponent (vueOptions) {
vueOptions = vueOptions.default || vueOptions
let VueComponent
if (isFn(vueOptions)) {
VueComponent = vueOptions // TODO form-field props.name,props.value
vueOptions = VueComponent.extendOptions
if (__PLATFORM__ === 'mp-alipay') {
return my.createComponent(parseComponent(vueOptions))
} else {
VueComponent = Vue.extend(vueOptions)
}
const behaviors = getBehaviors(vueOptions)
const properties = getProperties(vueOptions.props, false, vueOptions.__file)
const componentOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions, Vue.prototype),
behaviors,
properties,
lifetimes: {
attached () {
initVm.call(this, VueComponent)
},
ready () {
initVm.call(this, VueComponent) // 目前发现部分情况小程序 attached 不触发
triggerLink(this) // 处理 parent,children
// 补充生命周期
this.$vm.__call_hook('created')
this.$vm.__call_hook('beforeMount')
this.$vm._isMounted = true
this.$vm.__call_hook('mounted')
this.$vm.__call_hook('onReady')
},
detached () {
this.$vm.$destroy()
}
},
pageLifetimes: {
show (args) {
this.$vm.__call_hook('onPageShow', args)
},
hide () {
this.$vm && this.$vm.__call_hook('onPageHide')
},
resize (size) {
this.$vm && this.$vm.__call_hook('onPageResize', size)
}
},
methods: {
__e: handleEvent,
__l: handleLink
}
return Component(parseComponent(vueOptions))
}
return initComponent(componentOptions, vueOptions)
}
import Vue from 'vue'
import parsePage from 'uni-platform/runtime/wrapper/page-parser'
import {
isFn
} from 'uni-shared'
import {
initPage,
handleLink
} from 'uni-platform/runtime/wrapper/index'
import {
getData,
initHooks,
handleEvent,
baiduPageDestroy
} from './util'
const hooks = [
'onShow',
'onHide',
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap',
'onBackPress',
'onNavigationBarButtonTap',
'onNavigationBarSearchInputChanged',
'onNavigationBarSearchInputConfirmed',
'onNavigationBarSearchInputClicked'
]
function initVm (VueComponent) { // 百度的 onLoad 触发在 attached 之前
if (this.$vm) {
return
}
this.$vm = new VueComponent({
mpType: 'page',
mpInstance: this
})
if (__PLATFORM__ === 'mp-baidu') {
this.$vm.$baiduComponentInstances = Object.create(null)
}
this.$vm.__call_hook('created')
this.$vm.$mount()
}
export function createPage (vueOptions) {
vueOptions = vueOptions.default || vueOptions
let VueComponent
if (isFn(vueOptions)) {
VueComponent = vueOptions
vueOptions = VueComponent.extendOptions
export function createPage (vuePageOptions) {
if (__PLATFORM__ === 'mp-alipay') {
return Page(parsePage(vuePageOptions))
} else {
VueComponent = Vue.extend(vueOptions)
return Component(parsePage(vuePageOptions))
}
const pageOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions, Vue.prototype),
lifetimes: { // 当页面作为组件时
attached () {
initVm.call(this, VueComponent)
},
ready () {
this.$vm.__call_hook('beforeMount')
this.$vm._isMounted = true
this.$vm.__call_hook('mounted')
this.$vm.__call_hook('onReady')
},
detached () {
this.$vm.$destroy()
}
},
methods: { // 作为页面时
onLoad (args) {
initVm.call(this, VueComponent)
if (__PLATFORM__ === 'mp-baidu') { // 百度当组件作为页面时 pageinstancce 不是原来组件的 instance
this.pageinstance.$vm = this.$vm
}
this.$vm.$mp.query = args // 又要兼容 mpvue
this.$vm.__call_hook('onLoad', args) // 开发者可能会在 onLoad 时赋值,提前到 mount 之前
},
onUnload () {
this.$vm.__call_hook('onUnload')
if (__PLATFORM__ === 'mp-baidu') { // 百度组件不会在页面 unload 时触发 detached
baiduPageDestroy(this.$vm)
}
},
__e: handleEvent,
__l: handleLink
}
}
initHooks(pageOptions.methods, hooks)
return initPage(pageOptions, vueOptions)
}
......@@ -5,9 +5,14 @@ import {
isPlainObject
} from 'uni-shared'
import {
initBehavior
} from 'uni-platform/runtime/wrapper/index'
export const PAGE_EVENT_HOOKS = [
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap'
]
export function initMocks (vm, mocks) {
const mpInstance = vm.$mp[vm.mpType]
......@@ -21,12 +26,46 @@ export function initMocks (vm, mocks) {
export function initHooks (mpOptions, hooks) {
hooks.forEach(hook => {
mpOptions[hook] = function (args) {
return this.$vm.__call_hook(hook, args)
return this.$vm && this.$vm.__call_hook(hook, args)
}
})
}
export function getData (vueOptions, context) {
export function initVueComponent (Vue, vueOptions) {
vueOptions = vueOptions.default || vueOptions
let VueComponent
if (isFn(vueOptions)) {
VueComponent = vueOptions
vueOptions = VueComponent.extendOptions
} else {
VueComponent = Vue.extend(vueOptions)
}
return [VueComponent, vueOptions]
}
export function initSlots (vm, vueSlots) {
if (Array.isArray(vueSlots) && vueSlots.length) {
const $slots = Object.create(null)
vueSlots.forEach(slotName => {
$slots[slotName] = true
})
vm.$scopedSlots = vm.$slots = $slots
}
}
export function initVueIds (vueIds, mpInstance) {
vueIds = (vueIds || '').split(',')
const len = vueIds.length
if (len === 1) {
mpInstance._$vueId = vueIds[0]
} else if (len === 2) {
mpInstance._$vueId = vueIds[0]
mpInstance._$vuePid = vueIds[1]
}
}
export function initData (vueOptions, context) {
let data = vueOptions.data || {}
const methods = vueOptions.methods || {}
......@@ -68,7 +107,7 @@ function createObserver (name) {
}
}
export function getBehaviors (vueOptions) {
export function initBehaviors (vueOptions, initBehavior) {
const vueBehaviors = vueOptions['behaviors']
const vueExtends = vueOptions['extends']
const vueMixins = vueOptions['mixins']
......@@ -97,7 +136,7 @@ export function getBehaviors (vueOptions) {
if (isPlainObject(vueExtends) && vueExtends.props) {
behaviors.push(
initBehavior({
properties: getProperties(vueExtends.props, true)
properties: initProperties(vueExtends.props, true)
})
)
}
......@@ -106,7 +145,7 @@ export function getBehaviors (vueOptions) {
if (isPlainObject(vueMixin) && vueMixin.props) {
behaviors.push(
initBehavior({
properties: getProperties(vueMixin.props, true)
properties: initProperties(vueMixin.props, true)
})
)
}
......@@ -139,9 +178,13 @@ function parsePropType (key, type, defaultValue, file) {
return type
}
export function getProperties (props, isBehavior = false, file = '') {
export function initProperties (props, isBehavior = false, file = '') {
const properties = {}
if (!isBehavior) {
properties.vueId = {
type: String,
value: ''
}
properties.vueSlots = { // 小程序不能直接定义 $slots 的 props,所以通过 vueSlots 转换到 $slots
type: null,
value: [],
......@@ -397,18 +440,4 @@ export function handleEvent (event) {
})
}
})
}
function baiduComponentDestroy ($vm) {
$vm.$children.forEach(childVm => {
childVm.$mp.component.detached()
})
$vm.$mp.component.detached()
}
export function baiduPageDestroy ($vm) {
$vm.$destroy()
$vm.$children.forEach(childVm => {
baiduComponentDestroy(childVm)
})
}
import {
initHooks
} from 'uni-wrapper/util'
import parseBaseApp from '../../../mp-weixin/runtime/wrapper/app-parser'
const hooks = [
'onUniNViewMessage'
]
export default function parseApp (vm) {
const appOptions = parseBaseApp(vm)
initHooks(appOptions, hooks)
return appOptions
}
import parseBaseComponent from '../../../mp-weixin/runtime/wrapper/component-parser'
export default function parseComponent (vueComponentOptions) {
const componentOptions = parseBaseComponent(vueComponentOptions)
componentOptions.methods.$getAppWebview = function () {
return plus.webview.getWebviewById(`${this.__wxWebviewId__}`)
}
return componentOptions
}
export {
mocks,
initRefs,
handleLink,
triggerLink,
initBehavior
}
from '../../../mp-weixin/runtime/wrapper/index'
export function initPage (pageOptions) {
return initComponent(pageOptions)
}
export function initComponent (componentOptions) {
componentOptions.methods.$getAppWebview = function () {
return plus.webview.getWebviewById(`${this.__wxWebviewId__}`)
}
return Component(componentOptions)
}
import {
initHooks
} from 'uni-wrapper/util'
import parseBasePage from '../../../mp-weixin/runtime/wrapper/page-parser'
const hooks = [
'onBackPress',
'onNavigationBarButtonTap',
'onNavigationBarSearchInputChanged',
'onNavigationBarSearchInputConfirmed',
'onNavigationBarSearchInputClicked'
]
export default function parsePage (vuePageOptions) {
const pageOptions = parseBasePage(vuePageOptions)
initHooks(pageOptions.methods, hooks)
return pageOptions
}
import Vue from 'vue'
import parseBaseApp from '../../../mp-weixin/runtime/wrapper/app-base-parser'
import {
mocks,
initRefs
} from './util'
export default function parseApp (vm) {
Object.defineProperty(Vue.prototype, '$slots', {
get () {
return this.$scope && this.$scope.props.$slots
},
set () {
}
})
Object.defineProperty(Vue.prototype, '$scopedSlots', {
get () {
return this.$scope && this.$scope.props.$scopedSlots
},
set () {
}
})
return parseBaseApp(vm, {
mocks,
initRefs
})
}
import Vue from 'vue'
import {
initData,
initVueIds,
handleEvent,
initBehaviors,
initProperties,
initVueComponent
} from 'uni-wrapper/util'
import {
handleRef,
handleLink,
initBehavior,
initRelation,
triggerEvent,
createObserver,
isComponent2,
initChildVues
} from './util'
function initVm (VueComponent) {
if (this.$vm) {
return
}
const properties = this.props
const options = {
mpType: 'component',
mpInstance: this,
propsData: properties
}
initVueIds(properties.vueId, this)
if (isComponent2) {
// 处理父子关系
initRelation.call(this, {
vuePid: this._$vuePid,
vueOptions: options
})
// 初始化 vue 实例
this.$vm = new VueComponent(options)
// 触发首次 setData
this.$vm.$mount()
} else {
initChildVues(this)
// 处理父子关系
initRelation.call(this, {
vuePid: this._$vuePid,
vueOptions: options,
VueComponent,
mpInstance: this
})
if (options.parent) { // 父组件已经初始化,直接初始化子,否则放到父组件的 didMount 中处理
// 初始化 vue 实例
this.$vm = new VueComponent(options)
handleRef.call(options.parent.$scope, this)
// 触发首次 setData
this.$vm.$mount()
this.$vm._isMounted = true
this.$vm.__call_hook('mounted')
this.$vm.__call_hook('onReady')
}
}
}
export default function parseComponent (vueComponentOptions) {
let [VueComponent, vueOptions] = initVueComponent(Vue, vueComponentOptions)
const properties = initProperties(vueOptions.props, false, vueOptions.__file)
const props = {
onVueInit: function () {}
}
Object.keys(properties).forEach(key => {
if (key !== 'vueSlots') {
props[key] = properties[key].value
}
})
const componentOptions = {
mixins: initBehaviors(vueOptions, initBehavior),
data: initData(vueOptions, Vue.prototype),
props,
didMount () {
initVm.call(this, VueComponent)
if (isComponent2) {
this.$vm._isMounted = true
this.$vm.__call_hook('mounted')
this.$vm.__call_hook('onReady')
}
},
didUnmount () {
this.$vm.$destroy()
},
methods: {
__r: handleRef,
__e: handleEvent,
__l: handleLink,
triggerEvent
}
}
if (isComponent2) {
componentOptions.onInit = function onInit () {
initVm.call(this, VueComponent)
}
componentOptions.deriveDataFromProps = createObserver()
} else {
componentOptions.didUpdate = createObserver(true)
}
if (vueOptions.methods && vueOptions.methods.formReset) {
componentOptions.methods.formReset = vueOptions.methods.formReset
}
return componentOptions
}
import Vue from 'vue'
import {
initData,
initHooks,
handleEvent,
initBehaviors,
initVueComponent,
PAGE_EVENT_HOOKS
} from 'uni-wrapper/util'
import {
handleRef,
handleLink,
initBehavior,
initChildVues
} from './util'
const hooks = [
'onShow',
'onHide',
// mp-alipay 特有
'onTitleClick',
'onOptionMenuClick',
'onPopMenuClick',
'onPullIntercept'
]
hooks.push(...PAGE_EVENT_HOOKS)
export default function parsePage (vuePageOptions) {
let [VueComponent, vueOptions] = initVueComponent(Vue, vuePageOptions)
const pageOptions = {
mixins: initBehaviors(vueOptions, initBehavior),
data: initData(vueOptions, Vue.prototype),
onLoad (args) {
const properties = this.props
const options = {
mpType: 'page',
mpInstance: this,
propsData: properties
}
// 初始化 vue 实例
this.$vm = new VueComponent(options)
// 触发首次 setData
this.$vm.$mount()
this.$vm.$mp.query = args // 兼容 mpvue
this.$vm.__call_hook('onLoad', args)
},
onReady () {
initChildVues(this)
this.$vm._isMounted = true
this.$vm.__call_hook('mounted')
this.$vm.__call_hook('onReady')
},
onUnload () {
this.$vm.__call_hook('onUnload')
this.$vm.$destroy()
},
__r: handleRef,
__e: handleEvent,
__l: handleLink
}
initHooks(pageOptions, hooks)
return pageOptions
}
......@@ -4,6 +4,10 @@ import {
camelize
} from 'uni-shared'
import {
handleLink as handleBaseLink
} from '../../../mp-weixin/runtime/wrapper/util'
import deepEqual from './deep-equal'
const customizeRE = /:/g
......@@ -12,13 +16,62 @@ const customize = cached((str) => {
return camelize(str.replace(customizeRE, '-'))
})
export const isComponent2 = my.canIUse('component2')
export const mocks = ['$id']
export function initRefs () {
}
function handleRef (ref) {
export function initBehavior ({
properties
}) {
const props = {}
Object.keys(properties).forEach(key => {
props[key] = properties[key].value
})
return {
props
}
}
export function initRelation (detail) {
this.props.onVueInit(detail)
}
export function initChildVues (mpInstance) {
mpInstance._$childVues && mpInstance._$childVues.forEach(({
vuePid,
vueOptions,
VueComponent,
mpInstance: childMPInstance
}) => {
// 父子关系
handleBaseLink.call(mpInstance, {
detail: {
vuePid,
vueOptions
}
})
childMPInstance.$vm = new VueComponent(vueOptions)
handleRef.call(vueOptions.parent.$scope, childMPInstance)
childMPInstance.$vm.$mount()
childMPInstance.$vm._isMounted = true
childMPInstance.$vm.__call_hook('mounted')
childMPInstance.$vm.__call_hook('onReady')
})
delete mpInstance._$childVues
}
export function handleRef (ref) {
if (!ref) {
return
}
......@@ -31,37 +84,7 @@ function handleRef (ref) {
}
}
export function initPage (pageOptions, vueOptions) {
const {
lifetimes,
methods
} = pageOptions
pageOptions.onReady = lifetimes.ready
pageOptions.onUnload = function () {
lifetimes.detached.call(this)
methods.onUnload.call(this)
}
Object.keys(methods).forEach(method => {
if (method !== 'onUnload') {
pageOptions[method] = methods[method]
}
})
pageOptions['__r'] = handleRef
if (vueOptions.methods && vueOptions.methods.formReset) {
pageOptions['formReset'] = vueOptions.methods.formReset
}
delete pageOptions.lifetimes
delete pageOptions.methods
return Page(pageOptions)
}
function triggerEvent (type, detail, options) {
export function triggerEvent (type, detail, options) {
const handler = this.props[customize('on-' + type)]
if (!handler) {
return
......@@ -85,7 +108,7 @@ function triggerEvent (type, detail, options) {
const IGNORES = ['$slots', '$scopedSlots']
function createObserver (isDidUpdate) {
export function createObserver (isDidUpdate) {
return function observe (props) {
const prevProps = isDidUpdate ? props : this.props
const nextProps = isDidUpdate ? this.props : props
......@@ -104,92 +127,24 @@ function createObserver (isDidUpdate) {
}
}
export function initComponent (componentOptions, vueOptions) {
const {
lifetimes,
properties,
behaviors
} = componentOptions
componentOptions.mixins = behaviors
const props = {
onTriggerLink: function () {}
}
Object.keys(properties).forEach(key => {
if (key !== 'vueSlots') {
props[key] = properties[key].value
export const handleLink = (function () {
if (isComponent2) {
return function handleLink (detail) {
return handleBaseLink.call(this, {
detail
})
}
})
componentOptions.props = props
if (my.canIUse('component2')) {
componentOptions.onInit = lifetimes.attached
}
componentOptions.didMount = lifetimes.ready
componentOptions.didUnmount = lifetimes.detached
if (my.canIUse('component2')) {
componentOptions.deriveDataFromProps = createObserver() // nextProps
} else {
componentOptions.didUpdate = createObserver(true) // prevProps
}
if (!componentOptions.methods) {
componentOptions.methods = {}
}
if (vueOptions.methods && vueOptions.methods.formReset) {
componentOptions.methods['formReset'] = vueOptions.methods.formReset
}
componentOptions.methods['__r'] = handleRef
componentOptions.methods.triggerEvent = triggerEvent
delete componentOptions.properties
delete componentOptions.behaviors
delete componentOptions.lifetimes
delete componentOptions.pageLifetimes
return my.createComponent(componentOptions)
}
export function initBehavior ({
properties
}) {
const props = {}
Object.keys(properties).forEach(key => {
props[key] = properties[key].value
})
return {
props
}
}
export function triggerLink (mpInstance, vueOptions) {
mpInstance.props.onTriggerLink(mpInstance.$vm || vueOptions)
}
export function handleLink (detail) {
if (detail.$mp) { // vm
if (!detail.$parent) {
detail.$parent = this.$vm
if (detail.$parent) {
detail.$parent.$children.push(detail)
detail.$root = this.$vm.$root
if (!my.canIUse('component2')) {
handleRef.call(this, detail.$scope)
return function handleLink (detail) {
if (this.$vm) { // 父已初始化
return handleBaseLink.call(this, {
detail: {
vuePid: detail.vuePid,
vueOptions: detail.vueOptions
}
}
}
} else { // vueOptions
if (!detail.parent) {
detail.parent = this.$vm
})
}
// 支付宝通过 didMount 来实现,先子后父,故等父 ready 之后,统一初始化
(this._$childVues || (this._$childVues = [])).unshift(detail)
}
}
})()
import parseBaseApp from '../../../mp-weixin/runtime/wrapper/app-base-parser'
import {
initRefs
} from '../../../mp-weixin/runtime/wrapper/util'
import {
mocks
} from './util'
export default function parseApp (vm) {
return parseBaseApp(vm, {
mocks,
initRefs
})
}
import {
isPage,
initRelation
} from './util'
import parseBaseComponent from '../../../mp-weixin/runtime/wrapper/component-base-parser'
export default function parseComponent (vueOptions) {
const componentOptions = parseBaseComponent(vueOptions, {
isPage,
initRelation
})
const oldAttached = componentOptions.lifetimes.attached
componentOptions.lifetimes.attached = function attached () {
oldAttached.call(this)
if (isPage.call(this)) { // 百度 onLoad 在 attached 之前触发
// 百度 当组件作为页面时 pageinstancce 不是原来组件的 instance
this.pageinstance.$vm = this.$vm
this.$vm.$mp.query = this.pageinstance._$args // 兼容 mpvue
this.$vm.__call_hook('onLoad', this.pageinstance._$args)
}
}
componentOptions.messages = {
'__l': componentOptions.methods['__l']
}
delete componentOptions.methods['__l']
return componentOptions
}
export {
initRefs,
initBehavior
}
from '../../../mp-weixin/runtime/wrapper/index'
export const mocks = ['nodeId']
export function initPage (pageOptions) {
return initComponent(pageOptions)
}
export function initComponent (componentOptions) {
componentOptions.messages = {
'__l': handleLink
}
return Component(componentOptions)
}
export function triggerLink (mpInstance, vueOptions) {
mpInstance.dispatch('__l', mpInstance.$vm || vueOptions)
}
export function handleLink (event) {
const target = event.value
if (target.$mp) {
if (!target.$parent) {
target.$parent = this.$vm
target.$parent.$children.push(target)
target.$root = this.$vm.$root
}
} else {
if (!target.parent) {
target.parent = this.$vm
}
}
}
import {
isPage,
initRelation
} from './util'
import parseBasePage from '../../../mp-weixin/runtime/wrapper/page-base-parser'
function detached ($vm) {
$vm.$children.forEach(childVm => {
childVm.$scope.detached()
})
$vm.$scope.detached()
}
function onPageUnload ($vm) {
$vm.$destroy()
$vm.$children.forEach(childVm => {
detached(childVm)
})
}
export default function parsePage (vuePageOptions) {
const pageOptions = parseBasePage(vuePageOptions, {
isPage,
initRelation
})
pageOptions.methods.onLoad = function onLoad (args) {
// 百度 onLoad 在 attached 之前触发,先存储 args, 在 attached 里边触发 onLoad
this.pageinstance._$args = args
}
pageOptions.methods.onUnload = function onUnload () {
this.$vm.__call_hook('onUnload')
onPageUnload(this.$vm)
}
return pageOptions
}
export const mocks = ['nodeId']
export function isPage () {
return !this.ownerId
}
export function initRelation (detail) {
this.dispatch('__l', detail)
}
import Vue from 'vue'
import parseBaseApp from '../../../mp-weixin/runtime/wrapper/app-base-parser'
import {
mocks,
initRefs
} from './util'
export default function parseApp (vm) {
Vue.prototype._$fallback = true // 降级(调整原 vue 的部分生命周期,如 created,beforeMount,inject,provide)
Vue.mixin({
created () { // 处理 injections,头条 triggerEvent 是异步,且触发时机很慢,故延迟 relation 设置
if (this.mpType !== 'app') {
initRefs(this)
this.__init_injections(this)
this.__init_provide(this)
}
}
})
return parseBaseApp(vm, {
mocks,
initRefs: function () {} // attached 时,可能查询不到
})
}
import {
isPage,
initRelation,
handleLink
} from './util'
import {
initSlots,
initVueIds
} from 'uni-wrapper/util'
import parseBaseComponent from '../../../mp-weixin/runtime/wrapper/component-base-parser'
export default function parseComponent (vueOptions) {
const [componentOptions, VueComponent] = parseBaseComponent(vueOptions)
componentOptions.lifetimes.attached = function attached () {
const properties = this.properties
const options = {
mpType: isPage.call(this) ? 'page' : 'component',
mpInstance: this,
propsData: properties
}
initVueIds(properties.vueId, this)
// 初始化 vue 实例
this.$vm = new VueComponent(options)
// 处理$slots,$scopedSlots(暂不支持动态变化$slots)
initSlots(this.$vm, properties.vueSlots)
// 处理父子关系
initRelation.call(this, {
vuePid: this._$vuePid,
mpInstance: this
})
// 触发首次 setData
this.$vm.$mount()
}
// ready 比 handleLink 还早,初始化逻辑放到 handleLink 中
delete componentOptions.lifetimes.ready
componentOptions.methods.__l = handleLink
return componentOptions
}
import {
isPage,
initRelation
} from './util'
import parseBasePage from '../../../mp-weixin/runtime/wrapper/page-base-parser'
export default function parsePage (vuePageOptions) {
const pageOptions = parseBasePage(vuePageOptions, {
isPage,
initRelation
})
// 页面需要在 ready 中触发,其他组件是在 handleLink 中触发
pageOptions.lifetimes.ready = function ready () {
if (this.$vm && this.$vm.mpType === 'page') {
this.$vm.__call_hook('created')
this.$vm.__call_hook('beforeMount')
this.$vm._isMounted = true
this.$vm.__call_hook('mounted')
this.$vm.__call_hook('onReady')
} else {
this.is && console.warn(this.is + ' is not ready')
}
}
return pageOptions
}
import Vue from 'vue'
import {
initHooks,
initMocks
} from 'uni-wrapper/util'
const hooks = [
'onHide',
'onError',
'onPageNotFound'
]
export default function parseBaseApp (vm, {
mocks,
initRefs
}) {
Vue.prototype.mpHost = __PLATFORM__
Vue.mixin({
beforeCreate () {
if (!this.$options.mpType) {
return
}
this.mpType = this.$options.mpType
this.$mp = {
data: {},
[this.mpType]: this.$options.mpInstance
}
this.$scope = this.$options.mpInstance
delete this.$options.mpType
delete this.$options.mpInstance
if (this.mpType !== 'app') {
initRefs(this)
initMocks(this, mocks)
}
}
})
const appOptions = {
onLaunch (args) {
if (__PLATFORM__ === 'mp-weixin') {
if (!wx.canIUse('nextTick')) { // 事实 上2.2.3 即可,简单使用 2.3.0 的 nextTick 判断
console.error('当前微信基础库版本过低,请将 微信开发者工具-详情-项目设置-调试基础库版本 更换为`2.3.0`以上')
}
}
this.$vm = vm
this.$vm.$mp = {
app: this
}
this.$vm.$scope = this
this.$vm._isMounted = true
this.$vm.__call_hook('mounted', args)
this.$vm.__call_hook('onLaunch', args)
}
}
// 兼容旧版本 globalData
appOptions.globalData = vm.$options.globalData || {}
initHooks(appOptions, hooks)
return appOptions
}
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册