Day7 第五届青训营打卡

47 阅读8分钟

这是我参与[第五届青训营]伴学笔记创作活动的第七天

  1. react- 用于构建用户界面的javaScript 库

发送请求获取数据 处理数据(过滤、整理) 操作dom 呈现页面 react 是一个将数据渲染微HTML视图的开源 JavaScript库

  1. facebook开发,且开源

  2. 原生JavaScript

     原生javaScript操作DOM繁琐、效率低(DOM-APL 操作 UI)。
    
     使用javascript直接操作DOM,浏览器会进行大量的重绘重排。
    
     原生JavaScript没有组件化编码方案,代码复用率低。
    
  3. react 特点

采用组件化模式、声明式编码,提高开发效率及组件复用率。 React Native 中可以使用 React语法进行移动端开发 使用虚拟DOM + 优秀的Diffing算法,尽量减少与真实DOM的交互

1、hello react react 核心库和react-dom的引入要放在前面

hello_react
<script type="text/babel"> /* 此处一定为babel*/
  // 1. 创建虚拟DOM
  const VDOM = <h1>Hello,React</h1> /* 不用写引号,因为不是字符串,是jsx*/
  // 2. 渲染虚拟DOM到页面
  ReactDOM.render(VDOM, document.getElementById('test'))
</script>
  1. 创建虚拟DOM JSX创建虚拟DOM更方便,相当于语法糖

// 1. 创建虚拟DOM const VDOM = (

Hello,React

) /* 不用写引号,因为不是字符串,是jsx*/ JS 创建虚拟DON

// 1. 创建虚拟DOM const VDOM = React.createElement('h1', {id: 'title'}, React.createElement('span',{}, 'hello, React')) 3. 虚拟DOM和真实DOM 虚拟DOM:

            1、本质上是一个Object对象,一般对象

            2、虚拟DOM比较 ”轻",真实DOM比较 “重”, 因为虚拟DOM是React内部在用,无需真实DOM这么多的属性

            3、虚拟DOM最终会被React转化成真实DOM,呈现在页面上。

4. jsx 语法规则 --XML早期用于存储和传输的数据

    -- JSON 也是用于存储和传输的数据,js内置对象

      1、定义虚拟DOM时,不要写引号

      2、标签中混入JS表达式时要用 {}

      3、样式的类名指定不用 class,要用className.

      4、内联样式,要用style = {{key: value}} 的形式去写

      5、只有一个跟标签

      6、标签必须闭合

      7、标签首字母

        (1)若小写字母开头,则将该标签转为 html同名元素,若html中无该标签,则报错

        (2)若大写字母开头,react就去喧嚷对应的组件,若组件没有定义,则报错
4_JSX语法规则 .title { background: orange; }
<script type="text/babel"> /* 此处一定为babel*/
  // 1. 创建虚拟DOM
  const VDOM = (
    <h1 id="title" className="title">
      <span style= {{color: 'white',fontSize: '20px'}}>Hello,React</span>
    </h1>
    ) /* 不用写引号,因为不是字符串,是jsx*/
  // 2. 渲染虚拟DOM到页面
  ReactDOM.render(VDOM, document.getElementById('test'))
</script>
  1. 函数式组件
06_函数式组件
<!-- 引入react核心库 -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
  //1、创建函数式组件
  function MyComponent() {
    console.log(this); // 此处的this 是undefined,因为babel编译之后开启了严格模式
    return <h2>函数定义的组件(适用于【简单组件】定义)</h2>
  }
  // 2、渲染组件到页面
  ReactDOM.render(<MyComponent/>, document.getElementById('test'))
</script>
   * 执行了ReactDOM.render(<MyComponent/>...之后,发生了什么)
   *  1、React解析组件标签,找到了MyComponent组件
   *  2、发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实的DOM,随后呈现在页面上。

注:严格模式下,禁止 this 指向 window

官方例子:

function Welcome(props) { return

Hello, {props.name}

; }

const root = ReactDOM.createRoot(document.getElementById('root')); const element = ; root.render(element) 这个例子中发生了什么:

我们调用 root.render() 函数,并传入 作为参数。 React 调用 Welcome 组件,并将 {name: 'Sara'} 作为 props 传入。 Welcome 组件将

Hello, Sara

元素作为返回值。 React DOM 将 DOM 高效地更新为

Hello, Sara

