React特点:

  • 声明式编程 多平台适配 组件化开发

  • React开发依赖:

    • react:包含react必须的核心代码
    • react-dom:react渲染在不同平台所需要的核心代码
    • babel:将jsx转换成React代码的工具
      • jsx(JavaScript XML):普通的HTML方式开发界面
      • 通过Babel自动转成React.renderElement

jSX语法

使用的前提:

1
2
<script src="../react/babel.min.js"></script>
<script type="text/babel"></script>
  • JSX是一种JavaScript语法扩展(extension) 也在很多地方称为 JavaScript XML
  • react all in js React选择JSX原因
    • React认为渲染逻辑本质上与其他UI逻辑存在内在耦合
    • 比如UI需要绑定事件(button、a原生等等)
    • 比如UI中需要展示数据状态,在某些状态发生改变时,又需要改变UI;
  • JSX书写规范
    • JSX的顶层只有一个根元素 所以很多时候都在外层包裹一个div
    • 一般在jsx外层用一个()包裹
    • JSX中可以写双标签也可以单标签 如果是但标签必须用 /> 结尾

JSX使用

  • jsx注释

    • {/* 我是一段注释*/}
  • 嵌入变量

    • 情况一:当变量是Number、String、Array类型时,可以直接显示
    • 情况二:当变量是null、undefined、Boolean类型时,内容为空;
      • 如果希望可以显示null、undefined、Boolean,那么需要转成字符串;
      • 转换的方式有很多,比如toString方法、和空字符串拼接,String(变量)等方式;
    • 情况三:对象类型不能作为子元素(not valid as a React child)
  • 嵌入表达式

    • 运算符表达式
    • 三元表达式
    • 函数调用
  • JSX绑定属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{/*绑定普通属性*/}
<h2 title={title}>标题</h2>
<img src={getSizeImage(imgUrl,140)} alt=""/>
<a href={link}>bilibili</a>


{/*绑定class*/}
<div className="box">我是div</div>
<div className={"box" + (active?"active":"")}>我也是div</div>
<label htmlFor=""></label>


{/*绑定style*/}
<div style={{color:"red",fontSize:"30px"}}>div绑定style</div>
  • 绑定事件
  • 如果原生DOM原生有一个监听事件,操作方式
    • 获取DOM原生,添加监听事件;
    • 在HTML原生中,直接绑定onclick;
      • 在React中实现:
    • React 事件的命名采用小驼峰式(camelCase),而不是纯小写;
    • 我们需要通过{}传入一个事件处理函数,这个函数会在事件发生时被执行
  • this 的绑定

  • btnClick函数并不是我们主动调用的,而且当button发生改变时,React内部调用了btnClick函数;而它内部调用时,并不知道要如何绑定正确的this

    • 解决:

      • 方案一:bind给btnClick显示绑定this
      • 方案二:使用 ES6 class fields 语法
      • 方案三:事件监听时传入箭头函数(推荐)
  • 事件参数传递
  • 在执行事件函数时,有时获取一些参数信息:比如event对象、其他参数
    • 情况一:获取event对象
      • 很多时候我们需要拿到event对象来做一些事情(比如阻止默认行为)
      • 假如我们用不到this,那么直接传入函数就可以获取到event对象;
        情况二:获取更多参数
      • 有更多参数时,我们最好的方式就是传入一个箭头函数,主动执行的事件函数,并且传入相关的其他参数;

React条件渲染

  • 常见的条件渲染方式

    • 方式一:条件判断语句
      • 适合逻辑较多的情况
    • 方式二:三元运算符
      • 适合逻辑比较简单、
    • 与运算符&&
      • 适合如果条件成立,渲染某一个组件;如果条件不成立,什么内容也不渲染;
    • v-show的效果
      • 主要是控制display属性是否为none

React列表渲染

  • 在React中,展示列表最多的方式就是使用数组的map高阶函数;
  • 很多时候我们在展示一个数组中的数据之前,需要先对它进行一些处理:
    • 比如过滤掉一些内容:filter函数
    • 比如截取数组中的一部分内容:slice函数

