天天看点

【react】react v16.13知识点总结(一)

一、react中的虚拟DOM

  • react中组件化是直接使用js实现,vue中通过

    .vue文件

    实现;
  • 在vue中模板

    template(模板结构)->ast(普通对象)->render函数(带变量的函数对象)->虚拟dom(赋值的函数对象)

  • 在react中直接

    js->虚拟dom

  • 使用虚拟dom,页面源码中是没有相应的dom标签,页面中元素都来自于引入的js文件;浏览器也不用解析相应html元素,直接解析js,形成真正的dom。
  • 与vue:react中挂载是嵌套在

    index.html的#app

    中;vue是直接替换;import引入时,都是将子组件文件以对象键值对形式导入,不同的是react中jsx语法会被解析;但是生命周期都还是没有开始;
  • vue是在父组件

    beforeMount周期后

    开始子组件的生命周期,通过解析子组件标签触发的组件生命周期;react也是,在解析到子组件标签后才开始子组件的生命周期。
  • ** react中引入时子组件render函数中jsx语法已经被解析,但createElement()中值也是变量存在,因为生命周期还没有开始;vue中引入子组件是只是组件文件的对象形式(不是组件对象),生命周期也没有开始,加载时子组件render函数要在挂载阶段与自身Vue实例结合才把render函数中变量赋值,然后执行render函数,返回h(具体值/),这个执行完就是虚拟dom;**
  • DOM
    • 本质

      :就是指用js对象表示的UI元素;是浏览器在内存中形成的很复杂的对象;
    • 特点/区别

      :只不过是浏览器解析生成的,浏览器在js中提供了固定的 DOM API来操作 DOM 对象;
    • 工作原理

      :当元素发生更新时,内存中存在新旧两个DOM树对象,浏览器通过新树替换旧树进行重新渲染页面;
  • 虚拟DOM
    • 本质

      :手动使用JS对象来模拟(相对真正dom对象做了简化)DOM树,也是对象,然后通过特定方法渲染成真正的DOM,在页面中出效果;
    • 作用

      :弥补浏览器不能新旧对比、实现指定更新的不足,为了实现DOM节点的高效更新;
    • 怎么模拟DOM树

      :通过JS创建对象,模拟一个DOM节点,节点中有children属性描述子节点,通过节点间嵌套,就模拟出了一个DOM树;
    • 特点/区别

      :不是由浏览器提供的,是开发人员自己通过JS模拟出来的,浏览器解析的是js代码,然后参照这个js代码创建真正dom树展示;
    • 使用

      :在页面更新前,使用JS语法模拟出新旧两个DOM树,(因为浏览器没有提供获取它内存中DOM树的接口),然后

      diff算法

      进行比较;让浏览器只更新有变化的元素。
  • 像vue、react框架中,页面都是通过虚拟DOM转为真正DOM来展示的;原理都类似;只是感觉react更直白一些,vue更贴心一些,写的时候还是可以写html、css;
  • react中创建页面元素:与内部创建虚拟DOM做法类似,都是直接通过

    js+接口

    实现,不再直接写html标签;与vue不同(通过html标签写页面);

二、react中的Diff算法

【react】react v16.13知识点总结(一)

前提

:有新旧两个虚拟DOM了,通过把旧虚拟dom一步步修改变成新虚拟dom一样的过程中,得到需要变化的元素;

意义

:diff 是通过JS层面的计算,对比新旧虚拟DOM,返回一个patch对象,即补丁对象,在通过特定的操作解析patch对象,完成页面的重新渲染;

1、

tree diff

:将新旧两个整体的虚拟DOM,逐层对比;其中包含组件对比、元素对比;

2、

componment diff

:(react中组件类似vue,就是主页面引入的部分)在对比每一层时,进行组件之间对比;

  • 组件对比规则:
    • 相同位置,两个组件类型相同,就暂且保存先认为不用更新;如果类型都不同,那直接移除旧组件,新建组件替换到被移除的位置;(这些后会被记录)

3、

element diff

:组件中元素之间的对比;

4、

key属性

:把页面中 DOM节点 与虚拟DOM中的对象,做一层关系。

