React个人入门总结《一》

前言

首先我说说我为什么学React,React可以说是三大框架中思想最为先进的一款,在国内Vue虽然很火,但是Vue也开始引入了React的一些思想,比如虚拟DOM,接着Vue3.0还会引入JSX,React非常注重组件化和复用性,学习React可以比以前更熟悉一些架构知识。
工作时间也有3个多月了,该学会写东西总结了,最近利用工作时间学习React,今天又有空写一下总结,怕过了一段时间忘记,这篇文章我是看了 React小书 总结下来的,如果有人跟我一样是React入门的话,建议去React小书学起,从脚手架学起真的是快很多,我们老师教我们学东西首先要学会用,再学原理。
这篇文章不适合大神看,如果有大神想指导可以在下面发布评论。

概念

React是个帮你构建页面UI的库,他相当于MVC里面的View,React将我们界面分成各个独立的小块,每个小块都是一个组件,而这些组件可以互相嵌套丶结合,然后就形成了页面

脚手架安装(Create-react-app)

由于React要配合其他工具和库辅助,例如编译需要babel,组织代码需要redux等第三方状态管理工具,写单页面应用需要应用到React-router,这也相当于React的全家桶。

  • 使用npm安装Create-react-app

    npm install -g create-react-app

  • 使用create-react-app创建项目

    create-react-app my-app

    我第一次使用时报了一个这样的错误,然后我上网查才知道是使用脚手架创建项目时要下载许多依赖文件,然后它是默认使用npm下载的,这时候主要将npm切换成cnpm就可以解决了。

    • npm config set registry https://registry.npm.taobao.org //将npm切换成cnpm
    • 还有一解决方法就是找到resource下面的app里面的extensions打开后看到有git文件夹,删除,重启。

    创建完成之后然后cd到项目根目录然后npm start就可以启动项目了

  • 项目结构

    创建完成之后cd到创建的目录然后可以看到脚手架帮我们创建的结构

    node_modulespublicscriptssrc
    依赖文件静态库项目启动配置开发目录

    开发时候在src目录上开发就行了,如果要查看项目配置,可以输入npm run ejectconfig目录暴露出来,这个是默认隐藏的,你也可以在node_modules目录上找到,这个目录是项目的配置。如果运行npm run eject报错的话,把代码储存到本地就行了git add .,然后git commit -m '',接着重新运行指令,这执行是不可逆的。

    public有个index.html这是整个项目的页面,之后我们要把写好的东西渲染到里面去。
    src可以看到一个index.js,由于写React几乎都是组件,而index一般用来渲染其他组件

编写React

首先我们把index.js的代码改成这样再看看效果

