JiM-W

keep Moving


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

React Optimizing Performance

发表于 2017-04-24 | 分类于 react

1 首先理解shouldComponentUpdate 作用

shouldComponentUpdate: 这是一个和性能非常相关的方法,在每一次render方法之前被调用。它提供了一个机会让你决定是否要对组件进行实际的render

它是react性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤其是在dom结构复杂的时候。不过调用this.forceUpdate会跳过此步骤。

In some cases, your component can speed all of this up by overriding the lifecycle function shouldComponentUpdate, which is triggered before the re-rendering process starts. The default implementation of this function returns true, leaving React to perform the update:

1
2
3
shouldComponentUpdate(nextProps, nextState) {
return true;
}

If you know that in some situations your component doesn’t need to update, you can return false from shouldComponentUpdate instead, to skip the whole rendering process, including calling render() on this component and below.

也就是说,如果shouldComponentUpdate返回true,那么React会调用render方法重新渲染页面,如果返回false,则React不会调用render方法。该方法接受两个参数,一个是下一个状态,一个是下一个props.可以和原来的props和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
<div id="root"></div>
<script type='text/babel'>
class CounterButton extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1 }))}>
Count: {this.state.count}
</button>
);
}
}
ReactDOM.render(
<CounterButton />,
document.getElementById('root')
)
</script>

当我们改动一下代码,当count值改变之后,比较前后两者的值,如果不相等,返回false;此时不会更新视图,也就是说React没有再次调用render函数去重新渲染视图。

1
2
3
if (this.state.count !== nextState.count) {
return false;
}

2 React.PureComponent

In this code, shouldComponentUpdate is just checking if there is any change in props.coloror state.count. If those values don’t change, the component doesn’t update. If your component got more complex, you could use a similar pattern of doing a “shallow comparison” between all the fields of props and state to determine if the component should update. This pattern is common enough that React provides a helper to use this logic - just inherit from React.PureComponent. So this code is a simpler way to achieve the same thing:

React同样提供了简单的方法,可以不让我们每次都要写shouldComponnetUpdate判断props和state的状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class CounterButton extends React.PureComponent {
constructor(props) {
super(props);
this.state = {count: 1};
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
</button>
);
}
}

React Router

发表于 2017-04-24 | 分类于 react

react lifting State Up

发表于 2017-04-21 | 分类于 react

1 In React, sharing state is accomplished by moving it up to the closest common ancestor of the components that need it. This is called “lifting state up”.

props应该是只读的属性,如果我们要改变输出,可以通过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
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
<!DOCTYPE html>
<html>
<head>
<script src="../build/react.js"></script>
<script src="../build/react-dom.js"></script>
<script src="../build/browser.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type='text/babel'>
const scaleNames = {
c: 'Celsius',
f: 'Fahrenheit'
};
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}
function tryConvert(temperature, convert) {
const input = parseFloat(temperature);
if (Number.isNaN(input)) {
return '';
}
const output = convert(input);
const rounded = Math.round(output * 1000) / 1000;
return rounded.toString();
}
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>The water would boil.</p>;
}
return <p>The water would not boil.</p>;
}
class TemperatureInput extends React.Component {
constructor(props) {
console.log(props)
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.props.onTemperatureChange(e.target.value);
}
render() {
const temperature = this.props.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}
class Calculator extends React.Component {
constructor(props) {
console.log(props)
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {temperature: '', scale: 'c'};
}
handleCelsiusChange(temperature) {
this.setState({scale: 'c', temperature});
}
handleFahrenheitChange(temperature) {
this.setState({scale: 'f', temperature});
}
render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} />
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={this.handleFahrenheitChange} />
<BoilingVerdict
celsius={parseFloat(celsius)} />
</div>
);
}
}
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
</script>
</body>
</html>

2 props中的children

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
<body>
<div id="root"></div>
<script type='text/babel'>
function FacnyBorder(props){
console.log(props);
console.log(props.children);
return (
<div className = {'FancyBorder FancyBorder-'+ props.color}>
{props.children}
</div>
)
}
function WelecomeDialog(props){
console.log(props)
return (
<FacnyBorder color = 'blue'>
<h1 className = 'Dialog-title'>welcome</h1>
<p> className = 'Dialog-message'>thank you for visiting our website</p>
</FacnyBorder>
)
}
ReactDOM.render(
<WelecomeDialog />,
document.getElementById('root')
)
</script>

WelcomeDialog组件输出的props如下

1
2
3
4
5
6
Object {}
key:(...)
ref:(...)
get key:function ()
get ref:function ()
__proto__:Object

FacnyBorder输出的props如下

1
2
3
4
5
6
7
8
Object {color: "blue", children: Array(2)}
children:Array(2)
color:"blue"
key:(...)
ref:(...)
get key:function ()
get ref:function ()
__proto__:Object

FacnyBorder输出的 props.children 如下

1
2
3
4
[Object, Object]
0:Object
1:Object
length:2

其中 数组中的第一项展开内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$$typeof:Symbol(react.element)
key:null
props:Object
children:"welcome"
className:"Dialog-title"
key:(...)
ref:(...)
get key:function ()
get ref:function ()
__proto__:Object
ref:null
type:"h1"
_owner:ReactCompositeComponentWrapper
_store:Object
_self:null
_source:null

其中数组第二项展开如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$$typeof:Symbol(react.element)
key:null
props:Object
children:"thank you for visiting our website"
className:"Dialog-message"
key:(...)
ref:(...)
get key:function ()
get ref:function ()
__proto__:Object
ref:null
type:"p"
_owner:ReactCompositeComponentWrapper
_store:Object
_self:null
_source:null"

同样在React中

1
React elements like <Contacts /> and <Chat /> are just objects, so you can pass them as props like any other data.

还是上面的例子,我们尝试着打印出

1
console.log(<WelecomeDialog color = 'red'/>)

输出结果如下

1
2
3
4
5
6
7
8
9
10
Object {$$typeof: Symbol(react.element), key: null, ref: null, props: Object, type: function…}
$$typeof:Symbol(react.element)
key:null
props:Object
ref:null
type:function WelecomeDialog(props)
_owner:null
_store:Object
_self:null
_source:null

