React/Redux营造的同构Web应用

2018/07/30 · CSS ·
React,
Redux

原稿出处: 原 一成(Hara
Kazunari)   译文出处:侯斌   

世家好,作者是原百分之10(@herablog),近年来在CyberAgent首要担当前端开荒。

Ameblo(注: Ameba博客,Ameba
Blog,简称Ameblo)于2016年十月,将前端部分由原来的Java架构的应用,重构成为以node.js、React为根基的Web应用。那篇小说介绍了本次重构的起因、目的、系统规划以及尾声落成的结果。

新类别公布后,立时就有人注意到了那么些变化。

 金沙注册送58 1

twitter_msg.png

一、初入React世界

翻译按:方今React(web/native)仍旧如日方升,相信大家都尝试,大家团队也初始在React领域具有尝试.

二零一四年应当是 React
渐渐走向成熟的一年,让我们1道来探视国外的开拓者们都计算了何等”最棒实行”.

React Redux Sever Rendering(Isomorphic JavaScript)

金沙注册送58 2

React Redux Sever Rendering(Isomorphic)入门

系统重构的缘起

200四年起,Ameblo成为了东瀛境内最大局面包车型大巴博客服务。不过随着系统规模的增加,以及好些个有关人口持续充实种种模块、页面教导链接等,最后使得页面展现缓慢、对网页浏览量(PV)产生了要命惨重的熏陶。并且页面呈现速度方面,绝大繁多是前者的主题材料,并非是后端的主题材料。

基于以上这一个主题材料,大家决定以增长页面显示速度为机要对象,对系统实行透顶重构。与此同时后端系统也在进行重构,将过去的多少部分进行API化更动。此时正是2个将All-in-one的特大型Java应用举行适宜分割的绝佳良机。

1.2 JSX语法

  • class 属性修改为className

  • for 属性修改为 htmFor

  • 拓展属性
    选用ES陆 rest/spread 本性进步功用

const data = { name: 'foo', value : 'bar' };
// const component = <Component name={data.name} value={data.value} />;
const component = <Component {...data}>
  • 自定义HTML 属性
    万1要使用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
是指同1的意趣呢?针对那么些议题网路上某些开采者提议了和谐的观念:
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前缀。对于组件来讲,定义三个集结的class前缀,对体制与互为分离起了相当主要的遵循。
  • 用funtion prop 与父组件通讯
  • propTypes
    用于标准 props 的门类与必需的意况

20壹五年 React
在世上都有众多关于新的翻新和开拓者大会的钻探.关于二零一八年的主要事件,请参见
React in 20一5.

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
只管理大致的是否出示或是成分迭代的情景,如下图所示:

金沙注册送58 3

React Redux Sever Rendering(Isomorphic)入门

可是前端渲染 template 固然有它的收益但也蒙受有的题目蕴含功用、SEO
等议题。此时大家就从头思量 Isomorphic JavaScript
的恐怕性:为什麽我们不能够上下端都使用 JavaScript 以至是 React?

金沙注册送58 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 当做参数并对各种请求建构3个簇新的 Redux store 实体
  2. 选用性地 dispatch 一些 action
  3. 把 state 从 store 取出来
  4. 把 state 一同传到客户端

接下去我们就先导入手实作三个轻巧的 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 方法。

金沙注册送58 5

React生命周期全部流程图

那么,201陆年最佳玩的标题来了:大家应有如何开辟2个 App, 有怎么样推荐的库?

专案成果截图

金沙注册送58 6

image

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

陈年的Web应用是将数据以HTML的样式再次来到,二零一九年并不曾什么样难点。不过,随着剧情的充实,体验的丰富化,以及设备的各个化,使得前端所占的比例进一步大。从前要费用2个好的Web应用,借使要高质量,就势必不要将左右端分隔开分离。当年以那几个须要支付的系统,在经历了拾年过后,已经远远不能够适应当前的生态系统。