import React, { Component } from 'react' // 写React必须要引入这两样东西
import ReactDOM from 'react-dom' // 组件不需要引入,他只有一个作用,就是渲染DOM
import './index.css' // 可以直接引入css或者是sass,也可以引入图片
<!-- 使用组件继承Component类 -->
class Header extends Component {
  render () { // render方法把里面东西jsx返回
    return (
      <!-- jsx -->
      <div>
        <h1>React大法好</h1>
      </div>
    )
  }
}
<!-- 渲染出去 -->
ReactDOM.render(
  <Header />,
  document.getElementById('root')
)
复制代码
  • JSX

    JSX 是 JavaScript 语言的一种语法扩展,长得像 HTML,但并不是 HTML。

    平时我们写html是这样写的

        <div class='box' id='content'>
          <div class='title'>Hello</div>
          <button>Click</button>
        </div>
    复制代码

    如果我们用JS对象来表示的话

    {
      tag: 'div',
      attrs: { className: 'box', id: 'content'},
      children: [
        {
          tag: 'div',
          arrts: { className: 'title' },
          children: ['Hello']
        },
        {
          tag: 'button',
          attrs: null,
          children: ['Click']
        }
      ]
    }
    复制代码

    没错,其实JSX也相当于JS对象,React也会根据这样编译,转化为HTML结构之后就可以拿去构建真正的DOM,这是最后那段渲染DOM所做的事情

    ReactDOM.render(
      <Header />,
      document.getElementById('root')
    )
    复制代码

    这是整个过程流程图,单独把ReactDOM抽出来的原因是因为我们可以渲染到Canvas或者转化原生App(ReactNative)

  • 组件render方法

    React中一切皆为组件,写组件的时候一般都需要继承React.js的Component,这个方法必须返回一个JSX元素,而返回并列JSX元素是不合法的

    <!-- bad -->
    render() {
        return {
            <div>one</div>
            <div>two</div>
        }
    }
    复制代码

    必须要用一个外层元素包起来,不能有多个外层元素

    <!-- good -->
    render() {
        return {
            <div>
                <div>one</div>
                <div>two</div>
            </div>
        }
    }
    复制代码
    • 表达式插入

      表达式可以插入变量,还可以计算,还可以条件返回,并且可以写函数,render会把真实的内容返回,特别的灵活

      render() {
          const word  = 'word'
          const isGoodWord = true
          return(
              <div>
                  <h1>HELLO {word}</h1> // 变量
                  <h1>{1 + 2}</h1> // 计算
                  <h1>{(function(){ return 'React' })()}</h1> // 函数
                  // 条件返回
                  {  
                     isGoodWord 
                     ? <span>好文章</span>
                     : <span>坏文章</span>
                  }
              </div>
          )
      }
      复制代码

      表达式不仅可以插入标签内部,还可以插入属性

      render() {
          const className  = 'header'
          return(
              <div className={ className }></div>
          )
      }
      复制代码

      由于class是JS的关键字,React.js换成了className,还有一个就是for换成了htmlfor

  • 组件组合

    自定义的组件都必须要用大写字母开头

    class Title extends Component {
      render () {
        return (
          <h1>标题标题标题</h1>
        )
      }
    }
    
    class Header extends Component {
      render () {
        return (
          <div>
            <!-- 渲染三次 -->
            <Title />
            <Title />
            <Title />
          </div>
        )
      }
    }
    复制代码

    可以直接在Header标签里面使用,React.js会在<Title />,组件的render方法表示的JSX内容渲染出来,
    它会显示在相应的位置上

  • 事件监听

    在需要监听事件的元素加上属性类似 onClick onKeyDown这样的属性即可,事件属性必须使用驼峰命名法。

    class Title extends Component {
      handleOnClickTitle(hello) {
          console.log(hello)
          console.log(this)
      }
      render () {
        return (
          <!-- 绑定事件时需要绑定this,不绑定则拿不到 -->
          <h1 onClick={this.handleOnClickTitle.bind(this, 'hello')}>标题标题标题</h1>
        )
      }
    }
    复制代码

    在 React.js 不需要手动调用浏览器原生的addEventListener进行事件监听。React.js 帮我们封装好了一系列的 on* 的属性,当你需要为某个元素监听某个事件的时候,只需要简单地给它加上 on* 就可以了。而且你不需要考虑不同浏览器兼容性的问题,React.js 都帮我们封装好这些细节了。

    React.js 将浏览器原生的event对象封装了一下,对外提供统一的 API 和属性,这样你就不用考虑不同浏览器的兼容性问题。

    如果你不手动绑定this在函数里面打印的会是undefined,这是因为 React.js 调用你所传给它的方法的时候,并不是通过对象方法的方式调用,而是直接通过函数调用,所以事件监听函数内并不能通过this 获取到实例如果你想在事件函数当中使用当前的实例,你需要手动地将实例方法 bind 到当前实例上再传入给 React.js

组件的 state 和 setState

state是组件的状态,我们可以用它来进行状态切换,如显示或隐藏,或者是改变className等操作。

class Index extends Component {
  constructor(props) {
    super(props)
    <!-- 状态 -->
    this.state = {
        isShow: true
    }
  }
  render () {
    handleState() {
        this.setState({
            isShow: !this.state.isShow
        })
    }
    return (
        <div className='index'>
            {
                this.state.isShow === true
                ? <h1>显示<h1/> : null
            }
            <button onClick={this.handleState.bind(this)}></button>
        </div>
    )
  }
}
复制代码