submit

根据以上的输出我们可以得到结论:

  • 组件上通过属性添加的数据,会绑定在props对象中
  • 组件上通过子节点,比如文本节点或者元素节点添加在组件上的,会绑定到props.children对象上
1
<WelecomeDialog>hello</WelecomeDialog> 比如中的hello文本节点
1
2
3
4
5
<FacnyBorder color = 'blue'>
<h1 className = 'Dialog-title'>welcome</h1>
<p className = 'Dialog-message'>thank you for visiting our website</p>
</FacnyBorder>
中的元素节点 h1和p
  • 组件以标签形式显示的时候,其实也就是一个对象
  • 如果想要向组件中传递参数,可以通过属性或者子节点进行传递,传递的数据可以在组件定义中的props和props.children中获取到。
  • React区分标签是一个组件还是一个HTML标签是根据标签的首字母大小写来区分的
1
2
<div /> 这个就是代表HTML标签
<Contact /> 这个就代表组件
  • React中无论是组件还是标签,都必须被正确的闭合 每一个标签或者组件的 / 必不可少 ;

3 除了通过children传递data数据,我们其实可以利用props传递组件对象

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
<div id="root"></div>
<script type='text/babel'>
function Contacts(){
return (
<div className = 'contacts'>
this is contacts
</div>
)
}
function Chat(){
return (
<div className = 'chat' >this is chat </div>
)
}
function SplitPane(props){
console.log(props)
return(
<div className = 'SplitPane'>
<div className = 'SplitPane-left'>
{props.left}
</div>
<div className = 'SplitPane-right'>
{props.right}
</div>
</div>
)
}
function App(props){
return (
<SplitPane left = {<Contacts/>} right = {<Chat/>} />
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
</script>

通过以上用法,我们就可以传递多个不一样的组件,也就是可以对组件进行不一样的拼接。

react State 和 lifeCycle

发表于 2017-04-19 | 分类于 react

1 先来看下函数定义组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="root"></div>
<script type='text/babel'>
function Clock(props){
return (
<div>
<p>hello world</p>
<p>it is {props.date.toLocaleTimeString()}</p>
</div>
);
}
function tick (){
ReactDOM.render(
<Clock date = {new Date()}/>,
document.getElementById('root')
)
}
setInterval(tick,1000);
</script>

如何让Clock拥有自动更新UI的能力?就像这个样子实现功能?

1
2
3
4
ReactDOM.render(
<Clock />,
document.getElementById('root')
);

To implement this, we need to add “state” to the Clock component.

State is similar to props, but it is private and fully controlled by the component.

We mentioned before that components defined as classes have some additional features. Local state is exactly that: a feature available only to classes.

2 将函数定义组件的方式改为类定义组件

1
2
3
4
5
6
7
8
9
10
class Clock extends React.Component{
render(){
return (
<div>
<p>hello world</p>
<p>it is {this.props.date.toLocaleTimeString()}</p>
</div>
);
}
}

2.1 如何将当前状态添加进类定义的组件?如何将 date 属性从props 对象迁移至 state对象?

  • 添加一个constructor构造函数 将date赋值给this.state
  • 我们需要将this.props.date 改成 this.state.date
  • 移除掉组件上的date
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Clock extends React.Component{
constructor(props) {
super(props);
this.state = {date : new Date()} //第一步
}
render(){
return (
<div>
<p>hello world</p>
<p>it is {this.state.date.toLocaleTimeString()}</p> //第二步
</div>
);
}
}
ReactDOM.render(
<Clock />, //第三步
document.getElementById('root')
)

2.2 接下来就需要设定定时器,可以是组件Clock设置自己的定时器,并且可以自动更新

Adding Lifecycle Methods to a Class

In applications with many components, it’s very important to free up resources taken by the components when they are destroyed.

We want to set up a timer whenever the Clock is rendered to the DOM for the first time. This is called “mounting” in React.

We also want to clear that timer whenever the DOM produced by the Clock is removed. This is called “unmounting” in React.

We can declare special methods on the component class to run some code when a component mounts and unmounts:

我们可以在组件上定义一些特殊的方法,当组件装配或者装卸的时候,运行某些代码

React有两个内置的方法 componentDidMount() componentWillUnmount()

These methods are called “lifecycle hooks”.

  • The componentDidMount() hook runs after the component output has been rendered to the DOM. This is a good place to set up a timer:
1
2
3
4
5
6
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
  • We will tear down the timer in the componentWillUnmount() lifecycle hook:
1
2
3
componentWillUnmount() {
clearInterval(this.timerID);
}

完整代码如下:

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
<div id="root"></div>
<script type='text/babel'>
class Clock extends React.Component{
constructor(props) {
super(props);
this.state = {date : new Date()}
}
tick(){
this.setState(
{date:new Date()}
);
}
componentDidMount(){
this.timerID = setInterval(()=>this.tick(),1000)
}
componentWillUnmount(){
clearInterval(this.timerID)
}
render(){
return (
<div>
<p>hello world</p>
<p>it is {this.state.date.toLocaleTimeString()}</p>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
)
</script>

Let’s quickly recap what’s going on and the order in which the methods are called:

1) When <Clock /> is passed to ReactDOM.render(), React calls the constructor of the Clock component. Since Clock needs to display the current time, it initializes this.statewith an object including the current time. We will later update this state.

2) React then calls the Clock component’s render() method. This is how React learns what should be displayed on the screen. React then updates the DOM to match the Clock‘s render output.

3) When the Clock output is inserted in the DOM, React calls the componentDidMount()lifecycle hook. Inside it, the Clock component asks the browser to set up a timer to call tick() once a second.

4) Every second the browser calls the tick() method. Inside it, the Clock component schedules a UI update by calling setState() with an object containing the current time. Thanks to the setState() call, React knows the state has changed, and calls render()method again to learn what should be on the screen. This time, this.state.date in the render() method will be different, and so the render output will include the updated time. React updates the DOM accordingly.

