-
Notifications
You must be signed in to change notification settings - Fork 487
TDesign 中 CompositionAPI 与 TNode 的结合
PY edited this page Jun 19, 2022
·
2 revisions
首先了解一下 TDesign
中 TNode
的概念, 什么是TNode。在 TDesign
中, 与 TNode
相关的是 renderTNodeJSX
,在 compositionAPI
改造过程中增加了 useTNodeJSX
。
该函数在 optionsAPI
中使用,与组件相关的上下文会存在于 this
对象,函数参数需要传入 this
, TNode name
和拓展参数。
渲染逻辑优先级 function props
> (string | number | boolean) props
> slots
const renderTNodeJSX = (instance: ComponentPublicInstance, name: string, options?: OptionsType) => {
// assemble params && defaultNode
const params = getParams(options);
const defaultNode = getDefaultNode(options);
// 处理 props 类型的Node
let propsNode;
if (Object.keys(instance).includes(name)) {
propsNode = instance[name];
}
// 同名插槽和属性同时存在,则提醒用户只需要选择一种方式即可
if (instance.$slots[name] && propsNode && propsNode !== true) {
console.warn(`Both $scopedSlots.${name} and $props.${name} exist, $props.${name} is preferred`);
}
// propsNode 为 false 不渲染
if (propsNode === false) return;
if (propsNode === true && defaultNode) {
return instance.$slots[name]?.(params) ? instance.$slots[name]?.(params) : defaultNode;
}
// 同名 props 和 slot 优先处理 props
if (isFunction(propsNode)) return propsNode(h, params);
const isPropsEmpty = [undefined, params, ''].includes(propsNode);
// Props 为空,但插槽存在
if (isPropsEmpty && (instance.$slots[camelCase(name)] || instance.$slots[kebabCase(name)])) {
return handleSlots(instance, params, name);
}
return propsNode;
};
import { renderTNodeJSX } from '../utils/render-tnode';
defineComponent({
methods: {
renderChild() {
renderTNodeJSX(this, 'default')
}
},
render() {
<div>
{this.renderChild()}
{renderTNodeJSX(this, 'TNodeName', options)}
</div>
}
})
本质上这是一个 utils
, 不是传统意义上的 hook
。
以下为初版的 useTNodeJSX
实现, 只能用在 setup
当中,如在 render
函数中使用的话,可使用 renderTNodeJSX
。
const useTNodeJSX = (name: string, options?: OptionsType) => {
// 组装 params 和 defaultNode
const params = getParams(options);
const defaultNode = getDefaultNode(options);
const instance = getCurrentInstance();
// 处理 props 类型的 TNode
let propsNode;
if (Object.keys(instance).includes(name)) {
propsNode = instance[name];
}
// propsNode 为 false 不渲染
if (propsNode === false) return;
// 同名function和slot优先处理插槽
if (instance.slots[name]) {
return instance.slots[name](params);
}
if (isFunction(propsNode)) return propsNode(h, params);
// propsNode为true则渲染defaultNode
if (propsNode === true && defaultNode) {
return defaultNode;
}
// 处理字符串类型的 propsNode
return propsNode;
};
改造后利用闭包,在 render
函数中也可以在执行 TNode
渲染函数的时候能够访问到组件实例。并可以复用 renderTNodeJSX
母方法.
import { renderTNodeJSX } form '../utils/render-tnode';
const useTNodeJSX = () => {
const instance = getCurrentInstance();
return function (name: string, options?: OptionsType) {
// assemble params && defaultNode
const params = getParams(options);
const defaultNode = getDefaultNode(options);
// 处理 props 类型的Node
let propsNode;
if (Object.keys(instance.props).includes(name)) {
propsNode = instance.props[name];
}
const slotNode = instance.slots[name];
// 同名插槽和属性同时存在,则提醒用户只需要选择一种方式即可
if (slotNode && propsNode && propsNode !== true) {
log.warn('', `Both slots.${name} and props.${name} exist, props.${name} is preferred`);
}
// propsNode 为 false 不渲染
if (propsNode === false) return;
if (propsNode === true) {
return instance.slots[name]?.(params) || defaultNode;
}
// 同名 props 和 slot 优先处理 props
if (isFunction(propsNode)) return propsNode(h, params);
const isPropsEmpty = [undefined, params, ''].includes(propsNode);
if (isPropsEmpty && (instance.slots[camelCase(name)] || instance.slots[kebabCase(name)])) {
return handleSlots(instance, name, params);
}
return propsNode;
};
};
import { useTNodeJSX } from '../hooks/tnode';
defineComponent({
setup() {
const renderTNode = useTNodeJSX();
const renderChild = () => {
return renderTNode('default')
}
return {
renderTNode,
renderChild
}
},
render() {
<div>
{this.renderTNode('TNodeName', options)}
{this.renderChild()}
</div>
}
})