React基础v1

React,用于构建用户界面的 JavaScript 库,是Facebook公司的开源项目,用于开发单页面应用;

  • 声明式、模块化、组件化的开发模式,通过对虚拟DOM实现数据绑定,最大限度的减少DOM操作;
  • JSXReact的核心组成部分,它是一种HTMLJS的混合模式,使用XML标记的方式声明界面;
  • React库只做逻辑层,数据 --> VDOM
  • react-dom库做渲染层,去渲染真实的DOMVDOM --> DOM
  1. JSXJavaScript XML,一种类似XMLJS扩展语法;
    1. JSX = XML+JSReact通过 React.createElement() 将其转化成虚拟DOM元素;这也是为什么在使用JSX的地方都必须声明 import React from 'react'
    var ele = 

    Hello JSX

    ;
    1. 它不再是一个字符串,也不是 HTML/XML 的标签,它最终产生一个 JS 对象--VDOM
  2. 解析规则
    1. 遇到 < 开头的代码,以标签的语法解析;HTML同名的标签转为HTML同名元素,其它标签则当作React组件解析;
    2. 遇到 { 开头的代码,以JS语法解析:标签中的JS代码必须用 { } 包含。
  3. JS库
    1. react.jsReact的核心库;
    2. react-dom.js:提供操作DOMReact扩展库;
    3. babel.min.js:最初是个人开发的,用于把 ES6 转为 ES5,后来被Facebook所收购,增加了解析JSX语法代码并转为纯JS语法代码的功能。
  4. babel的作用:把 JSX 代码转译为纯JS代码,所以
    • 纯JS的方式
      
      
    • babel的方式
      
      
  5. 虚拟DOM最终都会被 React 转为真实DOM,这样才会渲染在页面上。

React脚手架

  1. 脚手架的方式搭建React开发环境
    npx create-react-app demo //安装脚手架 -> 创建项目-> 删除脚手架
    
    npm run eject  // 弹射出项目的配置文件,单向操作,不可逆
    
    但是,如果不想弹射webpack配置文件,又想实现webpack配置呢?试试 @craco/craco
    @craco/craco:https://www.jianshu.com/p/7de3d3b15ff3
  2. 创建项目的报错:npm ERR! Unexpected end of JSON input while parsing near...
    1. 由于网络原因,某些依赖包加载失败,设置 npm 镜像为 cnpm
      npm config set registry https://registry.npm.taobao.org
      
    2. 清除 npm 缓存,即删除 npm-cache 目录下的所有文件,重启create-react-app
      C:\Users\Administrator\AppData\Roaming\npm-cache
      
  3. 目录结构
    • 默认入口文件:src/index.js
      import React from 'react' //引入react的核心库
      import ReactDOM from 'react-dom' //引入react-dom.js,提供与DOM相关的功能
      import './index.css' //项目的公共CSS
      import App from './App' //引入App.js组件,也就是项目的根组件
      
    • registerServiceWorker.jsPWA支持,用于加快react的运行项目
    • ReactDOM.render(, document.getElementById('root')):把根组件App渲染到root节点上
    • src/index.test.js:用于测试
    • src/App.css:App组件中使用的css,import './App.css'
  4. 新建组件Home.js
    import React, { Component } from 'react'
    class Home extends Component {
        render() { return 
    Hello Home
    } } export default Home; // 暴露Home组件
    1. 挂载到根组件 App
      import Home from './components/Home'
      
      render() { return(
      ) }
    2. render() 返回的JSX语句称为模板,最外层必须由一个根节点包裹,对于多行的JSX节点,则必须使用 return ( ... )
      render()是唯一必要的方法,返回一个React元素。它不负责组件的实际渲染工作,只是返回一个UI描述。具体的渲染工作由React负责。
      注意:render()必须是一个纯函数,里面不能直接调用setState方法去修改state状态,不能执行任何有副作用的操作。
  5. React 组件也可以通过数组的形式返回一组元素节点:
    render() {
        return [  //不要忘记 key
            
  6. First item
  7. ,
  8. Second item
  9. , ]; }
  10. 除了返回数组,还可以使用 Fragment,达到聚合子元素的的目的,又不增加额外节点;
    render() {
        return (
            <>
                
                
            
        );
    }
    
    • <> 其实是 的语法糖
      render() {
          return (
              
                  
                  
              
          );
      }
      
    • <> 不能接受键值或属性,但 可以
      render() {
          return (
              
      {props.items.map(item => ( // 没有`key`,将会触发一个key警告
      {item.title}
      {item.description}
      ))}
      ); }
  11. 在模板中使用的HTML节点,必须严格遵循HTML的标准格式,如不能写作

类组件与函数式组件

  • 类组件继承自 React.Component,天生拥有状态state,能访问this
    import React from 'react'
    
    export default class Home extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                counter: 0
            }
        }
        render() {
            return 
    Hello Home
    } }
    VSCode插件:ES7 React/Redux/GraphQL/React-Nativercc 快速创建一个类组件模板。
  • 函数式组件在 React16.8 之前是没有状态的,只是一个默认接收props的函数,内部没有this。但是从 React16.8 开始引入了Hooks,使得函数式组件也能拥有状态state
    import React from 'react'
    export default function Home(props) {
        return 
    Hello Home
    }
    • 函数式组件是一种无状态的组件,为了创建纯展示组件,只负责根据传入的props来展示,不涉及state状态的操作;
    • 组件不会被实例化,整体渲染性能得到提升;
    • 内部不能访问this对象,也无法访问生命周期方法;
    • 无状态组件只能访问输入的props,相同的props得到同样的渲染结果,不会有副作用。

