乐闻世界logo
搜索文章和话题

面试题手册

Astro 中的状态管理是如何实现的?如何在 React、Vue、Svelte 组件中管理状态?

Astro 的状态管理对于构建交互式应用非常重要。虽然 Astro 默认是静态的,但可以通过多种方式实现状态管理。客户端状态管理:React 集成: // src/components/Counter.jsx import { useState, useEffect } from 'react'; export function Counter() { const [count, setCount] = useState(0); useEffect(() => { const saved = localStorage.getItem('count'); if (saved) setCount(parseInt(saved)); }, []); useEffect(() => { localStorage.setItem('count', count.toString()); }, [count]); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(c => c + 1)}>Increment</button> <button onClick={() => setCount(c => c - 1)}>Decrement</button> </div> ); }Vue 集成: <!-- src/components/TodoList.vue --> <script setup> import { ref, computed } from 'vue'; const todos = ref([ { id: 1, text: 'Learn Astro', completed: false }, { id: 2, text: 'Build app', completed: true }, ]); const completedCount = computed(() => todos.value.filter(t => t.completed).length ); function addTodo(text) { todos.value.push({ id: Date.now(), text, completed: false, }); } function toggleTodo(id) { const todo = todos.value.find(t => t.id === id); if (todo) todo.completed = !todo.completed; } </script> <template> <div> <p>Completed: {{ completedCount }} / {{ todos.length }}</p> <ul> <li v-for="todo in todos" :key="todo.id"> <input type="checkbox" :checked="todo.completed" @change="toggleTodo(todo.id)" /> <span :style="{ textDecoration: todo.completed ? 'line-through' : 'none' }"> {{ todo.text }} </span> </li> </ul> </div> </template>Svelte 集成: <!-- src/components/ShoppingCart.svelte --> <script> import { writable } from 'svelte/store'; const cart = writable([]); const total = writable(0); function addToCart(product) { cart.update(items => [...items, product]); total.update(t => t + product.price); } function removeFromCart(index) { cart.update(items => { const removed = items[index]; total.update(t => t - removed.price); return items.filter((_, i) => i !== index); }); } </script> <div> <h2>Shopping Cart</h2> <p>Total: ${$total}</p> <ul> {#each $cart as item, index} <li> {item.name} - ${item.price} <button on:click={() => removeFromCart(index)}>Remove</button> </li> {/each} </ul> </div>全局状态管理(Zustand):// src/store/useStore.tsimport { create } from 'zustand';interface StoreState { user: User | null; theme: 'light' | 'dark'; setUser: (user: User) => void; setTheme: (theme: 'light' | 'dark') => void;}export const useStore = create<StoreState>((set) => ({ user: null, theme: 'light', setUser: (user) => set({ user }), setTheme: (theme) => set({ theme }),}));// src/components/Header.jsximport { useStore } from '../store/useStore';export function Header() { const { user, theme, setTheme } = useStore(); return ( <header> <h1>My App</h1> {user ? <p>Welcome, {user.name}</p> : <p>Please login</p>} <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}> {theme === 'light' ? '🌙' : '☀️'} </button> </header> );}服务端状态管理(React Query):// src/lib/api.tsexport async function fetchPosts() { const response = await fetch('/api/posts'); return response.json();}export async function fetchPost(id: string) { const response = await fetch(`/api/posts/${id}`); return response.json();}// src/components/PostList.jsximport { useQuery } from '@tanstack/react-query';import { fetchPosts } from '../lib/api';export function PostList() { const { data, isLoading, error } = useQuery({ queryKey: ['posts'], queryFn: fetchPosts, }); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return ( <ul> {data.map(post => ( <li key={post.id}> <a href={`/blog/${post.slug}`}>{post.title}</a> </li> ))} </ul> );}表单状态管理:// src/components/ContactForm.jsximport { useState } from 'react';export function ContactForm() { const [formData, setFormData] = useState({ name: '', email: '', message: '', }); const [errors, setErrors] = useState({}); const [isSubmitting, setIsSubmitting] = useState(false); function handleChange(e) { const { name, value } = e.target; setFormData(prev => ({ ...prev, [name]: value })); } async function handleSubmit(e) { e.preventDefault(); setIsSubmitting(true); try { const response = await fetch('/api/contact', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData), }); if (!response.ok) throw new Error('Submission failed'); alert('Message sent!'); setFormData({ name: '', email: '', message: '' }); } catch (error) { setErrors({ submit: error.message }); } finally { setIsSubmitting(false); } } return ( <form onSubmit={handleSubmit}> <input name="name" value={formData.name} onChange={handleChange} placeholder="Name" required /> <input name="email" type="email" value={formData.email} onChange={handleChange} placeholder="Email" required /> <textarea name="message" value={formData.message} onChange={handleChange} placeholder="Message" required /> <button type="submit" disabled={isSubmitting}> {isSubmitting ? 'Sending...' : 'Send'} </button> {errors.submit && <p className="error">{errors.submit}</p>} </form> );}URL 状态管理:// src/components/ProductFilter.jsximport { useSearchParams } from 'react-router-dom';export function ProductFilter() { const [searchParams, setSearchParams] = useSearchParams(); const category = searchParams.get('category') || 'all'; const sort = searchParams.get('sort') || 'name'; const page = parseInt(searchParams.get('page') || '1'); function updateFilter(key, value) { setSearchParams(prev => { const newParams = new URLSearchParams(prev); newParams.set(key, value); return newParams; }); } return ( <div> <select value={category} onChange={(e) => updateFilter('category', e.target.value)} > <option value="all">All Categories</option> <option value="electronics">Electronics</option> <option value="clothing">Clothing</option> </select> <select value={sort} onChange={(e) => updateFilter('sort', e.target.value)} > <option value="name">Name</option> <option value="price">Price</option> <option value="date">Date</option> </select> <div> <button disabled={page === 1} onClick={() => updateFilter('page', page - 1)} > Previous </button> <span>Page {page}</span> <button onClick={() => updateFilter('page', page + 1)}> Next </button> </div> </div> );}服务端状态(SSR):---// src/pages/dashboard.astroimport { getEntry } from 'astro:content';// 服务端获取数据const user = await getUser(Astro.request);const posts = await getCollection('blog');const stats = await fetchStats(user.id);---<div class="dashboard"> <h1>Welcome, {user.name}</h1> <div class="stats"> <div class="stat"> <h3>Total Posts</h3> <p>{stats.totalPosts}</p> </div> <div class="stat"> <h3>Total Views</h3> <p>{stats.totalViews}</p> </div> </div> <div class="posts"> {posts.map(post => ( <article> <h2>{post.data.title}</h2> <p>{post.data.description}</p> </article> ))} </div></div>最佳实践:选择合适的状态管理方案:简单状态:使用框架内置状态(useState、ref)复杂状态:使用状态管理库(Zustand、Redux)服务端状态:使用数据获取库(React Query)保持状态简洁:只存储必要的数据避免冗余状态使用派生状态性能优化:使用 memo 避免不必要的重渲染实现虚拟滚动处理大量数据使用防抖和节流优化频繁更新类型安全:使用 TypeScript 定义状态类型确保状态更新类型正确使用类型推导减少错误测试状态管理:编写单元测试验证状态逻辑测试状态更新函数模拟异步状态更新Astro 的状态管理方案灵活多样,可以根据项目需求选择最适合的方案。
阅读 0·2月21日 15:22

