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

服务端面试题手册

Service Worker 的生命周期是什么,如何实现离线缓存?

Service Worker 是一种特殊的 Web Worker,它作为网络代理运行在浏览器和服务器之间,提供离线功能、推送通知和后台同步等能力。Service Worker 的核心概念特点独立于页面生命周期运行拦截和处理网络请求必须在 HTTPS 环境下运行(localhost 除外)可以实现离线缓存和资源预加载支持推送通知和后台同步注册 Service Worker// 检查浏览器支持if ('serviceWorker' in navigator) { // 注册 Service Worker navigator.serviceWorker.register('/service-worker.js') .then(function(registration) { console.log('Service Worker 注册成功:', registration.scope); }) .catch(function(error) { console.log('Service Worker 注册失败:', error); });}Service Worker 生命周期1. Install(安装)// service-worker.jsconst CACHE_NAME = 'my-cache-v1';const urlsToCache = [ '/', '/styles/main.css', '/script/main.js', '/images/logo.png'];self.addEventListener('install', function(event) { // event.waitUntil 延迟安装完成 event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { console.log('已打开缓存'); return cache.addAll(urlsToCache); }) ); // 立即激活新的 Service Worker self.skipWaiting();});2. Activate(激活)self.addEventListener('activate', function(event) { event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { // 删除旧版本的缓存 if (cacheName !== CACHE_NAME) { console.log('删除旧缓存:', cacheName); return caches.delete(cacheName); } }) ); }) ); // 立即控制所有客户端 self.clients.claim();});3. Fetch(拦截请求)self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { // 缓存命中,返回缓存 if (response) { return response; } // 缓存未命中,发起网络请求 return fetch(event.request).then( function(response) { // 检查是否为有效响应 if (!response || response.status !== 200 || response.type !== 'basic') { return response; } // 克隆响应(响应流只能使用一次) const responseToCache = response.clone(); caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); }); return response; } ); }) );});缓存策略1. Cache First(缓存优先)self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { return response || fetch(event.request); }) );});2. Network First(网络优先)self.addEventListener('fetch', function(event) { event.respondWith( fetch(event.request) .then(function(response) { const responseToCache = response.clone(); caches.open(CACHE_NAME).then(function(cache) { cache.put(event.request, responseToCache); }); return response; }) .catch(function() { return caches.match(event.request); }) );});3. Network Only(仅网络)self.addEventListener('fetch', function(event) { event.respondWith(fetch(event.request));});4. Cache Only(仅缓存)self.addEventListener('fetch', function(event) { event.respondWith(caches.match(event.request));});5. Stale While Revalidate(缓存优先,后台更新)self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request).then(function(cachedResponse) { const fetchPromise = fetch(event.request).then(function(networkResponse) { caches.open(CACHE_NAME).then(function(cache) { cache.put(event.request, networkResponse.clone()); }); return networkResponse; }); return cachedResponse || fetchPromise; }) );});推送通知订阅推送// 主线程function subscribeUser() { return navigator.serviceWorker.register('/service-worker.js') .then(function(registration) { const subscribeOptions = { userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array( 'BEl62iUYgUivxIkv69yViEuiBIa-IbRMhKDbjjVdMlzQJd0_...' ) }; return registration.pushManager.subscribe(subscribeOptions); }) .then(function(pushSubscription) { console.log('已接收推送订阅:', pushSubscription); return pushSubscription; });}处理推送消息// service-worker.jsself.addEventListener('push', function(event) { const options = { body: event.data ? event.data.text() : '新消息', icon: '/images/icon.png', badge: '/images/badge.png', vibrate: [100, 50, 100], data: { dateOfArrival: Date.now(), primaryKey: 1 } }; event.waitUntil( self.registration.showNotification('推送通知', options) );});// 处理通知点击self.addEventListener('notificationclick', function(event) { event.notification.close(); event.waitUntil( clients.openWindow('/') );});后台同步注册同步事件// 主线程navigator.serviceWorker.ready.then(function(registration) { registration.sync.register('sync-messages');});处理同步事件// service-worker.jsself.addEventListener('sync', function(event) { if (event.tag === 'sync-messages') { event.waitUntil(syncMessages()); }});function syncMessages() { return fetch('/api/sync-messages', { method: 'POST', body: JSON.stringify(getPendingMessages()) });}调试 Service WorkerChrome DevTools打开 Chrome DevTools切换到 "Application" 面板左侧选择 "Service Workers"可以查看状态、更新、注销等操作更新 Service Worker// 手动更新navigator.serviceWorker.ready.then(function(registration) { registration.update();});// 监听更新navigator.serviceWorker.addEventListener('controllerchange', function() { console.log('Service Worker 已更新'); window.location.reload();});最佳实践HTTPS 要求:生产环境必须使用 HTTPS缓存版本管理:使用版本号管理缓存错误处理:添加完善的错误处理渐进增强:确保在不支持 Service Worker 的浏览器中也能正常工作资源清理:及时清理旧版本的缓存性能优化:合理选择缓存策略安全性:验证请求来源,防止 CSRF 攻击
阅读 0·2月21日 15:24