5) If the Clock component is ever removed from the DOM, React calls the componentWillUnmount() lifecycle hook so the timer is stopped.

3 setState( )函数

3.1 接受一个对象作为参数

1
this.setState({comment: 'Hello'});

3.2 接受一个函数作为参数,箭头函数返回值还是一个对象,也可以直接接受一个函数,返回一个对象

1
2
3
4
5
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
//如果用用箭头函数自定义对象 需要用小括号将花括号包起来,这个时候返回的结果才是一个对象
1
2
3
4
5
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
};
});

3.3 注意对于state和props,它们的值可能被异步更新,所以当我们计算它们的下一个状态值的时候,不能依赖它们的当前值,比如如下demo不会更新

1
2
3
4
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="root"></div>
<script type='text/babel'>
class CounterButton extends React.Component{
constructor(props){
super(props);
this.state = {count : 1 }
}
render(){
return (
<button color = {this.props.color}
onClick = {()=>this.setState({count : this.state.count++})}>
count : {this.state.count}
</button>
);
}
}
ReactDOM.render(
<CounterButton />,
document.getElementById('root')
)
</script>
1
2
3
4
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="root"></div>
<script type='text/babel'>
class CounterButton extends React.Component{
constructor(props){
super(props);
this.state = {count : 1 }
}
render(){
return (
<button color = {this.props.color}
onClick = {() => this.setState(state=>({count:state.count + 1}))}>
count : {this.state.count}
</button>
);
}
}
ReactDOM.render(
<CounterButton />,
document.getElementById('root')
)
</script>

或者直接传入setState一个函数,该函数显示的返回一个对象

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
<div id="root"></div>
<script type='text/babel'>
class CounterButton extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(function(state){
return {
count : state.count + 1
}
})}>
Count: {this.state.count}
</button>
);
}
}
ReactDOM.render(
<CounterButton />,
document.getElementById('root')
)
</script>

以下贴上一段React中关于setstate的源码,便于理解;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ReactComponent.prototype.setState = function(partialState, callback) {
("production" !== "development" ? invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.'
) : invariant(typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null));
if ("production" !== "development") {
("production" !== "development" ? warning(
partialState != null,
'setState(...): You passed an undefined or null state object; ' +
'instead, use forceUpdate().'
) : null);
}

setState会自动调用render函数,触发视图的重新渲染,如果仅仅只是state数据的变化而没有调用setState,并不会触发更新。

4 react组件生命周期

对于自定义组件,唯一必须实现的方法就是render(),除此之外,还有一些方法会在组件生命周期中被调用,如下

  • constructor: 构造函数,组件被创建时执行;
  • componentDidMount: 当组件添加到DOM树之后执行;
  • componentWillUnmount: 当组件从DOM树中移除之后执行,在React中可以认为组件被销毁;
  • componentDidUpdate: 当组件更新时执行。
  • shouldComponentUpdate: 这是一个和性能非常相关的方法,在每一次render方法之前被调用。它提供了一个机会让你决定是否要对组件进行实际的render
1
2
3
shouldComponentUpdate(nextProps, nextState) {
return nextProps.id !== this.props.id;
}

当此函数返回false时,组件就不会调用render方法从而避免了虚拟DOM的创建和内存中的Diff比较,从而有助于提高性能。当返回true时,则会进行正常的render的逻辑。

react Event

发表于 2017-04-19 | 分类于 react

1 Handling events with React elements is very similar to handling events on DOM elements. There are some syntactic differences:

  • React events are named using camelCase, rather than lowercase.
  • With JSX you pass a function as the event handler, rather than a string.
  • 函数的this指向null,而原先的HTML绑定时间this指向的是绑定事件的元素
  • react并不会真正的绑定事件到每一个具体的元素上,而是采用事件代理的模式:在根节点document上为每种事件添加唯一的Listener,然后通过事件对象的event.target找到真实的触发元素。这样从触发元素到顶层节点之间的所有节点如果有绑定这个事件,React都会触发对应的事件处理函数。这就是所谓的React模拟事件系统。

建议先了解下箭头函数,我也有些过关于箭头函数的博文

1
2
3
4
5
6
7
8
9
<body>
<a href="http://www.baidu.com" onclick="console.log('The link was clicked.'); console.log(this);return false">
Click me
</a>
<a href="http://www.baidu.com" onclick="console.log('The link was clicked.');">
Click me
</a>
</body>

以上:这里面的this代表的是a标签 ;可以通过return false阻止默认事件 ;事件绑定命名用小写字母

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="root"></div>
<script type='text/babel'>
function ActionLink(props){
function HandlerClick(e){
e.preventDefault();
console.log(this)
}
return (
<a href="http://www.baidu.com" onClick = {HandlerClick}>click me react</a>
)
}
ReactDOM.render(
<ActionLink />,
document.getElementById('root')
)
</script>

以上:这里面的this代表的是null ;不可以通过return false阻止默认事件 ,只能用e.preventDefalut() ;事件绑定命名用驼峰命名的方式

以上函数声明组件等价于以下class类声明组件,重点注意this指向null,而不是元素标签,为了使handle函数内部的this不是指向null或者undefined,需要我们手动绑定这些函数执行的时候this的指向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="root"></div>
<script type='text/babel'>
class ActionLink extends React.Component{
HandlerClick(e){
e.preventDefault();
console.log(this);
}
render(){
return (
<a href="http://www.baidu.com" onClick = {this.HandlerClick}>click me react</a>
)
}
}
ReactDOM.render(
<ActionLink />,
document.getElementById('root')
)
</script>

那么如何使得HandlerClick函数内部的this指向改变?

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
<div id="root"></div>
<script type='text/babel'>
class ActionLink extends React.Component{
constructor(props){
super(props);
this.HandlerClick = this.HandlerClick.bind(this);
}
HandlerClick(e){
e.preventDefault();
console.log(this);
}
render(){
return (
<a href="http://www.baidu.com" onClick = {this.HandlerClick}>click me bind</a>
)
}
}
ReactDOM.render(
<ActionLink />,
document.getElementById('root')
)
</script>

这个时候可以发现控制台输出的this指向的是