Expo应用中如何进行状态管理?有哪些推荐方案?

Expo应用的状态管理是一个重要的架构决策,需要根据项目规模、团队经验和性能要求选择合适的方案。Expo本身不提供特定的状态管理库,但支持所有React Native的状态管理解决方案。主流状态管理方案:React Context + Hooks适用于中小型应用,简单直接。实现示例:// Context创建const AppContext = createContext();// Provider组件export function AppProvider({ children }) { const [state, setState] = useState(initialState); return ( <AppContext.Provider value={{ state, setState }}> {children} </AppContext.Provider> );}// 使用Contextfunction MyComponent() { const { state, setState } = useContext(AppContext); return <Text>{state.value}</Text>;}优点:无需额外依赖简单易用性能良好(配合useMemo和useCallback)缺点:大型应用可能导致性能问题缺少中间件支持调试工具有限Redux Toolkit适用于中大型应用,提供完整的生态系统。安装和配置:npm install @reduxjs/toolkit react-redux实现示例:// 创建sliceimport { createSlice } from '@reduxjs/toolkit';const counterSlice = createSlice({ name: 'counter', initialState: { value: 0 }, reducers: { increment: (state) => { state.value += 1; }, decrement: (state) => { state.value -= 1; }, },});export const { increment, decrement } = counterSlice.actions;export default counterSlice.reducer;// 配置storeimport { configureStore } from '@reduxjs/toolkit';const store = configureStore({ reducer: { counter: counterReducer, },});// 在Expo中使用import { Provider } from 'react-redux';function App() { return ( <Provider store={store}> <RootNavigator /> </Provider> );}优点:强大的中间件支持优秀的调试工具(Redux DevTools)时间旅行调试丰富的生态系统缺点:学习曲线较陡样板代码较多可能过度设计小型应用Zustand轻量级状态管理库,API简洁。安装和配置:npm install zustand实现示例:import create from 'zustand';const useStore = create((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })),}));// 在组件中使用function Counter() { const { count, increment, decrement } = useStore(); return ( <View> <Text>{count}</Text> <Button title="+" onPress={increment} /> <Button title="-" onPress={decrement} /> </View> );}优点:API简洁无需Provider性能优秀TypeScript支持良好缺点:生态系统较小中间件支持有限Jotai基于原子的状态管理,灵活且高性能。安装和配置:npm install jotai实现示例:import { atom, useAtom } from 'jotai';// 创建atomconst countAtom = atom(0);const doubledAtom = atom((get) => get(countAtom) * 2);// 在组件中使用function Counter() { const [count, setCount] = useAtom(countAtom); const [doubled] = useAtom(doubledAtom); return ( <View> <Text>Count: {count}</Text> <Text>Doubled: {doubled}</Text> <Button title="Increment" onPress={() => setCount(count + 1)} /> </View> );}优点:细粒度更新无需Provider高性能灵活组合缺点:相对较新学习曲线RecoilFacebook开发的实验性状态管理库。安装和配置:npm install recoil实现示例:import { atom, useRecoilState, useRecoilValue } from 'recoil';// 创建atomconst countState = atom({ key: 'countState', default: 0,});// 在组件中使用function Counter() { const [count, setCount] = useRecoilState(countState); return ( <View> <Text>{count}</Text> <Button title="Increment" onPress={() => setCount(count + 1)} /> </View> );}选择建议:小型应用:React Context + Hooks简单直接无需额外依赖快速开发中型应用:Zustand或Jotai轻量级API简洁性能优秀大型应用:Redux Toolkit完整生态系统强大的调试工具团队协作友好需要细粒度控制:Jotai或Recoil原子化状态精确更新高性能最佳实践:按需选择:根据项目规模选择合适的方案保持简单:不要过度设计TypeScript支持:优先选择有良好TypeScript支持的库性能优化:使用useMemo、useCallback等优化渲染持久化:考虑使用AsyncStorage或expo-secure-store持久化状态持久化方案:// 使用expo-secure-storeimport * as SecureStore from 'expo-secure-store';// 保存状态await SecureStore.setItemAsync('userState', JSON.stringify(userState));// 恢复状态const savedState = await SecureStore.getItemAsync('userState');if (savedState) { const userState = JSON.parse(savedState); // 恢复到状态管理库}选择合适的状态管理方案是Expo应用架构的关键决策,需要综合考虑项目需求、团队技能和长期维护成本。
阅读 0·2月21日 15:22

