八戒传-9

感想

  怀念那些已经离开我们的兄弟!

正文

  八戒,老半仙走了。连续几天为师路过他家门口都看到院门紧锁,为师问了隔壁的补鞋匠,原来老半仙在一天夜里赶着小毛驴,驮着两大箱行李,匆匆忙忙的不知去哪里了。恐怕再也没机会见到他了,为师还是蛮感谢他带来的惊喜,只是还没来得及好好告别。不知道那两个箱子里面装的啥,听补鞋匠说是馒头,不过又听你师母说是蜡烛,为师觉得可能是门口的银杏树叶。之前有谣言说金山寺门口的银杏树叶可以当作护身符,有趋吉避害之效果,而且上次老半仙还偷偷摸摸的问为师树上到底有多少片叶子,想想还是很可疑的。
  
  最近雨水不断,为师不常出门。记得西天路上,有一次突遇暴雨,雨水打在干燥的土地上,一股芬芳沁人心脾,为师问你们这种味道像什么?
  
  悟空说:像小猴子的味道。
  
  我们都大吃一惊,都以为这猴子原来还吃过小猴子,没想到猴子说完就不理我们了,怔怔地看着远处的彩虹。后来为师才知道,原来悟空的意思是当他还是个小猴子的时候的那种味道。原来悟空也有童年。

  为师原以为你会说是广寒宫或者高老庄的味道,没想到你却说是手指的味道。连在认真吃草的白龙马都被你的答案惊到了。你看着好奇的我们,慢慢的说:我老猪被打下凡间,转世为猪,刚开始一直不习惯,不愿意吃那些馊食恶水,一直忍着饿着,直到有一天,饿的实在受不了了,看着自己的手指以为是一只猪蹄,开心的不得了,狠狠的咬了一口,直到被疼醒。自此以后,不管发生什么事,我老猪发誓,绝对不会让自己饿着。

  你刚说完,白龙马就开心的笑了,猴子已经从树上掉下来了,十个桃子都塞不满他的嘴。只有老沙,泪眼汪汪的看着你,好像对你有了新的想法一样。为师攥紧了手中的馒头,避开了你渴望的双眼。

  为师又问老沙,老沙笑眯眯的说这种味道就像初次跪在为师面前剃度出家一样,哈哈,这倒一点也不出乎意料。

  为师本想问问白龙马,但是他偷偷的跑到更远的地方吃草去了,这头宁愿吃草也不愿意回忆过往的龙,不容易啊。

  好了,你师母要回来了。

  爱你的师父,再聊。

react es6 装饰器

主题

  如何使用es6装饰器?

正文

  ES6不支持Mixins,但是ES7支持Decorator,使用Decorator可以实现Mixins功能。Decorator本质上是一个高阶函数(高阶组件),在面向对象的语言中被定义为一个包装类。也就是说,使用Decorator,会为React组件添加一些通用的新功能。

  在React中,Decorator定义为一个函数,这个函数将一个组件A作为参数,并且重新render一个以A为基础的组件。此时只需要在A组件定义的时候加上@Decorator。

1
2
3
4
5
6
7
8
9
10
11
12
Component => {
constructor(props) {
super(props);
...
}

...

render() {
<Component {...this.props} ... />
}
}

  有些时候需要使用this.context,此时应该手动将contextTypes传入Decorator。此时需要在A组件定义的时候加上@Decorator(contextTypes, context)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(contextTypes = {}, context = {}) => Component => {
static childContextTypes = contextTypes;
static contextTypes = contextTypes;

constructor(props) {
super(props);
...
}

...

render() {
<Component {...this.props} ... />
}
}

  可以在Decorator中定义通用函数,将此函数作为props传入A组件中,此时A组件可以通过this.props.doSomething()使用此函数。

1
2
3
4
5
6
7
8
doSomething() {
...
}

render() {
<Component {...this.props}
doSomething={this.doSomething.bind(this)} />

}

  也可以重新封装A组件。

1
2
3
4
5
render() {
<Wrap ...>
<Component {...this.props} ... />
</Wrap>
}

  Decorator不但可以装饰组件类,还可以装饰函数,多个装饰器也可以同时使用装饰同一个组件类,但是要保证每次装饰后不影响源组件的基本功能。

  Decorator虽好,但是在使用之前还是要深思熟虑,props的来源会增加代码阅读和维护的难度,尤其是使用多个Decorator的时候。Decorator的使用对A来说应该是透明的,不能影响A的基本功能。

如何写好一个react组件

正文

  1. 设置默认属性 defaultProps
  2. 属性检测 propTypes
  3. 构造函数:获取默认值,绑定函数
  4. Controlled Componennt:更新Value
  5. 更新state,回调onChange
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import React from 'react';
import RaisedButton from 'material-ui/lib/raised-button';

//定义内部样式
const minBtnStyle = {
height: '30px',
margin: '5px 0',
marginLeft: '10px',
minWidth: 'auto'
};

const minBtnLabelStyle = {
lineHeight: '30px'
};

