组件
J20 的组件和 React 的组件类似,如果你熟悉 React,那么你可以快速地上手。
tsx
const App = () => {
const $msg = "hello world";
return <span>{$msg}</span>;
};组件状态
J20 的组件状态由信号驱动,因为 J20 创新的编译手段,你可以像普通变量一样无感使用信号。
声明信号
使用 let 关键字 + $ 前缀符号来声明一个信号。
信号的值可变,直接赋值可以更新视图。
tsx
const App = () => {
let $msg = "hello world";
const onClick = () => {
$msg = $msg + " j20";
};
return <span onClick={onClick}>{$msg}</span>;
};派生信号
使用 const 和 $ 前缀符号来声明一个派生信号,派生信号的值不可变,只读。
tsx
const App = () => {
let $msg = "hello world";
let $count = 1;
// 字符串拼接
const $displayMsg = "display: " + $msg;
// 数学计算
const $double = $count * 2;
const $isEven = $count % 2 === 0;
const onClick = () => {
$msg = $msg + " j20";
$count++;
};
return (
<span onClick={onClick}>
{$displayMsg} - Count: {$double} (Even: {$isEven.toString()})
</span>
);
};注:信号变量不能使用
$use开头,$use是自定义 hooks 的前缀,它拥有特殊的编译策略。
自定义 hooks
自定义 hooks 以 $use 为前缀,支持解构,解构变量以 $ 开头可以保持响应。
tsx
const $useCount = () => {
let $count = 0;
return {
count: $count,
};
};
const { count: $count } = $useCount();
console.log($count);
const $res = $useCount();
console.log($res.count);疑问:自定义 hooks 可不可以直接返回 { $count }
tsx
const $useCount = () => {
let $count = 0;
return {
$count, // $ 开头直接作为属性名,需要谨慎使用,容易和信号混淆。
};
};
const { $count } = $useCount();
// 干净的解构,方便使用
console.log($count);
// 错误实践
// `$res` 是信号
const $res = $useCount();
// `.$count` 只是 `$res` 的一个属性(非信号),却带着 $ 前缀,容易混淆。
console.log($res.$count);这么写会比较方便书写和解构,但是务必不要滥用,保持干净的解构使用,我们希望 $ 前缀的含义没有歧义。
注:解构声明的变量名必须是 $ 开头,如果不是 $ 开头,则需要设置 $ 前缀的别名,否则响应丢失。
具体可以了解响应链传递。
组件 Props
组件入参是一个派生信号,所以变量名必须以 $ 开头
tsx
function Msg($props: { name: string }) {
return <span>{$props.name}</span>;
}
function App() {
let $name = "hello";
return <Msg name={$name} />;
}如果
<Msg name="hello" />name 是个静态值,那么组件内的$name信号的值就永远是"hello"。
Props 解构
解构的字段变量名以 $ 开头才能保持响应性,是只读的派生信号。
tsx
function Msg({ name: $name }: { name: string }) {
return <span>{$name}</span>;
}
// 错误用法
// 如果函数入参名或者解构后的变量名没有 `$` 开头的变量,那么这个函数的入参**不会被编译**
// 意味着函数的入参是原始值,外部传入什么,函数内部就会拿到什么,可能会造成类型和实际值不一致的情况。
function Msg({ name }: { name: string }) {
return <span>{name}</span>;
}组件插槽
J20 的插槽的最佳实践是封装成 render 函数,除非是静态的插槽(类似 Web Component 的 slot),凡是需要复用的插槽,都应封装成 render 函数。
tsx
const App = ($props: {
header: JSX.Element,
option: () => JSX.Element,
}) => {
let $visible = false;
return (
<div>
<div class="header">
{$props.header}
</div>
<div>
<If of={$visible} else ={<div>{$props.option(false)}</div>}>
{$props.option()}
</If>
</div>
</div>
);
};
<App
header={<h1>header</h1>}
some={(visible) => <div>{visible ? "none" : "some"}</div>}
/>