Props
本文章中所有的编译结果都只是假想的,只是逻辑上的,不代表真实的编译结果
定义与传递
tsx
function ChildComponent(props: {label: string, value: string}){
return (
<div>{props.label}:{props.value}</div>
)
}
function ParentComponent(){
return (
<div>
<h3>用户信息</h3>
<ChildComponent label="用户名" value="张三"/>
<ChildComponent label="年龄" value="22"/>
<ChildComponent label="性别" value="男"/>
</div>
)
}
属性的定义和传递就是如上面代码这样写就行。如果你用的是jsx,那子组件中props的定义必定是不用写的。
Props的响应式
把ParentComponent中的value修改为响应式数据,如下
tsx
import {createSignal} from "solid-js";
function ParentComponent(){
const [username, setUsername] = createSignal("张三");
setTimeout(() => {
setUsername("李四")
}, 1000 * 3);
return (
<div>
<h3>用户信息</h3>
<ChildComponent label="用户名" value={username()}/>
<ChildComponent label="年龄" value="22"/>
<ChildComponent label="性别" value="男"/>
</div>
)
}
上面这样写了之后,会发现经过3s后,用户名确实是从张三变成了李四。不过这里有个地方要注意的,那就是有些开发者可能会习惯性的在子组件中使用解构,就像下面这样写
tsx
function ChildComponent(props: {label: string, value: string}){
const {label, value} = props;
return (
<div>{label}:{value}</div>
)
}
使用这种写法,会发现经过了3s后,用户名仍然是张三。也就是解构之后再使用就不是响应式数据了,所以各位不要随意的解构。这是因为,props中的属性其实有可能是一个getter。就下下面这样的,
ts
{
label: "用户名", //因为这个是字面量,不需要响应式,所以不需要做成getter
get value() {
return username();
}
}
这里其实就会发现,value这个getter函数,其实就是衍生Signal,直接与上一篇文章说的对应上了。 当我们解构props的时候就相当于,就相当于创建组件时调用了这个衍生Signal,所以失去了响应式的效果
tsx
function ChildComponent(props: {label: string, value: string}){
const label = props.label;
const value = props.value;
return (
<div>{label}:{value}</div>
)
}
分离Props
在上面的子组件中,我们使用的就是div,有些时候我们希望子组件可以接收所有的div属性,然后除了label和value,把其它的都原样传递给,在React中我们可能使用解构的写法,如下
tsx
import { HTMLAttributes } from 'react'
function ChildComponent(props: {label: string, value: string} & HTMLAttributes<HTMLDivElement>){
const {label, value, ...other} = props;
return (
<div {...other}>{label}:{value}</div>
)
}
但是刚刚说过了,SolidJS不能这样解构,所以SolidJS通过了一个函数让我面来分离Props,所以就写出下面这样
tsx
import {JSX, splitProps} from "solid-js";
function ChildComponent(props: {label: string, value: string} & JSX.HTMLAttributes<HTMLDivElement>){
const [local, other] = splitProps(props, ["label", "value"]);
return (
<div {...other}>{local.label}:{local.value}</div>
)
}
基本用法就是,第一个参数传递的是要切分的props,固定这么写就行;第二个参数是一个数组,写的是要切分的属性名称。这样切分之后,local包含了数组中指明的属性,other就是剩下的。
默认Props
在开发过程中,我们写的组件经常会有默认值,也就是有很多属性我们可以不用填,假设上面的子组件value如果不填默认是"-",我们同样需要用到SolidJS提供函数mergeProps实现。修改后如下代码:
tsx
import {JSX, splitProps, mergeProps} from "solid-js";
function ChildComponent(props: {label: string, value?: string} & JSX.HTMLAttributes<HTMLDivElement>){
props = mergeProps({value: "-"}, props);
const [local, other] = splitProps(props, ["label", "value"]);
return (
<div {...other}>{local.label}:{local.value}</div>
)
}
生命周期
在开发过程中,其实经常会需要在组件挂载或者卸载时执行一些代码,所以SolidJS提供了对应生命周期。 两个函数分别是onMount和onCleanup。比如说,现在我需要监听窗口宽度的变化,然后改变我的界面。 这个时候就需要在挂载的时候,添加监听reszie事件,在卸载的时候取消监听。
tsx
import {Accessor, createSignal, onCleanup, onMount} from "solid-js";
export function useInnerWidth(): Accessor<number> {
const [innerWidth, setInnerWidth] = createSignal(window.innerWidth)
const onResize = () => {
setInnerWidth(window.innerWidth)
}
onMount(() => window.addEventListener("resize", onResize))
onCleanup(() => window.removeEventListener("resize", onResize))
return innerWidth
}
上面这个代码就是创建了一个Signal,然后添加了监听事件。使用它也是非常的简单的,
tsx
function CurrentInnerWidth(){
const innerWidth = useInnerWidth();
return(
<h3>当前窗口宽度:{innerWidth()}px</h3>
)
}
一些需要请求后端数据的,也可以使用类似useInnerWidth函数的的写法,只是不需要onCleanup,不过这里我就不给大家演示。
最后
今天就先写到这里了,对于自定义组件来说,props是经常用到的。props是存在一点问题的,特别是一定一定要注意不要随意解构,要使用SolidJS提供的函数来实现。