真的只要看一次,你就能入门React

767 阅读7分钟

React 的入门笔记

写在前面,笔者才疏学浅,写此文章仅做参考!不对之处,敬请海涵!

阅读此文章,我相信你或有 Vue.js 的学习经验。并且有一定的编程能力,也相信你有学习 React 的想法。我是先阅读[React的官方文档](https://react.docschina.org/),然后再通过看[jspang视频](https://jspang.com/detailed?id=46#toc21)一步一步入门。

前言

分享 Winter老 师说过一个观点:对于任何语言来说,必定是“用规范的文法,去表达特定语义,最终操作运行时的”一个过程。关键字:文法 语义 运行时。笔者的能力有限,在这篇文章中,会按照自己学习之前想了解的几个方面入手。

初识

简介

  • React 是一个用于构建用户界面的JAVASCRIPT库。
  • React 主要用于构建UI,很多人认为 React 是 MVC 中的 V( 视图)。
  • React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站, 并于 2013 年 5 月开源。
  • React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。
  • React 需手动实现双向数据绑定,通过 react 中的状态管理。
  • React 的基本思想就是组件 每一个组件 同时也 可以看作是一个类 都继承自 React.Component。

安装步骤

所谓工欲善其事,必先利其器

  • 安装脚手架
npm install -g create-react-app
  • 初始项目
create-react-app demo
  • 安装插件
// 假如你用的是 vscode 编写代码
// 可以快速帮你生成文件结构
simple-react-snippets
  • Google 插件
// 科学上网,自行安装
// 可以帮你省去 console.log 的时间
React Developer Tools

启程

JSX

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

class App extends Component{
    render(){
        return (
            <ul className="my-list">
                <li>I love React</li>
            </ul>
        )
    }
}

简单点理解就是在 JS 中写 HTML 代码。

数据绑定

React 和 Vue 都不建议用户直接操作 DOM 元素,而是要通过数据进行驱动,改变界面中的效果。在 React 中我们定义数据是在组件中 constructor 中。

//js的构造函数,由于其他任何函数执行
constructor(props){
    super(props) //调用父类的构造函数,固定写法
    this.state={
        inputValue:'' , // input中的值
        list:[]    //列表
    }
}

具体为什么是在 this.state 中定义,[React的官方文档](https://react.docschina.org/)有详细说明 state 的文档。反正,作为初学者,咱们就先记着是这样定义。

<!-- 绑定数据 -->
<input value={this.state.inputValue} /> 

绑定事件

数据定义好之后,接下来的问题是我要对这个数据进行操作,这就需要绑定事件了。

<input value={this.state.inputValue} onChange={this.inputChange} />

这时候你就在 input 标签上绑定了 onChange 事件。

JSX 中有一些原生的方法或属性名称都有所改变。有一些是遵循驼峰命名,例如onchange=>onChange;有一些是改变了名称,例如:class=>className

特别注意

在 JSX 中绑定好事件之后,一定要同时绑定 this

// 在没有手动绑定 this 的时候,如果这样使用,直接会报错。
inputChange(e){
    console.log(this); // undefined
    this.state.inputValue=e.target.value; // 报错
}

有两个错误

  • this 指向不对,你需要重新用 bind 设置一下指向( ES6 的语法)
  • React 中改变值需要使用 this.setState 方法

解决方案

// 绑定 this
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
// 修改数据
inputChange(e){
    this.setState({
        inputValue:e.target.value
    })
}
// 我们最好不要直接改变 state 中的值,而是定义一个临时变量改变,这样有利于时空回溯
inputChange(e){
    let temp = e.target.value
    this.setState({
        inputValue:temp
    })
}

条件渲染

Vue 中的条件渲染表达式v-if="boolean";Angular 中的条件渲染表达式*ngIf="boolean";而 React 中使用逻辑表示式&&

<ul>
  {this.state.list.map((item, key) => {
    return (
      !item.checked && (
        <li key={key + item}>
          <input
            type="checkbox"
            checked={item.checked}
            onChange={this.checkBoxState.bind(this, key)}
          />
          {item.title}---{'  '}
          <button onClick={this.removeItem.bind(this, key)}>
            删除
          </button>
        </li>
      )
    );
  })}
</ul>

true && expression 始终计算为 expression,并且 false && expression 始终计算为 false

列表渲染

在 JSX 中我们使用 map 方法进行列表渲染

<ul>
  {this.state.list.map((item, key) => {
    return (
        <li key={key + item}>
           {item}
        </li>
      )
    );
  })}
</ul>

同时还需要为循环的每一项设置 key。

转轴

父子组件

React 和 Vue 都是单向数据流,这样有利于对数据的管理。好比是小公司人数较少,有啥事只要喊一声就行了。但是大公司发布命令,靠喊是解决不了问题的,最有效的方法就是一层一层单向的传达命令。

父组件

// content 是传递的数据
// funItem 是传递的方法
<ChildrenItem content={item} funItem={this.funItem.bind(this)} />

子组件

import React, { Component } from 'react'; //imrc(vscode 插件快速生成代码片段命令)
class ChildrenItem  extends Component { //cc
   constructor(props){
       super(props)
       // 最好是在 constructor 中绑定 this,这样有利于性能的优化
       this.handleClick=this.handleClick.bind(this)
   }
    render() { 
        return ( 
            <div onClick={this.handleClick}>{this.props.content}</div>
         );
    }
    handleClick(){
        // 切记别在子组件中修改父组件传来的数据
        this.props.funItem()
    }
}

export default ChildrenItem;

类型检查

import React, { Component } from 'react';
import PropTypes from 'prop-types'
class ChildrenItem  extends Component { //cc
   constructor(props){
      ...
   }
    render() { 
      ...
    }
}
// 对传入的值进行类型检查
ChildrenItem.propTypes={
    content:PropTypes.string.isRequired,// 加了 isRequired 就是必须得传
    funItem:PropTypes.func,
    name:PropTypes.string
}

// 设置默认值
ChildrenItem.defaultProps = {
    name:'张三'
}

export default ChildrenItem;

生命周期

生命周期函数指在某一个时刻组件会自动调用执行的函数。React 有四大生命周期,分别是:

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

constructor不算生命周期函数,是构造函数,它是ES6的基本语法。但是你要把它当成一个生命周期函数,可以看成 React 的 Initialization 阶段,定义属性(props)和状态(state)。

我们日常的开发任务中,主要是操作 Mounting 阶段的生命周期函数。伴随着整个虚拟DOM的生成,先后执行:

  • componentWillMount: 在组件即将被挂载到页面的时刻执行
  • render: 页面 state 或 props 发生变化时执行
  • componentDidMount: 组件挂载完成时被执行

componentWillMountcomponentDidMount这两个生命周期函数,只在页面刷新时执行一次,而render函数是只要有stateprops变化就会执行

数据请求

  • 安装 axios
npm install -S axios
  • Fast Mock
// 快速生成测试接口数据
https://www.fastmock.site/
  • 使用
// 建议在componentDidMount 函数里执行,因为在 render 里执行,会出现很多问题,比如一直循环渲染;在componentWillMount 里执行,在使用 RN 时,又会有冲突。
componentDidMount(){
    axios.post('https://www.fastmock.site/mock/你的api')
        .then((res)=>{console.log('axios 获取数据成功:'+JSON.stringify(res))  })
        .catch((error)=>{console.log('axios 获取数据失败'+error)})
}

-save(-S)的意思是将模块安装到项目目录下,并在 package 文件的 dependencies 节点写入依赖,项目生产环境需要用到。-save-dev(-D)的意思是将模块安装到项目目录下,并在 package 文件的 devDependencies 节点写入依赖,只需要在开发环境用到。

破晓

ToDoList 案例

class ToDoList extends React.Component {
  constructor(props) {
    super(props);
    // 实现双向数据绑定第一步 在 state 中声明
    this.state = {
      value: '',
      list: [
        { title: '吃饭', checked: false },
        { title: '睡觉', checked: false },
        { title: '打豆豆', checked: false },
      ],
    };
  }

  handleChange(e) {
    this.setState({
      value: e.target.value,
    });
  }

  addData() {
    let title = this.state.value;
    // 一定要注意,不能直接修改 state 的数据,虽然可以实现,但是 react 中特别强调了!如果这样做在后期的性能优化上会有很多麻烦
    // 所以修改 state 中的数据的时候,我们会定义一个局部变量,用来改变数据
    // 用 ... 剩余参数的方式给数组添加数据 更方便 而且更直接
    let tempList = [
      ...this.state.list,
      {
        title: title,
        checked: false,
      },
    ];
    // 注意 this.setState 是异步的方法
    this.setState({
      list: tempList,
      value: '',
    });
  }

  checkBoxState(key) {
    let tempList = this.state.list;
    tempList[key].checked = !tempList[key].checked;
    this.setState({
      list: tempList,
    });
  }

  removeItem(key) {
    let tempList = this.state.list;
    tempList.splice(key, 1);
    this.setState({
      list: tempList,
    });
  }

  // render 函数中 写 JSX
  // class -> className for -> htmlFor 因为 class 和 for 都是 js 中的关键字
  // 但是 React 中 map 循环代替了 for 循环
  // map 中一定要有 return 并且要注意 return 的使用 它只能返回一行 多行需要加括号
  render() {
    return (
      // Fragment 是 react 提供给我们使用的 一个根节点 不会渲染
      <Fragment>
        {/* 在 jsx 中写注释,推荐使用这种方式 */}
        <header className="title">
          TodoList:  
          <input
            value={this.state.value}
            onChange={this.handleChange.bind(this)}
          />{' '}
          {'   '}
          {/* {this.state.value} */}
          <button onClick={this.addData.bind(this)}>添加</button>
        </header>
        <h2>待办事件</h2>
        <ul>
          {this.state.list.map((item, key) => {
            return (
              // 在 react 中 key = key+item 这是比较合理的
              // true && expression 始终计算为 expression,并且 false && expression 始终计算为 false
              // dangerouslySetInnerHTML={{ __html: item.title }} 可以使我们直接在文本框中输入 html 标签
              // 当然我们是推荐这种用法的 因为 JSX 可以防止注入攻击(cro)解析器会把代码转换成字符串
              !item.checked && (
                <li key={key + item}>
                  <input
                    type="checkbox"
                    checked={item.checked}
                    onChange={this.checkBoxState.bind(this, key)}
                  />
                  {item.title}---{'  '}
                  <button onClick={this.removeItem.bind(this, key)}>
                    删除
                  </button>
                </li>
              )
            );
          })}
        </ul>
        <h2>已完成事件</h2>
        <ul>
          {this.state.list.map((item, key) => {
            return (
              item.checked && (
                <li key={key + item}>
                  <input
                    type="checkbox"
                    checked={item.checked}
                    onChange={this.checkBoxState.bind(this, key)}
                  />
                  {item.title}---{'  '}
                  <button onClick={this.removeItem.bind(this, key)}>
                    删除
                  </button>
                </li>
              )
            );
          })}
        </ul>
      </Fragment>
    );
  }
}

写在最后

到此,我相信你或许对 react 有了初步的了解。当然,文章可能出现错误,请不吝赐教。第一次在掘金上面发表文章,属实不容易。计划好的事情,踏出了第一步才会有接下来的一个个脚印。就像是种一个棵数最好的时间是10年前,其次实现!愿你如一位骑士,目光如炬,一往无前!