J A C K T O L D U S “ W H Y ”S I N G L E - PA G E A P P S
H O W
N I K O L A Y B A C H I Y S K I , A U T O M A T T I C , W O R D C A M P V I E N N A , A P R I L 1 1 , 2 0 1 5
A U T O M A T T I CE X T R A P O L A T E . M E@ N I K O L A Y B
F A S T E R
M O R E E N G A G I N G
C H A N G EL E T ' S TA L K A B O U T
S T A T EL E T ' S TA L K A B O U T
O L D - S C H O O L V S .
S I N G L E - P A G E A P P S
O L D S C H O O L
C H A N G E ⇒ R E F R E S H
O L D - S C H O O L
C H A N G E ⇒
S I N G L E - P A G E
U I L I B R A R Y
N O T A F R A M E W O R K
var Post = React.createClass( { function render() { return ( <div className="post"> <p className="content"> { this.props.post.content } </p> <PostMeta post={ this.props.post } /> </div> ); } } );
var Post = React.createClass( { function render() { return ( <div className="post"> <p className="content"> { this.props.post.content } </p> <PostMeta post={ this.props.post } /> </div> ); } } ); J S X
var Post = React.createClass( { function render() { return ( React.DOM.div( {className: 'post'}, [ React.DOM.p( null, this.props.post.content ), PostMeta( {post: this.props.post} ) ); ); } } );
var Post = React.createClass( { function render() { return ( <div className="post"> <p className="content"> { this.props.post.content } </p> <PostMeta post={ this.props.post } /> </div> ); } } );
var PostMeta = React.createClass( { function getInitialState() { return { hidden: true }; }, function onToggle() { this.setState( { hidden: ! this.state.hidden } ); }, function render() { return ( <div className="meta"> { this.props.post.author } <button onClick={ this.onToggle }> { this.state.hidden ? 'More' : 'Hide' } </button> ( this.state.hidden ? <p>…extra meta…</p> : null ) </div> ); } } );
var PostMeta = React.createClass( { function getInitialState() { return { hidden: true }; }, function onToggle() { this.setState( { hidden: ! this.state.hidden } ); }, function render() { return ( <div className="meta"> { this.props.post.author } <button onClick={ this.onToggle }> { this.state.hidden ? 'More' : 'Hide' } </button> ( this.state.hidden ? <p>…extra meta…</p> : null ) </div> ); } } );
var PostMeta = React.createClass( { function getInitialState() { return { hidden: true }; }, function onToggle() { this.setState( { hidden: ! this.state.hidden } ); }, function render() { return ( <div className="meta"> { this.props.post.author } <button onClick={ this.onToggle }> { this.state.hidden ? 'More' : 'Hide' } </button> ( this.state.hidden ? <p>…extra meta…</p> : null ) </div> ); } } );
var PostMeta = React.createClass( { function getInitialState() { return { hidden: true }; }, function onToggle() { this.setState( { hidden: ! this.state.hidden } ); }, function render() { return ( <div className="meta"> { this.props.post.author } <button onClick={ this.onToggle }> { this.state.hidden ? 'More' : 'Hide' } </button> ( this.state.hidden ? <p>…extra meta…</p> : null ) </div> ); } } );
R E - R E N D E R S
C O M P O N E N T SL E T ' S TA L K A B I T M O R E A B O U T
C O M P O S A B L E
<MasterBar> <UserGreeting user="…"> <Avatar login={ this.props.user.login } /> Howdy, { this.props.user.nickname } <LogOutLink id={ this.props.user.id } /> </UserGreeting> </MasterBar>
B U I L D I N G B L O C K S O F O U R A P P L I C A T I O N
<MasterBar /> <MastHead /> <Navigation active="archive" /> <Posts date="2015-‐04-‐01" author="crumb"> <Post> … </Post> <Post> … </Post> … </Posts>
<MasterBar /> <MastHead /> <Posts date="2015-‐04-‐01" author="crumb"> <Post post="…"> <p class="content"> { this.props.post.content} </p> <PostMeta post="…" /> </Post> <Post> … </Post> … </Posts>
T E S T A B L E
P R E V E N T S X S S
<p>{ this.props.content }</p>
<img src="javascript:alert('XSS');">
<img src="javascript:alert('XSS');">
<p dangerouslySetInnerHTML={{ __html: this.props.content }} />
N O T T E M P L A T E S
<div class="posts"> {{#each posts}} <div class="post"> {{#if author}} </div> {{/each}} </div>
R E U S A B L E
<Comment> <Avatar email={ this.props.comment.author.email} /> … </Comment>
<PostMeta> <Avatar email={ this.props.comment.author.email} /> … </PostMeta>
W A I T !
K E E P L O G I C A N D M A R K U P…A P O P U L A R B E S T P R A C T I C E S A Y S …
… S E P A R A T E !
S E P A R A T I O N O F C O N C E R N S I S G O O D , R I G H T ?
W H E N D O W E N E E D A N E V E N T H A N D L E R W I T H O U T T H E D O M E L E M E N T I T I S B O U N D T O ?
onClick()
<button>
W H E N D O W E N E E D A D O M E L E M E N T W I T H O U T T H E E V E N T H A N D L E R S F O R I T S A C T I O N S ?
onClick()
<button>
H T M L
D I S P L A Y L O G I C
T H E S A M E C O N C E R N
H T M LJ S
T H E S A M E C O N C E R N
R E - R E N D E R I N G
V I R T U A L D O M
0 . R U N render()
O N E V E R Y U P D A T E
1 . D I F F W I T H P R E V I O U S T R E E
O N E V E R Y U P D A T E
http://calendar.perfplanet.com/2013/diff/
2 . C O M P U T E M I N I M U M S E T O F D O M C H A N G E S
O N E V E R Y U P D A T E
3 . B A T C H - E X E C U T E A L L D O M U P D A T E S
O N E V E R Y U P D A T E
V I R T U A L D O M : N O T O N L Y S P E E D !
S E R V E R - S I D E R E N D E R I N G
S V G , V M L , C A N V A S
R U N N I N G I N A W E B W O R K E R
F L U Xhttps://facebook.github.io/flux/
http://blog.andrewray.me/flux-for-stupid-people/
https://scotch.io/tutorials/getting-to-know-flux-the-react-js-architecture
N O T A F R A M E W O R K
A R C H I T E C T U R E B L U E P R I N T
L E T ' S C H A T
S U M M A R Y
C O M P O N E N T S , N O T T E M P L A T E S
R E - R E N D E R , D O N ' T T O U C H
T H E D O M
V I R T U A L D O M I S S I M P L E , F A S T , F U N
L E T ' S TA L K A B O U T
Q U E S T I O N S ? @ N I K O L A Y B
Top Related