Redux营造的同构Web应用,最佳实践

React/Redux构建的同构Web应用

2018/07/30 · CSS ·
React,
Redux

原来的书文出处: 原 一成(Hara
Kazunari)   译文出处:侯斌   

世家好,作者是原10%(@herablog),近日在CyberAgent首要担任前端开发。

Ameblo(注: Ameba博客,Ameba
Blog,简称Ameblo)于二零一六年七月,将前端部分由原先的Java架构的利用,重构成为以node.js、React为底蕴的Web应用。那篇小说介绍了这一次重构的缘起、指标、系统规划以及最后达到的结果。

新种类公布后,立即就有人注意到了那一个变化。

 澳门葡京 1

twitter_msg.png

一、初入React世界

翻译按:近来React(web/native)还是如火如荼,相信大家都尝试,大家协会也伊始在React领域拥有尝试.

二零一四年应有是 React
逐步走向成熟的一年,让大家一齐来探望国外的开发者们都总括了哪些”最佳实践”.

React Redux Sever Rendering(Isomorphic JavaScript)

澳门葡京 2

React Redux Sever Rendering(Isomorphic)入门

系统重构的起因

2002年起,Ameblo成为了东瀛境内最大局面包车型客车博客服务。可是随着系统规模的增强,以及众多皮之不存毛将焉附人口不停增多各类模块、页面教导链接等,最后使得页面显示缓慢、对网页浏览量(PV)造成了极度严重的熏陶。并且页面展现速度方面,绝大部分是前者的难题,并非是后端的难点。

根据上述那么些难点,我们决定以增强页面彰显速度为重点对象,对系统进行到底重构。与此同时后端系统也在开始展览重构,将昔日的数量部分进行API化改造。此时便是1个将All-in-one的巨型Java应用进行适量分割的绝佳良机。

1.2 JSX语法

  • class 属性修改为className

  • for 属性修改为 htmFor

  • 举办属性
    行使ES6 rest/spread 个性升高效能

const data = { name: 'foo', value : 'bar' };
// const component = <Component name={data.name} value={data.value} />;
const component = <Component {...data}>
  • 自定义HTML 属性
    假诺要利用HTML自定义属性,要利用data- 前缀

<div data-attr="xxx">content</div>
  • HTML 转义
    dangerouslySetInnerHTML 属性

<div dangerouslySetInnerHTML={{__html: 'cc &copy:2015'}}></div>

=============以下为译文==============

前言

由于可能有点读者没听过 Isomorphic
JavaScript
。由此在进到开发 React Redux Sever Rendering
应用程式的主目的在于此以前大家先来聊聊 Isomorphic JavaScript 那个议题。

根据 Isomorphic
JavaScript
那些网站的印证:

Isomorphic JavaScript
Isomorphic JavaScript apps are JavaScript applications that can run
both client-side and server-side.
The backend and frontend share the same code.

Isomorphic JavaScript 系指浏览器端和伺服器端共用 JavaScript 的程式码。

其它,除了 Isomorphic JavaScript 外,读者大概也有听过 Universal
JavaScript 那些用词。那什麽是 Universal JavaScript 呢?它和 Isomorphic
JavaScript
是指同一的意趣呢?针对这些议题网路上有个别开发者提议了和睦的眼光:
Universal
JavaScript、Isomorphism
vs Universal
JavaScript。在那之中Isomorphism vs Universal JavaScript 那篇小说的小编 Gert Hengeveld 指出
Isomorphic JavaScript 首借使指左右端共用 JavaScript 的开发格局,而
Universal JavaScript 是指 JavaScript
程式码能够在不一致条件下运作,这自然蕴含浏览器端和伺服器端,甚至其余条件。也正是说
Universal JavaScript 在意义上得以分包的比 Isomorphic JavaScript
更广阔一些,可是在 Github
或是许多技艺切磋上数见不鲜会把两者视为等同件工作,这部份也请读者注意。

目标

本次系统重构确立了以下多少个对象。

1.3 React 组件

  • props
  1. classPrefix:
    class前缀。对于组件来说,定义2个集合的class前缀,对体制与相互分离起了非凡主要的成效。
  • 用funtion prop 与父组件通讯
  • propTypes
    用来规范 props 的连串与必需的场地

二零一四年 React
在世上都有为数不少有关新的翻新和开发者大会的商讨.关于二零一八年的要紧事件,请参见
React in 2014.

Isomorphic JavaScript 的好处

在开班真正撰写 Isomorphic JavaScript 前大家在进一步追究利用 Isomorphic
JavaScript 有啥样好处?在谈功利从前,大家先看看最早 Web
开发是何许处理页面渲染和 state 管理,还有遭遇哪些挑衅。

最早的时候大家谈论 Web 很单纯,都以由 Server
端实行模版的拍卖,你能够想成 template
是3个函数,大家传递资料进去,template 最终发生一张 HTML
给浏览器呈现。例如:Node
使用的(EJS、Jade)、Python/Django

Template澳门葡京
或代表方案
Jinja、PHP

Smarty、Laravel
使用的
Blade,甚至是
Ruby on Rails 用的
ERB。都以由后端去
render 全部材质和页面,前端处理相对单纯。

不过随著前端工程的软体育工作程化和使用者体验的供给,开首产出各式前端框架的百废具兴,例如:Backbone.js、Ember.js
和 Angular.js
等前端 MVC (Model-View-Controller) 或 MVVM (Model-View-ViewModel)
框架,将页面于前者渲染的不刷页单页式应用程式(Single Page
App)也为此伊始流行。

后端除了提供初步的 HTML 外,还提供 API Server
让前端框架能够拿走资料用于前端 template。複杂的逻辑由
ViewModel/Presenter 来处理,前端 template
只处理差不离的是还是不是出示或是成分迭代的现象,如下图所示:

澳门葡京 3

React Redux Sever Rendering(Isomorphic)入门

可是前端渲染 template 就算有它的裨益但也赶上有个别标题包含功效、SEO
等议题。此时大家就从头思考 Isomorphic JavaScript
的大概:为什麽我们不可能上下端都使用 JavaScript 甚至是 React?

澳门葡京 4

React Redux Sever Rendering(Isomorphic)入门

实质上,React 的优势就在于它可以很优雅地促成 Server Side Rendering 达到
Isomorphic JavaScript 的机能。在 react-dom/server 中有多个方式
renderToStringrenderToStaticMarkup 能够在 server 端渲染你的
components。其根本都以将 React Component 在 Server 端转成 DOM
String,也足以将 props 往下传,然则事件处理会失效,要到 client-side 的
React 接收到后才会把它丰裕去(但要注意 server-side 和 client-side 的
checksum 要一如既往不然会冒出谬误),那样一来能够增加渲染速度和 SEO
效果。renderToStringrenderToStaticMarkup 最大的异样在于
renderToStaticMarkup 会少加一些 React 内部选拔的 DOM
属性,例如:data-react-id,由此得以节省一些能源。

使用 renderToString 进行 Server 端渲染:

import ReactDOMServer from 'react-dom/server';

ReactDOMServer.renderToString(<HelloButton name="Mark" />);

渲染出来的功力:

<button data-reactid=".7" data-react-checksum="762752829">
  Hello, Mark
</button>

因此看来使用 Isomorphic JavaScript 会有以下的利益:

  1. 有助于 SEO
  2. Rendering 速度较快,效率较佳
  3. 放任蹩脚的 Template 语法拥抱 Component 元件化思考,便于维护
  4. 尽心尽力前后端共用程式码节省开支时间

唯独要小心的是即使有使用 Redux 在 Server Side Rendering
中,其流程相对複杂,不过大致流程如下:
由后端预先载入要求的 initialState,由于 Server 渲染必须全体都转成
string,所以先将 state 先 dehydration(脱水),等到 client 端再
rehydration(覆水),重建 store 往下传到前端的 React Component。

而要把材质从伺服器端传递到客户端,大家要求:

  1. 把得到初始 state 当做参数并对种种请求建立二个簇新的 Redux store 实体
  2. 选取性地 dispatch 一些 action
  3. 把 state 从 store 取出来
  4. 把 state 一起传到客户端

接下去我们就开首起头实作1个简单的 React Server Side Rendering app

页面彰显速度的创新(总而言之越快越好)

用来测定用户体验的目的有无数,大家觉得个中对用户最要害的目标就是页面呈现速度。页面显示速度越快,目的内容就能越快到达,让任务在长时间内实现。此次重构的对象是尽只怕的维持博客小说、以及在Ameblo内所表现的种种各类的始末的原有情势,在不破坏现有价值、体验的底子上,进步展现和页面行为的快慢。

