初始化
本文使用 SolidJs
举例, 你也可以使用其他的框架
Solid 拥有 React TypeScript 的开发体验, 还解决了 React 的缺点
还可以编译为 原生JS 体积非常的小, 还拥有 原生 JS 一样的性能.
创建项目
用官方模版创建一个 SolidJs 的 TypeScript 项目, 用 VSCode 打开项目
npx degit solidjs/templates/ts youhou-solid
配置 vite
- 安装
pnpm i -D vite-plugin-css-injected-by-js
插件, 可以把 CSS 全部打包到 JS 中. - 设置
build.rollupOptions.output
中的 3 个 name , 改为原文件名输出 (去掉 vite 默认的hash
后缀) - 配置这两项, 打包后
所有 CSS 与 JS 就会在一个 index.js 中
import { defineConfig } from 'vite';
import solidPlugin from 'vite-plugin-solid';
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
export default defineConfig({
// 使用插件 把 css 放入 js
plugins: [solidPlugin(), cssInjectedByJsPlugin()],
server: {
port: 3000,
},
build: {
target: 'esnext',
// 关键点
rollupOptions: {
output: {
entryFileNames: `[name].js`,
chunkFileNames: `[name].js`,
assetFileNames: `[name].[ext]`,
// manualChunks: undefined
}
}
},
});
执行 pnpm build
命令构建项目, 产出 /dist/index.js
package.json
"scripts": {
"dev": "vite",
"build": "vite build",
"w": "vite build --watch",
"serve": "vite preview"
},
新建一个油猴测试脚本
倒数第二行的 @require file://
是关键,
把编译后的 index.js 完整路径
写到这里
如下:
// ==UserScript==
// @name SolidJs-Test
// @namespace http://tampermonkey.net/
// @version 0.1
// @description 测试
// @author You
// @include *
// @icon https://www.google.com/s2/favicons?sz=64&domain=bing.com
// @grant none
// @require file://E:\Work\SolidProject\youhou-solid\dist\index.js
// ==/UserScript==
@include *
是匹配所有网站, 测试环境用,
全部编写完毕后,再新建一个正规一些的油猴脚本,
配置好后,
使用 pnpm w
来执行配置好的 vite build --watch
命令
只要保存代码,就会重新编译 , 耗时基本上都在 1 秒左右, 非常的快嗷~~
浏览器执行脚本只需要 F5 刷新页面,
最新的代码就会生效了.
配置 SolidJs
安装 UI 组件库 hope-ui
https://github.com/hope-ui/hope-ui/tree/v0.6.3
还有一个 solid-toast
也挺好用
"devDependencies": {
"typescript": "^4.8.2",
"vite": "^3.0.9",
"vite-plugin-css-injected-by-js": "^2.1.0",
"vite-plugin-solid": "^2.3.0"
},
"dependencies": {
"@hope-ui/solid": "^0.6.7",
"@stitches/core": "^1.2.8",
"@tanstack/solid-query": "^4.5.0",
"solid-js": "^1.5.1",
"solid-toast": "^0.3.5",
"solid-transition-group": "^0.0.11"
}
index.tsx
这段代码有两个关键点:
- body 添加一个
id 为 my-solid-root 的 div
- 配置 hope-ui 禁止规范化 CSS
- 因为会添加全局规范化 CSS, 对整个页面有影响
- TailWindCss 也不建议用, 因为也需要各种类名.
- 如果想用 Tailwind 可以试一试
module.css
中 使用@apply
写 tailwind
/* @refresh reload */
import { render } from "solid-js/web";
import "./style/global.css";
import App from "./components/app";
import { Toaster } from "solid-toast";
import { HopeProvider } from "@hope-ui/solid";
import { QueryClient, QueryClientProvider } from "@tanstack/solid-query";
// 给 body 添加一个 div
let div = document.createElement("div");
div.id = "my-solid-root"; // 把 Solid 都放入这个 div 中
document.body.appendChild(div);
let queryClient = new QueryClient();
render(
() => (
// 配置 hope-ui 禁止 规范化 css
<HopeProvider enableCssReset={false}>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
<Toaster /> // 配置 solid-toast
</HopeProvider>
),
document.getElementById("my-solid-root") as HTMLElement
);
app.tsx
可以用 module.css
,
可以使用组件库 , 可以自己封装子组件,
可以用任何的 Web 开发库
禁止 hope-ui 规范化 CSS 后, 也对原来页面没有任何的影响.
这里使用 "获取 github package.json 依赖"
为例
import { Component, createSignal, For, Match, onMount, Show, Switch } from "solid-js";
import cssModule from "./app.module.css";
import {
Button,
Table,
Thead,
Tr,
Th,
Tbody,
Td,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
createDisclosure,
} from "@hope-ui/solid";
import { createQuery } from "@tanstack/solid-query";
interface IPackageJson {
dependencies: { [key: string]: string };
devDependencies: { [key: string]: string };
}
interface IPkg {
name: string;
version: string;
}
interface IQueryObj {
dep: IPkg[];
devDep: IPkg[];
}
let fetchData = fetch("").then((r) => r.json());
const App: Component = () => {
const { isOpen, onOpen, onClose } = createDisclosure();
const [repoName, setRepoName] = createSignal<string>();
let query = createQuery<IQueryObj, Error>(
() => ["packageData"],
async () => {
try {
// github 文件有 5 分钟的 disk cache
let filePath = `https://raw.githubusercontent.com/${repoName()}/main/package.json`;
let apiResult: IPackageJson = await fetch(filePath).then((r) => {
if (r.status == 200) {
return r.json();
}
throw new Error("发生异常,状态码:" + r.status);
});
if (apiResult != undefined) {
console.log("来数据了");
// 运行时依赖
let depList = apiResult.dependencies;
let depArr: IPkg[] = [];
for (const key in depList) {
if (Object.prototype.hasOwnProperty.call(depList, key)) {
const value = depList[key];
depArr.push({ name: key, version: value });
}
}
// 开发依赖
let devDepArr: IPkg[] = [];
let devDepList = apiResult.devDependencies;
for (const key in devDepList) {
if (Object.prototype.hasOwnProperty.call(devDepList, key)) {
const value = devDepList[key];
devDepArr.push({ name: key, version: value });
}
}
let ret: IQueryObj = { dep: depArr, devDep: devDepArr };
return ret;
}
throw new Error("无数据");
} catch (error) {
throw error;
}
},
{
get enabled() {
let name = repoName();
return name != undefined && name != "";
},
retry: 2,
}
);
let fetchPackageJson = () => {
let sel = document.querySelector<HTMLAnchorElement>(
"#repository-container-header > div.d-flex.flex-wrap.flex-justify-end.mb-3.px-3.px-md-4.px-lg-5 > div > div > strong > a"
);
let name = sel?.href!;
if (name == undefined) {
return;
}
console.log(name);
name = name.replaceAll("https://github.com", "");
setRepoName(name);
};
let openDrawer = () => {
isOpen() ? onClose() : onOpen();
fetchPackageJson();
};
// 组件加载完毕执行
onMount(() => {});
return (
<div>
<Button class={cssModule.myButton} css={{ position: "fixed" }} onClick={openDrawer}>
Open
</Button>
<Drawer opened={isOpen()} placement="top" size="lg" onClose={onClose}>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader>查看项目依赖</DrawerHeader>
<DrawerBody>
<div style={{ display: "flex", "justify-content": "center" }}>
<Switch>
<Match when={query.isLoading}>
<div>loading...</div>
</Match>
<Match when={query.isError}>
<div>Error: {query!.error!.message}</div>
</Match>
<Match when={query.isSuccess}>
<Show when={query.data?.dep?.length ?? 0 > 0}>
<div>
<h1>dependencies</h1>
<Table>
<Thead>
<Tr>
<Th>名称</Th>
<Th>版本</Th>
</Tr>
</Thead>
<Tbody>
<For each={query.data?.dep}>
{(item) => {
return (
<Tr>
<Td>{item.name}</Td>
<Td>{item.version}</Td>
</Tr>
);
}}
</For>
</Tbody>
</Table>
</div>
</Show>
<Show when={query.data?.devDep?.length ?? 0 > 0}>
<div>
<h1>devDependencies</h1>
<Table>
<Thead>
<Tr>
<Th>名称</Th>
<Th>版本</Th>
</Tr>
</Thead>
<Tbody>
<For each={query.data?.devDep}>
{(item) => {
return (
<Tr>
<Td>{item.name}</Td>
<Td>{item.version}</Td>
</Tr>
);
}}
</For>
</Tbody>
</Table>
</div>
</Show>
</Match>
</Switch>
</div>
</DrawerBody>
<DrawerFooter>
<Button variant="outline" mr="$3" onClick={onClose}>
Cancel
</Button>
<Button>Save</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
</div>
);
};
export default App;
app.module.css
.myButton {
bottom: 12px;
right: 12px;
z-index: 10;
}
代码写完了, 执行 pnpm build
新建一个正规的油猴脚本
指定 @name
和 @match
把 /dist/index.js
中的所有代码都粘贴到最后,
如下:
// ==UserScript==
// @name github 查看 package.json 依赖
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://github.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=github.com
// @grant none
// ==/UserScript==
// 把 index.js中的所有代码粘贴到这里
保存 !! 完结撒花.
最终效果
非常的不错嗷… 动画也非常的丝滑 ~ ~
开发体验也非常的不错,
和 Web 开发体验一样…
除了热更新,
需要 VSCode 保存, 自动编译, 浏览器按 F5 即可
相比原生油猴那个编辑框, 开发体验好了百倍
以后还想做的话, 还能加一些功能
- 用 npm api 中获取 npm 信息
- 用 google 翻译 api , 把 npm 简介翻译成 中文 …
- 把名称弄成 github 链接, 点击直接打开对应的 Github 地址