5/3/17
Universal React Apps made simple
Charlie King - @thebringking
https://zeit.co
Data Fetching, Client reconciliation, Routing
https://zeit.co
https://zeit.co
Our bundle size
~2MB
~5MB
~3MB
Bundle split by Page
/
/deals
/careers
Size
~1MB
+= 200KB
+= 210KB
Time to First Paint, Time to First Meaningful Paint (TTFP, TTFMP)
Content in ~4s - 3G
Interactive in ~8s
Content in ~10s - 3G
Interactive right after paint
Content in ~5s - 2G!
Interactive in ~8s
Flash of un-styled content (FOUC)
.wm-slide-row-container {
height: auto;
}
.wm-slide-row-header {
align-items: center;
display: flex;
justify-content: space-between;
padding-left: 15px;
padding-right: 15px;
.wm-icon {
height: 21px;
margin-right: 9px;
width: 21px;
&.wm-icon-verified {
margin-right: 5px;
}
@include media($desktop) {
height: 26px;
margin-right: 11px;
width: 26px;
}
svg {
height: 21px;
@include media($desktop) {
height: 26px;
}
}
}
.pushup {
margin-bottom: 1px;
}
h2 {
align-items: center;
color: $charcoal;
display: flex;
font-size: 22px;
font-weight: 600;
line-height: 1;
margin: 0;
@include media($medium) {
font-size: 24px;
}
@include media($desktop) {
font-size: 26px;
}
a {
color: $charcoal;
text-decoration: none;
&:hover,
&:focus {
color: $teal;
}
}
.pushup {
margin-bottom: 1px;
}
}
nav {
white-space: nowrap;
> div {
display: inline-block;
vertical-align: middle;
+ div {
margin-left: 7.5px;
}
}
@include media($mobile) {
.arrows {
display: none;
.desktop & {
display: inline-block;
}
}
}
}
.arrows {
font-size: 0;
height: $scroll-button-size;
width: $scroll-button-size * 2;
a {
background: $white;
border: 1px solid $gainsboro;
border-radius: 5px 0 0 5px;
color: $steel;
cursor: pointer;
display: block;
float: left;
font-size: 24px;
height: $scroll-button-size;
line-height: $scroll-button-size;
position: relative;
text-align: center;
width: $scroll-button-size;
+ a {
border-left: 0;
border-radius: 0 5px 5px 0;
float: right;
}
&.disabled {
color: $gainsboro;
cursor: default;
pointer-events: none;
}
&.loading {
color: transparent;
}
.spinner {
left: 0;
position: absolute;
right: 0;
top: 4px;
}
}
}
.btn-rounded {
align-items: center;
background: $white;
border-color: $gainsboro;
color: $steel;
display: flex;
font-size: 14px;
height: 40px;
line-height: $scroll-button-size - 2px;
padding: 0 15px;
span {
display: none;
@include media($desktop) {
display: inline-block;
}
}
.wm-icon {
margin-right: 0;
@include media($desktop) {
margin-right: 1px;
}
}
}
.displayname-short {
display: inline-block;
@media screen and (min-width: 375px) {
display: none;
}
}
.displayname-long {
display: none;
@media screen and (min-width: 375px) {
display: inline-block;
}
}
}
.wm-slide-row-content {
width: 100%;
max-width: 100%;
position: relative;
overflow-x: scroll;
white-space: nowrap;
overflow-y: hidden;
@include media($medium) {
overflow: hidden;
}
}
.wm-slide-row-track {
position: relative;
top: 0;
left: 0;
display: block;
transition: transform ease 250ms;
}
.wm-slide-page {
display: inline-block;
width: 100%;
}
.test-card {
width: 33.333%;
@include media($medium) {
width: 25%;
}
@include media($desktop) {
width: 20%;
}
}
component.scss
<div class="wm-slide-row-container">
<div class="wm-slide-row-header">
<div ng-transclude="header" ng-if="vm.showCustomHeader"></div>
<nav>
<div ng-if="vm.showCount" class="category-count">
{{ vm.totalCount }} {{ vm.countName }}
</div>
<div class="extra" ng-transclude="controls" ng-if="vm.showExtraControls">
</div>
<div class="arrows" ng-if="vm.showArrows">
<a
ng-click="vm.onPreviousPage()">
<i class="icon ion-ios-arrow-back"></i>
</a>
<a
ng-click="vm.onNextPage()">
<i class="icon ion-ios-arrow-forward"></i>
</a>
</div>
</nav>
</div>
<div class="wm-slide-row-content">
<div ng-transclude="content" class="wm-slide-row-track"
ng-style="{transform:vm.currentTrackTransform}">
</div>
</div>
</div>
some_view.html
@component({
name: 'wmSlideRow',
template: 'js/components/wm_slide_row/templates/wm_slide_row.html',
bindings: {
name: '@',
itemCount: '<',
icon: '@',
hasMore: '<',
loadMore: '=?',
totalCount: '<',
useCount: '<',
countName: '@'
},
transclude: {
header: '?wmSlideRowHeader',
content: 'wmSlideRowContent',
controls: '?wmSlideRowControls'
}
})
class WmSlideRow { // eslint-disable-line no-unused-vars
static $inject = ['$element', 'wmLogger', '$transclude'];
constructor(...injected) {
WmSlideRow.$inject.forEach((dep, idx) => (this[dep] = injected[idx]));
this.$log = this.wmLogger.loggerFor(WmSlideRow);
this.pages = [];
this.currentPage = 0;
}
$onInit() {
const { $transclude } = this;
this.showExtraControls = $transclude.isSlotFilled('controls');
this.showCustomHeader = $transclude.isSlotFilled('header');
this.setShowArrows();
}
setCurrentTrackTransform() {
// return a negative percentage based on the current page divided by the
// total number of pages. e.g. 2 pages would be -100% because (1/2 * 100) * 2
this.currentTrackTransform = `translate3d(-${(this.currentPage / this.pages.length * 100) * 2}%, 0, 0)`;
}
setShowArrows() {
// return true if we have multiple pages, or the user tells
// us they can lazy load more
this.showArrows = this.pages.length > 1 || this.hasMore;
}
onNextPage() {
this.currentPage = Math.min(this.pages.length - 1, this.currentPage + 1);
this.setCurrentTrackTransform();
}
onPreviousPage() {
this.currentPage = Math.max(0, this.currentPage - 1);
this.setCurrentTrackTransform();
}
registerPage(page) {
this.pages.push({ page });
const result = { totalPages: this.pages.length, index: this.pages.length - 1 };
// update the arrow state
this.setShowArrows();
return result;
}
}
some_js.js
import Document from 'next/document';
import { flush } from '@ghostgroup/universal-sass-loader/runtime';
export class WeedmapsDocument extends Document {
static async getInitialProps({ req }) {
const page = renderPage();
const styles = flush(); // flush the styles used in the page render
return { ...page, styles}
}
render() {
return (
<html lang="en">
<Head>
<style id="all-the-styles" dangerouslySetInnerHTML={{ __html: this.props.style.contents }}/>
</Head>
<body>
<Main/>
<NextScript/>
</body>
</html>
);
}
}
_document.js
Result
React is faster than Angular 1.x (duh)
Reusable Components across the org
Active Community for quick fixes and many examples -
https://github.com/zeit/next.js/tree/master/examples