1. React入门 ## 1.1. React基本认识
## 1.2. React基本使用
## 1.3. JSX的理解和使用
## 1.4. 模块与模块化, 组件与组件化的理解
 
2. React组件化编程 ## 2.1. 组件的定义与使用
## 2.2. 组件的3大属性: state, props, refs
## 2.3. 组件中的事件处理
## 2.4. 组件的组合使用
## 2.5. 组件收集表单数据
## 2.6. 组件的生命周期
## 2.7. 虚拟DOM与DOM diff算法
## 2.8. 命令式编程与声明式编程
 
1. React入门 1.1. React的基本认识 1). Facebook开源的一个js库
2). 一个用来动态构建用户界面的js库
3). React的特点
    Declarative(声明式编码)
    Component-Based(组件化编码)
    Learn Once, Write Anywhere(支持客户端与服务器渲染)
    高效
    单向数据流
4). React高效的原因
    虚拟(virtual)DOM, 不总是直接操作DOM(批量更新, 减少更新的次数) 
    高效的DOM Diff算法, 最小化页面重绘(减小页面更新的区域)
 
1.2. React的基本使用 相关 js 库
react.js: React 的核心库 
react-dom.js: 提供操作 DOM 的 react 扩展库 
babel.min.js: 解析 JSX 语法代码转为纯 JS 语法代码的库 
 
在页面中导入 js
1 2 3 <script type="text/javascript" src="../js/react.development.js"></script> <script type="text/javascript" src="../js/react-dom.development.js"></script> <script type="text/javascript" src="../js/babel.min.js"></script> 
 
编码
1 2 3 4 5 6 7 8 9 <script type="text/babel"> //必须声明 babel //创建虚拟 DOM 元素 const vDom = <h1>Hello React</h1> //千万不要加引号 //渲染虚拟 DOM到页面真实 DOM 容器中 ReactDOM.render(vDom, document.getElementById('test')) </script> 
 
1.3. JSX的理解和使用 1). 理解
    * 全称: JavaScript XML
    * react定义的一种类似于XML的JS扩展语法: XML+JS
    * 作用: 用来创建react虚拟DOM(元素)对象
2). 编码相关
    * js中直接可以套标签, 但标签要套js需要放在{}中
    * 在解析显示js数组时, 会自动遍历显示
    * 把数据的数组转换为标签的数组: 
        var liArr = dataArr.map(function(item, index){
            return <li key={index}>{item}</li>
        })
3). 注意:
    * 标签必须有结束
    * 标签的class属性必须改为className属性
    * 标签的style属性值必须为: {{color:'red', width:12}}
/**
 *
 * jsx 语法规则
 * 1. 定义虚拟DOM时, 不要写引号
 * 2.标签中混入js表达式时要用 {}, if, for 都不是表达式。能返回一个值的 东西就是表达式。表达式可以放到 赋值符号右边。
 * 3.样式的类名指定不要用class,要用 className, class 是关键字
 * 4.内联样式,要用 style={{key:value}}的形式, 最外面的花括号是和2中一样的,里面的那个花括号 是js中 对象的写法。
 * 5. jsx 只能有一个 根标签
 * 6.标签必须闭合, jsx里面标签会转换为html标签
 * 7标签首字母,小写字母开头则转换为html同名元素,如果html中没有就会报错。如果首字母大写,react会去渲染组件,如果没有就报错。
 *
 * 8.函数式组件,函数名 必须 首字母 大写,,参考第7条
 * 9,组件必须用标签的方式 放到 render 方法里面, 标签必须闭合。
 *
 */
/**
 * js 中类相关的知识点
 * 1. 类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
 * 2. 子类必须调用父类的构造器,也就是必须调用super来初始化父类。
 * 3.类中所定义的方法 都是放到了类的原型 对象上,供实例去使用。
 *
 *
 *
 */
 