1.5 React 生命周期

React生命周期分成两类:

  • 当组件在挂载或卸载时
  • 当组件接收新的多寡时,即组件更新时

推荐介绍初叶化组件

import React, { Component, PropTypes } from 'react';
class App extends Component {
    // 类型检查
    static propTypes = {
        // ...
    };
    // 默认类型
    static defaultProps = {
        // ...
    };

    constructor(props) {
        super(props);
        this.state = {
            // ...
        };
    }

    componentWillMount() {
        // ...
    }
    // 在其中使用setState 会更新组件
    componentDidMount() {
        // ...
    }

    render() {
        return <div>This is a demo.</div>
    }
}

component威尔Unmount 平日会举行一些清理措施

  • 多少更新进度
    万一自个儿的state更新了,那么会挨个执行shouldComponentUpdate、component威尔Update
    、render 和 componentDidUpdate。

只要组件是由父组件更新 props 而创新的,那么在 shouldComponentUpdate
此前会先实施component威尔ReceiveProps 方法。

澳门葡京 5

React生命周期全体流程图

那正是说,二〇一五年最有趣的问题来了:我们理应如何开发2个 App, 有哪些推荐的库?

Redux营造的同构Web应用,最佳实践。专案成果截图

澳门葡京 6

image

系统的现代化(搭乘生态系统)

早年的Web应用是将数据以HTML的款式再次回到,这个时候并没有怎么难点。但是,随着剧情的加码,体验的丰裕化,以及配备的种种化,使得前端所占的百分比越来越大。之前要开发三个好的Web应用,要是要高质量,就决然不要将左右端分隔开分离。当年以那一个需要开发的连串,在经验了10年以往,已经远远不能适应当下的生态系统。

「跟上脚下生态系统」,以此来塑造系统会推动巨大的利益。因为作为基本的生态系统,其开发分外活跃,每日都会有大宗新的idea。由此新式的技艺和意义更便于被收取,同时完结高质量也进一步便于。同时,那么些「新」对于年轻的技能新人也尤其重庆大学。仅知道旧标准旧技术的父辈对于3个佳绩的组织来说是绝非前途的(自觉本身膝盖也中了一箭)。

1.6 React与DOM

1.6.1 ReactDOM
其API非常少,只有findDOMNode,unmountComponentAtNode和render

  • findDOMNode

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
  class App extends Component {
     componentDidMount() {
     // this 为当前组件的实例
     const dom = ReactDOM.findDOMNode(this);
   }
   render() {}
} 

findDOMNode只对曾经挂载的零部件有效
DOM 真正被添加到 HTML 中的生命周期方法是componentDidMount 和
componentDidUpdate 方法

  • render
    把 React 渲染的Virtual DOM 渲染到浏览器的 DOM 个中,就要动用 render
    方法

React 还提供了二个很少使用的 unmountComponentAtNode 方法来拓展
卸载操作。

  • refs
    refs即reference,组件被调用时会新建二个该器件的实例,而refs就会指向这些实例。

作为一名长时直接纳 React.js
的开发者,作者对此难题有自身的答案和特等实践,但你可能不自然完全同意.笔者对您的想法和见解很有趣味,请留言以便研商.

Server Rendering

获取数据能够调用 action,routes 在服务器端的处理参考 react-router server
rendering,在服务器端用二个 match 方法将得到的 request url
匹配到大家以前定义的 routes,解析成和客户端一致的 props 对象传递给组件。

./devServer.js

var express = require('express');
var webpack = require('webpack');
var config = require('./webpack.config.dev');

import React from 'react';
import { renderToString } from 'react-dom/server';
import { RouterContext, match } from 'react-router';
import { Provider } from 'react-redux';
import createRouter from './client/routes';
import configureStore from './client/store';

var app = express();
var compiler = webpack(config);

import comments from './client/data/comments';
import posts from './client/data/posts';

// create an object for the default data
const defaultState = {
  posts,
  comments
};

app.use(require('webpack-dev-middleware')(compiler, {
  noInfo: true,
  publicPath: config.output.publicPath
}));

app.use(require('webpack-hot-middleware')(compiler));

function renderFullPage(html, initialState) {
  return `
    <!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>isomorphic-redux-app</title>
    <link rel="shortcut icon" type="image/png" href="http://obl1r1s1x.bkt.clouddn.com/bitbug_favicon.ico"/>

  </head>
  <body>
    <div id="root">${html}</div>
    <script>
        window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};
    </script>
    <script src="/static/bundle.js"></script>
  </body>
</html>
  `;
}

app.use((req, res) => {
 const store = configureStore(defaultState);
 const routes = createRouter();
 const state = store.getState();

  match({ routes, location: req.url }, (err, redirectLocation, renderProps) => {
    if (err) {
      res.status(500).end(`Internal Server Error ${err}`);
    } else if (redirectLocation) {
      res.redirect(redirectLocation.pathname + redirectLocation.search);
    } else if (renderProps) {
      const html = renderToString(
          <Provider store={store}>
            <RouterContext {...renderProps} />
          </Provider>
        );
        res.end(renderFullPage(html, store.getState()));
    } else {
      res.status(404).end('Not found');
    }
  });
});

app.listen(7770, 'localhost', function(err) {
  if (err) {
    console.log(err);
    return;
  }

  console.log('Listening at http://localhost:7770');
});

劳动器端渲染部分能够直接通过共用客户端 store.dispatch(action) 来统一获取
Store 数据。其它注意 renderFullPage 生成的页面 HTML 在 React 组件 mount
的局地(<div id=”root”>),前后端的 HTML 结构应该是一模一样的。然后要把
store 的情状树写入1个全局变量(INITIAL_STATE),那样客户端起首化
render 的时候能够校验服务器生成的 HTML
结构,并且一路到初阶化状态,然后全部页面被客户端接管。

本项目地址:React-Redux-Server-Rendering

晋级界面设计、用户体验(二〇一五年版Ameblo)

Ameblo的手机版在2009年经历了1遍改版之后,就基本上并未太大的变化。那其间很多用户都早已习惯了原生应用的规划和感受。那几个体系也是为着不令人认为很土很难用,达到顺应时代的二〇一五年版界面设计和用户体验。

OK,接下去让本人实际详尽聊聊。

二、漫谈React

2.1 事件系统
壹 、事件委派
React没有把事件平昔绑定在真实的节点上,而是绑定在最外层,使用一个联合的轩然大波监听器,这一个事件监听器上保持了3个映射来保存全数组件内部的风浪监听和处理函数。
React中利用DOM原生事件时,要在组件卸载时手动一处,不然很只怕出现内部存款和储蓄器泄漏的题材。

澳门葡京 7

页面加载速度的一字不苟

2.2 表单

3.select组件
单选和多选二种。在JSX语法中,能够经过安装select标签的
multiple={true}来落到实处一个多选下拉列表。

要是你刚刚初叶接触React.js,能够查看大家的React.js 教程,或许 Pete Hunt
的React howto.

改善点

系统重构前,通过
SpeedCurve
进行辨析,得出了上面结论:

  • 服务器响应速度非常快
  • HTML文书档案较大(页面全体因素都含有在那之中)
  • 闭塞页面渲染的能源(JavaScript、Stylesheet)较多
  • 能源读取的次数过多,容量过大

依据那些规定了上边这几项基本方针:

  • 为了不致于降低服务器响应速度,对代码实行优化,缓存等
  • 尽恐怕减弱HTML文书档案大小
  • JavaScript异步地加载与履行
  • 早期展现页面时,仅仅加载所需的不可或缺能源

2.2.2 受控组件

每当表单的情事爆发变化时,都会被写入到零部件的state中,那种组件在React中被叫做受控组件(controlled
component)。
受控组件更新state的流水生产线:
(1)能够透过在开班 state 中设置表单的私下认可值
(2)每当表单的值爆发变化时,调用onChange事件处理器
(3)事件处理器通过合成事件指标e得到改变后的情事,并创新state
(4)setState触发视图的双重渲染,完结表单组件值得更新

在 React.js 应用中拍卖数量卓殊不难,但也洋溢挑衅.

SSR还是SPA

前不久相比较于添加到收藏夹中,用户更倾向于经过查找结果、推特(Twitter)(TWT昂Cora.US)、Facebook等应酬媒体上的分享链接打开博客页面。谷歌(Google)和推特(TWTR.US)的AMP,
Facebook的Instant
Article标志第壹页的表现速度小幅度影响到用户满足度。