setState 方法由父类 Component 所提供。当我们调用这个函数的时候,React.js 会更新组件的状态 state ,并且重新调用 render 方法,然后再把 render 方法所渲染的最新的内容显示到页面上。

setState可以接受函数或者对象作为参数,这里要注意一个问题,当你调用setState时React并不会马上修改state,而是把这个对象放在一个更新队列,稍后才会从队列当中把新的状态提取出来合并到 state 当中,然后再触发组件更新。

handleState() {
    console.log(this.state.isShow) // true
    this.setState({
        isShow: !this.state.isShow
    })
    console.log(this.state.isShow) // true
}
复制代码

如果要做到后续操作依赖前一个setState的这种操作的话,我们可以把参数换成函数。

handleState() {
    console.log(this.state.isShow) // true
    this.setState((prevState) => {
        return { isShow: !this.state.isShow }
    })
    console.log(this.state.isShow) // false
}
复制代码

在一个函数里面同步使用setState它只会执行一个,React.js 内部会把js 事件循环中的消息队列的同一个消息中的setState都进行合并以后再重新渲染组件,如果你先调用一个setState然后用定时器调用的话,就会触发两次

说到setState还要说到一个React的受控组件,比如 inputtextarea这种输入框,如果你直接写这种标签时候React会报一个错误。

React.js 认为所有的状态都应该由 React.js 的 state 控制,只要类似于 <input /><textarea /> 这样的输入控件被设置了 value 值,那么它们的值永远以被设置的值为准。这时候只要给他绑定一个 onChange就行了。

class InputWithUserName extends Component {
  constructor() {
    super()
    this.state = {
      value: ''
    }
  }
  changeValue(event) {
    console.log(event.target.value)
    this.setState({
      value: event.target.value
    })
  }
  render() {
    return (
      <div>
        {this.props.content}
        <input type="text" value={this.state.value} onChange={this.changeValue.bind(this)}/>
      </div>
    )
  }
}
复制代码
Props

每个组件都可以接受一个 props 参数,它是一个对象,包含了所有你对这个组件的配置。

在使用一个组件的时候,可以把参数放在标签的属性当中,所有的属性都会作为 props 对象的键值

class Index extends Component {
  render () {
    return (
        <div className='index'>
            <!-- 这里两个花括号只是JSX里面再嵌套一个对象而已 -->
            <!-- 还可以传函数 -->
            <Main options={{title: 'React', content: 'React大法好'}} onClick={()=> console.log('Click in Index')} />
        </div>
    )
  }
}

class Main extends Component {
    <!-- 如果没有东西传过来可以在这里设置默认值 -->
    static defaultProps = {
        options = {
            title: '默认头部',
            content: '默认内容'
        }
    }    
    <!-- 这里可以省略,因为React会自动生成 -->
    constructor(props) {
        super(props)
    }
    
    handleIndexClick() {
        if(this.props.onClick) {
            <!-- 执行Index传过来的函数 -->
            this.props.onClick()
        }
    }
    
    render() {
        const options = this.props.options
        <div className='main'>
            <h1>{options.title}</h1>
            <p>{options.content}</p>
            <button onClick={this.handleIndexClick.bind(this)}></button
        </div>
    }
}
复制代码

不要试图去改变传过来的props,React.js 希望一个组件在输入确定的 props 的时候,能够输出确定的 UI 显示形态。如果 props 渲染过程中可以被修改,那么就会导致这个组件显示形态和行为变得不可预测,这样会可能会给组件使用者带来困惑。

handleIndexClick() {
    this.props.options = null; // 报错
    if(this.props.onClick) {
        <!-- 执行Index传过来的函数 -->
        this.props.onClick()
    }
}
复制代码

但这并不意味着由props决定的显示形态不能被修改。组件的使用者可以主动地通过重新渲染的方式把新的props 传入组件当中,这样这个组件中由props决定的显示形态也会得到相应的改变。

