import { hasOwn, isPlainObject } from 'uni-shared' import { deepClone } from './deep-clone' 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}`) } } export 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() } }) } } export 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])) } }) } }