react

1. React简介

  • React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram(照片交友) 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了
  • Angular1 2009 年 谷歌 MVC 不支持 组件化开发
  • 由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。
  • 清楚两个概念:
    • library(库):小而巧的库,只提供了特定的API;优点就是 船小好掉头,可以很方便的从一个库切换到另外的库;但是代码几乎不会改变;
    • Framework(框架):大而全的是框架;框架提供了一整套的解决方案;所以,如果在项目中间,想切换到另外的框架,是比较困难的;

2. 前端三大主流框架

三大框架一大抄

  • Angular.js:出来较早的前端框架,学习曲线比较陡,NG1学起来比较麻烦,NG2 ~ NG5开始,进行了一系列的改革,也提供了组件化开发的概念;从NG2开始,也支持使用TS(TypeScript)进行编程;
  • Vue.js:最火(关注的人比较多)的一门前端框架,它是中国人开发的,对我我们来说,文档要友好一些;
  • React.js:最流行(用的人比较多)的一门框架,因为它的设计很优秀;

3. React与vue的对比

组件化方面

  1. 什么是模块化:是从代码的角度来进行分析的;把一些可复用的代码,抽离为单个的模块;便于项目的维护和开发;
  2. 什么是组件化: 是从 UI 界面的角度 来进行分析的;把一些可服用的UI元素,抽离为单独的组件;便于项目的维护和开发;
  3. 组件化的好处:随着项目规模的增大,手里的组件越来越多;很方便就能把现有的组件,拼接为一个完整的页面;
  4. Vue是如何实现组件化的: 通过 .vue 文件,来创建对应的组件;
    • template 结构
    • script 行为
    • style 样式
  1. React如何实现组件化:大家注意,React中有组件化的概念,但是,并没有像vue这样的组件模板文件;React中,一切都是以JS来表现的;因此要学习React,JS要合格;ES6 和 ES7 (async 和 await) 要会用;

开发团队方面

  • React是由FaceBook前端官方团队进行维护和更新的;因此,React的维护开发团队,技术实力比较雄厚;
  • Vue:第一版,主要是有作者 尤雨溪 专门进行维护的,当 Vue更新到 2.x 版本后,也有了一个以 尤雨溪 为主导的开源小团队,进行相关的开发和维护;

社区方面

  • 在社区方面,React由于诞生的较早,所以社区比较强大,一些常见的问题、坑、最优解决方案,文档、博客在社区中都是可以很方便就能找到的;
  • Vue是近两年才火起来的,所以,它的社区相对于React来说,要小一些,可能有的一些坑,没人踩过;

移动APP开发体验方面

  • Vue,结合 Weex 这门技术,提供了 迁移到 移动端App开发的体验(Weex,目前只是一个 小的玩具, 并没有很成功的 大案例;)
  • React,结合 ReactNative,也提供了无缝迁移到 移动App的开发体验(RN用的最多,也是最火最流行的);

4. 为什么要学习React

  1. 和Angular1相比,React设计很优秀,一切基于JS并且实现了组件化开发的思想;
  2. 开发团队实力强悍,不必担心断更的情况;
  3. 社区强大,很多问题都能找到对应的解决方案;
  4. 提供了无缝转到 ReactNative 上的开发体验,让我们技术能力得到了拓展;增强了我们的核心竞争力;
  5. 很多企业中,前端项目的技术选型采用的是React.js;

5. React中几个核心的概念

虚拟DOM(Virtual Document Object Model)

  • DOM的本质是什么:浏览器中的概念,用JS对象来表示 页面上的元素,并提供了操作 DOM 对象的API;

  • 什么是React中的虚拟DOM:是框架中的概念,是程序员 用JS对象来模拟 页面上的 DOM 和 DOM嵌套;

  • 为什么要实现虚拟DOM(虚拟DOM的目的):为了实现页面中, DOM 元素的高效更新

  • DOM和虚拟DOM的区别

  • DOM:浏览器中,提供的概念;用JS对象,表示页面上的元素,并提供了操作元素的API;

  • 虚拟DOM:是框架中的概念;而是开发框架的程序员,手动用JS对象来模拟DOM元素和嵌套关系;

    + 本质: 用JS对象,来模拟DOM元素和嵌套关系;
    
    • 目的:就是为了实现页面元素的高效更新;

      虚拟DOM的概念.png

Diff算法

  • tree diff:新旧两棵DOM树,逐层对比的过程,就是 Tree Diff; 当整颗DOM逐层对比完毕,则所有需要被按需更新的元素,必然能够找到;

  • component diff:在进行Tree Diff的时候,每一层中,组件级别的对比,叫做 Component Diff;

    • 如果对比前后,组件的类型相同,则暂时认为此组件不需要被更新;
    • 如果对比前后,组件类型不同,则需要移除旧组件,创建新组件,并追加到页面上;
  • element diff:在进行组件对比的时候,如果两个组件类型相同,则需要进行 元素级别的对比,这叫做 Element Diff;

    [图片上传失败...(image-19dc01-1595464286977)]

6. 创建项目

第一种方式

全局安装create-react-app
npm install -g create-react-app

创建项目
create-react-app 项目名

第二种方式

npx —— 它是 [npm 5.2+ 附带的 package 运行工具],这种方式会先局部安装脚手架,然后在安装项目

npx create-react-app your-app

项目安装这需要等待一段时间,这个过程实际上会安装三个东西

  • react: react的顶级库,专门用于创建组件和虚拟DOM的,同时组件的生命周期都在这个包中
  • react-dom:专门进行DOM操作的,最主要的应用场景,就是ReactDOM.render()
  • react-scripts: 包含运行和打包react应用程序的所有脚本及配置

7. 创建虚拟dom并渲染

1.创建虚拟DOM元素:

// 这是 创建虚拟DOM元素的 API    <h1 title="啊,五环" id="myh1">你比四环多一环</h1>
//  第一个参数: 字符串类型的参数,表示要创建的标签的名称
//  第二个参数:对象类型的参数, 表示 创建的元素的属性节点
//  第三个参数: 子节点
const myh1 = React.createElement('h1', { title: '啊,五环', id: 'myh1' }, '你比四环多一环')

2.渲染:

  // 3. 渲染虚拟DOM元素
  // 参数1: 表示要渲染的虚拟DOM对象
  // 参数2: 指定容器,注意:这里不能直接放 容器元素的Id字符串,需要放一个容器的DOM对象
  ReactDOM.render(myh1, document.getElementById('app'))