。 6. 类的基本知识 1. 类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写

    2. 如果A类继承了B类,且A类型写了构造器,那么A类构造器中的super是必须要调用的

    3. 类中所定义的方法,都是放在了类的原型对象上,供实列使用
07_类基本知识
    4、 类中可以直接写赋值语句, 如下的含义 会直接往Car实例对象添加一个属性,名为 a, 值为 1

class Car { // 类中可以直接写复制语句, // 会直接往Car实例对象添加一个属性,名为 a, 值为 1 a = 1 } const c1 = new Car() console.log(c1); 7. 类式组件

执行了ReactDOM.render(...之后,发生了什么) 1、React解析组件标签,找到了MyComponent组件 2、发现组件是使用类定义的,随后new 出来该类的实例,并通过该实例调用到原型上的render方法。 3、将render 返回的虚拟DOM转为真实DOM。随后呈现在页面上

  1. 组件中的state state 的值是对象(key-value的组合) 组件中render方法中的this为组件实例对象 组件自定义函数中的this 为 undefined解决办法: bind 方法 箭头函数 状态数据不能直接修改更新,必须要用内置APL--- setState
09_组件实例的三大核心属性_state
<!-- 引入react核心库 -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>

<script type="text/babel">
  // 1、创建类式组件
  class Weather extends React.Component {
    // 构造器 调用 1次
    constructor(props){
      super(props)
      // 初始化状态
      this.state = {
        isHot: true,
        wind: '微风'
      }
      // bind 可以做的事:1、生成新的函数,2、改了 函数里面的 this
      this.demo = this.changeWeather.bind(this)
    }
    // render调用 --- 1 + n 次, 1 是初始的那次,n 是状态更新的次数
    // render 调用,页面更新
    render() {
      return <h2 onClick={ this.demo }>今天天气很{this.state.isHot ? '炎热' : '凉爽'}, {this.state.wind}</h2>
    }
    changeWeather() {
      // changeWeather 放在了原型对象上,供实例使用
      // 由于changeWeather 是作为onClick的回调,所以不是通过实例调用的,是直接调用的,
      // 类型的方法默认开启了局部的严格模式,所以 changeWeather中的this 为 undefined
      const isHot = this.state.isHot

      // this.state.isHot = !isHot  // 状态(state)里面的值 不能直接更改,react不认可的

      // 要借助一个内置的API去更改
      // 注意:状态(state) 必须通过setState进行更新,且更新事一种合并,不是替换
      this.setState({isHot: !isHot})
    }
  }
  // 2、渲染组件
  ReactDOM.render(<Weather/>, document.getElementById('test')) 
</script>
    (1)  元素绑定事件

    注意写法:onClick = { this.demo },不是 onClick = { this.demo() },  是函数的回调,不是函数的执行。

render() { return <h2 onClick={ this.demo }>今天天气很{this.state.isHot ? '炎热' : '凉爽'}, {this.state.wind} } (2) this的指向问题

            bind 可以解决 1、生成新的 函数  2、更改函数中的 this

      (3) 状态(state) 必须通过setState进行更新,且更新事一种合并,不是替换

     (4)render 函数调用,页面更行; 状态更新多少次,render函数就会调用多少次

9. state 精简 1. 不再写构造器

    2.  自定义方法-----要用 赋值语句的形式 + 箭头函数

注: 箭头函数没有 this,它会直接找外层的 this

  1. props 在类组件中 直接 this.props就可以拿到 组件传进来的值对象了

ES6 中三点运算符,可以展开数组,但是不可以展开 Object

React 的组件的props传值,可以只用三点运算符 展开 Object,仅限于 组件的props的传值

props是只读,不可以修改

构造器是否接受 props,是否 传递给super,取决于:是否希望在构造器中通过this访问 props

// 1、创建类式组件 class Person extends React.Component { // 初始化状态 state = {} render() { const {name, sex, age} = this.props return (

  • name: {name}

  • sex: {sex}

  • age: {age}

) } }

const p = { name: 'tom', sex: 'man', age: 18} // 2、渲染组件 ReactDOM.render(<Person {...p}/>, document.getElementById('test'))

React内置了一些 方法 对 props传入的属性进行 检查,15.5版本之后React.PropTypes 已移入另一个包中了

propTypes 使用方式:

// react 15.6版本之后 Person.propTypes = { name: PropTypes.string.isRequired, // name 必填· 字符串 sex: PropTypes.string, // sex 字符串 age: PropTypes.number, // age 数字 speak: PropTypes.func, // speak 函数 } 设置默认值,defaultProps

