• React 学习笔记(1) 基础语法和生命周期


    参看:视频地址

    简单搭建一个react-cli: 

    2. React.createElement()

    将object转化为 React语法

    import React from 'react'
    import ReactDOM from 'react-dom'
    
    /**
     * React.createElement()
     * 参数1: 标签名
     * 参数2: 标签属性
     * 参数3: 标签内容
     * 参数4: 其他节点
     */
    // 需要 babel loader 解析jsx语法
    const myH1 = <div title='h1'>哈哈哈</div>
    
    const myDiv = React.createElement('div', { title: 'this is a div', id: 'mydiv' }, '这是一个div', myH1)
    
    // 将 myH1 放在 myDiv中,渲染到 #app 标签中
    ReactDOM.render(myDiv, document.getElementById('app'))
    React.createElement()

    3. jsx 语法

    3.1 jsx语法的配置

    jsx语法不能直接使用,需要@babel/preset-react翻译。

    // package.json
    "devDependencies": {
        "@babel/core": "^7.6.2",
        "@babel/plugin-proposal-class-properties": "^7.5.5",
        "@babel/plugin-transform-runtime": "^7.6.2",
        "@babel/preset-env": "^7.6.2",
        "@babel/preset-react": "^7.0.0",
        "@babel/runtime": "^7.6.2",
        "babel-loader": "^8.0.6",
        "html-webpack-plugin": "^3.2.0",
        "webpack": "^4.41.2",
        "webpack-cli": "^3.3.9",
        "webpack-dev-server": "^3.8.2"
    },
    "dependencies": {
        "react": "^16.10.2",
        "react-dom": "^16.10.2"
    }
    // .babelrc
    {
      "presets": ["@babel/preset-env", "@babel/preset-react"],
      "plugins": ["@babel/transform-runtime", "@babel/plugin-proposal-class-properties"]
    }
    // webpack.config.js
    const path = require('path')
    const HtmlWebPackPlugin = require('html-webpack-plugin')
    
    const htmlPlugin = new HtmlWebPackPlugin({
      template: path.join(__dirname, './src/index.html'),
      filename: 'index.html'
    })
    
    module.exports = {
      mode: 'development', // development    production
      plugins: [
        htmlPlugin
      ],
      module: {
        rules: [
          // 解析 jsx 语法
          {
            test: /(.jsx|.js)$/,
            use: {
              loader: "babel-loader"
            },
            exclude: /node_modules/
          }
        ]
      },
      resolve: {
        extensions: ['.js', '.jsx', '.json']
      }
    }

    3.2 语法

    • 原生标签比如p、li、div如果要使用自定义属性,必须用data-前缀

    • 样式使用双大括号

    import React, { Component } from "react"
    
    export default class User extends Component {
      render() {
        const num = 100 // 数字
        const str = "react 学习" // 字符串
        const bool = true // 布尔值
        const unde = undefined // undefined
        const nu = null // null
        const h4dom = <h4 style={{"width":"100px","height":20 + 30 + "px","backgroundColor" : "red"}}>这是一个h4标签</h4> // jsx 语法
        // 遍历数组
        const arrDom = [
          <h5 key="1">这是一个h5标签1</h5>,
          <h5 key="2">这是一个h5标签2</h5>,
          <h5 key="3">这是一个h5标签3</h5>
        ]
        // 字符串数组
        const arr = ["白板", "幺鸡", "二条", "三饼"]
        return (
          <div>
            {num + 1} - {str} - {bool.toString()} - {unde} - {nu}
            <h3 title={str}>这是一个h3标签</h3>
            {/* 数组会自动展开 */}
            {h4dom}
            {arrDom}
            <hr />
            {arr}
          </div>
        )
      }
    }

    4. 创建组件

    • 组件名必须大写

    • 组件方法必须return

    4.1 构造函数创建组件--无状态组件

    import React, { Component } from "react"
    
    // 将 person 传入 Stu 组件中 --- 子组件
    // 使用 props 接收传进来的参数; props 只读,不可更改
    function Stu(props) {
      return (
        <h1 style={{ color: "red", fontSize: 20 }}>
          {/* 接受父组件传递过来的值 */}
          Holle World: {props.name} - {props.age} - {props.gender}
        </h1>
      )
    }
    
    // 父组件 export
    default class User extends Component { render() { const person = { name: "Danny", age: 23, gender: "boy" } return ( <div> {/* 对象的解构赋值来传递数据 */} <Stu {...person}></Stu> </div> ) } }

    4.2 class关键字创建组件---有状态组件

    import React, { Component } from "react"
    
    // 将 person 传入 Stu 组件中 --- 子组件
    // 使用 props 接收传进来的参数; props 只读,不可更改
    class Stu extends React.Component{
      // render函数 是渲染当前组件对应的虚拟dom元素, props 接收 父组件 传递过来的值
      render(props){
        // 通过 this.props.*** 就可以接收数据
        //返回一个jsx语法
        return <h1 style={{color: '#1da57a'}}>Holle World { this.props.name } - { this.props.age} - { this.props.gender }</h1>
      }
    }
    
    export default class User extends Component {
      render() {
        const person = {
          name: "Danny",
          age: 23,
          gender: "boy"
        }
        return (
          <div>
            {/* 对象的解构赋值来传递数据 */}
            <Stu {...person}></Stu>
          </div>
        )
      }
    }

    用构造函数创建出来的组件,和用class创建出来的组件,

    这两种不同的组件之间的本质区别就是:有无state属性 和 生命周期函数!

    有状态组件和无状态组件之间的本质区别就是:有无state属性!

    5. react的样式

    5.1 渲染评论列表样式

    • 行内样式

    • css样式写成js对象的形式

    import React, { Component } from "react"
    
    // 这是评论列表组件
    export default class CommentList extends Component {
      constructor(params) {
        super()
        this.state = {
          CommentList: [
              { id: 1, user: '张三', content: '哈哈,沙发' },
              { id: 2, user: '张三2', content: '哈哈,板凳' },
              { id: 3, user: '张三3', content: '哈哈,凉席' },
              { id: 4, user: '张三4', content: '哈哈,砖头' },
              { id: 5, user: '张三5', content: '哈哈,楼下山炮' }
          ]
        }
      }
      render(props){
        return (
          <div>
            { /* 头部的行内样式 */}
            <h1 style={{ color: 'red', fontSize: '24px', backgroundColor: 'yellow', fontWeight: 700 }}>这是评论列表组件</h1>
            { this.state.CommentList.map(item => <CommentDetail key={ item.id } { ...item }></CommentDetail> )}
          </div>
        )
      }
    }
    
    // 定义子组件的object样式
    const styles = {
      itemStyle: { border: '1px dashed #ddd', margin: '10px', padding: '10px', boxShadow: '0 0 10px #ddd' },
      userStyle: { fontSize: '14px' },
      contentStyle: { fontSize: '12px' }
    }
    
    // 子组件
    function CommentDetail(props) {
      console.log(props)
      return (
        <div style={ styles.itemStyle }>
          <h1 style={ styles.userStyle }>评论人:{ props.user }</h1>
          <p style={ styles.contentStyle }>评论内容:{ props.content }</p>
        </div>
      )
    }

    使用sass

    第三方使用css;自己的样式使用sass

    配置loader

    {
      test: /.css$/,
      use: [ 'style-loader', 'css-loader']
    },
    {
      test: /.scss$/,
      use: [
        { loader: 'style-loader' },
        {
          loader: 'css-loader',
          options: {
            modules: {
              localIdentName: '[path][name]-[local]-[hash:base64:5]',
            },
          }
        },
        { loader: 'sass-loader' }
      ]
    }

    使用:

    // .jsx 文件
    // 自己的 sass 文件,需要使用 sass 配置规则解析
    import cssobj from '@/css/commentList.scss'
    console.log('commentList.css', cssobj)
    
    import 'bootstrap/dist/css/bootstrap.css'
    
    class CommentList extends React.Component {
      constructor(params) {
        super()
      }
      render(props){
        return (
          <div>
            {/* 自己的 css */}
            <h1 className={ [cssobj.title, 'test'].join(' ') }>这是评论列表组件</h1>.
            {/* 第三方的 css */}
            <button className="btn btn-primary">按钮</button>
          </div>
        )
      }
    }

    6. react中的事件

    模拟实现 VUE 中的双向绑定数据原理

    //#region 介绍 react 中绑定事件的标准格式
    import React from "react"
    
    class BindEvent extends React.Component {
      constructor(params) {
        super()
        this.state = {
          msg: '哈哈',
          name: 'LiMing',
          color: '#' + Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, '0')
        }
      }
      render(props){
        return (
          <div>
            <h1>BindEvent 组件</h1>
            {/* 绑定点击事件 onClick */}
            <button onClick={ () => this.myclickHandler(this.state.color) }>按钮</button>
            {/* 显示 state 中的数据 */}
            <h3>{ this.state.msg }</h3>
            {/* 模拟实现 VUE 中的双向绑定数据原理 */}
            <input type="text" ref="txt" style={{  '100%'}} value={ this.state.msg } onChange={e => this.txtChange(e) }/>
          </div>
        )
      }
      txtChange = (e) => {
        // 获取 input 中 value 的两种方法
        // console.log(e.target.value === this.refs.txt.value) // true
        this.setState({
          msg: e.target.value // this.refs.txt.value
        })
      }
      myclickHandler = color => {
        // this.setState({}, callbcak) 是异步的,需要在 callbcak 获取最新的值
        this.setState({
          msg: color
        },() => {
          console.log( '2', this.state.msg )
        })
        // 还是未修改的值
        console.log( '1', this.state.msg )
      }
    }
    
    export default BindEvent

    7. react组件的生命周期函数

    componentWillMount:组件将要被挂载,此时还没有开始渲染虚拟DOM
    render:第一次开始渲染真正的虚拟DOM,当render执行完,内存中就有了完整的虚拟DOM了
    componentDidMount:组件完成挂载,此时,组件已经渲染到页面上了,数据和页面达到同步,当这个方法执行完,组件进入 运行中 的状态
      • 组件运行阶段
    componentWillReceivrProps:组件将要接受新的属性,此时,只要这个方法被触发,就证明父组件为当前子组件传递了新的属性值
    shouldComponenUpdate:组件是否需要被更新,此时,组件尚未开始更新,但是 state 和 props 中的数据是最新的
    componentWillUpdate:组件将要被更新,此时尚未开始更新,内存中的虚拟DOM树还是旧的
    render:此时,重新根据最新的 state 和 props 重新渲染一颗存在内存中的 虚拟DOM树,当 render 调用完毕,内存中旧的DOM树 替换 成新的 DOM树了,但是页面还是旧的
    componentDidUpdate:此时,页面又被重新渲染了,state 和 虚拟DOM 页面都是最新的,并且保持同步
      • 组件销毁阶段
    componentDidUpdate:组件将要被卸载,此时组件还可以正常使用

     使用计数器学习react的生命周期:

    import React from "react"
    import ReactTypes from 'prop-types'
    
    class Counter extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          msg: 'ok',
          count: props.initcount
        }
      }
    
      // 父组件没有传递值是,设置默认值
      static defaultProps = {
        initcount: 0
      }
    
      // 创建静态的 propTypes 对象,可以校验 父组件传递过来的值得类型
      static propTypes = {
        initcount: ReactTypes.number // prop-types 指定数据类型
      }
    
      // 组件将要被挂载到页面上,此时,数据已经存在,但是还没有开始渲染虚拟DOM = VUE: created
      componentWillMount() {
        console.log('componentWillMount 函数')
        // 无法获取DOM,页面 和 虚拟DOM 还没有渲染
        console.log(document.querySelector('.myh3')) // null
        // props 和 state 可以获取到
        console.log(this.props.initcount) // 100
        console.log(this.state.msg) // ok
        this.myselfFunc()
      }
    
      // 渲染内存中的虚拟DOM,但是页面尚未真正显示DOM元素
      render() {
        // 每当调用 render 函数时,页面上的元素还是旧的
        console.log('render 函数')
        // return 之前,虚拟DOM还没有创建, return 之后,虚拟DOM创建,但是还没有挂载到页面上
        return (
          <div>
            <h1>这是计数器组件</h1>
            <input id="btn" type="button" value="+1" onClick={this.increment} />
            <hr/>
            <h3 ref="h3" className="myh3">当前数量为:{ this.state.count }</h3>
            <h3 className="myh3">当前数量为:{ this.props.initcount }</h3>
          </div>
        )
      }
    
      // 组件挂载到页面上,会进入这个生命周期函数,此时页面上渲染虚拟DOM了 == VUE mounted
      componentDidMount() {
        console.log('componentDidMount 函数')
        console.log(document.querySelector('.myh3')) // <h3 ... </h3>
        // 使用原生js方法,改变 count 值
        // document.querySelector('#btn').onclick = () => {
        //   this.setState({
        //     count: this.state.count + 2
        //   })
        // }
      }
    
      // 从这里判断组件是否需要更新
      shouldComponentUpdate(nextProps, nextState) {
        console.log('shouldComponentUpdate 函数')
        // 该周期中必须返回一个 boolean 值
        // 返回 false,则不执行其他生命周期函数,但是 state 的数据会被更改
        // 返回 true,继续执行其他生命周期函数
        // console.log(this.state.count) // 此时直接使用this.state是未更改的值
        // console.log(nextProps, nextState) // 最新的 props 和 state
        // return nextState.count % 2 === 0? true: false
        return true
      }
    
      // 组件将要更新,此时尚未更新,在进入这个生命周期函数的时候,内存中的虚拟DOM是旧的,页面上的DOM元素也是旧的
      componentWillUpdate() {
        console.log('componentWillUpdate 函数')
        console.log(document.querySelector('.myh3').innerText) // 旧的数据
        console.log(this.refs.h3.innerText)
      }
    
      // 此时虚拟DOM,页面,数据都是最新的
      componentDidUpdate() {
        console.log('componentDidUpdate 函数')
        console.log(this.refs.h3.innerText)
      }
      myselfFunc() {
        console.log('自己定义的函数');
      }
    
      increment = () => {
        this.setState({
          count: this.state.count + 1
        })
      }
    }
    
    export default Counter

    使用父子组件学习componentWillReceiveProps周期函数

    • 注意:第一次渲染子组件时,componentWillReceiveProps不会传递props属性。

    import React from "react"
    
    class Parent extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          msg: '父组件中的 msg 消息'
        }
      }
      render() {
        return (<div>
          <h1>这是父组件</h1>
          <input type="button" value="点击修改msg的值" onClick={this.changeMsg}/>
          <hr/>
          <Son pmsg={ this.state.msg }></Son>
        </div>)
      }
    
      changeMsg = () => {
        this.setState({
          msg: '更改了,变成新值了'
        })
      }
    }
    
    class Son extends React.Component {
      constructor(props) {
        super(props)
        this.state = {}
      }
      render() {
        return (<div>
          <h3>这是子组件</h3>
          <p>{ this.props.pmsg }</p>
        </div>)
      }
    
      // 组件接受外界传值触发
      // 子组件第一次被渲染到页面上时候,不会触发这个方法,第二次以后才会触发
      componentWillReceiveProps(nextProps) {
        console.log('被触发了')
        // 注意:在 componentWillReceiveProps 中拿到的值是未更改的值!!
        // console.log(this.props.pmsg)
        console.log(this.props.pmsg + '----' + nextProps.pmsg);
      }
    }
    
    export default Parent

    8. react中绑定this传递参数的3中方式

    import React from "react"
    
    class BindThis extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          msg: '这是 BindThis组件中的 msg 消息'
        }
    
        // 绑定 this 并传参的方式 ②
        // bind 的返回值 是调用函数和参数的 copy
        // 注意:bind 不会修改原函数的 this 指向
        this.changeMsg2Copy = this.changeMsg2.bind(this, '参数one', '参数two')
      }
      render() {
        return (<div>
          <h1>这是 BindThis 组件 </h1>
          {/* 方式 ① */}
          {/* bind 修改函数内部的 this 指向,指向 bind 参数列表中的第一个参数 */}
          <input type="button" value="react 中绑定 this 传递参数的3中方式1" onClick={ this.changeMsg1.bind(this, '参数1', '参数2')}/>
          <hr />
           {/* 方式 ② */}
          <input type="button" value="react 中绑定 this 传递参数的3中方式2" onClick={ this.changeMsg2Copy }/>
          <hr />
           {/* 方式 ③ */}
          <input type="button" value="react 中绑定 this 传递参数的3中方式3" onClick={ () => this.changeMsg3('参数1111', '参数2222') }/>
    
          <h2>{ this.state.msg }</h2>
        </div>)
      }
    
      changeMsg1(arg1, arg2) {
        console.log(this) // 指向实例
        this.setState({
          msg: '变成新值了' + arg1 + arg2
        })
      }
    
      changeMsg2(arg1, arg2) {
        console.log(this) // undefined
        this.setState({
          msg: '变成新值了' + arg1 + arg2
        })
      }
    
      changeMsg3 = (arg1, arg2) => {
        console.log(this) // 指向实例
        this.setState({
          msg: '变成新值了' + arg1 + arg2
        })
      }
    }
    
    export default BindThis

    9. context多重组件传值

    import React from "react"
    import ReactTypes from "prop-types"
    class Conm1 extends React.Component {
      constructor(params) {
        super()
        this.state = {
          color: "red"
        }
      }
    
      // 1. 在父组件定义 func,这个func固定的名字 getChildContext
      // 内部必须返回一个对象,这个对象就是要共给子孙组件的数据
      getChildContext() {
        return {
          color: this.state.color
        }
      }
    
      // 2. 使用 属性校验,规定一下传递给子组件的数据类型,固定名字 childContextTypes
      static childContextTypes = {
        color: ReactTypes.string // 规定传递给子组件的数据类型
      }
      render(props) {
        return (
          <div>
            <h1>这是父组件:通过 getChildContext 将父组件中的颜色 red 传递给孙子组件</h1>
            <hr />
            <Conm2></Conm2>
            
          </div>
        )
      }
    }
    
    class Conm2 extends React.Component {
      render(props) {
        return (
          <div>
            <h3>这是子组件</h3>
            <hr />
            <Conm3></Conm3>
          </div>
        )
      }
    }
    
    class Conm3 extends React.Component {
      constructor(params) {
        super()
        this.state = {}
      }
      // 3. 先来个属性校验,校验父组件传递过来的参数类型
      static contextTypes = {
        color: ReactTypes.string // 这里,子组件一定要校验一下父组件传递过来的 context 的数据类型
      }
      render(props) {
        return (
          <div>
            <h5 style={{ color: this.context.color }}>
              这是孙子组件 --- {this.context.color}
            </h5>
          </div>
        )
      }
    }
    export default Conm1

     

  • 相关阅读:
    Mybatis插件开发入门,运行原理,例子
    ActiveMQ是什么,为什么使用MQ
    sql优化
    用户注册流程
    JMS入门Demo
    运维自动化之Ansible
    ceph安装部署
    ceph工作原理
    linux进程
    任务计划
  • 原文地址:https://www.cnblogs.com/houfee/p/11706489.html
Copyright © 2020-2023  润新知