import Vue from 'vue'; const _toString = Object.prototype.toString; const hasOwnProperty = Object.prototype.hasOwnProperty; function isFn (fn) { return typeof fn === 'function' } function isStr (str) { return typeof str === 'string' } function isPlainObject (obj) { return _toString.call(obj) === '[object Object]' } function hasOwn (obj, key) { return hasOwnProperty.call(obj, key) } 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 = /subNVue|requireNativePlugin|upx2px|hideKeyboard|canIUse|^create|Sync$|Manager$/; const CONTEXT_API_RE = /^create|Manager$/; const CALLBACK_API_RE = /^on/; function isContextApi (name) { return CONTEXT_API_RE.test(name) } function isSyncApi (name) { return SYNC_API_RE.test(name) } function isCallbackApi (name) { return CALLBACK_API_RE.test(name) } function handlePromise (promise) { return promise.then(data => { return [null, data] }) .catch(err => [err]) } function shouldPromise (name) { if ( isContextApi(name) || isSyncApi(name) || isCallbackApi(name) ) { return false } return true } function promisify (name, api) { if (!shouldPromise(name)) { return api } return function promiseApi (options = {}, ...params) { if (isFn(options.success) || isFn(options.fail) || isFn(options.complete)) { return api(options, ...params) } return handlePromise(new Promise((resolve, reject) => { api(Object.assign({}, options, { 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 }) ) }; })) } } const EPS = 1e-4; const BASE_DEVICE_WIDTH = 750; let isIOS = false; let deviceWidth = 0; let deviceDPR = 0; function checkDeviceWidth () { const { platform, pixelRatio, windowWidth } = my.getSystemInfoSync(); // uni=>my runtime 编译目标是 uni 对象,内部不允许直接使用 uni deviceWidth = windowWidth; deviceDPR = pixelRatio; isIOS = platform === 'ios'; } function upx2px (number, newDeviceWidth) { if (deviceWidth === 0) { checkDeviceWidth(); } number = Number(number); if (number === 0) { return 0 } let result = (number / BASE_DEVICE_WIDTH) * (newDeviceWidth || deviceWidth); if (result < 0) { result = -result; } result = Math.floor(result + EPS); if (result === 0) { if (deviceDPR === 1 || !isIOS) { return 1 } else { return 0.5 } } return number < 0 ? -result : result } // 不支持的 API 列表 const todos = [ 'saveImageToPhotosAlbum', 'getRecorderManager', 'getBackgroundAudioManager', 'createInnerAudioContext', 'chooseVideo', 'saveVideoToPhotosAlbum', 'createVideoContext', 'createCameraContext', 'createLivePlayerContext', 'openDocument', 'onMemoryWarning', 'startAccelerometer', 'startCompass', 'addPhoneContact', 'setTabBarItem', 'setTabBarStyle', 'hideTabBar', 'showTabBar', 'setTabBarBadge', 'removeTabBarBadge', 'showTabBarRedDot', 'hideTabBarRedDot', 'setBackgroundColor', 'setBackgroundTextStyle', 'createIntersectionObserver', 'authorize', 'openSetting', 'getSetting', 'chooseAddress', 'chooseInvoiceTitle', 'addTemplate', 'deleteTemplate', 'getTemplateLibraryById', 'getTemplateLibraryList', 'getTemplateList', 'sendTemplateMessage', 'getUpdateManager', 'setEnableDebug', 'getExtConfig', 'getExtConfigSync', 'onWindowResize', 'offWindowResize' ]; // 存在兼容性的 API 列表 const canIUses = [ 'startPullDownRefresh' ]; function _handleNetworkInfo (result) { switch (result.networkType) { case 'NOTREACHABLE': result.networkType = 'none'; break case 'WWAN': // TODO ? result.networkType = '3g'; break default: result.networkType = result.networkType.toLowerCase(); break } return {} } function _handleSystemInfo (result) { let platform = result.platform ? result.platform.toLowerCase() : 'devtools'; if (!~['android', 'ios'].indexOf(platform)) { platform = 'devtools'; } result.platform = platform; } const protocols = { // 需要做转换的 API 列表 returnValue (methodName, res) { // 通用 returnValue 解析 if (res.error || res.errorMessage) { res.errMsg = `${methodName}:fail ${res.errorMessage || res.error}`; delete res.error; delete res.errorMessage; } else { res.errMsg = `${methodName}:ok`; } return res }, request: { name: my.canIUse('request') ? 'request' : 'httpRequest', args (fromArgs) { if (!fromArgs.header) { // 默认增加 header 参数,方便格式化 content-type fromArgs.header = {}; } return { header (header = {}, toArgs) { const headers = { 'content-type': 'application/json' }; Object.keys(header).forEach(key => { headers[key.toLocaleLowerCase()] = header[key]; }); return { name: 'headers', value: headers } }, method: 'method', // TODO 支付宝小程序仅支持 get,post responseType: false } }, returnValue: { status: 'statusCode', headers: 'header' } }, setNavigationBarColor: { name: 'setNavigationBar', args: { frontColor: false, animation: false } }, setNavigationBarTitle: { name: 'setNavigationBar' }, showModal ({ showCancel = true } = {}) { if (showCancel) { return { name: 'confirm', args: { cancelColor: false, confirmColor: false, cancelText: 'cancelButtonText', confirmText: 'confirmButtonText' }, returnValue (fromRes, toRes) { toRes.confirm = fromRes.confirm; toRes.cancel = !fromRes.confirm; } } } return { name: 'alert', args: { confirmColor: false, confirmText: 'buttonText' }, returnValue (fromRes, toRes) { toRes.confirm = true; toRes.cancel = false; } } }, showToast ({ icon = 'success' } = {}) { const args = { title: 'content', icon: 'type', duration: false, image: false, mask: false }; if (icon === 'loading') { return { name: 'showLoading', args } } return { name: 'showToast', args } }, showActionSheet: { name: 'showActionSheet', args: { itemList: 'items', itemColor: false }, returnValue: { index: 'tapIndex' } }, showLoading: { args: { title: 'content', mask: false } }, uploadFile: { args: { name: 'fileName' } // 从测试结果看,是有返回对象的,文档上没有说明。 }, downloadFile: { returnValue: { apFilePath: 'tempFilePath' } }, connectSocket: { args: { method: false, protocols: false } // TODO 有没有返回值还需要测试下 }, chooseImage: { returnValue: { apFilePaths: 'tempFilePaths' } }, previewImage: { args (fromArgs) { // 支付宝小程序的 current 是索引值,而非图片地址。 const currentIndex = Number(fromArgs.current); if (isNaN(currentIndex)) { if (fromArgs.current && Array.isArray(fromArgs.urls)) { const index = fromArgs.urls.indexOf(fromArgs.current); fromArgs.current = ~index ? index : 0; } } else { fromArgs.current = currentIndex; } return { indicator: false, loop: false } } }, saveFile: { args: { tempFilePath: 'apFilePath' }, returnValue: { apFilePath: 'savedFilePath' } }, getSavedFileInfo: { args: { filePath: 'apFilePath' }, returnValue (result) { if (result.fileList && result.fileList.length) { result.fileList.forEach(file => { file.filePath = file.apFilePath; delete file.apFilePath; }); } return {} } }, removeSavedFile: { args: { filePath: 'apFilePath' } }, getLocation: { args: { type: false, altitude: false } }, openLocation: { args: { // TODO address 参数在阿里上是必传的 } }, getNetworkType: { returnValue: _handleNetworkInfo }, onNetworkStatusChange: { returnValue: _handleNetworkInfo }, stopAccelerometer: { name: 'offAccelerometerChange' }, stopCompass: { name: 'offCompassChange' }, scanCode: { name: 'scan', args: { onlyFromCamera: 'hideAlbum', scanType: false }, returnValue: { code: 'result' } }, setClipboardData: { name: 'setClipboard', args: { data: 'text' } }, getClipboardData: { name: 'getClipboard', returnValue: { text: 'data' } }, pageScrollTo: { args: { duration: false } }, login: { name: 'getAuthCode', returnValue (result) { result.code = result.authCode; } }, getUserInfo: { name: 'getAuthUserInfo', returnValue (result) { result.userInfo = { nickName: result.nickName, avatarUrl: result.avatar }; } }, requestPayment: { name: 'tradePay', args: { orderInfo: 'tradeNO' } }, getBLEDeviceServices: { returnValue (result) { result.services.forEach((item) => { item.uuid = item.serviceId; }); } }, makePhoneCall: { args: { phoneNumber: 'number' } }, stopGyroscope: { name: 'offGyroscopeChange' }, getSystemInfo: { returnValue: _handleSystemInfo }, getSystemInfoSync: { returnValue: _handleSystemInfo }, // 文档没提到,但是实测可用。 canvasToTempFilePath: { returnValue (result) { // 真机的情况下会有 tempFilePath 这个值,因此需要主动修改。 result.tempFilePath = result.apFilePath; } }, setScreenBrightness: { args: { value: 'brightness' } }, getScreenBrightness: { returnValue: { brightness: 'value' } } }; const CALLBACKS = ['success', 'fail', 'cancel', 'complete']; function processCallback (methodName, method, returnValue) { return function (res) { return method(processReturnValue(methodName, res, returnValue)) } } function processArgs (methodName, fromArgs, argsOption = {}, returnValue = {}, keepFromArgs = false) { if (isPlainObject(fromArgs)) { // 一般 api 的参数解析 const toArgs = keepFromArgs === true ? fromArgs : {}; // returnValue 为 false 时,说明是格式化返回值,直接在返回值对象上修改赋值 if (isFn(argsOption)) { argsOption = argsOption(fromArgs, toArgs) || {}; } for (let key in fromArgs) { if (hasOwn(argsOption, key)) { let keyOption = argsOption[key]; if (isFn(keyOption)) { keyOption = keyOption(fromArgs[key], fromArgs, toArgs); } if (!keyOption) { // 不支持的参数 console.warn(`支付宝小程序 ${methodName}暂不支持${key}`); } else if (isStr(keyOption)) { // 重写参数 key toArgs[keyOption] = fromArgs[key]; } else if (isPlainObject(keyOption)) { // {name:newName,value:value}可重新指定参数 key:value toArgs[keyOption.name ? keyOption.name : key] = keyOption.value; } } else if (CALLBACKS.indexOf(key) !== -1) { toArgs[key] = processCallback(methodName, fromArgs[key], returnValue); } else { if (!keepFromArgs) { toArgs[key] = fromArgs[key]; } } } return toArgs } else if (isFn(fromArgs)) { fromArgs = processCallback(methodName, fromArgs, returnValue); } return fromArgs } function processReturnValue (methodName, res, returnValue, keepReturnValue = false) { if (isFn(protocols.returnValue)) { // 处理通用 returnValue res = protocols.returnValue(methodName, res); } return processArgs(methodName, res, returnValue, {}, keepReturnValue) } function wrapper (methodName, method) { if (hasOwn(protocols, methodName)) { const protocol = protocols[methodName]; if (!protocol) { // 暂不支持的 api return function () { console.error(`支付宝小程序 暂不支持${methodName}`); } } return function (arg1, arg2) { // 目前 api 最多两个参数 let options = protocol; if (isFn(protocol)) { options = protocol(arg1); } arg1 = processArgs(methodName, arg1, options.args, options.returnValue); const returnValue = my[options.name || methodName](arg1, arg2); if (isSyncApi(methodName)) { // 同步 api return processReturnValue(methodName, returnValue, options.returnValue, isContextApi(methodName)) } return returnValue } } return method } const todoApis = Object.create(null); const TODOS = [ 'subscribePush', 'unsubscribePush', 'onPush', 'offPush', 'share' ]; function createTodoApi (name) { return function todoApi ({ fail, complete }) { const res = { errMsg: `${name}:fail:暂不支持 ${name} 方法` }; isFn(fail) && fail(res); isFn(complete) && complete(res); } } TODOS.forEach(function (name) { todoApis[name] = createTodoApi(name); }); var providers = { oauth: ['alipay'], share: ['alipay'], payment: ['alipay'], push: ['alipay'] }; function getProvider ({ service, success, fail, complete }) { let res = false; if (providers[service]) { res = { errMsg: 'getProvider:ok', service, provider: providers[service] }; isFn(success) && success(res); } else { res = { errMsg: 'getProvider:fail:服务[' + service + ']不存在' }; isFn(fail) && fail(res); } isFn(complete) && complete(res); } var extraApi = /*#__PURE__*/Object.freeze({ getProvider: getProvider }); function setStorageSync (key, data) { return my.setStorageSync({ key, data }) } function getStorageSync (key) { const result = my.getStorageSync({ key }); // 支付宝平台会返回一个 success 值,但是目前测试的结果这个始终是 true。当没有存储数据的时候,其它平台会返回空字符串。 return result.data !== null ? result.data : '' } function removeStorageSync (key) { return my.removeStorageSync({ key }) } function startGyroscope (params) { if (hasOwn(params, 'interval')) { console.warn('支付宝小程序 startGyroscope暂不支持interval'); } params.success && params.success({ errMsg: 'startGyroscope:ok' }); params.complete && params.complete({ errMsg: 'startGyroscope:ok' }); } var api = /*#__PURE__*/Object.freeze({ setStorageSync: setStorageSync, getStorageSync: getStorageSync, removeStorageSync: removeStorageSync, startGyroscope: startGyroscope }); const isArray = Array.isArray; const keyList = Object.keys; function equal (a, b) { if (a === b) return true if (a && b && typeof a === 'object' && typeof b === 'object') { const arrA = isArray(a); const arrB = isArray(b); let i, length, key; if (arrA && arrB) { length = a.length; if (length !== b.length) return false for (i = length; i-- !== 0;) { if (!equal(a[i], b[i])) return false } return true } if (arrA !== arrB) return false const dateA = a instanceof Date; const dateB = b instanceof Date; if (dateA !== dateB) return false if (dateA && dateB) return a.getTime() === b.getTime() const regexpA = a instanceof RegExp; const regexpB = b instanceof RegExp; if (regexpA !== regexpB) return false if (regexpA && regexpB) return a.toString() === b.toString() const keys = keyList(a); length = keys.length; if (length !== keyList(b).length) { return false } for (i = length; i-- !== 0;) { if (!hasOwn.call(b, keys[i])) return false } for (i = length; i-- !== 0;) { key = keys[i]; if (!equal(a[key], b[key])) return false } return true } return false } const customizeRE = /:/g; const customize = cached((str) => { return camelize(str.replace(customizeRE, '-')) }); const mocks = ['$id']; function handleRef (ref) { if (!ref) { return } const refName = ref.props['data-ref']; const refInForName = ref.props['data-ref-in-for']; if (refName) { this.$vm.$refs[refName] = ref.$vm || ref; } else if (refInForName) { this.$vm.$refs[refInForName] = [ref.$vm || ref]; } } 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) { const handler = this.props[customize('on-' + type)]; if (!handler) { return } const eventOpts = this.props['data-event-opts']; const target = { dataset: { eventOpts } }; handler({ type: customize(type), target, currentTarget: target, detail }); } const IGNORES = ['$slots', '$scopedSlots']; function createObserver (isDidUpdate) { return function observe (props) { const prevProps = isDidUpdate ? props : this.props; const nextProps = isDidUpdate ? this.props : props; if (equal(prevProps, nextProps)) { return } Object.keys(prevProps).forEach(name => { if (IGNORES.indexOf(name) === -1) { const prevValue = prevProps[name]; const nextValue = nextProps[name]; if (!isFn(prevValue) && !isFn(nextValue) && !equal(prevValue, nextValue)) { this.$vm[name] = nextProps[name]; } } }); } } 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; } }); 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) } function initBehavior ({ properties }) { const props = {}; Object.keys(properties).forEach(key => { props[key] = properties[key].value; }); return { props } } function triggerLink (mpInstance, vueOptions) { mpInstance.props.onTriggerLink(mpInstance.$vm || vueOptions); } 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); } } } } else { // vueOptions if (!detail.parent) { detail.parent = this.$vm; } } } function initMocks (vm, mocks$$1) { const mpInstance = vm.$mp[vm.mpType]; mocks$$1.forEach(mock => { if (hasOwn(mpInstance, mock)) { vm[mock] = mpInstance[mock]; } }); } function initHooks (mpOptions, hooks) { hooks.forEach(hook => { mpOptions[hook] = function (args) { return this.$vm.__call_hook(hook, args) }; }); } function getData (vueOptions, context) { let data = vueOptions.data || {}; const methods = vueOptions.methods || {}; if (typeof data === 'function') { try { data = data.call(context); // 支持 Vue.prototype 上挂的数据 } catch (e) { if (process.env.VUE_APP_DEBUG) { console.warn('根据 Vue 的 data 函数初始化小程序 data 失败,请尽量确保 data 函数中不访问 vm 对象,否则可能影响首次数据渲染速度。', data); } } } else { try { // 对 data 格式化 data = JSON.parse(JSON.stringify(data)); } catch (e) {} } if (!isPlainObject(data)) { data = {}; } Object.keys(methods).forEach(methodName => { if (context.__lifecycle_hooks__.indexOf(methodName) === -1 && !hasOwn(data, methodName)) { data[methodName] = methods[methodName]; } }); return data } const PROP_TYPES = [String, Number, Boolean, Object, Array, null]; function createObserver$1 (name) { return function observer (newVal, oldVal) { if (this.$vm) { this.$vm[name] = newVal; // 为了触发其他非 render watcher } } } function getBehaviors (vueOptions) { const vueBehaviors = vueOptions['behaviors']; const vueExtends = vueOptions['extends']; const vueMixins = vueOptions['mixins']; let vueProps = vueOptions['props']; if (!vueProps) { vueOptions['props'] = vueProps = []; } const behaviors = []; if (Array.isArray(vueBehaviors)) { vueBehaviors.forEach(behavior => { behaviors.push(behavior.replace('uni://', `${"my"}://`)); if (behavior === 'uni://form-field') { if (Array.isArray(vueProps)) { vueProps.push('name'); vueProps.push('value'); } else { vueProps['name'] = String; vueProps['value'] = null; } } }); } if (isPlainObject(vueExtends) && vueExtends.props) { behaviors.push( initBehavior({ properties: getProperties(vueExtends.props, true) }) ); } if (Array.isArray(vueMixins)) { vueMixins.forEach(vueMixin => { if (isPlainObject(vueMixin) && vueMixin.props) { behaviors.push( initBehavior({ properties: getProperties(vueMixin.props, true) }) ); } }); } return behaviors } function parsePropType (key, type, defaultValue, file) { // [String]=>String if (Array.isArray(type) && type.length === 1) { return type[0] } return type } function getProperties (props, isBehavior = false, file = '') { const properties = {}; if (!isBehavior) { properties.vueSlots = { // 小程序不能直接定义 $slots 的 props,所以通过 vueSlots 转换到 $slots type: null, value: [], observer: function (newVal, oldVal) { const $slots = Object.create(null); newVal.forEach(slotName => { $slots[slotName] = true; }); this.setData({ $slots }); } }; } if (Array.isArray(props)) { // ['title'] props.forEach(key => { properties[key] = { type: null, observer: createObserver$1(key) }; }); } else if (isPlainObject(props)) { // {title:{type:String,default:''},content:String} Object.keys(props).forEach(key => { const opts = props[key]; if (isPlainObject(opts)) { // title:{type:String,default:''} let value = opts['default']; if (isFn(value)) { value = value(); } opts.type = parsePropType(key, opts.type, value, file); properties[key] = { type: PROP_TYPES.indexOf(opts.type) !== -1 ? opts.type : null, value, observer: createObserver$1(key) }; } else { // content:String const type = parsePropType(key, opts, null, file); properties[key] = { type: PROP_TYPES.indexOf(type) !== -1 ? type : null, observer: createObserver$1(key) }; } }); } return properties } function wrapper$1 (event) { // TODO 又得兼容 mpvue 的 mp 对象 try { event.mp = JSON.parse(JSON.stringify(event)); } catch (e) {} event.stopPropagation = noop; event.preventDefault = noop; event.target = event.target || {}; if (!hasOwn(event, 'detail')) { event.detail = {}; } if (isPlainObject(event.detail)) { event.target = Object.assign({}, event.target, event.detail); } return event } 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, event) { 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 { if (dataPath === '$event') { // $event extraObj['$' + index] = event; } else if (dataPath.indexOf('$event.') === 0) { // $event.target.value extraObj['$' + index] = vm.__get_value(dataPath.replace('$event.', ''), event); } else { extraObj['$' + index] = vm.__get_value(dataPath); } } } else { extraObj['$' + index] = getExtraValue(vm, dataPath); } }); } return extraObj } function getObjByArray (arr) { const obj = {}; for (let i = 1; i < arr.length; i++) { const element = arr[i]; obj[element[0]] = element[1]; } return obj } 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, event); const ret = []; args.forEach(arg => { if (arg === '$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 { if (Array.isArray(arg) && arg[0] === 'o') { ret.push(getObjByArray(arg)); } else if (typeof arg === 'string' && hasOwn(extraObj, arg)) { ret.push(extraObj[arg]); } else { ret.push(arg); } } }); return ret } const ONCE = '~'; const CUSTOM = '^'; function handleEvent (event) { event = wrapper$1(event); // [['tap',[['handle',[1,2,a]],['handle1',[1,2,a]]]]] const eventOpts = (event.currentTarget || event.target).dataset.eventOpts; if (!eventOpts) { return console.warn(`事件信息不存在`) } // [['handle',[1,2,a]],['handle1',[1,2,a]]] const eventType = event.type; eventOpts.forEach(eventOpt => { let type = eventOpt[0]; const eventsArray = eventOpt[1]; const isCustom = type.charAt(0) === CUSTOM; type = isCustom ? type.slice(1) : type; const isOnce = type.charAt(0) === ONCE; type = isOnce ? type.slice(1) : type; if (eventsArray && eventType === type) { eventsArray.forEach(eventArray => { 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.apply(this.$vm, processEventArgs( this.$vm, event, eventArray[1], eventArray[2], isCustom, methodName )); } }); } }); } const hooks = [ '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 { 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') { 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); return vm } 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 } 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 = { 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 } }; initHooks(pageOptions.methods, hooks$1); return initPage(pageOptions, vueOptions) } function initVm$2 (VueComponent) { if (this.$vm) { return } const properties = this.props; 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(); } 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); } 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$2.call(this, VueComponent); }, 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'); }, 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 initComponent(componentOptions, vueOptions) } todos.forEach(todoApi => { protocols[todoApi] = false; }); canIUses.forEach(canIUseApi => { const apiName = protocols[canIUseApi] && protocols[canIUseApi].name ? protocols[canIUseApi].name : canIUseApi; if (!my.canIUse(apiName)) { protocols[canIUseApi] = false; } }); let uni = {}; if (typeof Proxy !== 'undefined') { uni = new Proxy({}, { get (target, name) { if (name === 'upx2px') { return upx2px } 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 (!hasOwn(my, name) && !hasOwn(protocols, name)) { return } return promisify(name, wrapper(name, my[name])) } }); } 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(api).forEach(name => { uni[name] = promisify(name, api[name]); }); Object.keys(my).forEach(name => { if (hasOwn(my, name) || hasOwn(protocols, name)) { uni[name] = promisify(name, wrapper(name, my[name])); } }); } var uni$1 = uni; export default uni$1; export { createApp, createPage, createComponent };