Arunoda Susiripala, de MeteorJS à Next.js
Disclaimer !
MeteorJS
Brièvement
Réconcilier client/serveur
Déploiement
~ meteor deploy your-app.com --settings production-settings.json
Contributions
- Meteor-up
- Flow-router
- Meteor hacks (SSR ...)
- ...
Le 30/12/2016 ...
Kadira is a 100 per cent bootstrapped company from my personal funds.
[...]
We were pretty successful with Meteor, but the market of Meteor apps was pretty small for us.
Universal JS Application
That situation was really no better than, say, PHP's. In many ways, PHP was actually more suited for the "server rendering of HTML" job
NEXTJs
- ReactJS
- SSR
- Automatic code splitting
- Zero setup
Installation
~ yarn create next-app my-app
~ cd my-app
~ yarn next
Yarn > 0.25
~ npm install -g create-next-app
~ create-next-app my-app
~ cd my-app
~ npm run dev
NPM
pages/index.js
pages/rennes.js
export default () => <h1>What a new Page !</h1>;
A partir d'une url, seul le code nécessaire est chargé
Configuration des Metas
import Head from 'next/head';
export default ({ title = 'This is the default title' }) => (
<Head>
<title>{title}</title>
<meta charSet="utf-8" />
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
);
Query Param
export default ({ url: { query: { id } } }) => (
<div>
<h1>Mon identifiant est : {id}</h1>
</div>
);
/sub/path
Path Param
{
"name": "create-next-example-app",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "^2.3.1",
"react": "^15.5.4",
"react-dom": "^15.5.4"
}
}
{
"name": "create-next-example-app",
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "NODE_ENV=production node server.js"
},
"dependencies": {
"next": "^2.3.1",
"react": "^15.5.4",
"react-dom": "^15.5.4"
}
}
Un peu de prog côté serveur
package.json
Path Param
const { createServer } = require('http');
const { parse } = require('url');
const next = require('next');
const pathMatch = require('path-match');
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
const route = pathMatch();
const match = route('/rennesjs/:param');
app.prepare().then(() => {
createServer((req, res) => {
const { pathname, query } = parse(req.url, true);
const params = match(pathname);
if (params === false) {
handle(req, res);
return;
}
app.render(req, res, '/sub/path', Object.assign(params, query));
}).listen(3000, err => {
if (err) throw err;
console.log('> Ready on http://localhost:3000');
});
});
server.js
Path Param
export default ({ url: { query: { id, param } } }) => (
<div>
<h1>Mon identifiant est : {id} et mon chemin est : {param}</h1>
</div>
);
/pages/sub/path.js
Links
import Link from 'next/link';
export default ({ url: { query: { id, param } } }) => (
<div>
<h1>Mon identifiant est : {id} et mon chemin est : {param}</h1>
<Link href="/rennesjs">
<a title="Retour à Rennes Js">
plip
</a>
</Link>
</div>
);
/pages/sub/path.js
Prefetch
<Link prefetch href="/rennesjs">
<a title="Retour à Rennes Js">
plip
</a>
</Link>
import Router from 'next/router'
...
{
Router.prefetch('/rennesjs')
}
Layouts
import Link from 'next/link';
import Head from 'next/head';
export default ({ children, title = 'This is the default title' }) => (
<div>
<Head>
<title>{title}</title>
<meta charSet="utf-8" />
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
<header>
<nav>
<Link href="/"><a>Home</a></Link> |
<Link href="/rennesjs"><a>Rennes JS</a></Link> |
</nav>
</header>
{children}
<footer>
Je suis en bas mais je suis bien
</footer>
</div>
);
/components/layout.js
Layouts
import Link from 'next/link';
import Layout from '../../components/layout';
export default ({ url: { query: { id, param } } }) => (
<Layout>
<h1>Mon identifiant est : {id} et mon chemin est : {param}</h1>
<Link href="/rennesjs">
<a title="Retour à Rennes Js">
plip
</a>
</Link>
</Layout>
);
/pages/sub/path.js
Data
import Link from 'next/link';
import Layout from '../../components/layout';
import axios from 'axios';
const path = ({ url: { query: { id, param } }, avatar }) => (
<Layout>
<h1>Mon identifiant est : {id} et mon chemin est : {param}</h1>
<Link href="/rennesjs">
<a title="Retour à Rennes Js">
plip
</a>
</Link>
<br />
<img src={avatar} width="60px" />
</Layout>
);
path.getInitialProps = async ({ req }) => {
const res = await axios.get('https://api.github.com/users/mlecoq/events');
return { avatar: res.data[0].actor.avatar_url };
};
export default path;
/pages/sub/path.js
Data
/pages/sub/path.js
State (Redux)
~ yarn add redux
~ yarn add react-redux
~ yarn add next-redux-wrapper
State (Redux)
import { createStore } from 'redux';
export const actionTypes = {
ADD: 'ADD',
REMOVE: 'REMOVE',
};
const reducer = (state = { count: 0 }, action) => {
switch (action.type) {
case actionTypes.ADD:
return { ...state, count: state.count + 1 };
case actionTypes.REMOVE:
return { ...state, count: state.count - 1 };
default:
return state;
}
};
export const makeStore = initialState => {
return createStore(reducer, initialState);
};
export function addCount() {
return { type: actionTypes.ADD };
}
export function removeCount() {
return { type: actionTypes.REMOVE };
}
Store
State (Redux)
import withRedux from 'next-redux-wrapper';
import Link from 'next/link';
import { bindActionCreators } from 'redux';
import { makeStore, addCount } from '../store/store';
const AddCounter = ({ add, count }) => (
<div>
<h1>Compteur: <span>{count}</span></h1>
<button onClick={add}>Plus !</button>
<br />
<Link href="/remove">Moins</Link>
</div>
);
const mapStateToProps = ({ count }) => ({ count });
const mapDispatchToProps = dispatch => ({
add: bindActionCreators(addCount, dispatch),
});
export default withRedux(makeStore, mapStateToProps, mapDispatchToProps)(
AddCounter,
);
pages/add.js
State (Redux)
import withRedux from 'next-redux-wrapper';
import Link from 'next/link';
import { bindActionCreators } from 'redux';
import { makeStore, addCount } from '../store/store';
const AddCounter = ({ add, count }) => (
<div>
<h1>Compteur: <span>{count}</span></h1>
<button onClick={add}>Plus !</button>
<br />
<Link href="/remove">Moins</Link>
</div>
);
AddCounter.getInitialProps = ({ store }) => {
store.dispatch(addCount());
};
const mapStateToProps = ({ count }) => ({ count });
const mapDispatchToProps = dispatch => ({
add: bindActionCreators(addCount, dispatch),
});
export default withRedux(makeStore, mapStateToProps, mapDispatchToProps)(
AddCounter,
);
pages/add.js
Et si je préfère Vue
Deploiement avec now
Site Statique
Next 3.0
- Dynamic Import & Export
- Export en site statique
import dynamic from 'next/dynamic'
const DynamicComponent1 = dynamic(import('../components/hello1'))
const DynamicComponent2 = dynamic(import('../components/hello2'))
export default () => (
<div>
<Header />
<DynamicComponent />
<p>HOME PAGE is here!</p>
</div>
)
Autres projets oss de Zeit
Micro Service
Hyper
Pkg
pkg . --targets node6-macos-x64
Arunoda Susiripala, de MeteorJS à Next.js
By Mickael Lecoq
Arunoda Susiripala, de MeteorJS à Next.js
- 2,480