除此以外,从GoogleAnalytics等日志记录中询问到在小说列表页面和前后作品间开展跳转的用户也很多。那大概是因为博客作为个人体媒介体,当某一用户看到一篇不错的文章,非常感兴趣的时候,他也同时想看一看同一博客内的任何作品。也正是说,博客这种服务
先是页快捷加载与页面间神速跳转同等首要

故而,为了让互相都能表达最佳质量,大家决定在率先页使用劳务器端渲染(Server-side
Rendering, SS奥德赛),从第1页起利用单页面应用(Single Page Application,
SPA)。这样一来,既能确定保证率先页的显得速度和机械可读性(Machine-Readability)(含SEO),又能博取SPA带来的敏捷展现速度。

BTW,对于眼下的架构,由于服务器和客户端选用同样的代码,全部进展SS哈弗或是全体进展SPA也是唯恐的。如今早已落到实处就算在不能够运转JavaScript的环境中,也能够健康通过SS瑞虎来浏览。能够预言现在等到ServiceWorker普及之后,初叶页面将特别高速化,而且能够兑现离线浏览。

澳门葡京 8

z-ssrspa.png

从前的种类完全选择SS奥迪Q7,而以后的系统从第一页起变为SPA。

 澳门葡京 9

z-spa-speed.gif

SPA的魅力在于显示速度之快。因为只有通过API获取所需的需求数据,所以速度尤其快!

2.2.3 非受控组件

假使一个表单组件没有 value props(单选按钮和复选框对应的是 checked
prop)时,就能够称呼非受控组件。相应地,也能够行使 defaultValue 和
defaultChecked prop来表示组件的暗许状态。平常,需求经过为其添加ref
prop来访问渲染后的平底DOM元素。

那是因为你能够运用多样艺术将属性数据传递给 React
组件,从而创设出渲染树,但您应该什么翻新视图却不是引人侧目标.

延期加载

我们利用SS中华V+SPA的点子来优化页面间跳转那种横向移动的快慢,并且动用延缓加载来革新页面包车型客车纵向移动速度。一开头要显现的始末以及导航,还有博客文章等最早显示,在这一个剧情之下的援救内容随着页面包车型客车滚动逐步展现。那样一来,首要的内容不会受页面上边内容的影响而更快的来得出来。对于那么些想不久读文章的用户来说,既不增添用户体验上的下压力,又能完整的提供页面下方的剧情。

 澳门葡京 10

z-lazyload.png

事先的系统因为将页面内的全体内容都放到HTML文书档案里,所以使得HTML文书档案体积相当大。而现行反革命的序列,仅仅将重视内容放到HTML里重返,减弱了HTML的体积和数目请求的轻重。

2.3 样式处理

2.3.3 CSS Modules
CSS Modules 内部通过ICSS来缓解体制导入和导出四个难点,分别对应 :import
和 :export 多少个新增的伪类

:import("path/to/dep.css") {
  localAlias: keyFromDep;
  /*...*/
}

:export {
  exporteKey: exportedValue;
  /*...*/
}

启用 CSS Modules

// webpack.config.js
css?modules&localIdentName=[name]_[local]-[hash:base64:5]

加上 modules 即为启用,在那之中 localIdentName 是设置生成样式的命名规则

使用webpack能够让全局样式和CSS Modules的有些样式和谐共处

module: {
  loaders: [{
    test: /\.jsx?$/,
    loader: 'babel',  
  }, {
    test: /\.scss$/,
    exclude: path.resolve(__dirname, 'src/styles'),
    loader: 'style!css?modules&localIdentName=[name]_[local]!sass?sourceMap=true',
  },{
    test: /\.scsss$/,
    include: path.resolve(__dirname,'src/styles'),
    loader: 'style!css!sass?sourceMap=true',
  }]
}

二零一六一从头便出生了许多见仁见智 Flux
库,随后涌现出出更多颇具更强效用和进一步响应式消除方案.

HTML缓存

博客文章是静态文书档案,对于特定UMuranoL的哀求会回来固定的剧情,因而万分适合进行缓存。缓存使得服务器处理内容收缩,在增强页面响应速度的还要减轻了服务器的负担。大家将不变的剧情(文章等)生成的HTML实行缓存重回,对于由于变化的始末能过JavaScript、CSS等进行操作(比如展现、隐藏等)。

 澳门葡京 11

z-newrelic-entrylist.png

那张图体现了贰零壹伍年十月最终七日New
relic上的总计数据。小说列表页面包车型客车HTML的响应时间基本在50ms以下。

 澳门葡京 12

z-newrelic-entry.png

那张图是文章详细页面包车型客车总结数据。可以看出,那个页面包车型大巴响应时间也大半是在50ms以下。由于存在小说过长的时候会导致页面容量变大,以及小说页面无法一心缓存等情事,所以对待列表页面会存在更多较慢的响应。

对此因请求的客户端而爆发变化部分的拍卖,大家在HTML的body标签中经过到场相应的class,然后在客户端通过JavaScript和CSS等展开操作。比如,一些剧情不想在一些操作系统上显得,我们就用CSS对那一个内容开始展览隐蔽。由于CSS样式表会先载入,页面布局分明下来未来再举办页面渲染,所以那么些也得以化解前边要提到的「咯噔」难题。

<!– html –> <body class=”OsAndroid”>

1
2
3
<!– html –>
 
<body class="OsAndroid">

CSS

/* main.css */ body.OsAndroid .BannerForIos { dsplay: none; }

1
2
3
4
5
/* main.css */
 
body.OsAndroid .BannerForIos {
  dsplay: none;
}

2.4 组件间通讯

  1. 父组件通过props向子组件传递供给的新闻。
  2. 子组件向父组件通讯
  • 采取回调函数
  • 行使自定义事件机制
  1. 跨级组件通讯
    React中,大家得以应用 context 来完成跨级父子组件间的通讯

// listItem组件
class ListItem extends Component {
  static contextTypes = {
    color: PropTypes.string,
  };
  render() {
    const { value } = this.props;

    return (
      <li style={{background: this.context.color}}>{value}</li>
    )
  }
 }


// List组件
class List extends Component {
  static childContextTypes = {
    color: PropTypes.string,
  };
  getChildContext() {
    return {
        color: 'red'
    }
  }
}

父组件中定义了
ChildContext,那样从这一层初始的子组件都足以获得定义的context。

让大家一同来看望:

系统的现代化(搭乘生态系统)

2.5.2 高阶组件

福衢寿车高阶组件的法门有如下三种

  • 属性代理(props proxy)。高阶组件通过棉被服装进的React组件来操作 props
  • 反向继承(inheritance inversion)。高阶组件继承于被卷入的React组件
  1. 属性代理

import React, { Component } from 'react';

const MyContainer = (WrappedComponent) => {
  class extends Component {
    render() {
      return <WrappedComponent {...this.props}>
    }
  }
}

自然大家也能够用 decorator 来更换

import React, { Component } from 'react';
@MyContainer
class MyComponent extends Component {
  render();
}

export default MyComponent;

简易地替换到功能在类上的decorator,即接受供给装饰的类为参数。

  • 控制 props

import React, { Component } from 'react';
const MyContainer = (WrappedComponent) => {
  class extends Component {
    render() {
      const newProps = {
         text: newText,
      };
      return <WrappedComponent {...this.props} {...newProps}>
    }
  }
}
  • 由此 refs 使用引用
    高阶组价中,大家得以接受 refs 使用 WrappedComponent 的引用。

import React, { Component } from 'react';
const MyContainer = (WrappedComponent) => {
  class extends Component {
    proc(wrappedComponentInstance) {
      wrappedComponentInstance.method();
    }
    render() {
      const props = Object.assign({}, this.props, {
          ref: this.proc.bind(this),
      });
      return <WrappedComponent {...props}>
    }
  }
}
  • 抽象 state
    虚幻二个input组件

import React, { Componet } from 'react';
const MyContainer = (WrappedComponent) => {
  class extends Component {
    constructor(props) {
      super(props);
      this.state = {
        name:'',
      }

      this.onNameChange = this.onNameChange.bind(this);
    }
  }
 onNameChange(event) {
      this.setState({
          name: event.target.value,
      });
    }

    render() {
      const newProps = {
        name: {
          value: this.state.name,
          onChange: this.onNameChange,
        }
      }
      return <WrappedComponent {...this.props} {...newProps} />
    }
}
  • 行使此外因素包裹 WrappedComponent