Serverless 架构下的本地开发环境如何搭建?

Serverless 架构下的本地开发环境搭建需要模拟云端执行环境,以便在本地进行开发和调试:开发工具选择:1. Serverless Framework功能特点:支持多云平台,提供完整的开发、测试、部署流程本地模拟:通过 serverless offline 插件模拟 API Gateway 和 Lambda优势:跨平台支持,社区活跃,文档完善2. AWS SAM CLI功能特点:AWS 官方工具,与 SAM 模板无缝集成本地模拟:sam local invoke 可以在本地调用 Lambda 函数优势:与 AWS 生态深度集成,支持热重载3. Docker 容器功能特点:使用 Docker 容器模拟 Lambda 运行时环境本地模拟:可以完全模拟云端环境优势:环境一致性高,适合复杂场景本地开发最佳实践:1. 环境配置环境变量管理:使用 dotenv 管理本地环境变量配置文件:使用配置文件区分不同环境依赖管理:使用 npm、pip 等工具管理依赖2. 调试技巧断点调试:使用 VS Code 的调试功能设置断点日志输出:使用 console.log、print 等输出调试信息单元测试:编写单元测试验证函数逻辑3. 常见挑战环境差异:本地环境与云端环境可能存在差异依赖问题:云端依赖版本可能与本地不一致资源限制:本地无法完全模拟云端资源限制解决方案:使用 Docker:确保环境一致性CI/CD 集成:在 CI/CD 流程中进行测试云上测试:定期在云端进行集成测试面试者应能分享本地开发环境搭建的经验和遇到的问题。
阅读 0·2月21日 15:24

SharedWorker 如何实现跨标签页通信?