JSX的本质

  • 实际上 jsx就是 React.createElement(component,props,…children)函数的语法糖

    • jsx通过babel转化
    • 所有的jsx最终都会被转成React.createElement的函数调用
  • 比如:

    • <MyButton color="blue" shadowSize={2}>
        Click Me
      </MyButton>
      <!--hexoPostRenderEscape:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">* 编译为</span><br><span class="line"></span><br><span class="line">* &#96;&#96;&#96;js</span><br><span class="line">  React.createElement(</span><br><span class="line">    MyButton,</span><br><span class="line">    &#123;color: &#39;blue&#39;, shadowSize: 2&#125;,</span><br><span class="line">    &#39;Click Me&#39;</span><br><span class="line">  )</span><br></pre></td></tr></table></figure>:hexoPostRenderEscape-->
      
      
      
  • 源码中:

  • createElement 需传递三个参数

    • 参数1 : type

      • 当前ReactElement类型
      • 如果是标签元素 使用字符串表示”div”
      • 如果是组件元素 直接使用组件名称
    • 参数2:config

      • 所有jsx中的属性都在config中以对象的属性和值的形式存储
    • 参数3:children

      • 存放在标签中的内容,以children数组的方式进行存储

      • 源码中对children进行的操作

      • // Children can be more than one argument, and those are transferred onto
        // the newly allocated props object.
        
        //获取到除了type config两个参数后面的参数数量 (arguments是总参数数量)
          const childrenLength = arguments.length - 2;
          if (childrenLength === 1) {
            props.children = children;
          } else if (childrenLength > 1) {
            const childArray = Array(childrenLength);
            for (let i = 0; i < childrenLength; i++) {
              childArray[i] = arguments[i + 2];
            }
            if (__DEV__) {
              if (Object.freeze) {
                Object.freeze(childArray);
              }
            }
              //将取得的参数放在了childArray这个数组中 然后给了children
            props.children = childArray;
          }
        <!--hexoPostRenderEscape:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">      </span><br><span class="line"></span><br><span class="line">## 虚拟DOM</span><br><span class="line"></span><br><span class="line">#### 虚拟DOM创建过程</span><br><span class="line"></span><br><span class="line">* 通过React.createElement最终创建出来一个ReactElement对象</span><br><span class="line"></span><br><span class="line">  * &#96;&#96;&#96;javascript</span><br><span class="line">    &#x2F;&#x2F;源码中 createElement方法返回</span><br><span class="line">    return ReactElement(</span><br><span class="line">        type,</span><br><span class="line">        key,</span><br><span class="line">        ref,</span><br><span class="line">        self,</span><br><span class="line">        source,</span><br><span class="line">        ReactCurrentOwner.current,</span><br><span class="line">        props,</span><br><span class="line">      );</span><br></pre></td></tr></table></figure>:hexoPostRenderEscape-->
    • 创建ReactElement对象的原因

      • React利用ReactElement对象组成了一个JavaScript的对象树
      • JS的对象树就是 虚拟DOM(Virtual DOM)

  • ReactDOM.render函数 将Virtual DOM映射到真实DOM中

  • jsx -> createElement函数 ->ReactElement (对象树) ->ReactDOM.render ->真实DOM

为什么使用虚拟DOM

  • 采用虚拟DOM而不修改真实DOM原因
    • 原有的开发模式 状态发生改变很难跟踪,不方便对应用程序调试
    • 操作真实DOM的性能较低,传统开发模式会频繁DOM操作,性能很低
  • DOM操作性能非常低
    • document.createElement本身创建的对象就非常复杂
    • DOM会引起浏览器回流和重绘
  • 通过Virtual DOM可以减少对DOM频繁操作

声明式编程

  • 虚拟DOM帮助我们从命令式编程转到了声明式编程的模式
  • React官方说法
    • Virtual DOM是一种编程理念
    • 在这个理念中 UI以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的Javascript对象
    • 我们通过ReactDOM.render能让虚拟DOM和真实DOM同步起来 这个过程中叫做协调
  • 这种编程方式赋予React声明式的API

React中设计原则:state 中的数据不可变性

JSX案例练习

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
<script type="text/babel">
class App extends React.Component {
constructor() {
super()
this.state = {
books: [
{
id: 1,
name: '《算法导论》',
date: '2006-9',
price: 85.00,
count: 2
},
{
id: 2,
name: '《UNIX编程艺术》',
date: '2006-2',
price: 59.00,
count: 1
},
{
id: 3,
name: '《编程珠玑》',
date: '2008-10',
price: 39.00,
count: 1
},
{
id: 4,
name: '《代码大全》',
date: '2006-3',
price: 128.00,
count: 1
}]
}
}
renderBooks(){
return (
<div>
<table>
<thead>
<tr>
<th></th>
<th>书名</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>

</tr>
</thead>
<tbody>
{
this.state.books.map((item, index) => {
return (
<tr>
<td>{index + 1}</td>
<td>{item.name}</td>
<td>{item.date}</td>
<td>{formatPrice(item.price)}</td>
<td>
<button disabled={item.count<=1} onClick={e=>this.changeBookCount(index,-1)}>-</button>
<span className='count1'>{item.count}</span>
<button onClick={e=>this.changeBookCount(index,+1)}>+</button>
</td>
<td><button onClick = {e=>this.removeBook(index)}>移除</button></td>
</tr>
)
})
}
</tbody>
</table>
<h2>总价格:{this.getTotalPrice()}</h2>
</div>

)
}

renderEmpty(){
return <h2>购物车为空</h2>
}

render() {
return this.state.books.length?this.renderBooks():this.renderEmpty()
}

changeBookCount(index,count){
const newBooks=[...this.state.books]
newBooks[index].count += count
this.setState({
books:newBooks
})
}

removeBook(index){
//React中设计原则:state 中的数据不可变性
this.setState({
books:this.state.books.filter((item,indey)=>index!=indey)
})
}


getTotalPrice(){
//for循环遍历
// let totalPrice = 0
// for(let item of this.state.books){
// totalPrice += item.price * item.count
// }
// return formatPrice(totalPrice)

//filter/map/reduce(归纳)
//回调函数的参数
//参数一:上一次回调函数的结果 (第一次没有上一次回调函数的结果,没有就使用初始化值)
const totalPrice = this.state.books.reduce((preValue,item)=>{
return preValue + item.price * item.count
},0)
return formatPrice(totalPrice)
}
}

ReactDOM.render(<App />, document.getElementById("app"))