UNPKG

veui

Version:

Baidu Enterprise UI for Vue.js.

101 lines (92 loc) 2.84 kB
/** * 用户直接使用 Provider, 接受一个 value prop 定义 context * 采用 Provider(functional) + ProviderImpl 方式实现的原因: * 1. 支持给多个子节点传递 context : <Provider><a/><b/></Provider> * 2. functional 组件定义 provide 无效(inject 有效) */ import { uniqueId, isPlainObject, defaults } from 'lodash' const CommonProviderImpl = { name: 'veui-provider', // for better readability in devtools uiTypes: ['transparent'], props: { // eslint-disable-next-line vue/require-prop-types value: {} }, render () { return this.$slots.default } } export function createContext (name, defaultValue) { const realName = name ? `${name}-provider` : CommonProviderImpl.name let contextId = `__${uniqueId(realName)}` let RealProviderImpl = { ...CommonProviderImpl, name: realName, inject: { [contextId]: { from: contextId, default: () => () => undefined } }, provide () { return { [contextId]: () => { let parentContextValue = this[contextId]() if ( isPlainObject(parentContextValue) && isPlainObject(this.value) // provide 的值时函数,最后在消费方的 computed 调用,这样保证最终值依赖每个 provider 的 this.value ) { return defaults({}, this.value, parentContextValue) // 每层 provider 都会和上一层的值合并 } // 无法合并,则以最近的 provider 为准 return this.value } } } } let Provider = { functional: true, // 这里实际上接受一个 value prop,用来传递 context,但是因为直接透传给 ProviderImpl,所以不用声明了 render: (h, context) => wrapChildren(h, context, RealProviderImpl) } let useConsumer = (injectionKey) => { return { inject: { [contextId]: { from: contextId, default: () => () => undefined } }, computed: { [injectionKey] () { if (defaultValue) { defaultValue = typeof defaultValue === 'function' ? defaultValue() : defaultValue // 消费方获取 context 值时和初始值做合并 return defaults({}, this[contextId](), defaultValue) } return this[contextId]() } } } } let Consumer = { ...useConsumer('context'), render () { return this.$scopedSlots.default(this.context) } } return { Provider, Consumer, useConsumer } } function wrapChildren (h, { data, children }, Provider) { const value = data.attrs ? data.attrs.value : undefined return children.map((child) => h( Provider, { attrs: { value } // 分开避免相互影响 }, [child] ) ) }