三、react中的JSX语法

背景

:因为在react中书写元素都是在js中,react原生提供了

react、react-dom

来支持;

import React from 'react'
/react模块,用于专门来创建react组件、生命周期等;
import ReactDom from 'react-dom'
/react-dom模块,用于将组件渲染到页面上,内部封装了和DOM相关的包;

var myDiv = React.createElement('div',{title:'mytitle',id:'myDiv'},'文本内容');
ReactDom.render(myDiv,document.getElementById('app'))
/=ReactDom.render(虚拟dom,嵌套元素)
           

不足

:原生方法书写html元素,比较麻烦,开发人员更喜欢可以以手写

html代码

的方式定义页面,所以react中提供了

JSX语法

来支持在 js 中书写html标签;

1、jsx介绍

  • 定义

    :符合 XML 规范的 JS 语法,即手写的html标签要符合XML标准;
  • 原理

    :内部将js中的html代码用原生

    React.createElement方法

    转换成

    js对象

    ,再交给

    ReactDOM.render()

    渲染;
  • 怎么用

    :webpack一开始是不识别JSX语法的,需要安装配置

    @babel/preset-react

    语法转换工具(

    注:

    babel-loader 7.x对应:babel-preset-react);
  • 效果展示

var myDiv = <div id="myDiv">jsx识别的语法
  <p id="mySpan">js中写html没有语法提示</p>
  </div> 
// =后面就是jsx语法,
//会被解析成render函数的返回值:
//React.createElement('div',{title:'mytitle',id:'myDiv'},'文本内容')

ReactDom.render(myDiv,document.getElementById('app')) //传入render函数,内部执行形成得到createElement()函数,这个函数执行生成虚拟dom,并渲染出真正dom
           

2、jsx语法解析规则:

  • 基本规则

    :在

    js文件中

    遇到

    <>

    就按html代码解析,转成js对象;遇到

    {}

    或者正常的js代码就按js代码解析;
  • 变化

    :在HTML代码中有一些名称与js关键字冲突,所以在使用时要改名;如:JSX中要写

    <div className="myClass"></div>

    ,语法内会解析成正常

    <div class = "meClass">

  • JSX中html的注释

    :单行、多行注释
{/* 这是p标签 */}
  <p id="mySpan">js中写html没有语法提示</p>
  {
    // 多行注释
    // 多行注释
  }
           

四、react中的组件

组件文件

:组件文件名后缀:

.jsx/.js

,区别在于

.jsx文件

中有html语法提示;都需要然后在webpack中配置

babel语法转换器

1、

function方式

定义组件

  • 函数组件通过

    props

    形参,接收外界传进来的数据,将

    接收的数据以props对象的属性方式存储

    ,而且数据是只读的,不能内部修改;
  • 外界通过在函数标签中定义属性的方式传递数据;
  • 函数组件定义时必须大写开头,因为函数的组件使用方式就是以函数名为标签名,而

    jsx语法

    中解析标签是:小写标签按正常html标签解析,大写标签才会当做组件标签解析;
组件:Hello.js
import React from 'react'
function Person(props){
  return <div>
    <h1>哈哈---{props.name+"--"+props.age}</h1>
  </div>
}
export default Person
---
入口文件:main.jsx
import Hello from './componments/Hello'  /=>类似vue,引入Hello组件直接就是render函数,
/=>这是由文件内部导出时解析结果决定的
var one = {
  name:'zs',
  age:12
}
ReactDom.render(<Person one={one}></Person>
,document.getElementById('app'))
//或者可以展开方式:{props.name+"---"+props.age}
ReactDom.render(<Person {...one}></Person>
,document.getElementById('app'))
           

2、

class方式

定义组件

  • 类的实例是继承构造函数中的属性和方法;
  • 类中构造函数外定义的普通方法挂载到原型对象上,类的实例可以通过原型链使用;
  • 类中构造函数外定义的属性(只能定义静态)、静态方法是挂载到了构造器上,类的实例访问不到;
  • 补充

    :对比class与function构造函数