class Index extends Component {
  constructor (props) {
    super(props)
    this.state = {
      options: {
        title: 'React',
        content: 'React大法好'
      },
    }
  }
  <!-- 修改传过去的值 -->
  handleOptionsChange() {
      this.setState({
        options: {
            title: 'Vue',
            content: 'Vue大法好'
        },
      })
  }
  render () {
    return (
        <div className='index'>
            <Main options={ this.state.options } onClick={()=> console.log('Click in Index')} />
            <!-- 点击修改 -->
            <button onClick={this.handleOptionsChange.bind(this)}></button>
        </div>
    )
  }
}
复制代码

由于setState会导致Index重新渲染,所以<Main />也会接受到新的值并且重新渲染,这样就能做到修改<Main />的显示效果

stateprops

state 的主要作用是用于组件保存、控制、修改自己的可变状态。state 在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。state 中状态可以通过 this.setState 方法进行更新,setState 会导致组件的重新渲染。

props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。

stateprops 有着千丝万缕的关系。它们都可以决定组件的行为和显示形态。一个组件的 state 中的数据可以通过 props 传给子组件,一个组件可以使用外部传入的 props 来初始化自己的 state。但是它们的职责其实非常明晰分明:state 是让组件控制自己的状态,props 是让外部对组件自己进行配置。

如果你觉得还是搞不清 stateprops 的使用场景,那么请记住一个简单的规则:尽量少地用 state,尽量多地用 props。没有 state 的组件叫 无状态组件(stateless component),设置了 state 的叫做 有状态组件件(stateful component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。前端应用状态管理是一个复杂的问题。

有状态组件

class HelloWorld extends Component {
  constructor() {
    super()
    this.state = {
        isShow: true
    }
  }

  sayHi () {
    alert('Hello World')
  }

  render () {
    return (
      <div onClick={this.sayHi.bind(this)}>Hello World</div>
    )
  }
}
复制代码

无状态组件

const HelloWorld = (props) => {
  const sayHi = (event) => alert('Hello World')
  return (
    <div onClick={sayHi}>Hello World</div>
  )
}
复制代码

函数式的组件编写方式是一个函数就是一个组件,你可以和以前一样通过<HellWorld /> 使用该组件。不同的是,函数式组件只能接受 props 而无法像跟类组件一样可以在 constructor 里面初始化 state。你可以理解函数式组件就是一种只能接受 props 和提供render 方法的类组件。

渲染列表数据

直接使用ES6的map遍历代码比较简洁,也可以手动写循环构建列表的JSX,在React遍历使用map是非常常见的。

const users = [
  { username: 'Jerry', age: 21, gender: 'male' },
  { username: 'Tomy', age: 22, gender: 'male' },
  { username: 'Lily', age: 19, gender: 'female' },
  { username: 'Lucy', age: 20, gender: 'female' }
]

class Index extends Component {
  render () {
    return (
      <div>
        {users.map((user, i) => <User user={user} key={i} />)}
      </div>    
    )
  }
}

class User extends Component {
  render () {
    const { user } = this.props
    return (
        <div>
            <div>姓名:{user.username}</div>
            <div>年龄:{user.age}</div>
            <div>性别:{user.gender}</div>
            <hr />
        </div>
    )
  }
}
复制代码

React.js 的是非常高效的,它高效依赖于所谓的 Virtual-DOM 策略。简单来说,能复用的话 React.js 就会尽量复用,没有必要的话绝对不碰 DOM。对于列表元素来说也是这样,但是处理列表元素的复用性会有一个问题:元素可能会在一个列表中改变位置。但其实 React.js 只需要交换一下 DOM 位置就行了,但是它并不知道其实我们只是改变了元素的位置,所以它会重新渲染后面两个元素(再执行 Virtual-DOM 策略),这样会大大增加 DOM 操作。但如果给每个元素加上唯一的标识,React.js 就可以知道这两个元素只是交换了位置

render () {
    return (
      <div>
        {users.map((user, i) => <User user={user} key={i} />)}
      </div>    
    )
}
复制代码

这样 React.js 就简单的通过 key 来判断出来,这两个列表元素只是交换了位置,可以尽量复用元素内部的结构。
对于用表达式套数组罗列到页面上的元素,都要为每个元素加上 key 属性,这个 key 必须是每个元素唯一的标识,一般后台会返回。

下一篇 --- React个人入门总结《二》
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值