react、vue、preact 和 inferno 都是用于构建现代 Web 用户界面的核心库。react 由 Facebook 维护,是目前生态最丰富的库,采用 JSX 和虚拟 DOM 技术。vue 以其易上手和模板语法著称,Vue 3 引入了基于 Proxy 的响应式系统。preact 是 React 的轻量级替代品,兼容 React API 但体积更小。inferno 专注于极致性能,同样采用类 React 的 API 设计,但在渲染优化上有独特实现。
在构建现代 Web 应用时,选择合适的基础 UI 库是架构决策中最关键的一步。react、vue、preact 和 inferno 都是业界知名的解决方案,它们都能高效地管理界面状态和渲染,但底层实现和开发体验存在显著差异。作为架构师,我们需要透过表面语法,深入理解它们在处理渲染、状态管理和生态兼容性上的不同。
这四个库的核心区别在于它们如何追踪数据变化并更新页面。
react 和 preact 以及 inferno 都主要依赖 虚拟 DOM (Virtual DOM)。当状态改变时,它们会创建一个新的虚拟树,与旧树进行对比(Diffing),然后最小化地更新真实 DOM。
// react/preact/inferno: 虚拟 DOM diff 机制
// 状态更新触发重新渲染,库自动计算差异
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
vue (Vue 3) 则采用了 细粒度响应式系统。它使用 JavaScript Proxies 来拦截数据访问。当数据变化时,Vue 直接知道哪些组件需要更新,无需对比整个虚拟树。
// vue: 基于 Proxy 的响应式
// 数据变化直接通知依赖该数据的组件更新
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
💡 注意:虽然
preact和inferno也使用虚拟 DOM,但它们的 Diff 算法经过特殊优化,旨在比标准react更快,尤其是在内存占用和计算开销上。
代码的组织方式直接影响开发效率和可读性。
react、preact 和 inferno 强制或推荐使用 JSX。这意味着你可以在 JavaScript 中直接写 HTML 结构。这赋予了开发者完整的 JavaScript 能力,但需要构建工具支持。
// react/preact/inferno: JSX 语法
// JavaScript 与 HTML 混合,逻辑灵活
function UserCard({ user }) {
return (
<div className="card">
<h2>{user.name}</h2>
{user.isActive ? <span>Online</span> : <span>Offline</span>}
</div>
);
}
vue 默认使用 单文件组件 (SFC),将模板、逻辑和样式分开。模板语法更接近标准 HTML,对设计师或前端新手更友好,但也限制了在模板中直接使用复杂 JS 逻辑的能力。
<!-- vue: 模板语法 -->
<!-- 结构清晰,逻辑与视图分离 -->
<template>
<div class="card">
<h2>{{ user.name }}</h2>
<span v-if="user.isActive">Online</span>
<span v-else>Offline</span>
</div>
</template>
<script setup>
defineProps(['user']);
</script>
在复杂组件中,如何组织状态逻辑是另一个关键点。
react、preact 和 inferno 使用 Hooks。你可以在函数组件中调用 useState、useEffect 等函数来提取逻辑。这非常灵活,但需要严格遵守规则(例如只能在顶层调用)。
// react/preact/inferno: Hooks
// 使用 useEffect 处理副作用
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return width;
}
vue 3 引入了 组合式 API (Composition API)。它通过 setup 函数和 ref、computed 等工具实现类似的逻辑复用。Vue 会自动清理副作用,减少了内存泄漏的风险。
// vue: 组合式 API
// 使用 watchEffect 自动追踪依赖并处理副作用
import { ref, onMounted, onUnmounted } from 'vue';
export function useWindowWidth() {
const width = ref(window.innerWidth);
const handleResize = () => width.value = window.innerWidth;
onMounted(() => window.addEventListener('resize', handleResize));
onUnmounted(() => window.removeEventListener('resize', handleResize));
return width;
}
选择库不仅仅是选择代码写法,更是选择整个生态。
react 拥有最庞大的生态系统。几乎任何功能都有现成的库(路由、表单、图表)。preact 提供了兼容层 (preact/compat),允许你使用大多数 React 库,这大大降低了迁移成本。inferno 虽然 API 相似,但许多 React 库可能无法直接运行,需要寻找特定替代品或自行适配。
vue 的生态是官方维护为主的。路由 (vue-router) 和状态管理 (pinia/vuex) 通常由核心团队提供,这意味着更好的集成度和文档一致性,但第三方库的总量少于 React。
// preact: 兼容 React 生态
// 可以引入 React 的第三方库(如 react-router 的预适应版本)
import { render } from 'preact';
import { useState } from 'preact/hooks';
// inferno: 需确认库支持
// 许多 React 库不直接支持 Inferno,需检查兼容性
import { render } from 'inferno';
import { useState } from 'inferno-hooks';
| 特性 | react | vue | preact | inferno |
|---|---|---|---|---|
| 渲染原理 | 虚拟 DOM | 响应式 Proxy + 虚拟 DOM | 优化版虚拟 DOM | 高性能虚拟 DOM |
| 主要语法 | JSX | 模板 (Template) | JSX | JSX |
| 状态管理 | Hooks (useState) | 组合式 API (ref/reactive) | Hooks (兼容 React) | Hooks (类 React) |
| 包体积 | 较大 | 中等 | 极小 | 小 |
| 生态规模 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ (兼容 React) | ⭐⭐ |
| 学习曲线 | 中等 | 低 | 低 (若懂 React) | 中等 |
在实际工程落地中,没有绝对的“最好”,只有“最合适”。
react 是 通用型首选 🏆。如果你的项目周期长、团队规模大、需要招聘容易,或者依赖大量第三方集成,React 是最安全的选择。它的稳定性经过了全球数百万应用的验证。
vue 是 开发体验优选 🎨。如果你追求开发速度,喜欢清晰的模板结构,或者团队对 JSX 接受度不高,Vue 3 能提供非常流畅的开发体验。它在中小型项目到大型后台系统中都表现优异。
preact 是 性能与兼容的平衡点 ⚖️。当你需要 React 的开发体验但受限于网络环境(如弱网、嵌入式设备)时,Preact 是最佳替代品。你可以用几乎相同的代码获得更小的体积。
inferno 是 特定场景的利器 🚀。仅当你有明确的性能瓶颈,且标准 React 无法满足时,才考虑 Inferno。它适合高频更新的数据仪表盘或游戏化界面,但需准备好应对较小的社区支持。
最终思考:技术选型的本质是权衡。不要为了微小的性能提升而牺牲团队的生产力,也不要为了追随潮流而忽略项目的实际约束。理解这些库的底层差异,能帮助你在面对复杂需求时做出更 confident 的决策。
如果你在极端性能要求的场景下工作(如高频交易界面、复杂数据可视化),且团队能够接受相对较小的社区规模,选择 inferno。它在处理大量 DOM 节点更新时表现优异,但需要评估其生态插件是否满足项目需求。
如果你已经熟悉 React 的开发模式,但受限于包体积或性能预算,选择 preact。它可以无缝迁移现有的 React 代码,无需重写逻辑,同时显著减少加载时间。适合对首屏加载速度敏感的内容型网站或嵌入式 Web 应用。
如果你的团队需要最丰富的生态系统、大量的第三方库支持以及长期的稳定性,选择 react。它适合大型企业级应用,尤其是需要招聘大量开发者的项目。虽然体积不是最小,但其社区资源和工具链是最完善的。
如果你希望开发体验更直观,喜欢模板语法与逻辑分离,或者团队主要面向亚洲市场,选择 vue。Vue 3 的组合式 API 提供了很好的逻辑复用能力,且官方维护的路由和状态管理库集成度极高,适合快速构建中小型到大型应用。
Inferno is an insanely fast, React-like library for building high-performance user interfaces on both the client and server.
The main objective of the InfernoJS project is to provide the fastest possible runtime performance for web applications. Inferno excels at rendering real time data views or large DOM trees.
The performance is achieved through multiple optimizations, for example:
createVNode calls, instead of createElement calls.
Optimizing runtime performance of the application.
linkEvent feature removes the need to use arrow functions or binding event callbacksinferno-servercreatePortal - API<div style="background-color: red"></div> or using object literal syntax <div style={{"background-color": "red"}}></div>. For camelCase syntax support see inferno-compat.Inferno v9 requires following features to be present in the executing runtime:
PromiseString.prototype.includes()String.prototype.startsWith()Array.prototype.includes()Object.spread()Since version 4 we have started running our test suite without any polyfills. Inferno is now part of Saucelabs open source program and we use their service for executing the tests.
InfernoJS is actively tested with browsers listed below, however it may run well on older browsers as well. This is due to limited support of browser versions in recent testing frameworks. https://github.com/jasmine/jasmine/blob/main/release_notes/5.0.0.md
Live examples at https://infernojs.github.io/inferno
Let's start with some code. As you can see, Inferno intentionally keeps the same design ideas as React regarding components: one-way data flow and separation of concerns.
In these examples, JSX is used via the Inferno JSX Babel Plugin to provide a simple way to express Inferno virtual DOM. You do not need to use JSX, it's completely optional, you can use hyperscript or createElement (like React does). Keep in mind that compile time optimizations are available only for JSX.
import { render } from 'inferno';
const message = 'Hello world';
render(<MyComponent message={message} />, document.getElementById('app'));
Furthermore, Inferno also uses ES6 components like React:
import { render, Component } from 'inferno';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
};
}
render() {
return (
<div>
<h1>Header!</h1>
<span>Counter is at: {this.state.counter}</span>
</div>
);
}
}
render(<MyComponent />, document.getElementById('app'));
Because performance is an important aspect of this library, we want to show you how to optimize your application even further.
In the example below we optimize diffing process by using JSX $HasVNodeChildren and $HasTextChildren to predefine children shape compile time.
In the MyComponent render method there is a div that contains JSX expression node as its content. Due to dynamic nature of Javascript
that variable node could be anything and Inferno needs to go through the normalization process to make sure there are no nested arrays or other invalid data.
Inferno offers a feature called ChildFlags for application developers to pre-define the shape of vNode's child node. In this example case
it is using $HasVNodeChildren to tell the JSX compiler, that this vNode contains only single element or component vNode.
Now inferno will not go into the normalization process runtime, but trusts the developer decision about the shape of the object and correctness of data.
If this contract is not kept and node variable contains invalid value for the pre-defined shape (fe. null), then application would crash runtime.
There is also span-element in the same render method, which content is set dynamically through _getText() method. There $HasTextChildren child-flag
fits nicely, because the content of that given "span" is never anything else than text.
All the available child flags are documented here.
import { createTextVNode, render, Component } from 'inferno';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
};
}
_getText() {
return 'Hello!';
}
render() {
const node =
this.state.counter > 0 ? (
<div>0</div>
) : (
<span $HasTextChildren>{this._getText()}</span>
);
return (
<div>
<h1>Header!</h1>
<div $HasVNodeChildren>{node}</div>
</div>
);
}
}
render(<MyComponent />, document.getElementById('app'));
To tear down inferno application you need to render null on root element.
Rendering null will trigger unmount lifecycle hooks for whole vDOM tree and remove global event listeners.
It is important to unmount unused vNode trees to free browser memory.
import { createTextVNode, render, Component } from 'inferno';
const rootElement = document.getElementById('app');
// Start the application
render(<ExampleComponent />, rootElement);
// Tear down
render(null, rootElement);
If you have built something using Inferno you can add them here:
The easiest way to get started with Inferno is by using Create Inferno App.
Alternatively, you can try any of the following:
Core package:
npm install --save inferno
Addons:
# server-side rendering
npm install --save inferno-server
# routing
npm install --save inferno-router
Pre-bundled files for browser consumption can be found on our cdnjs:
Or on jsDelivr:
https://cdn.jsdelivr.net/npm/inferno@latest/dist/inferno.min.js
Or on unpkg.com:
https://unpkg.com/inferno@latest/dist/inferno.min.js
npm install --save-dev babel-plugin-inferno
npm install --save inferno-hyperscript
npm install --save inferno-create-element
npm install --save-dev inferno-compat
Note: Make sure you read more about inferno-compat before using it.
Inferno now has bindings available for some of the major state management libraries out there:
inferno-reduxinferno-mobx@cerebral/infernoInferno has its own JSX Babel plugin.
onClick).createRef or callback ref APIinput/select/textarea elements. This prevents lots of edgecases where the virtual DOM is not the source of truth (it should always be). Preact pushes the source of truth to the DOM itself.Like React, Inferno also uses a light-weight synthetic event system in certain places (although both event systems differ massively). Inferno's event system provides highly efficient delegation and an event helper called linkEvent.
One major difference between Inferno and React is that Inferno does not rename events or change how they work by default. Inferno only specifies that events should be camel cased, rather than lower case. Lower case events will bypass
Inferno's event system in favour of using the native event system supplied by the browser. For example, when detecting changes on an <input> element, in React you'd use onChange, with Inferno you'd use onInput instead (the
native DOM event is oninput).
Available synthetic events are:
onClickonDblClickonFocusInonFocusOutonKeyDownonKeyPressonKeyUponMouseDownonMouseMoveonMouseUponTouchEndonTouchMoveonTouchStartlinkEvent (package: inferno)linkEvent() is a helper function that allows attachment of props/state/context or other data to events without needing to bind() them or use arrow functions/closures. This is extremely useful when dealing with events in functional components. Below is an example:
import { linkEvent } from 'inferno';
function handleClick(props, event) {
props.validateValue(event.target.value);
}
function MyComponent(props) {
return <div><input type="text" onClick={ linkEvent(props, handleClick) } /><div>;
}
This is an example of using it with ES2015 classes:
import { linkEvent, Component } from 'inferno';
function handleClick(instance, event) {
instance.setState({ data: event.target.value });
}
class MyComponent extends Component {
render () {
return <div><input type="text" onClick={ linkEvent(this, handleClick) } /><div>;
}
}
linkEvent() offers better performance than binding an event in a class constructor and using arrow functions, so use it where possible.
In HTML, form elements such as <input>, <textarea>, and <select> typically maintain their own state and update it based on user input.
In Inferno, mutable state is typically kept in the state property of components, and only updated with setState().
We can combine the two by making the Inferno state be the "single source of truth". Then the Inferno component that renders a form also controls what happens in that form on subsequent user input. An input form element whose value is controlled by Inferno in this way is called a "controlled component".
render (package: inferno)import { render } from 'inferno';
render(<div />, document.getElementById('app'));
Render a virtual node into the DOM in the supplied container given the supplied virtual DOM. If the virtual node was previously rendered into the container, this will perform an update on it and only mutate the DOM as necessary, to reflect the latest Inferno virtual node.
Warning: If the container element is not empty before rendering, the content of the container will be overwritten on the initial render.
createRenderer (package: inferno)createRenderer creates an alternative render function with a signature matching that of the first argument passed to a reduce/scan function. This allows for easier integration with reactive programming libraries, like RxJS and Most.
import { createRenderer } from 'inferno';
import { scan, map } from 'most';
const renderer = createRenderer();
// NOTE: vNodes$ represents a stream of virtual DOM node updates
scan(renderer, document.getElementById('app'), vNodes$);
See inferno-most-fp-demo for an example of how to build an app architecture around this.
createElement (package: inferno-create-element)Creates an Inferno VNode using a similar API to that found with React's createElement()
import { Component, render } from 'inferno';
import { createElement } from 'inferno-create-element';
class BasicComponent extends Component {
render() {
return createElement(
'div',
{
className: 'basic',
},
createElement(
'span',
{
className: this.props.name,
},
'The title is ',
this.props.title,
),
);
}
}
render(
createElement(BasicComponent, { title: 'abc' }),
document.getElementById('app'),
);
Component (package: inferno)Class component:
import { Component } from 'inferno';
class MyComponent extends Component {
render() {
return <div>My Component</div>;
}
}
This is the base class for Inferno Components when they're defined using ES6 classes.
Functional component:
const MyComponent = ({ name, age }) => (
<span>
My name is: {name} and my age is: {age}
</span>
);
Another way of using defaultHooks.
export function Static() {
return <div>1</div>;
}
Static.defaultHooks = {
onComponentShouldUpdate() {
return false;
},
};
Default props
export function MyFunctionalComponent({ value }) {
return <div>{value}</div>;
}
MyFunctionalComponent.defaultProps = {
value: 10,
};
Functional components are first-class functions where their first argument is the props passed through from their parent.
createVNode (package: inferno)import { createVNode } from 'inferno';
createVNode(
flags,
type,
[className],
[...children],
[childFlags],
[props],
[key],
[ref],
);
createVNode is used to create html element's virtual node object. Typically createElement() (package: inferno-create-element), h() (package: inferno-hyperscript) or JSX are used to create
VNodes for Inferno, but under the hood they all use createVNode(). Below is an example of createVNode usage:
import { VNodeFlags, ChildFlags } from 'inferno-vnode-flags';
import { createVNode, createTextVNode, render } from 'inferno';
const vNode = createVNode(
VNodeFlags.HtmlElement,
'div',
'example',
createTextVNode('Hello world!'),
ChildFlags.HasVNodeChildren,
);
// <div class="example">Hello world!</div>
render(vNode, container);
createVNode arguments explained:
flags: (number) is a value from VNodeFlags, this is a numerical value that tells Inferno what the VNode describes on the page.
type: (string) is tagName for element for example 'div'
className: (string) is the class attribute ( it is separated from props because it is the most commonly used property )
children: (vNode[]|vNode) is one or array of vNodes to be added as children for this vNode
childFlags: (number) is a value from ChildFlags, this tells inferno shape of the children so normalization process can be skipped.
props: (Object) is object containing all other properties. fe: {onClick: method, 'data-attribute': 'Hello Community!}
key: (string|number) unique key within this vNodes siblings to identify it during keyed algorithm.
ref: (function) callback which is called when DOM node is added/removed from DOM.
createComponentVNode (package: 'inferno')import { createComponentVNode } from 'inferno';
createComponentVNode(flags, type, [props], [key], [ref]);
createComponentVNode is used for creating vNode for Class/Functional Component.
Example:
import { VNodeFlags, ChildFlags } from 'inferno-vnode-flags';
import {
createVNode,
createTextVNode,
createComponentVNode,
render,
} from 'inferno';
function MyComponent(props, context) {
return createVNode(
VNodeFlags.HtmlElement,
'div',
'example',
createTextVNode(props.greeting),
ChildFlags.HasVNodeChildren,
);
}
const vNode = createComponentVNode(
VNodeFlags.ComponentFunction,
MyComponent,
{
greeting: 'Hello Community!',
},
null,
{
onComponentDidMount() {
console.log('example of did mount hook!');
},
},
);
// <div class="example">Hello Community!</div>
render(vNode, container);
createComponentVNode arguments explained:
flags: (number) is a value from VNodeFlags, this is a numerical value that tells Inferno what the VNode describes on the page.
type: (Function/Class) is the class or function prototype for Component
props: (Object) properties passed to Component, can be anything
key: (string|number) unique key within this vNodes siblings to identify it during keyed algorithm.
ref: (Function|Object) this property is object for Functional Components defining all its lifecycle methods. For class Components this is function callback for ref.
createTextVNode (package: 'inferno')createTextVNode is used for creating vNode for text nodes.
createTextVNode arguments explained:
text: (string) is a value for text node to be created.
key: (string|number) unique key within this vNodes siblings to identify it during keyed algorithm.
import { createTextVNode } from 'inferno';
createTextVNode(text, key);
cloneVNode (package: inferno-clone-vnode)This package has same API as React.cloneElement
import { cloneVNode } from 'inferno-clone-vnode';
cloneVNode(vNode, [props], [...children]);
Clone and return a new Inferno VNode using a VNode as the starting point. The resulting VNode will have the original VNode's props with the new props merged in shallowly. New children will replace existing children. key and ref from the original VNode will be preserved.
cloneVNode() is almost equivalent to:
<VNode.type {...VNode.props} {...props}>
{children}
</VNode.type>
An example of using cloneVNode:
import { createVNode, render } from 'inferno';
import { cloneVNode } from 'inferno-clone-vnode';
import { VNodeFlags } from 'inferno-vnode-flags';
const vNode = createVNode(
VNodeFlags.HtmlElement,
'div',
'example',
'Hello world!',
);
const newVNode = cloneVNode(vNode, { id: 'new' }); // we are adding an id prop to the VNode
render(newVNode, container);
If you're using JSX:
import { render } from 'inferno';
import { cloneVNode } from 'inferno-clone-vnode';
const vNode = <div className="example">Hello world</div>;
const newVNode = cloneVNode(vNode, { id: 'new' }); // we are adding an id prop to the VNode
render(newVNode, container);
createPortal (package: 'inferno')HTML:
<div id="root"></div>
<div id="outside"></div>
Javascript:
const { render, Component, version, createPortal } from 'inferno';
function Outsider(props) {
return <div>{`Hello ${props.name}!`}</div>;
}
const outsideDiv = document.getElementById('outside');
const rootDiv = document.getElementById('root');
function App() {
return (
<div>
Main view
...
{createPortal(<Outsider name="Inferno" />, outsideDiv)}
</div>
);
}
// render an instance of Clock into <body>:
render(<App />, rootDiv);
Results into:
<div id="root">
<div>Main view ...</div>
</div>
<div id="outside">
<div>Hello Inferno!</div>
</div>
Cool, huh? Updates (props/context) will flow into "Outsider" component from the App component the same way as any other Component. For inspiration on how to use it click here!
createRef (package: inferno)createRef API provides shorter syntax than callback ref when timing of element is not needed.
import { Component, render, createRef } from 'inferno';
class Foobar extends Component {
constructor(props) {
super(props);
// Store reference somewhere
this.element = createRef(); // Returns object {current: null}
}
render() {
return (
<div>
<span id="span" ref={this.element}>
Ok
</span>
</div>
);
}
}
render(<Foobar />, container);
createFragment (package: inferno)createFragment is the native way to createFragment vNode. createFragment(children: any, childFlags: ChildFlags, key?: string | number | null)
createFragment arguments explained:
children: (Array) Content of fragment vNode, typically array of VNodes
childFlags: (number) is a value from ChildFlags, this tells inferno shape of the children so normalization process can be skipped.
key: (string|number) unique key within this vNodes siblings to identify it during keyed algorithm.
Alternative ways to create fragment vNode are:
<> ... </>, <Fragment> .... </Fragment> or <Inferno.Fragment> ... </Inferno.Fragment>createElement(Inferno.Fragment, {key: 'test'}, ...children)h(Inferno.Fragment, {key: 'test'}, children)In the below example both fragments are identical except they have different key
import { Fragment, render, createFragment } from 'inferno';
import { ChildFlags } from 'inferno-vnode-flags';
function Foobar() {
return (
<div $HasKeyedChildren>
{createFragment(
[<div>Ok</div>, <span>1</span>],
ChildFlags.HasNonKeyedChildren,
'key1',
)}
<Fragment key="key2">
<div>Ok</div>
<span>1</span>
</Fragment>
</div>
);
}
render(<Foobar />, container);
forwardRef (package: inferno)forwardRef is a new mechanism to "forward" ref inside a functional Component. It can be useful if you have simple functional Components and you want to create reference to a specific element inside it.
import { forwardRef, Component, render } from 'inferno';
const FancyButton = forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
class Hello extends Component {
render() {
return (
<FancyButton
ref={(btn) => {
if (btn) {
// btn variable is the button rendered from FancyButton
}
}}
>
Click me!
</FancyButton>
);
}
}
render(<Hello />, container);
hydrate (package: inferno-hydrate)import { hydrate } from 'inferno-hydrate';
hydrate(<div />, document.getElementById('app'));
Same as render(), but is used to hydrate a container whose HTML contents were rendered by inferno-server. Inferno will attempt to attach event listeners to the existing markup.
findDOMNode (package: inferno-extras)This feature has been moved from inferno to inferno-compat in v6. No options are needed anymore.
Note: we recommend using a ref callback on a component to find its instance, rather than using findDOMNode(). findDOMNode() cannot be used on functional components.
If a component has been mounted into the DOM, this returns the corresponding native browser DOM element. This method is useful for reading values out of the DOM, such as form field values and performing DOM measurements.
In most cases, you can attach a ref to the DOM node and avoid using findDOMNode() at all. When render returns null or false, findDOMNode() returns null.
If Component has rendered fragment it returns the first element.
VNodeFlags:
VNodeFlags.HtmlElementVNodeFlags.ComponentUnknownVNodeFlags.ComponentClassVNodeFlags.ComponentFunctionVNodeFlags.TextVNodeFlags.SvgElementVNodeFlags.InputElementVNodeFlags.TextareaElementVNodeFlags.SelectElementVNodeFlags.PortalVNodeFlags.ReCreate (JSX $ReCreate) always re-creates the vNodeVNodeFlags.ContentEditableVNodeFlags.FragmentVNodeFlags.InUseVnodeFlags.ForwardRefVNodeFlags.NormalizedVNodeFlags Masks:
VNodeFlags.ForwardRefComponent Functional component wrapped in forward refVNodeFlags.FormElement - Is form elementVNodeFlags.Element - Is vNode elementVNodeFlags.Component - Is vNode ComponentVNodeFlags.DOMRef - Bit set when vNode holds DOM referenceVNodeFlags.InUseOrNormalized - VNode is used somewhere else or came from normalization processVNodeFlags.ClearInUseNormalized - Opposite mask of InUse or NormalizedChildFlags
ChildFlags.UnknownChildren needs NormalizationChildFlags.HasInvalidChildren is invalid (null, undefined, false, true)ChildFlags.HasVNodeChildren (JSX $HasVNodeChildren) is single vNode (Element/Component)ChildFlags.HasNonKeyedChildren (JSX $HasNonKeyedChildren) is Array of vNodes non keyed (no nesting, no holes)ChildFlags.HasKeyedChildren (JSX $HasKeyedChildren) is Array of vNodes keyed (no nesting, no holes)ChildFlags.HasTextChildren (JSX $HasTextChildren) vNode contains only textChildFlags Masks
ChildFlags.MultipleChildren Is ArrayrenderToString (package: inferno-server)import { renderToString } from 'inferno-server';
const string = renderToString(<div />);
Render a virtual node into an HTML string, given the supplied virtual DOM.
| Name | Triggered when | Arguments to callback |
|---|---|---|
onComponentWillMount | a functional component is about to mount | |
onComponentDidMount | a functional component has mounted successfully | domNode |
onComponentShouldUpdate | a functional component has been triggered to update | lastProps, nextProps |
onComponentWillUpdate | a functional component is about to perform an update | lastProps, nextProps |
onComponentDidUpdate | a functional component has performed an update | lastProps, nextProps |
onComponentWillUnmount | a functional component is about to be unmounted | domNode |
onComponentDidAppear | a functional component has mounted and is ready for animations | domNode, props |
onComponentWillDisappear | a functional component is unmounted before DOM node is removed | domNode, props, callback |
onComponentWillDisappear has special type of argument "callback" which needs to be called when component is ready to be removed from the DOM. fe. after animations are finished.
All these Component lifecycle methods ( including render and setState - callback) are called with Component instance context. You don't need to "bind" these methods.
| Name | Triggered when | Arguments to callback |
|---|---|---|
componentDidMount | component has been mounted successfully | |
componentWillMount | component is about to mount | |
componentWillReceiveProps | before render when component updates | nextProps, context |
shouldComponentUpdate | component has been triggered to update | nextProps, nextState |
componentWillUpdate | component is about to perform an update | nextProps, nextState, context |
componentDidUpdate | component has performed an update | lastProps, lastState, snapshot |
componentWillUnmount | component is about to be unmounted | |
getChildContext | before render method, return value object is combined to sub tree context | |
getSnapshotBeforeUpdate | before component updates, return value is sent to componentDidUpdate as 3rd parameter | lastProps, lastState |
static getDerivedStateFromProps | before render method | nextProps, state |
componentDidAppear | component has mounted and is ready for animations | domNode |
componentWillDisappear | component is unmounted before DOM node is removed | domNode, callback |
componentWillDisappear has special type of argument "callback" which needs to be called when component is ready to be removed from the DOM. fe. after animations are finished.
Functional lifecycle events must be explicitly assigned via props onto a functional component like shown below:
import { render } from 'inferno';
function mounted(domNode) {
// [domNode] will be available for DOM nodes and components (if the component has mounted to the DOM)
}
function FunctionalComponent({ props }) {
return <div>Hello world</div>;
}
render(
<FunctionalComponent onComponentDidMount={mounted} />,
document.getElementById('app'),
);
Please note: class components (ES2015 classes) from inferno do not support the same lifecycle events (they have their own lifecycle events that work as methods on the class itself).
By default, Inferno will run in development mode. Development mode provides extra checks and better error messages at the cost of slower performance and larger code to parse. When using Inferno in a production environment, it is highly recommended that you turn off development mode.
Ensure the environment variable process.env.NODE_ENV is set to production.
When building your application bundle, ensure process.env.NODE_ENV is replaced with string"development" or "production" based on the workflow.
It is recommended to use ts-plugin-inferno for typescript TSX compilation and babel-plugin-infeno for javascript JSX compilation.
When building for development, you may want to use inferno.dev.mjs for v9 or newer and inferno.dev.esm.js for older than v9. That bundle file contains ES6 exports for better tree-shaking support, improved error messages and added validation to help fixing possible issues during development.
The file is found from package.json - dev:module entry point and the files are physically located in node_modules/inferno/dist/ folder.
Remember that it is not recommended to use that file in production due to slower performance. For production usage use node_modules/inferno/dist/inferno.mjs -file for v9 or newer and node_modules/inferno/dist/inferno.esm.js -file for older than v9.
Example of Webpack configuration:
const path = require('path');
const infernoTsx = require('ts-plugin-inferno').default;
... webpack config ...
module: {
rules: [
{
test: /\.js$/, // Add "jsx" if your application uses `jsx` file extensions
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
options: {
plugins: [
// Compile javascript JSX syntax using inferno's own plugin
['babel-plugin-inferno', {imports: true}]
]
}
}]
},
{
test: /\.ts+(|x)$/, // Compile ts and tsx extensions
exclude: /node_modules/,
use: [{
loader: 'ts-loader',
options: {
getCustomTransformers: () => ({
// inferno custom TSX plugin
after: [infernoTsx()]
}),
compilerOptions: {
/* typescript compiler options */
}
}
}]
}
]
},
resolve: {
extensions: ['.js', '.ts', '.tsx'],
alias: {
// This maps import "inferno" to es6 module entry based on workflow
inferno: path.resolve(__dirname, 'node_modules/inferno/dist', isProduction ? 'index.dev.mjs' : 'index.mjs')
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify(isProduction ? 'production' : 'development')
}
})
]
Example of Rollup configuration:
const path = require('path');
const alias = require('@rollup/plugin-alias');
const {babel} = require('@rollup/plugin-babel');
const replace = require('@rollup/plugin-replace');
const typescript = require('rollup-plugin-typescript2');
const transformInferno = require('ts-plugin-inferno').default;
... Rollup config ...
{
input: /* entry file */,
plugins: [
alias({
resolve: ['.js'],
entries: [
// This maps import "inferno" to es6 module entry based on workflow
{find: 'inferno', replacement: path.resolve(__dirname, 'node_modules/inferno/dist', isProduction ? 'index.dev.mjs' : 'index.mjs')}
]
}),
typescript({
include: ['*.ts+(|x)', '**/*.ts+(|x)'],
transformers: [
() => ({
after: [transformInferno()]
})
],
tsconfig: 'tsconfig.json',
tsconfigOverride: {
/* typescript compiler options */
}
}),
babel({
babelrc: false,
sourceMaps: isDeploy,
plugins: [
// Compile javascript JSX syntax using inferno's own plugin
['babel-plugin-inferno', {imports: true}]
],
babelHelpers: 'bundled'
})
]
}
Inferno always wants to deliver great performance. In order to do so, it has to make intelligent assumptions about the state of the DOM and the elements available to mutate. Custom namespaces conflict with this idea and change the schema of how different elements and attributes might work, so Inferno makes no attempt to support namespaces. Instead, SVG namespaces are automatically applied to elements and attributes based on their tag name.
If you want to contribute code, fork this project and submit a PR from your fork. To run browser tests you need to build the repos. A complete rebuild of the repos can take >5 mins.
$ git clone git@github.com:infernojs/inferno.git
$ cd inferno && npm i
$ npm run test:node
$ npm run build
$ npm run test:browser
If you only want to run the browser tests when coding, use the following to reduce turnaround by 50-80%:
$ npm run quick-test:browser # Compiles all packages and runs browser tests
$ npm run quick-test:browser-inferno # Only compiles the inferno package and runs browser tests
$ npm run quick-test:browser-debug # Compiles all packages and runs browser tests with "debug"
There is an InfernoJS Discord. You can join via https://discord.gg/SUKuhgaBpF.
This project exists thanks to all the people who contribute. [Contribute].
Thank you to all our backers! 🙏 [Become a backer]
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]