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

服务端面试题手册

SolidJS 中的控制流组件有哪些?如何使用 Show、For、Switch 等?

SolidJS 提供了多种控制流组件,用于条件渲染和列表渲染:条件渲染:// Show - 条件渲染(类似 React 的条件渲染)<Show when={isLoggedIn()} fallback={<Login />}> <Dashboard /></Show>// Switch - 多条件分支<Switch fallback={<NotFound />}> <Match when={status() === 'loading'}> <Loading /> </Match> <Match when={status() === 'success'}> <Success /> </Match> <Match when={status() === 'error'}> <Error /> </Match></Switch>列表渲染:// For - 列表渲染(推荐使用)<For each={items()}> {(item, index) => ( <div> {index()}: {item.name} </div> )}</For>// Index - 使用索引作为 key(性能更好)<Index each={items()}> {(item, index) => ( <div> {index}: {item().name} </div> )}</Index>动态组件:// Dynamic - 动态组件渲染<Dynamic component={currentComponent()} props={props} />// Portal - 传送门到其他 DOM 节点<Portal mount={document.getElementById('modal-root')}> <Modal /></Portal>Suspense - 异步加载:<Suspense fallback={<Loading />}> <AsyncComponent /></Suspense>// 嵌套 Suspense<Suspense fallback={<Skeleton />}> <Suspense fallback={<LoadingAvatar />}> <UserAvatar /> </Suspense> <Suspense fallback={<LoadingPosts />}> <UserPosts /> </Suspense></Suspense>ErrorBoundary - 错误边界:<ErrorBoundary fallback={(err) => <ErrorPage error={err} />}> <Component /></ErrorBoundary>最佳实践:使用 For 而不是 map 进行列表渲染使用 Index 当列表项顺序不变时使用 Show 进行简单条件判断使用 Switch 处理多条件分支使用 Suspense 处理异步组件
阅读 0·2月21日 15:23

SolidJS 如何与 TypeScript 配合使用?有哪些类型定义最佳实践?

SolidJS 提供了完善的 TypeScript 支持和类型系统:基本类型定义:import { createSignal, createEffect } from 'solid-js';// 定义 signal 类型const [count, setCount] = createSignal<number>(0);const [user, setUser] = createSignal<User | null>(null);// 定义 effectcreateEffect(() => { const value = count(); // value 自动推断为 number console.log(value);});组件类型定义:// 函数组件interface Props { title: string; count: number; onIncrement?: () => void;}function Counter(props: Props) { return ( <div> <h1>{props.title}</h1> <p>Count: {props.count}</p> <button onClick={props.onIncrement}>+</button> </div> );}// 使用 JSX.IntrinsicElements 扩展原生元素类型declare module 'solid-js' { namespace JSX { interface IntrinsicElements { 'my-custom-element': { value: string; onChange: (value: string) => void; }; } }}Store 类型定义:import { createStore } from 'solid-js/store';interface AppState { user: { name: string; age: number; email: string; }; items: Array<{ id: number; name: string; }>;}const [state, setState] = createStore<AppState>({ user: { name: '', age: 0, email: '' }, items: []});// 类型安全的更新setState('user', 'name', 'John'); // ✅ 正确setState('user', 'invalid', 'value'); // ❌ 类型错误Resource 类型定义:import { createResource } from 'solid-js';interface User { id: number; name: string; email: string;}const [user] = createResource<User>(fetchUser);const userValue = user(); // User | undefined// 使用泛型定义返回类型const [data] = createResource<User[], Error>(fetchUsers, { initialValue: []});Context 类型定义:import { createContext, useContext } from 'solid-js';interface ThemeContextType { theme: 'light' | 'dark'; toggleTheme: () => void;}const ThemeContext = createContext<ThemeContextType>();function useTheme() { const context = useContext(ThemeContext); if (!context) { throw new Error('useTheme must be used within ThemeProvider'); } return context;}类型工具:// 使用 utility typestype PartialState = Partial<AppState>;type RequiredState = Required<AppState>;type ReadonlyState = Readonly<AppState>;// 条件类型type SignalType<T> = T extends (...args: any[]) => any ? ReturnType<T> : T;最佳实践:为所有组件定义 Props 接口使用泛型定义 signal 和 store 类型为 context 定义明确的类型使用 utility types 简化类型定义启用严格模式检查
阅读 0·2月21日 15:23