1
ActionLink {props: Object, context: Object, refs: Object, updater: Object, HandlerClick: function…}

2 接下来走一个改变this指向应用的实例

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
<div id="root"></div>
<script type='text/babel'>
class Toggle extends React.Component{
constructor(props){
super(props);
this.state = {isToggleOn : true};
this.handlerClick = this.handlerClick.bind(this);
}
handlerClick(){
console.log(this);
this.setState((prevState)=>({isToggleOn : !prevState.isToggleOn}));
}
render(){
return (
<button onClick = {this.handlerClick}>
{this.state.isToggleOn?'on':'off'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
)
</script>

同样使用箭头函数也可以达到类似的效果,改变render函数,一定要明确箭头函数的特性,同时()不要忘记加;

1
2
3
4
5
6
7
render(){
return (
<button onClick = {()=>this.handlerClick()}>
{this.state.isToggleOn?'on':'off'}
</button>
);
}

看一下这段代码,也是通过箭头函数指定的this指向.

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
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class Counter extends Component {
static propTypes = {
value: PropTypes.number.isRequired,
onIncrement: PropTypes.func.isRequired,
onDecrement: PropTypes.func.isRequired
}
incrementIfOdd = () => {
if (this.props.value % 2 !== 0) {
this.props.onIncrement()
}
}
incrementAsync = () => {
setTimeout(this.props.onIncrement, 1000)
}
render() {
const { value, onIncrement, onDecrement } = this.props
return (
<p>
Clicked: {value} times
{' '}
<button onClick={onIncrement}>
+
</button>
{' '}
<button onClick={onDecrement}>
-
</button>
{' '}
<button onClick={this.incrementIfOdd}>
Increment if odd
</button>
{' '}
<button onClick={this.incrementAsync}>
Increment async
</button>
</p>
)
}
}
export default Counter

理解箭头函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<input id='btn' value = 'button' type="button" >
<script>
function arrow(){
console.log('this is arrow func');
}
document.getElementById('btn').onclick = ()=>arrow()
//等价于
document.getElementById('btn').onclick = function()(
arrow() ;
)
//当点击按钮的时候 onclick的事件监听函数执行,该函数执行的过程中,内部代码 arrow()会执行函数arrow,所以这就是()不要忘记加的原因
</script>
</body>

控制台输出的this都是

1
Toggle {props: Object, handlerClick:function (),context: Object, refs: Object, updater: Object, state: Object…}

3 使用箭头函数注意细节

但是如果使用以下写法就不会绑定this到Togglel了

isNaN And NumberisNaN

发表于 2017-04-19 | 分类于 ES6
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
//Number.isNaN() 不会进行类型转化,直接进行isNaN的判断;简而言之就是分两步
//第一步,如果传入的参数不是类型不是number直接返回 false
//第二步,如果传入的参数类型是number,判断是不是NaN,如果是返回true,否则返回false
//这里需要注意的一点是 NaN也是number类型的数据
/*
Number.isNan() does not convert the values to a Number, and will return false for any value that is not of the type Number【
If Type(number) is not Number, return false.
If number is NaN, return true.
Otherwise, return false.*/
console.log(Number.isNaN(1));//false
console.log(Number.isNaN('str'));//false
console.log(Number.isNaN(NaN));//True
//全局方法isNaN()会先将参数转为Number 类型,在判断是否为NaN ,所以在类型转换失败或运算错误时值为NaN,返回true,其他全为false
//全局isNaN会先进行类型转化为Number类型,这个时候需要掌握Number类型转化的规则,如果转化结果是NaN,那么isNaN返回true,其他全部为false
/*
The global isNaN() function converts the tested value to a Number, then tests it.【
If value can not convert to Number, return true.
else If number arithmethic result is NaN, return true.
Otherwise, return false.*/
console.log(isNaN('str'));//true
console.log(isNaN(1));//fasle
console.log(isNaN(undefined));//true
console.log(isNaN("Hello"));//true
/*
isNaN() 函数通常用于检测 parseFloat() 和 parseInt() 的结果,以判断它们表示的是否是合法的数字。当然也可以用 isNaN() 函数来检测算数错误,比如用 0 作除数的情况。*/
</script>
</body>
</html>

react Compare Vue

发表于 2017-04-19 | 分类于 work

1 react和vue优劣对比

1.1 react

  • 更适合大型应用和更好的可测试性
  • Web端和移动端原生APP通用
  • 更大的生态系统,更多的支持和好用的工具,大量数据渲染react优势明显
  • 可以创建一个同时适用于Web端和原生APP的框架
  • react由facebook维护,并且相应的社区以及组件都比较成熟

1.2 vue

  • 模板和渲染函数的弹性选择
  • 简单的语法和项目配置
  • 更快的渲染速度和更小的体积
  • 更加适合小且快,‘简单能用’,的单页面应用程序
  • vue 不适合持续的工程迭代,因为数据可以在view和model双向流动,但是带来的一个缺点是:工程规模比较大的时候,数据双向流动会带来难以预测的结果
  • vue是尤雨溪个人开发和维护,相应 的社区和组件不够成熟

2 react 和 vue 技术点对比

2.1 react

  • 采用ES6语法规范以及jsx语法
  • 创建virtualDOM ,因为虚拟DOM是内存数据,大大提高了性能。
  • 组件化的开发思路,带来了UI功能模块之间的分离
  • 数据绑定: 采用单向数据流
  • React可以拓展到服务端,移动端Native部分

2.2 vue

  • 采用模板语法,通过模板渲染前端页面
  • 同时借鉴了angular和react,更加轻量级、简单、快捷
  • 数据绑定默认单向数据流,也可以实现双向绑定
  • 同样可以组件化开发,对于部署小型应用特别方便
  • Vue因为比较轻量,还能用于业务场景非常轻的页面中。

###

svn

发表于 2017-04-18 | 分类于 tools

一、SVN工作原理

SVN(SubVersion)的基本工作思路是这样的:在一台服务器上建立一个源代码库,库里可以存放许多不同项目的源程序,由源代码库管理员统一管理这些源程序。

每个用户在使用源代码库之前,首先要把源代码库里的项目文件下载到本地(Checkout),然后用户可以在本地任意修改,最后用svn命令进行提交(Commit),由svn源代码库统一管理修改;update可以更新服务器上的最新的代码;

  • SVN服务器:运行SubVersion服务的计算机,SubVersion支持Linux和Windows,更多的是安装在Linux下。SVN提供服务有两种方式(运行方式):独立服务器和借助Apache服务器,分别使用SVN协议和Http协议。
  • SVN客户端:用户通过SVN客户端同SVN服务器打交道,SVN客户端分为命令行工具和图形化工具。最流行的客户端是TortoiseSVN。也可以在Eclipse中使用SVN插件。

二、SubVersion的使用

shift+右键 可以直接在摸个目录打开终端命令行

1、创建SVN仓库

1)先创建一个目录:E:\svnrepo\repoDemo1。后面就使用该目录作为SVN仓库。

2)创建仓库:svnadmin create E:\svnrepo\repoDemo1。

3)启动SVN服务:svnserve -d -r E:\svnrepo\repoDemo1,

