Vue 提供一个h()函数用于创建vnodes,用于渲染网页。
JSX是JS的一个类似XML的扩展,与h()函数类似,也是可以创建vnodes,用于渲染网页。
1 渲染函数 & JSX
1.1 渲染函数
组合式API,setup()钩子的返回值是用于暴露数据给模版,使用渲染函数时,可以直接把渲染函数返回,确保setup 返回是一个函数而不是一个值。这个函数的返回值可以是一个字符串、单个h(),或者有h()构成的数组。
setup(props, { slots,emit }) {return () => 'hello'
}return () => h('div','hello')
return () => [h('div','hello'),h('span','js')]
1.2 JSX
可以通过以下方式定义vnode:
const vnode = <div>hello</div>
在JSX表达式中,使用大括号来嵌套动态值:
export default defineComponent({setup(props,{ emit,slots }) {const count = ref(0)return () => <div><input value={props.modelValue} onInput={(event) => emit('update:modelValue',event.target?.value)}/></div>}
})
使用事件修饰符,可以使用withModifiers(事件处理器,[修饰符])函数。
使用自定义指令时,可以使用withDirectives(vnode,[[自定义指定]])。
// <div v-show-element:top.animate="200">hello</div>
withDirectives(h('div','hello'),[[showElement,200,'top', { animate: true }]])// <div @onClick.self='() => {}'>hello</div>
h('div', {onClick: withModifiers(() => {}, ['self'])
},'hello')
1.2.1 插槽
插槽可以通过setup(props,{ slots })的上下文来访问。每个slots对象中的插槽都是一个返回vnodes数组的函数。
传递插槽需要传递一个插槽函数或者一个包含插槽函数的对象。插槽函数的返回值同一个正常渲染函数的返回值一样。
作用域插槽,在子组件通过slots对象放回插槽的函数传递具体的值,父组件则在定义插槽函数时,使用这个函数的变量来获取从子组件传递过来的值。
// JSXView.tsx 子组件
export default defineComponent({setup(props,{ emit,slots }) {const count = ref(0)return () => <div><h2>JSXView</h2>{ slots && slots.default ? <div><h3>default</h3><div>{ slots.default() }</div></div> : ''}{slots && slots.footer ? <div><h3>footer</h3><div>{ slots.footer(count) }</div></div> : ''}</div>}
})// 父组件
export default defineComponent({setup(props, ctx) {const text = ref('haha')return () => withDirectives((<div><JSXView>{{default: () => <h3>jsx Show Default: { text.value }</h3>,footer: (count: Ref<number>) => <h3 onClick={ () => count.value++ }>jsx Show Footer { count.value }</h3>}}</JSXView></div>),[[showElement]])}
})
// 子组件
export default {setup(props, { slots,emit }) {const count = ref(0)return () => h('div',[h('h2','VNodeView'),slots && slots.default ? h('div',[h('h3','default'),h('div',slots.default())]) : '',slots && slots.footer ? h('div',[h('h3',"footer"),h('div','传递给父组件的值:' + count.value),h('div',slots?.footer(count))]) : ''])},
}// 父组件
export default {setup(props, ctx) {const text = ref('hi')return () => withDirectives(h(VNodeView,null,{default: () => h('h3','vNode Show Default,值' + text.value),footer: (count:Ref<number>) => h('h3',{onClick: () => { count.value++ }},'vNode Show Footer,count:' + count.value),}),[[showElement]])},
}
1.2.2 v-model
v-model 指令扩展为modelValue 和onUpdate:modelValue,在模版编译过程中,我们需要提供这些props。
// 子组件
export default defineComponent({props: ['modelValue'],emits: ['update:modelValue'],setup(props,{ emit,slots }) {const count = ref(0)return () => <div><input value={props.modelValue} onInput={(event) => emit('update:modelValue',event.target?.value)}/></div>}
})// 父组件
export default defineComponent({setup(props, ctx) {const text = ref('haha')return () => <div><JSXView onUpdate:modelValue={(val) => text.value = val} modelValue={ text.value } /></div>}
})
1.3 函数式组件
函数式组件是一种定义自身没有任何状态的组件方式。是一个纯函数,接收props及上下文(与setup()钩子相同),返回vnodes。
props和emits通过为函数添加对应的属性来声明它们:
function MyComponent(props) {}
MyComponent.props = [‘length’]
MyComoonetn.emits = [‘click’]
如果这个props选项没有被定义,那么被传入函数的props对象就会像attrs一样包含所有attribute。
export default function FunctionView(props,{ slots, emit, attrs }) {return <div>Hello FunctionView</div>
}