注:像这样创建虚拟dom太麻烦了,
React.createElement('h1', { title: '啊,五环', id: 'myh1' }, '你比四环多一环')
所以有了jsx 语法,写jsx语法,webpack的babel-loader会转化为这样
React.createElement('h1', { title: '啊,五环', id: 'myh1' }, '你比四环多一环')

8. JSX语法

  • 什么是JSX语法:

    就是符合 xml 规范的 JS 语法;(语法格式相对来说,要比HTML严谨很多)

    REACT独有的语法 JAVASCRIPT+XML(HTML)

    和我们之前自己拼接的HTML字符串类似,都是把HTML结构代码和JS代码或者数据混合在一起了,

    但是它不是字符串

下面这些安装,如果用脚手架创建的项目,脚手架已经做了,不用安装了

  1. 如何启用 jsx 语法?
  • 安装 babel 插件

    • 运行cnpm i babel-core babel-loader babel-plugin-transform-runtime -D
    • 运行cnpm i babel-preset-env babel-preset-stage-0 -D
  • 安装能够识别转换jsx语法的包 babel-preset-react

    • 运行cnpm i babel-preset-react -D
  • 添加 .babelrc 配置文件

    {
      "presets": ["env", "stage-0", "react"],
      "plugins": ["transform-runtime"]
    }
    
  • 添加babel-loader配置项:

    module: { //要打包的第三方模块
        rules: [
          { test: /\.js|jsx$/, use: 'babel-loader', exclude: /node_modules/ }
        ]
    }
    
  1. jsx 语法的本质:并不是直接把 jsx 渲染到页面上,而是 内部先转换成了 createElement 形式,再渲染的;

  2. 在 jsx 中混合写入 js 表达式:在 jsx 语法中,要把 JS代码写到 { } 中,但是要求JS代码指执行完成有返回结果(JS表达式)

    • 渲染数字
    • 渲染字符串
    • 渲染布尔值
    • 为属性绑定值
    • 渲染jsx元素
    • 渲染jsx元素数组
  3. 在 jsx 中 写注释:推荐使用{ /* 这是注释 */ }

  4. 为 jsx 中的元素添加class类名:需要使用className 来替代 classhtmlFor替换label的for属性

  5. 在JSX创建DOM的时候,所有的节点,必须有唯一的根元素进行包裹;

  6. 在 jsx 语法中,标签必须 成对出现,如果是单标签,则必须自闭和!

当 编译引擎,在编译JSX代码的时候,如果遇到了<那么就把它当作 HTML代码去编译,

如果遇到了 {} 就把 花括号内部的代码当作 普通JS代码去编译;

9. React中创建组件

  • 不管是VUE还是REACT框架,设计之初都是期望我们按照“组件/模块管理”的方式来构建程序的,也就是把一个程序划分为一个个的组件来单独处理

  • 优势
    1.有助于多人协作开发
    2.我们开发的组件可以被复用

  • REACT中创建组件有两种方式:
    函数声明式组件
    基于继承COMPONENT类来创建组件

  • SRC -> COMPONENT :这个文件夹中存放的就是开发的组件

  • 返回的jsx,必须是唯一的节点包裹,如果不想让这个节点展示,可以用这个包裹 <> <h1>hhh</h1>

​ <h1>hhh</h1> </>

第1种 - 函数声明式组件(无状态组件)

函数声明式组件,如果要接收外界传递的数据,需要在 构造函数的参数列表中使用props来接收;

必须要向外return一个合法的JSX;

  • 创建组件:

    //Dialog.js
    import React from 'react';
    //=>每一个组件中都要导入REACT,因为需要基于它的CREATE-ELEMENT把JSX进行解析渲染呢
    
    /*
     * 函数式声明组件
     *   1.函数返回结果是一个新的JSX(也就是当前组件的JSX结构)
     *   2.PROPS变量存储的值是一个对象,包含了调取组件时候传递的属性值(不传递是一个空对象)
     *   3.children 代表双闭合组件中的子元素,相当于vue中插槽的概念
     *   
     */
    export default function Dialog(props) {//props形参,接收外界传递过来的数据,只读的;不能被重新赋值;
        let {con, lx = 0, style = {},children} = props,
            
          //=>children(代表双闭合组件中的子元素)
        return <section style={style}>
            <h2>{title}</h2>
            <div>{con}</div>
             {children}
        </section>;
    };
    
  • 使用组件,并为组件传递数据:

    import React from 'react';
    import ReactDOM from 'react-dom';
    

import Dialog from './component/Dialog'; //引入组件,组件的名称首字母必须是大写

ReactDOM.render(<div>
{/注释:JSX中调取组件,只需要把组件当做一个标签调取使用即可(单闭合和双闭合都可以)/}
<Dialog con='哈哈哈' style={{color: 'red'}}/>

 {/*属性值不是字符串,我们需要使用大括号包起来*/}
  <Dialog con='嘿嘿嘿' lx={1}  style={{color: 'red'}}>
      <span>1</span>  //这个双闭合标签里面的内容就是用children接受
      <span>2</span>
</Dialog>

</div>, root);



### 第2种 - 创建类式(有状态组件)

>必须继承 React.Component 和有一个render函数并且返回jsx

* 创建组件