SVG 如何与 CSS 结合使用

SVG 与 CSS 的结合使用可以创建丰富的视觉效果和交互体验。以下是 SVG 与 CSS 结合的多种方式:1. 基本样式应用CSS 可以直接应用于 SVG 元素,就像应用于 HTML 元素一样。<svg width="200" height="200"> <style> .circle { fill: blue; stroke: red; stroke-width: 2; } .rectangle { fill: green; stroke: black; stroke-width: 3; } </style> <circle class="circle" cx="100" cy="100" r="50" /> <rect class="rectangle" x="20" y="20" width="80" height="60" /></svg>2. CSS 伪类使用 CSS 伪类实现交互效果。<svg width="200" height="200"> <style> .interactive { fill: blue; transition: all 0.3s ease; cursor: pointer; } .interactive:hover { fill: red; transform: scale(1.1); } .interactive:active { fill: green; } .interactive:focus { outline: 3px solid #005fcc; outline-offset: 2px; } </style> <circle class="interactive" cx="100" cy="100" r="50" tabindex="0" /></svg>3. CSS 动画使用 CSS 的 @keyframes 实现动画效果。<svg width="200" height="200"> <style> .rotating { transform-origin: center; animation: rotate 2s linear infinite; } .pulsing { animation: pulse 1s ease-in-out infinite; } @keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.2); } } </style> <rect class="rotating" x="75" y="75" width="50" height="50" fill="blue" /> <circle class="pulsing" cx="100" cy="100" r="30" fill="red" /></svg>4. CSS 变量使用 CSS 变量实现动态样式。<svg width="200" height="200"> <style> :root { --primary-color: #3498db; --secondary-color: #e74c3c; --stroke-width: 2px; } .dynamic { fill: var(--primary-color); stroke: var(--secondary-color); stroke-width: var(--stroke-width); } .dynamic:hover { --primary-color: #2ecc71; } </style> <circle class="dynamic" cx="100" cy="100" r="50" /></svg>5. 外部 CSS 文件将 SVG 样式放在外部 CSS 文件中。<!-- HTML 文件 --><link rel="stylesheet" href="styles.css" /><svg width="200" height="200"> <circle class="styled-circle" cx="100" cy="100" r="50" /></svg>/* styles.css */.styled-circle { fill: #3498db; stroke: #2c3e50; stroke-width: 3px; transition: all 0.3s ease;}.styled-circle:hover { fill: #e74c3c; transform: scale(1.1);}6. CSS 选择器使用各种 CSS 选择器精确控制 SVG 元素。<svg width="200" height="200"> <style> /* ID 选择器 */ #unique-circle { fill: red; } /* 类选择器 */ .blue-circle { fill: blue; } /* 属性选择器 */ circle[fill="green"] { stroke: black; stroke-width: 2; } /* 后代选择器 */ .group circle { fill: purple; } /* 伪元素 */ .special::after { content: ''; /* SVG 不支持伪元素,但可以用于 SVG 内的 HTML 元素 */ } </style> <circle id="unique-circle" cx="50" cy="50" r="20" /> <circle class="blue-circle" cx="100" cy="50" r="20" /> <circle fill="green" cx="150" cy="50" r="20" /> <g class="group"> <circle cx="50" cy="100" r="20" /> <circle cx="100" cy="100" r="20" /> </g></svg>7. 响应式 SVG结合 CSS 媒体查询实现响应式 SVG。<svg width="100%" height="auto" viewBox="0 0 200 200"> <style> @media (max-width: 768px) { .responsive-element { fill: blue; } } @media (min-width: 769px) { .responsive-element { fill: red; } } </style> <circle class="responsive-element" cx="100" cy="100" r="50" /></svg>8. CSS transform使用 CSS transform 实现变换效果。<svg width="200" height="200"> <style> .transformed { transition: transform 0.3s ease; } .transformed:hover { transform: translate(20px, 20px) rotate(45deg) scale(1.2); } </style> <rect class="transformed" x="75" y="75" width="50" height="50" fill="blue" /></svg>最佳实践:优先使用 CSS 实现动画和交互将样式与结构分离使用 CSS 变量提高可维护性考虑性能,避免过度使用复杂动画测试跨浏览器兼容性使用 CSS 过渡实现平滑效果
阅读 0·2月21日 15:22

SVG 与其他图形格式有什么区别和优劣

SVG 与其他图形格式的对比是选择合适技术的重要依据。以下是 SVG 与其他常见图形格式的详细对比:1. SVG vs PNG/JPG(位图格式)SVG 优势:矢量图形,无限缩放不失真文件大小通常更小可编辑和动画支持 CSS 和 JavaScript 交互可访问性好,支持屏幕阅读器SEO 友好,内容可被搜索引擎索引PNG/JPG 优势:适合照片和复杂图像浏览器兼容性更好文件格式更简单不需要额外的解析开销适用场景:SVG:图标、logo、图表、插画、需要缩放的图形PNG/JPG:照片、复杂图像、不需要编辑的图形2. SVG vs GIFSVG 优势:动画更流畅(CSS/JS 动画)文件大小更小支持交互颜色深度更高无版权问题GIF 优势:动画兼容性更好支持透明背景文件格式简单社交媒体支持广泛适用场景:SVG:现代网页动画、交互式动画GIF:简单的循环动画、社交媒体分享3. SVG vs CanvasSVG 优势:矢量图形,无限缩放DOM 元素,支持事件和交互可访问性好易于调试和编辑SEO 友好Canvas 优势:渲染大量对象时性能更好适合游戏和复杂动画像素级控制不受 DOM 限制适用场景:SVG:图标、图表、简单动画、需要交互的场景Canvas:游戏、大数据可视化、复杂动画、高性能场景4. SVG vs WebPSVG 优势:矢量图形,可无限缩放可编辑和动画支持交互可访问性好WebP 优势:压缩率更高,文件更小支持动画浏览器支持越来越广泛适合照片和复杂图像适用场景:SVG:矢量图形、图标、图表WebP:照片、复杂图像、需要高压缩率的场景5. SVG vs 图标字体(Icon Fonts)SVG 优势:可以使用多种颜色和渐变支持动画和交互更好的可访问性可以精确控制样式不需要额外的字体文件图标字体优势:实现简单,兼容性好可以像文字一样设置样式文件大小小支持文本效果适用场景:SVG:需要多色图标、动画图标、交互式图标图标字体:单色图标、需要广泛兼容性的场景6. SVG vs PDFSVG 优势:网页原生支持可以嵌入 HTML支持动画和交互文件格式更轻量PDF 优势:打印质量更好跨平台一致性支持复杂的文档结构更适合文档分发适用场景:SVG:网页图形、交互式内容PDF:文档、打印、跨平台分发7. 性能对比文件大小:简单图形:SVG < PNG < JPG复杂图像:JPG < WebP < PNG < SVG图标:SVG ≈ 图标字体 < PNG渲染性能:少量元素:SVG > Canvas大量元素:Canvas > SVG动画:CSS 动画 > SMIL > JS 动画8. 选择建议选择 SVG 的情况:需要矢量缩放需要交互和动画需要可访问性需要编辑和修改SEO 重要选择其他格式的情况:照片或复杂图像:PNG/JPG/WebP高性能渲染大量对象:Canvas简单单色图标:图标字体文档分发:PDF最佳实践:根据需求选择合适的格式可以组合使用多种格式考虑浏览器兼容性优化文件大小和性能测试不同场景的效果
阅读 0·2月21日 15:22

如何优化 SVG 以提升性能

SVG 优化对于提升网页性能和用户体验非常重要。以下是常见的 SVG 优化技巧:1. 移除不必要的代码删除编辑器添加的元数据(如 <title>Created with...</title>)移除注释和空行删除未使用的定义和样式使用工具如 SVGO 进行自动优化2. 简化路径使用更短的路径命令(如用 h 代替 H)合并相邻的相同命令减少小数位数精度(如 50.123456 改为 50.12)使用相对坐标代替绝对坐标3. 优化属性移除默认值属性(如 fill="black" 可以省略)使用简写属性(如 stroke 代替 stroke-color)合并相同的样式到 class 或 <style> 标签4. 压缩 SVG 文件使用 gzip 压缩(服务器配置)使用 SVGO 等工具进行优化移除 DOCTYPE 声明(不影响渲染)5. 使用 SVG Sprite将多个图标合并到一个 SVG 文件中使用 <symbol> 和 <use> 元素复用图形减少 HTTP 请求示例:<svg style="display: none;"> <symbol id="icon-home" viewBox="0 0 24 24"> <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/> </symbol> <symbol id="icon-user" viewBox="0 0 24 24"> <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/> </symbol></svg><!-- 使用图标 --><svg><use href="#icon-home"/></svg><svg><use href="#icon-user"/></svg>6. 内联关键 SVG首屏渲染的关键 SVG 内联到 HTML避免额外的 HTTP 请求非关键 SVG 可以延迟加载7. 使用 viewBox 代替 width/height使用 viewBox 实现响应式缩放通过 CSS 控制显示尺寸提高灵活性8. 减少元素数量合并可以合并的路径使用 <g> 分组而不是多个独立元素避免不必要的嵌套9. 优化动画性能优先使用 CSS 动画(GPU 加速)使用 transform 和 opacity 属性避免动画 width、height、left、top 等属性10. 工具推荐SVGO:强大的 SVG 优化工具SVGOMG:在线 SVG 优化工具Iconfont:图标字体和 SVG 管理Figma/Sketch:导出时优化 SVG优化示例:# 使用 SVGO 优化npx svgo input.svg -o output.svg# 批量优化npx svgo -f ./icons -o ./optimized性能测试:使用 Lighthouse 测试页面性能检查 SVG 文件大小和加载时间监控渲染性能
阅读 0·2月21日 15:21

SVG 动画有哪些实现方式,它们之间有什么区别

SVG 动画可以通过多种方式实现,每种方式都有其特点和适用场景:1. SMIL 动画(原生 SVG 动画)SMIL(Synchronized Multimedia Integration Language)是 SVG 原生支持的动画语法。常用元素:<animate>:基本属性动画<animateTransform>:变换动画(旋转、缩放、平移、倾斜)<animateMotion>:沿路径运动示例:<svg width="200" height="200"> <circle cx="50" cy="50" r="20" fill="red"> <animate attributeName="cx" from="50" to="150" dur="2s" repeatCount="indefinite" /> <animate attributeName="fill" values="red;blue;red" dur="2s" repeatCount="indefinite" /> </circle></svg>优点:原生支持,无需 JavaScript声明式语法,易于理解性能良好缺点:Chrome 已宣布弃用 SMIL灵活性有限调试困难2. CSS 动画使用 CSS 的 @keyframes 和 transition 属性。示例:<svg width="200" height="200"> <style> .circle { animation: move 2s infinite alternate; transition: fill 0.3s; } .circle:hover { fill: blue; } @keyframes move { from { transform: translateX(0); } to { transform: translateX(100px); } } </style> <circle class="circle" cx="50" cy="50" r="20" fill="red" /></svg>优点:广泛支持,标准技术性能优秀(GPU 加速)易于维护和调试支持 hover 等交互状态缺点:无法直接操作 SVG 内部属性复杂动画需要大量 CSS3. JavaScript 动画使用 JavaScript 操作 SVG DOM 或使用动画库。原生 JavaScript 示例:const circle = document.querySelector('circle');let position = 50;function animate() { position += 1; circle.setAttribute('cx', position); if (position < 150) { requestAnimationFrame(animate); }}animate();使用 GSAP 库示例:gsap.to('circle', { attr: { cx: 150 }, duration: 2, repeat: -1, yoyo: true});优点:最大灵活性可以实现复杂逻辑丰富的动画库支持(GSAP、Anime.js 等)可以与其他 JavaScript 交互缺点:需要编写更多代码性能需要优化依赖 JavaScript选择建议:简单动画:优先使用 CSS 动画复杂交互:使用 JavaScript + 动画库向后兼容:避免使用 SMIL性能关键:优先 CSS 和 requestAnimationFrame
阅读 0·2月21日 15:21

SVG 和 Canvas 有什么区别,如何选择使用

SVG 和 Canvas 都是用于在网页上绘制图形的技术,但它们在工作原理、性能和适用场景上有显著差异:1. 工作原理SVG:基于 DOM 的矢量图形,每个图形元素都是独立的 DOM 节点Canvas:基于像素的位图,通过 JavaScript 在画布上绘制,最终生成位图2. 图形类型SVG:矢量图形,无限缩放不失真Canvas:位图,缩放会失真3. DOM 交互SVG:每个元素都可以绑定事件(click, hover 等)可以通过 CSS 样式化支持标准的 DOM 操作适合需要交互的场景Canvas:整个画布是一个 DOM 元素需要手动计算点击位置和碰撞检测无法直接通过 CSS 样式化内部元素适合纯展示场景4. 性能对比SVG:元素数量较少时性能良好元素数量过多(数千个)时性能下降适合图标、图表、简单动画Canvas:元素数量多时性能优秀渲染大量对象时性能稳定适合游戏、数据可视化、复杂动画5. 可访问性SVG:支持屏幕阅读器可以添加 title 和 desc 元素搜索引擎可以索引内容SEO 友好Canvas:可访问性较差需要额外实现 ARIA 支持搜索引擎难以索引内容6. 文件大小SVG:简单图形文件小复杂图形文件可能较大可以 gzip 压缩Canvas:不涉及文件大小(动态生成)导出为图片时文件大小取决于分辨率7. 适用场景SVG 适合:图标和 logo简单到中等复杂度的图表需要交互的图形需要缩放的图形打印和高质量输出SEO 重要的内容Canvas 适合:游戏开发大数据可视化复杂动画图像处理实时渲染高性能要求的场景8. 选择建议需要交互和可访问性:选择 SVG需要高性能渲染大量对象:选择 Canvas需要矢量缩放:选择 SVG需要像素级控制:选择 Canvas可以结合使用两者,发挥各自优势
阅读 0·2月21日 15:21

如何创建和管理 SVG 图标系统

SVG 图标系统是现代 Web 开发中的重要组成部分。以下是创建和管理 SVG 图标系统的最佳实践:1. SVG Sprite 技术将多个图标合并到一个 SVG 文件中,减少 HTTP 请求。<!-- icons.svg --><svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="icon-home" viewBox="0 0 24 24"> <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" fill="currentColor"/> </symbol> <symbol id="icon-user" viewBox="0 0 24 24"> <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="currentColor"/> </symbol> <symbol id="icon-search" viewBox="0 0 24 24"> <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" fill="currentColor"/> </symbol></svg>2. 使用 SVG Sprite在 HTML 中引用图标。<!-- 引用图标文件 --><body> <!-- 隐藏的 SVG sprite --> <svg style="display: none;"> <use href="icons.svg#icon-home" /> <use href="icons.svg#icon-user" /> <use href="icons.svg#icon-search" /> </svg> <!-- 使用图标 --> <button> <svg width="24" height="24"> <use href="#icon-home" /> </svg> 首页 </button> <button> <svg width="24" height="24"> <use href="#icon-user" /> </svg> 用户 </button></body>3. 内联 SVG 图标直接在 HTML 中内联 SVG 代码。<button> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" /> </svg> 首页</button>4. CSS 图标类使用 CSS 类管理图标样式。.icon { display: inline-block; width: 1em; height: 1em; vertical-align: middle; fill: currentColor;}.icon-sm { width: 16px; height: 16px;}.icon-md { width: 24px; height: 24px;}.icon-lg { width: 32px; height: 32px;}.icon-xl { width: 48px; height: 48px;}/* 图标颜色 */.icon-primary { color: #3498db;}.icon-success { color: #2ecc71;}.icon-warning { color: #f39c12;}.icon-danger { color: #e74c3c;}5. React 组件封装在 React 中封装 SVG 图标组件。// Icon.jsximport React from 'react';const Icon = ({ name, size = 24, color = 'currentColor', className = '' }) => { const icons = { home: ( <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" /> ), user: ( <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" /> ), search: ( <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" /> ) }; return ( <svg width={size} height={size} viewBox="0 0 24 24" fill={color} className={`icon ${className}`} aria-hidden="true" > {icons[name]} </svg> );};export default Icon;// 使用示例<Icon name="home" size={24} color="#3498db" /><Icon name="user" size={32} />6. Vue 组件封装在 Vue 中封装 SVG 图标组件。<!-- Icon.vue --><template> <svg :width="size" :height="size" viewBox="0 0 24 24" :fill="color" class="icon" aria-hidden="true" > <slot /> </svg></template><script>export default { name: 'Icon', props: { size: { type: Number, default: 24 }, color: { type: String, default: 'currentColor' } }}</script><!-- 使用示例 --><Icon :size="24" color="#3498db"> <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" /></Icon>7. 自动化工具使用工具自动生成图标系统。使用 SVGO 优化:npx svgo -f ./icons -o ./optimized使用 Iconify:<script src="https://code.iconify.design/1/1.0.7/iconify.min.js"></script><span class="iconify" data-icon="mdi:home"></span>8. 最佳实践性能优化:使用 SVG Sprite 减少 HTTP 请求优化 SVG 文件大小(SVGO)首屏图标内联,其他延迟加载使用 gzip 压缩可访问性:添加 aria-hidden="true"(装饰性图标)添加 aria-label(功能性图标)使用 role="img"支持键盘导航维护性:统一命名规范使用组件封装版本控制图标库文档化图标使用响应式设计:使用 viewBox 实现响应式通过 CSS 控制尺寸支持不同屏幕密度测试各种设备
阅读 0·2月21日 15:21