React服务端渲染之路01——项目基础架构搭建

全部源代码、文档和图片都在 github 的仓库里,点击进入仓库javascript

相关阅读

1. 客户端渲染与服务端渲染

1.1 客户端渲染

  • 客户端渲染,实际上就是客户端向服务端请求页面,服务端返回的是一个很是简单的 HTML 页面,在这个页面里,只有不多的一些 HTML 标签
  • 客户端渲染时,页面中有两个比较重要的点,第一个是 script 标签,可能会有好几个 script 标签,这个标签是打包后的 js 代码,用来生成 DOM 元素,发送请求,事件绑定等。可是,生成的 DOM 元素须要有一个在页面上展现的容器,因此另一个点就是容器,通常是一个 id 名为 root 或 app 的 div 标签,相似于这样 <div id="root"></div><div id="app"></div>
  • 客户端渲染的特色前端

    • 客户端加载全部的 js 资源,渲染 DOM 元素
    • 在浏览器页面上的全部资源,都由客户端主动去获取,服务端只负责静态资源的提供和 API 接口,再也不负责页面的渲染,若是采用 CDN 的话,服务端仅仅须要提供 API 接口
    • 优势: 先后端分离,前端专一页面的开发,后端专一接口的开发
    • 缺点: 首屏加载资源多,首屏加载时响应慢。页面上没有 DOM 元素,不利于 SEO 优化

1.2 服务端渲染

  • 服务端渲染,就是客户端页面上的 HTML 元素,都要由服务端负责渲染。服务端利用模板引擎,把数据填充到模板中,生成 HTML 字符串,最终把 HTML 字符串返回到浏览器,浏览器接收到 HTML 字符串,经过 HTML 引擎解析,最终生成 DOM 元素,显示在页面上
  • 好比 Node.js 能够渲染的模板引擎有 ejs,nunjucks,pug 等。Java 最多见的是 JSP 模板引擎。
  • 服务端渲染的特色java

    • 优势: 页面资源大多由服务端负责处理,因此页面加载速度快,缩短首屏加载时间。有利于 SEO 优化。无需占用客户端资源
    • 缺点: 不利于先后端分离,开发效率低。占用服务器资源

1.3 区分与选择

  • 客户端渲染和服务端渲染本质的区别就是,是谁负责 HTML 页面的拼接,那么就是谁渲染的页面
  • 若是对首屏加载时间有很是高的需求,或者是须要 SEO 优化,那么就选择服务端渲染
  • 若是对首屏加载时间没有要求,也不须要作 SEO 优化,相似于作后台管理系列的业务,那么就能够选择客户端渲染
  • 具体选择客户端渲染仍是服务端渲染,没有强制的要求,具体要根据项目的需求来区分选择

2. 项目基础架构搭建

  • 咱们如今搭建的项目架构,是一个总体的项目架构,如今建立的一些目录,可能暂时会用不上,可是为了了解每个目录每个模块具体都是作什么的,咱们提早先知道,方便之后使用
  • 咱们把项目叫作 react-ssr-webpack

2.1 建立项目

  • 新建一个 react-ssr-webpack 的文件夹,这个文件夹存放咱们全部的源代码,也就是根目录。
  • 使用 npm init 初始化项目
  • 下载 webpack 的依赖包 npm i webpack webpack-cli -Dnode

    • webpack 和 webpack-cli 是 wepack 打包的主要模块
  • 下载 Babel 的依赖包 npm i @babel/core @babel/preset-env @babel/preset-react babel-loader @babel/plugin-proposal-class-properties -Dreact

    • 咱们采用的是 babel@7 版本,再也不采用 babel@6 版本,由于 babel@7 使用起来更加的方便,简单
    • @babel/core,Babel 编译的核心库
    • @babel/preset-env,Babel 官方预置的一个库,一系列插件的集合
    • @babel/preset-react,主要是用来编译 jsx 语法
    • babel-loader,webpack 处理 js 文件所须要的加载器
    • @babel/plugin-proposal-class-properties,编译 class 的一些新的特性
  • 下载 React 的依赖包 npm i react react-dom react-router-dom -S
  • 下载 express,npm i express -S,咱们采用 express 作后端服务,也能够采用 koa,hapi,egg 等
  • 根目录的文件目录结构
├── node_modules/   第三方依赖包
|── build/  服务端打包后生成的代码
|   └── server.js 
├── public/      客户端打包后生成的代码
│   └── client.js
├── src
│   ├── client/        客户端源代码
│   ├── components/        React 组件
│   ├── containers/        React 容器组件
│   ├── server/        服务端源代码
│   ├── store/        redux
|      └── routes.js     路由
├── .babelrc        babel 编译
├── .gitignore        git 忽略文件
├── package.json
├── webpack.base.js        webpack 基础配置
├── webpack.client.js        webpack 客户端配置
└── webpack.server.js        webpack 服务端配置

