wx-mini-weview
Version:
weixin UI
303 lines (259 loc) • 10.1 kB
JavaScript
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
/**
* 生成支持父子组件传值优先级管理的行为
* @param {Object} config - 配置对象
* @param {Object} config.properties - 可继承的属性定义
* @param {Object} config.childrenProperties - 下发给子组件的属性,格式为 {relationName: {propName: {type, value}}}
* @param {Object} config.childrenPropsObjPrefix - 设置子组件属性前缀,格式为 {relationName: prefix},空字符串表示无前缀
*/
const WvInheritable = (config) => {
const propsObj = config.properties || {}
const childrenPropsObj = config.childrenProperties || {}
const childrenPropsObjPrefix = config.childrenPropsObjPrefix || {}
const data = {
wvInheritableApply: {},
_wvInheritableParentNodes: {}, // 存储父组件引用
_wvInheritableChildNodes: {} // 存储子组件引用
}
const properties = {}
const observers = {}
const childrenObservers = {}
const lifetimesAttached = []
// 处理自身的可继承属性
Object.keys(propsObj).forEach(key => {
const propConfig = propsObj[key]
const type = propConfig.type || null
const defaultValue = propConfig.value
const capKey = capitalize(key)
// 保存原始的observer函数(如果存在)
const originalObserver = propConfig.observer
// 使用更明确的前缏来避免命名冲突
const hasUserKey = `_wvInheritable_hasUserProp_${key}`
const defaultKey = `wvInheritableDefault${capKey}`
const inheritedKey = `wvInheritableInherited${capKey}`
const applyKey = `wvInheritableApply.${key}`
properties[key] = {
type,
value: 'INHERITED_PROPS_NULL',
observer(val, oldVal) {
this.setData({ [hasUserKey]: true })
// 调用原始observer(如果存在)
if (typeof originalObserver === 'function') {
originalObserver.call(this, val, oldVal)
} else if (typeof originalObserver === 'string' && typeof this[originalObserver] === 'function') {
this[originalObserver](val, oldVal)
}
}
}
// 数据字段
data[hasUserKey] = false
data[defaultKey] = defaultValue
data[inheritedKey] = undefined
data[applyKey] = defaultValue
// 初始化代码
lifetimesAttached.push(function() {
const final = this.data[hasUserKey]
? this.data[key] !== 'INHERITED_PROPS_NULL'
? this.data[key]
: this.data[defaultKey]
: this.data[inheritedKey] !== undefined
? this.data[inheritedKey]
: this.data[defaultKey]
this.setData({
[applyKey]: final
})
})
// 观察者
observers[`${key},${inheritedKey},${hasUserKey}`] = function () {
const final = this.data[hasUserKey]
? this.data[key] !== 'INHERITED_PROPS_NULL'
? this.data[key]
: this.data[defaultKey]
: this.data[inheritedKey] !== undefined
? this.data[inheritedKey]
: this.data[defaultKey]
this.setData({
[applyKey]: final
})
}
})
// 处理子组件属性传递
Object.keys(childrenPropsObj).forEach(relationName => {
const relationProps = childrenPropsObj[relationName] || {}
// 获取该关系的前缀配置,优先使用 childrenPropsObjPrefix 中的配置
// 如果 childrenPropsObjPrefix[relationName] 存在(包括空字符串),则使用它
// 否则使用 relationName 作为前缀
const prefix = childrenPropsObjPrefix.hasOwnProperty(relationName) ?
childrenPropsObjPrefix[relationName] : relationName
// 为每个子组件关系创建属性
Object.keys(relationProps).forEach(propName => {
const propConfig = relationProps[propName]
const type = propConfig.type || null
const defaultValue = propConfig.value
// 创建带前缀的属性名,根据是否有前缀来处理大小写
let prefixedPropName
if (!prefix) {
prefixedPropName = propName
} else {
prefixedPropName = `${prefix}${capitalize(propName)}`
}
// 添加到properties
properties[prefixedPropName] = {
type,
value: defaultValue
}
// 添加观察者,当属性变化时,传递给子组件
const observerKey = `${prefixedPropName}`
childrenObservers[observerKey] = function(newVal) {
// 获取子组件并设置属性
this._updateChildrenProp(relationName, propName, newVal)
}
})
})
// 合并所有观察者
const allObservers = { ...observers, ...childrenObservers }
return Behavior({
properties,
data,
observers: allObservers,
lifetimes: {
attached() {
lifetimesAttached.forEach(fn => fn.call(this))
}
},
methods: {
/**
* 设置被继承的字段
* @param {object} inheritedProps - 例如 { status: 'x', color: 'y' }
*/
setInheritedProps(inheritedProps) {
const update = {}
Object.keys(inheritedProps).forEach(k => {
const inheritedKey = `wvInheritableInherited${capitalize(k)}`
update[inheritedKey] = inheritedProps[k]
})
this.setData(update)
},
/**
* 更新子组件的属性
* @param {string} relationName - 关系名称
* @param {string} propName - 属性名称
* @param {any} value - 属性值
*/
_updateChildrenProp(relationName, propName, value) {
const children = this.getChildrenNodes(relationName) || []
children.forEach(child => {
if (child && typeof child.setInheritedProps === 'function') {
const props = {}
props[propName] = value
child.setInheritedProps(props)
}
})
},
linkParentNode(relationName, parent) {
this.data._wvInheritableParentNodes[relationName] = parent
},
unlinkParentNode(relationName) {
delete this.data._wvInheritableParentNodes[relationName]
},
linkChildNode(relationName, child) {
if(!this.data._wvInheritableChildNodes[relationName]) {
this.data._wvInheritableChildNodes[relationName] = []
}
this.data._wvInheritableChildNodes[relationName].push(child)
// 当新子节点加入时,立即设置所有该关系的属性
this._syncAllPropsToChild(relationName, child)
},
unlinkChildNode(relationName, child) {
const childNodes = this.data._wvInheritableChildNodes[relationName] || []
const index = childNodes.indexOf(child)
if (index > -1) {
childNodes.splice(index, 1)
}
},
getParentNode(relationName) {
return this.data._wvInheritableParentNodes[relationName]
},
getChildrenNodes(relationName) {
return this.data._wvInheritableChildNodes[relationName] || []
},
queryChildNodeByAttrs(relationName, attrs) {
const children = this.getChildrenNodes(relationName)
return children.filter(child => {
return Object.keys(attrs).every(attr => {
// 支持多级嵌套的属性路径
const attrValue = this._getNestedProperty(child.data, attr);
return attrValue === attrs[attr];
});
});
},
queryNodeByAttrs(selector, attrs) {
const nodes = this.selectAllComponents(selector);
return nodes.find(node => {
return Object.keys(attrs).every(attr => {
const attrValue = this._getNestedProperty(node, attr);
return attrValue === attrs[attr];
});
});
},
/**
* 将所有当前子组件关系的属性同步到一个新子组件
* @param {string} relationName - 关系名称
* @param {Object} child - 子组件引用
*/
_syncAllPropsToChild(relationName, child) {
if (!child || !relationName || !config.childrenProperties || !config.childrenProperties[relationName]) {
return
}
const relationProps = config.childrenProperties[relationName]
const prefix = childrenPropsObjPrefix.hasOwnProperty(relationName) ?
childrenPropsObjPrefix[relationName] : relationName
const propsToSync = {}
Object.keys(relationProps).forEach(propName => {
let prefixedPropName
if (!prefix) { // 处理所有falsy值('', null, undefined等)
prefixedPropName = propName
} else {
prefixedPropName = `${prefix}${capitalize(propName)}`
}
if (this.data[prefixedPropName] !== undefined) {
propsToSync[propName] = this.data[prefixedPropName]
}
})
// 只有当有属性需要设置时才调用setInheritedProps
if (Object.keys(propsToSync).length > 0 && typeof child.setInheritedProps === 'function') {
child.setInheritedProps(propsToSync)
}
},
/**
* 获取对象的嵌套属性值
* @param {Object} obj - 要获取属性的对象
* @param {String} path - 属性路径,例如:"prop1.prop2.prop3"
* @return {any} 属性值,如果路径不存在则返回undefined
*/
_getNestedProperty(obj, path) {
// 如果obj为null或undefined,直接返回undefined
if (obj == null) {
return undefined;
}
// 分割路径字符串
const pathArray = path.split('.');
// 遍历路径数组
let result = obj;
for (let i = 0; i < pathArray.length; i++) {
result = result[pathArray[i]];
// 如果当前路径不存在,返回undefined
if (result === undefined) {
return undefined;
}
}
return result;
},
}
})
}
module.exports = {
WvInheritable
};