react基础01-jsx、渲染虚拟DOM、函数组件和类组件、类组件的三个核心(state、props、ref)

react介绍:

  用于动态构建用户界面的JavaScript库,由facebook开源

react特点:

  1、声明式

    命名式:获取dom,设置属性,等操作,每一步都要亲力亲为

    声明式:告诉react想到达到的效果,react去处理

    * 大部分ES5的方法都是声明式,例如forEach、map等,只需要会用,不用管内部怎么实现的

  2、组件:灵活、复用

  3、虚拟DOM:提高性能,高效的diff算法

  4、react native编写原生应用

  5、单向数据流

    vue:Object.defineProperty() 双向响应式数据流

    react:redux(观察者模式) 单向响应数据流

react高效的原因:

  使用虚拟dom,不总是直接操作页面真实dom

 

react基本使用:

相关js库:

  1、reactjs:react核心库

  2、react-dom.js:提供操作dom的react扩展库

  3、babel.min.js:解析jsx语法转为js的库

 

 

创建虚拟dom的两种方式

1、jsx(js的语法糖,需要通过babel转为js供浏览器识别)

  <body>
    <div id="test"></div>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.min.js"></script>
    <script type="text/babel">
      /* text/babel 表示这里要写jsx,并且用babel转成js */
      const vDOM = (
        <h1
          id="title"
          onClick={() => {
            console.log(13)
          }}
        >
          <span>helloreact</span>
        </h1>
      )
      ReactDOM.render(vDOM, document.querySelector('#test'))
    </script>
  </body>

2、js

  <body>
    <div id="test"></div>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script type="text/javascript">
      const vDOM = React.createElement(
        'h1', // 标签名
        { id: 'title' }, // 标签属性
        React.createElement('span', {}, 'helloreact') // 内容
      )
      ReactDOM.render(vDOM, document.querySelector('#test'))
    </script>
  </body>

效果都是在页面中插入dom

  

它俩的区别:

  1、虚拟dom本质是Object类型的对象(真实dom也是)

  2、虚拟dom比较“轻”,真实dom比较“重”,虚拟dom是react内部使用,不需要那么多的属性

  3、虚拟dom最终会被react转化为真实dom

 

jsx

1、全称:JavaScript XML

2、react定义的一种类似于XML的js扩展语法:js+XML,本质是React.createElement(component, props, ...children)的语法糖

3、作用:用来简化创建虚拟DOM

  写法:const ele= <h1>hello jsx</h1>

  注意:它不是字符串,也不是html/xml标签。它最终产生的是一个js对象

4、标签名任意:html标签或其它标签

5、标签属性任意:html标签属性或其它

6、基本语法规则

  ①定义虚拟dom时,不要写引号

  ②标签中混入js表达式时要用 {}

    表达式和语句的区别:

      表达式会产生一个值,可以放在任何一个需要值的地方。以下这些都是表达式:

        a

        a+b

        arr.map()

        function test() {}

      下面这些都是js语句:

        if() {}

        for() {}

        switch() { case: xxx }

  ③样式的类名指定不要用class,要用className

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

  ⑤只能有一个根标签

  ⑥标签必须闭合

  ⑦标签首字母

    若小写字母开头,则将该标签转为html中同名元素,若html中没有与该标签对应的同名元素,则报警告

    若大写字母开头,则渲染对应的组件,如果组件没有定义,则报错

      const vDOM = (
        <>
          <h1
            id="title"
            className="orange"
            style={{ color: '#fff', fontSize: '22px' }}
          >
            <span>helloreact</span>
          </h1>
          <h1 className="orange">
            <span>helloreact</span>
          </h1>
          <Add />
        </>
      )

7、babel.js的作用

  ①浏览器不能直接解析jsx代码,需要babel转换为js代码才能运行

  ②只要用了jsx,都要加上 type='text/babel' ,声明需要babel处理

 

渲染虚拟dom(元素)

1、语法:ReactDOM.render(virtualDOM, containerDOM)

2、作用:将虚拟dom元素渲染到页面中的真实容器dom中显示

3、参数说明:

  virtualDOM:js或jsx创建的虚拟dom对象

  containerDOM:用来包含虚拟dom元素的真实dom元素对象,一般是一个div

 

