当然可以使用 UnoCSS 替代 Tailwind CSS。我们需要做一些调整来集成 UnoCSS 到我们的 Next. Js 项目中。以下是修改后的步骤和代码:
- 首先,移除 Tailwind CSS 相关的依赖,并安装 UnoCSS:
bun remove tailwindcss postcss autoprefixer @tailwindcss/typography
bun add -d unocss @unocss/webpack- 创建
uno.config.ts文件在项目根目录:
import { defineConfig, presetUno, presetTypography } from 'unocss'
export default defineConfig({
presets: [
presetUno(),
presetTypography(),
],
})- 更新
next.config.js:
const UnoCSS = require('@unocss/webpack').default
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
webpack: (config) => {
config.plugins.push(UnoCSS())
return config
},
}
module.exports = nextConfig- 更新
pages/_app.tsx:
import 'uno.css'
import type { AppProps } from 'next/app'
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
export default MyApp-
删除
tailwind.config.js文件。 -
更新
styles/globals.css:
/* 你可以保留一些全局样式,但不再需要导入 Tailwind */- 更新
pages/index.tsx,将 Tailwind 类替换为 UnoCSS 类:
import { useState, useEffect } from 'react';
import { marked } from 'marked';
interface Todo {
id: number;
text: string;
completed: boolean;
}
export default function Home() {
const [todos, setTodos] = useState<Todo[]>([]);
const [newTodo, setNewTodo] = useState('');
const [editingId, setEditingId] = useState<number | null>(null);
const [editingText, setEditingText] = useState('');
useEffect(() => {
fetchTodos();
}, []);
const fetchTodos = async () => {
const response = await fetch('/api/todos');
const data = await response.json();
setTodos(data);
};
const addTodo = async (e: React.FormEvent) => {
e.preventDefault();
if (newTodo.trim()) {
const response = await fetch('/api/todos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: newTodo })
});
const data = await response.json();
setTodos([...todos, data]);
setNewTodo('');
}
};
const updateTodo = async (id: number, text: string, completed: boolean) => {
const response = await fetch(`/api/todos/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text, completed })
});
const updatedTodo = await response.json();
setTodos(todos.map(todo => todo.id === id ? updatedTodo : todo));
};
const deleteTodo = async (id: number) => {
await fetch(`/api/todos/${id}`, { method: 'DELETE' });
setTodos(todos.filter(todo => todo.id !== id));
};
const startEditing = (todo: Todo) => {
setEditingId(todo.id);
setEditingText(todo.text);
};
const saveEdit = async () => {
if (editingId !== null) {
await updateTodo(editingId, editingText, todos.find(t => t.id === editingId)?.completed || false);
setEditingId(null);
setEditingText('');
}
};
const renderTodoText = async (id: number) => {
const response = await fetch(`/api/todos/render/${id}`);
const html = await response.text();
return { __html: html };
};
return (
<div className="p-4 max-w-2xl mx-auto">
<h1 className="text-3xl font-bold mb-4">Todo App with Markdown</h1>
<form onSubmit={addTodo} className="mb-4">
<input
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="Add a new todo"
className="border p-2 mr-2 w-full mb-2"
/>
<button type="submit" className="bg-blue-500 text-white p-2 rounded hover:bg-blue-600">Add</button>
</form>
<ul>
{todos.map(todo => (
<li key={todo.id} className="mb-4 bg-gray-100 p-4 rounded">
{editingId === todo.id ? (
<div>
<textarea
value={editingText}
onChange={(e) => setEditingText(e.target.value)}
className="w-full p-2 border rounded mb-2"
/>
<button onClick={saveEdit} className="bg-green-500 text-white p-2 rounded mr-2">Save</button>
<button onClick={() => setEditingId(null)} className="bg-gray-500 text-white p-2 rounded">Cancel</button>
</div>
) : (
<div>
<div className="flex items-center mb-2">
<input
type="checkbox"
checked={todo.completed}
onChange={(e) => updateTodo(todo.id, todo.text, e.target.checked)}
className="mr-2"
/>
<span className={todo.completed ? 'line-through' : ''}>{todo.text}</span>
<button onClick={() => startEditing(todo)} className="ml-auto text-blue-500 mr-2">Edit</button>
<button onClick={() => deleteTodo(todo.id)} className="text-red-500">Remove</button>
</div>
<div dangerouslySetInnerHTML={renderTodoText(todo.id)} className="prose prose-sm text-sm text-gray-600" />
</div>
)}
</li>
))}
</ul>
</div>
);
}注意:UnoCSS 和 Tailwind CSS 的类名在大多数情况下是相同的,所以我们不需要对 HTML 结构做太多更改。主要的区别在于配置和集成方式。
- 如果你想使用 UnoCSS 的 attributify 模式,你可以在
uno.config.ts中添加相应的预设:
import { defineConfig, presetUno, presetAttributify, presetTypography } from 'unocss'
export default defineConfig({
presets: [
presetUno(),
presetAttributify(),
presetTypography(),
],
})然后你可以在 JSX 中使用属性模式,例如:
<div bg="blue-500" text="white" p="2" rounded>
This is a button
</div>这些更改将使你的项目从 Tailwind CSS 迁移到 UnoCSS。UnoCSS 提供了类似的功能,但具有更高的性能和更大的灵活性。记住要根据 UnoCSS 的文档调整任何自定义配置或使用方式。