「跟上脚下生态系统」,以此来营造系统会拉动巨额的裨益。因为作为着力的生态系统,其付出相当活跃,每一日都会有巨大新的idea。因此新颖的本事和机能更便于被接受,同时达成高性能也更为轻巧。同时,那一个「新」对于身强力壮的才具新人也尤其关键。仅知道旧规范旧本领的伯伯对于2个美好的集体来讲是未曾前途的(自觉本身膝盖也中了一箭)。

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
的开辟者,小编对此主题材料有谈得来的答案和极品施行,但您或许不必然完全同意.作者对你的主见和见地很有意思味,请留言以便商讨.

金沙注册送58 ,Server Rendering

获取数据能够调用 action,routes 在劳动器端的拍卖参考 react-router server
rendering,在服务器端用3个 match 方法将得到的 request url
相配到大家前面定义的 routes,解析成和客户端1致的 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');
});

最棒施行,Redux创设的同构Web应用。劳务器端渲染部分能够直接通过共用客户端 store.dispatch(action) 来统1获取
Store 数据。此外注意 renderFullPage 生成的页面 HTML 在 React 组件 mount
的片段(<div id=”root”>),前后端的 HTML 结构应当是如出1辙的。然后要把
store 的景色树写入一个全局变量(INITIAL_STATE),这样客户端早先化
render 的时候能够校验服务器生成的 HTML
结构,并且一路到开端化状态,然后全数页面被客户端接管。

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

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

Ameblo的手机版在20拾年经历了一回改版之后,就多数并没有太大的扭转。那当中繁多用户都已经司空眼惯了原生应用的统一筹划和体验。那么些体系也是为着不令人感觉很土很难用,达到顺应时期的201陆年版分界面设计和用户体验。

OK,接下去让笔者具体详尽聊聊。

二、漫谈React

二.一 事件系统
一、事件委派
React没有把事件直接绑定在不务空名的节点上,而是绑定在最外层,使用八个统1的轩然大波监听器,那一个事件监听器上保持了一个映射来保存全体组件内部的风云监听和管理函数。
React中动用DOM原滋事件时,要在组件卸载时手动1处,不然很恐怕出现内部存款和储蓄器泄漏的标题。

金沙注册送58 7

页面加载速度的改正

2.2 表单

3.select组件
单选和多选两种。在JSX语法中,能够经过设置select标签的
multiple={true}来兑现3个多选下拉列表。

万一你刚刚初步接触React.js,能够查阅大家的React.js 教程,恐怕 Pete Hunt
的React howto.

改善点

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

  • 服务器响应速度十分的快
  • HTML文书档案十分的大(页面全部因素都包涵当中)
  • 卡住页面渲染的能源(JavaScript、Stylesheet)较多
  • 能源读取的次数过多,容量过大

据他们说这么些规定了下边这几项基本方针:

  • 为了不致于下降服务器响应速度,对代码实行优化,缓存等
  • 尽或然缩小HTML文书档案大小
  • JavaScript异步地加载与实施
  • 前期显示页面时,仅仅加载所需的要求财富

二.二.二 受控组件

每当表单的图景发生变化时,都会被写入到零部件的state中,那种组件在React中被称之为受控组件(controlled
component)。
受控组件更新state的流程:
(一)能够经过在起来 state 中安装表单的暗中同意值
(二)每当表单的值爆发变化时,调用onChange事件管理器
(3)事件管理器通过合成事件目的e获得改变后的图景,并立异state
(四)setState触发视图的双重渲染,落成表单组件值得更新

在 React.js 应用中管理多少卓殊轻易,但也洋溢挑衅.

SSR还是SPA

多年来相比较于增添到收藏夹中,用户更倾向于经过搜索结果、推特、照片墙等应酬媒体上的享用链接张开博客页面。谷歌(Google)和推文(Tweet)的AMP,
Facebook的Instant
Article标明第一页的显现速度大幅震慑到用户知足度。

其它,从谷歌Analytics等日志记录中询问到在篇章列表页面和前后小说间展开跳转的用户也大多。那也许是因为博客作为个体媒体,当某一用户看到1篇不错的稿子,卓殊感兴趣的时候,他也还要想看1看同一博客内的别样文章。也正是说,博客那种服务
先是页神速加载与页面间急速跳转同等首要

