没错 solid比react对新人更加友好 不过我这种半新不新的 也更容易进入一些比较隐蔽的坑
上篇文章提到了我自制的文件路由系统 正是在写的过程中 我意识到solid没有想象中那么美好
虽然没有闭包的问题 但它有getter的问题
简单来说就是------------
你不能觉得类型没问题就真的没问题了
想要响应式 那就得传getter
以JSX对象为例 不应该const a = \<div xx/>
然后传到底下
而是要const a = ()=>\<div xx/>
然后把a传下去 给别人调用
其他类型的值其实还好 主要是JSX
它的语法比较暧昧 新手(我)很容易弄不清具体的生效时间
复盘一下
文件路由插件的原理
js
//index.tsx
import routes from '~solid-pages'
//xxx
render(() => <Router>{routes}</Router>, root!)
vite.env.d.ts
中 '~solid-pages'
这个模块的默认导出是<Router>可以接受的静态配置项 所以这里没有类型错误
而实际上 vite-plugin-pages
会生成一个临时的js文件 它的默认导出就是这个routes数组
我希望使用类似next.js app router的路由结构 必须重写resolver 自己实现从文件结构到js文件的全过程
最终目的是把文件结构中的layout error loading page 404转化为如下的配置项
js
{
path: '/',
component: (props) => (
<Layout>
<ErrorBoundary
fallback={(err, reset) => <Error err={err} reset={reset}></Error>}
>
<Suspense fallback={<Loading></Loading>}>{props.children}</Suspense>
</ErrorBoundary>
</Layout>
),
children: [
{
path: '',
component: Page,
},
//..其他的下级路由
{
path:'*rest',
component: NotFound
}
],
}
尝试
需要注意的是 js ts文件是不能使用jsx语法的 vite会报错
至少我反复拷打ai 没有得到解决办法
所以往'~solid-pages'
里输出的其实是这种东西
js
component:(props) =>
createComponent(Layout, {
children: createComponent(ErrorBoundary, {
fallback: (err, reset) => createComponent(Error, { err, reset }),
children: createComponent(Suspense, {
fallback: createComponent(Loading, {}),
children: props.children,
}),
}),
})
但是发现不顶用 ErrorBoundary和Suspense都不生效 只有Layout正常
一开始我以为是这两个组件的问题 比如需要运行时什么的
想起之前看过一篇远程组件的文章 我把输出内容改成了这样
js
export default function getRoutes(ErrorBoundary,Suspense){
//...
}
不过还是没用
后来还试了Dynamic组件 依旧没用
解决
我又尝试改了一下写法 把组装的工作放在外面做
js
//~solid-pages
const routesMap = {
path:'/',
component:{Layout,Error,Loading}
//xxx
}
export default routesMap
// index.tsx
import routesMap from '~solid-pages'
const routes = [{
path:'/',
component:(props:ParentProps)=>{
const { Layout,Error,Loading } = routesMap.xxx.xxx.xx
let res = null
if (Loading) {
res = <Suspense fallback={<Loading />}>{props.children}</Suspense>
}
if (Error) {
res = (
<ErrorBoundary
fallback={(err, reset) => <Error err={err} reset={reset} />}
>
{res ?? props.children}
</ErrorBoundary>
)
}
if (Layout) {
res = <Layout>{res ?? props.children}</Layout>
}
return res
}
}]
这次行了一半 能loading 不能error
把loading的部分注释掉 就可以error了
此刻我突然意识到问题的关键 那就是solid实现响应式的原理
要在使用一个值的时候调用getter函数 而不能提前把这个值取出来
所以要写成
js
const { Layout,Error,Loading } = routesMap.xxx.xxx.xx
let res = null
if (Loading) {
res = ()=><Suspense fallback={<Loading />}>{props.children}</Suspense>
}
if (Error) {
const _res = res
res = ()=>(
<ErrorBoundary
fallback={(err, reset) => <Error err={err} reset={reset} />}
>
{_res?_res(): props.children}
</ErrorBoundary>
)
}
if (Layout) {
const _res = res
res = ()=> <Layout>{_res ?_res(): props.children}</Layout>
}
return res()
其实刚才的写法 Layout的children也不会自动更新 但一开始我以为是热更新的问题 并没有发现
那么为什么一开始的createComponent也不行呢?
js
createComponent(Suspense, {
fallback: createComponent(Loading, {}),
children: props.children,
})
原因是这里已经把props.children
的值取了
正确的写法是
js
createComponent(Suspense, {
fallback: createComponent(Loading, {}),
get children(){
return props.children
},
})
此事(get children()
)在文档里亦有记载 不过当时我不知道为什么要这么写 现在又找不到具体位置了
所以这种写法是错误的
js
const comp = <div xx/>
return <div>{comp}</div>
要写成
js
const comp = ()=><div xx/>
或者
const comp = children(()=><div xxx/>)
return <div>{comp}</div>
其实换成数字或者随便一个其他类型 都不会出错 主要是JSX语法和react太像 太具有迷惑性了
更新
还是存在问题 但是我已经不知道原因了 热更新就是不行 ErrorBoundary 就是拦截不到异常
what can i say 摆了
还有一个问题

'/test1'切换回'/' 居然不能触发loading?
这下有正当理由切回next了