函数式组件

      function Demo() {
        console.log(this) // 此处的this是undefined,因为babel编译后开启了严格模式
        return <h2>h2</h2>
      }
      ReactDOM.render(<Demo />, document.querySelector('#test'))
      /*
        render函数在干嘛:
          1、react解析组件标签,找到了Demo组件
          2、发现组件是使用函数定义的,于是调用该函数,将返回的虚拟dom转为真实dom,插入到#test中
      */

  

  tip:严格模式下,函数中的this会指向undefined

      'use strict'
      demo()
      function demo() {
        console.log(this) // 函数体处于严格模式下,this会被绑定到undefined
      }

  注意:函数体处于严格模式下,函数内的this才会指向undefined;而函数的调用处于严格模式下,函数内的this还是指向window

      function demo() {
        console.log(this) // window
      }
      'use strict'
      demo() // 函数的调用处于严格模式下,函数内的this还是指向window

 

类组件

      class Demo extends React.Component {
        render() {
          console.log(this) // 指向当前类(Demo组件)的实例对象 Demo {...}
          return <h1>title-类组件</h1>
        }
      }
      ReactDOM.render(<Demo />, document.querySelector('#test'))
      /*
        render函数在干嘛:
          1、react解析组件标签,找到了Demo组件
          2、发现组件是使用类定义的,于是new出来该类的实例,并调用该实例调用到原型上的render方法
          3、将render返回的虚拟dom转为真实dom,插入到#test中
      */

 

类组件三大核心属性1:state

  小案例:声明与更改state

      class Weather extends React.Component {
        // constructor调用一次,有几个Weather实例就调用几次
        constructor(props) {
          super(props)
          this.state = { isHot: true, wind: '风很大' }
        }
        // render调用1+n次,1是初始化时,后期每次点击都会触发render以更新dom
        render() {
          const { isHot, wind } = this.state
          return (
            <h1
              // onClick={this.change} // 通过this查到到原型链上的change方法,点击时从堆中直接调用change,不是通过实例去调用的,所以这里this指向undefined(严格模式指向undefined,非严格模式下函数中this指向window)
              onClick={this.change.bind(this)} // 通过bind将change中的this由undefined改成this
            >
              今天天气很{isHot ? '炎热' : '凉爽'},{wind}
            </h1>
          )
        }
        // change调用n次,点击几次就调用几次
        change() {
          const isHot = !this.state.isHot
          // this.state.isHot = isHot // state不可直接更改
          this.setState({ isHot }) // 只会更改isHot,wind依然保留
        }
      }
      ReactDOM.render(<Weather />, document.querySelector('#test'))

  state的简写方式:

      class Weather extends React.Component {
        state = { isHot: true, wind: '风很大' } // 初始化状态,类中可以通过赋值语句直接将state设置给实例
        render() {
          const { isHot, wind } = this.state
          return (
            <h1 onClick={this.change}>
              今天天气很{isHot ? '炎热' : '凉爽'},{wind}
            </h1>
          )
        }
        // 自定义方法,直接通过赋值语句给实例身上添加change方法
        change = () => {
          const isHot = !this.state.isHot
          this.setState({ isHot })
        }
      }
      ReactDOM.render(<Weather />, document.querySelector('#test'))

  总结:

    1、组件中render中的this为实例对象

    2、组件自定义方法(一般都是作为事件的回调去使用)中this为undefined,如何解决:

      第一种:通过bind改变this指向

      第二种:箭头函数

      **  类中方法的this指向问题

      class Person {
        constructor(name, age) {
          this.name = name
          this.age = age
        }
        speak() {
          console.log(this)
        }
      }
      const p = new Person('小明', 20)
      p.speak() // 实例调用,this指向实例对象
      const aa = p.speak
      aa() // 直接调用,this指向undefined,类中的方法默认开启了局部严格模式

    3、state中的数据不能直接修改,如this.state.isHot=isHot,应该使用setState

 