于是,为了让相互都能公布最好质量,大家决定在第三页使用服务器端渲染(Server-side
Rendering, SSR),从第1页起选用单页面应用(Single Page Application,
SPA)。那样1来,既能确定保障率先页的显得速度和机械和工具可读性(Machine-Readability)(含SEO),又能获得SPA带来的高效显示速度。

BTW,对于近日的架构,由于服务器和客户端选取一样的代码,全体实行SS卡宴或是全体进行SPA也是唯恐的。最近曾经落到实处就算在不能运转JavaScript的景况中,也得以健康通过SS途锐来浏览。能够预知今后等到ServiceWorker广泛之后,伊始页面将更加高速化,而且能够兑现离线浏览。

金沙注册送58 8

z-ssrspa.png

在此之前的类别完全使用SS汉兰达,而未来的体系从第叁页起变为SPA。

 金沙注册送58 9

z-spa-speed.gif

SPA的吸引力在于呈现速度之快。因为唯有通过API获取所需的不可缺少数据,所以速度十分的快!

二.二.三 非受控组件

假诺3个表单组件未有 value props(单选按键和复选框对应的是 checked
prop)时,就能够称作非受控组件。相应地,也足以运用 defaultValue 和
defaultChecked prop来表示组件的暗中认可状态。平时,要求通过为其增多ref
prop来访问渲染后的平底DOM成分。

那是因为你能够动用四种格局将属性数据传递给 React
组件,从而创设出渲染树,但您应当怎么着翻新视图却不是举世瞩目标.

延迟加载

作者们运用SS奥迪Q7+SPA的方法来优化页面间跳转那种横向移动的进程,并且选取延缓加载来创新页面包车型客车纵向移动速度。1开端要显现的剧情以及导航,还有博客小说等最早展现,在那些内容之下的次要内容随着页面包车型地铁轮转逐步显现。这样一来,主要的剧情不会受页面上边内容的震慑而越来越快的显得出来。对于那1个想神速读小说的用户来讲,既不增添用户体验上的压力,又能完整的提供页面下方的始末。

 金沙注册送58 10

z-lazyload.png

事先的系统因为将页面内的全体内容都置于HTML文书档案里,所以使得HTML文书档案体积十分的大。而现行反革命的系统,仅仅将重大内容放到HTML里重回,裁减了HTML的体量和数量请求的轻重缓急。

2.三 样式管理

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',
  }]
}

20一⑤一齐来便出生了过多两样 Flux
库,随后涌现出出更多具备更加强效用和越来越响应式解决方案.

HTML缓存

博客小说是静态文档,对于特定U奇骏L的伏乞会回到固定的始末,由此格外适合实行缓存。缓存使得服务器管理内容减弱,在拉长页面响应速度的还要缓慢解决了服务器的担任。大家将不改变的剧情(小说等)生成的HTML实行缓存再次回到,对于由于变化的内容能过JavaScript、CSS等开始展览操作(例如展现、隐藏等)。

 金沙注册送58 11

z-newrelic-entrylist.png

那张图彰显了201陆年九月最终一周New
relic上的总括数据。小说列表页面包车型大巴HTML的响应时间基本在50ms以下。

 金沙注册送58 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;
}

二.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。

让我们一同来探望:

系统的今世化(搭乘生态系统)

二.5.二 高阶组件