SharedWorker 允许多个浏览器上下文(如不同的标签页、iframe)共享同一个 Worker 实例,实现跨页面通信和状态共享。SharedWorker 的核心概念特点多个页面可以连接到同一个 SharedWorker使用 port 对象进行通信通过 onconnect 事件处理新连接适合需要跨标签页同步的场景基本使用创建 SharedWorker// 主线程代码const sharedWorker = new SharedWorker('shared-worker.js');// 启动端口连接sharedWorker.port.start();// 发送消息sharedWorker.port.postMessage('Hello from page 1');// 接收消息sharedWorker.port.onmessage = function(event) { console.log('Received:', event.data);};SharedWorker 脚本实现// shared-worker.jsconst connections = [];// 监听新连接self.onconnect = function(event) { const port = event.ports[0]; connections.push(port); // 监听来自该端口的消息 port.onmessage = function(e) { console.log('Received from port:', e.data); // 广播消息给所有连接 connections.forEach(conn => { if (conn !== port) { conn.postMessage(e.data); } }); }; // 启动端口 port.start();};实际应用场景1. 跨标签页状态同步// shared-worker.jslet sharedState = { user: null, theme: 'light', notifications: []};self.onconnect = function(event) { const port = event.ports[0]; // 发送当前状态给新连接 port.postMessage({ type: 'init', state: sharedState }); port.onmessage = function(e) { if (e.data.type === 'updateState') { sharedState = { ...sharedState, ...e.data.state }; // 广播状态更新给所有连接 connections.forEach(conn => { conn.postMessage({ type: 'stateUpdated', state: sharedState }); }); } }; connections.push(port); port.start();};2. 跨标签页聊天// shared-worker.jsconst connections = [];const messages = [];self.onconnect = function(event) { const port = event.ports[0]; connections.push(port); // 发送历史消息 port.postMessage({ type: 'history', messages }); port.onmessage = function(e) { const message = { text: e.data.text, user: e.data.user, timestamp: Date.now() }; messages.push(message); // 广播新消息给所有连接 connections.forEach(conn => { conn.postMessage({ type: 'newMessage', message }); }); }; port.start();};3. 共享计数器// shared-worker.jslet counter = 0;self.onconnect = function(event) { const port = event.ports[0]; // 发送当前计数 port.postMessage({ type: 'counter', value: counter }); port.onmessage = function(e) { if (e.data.type === 'increment') { counter++; } else if (e.data.type === 'decrement') { counter--; } else if (e.data.type === 'reset') { counter = 0; } // 广播更新后的计数 connections.forEach(conn => { conn.postMessage({ type: 'counter', value: counter }); }); }; connections.push(port); port.start();};高级用法1. 连接管理// shared-worker.jsconst connections = new Map(); // 使用 Map 管理连接self.onconnect = function(event) { const port = event.ports[0]; const connectionId = Date.now() + Math.random(); connections.set(connectionId, port); port.onmessage = function(e) { if (e.data.type === 'ping') { port.postMessage({ type: 'pong', id: connectionId }); } }; port.onclose = function() { connections.delete(connectionId); console.log('Connection closed:', connectionId); }; port.start();};2. 广播和定向消息// shared-worker.jsconst connections = new Map();self.onconnect = function(event) { const port = event.ports[0]; const connectionId = Date.now().toString(); connections.set(connectionId, port); port.postMessage({ type: 'connected', id: connectionId }); port.onmessage = function(e) { if (e.data.type === 'broadcast') { // 广播给所有连接 connections.forEach((conn, id) => { conn.postMessage({ type: 'broadcast', from: connectionId, message: e.data.message }); }); } else if (e.data.type === 'sendTo') { // 定向发送给特定连接 const targetPort = connections.get(e.data.targetId); if (targetPort) { targetPort.postMessage({ type: 'private', from: connectionId, message: e.data.message }); } } }; port.start();};3. 持久化状态// shared-worker.jslet sharedState = {};// 从 localStorage 加载初始状态try { const savedState = localStorage.getItem('sharedWorkerState'); if (savedState) { sharedState = JSON.parse(savedState); }} catch (e) { console.error('Failed to load state:', e);}function saveState() { try { localStorage.setItem('sharedWorkerState', JSON.stringify(sharedState)); } catch (e) { console.error('Failed to save state:', e); }}self.onconnect = function(event) { const port = event.ports[0]; port.postMessage({ type: 'init', state: sharedState }); port.onmessage = function(e) { if (e.data.type === 'update') { sharedState = { ...sharedState, ...e.data.state }; saveState(); // 广播更新 connections.forEach(conn => { conn.postMessage({ type: 'stateUpdated', state: sharedState }); }); } }; connections.push(port); port.start();};注意事项1. 端口必须启动// ❌ 忘记启动端口const sharedWorker = new SharedWorker('worker.js');sharedWorker.port.postMessage('Hello'); // 可能不会发送// ✅ 正确启动端口const sharedWorker = new SharedWorker('worker.js');sharedWorker.port.start();sharedWorker.port.postMessage('Hello');2. 消息是异步的// 消息不会立即到达sharedWorker.port.postMessage('message1');sharedWorker.port.postMessage('message2');// 需要通过 onmessage 接收响应3. 连接断开处理// Worker 端port.onclose = function() { console.log('Port closed'); // 清理资源};// 主线程端sharedWorker.port.close();与 Dedicated Worker 的对比| 特性 | Dedicated Worker | Shared Worker ||------|------------------|---------------|| 作用域 | 单页面 | 多页面共享 || 连接数 | 1:1 | 1:N || 通信方式 | postMessage | port.postMessage || 适用场景 | 单页面后台任务 | 跨页面通信 |最佳实践连接管理:使用 Map 或数组管理所有连接错误处理:在连接和消息处理中添加错误处理状态同步:新连接时发送当前状态资源清理:连接断开时清理相关资源消息格式:使用统一的消息格式(如 { type, data })
阅读 0·2月21日 15:24

什么是Solidity编程语言?请解释Solidity的基本语法、特性和最佳实践