这样就启动了repoDemo1这个仓库的服务(单仓库),如果在svnrepo目录下还有其他仓库,且要同时启动多个仓库,执行svnserve -d -r E:\svnrepo即可(多仓库)。

注:-d参数效果同于-daemon
-r参数效果同于-root
svnserve 将会在端口 3690 等待请求,-daemon(两个短横线)告诉 svnserve 以守护 进程方式运行,这样在手动终止之前不会退出。不要关闭命令行窗口,关闭窗口会把 svnserve 停止。-root设置根位置来限制服务器的访问目录,从而增加安全性和节约输入svnserve URL的时间。
如果不加root参数,服务url为:svn://192.168.1.188/svnrepo/repoDemo1
如果加上root参数,服务url为:svn://192.168.1.188/repoDemo1
此处的启动配置会影响服务url,如果输入url错误,会导致访问的时候出现异常。

一般情况下,访问SVN仓库的URL格式形如:svn://192.168.1.6/repoDemo1,但如果启动的是单仓库,则URL直接用:svn://192.168.1.6表示。svn协议的默认端口号为3690。

2、SVN客户端操作(命令行)

重点:checkout(检出)、commit(提交)、update(更新)

1)在E盘下建立user1、user2两个目录,模拟两个协同工作的用户的workspace。

2)检出:第一次和SVN服务器交互时,需要使用checkout将仓库检出到本地。

说明:检出一次,就建立了与SVN仓库的连接。

3)提交:commit

在user1目录下新建Demo1.java文件,将该文件提交到SVN仓库,提交之后,别人就可以从这个源代码库进行更新代码了

4)更新:update

切换到user2的工作空间(user2目录下),user2第一次使用SVN仓库,需要检出。user2修改Demo1.java后提交。切换到user1目录,更新(update)。

5)启动SVN服务。这步需要在命令行中输入:svnserve -d -r E:\svnrepo

6)访问SVN仓库。在任意空白位置右击 — TortoiseSVN — Repo browser,URL输入:svn://192.168.1.6/repoDemo2即可浏览SVN仓库中的内容

三、SVN的目录约定

  • /trunck:开发主线
  • /branches:支线副本
  • /tags:标签副本(一旦创建,不允许修改)

1 使用trunk作为主要的开发目录

一般的,我们的所有的开发都是基于trunk进行开发,当一个版本(release)开发告一段落(开发、测试、文档、制作安装程序、打包等结束后),代码处于冻结状态(人为规定,可以通过hook来进行管理)。此时应该基于当前冻结的代码库,打tag。

当下一个版本/阶段的开发任务开始时,继续在trunk进行开发。此时,如果发现了上一个已发行版本(Released Version)有一些bug,或者一些很急迫的功能要求,而正在开发的版本(Developing Version)无法满足时间要求,这时候就需要在上一个版本上进行修改了。解决方法是基于发行版对应的tag,做相应的分支(branch)进行开发。

2、其他操作

下面的操作都位于右键菜单的TortoiseSVN中。

1)删除:delete

删除文件或目录,不能直接用Windows的删除命令来操作,那样只是没有显示出来,实际并没有删除,在更新后,删除的文件又会被更新出来的。要想从库中 删除,必须选中你要删除的内容,TortoiseSVN — delete,这样才会将这个文件标记成要删除的。确认需要删除后,使用前面所讲的提交命令,就会真正的在库中删除了。否则版本库中依旧存在这原先的代码

2)重命名:rename

重命名也不能直接用Windows的重命名命令来操作,必须选中你要重命名的文件,TortoiseSVN — rename。修改后提交就可以更新到仓库。

改名的处理方式相当于新增了一个以新名称命名的文件,原名称命名的文件进行了删除。

3)还原:revert

在未提交之前,你对前面做的操作反悔了,可以使用revert来恢复。

4)检查更新:Check for modifications

① 此功能可以显示你所做的修改有哪些还没有提交的。② 还可以看到版本库里的改动,即别人提交了哪些文件的改动,你还没更新到本地。

5)导出:export

使用SVN的工作空间每个目录下面都有一个.svn隐藏目录,利用SVN的export命令可轻松地导出不含.svn目录的工作空间。

3、冲突问题的解决(☆)

何时发生:假设user3和user4目录下的Demo1.java都更新到了最新版本号100。user3修改Demo1.java,提交至仓库,此时显示提交成功,这个时候Demo.java文件版本号变成了101。若 user4也修改Demo1.java并提交(在版本号为100的上面进行文件修改,由于不是在最新的101版本号上进行的修改和提交,所以显示提交失败),此时user4的TortoiseSVN会报提交版本过时的错误,并提醒user4需要更新,更新时就会发 生冲突。

如果两个程序员同时修改了同一个文件呢, SVN可以合并这两个程序员的改动,实际上SVN管理源代码是以行为单位的,就是说两个程序员只要不是修改了同一行程序,SVN都会自动合并两种修改。如果是同一行,SVN会提示文件Confict, 冲突,需要手动确认。

