关于React前端构建的一般过程 - 理论篇

  

概要

本文以个人阅读实践经验归纳前端架构构建过程,以Step by Step方式说明创建一个前端项目的过程。并会对每个阶段所使用的技术进行可替代分析,如Express替换Hapi或者Koa的优缺点分析。本文仅供参考。

流程

1. Package.json

首先,我们需要创建package.json文件。对设计初期已知的引用包和依赖包进行管理,使用ES6的,需要设置babel。其次编写脚本命令。一般文件形式如下:

{
  "name": "practice",
  "description": "Ryan Project",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "watch": "nodemon server.js"
  },
  "babel": {
    "presets": [
      "es2015",
      "react"
    ]
  },
  "dependencies": {
    "alt": "^0.17.8",
    "async": "^1.5.0",
    "body-parser": "^1.14.1",
    "colors": "^1.1.2",
    "compression": "^1.6.0",
    "express": "^4.13.3",
    "history": "^1.13.0",
    "mongoose": "^4.2.5",
    "morgan": "^1.6.1",
    "react": "latest",
    "react-dom": "latest",
    "react-highcharts": "^10.0.0",
    "react-router": "^1.0.0",
    "request": "^2.65.0",
    "serve-favicon": "^2.3.0",
    "socket.io": "^1.3.7",
    "swig": "^1.4.2",
    "underscore": "^1.8.3",
    "xml2js": "^0.4.15"
  },
  "devDependencies": {
    "babel-core": "^6.1.19",
    "babel-preset-es2015": "^6.1.18",
    "babel-preset-react": "^6.1.18",
    "babel-register": "^6.3.13",
    "babelify": "^7.2.0",
    "bower": "^1.6.5",
    "browserify": "^12.0.1",
    "gulp": "^3.9.0",
    "gulp-autoprefixer": "^3.1.0",
    "gulp-concat": "^2.6.0",
    "gulp-cssmin": "^0.1.7",
    "gulp-if": "^2.0.0",
    "gulp-less": "^3.0.3",
    "gulp-plumber": "^1.0.1",
    "gulp-sourcemaps": "^1.6.0",
    "gulp-uglify": "^1.4.2",
    "gulp-util": "^3.0.7",
    "optimize-js": "^1.0.0",
    "vinyl-buffer": "^1.0.0",
    "vinyl-source-stream": "^1.1.0",
    "watchify": "^3.6.0"
  },
  "license": "MIT"
}

输入完成后,运行npm install,将package.json中的包安装到项目目录中,存放于对应node_modules文件夹

2. Server.js

即服务端,可以使用Express、Koa、Hapi等方式去创建服务端,设置服务端口。也可以设置socket相关的工作。

Express创建服务端

var express = require(‘express‘); var app = express();