绑定数据

class Home extends Component {
    constructor(props) {  // 参数props用于父子组件的数据传递
        super(props);    // 必须先初始化父类Component的构造方法
        this.state = {  // state 是react用于管理数据的对象
            name:'Mack', age:20, color:'red',
            styles: { color:'red', fontSize:'20px' }
        }
    }
    render() { 
        return (

{this.state.name} --- {this.state.age}

) } }
  1. 模板中的 { ... } 是JS语句的标识,可以数值、JS变量、JS表达式等;
  2. 状态state
    1. 访问:this.state.name
    2. 更新:this.setState({ name: 'Jack' }),会触发React更新DOM
  3. setState(state, cb)
    • state 可以是一个对象,也可以是一个返回对象的函数;该对象会与状态对象合并,实现更新的目的。
      当它是一个函数时,接收两个参数,分别是上一次 stateprops
      this.setState((state, props) => ({
          name: state.name + props.age  // 更新操作
      }), () => {
          console.log(this.state.name)  //更新后的状态
      });
      
      另外,state、props 总能拿到最新的状态,但外部状态state 以及 DOM 还尚未更新(render()还没有执行)
      this.state = { counter: 0 }
      add() {
          // 第一次调用 setState 更新
          this.setState(state => ({ counter: state.counter + 1 }))
          console.log(this.state.counter)  // 0 ->外部状态state尚未更新
          // 第二次调用 setState 更新
          this.setState(state => {
              console.log(state.counter)  // 1  ->拿到了最新state的状态
              return {
                  counter: state.counter + 2   // 1 + 2 = 3
              }
          })
          console.log(this.state.counter)  // 0  ->外部状态state尚未更新
      }
      render() {
          // 此时的 state 已经更新
          console.log(this.state.counter)  // 3
          // ...
      }
      
    • cb 可选,回调函数。在 state 状态改变,且 render() 执行渲染之后 被回调。
  4. 关于 setState() 的同步异步问题
    概述:在React库控制时,setState 是异步的;否则,它就是同步的!
    • React事件监听回调和生命周期钩子中,setState 是异步的,多次调用会合并更新状态,最后执行一次 render() 渲染;
    • React控制的异步函数,如定时器,promise,原生DOM事件,setState 是同步的,state状态立即改变,并执行 render() 渲染,调用几次 setState 就会同步调用几次render()
  5. 节点属性的变更
    1. 使用class选择器时,建议使用 className
        {this.state.name}
    
    1. 对于上的 for 属性,建议使用 htmlFor
        
    
    1. 行内样式:style属性绑定的其实是一个对象
        
  6. 引入图片
    • ES6的 import 导入本地图片
       import logo from '../asseets/img/logo.png'
       
      
    • 使用ES5的 require 语法
       
      
    • 引入远程图片与HTML的标准格式一致,直接引入即可。
  7. 循环数组:必须使用 key 属性标识唯一性
    1. 模板中支持直接循环数组
      this.state = { lists:[

      11

      ,

      22

      ] } render() { return (
      {this.state.lists}
      ) }
    2. 拼接
    3. 标签数组
      this.state = { nums:[11, 22, 33] }
      render() {
          let result = this.state.nums.map((item, index) => { 
              return 
    4. {item}
    5. }) return
        {result}
      }
    6. 直接在模板中使用JS语句
      render() {
          return (
        { this.state.nums.map((item, index) => { return
      • {item}
      • }) }
      ) }
  8. React/Vue 都不建议用数组的索引作为 key
    1. diff算法是比较同级之间的不同,以key来进行关联,当数组的索引变化时,如删除第一条数据,那么后面的所有 index 都会改变,key自然也随之改变;所以,index 作为 key 值是不稳定的,这种不稳定性导致diff无法关联起上一次一样的数据,造成性能的浪费;
    2. Key 应该是稳定的,可预测的,且唯一的。
  9. 模板中的注释:{ /* 注释内容 */ }

