createSignal
当改变值要重新渲染界面,可以使用 createSignal API;
当点击的时候调用 setA 函数改变 a 的值,界面上也会更新。
import { createSignal } from "solid-js";
const test1 = () => {
const [a, setA] = createSignal(1);
const handleClick = (data)=>{
setA(data)
}
const handleClick2 = ()=>{
setA(a()+1)
}
// 只执行一遍
console.log(0,'render')
return (
<div>
<xy-button onClick={() => handleClick(a() + 1)}>{a()}</xy-button>
<xy-button onClick={() => handleClick2()}>{a()}</xy-button>
{/* onClick 的内容不会随之按钮的点击而变化,所以连续点击不会连续增加 */}
<xy-button onClick={[handleClick,a()+1]}>{ a() }</xy-button>
</div>
);
};
可以设置第二个参数;
当设置 { equals: false } 时,就会强制更新。
每次点击按钮 调用 setA 时会被 createEffect 监听到依赖项变化,每次都打印 1, 监听a的值:a =xxx
equals 值可以是false,一个返回布尔值的函数,如 (newVal, oldVal) => newVal.length === oldVal.length。当长度不相等返回 false 响应更新。
const test2 = () => {
const [a, setA] = createSignal(1, { equals: false });
createEffect(() => console.log(1, "监听a的值:a =", a()));
const handleClick = () => {
setA(a());
};
// 只执行一遍
console.log(0, "render");
return (
<div>
<xy-button onClick={() => handleClick()}>{a()}</xy-button>
</div>
);
};
监听对象
const test3 = () => {
const [a, setA] = createSignal({ b: 1 });
createEffect(() => console.log(1, "监听a的值:a =", a()));
const handleClick = () => {
setA({ b: a().b + 1 });
};
// 只执行一遍
console.log(0, "render");
return (
<div>
<xy-button onClick={() => handleClick()}>{a().b}</xy-button>
</div>
);
};
设置为一个函数
const test4 = () => {
const [a, setA] = createSignal({ b: 1 });
createEffect(() => console.log(1, "监听a的值:a =", a()));
const handleClick = () => {
setA((current)=>{
current.b+=1;
// 直接返回 current 会判断是一个值,不会响应依赖更新,因为 current 是引用类型
// 可以返回一个新对象,或者 设置{ equals: false }
// return current
return {...current}
});
};
// 只执行一遍
console.log(0, "render");
return (
<div>
<xy-button onClick={() => handleClick()}>{a().b}</xy-button>
</div>
);
};
createEffect
在依赖项发生变化时运行的方法
import { createSignal, createEffect } from "solid-js";
const test1 = () => {
const [a, setA] = createSignal(2);
createEffect(() => console.log(1, "监听a的值:a =", a()));
const handleClick = () => {
setA(a()+1);
};
// 只执行一遍
console.log(0, "render");
return (
<div>
<xy-button onClick={() => handleClick()}>{a()}</xy-button>
</div>
);
};
export default test1;
除了回调函数可以传一个参数
// 俩兔子,兔子寿命1年,生一窝11个,不考虑人类社会道德因素,计算 a 年有多少兔子
const test2 = () => {
const [a, setA] = createSignal(1);
createEffect(([preSum,dieNum]) => {
var newBorn = Math.floor(preSum/2)*11
const currentSum = preSum + newBorn - dieNum;
console.log(1,`第${a()}年数量:${currentSum},前一年数量:${preSum},今年新生:${newBorn},今年死亡:${dieNum}`)
return [currentSum,preSum];
}, [2,0]);
const handleClick = () => {
setA(a()+1);
};
// 只执行一遍
console.log(0, "render");
return (
<div>
<xy-button onClick={() => handleClick()}>{a()}</xy-button>
</div>
);
};
如果要等待createEffect里的回调函数第一次执行发生一些什么,最简单的是用 setTimeout ,还有 Promise,queueMicrotask
const test3 = () => {
// 0 'render'
// 1 'a= ' 1
// 2 'Promise'
// 3 'microtask'
// 2 'a= ' 3
// 4 'goodbye'
const [a, setA] = createSignal(1);
Promise.resolve(null).then(()=>{
console.log(2,'Promise')
})
self.queueMicrotask(() => {
// 现在将打印 `a = 1`
console.log(3,"microtask");
setA(3); // 立即打印 `a = 3`
console.log(4,"goodbye");
});
createEffect(() => {
console.log(1,"a= ",a())
});
const handleClick = () => {
setA(a()+1);
};
// 只执行一遍
console.log(0, "render");
return (
<div>
<xy-button onClick={() => handleClick()}>{a()}</xy-button>
</div>
);
};
清空副作用中定义的定时器
const test4 = () => {
const [a, setA] = createSignal(1);
createEffect(() => {
var t = setInterval(() => {
console.log("setInterval");
}, 800);
// onCleanup 是 SolidJS 中的一个钩子函数,它在组件卸载(unmount)之前调用。它常用来进行清理和收尾工作,例如释放资源、取消定时器、删除事件监听等等。
onCleanup(() => {
window.clearInterval(t);
});
});
const handleClick = () => {
setA(a() + 1);
};
// 只执行一遍
console.log(0, "render");
return (
<div>
<xy-button onClick={() => handleClick()}>{a()}</xy-button>
</div>
);
};
createEffect
依赖变化时计算并返回,相当于vue中的computed,如果square的那个函数不用createMemo包裹就会每次都能检测square是变化的,用了createMemo只有当返回值不一样createEffect才检测到,这和vue中用computed和用method的区别一样的。
memo 函数不应该通过调用 setter 来改变其他 signal(它应该是“纯的”)。这使得 Solid 能够根据其依赖关系图优化 memo 更新的执行顺序,以便所有 memo 最多可以更新一次以响应依赖关系的变化。
import { createSignal, createEffect, createMemo } from "solid-js";
const test1 = () => {
const [a, setA] = createSignal(2);
const square = createMemo(() => {
return a()*a()
});
createEffect(() => console.log(1, "监听square的值:square =", square()));
const handleClick = () => {
setA(a() + 1);
};
// 只执行一遍
console.log(0, "render");
return (
<div>
<xy-button onClick={() => handleClick()}>{square}</xy-button>
</div>
);
};
export default test1;
接受初始值和上一次的值
// 计算等差数列之和
const test2 = () => {
const [a, setA] = createSignal(1);
const s = createMemo((pre) => {
return pre+a()
},0);
const handleClick = () => {
setA(a() + 1);
};
// 只执行一遍
console.log(0, "render");
return (
<div>
<xy-button onClick={() => handleClick()}>{s}</xy-button>
</div>
);
};
可以传第三个参数,它的结构是这样的,意思和createSignal的意思差不多。
options?: { equals?: false | ((prev: T, next: T) => boolean) }
createResource
用于触发请求,两种用法
const [data, { mutate, refetch }] = createResource(fetchData);
const [data, { mutate, refetch }] = createResource(sourceSignal, fetchData);
import {
createSignal,
createEffect,
createMemo,
createResource,
} from "solid-js";
// 对象转为查询字符串
const obj2queryStr = (obj = {}) => {
var str = "";
for (const k in obj) {
if (Object.hasOwnProperty.call(obj, k)) {
str += `${k}=${obj[k]}`;
}
}
return str ? `?${str}` : "";
};
// 一个请求方法
const getList = async (params, { value, refetching }) => {
const res = await fetch(
`/node_modules/.vite/deps/solid-js.js${obj2queryStr(
Object.assign({}, params, refetching)
)}`
).then((res) => res.text());
// `value` 告诉你 fetcher 的最后一个返回值。如果使用 refetch 函数触发了 fetcher,则 refetching 为 true,也可以是refetch传的数据
console.log({ value, refetching, params, res });
return res;
};
const test1 = () => {
// 表单数据
const [formData, setFormData] = createSignal({
v: "52fe8771",
});
// 请求参数: 对表单数据进行处理得到
const queryData = createMemo(() => {
let data = formData();
data.v = data.v.trim();
return data;
});
// 发起请求: 每当 queryData 的值发生变化时,都会再次调用getList,并且该值将始终作为第一个参数传递给 getList。
const [listData, { mutate, refetch }] = createResource(queryData, getList);
// 设置 listData 的值: 可以调用 mutate 来直接更新 data signal(它的工作方式与任何其他 signal setter 一样)。
mutate(() => {
// 这可以设置 listData 的初始值
return "listData的初始值";
});
// 监听到返回值获取loading变化来了做点什么
createEffect(() => {
console.log(listData.loading, "loading");
console.log(listData(), "data");
console.log(listData.error, "err"); // undefined
});
// 点击按钮重新请求
const handleClick = () => {
refetch({ timeStamp: new Date().getTime() });
};
return (
<div>
<xy-button onClick={() => handleClick()}>refetch</xy-button>
</div>
);
};
export default test1;
onMount
import { onMount } from "solid-js";
const test1 = () => {
let myDiv;
onMount(() => {
console.log(myDiv);
});
return (
<div>
<div ref={myDiv}>myDiv</div>
</div>
);
};
export default test1;
onCleanup
import { onCleanup } from "solid-js";
const test1 = () => {
let timer = setInterval(()=>{
console.log(1)
},1000)
// 注册一个清理方法,该方法在当前反应范围的清除或重新计算时执行。可以在任何组件或 Effect 中使用
// 可以清理定时器,解除事件绑定等。
onCleanup(() => {
window.clearInterval(timer)
});
return (
<div>
</div>
);
};
export default test1;
onError
import { onError } from "solid-js";
const MyComponent = ()=>{
// 可以被捕获
throw new Error("MyComponent Error");
}
const MyComponent2 = ()=>{
onError((err) => {
console.log("MyComponent2 err is:");
console.log(err);
});
// 可以就地被捕获
throw new Error("MyComponent2 Error");
}
const MyComponent3 = ()=>{
onError((err) => {
// 不可以被捕获
throw err
});
// 可以就地被捕获
throw new Error("MyComponent2 Error");
}
const test1 = () => {
onError((err) => {
console.log("err is:");
console.log(err);
});
// 可以被捕获
// throw new Error("throw new Error");
// setTimeout(()=>{
// // 不可以被捕获
// throw new Error("setTimeout Error");
// },1000)
// 不可以被捕获
// Promise.reject("Promise.reject")
// setTimeout(() => {
// onError((err) => {
// console.log("err is:");
// console.log(err);
// });
// // 不可以被捕获
// throw new Error("setTimeout Error");
// }, 1000);
console.log("render");
return (
<div>
<MyComponent />
<MyComponent2 />
<MyComponent3 />
------
{a()}
------
</div>
);
};
export default test1;
untrack
停止跟踪依赖变化
这样是不行的,界面还会更新。
import { createSignal, untrack } from "solid-js";
const test1 = () => {
const [a, setA] = createSignal(1);
const t = setInterval(() => {
if (a() >= 5) {
window.clearInterval(t);
untrack(a);
return;
}
setA(a() + 1);
}, 1000);
setTime
return <div>尝试连接:{a()}次</div>;
};
export default test1;
正确的用法
import { createSignal, createEffect,untrack } from "solid-js";
const test1 = () => {
const [a, setA] = createSignal(0);
untrack(a);
createEffect(() => {
// 只执行一次了
console.log(untrack(a));
});
setA(a() + 100);
setInterval(()=>{
setA(a() + 100);
},500)
// 界面还是在更新
return <div>{a()}</div>;
};
export default test1;
batch
在块内保持执行下游计算直到结束,以防止不必要的重新计算。Solid Store 的 set 方法、Mutable Store 的数组方法和 effect 会自动将它们的代码打包成批处理。
import { createSignal, createEffect,batch } from "solid-js";
const test1 = () => {
const [a, setA] = createSignal(0);
const [b, setB] = createSignal(0);
batch(a);
createEffect(() => {
// 正常下会这执行3遍:初始一遍,setTimeout 设置了两次执行两遍
// 用了 batch 就只执行一遍
console.log(a(),b());
});
setA(a() + 1);
setB(b() + 1);
setTimeout(batch(()=>{
console.log('执行一遍')
setA(a() + 1);
setB(b() + 1);
}),100)
// setTimeout(()=>{
// console.log('执行一遍')
// setA(a() + 1);
// setB(b() + 1);
// },100)
// 界面还是在更新
return <div>{a()}</div>;
};
export default test1;
on
import { createSignal, createEffect, on } from "solid-js";
const test1 = () => {
const [a, setA] = createSignal("a");
const [b, setB] = createSignal("b");
// 只监听 a
// 等同于:
// createEffect(() => {
// const v = a();
// untrack(() => console.log(v, b()));
// });
createEffect(on(a, (v) => console.log(v, b()))); // a b
setInterval(() => {
setB(b() + 100);
}, 500);
return <div>{a()}</div>;
};
const test2 = () => {
const [a, setA] = createSignal("a");
// 避免首次执行
createEffect(on(a, (v) => console.log(v), { defer: true }));
setA(a() + 100);
setTimeout(() => {
setA(a() + 100);
}, 100);
console.log("render");
return <div>{a()}</div>;
};
export default test2;
createRoot
当父组件更新,createRoot 里面的状态不会更新。这可以做缓存。
所有 Solid 代码都应被 createRoot 包裹,因为它们确保释放所有内存/计算,render 中使用了 createRoot,我们就不用手写一个了,大部分情况下不用。
import { createSignal, createEffect, createRoot } from "solid-js";
(() => {
const [a, setA] = createSignal(0);
createEffect(() => {
console.log("a effect01 =", a());
});
createEffect(() => {
console.log("a effect02 =", a());
});
// count effect01 = 0
// count effect02 = 0
// count effect01 = 1
// count effect02 = 1
setA(1);
return <div>{a()}</div>;
})();
createRoot(() => {
const [a, setA] = createSignal(0);
createEffect(() => {
console.log("a effect01 =", a());
});
createEffect(() => {
console.log("a effect02 =", a());
});
// count effect01 = 1
// count effect02 = 1
setA(1);
return <div>{a()}</div>;
})
const test1 = () => {
return <div></div>;
};
export default test1;
getOwner
import { createSignal, createEffect,getOwner } from "solid-js";
const test1 = () => {
const [a, setA] = createSignal(0);
createEffect(() => {
console.log();
console.log(getOwner());
});
setA(a() + 1);
// 获取拥有当前运行代码的响应范围
const owner = getOwner()
console.log(owner)
console.log(owner.componentName) // "test1"
console.log(owner.value) // div
console.log(owner.props) // {}
console.log(owner.name) // "c-1-1-1"
return <div>{a()}</div>;
};
export default test1;
runWithOwner
应该就是提供一个所有者,执行一些所有者下才执行的动作。
在所有者者范围内才能使用 useContext;和清理 createEffect 监听。
import {
createSignal,
createEffect,
getOwner,
runWithOwner,
useContext,
createContext,
} from "solid-js";
let owner = null;
let Ctx = null
setTimeout(() => {
// 此回调在没有所有者的情况下运行。
// 通过 runWithOwner 恢复所有者:
runWithOwner(owner, () => {
console.log(Ctx,'Ctx')
const foo = useContext(Ctx);
createEffect(() => {
console.log(foo,'foo');
});
});
}, 2000);
const MyComponent = () => {
const [state] = useContext(Ctx);
return <div>{JSON.stringify(state)}</div>;
};
const test1 = () => {
Ctx = createContext([{ count: 0 }, {}]);
// 获取拥有当前运行代码的响应范围
owner = getOwner();
console.log(owner,'owner');
return (
<Ctx.Provider value={[{ count1: 0 }]}>
<MyComponent />
</Ctx.Provider>
);
};
export default test1;
mergeProps
import { createSignal, createEffect, mergeProps } from "solid-js";
const MyComponents = (props) => {
// 默认 props
props = mergeProps({ name: "Smith" }, props);
// 克隆 props (克隆的和不克隆的一样,修改了一样会影响到外面)
// setTimeout(()=>{
// let newProps = mergeProps(props);
// newProps.obj.a[0] = 100;
// newProps.obj.b = 100;
// newProps.signalObj.a[0] = 100;
// newProps.signalObj.b = 100;
// props.obj.a[0] = 100;
// props.obj.b = 100;
// props.signalObj.a[0] = 100;
// props.signalObj.b = 100;
// },100)
let newProps = mergeProps(props);
// newProps.obj.a[0] = 100;
// newProps.obj.b = 100;
newProps.signalObj.a[0] = 100;
newProps.signalObj.b = 100;
// props.obj.a[0] = 100;
// props.obj.b = 100;
// props.signalObj.a[0] = 100;
// props.signalObj.b = 100;
// props.name = "122";
// newProps.name = 'Jane'; // 不能直接改它会报错
return (
<>
{props.name} {props.children}
</>
);
};
const MyComponents2 = (props = { name: "Smith" }) => {
return (
<>
{props.name}
<br />
{props.obj.a[0]}
<br />
{props.obj.b}
<br />
{props.signalObj.a[0]}
<br />
{props.signalObj.b}
</>
);
};
const test1 = () => {
// 普通的引用值在子组件中被修改会影响到界面,createEffect 监听不到
// 响应的引用值在子组件中被修改会影响到界面,createEffect 也监听得到
// <MyComponents signalObj={signalObj} /> <MyComponents signalObj={signalObj()} /> 这两种都行
// props 属性不能修改(props.name = "122";),会报错。除非是对象
// newProps = mergeProps(props); newProps和props没什么区别
var name = "Jack";
var [signalObj, setSignalObj] = createSignal({ a: [1, 2, 3], b: 1 });
var obj = { a: [1, 2, 3], b: 1 };
createEffect(() => {
console.log("执行一次:打印obj", obj);
console.log("执行一次:打印obj.b", obj.b);
console.log("执行一次:打印signalObj", signalObj());
console.log("执行一次:打印signalObj.b", signalObj().b);
});
return (
<>
<MyComponents
name={name}
obj={obj}
signalObj={signalObj()}
></MyComponents>
---------------------------------------------------
<MyComponents2
name={name}
obj={obj}
signalObj={signalObj()}
></MyComponents2>
</>
);
};
export default test1;
splitProps
分割 props。
import { splitProps } from "solid-js";
const MyComponents = (props = { name: "Smith" }) => {
console.log(props); // {a: 1, b: 2, c: 3, d: 4, e: 5, foo: "bar"}
const [vowels, consonants, leftovers] = splitProps(
props,
["a", "e"],
["b", "c", "d"]
);
console.log(vowels); // {a: 1, e: 5}
console.log(consonants); // {b: 2, c: 3, d: 4}
console.log(leftovers.foo); // bar
};
const test1 = () => {
return (
<>
<MyComponents a={1} b={2} c={3} d={4} e={5} foo="bar"></MyComponents>
</>
);
};
export default test1;
useTransition
TODO
用于批量异步更新延迟提交,直到所有异步进程完成。这与 Suspense 相关联,并且仅跟踪在 Suspense 边界下读取的资源。
startTransition
TODO
类似于useTransition,除了没有关联的 pending 状态。这个可以直接用来启动 Transition。
observable
TODO
from
TODO
mapArray
import { createSignal, mapArray, For } from "solid-js";
const test1 = () => {
var [arr, setArr] = createSignal([
{
id: 1,
name: "xiaom",
description: "xiaomi",
},
{
id: 2,
name: "xiaom2",
description: "xiaomi2",
},
]);
// 通过引用缓存每个 item,以减少不必要的更新映射。它只对每个值运行一次映射函数,然后根据需要移动或删除它。index 参数是一个 signal。map 函数本身不跟踪。
// <For> 控制流的底层工具类。
// 感觉没什么L用
const mapped = mapArray(arr, (model) => {
console.log(model);
const [name, setName] = createSignal(model.name);
const [description, setDescription] = createSignal(model.description);
return {
id: model.id,
get name() {
return name();
},
get description() {
return description();
},
setName,
setDescription,
};
});
setTimeout(() => {
setArr([
{
id: 1,
name: "xiaom",
description: "xiaomi",
},
]);
}, 1000);
return (
<div>
<For each={mapped()} fallback={<div>Loading...</div>}>
{(item) => <div>{item.name}</div>}
</For>
</div>
);
};
export default test1;
indexArray
import { createSignal, indexArray, For } from "solid-js";
const test1 = () => {
var [arr, setArr] = createSignal([
{
id: 1,
name: "xiaom",
description: "xiaomi",
},
{
id: 2,
name: "xiaom2",
description: "xiaomi2",
},
]);
// 类似于mapArray,除了它按 index 映射。该 item 是一个 signal,index 现在是常数。
const mapped = indexArray(arr, (model) => {
console.log(model);
return {
get id() {
return model.id;
},
get name() {
return model().name;
},
get description() {
return description();
},
};
});
setTimeout(() => {
setArr([
{
id: 1,
name: "xiaom",
description: "xiaomi",
},
]);
}, 1000);
return (
<div>
<For each={mapped()} fallback={<div>Loading...</div>}>
{(item) => <div>{item.name}</div>}
</For>
</div>
);
};
const test2 = () => {
// Solid.js 中的 IndexArray 是一个特殊的数组,它可以用于优化列表渲染的性能。
// 在 Solid.js 中,当我们使用 for...of 循环或 map 函数来渲染一组数据时,每当数据发生变化时,Solid.js 都会重新渲染整个列表。这种方式可能会导致性能问题,特别是在大量数据的情况下。
// 在上面的例子中,我们使用 IndexArray 包装了一个普通的数组 items,并将其传递给 map 函数来渲染列表。key 属性使用了每一项的索引作为唯一标识。当数据发生变化时,只有发生变化的项会被重新渲染,从而提高了性能。
// 需要注意的是,IndexArray 仅适用于静态列表(即不包含过滤、排序等动态操作的列表)。因为 IndexArray 使用了标识符来区分每一项,如果列表中的项发生动态变化,可能会导致标识符的重复或丢失,从而引发错误。
var [items] = createSignal(["apple", "banana", "cherry"]);
const indexedArray = indexArray(items, (item) => item);
return (
<div>
<ul>
{indexedArray().map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
};
export default test2;
// TODO
参考:
https://www.npmjs.com/package/solid-js/v/1.3.9
https://gitee.com/petter_pan/soidjs-demo