React我的入门总结《一》

前言

首先我说说我为何学React,React能够说是三大框架中思想最为先进的一款,在国内Vue虽然很火,可是Vue也开始引入了React的一些思想,好比虚拟DOM,接着Vue3.0还会引入JSX,React很是注重组件化和复用性,学习React能够比之前更熟悉一些架构知识。

工做时间也有3个多月了,该学会写东西总结了,最近利用工做时间学习React,今天又有空写一下总结,怕过了一段时间忘记,这篇文章我是看了 React小书 总结下来的,若是有人跟我同样是React入门的话,建议去React小书学起,从脚手架学起真的是快不少,咱们老师教咱们学东西首先要学会用,再学原理。

这篇文章不适合大神看,若是有大神想指导能够在下面发布评论。

概念

React是个帮你构建页面UI的库,他至关于MVC里面的View,React将咱们界面分红各个独立的小块,每一个小块都是一个组件,而这些组件能够互相嵌套丶结合,而后就造成了页面css

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

因为React要配合其余工具和库辅助,例如编译须要babel,组织代码须要redux等第三方状态管理工具,写单页面应用须要应用到React-router,这也至关于React的全家桶。html

  • 使用npm安装Create-react-app

    npm install -g create-react-app前端

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

    create-react-app my-app
    node

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

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

    建立完成以后而后cd到项目根目录而后npm start就能够启动项目了react

  • 项目结构

    建立完成以后cd到建立的目录而后能够看到脚手架帮咱们建立的结构git

    node_modules public scripts src
    依赖文件 静态库 项目启动配置 开发目录

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

    public有个index.html这是整个项目的页面,以后咱们要把写好的东西渲染到里面去。
    src能够看到一个index.js,因为写React几乎都是组件,而index通常用来渲染其余组件redux


编写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我的入门总结《二》

相关文章
相关标签/搜索