也就是说,如果两个人同时修改了文件,然后同时进行提交,那么就会出现冲突,解决冲突的办法就是编辑冲突,或者在另外一个人提交之后,先update到最新版本,然后进行文件修改。

对于每个更新冲突的文件,Subversion会在冲突文件所在目录下放置了三个文件:

  • Demo1.java.mine:发生冲突时的本地版本。
  • Demo1.java.r3:最后更新之后的本地版本。
  • Demo1.java.r4:仓库中的最新版本。

解决方法 1:

① 在Demo1.java上右击 — TortoiseSVN — Edit conflicts,这时你需要确定哪些代码是需要的,做一些必要的修改然后保存。小技巧:编辑冲突时,可使用直接复制需要的代码到Merged窗口即可。

② 编辑完成后保存,直接选择Mark as resolved,即标记为冲突已解决。退出编辑冲突窗口,发现冲突发生时生成的三个文件被自动删除了,且Demo1.java变成了未提交状态。

③ commit,OK。

解决方法 2:

直接修改Demo1.java,把其中的一些标记删除即可(前提是服务器上的和本地的内容都需要保存)。

react 元素渲染和组件

发表于 2017-04-18 | 分类于 react

react特点

  • 1.声明式设计 −React采用声明范式,可以轻松描述应用。
  • 2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。
  • 3.灵活 −React可以与已知的库或框架很好地配合。
  • 4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
  • 5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
  • 6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

React的原理

在Web开发中,我们总需要将变化的数据实时反应到UI上,这时就需要对DOM进行操作。而复杂或频繁的DOM操作通常是性能瓶颈产生的原因(如何进行高性能的复杂DOM操作通常是衡量一个前端开发人员技能的重要指标)。React为此引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。而且React能够批处理虚拟DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从A变成B,然后又从B变成A,React会认为UI不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的。尽管每一次都需要构造完整的虚拟DOM树,但是因为虚拟DOM是内存数据,性能是极高的,而对实际DOM进行操作的仅仅是Diff部分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只需要关心在任意一个数据状态下,整个界面是如何Render的。

1 HTML 标签 vs. React 组件

React 可以渲染 HTML 标签 (strings) 或 React 组件 (classes)。

要渲染 HTML 标签,只需在 JSX 里使用小写字母的标签名。

1
2
var myDivElement = <div className="foo" />;
ReactDOM.render(myDivElement, document.getElementById('example'));

要渲染 React 组件,只需创建一个大写字母开头的本地变量。

1
2
3
var MyComponent = React.createClass({/*...*/});
var myElement = < MyComponent someProperty={true} />;
ReactDOM.render(myElement, document.getElementById('example'));

React 的 JSX 使用大、小写的约定来区分本地组件的类和 HTML 标签。

注意,

  • 原生 HTML 元素名以小写字母开头,而自定义的 React 类名以大写字母开头,比如 HelloMessage 不能写成 helloMessage。除此之外还需要注意组件类只能包含一个顶层标签,否则也会报错。
  • 在定义一个组件的时候,output的所有的标签必须被正确的闭合,这样子react框架才能正确识别每个标签
1
2
3
4
5
6
7
8
9
10
render(){
return (
<form onSubmit = {this.handleSubmit}>
<label for="name">name
<input type = "text" name="gender" value = {this.state.value} onChange = {this.handleChange}/>
</label>
<input type="submit" value="submit"/>
</form>
)
}

注意每一个input标签里面的 最后的 / 不能缺少

Components must return a single root element.

2 react 元素的不可变性

2.1 React Only Updates What’s Necessary

React DOM compares the element and its children to the previous one, and only applies the DOM updates necessary to bring the DOM to the desired state.

3 react组件

  • 组件是React中构建用户界面的基本单位。它们和外界的交互除了状态(state)之外,还有就是属性(props)。事实上,状态更多的是一个组件内部去自己维护,而属性则由外部在初始化这个组件时传递进来(一般是组件需要管理的数据)。React认为属性应该是只读的,一旦赋值过去后就不应该变化。
  • 每个组件只需要前端开发者提供一个 render 函数,把 props 和 state 映射成网页元素。
  • setState : 组件规范中定义了setState方法,每次调用时都会更新组件的状态,触发render方法。需要注意,render方法是被异步调用的,这可以保证同步的多个setState方法只会触发一次render,有利于提高性能。和props不同,state是组件的内部状态,除了初始化时可能由props来决定,之后就完全由组件自身去维护。在组件的整个生命周期中,React强烈不推荐去修改自身的props,因为这会破坏UI和Model的一致性,props只能够由使用者来决定。
  • props : 组件自身定义了一组props作为对外接口,展示一个组件时只需要指定props作为XML节点的属性。组件很少需要对外公开方法,唯一的交互途径就是props。这使得使用组件就像使用函数一样简单,给定一个输入,组件给定一个界面输出。
  • this.props.children : this.props.children 的值有三种可能:如果当前组件没有子节点,它就是 undefined ;如果有一个子节点,数据类型是 object ;如果有多个子节点,数据类型就是 array 。所以,处理 this.props.children 的时候要小心。

声明组件有两种方式,一种是直接函数function声明 一种是直接class类声明

Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.

Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen.

函数声明组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="root"></div>
<script type='text/babel'>
function Welcome(props){
return <h1>hello {props.name}</h1>
}
const element = <Welcome name = "Jhon"/>
ReactDOM.render(
element,
document.getElementById('root')
)
</script>

类声明组件 super调用了父类的constructor创造了父类的实例对象this,然后用子类的构造函数进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="root"></div>
<script type='text/babel'>
class Welcome extends React.Component {
render(){
return <h1>hello {this.props.name}</h1>
}
}
const element = <Welcome name = 'Jhon'/>
ReactDOM.render(
element,
document.getElementById('root')
)
</script>

当我们使用组件<Welcome />时,其实是对Main类的实例化——new Welcome,只不过react对这个过程进行了封装,让它看起来更像是一个标签。类和模块内部默认使用严格模式,所以不需要用use strict指定运行模式。

React.Component is an abstract base class, so it rarely makes sense to refer to React.Component directly. Instead, you will typically subclass it, and define at least a render() method.

