帮助
支持我们

调试 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 将抛出此错误。导致此错误的常见原因是将 defaultnamed 导出混淆。

// 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" />

无效的事件处理程序

有时您可能会意外地将错误的值传递给事件处理程序。如果要删除它们,它们必须始终是 functionnull。所有其他类型都是无效的。

// 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.xPreact 10.x
vnode.nodeNamevnode.type
vnode.attributesvnode.props
vnode.childrenvnode.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>