React基本认识和组件化开发

目录

1.1. React的基本认识

1.2. React的基本使用

1.3. JSX的理解和使用

1.4. 几个重要概念理解

1). 模块与组件

2). 模块化与组件化

2. react组件化开发

2.1. 基本理解和使用

2.2. 组件的3大属性: state

2.2. 组件的3大属性: props

2.2. 组件的3大属性: refs

2.3. 组件中的事件处理

2.4. 组件的组合使用

2.5. 组件收集表单数据(受控组件和非受控组件)

2.6. 组件的生命周期

2.7. 虚拟DOM与DOM diff算法

1). 虚拟DOM是什么?

2). Virtual DOM 算法的基本步骤

3). 进一步理解

4). key的作用

2.8. 命令式编程与声明式编程

2.9. 高阶函数__函数柯里化


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的基本使用

1). 导入相关js库文件(react.js, react-dom.js, babel.min.js)
2). 编码:
  

 
    

 

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

  • {item}

  •         })
    3). 注意:
        * 标签必须有结束
        * 标签要套js需要放在{}中
        * 标签的class属性必须改为className属性
        * 标签的style属性值必须为: {{color:'red', width:12}}
            小驼峰:fontSize
        * 标签首字母
            (1)小写开头,标签改为HTML同名元素,没有则报错
            (2)大写字母,react渲染相应的组件
            
    4). React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-            scripting, 跨站脚本)攻击。

    5). Babel 会把JSX转译成一个名为React.createElement()函数调用

     

    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 MyComponent1(props) {
            return 

    自定义组件标题11111

        }          //方式2: ES6类语法(复杂组件, 推荐使用)     class MyComponent3 extends React.Component {         render () {           return

    自定义组件标题33333

            }     }


    3). 渲染组件标签
        

    ReactDOM.render(,  cotainerEle)

    4). ReactDOM.render()渲染组件标签的基本流程
        React内部会创建组件实例对象/调用组件函数, 得到虚拟DOM对象
        将虚拟DOM并解析为真实DOM
        插入到指定的页面元素内部
        
    5) constructor 构造器执行了一次
        render 调用了n+1次 1是初始化的次数 n是状态更新的次数
        方法()  点几次调用几次

     

    2.2. 组件的3大属性: state

    1. 组件被称为"状态机", 页面的显示是根据组件的state属性的数据来显示
    2. 初始化指定:
        constructor(props) {      // 通过此类方式将props传递到父类的构造函数中
          super(props)
          this.state = {
            stateName1 : stateValue1,
            stateName2 : stateValue2
          }
        }
    3. 读取显示: 
        this.state.stateName1
    4. 更新状态-->更新界面 : (状态数据不允许直接更改)
        this.setState({stateName1 : newValue})
    5.  组件中的render方法中的this为组件实例对象
        组件自定义的方法默认开启了局部的严格模式,this指向undefined
        方法放在类的原型对象上,修改this指向
        由于change是作为onClick的回调,所以不是通过实例调用的,是直接调用
        (1) this.change = this.change.bind(this)
        (2) 箭头函数  change=()->{}  
            箭头函数没有this,找到外层函数的this作为自己的this使用,即为类的实例对象
    6. 类中直接写赋值语句,给实例对象添加属性

    2.2. 组件的3大属性: props

    props的只读性:所有React组件必须像纯函数一样保护他们的props不被更改

    所有组件标签的属性的集合对象
    给标签指定属性, 保存外部数据(可能是一个function)
    在组件内部读取属性: this.props.propertyName
    作用: 从目标组件外部向组件内部传递数据

    props是只读的 
    this.props.name = 'jack' 错误,因为props只读

    *** 对props中的属性值进行类型限制和必要性限制 static 给自身加了属性
        static propTypes = {
                name:PropTypes.string.isRequired, //限制name必传,且为字符串
                sex:PropTypes.string,//限制sex为字符串
                speak:PropTypes.func,//限制speak为函数  函数限制为func
            }
        
    *** 指定默认标签属性值
            static defaultProps = {
                sex:'男',//sex默认值为男
                age:18 //age默认值为18
            }

    *** 扩展属性: 将对象的所有属性通过props传递
       
        
    *** 构造器与props
    构造函数只有两种使用情况:
        (1)通过给this.state赋值对象来初始化内部state
        (2)为事件处理函数绑定实例

    constructor(props){
            //构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
            // console.log(props);
            super(props)
            console.log('constructor',this.props);
        }

    2.2. 组件的3大属性: refs

    组件内包含ref属性的标签元素的集合对象
    给操作目标标签指定ref属性, 打一个标识
    在组件内部获得标签对象: this.refs.refName(只是得到了标签元素对象)
    作用: 找到组件内部的真实dom元素对象, 进而操作它

    *** 1 字符串形式的ref
      

       
        showData = ()=>{
            const {input1} = this.refs
            alert(input1.value)
        }
        
        
        showData = ()=>{
            const {input1} = this.refs
            alert(input1.value)
        }

    *** 2 回调函数形式的ref  
    以内联函数的方式定义,在更新过程中执行两次,第一次null(清空) 第二次传入DOM元素
    每次渲染时会创建一个新的函数实例,react清空旧的ref设置新的
    将ref的回调函数定义成class的绑定函数的方式
      

      this.input1 = c } type="text" placeholder="点击"/> 
        showData = ()=>{
            const {input1} = this
            alert(input1.value)
        }
       
        
        
        saveInput = (c)=>{
            this.input1 = c;
            console.log('@',c);
        }


        
    *** 3 createRef
        

    myRef = React.createRef()
    //React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
         
        showData = ()=>{
            alert(this.myRef.current.value);
        }
        
        myRef = React.createRef()
        
        showData=()=>{
            alert(this.myRef.current.value)
        }

     

    2.3. 组件中的事件处理

    自定义合成事件,而不是原生的DOM事件-----为了更好的兼容性

    事件委托方式(委托给最外层的元素)----为了更加的高效

    1. 给标签添加属性: onXxx={this.eventHandler}
    2. 在组件中添加事件处理方法
        eventHandler(event) {
                    
        }
    3. 使自定义方法中的this为组件对象
          在constructor()中bind(this)
          使用箭头函数定义方法(ES6模块化编码时才能使用)
    4. 事件监听
        绑定事件监听
            事件名
            回调函数
        触发事件
            用户对对应的界面做对应的操作
            编码
    5. 阻止默认行为
        通过 preventDefault ,不能通过 return false
        

    ActionLink = ()=>{
            function handleClick(e){  // e 是合成事件
                e.preventDefault();
                console.log('hello')
            }
        }


        
    (1).通过onXxx属性指定事件处理函数(注意大小写)
            a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
            b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) —————为了的高效
            
    (2).通过event.target得到发生事件的DOM元素对象 —————不要过度使用ref

      

      showData = (event)=>{
            alert(event.target.value)
            alert(this.myRef.current.value);
        }

     

    2.4. 组件的组合使用

    1)拆分组件: 拆分界面,抽取组件
    2)实现静态组件: 使用组件实现静态页面效果
    3)实现动态组件
        ① 动态显示初始化数据
        ② 交互功能(从绑定事件监听开始)

     

    2.5. 组件收集表单数据(受控组件和非受控组件)

    受控组件
        页面中所有输入类的DOM随着输入维护到状态中,用到的时候从状态中取出来
        渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。
        阻止表单的默认提交行为:event.preventDefault()
        value:event.target.value
    受控组件更新state的流程:
    可以通过初始state中设置表单的默认值
    每当表单的值发生变化时,调用onChange事件处理器
    事件处理器通过事件对象e拿到改变后的状态,并更新组件的state
    一旦通过setState方法更新state,就会触发视图的重新渲染,完成表单组件的更新

    非受控组件
        页面中所有输入类的DOM,现用现取 用到ref,不要过度使用ref
        不是为每个状态更新都编写数据处理函数,可以使用 ref 来从 DOM 节点中获取表单数据。

     

    2.6. 组件的生命周期

    react旧版生命周期包含三个过程:

    1、挂载过程
    constructor()
    componentWillMount()   
    componentDidMount()

    2、更新过程
    componentWillReceiveProps(nextProps)
    shouldComponentUpdate(nextProps,nextState)
    componentWillUpdate (nextProps,nextState)
    render()
    componentDidUpdate(prevProps,prevState)

    3、卸载过程
    componentWillUnmount()

    其具体作用分别为:
    1、constructor()
    完成了React数据的初始化。它接受两个参数:props和context,当想在函数内部使用这两个参数时,需使用super()传入这两个参数。
    注意:只要使用了constructor()就必须写super(),否则会导致this指向错误。

    2、componentWillMount()
    组件已经完成初始化数据,但是还未渲染DOM时执行的逻辑,主要用于服务端渲染。

    3、componentDidMount()
    组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染

    4、componentWillReceiveProps(nextProps)
        1).在接受父组件改变后的props需要重新渲染组件时用到的比较多
        2).接受一个参数nextProps
        3).通过对比nextProps和this.props,将nextProps的state为当前组件的state,从而重新渲染组件

    5、shouldComponentUpdate(nextProps, nextState)
        1).主要用于性能优化(部分更新)
        2).唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
        3).因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断

    6、componentWillUpdate(nextProps, nextState)
    shouldComponentUpdate返回true以后,组件进入重新渲染的流程时执行的逻辑。

    7、render()
    页面渲染执行的逻辑,render函数把jsx编译为函数并生成虚拟dom,然后通过其diff算法比较更新前后的新旧DOM树,并渲染更改后的节点。

    8、componentDidUpdate(prevProps, prevState)
    组件更新完毕后,react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state。

    9、componentWillUnmount()
    在此处完成组件的卸载和数据的销毁。
    clear你在组建中所有的setTimeout,setInterval
    移除所有组建中的监听 removeEventListener

    ————————————————
    getDerivedStateFromProps : 传入的props映射到State上面 每次re-rendering之前被调用
    getSnapshotBeforeUpdate  :  在更新之前获得快照在更改之前从DOM中捕获信息,任何返回值将作为参数传递给                                componentDidUpdate()

    使用getDerivedStateFromProps代替了旧的componentWillReceiveProps及componentWillMount。使用getSnapshotBeforeUpdate代替了旧的componentWillUpdate。

    ***使用getDerivedStateFromProps(nextProps, prevState)的原因:
    旧的React中componentWillReceiveProps方法是用来判断前后两个 props 是否相同,如果不同,则将新的 props 更新到相应的 state 上去。在这个过程中我们实际上是可以访问到当前props的,这样我们可能会对this.props做一些奇奇怪怪的操作,很可能会破坏 state 数据的单一数据源,导致组件状态变得不可预测。

    而在 getDerivedStateFromProps 中禁止了组件去访问 this.props,强制让开发者去比较 nextProps 与 prevState 中的值,以确保当开发者用到 getDerivedStateFromProps 这个生命周期函数时,就是在根据当前的 props 来更新组件的 state,而不是去访问this.props并做其他一些让组件自身状态变得更加不可预测的事情。

    ***使用getSnapshotBeforeUpdate(prevProps, prevState)的原因:
    在 React 开启异步渲染模式后,在执行函数时读到的 DOM 元素状态并不总是渲染时相同,这就导致在 componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。

    而getSnapshotBeforeUpdate 会在最终的 render 之前被调用,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与componentDidUpdate 中一致的。
     

    1. 组件的三个生命周期状态:
        Mount:插入真实 DOM
        Update:被重新渲染
        Unmount:被移出真实 DOM
    2. 生命周期流程:
        * 第一次初始化显示: ReactDOM.render(, containDom)触发---初次渲染
            constructor()
            componentWillMount() : 将要插入回调
            render() : 用于插入虚拟DOM回调
            *componentDidMount() : 已经插入回调
                一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
                
        * 每次更新state: this.setState({})或父组件render触发
            componentWillReceiveProps(): 组件将要接收父组件新的属性,父组件render
            shouldComponentUpdate: 调用setState()之后调用,默认返回值为true,控制组件更新的阀门
            componentWillUpdate() : 将要更新回调  (forceUpdate()强制更新)
            *render() : 更新(重新渲染)
            componentDidUpdate() : 已经更新回调
            
        * 删除组件: ReactDOM.unmountComponentAtNode(div): 移除组件  ---触发
            *componentWillUnmount() : 组件将要被移除回调
                一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
    3. 常用的方法
        render(): 必须重写, 返回一个自定义的虚拟DOM
        constructor(): 初始化状态, 绑定this(可以箭头函数代替)
        componentDidMount() : 只执行一次, 已经在dom树中, 适合启动/设置一些监听
          
    render():初始化渲染,状态更新之后
    componentDidMount():组件挂载完毕
    componentWillUnmount():组件将要卸载
    ReactDOM.unmountComponentAtNode(div):卸载组件 

    ***新的生命周期 钩子函数
        <--
        避免使用UNSAFE_ 可能出现bug
        UNSAFE_componentWillMount()
        UNSAFE_componentWillReceiveProps()
        UNSAFE_componentWillUpdate()
        弃用:componentWillMount()、componentWillReceiveProps()、componentWillUpdate()
        -->

    1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
                1.    constructor()
                2.    getDerivedStateFromProps(nextProps,prevState) :state的值在任何时候都取决于    props,前面加static
                3.    render()
                4.    componentDidMount() :组件挂载完毕的钩子
                        一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
                        
    2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
                1.    getDerivedStateFromProps
                2.    shouldComponentUpdate() :控制组件更新的“阀门” 
                3.    render()
                4.    getSnapshotBeforeUpdate(prevProps,prevState,s) :在更新之前获得快照
                        在更改之前从DOM中捕获信息,任何返回值将作为参数传递给componentDidUpdate()
                5.    componentDidUpdate() :组件更新完毕的钩子

    3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
                1.    componentWillUnmount()  =====> 常用
                        一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

                        
     ***  重要的钩子
        1.render:初始化渲染或更新渲染调用
        2.componentDidMount:开启监听, 发送ajax请求
        3.componentWillUnmount:做一些收尾工作, 如: 清理定时器

     

    React基本认识和组件化开发_第1张图片

     

    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)。

     

    4). key的作用

    1. 虚拟DOM中key的作用:
            1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。

            2). 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】, 
                        随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:

                    a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
                        (1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
                        (2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM

                    b. 旧虚拟DOM中未找到与新虚拟DOM相同的key
                        根据数据创建新的真实DOM,随后渲染到到页面
                                        
    2. 用index作为key可能会引发的问题:
            1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
                    会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

            2. 如果结构中还包含输入类的DOM:
                    会产生错误DOM更新 ==> 界面有问题。
                                                    
            3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,
                    仅用于渲染列表用于展示,使用index作为key是没有问题的。
                        
    3. 开发中如何选择key?:
            1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
            2.如果确定只是简单的展示数据,用index也是可以的。

     

     

    2.8. 命令式编程与声明式编程

    声明式编程
        只关注做什么, 而不关注怎么做(流程),  类似于填空题
    命令式编程
        要关注做什么和怎么做(流程), 类似于问答题

    var arr = [1, 3, 5, 7]
    // 需求: 得到一个新的数组, 数组中每个元素都比arr中对应的元素大10: [11, 13, 15, 17]
    // 命令式编程
    var arr2 = []
    for(var i =0;i

     

    2.9. 高阶函数__函数柯里化

    高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
                1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
                2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
            常见的高阶函数有:Promise、setTimeout、arr.map()等等

    函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。 
                          

     function sum(a){
            return(b)=>{
              return (c)=>{
                return a+b+c
                      }
                  }
         }

    你可能感兴趣的:(React,react.js,javascript,前端)