Skip to content

Commit e9f1a56

Browse files
committed
Initial commit
0 parents  commit e9f1a56

File tree

10 files changed

+946
-0
lines changed

10 files changed

+946
-0
lines changed

.babelrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"modules": "umdStrict",
3+
"sourceMaps": true,
4+
"compact": true,
5+
"comments": false,
6+
"loose": "all"
7+
}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
npm-debug.log
3+
.DS_Store

.jshintrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"esnext": true,
3+
"expr": true
4+
}

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
language: node_js

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2015 Jason Miller
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
# Preact
2+
3+
[![npm Version](https://img.shields.io/npm/v/preact.svg)](http://npm.im/preact)
4+
[![travis-ci](https://travis-ci.org/developit/preact.svg)](https://travis-ci.org/developit/preact)
5+
6+
> Name stolen, if found return to jason [at] developit [dot] ca
7+
8+
9+
## Overview
10+
11+
Preact is an attempt to recreate the core value proposition of React (or similar libraries like Mithril) using as little code as possible, with first-class support for ES2015. Currently the library is around **3kb** (minified & gzipped).
12+
13+
It retains a large amount of compatibility with React, but only the [ES6 Classes] interface. As one would expect coming from React, Components are simple building blocks for composing a User Interface.
14+
15+
16+
## Getting Started
17+
18+
> You don't _have_ to use ES2015 to use Preact... but you should.
19+
>
20+
> I'm going to assume 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].
21+
22+
23+
### Import what you need
24+
25+
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:
26+
27+
##### Default:
28+
29+
```js
30+
import preact from 'preact';
31+
32+
// Tell Babel to transform JSX into preact.h() calls:
33+
/** @jsx preact.h */
34+
```
35+
36+
##### Named:
37+
38+
```js
39+
import { h, render, Component } from 'preact';
40+
41+
// Tell Babel to transform JSX into h() calls:
42+
/** @jsx h */
43+
```
44+
45+
> 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.
46+
>
47+
> Instead of declaring the `@jsx` pragma in your code, it's best to configure it globally in a `.babelrc`:
48+
>
49+
> ```
50+
> { "jsxPragma": "h" }
51+
> ```
52+
53+
54+
### Rendering JSX
55+
56+
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.
57+
58+
To render some JSX, just import those two functions and use them like so:
59+
60+
```js
61+
import { h, render } from 'preact';
62+
63+
render((
64+
<div id="foo">
65+
<span>Hello, world!</span>
66+
<button onClick={ e => alert("hi!"); }>Click Me</button>
67+
</div>
68+
), document.body);
69+
```
70+
71+
This should seem pretty straightforward if you've used [hyperscript] or one of its many friends.
72+
73+
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.
74+
75+
76+
### Components
77+
78+
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.
79+
80+
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.
81+
82+
Let's take a look at a very simple `Clock` component, which shows the current time.
83+
84+
```js
85+
import { h, render, Component } from 'preact';
86+
87+
class Clock extends Component {
88+
render() {
89+
let time = new Date().toLocaleTimeString();
90+
return <span>{ time }</span>;
91+
}
92+
}
93+
94+
// render an instance of Clock into <body>:
95+
render(<Clock />, document.body);
96+
```
97+
98+
99+
That's great. Running this produces the following HTML DOM structure:
100+
101+
```html
102+
<span>10:28:57 PM</span>
103+
```
104+
105+
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:
106+
107+
| Lifecycle method | When it gets called |
108+
|-----------------------------|--------------------------------------------------|
109+
| `componentWillMount` | before the component gets mounted to the DOM |
110+
| `componentDidMount` | after the component gets mounted to the DOM |
111+
| `componentWillUnmount` | prior to removal from the DOM |
112+
| `componentDidUnmount` | after removal from the DOM |
113+
| `componentWillReceiveProps` | before new props get accepted |
114+
| `shouldComponentUpdate` | before `render()`. Return `false` to skip render |
115+
| `componentWillUpdate` | before `render()` |
116+
| `componentDidUpdate` | after `render()` |
117+
118+
119+
120+
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.
121+
122+
```js
123+
import { h, render, Component } from 'preact';
124+
125+
class Clock extends Component {
126+
constructor() {
127+
super();
128+
// set initial time:
129+
this.state.time = Date.now();
130+
}
131+
132+
componentDidMount() {
133+
// update time every second
134+
this.timer = setInterval(() => {
135+
this.setState({ time: Date.now() });
136+
}, 1000);
137+
}
138+
139+
componentWillUnmount() {
140+
// stop when not renderable
141+
clearInterval(this.timer);
142+
}
143+
144+
render(props, state) {
145+
let time = new Date(state.time).toLocaleTimeString();
146+
return <span>{ time }</span>;
147+
}
148+
}
149+
150+
// render an instance of Clock into <body>:
151+
render(<Clock />, document.body);
152+
```
153+
154+
Now we have a ticking clock!
155+
156+
157+
### Props & State
158+
159+
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`.
160+
161+
162+
## Examples
163+
164+
Here is a somewhat verbose Preact `<Link>` component:
165+
166+
```js
167+
class Link extends Component {
168+
render(props, state) {
169+
return <a href={ props.href }>{ props.children }</a>;
170+
}
171+
}
172+
```
173+
174+
Since this is ES6/ES2015, we can further simplify:
175+
176+
```js
177+
class Link extends Component {
178+
render({ href, children }) {
179+
return <a {...{ href, children }} />;
180+
}
181+
}
182+
183+
// or, for wide-open props support:
184+
class Link extends Component {
185+
render(props) {
186+
return <a {...props} />;
187+
}
188+
}
189+
```
190+
191+
192+
## Extensions
193+
194+
It is likely that some projects based on Preact would wish to extend Component with great new functionality.
195+
196+
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:
197+
198+
```js
199+
class BoundComponent extends Component {
200+
constructor(props) {
201+
super(props);
202+
this.bind();
203+
}
204+
bind() {
205+
this.binds = {};
206+
for (let i in this) {
207+
this.binds[i] = this[i].bind(this);
208+
}
209+
}
210+
}
211+
212+
// example usage
213+
class Link extends BoundComponent {
214+
click() {
215+
open(this.href);
216+
}
217+
render() {
218+
let { click } = this.binds;
219+
return <span onclick={ click }>{ children }</span>;
220+
}
221+
}
222+
```
223+
224+
225+
The possibilities are pretty endless here. You could even add support for rudimentary mixins:
226+
227+
```js
228+
class MixedComponent extends Component {
229+
constructor() {
230+
super();
231+
(this.mixins || []).forEach( m => Object.assign(this, m) );
232+
}
233+
}
234+
```
235+
236+
237+
## License
238+
239+
MIT
240+
241+
242+
243+
[ES6 Classes]: https://facebook.github.io/react/docs/reusable-components.html#es6-classes
244+
[hyperscript]: https://github.com/dominictarr/hyperscript
245+
[preact-boilerplate]: https://github.com/developit/preact-boilerplate
246+
[lifecycle methods]: https://facebook.github.io/react/docs/component-specs.html

package.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "preact",
3+
"version": "1.2.0",
4+
"description": "Tiny & fast Component-based virtual DOM framework.",
5+
"main": "preact.js",
6+
"scripts": {
7+
"build": "npm run transpile && npm run minify && npm run size",
8+
"transpile": "babel src --source-root src -s -d .",
9+
"minify": "uglifyjs -c sequences,dead_code,conditionals,booleans,unused,if_return,join_vars,drop_console -m -o $npm_package_main --in-source-map ${npm_package_main}.map --source-map ${npm_package_main}.map -- $npm_package_main",
10+
"size": "size=$(gzip-size $npm_package_main) && echo \"gzip size: $size / $(pretty-bytes $size)\"",
11+
"test": "jshint {src,tests}/**.js && mocha --compilers js:babel/register test",
12+
"prepublish": "npm run build",
13+
"release": "npm run build && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags && npm publish"
14+
},
15+
"repository": {
16+
"type": "git",
17+
"url": "https://github.com/developit/preact.git"
18+
},
19+
"author": "Jason Miller <jason@developit.ca>",
20+
"license": "ISC",
21+
"bugs": {
22+
"url": "https://github.com/developit/preact/issues"
23+
},
24+
"homepage": "https://github.com/developit/preact",
25+
"devDependencies": {
26+
"babel": "^5.8.23",
27+
"chai": "^3.2.0",
28+
"gzip-size": "^3.0.0",
29+
"jshint": "^2.8.0",
30+
"mocha": "^2.3.2",
31+
"pretty-bytes": "^2.0.1",
32+
"uglify-js": "^2.4.24",
33+
"xo": "^0.8.0"
34+
},
35+
"xo": {
36+
"esnext": true,
37+
"ignore": [
38+
"*.js"
39+
]
40+
}
41+
}

preact.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

preact.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)