function Person(name,age){
  this.name = name;
  this.age = age;
}
Person.prototype.say = function(){
  console.log('函数原型上的方法');
}
Person.info = '构造函数自身的属性';
Person.addInfo =function(){
  console.log('构造函数自身的方法');
};
var p1 = new Person('zs',21)
console.log(p1);
----
class Per{
  /类中只能定义方法(实例方法、静态方法)和静态属性
  /类中方法定义到构造函数原型上,只能调用原型或通过实例使用
  /类中静态属性、静态方法定义在构造函数自身,只能自身或子类继承后调用
  constructor(name,age){//类中的构造函数(方法),new实例自动执行
    this.name = name;
    this.age = age;
  }
  say(){//方法
    console.log('class中构造函数原型上的方法');
  }
  static info = '类中的静态属性';//静态属性
  static add = function(){
    console.log('静态方法也定义构造函数自身');
  }
}
           
  • 补充

    :在class定义组件中主要用到

    类的封装、继承特性

class Per{
  constructor(name,age){
    this.name = name;
    this.age = age;
  }
  say(){
    console.log('定义在Per构造函数原型上的方法');
  }
  static info = 'zs';/构造函数中的属性,只能通过构造函数调用
}
class Chinese extends Per{
  constructor(name,age,color){
    super(name,age);/父类的传参必须放在前面
    this.color = color;
  }
}
var p2 = new Chinese('yellow','zs',22)
console.log(Chinese.info);/子构造函数可以调用父构造函数中静态属性/方法,但是实例不行
           
  • 需要继承

    React.component

  • 类中需要定义

    render方法返回null或jsx语法(让内部解析成createElement()函数)

  • 与函数组件类似,最终都是用名字做标签名,内部最终返回的都是:h();
  • ReactDom.render()

    ,参数1:使用的就是

    React.createElement()

main.js:
class Hello2 extends React.Component{ /=>继承react组件功能
  render(){
    return <div>这是class类创建的组件{this.props.aa}</div>
  } /=>定义render函数,内部返回值会被JSX解析,所以最终return的结果就是:createElement()函数;
}
ReactDom.render(<div>
  <Hello2 aa='asdClass'></Hello2> ://=这就相当于new了一个Hello2类的实例并执行了render函数,render是实例原型链上的方法
</div>,document.getElementById('app'))
           

3、对比两种组件定义方式

  • 区别一:参数props的使用
    • 在function组件中,通过标签接收参数,需要传入

      props

      ,通过

      {props.[属性名]}

      的方式使用;
    • 在class组件中,同样标签接收参数,不需要传参数(也没有地方接收),直接通过

      {this.props.[属性名]}

      使用;
  • 区别二:两者都不能对传入参数做修改;
  • 区别三:函数组件是无状态的组件;class类定义组件是有状态的;
    • 有状态:可以有自己的类似vue中data的属性,即

      this.state={}

    • 函数组件使用时就是执行构造函数返回 h()虚拟dom
    • class组件使用时也执行了new,同时也执行了render函数,状态就可以在new时创建,在render函数执行时改变;
  • 有无状态区别:
    • 有状态组件:有私有数据(state属性),有生命周期函数;适用于需要修改数据的组件;
    • 无状态组件:没有私有数据,无state属性,无生命周期函数;适用于数据不改变的组件,

      如子组件

4、组件中state/data与props区别

  • 前者是组件私有的数据,通过ajax请求来的;
  • 后者是外界传过来的数据;
  • 前者的数据是可读可写的;
  • 后者的数据是只读的;

五、react中的class有状态组件应用

1、路径问题

path.join(__dirname,'./aa'); /='aa' '/aa' './aa' 效果一样:e:\QD\react\aa
path.join(__dirname,'../aa'); /= 向上一级:e:\QD\aa
不识别'/'根目录

path.resolve(__dirname,'./aa'); /='aa' './aa' 效果一样:e:\QD\react\aa
path.resolve(__dirname,'../aa'); /= 向上一级:e:\QD\aa
path.resolve(__dirname,'/aa'); /= e:\aa
都是转为绝对路径;resolve识别/,为根目录
           

下一个》》2、

继续阅读