Files
RentWeAppFront/node_modules/@dcloudio/uni-mp-weixin/dist/mp.js
2025-11-14 11:39:33 +08:00

817 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Vue from 'vue';
function parseData (data, vueComponentOptions) {
if (!data) {
return
}
vueComponentOptions.mpOptions.data = data;
}
function parseComponents (vueComponentOptions) {
vueComponentOptions.components = global.__wxVueOptions.components;
}
const _toString = Object.prototype.toString;
const hasOwnProperty = Object.prototype.hasOwnProperty;
function isFn (fn) {
return typeof fn === 'function'
}
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 SOURCE_KEY = '__data__';
const COMPONENT_LIFECYCLE = {
created: 'onServiceCreated',
attached: 'onServiceAttached',
ready: 'mounted',
moved: 'moved',
detached: 'destroyed'
};
const COMPONENT_LIFECYCLE_KEYS = Object.keys(COMPONENT_LIFECYCLE);
const PAGE_LIFETIMES = {
show: 'onPageShow',
hide: 'onPageHide',
resize: 'onPageResize'
};
const PAGE_LIFETIMES_KEYS = Object.keys(PAGE_LIFETIMES);
const PAGE_LIFECYCLE = [
'onLoad',
'onShow',
'onReady',
'onHide',
'onUnload',
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap'
];
function parsePageMethods (mpComponentOptions, vueComponentOptions) {
const methods = Object.create(null);
Object.keys(mpComponentOptions).forEach(key => {
const value = mpComponentOptions[key];
if (isFn(value) && PAGE_LIFECYCLE.indexOf(key) === -1) {
methods[key] = value;
}
});
vueComponentOptions.methods = methods;
}
function parsePageLifecycle (mpComponentOptions, vueComponentOptions) {
Object.keys(mpComponentOptions).forEach(key => {
if (PAGE_LIFECYCLE.indexOf(key) !== -1) {
vueComponentOptions[key] = mpComponentOptions[key];
}
});
}
function parsePage (mpComponentOptions) {
const vueComponentOptions = {
mixins: [],
mpOptions: {}
};
parseComponents(vueComponentOptions);
parseData(mpComponentOptions.data, vueComponentOptions);
parsePageMethods(mpComponentOptions, vueComponentOptions);
parsePageLifecycle(mpComponentOptions, vueComponentOptions);
return vueComponentOptions
}
function parseProperties (properties, vueComponentOptions) {
if (!properties) {
return
}
vueComponentOptions.mpOptions.properties = properties;
}
function parseOptions (options, vueComponentOptions) {
if (!options) {
return
}
vueComponentOptions.mpOptions.options = options;
}
function parseMethods (methods, vueComponentOptions) {
if (!methods) {
return
}
if (methods.$emit) {
console.warn('Method "$emit" conflicts with an existing Vue instance method');
delete methods.$emit;
}
vueComponentOptions.methods = methods;
}
function parseLifecycle (mpComponentOptions, vueComponentOptions) {
COMPONENT_LIFECYCLE_KEYS.forEach(name => {
if (hasOwn(mpComponentOptions, name)) {
(vueComponentOptions[COMPONENT_LIFECYCLE[name]] || (vueComponentOptions[COMPONENT_LIFECYCLE[name]] = []))
.push(mpComponentOptions[name]);
}
});
}
const mpBehaviors = {
'wx://form-field': {},
'wx://component-export': {}
};
function callDefinitionFilter (mpComponentOptions) {
const {
behaviors,
definitionFilter
} = mpComponentOptions;
const behaviorDefinitionFilters = [];
if (Array.isArray(behaviors)) {
behaviors.forEach(behavior => {
behavior = typeof behavior === 'string' ? mpBehaviors[behavior] : behavior;
if (behavior.definitionFilter) {
behaviorDefinitionFilters.push(behavior.definitionFilter);
behavior.definitionFilter.call(null, mpComponentOptions, []);
}
});
}
if (isFn(definitionFilter)) {
return function (defFields) {
definitionFilter(defFields, behaviorDefinitionFilters);
}
}
}
function parseDefinitionFilter (mpComponentOptions, vueComponentOptions) {
callDefinitionFilter(mpComponentOptions);
}
function parseBehavior (behavior) {
const {
data,
methods,
behaviors,
properties
} = behavior;
const vueComponentOptions = {
watch: {},
mpOptions: {
mpObservers: []
}
};
parseData(data, vueComponentOptions);
parseMethods(methods, vueComponentOptions);
parseBehaviors(behaviors, vueComponentOptions);
parseProperties(properties, vueComponentOptions);
parseLifecycle(behavior, vueComponentOptions);
parseDefinitionFilter(behavior);
return vueComponentOptions
}
const BEHAVIORS = {
'wx://form-field': {
beforeCreate () {
const mpOptions = this.$options.mpOptions;
if (!mpOptions.properties) {
mpOptions.properties = Object.create(null);
}
const props = mpOptions.properties;
// TODO form submit,reset
if (!hasOwn(props, 'name')) {
props.name = {
type: String
};
}
if (!hasOwn(props, 'value')) {
props.value = {
type: String // 默认类型调整为 String,否则默认值为 null,导致一些自定义 input 显示不正确
};
}
}
}
};
function parseBehaviors (behaviors, vueComponentOptions) {
if (!behaviors) {
return
}
behaviors.forEach(behavior => {
if (typeof behavior === 'string') {
BEHAVIORS[behavior] && vueComponentOptions.mixins.push(BEHAVIORS[behavior]);
} else {
vueComponentOptions.mixins.push(parseBehavior(behavior));
}
});
}
function parseSinglePath (path) {
return path.split('.')
}
function parseMultiPaths (paths) {
return paths.split(',').map(path => parseSinglePath(path))
}
function parseObservers (observers, vueComponentOptions) {
if (!observers) {
return
}
const {
mpObservers
} = vueComponentOptions.mpOptions;
Object.keys(observers).forEach(path => {
mpObservers.push({
paths: parseMultiPaths(path),
observer: observers[path]
});
});
}
function relative (from, to) {
if (to.indexOf('/') === 0) {
from = '';
}
const fromArr = from.split('/');
const toArr = to.split('/');
fromArr.pop();
while (toArr.length) {
const part = toArr.shift();
if (part !== '' && part !== '.') {
if (part !== '..') {
fromArr.push(part);
} else {
fromArr.pop();
}
}
}
return fromArr.join('/')
}
function parseRelations (relations, vueComponentOptions) {
if (!relations) {
return
}
Object.keys(relations).forEach(name => {
const relation = relations[name];
relation.name = name;
relation.target = relation.target ? String(relation.target) : relative(global.__wxRoute, name);
});
vueComponentOptions.mpOptions.relations = relations;
}
function parseExternalClasses (externalClasses, vueComponentOptions) {
if (!externalClasses) {
return
}
if (!Array.isArray(externalClasses)) {
externalClasses = [externalClasses];
}
vueComponentOptions.mpOptions.externalClasses = externalClasses;
if (!vueComponentOptions.mpOptions.properties) {
vueComponentOptions.mpOptions.properties = Object.create(null);
}
externalClasses.forEach(externalClass => {
vueComponentOptions.mpOptions.properties[camelize(externalClass)] = {
type: String,
value: ''
};
});
}
function parseLifetimes (lifetimes, vueComponentOptions) {
if (!lifetimes) {
return
}
parseLifecycle(lifetimes, vueComponentOptions);
}
function parsePageLifetimes (pageLifetimes, vueComponentOptions) {
if (!pageLifetimes) {
return
}
PAGE_LIFETIMES_KEYS.forEach(key => {
const lifetimeFn = pageLifetimes[key];
isFn(lifetimeFn) && (vueComponentOptions[PAGE_LIFETIMES[key]] = lifetimeFn);
});
}
function parseComponent (mpComponentOptions) {
const {
data,
options,
methods,
behaviors,
lifetimes,
observers,
relations,
properties,
pageLifetimes,
externalClasses
} = mpComponentOptions;
const vueComponentOptions = {
mixins: [],
props: {},
watch: {},
mpOptions: {
mpObservers: []
}
};
parseComponents(vueComponentOptions);
parseData(data, vueComponentOptions);
parseOptions(options, vueComponentOptions);
parseMethods(methods, vueComponentOptions);
parseBehaviors(behaviors, vueComponentOptions);
parseLifetimes(lifetimes, vueComponentOptions);
parseObservers(observers, vueComponentOptions);
parseRelations(relations, vueComponentOptions);
parseProperties(properties, vueComponentOptions);
parsePageLifetimes(pageLifetimes, vueComponentOptions);
parseExternalClasses(externalClasses, vueComponentOptions);
parseLifecycle(mpComponentOptions, vueComponentOptions);
parseDefinitionFilter(mpComponentOptions);
return vueComponentOptions
}
function initRelationHandlers (type, handler, target, ctx) {
if (!handler) {
return
}
const name = `_$${type}Handlers`;
(ctx[name] || (ctx[name] = [])).push(function () {
handler.call(ctx, target);
});
}
function initLinkedHandlers (relation, target, ctx) {
const type = 'linked';
const name = relation.name;
const relationNodes = ctx._$relationNodes || (ctx._$relationNodes = Object.create(null));
(relationNodes[name] || (relationNodes[name] = [])).push(target);
initRelationHandlers(type, relation[type], target, ctx);
}
function initUnlinkedHandlers (relation, target, ctx) {
const type = 'unlinked';
initRelationHandlers(type, relation[type], target, ctx);
}
function findParentRelation (parentVm, target, type) {
const relations = parentVm &&
parentVm.$options.mpOptions &&
parentVm.$options.mpOptions.relations;
if (!relations) {
return []
}
const name = Object.keys(relations).find(name => {
const relation = relations[name];
return relation.target === target && relation.type === type
});
if (!name) {
return []
}
return [relations[name], parentVm]
}
function initParentRelation (vm, childRelation, match) {
const [parentRelation, parentVm] = match(vm, vm.$options.mpOptions.path);
if (!parentRelation) {
return
}
initLinkedHandlers(parentRelation, vm, parentVm);
initLinkedHandlers(childRelation, parentVm, vm);
initUnlinkedHandlers(parentRelation, vm, parentVm);
initUnlinkedHandlers(childRelation, parentVm, vm);
}
function initRelation (relation, vm) {
const type = relation.type;
if (type === 'parent') {
initParentRelation(vm, relation, function matchParent (vm, target) {
return findParentRelation(vm.$parent, target, 'child')
});
} else if (type === 'ancestor') {
initParentRelation(vm, relation, function matchAncestor (vm, target) {
let $parent = vm.$parent;
while ($parent) {
const ret = findParentRelation($parent, target, 'descendant');
if (ret.length) {
return ret
}
$parent = $parent.$parent;
}
return []
});
}
}
function initRelations (vm) {
const {
relations
} = vm.$options.mpOptions || {};
if (!relations) {
return
}
Object.keys(relations).forEach(name => {
initRelation(relations[name], vm);
});
}
function handleRelations (vm, type) {
// TODO 需要移除 relationNodes
const handlers = vm[`_$${type}Handlers`];
if (!handlers) {
return
}
handlers.forEach(handler => handler());
}
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
};
function proxy (target, sourceKey, key) {
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
};
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val;
};
Object.defineProperty(target, key, sharedPropertyDefinition);
}
function setDataByExprPath (exprPath, value, data) {
const keys = exprPath.replace(/\[(\d+?)\]/g, '.$1').split('.');
keys.reduce((obj, key, idx) => {
if (idx === keys.length - 1) {
obj[key] = value;
} else {
if (typeof obj[key] === 'undefined') {
obj[key] = {};
}
return obj[key]
}
}, data);
return keys.length === 1
}
function setData (data, callback) {
if (!isPlainObject(data)) {
return
}
Object.keys(data).forEach(key => {
if (setDataByExprPath(key, data[key], this.data)) {
!hasOwn(this, key) && proxy(this, SOURCE_KEY, key);
}
});
this.$forceUpdate();
isFn(callback) && this.$nextTick(callback);
}
/**
* https://github.com/swan-team/swan-js/blob/61e2a63f7aa576b5daafbe77fdfa7c65b977060c/src/utils/index.js
*/
const _toString$1 = Object.prototype.toString;
/**
* 深度assign的函数
* @param {Object} targetObject 要被拷贝的目标对象
* @param {Object} originObject 拷贝的源对象
* @return {Object} merge后的对象
*/
const deepAssign = (targetObject = {}, originObject) => {
const originType = _toString$1.call(originObject);
if (originType === '[object Array]') {
targetObject = originObject.slice(0);
return targetObject
} else if (originType === '[object Object]') {
for (const key in originObject) {
targetObject[key] = deepAssign(targetObject[key], originObject[key]);
}
return targetObject
} else if (originType === '[object Date]') {
return new Date(originObject.getTime())
} else if (originType === '[object RegExp]') {
const target = String(originObject);
const lastIndex = target.lastIndexOf('/');
return new RegExp(target.slice(1, lastIndex), target.slice(lastIndex + 1))
}
return originObject
};
/**
* 深度拷贝逻辑不同于lodash等库但是与微信一致
* @param {*} [originObj] 原对象
* @return {Object|Array} 拷贝结果
*/
const deepClone = originObj => {
return deepAssign(_toString$1.call(originObj) === '[object Array]' ? [] : {}, originObj)
};
const PROP_DEFAULT_VALUES = {
[String]: '',
[Number]: 0,
[Boolean]: false,
[Object]: null,
[Array]: [],
[null]: null
};
function getDefaultVal (propType) {
return PROP_DEFAULT_VALUES[propType]
}
function getPropertyVal (options) {
if (isPlainObject(options)) {
if (hasOwn(options, 'value')) {
return options.value
}
return getDefaultVal(options.type)
}
return getDefaultVal(options)
}
function getType (propOptions) {
return isPlainObject(propOptions) ? propOptions.type : propOptions
}
function validateProp (key, propsOptions, propsData, vm) {
let value = propsData[key];
if (value !== undefined) {
const propOptions = propsOptions[key];
const type = getType(propOptions);
value = formatVal(value, type);
const observer = propOptions && propOptions.observer;
if (observer) {
// 初始化时,异步触发 observer,否则 observer 中无法访问 methods 或其他
setTimeout(function () {
observe(observer, vm, value);
}, 4);
}
return value
}
return getPropertyVal(propsOptions[key])
}
function formatVal (val, type) {
if (type === Boolean) {
return !!val
} else if (type === String) {
return String(val)
}
return val
}
function observe (observer, vm, newVal, oldVal) {
try {
if (typeof observer === 'function') {
observer.call(vm, newVal, oldVal);
} else if (typeof observer === 'string' &&
typeof vm[observer] === 'function'
) {
vm[observer](newVal, oldVal);
}
} catch (err) {
console.error(`execute observer ${observer} callback fail! err: ${err}`);
}
}
function initProperties (vm, instanceData) {
const properties = vm.$options.mpOptions.properties;
if (!properties) {
return
}
const propsData = deepClone(vm.$options.propsData) || {};
for (const key in properties) {
const observer = isPlainObject(properties[key]) ? properties[key].observer : false;
let value = validateProp(key, properties, propsData, vm);
Object.defineProperty(instanceData, key, {
enumerable: true,
configurable: true,
get () {
return value
},
set (newVal) {
const oldVal = value;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
// TODO 临时方案,clone array
value = Array.isArray(newVal) ? newVal.slice(0) : newVal;
if (observer) {
observe(observer, vm, newVal, oldVal);
}
// 触发渲染
vm.$forceUpdate();
}
});
}
}
function updateProperties (vm) {
const properties = vm.$options.mpOptions && vm.$options.mpOptions.properties;
const propsData = vm.$options.propsData;
if (propsData && properties) {
Object.keys(properties).forEach(key => {
if (hasOwn(propsData, key)) {
vm[key] = formatVal(propsData[key], getType(properties[key]));
}
});
}
}
function initState (vm) {
const instanceData = JSON.parse(JSON.stringify(vm.$options.mpOptions.data || {}));
vm[SOURCE_KEY] = instanceData;
const propertyDefinition = {
get () {
return vm[SOURCE_KEY]
},
set (value) {
vm[SOURCE_KEY] = value;
}
};
Object.defineProperties(vm, {
data: propertyDefinition,
properties: propertyDefinition
});
vm.setData = setData;
initProperties(vm, instanceData);
Object.keys(instanceData).forEach(key => {
proxy(vm, SOURCE_KEY, key);
});
}
function initMethods (vm) {
const oldEmit = vm.$emit;
vm.triggerEvent = (eventName, detail, options) => {
const target = {
dataset: vm.$el.dataset
};
const event = {
target,
currentTarget: target,
detail,
preventDefault: noop,
stopPropagation: noop
};
oldEmit.call(vm, eventName, event);
};
// 主要是Vant 自己封装了 $emit,放到 methods 中会触发 Vue 的警告,索性,框架直接重写该方法
vm.$emit = (...args) => {
vm.triggerEvent(...args);
};
vm.getRelationNodes = (relationKey) => {
// 需要过滤已被销毁的vm
/* eslint-disable no-mixed-operators */
return (vm._$relationNodes && vm._$relationNodes[relationKey] || []).filter(vm => !vm._isDestroyed)
};
vm._$updateProperties = updateProperties;
}
function handleObservers (vm) {
const watch = vm.$options.watch;
if (!watch) {
return
}
Object.keys(watch).forEach(name => {
const observer = watch[name];
if (observer.mounted) {
const val = vm[name];
let handler = observer.handler;
if (typeof handler === 'string') {
handler = vm[handler];
}
handler && handler.call(vm, val, val);
}
});
}
var polyfill = {
beforeCreate () {
// 取消 development 时的 Proxy,避免小程序组件模板中使用尚未定义的属性告警
this._renderProxy = this;
this._$self = this;
this._$noop = noop;
},
created () { // properties 中可能会访问 methods,故需要在 created 中初始化
initState(this);
initMethods(this);
initRelations(this);
},
mounted () {
handleObservers(this);
},
destroyed () {
handleRelations(this, 'unlinked');
}
};
global.__wxRoute = '';
global.__wxComponents = Object.create(null);
global.__wxVueOptions = Object.create(null);
function Page (options) {
const pageOptions = parsePage(options);
pageOptions.mixins.unshift(polyfill);
pageOptions.mpOptions.path = global.__wxRoute;
global.__wxComponents[global.__wxRoute] = pageOptions;
}
function initRelationsHandler (vueComponentOptions) {
// linked 需要在当前组件 attached 之后再执行
if (!vueComponentOptions.onServiceAttached) {
vueComponentOptions.onServiceAttached = [];
}
vueComponentOptions.onServiceAttached.push(function onServiceAttached () {
handleRelations(this, 'linked');
});
}
function Component (options) {
const componentOptions = parseComponent(options);
componentOptions.mixins.unshift(polyfill);
componentOptions.mpOptions.path = global.__wxRoute;
initRelationsHandler(componentOptions);
global.__wxComponents[global.__wxRoute] = componentOptions;
}
function Behavior (options) {
return options
}
const nextTick = Vue.nextTick;
var index = uni.__$wx__;
export default index;
export { Behavior, Component, Page, nextTick };