组件的

Let’s recap what happens in this example:

  1. We call ReactDOM.render() with the <Welcome name="Jhon" /> element.
  2. React calls the Welcome component with {name: 'Jhon'} as the props.
  3. Our Welcome component returns a <h1>Hello, Jhon</h1> element as the result.
  4. React DOM efficiently updates the DOM to match <h1>Hello,Jhon</h1>.

我们可以通过 this.props对象向组件传递参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="root"></div>
<script type='text/babel'>
var HelloWorld = React.createClass({
render : function(){
return <h1>hello {this.props.name1}
<p>hello {this.props.name2}</p>
</h1>
}
})
ReactDOM.render(
<HelloWorld name1 = 'Jhon' name2 = "JiM"/>,
document.getElementById('root')
)
</script>

复合组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="root"></div>
<script type='text/babel'>
function Welcome(props){
return <h1>hello {props.name}</h1>
}
function App(){
return (
<div>
<Welcome name = 'Jhon'/>
<Welcome name = 'JiM'/>
<Welcome name = 'Kobe'/>
</div>
)
}
const element = <App />
ReactDOM.render(
element ,
document.getElementById('root')
)
</script>

注意组件实例化的时候传入的属性是js表达式的时候,要用{} 括起来 , javascritpExpression必须要用{ } 括起来

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
<div id="root"></div>
<script type='text/babel'>
function formateDate(date){
return date.toLocaleDateString();
}
function Comment(props){
console.log(props)
return (
<div className = "Comment">
<div className = 'UserInfo'>
<img className = 'avatar' src={props.author.avatarUrl} alt={props.author.name}/>
<div className = 'UserInfo-name'>
{props.author.name}
</div>
</div>
<div className = 'Comment-text'>
{props.text}
</div>
<div className = 'Comment-data'>
{formateDate(props.date)}
</div>
</div>
);
}
const comment = {
date : new Date(),
text : 'Thank you for give me the chance',
author : {
name : 'Jhon',
avatarUrl : 'http://placekitten.com/g/64/64'
}
}
const element = <Comment author = {comment.author} text = {comment.text} date = {comment.date} />
ReactDOM.render(
element,
document.getElementById('root')
)
</script>

对于组合组件要进行抽取出来,即使功能在小,要充分利用react的组件化思想,以上代码通过组件化思想进行抽取之后

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
<div id="root"></div>
<script type='text/babel'>
function formateDate(date){
return date.toLocaleDateString();
}
function UserInfo(props){
return (
<div className = 'UserInfo'>
<img calssName = 'avatar' src = {props.author.avatarUrl} alt = {props.author.name}/>
<div className = 'UserInfo-name'>
{props.author.name}
</div>
</div>
)
}
function CommentText(props){
return (
<div className = 'Comment-text'>
{props.text}
</div>
)
}
function CommentData (props){
return (
<div className = 'Comment-data'>
{formateDate(props.date)}
</div>
)
}
function Comment(props){
console.log(props)
return (
<div className = "Comment">
<UserInfo author = {props.author} />
<CommentText text = {props.text}/>
<CommentData date = {props.date}/>
</div>
);
}
const comment = {
date : new Date(),
text : 'Thank you for give me the chance',
author : {
name : 'Jhon',
avatarUrl : 'http://placekitten.com/g/64/64'
}
}
const element = <Comment author = {comment.author} text = {comment.text} date = {comment.date} />
ReactDOM.render(
element,
document.getElementById('root')
)
</script>
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
<div id="root"></div>
<script type='text/babel'>
var WebSite = React.createClass({
render : function(){
return (
<div>
<Name name = {this.props.name}/>
<Link link = {this.props.link}/>
</div>
);
}
})
var Name = React.createClass({
render : function(){
return (
<p>hello {this.props.name}</p>
);
}
})
var Link = React.createClass({
render : function(){
return (
<a href = {this.props.link}> {this.props.link}</a>
);
}
}
)
ReactDOM.render(
<WebSite name = 'Jhon' link = 'www.baidu.com'/>,
document.getElementById('root')
)
</script>

4 组件的属性

我们声明一个组件,然后看下组件到底是什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script type='text/babel'>
class ListItem extends React.Component{
render(){
return (
<li>{this.props.name}</li>
)
}
}
var element = <ListItem name = 'Jhon'/> ;
console.log(element);
ReactDOM.render(
element,
document.getElementById('root')
)
</script>

控制台输出如下

1
Object {$$typeof: Symbol(react.element), key: null, ref: null, props: Object, type: function…}
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
<div id="root"></div>
<script type='text/babel'>
class Clock extends React.Component{
constructor(props) {
super(props);
console.log(this);
}
render(){
return (
<div>
<p>hello world</p>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
)
</script>

console.log(this)输出如下

1
Clock {props: Object, context: undefined, refs: Object, updater: Object}

console.log(<Clock />) 输出如下

1
Object {$$typeof: Symbol(react.element), key: null, ref: null, props: Object, type: function…}

刚开始学习React,总是有点模糊 props和state的区别,可以看出在打印this的时候,props就是存在的,而state需要定义之后才会显示

改变constructor函数,可以看出输出了state对象

state必须被设置为一个对象,否则React编译会报错,大家可以自行尝试下

1
2
3
4
5
6
7
constructor(props) {
super(props);
this.state = {color : 'red'}
console.log(this);
console.log(this.setState);
console.log(this.props);
}
1
Clock {props: Object, context: undefined, refs: Object, updater: Object, state: Object}

以上consturctor函数中,我们也打印了 console.log(this.setState); 其实在React源码中,setState是存在于 React.Component上的原型上的

1
ReactComponent.prototype.setState = function (partialState, callback) { sonmeCode } ;

5 组件的render函数

The render() method is required.

When called, it should examine this.props and this.state and return a single React element. This element can be either a representation of a native DOM component, such as <div />, or another composite component that you’ve defined yourself.

You can also return null or false to indicate that you don’t want anything rendered. When returning null or false, ReactDOM.findDOMNode(this) will return null.

The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it’s invoked, and it does not directly interact with the browser. If you need to interact with the browser, perform your work in componentDidMount()or the other lifecycle methods instead. Keeping render() pure makes components easier to think about.

需要注意的是:

render() will not be invoked if shouldComponentUpdate() returns false.

6 PropTypes

该属性用来规范组件的props属性的类型

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
React.PropTypes.array // 陣列
React.PropTypes.bool.isRequired // Boolean 且必要。
React.PropTypes.func // 函式
React.PropTypes.number // 數字
React.PropTypes.object // 物件
React.PropTypes.string // 字串
React.PropTypes.node // 任何類型的: numbers, strings, elements 或者任何這種類型的陣列
React.PropTypes.element // React 元素
React.PropTypes.instanceOf(XXX) // 某種XXX類別的實體
React.PropTypes.oneOf(['foo', 'bar']) // 其中一個字串
React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.array]) // 其中一種格式類型
React.PropTypes.arrayOf(React.PropTypes.string) // 某種類型的陣列(字串類型)
React.PropTypes.objectOf(React.PropTypes.string) // 具有某種屬性類型的物件(字串類型)
React.PropTypes.shape({ // 是否符合指定格式的物件
color: React.PropTypes.string,
fontSize: React.PropTypes.number
});
React.PropTypes.any.isRequired // 可以是任何格式,且必要。

