(翻译)Smart and Dumb Components

原文链接:Smart and Dumb Components
作者:Dan Abramov

我发现当我写React应用时有一个非常有用的小模式(pattern)。如果你使用React有一段时间了,你应该早就发现它了。这篇文章解释的很好(我也翻译了),不过我想加多一些观点。

你会发现你的组件要是能分成两类,复用起来会变得更轻松。我称他们两类分别为SmartDumb,我也听说过FatSkinnyStatefulPureScreensComponents等等。这些虽然不完全一样的,但本质是相类似的。

我的 dumb 组件:

  • 对应用的其他部分没有依赖,例如 Flux actions 或者 stores。
  • 通过 this.props.children通常允许放在容器里。(Often allow containment via this.props.children)
  • 统一通过 props 来获取数据和回调函数。
  • 有 CSS 文件关联他们。
  • 几乎没有它们自己的 state。
  • 或许里面也使用了其他的 dumb 组件。
  • 例子:Page, Sidebar, Story, UserInfo, List.

我的 smart 组件:

  • 包含了一个或多个 dumb 或者 smart 组件。
  • 在 Flux stores 中保存 state 并 通过 objects 的形式传递给 dumb 组件。
  • 访问 Flux actions 并作为回调函数提供给 dumb 组件。
  • 从来没有它们自己的 CSS 样式。
  • 几乎不通过它们自己生成 DOM ,用 dumb 组件来布局。
  • 例子:UserPage, FollowersSidebar, StoryContainer, FollowedUserList

我将它们两类放在不同的文件夹来让它们的区别更明确。

阅读全文 »

(翻译)Container Components

原文:Container Components

Container Components

在 React 模式上对我的代码有最深远影响的一个模式叫 container component 模式。

在 React.js Conf 上,Jason Bonta 和我们讲了他们在Facebook上是如何建立高性能组件(High Performance Components)。Nestled 在这个演讲中讲的就是 this gem about container components

这个概念很简单:

一个 container 只是做数据拉取然后渲染与它的 corresponding 子组件。就是这样。

“Corresponding” 意味着分享同一个名称的组件,例如:

1
StockWidgetContainer => StockWidget
TagCloudContainer => TagCloud
PartyPooperListContainer => PartyPooperList

这就是其中的概念。

Why containers?

比如你有一个用于展示评论的组件。你并不知道有关 container 组件。所以,你会将所有东西都放在一个地方。

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
//  CommentList.js

class CommentList extends React.Component {
constructor(){
super();
this.state = { comments: []}
}

componentDidMount(){
$.ajax({
url: "/my-comments.json",
dataType: 'json',
success: function(comments){
this.setState({comments});
}.bind(this)
});
}

render(){
return <ul> {this.state.comments.map(renderComponent)} </ul>;
}

renderComponent({body, author}){
return <li> {body}-{author} </li>;
}
}

你的组件就是用于拉取数据并展示它。这并没有什么”错误”,但是你却错过了一些React的优点。

可复用性

CommentList组件除了在同一精确的条件情况下才能复用。

数据结构

你希望的组件应该规定他们需要的数据类型的预期。PropTypes正是干这个的。

我们的组件对数据结构要求很高但是没有办法说出这些要求。如果json的接口数据改变了,这个组件会不做出任何提示出错。(其实想说的就是,无法好好利用PropTypes来把控数据结构是否正确)

阅读全文 »

NodeJs通过async/await处理异步

场景


远古时代

我们在编写express后台,经常要有许多异步IO的处理。在远古时代,我们都是用chunk函数处理,也就是我们最熟悉的那种默认第一个参数是error的函数。我们来模拟一个Mongo数据库的操作,感受一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mongoDb.open(function(err, db){
if(!err){
db.collection("users", function(err, collection){
if(!err){
let person = {name: "yika", age: 20};
collection.insert(person, function(err, result){
if(!err){
console.log(result);
}
});
}
})
}
});

这个也就是被我们所诟病的callback hell,一堆横向金字塔,如果将回调拆分成函数,则会变得非常支离破碎。为了防止到恶心到大家,我甚至没有写关于错误的处理,正常来说,每一个异步的操作都需要都它的error进行相应的显示或处理的。

Promise时代

后来进入了好一点的时代就是Promise,我们也可以称作链式操作。关于Promise,我也是之前有专门写过一系列的博文,有兴趣可以回头翻一下。这里来看看,将以上改写之后的状况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let person = {name: "yika"};
mongoDb
.open()
.then(function(database){
return database.collection("users");
})
.then(function(collection){
return collection.insert(person);
})
.then(function(result){
console.log(result);
})
.catch(function(e){
throw new Error(e);
})

我们可以看到,我们将金字塔已经平铺成一条线状结构了。相比之前恶心难以维护的chunk函数,变成了promise函数,并且错误的处理也变得十分优雅。但是我们仍然不可忽视某些问题,例如我们必须忍受各个逻辑被一个又一个的then()包裹起来,每一个函数都有其独立的作用域,如果为了共享某个数据就必须挂在最外层,最重要的还是,它与我们熟悉的同步编程仍然有差别。

