当咱们考虑一些单页应用的时候(SPAs),通常考虑浏览器,JavaScript和速度,对搜索引擎是不可见的。因为单页应用使用JavaScript来渲染页面中的内容,同时web网络爬虫不经过浏览器来查看整个网页,这样就不能看到和索引页面中全部的内容。或者,更好的说,其中大部分是不能。一些开发人员试图以各类方式来解决这个问题。css
在服务器端使用Node.js在客户端使用React,咱们能够构建通用的JavaScript应用程序。这能够从服务器端和浏览器端的渲染中提供许多好处,容许搜索引擎和人类使用浏览器来查看单页应用中的内容。html
在这个教程中分为两个部分,我将向您展现如何经过服务器端渲染大搭建一个普通的React博客应用系统来使应用对搜素引擎可见。而后,它可以使该应用在浏览器中快速和响应式。前端
该博客系统将使用到如下一些技术和工具:html5
Node.js用于包管理和服务端渲染node
React用于视图层react
Express做为一个简单的后端JS服务端框架jquery
React Router用于路由webpack
React Hot Loader 用于开发中的热加载git
Flux 用于数据流github
Cosmic JS用于内容管理
开始以前,首先在命令行中运行一下内容:
mkdir react-universal-blog cd react-universal-blog
新建一个package.json
文件,在里面添加一下内容:
{ "name": "react-universal-blog", "version": "1.0.0", "description": "", "main": "app-server.js", "dependencies": { "babel": "^5.8.29", "babel-core": "^5.8.32", "babel-loader": "^5.3.2", "cosmicjs": "^2.0.0", "events": "^1.1.0", "express": "^4.13.3", "flux": "^2.1.1", "history": "^1.14.0", "hogan-express": "^0.5.2", "lodash": "^3.10.1", "react": "^0.14.1", "react-dom": "^0.14.1", "react-router": "^1.0.1", "webpack": "^1.12.2" }, "scripts": { "development": "cp views/index.html public/index.html && NODE_ENV=development webpack && webpack-dev-server --content-base public/ --hot --inline --devtool inline-source-map --history-api-fallback" }, "author": "", "license": "ISC", "devDependencies": { "react-hot-loader": "^1.3.0", "webpack-dev-server": "^1.12.1" } }
在这个文件中你应该已经注意到了咱们添加了一下内容:
Babel 用于打包符合CommonJS
模块规范,同时将ES6和React JSX的语法格式转换为浏览器兼容的JavaScript。
Cosmic JS官方的Node.js客户端可以经过Cosmic JS云端内容接口服务于咱们的博客内容系统。
Flux用于应用的数据管理(这在React 应用程序中很是重要)。
React 用于服务器和浏览器的视图管理
Webpack将全部文件打包成一个bundle.js文件。
同时咱们在package.json
文件中加入了一个脚本文件,当咱们运行npm run development
时,脚本将从咱们的views
文件夹复制index.html
文件到public
文件夹。
配置webpack文件,webpack.config.js
:
// webpack.config.js if(process.env.NODE_ENV === 'development'){ var loaders = ['react-hot','babel'] } else { var loaders = ['babel'] } module.exports = { devtool: 'eval', entry: './app-client.js', output: { path: __dirname + '/public/dist', filename: 'bundle.js', publicPath: '/dist/' }, module: { loaders: [{ test: /\.js$/, loaders: loaders, exclude: /node_modules/ }] } };
注意到咱们这里添加了一个entery属性,属性的值为app-client.js。这个文件将做为咱们应用的入口点,意味着webpack将从这个点开始打包咱们的应用,而且将其输出到路径/public/dist/bundle.js
。同时使用加载器使Babel
在包含ES6
和JSX
的代码中运行。
在讲解与React相关的一些技术内容以前,先来看一些咱们完成整个博客将要展示的样子。因为次教程中咱们这里但愿你可以将更多的精力放在搭建应用的功能性上而不是博客的样式上面,这里咱们选择使用已经建好的前端样式主题,选择Start Bootsrtap样式里面的Clean Blog
新建一个文件夹,命名为views
,在文件夹内新建文件index.html
在html文件中添加一下代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content=""> <title>{{ site.title }}{{# page }} | {{ page.title }}{{/ page }}</title> <!-- Bootstrap Core CSS --> <link href="/css/bootstrap.min.css" rel="stylesheet"> <!-- Custom CSS --> <link href="/css/clean-blog.min.css" rel="stylesheet"> <link href="/css/cosmic-custom.css" rel="stylesheet"> <!-- Custom Fonts --> <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet" type="text/css"> <link href="//fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic" rel="stylesheet" type="text/css"> <link href="//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css"> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script> <![endif]--> </head> <body class="hidden"> <div id="app">{{{ reactMarkup }}}</div> <script src="/js/jquery.min.js"></script> <script src="/js/bootstrap.min.js"></script> <script src="/js/clean-blog.min.js"></script> <script src="/dist/bundle.js"></script> </body> </html>
全部的公共JS和CSS文件下载GitHub repository。点击这里下载
一般为了防止使用jQuery,通常我会选择经典的React Bootstrap。然而为了简洁,在主题框架中使用了部分jQuery的功能。
在index.html
文件中,咱们在id="app"
的div
结点上搭建本身的React
点。
在这个点上,你的应用程序拥有如下结构:
package.json public |-css |-bootstrap.min.css |-cosmic-custom.css |-js |-jquery.min.js |-bootstrap.min.js |-clean-blog.min.js views |-index.html webpack.config.js
搭建好静态目录以后,开始创建React的组件。
开始搭建博客的应用界面,博客包括如下页面:
Home
About
Work
Contact
开始创建新的文件,命名app-clietn.js
,具体内容以下:
// app-client.js import React from 'react' import {render} from 'react-dom' import {Route} from 'react-router' import createBrowserHistory from 'history/lib/createBrowserHistory' const history = createBrowerHistory() // Routes import routes from './routes' const Routes = ( <Router history={history}> {routes} </Router> ) const app = document.getElementById('app') render(Routes, app)
若是你想进一步了解有关React Router
的工做原理,能够访问Github 地址。app-client.js
文件中Router
组件使得浏览器客户端路由,服务器端渲染不须要浏览器历史记录,因此这里咱们须要创建一个分开的routes.js
文件用来共享服务端和客户端的入口点。
添加routes.js
文件:
// routes.js import React, {Component} from 'react' import { Route, IndexRoute, Link} from 'react-router' //Main component class App extends Component{ componentDidMount(){ document.body.className='' } render(){ return ( <div> <h1>React Universal Blog</h1> <nav> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/about">About</Link></li> <li><Link to="/work">Work</Link></li> <li><Link to="/contact">Contact</Link></li> </ul> </nav> { this.props.children } </div> ) } } //Pages class Home extends Component{ render(){ return ( <div> <h2>Home</h2> <div>Some home page content</div> </div> ) } } class About extends Component { render(){ return ( <div> <h2>About</h2> <div>Some about page content</div> </div> ) } } class Work extends Component { render(){ return ( <div> <h2>Work</h2> <div>Some work page content</div> </div> ) } } class Contact extends Component { render(){ return ( <div> <h2>Contact</h2> <div>Some contact page content</div> </div> ) } } class NoMatch extends Component { render(){ return ( <div> <h2>NoMatch</h2> <div>404 error</div> </div> ) } } export default ( <Route path="/" component={App}> <IndexRoute component={Home}/> <Route path="about" component={About}/> <Route path="work" component={Work}/> <Route path="contact" component={Contact}/> <Route path="*" component={NoMatch}/> </Route> )
到目前为止,咱们搭建好了一个基本的包含不一样页面的博客应用例子。如今,让咱们来具体运行一下应用,在终端中运行一下内容:
mkdir public npm install npm run development
在浏览器中输入网址http://localhost:8080来查看博客运行的基本效果。
上述步骤完成以后,如今运行至服务器端,新建文件app-server.js
并添加如下内容:
// app-server.js import React from 'react' import { match, RoutingContext } from 'react-router' import ReactDOMServer from 'react-dom/server' import express from 'express' import hogan from 'hogan-express' // Routes import routes from './routes' // Express const app = express() app.engine('html', hogan) app.set('views', __dirname + '/views') app.use('/', express.static(__dirname + '/public/')) app.set('port', (process.env.PORT || 3000)) app.get('*',(req, res) => { match({ routes, location: req.url }, (error, redirectLocation, renderProps) => { const reactMarkup = ReactDOMServer.renderToStaticMarkup(<RoutingContext {...renderProps}/>) res.locals.reactMarkup = reactMarkup if (error) { res.status(500).send(error.message) } else if (redirectLocation) { res.redirect(302, redirectLocation.pathname + redirectLocation.search) } else if (renderProps) { // Success! res.status(200).render('index.html') } else { res.status(404).render('index.html') } }) }) app.listen(app.get('port')) console.info('==> Server is listening in ' + process.env.NODE_ENV + ' mode') console.info('==> Go to http://localhost:%s', app.get('port'))
在app-server.js
文件中,咱们载入了基本的路由文件route.js
.这些都是呈现标记转换成一个字符串,而后将它做为变量传递给咱们的模板。
在接下来的步骤中,咱们将创建文件app.js
这可使咱们在Node.js
中使用ES6是语法格式。文件包含如下内容:
//app.js require('babel/register') require('./app-server.js')
咱们将从该文件启动咱们的服务, 不过首先,让咱们先建立一个脚本。
打开package.json
文件,编辑里面的脚本部分文件以下:
// ... "scripts": { "development": "cp views/index.html public/index.html && NODE_ENV=development webpack && webpack-dev-server --content-base public/ --hot --inline --devtool inline-source-map --history-api-fallback", "production": "rm -rf public/index.html && NODE_ENV=production webpack -p && NODE_ENV=production node app.js", "start": "npm run production" } // ...
到目前为止,已经部署好了生产环境的脚本代码,咱们能够同时在服务器d端和客户端运行代码,终端中运行如下内容:
npm start
在浏览器地址栏中输入http://localhsot:3000.你就能够看到你的博客单页应用了。
在浏览器中点击查看源码。
在这部分中,咱们初步了解了使用React和Node.js一块儿搭建一个React普通博客应用。