// 指定默认标签属性值 Person.defaultProps = { sex: 'man', // sex 默认值为 man age: 18, } 12. props简写 把 props的限制都写在类里面,static 关键字定义

12_props的简写
<!-- 引入react核心库 -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<!-- 引入prop-types -->
<script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script>

<script type="text/babel">
  // 1、创建类式组件
  class Person extends React.Component {
    // 对标签属性进行类型、必要性的限制
    static propTypes = {
      name: PropTypes.string.isRequired,
      sex: PropTypes.string,
      age: PropTypes.number,
    }
    // 指定默认标签属性值
    static defaultProps = {
      sex: 'man', // sex 默认值为 man
      age: 18,
    }
    // 初始化状态
    state = {}
    render() {
      const {name, sex, age} = this.props
      return (
        <ul>
          <li>name: {name}</li>  
          <li>sex: {sex}</li>  
          <li>age: {age}</li>  
        </ul>
      )
    }
  }

  const p = { name: 'tom', sex: 'man', age: 20}
  // 2、渲染组件
  ReactDOM.render(<Person {...p}/>, document.getElementById('test')) 
</script>
  1. 函数组件使用 function Person(props) { const {name, sex, age} = props return (

    • name: {name}

    • sex: {sex}

    • age: {age}

    ) } Person.propTypes = { name: PropTypes.string.isRequired, sex: PropTypes.string, age: PropTypes.number, } // 指定默认标签属性值 Person.defaultProps = { sex: 'man', // sex 默认值为 man age: 18, } const p = { name: 'tom', sex: 'man', age: 20} // 2、渲染组件 ReactDOM.render(<Person {...p}/>, document.getElementById('test'))

  2. 字符串形式的 ref 官方不建议使用,以后会弃用

注意是 ref ,不是 refs

<script type="text/babel">
  // 1、创建组件
  class Demo extends React.Component{
    showMsg = () => {
      console.log(this.refs.input1.value);
    }
    showMsg2 = () => {
      console.log(this.refs.input2.value);
    }
    render() {
      return (
        <div>
          <input ref="input1" type="text" placeholder="请输入"/>&nbsp;&nbsp;
          <button onClick={this.showMsg}>点击</button>&nbsp;&nbsp;
          <input ref="input2" onBlur={ this.showMsg2} type="text" placeholder="请输入"/>
          </div>
      )
    }
  }
  ReactDOM.render(<Demo/>, document.getElementById('test'))

15. 回调形式的 ref    <input ref={c=> this.input2 = c} onBlur={ this.showMsg2} type="text" placeholder="请输入"/> ) } } ReactDOM.render(, document.getElementById('test'))

  1. React.createRef() 官方最推荐的写法,createRef是只能存一个值,后面的值会直接覆盖前面定义的值,键值对的形式存储

input1 = React.createRef()

获取值: this.input1.current.value

  1. 事件处理 通过onXxx属性指定事件处理函数(注意大小写) React使用的是自定义(合成)事件,而不是使用的原生的DOM事件 --- 为了更好的兼容性 React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)--为了高效 通过event.target得到发生事件的DOM元素对象--- 不要过渡使用re

  2. 非受控组件 非受控组件相对于 受控组件,属性没有在 state状态里面维护的都是 非受控组件,现造现取

  3. 受控组件 受控组件,数据都放在 state状态里面维护,相当于 vue的数据双向绑定

  4. 高阶函数 高阶函数: 如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数 若A函数,调用的返回值仍然是一个函数,那么A就可以称之为高阶函数 常见的高阶函数: Promise、setTimeout、 函数的柯里化: 通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。

疑问: this.setState({[data]: event.target.value}) 中的 [data]怎么就可以读变量了呢

在数组里面是 array[a] = 223 就可以赋值数据, 同理

  1. 生命周期(旧)

  2. 初始化阶段:由ReactDOM.render() 触发 -----初次渲染

    1. constructor()

    2. componentWillMount()

    3. render()

    4. componentDidMount() ------------ 常用,

       一般在这做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
      
  3. 更新阶段:由组件内部this.setState() 或父组件render触发

     1. shouldComponentUpdate()
    
     2. componentWillUpdate()
    
     3. render()     ------------------- 必须使用的
    
     4. componentDidUpdate()
    
  4. 卸载组件:ReactDOM.unmountComponentAtNode()触发

     1. componentWillUnmount()  ----------- 常用
    
             一般在这做一些收尾的事,例如:关闭定时器、取消订阅消息
    