落到实处高阶组件的办法有如下二种

  • 品质代理(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的例外

金沙注册送58 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」。这样一来,无论是何人都得以轻巧的收获到相应的文书档案等音讯,同时此外的团体和供销合作社借使要参与到项目中来也能不慢的左手。然则在真的进展付出的时候,一些细节完毕上因为美妙绝伦的由来存在部分不等的气象,可是在特大程度上维持了逐1模块的独立性。最后系统的大概构成如下图所示:

 金沙注册送58 14

z-bigpicture.png

(某些地点做了归纳)

2.六 组件质量优化

  1. 纯函数
  • 加以一样的输入,它总能重返相同的输出
  • 进度没有副作用
  • 未曾额外的景观正视
  1. PureRender
    为重复达成了 shouldComponentUpdate 生命周期方法,让眼前传播的
    props
    和 state 与事先的作浅相比,假如回到 false,那么组件就不会实施 render
    方法。

  2. react-addons-perf
    量化所做的质量优化职能
    Perf.start()
    Perf.stop()

Flux 提供了1种十二分明显的点子来囤积和换代App 全局 state(译者注:对应
react 中的 state),并在急需的时候触发渲染.

React with Redux

应用React和React进行付出的的时候,繁多地点能够用 纯函数
的款型开始展览重组。纯函数是指特定的参数总是回到特定的结果,不会对函数以外的限量变成污染。使用纯函数进行付出能够保障各种管理模块最小化,不用顾忌会无意改换引用对象的值。那样壹来,12分推向大规模开荒以及在一样客户端中维系多少个状态。

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

那是3个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识别到有个别子组件增或删时,则调用它对应地生命周期函数。大家得以再生命周期函数中达成动画逻辑。
假定每三个子零部件的动作效果一样,那么每三个子零部件能够壹并用三个生命周期函数。因此React
Transition 提供了 childFactory
配置,让用户自定义叁个封装子组件的工厂方法,为子组件加上相应地生命周期函数。
React Transition提供的生命周期

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

componentWillxxx 只要在
componentWillReceiveProps中对this.props.childrennextProps.children做贰个相比较就能够了。componentDidxxx可以在componentWillxxx提供3个回调函数,用来实践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
201陆年版基本上完全是用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 客户端入口(客户端专用)

 金沙注册送58 15

z-isomorphic.png

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

金沙注册送58 16

z-code-stats.png

运用Github的语言计算能够见到
,JavaScript占了总体项目的94.0%,大约全体都是由JavaScript写成的。

深远Redux 应用框架

咱俩不引入使用 Flux 来管理路由相关的数码,比如/items/:itemId.获取路由数据并蕴藏在组件的 state
之中.那种气象下,它会在组件销毁时手拉手被销毁.

原子设计(Atomic Design)

对此组件的规划,我们运用了
原子设计
思想。其实项目并从未一上马就利用原子设计,而是基于 Presentational and
Container
Components
,对 containercomponent
进行了两层划分。然则Ameblo中的组件实在是太多,很轻易导致职务不引人注目标状态,由此最终使用了原子设计思想。项目标骨子里运用中,采纳了以下的平整。

 金沙注册送58 17

z-atomic-design.png

5.1 Redux简介

假让你想精通越多关于 Flux 的新闻,提议阅读The 埃沃lution of Flux
Frameworks.

Atoms

零件的微小单位,例如Icon、Button等。原则上不具备状态,从父组件中获取传递过来的props,并回到HTML。

5.一.贰Redux3大标准

  1. 纯净数据源
  2. 境况是只读地
  3. 情形修改均由纯函数完毕

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

Molecules

以复用为前提的机件,比如List、Modal、User
thunmbnail等。原则上不具有状态,从父组件中收获传递过来的props,并再次来到HTML。

5.1.3 Redux 核心API

Redux核心是三个store,那几个store是由createStore(reducers[,initialState])方法生成

通过createStore办法成立的store是3个目的,包罗多少个办法

  • getState(): 获取store中当前的意况
  • dispatch(action):分发叁个action,并赶回这几个action,那是唯壹能退换store中数据的方法
  • subscribe(listener): 注册多个监听者,它在store暴发改动时被调用
  • replaceReducer(nextReducer):
    更新当前store里的reducer,一般只会在付出情势中调用该办法。

假令你以为需求 Flux 可能相似的化解方案,你应该精通一下 redux,并学习Dan
Abramov的Getting started with redux,那能够高效增进你的费用技术.

Organisms

页面上不小的一块组件,比如Header,Entry,Navi等。对于那1层的零件,可以在内部开始展览数据得到管理,以及选择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举办绑定,2个是
React组件<Provider />,三个是 connect(),<Provider />接受2个store
作为props,它是任何Redux应用的顶层组件,而connect()提供了在方方面面React应用的私自己塑造件中获得store中数据的效益。

Redux 一而再并改良了 Flux 的考虑,学习了 Elm ,避开了 Flux
的复杂度(译者注:Elm是1门函数式编制程序语言).

Template

逐1请求路线(U途睿欧L)所对应的零件。其职分是将所需的构件从Organisms中import过来,以自然的逐条和格式整合在1块儿。

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) 再次来到的是3个无名函数,个中 funcs 正是 chain
数组。当调用 reduceRight
时,依次从 funcs 数组的右端取多个函数 fx 拿来举办,fx 的参数 composed
就是前1回 fx+一 执
行的结果,而首先次施行的 fn(n 代表 chain 的尺寸)的参数 arg 正是store.dispatch。

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

