@web-mods/articles
v1.1.1
Published
Reusable articles management module
Readme
@web-mods/articles
文章管理组件库,提供完整的文章 CRUD 管理界面(列表、编辑、详情)以及服务端 HTML 渲染能力。
安装
pnpm add @web-mods/articlesPeer dependencies:
react >=18、react-dom >=18
react-router仅在 react-router 环境下需要(见下方适配说明)。dayjs、lucide-react、@monaco-editor/react均已打包在内,无需单独安装。
快速开始
1. 配置 ArticleService(可选)
service prop 不传即使用内置默认值,无需任何配置。
只有以下情况才需要手动配置:
自定义 API 地址:
import { createArticleService } from '@web-mods/articles'
const articleService = createArticleService('https://your-api.example.com')
// 然后传给 Provider:<ArticlesProvider service={articleService} ...>完全自定义请求逻辑(如附带鉴权 Header、适配不同数据格式等):
import type { ArticleService } from '@web-mods/articles'
const articleService: ArticleService = {
getList: (params) => api.get('/news', { params }), // 返回 { items, total }
getById: (id) => api.get(`/news/${id}`),
create: (data) => api.post('/news', data),
update: (id, data) => api.put(`/news/${id}`, data),
delete: (id) => api.delete(`/news/${id}`),
getByPathname: (pathname) => api.get(`/news/detail?pathname=${pathname}`), // 可选,服务端渲染时使用
}getList 返回格式:
{ items: Article[], total?: number }2. 配置路由
以 React Router v7 为例,用 layout 包裹所有文章相关路由:
// routes.ts
import { layout, route, prefix } from '@react-router/dev/routes'
export default [
layout('./pages/articles/layout.tsx', [
route('articles', './pages/articles/index.tsx'),
route('articles/create', './pages/articles/edit.tsx'),
route('articles/:id/edit', './pages/articles/edit.tsx'),
route('articles/:id/detail', './pages/articles/detail.tsx'),
]),
]3. 创建 Layout(注入 Provider)
组件库支持两种框架环境,写法略有不同:
React Router v7
用 ReactRouterAdapter 桥接路由,这是唯一需要 react-router 的地方:
// pages/articles/layout.tsx
import { Outlet } from 'react-router'
import { ArticlesProvider, ReactRouterAdapter } from '@web-mods/articles'
import '@web-mods/articles/style.css'
export default function ArticlesLayout() {
return (
<ArticlesProvider basePath="/articles">
<ReactRouterAdapter>
<Outlet />
</ReactRouterAdapter>
</ArticlesProvider>
)
}Next.js(或其他非 react-router 框架)
通过 Provider 的 navigate prop 注入路由能力,页面组件通过 id prop 接收参数:
// app/articles/layout.tsx
'use client'
import { useRouter } from 'next/navigation'
import { ArticlesProvider } from '@web-mods/articles'
import '@web-mods/articles/style.css'
export default function ArticlesLayout({ children }: { children: React.ReactNode }) {
const router = useRouter()
return (
<ArticlesProvider basePath="/articles" navigate={(path) => router.push(path)}>
{children}
</ArticlesProvider>
)
}// app/articles/[id]/edit/page.tsx
'use client'
import { useParams } from 'next/navigation'
import { ArticleEdit } from '@web-mods/articles'
export default function EditPage() {
const { id } = useParams<{ id: string }>()
return <ArticleEdit id={id} />
}4. 使用页面组件
// React Router:无需传 props,路由参数由 ReactRouterAdapter 自动注入
import { ArticleList, ArticleEdit, ArticleDetail } from '@web-mods/articles'
export default function ListPage() { return <ArticleList /> }
export default function EditPage() { return <ArticleEdit /> }
export default function DetailPage() { return <ArticleDetail /> }
// Next.js / 其他框架:通过 id prop 传入
export default function EditPage() { return <ArticleEdit id={id} /> }
export default function DetailPage() { return <ArticleDetail id={id} /> }ArticlesProvider 配置项
| 属性 | 类型 | 必填 | 说明 |
|------|------|------|------|
| basePath | string | ✅ | 文章路由根路径,如 /articles |
| service | ArticleService | — | 数据服务,不传使用内置默认值 |
| navigate | (path: string) => void | — | 非 react-router 环境必传,由框架路由实现跳转 |
| previewBaseUrl | string | — | 预览 iframe 注入 <base href="...",用于正确加载相对路径资源 |
| previewTailwind | boolean | — | 预览时自动注入 Tailwind CDN,适合内容使用了 Tailwind 类名的场景 |
<ArticlesProvider
basePath="/blog/articles"
previewBaseUrl="https://example.com"
previewTailwind={true}
>
...
</ArticlesProvider>Article 数据结构
interface Article {
id: string
pathname: string // 前台访问路径,如 about-us
content: string // 完整 HTML 内容
host?: string // 可跳转的网站地址,如 https://example.com
createdAt?: string
updatedAt?: string
}服务端渲染(Server-side Rendering)
适用于 Next.js Route Handler、Remix、Cloudflare Workers 等支持 Web
Request/ResponseAPI 的环境。
从独立的 server 入口引入,不包含任何 React 依赖,可安全在服务端使用:
import { renderArticleByPathname } from '@web-mods/articles/server'Next.js 示例
// app/[slug]/route.ts
import { renderArticleByPathname, defaultArticleService } from '@web-mods/articles/server'
export async function GET(_req: Request, { params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
return renderArticleByPathname(slug, defaultArticleService, {
faviconHref: '/favicon.svg', // 可选,自动注入 favicon
})
}如需自定义 API 地址:
import { renderArticleByPathname, createArticleService } from '@web-mods/articles/server'
const service = createArticleService('https://your-api.example.com')
export async function GET(_req: Request, { params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
return renderArticleByPathname(slug, service)
}访问 https://your-site.com/about-us 时,浏览器直接渲染 manage 后台编辑的 HTML 内容,无任何框架包裹。
renderArticleByPathname 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| pathname | string | 文章路径 |
| service | ArticleService | 需实现 getByPathname 方法 |
| options.faviconHref | string | 自动注入 favicon,如 /favicon.svg |
| options.notFoundHtml | string | 自定义 404 页面 HTML,不传则使用内置样式 |
样式
组件库使用 Tailwind CSS 构建,样式已编译打包至 dist/style.css。在 Layout 中引入一次即可,框架会在构建时将其内联进 <head>,不会有样式闪烁(FOUC):
import '@web-mods/articles/style.css'