调试 Preact 应用

¥Debugging Preact Apps

Preact 附带了许多工具,使调试变得更加容易。它们打包在一次导入中,并且可以通过导入 preact/debug 来包含它们。

¥Preact ships with a lot of tools to make debugging easier. They're packaged in a single import and can be included by importing preact/debug.

其中包括与我们自己的 Chrome 和 Firefox Preact 开发工具 扩展集成。

¥These include integration with our own Preact Devtools Extension for Chrome and Firefox.

每当我们检测到错误(例如 <table> 元素中的嵌套不正确)时,我们都会打印警告或错误。

¥We'll print a warning or an error whenever we detect something wrong like incorrect nesting in <table> elements.



Preact 开发工具 可以安装在浏览器的扩展商店中。

¥The Preact Devtools can be installed in the extension store of your browser.

安装后,我们需要在某处导入 preact/debug 来初始化与扩展的连接。确保此导入是整个应用中的第一个导入。

¥Once installed we need to import preact/debug somewhere to initialize the connection to the extension. Make sure that this import is the first import in your whole app.

@preact/preset-vite 自动包含 preact/debug 包。如果你正在使用它,你可以安全地跳过设置和剥离步骤!

¥@preact/preset-vite includes the preact/debug package automatically. You can safely skip the setup & strip steps if you're using it!


¥Here is an example of how your main entry file to your application may look like.

// Must be the first import
import "preact/debug";
import { render } from 'preact';
import App from './components/App';

render(<App />, document.getElementById('root'));


¥Strip devtools from production

大多数打包器在检测到 if 语句内的分支永远不会被命中时允许你删除代码。我们可以使用它在开发过程中仅包含 preact/debug 并在生产版本中保存这些宝贵的字节。

¥Most bundlers allow you strip out code when they detect that a branch inside an if-statement will never be hit. We can use this to only include preact/debug during development and save those precious bytes in a production build.

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

import { render } from 'preact';
import App from './components/App';

render(<App />, document.getElementById('root'));

确保在构建工具中将 NODE_ENV 变量设置为正确的值。

¥Make sure to set the NODE_ENV variable to the correct value in your build tool.


¥Debug Warnings and Errors

有时,当 Preact 检测到无效代码时,你可能会收到警告或错误。应修复这些问题以确保你的应用完美运行。

¥Sometimes you may get warnings or errors when Preact detects invalid code. These should be fixed to ensure that your app works flawlessly.

undefined 父级传递给 render()

¥undefined parent passed to render()

这意味着代码试图将你的应用渲染为空,而不是 DOM 节点。这是以下之间的区别:

¥This means that the code is trying to render your app into nothing instead of a DOM node. It's the difference between:

// What Preact received
render(<App />, undefined);

// vs what it expected
render(<App />, actualDomNode);

发生此错误的主要原因是调用 render() 函数时 DOM 节点不存在。确保它存在。

¥The main reason this error occurs is that the DOM node isn't present when the render() function is called. Make sure it exists.

undefined 组件传递给 createElement()

¥undefined component passed to createElement()

每当你传递 undefined 而不是组件时,Preact 都会抛出此错误。造成这种情况的常见原因是混淆了 defaultnamed 导出。

¥Preact will throw this error whenever you pass undefined instead of a component. The common cause for this one is mixing up default and named exports.

// 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 导出时。检查这一点的一种快速方法(如果你的编辑器还没有这样做),就是注销导入:

¥The same error will be thrown when it's the other way around. When you declare a named export and are trying to use it as a default export. One quick way to check this (in case your editor won't do it already), is to just log out the import:

// app.js
export function App() {
  return <div>Hello World</div>;

// index.js
import App from './app';

// Logs: { default: [Function] } instead of the component

将 JSX 字面量作为 JSX 传递两次

¥Passed a JSX literal as JSX twice

再次将 JSX-Literal 或组件传递到 JSX 中是无效的,并且会触发此错误。

¥Passing a JSX-Literal or Component into JSX again is invalid and will trigger this error.

const Foo = <div>foo</div>;
// Invalid: Foo already contains a JSX-Element
render(<Foo />, dom);


¥To fix this, we can just pass the variable directly:

const Foo = <div>foo</div>;
render(Foo, dom);


¥Improper nesting of table detected

HTML 对于如何构建表格有非常明确的指示。偏离这一点将导致很难调试的渲染错误。在 Preact 中,我们将检测到这一点并打印错误。要了解有关如何构建表格的更多信息,我们强烈推荐 mdn 文档

¥HTML has a very clear directions on how tables should be structured. Deviating from that will lead to rendering errors that are very hard to debug. In Preact we'll detect this and print an error. To learn more about how tables should be structured we can highly recommend the mdn documentation

ref 属性无效

¥Invalid ref-property

ref 属性包含意外内容时,我们将抛出此错误。这包括不久前已弃用的基于字符串的 refs

¥When the ref property contains something unexpected we'll throw this error. This includes string-based refs that have been deprecated a while ago.

// valid
<div ref={e => {/* ... */)}} />

// valid
const ref = createRef();
<div ref={ref} />

// Invalid
<div ref="ref" />


¥Invalid event handler

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

¥Sometimes you'll may accidentally pass a wrong value to an event handler. They must always be a function or null if you want to remove it. All other types are invalid.

// valid
<div onClick={() => console.log("click")} />

// invalid
<div onClick={console.log("click")} />

Hook 只能从渲染方法中调用

¥Hook can only be invoked from render methods


¥This error occurs when you try to use a hook outside of a component. They are only supported inside a function component.

// 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] 已被弃用

¥Getting vnode.[property] is deprecated

在 Preact X 中,我们对 vnode 的内部形状做了一些突破性的改变。

¥With Preact X we did some breaking changes to our internal vnode shape.

Preact 8.x Preact 10.x
vnode.nodeName vnode.type
vnode.attributes vnode.props
vnode.children vnode.props.children


¥Found children with the same key

基于虚拟 dom 的库的一个独特之处是,它们必须检测子级何时被移动。然而,要知道哪个子级是哪个,我们需要以某种方式标记它们。仅当你动态创建子项时才需要这样做。

¥One unique aspect about virtual-dom based libraries is that they have to detect when a children is moved around. However to know which child is which, we need to flag them somehow. This is only necessary when you're creating children dynamically.

// Both children will have the same key "A"
  {['A', 'A'].map(char => <p key={char}>{char}</p>)}

正确的方法是为他们提供唯一的密钥。在大多数情况下,你要迭代的数据将具有某种形式的 id

¥The correct way to do it is to give them unique keys. In most cases the data you're iterating over will have some form of id.

const persons = [
  { name: 'John', age: 22 },
  { name: 'Sarah', age: 24}

// Somewhere later in your component
  {persons.map(({ name, age }) => {
    return <p key={name}>{name}, Age: {age}</p>;
Preact 中文网 - 粤ICP备13048890号