2.2 配置服务端的 wbpack.server.js

  • 配置服务端的 webpack,是由于咱们要在服务端使用 jsx 语法,须要借助 babel 编译
  • 既然服务端使用了 babel ,那么服务端也可使用新的 ES6/7/8 语法
  • 还有一点须要注意的是,在 webpack.server.js 进行编译的时候,仅仅是将 jsx 和 ES6/7/8 高级语法转为 ES5 语法,生成一个 build/server.js 文件。因此咱们还须要用 build/server.js 建立一个服务,这个服务就是咱们最终访问的服务
  • webpack.server.js
// webpack.server.js
const path = require('path');
// webpack-node-externals 模块是为了避免打包 node 的模块,好比 path, fs 等。由于咱们的 Node 已经内置了这些模块,因此没有必要打包
const WebpackNodeExternals = require('webpack-node-externals');

module.exports = {
  target: 'node',
  // 服务端的入口文件,是 src/server/index.js
  mode: 'development',
  entry: './src/server/index.js',
  output: {
    // 打包后生成的文件的路径与文件名
    filename: 'server.js',
    path: path.resolve(__dirname, 'build/')
  },
  externals: [nodeExternals()],
  module: {
    rules: [
      {
        test: /\.js?$/,
        // 能够在这里经过 option 配置 Babel,也可使用 .babelrc 文件配置 Babel
        loader: 'babel-loader',
        exclude: /node_modules/
      }
    ]
  }
};

2.3 配置 package.json

  • 前边咱们说了,咱们一共须要作两件事,一件事是使用 webpack 编译服务端的代码,另外一件事是把服务端编译后生成的代码做为一个服务启动
  • 因此咱们先下载两个全局的包,npm-run-all 和 nodemonwebpack

    • npm i npm-run-all -g,这个工具能够在一个 Terminal 里同时启动多个服务,便于咱们开发
    • npm i nodemon -g,热重启 Node 服务,nodemon 的使用方法和 Node 是同样的,nodemon 监听到 build/server.js 文件的变更,就会自动重启服务
  • 咱们在 scripts 里添加两条命令
  • 命令1 dev:build:server ,这个命令是调用 webpack.server.js 进行打包git

    • "dev:build:server": "webpack --config webpack.server.js --watch"
    • --config 参数的意思是指定 webpack.server.js 为配置文件,若是没有 --config 参数,那么 webpack 会默认调用 webpack.config.js 配置文件,若是没有指定配置文件,也没有 webpack.config.js 文件,那么会报错
    • --watch 参数的意思是开启监听,每次修改代码,都会自动打包
  • 命令2 dev:start ,这个命令是使用打包后代码开启服务github

    • "dev:start": "nodemon build/server.js"
    • 每次 dev:build:server 打包后,都会生成新的 build/server.js 文件
    • nodemon 监测到文件的改动,就会从新开启服务
  • 咱们使用 npm-run-all 再添加一条命令,用来启动这两个服务web

    • "dev": "npm-run-all --parallel dev:**"
    • dev 命令是使用 npm-run-all 工具开启多个服务,--parallel 参数的意思是并行开启服务,dev:** 的意思是 npm-run-all 会启动 scripts 里全部的以 dev: 开头的命令。 dev 只是一个代称,也可使用其余的字符
  • 建议使用 npm-run-all 和 nodemon,这样咱们能够在一个 Terminal 里进行全部的操做,也能够开启多个 Terminal,每一个 Terminal 开启不一样的服务
{
    "dev": "npm-run-all --parallel dev:**",
    "dev:build:server": "webpack --config webpack.server.js --watch",
    "dev:start": "nodemon build/server.js"
}
  • 预览 package.json
{
  "name": "react-ssr-docs",
  "version": "1.0.0",
  "description": "彻底解读 react 服务端渲染",
  "main": "index.js",
  "scripts": {
    "dev": "npm-run-all --parallel dev:**",
    "dev:build:server": "webpack --config webpack.server.js --watch",
    "dev:start": "nodemon build/server.js"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/dawnight/react-ssr-docs.git"
  },
  "keywords": [
    "react",
    "redux",
    "react-ssr"
  ],
  "author": "dawnight",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/dawnight/react-ssr-docs/issues"
  },
  "homepage": "https://github.com/dawnight/react-ssr-docs#readme",
  "devDependencies": {
    "@babel/core": "^7.4.3",
    "@babel/plugin-proposal-class-properties": "^7.4.0",
    "@babel/preset-env": "^7.4.3",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.5",
    "webpack": "^4.30.0",
    "webpack-cli": "^3.3.1",
    "webpack-node-externals": "^1.7.2"
  },
  "dependencies": {
    "express": "^4.16.4",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-redux": "^7.0.2",
    "react-router-dom": "^5.0.0",
    "redux": "^4.0.1",
    "redux-logger": "^3.0.6",
    "redux-thunk": "^2.3.0"
  }
}

2.4 配置 .babelrc

  • @babel/core, @babel/preset-env 和 @babel/preset-react 库是必需要有的
  • @babel/plugin-proposal-class-properties 库并非必须的,由于在后边咱们使用了 class 的新语法特性,因此须要使用这个库,若是不安装这个库,也可使用传统的 class 的属性方法语法,效果是同样的。
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties"
  ]
}

相关阅读

相关文章
相关标签/搜索