//创建路由app.get(‘/‘, function(req, res) {   res.send(‘Hello world‘); }); //创建REST APIvar router = express.Router(); router.route(‘/items/:id‘) .get(function(req, res, next) {   res.send(‘Get id: ‘ + req.params.id); }).put(function(req, res, next) {   res.send(‘Put id: ‘ + req.params.id); }).delete(function(req, res, next) {     res.send(‘Delete id: ‘ + req.params.id); });
app.use(‘/api‘, router);var server = app.listen(3000, function() {   console.log(‘Express is listening to http://localhost:3000‘); });

Koa创建服务端

var koa = require(‘koa‘);
var app = koa();
//创建路由app.use(function *() {   this.body = ‘Hello world‘; });//创建REST APIapp.use(route.get(‘/api/items‘, function*() { this.body = ‘Get‘; }));app.use(route.post(‘/api/items‘, function*() { this.body = ‘Post‘; }));app.use(route.put(‘/api/items/:id‘, function*(id) { this.body = ‘Put id: ‘ + id; })); app.use(route.delete(‘/api/items/:id‘, function*(id) { this.body = ‘Delete id: ‘ + id; }));
var server = app.listen(3000, function() {
    console.log(‘Koa is listening to http://localhost:3000‘);
});

Hapi创建服务端

var Hapi = require(‘hapi‘);
var server = new Hapi.Server(3000);
server.route({   method: ‘GET‘,   path: ‘/‘,   handler: function(request, reply) {     reply(‘Hello world‘); } });server.route([   { method: ‘GET‘, path: ‘/api/items‘, handler: function(request, reply) { reply(‘Get item id‘); } },   { method: ‘GET‘, path: ‘/api/items/{id}‘, handler: function(request, reply) { reply(‘Get item id: ‘ + request.params.id); } },   { method: ‘POST‘, path: ‘/api/items‘, handler: function(request, reply) { reply(‘Post item‘); } },   { method: ‘PUT‘, path: ‘/api/items/{id}‘, handler: function(request, reply) { reply(‘Put item id: ‘ + request.params.id); } },   { method: ‘DELETE‘, path: ‘/api/items/{id}‘, handler: function(request, reply) { reply(‘Delete item id: ‘ + request.params.id); } },   { method: ‘GET‘, path: ‘/‘, handler: function(request, reply) { reply(‘Hello world‘); } } ]);
server.start(function() {
    console.log(‘Hapi is listening to http://localhost:3000‘);
});

三者间优缺点比较

  优点 缺点
Express 庞大的社区,相对成熟。极易方便创建服务端,创建路由方面代码复用率高 基于callback机制,不可以组合使用,也不能捕获异常
Koa
相比Express,移除Route和View,中间件的使用移植和编写都比较方便,拥抱ES6,

借助Promise和generator而非callback,能够捕获异常和组合使用

以Express一样,需要routers中间件处理不同的选择
Hapi  基于配置而非代码的框架,对于大型项目的一致性和可重用性比较有用。 为大型项目定制,导致在小项目中,常见的过于形式化的代码。相关的开源资料也比较少

3. 工程化工具

首先,我们需要先设计好我们项目的目录结构,以便使用工程化工作进行压缩打包等操作。

简单举例如下项目的结构

--/public
--/css
--/js
--/fonts
--/img
--/app
    --/actions
    --/components
    --/stores
    --/stylesheets
        --main.less
    --alt.js
    --route.js
    --main.js

其次,需要webpack或者browserify工具,打包压缩一系列的脚本文件。使用babel转换ES6语法,因为绝大部分的浏览器还不支持ES6,所以需要转换为ES5。最后,创建gulpfile.js文件,使用gulp创建系列的工程指令,如绑定vendor文件、引用sourcemap、使用类似uglify、gulp-cssmin等辅助压缩文件。

如下是简易的gulpfile.js文件的配置

var gulp = require(‘gulp‘);
var gutil = require(‘gulp-util‘);
var gulpif = require(‘gulp-if‘); //conditionally run a task
var autoprefixer = require(‘gulp-autoprefixer‘); //Prefix CSS
var cssmin = require(‘gulp-cssmin‘);
var less = require(‘gulp-less‘); //Less for Gulp
var concat = require(‘gulp-concat‘);
var plumber = require(‘gulp-plumber‘); //Prevent pipe breaking caused by errors from gulp plugins
var buffer = require(‘vinyl-buffer‘); //convert streaming vinyl files to use buffers
var source = require(‘vinyl-source-stream‘); //Use conventional text streams at the start of your gulp or vinyl pipelines
var babelify = require(‘babelify‘);
var browserify = require(‘browserify‘);
var watchify = require(‘watchify‘);
var uglify = require(‘gulp-uglify‘); //Minify files with UglifyJS.
var sourcemaps = require(‘gulp-sourcemaps‘);

var production = process.env.NODE_ENV === ‘production‘

var dependencies = [
    ‘alt‘,
    ‘react‘,
    ‘react-dom‘,
    ‘react-router‘,
    ‘underscore‘
]
/*
 |--------------------------------------------------------------------------
 | Combine all JS libraries into a single file for fewer HTTP requests.
 |--------------------------------------------------------------------------
 */
gulp.task(‘vendor‘, function () {
    return gulp.src([
        ‘bower_components/jquery/dist/jquery.js‘,
        ‘bower_components/bootstrap/dist/js/bootstrap.js‘,
        ‘bower_components/magnific-popup/dist/jquery.magnific-popup.js‘,
        ‘bower_components/toastr/toastr.js‘
    ]).pipe(concat(‘vendor.js‘))
    .pipe(gulpif(production, uglify({mangle: false})))
    .pipe(gulp.dest(‘public/js‘))
})
/*
 |--------------------------------------------------------------------------
 | Compile third-party dependencies separately for faster performance.
 |--------------------------------------------------------------------------
 */
gulp.task(‘browserify-vendor‘, function(){
    return browserify()
    .require(dependencies)
    .bundle()
    .pipe(source(‘vendor.bundle.js‘))
    .pipe(buffer())
    .pipe(gulpif(production, uglify({mangle: false})))
    .pipe(gulp.dest(‘public/js‘))
})

/*
 |--------------------------------------------------------------------------
 | Compile only project files, excluding all third-party dependencies.
 |--------------------------------------------------------------------------
 */
gulp.task(‘browserify‘,[‘browserify-vendor‘], function(){
    return browserify({entries:‘app/main.js‘, debug: true})
    .external(dependencies)
    .transform(babelify, {presets: [‘es2015‘,‘react‘]})
    .bundle()
    .pipe(source(‘bundle.js‘))
    .pipe(buffer())
    .pipe(soucemaps.init({loadMaps: true}))
    .pipe(gulpif(production, uglify({mangle: false})))
    .pipe(sourcemaps.write(‘.‘))
    .pipe(gulp.dest(‘public/js‘))
})

/*
 |--------------------------------------------------------------------------
 | Same as browserify task, but will also watch for changes and re-compile.
 |--------------------------------------------------------------------------
 */
gulp.task(‘browserify-watch‘, [‘browserify-vendor‘], function(){
    var bundler = watchify(browserify({ entries:‘app/main.js‘, debug: true}), watchify.args)
    bundler.external(dependencies)
    bundler.transform(babelify, {presets: [‘es2015‘, ‘react‘]})
    bundler.on(‘update‘, rebundle)
    return rebundle()

    function rebundle() {
        var start = Date.now()
        return bundler.bundle()
        .on(‘error‘, function(err){
            gutil.log(gutil.colors.red(err.toString()))
        })
        .on(‘end‘, function() {
            gutil.log(gutil.colors.green(`Finished rebundling in ${(Date.now() - start)} ms`))
        })
        .pipe(source(‘bundle.js‘))
        .pipe(buffer())
        .pipe(sourcemaps.init({loadMaps: true}))
        .pipe(sourcemaps.write(‘.‘))
        .pipe(gulp.dest(‘public/js‘))
    }
})

gulp.task(‘styles‘, function(){
    return gulp.src(‘app/stylesheets/main.less‘)
    .pipe(plumber())
    .pipe(less())
    .pipe(autoprefixer())
    .pipe(gulpif(production, cssmin()))
    .pipe(gulp.dest(‘public/css‘))
})

gulp.task(‘watch‘, function(){
    gulp.watch(‘app/stylesheets/**/*.less‘, [‘styles‘])
})

gulp.task(‘default‘, [‘styles‘,‘vendor‘,‘browserify-watch‘,‘watch‘])
gulp.task(‘build‘, [‘styles‘, ‘vendor‘, ‘browserify‘])

Gulp Task所做的操作如下说明:


Gulp Task


说明


Vendor


将所有第三方的js类库合并到一个文件


Browserify-vendor


将package.json中dependencies的依赖模块buffer化,以提供性能


Browserify


编译和绑定只与app相关的文件(无依赖项),并引用sourcemap对应、uglify压缩、buffer优化、babel转化ES6


Browserify-watch


利用watchify监测bundle.js文件的变化,并重新编译


Styles


编译less样式文件,自动添加前缀


Watch


监测Less文件,发生变化重新编译


Default


运行以上所有任务,且进程挂起监控watch


Build


运行以上所有任务,退出

4. 其他包管理(可无)

bower包管理工具的引入。由于NPM主要运用于Node.js项目的内部依赖包管理,安装的模块位于项目根目录下的node_modules文件夹内。并且采用嵌套的依赖关系树,即子依赖包各自有自己的依赖关系树,并不会造成他们之间的冲突。但是这种情况在纯前端的包管理就不那么友好了,比如你使用多个jquery版本。在使用方面npm主要用于管理类似grunt,gulp, ESlint,CoffeScript等npm模块。而bower管理纯前端css/js的包,比如jquery, bootstrap

使用步骤

1. 创建bower.json文件,将依赖包添加进(作用跟package.json类似)

{
    "name": "practice",
    "dependencies": {
        "jquery": "^2.1.4",
        "bootstrap": "^3.3.5",
        "magnific-popup": "^1.0.0",
        "toastr": "^2.1.1"
    }
}

2. 运行

npm install bower -g

bower install

5. 渲染部件

在渲染部分,React提供了客户端、服务端的渲染方式。具体区别如下:

1. 客户端渲染:

  可以直接在浏览器运行ReactJS,这是通用的比较简单的方式,网上也有很多例子。http://reactjs.org。服务端只创建初始化的html,装载你的组件UI,提供接口和数据。前端做路由与渲染的工作。缺点就是用户等待时间长。

2. 服务端渲染:

  html从后端生成,包含所有你的组件脚本UI以及数据。可以理解为生成一个静态的结果集页面。响应快,体验好。主要运用于提高主屏性能和SEO。服务端渲染,需要消耗CPU,但可以借助缓存实现优化。React中,通过renderToStaticMarkup方法实现。并且,你还需要保留对应的State以及所需要的数据。

例子援引如下开源项目,有兴趣的朋友可以去了解下。

http://sahatyalkabov.com/create-a-character-voting-app-using-react-nodejs-mongodb-and-socketio/

以React-Router为例(客户端)

1. 创建app/component/App.js

首先创建组件的容器app,this.props.children用于渲染其他组件

import React, {Component} from ‘react‘

class App extends Component {
    render() {
        return (
            <div>
                {this.props.children}
            </div>
        );
    }
}

export default App

2. 创建app/routes.js

如下点,指定路由/和/add,对应Home和AddCharacter组件

import React from ‘react‘
import {Route} from ‘react-router‘
import App from ‘./components/App‘
import Home from ‘./components/Home‘
import AddCharacter from ‘./components/AddCharacter‘;

export default (
    <Route component ={App} >
        <Route path= ‘/‘ component={Home} />
        <Route path= ‘/add‘ component={AddCharacter} />
    </Route>
)

3.创建main.js

将Router组合的组件渲染到id为app的div里。

import React from ‘react‘
import Router from ‘react-router‘
import ReactDOM from ‘react-dom‘
import { createHistory } from ‘history‘; // you need to install this package
import routers from ‘./routes‘

let history = createHistory();
ReactDOM.render(<Router history={history}>
        {routers}
        </Router>, document.getElementById(‘app‘)) 

5. app/components/添加home组件

Import React from ‘react’
Class Home extends React.Component{
    Render(){
        Return (
            <div className =’home’>
                Hello </div>)
    } }
Export default Home

6. 组件

app/component/添加AddCharacter组件

这里采用的是alt(基于Flux)第三方库,所以还需要添加Actions和Store,以及alt.js文件。这里不一一列举,可以查看上面的源码地址。

Tip: 也可以使用react-redux来构建我们自己的app组件,redux能更好的管理react的state。 

7. 数据库

创建数据库数据,如果你是单页应用,那么建议使用mongoDB。具体实现不再一一描述,可以上网搜索相关内容

8. API

如果是基于mongoose的话,则只需要利用上面的Express、Koa或者Hapi创建API,访问mongoose数据.

如果是大型项目,有自己独立的后端语言,如C#或者Java。则可以基于微服务框架创建服务API。使用axios或者superagent等库访问数据。

参考文献

http://sahatyalkabov.com/create-a-character-voting-app-using-react-nodejs-mongodb-and-socketio/

http://stackoverflow.com/questions/27290354/reactjs-server-side-rendering-vs-client-side-rendering

http://stackoverflow.com/questions/18641899/what-is-the-difference-between-bower-and-npm

https://ifelse.io/2015/08/27/server-side-rendering-with-react-and-react-router/

https://www.airpair.com/node.js/posts/nodejs-framework-comparison-express-koa-hapi

时间: 2024-08-29 06:57:06

关于React前端构建的一般过程 - 理论篇的相关文章

记学习前端构建工具gulp的过程,略心酸

初学前端的时候就听过友人说,前端不好学,很多软件都是要自己下载安装插件的,当时是很不以为然的,不就是下载几个软件外加安装插件吗?!怎么会很难呢!后面才发现自己真的错了. 今天刚好准备好好看看前端构建工具gulp的使用,于是乎就各种上网查资料.刚开始的时候有点摸不着头脑,这个东西不是一个软件,拿来就用,需要自己配置环境,自己根据需求安装package,完全是自己DIY的一个工具. 下面就把整个安装过程,记录下来,方便以后查看吧. 先明确几个概念:1.gulp是基于node.js环境下工作的:2.命

【前端构建】WebPack实例与前端性能优化

计划把微信的文章也搬一份上来. 这篇主要介绍一下我在玩Webpack过程中的心得.通过实例介绍WebPack的安装,插件使用及加载策略.感受构建工具给前端优化工作带来的便利. 壹 | Fisrt 曾几何时,我们是如上图的方式引入JS资源的,相信现在很少遇见了.近年来Web前端开发领域朝着规范开发的方向演进.体现在以下两点: MVC研发构架.多多益处(逻辑清晰,程序注重数据与表现分离,可读性强,利于规避和排查问题...) 构建工具层出不穷.多多益处(提升团队协作,以及工程运维,避免人工处理琐碎而重

如何用webpack实现自动化的前端构建工作流

什么是自动化的前端构建流? 1. 自动补全css私有前缀,自动转化less\sass为css,自动转化es6\vue\jsx语法为js,自动打包小图片为base64以减少http请求,自动给js,css,甚至img加hash值,以避免浏览器缓存,自动合并压缩代码,自动刷新实时预览效果(甚至免刷新),可以按照自己喜欢的目录结构存放原始资源文件,为了方便手机等访问,不需要搭建apache.nginx等服务器实现http访问...... 如何快速开始 首先 git clone https://gith

前端构建:Less入了个门

一.前言   说到前端构建怎能缺少CSS预处理器呢!其实CSS的预处理器有很多啦,比较出名的有Scss.Sass.Stylus和Less.(最近还听说出现了Autoprefixer等CSS后处理器,可参考@一丝的PPT) 众多CSS预处理器中Less的语法最接近原生CSS,因此相对来说更容易上手,假如有JS.C#等编程经验的话,其实上述的几种预处理器的学习成本也不会特别高.下面是我们这阵子的学习笔记,以便日后查阅. 最好的入门教程——官网地址:http://lesscss.org/ 最佳实践之一

前端构建

后端有构建工具,比如Java的ant.maven,前端也有构建工具,比如现在非常流行的grunt. 前端构建,是在项目部署上线前,将js css html 图片合并压缩的过程,使用模块化开发时,又多了个规范化过程. Seajs是非常好用,好用的代价是SPM构建打包起来很麻烦,文档不全,没好的实例. demo地址 https://github.com/sprying/ys7 一.项目需求 我们要做成什么? 充分利用缓存,并减少请求数. 方案一:提炼出相同功能的组件,并存独立文件(dialog.js

“流式”前端构建工具——gulp.js 简介

Grunt 一直是前端领域构建工具(任务运行器或许更准确一些,因为前端构建只是此类工具的一部分用途)的王者,然而它也不是毫无缺陷的,近期风头正劲的 gulp.js 隐隐有取而代之的态势.那么,究竟是什么使得 gulp.js 备受关注呢? Grunt 之殇 gulp.js 的作者 Eric Schoffstall 在他介绍 gulp.js 的 presentation 中总结了 Grunt 的几点不足之处: 插件很难遵守单一责任原则.因为 Grunt 的 API 设计缺憾,使得许多插件不得不负责一

前端构建工具之争——Webpack vs Gulp 谁会被拍死在沙滩上

.table tr>td:nth-child(1){width: 2em !important;padding-left: .6rem !important;padding-right: .6rem !important;} 本文组织结构 理想的前端开发流程 Gulp 为何物 Webpack 又是从哪冒出来的 结论 文章有点长,总共 1800 字,阅读需要 18 分钟.哈哈,没耐心的直接戳我到高潮部分. 理想的前端开发流程 在说构建工具之前得先说说咱期望的前端开发流程是怎样的? 写业务逻辑代码(

如何利用Truffle React框架构建完整的智能合约

如何利用Truffle React框架构建完整的智能合约 使用solidity的truffle框架开发智能合约,前端使用react框架,最终完成智能合约从前端到后端,从开发到部署的完整流程. 1. 版本需求 Truffle v5.0.28 (core: 5.0.28) Solidity v0.5.0 (solc-js) Node v8.11.2 Web3.js v1.0.0-beta.37 2. 项目初始化 mkdir -p truffle cd truffle truffle unbox re

前端构建的初步尝试

前言 这篇文章的主要目的是告诉大家,构建工具可以做那些事情.大家不必去深入研究这个东西.最基本的是有个概念. 什么是前端构建? 在平时我们浏览一些大型的站点,会发现其中的一些css经过压缩(去掉了空白符,注释),js经过了混淆和压缩.一些引用的文件的链接会加上奇怪的字串(文件md5),例如: <link rel="stylesheet" href="/css/popModal-5c7f30ff2b.css" type="text/css"