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). 编码:
  

 <div id="container"></div>
    <script type="text/babel">
        var aa = 123
        var bb = 'test'
        ReactDOM.render(<h1 id={bb}>{aa}</h1>, containerDOM)
    </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). 注意:
    * 标签必须有结束
    * 标签要套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 <h1>自定义组件标题11111</h1>
    }
    
    //方式2: ES6类语法(复杂组件, 推荐使用)
    class MyComponent3 extends React.Component {
        render () {
          return <h1>自定义组件标题33333</h1>
        }
    }


3). 渲染组件标签
    

ReactDOM.render(<MyComp />,  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传递
    <Person {...person}/>
    
*** 构造器与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
  

  <input ref="input1" type="text" placeholder="点击按钮提示数据"/>&nbsp;
    showData = ()=>{
        const {input1} = this.refs
        alert(input1.value)
    }
    
    <input ref='input1' type='text'>
    showData = ()=>{
        const {input1} = this.refs
        alert(input1.value)
    }

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

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


    
*** 3 createRef
    

myRef = React.createRef()
//React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
    <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
    showData = ()=>{
        alert(this.myRef.current.value);
    }
    
    myRef = React.createRef()
    <input ref={this.myRef} type='text'>
    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(<Xxx/>, 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:做一些收尾工作, 如: 清理定时器

 

 

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<arr.length;i++) {
    arr2.push(arr[i]+10)
}
console.log(arr2)
// 声明式编程
var arr3 = arr.map(function(item){
    return item +10
})
// 声明式编程是建立命令式编程的基础上

// 数组中常见声明式方法
    map() / forEach() / find() / findIndex()

 

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

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

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

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值