React特点:
声明式编程 多平台适配 组件化开发
React开发依赖:
- react:包含react必须的核心代码
- react-dom:react渲染在不同平台所需要的核心代码
- babel:将jsx转换成React代码的工具
- jsx(JavaScript XML):普通的HTML方式开发界面
- 通过Babel自动转成React.renderElement
jSX语法
使用的前提:
1 | <script src="../react/babel.min.js"></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 | {/*绑定普通属性*/} |
- 绑定事件
- 如果原生DOM原生有一个监听事件,操作方式
- 获取DOM原生,添加监听事件;
- 在HTML原生中,直接绑定onclick;
- 在React中实现:
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写;
- 我们需要通过{}传入一个事件处理函数,这个函数会在事件发生时被执行
this 的绑定
btnClick函数并不是我们主动调用的,而且当button发生改变时,React内部调用了btnClick函数;而它内部调用时,并不知道要如何绑定正确的this
解决:
- 方案一:bind给btnClick显示绑定this
- 方案二:使用 ES6 class fields 语法
- 方案三:事件监听时传入箭头函数(推荐)
- 事件参数传递
- 在执行事件函数时,有时获取一些参数信息:比如event对象、其他参数
- 情况一:获取event对象
- 很多时候我们需要拿到event对象来做一些事情(比如阻止默认行为)
- 假如我们用不到this,那么直接传入函数就可以获取到event对象;
情况二:获取更多参数 - 有更多参数时,我们最好的方式就是传入一个箭头函数,主动执行的事件函数,并且传入相关的其他参数;
- 情况一:获取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">* ```js</span><br><span class="line"> React.createElement(</span><br><span class="line"> MyButton,</span><br><span class="line"> {color: 'blue', shadowSize: 2},</span><br><span class="line"> 'Click Me'</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"> * ```javascript</span><br><span class="line"> //源码中 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 | <script type="text/babel"> |