介绍
SolidStart是基于SolidJ和实心路由器的元式框架(例如for vuejs的react或nuxt)。
它仍在Beta中,但我们可以创建出色的应用程序。
在本文中,我们将看到如何将MongoDB数据库与SolidStart集成。
为了说明该方法,我们将创建一个基本应用程序:“购物清单” WebApp。
我们假设您在端口27017上可以访问一个运行的mongoDB实例。
准备
在整合MongoDB之前,我们需要准备我们的应用程序。
生成应用程序
首先,我们生成一个solidstart应用程序。我们使用recommended starter:
mkdir shopping-list && cd shopping-list
npm init solid@latest
并选择bare
模板:
? Which template do you want to use?
> bare
hackernews
todomvc
with-auth
with-authjs
with-mdx
with-prisma
with-solid-styled
with-tailwindcss
with-vitest
在模板生成器的其余部分中,我们选择了Server Side Rendering
和TypeScript
的用法。
最后,我们安装了NPM依赖项并运行DEV脚本:
npm install
npm run dev -- --open
在此步骤中,我们的应用程序已准备好开发!
在添加MongoDB集成之前,我们通过做一些更改来准备我们的应用程序。
模型
我们通过使用以下内容创建文件src/models/shopping.ts
来创建ShoppingListItem
打字字体类型:
src/models/shopp.ts
export type ShoppingListItem = {
_id: string;
label: string;
};
export type NewShoppingListItem = Omit<ShoppingListItem, "_id">;
风格
我们在src/root.css
的末尾添加了一些CS。
src/root.css
ul {
display: flex;
flex-direction: column;
align-items: baseline;
}
li {
margin-top: 10px;
}
li > button {
margin-left: 10px;
}
成分
为了帮助我们在应用程序中,我们创建了一个ListInput
组件,使我们可以键入一个新项目。我们创建src/components/ListInput.tsx
:
src/components/listInput.tsx
import { createSignal } from "solid-js";
interface ListInputProps {
onValidate: (value: string) => void;
}
export default function ListInput(props: ListInputProps) {
const [value, setValue] = createSignal("");
const onValidateValue = () => {
props.onValidate(value());
setValue("");
};
return (
<div>
<input
value={value()}
onInput={(evt) => setValue(evt.currentTarget.value)}
type="text"
placeholder="New item"
onKeyPress={(evt) => {
if (evt.key === "Enter") {
evt.preventDefault();
onValidateValue();
}
}}
/>
<button type="button" onClick={onValidateValue}>
Add
</button>
</div>
);
}
卸下导航标头
不需要来自模板的默认导航标头,因此我们将其删除。
为此,您只需要删除src/root.tsx
文件中的<A />
标签。
index.tsx页面
在此步骤中,我们使用createSignal
处理购物清单项目。没有持久性,但是逻辑有效。
我们用以下内容替换src/routes/index.tsx
:
src/routes/index.tsx
import { createSignal, For } from "solid-js";
import ListInput from "~/components/ListInput";
import { ShoppingListItem } from "~/models/shopping";
export default function Home() {
const [items, setItems] = createSignal<ShoppingListItem[]>([]);
const addNewItem = (label: string) => {
setItems((prev) => [...prev, { label, _id: Date.now().toString() }]);
};
const deleteItem = (itemId: string) => {
setItems((prev) => prev.filter((item) => item._id !== itemId));
};
return (
<main>
<ul>
<For each={items()}>
{(item) => (
<li>
<span>{item.label}</span>
<button type="button" onClick={() => deleteItem(item._id)}>
Delete
</button>
</li>
)}
</For>
</ul>
<ListInput onValidate={addNewItem} />
</main>
);
}
添加此点,该应用程序无效。
MongoDB的整合
添加mongodb
首先,我们安装了mongodb
和@types/mongodb
npm依赖项:
npm install --save mongodb
npm install --save-dev @types/mongodb
然后,我们创建一个类来管理与数据库的连接。我们使用以下内容创建src/lib/database.ts
:
src/lib/database.ts
import { Db, MongoClient } from "mongodb";
const MONGODB_HOST = "localhost";
const MONGODB_PORT = 27017;
export class Database {
private db?: Db;
private static instance: Database;
private constructor() {}
async init() {
const client = new MongoClient(`mongodb://${MONGODB_HOST}:${MONGODB_PORT}`);
await client.connect();
this.db = client.db("shopping-list");
}
public static getInstance() {
if (!Database.instance) {
Database.instance = new Database();
}
return Database.instance;
}
getDb() {
if (!this.db) {
throw new Error("DB is not init");
}
return this.db;
}
}
初始数据库连接
启动应用程序时,我们需要启动数据库连接。
这是使用Database
类的init()
方法完成的。 Singleton模式的使用有助于我们。
我们编辑src/entry-server.tsx
文件以在createHandler
函数之前添加数据库初始化:
src/entry-server.tsx
import {
createHandler,
renderAsync,
StartServer,
} from "solid-start/entry-server";
import { Database } from "./lib/database";
// Initialization of the database (connection + retrieve db instance)
const database = Database.getInstance();
database.init();
export default createHandler(
renderAsync((event) => <StartServer event={event} />)
);
创建一个与数据库互动的ShoppingService
我们创建一个ShoppingService
与Database
类交互,并添加 /列表 /删除购物列表项目。< / p>
您可以使用以下内容创建文件src/lib/shopping-service.ts
:
src/lib/shopping-service.ts
import { Db, ObjectId } from "mongodb";
import { NewShoppingListItem, ShoppingListItem } from "../models/shopping";
import { Database } from "./database";
export class ShoppingService {
db: Db;
collectionName = "items";
private static instance: ShoppingService;
private constructor() {
this.db = Database.getInstance().getDb();
}
public static getInstance() {
if (!ShoppingService.instance) {
ShoppingService.instance = new ShoppingService();
}
return ShoppingService.instance;
}
async getAllItems(): Promise<ShoppingListItem[]> {
const itemsFromDb = await this.db
.collection(this.collectionName)
.find()
.toArray();
return itemsFromDb.map((elt) => ({
...elt,
_id: elt._id.toString(), // Get _id as String
})) as ShoppingListItem[];
}
async addNewItem(item: NewShoppingListItem) {
await this.db.collection(this.collectionName).insertOne(item);
}
async deleteItem(itemId: string) {
await this.db
.collection(this.collectionName)
.deleteOne({ _id: new ObjectId(itemId) });
}
}
与头版集成
在这一点上,我们需要在ShoppingService
和我们的index.tsx
页面之间添加交互。这将以2个步骤完成:
- 创建
routeData
来检索我们的物品并通过useRouteData使用它们 - 创建服务器端操作以使用createServerAction$添加和删除项目
使用routeData
检索我们的数据
要在我们的页面中检索数据,SolidStart使用routeData
。我们创建一个名为routeData
的函数,并使用createServerData$
在服务器端执行操作并获取我们的项目。
在我们的组件中,我们具有useRouteData
功能来检索我们的项目。
要识别我们的请求(并在更新数据时能够使其无效),我们在src/constants.ts
文件中创建一个常数。
src/startants.ts
export const SHOPPING_ITEMS_REQ_KEY = "shoppingItems";
然后,我们在src/routes/index.tsx
文件中创建函数routeData
:
src/routes/index.tsx
import { createServerData$ } from "solid-start/server";
import { ShoppingService } from "~/lib/shopping-service";
import { ShoppingListItem } from "~/models/shopping";
import { SHOPPING_ITEMS_REQ_KEY } from "~/constants";
export function routeData() {
return createServerData$(
async () => {
const shoppingService = ShoppingService.getInstance();
const data = await shoppingService.getAllItems();
return data as ShoppingListItem[];
},
{ key: SHOPPING_ITEMS_REQ_KEY }
);
}
export default function Home() {
// Content of our page
}
在routeData
中,我们使用ShoppingService
获取我们的物品并返回。
要使用我们检索的项目,我们只需要在我们的Home
组件中使用useRouteData
:
src/routes/index.tsx
import { useRouteData } from "solid-start";
export default function Home() {
const items = useRouteData<typeof routeData>();
// Rest of our component
}
因为我们由于以前的createSignal
逻辑而进行了重复,因此我们可以删除createSignal
函数并编辑我们的addNewItem
和deleteItem
,如下所示:
src/routes/index.tsx
import { useRouteData } from "solid-start";
export default function Home() {
const items = useRouteData<typeof routeData>();
const addNewItem = (label: string) => {};
const deleteItem = (itemId: string) => {};
// Rest of our component
}
目前,我们的项目是从数据库中检索的! (但是我们在数据库中没有数据,现在我们将添加操作以添加 /删除项目来解决此问题)< / p>
添加服务器端操作
对于 add 和 delete 操作,我们将创建一些将在服务器端执行的功能。查看routeData
功能下的功能:
src/routes/index.tsx
import { ShoppingListItem, NewShoppingListItem } from "~/models/shopping";
export function routeData() {
// routeData content
}
async function addNewItemAction(newItem: NewShoppingListItem) {
const shoppingService = ShoppingService.getInstance();
await shoppingService.addNewItem(newItem);
}
async function deleteItemAction(itemId: string) {
const shoppingService = ShoppingService.getInstance();
await shoppingService.deleteItem(itemId);
}
使用createServerAction$
,我们的Home
组件将调用这些功能,该功能使我们调用服务器端函数。它特别是为更新数据而设计的,因为此功能使我们可以指出要无效的数据(让SolidStart自动重新提取它们)。
我们在Home
组件中添加我们的动作,在我们的useRouteData
下:
src/routes/index.tsx
import { createServerAction$ } from "solid-start/server";
import { SHOPPING_ITEMS_REQ_KEY } from "~/constants.ts";
export default function Home() {
const items = useRouteData<typeof routeData>();
const [_adding, addNewItem] = createServerAction$(addNewItemAction, {
invalidate: SHOPPING_ITEMS_REQ_KEY,
});
const [_deleting, deleteItem] = createServerAction$(deleteItemAction, {
invalidate: SHOPPING_ITEMS_REQ_KEY,
});
// Rest of our component
}
我们还可以删除我们的空addNewItem
和deleteItem
函数。它们被我们的服务器操作所取代。有关使用我们的行动的更多详细信息,请参阅文档。
简而言
- 在第一个索引中:动作状态(待处理,错误等...)
- 在第二个索引中:将调用我们的服务器端操作的功能
在我们的情况下,我们没有使用元组的第一个索引。
在选项中,我们还指出了提取请求的数据无效(使用SHOPPING_ITEMS_REQ_KEY
)。
最后,我们更新src/models/shopping.ts
的onValidate
Prop6以匹配参数:
src/routes/index.tsx
<ListInput onValidate={(label: string) => addNewItem({ label })} />
那就是!
我们的组件看起来像这样:
src/routes/index.tsx
import { For } from "solid-js";
import { useRouteData } from "solid-start";
import { createServerAction$, createServerData$ } from "solid-start/server";
import ListInput from "~/components/ListInput";
import { ShoppingService } from "~/lib/shopping-service";
import { NewShoppingListItem, ShoppingListItem } from "~/models/shopping";
import { SHOPPING_ITEMS_REQ_KEY } from "~/constants.ts";
export function routeData() {
return createServerData$(
async () => {
const shoppingService = ShoppingService.getInstance();
const data = await shoppingService.getAllItems();
return data as ShoppingListItem[];
},
{ key: SHOPPING_ITEMS_REQ_KEY }
);
}
async function addNewItemAction(newItem: NewShoppingListItem) {
const shoppingService = ShoppingService.getInstance();
await shoppingService.addNewItem(newItem);
}
async function deleteItemAction(itemId: string) {
const shoppingService = ShoppingService.getInstance();
await shoppingService.deleteItem(itemId);
}
export default function Home() {
const items = useRouteData<typeof routeData>();
const [_adding, addNewItem] = createServerAction$(addNewItemAction, {
invalidate: SHOPPING_ITEMS_REQ_KEY,
});
const [_deleting, deleteItem] = createServerAction$(deleteItemAction, {
invalidate: SHOPPING_ITEMS_REQ_KEY,
});
return (
<main>
<ul>
<For each={items()}>
{(item) => (
<li>
<span>{item.label}</span>
<button type="button" onClick={() => deleteItem(item._id)}>
Del
</button>
</li>
)}
</For>
</ul>
<ListInput onValidate={(label: string) => addNewItem({ label })} />
</main>
);
}
结论
现在您知道如何将MongoDB与SolidStart整合!
我们看到了如何:
- 用启动器生成一个实体应用
- 与IT一起使用Typescript
- 创建MongoDB连接
- 请从我们的组件向MongoDB做服务器端请求
随时发表评论以给我您的反馈或提供一些改进。
感谢您的阅读!