阅读全文 »

React爬坑秘籍(一)——提升渲染性能

前言


来到腾讯实习后,有幸八月份开始了腾讯办公助手PC端的开发。因为办公助手主推的是移动端,所以导师也是大胆的让我们实习生来技术选型并开发,他来做code review。之前也学习过React,当然也是非常合适这一次的开发。

我会梳理这一个月来,自己对架构的思考过程和踩过的坑。当然这一切都不一定是最佳的,所以希望能有更多的建议和讨论。

例子所需库:Webpack、React、Immutable。其中Webpack用于前端构建,如果不清楚的同学可以看这里:webpack前端构建体验

出现场景


一般来说,React作为一个高效的UI Library,如果合理使用是很难出现性能问题的。它内部提供了虚拟DOM搭配上Diff算法,和子组件必要的key属性,都是非常优秀的优化了绝大部分的性能。

但是我们来模拟一个场景,在一个数组里有10000个对象,我们把这个数组的数据渲染出来后,其中一个属性用于控制页面状态。

在这里我希望大家知道有一点就是,当父组件的状态state发生变化时,传入state的子组件都会进行重新渲染。

下面我们来模拟一下这种情况,一起来看看。

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* Created by YikaJ on 15/9/17.
*/
'use strict';
var React = require("react");

var App = React.createClass({

getInitialState(){
return {
list: this.props.dataArr
}
},

// 对数据的状态进行变更
toggleChecked(event){
let checked = event.target.checked;
let index = event.target.getAttribute("data-index");
let list = this.state.list;
list[index].checked = checked;

this.setState({list});
},

render(){
// 将数组的数据渲染出来
return (
<ul>
{this.state.list.map((data, index)=>{
return (
<ListItem data={data}
index={index} key={data.name}
toggleChecked={this.toggleChecked}
/>
)
})}
</ul>
)
}
});

// 代表每一个子组件
var ListItem = React.createClass({
render(){
let data = this.props.data;
let index = this.props.index;

// checkbox选择框是一个受限组件,用数据来决定它是否选中
return (
<li>
<input type="checkbox" data-index={index} checked={data.checked} onChange={this.props.toggleChecked}/>
<span>{data.name}</span>
</li>
)
}
});

// 构造一个2000个数据的数组
let dataArr = [];
for(let i = 0; i < 2000; i++){
let checked = Math.random() < 0.5;
dataArr.push({
name: i,
checked
});
}

React.render(<App dataArr={dataArr}/>, document.body);

这个就是我们的有性能问题的组件。当我们去点击选框时,因为父组件的state传到了子组件的props里,我们就会遇到10000个子组件重新渲染的情况。所以表现出来的情况就是,我点一下,等个一两秒那个框才真正被勾上。我相信用户在这一秒内肯定已经关掉页面了。

阅读全文 »

(转)Chrome开发者工具不完全指南(一、基础功能篇)

本篇转载自卖烧烤夫斯基,并做了小部分的修改。

原文地址:Chrome开发者工具不完全指南(一、基础功能篇)

原作者:卖烧烤夫斯基


就算你不是一名前端开发工程师,相信你也不会对Chrome浏览器感到陌生。根据最新的一份(2015/06)的浏览器市场占有率报告,Chrome近乎占有浏览器天下的半壁江山。简单、快捷使它成为了新时代人们的新宠。如果你是一名web开发人员,我推荐你使用Chrome。作为前端开发的”IDE”,你只需要搭配一个编辑器就能完成几乎所有的开发任务了。关于它的使用和功能分析要么都是大而不全,要么是巨细糜烦。本系会比较详细地分享卤煮的一些Chrome(F12开发者功能)使用经验,从一些基础的功能开始到它的一些高级性能分析器(Timeline、Profiles),在最后,将会推荐几款好的插件,希望对您的开发工作有些许的作用。如果你对一些面板模块功能已经很了解可以直接跳过去阅读你感兴趣的部分。

Elements


界面图片

在Elements中主要分两块大的部分:

A. HTML结构面板
B. 操作DOM样式、结构、时间的显示面板

  1. 在A中,每当你的鼠标移动到任何一个元素上,对应的HTML视图中会给该元素蓝色的背景。

    例子

  2. 如果你单击选中一个元素,在A部分的底部,会显示该元素在HTML结构中的位置关系。

    例子

  3. 然后你可以在B部分的styles选项中编辑该元素的样式,并且看到HTML结构的实时更新,无需刷新。

    例子

  4. 你可以在B界面中切换到Event Listener选项,观察该元素绑定的事件。

    例子

    • click 是事件名称
    • .div1 事件是索引名称(也就是通过什么进行绑定)
    • attachment 事件来源
    • handler 事件回调的详细内容
    • useCapture 事件是否为向上冒泡
      阅读全文 »