组件化
组件声明
组件声明可以是一个函数或是一个类,当组件为类时,必须要有 render 方法返回 JSX。
// 函数组件
function Comp() {
return <div>hello</div>
}
// 类组件
class Comp {
render() {
return <div>hello</div>
}
}
响应式数据绑定
响应式数据一律以函数的形式返回,否则数据无法更新。
因为 plvue 在实现响应式数据时,并没有虚拟 DOM 的参与,用箭头函数将数据返回是最简单直接的,也是最高效的。
import { ref } from 'pl-vue';
function Comp() {
const count = ref(0);
return <div>
<h1>{() => count.value}</h1>
<button onclick={() => count.value++}></button>
</div>
}
props 传递
- 原则上响应式必须确保为一个函数,并确保内部使用了响应式数据。所以,props 响应式数据也需要传递为函数,对应的节点才会重新渲染;
- 在组件之间数据传递上,如果传给子组件的为响应式数据(代理对象),即子组件收到为响应式数据;如果传递给子组件为引用值对象,即子组件也可修改该对象。PlVue 并无做过多干预,一切符合 JS 规则。
import { ref, PropsType } from 'pl-vue';
// 父组件
function App() {
const count = ref(0);
return <div>
<Comp>插槽</Comp>
</div>
}
// 子组件
type Props = PropsType<{
count: () => number
}>
function Comp(props: Props) {
return <div>
<h1>{props.count}</h1>
{props.children}
</div>
}
双向数据绑定
该库并未实现双向数据绑定,而是通过 ref 来实现双向数据流。
import { PropsType, h, reactive, toRef } from "~/core";
function App() {
const form = reactive({
name: 0,
})
return <div>
<Input model={toRef(form, 'name')} />
<button onclick={() => form.name ++}>click</button>
</div>
}
type Props = PropsType<{
model: { value: number } // 传递一个 ref,方便子组件做修改
}>
function Input(props: Props) {
return <input value={() => props.model.value} oninput={e => props.model.value = e.target.value} />
}
暴露组件方法
import { ref, defineExpose } from 'pl-vue';
function App() {
const compRef = ref();
return <div>
<Comp ref={compRef} />
<button onclick={() => compRef.value.addCount()}>click</button>
</div>
}
function Comp() {
const count = ref(0);
function addCount() {
count.value++;
}
defineExpose({ addCount });
return <h1>{() => count.value}</h1>
}
生命周期钩子
onBeforeMount
在组件挂载前执行,也就是说通过 DOM 选择器还访问不到 DOM 节点,但可以通过 ref 来修改节点信息。
import { onBeforeMount, ref } from 'pl-vue';
function App(props) {
const elRef = ref<HTMLElement>();
onBeforeMount(() => {
console.log(document.getElementById('demo')); //--> null
console.log(elRef.value); //--> <div id="demo">hello</div>
});
return <div id="demo" ref={elRef}>hello</div>
}
onMounted
组件挂载后执行。此时可以通过 DOM 选择器访问到 DOM 节点。
import { onMounted } from 'pl-vue';
function App(props) {
onMounted(() => {
console.log(document.getElementById('demo')); //--> <div id="demo">hello</div>
});
return <div id="demo">hello</div>
}
onBeforeUnmount
组件卸载前执行。此时还能通过 DOM 选择器访问到 DOM 节点。
import { onBeforeUnmount } from 'pl-vue';
function App(props) {
onBeforeUnmount(() => {
console.log(document.getElementById('demo')); //--> <div id="demo">hello</div>
});
return <div id="demo">hello</div>
}
onUnmounted
组件卸载后执行。此时通过 DOM 选择器已经访问不到 DOM 节点,因为整个组件节点已经被移除掉。
import { onUnmounted } from 'pl-vue';
function App() {
onUnmounted(() => {
console.log(document.getElementById('demo')); //--> null
});
return <div id="demo">hello</div>
}
高阶组件
高阶组件作用
- 减少重复代码;
- 代码逻辑复用;
- 强化组件;
- 修改组件行为;
function HignOrderComp(props) {
props.text = props.text.trim();
return <Comp {...props} />
}
function Page() {
return <div>
<HignOrderComp text=' hello ' />
</div>
}
function Comp(props) {
return <div>{props.text}</div>
}
类组件
设计类组件的目的:为了暴露一些方法用来继承/重写组件的逻辑。
class Comp {
count = ref(0);
render = () => { // render 函数为渲染界面的主函数
return <div>{() => this.count.value}</div>
}
}
class NewComp extends Comp {
// 重写某些方法
}
function Page() {
return <NewComp />
}
全局组件
全局组件必须通过
createApp来创建应用
import { createApp } from 'pl-vue';
function Comp(props) {
return <div>hello {props.text}</div>
}
function App() {
return <div>
<my-comp text='world' />
</div>
}
const app = createApp();
app.component("my-comp", Comp);
const root = document.getElementById('root');
root.appendChild(app.render(<App />));