1 | <a href="https://preactjs.com">
|
2 | <img alt="Preact" title="Preact" src="https://cdn.rawgit.com/developit/b4416d5c92b743dbaec1e68bc4c27cda/raw/3235dc508f7eb834ebf48418aea212a05df13db1/preact-logo-trans.svg" width="550">
|
3 | </a>
|
4 |
|
5 | **Preact is a fast, `3kB` alternative to React, with the same ES2015 API.**
|
6 |
|
7 | Preact retains a large amount of compatibility with React, but only the modern ([ES6 Classes] and [stateless functional components](https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html#stateless-functional-components)) interfaces.
|
8 | As one would expect coming from React, Components are simple building blocks for composing a User Interface.
|
9 |
|
10 | ### :information_desk_person: Full documentation is available at the [Preact Website ➞](https://preactjs.com)
|
11 |
|
12 | [![CDNJS](https://img.shields.io/cdnjs/v/preact.svg)](https://cdnjs.com/libraries/preact)
|
13 | [![npm](https://img.shields.io/npm/v/preact.svg)](http://npm.im/preact)
|
14 | [![travis](https://travis-ci.org/developit/preact.svg?branch=master)](https://travis-ci.org/developit/preact)
|
15 | [![gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/developit/preact)
|
16 |
|
17 | [![Browsers](https://saucelabs.com/browser-matrix/preact.svg)](https://saucelabs.com/u/preact)
|
18 |
|
19 |
|
20 | ---
|
21 |
|
22 |
|
23 | ## Demos
|
24 |
|
25 | - [**ESBench**](http://esbench.com) is built using Preact.
|
26 | - [**Nectarine.rocks**](http://nectarine.rocks) _([Github Project](https://github.com/developit/nectarine))_ :peach:
|
27 | - [**Documentation Viewer**](https://documentation-viewer.firebaseapp.com) _([Github Project](https://github.com/developit/documentation-viewer))_
|
28 | - [**TodoMVC**](https://preact-todomvc.surge.sh) _([Github Project](https://github.com/developit/preact-todomvc))_
|
29 | - [**Hacker News Minimal**](https://developit.github.io/hn_minimal/) _([Github Project](https://github.com/developit/hn_minimal))_
|
30 | - [**Preact Boilerplate**](https://preact-boilerplate.surge.sh) _([Github Project](https://github.com/developit/preact-boilerplate))_ :zap:
|
31 | - [**Preact Redux Example**](https://github.com/developit/preact-redux-example) :star:
|
32 | - [**Flickr Browser**](http://codepen.io/developit/full/VvMZwK/) (@ CodePen)
|
33 | - [**Animating Text**](http://codepen.io/developit/full/LpNOdm/) (@ CodePen)
|
34 | - [**60FPS Rainbow Spiral**](http://codepen.io/developit/full/xGoagz/) (@ CodePen)
|
35 | - [**Simple Clock**](http://jsfiddle.net/developit/u9m5x0L7/embedded/result,js/) (@ JSFiddle)
|
36 | - [**3D + ThreeJS**](http://codepen.io/developit/pen/PPMNjd?editors=0010) (@ CodePen)
|
37 | - [**Stock Ticker**](http://codepen.io/developit/pen/wMYoBb?editors=0010) (@ CodePen)
|
38 | - [**Create your Own!**](https://jsfiddle.net/developit/rs6zrh5f/embedded/result/) (@ JSFiddle)
|
39 | - [**Preact Coffeescript**](https://github.com/crisward/preact-coffee)
|
40 | - [**GuriVR**](https://gurivr.com) _([Github Project](https://github.com/opennewslabs/guri-vr))_
|
41 | - [**V2EX Preact**](https://github.com/yanni4night/v2ex-preact)
|
42 |
|
43 | ## Libraries & Add-ons
|
44 |
|
45 | - :raised_hands: [**preact-compat**](https://git.io/preact-compat): use any React library with Preact *([full example](http://git.io/preact-compat-example))*
|
46 | - :repeat: [**preact-cycle**](https://git.io/preact-cycle): Functional-reactive paradigm for Preact
|
47 | - :page_facing_up: [**preact-render-to-string**](https://git.io/preact-render-to-string): Universal rendering.
|
48 | - :earth_americas: [**preact-router**](https://git.io/preact-router): URL routing for your components
|
49 | - :bookmark_tabs: [**preact-markup**](https://git.io/preact-markup): Render HTML & Custom Elements as JSX & Components
|
50 | - :satellite: [**preact-portal**](https://git.io/preact-portal): Render Preact components into (a) SPACE :milky_way:
|
51 | - :pencil: [**preact-richtextarea**](https://git.io/preact-richtextarea): Simple HTML editor component
|
52 | - :bookmark: [**preact-token-input**](https://github.com/developit/preact-token-input): Text field that tokenizes input, for things like tags
|
53 | - :card_index: [**preact-virtual-list**](https://github.com/developit/preact-virtual-list): Easily render lists with millions of rows ([demo](https://jsfiddle.net/developit/qqan9pdo/))
|
54 | - :triangular_ruler: [**preact-layout**](https://download.github.io/preact-layout/): Small and simple layout library
|
55 | - :thought_balloon: [**preact-socrates**](https://github.com/matthewmueller/preact-socrates): Preact plugin for [Socrates](http://github.com/matthewmueller/socrates)
|
56 | - :rowboat: [**preact-flyd**](https://github.com/xialvjun/preact-flyd): Use [flyd](https://github.com/paldepind/flyd) FRP streams in Preact + JSX
|
57 | - :speech_balloon: [**preact-i18nline**](https://github.com/download/preact-i18nline): Integrates the ecosystem around [i18n-js](https://github.com/everydayhero/i18n-js) with Preact via [i18nline](https://github.com/download/i18nline).
|
58 | - :white_square_button: [**preact-mdl**](https://git.io/preact-mdl): Use [MDL](https://getmdl.io) as Preact components
|
59 | - :rocket: [**preact-photon**](https://git.io/preact-photon): build beautiful desktop UI with [photon](http://photonkit.com)
|
60 | - :microscope: [**preact-jsx-chai**](https://git.io/preact-jsx-chai): JSX assertion testing _(no DOM, right in Node)_
|
61 | - :tophat: [**preact-classless-component**](https://github.com/ld0rman/preact-classless-component): create preact components without the class keyword
|
62 | - :hammer: [**preact-hyperscript**](https://github.com/queckezz/preact-hyperscript): Hyperscript-like syntax for creating elements
|
63 | - :white_check_mark: [**shallow-compare**](https://github.com/tkh44/shallow-compare): simplified `shouldComponentUpdate` helper.
|
64 |
|
65 |
|
66 | ## Getting Started
|
67 |
|
68 | > :information_desk_person: You [don't _have_ to use ES2015 to use Preact](https://github.com/developit/preact-without-babel)... but you should.
|
69 |
|
70 | The following guide assumes you have some sort of ES2015 build set up using babel and/or webpack/browserify/gulp/grunt/etc. If you don't, start with [preact-boilerplate] or a [CodePen Template](http://codepen.io/developit/pen/pgaROe?editors=0010).
|
71 |
|
72 |
|
73 | ### Import what you need
|
74 |
|
75 | The `preact` module provides both named and default exports, so you can either import everything under a namespace of your choosing, or just what you need as locals:
|
76 |
|
77 | ##### Named:
|
78 |
|
79 | ```js
|
80 | import { h, render, Component } from 'preact';
|
81 |
|
82 | // Tell Babel to transform JSX into h() calls:
|
83 | /** @jsx h */
|
84 | ```
|
85 |
|
86 | ##### Default:
|
87 |
|
88 | ```js
|
89 | import preact from 'preact';
|
90 |
|
91 | // Tell Babel to transform JSX into preact.h() calls:
|
92 | /** @jsx preact.h */
|
93 | ```
|
94 |
|
95 | > Named imports work well for highly structured applications, whereas the default import is quick and never needs to be updated when using different parts of the library.
|
96 | >
|
97 | > Instead of declaring the `@jsx` pragma in your code, it's best to configure it globally in a `.babelrc`:
|
98 | >
|
99 | > **For Babel 5 and prior:**
|
100 | >
|
101 | > ```json
|
102 | > { "jsxPragma": "h" }
|
103 | > ```
|
104 | >
|
105 | > **For Babel 6:**
|
106 | >
|
107 | > ```json
|
108 | > {
|
109 | > "plugins": [
|
110 | > ["transform-react-jsx", { "pragma":"h" }]
|
111 | > ]
|
112 | > }
|
113 | > ```
|
114 |
|
115 |
|
116 | ### Rendering JSX
|
117 |
|
118 | Out of the box, Preact provides an `h()` function that turns your JSX into Virtual DOM elements _([here's how](http://jasonformat.com/wtf-is-jsx))_. It also provides a `render()` function that creates a DOM tree from that Virtual DOM.
|
119 |
|
120 | To render some JSX, just import those two functions and use them like so:
|
121 |
|
122 | ```js
|
123 | import { h, render } from 'preact';
|
124 |
|
125 | render((
|
126 | <div id="foo">
|
127 | <span>Hello, world!</span>
|
128 | <button onClick={ e => alert("hi!") }>Click Me</button>
|
129 | </div>
|
130 | ), document.body);
|
131 | ```
|
132 |
|
133 | This should seem pretty straightforward if you've used [hyperscript] or one of its many friends.
|
134 |
|
135 | Rendering hyperscript with a virtual DOM is pointless, though. We want to render components and have them updated when data changes - that's where the power of virtual DOM diffing shines. :star2:
|
136 |
|
137 |
|
138 | ### Components
|
139 |
|
140 | Preact exports a generic `Component` class, which can be extended to build encapsulated, self-updating pieces of a User Interface. Components support all of the standard React [lifecycle methods], like `shouldComponentUpdate()` and `componentWillReceiveProps()`. Providing specific implementations of these methods is the preferred mechanism for controlling _when_ and _how_ components update.
|
141 |
|
142 | Components also have a `render()` method, but unlike React this method is passed `(props, state)` as arguments. This provides an ergonomic means to destructure `props` and `state` into local variables to be referenced from JSX.
|
143 |
|
144 | Let's take a look at a very simple `Clock` component, which shows the current time.
|
145 |
|
146 | ```js
|
147 | import { h, render, Component } from 'preact';
|
148 |
|
149 | class Clock extends Component {
|
150 | render() {
|
151 | let time = new Date().toLocaleTimeString();
|
152 | return <span>{ time }</span>;
|
153 | }
|
154 | }
|
155 |
|
156 | // render an instance of Clock into <body>:
|
157 | render(<Clock />, document.body);
|
158 | ```
|
159 |
|
160 |
|
161 | That's great. Running this produces the following HTML DOM structure:
|
162 |
|
163 | ```html
|
164 | <span>10:28:57 PM</span>
|
165 | ```
|
166 |
|
167 | In order to have the clock's time update every second, we need to know when `<Clock>` gets mounted to the DOM. _If you've used HTML5 Custom Elements, this is similar to the `attachedCallback` and `detachedCallback` lifecycle methods._ Preact invokes the following lifecycle methods if they are defined for a Component:
|
168 |
|
169 | | Lifecycle method | When it gets called |
|
170 | |-----------------------------|--------------------------------------------------|
|
171 | | `componentWillMount` | before the component gets mounted to the DOM |
|
172 | | `componentDidMount` | after the component gets mounted to the DOM |
|
173 | | `componentWillUnmount` | prior to removal from the DOM |
|
174 | | `componentDidUnmount` | after removal from the DOM |
|
175 | | `componentWillReceiveProps` | before new props get accepted |
|
176 | | `shouldComponentUpdate` | before `render()`. Return `false` to skip render |
|
177 | | `componentWillUpdate` | before `render()` |
|
178 | | `componentDidUpdate` | after `render()` |
|
179 |
|
180 |
|
181 |
|
182 | So, we want to have a 1-second timer start once the Component gets added to the DOM, and stop if it is removed. We'll create the timer and store a reference to it in `componentDidMount`, and stop the timer in `componentWillUnmount`. On each timer tick, we'll update the component's `state` object with a new time value. Doing this will automatically re-render the component.
|
183 |
|
184 | ```js
|
185 | import { h, render, Component } from 'preact';
|
186 |
|
187 | class Clock extends Component {
|
188 | constructor() {
|
189 | super();
|
190 | // set initial time:
|
191 | this.state.time = Date.now();
|
192 | }
|
193 |
|
194 | componentDidMount() {
|
195 | // update time every second
|
196 | this.timer = setInterval(() => {
|
197 | this.setState({ time: Date.now() });
|
198 | }, 1000);
|
199 | }
|
200 |
|
201 | componentWillUnmount() {
|
202 | // stop when not renderable
|
203 | clearInterval(this.timer);
|
204 | }
|
205 |
|
206 | render(props, state) {
|
207 | let time = new Date(state.time).toLocaleTimeString();
|
208 | return <span>{ time }</span>;
|
209 | }
|
210 | }
|
211 |
|
212 | // render an instance of Clock into <body>:
|
213 | render(<Clock />, document.body);
|
214 | ```
|
215 |
|
216 | Now we have [a ticking clock](http://jsfiddle.net/developit/u9m5x0L7/embedded/result,js/)!
|
217 |
|
218 |
|
219 | ### Props & State
|
220 |
|
221 | The concept (and nomenclature) for `props` and `state` is the same as in React. `props` are passed to a component by defining attributes in JSX, `state` is internal state. Changing either triggers a re-render, though by default Preact re-renders Components asynchronously for `state` changes and synchronously for `props` changes. You can tell Preact to render `prop` changes asynchronously by setting `options.syncComponentUpdates` to `false`.
|
222 |
|
223 |
|
224 | ---
|
225 |
|
226 |
|
227 | ## Linked State
|
228 |
|
229 | One area Preact takes a little further than React is in optimizing state changes. A common pattern in ES2015 React code is to use Arrow functions within a `render()` method in order to update state in response to events. Creating functions enclosed in a scope on every render is inefficient and forces the garbage collector to do more work than is necessary.
|
230 |
|
231 | One solution to this is to bind component methods declaratively.
|
232 | Here is an example using [decko](http://git.io/decko):
|
233 |
|
234 | ```js
|
235 | class Foo extends Component {
|
236 | @bind
|
237 | updateText(e) {
|
238 | this.setState({ text: e.target.value });
|
239 | }
|
240 | render({ }, { text }) {
|
241 | return <input value={text} onInput={this.updateText} />;
|
242 | }
|
243 | }
|
244 | ```
|
245 |
|
246 | While this achieves much better runtime performance, it's still a lot of unnecessary code to wire up state to UI.
|
247 |
|
248 | Fortunately there is a solution, in the form of `linkState()`. Calling `component.linkState('text')` returns a function that accepts an Event and uses it's associated value to update the given property in your component's state. Calls to linkState() with the same state property are cached, so there is no performance penalty. Here is the previous example rewritten using _Linked State_:
|
249 |
|
250 | ```js
|
251 | class Foo extends Component {
|
252 | render({ }, { text }) {
|
253 | return <input value={text} onInput={this.linkState('text')} />;
|
254 | }
|
255 | }
|
256 | ```
|
257 |
|
258 | Simple and effective. It handles linking state from any input type, or an optional second parameter can be used to explicitly provide a keypath to the new state value.
|
259 |
|
260 |
|
261 | ## Examples
|
262 |
|
263 | Here is a somewhat verbose Preact `<Link>` component:
|
264 |
|
265 | ```js
|
266 | class Link extends Component {
|
267 | render(props, state) {
|
268 | return <a href={ props.href }>{ props.children }</a>;
|
269 | }
|
270 | }
|
271 | ```
|
272 |
|
273 | Since this is ES6/ES2015, we can further simplify:
|
274 |
|
275 | ```js
|
276 | class Link extends Component {
|
277 | render({ href, children }) {
|
278 | return <a {...{ href, children }} />;
|
279 | }
|
280 | }
|
281 |
|
282 | // or, for wide-open props support:
|
283 | class Link extends Component {
|
284 | render(props) {
|
285 | return <a {...props} />;
|
286 | }
|
287 | }
|
288 |
|
289 | // or, as a stateless functional component:
|
290 | const Link = ({ children, ...props }) => (
|
291 | <a {...props}>{ children }</a>
|
292 | );
|
293 | ```
|
294 |
|
295 |
|
296 | ## Extensions
|
297 |
|
298 | It is likely that some projects based on Preact would wish to extend Component with great new functionality.
|
299 |
|
300 | Perhaps automatic connection to stores for a Flux-like architecture, or mixed-in context bindings to make it feel more like `React.createClass()`. Just use ES2015 inheritance:
|
301 |
|
302 | ```js
|
303 | class BoundComponent extends Component {
|
304 | constructor(props) {
|
305 | super(props);
|
306 | this.bind();
|
307 | }
|
308 | bind() {
|
309 | this.binds = {};
|
310 | for (let i in this) {
|
311 | this.binds[i] = this[i].bind(this);
|
312 | }
|
313 | }
|
314 | }
|
315 |
|
316 | // example usage
|
317 | class Link extends BoundComponent {
|
318 | click() {
|
319 | open(this.href);
|
320 | }
|
321 | render() {
|
322 | let { click } = this.binds;
|
323 | return <span onclick={ click }>{ children }</span>;
|
324 | }
|
325 | }
|
326 | ```
|
327 |
|
328 |
|
329 | The possibilities are pretty endless here. You could even add support for rudimentary mixins:
|
330 |
|
331 | ```js
|
332 | class MixedComponent extends Component {
|
333 | constructor() {
|
334 | super();
|
335 | (this.mixins || []).forEach( m => Object.assign(this, m) );
|
336 | }
|
337 | }
|
338 | ```
|
339 |
|
340 | ## Developer Tools
|
341 |
|
342 | You can inspect and modify the state of your Preact UI components at runtime using the
|
343 | [React Developer Tools](https://github.com/facebook/react-devtools) browser extension.
|
344 |
|
345 | 1. Install the [React Developer Tools](https://github.com/facebook/react-devtools) extension
|
346 | 2. Import the "preact/devtools" module in your app
|
347 | 3. Reload and go to the 'React' tab in the browser's development tools
|
348 |
|
349 |
|
350 | ```js
|
351 | import { h, Component, render } from 'preact';
|
352 |
|
353 | // Enable devtools. You can reduce the size of your app by only including this
|
354 | // module in development builds. eg. In Webpack, wrap this with an `if (module.hot) {...}`
|
355 | // check.
|
356 | require('preact/devtools');
|
357 | ```
|
358 |
|
359 | ## License
|
360 |
|
361 | MIT
|
362 |
|
363 |
|
364 |
|
365 | [![Preact](http://i.imgur.com/YqCHvEW.gif)](https://preactjs.com)
|
366 |
|
367 |
|
368 |
|
369 | [ES6 Classes]: https://facebook.github.io/react/docs/reusable-components.html#es6-classes
|
370 | [hyperscript]: https://github.com/dominictarr/hyperscript
|
371 | [preact-boilerplate]: https://github.com/developit/preact-boilerplate
|
372 | [lifecycle methods]: https://facebook.github.io/react/docs/component-specs.html
|