SolidJS 如何进行单元测试和集成测试?有哪些测试工具推荐?

SolidJS 提供了强大的测试工具和方法来测试组件和响应式逻辑:测试工具:import { render, screen, fireEvent, waitFor } from '@solidjs/testing-library';import { describe, it, expect } from 'vitest';describe('Counter Component', () => { it('renders initial count', () => { render(() => <Counter />); expect(screen.getByText('Count: 0')).toBeInTheDocument(); }); it('increments count when button clicked', async () => { render(() => <Counter />); const button = screen.getByText('+'); fireEvent.click(button); await waitFor(() => { expect(screen.getByText('Count: 1')).toBeInTheDocument(); }); });});测试响应式逻辑:import { createSignal, createEffect } from 'solid-js';import { createRoot } from 'solid-js';describe('Reactive Logic', () => { it('tracks signal changes', () => { createRoot((dispose) => { const [count, setCount] = createSignal(0); let effectCallCount = 0; createEffect(() => { effectCallCount++; count(); }); expect(effectCallCount).toBe(1); setCount(1); expect(effectCallCount).toBe(2); dispose(); }); });});测试异步操作:import { createResource } from 'solid-js';describe('Async Operations', () => { it('handles loading state', async () => { const fetchMock = vi.fn(() => new Promise(resolve => setTimeout(() => resolve({ data: 'test' }), 100)) ); const [data] = createResource(fetchMock); expect(data.loading).toBe(true); await waitFor(() => expect(data.loading).toBe(false)); expect(data()).toEqual({ data: 'test' }); });});测试用户交互:describe('User Interactions', () => { it('handles form submission', () => { render(() => <LoginForm />); const input = screen.getByLabelText('Email'); const button = screen.getByText('Submit'); fireEvent.input(input, { target: { value: 'test@example.com' } }); fireEvent.click(button); expect(screen.getByText('Submitted: test@example.com')).toBeInTheDocument(); });});测试路由:import { Router } from '@solidjs/router';describe('Routing', () => { it('renders correct component for route', () => { render(() => ( <Router> <Routes> <Route path="/" component={Home} /> <Route path="/about" component={About} /> </Routes> </Router> )); expect(screen.getByText('Home Page')).toBeInTheDocument(); });});最佳实践:使用 @solidjs/testing-library 进行组件测试使用 createRoot 包装测试以自动清理测试用户行为而非实现细节使用 waitFor 处理异步操作Mock 外部依赖和 API 调用保持测试独立和可重复
阅读 0·2月21日 15:23

SolidJS 如何实现服务端渲染(SSR)?有哪些渲染模式?

SolidJS 支持服务端渲染(SSR),提供多种渲染模式:基本 SSR 设置:// entry-server.jsximport { renderToString } from 'solid-js/web';import App from './App';export function render(url) { return renderToString(() => <App url={url} />);}// entry-client.jsximport { hydrate } from 'solid-js/web';import App from './App';hydrate(() => <App />, document);渲染模式:静态生成(SSG):import { renderToStringAsync } from 'solid-js/web';export async function getStaticPaths() { return ['/about', '/contact'];}export default async function Page({ params }) { return renderToStringAsync(() => <Component />);}服务端渲染(SSR):import { StartServer, createHandler } from '@solidjs/start/server';export default createHandler(() => ( <StartServer document={({ assets, children }) => ( <html> <head>{assets}</head> <body>{children}</body> </html> )} />));流式渲染:import { renderToStream } from 'solid-js/web';app.get('*', async (req, res) => { const stream = renderToStream(() => <App />); stream.pipe(res);});数据获取:// 使用 createResource 处理异步数据const [data] = createResource(fetchData, { initialValue: null, deferStream: true // 支持流式传输});// 服务端数据预取export async function getData() { return await fetchData();}同构应用:// 在服务端和客户端都能运行的代码function Component() { const isServer = useIsServer(); return ( <div> {isServer() ? 'Server' : 'Client'} </div> );}性能优化:使用 deferStream 延迟加载预取关键数据优化 hydration 过程使用 Suspense 边界
阅读 0·2月21日 15:22

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