```js
import React from 'react'

export default class Dialog extends React.Component {
      render() {
       //“this.props组件的属性是只读的”
        return <section>
            <h3>{this.props.lx}</h3>
            <div>{this.props.con}</div>
        </section>;
    }
   
}
  • 使用组件

    import React from 'react';
    import ReactDOM from 'react-dom';
    import Dialog from './01-component/Dialog' //引入组件
    
     
    ReactDOM.render(
      <Dialog con='哈哈哈' lx={10}/>, //使用组件
      document.getElementById('root')
    );
    

两种创建组件方式的对比

  1. 函数创建出来的组件:叫做“无状态组件”
  2. class关键字创建出来的组件:叫做“有状态组件”
  1. 用函数**创建出来的组件:叫做“无状态组件”【无状态组件今后用的不多】
  2. class关键字创建出来的组件:叫做“有状态组件”【今后用的最多】
  3. 什么情况下使用有状态组件?什么情况下使用无状态组件?
    • 如果一个组件需要有自己的私有数据,则推荐使用:class创建的有状态组件;
    • 如果一个组件不需要有私有的数据,则推荐使用:无状态组件;
    • React官方说:无状态组件,由于没有自己的state和生命周期函数,所以运行效率会比 有状态组件稍微高一些;

有状态组件和无状态组件之间的本质区别就是:有无state属性、和 有无生命周期函数;

10.组件的数据挂载方式

属性(props)

props是正常是外部传入的,属性不能被组件自己更改,组件内部也可以设置默认值,字符类型,

属性是描述性质、特点的,组件自己不能随意更改。

在使用一个组件的时候,可以把参数放在标签的属性当中,所有的属性都会作为组件 props 对象的键值。通过箭头函数创建的组件,需要通过函数的参数来接收props

给属性设置规则,需要安装第三方插件

yarn add prop-types

给属性设置默认值和规则

import React from 'react'
import PropTypes from 'prop-types';
export default class Dialog extends React.Component {
    //THIS.PROPS是只读的,我们无法在方法中修改它的值,但是可以给其设置默认值 
    static defaultProps = {
        lx: '系统提示'
    };

    //PROP-TYPES是FACEBOOK公司开发的一个插件
     基于这个插件我们可以给组件传递的属性设置规则
    static propTypes = {
         //=>不仅传递的内容是字符串,并且还必须传递
        con: PropTypes.string.isRequired, 

        //=>传递的内容需要是数字
        lx: PropTypes.number,

         //=>传递的内容需要是数组
        city:PropTypes.array,

        //=>数组中的每一项需要是字符串
        list:PropTypes.arrayOf(PropTypes.string),

        //=>自定义函数验证
        address:function(props,propname){
            if(props[propname] !=='aa'){
                return new Error('内容非法')
            }
            //console.log(arguments);
        }
    };

    render() {
       //“this.props组件的属性是只读的”
        return <section>
            <h3>{this.props.lx}</h3>
            <div>{this.props.con}</div>
        </section>;
    }
   
}

状态state

  • react中的state相当于vue中的data属性

  • state顾名思义就是状态,它只是用来控制这个组件本身自己的状态,我们可以用state来完成对行为的控制、数据的更新、界面的渲染,由于组件不能修改传入的props,所以需要记录自身的数据变化。

  • 更改状态,<span style="color:red">只能用 setState()方法</span>,当我们调用这个函数的时候,<span style="color:red">React.js 会更新组件的状态 state ,并且重新调用 render 方法</span>,然后再把 render 方法所渲染的最新的内容显示到页面上

  • setState 本身在生命周期函数或者在合成事件中执行是异步的

在保证生命周期函数执行的顺序不紊乱

保证其实现渲染队列的机制(可以合并setState统一渲染)

在原生的事件绑定中和其他异步操作中的set-state是同步的,没有渲染队列效果了

import React, { Component } from 'react'

export default class movie extends Component {
    constructor(){
        super()
       //state定义的第一种方式
      //设置状态
        this.state = {
            title:"中国机长"
        }
    }
  
  //state定义的第二种方式
 // state ={
//   title:"中国机长",
//   count:1
// }
  
    componentDidMount(){
      //更改状态,因为是异步的,想立即获取更改后的内容,可以用第2个参数:回调函数
      //更改state第一种方式
        this.setState({
            title:"战狼"
        },() => {
          //相当于vue $nextTick
            console.log(this.state.title);
        }
        )
        
      //更改state的第二种方式
      //this.state.title="战狼"
      //this.setState({})
    }
    render() {
        return (
            <div>
                {this.state.title}
            </div>
        )
    }
}

组件中的 propsstate/data 之间的区别

  • props 中的数据都是外界传递过来的;
  • state/data 中的数据,都是组件私有的;(通过 Ajax 获取回来的数据,一般都是私有数据);
  • props 中的数据都是只读的;不能重新赋值;
  • state/data 中的数据,都是可读可写的;

状态提升

如果有多个组件共享一个数据,把这个数据放到共同的父级组件中来管理

受控组件与非受控组件

  • 受控组件

    • 其实就相当于vue中的v-model指令,在react中是自己手动实现,用onChange事件

    • <span style="color:red">双向数据绑定就是受控组件</span>

    • 受控组件就是可以被 react 状态控制的组件
      在 react 中,Input textarea 等组件默认是非受控组件(输入框内部的值是用户控制,和React无关)。

      但是也可以转化成受控组件,<span style="color:red">就是通过 onChange 事件获取当前输入内容,将当前输入内容作为 value 传入,此时就成为受控组件</span>。
      好处:可以通过 onChange 事件控制用户输入,过滤不合理输入。

    image.png
    • import React, { Component} from 'react';
      
      export default class Form extends Component {
        constructor() {
          super();
          this.state = {
            name: "",
            desc: "",
          }
           
        }
      
        handleChange(event) {
          this.setState({
            [event.target.name]: event.target.value //把表单中的值赋值给状态
          })
        }
       
      
        render() {
          return (
            <div>//表单中的value,绑定了state上的状态值
                 <input type="text" value={this.state.name} name="name" 
                                onChange={this.handleChange.bind(this)} /> 
                    <textarea          value={this.state.desc} name="desc"
                            onChange={this.handleChange.bind(this)}>
              </textarea> 
            </div>
          );
        }
      }
      
      
      
  • 非受控组件

    • 不受状态控制的就是非受控组件

    • 基于REF操作DOM实现视图更新的,叫做“非受控组件”

    • 使用场景,必须手动操作DOM元素,setState 实现不了的,例如文件上传 <input type=file>

    • import React,{Component} from 'react';
      import ReactDOM from 'react-dom';
      
      export default class Sum extends Component{
          handleChange=(event)=>{
              let a = parseInt(this.refs.a.value||0);
              let b = parseInt(this.refs.b.value||0);
              this.refs.result.value = a+b;
          }
          render(){
              return (
                  //经过React封装可以onChange可以写在div上
                  <div onChange={this.handleChange}> 
                      <input type="text" ref="a" /> +
                      <input type="text" ref="b" /> = 
                      <input type="text" ref="result" />
                  </div>
                  //input是非受控组件,因为不受状态控制
              )
          }
      }
      

11.组件中DOM样式

  • 行内

    import React from 'react'
     
    export default class Movie extends React.Component {
        render() {
            return <div>
                {/* 行内样式 */}
                <h1 style={{ fontSize: '20px', color: 'red' }}>正在热映</h1>
            </div>
        }
    }
    
  • 外部引入

    <span style="color:red">这种引入方式是全局生效</span>

    import React from 'react'
    import './Movie.css' //从外部引入
     
    
    export default class Movie extends React.Component {
        render() {
            return <div className="wrap">
                <h1>正在热映</h1>
            </div>
        }
    }
    
  • 模块化

    <span style="color:red">这种引入方式是局部生效</span>,目的解决全局冲突

    • Vue 组件中的样式表,有没有 冲突的问题???

      Vue 组件中的样式表,也有冲突的问题;但是,可以使用 <style scoped></style>

    • React 中,有没有类似于 scoped 这样的指令呢?

    ​ 没有;因为 在 React 中,根本就没有指令的概念;

    ​ 模块块就是为了解决冲突的问题,局部生效

    • 文件名必须是 *.module.css结尾,同样的,如是要是.sass.scss的话,文件名格式应该是[name].module.sass[name].module.scss

    • 其实模块化,就是webpack给css-loader配置了参数modules

     module: {  
        rules: [ 
          // 其中,有个固定的参数,叫做 modules , 表示为 普通的 CSS 样式表,启用模块化
          { test: /\.scss$/, use: ['style-loader', 'css-loader?modules', 'sass-loader'] }  
        ]
      }
    
    import React from 'react'
    import common from "./common.module.css"  //引入css文件
    
    export default class Movie extends React.Component {
       render() {
           return <div>
               <h1 className={common.error}> //使用css
                   gggg
               </h1>
           </div>
       }
    }
    
    
    //common.module.css 文件
    .error{
       color:red;
       font-size: 30px;
    }
    
  • 不同的条件添加不同的样式

    • 安装
    yarn add classnames
    
    • 特点就是根据动态数据控制样式

    • 局部,不会全局污染

    代码:

    import React  from 'react'
    import classNames from 'classnames/bind' //引入第三方插件
    import styles from './styles.css'       //引入样式
    
    let cx = classNames.bind(styles) //绑定在一块
    export default class profile extends React.Component{
       render(){
           let names = cx({
               inProcess:true,  //根据数据来决定使用哪一个样式
               error:false
           })
           return <div>
               <h1 className={names}>gp18</h1>
           </div>
       }
    }
    
    //styles.css文件
    .inProcess {
     color: orange;
     font-size: 30px;
    }
    
    .error {
     color: red;
     font-size: 30px;
    }
    
  • css-in-js

    • styled-components`是针对React写的一套css-in-js框架,简单来讲就是在js中写css

    • 解决全局污染

    • 安装

    yarn add styled-components
    

    代码:

    import React from 'react'
    
    import {Wrap} from './search-style' //引入样式
    export default class Search extends  React.Component{
        render(){
            return <Wrap color="red">  //使用
                yyyy
                <h1>hhh</h1>
            </Wrap>
        }
    }
    
    //search-style.js 文件 
    import React from 'react'
    import styled from 'styled-components' //引入第三方插件
    
    const Wrap = styled.div`
        width:500px;
        height:500px;
        background:${(props)=>props.color}; //动态赋值
        font-size:30px;
        h1 {
          font-size:50px;
        }
    `
    export {
        Wrap
    }
    

12.事件处理

  • 采用on+事件名的方式来绑定一个事件,注意,这里和原生的事件是有区别的,原生的事件全是小写onclick, React里的事件是驼峰onClickReact的事件并不是原生事件,而是合成事件

  • Event对象

    和普通浏览器一样,事件handler会被自动传入一个 event 对象,这个对象和普通的浏览器 event 对象所包含的方法和属性都基本一致。不同的是 React中的 event 对象并不是浏览器提供的,而是它自己内部所构建的。它同样具有event.stopPropagationevent.preventDefault 这种常用的方法

  • 事件的几种写法,目的能访问到this

    • 第一种写法 <span style="color:red"> (不能传递参数)</span>
    import React, { Component } from 'react'
    
    export default class event1 extends Component {
        constructor() {
            super()
            this.state = {
                name: 'gp18'
            }
        }
    
        handleClick=(event) => { //箭头函数
            console.log(this.state.name);
        }
    
        render() {
            return (
                <div> 
                    /*这种绑定事件的方法,要想this能访问到,
                               handleClick函数必须用箭头函数
                               */
                  <button onClick={this.handleClick}>add</button>
                </div>
            )
        }
    }
    
    • 第二种写法 <span style="color:red"> (不能传递参数)</span>
    import React, { Component } from 'react'
    
    export default class event1 extends Component {
        constructor() {
            super()
            this.state = {
                name: 'gp18'
            }
          
          //@key
            this.myhandleclick = this.handleClick.bind(this)
        }
    
        //@key 最后追加一个参数,即可接受event
        handleClick(val, event) {
            console.log(val, event, this.state.name);
        }
    
        render() {
            return (
                <div>
                    <button 
                    onClick={this.myhandleclick}  //@key
                    >添加</button>
                </div>
            )
        }
    }
    
    • 第三种写法 <span style="color:red"> (可以传递参数)</span>
    import React, { Component } from 'react'
    export default class event1 extends Component {
        constructor() {
            super()
            this.state = {name: 'gp18'}
        }
    
       //触发函数
        handleClick(val,event){
            console.log(this.state.name,val,event);
        }
    
        render() {
            return (
                <div>
                   //因为handleClick函数不是箭头函数,需要改变this指向
                    <button 
                    onClick={this.handleClick.bind(this,'abc')}
                    >添加</button>
                </div>
            )
        }
    }
    
    • 第四种写法 <span style="color:red"> (可以传递参数)</span>
    import React, { Component } from 'react'
    export default class event1 extends Component {
        constructor() {
            super()
            this.state = {name: 'gp18'}
        }
    
        handleClick(val,event){
            console.log(this.state.name,val,event);
        }
    
        render() {
            return (
                <div>  
                      <button onClick={
                          (event) =>{return this.handleClick('abc',event)}
                      }>添加</button>
                </div>
            )
        }
    }
    

13. 组件生命周期

  • 生命周期的概念

每个组件的实例,从创建、到运行、直到销毁,在这个过程 中,会出发一些列 事件,这些事件就叫做组件的生命周期函数;

  • 生命周期分为三部分:

  • 组件创建阶段

组件创建阶段的生命周期函数,有一个显著的特点:创建阶段的生命周期函数,在组件的一辈子中,只执行一次;

  • componentWillMount

​ 组件将要被挂载,此时还没有开始渲染虚拟DOM

  • render:

​ 第一次开始渲染真正的虚拟DOM,当render执行完,

​ 内存 中就有了完整的虚拟DOM了

  • componentDidMount

​ 组件完成了挂载,此时,组件已经显示到了页面上,

​ 当这个方法执行完,组件就进入都了 运行中 的状态

​ 如果要初始化第三方的dom库,也在这里进行初始化。只有到这里才能获取到真实的dom.

通常在这里进行ajax请求

  • 组件运行阶段

也有一个显著的特点,根据组件的state和props的改变,有选择性的触发0次或多次;

  • componentWillReceiveProps:

​ 组件将要接收新属性,此时,只要这个方法被触发,

​ 就证明父组件为当前子组件传递了新的属性值;

         props改变的时候
  • shouldComponentUpdate:

​ 组件是否需要被更新,此时,组件尚未被更新,

​ 但是,state 和 props 肯定是最新的

​ 在渲染新的propsstate前,shouldComponentUpdate会被调用。默认为true

​ 这个方法不会在初始化时被调用,也不会在forceUpdate()时被调用。

​ 返回false不会阻止子组件在state更改时重新渲染。

​ 如果shouldComponentUpdate()返回falsecomponentWillUpdate,rendercomponentDidUpdate不会被调用。

  • componentWillUpdate:

​ 组件将要被更新,此时,尚未开始更新,

​ 内存中的虚拟DOM 树还是旧的

  • render:

​ 此时,又要重新根据最新的 state 和 props

               重新渲染一棵内存中的 虚拟DOM树,当 render 调用完毕,内存中的旧DOM树,

​ 已经被新DOM树替换了!此时页面还是旧的

  • componentDidUpdate

​ 此时,页面又被重新渲染了,state 和 虚拟DOM 和页面已经 完全保持同步

  • 组件销毁阶段

也有一个显著的特点,一辈子只执行一次;

  • componentWillUnmount:

​ 组件将要被卸载,此时组件还可以正常使用;

​ 在组件被卸载并销毁之前立即被调用。在此方法中执行任何必要的清理,

​ 例如使定时器无效,取消网络请求或清理在componentDidMount中创建的任何监听

  • 生命周期图

    image.png

新版本v16.4 的生命周期函数

  • v17版本

componentWillMount,componentWillReceiveProps,componentWillUpdate这三个函数将要作废

  • v16.4新增的生命周期函数

  • getDerivedStateFromProps

    //可以拿到父组件传递过来的属性,同时可以拿到当前组件的state
    //可以把传递过来的属性合并到当前组件state上
      static getDerivedStateFromProps(props, state) {
        console.log('getDerivedStateFromProps:', props, state)
        return {
          ...props,
          ...state
        }
      }
    
  • getSnapshotBeforeUpdate()

    在react render()后的输出被渲染到DOM之前被调用。它使您的组件能够在它们被潜在更改之前捕获当前值(如滚动位置)。这个生命周期返回的任何值都将作为参数传componentDidUpdate()

    只要写这个生命周期函数就必须写componentDidUpdate

    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log("getSnapshotBeforeUpdate:", prevProps, prevState)
        return {
          username:'hanye',
          age:20
        };
      }
    
    componentDidUpdate(prevProps, prevState,sanpshot){
        console.log("componentDidUpdate:",prevProps, prevState,sanpshot)
      }
    
image.png

PureComponent

PureComponnet里如果接收到的新属性或者是更改后的状态和原属性、原状态相同的话,就不会去重新render了
在里面也可以使用shouldComponentUpdate,而且。是否重新渲染以shouldComponentUpdate的返回值为最终的决定因素。此方法仅作为性能优化的方式而存在。不要企图依靠此方法来“阻止”渲染,因为这可能会产生 bug。你应该**考虑使用内置的 [PureComponent](当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。返回值默认为 true。首次渲染或使用 forceUpdate() 时不会调用该方法。)

import React, { Component,PureComponent } from 'react';
export default class Parent extends PureComponent { //@Key

 constructor() {
   super();
   this.state = { count: 1,}
  
 }

 render() {
   return (
     <div id="app">
       <button onClick={this.handleClick}> 强制更新 </button>
     </div>
   );
 }

 handleClick = () => {
   this.state.count = 100;
   this.forceUpdate(); //@key
 }
}

14 HOC高阶组件

  • 定义

具体而言,高阶组件是参数为组件,返回值为新组件的函数

const EnhancedComponent = higherOrderComponent(WrappedComponent);
  • 作用

在多个不同的组件中需要用到相同的功能,这个解决方法,通常是高阶组件。

  • 例子
//定义一个高阶组件 Hoc.js
import React, { Component } from 'react'
const Hoc = (WrapperCommonent)=>{
    return class Copyright extends Component {
        render() {
            return (
                <div>
                    <WrapperCommonent></WrapperCommonent>
                    <div>版权所有</div>
                </div>
            )
        }
    }
}
export default Hoc


//使用高阶组件
import React, { Component } from 'react'
import Hoc from './Hoc'
class Base extends Component {
    render() {
        return (
            <div>
                react.js 是一个构建用户节目的库
            </div>
        )
    }
}
export default  Hoc(base)  //@Key

15.组件通信

父组件与子组件通信

父组件将自己的状态传递给子组件,子组件当做属性来接收,当父组件更改自己状态的时候,子组件接收到的属性就会发生改变

//父组件
import React, { Component } from 'react'
import Child from './Child'
export default class Parent extends Component {
   constructor(){
       super()
       this.state = {count:1}
   }
   render() {
       return (
        <Child count={this.state.count}></Child>  //状态传递给子组件
       )
   }
}
//子组件
import React, {Component} from 'react'

export default class Child extends Component {
   render() {
       return  <div>
           {this.props.count} //子组件当做属性来接收
       </div>
       
   }
}

子组件与父组件通信

父组件将自己的某个方法传递给子组件,在方法里可以做任意操作,比如可以更改状态,子组件通过this.props接收到父组件的方法后调用。

//父组件
import React, { Component } from 'react'
import Child from './Child'
export default class Parent extends Component {
   constructor(){
       super()
       this.state = {count:1}
   }

   handclick=()=>{
       this.setState({
           count:10
       })
   }
   render() {
       return (
         <div>
               {this.state.count}
               <Child  fn={this.handclick}></Child>  //将方法传递给子组件
         </div>
       
       )
   }
}

//子组件
import React, {Component} from 'react'

export default class Child extends Component {
   componentDidMount(){
       this.props.fn()
   }
   render() {
       return  <div>
          child
       </div>
       
   }
}

