学习《SolidJS》(三)自定义组件

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提供的函数来实现。

相关推荐
牛马111几秒前
Flutter Web性能优化标签解析
前端·flutter·性能优化
Bigger几秒前
Tauri (25)——消除搜索列表默认选中的 UI 闪动
前端·react.js·weui
李少兄12 分钟前
简单讲讲 SVG:前端开发中的矢量图形
前端·svg
前端小万13 分钟前
告别 CJS 库加载兼容坑
前端·前端工程化
恋猫de小郭13 分钟前
Flutter 3.38.1 之后,因为某些框架低级错误导致提交 Store 被拒
android·前端·flutter
JarvanMo17 分钟前
Flutter 需要 Hooks 吗?
前端
光影少年28 分钟前
前端如何虚拟列表优化?
前端·react native·react.js
Moment29 分钟前
一杯茶时间带你基于 Yjs 和 reactflow 构建协同流程图编辑器 😍😍😍
前端·后端·面试
invicinble1 小时前
对于前端数据的生命周期的认识
前端
PieroPc1 小时前
用FastAPI 后端 和 HTML/CSS/JavaScript 前端写一个博客系统 例
前端·html·fastapi