绑定事件

  1. 事件的方法就是在组件内定义的方法
        onTest() { ... }
        render() { 
            return 
    }
    注:绑定事件时,不能写作 this.onTest(),这种方式表示直接调用方法。
  2. 事件方法中的 this 指向问题与解决方式
    函数中的 this 指向默认由调用者决定的,模板的 this 是未定义的,所以在绑定的事件方法中,其 this 也指向undefined,它也不能再访问当前组件内的属性和方法。
    • 通过函数的方法 bind() 修改 this 指向;
      // 方式一:在初始化时修改 this 指向
      // 初始化只有一次,推荐这种方式
      constructor() {
          this.onTest = this.onTest.bind(this)
      }
      // 方式二:在绑定事件方法时就修改 this 指向
      // render每次渲染执行都会因为 bind() 而返回一个新的方法,浪费性能
      render() {
          return 
      }
      
    • 把方法声明为箭头函数,JSX模板的上一级作用域正是 render(),其this指向组件对象;
      // 推荐方式
      onTest = () => {
          // ......
      }
      
    • 绑定事件时,使用箭头函数调用
      render() {
          // 这种方式也会在每次渲染时返回一个新的方法,浪费性能
          return 
      }
      
  3. 事件对象event:在绑定事件方法时,方法的最后一个形参默认为事件对象
    • 无实参时
      onTest(event) {
          let btn = event.target;  //获取当前点击的DOM节点
      }
      render() {
          return 
      }
      
    • 有实参时
      onMove(a, b, event) {
          let btn = event.target;
      }
      render() {
          return 
      }
      
  4. 双向数据绑定MVVM
    1. react的数据是单向绑定的,即model变化引起view变化,this.setState()更新model
    2. react没有提供双向数据绑定,只能通过表单节点的 onChange 事件,间接实现MVVM
      this.state = { uname: 'Mack' }
      
      inputChange = ev => {
          this.setState({ uname: ev.target.value }) // view变化触发model变化
      }
      
    3. 如果只是使用 value 绑定数据,而没有绑定 onChange 事件,会报错;
    4. 如果只希望绑定数据,而不绑定onChange事件,则使用defaultValue={this.state.uname}

受控组件与非受控组件

  1. 约束性组件和非约束性组件(受控组件与非受控组件):React中提出的概念
  2. 非约束性组件
    1. 通过 defaultValuedefaultChecked 来设置表单的默认值,value值是用户输入的内容;
      
       {this.setState({value: e.target.value})}} />
      
    2. 表单的值只会被渲染一次,在后续的渲染时并不起作用。
  3. 约束性组件(受控组件):
     this.setState({value: e.target.value})} />
    
    1. value属性绑定了一个变化的数据,它由onChange事件的方法负责管理;
    2. 此时的value不再是用户输入的内容,而是onChange事件触发后,由 this.setState() 引发的一次重新渲染;
    3. 不同的是,React会通过操作虚拟DOM,优化受控组件的整个渲染过程。
  4. 始终是一个不受控制的组件,因为它的值只能由用户设置!

你可能感兴趣的:(React基础v1)