美文网首页
react学习之路

react学习之路

作者: 余音绕梁_0809 | 来源:发表于2019-11-07 14:44 被阅读0次

    一、简介

    image.png

    1、安装Node.js

    使用React.js是可以用最原始的<Script>标签进行引入的,但是这实在是太low了,工作中也不会有这种形式进行引入。所以在学习的时候,我们就采用脚手架形式的安装。这就需要我们安装Node.js,用里边的npm来进行安装。

    安装Node只需要进入Node网站,进行响应版本的下载,然后进行双击安装就可以了。

    Node中文网址:http://nodejs.cn/ (建议你在这里下载,速度会快很多)

    需要你注意的是,一定要正确下载对应版本,版本下载错误,可是没有办法使用的哦。

    Node.js 安装好以后,如果是Windows系统,可以使用 Win+R打开运行,然后输入cmd,打开终端(或者叫命令行工具)。

    输入代码:

    node -v 
    
    

    如果正确出现版本号,说明Node安装成功了,需要说明的是,你的版本号可能跟我视频中的有所不同,这无关紧要。

    然后再输入代码:

    npm -v
    
    

    如果正确出现版本号,说明npm也是没问题的,这时候我们的Node.js安装就算完成了。

    2、脚手架的安装

    Node安装好之后,你就可以使用npm命令来安装脚手架工具了,方法很简单,只要打开终端,然后输入下面的命令就可以了。

    npm install -g create-react-app
    
    

    create-react-app是React官方出的脚手架工具,其实有很多第三方的脚手架工具,也有很多优秀的。但是作为初学者为了减少踩坑,所以我们使用官方的脚手架。

    3、创建第一个React项目

    脚手架安装好以后,就可以创建项目了,我们在D盘创建一个ReactDemo文件夹,然后进入这个文件夹,创建新的React项目。

    D:  //进入D盘
    mkdir ReactDemo  //创建ReactDemo文件夹
    create-react-app demo01   //用脚手架创建React项目
    cd demo01   //等创建完成后,进入项目目录
    npm start   //预览项目,如果能正常打开,说明项目创建成功
    

    报错解决备注:

    npm install -g create-react-app
    在mac上安装时,没有效果,使用了    cnpm install -g create-react-app
    
    对应的在使用  create-react-app demo01 时,同样没有效果,此时应将cnpm设置为默认的安装方式
    npm config set registry https://registry.npm.taobao.org
    

    二、代码之路

    import React,{Component} from 'react';
    等价于 下面的两行(es6解构赋值)
    import React from 'react';
    const Component = React.Component
    

    1、JSX简介

    JSX就是Javascript和XML结合的一种格式。React发明了JSX,可以方便的利用HTML语法来创建虚拟DOM,当遇到<,JSX就当作HTML解析,遇到{就当JavaScript解析.

    比如我们写一段JSX语法

    <ul className="my-list">
        <li>JSPang.com</li>
        <li>I love React</li>
    </ul>
    
    

    比如我们以前写一段代码JS代码:

    var child1 = React.createElement('li', null, 'JSPang.com');
    var child2 = React.createElement('li', null, 'I love React');
    var root = React.createElement('ul', { className: 'my-list' }, child1, child2);
    
    

    从代码量上就可以看出JSX语法大量简化了我们的工作。

    2、组件和普通JSX语法区别

    这个说起来也只有简单的一句话,就是你自定义的组件必须首写字母要进行大写,而JSX是小写字母开头的。
    这个也算是一个比较重要的知识点吧。

    注意: 这里的HTML类名 要用className定义,不能使用class

    3、 JSX使用注意事项

    在React中,最外层需要有一个包裹元素,最外层不想有包裹元素的话,使用<Fragment>标签。

    要想使用<Fragment>,需要先进行引入。

    import React,{Component,Fragment } from 'react'
    

    然后把最外层的<div>标签,换成<Fragment>标签。

    import React,{Component,Fragment } from 'react'
    
    class Xiaojiejie extends Component{
        constructor (props) {
          super(props) //调用父类的构造函数,固定写法
          this.state = {
            inputValue: '',
            list: ['haha', '精油推背'],
            ceshiHTML:'<h1>ssss</h1>'
          }
        }
        render(){
            return  (
                <Fragment>
                  {/* 正确注释的写法 */}
                   <label htmlFor="jspang">加入服务:</label>
                   <div><input id='jspang' /> <button> 增加服务 </button></div>
                   <ul>
                       <li>头部按摩</li>
                       <li>精油推背</li>
                       <li dangerouslySetInnerHTML={{__html:this.state.ceshiHTML}}></li> 
                   </ul> 
                </Fragment>
            )
        }
    }
    export default Xiaojiejie 
    

    注意:
    1、其实这边的写法,类似于Vue的组件写法,html必须有一个最外层html包裹;此时如果你不需要最外层的html,可以将最外层的html用Fragment代替,vscode不会报错,同时在html中也不会有最外层的html显示
    2、jsx的注释写法,{/* 正确注释的写法 */}
    3、HTML类名要把class换成className,它是防止和js中的class类名冲突
    4、dangerouslySetInnerHTML={{__html:XX}},如果XX是含html标签的,则可以解析html标签;
    5、label标签不能使用for。它容易和javascript里的for循环混淆,会提示你使用htmlFor。(想点击“加入服务”直接可以激活文本框,方便输入。按照html的原思想,是直接加ID就可以了)

    4、方法定义以及使用

    import React,{Component,Fragment} from 'react';
    
    class Xiaojiejie extends Component{
      constructor (props) {
        super(props) //调用父类的构造函数,固定写法
        this.state = {
          inputValue: '',
          list: ['头部按摩', '精油推背']
        }
      }
      render() {
        return(
          <Fragment>
            <div>
              <input value = {this.state.inputValue} onChange={this.inputChange.bind(this)}/> 
              <button onClick={this.addList.bind(this)}> 增加服务 </button>
            </div>
            <ul>
                {
                  this.state.list.map((item, index) => {
                    return (
                      <li key={item + index}
                      onClick = {this.deleteItem.bind(this, index)}
                      > {item} </li>
                    )
                  })
                }
            </ul> 
        </Fragment>
        )
      }
      inputChange(e) {
        this.setState({
          inputValue: e.target.value
        })
      }
      addList(){
        this.setState({
          list: [...this.state.list, this.state.inputValue],
          inputValue: ''
        })
      }
      deleteItem(index){
        console.log(index)
        let list = this.state.list
        list.splice(index,1)
        this.setState({
          list: list
        })
      }
    }
    export default Xiaojiejie
    

    注意:
    1、onChange是一个change事件,在React里,事件的绑定要使用小驼峰命名法。
    2、这里的.bind(this),是ES6的语法,此时里面需要绑定this,否则在方法中使用this时,this的指向并不是当前的dom元素。
    3、React中的循环是写在{}中的,在循环中绑定方法的话,需要写在return()的括号中,此时若想要传值,和this一样写在.bind()中,在定义方法的时候,可以带参数,可以拿到。类似vue
    4、React中对state里面的数据进行操作时,不能直接写this.state.XX = '',需要使用this.setState({XX:''})
    5、React是禁止直接操作state的,即我们在删除this.state.list中的数据时,是将this.state.list赋值给一个新的数组,去操作这个新数据,最后将新数组的值赋值给this.state.list

    5、父子组件之间的通信

    • 父组件
    import React, { Component } from 'react';
    // 这里的引入 FoodItem首字母要大写,不然会报错
    import FoodItem from './foodItem'
    import  './shitang.css'
    
    class Shitang extends Component {
      constructor(props){
        super(props)
        this.state = {
          foodValue: '',
          foodList: ['土豆丝', '土豆片', '土豆块', '土豆泥']
        }
        this.addFood = this.addFood.bind(this)
        this.inputChange = this.inputChange.bind(this)
        this.deleteItem = this.deleteItem.bind(this)
      }
      render() { 
        return (  
          <div>
            <label htmlFor='food'>添加新菜品</label>
            <input
            id='food'
            vaule={this.state.foodValue}
            onChange={this.inputChange}
            // ES6语法 -- 绑定DOM元素
            ref={ input => {this.input = input}}></input>
            <button onClick={this.addFood}>添加菜品</button>
            <ul>
              {
                this.state.foodList.map((item, index) => {
                  return (
                    <li 
                    key={item + index}
                    // onClick={this.deleteItem(index)} --- 使用时会立即执行deleteItem方法
                    onClick={() => this.deleteItem(index)}
                    >{item}</li>
                  )
                })
              }
            </ul>
            {/* 这里使用了子组件 FoodItem */}
            <ul className='food' ref = {ul => {this.ul = ul}}>
              {
                this.state.foodList.map((item, index) => {
                  return (
                    <FoodItem 
                    key={item + index}
                    content ={item}
                    index = {index}
                    onClick={() => this.deleteItem(index)}
                    deleteItem = {this.deleteItem}
                    />
                  )
                })
              }
            </ul>
          </div>
        ); 
      }
      inputChange () {
        this.setState({
          foodValue: this.input.value
        })
      }
      addFood () {
        this.setState({
          foodList: [...this.state.foodList,this.state.foodValue]
        })
        console.log(this.ul.querySelectorAll('li').length)
        // 现象: 此时调用打印的值是与实际不相符的 
        // 原因: this.setState这个方法是异步的,当执行打印的时候,this.setState可能并没有执行完成
        // 解决方案: this.setState有一个回调函数,此时将打印放到回调函数中即可
        // this.setState({
        //   foodList: [...this.state.foodList,this.state.foodValue]
        // },() => {
        //   console.log(this.ul.querySelectorAll('li').length)
        // })
        this.input.value = ''
      }
      deleteItem (index) {
        let list = this.state.foodList
        list.splice(index, 1)
        this.setState({
          foodList: list
        })
      }
    }
     
    export default Shitang;
    
    • 子组件
    import React, { Component } from 'react';
    import PropTypes from 'prop-types'
    
    class foodItem extends Component {
      constructor(props){
        super(props)
        this.deleteFood = this.deleteFood.bind(this)
      }
      render() { 
        return (  
          <li 
          onClick={this.deleteFood}
          >
          {this.props.username}--- 会做的菜 ---{this.props.content}
          </li>
        );
      }
      deleteFood () {
        this.props.deleteItem(this.props.index)
      }
    }
    // 校验传递值 PropTypes  使用时 需先引入
    foodItem.propTypes ={
      // 必传值的校验 ---isRequired
      content:PropTypes.string.isRequired,
      deleteItem:PropTypes.func,
      index:PropTypes.number,
      username: PropTypes.string.isRequired
    }
    // 若设置必传值,又担心没有值则可使用默认值defaultProps;
    // 此时若是父组件没有传递username这个参数,则会使用默认值渲染
    foodItem.defaultProps = {
      username: '小花'
    }
     
    export default foodItem;
    
    image.png

    注意:
    1、事件绑定的另一种写法:onClick={this.addFood.bind(this)} ====> onClick = {this.addFood};前面的写法是在绑定事件的时候将this指向一起绑定;后面的写法,this的指向是在constructor方法中绑定
    this.addFood = this.addFood.bind(this)
    2、在引入子组件时,子组件名称首字母要大写import FoodItem from './foodItem',否则会报错
    3、在使用 onClick={this.deleteItem(index)}进行传参的时候,会立即执行deleteItem方法,并不是点击后触发deleteItem方法。修改为onClick={() => this.deleteItem(index)} 原因暂时不知道,但是这样可以解决
    4、ref来绑定DOM元素:绑定时最好使用ES6语法中的箭头函数。 ref={ ul => {this.ul = ul}},此时需要注意:若此时我们想要获取ul下面li的个数,且li是动态生成的(addFood方法中),若是在this.setState()方法后执行,则会出现与实际不符的情况。 原因是this.setState()方法是异步加载的,当执行打印的时候,this.setState()可能并没有执行完成。解决方案:React的this.setState()方法提供了回调函数,写在回调中即可。
    5、React有一个特性是单项数据流,即父组件向子组件传递一个数组的话,是可以传递成功的,但是这个数组在子组件中只可使用不可改变。
    6、父组件向子组件传递参数和方法:在父组件中的子组件上传递(如下),传递的数据在子组件中用this.props.XXX接收,方法也一样

    <FoodItem 
      key={item + index}
      content ={item}
      index = {index}
      onClick={() => this.deleteItem(index)}
      deleteItem = {this.deleteItem}
    />
    

    7、在子组件中校验传递值( PropTypes) 使用时 需先引入import PropTypes from 'prop-types'

    //foodItem --- 子组件的类名
    foodItem.propTypes ={
      // 必传值的校验 ---isRequired
      content:PropTypes.string.isRequired,
      deleteItem:PropTypes.func,
      index:PropTypes.number,
      username: PropTypes.string.isRequired
    }
    // 若设置必传值,又担心没有值则可使用默认值defaultProps;
    // 此时若是父组件没有传递username这个参数,则会使用默认值渲染
    foodItem.defaultProps = {
      username: '小花'
    }
    

    8、子组件向父组件传递数据时,需要在子组件中使用this.props.xx(val)(其中val为需要传递给父组件的数据,可以为单个数据,也可以为对象的形式;xx表示在父组件中绑定的是方法名,这个方法在绑定之后也需要在父组件的constructor中使用bind()方法绑定this的指向)

    6、react的生命周期

    Initialization:初始化阶段。
    Mounting: 挂载阶段。
    Updation: 更新阶段。
    Unmounting: 销毁阶段

    image.png

    备注:以上来自技术胖博主,以及自己的遇到的问题

    相关文章

      网友评论

          本文标题:react学习之路

          本文链接:https://www.haomeiwen.com/subject/qvjsbctx.html