react

react读书笔记

现在基本上angularjs,react和vue都用过好几个项目了。根据项目,技术栈也在不断变化,长时间不写总有点生疏,最近回过头来看一下react的东西,记录一下,方便后面回过头来看…

reactjs

  • 一个根据数据驱动视图的UI库,相当于MVC中的view
  • reactjs帮我们将视图切分成各个独立的小块,每个快就是一个组件,可以任意嵌套拼装成需要的视图
  • reactjs不是一个框架,她提供了视图层的解决方案,还要依赖类似于react-router和redux等库才能提供完整的解决方法

前端组件化

  • 什么问题导致我们需要前端页面进行组件化?
  • 组件化是为了解决什么问题?

答:为了解决结构复用,避免产生重复冗余的代码

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<div class='wrapper'>
<button class='like-btn'>
<span class='like-text'>点赞</span>
<span>👍</span>
</button>
</div>
<script>
let likeButton = document.querySelector('.like-btn');
let likeButtonText = document.querySelector('.like-text');
let isLike = false;
likeButton.addEventListener('click',function(){
isLike = !isLike;
likeButtonText.innerText = isLike?'取消':'点赞';
}),false;
</script>
</body>

这样就可以完成一个点击按钮切换按钮功能,可是这个功能要共享,只能将全部的代码全部粘贴过去,复用性何在?

组件复用-引入class

假如定义一个构造函数

1
2
3
4
5
6
7
8
9
10
11
class LikeButton{
render(){
return
`<div class='wrapper'>
<button class='like-btn'>
<span class='like-text'>点赞</span>
<span>👍</span>
</button>
</div>`
}
}

这样就可以使用new生成实例了。

1
2
let buttonOne = new LikeButton()
document.getElementById('root').innerHTML = buttonOne.render();

问题又来了,点击没有作用,因为没有绑定事件。没法给字符串添加事件,只有dom结构才能绑定dom事件

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
  // 使用createElement方法将字符串转换成dom对象
let string2Html = function(str){
let divDom = document.createElement('div');
divDom.innerHTML = str;
return divDom;
}

class LikeButton{
constructor(){
this.state = {
isLike:false
}
}
changeButtonText(){
const likeText = this.element.querySelector('.like-text');
this.state.isLike = !this.state.isLike;
likeText.innerHTML = this.state.isLike?'取消':'点赞'
}
render(){
this.element = string2Html(`
<div class='wrapper'>
<button class='like-btn'>
<span class='like-text'>点赞</span>
<span>👍</span>
</button>
</div>
`);
this.element.addEventListener('click',this.changeButtonText.bind(this))
return this.element
}
}
let button1 = new LikeButton()
document.querySelector('.root').appendChild( button1.render()) // 将生成的dom实例放入root节点

问题又来了,再changeButtonText方法中,通过state的状态来修改按钮的文字,这样频繁操作dom,这样手动管理state的变化刷新dom视图
react提供了一种解决办法: 状态改变-》更新状态-》用新的state重新调用render方法刷新视图

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
// 使用createElement方法将字符串转换成dom对象
let string2Html = function(str){
let divDom = document.createElement('div');
divDom.innerHTML = str;
return divDom;
}

class LikeButton{
constructor(){ // 初始化一个状态
this.state = {
isLike:false
}
}
// 状态变化更新状态,重新使用新的状态调用render生成视图
setState(state){
const preElement = this.element;
this.state = state;
this.element = this.render();
if(this.onStateChange){ // 判断一下实例有没有这个方法
// 通过这个方法的调用刷新视图
this.onStateChange(preElement,this.element);
}
}

// 调用状态更新的方法
changeButtonText(){
this.setState({
isLike:!this.state.isLike
})
}
render(){
this.element = string2Html(`
<div class='wrapper'>
<button class='like-btn'>
<span class='like-text'>${this.state.isLike?"取消":"点赞"}</span>
<span>👍</span>
</button>
</div>
`);
this.element.addEventListener('click',this.changeButtonText.bind(this))
return this.element
}
}

// 调用这个构造函数生成这个一个简单功能的组件
let button1 = new LikeButton()
let container = document.querySelector('.root');
container.appendChild( button1.render()) // 将生成的dom实例放入root节点
button1.onStateChange= function(pre,curr){
container.insertBefore(curr,pre) // 在上次渲染的元素前面插入元素
container.removeChild(pre) // 删除旧的dom
}

完成一个基本的组件化,那么又有一个细节,就是每次state状态变化的时候,都要操作dom刷新视图,这个在reactjs中通过virtual-dom的策略,可以将几次视图的刷新进行合并,提高性能。但是还有一个问题,就是如果要新建一个组件,那么中间的setState更新状态的方法是重复的,所以需要抽出一个公共的类

抽象出公共组件的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class React.Component {
setState(state){
const preElement = this.element; // 组件的dom
this.state = state; // 根据传入的新的state更新组件的state
this.element = this._renderDOM() // 重新调用render方法生成新的dom
if(this.onStateChange){
this.onStateChange(preElement,this.element) // 通知组件的实例更新页面的视图
}
}
_renderDOM(){
this.element = string2Html(this.render()) // 每个组件都有一个render方法
if(this.onClick){
this.element.addEventListener('click',this.onClick.bind(this));
}
return this.element
}
}

这样所有的组件都可以集成这个父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import {Component} from 'React';
class LikeButton extends Component{
constructor () {
super()
this.state = { isLiked: false }
}
onClick () {
this.setState({
isLiked: !this.state.isLiked
})
}

render () {
return `
<button class='like-btn'>
<span class='like-text'>${this.state.isLiked ? '取消' : '点赞'}</span>
<span>👍</span>
</button>
`
}
}

这样定义的组件LikeButton就可以在state状态变更的时候调用基类的setState方法重新render

参考链接

react.js

初到贵宝地,有钱的给个钱场,没钱的挤一挤给个钱场