1.4. 几个重要概念理解 1). 模块与组件 1. 模块:
  	理解: 向外提供特定功能的js程序, 一般就是一个js文件
  	为什么: js代码更多更复杂
  	作用: 复用js, 简化js的编写, 提高js运行效率
2. 组件: 
    理解: 用来实现特定功能效果的代码集合(html/css/js)
  	为什么: 一个界面的功能太复杂了
  	作用: 复用编码, 简化项目界面编码, 提高运行效率
 
2). 模块化与组件化 1. 模块化:
    当应用的js都以模块来编写的, 这个应用就是一个模块化的应用
2. 组件化:
    当应用是以多组件的方式实现功能, 这上应用就是一个组件化的应用
 
2. react组件化开发 2.1. 基本理解和使用 1). 自定义的标签: 组件类(函数)/标签
2). 创建组件类
    //方式1: 无状态函数(简单组件, 推荐使用) 没有状态的称为简单组件
    function MyComponentFunction(props) {
        return <h2>我是函数定义的组件,适用于 简单组件 的定义</h2>
    }
    //方式2: ES6类语法(复杂组件, 推荐使用) 有状态的称为复杂组件
    class MyComponentClass extends React.Component {
        render () {
            // render 是放在哪里的? MyComponentClass 的原型对象上的,供实例使用。
            // render 中的 this 是谁? MyComponentClass 实例对象。 <=> MyComponentClass 组件实例对象
            <h2>我是类定义的组件,适用于 复杂组件 的定义</h2>
        }
    }
3). 渲染组件标签
    // 1. react 解析组件标签找到 MyComponentFunction 组件
    // 2. 发现组件是使用函数定义的,随后调用该函数,将返回的 虚拟DOM转为真是DOM,随后渲染页面
    //ReactDOM.render(<MyComponentFunction></MyComponentFunction>, document.getElementById("test"))
    ReactDOM.render(<MyComponentFunction/>, document.getElementById("test"))
    //ReactDOM.render(<MyComponentClass></MyComponentClass>, document.getElementById("test"))
    ReactDOM.render(<MyComponentClass />,  document.getElementById("test"))
        // 1. react 解析组件标签找到 MyComponentClass 组件
        // 2. 发现组件是使用 类 定义的,随后 new 出来该实例,并通过 该实例调用原型上的render 方法
        // 3. 将render 返回的虚拟DOM转换为真实DOM,渲染页面
        
        
4). ReactDOM.render()渲染组件标签的基本流程
    React内部会创建组件实例对象/调用组件函数, 得到虚拟DOM对象
    将虚拟DOM并解析为真实DOM
    插入到指定的页面元素内部
 
2.2. 组件的3大属性: state 1. 组件被称为"状态机", 页面的显示是根据组件的state属性的数据来显示
2. 初始化指定:
    constructor() {
      super()
      this.state = {
        stateName1 : stateValue1,
        stateName2 : stateValue2
      }
    }
3. 读取显示: 
    this.state.stateName1
4. 更新状态-->更新界面 : 
    this.setState({stateName1 : newValue})
 