import React,{ Component } from 'react';
const MyContainer = (WrappedComponent) => {
  class extends Component {
      render() {
        return (
            <div style={{display: 'block'}}>
              <WrappedComponent {...this.props}>
            </div>
         )
      }
   }
}


// 受控input组件使用
@MyContainer
class MyComponent extends Component {
  render() {
    return <input name="name" {...this.props.name}>
  }
}

高阶组件和mixin的例外

澳门葡京 13

mixin与高阶组件的不一样.png

  1. 反向继承
  • 渲染威胁

const MyContainer = (WrappedComponent) => {
  class extends WrappedComponent {
    render() {
      if(this.props.loggedIn) {
        return super.render();
      } else {
        return null;
      }
    }
  }
}

// 实例二:对render结果进行修改
const MyContainer = (WrappedComponent) => {
   class extends WrappedComponent {
    render() {
       const elementsTree = super.render();
       let newProps = {};

      if(elementsTree && elementsTree.type === 'input'){
        newProps = {value: 'May the force be with you'};
      }
      const props = Object.assign({}, elementsTree.props, newProps);
      const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.childre);
      return newElementsTree;
     }
   }
}
  • 控制state

const MyContainer = (WrappedComponent) => {
  class extends WrappedComponent {
    render() {
      return (
        <div>
            <h2>HOC Debugger Component</h2>
             <p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre>
             <p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre>
             {super.render()} 
        </div>
      )
    }
  }
}
  1. 零件命名
  2. 组件参数

import React, { Component } from 'react';
function HOCFactory(...params) {
  return function HOCFactory(WrappedComponent) {
    return class HOC extends Component {
      render() {
        return <WrappedComponent {...this.props}>
      }
    }
   }
}

// 使用
HOCFactoryFactory(params)(WrappedComponent);
// 或者
@HOCFactoryFactory(params);
class WrappedComponent extends React.Component{}

基于大家的经历,Flux
平时被过分施用,(正是大家总是在不必要它的时候依旧用了它).

技能选型

本次项目标技能选拔时,遵从了苦斗采纳当下当前市面上业已存在的宽泛选用的技艺这一规范。暗号便是:「活脱脱像范例应用相同Start」。那样一来,无论是哪个人都能够轻松的取得到相应的文书档案等音讯,同时此外的团体和商号如果要插足到花色中来也能不慢的右侧。不过在真的展费用付的时候,一些细节实现上因为各样各种的由来存在一些不等的气象,可是在特大程度上保持了各种模块的独立性。最后系统的光景构成如下图所示:

 澳门葡京 14

z-bigpicture.png

(有个别地方做了简便易行)

2.6 组件品质优化

  1. 纯函数
  • 加以相同的输入,它总能重返相同的输出
  • 经过并未副成效
  • 未曾额外的景观正视
  1. PureRender
    为再度达成了 shouldComponentUpdate 生命周期方法,让日前传来的
    props
    和 state 与以前的作浅比较,假设回去 false,那么组件就不会实施 render
    方法。

  2. react-addons-perf
    量化所做的习性优化功能
    Perf.start()
    Perf.stop()

Flux 提供了一种非凡清楚的艺术来存款和储蓄和立异App 全局 state(译者注:对应
react 中的 state),并在急需的时候触发渲染.

React with Redux

使用React和React实行开发的的时候,很多地点可以用 纯函数
的款式进行结合。纯函数是指特定的参数总是回到特定的结果,不会对函数以外的限量造成污染。使用纯函数进行付出能够确定保证各样处理模块最小化,不用顾虑会无意改变引用对象的值。那样一来,十三分推向大规模开发以及在同等客户端中维系多个状态。

界面更新的流程是:
Action(Event) -> Reducer (返回新的state(状态)) -> React (基于更新后的store内的state更新显示内容)

那是八个Redux Action的例子,演示了React Action (Action Creator)
基于参数重回二个Plain Object。处理异步请求的时候,我们参考
合法文书档案
,分别定义了成功请求和失利请求。获取数据时选拔了
redux-dataloader

JavaScript

// actions/blogAction.js export const FETCH_BLOG_REQUEST =
‘blog/FETCH_BLOG/REQUEST’; export function fetchBlogRequest(blogId) {
return load({ type: FETCH_BLOG_REQUEST, payload: { blogId, }, }); }

1
2
3
4
5
6
7
8
9
10
11
12
// actions/blogAction.js
 
export const FETCH_BLOG_REQUEST = ‘blog/FETCH_BLOG/REQUEST’;
 
export function fetchBlogRequest(blogId) {
  return load({
    type: FETCH_BLOG_REQUEST,
    payload: {
      blogId,
    },
  });
}

Redux
Reducer是一截然基于Action中带走的多寡,对已有state进行复制并更新的函数。

JavaScript

// reducers/blogReducer.js import as blogAction from
‘../actions/blogAction’; const initialState = {}; function
createReducer(initialState, handlers) { return (state = initialState,
action) => { const handler = (action && action.type) ?
handlers[action.type] : undefined; if (!handler) { return state; }
return handler(state, action); }; } export default
createReducer(initialState, { [blogAction.FETCH_BLOG_SUCCESS]:
(state, action) => { const { blogId, data } = action.payload; return
{ …state, [blogId]: data, }; }, });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// reducers/blogReducer.js
 
import  as blogAction from ‘../actions/blogAction’;
 
const initialState = {};
 
function createReducer(initialState, handlers) {
  return (state = initialState, action) => {
    const handler = (action && action.type) ? handlers[action.type] : undefined;
    if (!handler) {
      return state;
    }
    return handler(state, action);
  };
}
 
export default createReducer(initialState, {
  [blogAction.FETCH_BLOG_SUCCESS]: (state, action) => {
    const { blogId, data } = action.payload;
    return {
      …state,
      [blogId]: data,
    };
  },
});

React/Redux基于更新后的store中的数据,对UI举行更新。种种零部件依照传递过来的props值,总是以平等的结果回到HTML。React将View组件也视作函数来相比。

JavaScript

