817 lines
20 KiB
JavaScript
817 lines
20 KiB
JavaScript
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 };
|