Universal rendering
This commit is contained in:
parent
f170fa609e
commit
e6b71ebc34
File diff suppressed because one or more lines are too long
11
package.json
11
package.json
|
@ -4,9 +4,11 @@
|
||||||
"description": "portfolio",
|
"description": "portfolio",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"start": "npm run build && babel-node src/server.js",
|
||||||
"production": "webpack -p",
|
"start:dev": "export NODE_ENV=development && npm run build:dev && nodemon --exec babel-node -- src/server.js",
|
||||||
"start": "webpack-dev-server"
|
"build": "NODE_ENV=production webpack -p",
|
||||||
|
"build:dev": "webpack -d",
|
||||||
|
"build:dev:watch": "webpack -d --watch"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"porfolio",
|
"porfolio",
|
||||||
|
@ -15,6 +17,8 @@
|
||||||
"author": "Matúš Námešný",
|
"author": "Matúš Námešný",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"ejs": "^2.5.6",
|
||||||
|
"express": "^4.15.2",
|
||||||
"react": "^15.4.2",
|
"react": "^15.4.2",
|
||||||
"react-dom": "^15.4.2",
|
"react-dom": "^15.4.2",
|
||||||
"react-router-dom": "^4.0.0"
|
"react-router-dom": "^4.0.0"
|
||||||
|
@ -29,6 +33,7 @@
|
||||||
"html-webpack-plugin": "^2.28.0",
|
"html-webpack-plugin": "^2.28.0",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"node-sass": "^4.5.2",
|
"node-sass": "^4.5.2",
|
||||||
|
"nodemon": "^1.11.0",
|
||||||
"sass-loader": "^6.0.3",
|
"sass-loader": "^6.0.3",
|
||||||
"style-loader": "^0.16.1",
|
"style-loader": "^0.16.1",
|
||||||
"url-loader": "^0.5.8",
|
"url-loader": "^0.5.8",
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {render} from 'react-dom';
|
||||||
|
import {BrowserRouter as Router} from 'react-router-dom';
|
||||||
|
import {App} from './components';
|
||||||
|
import './static/stylesheets/main.scss';
|
||||||
|
|
||||||
|
const AppClient = () => (
|
||||||
|
<Router>
|
||||||
|
<App />
|
||||||
|
</Router>
|
||||||
|
)
|
||||||
|
|
||||||
|
window.onload = () => {
|
||||||
|
render(
|
||||||
|
<AppClient />,
|
||||||
|
document.getElementById('app')
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,15 +1,9 @@
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
export default class About extends Component{
|
export const About = () => (
|
||||||
constructor() {
|
<div className="content">
|
||||||
super();
|
<h1>About</h1>
|
||||||
}
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
render() {
|
export default About;
|
||||||
return (
|
|
||||||
<div className="content">
|
|
||||||
<h1>About</h1>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {Route, Switch} from 'react-router-dom';
|
||||||
|
import {Home, About, Blog, Portfolio, Resume, NotFoundPage} from '.';
|
||||||
|
|
||||||
|
|
||||||
|
export const App = () => (
|
||||||
|
<div>
|
||||||
|
<Route component={Home}/>
|
||||||
|
<Switch>
|
||||||
|
<Route exact path='/about' component={About} />
|
||||||
|
<Route exact path='/blog' component={Blog} />
|
||||||
|
<Route exact path='/portfolio' component={Portfolio} />
|
||||||
|
<Route exact path='/resume' component={Resume} />
|
||||||
|
<Route component={NotFoundPage} />
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default App;
|
|
@ -1,9 +1,6 @@
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
export default class Blog extends Component {
|
export default class Blog extends Component {
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -3,9 +3,6 @@ import config from '../config/config.json';
|
||||||
import {Link} from 'react-router-dom';
|
import {Link} from 'react-router-dom';
|
||||||
|
|
||||||
export default class Home extends Component {
|
export default class Home extends Component {
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
var socialLinks = [];
|
var socialLinks = [];
|
||||||
|
@ -33,10 +30,10 @@ export default class Home extends Component {
|
||||||
</div>
|
</div>
|
||||||
<div className="menu-links">
|
<div className="menu-links">
|
||||||
<ul>
|
<ul>
|
||||||
<li><Link to='/about'>About</Link></li>
|
<li><Link to='/about'><i className="fa fa-question" aria-hidden="true"></i> About</Link></li>
|
||||||
<li><Link to='/blog'>Blog</Link></li>
|
<li><Link to='/blog'><i className="fa fa-rss" aria-hidden="true"></i> Blog</Link></li>
|
||||||
<li><Link to='/portfolio'>Portfolio</Link></li>
|
<li><Link to='/portfolio'><i className="fa fa-briefcase" aria-hidden="true"></i> Portfolio</Link></li>
|
||||||
<li><Link to='/resume'>Resume</Link></li>
|
<li><Link to='/resume'><i className="fa fa-file-text-o" aria-hidden="true"></i> Resume</Link></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
export class NotFoundPage extends React.Component {
|
||||||
|
componentWillMount() {
|
||||||
|
const { staticContext } = this.props;
|
||||||
|
if (staticContext) {
|
||||||
|
staticContext.is404 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="content">
|
||||||
|
<h1>Uhm... WHAT?</h1>
|
||||||
|
<h2>Looks like you're lost</h2>
|
||||||
|
<p>404 Page not found</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotFoundPage;
|
|
@ -1,10 +1,6 @@
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
export default class Portfolio extends Component{
|
export default class Portfolio extends Component{
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="content">
|
<div className="content">
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
export default class Resume extends Component{
|
export default class Resume extends Component{
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="content">
|
<div className="content">
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
export { default as Home } from './Home';
|
export { default as Home } from './Home';
|
||||||
export { default as About} from './About';
|
export { default as About } from './About';
|
||||||
export { default as Blog} from './Blog';
|
export { default as Blog } from './Blog';
|
||||||
export { default as Portfolio} from './Portfolio';
|
export { default as Portfolio } from './Portfolio';
|
||||||
export { default as Resume} from './Resume';
|
export { default as Resume } from './Resume';
|
||||||
|
export { default as NotFoundPage } from './NotFoundPage';
|
||||||
|
export { default as App } from './App';
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Portfolio</title>
|
|
||||||
<!-- Google Fonts -->
|
|
||||||
<link href="https://fonts.googleapis.com/css?family=Inconsolata|Open+Sans|Roboto|Montserrat|Concert+One" rel="stylesheet">
|
|
||||||
<!-- Font Awesome -->
|
|
||||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-T8Gy5hrqNKT+hzMclPo118YTQO6cYprQmhrYwIiQ/3axmI1hQomh7Ud2hPOy8SP1" crossorigin="anonymous">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app">
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
18
src/index.js
18
src/index.js
|
@ -1,18 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import ReactDom from 'react-dom';
|
|
||||||
import {HashRouter, Route, Switch} from 'react-router-dom';
|
|
||||||
import {Home, About, Blog, Portfolio, Resume} from './components';
|
|
||||||
import './static/stylesheets/main.scss';
|
|
||||||
|
|
||||||
ReactDom.render(
|
|
||||||
<HashRouter>
|
|
||||||
<div className="container">
|
|
||||||
<Route component={Home}/>
|
|
||||||
<Route exact path='/about' component={About} />
|
|
||||||
<Route exact path='/blog' component={Blog} />
|
|
||||||
<Route exact path='/portfolio' component={Portfolio} />
|
|
||||||
<Route exact path='/resume' component={Resume} />
|
|
||||||
</div>
|
|
||||||
</HashRouter>,
|
|
||||||
document.getElementById('app')
|
|
||||||
);
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
import path from 'path';
|
||||||
|
import { Server } from 'http';
|
||||||
|
import Express from 'express';
|
||||||
|
import React from 'react';
|
||||||
|
import { renderToString } from 'react-dom/server';
|
||||||
|
import { StaticRouter as Router } from 'react-router-dom';
|
||||||
|
import { App } from './components/App';
|
||||||
|
|
||||||
|
const app = new Express();
|
||||||
|
const server = new Server(app);
|
||||||
|
|
||||||
|
// use ejs templates
|
||||||
|
app.set('view engine', 'ejs');
|
||||||
|
app.set('views', path.join(__dirname, 'views'));
|
||||||
|
|
||||||
|
// define the folder that will be used for static assets
|
||||||
|
app.use(Express.static(path.join(__dirname, 'static')));
|
||||||
|
|
||||||
|
// universal routing and rendering
|
||||||
|
app.get('*', (req, res) => {
|
||||||
|
let markup = '';
|
||||||
|
let status = 200;
|
||||||
|
|
||||||
|
const context = {};
|
||||||
|
markup = renderToString(
|
||||||
|
<Router location={req.url} context={context}>
|
||||||
|
<App />
|
||||||
|
</Router>,
|
||||||
|
);
|
||||||
|
|
||||||
|
// context.url will contain the URL to redirect to if a <Redirect> was used
|
||||||
|
if (context.url) {
|
||||||
|
return res.redirect(302, context.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.is404) {
|
||||||
|
status = 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return res.status(status).render('index', { markup });
|
||||||
|
});
|
||||||
|
|
||||||
|
// start the server
|
||||||
|
const port = process.env.PORT || 3000;
|
||||||
|
const env = process.env.NODE_ENV || 'production';
|
||||||
|
server.listen(port, (err) => {
|
||||||
|
if (err) {
|
||||||
|
return console.error(err);
|
||||||
|
}
|
||||||
|
return console.info(
|
||||||
|
`
|
||||||
|
Server running on http://localhost:${port} [${env}]
|
||||||
|
`);
|
||||||
|
});
|
File diff suppressed because one or more lines are too long
|
@ -69,6 +69,11 @@ body {
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
margin-left: 320px;
|
margin-left: 320px;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 20px;
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
@media screen and (max-width: $break-medium) {
|
@media screen and (max-width: $break-medium) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
float: none;
|
float: none;
|
||||||
|
@ -92,18 +97,12 @@ body {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
li {
|
li {
|
||||||
display: inline-block;
|
margin: 5px;
|
||||||
}
|
a {
|
||||||
li a {
|
|
||||||
color: $white;
|
color: $white;
|
||||||
display: block;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-size: 1.4em;
|
font-size: 1.4em;
|
||||||
border: 1px solid white;
|
}
|
||||||
border-radius: 20px;
|
|
||||||
padding: 2px;
|
|
||||||
margin: 2px;
|
|
||||||
width: initial;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,9 @@
|
||||||
<link href="https://fonts.googleapis.com/css?family=Inconsolata|Open+Sans|Roboto|Montserrat|Concert+One" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Inconsolata|Open+Sans|Roboto|Montserrat|Concert+One" rel="stylesheet">
|
||||||
<!-- Font Awesome -->
|
<!-- Font Awesome -->
|
||||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-T8Gy5hrqNKT+hzMclPo118YTQO6cYprQmhrYwIiQ/3axmI1hQomh7Ud2hPOy8SP1" crossorigin="anonymous">
|
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-T8Gy5hrqNKT+hzMclPo118YTQO6cYprQmhrYwIiQ/3axmI1hQomh7Ud2hPOy8SP1" crossorigin="anonymous">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app">
|
<div id="app"><%- markup -%></div>
|
||||||
</div>
|
<script src="/js/bundle.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Portfolio</title>
|
||||||
|
<!-- Bootstrap -->
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
|
||||||
|
<!-- Google Fonts -->
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Inconsolata|Open+Sans|Roboto|Montserrat|Concert+One" rel="stylesheet">
|
||||||
|
<!-- Font Awesome -->
|
||||||
|
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-T8Gy5hrqNKT+hzMclPo118YTQO6cYprQmhrYwIiQ/3axmI1hQomh7Ud2hPOy8SP1" crossorigin="anonymous">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
<div data-reactroot="" data-reactid="1" data-react-checksum="-1250583058"><div id="cover-page" class="cover-page-collapsed" data-reactid="2"><div id="cover-page-content" data-reactid="3"><div data-reactid="4"><h1 id="cover-page-name" data-reactid="5"><a href="/" data-reactid="6">Matúš Námešný</a></h1></div><div class="social" data-reactid="7"><a href="https://github.com/LordMathis" data-reactid="8"><i class="fa fa-github fa-3x" data-reactid="9"></i></a><a href="https://twitter.com/matus_n" data-reactid="10"><i class="fa fa-twitter fa-3x" data-reactid="11"></i></a><a href="mailto:matus@namesny.com" data-reactid="12"><i class="fa fa-envelope-o fa-3x" aria-hidden="true" data-reactid="13"></i></a></div><div class="menu-links" data-reactid="14"><ul data-reactid="15"><li data-reactid="16"><a href="/about" data-reactid="17">About</a></li><li data-reactid="18"><a href="/blog" data-reactid="19">Blog</a></li><li data-reactid="20"><a href="/portfolio" data-reactid="21">Portfolio</a></li><li data-reactid="22"><a href="/resume" data-reactid="23">Resume</a></li></ul></div></div></div><div class="content" data-reactid="24"><h1 data-reactid="25">404</h1><h2 data-reactid="26">Page not found!</h2><p data-reactid="27"><a href="/" data-reactid="28">Go back to the main page</a></p></div></div> </div>
|
||||||
|
<script src="/js/bundle.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,17 +1,13 @@
|
||||||
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
const path = require('path');
|
||||||
var HtmlWebpackPluginConfig = new HtmlWebpackPlugin({
|
|
||||||
template: __dirname + '/src/index.html',
|
|
||||||
filename: 'index.html',
|
|
||||||
inject: 'body'
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: [
|
entry: [
|
||||||
'./src/index.js'
|
'./src/app-client.js'
|
||||||
],
|
],
|
||||||
output: {
|
output: {
|
||||||
path: __dirname + '/dist',
|
path: path.join(__dirname, 'src', 'static', 'js'),
|
||||||
filename: "index_bundle.js"
|
publicPath: "/js/",
|
||||||
|
filename: 'bundle.js'
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
loaders: [
|
loaders: [
|
||||||
|
@ -29,5 +25,5 @@ module.exports = {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
plugins: [HtmlWebpackPluginConfig]
|
plugins: []
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue