React之旅一

一.引子

一个优秀的程序员不会局限于一种语言
一个优秀的前端攻城狮不会局限于一种框架
何况它还那么的强大

一开始还是我是拒绝学习的 直到我司来了一位10年开发经验的大佬…向大佬学习!!!

二.什么是react

React是Facebook开发的一款JS库。React一般被用来作为MVC中的V层,它不依赖其他任何的库,因此开发中,可以与任何其他的库集成使用,包括Jquery、Backbone等。它可以在浏览器端运行,也可以通过nodejs在服务端渲染。React的思想非常独特,性能出众,可以写出重复代码少,逻辑清晰的前端代码。React的语法是jsx,通过使用这种语法,可以在react代码中直接混合使用js和html来编写代码,这样代码的逻辑就非常清晰,当然也意味着,需要将jsx代码编译成普通的javascript代码,才能在浏览器中运行,这个过程根据实际项目情况,可以选择多种不同的思路,或者在服务器端通过webpack进行编译。
React是MVC框架使用数据驱动视图,当数据发生变化视图发生变化。若只是修改视图,数据并不会改变。
Vue是MVVM框架。双向数据绑定,当数据发生变化视图发生变化。视图发生变化,数据也会随之改变
React官网:https://zh-hans.reactjs.org/

三.React脚手架

  1. 什么是react脚手架:
    FaceBook官方发布了一个无需配置,用于快速构建开发环境的脚手架工(create-react-app)
  2. 如何使用:

    1. 使用npm自带的npx安装(官方推荐,本地就不会下载create-react-app,随用随创)

      1
      2
      3
      npx create-react-app project-nameprojectName
      cd project-name
      npm run start
    2. 全局安装脚手架创建

      1
      2
      3
      4
      5
      6
      <!-- 全局安装脚手架 -->
      npm install -g create-react-app
      <!-- 在指定的目录下创建你的新项目 -->
      create-react-app project-name
      cd project-name
      npm run start
    3. 注意:
      React帮我们将艮长的配置文件全部隐藏了。只能通过eject命令将所有的配置文件暴露出来,但请牢记这个操作是不可逆的。

      1
      npm run eject

四.React中的虚拟Dom是什么?

React中是如何通过ReactDOM.render()方法生成Dom节点的呢?

  1. 什么是JSX?
    JSX是React的核心组成部分,它使用XML标记的方式去直接声明界面,界面组件之间可以互相嵌套。可以理解为在JS中编写与XML类似的语言,一种定义带属性树结构(DOM结构)的语法,它的目的不是要在浏览器或者引擎中实现,它的目的是通过各种编译器将这些标记编译成标准的JS语言。
    你可以将HTML 语言直接写在 JavaScript 语言之中,不加任何引号,这就是 JSX 的语法,它允许 HTML 与 JavaScript 的混写

    1
    2
    3
    4
    5
    6
    7
    render() {
    return (
    <div>
    <h1 className="text-style" >Hello, world!</h1>
    </div>
    );
    }

    JSX将原本HTML与javaScript混写的代码通过 babel-preset-react-app 插件将代码转换成React.createElement(elementn,props,[…children])可以创建的方式

  2. 他是如何转换的?
    看上面那部分代码,我们将它放在babeljs上查看下

    1
    2
    3
    4
    5
    <!-- 转换后的结果 -->
    React.createElement("div", null, React.createElement("h1", {
    className: "text-style",
    name: 1212
    }, "Hello, world!"));
  3. React.createElement(elementn,props,[…children])解析
    element: 要创建的HTML元素 也就是我们常说的HTML标签 (这个是必须的,还可以为函数组件/类组件)
    props: 是在这个标签上的所有的属性 比如上面的列子中含有的 className 和 name(这是必须的,没有参数则为Null)
    children: 这个参数是标签中是否含有文本或标签

    什么都没有:'' 就是空  
    仅含有文本:呈现方式为 '你好世界';  
    仅含有标签:呈现方式为{className:'border-red'}
    含有文本+标签(多个):呈现方式为['你好世界',{className:'border-red'},{···}]  
    
  4. React何时创建的虚拟DOM
    执行React.createElement(elementn,props,[…children])就会创建虚拟DOM对象了。我们来看看是什么样子的
    img4
    可以看出type属性就是我们的第一个参数
    props就是我们attr
    children就是我们嵌套的内容了

  5. React.render(JSX,document.getElementById(‘root’))
    这一切的努力都是为了回到原点。使用React.render()方法将虚拟DOM对象挂在节点上

  6. 什么是纯函数

    • 相同的输入 一定会返回相同的输出
    • 永远 不会修改传入的参数值
      \

五.JSX语法细节

  1. JSX如何解析html文本
    使用dangerouslySetInnerHTML 属性 但这是危险的

    1
    <span dangerouslySetInnerHTML={{__html:item}}></span>
  2. JSX中添加CSS属性class 要使用className 属性

    1
    <input type="text" className="input" />
  3. JSXlabel的坑 要使用htmlFor 关联

    1
    2
    <label htmlFor="inse">输入内容</label>
    <input id="inse" className="input"/>

六.知识点

  1. setState:React中修改数据不能直接修改 要使用setState方法 这个分两种方式
  • setState修改数据是异步的

    1. 第一种方法

      1
      2
      3
      this.setState = {
      xxx: 123
      }
      1. 第二种方法
        1
        2
        3
        4
        5
        6
        7
        8
        9
        this.setState((prevState)=>{
        return {
        status:true
        }
        },()=>{
        // 在这里可以获取到this.setState修改后的数据

        })
        this.setState(异步获取 可能获取不到修改后的值)
  1. 事件绑定

    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
    constructor() { 
    super()
    this.state = {
    inputValue:'XXX',
    }
    this.handleAdd = this.handleAdd.bind(this)
    }
    // 第一种
    handleAdd(){
    console.log(this)
    // 没有this.handleAdd = this.handleAdd.bind(this) 会获取不到this

    }
    // 第二种 推荐写法
    chnageInputValue = (e) => {
    console.log(e);
    this.setState({
    inputValue:e.target.value
    })
    }

    handleKeyUp = (e) => {
    if (e.keyCode === 13) {
    this.handleClickAdd()
    }
    }
    render() {
    return (
    <>
    <input type="text" onChange={this.chnageInputValue} value={this.state.inputValue} /> <button onClick={this.handleAdd}>{this.props.btnText}</button>
    // 添加参数
    <input type="text" onChange={()=>{this.chnageInputValueCanShu(传递的参数)}} value={this.state.inputValue} />
    // 绑定键盘事件
    <input type="text" onChange={this.chnageInputValue} onKeyUp={this.handleKeyUp}
    value={this.state.inputValue} />
    </>
    )
    }
  2. Ref
    引入 createRef

    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
    import React, { Component, createRef} from 'react'

    constructor() {
    super()
    this.state = {
    inputValue:'XXX',
    }
    }
    chnageInputValue = (e) => {
    console.log(e);
    this.setState({
    inputValue:e.target.value
    })
    }
    handleClickAdd = () => {
    this.props.addTodo(this.state.inputValue)
    this.setState({
    inputValue:''
    }, () => {
    this.inputRef.current.focus();
    })
    }

    inputRef = createRef() // 点击添加按钮 鼠标光标自动回到input上
    render() {
    return (
    <>
    <input type="text" onChange={this.chnageInputValue}
    value={this.state.inputValue} ref={this.inputRef}/>
    <button onClick={this.handleClickAdd}>{this.props.btnText}</button>
    </>
    )
    }
  3. 使用prop-types校验参数类型: 子组件中效验父组件传递的参数是否符合规范

    1. 安装 prop-types

      1
      npm install -S prop-types
    2. 如何使用 PropTypes

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      import PropTypes from 'prop-types'

      static propTypes = {
      todoList: PropTypes.arrayOf(PropTypes.object)// 验证参数是数组 里面的每一项是对象
      todoList:PropTypes.arrayOf(PropTypes.shape({
      id:PropTypes.number.isRequired,
      title:PropTypes.string.isRequired,
      isCompleted:PropTypes.bool.isRequired,
      })).isRequired// // 验证参数是数组 每一项是对象 对象的每一个参数类型
      }
      1
      2
      3
      4
      5
      6
      7
      //组件名.propTypes
      TodoItem.propTypes = {
      // 属性值
      item: PropTypes.oneOfType([PropTypes.string,PropTypes.number]),// 可以使字符串 也可以是number
      itemDelete: PropTypes.func,
      index:PropTypes.number
      }
    3. defaultProps的使用

      1
      2
      3
      4
      5
      6
      TodoItem.propTypes = {
      test:PropTypes.string.isRequired //isRequired:父组件必须传递给我
      }
      TodoItem.defaultProps = {
      test:'Hello World'
      }
  4. react 父组件传值给子组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 父组件
    <ul>
    {
    this.state.list.map((item, index) => {
    // 在子组件上定义自定义属性 并传递参数
    return <TodoItem item={item} index={index} key={index} />
    })
    }
    </ul>
    // 子组件 通过this.props.属性取值
    <li >
    <span dangerouslySetInnerHTML={{__html:this.props.item}}></span>
    <button onClick={() => { this.handleItemDelete(this.props.index) }}>删除</button>
    </li>
    // 子组件也可以使用对象结构的方式 获取
    render() {
    let { item } = this.props
    return (
    <li >
    <span dangerouslySetInnerHTML={{__html:item}}></span>
    </li>
    )
    }
  5. react 子组件如何修改父组件中state中的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 在父组件中的子组件上添加属性 传递方法 itemDelete方法
    <ul>
    {
    this.state.list.map((item, index) => {
    return <TodoItem itemDelete={this.handleItemDelete} item={item} index={index} key={index} />
    })
    }
    </ul>

    // 在子组件中使用this.props.itemDelete调用父组件的方法
    handleItemDelete = () => {
    this.props.itemDelete(this.props.index);
    }
  6. React中的生命周期函数:指某一时刻组件会自动调用执行函数

    1. componentWillMount:在组件即将挂载到页面的时刻自动调用
    2. componentDidMount: 在组件挂载到页面的时刻自动调用
    3. shouldComponentUpdate(nextProps,nextState):在组件更新之前或自动调用 需要返回boolean值 return true 更新组件 false 不更新组件

      1. nextProps:更新后(下一次)的props的值
      2. nextState:下一次state的值
        使用场景:在父组件中输入框中更新数据 会使子组件render自动更新 设置false 强制不更新 只在props的值变更影响子组件渲染的时候更新
        1
        2
        3
        4
        5
        6
        7
        8
        shouldComponentUpdate(nextProps, nextState) {
        // 下一次的item的值与当前的item的是否相等 不相等更新子组件 相等不更新
        if (nextProps.item !== this.props.item) {
        return true
        } else {
        return false
        }
        }
    4. componentWillUpdate:shouldComponentUpdate执行后执行 在render执行前执行

    5. componentDidUpdate:组件更新完成之后 执行
    6. componentWillReceiveProps:当一个组件从父组件接收参数 只要父组件render执行了 该函数就会执行
      若当前子组件第一次现在在父组件中 改钩子不会执行  
      若当前子组件已经在父组件中 该钩子会执行  
      
    7. componentWillUnmount:组件销毁前的钩子
  7. React中如何使用动画

    1. 使用css写动画

      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
      jsx
      handleToggle = () => {
      this.setState(() => ({
      show:!this.state.show
      }))
      }
      render() {
      return (
      <>
      <div className={this.state.show ? 'show' : 'hied'}>Hello Wrold</div>
      <button onClick={this.handleToggle}>toggle</button>
      </>
      )
      }
      css
      .show{
      animation: show-item 2s ease-in forwards;
      }
      .hied{
      animation: hide-item 2s ease-in forwards;
      }
      @keyframes show-item{
      0% { color: pink; opacity:0; }
      50% { color: blue; opacity: .5; }
      100% { color: red; opacity: 1; }
      }
      @keyframes hide-item{
      0% { color: pink; opacity: 1; }
      50% { color: blue; opacity: .5; }
      100% { color: red; opacity: 0; }
      }
    2. 使用react-transition-group 做动画

      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
      1. 安装 react-transition-group 
      2. npm install react-transition-group
      3. 使用
      import { CSSTransition,TransitionGroup } from "react-transition-group";// 引入组件
      // CSSTransition 只能对于单个元素 单个组件
      // TransitionGroup 多个组件 用TransitionGroup 包裹多个CSSTransition
      import './index.css'// 引入样式

      <TransitionGroup>
      this.state.list.map((item,index)=>{
      return (
      <CSSTransition
      key={index}
      // true 执行入场动画 false执行出场动画
      in={this.state.show}
      // 设置动画执行时间
      timeout={2000}
      // 动画库的名字 自定义
      classNames="fade"
      // 动画结束后是否显示Dom元素
      unmountOnExit
      // 页面加载出来 是否加载动画
      appear={true}
      // enter”或“ appear”类后立即触发的回调。 入场动画开始之前 执行结束
      onEnter={() => { console.log(1111); }}
      // enter-active”或“ appear-active”类后立即触发的回调。 /* 入场动画开始后 入场动画执行中 还没有结束 时调用
      onEntering={() => { console.log(22222); }}
      // 入场动画完全 结束后调用
      onEntered={() => { console.log(3333); }}
      // 出场动画开始前调用
      onExit={() => { console.log(4444); }}
      // 出场动画开始后 调用 动画还在执行中
      onExiting={() => { console.log(55555); }}
      // 出场动画执行完后 调用
      onExited={() => { console.log(666); }}
      >
      <div >Hello Wrold</div>
      </CSSTransition>
      )
      })
      </TransitionGroup>

      css

      /* 入场动画开始前 */
      /* fade-appear 页面加载就会执行 配合appear属性 */
      .fade-enter, .fade-appear{ opacity: 0; color: blue; }
      /* 入场动画开始后 入场动画执行中 还没有结束 */
      /* fade-appear-active 页面加载就会执行 配合appear属性 */
      .fade-enter-active, .fade-appear-active{
      opacity: 1; color: blue; transition:all 2s ease-in; }
      /* 入场动画结束后 */
      /* fade-appear-done 页面加载就会执行 配合appear属性 */
      .fade-enter-done, .fade-appear-done{ opacity: 1; color: blue; }


      /* 出场动画前 */
      .fade-exit{ opacity: 1; }
      /* 出场动画后 出场动画执行中 */
      .fade-exit-active{ opacity: 0; transition:all 2s ease-in; }
      /* 出场动画后 */
      .fade-exit-done{ opacity: 0; }