componentWillReceiveProps 这个钩子是 父组件改变的时候,子组件会调这个勾子,

注意:初次渲染是不会调这个勾子的

  1. 生命周期(新) 新的生命周期 对于 旧的生命周期 ,废弃(即将废弃)了 三个 生命钩子: ;新提出了两个生命钩子:getDerivedStateFromProps、getSnapshotBeforeUpdate

  2. 初始化阶段: 由ReactDOM.render()触发 ---------------初次渲染

     1. constructor()
    
     2. getDerivedStateFromProps
    
     3. render()
    
     4. componentDidMount() ------------ 常用,
    
             一般在这做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
    
  3. 更新阶段: 由组件内部this.setState()或父组件重新render触发

     1. getDerivedStateFromProps
    
     2. shouldComponentUpdate()
    
     3. render()
    
     4. getSnapshotBeforeUpdate()
    
     5. componentDidUpdate()
    
  4. 卸载组件:由ReactDOM.unmountComponentAtNode()触发

     1. componentWilUnmount() ----------- 常用
    
             一般在这做一些收尾的事,例如:关闭定时器、取消订阅消息
    
     
    

重要的钩子

    1. render: 初始化渲染或更新渲染调用

    2. componentDidMount: 开启监听,发送ajax请求

    3. componentWilUnmount: 做一些收尾工作,如:清理定时器

即将废弃的钩子

    1. componentWillMount

    2. componentWillReceiveProps

    3. componentWillUpdate

23. DOM的 diffing算法 逐层对比,最小粒度是 标签

经典面试题

1). react/vue 中key有什么作用?(key的内部原理是什么?)

2). 为什么遍历列表是,key最好不要用index?

  1. 虚拟DOM中key的作用

     1) 简单的说:key是虚拟DOM对象的标识,在更新显示时key起到及其重要的作用
    
     2)详细说:当状态中的数据发生变化时,react会根据 【新数据】生成 【新的虚拟DOM】,随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
    
             a. 旧虚拟DOM中找到了与新虚拟DOM相同的key
    
               1. 若虚拟DOM中内容没有变,直接使用之前的真实DOM
    
               2. 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
    
             b. 旧虚拟DOM中未找到与新虚拟DOM相同的key
    
               根据数据创建新的真实的DOM,随后渲染到页面
    
  2. 用index作为key可能会引发的问题:

     1. 若对数据进行:逆序添加、逆序删除等破环顺序操作:会产生没有必要的真实DOM更新 =》界面效果没有问题,单效率低
    
     2. 如果结构中还包含输入类的DOM:会产生错误DOM更新 =》界面有问题
    
     3. 注意:如果不存在对数据的逆序添加、逆序删除等破环顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
    
  3. 开发中如何选择key?

     1. 最好使用每条数据的唯一标识作为key,比如:id、手机号、身份证号、学号等唯一值
    
     2. 如果确定只是简单的展示数据,用index也是可以的。
    
  4. 手脚架创建react项目 基于webpack 手脚架搭建

1、 react 提供用于创建react项目的脚手架:create-react-app

2、项目的整体技术架构:react+webpack+es6_eslint

3、使用脚手架开发的项目:模块化、组件化、工程化

创建项目并启动

1、全局安装:npm install -g create-react-app

2、切换目录,使用:create-react-app react_demo2

3、进入项目文件夹:cd react_demo2

4、启动项目:npm start

目录

  1. 功能界面的组件化编码流程 SPA (单页面应用)

  2. 拆分组件:拆分界面,抽取组件

  3. 实现静态组件:使用组件实现静态页面效果

  4. 实现动态组件:

     a. 动态显示初始化数据
    
             数据类型
    
             数据名称
    
             保存在哪个组件
    
     b. 交互(从绑定事件监听开始)
    
  • state 放在哪个组件:

      ----某个组件组件使用: 放在其自身的state中
    
      ---某些组件使用:放在他们共同的父组件state中,(状态提升)
    
  • 父子组件通信

      1. 父组件 给 子组件传递数据: 通过props传递
    
      2. 子组件 给父组件传递数据:通过 props传递,要求 父 提前给 子 传一个函数
    
  • 注意 defaultChecked 和 checked 的区别,类似 defaultValue 和 value 也是

  • 状态在哪里,操作状态的方法就在哪里

  1. react项目开发,vscode 好用的插件