自定义指令Directives
把自定义指令单独拿出来是因为在TypeScript下这里我也踩了不少坑,这里连同我遇到的问题一起说一下。
SolidJS使用use namespace绑定到一个原生dom上,相当于一个ref的语法糖,同一个dom可以绑定多个指令且不会产生冲突。
自定义指令是一个具有两个参数的函数
type Accessor<T> = () => T
interface Directives {
[x: string]: (el: Element, accessor: Accessor<any>) => void;
}
el参数为绑定的dom,而accessor为指令传入的回调函数
注意: 指令不适用于非原生dom也就是自定义组件,可行的解决方办法是使用props去传递回调函数
下面给出官网案例,按照官方文档中的描述:
<div class="modal" use:clickOutside={() => setShow(false)}>
Some Modal
</div>
// clickOutside.ts
export default function clickOutside(el, accessor) {
const onClick = (e) => !el.contains(e.target) && accessor()?.();
document.body.addEventListener("click", onClick);
onCleanup(() => document.body.removeEventListener("click", onClick));
}
这里编译前直接报错,要给原生dom上指令,所以只能从namespace上下手
不能将类型“{ children: string; "use:clickOutside": () => void; }”分配给类型“IntrinsicAttributes & ButttonProps”。
类型“IntrinsicAttributes & ButttonProps”上不存在属性“use:clickOutside”。
经过一系列查询之后,发现官方大佬给出了正确的书写方式
declare module "solid-js" {
namespace JSX {
interface Directives {
clickOutside?: () => void;
}
}
}
加上这一段之后代码确实没有飘红了,但奇怪的是我的ide依然提示引入的clickOutside是灰色的,而这个自定义指令也并没有正常加载
import type { Component } from "solid-js";
import { clickOutside } from "../directives/clickOutside";
const Index: Component = () => {
console.log(clickOutside); //这里正常输出clickOutside返回的内容,但是指令并未生效
return (
<div>
<button
use:clickOutside={() => {
console.log("clickOutside");
}}
>
Click
</button>
</div>
);
};
export default Index;
推测可能是由于babel在tree shaking的时候把这个变量删掉了,导致SolidJS读取不到这个指令
查了一下vite相关的设定,给出的设置是
// vite.config.ts
import { defineConfig } from "vite";
import solidPlugin from "vite-plugin-solid";
export default defineConfig({
plugins: [solidPlugin({ babel: { onlyRemoveTypeImports: true } })],
// ...
})
但这里依然会报错
[vite] Internal server error: Unknown option: .onlyRemoveTypeImports. Check out https://babeljs.io/docs/en/babel-core/#options for more information about options.
之后我搜索到这个设置并不是在babel下,应该是这样的
// vite.config.ts
import { defineConfig } from "vite";
import solidPlugin from "vite-plugin-solid";
export default defineConfig({
plugins: [solidPlugin({ typescript: { onlyRemoveTypeImports: true } })],
// ...
})
并且要在全局内保持对指令的引用
因此改造clickOutside.tsx文件
import { onCleanup } from "solid-js";
declare module "solid-js" {
namespace JSX {
interface Directives {
clickOutside?: () => void;
}
}
}
export const clickOutside = function (el: Element, accessor: () => Function) {
console.log("active");
const onClick = (e: any) => !el.contains(e.target) && accessor()?.();
document.body.addEventListener("click", onClick);
onCleanup(() => document.body.removeEventListener("click", onClick));
};
export const useDirective = (fn: (el: HTMLElement, accessor: () => Function) => void) => {};
挂载指令之前使用useDirective包裹指令
import type { Component } from "solid-js";
import { clickOutside, useDirective } from "../directives/clickOutside";
useDirective(clickOutside);
const Index: Component = () => {
return (
<div>
<button
use:clickOutside={() => {
console.log("clickOutside");
}}
>
Click
</button>
</div>
);
};
export default Index;
此时控制台输出active
说明指令被正常挂载在dom上了
自定义指令是我在官方文档上踩过最大的坑,而且自定义指令无法支持自定义组件,因为官方文档没有给出TypeScript的相关设置,stackoverflow也没有给出什么有用的信息,我一度怀疑官方对于ts的支持是消极态度,还好在各种查询后解决了官方文档未提及的问题。
本文链接:https://blog.csdn.net/weixin_41907106/article/details/126348166