Solidity是以太坊智能合约的主要编程语言,理解其特性和最佳实践对于开发安全的智能合约至关重要。以下是Solidity的全面解析:Solidity简介Solidity是一种面向合约的高级编程语言,专门用于在以太坊虚拟机(EVM)上实现智能合约。它的语法受到C++、Python和JavaScript的影响。基本语法1. 合约结构// SPDX-License-Identifier: MITpragma solidity ^0.8.19;contract MyContract { // 状态变量 uint256 public myVariable; // 构造函数 constructor(uint256 initialValue) { myVariable = initialValue; } // 函数 function setVariable(uint256 newValue) public { myVariable = newValue; } // 事件 event ValueChanged(uint256 newValue);}2. 数据类型contract DataTypes { // 布尔类型 bool public isActive = true; // 整数类型 uint256 public amount = 100; int256 public temperature = -10; // 地址类型 address public owner; address payable public wallet; // 字节数组 bytes32 public hash; bytes public data; // 字符串 string public name = "My Contract"; // 数组 uint256[] public numbers; address[] public users; // 映射 mapping(address => uint256) public balances; // 结构体 struct User { string name; uint256 balance; } User public user; // 枚举 enum Status { Active, Inactive, Pending } Status public currentStatus;}函数和修饰符1. 函数类型contract FunctionTypes { // public函数:外部和内部可调用 function publicFunction() public pure returns (uint256) { return 1; } // private函数:仅内部可调用 function privateFunction() private pure returns (uint256) { return 2; } // internal函数:合约和派生合约可调用 function internalFunction() internal pure returns (uint256) { return 3; } // external函数:仅外部可调用 function externalFunction() external pure returns (uint256) { return 4; } // view函数:不修改状态 function viewFunction() public view returns (uint256) { return myVariable; } // pure函数:不读取或修改状态 function pureFunction(uint256 a, uint256 b) public pure returns (uint256) { return a + b; } // payable函数:可以接收ETH function deposit() public payable { balances[msg.sender] += msg.value; }}2. 修饰符contract Modifiers { address public owner; bool public paused; constructor() { owner = msg.sender; } // onlyOwner修饰符 modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } // whenNotPaused修饰符 modifier whenNotPaused() { require(!paused, "Contract is paused"); _; } // 使用修饰符 function sensitiveFunction() public onlyOwner whenNotPaused { // 敏感操作 }}继承和接口1. 继承contract Parent { uint256 public parentValue; function parentFunction() public pure returns (uint256) { return 1; }}contract Child is Parent { uint256 public childValue; function childFunction() public pure returns (uint256) { return 2; } // 重写父合约函数 function parentFunction() public pure override returns (uint256) { return 10; }}2. 接口interface IERC20 { function transfer(address to, uint256 amount) external returns (bool); function balanceOf(address account) external view returns (uint256);}contract MyContract { IERC20 public token; constructor(address tokenAddress) { token = IERC20(tokenAddress); } function getTokenBalance(address account) public view returns (uint256) { return token.balanceOf(account); }}事件和日志1. 事件定义和使用contract Events { event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); event LogData(uint256 timestamp, string message); function transfer(address to, uint256 value) public { emit Transfer(msg.sender, to, value); } function approve(address spender, uint256 value) public { emit Approval(msg.sender, spender, value); } function logMessage(string memory message) public { emit LogData(block.timestamp, message); }}错误处理1. requirecontract RequireExample { mapping(address => uint256) public balances; function withdraw(uint256 amount) public { require(balances[msg.sender] >= amount, "Insufficient balance"); balances[msg.sender] -= amount; payable(msg.sender).transfer(amount); }}2. revertcontract RevertExample { function process(uint256 value) public { if (value > 100) { revert("Value too large"); } // 处理逻辑 }}3. assertcontract AssertExample { uint256 public counter; function increment() public { counter++; assert(counter > 0); // 永远不应该失败 }}4. 自定义错误contract CustomErrors { error InsufficientBalance(uint256 requested, uint256 available); error InvalidAddress(); mapping(address => uint256) public balances; function withdraw(uint256 amount) public { uint256 balance = balances[msg.sender]; if (amount > balance) { revert InsufficientBalance(amount, balance); } balances[msg.sender] -= amount; payable(msg.sender).transfer(amount); }}全局变量1. 常用全局变量contract GlobalVariables { function getGlobalVariables() public view returns ( address sender, uint256 value, uint256 timestamp, uint256 blockNumber, bytes calldata data ) { return ( msg.sender, // 消息发送者 msg.value, // 发送的ETH数量 block.timestamp, // 当前区块时间戳 block.number, // 当前区块号 msg.data // 完整的调用数据 ); }}安全最佳实践1. 重入攻击防护import "@openzeppelin/contracts/security/ReentrancyGuard.sol";contract SecureContract is ReentrancyGuard { mapping(address => uint256) public balances; function withdraw() public nonReentrant { uint256 amount = balances[msg.sender]; require(amount > 0, "No balance"); balances[msg.sender] = 0; payable(msg.sender).transfer(amount); }}2. 访问控制import "@openzeppelin/contracts/access/Ownable.sol";contract AccessControl is Ownable { function onlyOwnerFunction() public onlyOwner { // 仅所有者可调用 }}3. 安全的数学运算import "@openzeppelin/contracts/utils/math/SafeMath.sol";contract SafeMathExample { using SafeMath for uint256; function add(uint256 a, uint256 b) public pure returns (uint256) { return a.add(b); // 安全的加法 }}Gas优化1. 使用calldatacontract GasOptimization { // 不推荐:使用memory function badFunction(string memory data) public pure returns (string memory) { return data; } // 推荐:使用calldata function goodFunction(string calldata data) external pure returns (string memory) { return data; }}2. 批量操作contract BatchOperations { address[] public users; // 不推荐:多次存储操作 function addUserBad(address user) public { users.push(user); } // 推荐:批量添加 function addUsersGood(address[] calldata newUsers) public { for (uint256 i = 0; i < newUsers.length; i++) { users.push(newUsers[i]); } }}测试1. 使用Hardhat测试const { expect } = require("chai");describe("MyContract", function () { let contract; beforeEach(async function () { const MyContract = await ethers.getContractFactory("MyContract"); contract = await MyContract.deploy(); await contract.deployed(); }); it("Should set the value correctly", async function () { await contract.setValue(42); expect(await contract.getValue()).to.equal(42); }); it("Should emit an event", async function () { await expect(contract.setValue(42)) .to.emit(contract, "ValueChanged") .withArgs(42); });});最佳实践总结使用最新版本的Solidity:0.8.0+版本有更好的安全特性遵循检查-效果-交互模式:防止重入攻击使用OpenZeppelin库:避免重复造轮子充分的测试:单元测试、集成测试、模糊测试Gas优化:优化代码以降低Gas成本安全审计:在部署前进行专业审计代码文档:使用NatSpec注释事件日志:记录重要操作Solidity是以太坊智能合约开发的核心语言,掌握其特性和最佳实践对于构建安全、高效的应用至关重要。
阅读 0·2月21日 15:24