类组件三大核心属性2:props

    <div id="test"></div>
    <div id="test1"></div>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.min.js"></script>
    <script src="./js/prop-types.js"></script>
    <script type="text/babel">
      class Person extends React.Component {
        render() {
          const { name, sex, age, speak } = this.props
          console.log(this)
          speak && speak()
          return (
            <ul>
              <li>姓名:{name}</li>
              <li>性别:{sex}</li>
              <li>年龄:{age}</li>
            </ul>
          )
        }
      }
      // 对标签属性进行类型、必要性的限制  15.5之前写法:React.PropTypes.string.isRequired
      Person.propTypes = {
        name: PropTypes.string.isRequired,
        sex: PropTypes.string,
        age: PropTypes.number,
        speak: PropTypes.func
      }
      // 标签属性设置默认值
      Person.defaultProps = {
        sex: '保密',
        age: 16
      }
      const p = { name: '小明', sex: '', age: 18 }
      ReactDOM.render(<Person {...p} />, document.querySelector('#test')) // {...p}这里的{}是表明这里面的 ...p 是js,但js中不允许通过...来展开对象;引入的react+babel允许使用展开运算符展开对象,仅适用于标签属性的传递

      ReactDOM.render(
        <Person name="小红" sex="" age={12} speak={speak} />,
        document.querySelector('#test1')
      )

      function speak() {
        console.log('说话')
      }
    </script>

  可以将属性限制写在类中:

        static propTypes = {
          name: PropTypes.string.isRequired,
          sex: PropTypes.string,
          age: PropTypes.number,
          speak: PropTypes.func
        }
        static defaultProps = {
          sex: '保密',
          age: 16
        }

 

类组件中的构造器与props:

  

  构造器函数的这两个作用,第一个,可以在类中直接 state={...} ,这是给实例初始化状态;第二个,为事件处理函数绑定实例,

    可以直接写onClick={this.changeWeather.bind(this)}或箭头函数解决

  如果写了构造器,一定要在第一行写上super()

  构造器的super传不传props?

        constructor(props){
          super(props) // 如果希望在构造器中通过this访问props,这里就传props进去
          console.log(this.props)
        }

  由此可见,类中的constructor能不写就不写吧,没啥用

 

函数组件使用props(类组件的三大属性state、props、refs,函数组件只能使用props。后期的hooks都会支持)

      function Person(props) {
        const { name, sex, age, speak } = props
        speak()
        return (
          <ul>
            <li>姓名:{name}</li>
            <li>性别:{sex}</li>
            <li>年龄:{age}</li>
          </ul>
        )
      }
      Person.propTypes = {
        name: PropTypes.string.isRequired,
        sex: PropTypes.string,
        age: PropTypes.number,
        speak: PropTypes.func
      }
      Person.defaultProps = {
        sex: '保密',
        age: 16
      }
      const p = { name: '小明', speak }
      ReactDOM.render(<Person {...p} />, document.querySelector('#test'))
      function speak() {
        console.log('说话')
      }

 

类组件三大核心属性3:refs与事件处理

  组件内的标签可以通过ref来标记自己

      class Demo extends React.Component {
        state = { isHot: true }
        render() {
          const {
            state: { isHot },
            showInput1,
            saveInput2
          } = this
          return (
            <div>
              <h1 onClick={() => this.setState({ isHot: !isHot })}>
                今天天气很{isHot ? '炎热' : '凉爽'}
              </h1>
              {/* ref第一种方式:字符串形式,存在效率问题,未来可能会被移除 */}
              <input ref="input1" type="text" />
              <button onClick={showInput1}>点击打印</button>
              {/* ref第二种形式:回调形式,推荐将ref的回调函数定义成class的绑定函数 */}
              <input
                // ref={(ele) => {
                //   this.input2 = ele
                //   console.log(ele) // 以内联函数的形式定义时,在更新的过程中会执行2次,第一次传入null,第二次传入dom。因为每次渲染时会创建一个新的函数实例,react清空旧的设置新的
                // }}
                ref={saveInput2} // 回调形式推荐写法
                onBlur={() => {
                  const { input2 } = this
                  console.log(input2.value)
                }}
                type="text"
              />
            </div>
          )
        }
        showInput1 = () => {
          const { input1 } = this.refs
          console.log(input1.value)
        }
        saveInput2 = (ele) => {
          this.input2 = ele
          console.log(ele) // 当状态改变时,不会触发到这里,因为已经放在实例自身了
        }
      }
      ReactDOM.render(<Demo />, document.querySelector('#test'))

  react推荐使用第三种方式:通过createRef创建ref容器

      class Demo extends React.Component {
        input1Ref = React.createRef()
        input2Ref = React.createRef()
        render() {
          const { input1Ref, input2Ref } = this
          return (
            <div>
              <input ref={input1Ref} type="text" />
              <button onClick={() => console.log(input1Ref.current.value)}>
                点击打印
              </button>
              <input
                ref={input2Ref}
                onBlur={() => console.log(input2Ref.current.value)}
                type="text"
              />
            </div>
          )
        }
      }
      ReactDOM.render(<Demo />, document.querySelector('#test'))

  勿过度使用Refs:当事件的元素正好是需要操作的元素,这里的ref就可以不写了,回调中的参数是事件源,e.target可以获取发生事件的dom对象

              <input onBlur={(e) => console.log(e.target.value)} type="text" />

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

