调试 Preact 应用
Preact 附带许多工具以简化调试。它们打包在一个导入中,可以通过导入 preact/debug
来包含它们。
这些工具包括与我们自己的 Preact Devtools Chrome 和 Firefox 扩展的集成。
每当我们检测到错误,例如 <table>
元素中的嵌套不正确时,我们都会打印警告或错误。
安装
Preact Devtools 可以安装在浏览器的扩展商店中。
安装后,我们需要在某个地方导入 preact/debug
以初始化与扩展的连接。确保此导入是整个应用程序中的第一个导入。
preact-cli
会自动包含preact/debug
包。如果你使用它,可以安全地跳过下一步!
以下是你应用程序的主入口文件可能的样子示例。
// Must be the first import
import "preact/debug";
import { render } from 'preact';
import App from './components/App';
render(<App />, document.getElementById('root'));
从生产中删除 devtools
大多数捆绑器允许你在检测到 if
语句内的分支永远不会命中时删除代码。我们可以利用这一点,仅在开发期间包含 preact/debug
,并在生产构建中节省这些宝贵的字节。
// Must be the first import
if (process.env.NODE_ENV==='development') {
// Must use require here as import statements are only allowed
// to exist at top-level.
require("preact/debug");
}
import { render } from 'preact';
import App from './components/App';
render(<App />, document.getElementById('root'));
确保在构建工具中将 NODE_ENV
变量设置为正确的值。
调试警告和错误
有时,当 Preact 检测到无效代码时,你可能会收到警告或错误。应该修复这些问题以确保你的应用程序完美运行。
传递给 render()
的父级为 undefined
这意味着代码试图将你的应用程序渲染到空处,而不是 DOM 节点。这与以下内容的区别在于
// What Preact received
render(<App />, undefined);
// vs what it expected
render(<App />, actualDomNode);
此错误发生的主要原因是调用 render()
函数时 DOM 节点不存在。确保它存在。
传递给 createElement()
的组件为 undefined
当您传递 undefined
而不是组件时,Preact 将抛出此错误。导致此错误的常见原因是将 default
和 named
导出混淆。
// app.js
export default function App() {
return <div>Hello World</div>;
}
// index.js: Wrong, because `app.js` doesn't have a named export
import { App } from './app';
render(<App />, dom);
当出现相反的情况时,也会抛出相同的错误。当您声明 named
导出并尝试将其用作 default
导出时。一种快速检查此问题的方法(如果您的编辑器尚未执行此操作),只需记录导入
// app.js
export function App() {
return <div>Hello World</div>;
}
// index.js
import App from './app';
console.log(App);
// Logs: { default: [Function] } instead of the component
将 JSX 文字作为 JSX 传递了两次
将 JSX 文字或组件再次传递到 JSX 中无效,并且将触发此错误。
const Foo = <div>foo</div>;
// Invalid: Foo already contains a JSX-Element
render(<Foo />, dom);
为了解决此问题,我们可以直接传递变量
const Foo = <div>foo</div>;
render(Foo, dom);
检测到表格嵌套不当
HTML 对表格的结构方式有非常明确的指示。偏离此指示将导致很难调试的渲染错误。在 Preact 中,我们将检测到此问题并打印错误。要详细了解表格的结构方式,我们强烈推荐 mdn 文档
无效的 ref
属性
当 ref
属性包含意外内容时,我们将抛出此错误。这包括已弃用一段时间的基于字符串的 refs
。
// valid
<div ref={e => {/* ... */)}} />
// valid
const ref = createRef();
<div ref={ref} />
// Invalid
<div ref="ref" />
无效的事件处理程序
有时您可能会意外地将错误的值传递给事件处理程序。如果要删除它们,它们必须始终是 function
或 null
。所有其他类型都是无效的。
// valid
<div onClick={() => console.log("click")} />
// invalid
<div onClick={console.log("click")} />
只能从渲染方法调用 Hook
当您尝试在组件外部使用 Hook 时,会发生此错误。它们仅在函数组件内受支持。
// Invalid, must be used inside a component
const [value, setValue] = useState(0);
// valid
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 的库的一个独特方面是它们必须检测到子级何时被移动。然而,要了解哪个子级是哪个,我们需要以某种方式标记它们。仅在动态创建子级时才需要这样做。
// Both children will have the same key "A"
<div>
{['A', 'A'].map(char => <p key={char}>{char}</p>)}
</div>
正确的方法是为它们提供唯一的键。在大多数情况下,您正在迭代的数据将具有一些形式的 id
。
const persons = [
{ name: 'John', age: 22 },
{ name: 'Sarah', age: 24}
];
// Somewhere later in your component
<div>
{persons.map(({ name, age }) => {
return <p key={name}>{name}, Age: {age}</p>;
})}
</div>