--- /dev/null
+import React from 'react';
+
+// 全局错误边界:捕获渲染期异常并显示, 避免生产构建下整页白屏无提示
+export default class ErrorBoundary extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { hasError: false, error: null, info: null };
+ }
+
+ static getDerivedStateFromError(error) {
+ return { hasError: true, error };
+ }
+
+ componentDidCatch(error, info) {
+ this.setState({ info });
+ console.error('App crashed:', error, info);
+ }
+
+ render() {
+ if (this.state.hasError) {
+ return (
+ <div style={{ padding: '24px', fontFamily: 'sans-serif', color: '#c0392b' }}>
+ <h2>页面加载出错</h2>
+ <pre style={{ whiteSpace: 'pre-wrap', background: '#fdecea', padding: '12px', borderRadius: '6px' }}>
+ {this.state.error && (this.state.error.stack || this.state.error.toString())}
+ </pre>
+ <button onClick={() => window.location.reload()} style={{ marginTop: '12px', padding: '8px 16px' }}>
+ 重新加载
+ </button>
+ </div>
+ );
+ }
+ return this.props.children;
+ }
+}
const TOKEN_KEY = 'words_auth_token';
+// 无痕/隐私模式下部分浏览器会禁止 localStorage 访问并抛 SecurityError,
+// 这里统一兜底, 避免初始化时崩溃导致整页白屏。
+function safeGet(key) {
+ try { return localStorage.getItem(key); } catch (e) { return null; }
+}
+function safeSet(key, val) {
+ try { localStorage.setItem(key, val); } catch (e) {}
+}
+function safeRemove(key) {
+ try { localStorage.removeItem(key); } catch (e) {}
+}
+
export function getToken() {
- return localStorage.getItem(TOKEN_KEY) || '';
+ return safeGet(TOKEN_KEY) || '';
}
export function setToken(t) {
- localStorage.setItem(TOKEN_KEY, t);
+ safeSet(TOKEN_KEY, t);
}
export function clearToken() {
- localStorage.removeItem(TOKEN_KEY);
+ safeRemove(TOKEN_KEY);
}
export function isLoggedIn() {
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
+import ErrorBoundary from './ErrorBoundary';
import { isLoggedIn } from './auth';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
- <App />
+ <ErrorBoundary>
+ <App />
+ </ErrorBoundary>
</React.StrictMode>
);