• 前端(九):react生命周期


    一、组件渲染

      当组件的props或者state发生改变时,组件会自动调用render方法重新渲染。当父组件被重新渲染时,子组件也会被递归渲染。那么组件是如何渲染的呢?

    # 方案一
    1.state数据
    2.JSX模板
    3.数据 + 模板 生成真实DOM来显示
    4.state发生改变
    5.JSX模板
    6.数据 + 模板重新结合,生成新的真实的DOM,替换掉以前的DOM
    缺陷:
        第一次生成了完整的DOM片段
        第二次生成了完整的DOM片段,替换第一次的DOM片段,比较耗性能
    # 方案二
    1.state数据
    2.JSX模板
    3.数据 + 模板 生成真实DOM来显示
    4.state发生改变
    5.sJSX模板
    6.数据 + 模板重新结合,生成新的真实的DOM
    7.新的DOM和原来的DOM进行比对,只用新的DOM中改变的部分替换掉以前DOM对应的部分
    缺陷:
        因为仍然是生成了一个新的真实的DOM,所以性能提升并不明显
    # 方案三
    1.state数据
    2.JSX模板
    3.生成虚拟DOM,也就是一个js对象
        {
            "div": {
                id: "div-id",
                span: {
                    id: "span-d",
                    class: "span-class",
                    content: "hello, world"
                }
            }
        }
    4.数据 + 模板 生成真实DOM来显示
        <div id="div-id"><span id="span-id" class="span-class">hello, world</span></div>
    5.state发生变化
    6.生成新的虚拟DOM
        {
            "div": {
                id: "div-id",
                span: {
                    id: "span-d",
                    class: "span-class",
                    content: "what the hell"
                }
            }
        }
    7.对比两个虚拟DOM,将obj中span的content变动更改到原始真实DOM中
        <div id="div-id"><span id="span-id" class="span-class">what the hell</span></div>
    优点:
        不再重新创建真实DOM,只是比较两个js对象的不同来修改原始DOM
    # 将一个object对象转换成DOM
    const dom = {
                "div": {
                    id: "div-id",
                    span: {
                        id: "span-id",
                        class: "span-class",
                        content: "hello, world"
                    }
                }
            };
            function createDom(dom) {
                const div = Object.keys(dom)[0];
                const divNode = document.createElement(div);
                const innerKeys = Object.keys(dom.div);
                for(const i in innerKeys){
                    const innerKey = innerKeys[i];
                    if(innerKey === "span"){
                        const spanNode = document.createElement(innerKey);
                        const span = dom.div["span"];
                        const spanKeys = Object.keys(span);
                        for (const j in spanKeys){
                            const spanKey = spanKeys[j];
                            if(spanKey === "content"){
                                spanNode.innerText = "hello, world";
                            }else {
                                spanNode.setAttribute(spanKey, span[spanKey]);
                            }
                        }
                        divNode.appendChild(spanNode);
                    }else {
                        divNode.setAttribute(innerKey, dom.div[innerKey]);
                    }
                }
                return divNode;
            };
            div = createDom(dom);

    二、组件的生命周期

      组件的声明周期可分成三个状态:Mounting,已插入真实 DOM;Updating,正在被重新渲染;Unmounting:已移出真实 DOM。如下图所示(本图根据Rosen老师示例改写)。

      

      各个函数的释义(摘自菜鸟教程):

      componentWillMount 在渲染前调用,在客户端也在服务端。

      componentDidMount : 在第一次渲染后调用,只在客户端,只执行一次。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异部操作阻塞UI)。

      componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。

      shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
    可以在你确认不需要更新组件时使用。

    shouldComponentUpdate(nextProps, nextState) {
    console.log(nextProps, nextState);
    console.log(this.props, this.state);
    if(this.state.update === true){
    // 只有在给定条件发生的时候返回true,也就是可以重新渲染
    return true
    }
       // 只有在给定条件发生的时候返回true,也就是可以重新渲染
       if(nextState.update === true){
    // 在下一个状态达到条件时重新渲染
    return true
    }
    return false // 其它条件返回false,不重新渲染
    }

      componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。

      componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。

      componentWillUnmount在组件从 DOM 中移除的时候立刻被调用。

      一个示例:

    import React, {Fragment} from "react";
    
    class LifeCircle extends React.Component{
        constructor(props){
            super(props);
            console.log("STEP1: constructor is called.");
            this.state = {
                list: [],
                input: ""
            }
        }
        componentWillMount() {
            console.log("STEP2: componentWillMount is called.");
        }
        componentDidMount() {
            console.log("STEP4: componentDidMount is called."); // 可以在这里写ajax数据请求,因为它只在组件被挂载到页面上时执行一次,而不是render里(因为render会被重复渲染)
        }
    
        componentWillReceiveProps(nextProps, nextContext) {
            console.log("STEP5: componentWillReceiveProps is called.")
        }
    
        shouldComponentUpdate(nextProps, nextState, nextContext) {
            console.log("STEP6: shouldComponentUpdate is called.");
            return true; // 是否更新,true更新,更新就是要把有关的子组件也一并渲染;如果子组件没有数据更改那就没重新渲染,此时可以返回一个false
        }
        componentWillUpdate(nextProps, nextState, nextContext) {
            console.log("STEP7: componentWillUpdate is called.")
        }
        componentDidUpdate(prevProps, prevState, snapshot) {
            console.log("STEP8: componentDidUpdate is called.")
        }
        componentWillUnmount() {
            console.log("STEP9: componentDidUpdate is called.")
        }
    
        render() {
            console.log("STEP3: render is called.");
            return (
                <Fragment>
                    <div>
                        <input
                            value={this.state.input}
                            onChange={(e) => {this.handleChange(e)}}
                        />
                        <button onClick={() => {this.handleClick()}}>添加句子</button>
                    </div>
                    <ul>
                        {
                            this.state.list.map((name, index) => {
                                return <li
                                    key={index}
                                    onClick={() => {this.handleliClick(index)}}
                                > { name }</li>
                            })
                        }
                    </ul>
                </Fragment>
            );
        }
        handleChange(e){
            const value = e.target.value;
            this.setState(() => ({
                input: value
            }))
        }
        handleClick(){
            this.setState((prevState)=>({
                list: [...prevState.list, prevState.input],
                input: ""
            }))
        }
        handleliClick(index){
            this.setState((prevState) => {
                const list =[...prevState.list];
                list.splice(index, 1);
                return {
                    list
                }
            })
        }
    }
    
    export default LifeCircle;

    三、使用component请求后台数据并交给组件

      在my-app下建立server/server.js文件,启动一个后端服务:

    const express = require('express');

    const app = express();
    app.get("/data", function (req, res) {
    res.json({"name": "old monkey", "age": 5000})
    });
    app.listen(3002, function () {
    console.log("Node app start at port 3002.")
    });

      Terminal启动该服务: node server/server.js。此时可以访问http://localhost:3002/data来获取json数据。 

      安装axios: cnpm install axios --save。并在package.json的配置文件中添加"proxy"配置,让它转换端口到3002:

    // package.json
    {
    "name": "my-app",
    "version": "0.1.0",
    "private": true,
    "dependencies": {
    "axios": "^0.18.0",
    "react": "^16.4.1",
    "react-dom": "^16.4.1",
    "react-scripts": "1.1.4"
    },
    "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
    },
    "proxy": "http://localhost:3002"
    }

      在src/index.js中写组件:

    // src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import axios from 'axios';

    class App extends React.Component{
    constructor(props){
    super(props);
    this.state = {name: "monkey", age: 100}

    }
      // 在component内部使用ajax请求数据,并通过setState传递给App。
    componentDidMount(){
    axios.get("/data").then(res=>{
    if(res.status === 200){
    console.log(res);
    this.setState({name: res.data.name, age: res.data.age});
    }
    }, err=>{
    console.log(err);
    })
    }
    addAge(){
    this.setState({age: this.state.age + 1})
    };
    decAge(){
    this.setState({age: this.state.age - 1})
    }
    render (){
    const style={
    display: "inline-block",
    "150px",
    height: "40px",
    backgroundColor: "rgb(173, 173, 173)",
    color: "white",
    marginRight: "20px"
    };
    return (
    <div>
    <h2>this {this.state.name } is { this.state.age } years old.</h2>
    <button style={style} onClick={()=>this.addAge()}>增加一岁</button>
    <button style={style} onClick={()=>this.decAge()}>减少一岁</button>
    </div>
    )
    }
    }
    ReactDOM.render(<App />, document.getElementById('root'));
  • 相关阅读:
    哈哈,拖了一天
    内置函数续
    内置函数2
    内置函数1
    内置函数补充
    生成器面试题
    字段值为null的处理
    oracle日期转换报ORA-01810: 格式代码出现两次 01810. 00000 -  "format code appears twice"
    通过sql脚本可以从数据库中查到数据,但是通过jdbc却获取不到
    java外部捕获了异常,但是并没有打印异常和将异常信息写入日志文件
  • 原文地址:https://www.cnblogs.com/kuaizifeng/p/10158772.html
Copyright © 2020-2023  润新知