跨组件通信 context

在react没有类似vue中的事件总线来解决这个问题,我们只能借助它们共同的父级组件来实现,将非父子关系装换成多维度的父子关系。react提供了context api来实现跨组件通信, React 16.3之后的contextapi较之前的好用。

在父组件上提供数据,其他后代组件,都能访问到该数据

举个计数器的例子,这个例子是context在实际项目这样写,具体基础的看文档

//Mycontext.js
import React, { Component,createContext} from 'react'
const {Provider,Consumer:MyConsumer} = createContext()

class MyProvider extends Component {
   constructor(){
       super()
       this.state = {count:1}
   }

   incr=()=>{
       this.setState({
           count:this.state.count+1
       })
   }

   render() {
       return ( //需要共享的数据写在value里
           <Provider value={{...this.state,'incr':this.incr,'decr':this.decr}}>
               {this.props.children}
           </Provider>
       )
   }
}
export {MyProvider,MyConsumer}

//父组件 Parent.js
import React, { Component } from 'react'
import Child from './Child'

import {MyProvider} from './Mycontext'
export default class Parent extends Component {
   render() {
       return (
           <MyProvider>
               <Child></Child>
           </MyProvider>
       )
   }
}
//后代组件 Child.js
import React, { Component } from 'react'
import {MyConsumer} from './Mycontext'

export default class Button extends Component {
   render() {
       return (//后代访问共享数据
           <MyConsumer>
               {
                   ({incr,count})=>{
                   return  <button onClick={incr}>add按钮:{count}</button>
                   }
               }
           </MyConsumer>
       )
   }
}

redux

  • 什么是redux?

    进行状态统一管理的类库(适应于任何技术体系的项目)

1.只要两个或多个组件之间想要实现信息的共享,都可以基于redux解决,把共享的信息存储到redux容器进行管理

2.还可以做临时存储,页面加载的时候,把从服务器获取的数据信息存储到redux中

  • 为什么要用redux

因为对于react来说,同级组件之间的通信尤为麻烦,或者是非常麻烦了,所以我们把所有需要多个组件使用的state拿出来,整合到顶部容器,进行分发

  • 需要使用redux的项目

  • 用户的使用方式复杂
  • 不同身份的用户有不同的使用方式(比如普通用户和管理员)
  • 多个用户之间可以协作
  • 与服务器大量交互,或者使用了WebSocket
  • View要从多个来源获取数据
  • 从组件层面考虑,什么样子的需要Redux:

  • 某个组件的状态,需要共享
  • 某个状态需要在任何地方都可以拿到
  • 一个组件需要改变全局状态
  • 一个组件需要改变另一个组件的状态
  • Redux的设计思想

  1. Web 应用是一个状态机,视图与状态是一一对应的。
  2. 所有的状态,保存在一个对象里面(唯一数据源)
  • Redux的使用的三大原则

  • Single Source of Truth(唯一的数据源)
  • State is read-only(状态是只读的)
  • Changes are made with pure function(数据的改变必须通过纯函数完成)
  • Redux的流程

image.png
  • Store的角色是整个应用的数据存储中心,集中大部分页面需要的状态数据;
    ActionCreators ,view 层与data层的介质;
    Reduce ,接收action并更新Store。
    所以流程是 用户通过界面组件 触发ActionCreator,携带Store中的旧State与Action 流向Reducer,Reducer返回新的state,并更新界面。

  • 自己实现的redux

<!DOCTYPE html>
<html lang="en">
<body>
    <button onclick="store.dispatch({type:'DECR',num:1})">-</button>
    <h1 id="count"></h1>
    <button onclick="store.dispatch({type:'INCR',num:1})">+</button>

    <script>
        //集中管理state,和dispatch,action
        const createStore = (reducer) => {
            let state;
            const getState = () => state
            //定义一个监听器队列,管理所有订阅的方法
            const listeners = []
            const subscribe = (listener) => listeners.push(listener)

            const dispatch = (action) => {
                //1.执行REDUCER,修改容器中的状态信息
                state = changeState(state, action)
                //2.容器中状态信息经过REDUCER修改后,通知事件池中的方法依次执行
                listeners.forEach((item) => {
                    item()
                })
            }

            //=>创建容器的时候执行一次DISPATCH,
            //=>目的是把REDUCER中的默认状态信息赋值给REDUX容器中的状态
            dispatch({
                type: '$$INIT_DEFAULT_STATE'
            });

            return {
                getState,
                subscribe,
                dispatch
            }
        }

        //其实这个方法就是写自己的逻辑的地方
        const changeState = (state = {
            count: 0
        }, action) => {
            switch (action.type) {
                case "INCR":
                    return {
                        count: state.count + action.num
                    }
                    case "DECR":
                        return {
                            count: state.count - action.num
                        }
                        default:
                            return state
            }
        }


        const store = createStore(changeState)
        store.subscribe(render)

        function render() {
            document.getElementById("count").innerHTML = store.getState().count;
        }
        render()
    </script>
</body>
</html>

react-redux

  • 安装

yarn add redux react-redux
yarn add redux-thunk //异步请求数据要用到这个插件
  • redux

适应于所有项目,store = createStore(reducer),然后在需要的地方通过store.getState()去获取数据,通过store.dispatch去更新数据,通过store.subscribe去订阅数据变化然后进行setState...如果很多地方都这样做一遍,实在是不堪其重

  • REACT-REDUX

  • 描述

​ react-redux只是适应react项目

​ 是把REDUX进一步封装,适配REACT项目,让REDUX操作更简洁

​ STORE文件夹中的内容和REDUX一模一样

​ 在组件调取使用的时候可以优化一些步骤

  • 相对于传统的REDUX,我们做的步骤优化

​ 导出的不在是我们创建的组件,而是基于CONNECT构造后的高阶组件

​ export default connect([mapStateToProps], [mapDispatchToProps])([自己创建的组件]);

​ REACT-REDUX帮我们做了一件非常重要的事情:

​ 以前我们需要自己基于SUBSCRIBE向事件池追 加方法,以达到容器状态信息改变,

​ 执行我们追加的方法,重新渲染组件的目的,但是现在不用了,

​ REACT-REDUX帮我们做了这件事:“所有用到REDUX容器状态信息的组件,都会向事件池 中追加一个方法,当状态信息改变,通知方法执行,

​ 把最新的状态信息作为属性传递给组件,组件的属性值改变了,组件也会重新渲染”

  • react-redux 其实就提供了这四个东西

  • Provider 根组件