// main.js <SpBlogTitle blogTitle=”渋谷のブログ” /> //
SpBlogTitle.js import React from ‘react’; export class SpBlogTitle
extends React.Component { static propTypes = { blogTitle:
React.PropTypes.string, }; shouldComponentUpdate(nextProps) { return
this.props.blogTitle !== nextProps.blogTitle; } render() { return (
<h1>{this.props.blogTitle}</h1> ); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// main.js
<SpBlogTitle blogTitle="渋谷のブログ" />
 
// SpBlogTitle.js
import React from ‘react’;
 
export class SpBlogTitle extends React.Component {
  static propTypes = {
    blogTitle: React.PropTypes.string,
  };
 
  shouldComponentUpdate(nextProps) {
    return this.props.blogTitle !== nextProps.blogTitle;
  }
 
  render() {
    return (
      <h1>{this.props.blogTitle}</h1>
    );
  }
}

至于Redux的新闻在
法定文书档案
中说明得老大详细,推荐随时参考一下以此文书档案。

2.7 动画

TransitionGroup 能接济大家快捷地分辨出扩大或删除的零部件。

React
Transition设计了以生命周期函数的点子来兑现,即让子组件的每叁个实例都达成相应地生命周期函数。当React
Transition识别到有些子组件增或删时,则调用它对应地生命周期函数。我们可以再生命周期函数中贯彻动画逻辑。
要是每贰个子零件的动效相同,那么每三个子零件能够协同用3个生命周期函数。因而React
Transition 提供了 childFactory
配置,让用户自定义一个封装子组件的工厂方法,为子组件加上相应地生命周期函数。
React Transition提供的生命周期

  • componentWillAppear
  • componentDidAppear
  • componentWillEnter
  • componentDidEnter
  • componentWillLeave
  • componentDidLeave

componentWillxxx 只要在
componentWillReceiveProps中对this.props.childrennextProps.children做八个比较就能够了。componentDidxxx可以在componentWillxxx提供一个回调函数,用来实行componentDidxxx

React CSS Transition
为子组件的各样生命周期加了差异的className,那样用户能够很便利地依据className 地变化来达成动画

<ReactCSSTransitionGroup
  transitionName="example"
  transitionEnterTimeout={400}
>
{items}
</ReactCSSTransitionGroup>

对应地css代码

.example-enter {
  transform: scaleY(0);
  &.example-enter-active {
    transform: scaleY(1);
    transition: transform .4s ease;
  }
}

应用react-motion达成二个spring开关

import React, {Component} from ''react;

class Switch extends Component {
  constructor(props) {
    super(props);

    this.handleClick = this.handleClick.bind(this);

    this.state = {
      open: false,
    }
  }
  handleClick() {
     this.setState({
      open: !this.state.open
    })
  }
  render() {
    return (
      <Motion style={{x: spring(this.state.open ? 400 : 0)}}>
          {({x}) =>
           <div className="demo">
             <div
               className="demo-block"
               onClick={this.handleClick}
               style={{
                 transform: `translate3d(${x}px, 0, 0)`,
          }}
 />
 </div>
 } 
      </Motion>
    )
  }
}

Flux
在管理App的大局状态时很有用,比如:管理已报到用户景况,路由气象,可能是虎虎有生气账号状态,但假如用来保管权且数据恐怕当地数据,立时就会变得很难过.

同构Web应用(Isomorphic web app)

Ameblo
二零一五年版基本上完全是用JavaScript重写的。无论是Node服务器上依然客户端上都应用了同样的代码和流程,也正是所谓的同构Web应用。项指标目录结构大体上上如下所示,服务器端的进口文件是
server.js ,浏览器的输入文件是 client.js

  • actions/ Redux Action (服务器,客户端共用)
  • api/ 封装的API接口
  • components/ React组件 (服务器,客户端共用)
  • reducer/ <span class=”underline”>Redux
    Reducers</span> (服务器,客户端共用)
  • services/ 服务层模型,使用
    Fetchr
    对数据请求实行适度粒度的撤销合并。同时那些也使得node.js作为代理,直接请求API(服务器专用)。
  • server.js 服务器入口(服务器专用)
  • app.js node服务器的安顿、运行,由server.js调用(服务器专用)
  • client.js 客户端入口(客户端专用)

 澳门葡京 15

z-isomorphic.png

写好的JavaScript同时运维在劳动器端依然客户端上的运作行为、以及从数额读取直到在页面上出示结束的一体浏程,都是同等的款式进行。

澳门葡京 16

z-code-stats.png

使用Github的语言计算能够看看
,JavaScript占了全套项目标94.0%,大致全体都是由JavaScript写成的。

深刻Redux 应用框架

咱俩不引进应用 Flux 来管理路由相关的多寡,比如
/items/:itemId.获取路由数据并蕴藏在组件的 state
之中.那种景况下,它会在组件销毁时三只被销毁.

原子设计(Atomic Design)

对此组件的宏图,大家运用了
原子设计
理念。其实项目并从未一初阶就利用原子设计,而是基于 Presentational and
Container
Components
,对 containercomponent
举办了两层划分。不过Ameblo中的组件实在是太多,很不难导致职务不显眼的情形,因而最后使用了原子设计理念。项指标骨子里运用中,选取了以下的平整。

 澳门葡京 17

z-atomic-design.png

5.1 Redux简介

假定您想询问越来越多关于 Flux 的音讯,提出阅读The 埃沃lution of Flux
Frameworks.

Atoms

组件的小小单位,比如Icon、Button等。原则上不持有状态,从父组件中拿走传递过来的props,并回到HTML。

5.1.2Redux三大原则

  1. 单一数据源
  2. 情况是只读地
  3. 情景修改均由纯函数实现

Redux 是三个 JavaScript App的可预测 state 容器.

Molecules

以复用为前提的组件,比如List、Modal、User
thunmbnail等。原则上不富有状态,从父组件中取得传递过来的props,并回到HTML。

5.1.3 Redux 核心API

Redux焦点是1个store,那几个store是由createStore(reducers[,initialState])方法生成

通过createStore格局创立的store是三个目标,包罗5个点子

  • getState(): 获取store中当前的动静
  • dispatch(action):分发1个action,并回到那么些action,那是唯一能改变store中数量的艺术
  • subscribe(listener): 注册贰个监听者,它在store发生变动时被调用
  • replaceReducer(nextReducer):
    更新当前store里的reducer,一般只会在开发情势中调用该措施。

假设您觉得供给 Flux 可能相似的化解方案,你应当驾驭一下 redux,并就学Dan
Abramov的Getting started with redux,那能够连忙拉长你的付出技能.

Organisms

页面上较大的一块组件,比如Header,Entry,Navi等。对于这一层的零件,能够在内部开始展览数据获得处理,以及采纳Redux
State 和
connect
,维护组件的动静。那里获得的组件状态以props的款式,传递给 Molecules
Atom

JavaScript

// components/organisms/SpProfile.js import React from ‘react’; import {
connect } from ‘react-redux’; import { routerHooks } from
‘react-router-hook’; import { fetchBloggerRequest } from
‘../../../actions/bloggerAction’; // 数据获得处理
(使用react-router-hook) const defer = async ({ dispatch }) => { await
dispatch(fetchBloggerRequest()); }; // Redu store的state作为props const
mapStateToProps = (state, owndProps) => { const amebaId =
owndProps.params.amebaId; const bloggerMap = state.bloggerMap; const
blogger = bloggerMap[amebaId]; const nickName = blogger.nickName;
return { nickName, }; }; @connect(mapStateToProps) @routerHooks({ done
}) export class SpProfileInfo extends React.Component { static propTypes
= { nickName: React.PropTypes.string.isRequired, }; render() { return (
<div>{this.props.nickName}</div> ); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// components/organisms/SpProfile.js
 
import React from ‘react’;
import { connect } from ‘react-redux’;
import { routerHooks } from ‘react-router-hook’;
 
import { fetchBloggerRequest } from ‘../../../actions/bloggerAction’;
 
// 数据获取处理 (使用react-router-hook)
const defer = async ({ dispatch }) => {
  await dispatch(fetchBloggerRequest());
};
 
// Redu store的state作为props
const mapStateToProps = (state, owndProps) => {
  const amebaId = owndProps.params.amebaId;
  const bloggerMap = state.bloggerMap;
  const blogger = bloggerMap[amebaId];
  const nickName = blogger.nickName;
 
  return {
    nickName,
  };
};
 
@connect(mapStateToProps)
@routerHooks({ done })
export class SpProfileInfo extends React.Component {
  static propTypes = {
    nickName: React.PropTypes.string.isRequired,
  };
 
  render() {
    return (
      <div>{this.props.nickName}</div>
    );
  }
}

5.1.4 与React 绑定

亟需选择react-redux拓展react和redux的绑定,其提供了贰个零件和API帮衬Redux和React实行绑定,叁个是
React组件<Provider />,2个是 connect(),<Provider />接受一个store
作为props,它是全体Redux应用的顶层组件,而connect()提供了在总体React应用的任意组件中取得store中多少的功用。

Redux 三番九遍并革新了 Flux 的构思,学习了 Elm ,避开了 Flux
的复杂度(译者注:Elm是一门函数式编制程序语言).

Template

各类请求路径(UQX56L)所对应的机件。其职分是将所需的预制构件从Organisms中import过来,以自然的各种和格式整合在联名。

5.2 Redux middleware

Redux 提供了 applyMiddleware 方法来加载 middleware,其源码如下

import compose from './compose';

export default function applyMiddleware(...middlewares) {
  return (next) => (reducer, initialState) => {
    // 获取得到原始的store
    let store = next(reducer, initialState);
    let dispatch = store.dispatch;
    // 赋值一个空数组,用来存储后新的dispatch分裂函数
    let chain = [];

    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
     };

    chain = middlewares.map(middleware => middleware(middlewareAPI));
    // 将分裂的函数组合每次都会执行,即每次都执行这些中间件函数
    dispatch = compose(...chain)(store.dispatch);

    return {
      ...store,
      dispath
    }
  }
}

middleware运营规律

  1. 函数式编制程序思想设计
    因而函数式编程中的 currying。currying
    的middleware结构的益处有以下两点
  • 易串联:不断currying形成的middleware能够积累参数,再协作组合措施,很不难形成
    pipeline来处理数据流
  • 共享store:applyMiddleware执行进程中,store如故旧的,applyMiddleware达成后,全数的middleware内部得到的sotore都以风靡且同样的
  1. 给middleware分发store
    let newStore = applyMiddleware(mid1, mid2, mid3)(createStore)(reducer, null);

  2. 重组串联middleware
    dispatch = compose(...chain)(store.dispatch)
    Redux中compose的实现

function compose(...funs) {
  return arg => funs.reduceRight((composed, f) => f((composed), arg))
}

compose(…funcs) 再次来到的是二个匿名函数,个中 funcs 就是 chain
数组。当调用 reduceRight
时,依次从 funcs 数组的右端取1个函数 fx 拿来施行,fx 的参数 composed
正是前3遍 fx+1 执
行的结果,而首先次施行的 fn(n 代表 chain 的尺寸)的参数 arg 就是store.dispatch。

  1. 在 middleware 中调用 dispatch 会发生什么样

澳门葡京 18

Redux middleware流程图.png

借使这几个middleware凶狠的调用
store.dispatch(acton),就会形成有线循环了。
这里大家就用到了Redux Thunk。
Redux Thunk 会判断 action 是不是是函数。固然是,则实行action,不然继续传递 action 到下三个 middleware。

const tuhun = store => next => action => {
  typeof action === 'function' ?
    action(store.dispatch, store.getState) :
    next(action)
}

1. 扁平化 state

API 平常会再次来到嵌套的能源.那在 Flux 或基于 Redux
的架构中处理起来会尤其困难.大家推荐使用normalizr这类库将数据开始展览扁平化处理,尽只怕地扁平化state.

像这样:

const data = normalize(response, arrayOf(schema.user))

state = _.merge(state, data.entities)

(我们利用isomorphic-fetch与API进行通讯)

Pages

作为页面包车型地铁页面组件。基本上是把传递过来的 this.props.children
原原本本的突显出来。由于Ameblo是单页面应用,由此唯有2个页面组件。

5.3 Redux 异步流

2. 使用 immutable state

共享的可变性 state 是十恶不赦的根源. – Pete Hunt, React.js Conf 2016

澳门葡京 19

不可变对象是指在开创后不足再被修改的对象.

不可变对象足以让大家免受痛苦,并且通过引用级的比对检查来晋升渲染品质.比如在shouldComponentUpdate中:

shouldComponentUpdate {

// 不进行对象的深浅比较

return this.props.immutableFoo !== nexProps.immutableFoo

}

CSS Modules

CSS样式表使用 CSS
Modules
将CSS样式规则的作用范围严厉限制到了各类零部件内。各种样式规则的作用范围开始展览限定使得样式的改动和删除越发不难。因为Ameblo是由众几个人合伙开发成功,不必然每一种人都明白CSS,而且不免要时常对有的不知是何人哪天写的代码实行更改,在这一个时候将功用范围限制到零部件的CSS
Modules就公布其成效了。

CSS

/ components/organisms/SpNavigationBar.css / .Nav { background: #fff;
border-bottom: 1px solid #e3e5e4; display: flex; height: 40px; width:
100%; } .Logo { text-align: center; }

1
2
3
4
5
6
7
8
9
10
11
12
13
/ components/organisms/SpNavigationBar.css /
 
.Nav {
  background: #fff;
  border-bottom: 1px solid #e3e5e4;
  display: flex;
  height: 40px;
  width: 100%;
}
 
.Logo {
  text-align: center;
}

JavaScript

// components/organisms/SpNavigationBar.js import React from ‘react’;
import style from ‘./SpNavigationBar.css’ export class SpBlogInfo
extends React.Component { render() { return ( <nav
className={style.Nav}> <div className={style.Logo}> <img
alt=”Ameba” height=”24″ src=”logo.svg” width=”71″ /> </div>
<div …> </nav> ); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// components/organisms/SpNavigationBar.js
 
import React from ‘react’;
import style from ‘./SpNavigationBar.css’
 
export class SpBlogInfo extends React.Component {
  render() {
    return (
      <nav className={style.Nav}>
        <div className={style.Logo}>
          <img
            alt="Ameba"
            height="24"
            src="logo.svg"
            width="71"
           />
        </div>
        <div …>
      </nav>
    );
  }
}

逐条class的称谓经过webpack编译之后,变成像
SpNavigationBar__Nav___3g5MH 那样含hash值的大局唯一名称。

5.3.1 使用 middleware 简化异步请求

  1. redux-thunk
    咱俩再来看看 redux-thunk 的源代码:

function createThunkMiddleware(extraArgument) {
 return ({ dispatch, getState }) => next => action => {
   if (typeof action === 'function') {
     return action(dispatch, getState, extraArgument);
   }
   return next(action);
 };

效仿请求天气的异步请求,action的写法

function getWeather(url, params) {
   return (dispatch, action) {
    fetch(url, params)
      .then(result => {
        dispatch({
          type: 'GET_WEATHER_SUCCESS',
          payload: result,
        })
      })
      .catch(err => {
        dispatch({
          type: 'GET_WEATHER_ERROR',
          payload: err,
        })
      })
  }
}
  1. redux-promise

import { isFSA } from 'flux-standard-action';
function isPromise(val) {
 return val && typeof val.then === 'function';
}
export default function promiseMiddleware({ dispatch }) {
 return next => action => {
 if (!isFSA(action)) {
 return isPromise(action)
 ? action.then(dispatch)
 : next(action);
 }
 return isPromise(action.payload)
 ? action.payload.then(
 result => dispatch({ ...action, payload: result }),
 error => {
 dispatch({ ...action, payload: error, error: true });
 return Promise.reject(error);
 }
 )
 : next(action);
 };
} 

大家运用 ES7 的 async 和 await 语法,能够简化上述异步进程:

const fetchData = (url, params) => fetch(url, params);
async function getWeather(url, params) {
 const result = await fetchData(url, params);
 if (result.error) { 
220 第 5 章 深入 Redux 应用架构
 return {
 type: 'GET_WEATHER_ERROR',
 error: result.error,
 };
 }
 return {
 type: 'GET_WEATHER_SUCCESS',
 payload: result,
 };
} 
  1. redux-saga
    在 Redux 社区,还有一个处理异步流的老将,名为
    redux-saga。它与上述办法最直观的
    区别正是用 generator 替代了 promise,大家经过 Babel 能够很有益地支撑
    generator.

3. 怎样在JavaScript中贯彻不可变?

本办法是小心的写代码,示例代码如下,你须要在单元测试中通过deep-freeze-node来反复验证.

return {

…state,

foo

}

return arr1.concat

深信不疑自个儿,那是最醒指标例证了.

更简约也更自然的措施是行使 Immutable.js.

import { fromJS } from ‘immutable’

const state = fromJS({ bar: ‘biz’ })

const newState = foo.set(‘bar’, ‘baz’)

Immutable.js
分外之快,背后理念也极度美艳.哪怕您并不准备使用它,我也推荐阅读那个由Lee
Byron所制作的录像Immutable Data and React.它充足浓密的讲授了
Immutable.js 的行事原理.

ESLint, stylelint

此次的体系将ESLint和stylelint放到了总得的地点,就算叁个字母出错,整个项目也无从测试通过。指标就在于统一代码风格,节约代码审查时的劳动。具体规则分别继承自
eslint-config-airbnb

stylelint-config-standard
,对于一些必不可少的底细做了区区定制。因为规则较严,起首的时候恐怕有点困难。新成员进入项目组时,代码通过Lint测试便成了要通过的首先关。

 澳门葡京 20

z-code-review.png

防止了代码审查时对于那些细小写法挑错。被机器告知错误时,心情上会感觉稍好一些。

澳门葡京 21

z-ci-error.png

参与项目组之后,最初的那段时间里发出Lint错误是根本的事。

Redux 与 路由

咱俩得以通过 <Router> 、<Route> 那多个标签以及一星罗棋布属性
概念整个 React 应用的路由方案。

前端开发热加载,安装 webpack-dev-server

npm install -D webpack-dev-server

./node_modules/.bin/webpack-dev-server --hot --inline --content-base

在 mapStateToProps 中,大家从整棵 Redux 状态树中接纳了 state.home.list
分支作为当下
组件的 props,并将其命名为 list。那样,在 Home 组件中,就足以行使
this.props.list 来获取
到具备 PreviewListRedux 中定义的图景。
而在 mapDispatchToProps 中,咱们之前边提到的 HomeRedux.js 中引入了
listActions,并使
用 Redux 提供的工具函数将 listActions 中的每贰个 action
creator(近年来唯有1个)与 dispatch 进
行绑定,最后我们能够在 Home 组件中利用 this.props.listActions
来赢获得绑定之后的 action
creator。

4. Observables and Reactive 消除方案

假设您不希罕 Flux/Redux 也许只是想要越发reactive,不用失望!还有不少数据处理的方案供您采纳,那里有2个也许是你想要的库的简要列表:

cycle.js(“二个更清晰简洁的函数式 reactive JavaScript 框架”)

rx-flux(“Flux 架构与 Rxjs 的结合”)

redux-rx(“Redux的 Rxjs 工具集”)

mobservable(“可预测的多寡,reactive的成效,简洁的代码”)

少了一些全体 App 都有路由功效.要是你在浏览器中动用
React.js,你将会在挑选库的时候遭受选拔性难点.

小编们的选择是react-router, 来自非凡的 rackt 社区.Racket 给 React.js
爱好者们带来了过多高品质财富.

要利用
react-router,请查看它的文书档案.但更要紧的是:假如你选用Flux/Redux,大家引进您将路由
state 与 store 或全局 state 保持同步.

共同的路由 state 会援救您控制 Flux/Redux Actions
的路由行为,并能在组件中读取路由气象和参数.

Redux 用户可以通过redux-simple-router那个库轻松完成它.

唯有一小部分webpack用户知 App 代码能够分开成多少个 JavaScript 块.

require.ensure => {

const Profile = require(‘./Profile.js’)

this.setState({

currentComponent: Profile

})

})

那对于大型应用尤其有用,每回陈设之后用户浏览器不用下载那二个很少会选择到的代码,比如Profile页面.
更加多代码块将招致越多 HTTP 请求 – 不过使用 HTTP/2 多路复用就不曾难点.

构成chunk hashing,能够在代码更新之后优化缓存命中率.

下个版本的 react-router 将会对代码分隔做越来越多援救.

对此 react-router 的今后设计,能够去查看博客Ryan Florence:Welcome to
Future of Web Application Delivery.

洋德国人都在抱怨JSX,但第3要知道,它在 React 中是可选的.

JSX 在终极都会由此 Babel 被编译成 JavaScript.你能够一贯编写 JavaScript
来代表 JSX,然则在拍卖 HTML 的时候利用 JSX 会感觉越来越自然.

特意是对此不懂技术的人的话,他们只能够知道和改动须要的部分.

JSX 是一种与 XML 类似的 JavaScript 语法扩张.你能够因而二个大约的 JSX
语法转换器来转换它.—JSX in depth

要是你想明白越来越多 JSX 的内容,查看小说JSX Looks Like An Abomination – But
it’s Good for You

React 与 ES2014 的 Class 语法搭配的很好.

class HelloMessage extends React.Component {

render() {

return

Hello {this.props.name}

} }

相对于mixins,我们更爱好高阶组件,所以保留 createClass
更像是二个语法问题,而不是技术难题. 大家认为利用 createClass 或然React.Component 只是选取分裂而已,没有好坏之分.

若果您照旧没有检查
明白类型,那么你应当从二〇一六年发轫做起,那将为您节省大量的时光,相信小编.

MyComponent.propTypes = {

isLoading: PropTypes.bool.isRequired,

items: ImmutablePropTypes.listOf(

ImmutablePropTypes.contains({

name: PropTypes.string.isRequired,

})

).isRequired

}

本来,也足以采取react-immutable-proptypes验证 Immutable.js 所编写的属性.

脚下 mixins 将死,而且在 ES6 的 Class 不再扶助 mixins,大家应该寻找新方案.

PassData({ foo: ‘bar’ })(MyComponent)

简单的说来讲,从由原有组件创建3个新的零部件并且扩大它的行为.你能够在多样气象来使用它,比如鉴权:requireAuth({
role: ‘admin’
})(MyComponent)
(检查用户权限,如若未登录就跳转),或然将零件与 Flux/Redux
的 store 连通.


RisingStack,大家也欢快将数据拉取和控制类的逻辑分离到高阶组件中,以尽力而为地有限支撑view 层的不难.

保障测试的高代码覆盖率是开发周期中的主要一环.幸运的是,React.js
社区有诸多这么的库来扶持我们.

AirBnb
的enzyme是大家最热衷的零部件测试库之一.使用它的浅渲染性情能够对组件的逻辑和渲染结果开始展览测试,分外神奇.它今后还无法代表selenium测试,不过将前端测试提高到了二个新的高峰度.

it(‘simulates click events’, () => {

const onButtonClick = sinon.spy()

const wrapper = shallow

wrapper.find.simulate

expect(onButtonClick.calledOnce).to.be.true

})

看起来越发简短,不是吧?

您使用 chai 作为测试断言库嘛?相信你会欣赏chai-enyzime的!

CI, Build, Tesing

代码的
构建
、测试

部署
统一行使CI(公司内部接纳
CircleCI
)来成功。各类分支向GHE(Github
Enterprise)PUSH之后,根据各类分支发生差别的动作。那些流程的益处便是营造相关的拍卖不须求尤其职员来成功,而是统一写在
circle.ymlpackage.json (node环境下)里。

  • develop
    开发(下次揭橥)用分支。创设、测试之后自动铺排到staging环境中。
  • release/vX.X.X
    发表分支。由develop分支派生,构建、测试之后,自动陈设到semi(准生育)环境中。
  • hotfix/vX.X.X
    hotfix分支。由master分支派生,营造、测试之后,自动布署到semi(准生育)环境中。
  • deploy/${SERVER_NAME}
    安顿到支行所钦命的应和服务器上。主若是在付出环境中运用。
  • master
    这些分支塑造之后生成能够用来铺排到production(生产)环境的docker镜像。
  • 其它 开发用分支。仅举行创设和测试。

Redux测试

测试 reducer分外不难,它响应新到来的 actions 然后将原本的 state
转换为新的 state:

it(‘should set token’, () => {

const nextState = reducer(undefined, {

type: USER_SET_TOKEN,

token: ‘my-token’

})

// immutable.js state output

expect(nextState.toJS.to.be.eql({

token: ‘my-token’

})

})

测试 actions也非常的粗略,不过异步 actions 就不太一样了.对于测试异步的
actions 来说,大家引进应用redux-mock-store,万分有帮助.

it(‘should dispatch action’, => {

const getState = {}

const action = { type: ‘ADD_TODO’ }

const expectedActions = [action]

const store = mockStore(getState, expectedActions, done)

store.dispatch

})

关于更透彻的redux测试,请参见官方文书档案.

虽说 React.js 并不借助代码打包工具就能够干活得很好,但大家还是引进应用
Webpack 可能 Browserify 来表达 npm 的能力.Npm 有过多 React.js
的包,能够支持您优雅地保管信赖.

(请不要遗忘复用你自身的零件,那是优化代码的绝佳格局.)

那本身不是贰个 React 相关的标题,可是半数以上人都在包装他们的 React
应用,所以自个儿有必不可少在这边提一下.

当你打包源代码的时候,要随时警惕打包后文件的大小.想要将其控制在十分的小体积,你须求考虑怎样怎么样require/import 信赖.

翻开上面包车型大巴代码片段,这二种艺术得以对出口大小会生出重庆大学影响:

import { concat, sortBy, map, sample } from ‘lodash’

// vs.

import concat from ‘lodash/concat’;

import sortBy from ‘lodash/sortBy’;

import map from ‘lodash/map’;

import sample from ‘lodash/sample’;

翻开Reduce Your bundle.js File Size By Doing This One
Thing,以博取更加多新闻.

我们也爱不释手将代码分离到至少 vendors.js 和 app.js 八个文本,因为 vendors
相对于大家的代码库来说更新频率低很多.

对出口文件举行 hash 命名(WebPack中的chunk
hash),并选拔长缓存,大家能够显著地缩减访问用户供给下载的代码.结合代码懒加载,优化功能特别显明.

一经您还不太熟识 Webpack,能够查看那本优良的React webpack cookbook.

设若你曾选用过hot
reload编写单页面应用,当您在处理有个别与气象相关的工作时,恐怕你就会清楚当您在编辑器中式点心击保存,整个页面就重新加载了是多么令人讨厌.你须求稳步点击操作到刚刚的环节,然后在这么的重新中奔溃.

由此 React,在重载组件的还要保证组件状态已经成为或者,从此不再忧伤!

至于怎么着搭建hot reload,可参看react-transform-boilerplate.

前方有关联过,我们在 React.js 组件中利用 JSX,然后使用 Babel.js 实行编译.

澳门葡京 22

Babel 的力量远不止这几个,它也足以让我们前几日就能够给浏览器编写 ES6/ES二零一六代码.在RisingStack,我们在服务器端和客户端都使用了ES二零一四的特征,ES二〇一四业已足以在新式的LTS
Node.js版本中选用了.

想必你早就给您的 JavaScript 代码制定了代码规范,不过你精通也有用于 React
的代码规范了呢?大家建议你挑选2个代码规范,然后照着它说的来做.

在 RisingStack,大家也将 linters 强制运转在 CI 系统上,git
push
亦然.能够尝试pre-push或者pre-commit.

大家采用正式的 JavaScript
代码风格,并利用eslint-plugin-react来检查React.js代码.

(是的,大家曾经不复行使分号了)

相对而言 GraphQL 和 Relay 还属于新技巧,在
RisingStack,大家还未曾在成品环境中应用它们,但保持关怀.

我们写过一个 Relay 的 MongoDB OLANDM 库,叫做 graffiti,能够动用你已部分
mongoose models 来创设 GraphQL server.

假定您想要学习这么些新技巧,我们建议你能够找来玩一玩.

有些杰出的技巧和库其实跟React都差不离没什么,但要关切社区的别的人都在做些什么.二零一五这一年,React社区被Elm
架构启发了很多.

假使您精晓其余在二零一四年必备的 React.js 工具,请在上面给大家留言!

*最初的文章我:PeterMarton,RisingStack技术首席营业官初稿链接:

Docker

本次系统重构,也对node.js应用进行docker化构建。此次重构的是前者系统,大家希望能够在细微改良之后随即开始展览布局。docker化之后,一旦将镜像营造完毕,能够不受node模块版本的左右进行配置,回滚也很不难。

其它,node.js自个儿宣布分外频仍,假如放置不管,不知不觉之间系统就成古董了。docker化之后,能够不受各主机环境的熏陶自由的开始展览升级。

更要紧的是,设置docker容器数是相比较易于的,那对于系统横向扩大容积以及对服务器配置作优化时也要命造福。

提高界面设计、用户体验(2014年版Ameblo)

不再「咯噔」

系统重构以前的Ameblo由于存在有的惊人没有永恒的模块,出现了「咯噔」现象。那种「咯噔」会招致误点击以及页面包车型大巴重绘,十三分令人高烧。而此模块中度稳定也做为本次系统重构的UI设计的前提。特别是页面间导航作为特别重点的因素,咱们经过努力使得在页面跳转时老是都得以触击到同一的岗位。

澳门葡京 23

z-gatan.gif

「咯噔」的二个例证。点击[次のページ](下一页)的时候,额外的成分由于加载缓慢,造成误点击。

 澳门葡京 24

z-paging-fixed.gif

系统重构之后,成分的职分被固定下来,减轻了页面跳转时给用户心境上带来的承担。

智能手机时期的用户界面

2015年在活动环境下使用的用户大概都在应用智能手机。在智能手提式有线话机上,由于种种平台的提供者制定了各自不一样的用户界面规范,用户已经不乏先例并适应了用户界面。相比较之下,虽说浏览器上的正规化分外少,然则要是和当今风行的界面差异太大的话,就会变得很难用。

Ameblo的无绳话机版在二〇〇九年开始展览改版之后,自然对有个别细节进行了改良,不过出于并未太大的更改,所以今后总的来说众多地点已经给人一种很旧的回想。用户在浏览的时候,对于界面并不区分是原生应用照旧浏览器,由此制作出适应当前一代这一个平台的用户界面显得越来越重庆大学。那里介绍一下本次重构中,对于界面包车型大巴一部分升官。

 澳门葡京 25

z-update-design.png

剧情占据界面上横向整个空间。贰零壹零年的时候,一般选取推特(Twitter)倡导的「将逐一模块圈起来的宏图」。

澳门葡京 26

z-searchbar.gif

追加了导航栏,把导航相关操作集中停放在此间。

可访问性

本次系统重构正值可访问性成为热点话题的时候。仔细的为HTML扩充一定标签属生就足以使任何系列丰盛可访问。首先在HTML标签属性添加上时要用心商讨。对于标题、
img 等足够适当的 alt 属性,对于可点击的要素一定要动用 a button
等可点击的标签。若是能自行对可访问性举办验证就再好然而了,ESlint的
jsx-a11y
插件能够支持完结那点。

在类型进展的时候,正好公司内开展了三遍可访问性的读书活动( Designing
Web
Accessibility
的作者太田先生和伊原来的小说人也列席了此次活动),在本次活动上也尝试了Ameblo到最近截至没有在意过的语音朗读器。当时用语音朗读器在Ameblo上进行朗读时,有几处有题指标地点,使用
WAI-ARIA
对这几处加以校勘(与 data-* 相同,JSX也支持 aria-* 属性)。

这里
的PPT中有详细的介绍,欢迎观察(日文)。

结果

OK,上边介绍了本次重构带来的诸多变型,那么结果什么呢?

先是是性质相关指标(测试的U凯雷德L都以Ameblo中单一页面请求资源最多,显示速度最慢的页面)。

闭塞渲染的财富(Critical Blocking Resources)

 澳门葡京 27

z-speed-blocking.png

卡住渲染的资源数 减少了75%
!JavaScript全体变为了异步读取与履行。CSS样式因为运行的原故,维持了重构前的图景。

情节请求(Content Requests)

 澳门葡京 28

z-speed-requests.png

财富请求数 减少了58.04%
!由于使用了推迟加载,首屏展现只加载须要的财富,与此同时对文本举办适当的盘整,并删除了有的不须求的模块,最后实现了这几个意况。

渲染(Rendering)

 澳门葡京 29

z-speed-rendering.png

渲染速度做为前端的要害质量指标,本次 提升了44.68%

页面加载时间(Page Load Time)

 澳门葡京 30

z-speed-pageload.png

页面加载时间 缩短了40.5 !别的,后端的回来时间也保证在了0.2ms ~
0.3ms之间。

接下去介绍一下唇齿相依的工作指标。

网页浏览量(Pageviews)

 澳门葡京 31

z-ga-pv.png

因为2015年十二月有一人盛名的博客主成为了热点话题,所以这些指标内富含特殊情形。网页浏览量提高了57.15%。假设将热点话题所推动的数值除去后,实际上惟有由系统重构所带来的晋级在十分一到五分一里面。

老是对话浏览页数 (Pages / Session)

 澳门葡京 32

z-ga-pps.png

Pages / Session是指在单个会话内页面包车型客车浏览数,这些指标 提升了35.54
。SPA革新了页面间跳转的速度,获取了为之侧目标效劳。

跳出率(Bounce Rate)

 澳门葡京 33

z-ga-bounce.png

跳出率指在一个会话内,仅看了八个页面的比值,那几个指标 改善了44.44%
。大家觉得这是由于首屏和页面跳转速度的革新,用户界面升级(更便于了解的分页),「咯噔」革新所推动的结果。

而是还设有诸多改善的后路,任何三个目标都能够重复升级。大家想以此标志
网站质量的晋级会拉动业务指标的晋升

上述数量是在以下标准下获得的:

  • 页面品质
    • 使用
      SpeedCurve
    • 测试的URL是
      http://s.ameblo.jp/ebizo-ichikawa/entry-12152370365.html
    • 浏览器钦命为 Chrome, 53.0.2785.143移动端模拟格局
    • 互连网钦定为4G仿照格局(14.6 Mbps,Upload 7.8Mbps,Latency 53ms)
  • 事务目标
    • 使用 Google
      Analytics
    • 获取自 s.ameblo.jp 内的总体数据
    • 对二〇一六年六月和二零一四年十二月的数值实行比较

写在终极

这一次系统重构的出发点是对技术的挑衅,结果获得了美好的用户举报,并对业务作出了孝敬,大家自身也倍感非凡有价值,获得了高大的引以自豪。采纳新式迎合时流的技能自然提高服务的质量,也使得那种知识在信用合作社在生根。在此,对不久导入Isomorphic
JavaScript,并向东瀛境内推广的同事
@ahomu
表示多谢! 

作者介绍:

笔者:原 一成(Hara Kazunari),二零零六年到场东瀛CyberAgent集团。担任Ameblo
二〇一四运动前端改版项目总老董。著有《GitHubの教科書》,《CSS3逆引きデザインレシピ》,《フロントエンドエンジニア育成読本》。

翻译:侯 斌(Hou
Bin),二〇一五年入职东瀛CyberAgent公司。现任Ameblo前端开发。在本次Ameblo
二零一六移动前端改版项目中出任重(Ren Zhong)要支出,负责基础架构和技能选型以及首要模块开发等。

1 赞 收藏
评论

澳门葡京 34

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website