diff --git a/packages/uni-app-plus/dist/index.js b/packages/uni-app-plus/dist/index.js index e7fcff6f0446e3fbda840a980a380d93bc884fe6..46c3bf3d49baf1b86c144055441c539dc1680d19 100644 --- a/packages/uni-app-plus/dist/index.js +++ b/packages/uni-app-plus/dist/index.js @@ -44,8 +44,6 @@ const SYNC_API_RE = /requireNativePlugin|upx2px|hideKeyboard|canIUse|^create|Syn const CONTEXT_API_RE = /^create|Manager$/; -const TASK_APIS = ['request', 'downloadFile', 'uploadFile', 'connectSocket']; - const CALLBACK_API_RE = /^on/; function isContextApi (name) { @@ -59,10 +57,6 @@ function isCallbackApi (name) { return CALLBACK_API_RE.test(name) } -function isTaskApi (name) { - return TASK_APIS.indexOf(name) !== -1 -} - function handlePromise (promise) { return promise.then(data => { return [null, data] @@ -74,8 +68,7 @@ function shouldPromise (name) { if ( isContextApi(name) || isSyncApi(name) || - isCallbackApi(name) || - isTaskApi(name) + isCallbackApi(name) ) { return false } @@ -268,8 +261,8 @@ var api = /*#__PURE__*/Object.freeze({ requireNativePlugin: requireNativePlugin }); -const WXPage = Page; -const WXComponent = Component; +const MPPage = Page; +const MPComponent = Component; const customizeRE = /:/g; @@ -278,12 +271,10 @@ const customize = cached((str) => { }); function initTriggerEvent (mpInstance) { - if (wx.canIUse('nextTick')) { // 微信旧版本基础库不支持重写triggerEvent - const oldTriggerEvent = mpInstance.triggerEvent; - mpInstance.triggerEvent = function (event, ...args) { - return oldTriggerEvent.apply(mpInstance, [customize(event), ...args]) - }; - } + const oldTriggerEvent = mpInstance.triggerEvent; + mpInstance.triggerEvent = function (event, ...args) { + return oldTriggerEvent.apply(mpInstance, [customize(event), ...args]) + }; } Page = function (options = {}) { @@ -299,7 +290,7 @@ Page = function (options = {}) { return oldHook.apply(this, args) }; } - return WXPage(options) + return MPPage(options) }; const behavior = Behavior({ @@ -310,10 +301,10 @@ const behavior = Behavior({ Component = function (options = {}) { (options.behaviors || (options.behaviors = [])).unshift(behavior); - return WXComponent(options) + return MPComponent(options) }; -const MOCKS = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__']; +const MOCKS = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__', '__webviewId__']; function initMocks (vm) { const mpInstance = vm.$mp[vm.mpType]; @@ -515,7 +506,7 @@ function processEventArgs (vm, event, args = [], extra = [], isCustom, methodNam if (isCustomMPEvent) { return [event] } - return event.detail + return event.detail.__args__ || event.detail } } @@ -528,7 +519,7 @@ function processEventArgs (vm, event, args = [], extra = [], isCustom, methodNam ret.push(event.target.value); } else { if (isCustom && !isCustomMPEvent) { - ret.push(event.detail[0]); + ret.push(event.detail.__args__[0]); } else { // wxcomponent 组件或内置组件 ret.push(event); } @@ -600,7 +591,7 @@ function initRefs (vm) { const mpInstance = vm.$mp[vm.mpType]; Object.defineProperty(vm, '$refs', { get () { - const $refs = Object.create(null); + const $refs = {}; const components = mpInstance.selectAllComponents('.vue-ref'); components.forEach(component => { const ref = component.dataset.ref; @@ -620,13 +611,24 @@ function initRefs (vm) { } const hooks = [ - 'onShow', 'onHide', 'onError', 'onPageNotFound', 'onUniNViewMessage' ]; +function initVm (vm) { + if (this.$vm) { // 百度竟然 onShow 在 onLaunch 之前? + return + } + + this.$vm = vm; + + this.$vm.$mp = { + app: this + }; +} + function createApp (vm) { // 外部初始化时 Vue 还未初始化,放到 createApp 内部初始化 mixin Vue.mixin({ @@ -643,7 +645,9 @@ function createApp (vm) { delete this.$options.mpInstance; if (this.mpType !== 'app') { - initRefs(this); + { // 头条的 selectComponent 竟然是异步的 + initRefs(this); + } initMocks(this); } }, @@ -655,17 +659,17 @@ function createApp (vm) { const appOptions = { onLaunch (args) { - - this.$vm = vm; - - this.$vm.$mp = { - app: this - }; + 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); } }; @@ -701,6 +705,16 @@ function handleLink (event) { } } +function initPage$1 (pageOptions) { + initComponent$1(pageOptions); +} + +function initComponent$1 (componentOptions) { + componentOptions.methods.$getAppWebview = function () { + return plus.webview.getWebviewById(`${this.__wxWebviewId__}`) + }; +} + const hooks$1 = [ 'onShow', 'onHide', @@ -717,6 +731,20 @@ const hooks$1 = [ 'onNavigationBarSearchInputClicked' ]; +function initVm$1 (VueComponent) { // 百度的 onLoad 触发在 attached 之前 + if (this.$vm) { + return + } + + this.$vm = new VueComponent({ + mpType: 'page', + mpInstance: this + }); + + this.$vm.__call_hook('created'); + this.$vm.$mount(); +} + function createPage (vueOptions) { vueOptions = vueOptions.default || vueOptions; let VueComponent; @@ -734,14 +762,7 @@ function createPage (vueOptions) { data: getData(vueOptions, Vue.prototype), lifetimes: { // 当页面作为组件时 attached () { - - this.$vm = new VueComponent({ - mpType: 'page', - mpInstance: this - }); - - this.$vm.__call_hook('created'); - this.$vm.$mount(); + initVm$1.call(this, VueComponent); }, ready () { this.$vm.__call_hook('beforeMount'); @@ -755,6 +776,7 @@ function createPage (vueOptions) { }, methods: { // 作为页面时 onLoad (args) { + initVm$1.call(this, VueComponent); this.$vm.$mp.query = args; // 又要兼容 mpvue this.$vm.__call_hook('onLoad', args); // 开发者可能会在 onLoad 时赋值,提前到 mount 之前 }, @@ -767,41 +789,37 @@ function createPage (vueOptions) { }; initHooks(pageOptions.methods, hooks$1); - - { - pageOptions.methods.$getAppWebview = function () { - return plus.webview.getWebviewById(`${this.__wxWebviewId__}`) - }; - } + + initPage$1(pageOptions); return Component(pageOptions) } -function initVueComponent (mpInstace, VueComponent, extraOptions = {}) { - if (mpInstace.$vm) { +function initVm$2 (VueComponent) { + if (this.$vm) { return } - const options = Object.assign({ + const options = { mpType: 'component', - mpInstance: mpInstace, - propsData: mpInstace.properties - }, extraOptions); + mpInstance: this, + propsData: this.properties + }; // 初始化 vue 实例 - mpInstace.$vm = new VueComponent(options); + this.$vm = new VueComponent(options); // 处理$slots,$scopedSlots(暂不支持动态变化$slots) - const vueSlots = mpInstace.properties.vueSlots; + const vueSlots = this.properties.vueSlots; if (Array.isArray(vueSlots) && vueSlots.length) { const $slots = Object.create(null); vueSlots.forEach(slotName => { $slots[slotName] = true; }); - mpInstace.$vm.$scopedSlots = mpInstace.$vm.$slots = $slots; + this.$vm.$scopedSlots = this.$vm.$slots = $slots; } // 性能优先,mount 提前到 attached 中,保证组件首次渲染数据被合并 // 导致与标准 Vue 的差异,data 和 computed 中不能使用$parent,provide等组件属性 - mpInstace.$vm.$mount(); + this.$vm.$mount(); } function createComponent (vueOptions) { @@ -820,10 +838,10 @@ function createComponent (vueOptions) { properties, lifetimes: { attached () { - initVueComponent(this, VueComponent); + initVm$2.call(this, VueComponent); }, ready () { - initVueComponent(this, VueComponent); // 目前发现部分情况小程序 attached 不触发 + initVm$2.call(this, VueComponent); // 目前发现部分情况小程序 attached 不触发 triggerLink(this); // 处理 parent,children // 补充生命周期 @@ -854,6 +872,8 @@ function createComponent (vueOptions) { } }; + initComponent$1(componentOptions); + return Component(componentOptions) } diff --git a/packages/uni-app-plus/package.json b/packages/uni-app-plus/package.json index df98e96fe9f0311ec9c740c1edf688463398a636..95613a2e1b32d4d1cfcb1f5a7ad43295cada6f4b 100644 --- a/packages/uni-app-plus/package.json +++ b/packages/uni-app-plus/package.json @@ -1,6 +1,6 @@ { "name": "@dcloudio/uni-app-plus", - "version": "0.0.216", + "version": "0.0.217", "description": "uni-app app-plus", "main": "dist/index.js", "scripts": { diff --git a/packages/uni-mp-baidu/dist/index.js b/packages/uni-mp-baidu/dist/index.js index 91dcea59906c0eda872cb10df9266efea541075a..b71e33d938f5a0b310829d750e0c0e62b79de28a 100644 --- a/packages/uni-mp-baidu/dist/index.js +++ b/packages/uni-mp-baidu/dist/index.js @@ -19,9 +19,28 @@ function hasOwn (obj, key) { return hasOwnProperty.call(obj, key) } -function noop () {} +function noop () {} + +/** + * Create a cached version of a pure function. + */ +function cached (fn) { + const cache = Object.create(null); + return function cachedFn (str) { + const hit = cache[str]; + return hit || (cache[str] = fn(str)) + } +} + +/** + * Camelize a hyphen-delimited string. + */ +const camelizeRE = /-(\w)/g; +const camelize = cached((str) => { + return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '') +}); -const SYNC_API_RE = /hideKeyboard|upx2px|canIUse|^create|Sync$|Manager$/; +const SYNC_API_RE = /requireNativePlugin|upx2px|hideKeyboard|canIUse|^create|Sync$|Manager$/; const CONTEXT_API_RE = /^create|Manager$/; @@ -46,10 +65,11 @@ function handlePromise (promise) { } function shouldPromise (name) { - if (isSyncApi(name)) { - return false - } - if (isCallbackApi(name)) { + if ( + isContextApi(name) || + isSyncApi(name) || + isCallbackApi(name) + ) { return false } return true @@ -381,7 +401,50 @@ var api = /*#__PURE__*/Object.freeze({ requestPayment: requestPayment }); -const MOCKS = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__']; +const MPPage = Page; +const MPComponent = Component; + +const customizeRE = /:/g; + +const customize = cached((str) => { + return camelize(str.replace(customizeRE, '-')) +}); + +function initTriggerEvent (mpInstance) { + const oldTriggerEvent = mpInstance.triggerEvent; + mpInstance.triggerEvent = function (event, ...args) { + return oldTriggerEvent.apply(mpInstance, [customize(event), ...args]) + }; +} + +Page = function (options = {}) { + const name = 'onLoad'; + const oldHook = options[name]; + if (!oldHook) { + options[name] = function () { + initTriggerEvent(this); + }; + } else { + options[name] = function (...args) { + initTriggerEvent(this); + return oldHook.apply(this, args) + }; + } + return MPPage(options) +}; + +const behavior = Behavior({ + created () { + initTriggerEvent(this); + } +}); + +Component = function (options = {}) { + (options.behaviors || (options.behaviors = [])).unshift(behavior); + return MPComponent(options) +}; + +const MOCKS = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__', '__webviewId__']; function initMocks (vm) { const mpInstance = vm.$mp[vm.mpType]; @@ -392,25 +455,21 @@ function initMocks (vm) { }); } -function initHooks (mpOptions, hooks, delay = false) { +function initHooks (mpOptions, hooks) { hooks.forEach(hook => { mpOptions[hook] = function (args) { - if (delay) { - setTimeout(() => this.$vm.__call_hook(hook, args)); - } else { - this.$vm.__call_hook(hook, args); - } + return this.$vm.__call_hook(hook, args) }; }); } -function getData (vueOptions) { +function getData (vueOptions, context) { let data = vueOptions.data || {}; const methods = vueOptions.methods || {}; if (typeof data === 'function') { try { - data = data(); + data = data.call(context); // 支持 Vue.prototype 上挂的数据 } catch (e) { if (process.env.VUE_APP_DEBUG) { console.warn('根据 Vue 的 data 函数初始化小程序 data 失败,请尽量确保 data 函数中不访问 vm 对象,否则可能影响首次数据渲染速度。', data); @@ -424,7 +483,7 @@ function getData (vueOptions) { } Object.keys(methods).forEach(methodName => { - if (!hasOwn(data, methodName)) { + if (context.__lifecycle_hooks__.indexOf(methodName) === -1 && !hasOwn(data, methodName)) { data[methodName] = methods[methodName]; } }); @@ -494,30 +553,133 @@ function wrapper$1 (event) { event.preventDefault = noop; event.target = event.target || {}; - event.detail = event.detail || {}; + + if (!hasOwn(event, 'detail')) { + event.detail = {}; + } { // mp-baidu,checked=>value - if (hasOwn(event.detail, 'checked') && !hasOwn(event.detail, 'value')) { + if ( + isPlainObject(event.detail) && + hasOwn(event.detail, 'checked') && + !hasOwn(event.detail, 'value') + ) { event.detail.value = event.detail.checked; } } // TODO 又得兼容 mpvue 的 mp 对象 event.mp = event; - event.target = Object.assign({}, event.target, event.detail); + + if (isPlainObject(event.detail)) { + event.target = Object.assign({}, event.target, event.detail); + } + return event } -function processEventArgs (event, args = [], isCustom) { - if (isCustom && !args.length) { // 无参数,直接传入 detail 数组 - return event.detail +function getExtraValue (vm, dataPathsArray) { + let context = vm; + dataPathsArray.forEach(dataPathArray => { + const dataPath = dataPathArray[0]; + const value = dataPathArray[2]; + if (dataPath || typeof value !== 'undefined') { // ['','',index,'disable'] + const propPath = dataPathArray[1]; + const valuePath = dataPathArray[3]; + + const vFor = dataPath ? vm.__get_value(dataPath, context) : context; + + if (Number.isInteger(vFor)) { + context = value; + } else if (!propPath) { + context = vFor[value]; + } else { + if (Array.isArray(vFor)) { + context = vFor.find(vForItem => { + return vm.__get_value(propPath, vForItem) === value + }); + } else if (isPlainObject(vFor)) { + context = Object.keys(vFor).find(vForKey => { + return vm.__get_value(propPath, vFor[vForKey]) === value + }); + } else { + console.error('v-for 暂不支持循环数据:', vFor); + } + } + + if (valuePath) { + context = vm.__get_value(valuePath, context); + } + } + }); + return context +} + +function processEventExtra (vm, extra) { + const extraObj = {}; + + if (Array.isArray(extra) && extra.length) { + /** + *[ + * ['data.items', 'data.id', item.data.id], + * ['metas', 'id', meta.id] + *], + *[ + * ['data.items', 'data.id', item.data.id], + * ['metas', 'id', meta.id] + *], + *'test' + */ + extra.forEach((dataPath, index) => { + if (typeof dataPath === 'string') { + if (!dataPath) { // model,prop.sync + extraObj['$' + index] = vm; + } else { + extraObj['$' + index] = vm.__get_value(dataPath); + } + } else { + extraObj['$' + index] = getExtraValue(vm, dataPath); + } + }); } + + return extraObj +} + +function processEventArgs (vm, event, args = [], extra = [], isCustom, methodName) { + let isCustomMPEvent = false; // wxcomponent 组件,传递原始 event 对象 + if (isCustom) { // 自定义事件 + isCustomMPEvent = event.currentTarget && + event.currentTarget.dataset && + event.currentTarget.dataset.comType === 'wx'; + if (!args.length) { // 无参数,直接传入 event 或 detail 数组 + if (isCustomMPEvent) { + return [event] + } + return event.detail.__args__ || event.detail + } + } + + const extraObj = processEventExtra(vm, extra); + const ret = []; args.forEach(arg => { if (arg === '$event') { - ret.push(isCustom ? event.detail[0] : event); + if (methodName === '__set_model' && !isCustom) { // input v-model value + ret.push(event.target.value); + } else { + if (isCustom && !isCustomMPEvent) { + ret.push(event.detail.__args__[0]); + } else { // wxcomponent 组件或内置组件 + ret.push(event); + } + } } else { - ret.push(arg); + if (typeof arg === 'string' && hasOwn(extraObj, arg)) { + ret.push(extraObj[arg]); + } else { + ret.push(arg); + } } }); @@ -549,17 +711,27 @@ function handleEvent (event) { if (eventsArray && eventType === type) { eventsArray.forEach(eventArray => { - const handler = this.$vm[eventArray[0]]; - if (!isFn(handler)) { - throw new Error(` _vm.${eventArray[0]} is not a function`) - } - if (isOnce) { - if (handler.once) { - return + const methodName = eventArray[0]; + if (methodName) { + const handler = this.$vm[methodName]; + if (!isFn(handler)) { + throw new Error(` _vm.${methodName} is not a function`) + } + if (isOnce) { + if (handler.once) { + return + } + handler.once = true; } - handler.once = true; + handler.apply(this.$vm, processEventArgs( + this.$vm, + event, + eventArray[1], + eventArray[2], + isCustom, + methodName + )); } - handler.apply(this.$vm, processEventArgs(event, eventArray[1], isCustom)); }); } }); @@ -569,11 +741,11 @@ function initRefs (vm) { const mpInstance = vm.$mp[vm.mpType]; Object.defineProperty(vm, '$refs', { get () { - const $refs = Object.create(null); + const $refs = {}; const components = mpInstance.selectAllComponents('.vue-ref'); components.forEach(component => { const ref = component.dataset.ref; - $refs[ref] = component.$vm; + $refs[ref] = component.$vm || component; }); const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for'); forComponents.forEach(component => { @@ -581,7 +753,7 @@ function initRefs (vm) { if (!$refs[ref]) { $refs[ref] = []; } - $refs[ref].push(component.$vm); + $refs[ref].push(component.$vm || component); }); return $refs } @@ -603,14 +775,25 @@ function baiduPageDestroy ($vm) { } const hooks = [ - 'onShow', 'onHide', 'onError', - 'onPageNotFound' + 'onPageNotFound', + 'onUniNViewMessage' ]; -function createApp (vueOptions) { - vueOptions = vueOptions.default || vueOptions; +function initVm (vm) { + if (this.$vm) { // 百度竟然 onShow 在 onLaunch 之前? + return + } + + this.$vm = vm; + + this.$vm.$mp = { + app: this + }; +} + +function createApp (vm) { // 外部初始化时 Vue 还未初始化,放到 createApp 内部初始化 mixin Vue.mixin({ beforeCreate () { @@ -626,7 +809,9 @@ function createApp (vueOptions) { delete this.$options.mpInstance; if (this.mpType !== 'app') { - initRefs(this); + { // 头条的 selectComponent 竟然是异步的 + initRefs(this); + } initMocks(this); } }, @@ -638,49 +823,57 @@ function createApp (vueOptions) { const appOptions = { onLaunch (args) { - this.$vm = new Vue(Object.assign(vueOptions, { - mpType: 'app', - mpInstance: this - })); + initVm.call(this, vm); - this.$vm.$mount(); - setTimeout(() => this.$vm.__call_hook('onLaunch', args)); + 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); } }; - initHooks(appOptions, hooks, true); // 延迟执行,因为 App 的注册在 main.js 之前,可能导致生命周期内 Vue 原型上开发者注册的属性无法访问 + // 兼容旧版本 globalData + appOptions.globalData = vm.$options.globalData || {}; + + initHooks(appOptions, hooks); // 延迟执行,因为 App 的注册在 main.js 之前,可能导致生命周期内 Vue 原型上开发者注册的属性无法访问 App(appOptions); - return vueOptions + return vm } -function triggerLink (mpInstance) { - const baiduComponentInstances = mpInstance.pageinstance.$baiduComponentInstances; - - baiduComponentInstances[mpInstance.id] = mpInstance; - if (mpInstance.ownerId) { // 组件嵌组件 - const parentBaiduComponentInstance = baiduComponentInstances[mpInstance.ownerId]; - if (parentBaiduComponentInstance) { - handleLink.call(parentBaiduComponentInstance, { - detail: mpInstance - }); - } else { - console.error(`查找父组件失败${mpInstance.ownerId}`); - } - } else { // 页面直属组件 - handleLink.call(mpInstance.pageinstance, { - detail: mpInstance - }); - } +function initPage (pageOptions) { + initComponent(pageOptions); +} + +function initComponent (componentOptions) { + componentOptions.messages = { + '__l': handleLink + }; +} + +function triggerLink (mpInstance, vueOptions) { + mpInstance.dispatch('__l', mpInstance.$vm || vueOptions); } function handleLink (event) { - if (!event.detail.$parent) { - event.detail.$parent = this.$vm; - event.detail.$parent.$children.push(event.detail); + const target = event.value; + if (target.$mp) { + if (!target.$parent) { + target.$parent = this.$vm; + target.$parent.$children.push(target); - event.detail.$root = this.$vm.$root; + target.$root = this.$vm.$root; + } + } else { + if (!target.parent) { + target.parent = this.$vm; + } } } @@ -700,66 +893,105 @@ const hooks$1 = [ 'onNavigationBarSearchInputClicked' ]; +function initVm$1 (VueComponent) { // 百度的 onLoad 触发在 attached 之前 + if (this.$vm) { + return + } + + this.$vm = new VueComponent({ + mpType: 'page', + mpInstance: this + }); + + { + this.$vm.$baiduComponentInstances = Object.create(null); + } + + this.$vm.__call_hook('created'); + this.$vm.$mount(); +} + function createPage (vueOptions) { vueOptions = vueOptions.default || vueOptions; + let VueComponent; + if (isFn(vueOptions)) { + VueComponent = vueOptions; + vueOptions = VueComponent.extendOptions; + } else { + VueComponent = Vue.extend(vueOptions); + } const pageOptions = { - data: getData(vueOptions), - onLoad (args) { - { - this.$baiduComponentInstances = Object.create(null); - } - - this.$vm = new Vue(Object.assign(vueOptions, { - mpType: 'page', - mpInstance: this - })); - - this.$vm.__call_hook('created'); - this.$vm.__call_hook('onLoad', args); // 开发者一般可能会在 onLoad 时赋值,所以提前到 mount 之前 - this.$vm.$mount(); - }, - onReady () { - this.$vm._isMounted = true; - this.$vm.__call_hook('mounted'); - this.$vm.__call_hook('onReady'); + options: { + multipleSlots: true, + addGlobalClass: true }, - onUnload () { - this.$vm.__call_hook('onUnload'); - { // 百度组件不会在页面 unload 时触发 detached - baiduPageDestroy(this.$vm); + 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(); } }, - __e: handleEvent, - __l: handleLink + 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); + } + }, + __e: handleEvent, + __l: handleLink + } }; - initHooks(pageOptions, hooks$1); + initHooks(pageOptions.methods, hooks$1); - return Page(pageOptions) + initPage(pageOptions); + + return Component(pageOptions) } -function initVueComponent (mpInstace, VueComponent, extraOptions = {}) { - if (mpInstace.$vm) { +function initVm$2 (VueComponent) { + if (this.$vm) { return } - const options = Object.assign({ + const options = { mpType: 'component', - mpInstance: mpInstace, - propsData: mpInstace.properties - }, extraOptions); + mpInstance: this, + propsData: this.properties + }; // 初始化 vue 实例 - mpInstace.$vm = new VueComponent(options); + this.$vm = new VueComponent(options); // 处理$slots,$scopedSlots(暂不支持动态变化$slots) - const vueSlots = mpInstace.properties.vueSlots; + const vueSlots = this.properties.vueSlots; if (Array.isArray(vueSlots) && vueSlots.length) { const $slots = Object.create(null); vueSlots.forEach(slotName => { $slots[slotName] = true; }); - mpInstace.$vm.$scopedSlots = mpInstace.$vm.$slots = $slots; + this.$vm.$scopedSlots = this.$vm.$slots = $slots; } + // 性能优先,mount 提前到 attached 中,保证组件首次渲染数据被合并 + // 导致与标准 Vue 的差异,data 和 computed 中不能使用$parent,provide等组件属性 + this.$vm.$mount(); } function createComponent (vueOptions) { @@ -774,27 +1006,24 @@ function createComponent (vueOptions) { multipleSlots: true, addGlobalClass: true }, - data: getData(vueOptions), + data: getData(vueOptions, Vue.prototype), properties, lifetimes: { attached () { - initVueComponent(this, VueComponent); + initVm$2.call(this, VueComponent); }, ready () { - initVueComponent(this, VueComponent); // 目前发现部分情况小程序 attached 不触发 + initVm$2.call(this, VueComponent); // 目前发现部分情况小程序 attached 不触发 triggerLink(this); // 处理 parent,children - // 初始化渲染数据(需要等 parent,inject 都初始化完成,否则可以放到 attached 里边初始化渲染) + // 补充生命周期 this.$vm.__call_hook('created'); - this.$vm.$mount(); + this.$vm.__call_hook('beforeMount'); this.$vm._isMounted = true; this.$vm.__call_hook('mounted'); this.$vm.__call_hook('onReady'); }, detached () { - { - delete this.pageinstance.$baiduComponentInstances[this.id]; - } this.$vm.$destroy(); } }, @@ -815,6 +1044,8 @@ function createComponent (vueOptions) { } }; + initComponent(componentOptions); + return Component(componentOptions) } @@ -828,12 +1059,14 @@ if (typeof Proxy !== 'undefined') { } if (api[name]) { return promisify(name, api[name]) - } - if (extraApi[name]) { - return promisify(name, extraApi[name]) - } - if (todoApis[name]) { - return promisify(name, todoApis[name]) + } + { + if (extraApi[name]) { + return promisify(name, extraApi[name]) + } + if (todoApis[name]) { + return promisify(name, todoApis[name]) + } } if (!hasOwn(swan, name) && !hasOwn(protocols, name)) { return @@ -844,13 +1077,14 @@ if (typeof Proxy !== 'undefined') { } else { uni.upx2px = upx2px; - Object.keys(todoApis).forEach(name => { - uni[name] = promisify(name, todoApis[name]); - }); - - Object.keys(extraApi).forEach(name => { - uni[name] = promisify(name, todoApis[name]); - }); + { + Object.keys(todoApis).forEach(name => { + uni[name] = promisify(name, todoApis[name]); + }); + Object.keys(extraApi).forEach(name => { + uni[name] = promisify(name, todoApis[name]); + }); + } Object.keys(api).forEach(name => { uni[name] = promisify(name, api[name]); diff --git a/packages/uni-mp-baidu/package.json b/packages/uni-mp-baidu/package.json index 9f0c4f9add90378ddf36dfa71677e8f2be178aff..a06a2a6e6873b935c7b2e822326dfcd9fad944fb 100644 --- a/packages/uni-mp-baidu/package.json +++ b/packages/uni-mp-baidu/package.json @@ -1,6 +1,6 @@ { "name": "@dcloudio/uni-mp-baidu", - "version": "0.0.812", + "version": "0.0.813", "description": "uni-app mp-baidu", "main": "dist/index.js", "scripts": { diff --git a/packages/uni-mp-toutiao/dist/index.js b/packages/uni-mp-toutiao/dist/index.js index de5682c7a1c093128c8477d6ee1c18c57714e3a1..59006669eb61530b50918540b8bebe5937da8693 100644 --- a/packages/uni-mp-toutiao/dist/index.js +++ b/packages/uni-mp-toutiao/dist/index.js @@ -19,9 +19,28 @@ function hasOwn (obj, key) { return hasOwnProperty.call(obj, key) } -function noop () {} +function noop () {} + +/** + * Create a cached version of a pure function. + */ +function cached (fn) { + const cache = Object.create(null); + return function cachedFn (str) { + const hit = cache[str]; + return hit || (cache[str] = fn(str)) + } +} + +/** + * Camelize a hyphen-delimited string. + */ +const camelizeRE = /-(\w)/g; +const camelize = cached((str) => { + return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '') +}); -const SYNC_API_RE = /hideKeyboard|upx2px|canIUse|^create|Sync$|Manager$/; +const SYNC_API_RE = /requireNativePlugin|upx2px|hideKeyboard|canIUse|^create|Sync$|Manager$/; const CONTEXT_API_RE = /^create|Manager$/; @@ -46,10 +65,11 @@ function handlePromise (promise) { } function shouldPromise (name) { - if (isSyncApi(name)) { - return false - } - if (isCallbackApi(name)) { + if ( + isContextApi(name) || + isSyncApi(name) || + isCallbackApi(name) + ) { return false } return true @@ -426,7 +446,50 @@ var api = /*#__PURE__*/Object.freeze({ }); -const MOCKS = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__']; +const MPPage = Page; +const MPComponent = Component; + +const customizeRE = /:/g; + +const customize = cached((str) => { + return camelize(str.replace(customizeRE, '-')) +}); + +function initTriggerEvent (mpInstance) { + const oldTriggerEvent = mpInstance.triggerEvent; + mpInstance.triggerEvent = function (event, ...args) { + return oldTriggerEvent.apply(mpInstance, [customize(event), ...args]) + }; +} + +Page = function (options = {}) { + const name = 'onLoad'; + const oldHook = options[name]; + if (!oldHook) { + options[name] = function () { + initTriggerEvent(this); + }; + } else { + options[name] = function (...args) { + initTriggerEvent(this); + return oldHook.apply(this, args) + }; + } + return MPPage(options) +}; + +const behavior = Behavior({ + created () { + initTriggerEvent(this); + } +}); + +Component = function (options = {}) { + (options.behaviors || (options.behaviors = [])).unshift(behavior); + return MPComponent(options) +}; + +const MOCKS = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__', '__webviewId__']; function initMocks (vm) { const mpInstance = vm.$mp[vm.mpType]; @@ -437,25 +500,21 @@ function initMocks (vm) { }); } -function initHooks (mpOptions, hooks, delay = false) { +function initHooks (mpOptions, hooks) { hooks.forEach(hook => { mpOptions[hook] = function (args) { - if (delay) { - setTimeout(() => this.$vm.__call_hook(hook, args)); - } else { - this.$vm.__call_hook(hook, args); - } + return this.$vm.__call_hook(hook, args) }; }); } -function getData (vueOptions) { +function getData (vueOptions, context) { let data = vueOptions.data || {}; const methods = vueOptions.methods || {}; if (typeof data === 'function') { try { - data = data(); + data = data.call(context); // 支持 Vue.prototype 上挂的数据 } catch (e) { if (process.env.VUE_APP_DEBUG) { console.warn('根据 Vue 的 data 函数初始化小程序 data 失败,请尽量确保 data 函数中不访问 vm 对象,否则可能影响首次数据渲染速度。', data); @@ -469,7 +528,7 @@ function getData (vueOptions) { } Object.keys(methods).forEach(methodName => { - if (!hasOwn(data, methodName)) { + if (context.__lifecycle_hooks__.indexOf(methodName) === -1 && !hasOwn(data, methodName)) { data[methodName] = methods[methodName]; } }); @@ -539,24 +598,123 @@ function wrapper$1 (event) { event.preventDefault = noop; event.target = event.target || {}; - event.detail = event.detail || {}; + + if (!hasOwn(event, 'detail')) { + event.detail = {}; + } // TODO 又得兼容 mpvue 的 mp 对象 event.mp = event; - event.target = Object.assign({}, event.target, event.detail); + + if (isPlainObject(event.detail)) { + event.target = Object.assign({}, event.target, event.detail); + } + return event } -function processEventArgs (event, args = [], isCustom) { - if (isCustom && !args.length) { // 无参数,直接传入 detail 数组 - return event.detail +function getExtraValue (vm, dataPathsArray) { + let context = vm; + dataPathsArray.forEach(dataPathArray => { + const dataPath = dataPathArray[0]; + const value = dataPathArray[2]; + if (dataPath || typeof value !== 'undefined') { // ['','',index,'disable'] + const propPath = dataPathArray[1]; + const valuePath = dataPathArray[3]; + + const vFor = dataPath ? vm.__get_value(dataPath, context) : context; + + if (Number.isInteger(vFor)) { + context = value; + } else if (!propPath) { + context = vFor[value]; + } else { + if (Array.isArray(vFor)) { + context = vFor.find(vForItem => { + return vm.__get_value(propPath, vForItem) === value + }); + } else if (isPlainObject(vFor)) { + context = Object.keys(vFor).find(vForKey => { + return vm.__get_value(propPath, vFor[vForKey]) === value + }); + } else { + console.error('v-for 暂不支持循环数据:', vFor); + } + } + + if (valuePath) { + context = vm.__get_value(valuePath, context); + } + } + }); + return context +} + +function processEventExtra (vm, extra) { + const extraObj = {}; + + if (Array.isArray(extra) && extra.length) { + /** + *[ + * ['data.items', 'data.id', item.data.id], + * ['metas', 'id', meta.id] + *], + *[ + * ['data.items', 'data.id', item.data.id], + * ['metas', 'id', meta.id] + *], + *'test' + */ + extra.forEach((dataPath, index) => { + if (typeof dataPath === 'string') { + if (!dataPath) { // model,prop.sync + extraObj['$' + index] = vm; + } else { + extraObj['$' + index] = vm.__get_value(dataPath); + } + } else { + extraObj['$' + index] = getExtraValue(vm, dataPath); + } + }); } + + return extraObj +} + +function processEventArgs (vm, event, args = [], extra = [], isCustom, methodName) { + let isCustomMPEvent = false; // wxcomponent 组件,传递原始 event 对象 + if (isCustom) { // 自定义事件 + isCustomMPEvent = event.currentTarget && + event.currentTarget.dataset && + event.currentTarget.dataset.comType === 'wx'; + if (!args.length) { // 无参数,直接传入 event 或 detail 数组 + if (isCustomMPEvent) { + return [event] + } + return event.detail.__args__ || event.detail + } + } + + const extraObj = processEventExtra(vm, extra); + const ret = []; args.forEach(arg => { if (arg === '$event') { - ret.push(isCustom ? event.detail[0] : event); + if (methodName === '__set_model' && !isCustom) { // input v-model value + ret.push(event.target.value); + } else { + if (isCustom && !isCustomMPEvent) { + ret.push(event.detail.__args__[0]); + } else { // wxcomponent 组件或内置组件 + ret.push(event); + } + } } else { - ret.push(arg); + if (typeof arg === 'string' && hasOwn(extraObj, arg)) { + ret.push(extraObj[arg]); + } else { + ret.push(arg); + } } }); @@ -588,54 +746,52 @@ function handleEvent (event) { if (eventsArray && eventType === type) { eventsArray.forEach(eventArray => { - const handler = this.$vm[eventArray[0]]; - if (!isFn(handler)) { - throw new Error(` _vm.${eventArray[0]} is not a function`) - } - if (isOnce) { - if (handler.once) { - return + const methodName = eventArray[0]; + if (methodName) { + const handler = this.$vm[methodName]; + if (!isFn(handler)) { + throw new Error(` _vm.${methodName} is not a function`) } - handler.once = true; - } - handler.apply(this.$vm, processEventArgs(event, eventArray[1], isCustom)); - }); - } - }); -} - -function initRefs (vm) { - const mpInstance = vm.$mp[vm.mpType]; - Object.defineProperty(vm, '$refs', { - get () { - const $refs = Object.create(null); - const components = mpInstance.selectAllComponents('.vue-ref'); - components.forEach(component => { - const ref = component.dataset.ref; - $refs[ref] = component.$vm; - }); - const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for'); - forComponents.forEach(component => { - const ref = component.dataset.ref; - if (!$refs[ref]) { - $refs[ref] = []; + if (isOnce) { + if (handler.once) { + return + } + handler.once = true; + } + handler.apply(this.$vm, processEventArgs( + this.$vm, + event, + eventArray[1], + eventArray[2], + isCustom, + methodName + )); } - $refs[ref].push(component.$vm); }); - return $refs } }); } const hooks = [ - 'onShow', 'onHide', 'onError', - 'onPageNotFound' + 'onPageNotFound', + 'onUniNViewMessage' ]; -function createApp (vueOptions) { - vueOptions = vueOptions.default || vueOptions; +function initVm (vm) { + if (this.$vm) { // 百度竟然 onShow 在 onLaunch 之前? + return + } + + this.$vm = vm; + + this.$vm.$mp = { + app: this + }; +} + +function createApp (vm) { // 外部初始化时 Vue 还未初始化,放到 createApp 内部初始化 mixin Vue.mixin({ beforeCreate () { @@ -651,7 +807,6 @@ function createApp (vueOptions) { delete this.$options.mpInstance; if (this.mpType !== 'app') { - initRefs(this); initMocks(this); } }, @@ -663,24 +818,69 @@ function createApp (vueOptions) { const appOptions = { onLaunch (args) { - this.$vm = new Vue(Object.assign(vueOptions, { - mpType: 'app', - mpInstance: this - })); + initVm.call(this, vm); - this.$vm.$mount(); - setTimeout(() => this.$vm.__call_hook('onLaunch', args)); + 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); } }; - initHooks(appOptions, hooks, true); // 延迟执行,因为 App 的注册在 main.js 之前,可能导致生命周期内 Vue 原型上开发者注册的属性无法访问 + // 兼容旧版本 globalData + appOptions.globalData = vm.$options.globalData || {}; + + initHooks(appOptions, hooks); // 延迟执行,因为 App 的注册在 main.js 之前,可能导致生命周期内 Vue 原型上开发者注册的属性无法访问 App(appOptions); - return vueOptions + return vm } const instances = Object.create(null); + +function initPage (pageOptions) { + initComponent(pageOptions); +} + +function initComponent (componentOptions) { + if (componentOptions.properties) { // ref + componentOptions.properties.vueRef = { + type: String, + value: '' + }; + } + const oldAttached = componentOptions.lifetimes.attached; + componentOptions.lifetimes.attached = function () { + oldAttached.call(this); + // TODO 需要处理动态变化后的 refs + initRefs$1.call(this); + }; +} + +function initRefs$1 () { + this.selectAllComponents('.vue-ref', (components) => { + components.forEach(component => { + const ref = component.data.vueRef; // 头条的组件 dataset 竟然是空的 + this.$vm.$refs[ref] = component.$vm || component; + }); + }); + this.selectAllComponents('.vue-ref-in-for', (forComponents) => { + forComponents.forEach(component => { + const ref = component.data.vueRef; + if (!this.$vm.$refs[ref]) { + this.$vm.$refs[ref] = []; + } + this.$vm.$refs[ref].push(component.$vm || component); + }); + }); +} + function triggerLink (mpInstance) { const nodeId = mpInstance.__nodeid__ + ''; const webviewId = mpInstance.__webviewId__ + ''; @@ -727,63 +927,95 @@ const hooks$1 = [ 'onNavigationBarSearchInputClicked' ]; +function initVm$1 (VueComponent) { // 百度的 onLoad 触发在 attached 之前 + if (this.$vm) { + return + } + + this.$vm = new VueComponent({ + mpType: 'page', + mpInstance: this + }); + + this.$vm.__call_hook('created'); + this.$vm.$mount(); +} + function createPage (vueOptions) { vueOptions = vueOptions.default || vueOptions; + let VueComponent; + if (isFn(vueOptions)) { + VueComponent = vueOptions; + vueOptions = VueComponent.extendOptions; + } else { + VueComponent = Vue.extend(vueOptions); + } const pageOptions = { - data: getData(vueOptions), - onLoad (args) { - - this.$vm = new Vue(Object.assign(vueOptions, { - mpType: 'page', - mpInstance: this - })); - - this.$vm.__call_hook('created'); - this.$vm.__call_hook('onLoad', args); // 开发者一般可能会在 onLoad 时赋值,所以提前到 mount 之前 - this.$vm.$mount(); - }, - onReady () { - this.$vm._isMounted = true; - this.$vm.__call_hook('mounted'); - this.$vm.__call_hook('onReady'); + options: { + multipleSlots: true, + addGlobalClass: true }, - onUnload () { - this.$vm.__call_hook('onUnload'); - { + 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(); } }, - __e: handleEvent, - __l: handleLink + 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 + } }; - initHooks(pageOptions, hooks$1); + initHooks(pageOptions.methods, hooks$1); + + initPage(pageOptions); - return Page(pageOptions) + return Component(pageOptions) } -function initVueComponent (mpInstace, VueComponent, extraOptions = {}) { - if (mpInstace.$vm) { +function initVm$2 (VueComponent) { + if (this.$vm) { return } - const options = Object.assign({ + const options = { mpType: 'component', - mpInstance: mpInstace, - propsData: mpInstace.properties - }, extraOptions); + mpInstance: this, + propsData: this.properties + }; // 初始化 vue 实例 - mpInstace.$vm = new VueComponent(options); + this.$vm = new VueComponent(options); // 处理$slots,$scopedSlots(暂不支持动态变化$slots) - const vueSlots = mpInstace.properties.vueSlots; + const vueSlots = this.properties.vueSlots; if (Array.isArray(vueSlots) && vueSlots.length) { const $slots = Object.create(null); vueSlots.forEach(slotName => { $slots[slotName] = true; }); - mpInstace.$vm.$scopedSlots = mpInstace.$vm.$slots = $slots; + this.$vm.$scopedSlots = this.$vm.$slots = $slots; } + // 性能优先,mount 提前到 attached 中,保证组件首次渲染数据被合并 + // 导致与标准 Vue 的差异,data 和 computed 中不能使用$parent,provide等组件属性 + this.$vm.$mount(); } function createComponent (vueOptions) { @@ -798,19 +1030,19 @@ function createComponent (vueOptions) { multipleSlots: true, addGlobalClass: true }, - data: getData(vueOptions), + data: getData(vueOptions, Vue.prototype), properties, lifetimes: { attached () { - initVueComponent(this, VueComponent); + initVm$2.call(this, VueComponent); }, ready () { - initVueComponent(this, VueComponent); // 目前发现部分情况小程序 attached 不触发 + initVm$2.call(this, VueComponent); // 目前发现部分情况小程序 attached 不触发 triggerLink(this); // 处理 parent,children - // 初始化渲染数据(需要等 parent,inject 都初始化完成,否则可以放到 attached 里边初始化渲染) + // 补充生命周期 this.$vm.__call_hook('created'); - this.$vm.$mount(); + this.$vm.__call_hook('beforeMount'); this.$vm._isMounted = true; this.$vm.__call_hook('mounted'); this.$vm.__call_hook('onReady'); @@ -836,6 +1068,8 @@ function createComponent (vueOptions) { } }; + initComponent(componentOptions); + return Component(componentOptions) } @@ -849,12 +1083,14 @@ if (typeof Proxy !== 'undefined') { } if (api[name]) { return promisify(name, api[name]) - } - if (extraApi[name]) { - return promisify(name, extraApi[name]) - } - if (todoApis[name]) { - return promisify(name, todoApis[name]) + } + { + if (extraApi[name]) { + return promisify(name, extraApi[name]) + } + if (todoApis[name]) { + return promisify(name, todoApis[name]) + } } if (!hasOwn(tt, name) && !hasOwn(protocols, name)) { return @@ -865,13 +1101,14 @@ if (typeof Proxy !== 'undefined') { } else { uni.upx2px = upx2px; - Object.keys(todoApis).forEach(name => { - uni[name] = promisify(name, todoApis[name]); - }); - - Object.keys(extraApi).forEach(name => { - uni[name] = promisify(name, todoApis[name]); - }); + { + Object.keys(todoApis).forEach(name => { + uni[name] = promisify(name, todoApis[name]); + }); + Object.keys(extraApi).forEach(name => { + uni[name] = promisify(name, todoApis[name]); + }); + } Object.keys(api).forEach(name => { uni[name] = promisify(name, api[name]); diff --git a/packages/uni-mp-toutiao/package.json b/packages/uni-mp-toutiao/package.json index 144216c17316e8b69512230d617111386b1334d3..6f595d5667ddb606ee1ee8b7a20bbaf7d3037582 100644 --- a/packages/uni-mp-toutiao/package.json +++ b/packages/uni-mp-toutiao/package.json @@ -1,6 +1,6 @@ { "name": "@dcloudio/uni-mp-toutiao", - "version": "0.0.312", + "version": "0.0.313", "description": "uni-app mp-toutiao", "main": "dist/index.js", "scripts": { diff --git a/packages/uni-mp-weixin/dist/index.js b/packages/uni-mp-weixin/dist/index.js index 5357910ddbe4e3cf190f0aec8a320058b4e7d1a4..8f2d3264c6b2f49f809ae35f05ba2313e8ddef4e 100644 --- a/packages/uni-mp-weixin/dist/index.js +++ b/packages/uni-mp-weixin/dist/index.js @@ -44,8 +44,6 @@ const SYNC_API_RE = /requireNativePlugin|upx2px|hideKeyboard|canIUse|^create|Syn const CONTEXT_API_RE = /^create|Manager$/; -const TASK_APIS = ['request', 'downloadFile', 'uploadFile', 'connectSocket']; - const CALLBACK_API_RE = /^on/; function isContextApi (name) { @@ -59,10 +57,6 @@ function isCallbackApi (name) { return CALLBACK_API_RE.test(name) } -function isTaskApi (name) { - return TASK_APIS.indexOf(name) !== -1 -} - function handlePromise (promise) { return promise.then(data => { return [null, data] @@ -74,8 +68,7 @@ function shouldPromise (name) { if ( isContextApi(name) || isSyncApi(name) || - isCallbackApi(name) || - isTaskApi(name) + isCallbackApi(name) ) { return false } @@ -295,8 +288,8 @@ var api = /*#__PURE__*/Object.freeze({ }); -const WXPage = Page; -const WXComponent = Component; +const MPPage = Page; +const MPComponent = Component; const customizeRE = /:/g; @@ -305,12 +298,10 @@ const customize = cached((str) => { }); function initTriggerEvent (mpInstance) { - if (wx.canIUse('nextTick')) { // 微信旧版本基础库不支持重写triggerEvent - const oldTriggerEvent = mpInstance.triggerEvent; - mpInstance.triggerEvent = function (event, ...args) { - return oldTriggerEvent.apply(mpInstance, [customize(event), ...args]) - }; - } + const oldTriggerEvent = mpInstance.triggerEvent; + mpInstance.triggerEvent = function (event, ...args) { + return oldTriggerEvent.apply(mpInstance, [customize(event), ...args]) + }; } Page = function (options = {}) { @@ -326,7 +317,7 @@ Page = function (options = {}) { return oldHook.apply(this, args) }; } - return WXPage(options) + return MPPage(options) }; const behavior = Behavior({ @@ -337,10 +328,10 @@ const behavior = Behavior({ Component = function (options = {}) { (options.behaviors || (options.behaviors = [])).unshift(behavior); - return WXComponent(options) + return MPComponent(options) }; -const MOCKS = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__']; +const MOCKS = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__', '__webviewId__']; function initMocks (vm) { const mpInstance = vm.$mp[vm.mpType]; @@ -542,7 +533,7 @@ function processEventArgs (vm, event, args = [], extra = [], isCustom, methodNam if (isCustomMPEvent) { return [event] } - return event.detail + return event.detail.__args__ || event.detail } } @@ -555,7 +546,7 @@ function processEventArgs (vm, event, args = [], extra = [], isCustom, methodNam ret.push(event.target.value); } else { if (isCustom && !isCustomMPEvent) { - ret.push(event.detail[0]); + ret.push(event.detail.__args__[0]); } else { // wxcomponent 组件或内置组件 ret.push(event); } @@ -627,7 +618,7 @@ function initRefs (vm) { const mpInstance = vm.$mp[vm.mpType]; Object.defineProperty(vm, '$refs', { get () { - const $refs = Object.create(null); + const $refs = {}; const components = mpInstance.selectAllComponents('.vue-ref'); components.forEach(component => { const ref = component.dataset.ref; @@ -647,13 +638,29 @@ function initRefs (vm) { } const hooks = [ - 'onShow', 'onHide', 'onError', 'onPageNotFound', 'onUniNViewMessage' ]; +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) { // 外部初始化时 Vue 还未初始化,放到 createApp 内部初始化 mixin Vue.mixin({ @@ -670,7 +677,9 @@ function createApp (vm) { delete this.$options.mpInstance; if (this.mpType !== 'app') { - initRefs(this); + { // 头条的 selectComponent 竟然是异步的 + initRefs(this); + } initMocks(this); } }, @@ -682,22 +691,17 @@ function createApp (vm) { const appOptions = { onLaunch (args) { - { - if (!wx.canIUse('nextTick')) { // 事实 上2.2.3 即可,简单使用 2.3.0 的 nextTick 判断 - console.error('当前微信基础库版本过低,请将 微信开发者工具-详情-项目设置-调试基础库版本 更换为`2.3.0`以上'); - } - } - - this.$vm = vm; - - this.$vm.$mp = { - app: this - }; + 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); } }; @@ -749,6 +753,20 @@ const hooks$1 = [ 'onNavigationBarSearchInputClicked' ]; +function initVm$1 (VueComponent) { // 百度的 onLoad 触发在 attached 之前 + if (this.$vm) { + return + } + + this.$vm = new VueComponent({ + mpType: 'page', + mpInstance: this + }); + + this.$vm.__call_hook('created'); + this.$vm.$mount(); +} + function createPage (vueOptions) { vueOptions = vueOptions.default || vueOptions; let VueComponent; @@ -766,14 +784,7 @@ function createPage (vueOptions) { data: getData(vueOptions, Vue.prototype), lifetimes: { // 当页面作为组件时 attached () { - - this.$vm = new VueComponent({ - mpType: 'page', - mpInstance: this - }); - - this.$vm.__call_hook('created'); - this.$vm.$mount(); + initVm$1.call(this, VueComponent); }, ready () { this.$vm.__call_hook('beforeMount'); @@ -787,6 +798,7 @@ function createPage (vueOptions) { }, methods: { // 作为页面时 onLoad (args) { + initVm$1.call(this, VueComponent); this.$vm.$mp.query = args; // 又要兼容 mpvue this.$vm.__call_hook('onLoad', args); // 开发者可能会在 onLoad 时赋值,提前到 mount 之前 }, @@ -803,31 +815,31 @@ function createPage (vueOptions) { return Component(pageOptions) } -function initVueComponent (mpInstace, VueComponent, extraOptions = {}) { - if (mpInstace.$vm) { +function initVm$2 (VueComponent) { + if (this.$vm) { return } - const options = Object.assign({ + const options = { mpType: 'component', - mpInstance: mpInstace, - propsData: mpInstace.properties - }, extraOptions); + mpInstance: this, + propsData: this.properties + }; // 初始化 vue 实例 - mpInstace.$vm = new VueComponent(options); + this.$vm = new VueComponent(options); // 处理$slots,$scopedSlots(暂不支持动态变化$slots) - const vueSlots = mpInstace.properties.vueSlots; + const vueSlots = this.properties.vueSlots; if (Array.isArray(vueSlots) && vueSlots.length) { const $slots = Object.create(null); vueSlots.forEach(slotName => { $slots[slotName] = true; }); - mpInstace.$vm.$scopedSlots = mpInstace.$vm.$slots = $slots; + this.$vm.$scopedSlots = this.$vm.$slots = $slots; } // 性能优先,mount 提前到 attached 中,保证组件首次渲染数据被合并 // 导致与标准 Vue 的差异,data 和 computed 中不能使用$parent,provide等组件属性 - mpInstace.$vm.$mount(); + this.$vm.$mount(); } function createComponent (vueOptions) { @@ -846,10 +858,10 @@ function createComponent (vueOptions) { properties, lifetimes: { attached () { - initVueComponent(this, VueComponent); + initVm$2.call(this, VueComponent); }, ready () { - initVueComponent(this, VueComponent); // 目前发现部分情况小程序 attached 不触发 + initVm$2.call(this, VueComponent); // 目前发现部分情况小程序 attached 不触发 triggerLink(this); // 处理 parent,children // 补充生命周期 diff --git a/packages/uni-mp-weixin/package.json b/packages/uni-mp-weixin/package.json index aa7f016a1e5007c23ae86dabb316c828545f7bbf..05fc48147dd60eeddb77274eccd746c5fbc4d9f1 100644 --- a/packages/uni-mp-weixin/package.json +++ b/packages/uni-mp-weixin/package.json @@ -1,6 +1,6 @@ { "name": "@dcloudio/uni-mp-weixin", - "version": "0.0.935", + "version": "0.0.936", "description": "uni-app mp-weixin", "main": "dist/index.js", "scripts": { diff --git a/src/core/helpers/promise.js b/src/core/helpers/promise.js index 05fecf823c62b461cc645f9efece49029524e22d..613d30f8cfef03a1c8e704186485fd0702f2a826 100644 --- a/src/core/helpers/promise.js +++ b/src/core/helpers/promise.js @@ -36,8 +36,7 @@ export function shouldPromise (name) { if ( isContextApi(name) || isSyncApi(name) || - isCallbackApi(name) || - isTaskApi(name) + isCallbackApi(name) ) { return false } diff --git a/src/core/runtime/wrapper/create-app.js b/src/core/runtime/wrapper/create-app.js index e904370d457801126f0850a476b7af4a975fdcb4..f95b39ac12337033d35ed67dc021a2090c86b919 100644 --- a/src/core/runtime/wrapper/create-app.js +++ b/src/core/runtime/wrapper/create-app.js @@ -9,13 +9,29 @@ import { } from './util' const hooks = [ - 'onShow', '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 + } +} + export function createApp (vm) { // 外部初始化时 Vue 还未初始化,放到 createApp 内部初始化 mixin Vue.mixin({ @@ -32,7 +48,9 @@ export function createApp (vm) { delete this.$options.mpInstance if (this.mpType !== 'app') { - initRefs(this) + if (__PLATFORM__ !== 'mp-toutiao') { // 头条的 selectComponent 竟然是异步的 + initRefs(this) + } initMocks(this) } }, @@ -44,22 +62,17 @@ export function createApp (vm) { 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 - } + 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) } } diff --git a/src/core/runtime/wrapper/create-component.js b/src/core/runtime/wrapper/create-component.js index 43cc4c83d810c784a14060532a003ebc6d93f775..e7ff868e6a4b709d1fac0875733ab699f72ace07 100644 --- a/src/core/runtime/wrapper/create-component.js +++ b/src/core/runtime/wrapper/create-component.js @@ -2,7 +2,8 @@ import Vue from 'vue' import { handleLink, - triggerLink + triggerLink, + initComponent } from 'uni-platform/runtime/wrapper/index' import { @@ -11,31 +12,31 @@ import { getProperties } from './util' -function initVueComponent (mpInstace, VueComponent, extraOptions = {}) { - if (mpInstace.$vm) { +function initVm (VueComponent) { + if (this.$vm) { return } - const options = Object.assign({ + const options = { mpType: 'component', - mpInstance: mpInstace, - propsData: mpInstace.properties - }, extraOptions) + mpInstance: this, + propsData: this.properties + } // 初始化 vue 实例 - mpInstace.$vm = new VueComponent(options) + this.$vm = new VueComponent(options) // 处理$slots,$scopedSlots(暂不支持动态变化$slots) - const vueSlots = mpInstace.properties.vueSlots + const vueSlots = this.properties.vueSlots if (Array.isArray(vueSlots) && vueSlots.length) { const $slots = Object.create(null) vueSlots.forEach(slotName => { $slots[slotName] = true }) - mpInstace.$vm.$scopedSlots = mpInstace.$vm.$slots = $slots + this.$vm.$scopedSlots = this.$vm.$slots = $slots } // 性能优先,mount 提前到 attached 中,保证组件首次渲染数据被合并 // 导致与标准 Vue 的差异,data 和 computed 中不能使用$parent,provide等组件属性 - mpInstace.$vm.$mount() + this.$vm.$mount() } export function createComponent (vueOptions) { @@ -54,10 +55,10 @@ export function createComponent (vueOptions) { properties, lifetimes: { attached () { - initVueComponent(this, VueComponent) + initVm.call(this, VueComponent) }, ready () { - initVueComponent(this, VueComponent) // 目前发现部分情况小程序 attached 不触发 + initVm.call(this, VueComponent) // 目前发现部分情况小程序 attached 不触发 triggerLink(this) // 处理 parent,children // 补充生命周期 @@ -68,9 +69,6 @@ export function createComponent (vueOptions) { this.$vm.__call_hook('onReady') }, detached () { - if (__PLATFORM__ === 'mp-baidu') { - delete this.pageinstance.$baiduComponentInstances[this.id] - } this.$vm.$destroy() } }, @@ -91,5 +89,7 @@ export function createComponent (vueOptions) { } } + initComponent(componentOptions) + return Component(componentOptions) } diff --git a/src/core/runtime/wrapper/create-page.js b/src/core/runtime/wrapper/create-page.js index 9be96ceb5a69acc00250776304e34a03e9a4429c..a97c86cb5a65a71391fddd37b02c0e9a5d5f9fce 100644 --- a/src/core/runtime/wrapper/create-page.js +++ b/src/core/runtime/wrapper/create-page.js @@ -4,7 +4,8 @@ import { isFn } from 'uni-shared' -import { +import { + initPage, handleLink } from 'uni-platform/runtime/wrapper/index' @@ -31,6 +32,24 @@ const hooks = [ '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 @@ -48,17 +67,7 @@ export function createPage (vueOptions) { data: getData(vueOptions, Vue.prototype), lifetimes: { // 当页面作为组件时 attached () { - if (__PLATFORM__ === 'mp-baidu') { - this.$baiduComponentInstances = Object.create(null) - } - - this.$vm = new VueComponent({ - mpType: 'page', - mpInstance: this - }) - - this.$vm.__call_hook('created') - this.$vm.$mount() + initVm.call(this, VueComponent) }, ready () { this.$vm.__call_hook('beforeMount') @@ -72,6 +81,10 @@ export function createPage (vueOptions) { }, 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 之前 }, @@ -87,12 +100,8 @@ export function createPage (vueOptions) { } initHooks(pageOptions.methods, hooks) - - if (__PLATFORM__ === 'app-plus') { - pageOptions.methods.$getAppWebview = function () { - return plus.webview.getWebviewById(`${this.__wxWebviewId__}`) - } - } + + initPage(pageOptions) return Component(pageOptions) } diff --git a/src/core/runtime/wrapper/util.js b/src/core/runtime/wrapper/util.js index e30b196a3758d947a1b425c651aa64df26f1e248..8034a59224e10326c23d2c11a0c566b908a6bc7b 100644 --- a/src/core/runtime/wrapper/util.js +++ b/src/core/runtime/wrapper/util.js @@ -5,7 +5,7 @@ import { isPlainObject } from 'uni-shared' -const MOCKS = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__'] +const MOCKS = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__', '__webviewId__'] export function initMocks (vm) { const mpInstance = vm.$mp[vm.mpType] @@ -217,7 +217,7 @@ function processEventArgs (vm, event, args = [], extra = [], isCustom, methodNam if (isCustomMPEvent) { return [event] } - return event.detail + return event.detail.__args__ || event.detail } } @@ -230,7 +230,7 @@ function processEventArgs (vm, event, args = [], extra = [], isCustom, methodNam ret.push(event.target.value) } else { if (isCustom && !isCustomMPEvent) { - ret.push(event.detail[0]) + ret.push(event.detail.__args__[0]) } else { // wxcomponent 组件或内置组件 ret.push(event) } @@ -302,7 +302,7 @@ export function initRefs (vm) { const mpInstance = vm.$mp[vm.mpType] Object.defineProperty(vm, '$refs', { get () { - const $refs = Object.create(null) + const $refs = {} const components = mpInstance.selectAllComponents('.vue-ref') components.forEach(component => { const ref = component.dataset.ref diff --git a/src/platforms/app-plus/runtime/wrapper/index.js b/src/platforms/app-plus/runtime/wrapper/index.js index bace4b0d1462dd1f9f5d5a837b3e32016ebe56a3..d5dceb800d3c45a9e56e321a4761d98557b19c33 100644 --- a/src/platforms/app-plus/runtime/wrapper/index.js +++ b/src/platforms/app-plus/runtime/wrapper/index.js @@ -1,21 +1,15 @@ -export function triggerLink (mpInstance, vueOptions) { - mpInstance.triggerEvent('__l', mpInstance.$vm || vueOptions, { - bubbles: true, - composed: true - }) +export { + handleLink, + triggerLink } + from '../../../mp-weixin/runtime/wrapper/index' -export function handleLink (event) { - if (event.detail.$mp) { // vm - if (!event.detail.$parent) { - event.detail.$parent = this.$vm - event.detail.$parent.$children.push(event.detail) +export function initPage (pageOptions) { + initComponent(pageOptions) +} - event.detail.$root = this.$vm.$root - } - } else { // vueOptions - if (!event.detail.parent) { - event.detail.parent = this.$vm - } +export function initComponent (componentOptions) { + componentOptions.methods.$getAppWebview = function () { + return plus.webview.getWebviewById(`${this.__wxWebviewId__}`) } } diff --git a/src/platforms/mp-baidu/runtime/index.js b/src/platforms/mp-baidu/runtime/index.js index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5a843bc821374df4c1959470c611958c8b82d732 100644 --- a/src/platforms/mp-baidu/runtime/index.js +++ b/src/platforms/mp-baidu/runtime/index.js @@ -0,0 +1 @@ +import '../../mp-weixin/runtime/index' diff --git a/src/platforms/mp-baidu/runtime/wrapper/index.js b/src/platforms/mp-baidu/runtime/wrapper/index.js index af66a9dca53c9f17bea905d13ec56ac8c162872c..1f664c4f1129c1278edf49849ace8d70c262b3d0 100644 --- a/src/platforms/mp-baidu/runtime/wrapper/index.js +++ b/src/platforms/mp-baidu/runtime/wrapper/index.js @@ -1,28 +1,29 @@ -export function triggerLink (mpInstance) { - const baiduComponentInstances = mpInstance.pageinstance.$baiduComponentInstances +export function initPage (pageOptions) { + initComponent(pageOptions) +} - baiduComponentInstances[mpInstance.id] = mpInstance - if (mpInstance.ownerId) { // 组件嵌组件 - const parentBaiduComponentInstance = baiduComponentInstances[mpInstance.ownerId] - if (parentBaiduComponentInstance) { - handleLink.call(parentBaiduComponentInstance, { - detail: mpInstance - }) - } else { - console.error(`查找父组件失败${mpInstance.ownerId}`) - } - } else { // 页面直属组件 - handleLink.call(mpInstance.pageinstance, { - detail: mpInstance - }) +export function initComponent (componentOptions) { + componentOptions.messages = { + '__l': handleLink } } +export function triggerLink (mpInstance, vueOptions) { + mpInstance.dispatch('__l', mpInstance.$vm || vueOptions) +} + export function handleLink (event) { - if (!event.detail.$parent) { - event.detail.$parent = this.$vm - event.detail.$parent.$children.push(event.detail) + const target = event.value + if (target.$mp) { + if (!target.$parent) { + target.$parent = this.$vm + target.$parent.$children.push(target) - event.detail.$root = this.$vm.$root + target.$root = this.$vm.$root + } + } else { + if (!target.parent) { + target.parent = this.$vm + } } } diff --git a/src/platforms/mp-toutiao/runtime/index.js b/src/platforms/mp-toutiao/runtime/index.js index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5a843bc821374df4c1959470c611958c8b82d732 100644 --- a/src/platforms/mp-toutiao/runtime/index.js +++ b/src/platforms/mp-toutiao/runtime/index.js @@ -0,0 +1 @@ +import '../../mp-weixin/runtime/index' diff --git a/src/platforms/mp-toutiao/runtime/wrapper/index.js b/src/platforms/mp-toutiao/runtime/wrapper/index.js index ad041c58b8cf7ca401c64b89a4310f9e0251f1b9..0dda3ecc12e4200451add1ffe824c6f9b631502d 100644 --- a/src/platforms/mp-toutiao/runtime/wrapper/index.js +++ b/src/platforms/mp-toutiao/runtime/wrapper/index.js @@ -1,4 +1,42 @@ const instances = Object.create(null) + +export function initPage (pageOptions) { + initComponent(pageOptions) +} + +export function initComponent (componentOptions) { + if (componentOptions.properties) { // ref + componentOptions.properties.vueRef = { + type: String, + value: '' + } + } + const oldAttached = componentOptions.lifetimes.attached + componentOptions.lifetimes.attached = function () { + oldAttached.call(this) + // TODO 需要处理动态变化后的 refs + initRefs.call(this) + } +} + +function initRefs () { + this.selectAllComponents('.vue-ref', (components) => { + components.forEach(component => { + const ref = component.data.vueRef // 头条的组件 dataset 竟然是空的 + this.$vm.$refs[ref] = component.$vm || component + }) + }) + this.selectAllComponents('.vue-ref-in-for', (forComponents) => { + forComponents.forEach(component => { + const ref = component.data.vueRef + if (!this.$vm.$refs[ref]) { + this.$vm.$refs[ref] = [] + } + this.$vm.$refs[ref].push(component.$vm || component) + }) + }) +} + export function triggerLink (mpInstance) { const nodeId = mpInstance.__nodeid__ + '' const webviewId = mpInstance.__webviewId__ + '' diff --git a/src/platforms/mp-weixin/runtime/index.js b/src/platforms/mp-weixin/runtime/index.js index 00832b87323d71205efb87fca02922f9c1498d93..0b0922ca4f7241f22d45917efa96c50615c18eec 100644 --- a/src/platforms/mp-weixin/runtime/index.js +++ b/src/platforms/mp-weixin/runtime/index.js @@ -3,8 +3,8 @@ import { camelize } from 'uni-shared' -const WXPage = Page -const WXComponent = Component +const MPPage = Page +const MPComponent = Component const customizeRE = /:/g @@ -13,11 +13,9 @@ const customize = cached((str) => { }) function initTriggerEvent (mpInstance) { - if (wx.canIUse('nextTick')) { // 微信旧版本基础库不支持重写triggerEvent - const oldTriggerEvent = mpInstance.triggerEvent - mpInstance.triggerEvent = function (event, ...args) { - return oldTriggerEvent.apply(mpInstance, [customize(event), ...args]) - } + const oldTriggerEvent = mpInstance.triggerEvent + mpInstance.triggerEvent = function (event, ...args) { + return oldTriggerEvent.apply(mpInstance, [customize(event), ...args]) } } @@ -34,7 +32,7 @@ Page = function (options = {}) { return oldHook.apply(this, args) } } - return WXPage(options) + return MPPage(options) } const behavior = Behavior({ @@ -45,5 +43,5 @@ const behavior = Behavior({ Component = function (options = {}) { (options.behaviors || (options.behaviors = [])).unshift(behavior) - return WXComponent(options) + return MPComponent(options) } diff --git a/src/platforms/mp-weixin/runtime/wrapper/index.js b/src/platforms/mp-weixin/runtime/wrapper/index.js index bace4b0d1462dd1f9f5d5a837b3e32016ebe56a3..b2fc64ca19cdff7310fd68d6513f4e899576e9d0 100644 --- a/src/platforms/mp-weixin/runtime/wrapper/index.js +++ b/src/platforms/mp-weixin/runtime/wrapper/index.js @@ -1,3 +1,11 @@ +export function initPage () { + +} + +export function initComponent () { + +} + export function triggerLink (mpInstance, vueOptions) { mpInstance.triggerEvent('__l', mpInstance.$vm || vueOptions, { bubbles: true,