调试 Preact 应用
Preact 自带一系列方便您调试的工具,您可以通过导入 preact/debug
包来使用。
我们为 Chrome 和 Firefox 提供 Preact 开发工具扩展程序,集成您的应用开发。
我们还会在错误发生时 (如 <table>
嵌套出错时) 输出警告信息。
安装
您可在您浏览器的扩展程序商店中下载安装 Preact 开发工具。
安装后,您需要在您的代码中导入 preact/debug
包来初始化与扩展的连接。请确保此包为您整个应用程序中第一个导入的包。
@preact/preset-vite
不自动添加preact/debug
包。若您使用此工具的话,您可以跳过这一步骤!
下面是您的应用入口文件可能的样子:
// 必须为第一个导入的包
import "preact/debug";
import { render } from 'preact';
import App from './components/App';
render(<App />, document.getElementById('root'));
从生产环境中移除开发工具
大多数打包工具会在检测到 if
中存在不可达的分支时自动为您去除代码。我们仅会在开发环境中导入 preact/debug
包,在生产环境中则会使用此方法来删除此包、削减空间。
// 必须为第一个导入的包
if (process.env.NODE_ENV==='development') {
// 只能在此处使用 require 语句,import 语句仅支持顶级模块。
require("preact/debug");
}
import { render } from 'preact';
import App from './components/App';
render(<App />, document.getElementById('root'));
请确保您的构建工具为 NODE_ENV
变量设置了正确的值。
调试警告和错误
有时候您可能会在 Preact 检测到无效代码时遇到警告或错误,这些是为了确保您的应用能完美工作而发出的。
undefined
父元素被传递进 render()
这意味着您的代码尝试渲染空内容,而非 DOM 节点,其区别在于:
// Preact 实际收到的
render(<App />, undefined);
// 和您期望的
render(<App />, actualDomNode);
这个错误发生的主要原因是 DOM 节点在 render()
调用时暂不存在,请确保其存在后再调用。
undefined
组件被传递至 createElement()
Preact 会在您传递 undefined
而非组件时抛出此错误,其常见原因是您混用了 default
和命名导出。
// app.js
export default function App() {
return <div>Hello World</div>;
}
// index.js:错误,因为 `app.js` 没有命名导出
import { App } from './app';
render(<App />, dom);
当您声明命名导出,并尝试将其作为 default
导出使用时此错误也会发生。您可以输出导入结果来快速检查 (假如您的编辑器没有自动为您检查的话):
// app.js
export function App() {
return <div>Hello World</div>;
}
// index.js
import App from './app';
console.log(App);
// 日志:{ default: [Function] } 而非组件
两次将 JSX 字面值传递为 JSX
再次将 JSX 字面值或组件传递进 JSX 是无效的,将触发如下错误。
const Foo = <div>foo</div>;
// 无效,Foo 已包含 JSX 元素
render(<Foo />, dom);
要修复此问题,我们可以直接传递变量。
const Foo = <div>foo</div>;
render(Foo, dom);
检测到表格中存在错误嵌套
HTML 对表格的结构有着严格规则,违反其中一条都会导致渲染错误,且很难调试。在 Preact 中,我们会检测此问题并输出错误。要了解表格结构,我们强烈推荐您参阅 MDN 文档
无效 ref
属性
当 ref
属性包含异常值时我们将抛出此错误。这包括很久前即启用的字符串型 refs
。
// 有效
<div ref={e => {/* ... */)}} />
// 有效
const ref = createRef();
<div ref={ref} />
// 无效
<div ref="ref" />
无效事件处理程序
有些时候,您可能不小心传递了错误的事件处理程序。您必须传入 function
,或传入 null
来移除,其他所有类型均无效。
// 有效
<div onClick={() => console.log("click")} />
// 无效
<div onClick={console.log("click")} />
仅渲染方法可调用钩子
此错误会在您尝试在组件外使用钩子函数时发生,它们仅支持函数组件。
// 无效,必须在组件内使用
const [value, setValue] = useState(0);
// 有效
function Foo() {
const [value, setValue] = useState(0);
return <button onClick={() => setValue(value + 1)}>{value}</button>;
}
已弃用获取 vnode.[property]
Preact X 中,我们对内部的 vnode
结构做出了重大变更。
Preact 8.x | Preact 10.x |
---|---|
vnode.nodeName |
vnode.type |
vnode.attributes |
vnode.props |
vnode.children |
vnode.props.children |
发现具有相同键的子元素
基于虚拟 DOM 的库一大独特性质是需要检测子元素何时被移动。但要知道何时被移动,我们先要标记它们。注意,您仅需要在动态创建子元素时注意这点。
// 两个子元素都会有相同键值 "A"
<div>
{['A', 'A'].map(char => <p key={char}>{char}</p>)}
</div>
正确的方法是为元素提供唯一键值。大多数情况下,您所遍历的数据会有一个 id
键。
const persons = [
{ name: '张三', age: 22 },
{ name: '李四', age: 24}
];
// 您组件之后的操作
<div>
{persons.map(({ name, age }) => {
return <p key={name}>{name}, Age: {age}</p>;
})}
</div>