class BSelect extends React.Component {

//1、设置默认属性
static defaultProps = {
/* 选择列表 */
checkList: [],

/* 根元素类名 */
className: undefined,

/* 默认值,multi为true时,是数组 */
defaultValue: undefined,

/* 组件说明文字 */
label: undefined,

/* 是否启用多选 */
multi: false,

/* 选中的值,是否是Controlled Components */
value: undefined,

/* event */
onChange: undefined
}

//2、属性检测
static propTypes = {
checkList: React.PropTypes.array.isRequired,
className: React.PropTypes.string,
defaultValue: (props, propName) => {
if(props.multi) {
if(props[propName] && !(props[propName] instanceof Array)) {
return new Error('defaultValue should be Array cause multi is true');
}
}
},
label: React.PropTypes.string,
multi: React.PropTypes.bool,
value: (props, propName) => {
if(props.multi) {
if(props[propName] && !(props[propName] instanceof Array)) {
return new Error('value should be Array cause multi is true');
}
}
},
onChange: React.PropTypes.func
}

//3、构造函数:获取默认值,绑定函数
constructor(props) {
super(props);

this._updateState = this._updateState.bind(this);
this._setValue = this._setValue.bind(this);

this.isMulti = this.props.multi;

//init states
this.state = {
value: this.props.defaultValue === undefined ? this.isMulti ? [] : '' : this.props.defaultValue
};
}

componentWillMount() {}

//4、Controlled Componennt:更新Value
componentWillReceiveProps(nextProps) {
this._setValue(nextProps, nextProps.value);
}

_setValue(props, value) {
if(value !== undefined) {
if(this.isMulti) {
this.state.value = value === undefined ? [] : value;
} else {
this.state.value = value === undefined ? '' : value;
}
}
}

//5、更新state,回调onChange
_updateState() {
if(typeof this.props.onChange === 'function') {
this.props.onChange(this.state.value);
}

this.setState({
value: this.state.value
});
}

render() {
let labelText = this.props.label;
let labelElement = labelText ? <label>{labelText + ':'}</label> : '';
return (
<div className={'row pdl1 pdr1 middle-xs ' + (this.props.className || '')}>
{labelElement}
<div className='col-xs nopadding'>
{
this.props.checkList.map((v, i) => {
return (
<RaisedButton className={v.btnClassName}
disabled={v.disabled}
label={v.label}
labelStyle={minBtnLabelStyle}
key={i}
onClick={() => {
if(this.isMulti) {
let index = this.state.value.indexOf(v.value);
if(index !== -1) {
this.state.value.splice(index, 1);
} else {
this.state.value.push(v.value);
}
} else {
this.state.value = v.value;
}
this._updateState();
}}
secondary={(() => {
if(this.state.value instanceof Array) {
return this.state.value.includes(v.value);
} else {
return this.state.value === v.value;
}
})()}
style={minBtnStyle}/>
)
})
}
</div>
</div>
)
}
}

export default BSelect;

BSelect

Material Icons can't display ?

正文

  
  Stark项目使用了react-material-ui,其中包含Material Icons,下载完成所有格式的字体文件后:

  1. 导入字体

1
2
3
4
5
6
7
8
9
10
11
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(MaterialIcons-Regular.eot); /* For IE6-8 */
src: local('Material Icons'),
local('MaterialIcons-Regular'),
url(MaterialIcons-Regular.woff2) format('woff2'),
url(MaterialIcons-Regular.woff) format('woff'),
url(MaterialIcons-Regular.ttf) format('truetype')
;

}

  2. 使用字体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.material-icons {
direction: ltr;
display: inline-block;
font-family: 'Material Icons';
font-size: 14px; /* Preferred icon size */
font-weight: normal;
font-style: normal;
letter-spacing: normal;
line-height: 1;
text-transform: none;
white-space: nowrap;
word-wrap: normal;

/* Support for all WebKit browsers. */
-webkit-font-smoothing: antialiased;
/* Support for Firefox. */
-moz-osx-font-smoothing: grayscale;
}

  上述两步完成后,在Mac下的chrome上完美的运行着,但是到了windows下的chrome却没法显示字体。最后发现还需要在步骤2的类中添加下述两句CSS:

1
2
3
4
5
6
7
.material-icons{
/* Support for Safari and Chrome. */
text-rendering: optimizeLegibility;

/* Support for IE. */
font-feature-settings: 'liga';
}

  这两句CSS的共同作用是启用字体的连字效果,就是下述这种效果,,oo和th都被短线连接:

  连字效果

  强烈建议阅读web-font这篇文章,对text-rendering和font-feature-settings讲解非常详细。

  参考:font-smoothing

Javascript中Node和Element区别

主题

  Javascript中Node和Element区别。

正文

  Node是HTML文档中所有内容的父类,包括元素Element,注释Comment,文本Text等等。Element是Node的子类。

  通过代码来测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
<title>Node & ELlement</title>
<meta charset="utf-8">
</head>
<body>
<!-- This is Comment Note -->
<div>Element Node</div>
Text Node
</body>
</html>

  在控制台中执行下述代码:

1
2
> document.body.childNodes //获取所有的Node节点
> [#text, <!-- This is Comment Note -->, #text, <div>Element Node</div>, "Text Node"]

  可以看到,结果中除了预见中的div元素节点,还有注释和文本节点,甚至还有两个#text字段,点击详情可以看到其实#text也是文本节点,只是内容是回车。

  再看下述代码:

1
2
> document.body.children //获取所有的Element节点
> [<div>Element Node</div>]

  可以看到,结果只用div元素节点。

  具体来说,Node只是Element的一个父元素。Element继承了Node的属性和方法,又加入了自己的属性和方法。

  参考:NodeElement