下载插件和启动:

  下载插件:

npm i @babel/preset-react webpack-dev-server -D

npm i react react-dom
react是react的核心语法,react-dom是用来解析jsx的语法

  启动:在原来的webpack搭的架子里执行npm run dev,localhost:9001打开页面

 

ReactDOM的参数:

ReactDOM.render(<App />, document.getElementById('root'), () => {
  console.log('启动react')
})

  参数一:需要渲染的节点或组件

  参数二:将渲染好的DOM节点放到哪个根节点上

  参数三:成功的回调

 

jsx语法需要注意的地方:

  class必须要写成className,因为class是关键字

  <label htmlFor='username'></label>    label中的for要写成htmlFor,for是循环的关键字

 

react中组件的创建:

  1、组件的名称必须要大写(App),为了区分组件和标签

  2、通过ES6的继承实现组件的创建,继承与React.Component

  3、组件内部必须要有一个render函数,render函数中必须返回一个jsx语法(jsx中只能有一个根节点,可以写成<></>或<Fragment></Fragment>都不会渲染成dom)

  4、组件中constructor是选填的,constructor用来做组件的初始化;存放组件需要的一些状态,如果需要存放必须要写在constructor中的this.state中;如果写了constructor必须要写super(),不写的话this指向不正确

import React, { Fragment } from 'react' // Fragment:react内置组件,用来做容器盒子,不会被渲染到页面上
class App extends React.Component {
  // constructor是用来做组件的初始化
  constructor() {
    super() // constructor中必须要写super(),不写的话this指向会发生错误
    this.state = {
      name: '小华'
    }
  }
  render() {
    let { name } = this.state
    return (
      <div className="app">
        <h1>App</h1>
      </div>
    )
  }
}

export default App

 

react中的事件:

  语法:给元素添加 on事件名 (事件首字母必须要大写,原生中可以小写也可以大写)= {事件函数}

  第一种:(需要传参)

        <h2 onClick={this.handleClick.bind(this,'wxm')}></h2>

  第二种:(不需要传参)

        constructor(){
          super()
          this.handleClick=this.handleClick.bind(this) // 初始化
        }
        <h2 onClick={this.handleClick}></h2>

  第三种:(少见,ui层和逻辑层未分离)

        <h2 onClick={()=>{console.log('xxxxxxxxxx')}}></h2>

  注意事项:

    1、react中的事件函数不要加(),加了就是自动执行的意思

    2、react中的事件函数默认情况下this的指向是undefined,如果需要将this指向为当前组件需要通过bind绑定

    3、因为render函数会多次执行,因此bind绑定的时候可以放在constructor中。当需要传参时在render函数内部的事件中bind,不需要传参可以在constructor中进行bind(this.handleClick=this.handleClick.bind(this))

 

this.setState():

  语法:

    第一种:

          this.setState({
            key:value
          },()=>{})

    第二种:

          this.setState(()=>({
            key:value
          }),()=>{})

  

  1、在react中,如果需要修改state中的值,必须要通过this.setState()修改

  2、this.setState()是异步的

  3、this.setState()中的第二个参数是一个回调函数。作用:①验证数据是否修改成功②可以获取数据更新后最新的dom结构

  4、只要this.setState()执行了那么render函数就会执行

 

组件传值:

  父传子:

    传递:给子组件标签上绑定一个自定义属性,值为需要传递的数据

        <One username={this.state.name}></One>

    接收:在子组件render函数中通过props接收

        let { username } = this.props

  子传父:

    传递:在子组件中通过 this.props.事件函数 来进行传值

        <button onClick={this.handleAdd.bind(this)}>点击发送给父组件</button>

handleAdd() { this.props.toApp('我是从two组件传来的') }

    接收:在子组件标签上绑定一个自定义属性,值为需要接收参数的函数

        <Two toApp={this.handle2.bind(this)}></Two>


        handle2(value) {
          this.setState({
            towValue: value
          })
        }    

 

posted @ 2021-01-12 15:52  吴小明-  阅读(168)  评论(0编辑  收藏  举报