SolidJS Router 如何使用?有哪些高级特性?

SolidJS Router 提供了强大的客户端路由功能:基本使用:import { Router, Route, Routes } from '@solidjs/router';function App() { return ( <Router> <Routes> <Route path="/" component={Home} /> <Route path="/about" component={About} /> <Route path="/users/:id" component={User} /> </Routes> </Router> );}路由参数:function User() { const params = useParams(); const userId = () => params.id; // 响应式访问 return <div>User ID: {userId()}</div>;}导航:import { useNavigate, Link, A } from '@solidjs/router';function Navigation() { const navigate = useNavigate(); const handleClick = () => { navigate('/about'); // 编程式导航 }; return ( <nav> <Link href="/">Home</Link> {/* 使用 Link 组件 */} <A href="/about">About</A> {/* 使用 A 组件(更轻量) */} <button onClick={handleClick}>Go to About</button> </nav> );}嵌套路由:function App() { return ( <Router> <Routes> <Route path="/" component={Layout}> <Route path="/" component={Home} /> <Route path="/about" component={About} /> </Route> </Routes> </Router> );}function Layout(props) { return ( <div> <Header /> {props.children} <Footer /> </div> );}路由守卫:function ProtectedRoute(props) { const isAuthenticated = createMemo(() => checkAuth()); return ( <Show when={isAuthenticated()}> {props.children} </Show> );}数据预加载:const [data] = createResource(fetchData);function DataRoute() { return ( <Show when={!data.loading} fallback={<Loading />}> <div>{data().content}</div> </Show> );}
阅读 0·2月21日 15:24

SolidJS 和 React 有什么区别?如何选择适合的框架?

SolidJS 和 React 都是现代前端框架,但设计理念和技术实现有显著差异:核心架构对比:| 特性 | React | SolidJS ||------|-------|---------|| 渲染方式 | 虚拟 DOM + 重新渲染 | 细粒度响应式 + 直接 DOM 更新 || 组件执行 | 每次状态变化重新执行 | 只执行一次 || 状态管理 | useState, useReducer | createSignal, createStore || 副作用处理 | useEffect(需声明依赖) | createEffect(自动追踪) || 派生状态 | useMemo(需声明依赖) | createMemo(自动追踪) || 列表渲染 | map + key | For, Index 组件 || 条件渲染 | 三元运算符, &&, || | Show, Switch 组件 |代码对比示例:// Reactfunction Counter() { const [count, setCount] = useState(0); useEffect(() => { document.title = `Count: ${count}`; }, [count]); const doubled = useMemo(() => count * 2, [count]); return ( <div> <p>Count: {count}</p> <p>Doubled: {doubled}</p> <button onClick={() => setCount(c => c + 1)}>+</button> </div> );}// SolidJSfunction Counter() { const [count, setCount] = createSignal(0); createEffect(() => { document.title = `Count: ${count()}`; }); const doubled = createMemo(() => count() * 2); return ( <div> <p>Count: {count()}</p> <p>Doubled: {doubled()}</p> <button onClick={() => setCount(c => c + 1)}>+</button> </div> );}性能对比:SolidJS 在大型列表渲染中性能更优SolidJS 内存占用更小React 生态系统更成熟React 学习资源更丰富适用场景:选择 React:团队熟悉 React、需要丰富生态、大型企业应用选择 SolidJS:追求极致性能、需要细粒度更新、响应式优先的应用迁移建议:React 开发者可以快速上手 SolidJS概念相似但实现方式不同需要改变思维方式(从重新渲染到响应式更新)
阅读 0·2月21日 15:23

SolidJS 有哪些性能优化技巧?如何避免常见的性能陷阱?

SolidJS 提供了多种性能优化技巧,可以显著提升应用性能:使用 createMemo 缓存计算结果:// 不好的做法 - 每次渲染都重新计算function Component() { const [items, setItems] = createSignal([]); const total = () => items().reduce((sum, item) => sum + item.price, 0); return <div>Total: {total()}</div>;}// 好的做法 - 使用 createMemo 缓存function Component() { const [items, setItems] = createSignal([]); const total = createMemo(() => items().reduce((sum, item) => sum + item.price, 0)); return <div>Total: {total()}</div>;}批量更新:import { batch } from 'solid-js';// 不好的做法 - 多次触发更新function updateMultiple() { setName('John'); setAge(25); setEmail('john@example.com');}// 好的做法 - 使用 batch 批量更新function updateMultiple() { batch(() => { setName('John'); setAge(25); setEmail('john@example.com'); });}使用 Index 优化列表渲染:// For - 使用对象作为 key(适合动态列表)<For each={items()} fallback={<div>No items</div>}> {(item) => <div>{item.name}</div>}</For>// Index - 使用索引作为 key(性能更好,适合静态列表)<Index each={items()}> {(item, index) => <div>{item().name}</div>}</Index>懒加载组件:import { lazy } from 'solid-js';const HeavyComponent = lazy(() => import('./HeavyComponent'));function App() { return ( <Suspense fallback={<Loading />}> <HeavyComponent /> </Suspense> );}避免不必要的响应式:// 不好的做法 - 创建不必要的 signalfunction Component() { const [count, setCount] = createSignal(0); const doubled = createSignal(() => count() * 2); // 不需要 signal return <div>{doubled()}</div>;}// 好的做法 - 使用普通函数function Component() { const [count, setCount] = createSignal(0); const doubled = () => count() * 2; return <div>{doubled()}</div>;}使用 untrack 避免追踪:import { untrack } from 'solid-js';createEffect(() => { const value = untrack(() => someSignal()); console.log(value); // 不会建立依赖关系});性能监控:import { devtools } from 'solid-js/dev';devtools; // 启用开发工具
阅读 0·2月21日 15:23

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