金沙注册送58 18

Redux middleware流程图.png

若是这一个middleware无情的调用
store.dispatch(acton),就能造成有线循环了。
此间大家就用到了Redux Thunk。
Redux Thunk 会判定 action 是不是是函数。借使是,则推行action,不然继续传递 action 到下2个 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是单页面应用,由此唯有3个页面组件。

5.3 Redux 异步流

2. 使用 immutable state

共享的可变性 state 是罪恶的根源. – Pete Hunt, React.js Conf 20一5

金沙注册送58 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值的大局唯一名称。

伍.三.一 使用 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);
 };
} 

大家应用 ES七 的 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.

三. 怎么在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测试便成了要透过的率先关。

 金沙注册送58 20

z-code-review.png

严防了代码审查时对于那一个一线写法挑错。被机器告知错误时,心思上会感觉稍好有的。

金沙注册送58 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(近日唯有三个)与 dispatch 进
行绑定,最终大家能够在 Home 组件中选择 this.props.listActions
来收获到绑定之后的 action
creator。

4. Observables and Reactive 减轻方案

假设您不喜欢 Flux/Redux 或许只是想要越发reactive,不用失望!还有为数不少数量管理的方案供你挑选,这里有3个大概是您想要的库的简约列表:

cycle.js(“3个更清晰简洁的函数式 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/二 多路复用就从未有过难点.

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

下个本子的 react-router 将会对代码分隔做更多帮忙.

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

过多人都在抱怨JSX,但第贰要精通,它在 React 中是可选的.

JSX 在终极都会因而 Babel 被编写翻译成 JavaScript.你能够一直编写 JavaScript
来替代 JSX,不过在拍卖 HTML 的时候利用 JSX 会认为越来越自然.

特意是对于不懂手艺的人来讲,他们只好够掌握和修改须要的部分.

JSX 是1种与 XML 类似的 JavaScript 语法增加.你能够透过3个简便的 JSX
语法调换器来改变它.—JSX in depth

壹经您想领会更加多 JSX 的内容,查看小说JSX Looks Like An Abomination – But
it’s Good for You

React 与 ES20一伍 的 Class 语法搭配的很好.

class HelloMessage extends React.Component {

render() {

return

Hello {this.props.name}

} }

相持于mixins,我们更欣赏高阶组件,所以保留 createClass
更像是2个语法难点,而不是本事难点. 大家以为接纳 createClass 只怕React.Component 只是挑选不相同而已,未有好坏之分.

万壹你照旧未有检查
领悟类型,那么您应当从201陆年上马做起,那将为你节省大批量的时辰,相信作者.

MyComponent.propTypes = {

isLoading: PropTypes.bool.isRequired,

items: ImmutablePropTypes.listOf(

ImmutablePropTypes.contains({

name: PropTypes.string.isRequired,

})

).isRequired

}

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

现阶段 mixins 将死,而且在 ES陆 的 Class 不再帮忙 mixins,大家理应搜索新方案.

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

简轻松单来讲,从由原有组件创制三个新的零部件并且扩充它的行为.你能够在两种风貌来使用它,比如鉴权:requireAuth({
role: ‘admin’
})(MyComponent)
(检查用户权限,倘若未登6就跳转),只怕将零件与 Flux/Redux
的 store 连通.


RisingStack,大家也喜欢将数据拉取和调节类的逻辑分离到高阶组件中,以尽量地保全
view 层的轻便.

管教测试的高代码覆盖率是开拓周期中的重要一环.幸运的是,React.js
社区有广大如此的库来赞助大家.

AirBnb
的enzyme是我们最重视的零部件测试库之一.使用它的浅渲染天性能够对组件的逻辑和渲染结果开始展览测试,分外玄妙.它以后还无法取代selenium测试,可是将前端测试提高到了2个新的高峰度.

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 进行编写翻译.

金沙注册送58 22

Babel 的技能远不止那一个,它也足以让我们今日就足以给浏览器编写 ES6/ES201五代码.在RisingStack,我们在劳动器端和客户端都使用了ES20一5的特征,ES20一五曾经足以在新型的LTS
Node.js版本中动用了.

只怕你早就给您的 JavaScript 代码制定了代码标准,然而你知道也有用于 React
的代码标准了呢?大家建议你挑选一个代码标准,然后照着它说的来做.

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

我们使用标准的 JavaScript
代码风格,并采纳eslint-plugin-react来检查React.js代码.

(是的,大家早已不再动用分号了)

相持来讲 GraphQL 和 Relay 还属于新技巧,在
RisingStack,我们还尚无在产品境况中选用它们,但保持关注.

作者们写过1个 Relay 的 MongoDB O奥迪Q3M 库,叫做 graffiti,能够选拔你已某个mongoose models 来创建 GraphQL server.

一经你想要学习这几个新技艺,我们提出您能够找来玩1玩.

些微卓越的才具和库其实跟React都大约没什么,但要关怀社区的其余人都在做些什么.201五这年,React社区被Elm
架构启发了许多.

假定您理解其他在201陆年少不了的 React.js 工具,请在上面给大家留言!

*原著小编:PeterMarton,RisingStack本领组长原来的小说链接:

Docker

本次系统重构,也对node.js应用进行docker化打造。本次重构的是前者系统,大家期望能够在一线更正之后马上进行配置。docker化之后,1旦将镜像创设完毕,能够不受node模块版本的左右张开安顿,回滚也很轻巧。

其它,node.js本人发表尤其频仍,要是放置不管,无声无息之间系统就成古董了。docker化之后,能够不受各主机情形的熏陶自由的进展升级。

更首要的是,设置docker容器数是相比易于的,那对于系统横向扩大体积以及对服务器配置作优化时也充足便于。

晋升分界面设计、用户体验(201陆年版Ameblo)

不再「咯噔」

系统重构以前的Ameblo由于存在一些惊人未有定点的模块,出现了「咯噔」现象。那种「咯噔」会导致误点击以及页面包车型大巴重绘,十三分令人胸口痛。而此模块中度稳固也做为本次系统重构的UI设计的前提。尤其是页面间导航作为越发生死攸关的要素,我们经过努力使得在页面跳转时每一趟都足以触击到同1的地点。

金沙注册送58 23

z-gatan.gif

「咯噔」的一个事例。点击[次のページ](下1页)的时候,额外的因素由于加载缓慢,产生误点击。

 金沙注册送58 24

z-paging-fixed.gif

系统重构之后,成分的职位被固定下来,缓慢化解了页面跳转时给用户情感上带来的担当。

智能手提式有线电话机时期的用户分界面

二〇一六年在运动情状下利用的用户差不多都在利用智能手提式有线话机。在智能手提式有线话机上,由于各类平台的提供者制定了独家不一致的用户分界面标准,用户已经习贯并适应了用户分界面。相比之下,虽说浏览器上的专门的工作非凡少,不过1旦和前日盛行的分界面差异太大的话,就能够变得很难用。

Ameblo的手提式有线电话机版在20十年举行改版之后,自然对有的细节实行了改正,可是由于尚未太大的退换,所以以后看来众多地方早就给人壹种很旧的影像。用户在浏览的时候,对于分界面并不区分是原生应用依旧浏览器,因此制作出适应当前年代这么些平台的用户界面显示尤为重大。这里介绍一下本次重构中,对于分界面包车型大巴部分升任。

 金沙注册送58 25

z-update-design.png

内容私吞分界面上横向整个空间。2010年的时候,一般选择Twitter倡导的「将逐一模块圈起来的宏图」。

金沙注册送58 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,下边介绍了本次重构带来的浩大调换,那么结果怎么着呢?

首先是性质相关目的(测试的UBMWX3L都以Ameblo中单1页面请求财富最多,体现速度最慢的页面)。

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

 金沙注册送58 27

z-speed-blocking.png

闭塞渲染的能源数 减少了75%
!JavaScript全体成为了异步读取与实行。CSS样式因为运营的原由,维持了重构前的地方。

剧情请求(Content Requests)

 金沙注册送58 28

z-speed-requests.png

财富请求数 减少了58.04%
!由于选择了推迟加载,首屏展现只加载供给的能源,与此同时对文本进行适宜的整理,并删除了壹部分不供给的模块,最终落得了这么些情状。

渲染(Rendering)

 金沙注册送58 29

z-speed-rendering.png

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

页面加载时间(Page Load Time)

 金沙注册送58 30

z-speed-pageload.png

页面加载时间 缩短了40.5 !其它,后端的回来时间也维持在了0.2ms ~
0.3ms之间。

接下去介绍一下皮之不存毛将焉附的事体目标。

网页浏览量(Pageviews)

 金沙注册送58 31

z-ga-pv.png

因为201陆年11月有一个人资深的博客主成为了火爆话题,所以那么些目标内涵盖非常意况。网页浏览量进步了5七.1伍%。如若将火热话题所拉动的数值除去后,实际上只是由系统重构所带来的晋级在百分之十到2/10以内。

每一回对话浏览页数 (Pages / Session)

 金沙注册送58 32

z-ga-pps.png

Pages / Session是指在单个会话内页面包车型客车浏览数,那几个目的 提升了35.54
。SPA改进了页面间跳转的速度,获取了引人侧目的作用。

跳出率(Bounce Rate)

 金沙注册送58 33

z-ga-bounce.png

跳出率指在四个会话内,仅看了3个页面包车型地铁比值,那几个目的 改善了44.44%
。大家以为那是出于首屏和页面跳转速度的创新,用户分界面晋级(更易于驾驭的分页),「咯噔」立异所带来的结果。

可是还设有重重改进的退路,任何1个目标都能够重复提高。大家想以此标识
网址质量的升高会带来业务目的的升级

上述数量是在偏下规则下获得的:

  • 页面质量
    • 使用
      SpeedCurve
    • 测试的URL是
      http://s.ameblo.jp/ebizo-ichikawa/entry-12152370365.html
    • 浏览器钦点为 Chrome, 5三.0.27八五.14三移动端模拟形式
    • 互联网内定为4G模仿情势(1肆.陆 Mbps,Upload 柒.八Mbps,Latency 伍三ms)
  • 政工目标
    • 使用 Google
      Analytics
    • 获取自 s.ameblo.jp 内的全体数目
    • 对2016年1月和201陆年十一月的数值进行相比

写在最终

本次系统重构的着重点是对才干的挑战,结果获得了完美的用户反映,并对工作作出了进献,大家本身也感到非常有价值,获得了强大的引以自豪。采纳最新迎合时流的才具自然进步服务的质量,也使得这种知识在信用合作社在生根。在此,对尽快导入Isomorphic
JavaScript,并向扶桑境内推广的同事
@ahomu
表示感激! 

我介绍:

小编:原 十分之一(Hara Kazunari),200玖年参预日本CyberAgent公司。担任Ameblo
201陆平移前端改版项目总老董。著有《GitHubの教科書》,《CSS叁逆引きデザインレシピ》,《フロントエンドエンジニア育成読本》。

翻译:侯 斌(Hou
Bin),2014年入职日本CyberAgent公司。现任Ameblo前端开拓。在此番Ameblo
201陆活动前端改版项目中担纲机要花费,负担基础架议和技巧选型以及重要模块开采等。

1 赞 收藏
评论

金沙注册送58 34

相关文章

网站地图xml地图