当前整个项目都在Provider组件下

作用就是把创建的STORE可以供内部任何后代组件使用(基于上下文完成的)

=>Provider组件中只允许出现一个子元素

=>把创建的STORE基于属性传递给Provider(这样后代组件中都可以使用这个STORE了)

redux的时候,每个组件想使用store,属性都得传store

这个函数的底层实现原理

​ 其实就是根据上下文实现的

​ PROVIDER:当前项目的“根”组件

​ 接收通过属性传递进来的STORE,把STORE挂载到上下文中,

​ 这样当前项目中任何一个组件中,想要使用REDUX中的STORE,

​ 直接通过上下文获取即可

class Provider extends React.Component {
   //=>设置上下文信息类型
   static childContextTypes = {
       store: PropTypes.object
   };

   //=>设置上下文信息值
   getChildContext() {
       return {
           store: this.props.store
       };
   }

   constructor(props, context) {
       super(props, context);
   }

   render() {
       return this.props.children;
   }
}
  • connect 高阶组件

CONNECT:高阶组件(基于高阶函数:柯理化函数)创建的组件就是高阶组件
@PARAMS
   mapStateToProps:回调函数,把REDUX中的部分状态信息挂载到指定组件的属性上
   function mapStateToProps(state){
        //=>state:REDUX容器中的状态信息
      return {};  //=>RETURN对象中有啥,就把啥挂载到属性上
        }
      ```
      
    mapDispatchToProps:回调函数,把一些需要派发的任务方法也挂载到组件的属性上
     ```
   function mapDispatchToProps(dispatch){
        //=>dispatch:store中的dispatch
          return {
                init(){
                   dispatch({...});
                }
           };
           //=>RETURN啥就把啥挂载到属性上(返回的方法中有执行dispatch派发任务的操作)
        }
      ```

  @RETURN
   返回一个新的函数 CONNECT-HOT

====

CONNECT-HOT
@PARAMS
传递进来的是要操作的组件,我们需要把指定的属性和方法都挂载到当前组件的属性上

@RETURN
返回一个新的组件Proxy(代理组件),在代理组件中,我们要获取Provider在上下文中存储的store,紧接着获取store中的state和dispatch,把mapStateToProps、mapDispatchToProps回调函数执行,接收返回的结果,在把这些结果挂载到Component这个要操作组件的属性上

function connect(mapStateToProps, mapDispatchToProps) {
return function connectHOT(Component) {
return class Proxy extends React.Component {
//=>获取上下文中的STORE
static contextTypes = {
store: PropTypes.object
};

         constructor(props, context) {
             super(props, context);
        this.state = this.queryMountProps();
         }
     
         //=>基于REDUX中的SUBSCRIBE向事件池中追加一个方法,当容器中状态改变,我们需要重新获取最新的状态信息,并且重新把COMPONENT渲染,把最新的状态信息通过属性传递给COMPONENT
         componentDidMount() {
            this.context.store.subscribe(() => {
                 this.setState(this.queryMountProps());
             });
     }
     
         //=>渲染COMPONENT组件,并且把获取的信息(状态、方法)挂载到组件属性上(单独调取POXY组件的是时候传递的属性也给COMPONENT)
         render() {
             return <Component {...this.state} {...this.props}/>
         }
    
         //=>从REDUX中获取最新的信息,基于回调函数筛选,返回的是需要挂载到组件属性上的信息
         queryMountProps = () => {
             let {store} = this.context,
                 state = store.getState();
             let propsState = typeof mapStateToProps === 'function' ? mapStateToProps(state) : {};
            let propsDispatch = typeof mapDispatchToProps === 'function' ? mapDispatchToProps(store.dispatch) : {};
     
             return {
                 ...propsState,
                 ...propsDispatch
             };
         };
     }
}

}


  • mapStateToProps函数
let mapStateToProps = state => {

  //=>state:就是REDUX容器中的状态信息

  //=>我们返回的是啥,就把它挂载到当前组件的属性上
  //=>(REDUX存储很多信息,我们想用啥就返回啥即 可)
  return {
     ...state.vote
  };

};
  • mapDispatchToProps
//=>把REDUX中的DISPATCH派发行为遍历,也赋值给组件的属性(ActionCreator)
let mapDispatchToProps = dispatch => {
   //=>dispatch:STORE中存储的DISPATCH方法
   //=>返回的是啥,就相当于把啥挂载到组件的属性上
   //(一般我们挂载一些方法,这些方法中完成了DISPATCH派发任务操作)
   return {
       init(initData) {
           //action.vote.init(initData)返回的就是类似{type:'aa',...}
           dispatch(action.vote.init(initData));
       }
   };
};

export default connect(state => ({...state.vote}), action.vote)(VoteBase);//=>REACT-REDUX帮我们做了一件事情,把ACTION-CREATOR中编写的方法(返回ACTION对象的方法),自动构建成DISPATCH派发任务的方法,也就是mapDispatchToProps这种格式
  • 具体使用

  • 目录

    store

​ reducer 存放每一个模块的reducer

​ vote.js

​ personal.js

​ ...

​ index.js 把每一个模块的reducer最后合并成为一个reducer

​ action 存放每一个模块需要进行的派发任务(ActionCreator)

​ vote.js

​ personal.js

​ ...

​ index.js 所有模块的ACTION进行合并

​ action-types.js 所有派发任务的行为标识都在这里进行宏观管理

​ index.js 创建STORE

  • 代码

  • reducer目录的代码

    image.png
  • action目录的代码

    aa.jpeg
  • action-types.js 文件

    image.png
  • index.js 文件

    image.png
  • 使用

  • 在入口index.js提供数据store

image.png
  • 在子组件消费数据
image.png

16 路由

  • 安装

    react-router-dom
    
  • 第一个例子:基本路由

    • 基本使用

    • 路由跳转

    • 匹配不到返回404

    • 两种方式的传参及接受参数

    • 使用component属性渲染组件

    import React, { Component } from 'react'
    import {BrowserRouter as Router,Route,Link,useLocation,Redirect,Switch} from 'react-router-dom'
    
    function Home(props) {
        //获取参数
        let result = new URLSearchParams(useLocation().search)
        console.log(result.get("city"));
        
        return <h2>Home</h2>
    }
    
    function About(props) {
        //获取参数
        console.log(props.match.params.id);
        return <h2>About</h2>;
    }
     
    
    export default class index extends Component {
        render() {
            return (
                <Router> //必须用Router包裹
                    <Link to="/">首页</Link>&nbsp;
                    <Link to="/about/66">关于</Link>&nbsp;
                    
                    <Link to="/my">我的</Link>
                    
    
                    <hr/>
                    <Switch> //Switch:排他性,只能匹配到一个路由
                        <Route exact path="/index/" component={Home}></Route>
                        <Route path="/about/:id" component={About}></Route>
                        
                        <Route path="/my">我的</Route>
    
                        <Redirect exact from="/" to="/index?city=北京"></Redirect>
    
                        <Route>404</Route>
                    </Switch>
                </Router>
            )
        }
    }
    
    
  • 第二例子 嵌套路由

    //一级路由
    import React, { Component } from 'react'
    import {BrowserRouter as Router,Route,Link,useLocation,Redirect,Switch} from 'react-router-dom'
    
    import Order from './Order'
    export default class index extends Component {
        render() {
            return (
                <Router>
                 <Link to="/order">订单</Link>
     
                  <Route  path="/order" component={Order}></Route>
                    
                </Router>
            )
        }
    }
    
    //二级路由 Order.js
    import React, { Component } from 'react'
    
    import  {Link,Route,Switch} from 'react-router-dom'
    export default class Movie extends Component {
        render() {
            return (
                <div>
                   <Link to="/order/pay">已支付</Link>
                   <Link to="/order/unpay">未支付</Link>
                <hr/>
                <Switch>
                    <Route exact path="/order/pay">pay</Route>
                    <Route exact path="/order/unpay">unpay</Route>
                </Switch>
               
                </div>
            )
        }
    }
    
  • 第三个例子 render

    • 用render函数渲染组件

    • 可以根据逻辑,来判断渲染哪一个组件

    import React, { Component } from 'react'
    import {BrowserRouter as Router,Route,Switch,Link} from 'react-router-dom'
    
    function Login(props) {
        return <h2>Login</h2>;
    }
    function Pos(props) {
        return <h2>Pos</h2>;
    }
    
    export default class index extends Component {
        constructor(){
            super()
            this.state = {isLogin:false}
        }
    
        render() {
            return (
                <Router>
                    <Link to="/index">首页</Link>&nbsp;
                    <Link to="pos">职位管理</Link>
    
                    <Route path="/index">index</Route>
                    <Route path="/pos" render={()=>{
                     return  this.state.isLogin ?<Pos></Pos> : <Login></Login>
                    }}></Route>
                </Router>
            )
        }
    }
    
    
  • 第四例子 NavLink

    • 一般用NavLink比较多
    • 点中哪个链接,自动给加个 class="active"
    • Link 标签不会自动加的
    import React, { Component } from 'react'
    import {BrowserRouter as Router,Route,NavLink,useLocation,Redirect,Switch} from 'react-router-dom'
    
    export default class index extends Component {
        render() {
            return (
                <Router>  
                    <NavLink to="/index">首页</NavLink>&nbsp;
                    <NavLink to="/about">关于</NavLink>&nbsp;
                    <hr/>
                   
                        <Route exact path="/index/" >index</Route>
                        <Route path="/about/"  >about</Route>
                </Router>
            )
        }
    }
    
  • 第五个例子 把a链接换成其他标签

    • NavLink /Link 生成的是a链接,在外边在用li标签包裹一下怎么做呢
    import React, { Component } from 'react'
    import {BrowserRouter as Router,Route,NavLink} from 'react-router-dom'
    
     //自定义链接
    const CustomLink = (props)=>{
      return <li>
          <NavLink to={props.path}>{props.name}</NavLink>
      </li>
    }
    export default class index extends Component {
        render() {
            return (
                <Router>
                      <CustomLink path="/index" name="首页"></CustomLink>
                        <NavLink to="/movie">电影</NavLink>&nbsp;
                    <hr></hr>
                        <Route  path="/index">home</Route>
                        <Route  path="/movie">movie</Route>
                </Router>
            )
        }
    }
    
  • 第六个例子 编程式导航

    • 不像vue那么简单,一个函数搞定
    • 这个是自己实现的
    import React, { Component } from 'react'
    import './style.css'  //这个就是定义激活active的样式
    import {BrowserRouter as Router,Route,Link,NavLink,Redirect,Switch} from 'react-router-dom'
    
    import City from './City' //这个就是自己定义的编程式导航
    export default class index extends Component {
        
        render() {
            return (
                <Router>
                        <NavLink exact to="/">我的</NavLink> &nbsp;
                        <City path="/city"></City>
                    <hr></hr>
                   
                        <Route exact path="/">profile</Route>
                        <Route path="/city">City</Route>
                </Router>
            )
        }
    }
    
    //自己定义的编程式导航  City.js
    import React, { Component } from 'react'
    import {withRouter} from 'react-router-dom'
    
    class City extends Component {
        handleClick=()=>{
          //编程式导航
          this.props.history.push(this.props.path)
        }
    
        render() {
            return (
                <li className={this.props.path == this.props.location.pathname? 'active':''} onClick={this.handleClick}>城市</li>
            )
        }
    }
    //自定义链接是拿不到路由信息, 这个必须用withRouter这个高阶函数增强一下,才能拿到路由相关的信息
    export default withRouter(City) 
    
  • 第七个例子 用children函数来渲染组件

    • 不管路由匹配上或者匹配不上,总会执行它

    • 只不过区别是匹配上 props.match 不为空,否则为空

    import React, { Component } from 'react'
    import './style.css'
    import {BrowserRouter as Router,Route,NavLink} from 'react-router-dom'
     
    export default class index extends Component {
        
        render() {
            return (
                <Router>
                        <NavLink exact to="/">我的</NavLink> &nbsp;
                        <Route path="/setting" children={(props)=>{
                            return <NavLink  to="/setting">设置</NavLink>
                        }}></Route>
    
                    <hr></hr>
                        <Route exact path="/">profile</Route>
                        <Route  path="/setting">setting</Route>
                </Router>
            )
        }
    }
    

安装 React Developer Tools 调试工具

React Developer Tools - Chrome 扩展下载安装地址

总结

理解React中虚拟DOM的概念
理解React中三种Diff算法的概念
使用JS中createElement的方式创建虚拟DOM
使用ReactDOM.render方法
使用JSX语法并理解其本质
掌握创建组件的两种方式
理解有状态组件和无状态组件的本质区别
理解props和state的区别

相关文章

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345