七. UI组件和容器组件&无状态组件

  1. 什么是UI:只负责页面渲染 不负责页面操作
  2. 什么书容器组件:只负责页面逻辑操作,不负责UI渲染
  3. 什么是无状态组件:当组件中只有render函数的时候 则可以更改此组件为无状态组件
    1. 好处:性能高

      八. 管理页面css文件

  4. 安装styled-components

    1
    npm i styled-components
  5. 使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     //在组件文件夹中新建style.js === style.css
    // 添加全局样式文件
    import { createGlobalStyle } from 'styled-components'
    export const GlobalStyle = createGlobalStyle`
    body{
    background-color:red;
    }
    `
    // 组件中
    import { GlobalStyle } from './style.js';
    ReactDOM.render(
    <>
    <GlobalStyle />
    <App > </App>
    </> , document.getElementById('root'));

九.添加PC端统一样式文件 reset.css

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
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body { line-height: 1; }
ol, ul { list-style: none; }
blockquote, q { quotes: none; }
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
};

注:

  1. 命令式开发:即jQuery直接操作DOM
  2. 声明式开发:react 根据数据显示页面
  3. react中的数据是单项数据流
  4. state,prpos 的值发生改变 render函数就会重新渲染一次

    神头鬼脸·魔法写法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <List
    style={{width:'300px'}}
    bordered
    dataSource={data}
    renderItem={(item,index) => (
    <List.Item onClick={(index) => { handleDeleteItem(index) }} >{item}</List.Item> // 获取不到index的值 因为click 的第一个参数就当前Dom 对象
    <List.Item onClick={()=>handleDeleteItem(index)} >{item}</List.Item> // 可以获取到
    ) }
    />