jsx基础

发表于 2017-04-18 | 分类于 react

1 初识jsx

React 使用 JSX 来替代常规的 JavaScript。

JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。

我们不需要一定使用 JSX,但它有以下优点:

  • JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
  • 它是类型安全的,在编译过程中就能发现错误。
  • 使用 JSX 编写模板更加简单快速。

    JSX 的基本语法规则:遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析。上面代码的运行结果如下。

JSX语法,像是在Javascript代码里直接写XML的语法,实质上这只是一个语法糖,每一个XML标签都会被JSX转换工具转换成纯Javascript代码,React 官方推荐使用JSX, 当然你想直接使用纯Javascript代码写也是可以的,只是使用JSX,组件的结构和组件之间的关系看上去更加清晰。

JSX是一种语法糖,关于如何编译JSX代码可以参见如下链接

bable%20%7B%0A%20%20return%20%3Cdiv%3EHello%20world!%3C%2Fdiv%3E%3B%0A%7D)

1
var person = <Person name={window.isLoggedIn ? window.name : ''} />;

上述代码经过JSX编译后会得到:

1
2
3
4
var person = React.createElement(
Person,
{name: window.isLoggedIn ? window.name : ''}
);
1
2
3
<MyButton color="blue" shadowSize={2}>
Click Me
</MyButton>
1
2
3
4
5
React.createElement(
MyButton,
{color: 'blue', shadowSize: 2},
'Click Me'
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//使用JSX
React.render(
<div>
<div>
<div>content</div>
</div>
</div>,
document.getElementById('example')
);
//不使用JSX
React.render(
React.createElement('div', null,
React.createElement('div', null,
React.createElement('div', null, 'content')
)
),
document.getElementById('example')
);
  • r如果标签是以小写字母开头,那么React会寻找内置标签,比如div sapn等
  • 如果标签是以大写字母开头,React会调用React.createElement去编译这个标签

2 我们可以在JSX中

  • 插入任何js表达式
1
2
3
4
5
6
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
1
2
3
4
5
6
7
8
<div id="root"></div>
<script type='text/babel'>
ReactDOM.render(
<div >{1+3}</div>
,
document.getElementById('root')
)
</script>
  • props如果没有提供值,那么默认值是true,以下声明是等价的
1
2
3
<MyTextBox autocomplete />
<MyTextBox autocomplete={true} />
  • props声明可以是字符串类型以及js表达式 {} 包括的代码
1
2
3
<MyComponent message="hello world" />
<MyComponent message={'hello world'} />
  • 指明类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const element1 = <div className='hello'>hello1</div>
//等价于以下
const element2 = React.createElement(
'div',
{className:'sayHello2'},
'hello2'
)
//内部大概的解析过程
// Note: this structure is simplified
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world'
}
};

完整代码如下

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
<div id="root1"></div>
<div id="root2"></div>
<script type='text/babel'>
const user = {
firstName:'Jhon',
lastName:'Kobe'
};
function formatName(user){
if(user){
return user.firstName + " " + user.lastName ;
}
return 'stranger hello '
}
const element1 = <div className='hello'>hello1</div>
const element2 = React.createElement(
'div',
{className:'sayHello2'},
'hello2'
)
ReactDOM.render(
element1,
document.getElementById('root1')
)
ReactDOM.render(
element2,
document.getElementById('root2')
)
</script>
  • 注意render函数中只能有一个顶层标签
1
2
3
4
5
6
7
8
9
10
<div id="root"></div>
<script type='text/babel'>
ReactDOM.render(
<div data-attr = 'name'>this is a test
<h2>this is a test 2 </h2>
</div>,
document.getElementById('root')
)
</script>

以下是错误写法,顶层标签只能含有一个。

1
2
3
4
5
6
7
8
<div id="root"></div>
<script type='text/babel'>
ReactDOM.render(
<div data-attr = 'name'>this is a test</div>
<h2>this is a test 2 </h2>,
document.getElementById('root')
)
</script>
1
2
3
4
5
6
7
8
9
10
11
<script type='text/babel'>
var HelloWorld = React.createClass({
render : function(){
return <h1>hello {this.props.name1}</h1>
<h1>hello {this.props.name2}</h1>
}
})
<HelloWorld name1 = 'Jhon' name2 = "JiM"/>
//这样也无法渲染出来
</script>
  • 可以使用三元运算符,但是不能使用if-else表达式
1
2
3
4
5
6
7
8
9
10
const i = 1 ;
ReactDOM.render(
<div >{
i == 1 ? ' 2' : '3'
}
</div>
,
document.getElementById('root')
)
1…456…20
JiM-W

JiM-W

keep fighting!

195 日志
52 分类
90 标签
© 2017 JiM-W
由 Hexo 强力驱动
主题 - NexT.Pisces