this.state 继承自  父类 React.Component 
后续 hooks 也可以让 函数式组件 有 stage。
实例:
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 <script type="text/babel" charset="utf-8">     class Weather extends React.Component {         constructor(props) {             super(props);             this.state = {                 isHot: true,             }         }         render(){             return (                 <h2>今天天气很  {this.state.isHot? "炎热": "凉爽"}</h2>             )         }     }     ReactDOM.render(<Weather/>, document.getElementById("test")) </script> <script type="text/babel" charset="utf-8">     class Weather extends React.Component {         constructor(props) {             super(props);             this.state = {                 isHot: true,             }             this.changeWeather = this.changeWeather.bind(this) // 绑定 this, 这里重新赋值了一个this.changeWeather,已经不是之前的  原型链上的 this.changeWeather 方法了。          }         render(){             return (                 <h2 onClick={this.changeWeather}>今天天气很  {this.state.isHot? "炎热": "凉爽"}</h2>             )         }         changeWeather(){ // changeWeather 函数放到 类里面了             // changeWeather 放在哪里,  weather 的原型对象上, 供实例使用             // 由于 changeWeather 是作为onClick的回调,所以不是通过实例调用的,是直接调用的             // 就是说 把 this.changeWeather 这个函数名 重新赋值给了另外一个变量,然后 另外一个变量直接调用了。             // const x =  this.changeWeather             // x()             //  类似上面这2行。             // this.state = {isHot: !this.state.isHot}  state 不能直接更改             // 直接更改不会 改变页面变化的             const isHot = this.state.isHot             console.log(this)             this.setState({isHot: !isHot})         }     }     ReactDOM.render(<Weather/>, document.getElementById("test")) </script> 
 
经过简化之后,去掉了 构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <script type="text/babel" charset="utf-8">     class Weather extends React.Component {         state = { // 这里时间给 实例对象 添加一个  成员属性。             isHot: true,         }         render(){             return (                 <h2 onClick={this.changeWeather}>今天天气很  {this.state.isHot? "炎热": "凉爽"}</h2>             )         }         changeWeather = ()=>{ // 箭头函数,里面的this 就是 外面的weather类实例对象             const isHot = this.state.isHot             this.setState({isHot: !isHot})  // 这里更新 动作 是一个合并的动作,不会把state 对象里面的 其他值给弄没了。         }     }     ReactDOM.render(<Weather/>, document.getElementById("test")) </script> 
 
2.2. 组件的3大属性: props 所有组件标签的属性的集合对象
给标签指定属性, 保存外部数据(可能是一个function)
在组件内部读取属性: this.props.propertyName
作用: 从目标组件外部向组件内部传递数据
对props中的属性值进行类型限制和必要性限制
    Person.propTypes = {
        name: React.PropTypes.string.isRequired,
        age: React.PropTypes.number.isRequired
    }
扩展属性: 将对象的所有属性通过props传递
    <Person {...person}/>
 
html 标签 属性, 就对应到 react 中的 prop了
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 <script type="text/babel" charset="utf-8">     class Person extends React.Component {         render(){             return (                 <ul>                     <li>姓名:{this.props.name}</li>                     <li>性别:{this.props.sex}</li>                     <li>年龄:{this.props.age}</li>                 </ul>             )         }     }     // html 标签 属性, 就对应到 react 中的 prop了     ReactDOM.render(<Person name="bright" sex="男" age="20"/>, document.getElementById("test"))     ReactDOM.render(<Person name="annie" age="18" sex="女"/>, document.getElementById("test1"))     const p = {name: "hans", age: 18, sex: "nan"}     ReactDOM.render(<Person {...p}/>, document.getElementById("test2")) //这里使用 ... 三个点 展开运算符,只能用于标签属性展开 </script> 
 
对标签属性进行 限制,react 15.5 之后的版本就不能如下这么使用了,
1 2 3 4 5 6 7 Person.propTypes = {     name: React.PropTypes.string,     age: React.PropTypes.number,     sex:React.PropTypes.string } 
 
使用  prop-types.js 来进行标签属性限制
1 2 3 4 <script type="text/javascript" src="../js/react.development.js"></script>  引入这个 全局多一个 React 对象 <script type="text/javascript" src="../js/react-dom.development.js"></script> 引入这个 全局多一个 ReactDOM 对象 <script type="text/javascript" src="../js/prop-types.js"></script>  引入这个全局多一个  PropTypes 对象 
 
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 <script type="text/babel" charset="utf-8">     class Person extends React.Component {         render() {             const {name, age, sex} = this.props             return (                 <ul>                     <li>姓名:{name}</li>                     <li>性别:{sex}</li>                     <li>年龄:{age + 1}</li>                 </ul>             )         }     }     // 对传递的标签属性 进行 类型的限制,如果整形,字符串型。     // 对 默认值的限制     // 对必填属性的限制     Person.propTypes = {         name: PropTypes.string.isRequired,         age: PropTypes.number,         sex: PropTypes.string,         speak:PropTypes.func,// 限制为函数类型     }     Person.defaultProps = {         sex: 'null',         age: 0     }     // html 标签 属性, 就对应到 react 中的 prop了     ReactDOM.render(<Person name="bright" sex="男" age={22}/>, document.getElementById("test"))     ReactDOM.render(<Person name="annie" age={15} sex="女"/>, document.getElementById("test1"))      	ReactDOM.render(<Person name="annie"/>, document.getElementById("test1")) 	// 这里再来个 标签,age , sex 都没有设置,那么 就会使用到 默认值 Person.defaultProps  中的设置的值。      	ReactDOM.render(<Person />, document.getElementById("test3"))     //Warning: Failed prop type: The prop `name` is marked as required in `Person`, but its value is `undefined`. 	// 这里如果 标签属性 name 都没设置,那么会 报警告的,因为 我们现在了name是 必填的         name: PropTypes.string.isRequired, 	//这里 speak 限制是func 类型,这里用双引号表示字符串类型,这是不对的,会报 警告的。     ReactDOM.render(<Person name="annie" age={15} sex="女" speak="speak"/>, document.getElementById("test1"))     //Warning: Failed prop type: Invalid prop `speak` of type `string` supplied to `Person`, expected `function`. 	// 正确的做法如下: 使用 {speak} 这样的方式设置标签speak的属性     ReactDOM.render(<Person name="annie" age={15} sex="女" speak={speak}/>, document.getElementById("test1"))     function speak(){         console.log("speak function")     }     const p = {name: "hans", age: 18, sex: "nan"}     ReactDOM.render(<Person {...p}/>, document.getElementById("test2"))  	//这里使用 ... 三个点 展开运算符,只能用于标签属性展开      </script> 
 
完成对标签属性的限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 // 对传递的标签属性 进行 类型的限制,如果整形,字符串型。 // 对 默认值的限制 // 对必填属性的限制 Person.propTypes = {     name: PropTypes.string.isRequired, //限制name必须的,且是字符串     age: PropTypes.number, // 限制年龄是 数字     sex: PropTypes.string, // 限制性别是字符串     speak:PropTypes.func,// 限制为函数类型 } Person.defaultProps = {     sex: 'null',     age: 0 } 
 
标签属性 props 是只读的,不能修改的
1 TypeError: "name" is read-only 
 
props的简写方式,直接放到类定义体里面,前面加上 static 关键字
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   class Person extends React.Component {       render() {           const {name, age, sex} = this.props           // this.props.name="12312" 属性是只读的,不能修改           return (               <ul>                   <li>姓名:{name}</li>                   <li>性别:{sex}</li>                   <li>年龄:{age + 1}</li>               </ul>           )       }       // 对传递的标签属性 进行 类型的限制,如果整形,字符串型。       // 对 默认值的限制       // 对必填属性的限制       static  propTypes = {           name: PropTypes.string.isRequired, //限制name必须的,且是字符串           age: PropTypes.number, // 限制年龄是 数字           sex: PropTypes.string, // 限制性别是字符串           speak:PropTypes.func,// 限制为函数类型       } // 如果不加 static 就会是 加到 类的实例对象上了,例如之前的 stage . // 这里 需要加上 static,这个props 是 属于 这个 类的,而不是 实例对象。       static defaultProps = {           sex: 'null',           age: 0       }   } 
 
类中的构造器到底有什么作用呢? 传入了props,如果不传给super会这么样呢?
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     class Weather extends React.Component {         constructor(props) {             super(props);             this.state = {                 isHot: true,             }             this.changeWeather = this.changeWeather.bind(this) // 绑定 this, 这里重新赋值了一个this.changeWeather,已经不是之前的  原型链上的 this.changeWeather 方法了。         } 构造函数的作用: 官网描述, 通过给 this.state 赋值对象来初始化 内部stage的。 为事件函数绑定实例的。 在构造函数中 不要调用 setStage() 方法, 如果你的组件需要使用 stage,直接在构造函数中 为 this.state 赋值。		     class Person extends React.Component {         constructor(props) {             super(props); // 构造器是否接收 props, 是否传递给super,取决于: 是否希望在构造器中通过 this 访问 props 			// 就是说这里没有传给 super 那么在后面的 构造器代码中 就不能使用 this.props 访问了。发现 在 render 方法还是能继续 使用 this.props 的。             console.log(props)         } 如果不初始化 stage 或 不进行方法绑定,则不需要为 react组件实现构造函数。 在react 组件挂载之前,会调用他的构造函数,在为 react.component子类实现构造函数 时, 应在其他语句之前调用super(props),否则,this.props 在构造函数中可能出现未定义的bug。 
 
函数式组件的 propsm, 函数式组件也只能 使用使用 props, 利用函数的参数来传入。 其他2个 stage, refs 都不能使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function PersonFunction(props){     const {name, age, sex} = props     return (         <ul>             <li>姓名:{name}</li>             <li>性别:{sex}</li>             <li>年龄:{age + 1}</li>         </ul>     ) } ReactDOM.render(<PersonFunction name="annie" age={15} sex="女"/>, document.getElementById("test2"))  
 
2.2. 组件的3大属性: refs 组件内包含ref属性的标签元素的集合对象
给操作目标标签指定ref属性, 打一个标识
在组件内部获得标签对象: this.refs.refName(只是得到了标签元素对象)
作用: 找到组件内部的真实dom元素对象, 进而操作它
 
普通字符串形式的 ref
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 <script type="text/babel" charset="utf-8">     class Person extends React.Component {         showDataLeft = ()=>{             console.log("input1 value:", this.refs.input1.value)         }         showDataRight = ()=>{             console.log("input2 value:", this.refs.input2.value)         }         render() {             return (                 <div>                     // 过时的 api,这里 使用 字符串 做 定义 refs了。后续不推荐使用了                     <input ref="input1" type="text" placeholder="点击按钮提示数据"/>                     <button ref="button1" onClick={this.showDataLeft}>点我</button>                     <input ref="input2" onBlur={this.showDataRight}type="text" placeholder="失去焦点提示数据"/>                 </div>             )         }     }     ReactDOM.render(<Person />, document.getElementById("test")) </script> 
 
回调 形式的ref
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 <script type="text/babel" charset="utf-8">     class Person extends React.Component {         showDataLeft = ()=>{             console.log("input1 value:", this.input1.value)         }         showDataRight = ()=>{             console.log("input2 value:", this.input2.value)         }         render() {             return (                 <div>                     <input ref={(param)=>{this.input1 = param}} type="text" placeholder="点击按钮提示数据"/>                     <button ref="button1" onClick={this.showDataLeft}>点我</button>                     <input ref={(param)=>{this.input2 = param}} onBlur={this.showDataRight}type="text" placeholder="失去焦点提示数据"/>                 </div>                 //这里的回调 还能简写如下                  <div>                     <input ref={ param =>this.input1 = param } type="text" placeholder="点击按钮提示数据"/>                     <button ref="button1" onClick={this.showDataLeft}>点我</button>                     <input ref={param =>this.input2 = param } onBlur={this.showDataRight}type="text" placeholder="失去焦点提示数据"/>                 </div>             )         }     }     ReactDOM.render(<Person />, document.getElementById("test")) </script> 
 
这种使用内联的方式 回调 ref ,这个回调方法会执行2次。第一次 会传入参数是 null的。
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 <script type="text/babel" charset="utf-8">     class Person extends React.Component {         state = { // 这里时间给 实例对象 添加一个  成员属性。             isHot: true,         }         showDataLeft = () => {             console.log("input1 value:", this.input1.value)         }         showDataRight = () => {             console.log("input2 value:", this.input2.value)         }         changeWeather = () => {             const isHot = this.state.isHot             this.setState({isHot: !isHot})  // 这里更新 动作 是一个合并的动作,不会把state 对象里面的 其他值给弄没了。         }         render() {             return (                 <div>                     <h2 onClick={this.changeWeather}>今天天气很 {this.state.isHot ? "炎热" : "凉爽"}</h2>                     <br/>                     <input ref={(param)=>{this.input1 = param;  console.log("input111111111111", param)}} type="text" placeholder="点击按钮提示数据"/>                     <button ref="button1" onClick={this.showDataLeft}>点我</button>                     <input ref={param => this.input2 = param} onBlur={this.showDataRight}                            type="text" placeholder="失去焦点提示数据"/>                 </div>             )         }     }     ReactDOM.render(<Person/>, document.getElementById("test")) </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 37 38 39 40 41 42 43 44 45 46 <script type="text/babel" charset="utf-8">     class Person extends React.Component {         state = { // 这里时间给 实例对象 添加一个  成员属性。             isHot: true,         }         showDataLeft = () => {             console.log(this)             console.log("input1 value:", this.input1.value)         }         showDataRight = () => {             console.log("input2 value:", this.input2.value)         }         changeWeather = () => {             const isHot = this.state.isHot             this.setState({isHot: !isHot})  // 这里更新 动作 是一个合并的动作,不会把state 对象里面的 其他值给弄没了。         }                  input2 = (param) => {             this.input2 = param;             console.log("input2222222222", param)         }         render() {             return (                 <div>                     <h2 onClick={this.changeWeather}>今天天气很 {this.state.isHot ? "炎热" : "凉爽"}</h2>                     <br/>                     // 这个还是内联方式回调的。这样会调用2次,第一次就会 把这个值设置为null了,第2次调用就会传入当前标签                     <input ref={(param) => {this.input1 = param;}} type="text" placeholder="点击按钮提示数据"/>                     <button ref="button1" onClick={this.showDataLeft}>点我</button>                     <input ref={this.input2} onBlur={this.showDataRight} type="text" placeholder="失去焦点提示数据"/>                 </div>             )         }     }     ReactDOM.render(<Person/>, document.getElementById("test")) </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 37 38 39 40 41 42 43 44 45 46 47 48 <script type="text/babel" charset="utf-8">     class Person extends React.Component {         state = { // 这里时间给 实例对象 添加一个  成员属性。             isHot: true,         }         showDataLeft = () => {             console.log(this)             console.log("input1 value:", this.input1.value)         }         showDataRight = () => {             console.log("input2 value:", this.input2.value)         }         changeWeather = () => {             const isHot = this.state.isHot             this.setState({isHot: !isHot})  // 这里更新 动作 是一个合并的动作,不会把state 对象里面的 其他值给弄没了。         }         // 注意这里的 inputRef 名称 千万别和 里面的 input1 命令成一样的了!!!!!!!!!!         input1ref = (param) => {             this.input1 = param;             console.log("11", param)         }         input2ref = (param) => {             this.input2 = param;             console.log("22", param)         }         render() {             return (                 <div>                     <h2 onClick={this.changeWeather}>今天天气很 {this.state.isHot ? "炎热" : "凉爽"}</h2>                     <br/>                     // 想这种的 把回调绑定到类上的,之后刷新组件也只是会调用一次的。                     <input ref={this.input1ref} type="text" placeholder="点击按钮提示数据"/>                     <button ref="button1" onClick={this.showDataLeft}>点我</button>                     <input ref={this.input2ref} type="text" placeholder="失去焦点提示数据"/>                 </div>             )         }     }     ReactDOM.render(<Person/>, document.getElementById("test")) </script> 
 
使用 React.createRef();创建ref,这是官方最推荐的,这个在reac 16.3 之后版本才有。
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 <!--<script type="text/javascript" src="../js/react.development.js"></script>--> <script src="https://unpkg.com/react@16.3.0/umd/react.development.js"></script> <!--<script type="text/javascript" src="../js/react-dom.development.js"></script>--> <script src="https://unpkg.com/react-dom@16.3.0/umd/react-dom.development.js"></script> <script type="text/javascript" src="../js/prop-types.js"></script> <!--    <script type="text/javascript" src="../js/babel.min.js" charset="utf-8"></script>--> <script src="https://unpkg.com/@babel/standalone@7.19.0/babel.min.js"></script> <script type="text/babel" charset="utf-8">     class Person extends React.Component {         state = { // 这里时间给 实例对象 添加一个  成员属性。             isHot: true,         }         showDataLeft = () => {             console.log(this)             console.log("input1 value:", this.input1.value)         }         showDataRight = () => {             console.log("input2 value:", this.input2.value)         }         changeWeather = () => {             const isHot = this.state.isHot             this.setState({isHot: !isHot})  // 这里更新 动作 是一个合并的动作,不会把state 对象里面的 其他值给弄没了。         }         input1ref = (param) => {             this.input1 = param;             console.log("11", param)         }         input2ref = (param) => {             this.input2 = param;             console.log("22", param)         }         input3ref = React.createRef();         render() {             return (                 <div>                     <h2 onClick={this.changeWeather}>今天天气很 {this.state.isHot ? "炎热" : "凉爽"}</h2>                     <br/>                     <input ref={this.input1ref} type="text" placeholder="点击按钮提示数据"/>                     <button ref="button1" onClick={this.showDataLeft}>点我</button>                     <input ref={this.input2ref} type="text" placeholder="失去焦点提示数据"/>                     <br/>                     <input ref={this.input3ref} type="text" placeholder="input3ref"/>                 </div>             )         }     }     ReactDOM.render(<Person/>, document.getElementById("test")) </script> 
 
2.3. 组件中的事件处理 1. 给标签添加属性: onXxx={this.eventHandler}
2. 在组件中添加事件处理方法
    eventHandler(event) {
                
    }
3. 使自定义方法中的this为组件对象
  	在constructor()中bind(this)
  	使用箭头函数定义方法(ES6模块化编码时才能使用)
4. 事件监听
    绑定事件监听
        事件名
        回调函数
    触发事件
        用户对对应的界面做对应的操作
        编码
 
2.4. 组件的组合使用 1)拆分组件: 拆分界面,抽取组件
2)实现静态组件: 使用组件实现静态页面效果
3)实现动态组件
    ① 动态显示初始化数据
    ② 交互功能(从绑定事件监听开始)
 
2.5. 组件收集表单数据 受控组件
非受控组件
 
受控组件,可以省略ref
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 <script type="text/babel" charset="utf-8">     class Login extends React.Component {         handleSubmit = (event) => {             event.preventDefault() // 阻止表单默认提交             console.log(this.state)         }         state = {             username: "",             password: ""         }         saveUsername = (event) => {             this.setState({username: event.target.value})         }         savePassword = (event) => {             this.setState({password: event.target.value})         }         render() {             return (                 <form action="" onSubmit={this.handleSubmit}>                     username<input onChange={this.saveUsername} type="text" name="username"/>                     password<input onChange={this.savePassword} type="text" name="password"/>                     <button>login</button>                 </form>             )         }     }     ReactDOM.render(<Login/>, document.getElementById("test")) </script> 
 
一个优化的写法,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 <script type="text/babel" charset="utf-8">     class Login extends React.Component {         handleSubmit = (event) => {             event.preventDefault() // 阻止表单默认提交             console.log(this.state)         }         state = {             username: "",             password: ""         }         saveUsername = (event) => {             this.setState({username: event.target.value})         }         savePassword = (event) => {             this.setState({password: event.target.value})         }         save = (data) => {             return (event) => {                 console.log(data, event.target.value)                 this.setState({[data]: event.target.value})             }         }         render() {             return (                 <form action="" onSubmit={this.handleSubmit}>                     username<input onChange={this.save("username")} type="text" name="username"/>                     password<input onChange={this.save("password")} type="text" name="password"/>                     <button>login</button>                 </form>             )         }     }     ReactDOM.render(<Login/>, document.getElementById("test")) </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 <script type="text/babel" charset="utf-8">     class Login extends React.Component {         handleSubmit = (event) => {             event.preventDefault() // 阻止表单默认提交             const {username, password} = this             console.log(this, username.value, password.value)         }         render() {             return (                 <form action="" onSubmit={this.handleSubmit}>                     username<input ref={c => this.username = c} type="text" name="username"/>                     password<input ref={c => this.password = c} type="text" name="password"/>                     <button>login</button>                 </form>             )         }     }     ReactDOM.render(<Login/>, document.getElementById("test")) </script> 
 
2.6. 组件的生命周期 1. 组件的三个生命周期状态:
    Mount:插入真实 DOM
    Update:被重新渲染
    Unmount:被移出真实 DOM
2. 生命周期流程:
    * 第一次初始化显示: ReactDOM.render(<Xxx/>, containDom)
        constructor()
        componentWillMount() : 将要插入回调
        render() : 用于插入虚拟DOM回调
        componentDidMount() : 已经插入回调
    * 每次更新state: this.setState({})
        componentWillReceiveProps(): 接收父组件新的属性
        componentWillUpdate() : 将要更新回调
        render() : 更新(重新渲染)
        componentDidUpdate() : 已经更新回调
    * 删除组件: ReactDOM.unmountComponentAtNode(div): 移除组件
        componentWillUnmount() : 组件将要被移除回调
3. 常用的方法
    render(): 必须重写, 返回一个自定义的虚拟DOM
  	constructor(): 初始化状态, 绑定this(可以箭头函数代替)
  	componentDidMount() : 只执行一次, 已经在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 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 <script type="text/babel" charset="utf-8">     class Test extends React.Component {         constructor(props) {             super(props);             console.log("this is constructor function")         }         state = {             count: 0         }         add = () => {             let {count} = this.state;             count += 1             this.setState({count})         }         componentWillMount(){             console.log("this is componentWillMount function")         }         componentDidMount() {             console.log("this is componentDidMount function")         }         componentWillUnmount(){             console.log("this is componentWillUnmount function")         }         shouldComponentUpdate(){             console.log("this is shouldComponentUpdate function")             return true         }         componentWillUpdate(){             console.log("this is componentWillUpdate function")         }         componentDidUpdate(){             console.log("this is componentDidUpdate function")         }         render() {             console.log("this is render function")             return (                 <div>                     <h2>逐渐+1 {this.state.count}</h2>                     <button onClick={this.add}>+1</button>                 </div>             )         }     }     ReactDOM.render(<Test/>, document.getElementById("test")) </script> 
 
2.7. 虚拟DOM与DOM diff算法 1). 虚拟DOM是什么? 一个虚拟DOM(元素)是一个一般的js对象, 准确的说是一个对象树(倒立的)
虚拟DOM保存了真实DOM的层次关系和一些基本属性,与真实DOM一一对应
如果只是更新虚拟DOM, 页面是不会重绘的
 
2). Virtual DOM 算法的基本步骤 用JS对象树表示DOM树的结构;然后用这个树构建一个真正的DOM树插到文档当中
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
把差异应用到真实DOM树上,视图就更新了
 
3). 进一步理解 Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。
可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)。
 
2.8. 命令式编程与声明式编程 声明式编程
    只关注做什么, 而不关注怎么做(流程),  类似于填空题
命令式编程
    要关注做什么和怎么做(流程), 类似于问答题
var arr = [1, 3, 5, 7]
// 需求: 得到一个新的数组, 数组中每个元素都比arr中对应的元素大10: [11, 13, 15, 17]
// 命令式编程
var arr2 = []
for(var i =0;i<arr.length;i++) {
    arr2.push(arr[i]+10)
}
console.log(arr2)
// 声明式编程
var arr3 = arr.map(function(item){
    return item +10
})
// 声明式编程是建立命令式编程的基础上
// 数组中常见声明式方法
    map() / forEach() / find() / findIndex()