1. 首页

3个小时能把 React 学到哪种程度?

2019年7月3日 天气小晴

现在早上9点,准时开始学习 React

半个小时过一遍 React 官方文档和教程 (9:00 – 9:30)

个小时能把

扫视一遍它的基本介绍(5分钟)

我们看到下面介绍了三个 React 最重要的特性:

  • 声明式:它是说:React 创建用户界面很简单,当数据改变就会高效的更新和立即渲染组件。同时声明式可以让你的代码更加可预测和易调试

怎么就是可预测的和易调试呢,暂时不是很理解

  • 组件化:可以创建一个管理自身状态的组件,通过组合这些组件来创造复杂的UI。因为组件逻辑是用 JavaScript 写的而不是模版,所以你可以传更丰富的数据到你的APP,同时还可以与 DOM 解藕。

  • 一学多用:不需要新的技术栈,重新写新的代码就可以开发一些新的功能。比如用作服务器渲染,React Native 编写移动端代码。

个小时能把 再往下扫,给出了 React 的 4 个简单的小例子

  • Helloworld(认识 组件 和 props)
class HelloMessage extends React.Component {
  render() {
    return (
      <div>
        Hello {this.props.name}
      </div>
    );
  }
}

ReactDOM.render(
  <HelloMessage name="Taylor" />,
  document.getElementById('hello-example')
);

看到它使用 ES6 的继承,继承了一个组件的基类,此刻充满好奇心的你,肯定会想我继承的这个 React.Component 到底是啥?(先留着之后去看 React 的源码)

没有写构造函数,说明它直接继承了基类的构造函数

里面有一个 render 函数 return 一个貌似模版的东西,这个就是最简单的组件

紧接着下面一个 ReactDOM.render函数接受两个参数一个是组件标签,一个id查找,现在大概的意思就是render函数就是要把组件渲染到找到id的这个地方。 组件标签里面的属性就给组件中作为this.props的属性。而且我们知道父组件通过 props 可以将数据传递给子组件

  • 定时器(认识 状态state)
class Timer extends React.Component {
  constructor(props) {
    super(props);
    this.state = { seconds: 0 };
  }

  tick() {
    this.setState(state => ({
      seconds: state.seconds + 1
    }));
  }

  componentDidMount() {
    this.interval = setInterval(() => this.tick(), 1000);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    return (
      <div>
        Seconds: {this.state.seconds}
      </div>
    );
  }
}

ReactDOM.render(
  <Timer />,
  document.getElementById('timer-example')
);

我们看到第二个例子会发现里面多了很多其他的东西:

  • 构造函数
  • state
  • 声明周期函数

发现我们可以在组件里面定义组件的构造函数,而且可以在构造函数里面初始化一个名为 状态(state)的东东。

怎样去理解状态(state)这个词?

我们假设组件是有生命的,它必然有个生命周期。大致分给三个阶段:

出生—-> 变化—-> 死亡 (对应) 创建—-> 更新—-> 销毁

伴随着生命周期的变化,它身上的属性也在发生变化,比如体温、寿命、心情、皮肤好坏、细胞生命力,甚至思维模式。我们可以把这些变化的属性成为状态(state)

但是作为它的创建者,我们即为它的上帝,所以需要控制管理它的状态的变化。所以我们在每一个阶段的变化中加了一个钩子,在相应的阶段去定义一些程序。

这些大概是我看完这段代码最粗浅的理解吧 。

  • Todo(一个小型增删改查APP)
class TodoApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = { items: [], text: '' };
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  render() {
    return (
      <div>
        <h3>TODO</h3>
        <TodoList items={this.state.items} />
        <form onSubmit={this.handleSubmit}>
          <label htmlFor="new-todo">
            What needs to be done?
          </label>
          <input
            id="new-todo"
            onChange={this.handleChange}
            value={this.state.text}
          />
          <button>
            Add #{this.state.items.length + 1}
          </button>
        </form>
      </div>
    );
  }

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

  handleSubmit(e) {
    e.preventDefault();
    if (!this.state.text.length) {
      return;
    }
    const newItem = {
      text: this.state.text,
      id: Date.now()
    };
    this.setState(state => ({
      items: state.items.concat(newItem),
      text: ''
    }));
  }
}

class TodoList extends React.Component {
  render() {
    return (
      <ul>
        {this.props.items.map(item => (
          <li key={item.id}>{item.text}</li>
        ))}
      </ul>
    );
  }
}

ReactDOM.render(
  <TodoApp />,
  document.getElementById('todos-example')
);

这个主要是对一下事件的一些运用,增加,删除数组的元素然后更新 dom 相关的操作。在React 中我们能很容易办到。

  • 输入绑定(认识插件和双向绑定)
class MarkdownEditor extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = { value: 'Hello, **world**!' };
  }

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

  getRawMarkup() {
    const md = new Remarkable();
    return { __html: md.render(this.state.value) };
  }

全部快速过一遍,尝试跟着敲一遍。哦,原来 React 是这么用的!!!

总结一下 React 可以做什么呢?

  • 创建一个简单组件,父组件可以通过 props 传值给子组件
  • 还可以创建拥有状态的组件,添加生命周期钩子管理组件状态
  • 对数据增删改查,渲染在 UI
  • 通过事件实现表单的双向绑定

差不多知道怎么用了,我们现在系统的来看一下文档,给以后深入研究铺路 接下来我们去系统的看看它相关的一些概念…

开始进入DOC 系统性的学习下有关 React 的文档概念相关(20分钟)

  1. 安装运行 React 安装运行有三种方式:
  • 直接使用 script 标签引入
<div id="like_button_container"></div>
  <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>

  <!-- Load our React component. -->
  <script src="like_button.js"></script>

<script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script>

  • 在 node 环境下安装
npx create-react-app my-app
cd my-app
npm start

  • 使用 CDN
  1. React 的相关概念
  • 简单的例子
ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('root')
);

大致理解为通过ReactDOM对象的render函数,将<h1>Hello, world!</h1>挂载在 id 为 root 下面

  • 我们来了解下JSX,长这个样子:
const element = <h1>Hello, world!</h1>;

这个写法称为JSX, 它既不是字符串,也不是HTML,而是JavaScript 的一种扩展。 看到它有几个特点: 它可以赋值给 一个引用

插入变量和函数 它可以使用{}插入变量和函数

const element = <h1>Hello, {name}</h1>;
// or
const element = (
  <h1>
    Hello, {formatName(user)}!
  </h1>
);

插入在属性值中

const element = <img src={user.avatarUrl}></img>;

可以嵌套子节点

const element = (
  <div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);

防止xss攻击

const title = response.potentiallyMaliciousInput;
// This is safe:
const element = <h1>{title}</h1>;

通过babel编译后长这个样子

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

  • 渲染节点是怎么渲染的 不像浏览器DOM, React 节点是一些简单的对象,所以很容易创建,而且它主要关心的是需要更新的部分。

HTML 中始终存在一个唯一的 root 节点<div id="root"></div>,React DOM 回去管理这个root 下面的东西。

然后把 JSX 和 这个root节点,传递给 ReactDOM.render(),使用这个方法吧节点渲染出来

那么它是怎么更新的呢?

React 节点是 immutable(不变的),一旦创建就不能改变它的子节点和属性,所以唯一能更新的方法就是,创建一个新的节点,传到ReactDOM.render()重新渲染

现在就很清楚了,也就是它要更新UI,存在两个状态,一个是更新前到,一个是更新后的

React 仅仅会更新它必须更新的部分,也就是差异的部分

这个可以放到后面研究,大概是使用 diff 算法比较 虚拟dom树 差异后来更新真实dom

现在我们大致清楚它是如何渲染和更新的了

  • 组件和 props 组件和函数一样接受一个输入值,返回一个React element

创建组件的两种方式: 通过函数创建

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

通过ES 6 class 创建

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

组件也可以组合成一个复杂的组件

return (
  <div>
    <Welcome name="Sara" />
    <Welcome name="Cahal" />
    <Welcome name="Edite" />
  </div>
);

组件的扩展

function Avatar(props) {
  return (
    <img className="Avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />
  );
}

父组件传值到子组件(props)

All React components must act like pure functions with respect to their props. 它这里强调了就像纯函数一样,所有 React 组件中props也是一样不能直接改变它

  • 状态和生命周期 之前也提到,大概组件会有三个阶段,每个阶段具体会有几个生命周期方法,大概罗列一下:

载入(挂载阶段 Mounting…)constructor(构造)、 static getDerivedStateFromProps(处于初始化挂载和更新之间,返回一个更新需要的对象或者null(不更新))、render(渲染 DOM )、componentDidMount(完成挂载)

变化(更新阶段 Updating…):static getDerivedStateFromProps(同上)、shouldComponentUpdate(更新前,决定是否更新,返回false不更新)、render(更新 DOM)、getSnapshotBeforeUpdate(更新完成前捕获DOM的信息,然后返回一个参数给DidUpdate 使用)、componentDidUpdate(更新完成)

销毁(卸载阶段 Unmounting…):componentWillUnmount(卸载前)

还有 2 个错误处理的钩子 static getDerivedStateFromError(),componentDidCatch() 具体了解访问 https://reactjs.org/docs/react-component.html

如何使用生命周期钩子呢?

将生命周期钩子添加到Class中

class Clock extends React.Component {
  constructor(props) {
    super(props);
  }

  componentDidMount() {
        //...
  }
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
      </div>
    );
  }
}

常用做法

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }
  componentWillUnmount() {
    clearInterval(this.timerID);
  }

componentDidMount()设置定时器(异步操作),componentWillUnmount()清除相关异步操作

如何更新状态呢? 使用this.setState({comment: 'Hello'});更新状态,不要直接修改state,因为这样不会重新渲染组件

  • 事件机制 事件处理和 DOM0 很相似但是有这些不同的地方
function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

驼峰而不是全小写

传递函数作为事件处理而不是字符串

需要使用 preventDefault 而不是返回false阻止浏览器默认事件

我们可以通过 箭头函数将 this 绑定到点击事件处理函数上

  handleClick = () => {
    console.log('this is:', this);
  }

或者在初始化时候就绑定

  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // This binding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  • 条件渲染

大概可以分为两种方式 在render函数外部判断,通过return 返回或者引用不同的 React element

if (isLoggedIn) {
    button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
    button = <LoginButton onClick={this.handleLoginClick} />;
}

在render函数内部判断,使用三元操作符来判断

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      {isLoggedIn ? (
        <LogoutButton onClick={this.handleLogoutClick} />
      ) : (
        <LoginButton onClick={this.handleLoginClick} />
      )}
    </div>
  );
}

  • 列表和 key 使用数组map()来渲染多组件
const listItems = numbers.map((number) =>
  <li>{number}</li>
);

渲染多组件需要使用 唯一的Key 来提高渲染性能,这个也和 diff 算法有关,大概是Key识别组件后,就可以直接复用了旧VDOM的节点了

<ul>
  {props.posts.map((post) =>
    <li key={post.id}>
      {post.title}
    </li>
  )}
</ul>

We don’t recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state. Check out Robin Pokorny’s article for an in-depth explanation on the negative impacts of using an index as a key. If you choose not to assign an explicit key to list items then React will default to using indexes as keys.

如果列表的顺序可能改变的话,这里不推荐使用 index 作为 keys,它可能造成性能问题。大概是因为数组顺序改变了和 改变前的index 和 改变后的index 与组件不对应,这样也是没法复用的。(猜测)这里放在后续研究,这篇文章有讲 https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318

  • 表单相关 这里提到了一个新的名词“”受控组件”,大概意思就是它可以通过输入来控制和改变自己的状态,一般组件只能通过 setState()改变状态,而它可以通过输入改变,它是受到用户输入控制的。 大概有以下几种:input, textarea,select

我们这样可以通过这种特性来实现一个双向绑定, 这里暂时留着后面补充

  • 状态提升 这个为来解决两个组件值同步问题的,有点像联级查询 通常来说,组件与组件之间是独立的,一个组件的状态改变不会影响另外一个组件。但是我们现在需要一个组件状态变化,另外一个组件状态也会变化,那该怎么办呢?

现在就又一种方式,给两个组件加一个相同的状态作为桥梁,一旦这个状态改变,同时改变这两个状态的其他状态,这样就达到了更新另一个组件状态的目的。

class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature: '', scale: 'c'};
  }

  handleCelsiusChange(temperature) {
    this.setState({scale: 'c', temperature});
  }

  handleFahrenheitChange(temperature) {
    this.setState({scale: 'f', temperature});
  }

  render() {
    const scale = this.state.scale;
    const temperature = this.state.temperature;
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

    return (
      <div>
        <TemperatureInput
          scale="c"
          temperature={celsius}
          onTemperatureChange={this.handleCelsiusChange} />

        <TemperatureInput
          scale="f"
          temperature={fahrenheit}
          onTemperatureChange={this.handleFahrenheitChange} />

        <BoilingVerdict
          celsius={parseFloat(celsius)} />

      </div>
    );
  }
}

  • 组合和继承 组合和继承真的是老生常谈的话题,设计模式里面推荐是使用组合,原因就在于组合是松耦合的模式,继承是紧耦合的模式。父亲的改变必定会影响到儿子的改变,所以会谨慎使用集成。

除非是父是很稳定不变的,像接口那样只是一个约束,而且约束原则不会频繁变化,使用继承是恰当的。但是一般情况使用组合实现松耦合。

  1. 高级指南(这里还有很多地方不是特别理解,后续添加)
  • Higher-Order Components(高阶组件)
  • Optimizing Performance(性能优化)
  • Reconciliation(协调)
  • Refs (引用)
  • Portals (门)
  • Render Props(渲染 Props)
  • Static Type Checking(静态类型检查)
  • Uncontrolled Components(非受控组件)
  • Web Components(web 组件)
  1. 了解相关API(这里还有很多地方不是特别理解,后续添加)
  • React
  • React.Component
  • ReactDOM
  • ReactDOMServer
  • Shallow Renderer
  • JS Environment Requirements
  • Glossary
  1. 研究新特性(Hooks)

Hooks 是什么?

为了让你放心大胆的使用,它给出了你使用Hooks的三大好处

  • 完全选择性加入:你不想用可以不需要学习和使用,使用它不需要重写你已有的组件代码
  • 100%的向后兼容:它不会中断现有的改变
  • 现在就可以用:16.8 之后就可以用了

为啥要用Hooks?

它可以让你不使用class也可以使用状态和其他功能

  • 组件之间的逻辑很难重用
  • 组件太复杂难以理解
  • class会造成迷惑

可能这些问题,在真正投入使用,真正实践的时候才会遇到吧,暂时先不讨论 看看它怎么去重用逻辑和去除 class 的?

mport React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

这里它通过 useState 来接受一个初始的状态,调用一个setCount函数来控制状态的逻辑. 点击按钮,就会调用这个函数,一旦调用这个函数,就触发了状态的变更。

也就是说我们可以任意时刻去管理状态了,函数组件中就可以了。

这个变更带来的一个直接的好处就是:我们完全可以将组件的某些处理状态的逻辑抽离出来单独处理。

这大概是我最初浅的认识,还需要实践中检验,之后会给出例子。

看看它的API

个小时能把

  1. FAQ
  • Virtual DOM and Internals(虚拟DOM 和它内部实现介绍-fiber) 之后再去研究

一个小时做一个后台项目的一个小订单管理页面

提出一个需求(2分钟)

/**
* 订单管理页面,包含3个部分
* 1. 筛选栏,包含日期选择、单选、输入、查询按钮
* 2. 表格,包含ID,商品信息,商品数量,金额,状态,操作
* 3. 分页
*/

选择技术方案(3分钟)

尽可能的简单快速的实现它

  • UI组件 antd
  • 预处理 sass
  • 获取数据 axios
  • 模拟数据 json-sever
  • 测试 jest
  • 路由,状态管理,持续集成…暂不需要

动手简单的实践一下(45分钟)

由于篇幅原因,具体实践请看下篇文章…这里跳转(之后添加链接)

效果展示和调优(10分钟)

大概差不多是这种效果 个小时能把 这个不是最终效果,还需要调优…

深入技术栈来研究 (10:00 – 11:00)

知乎上/掘金上搜索一些讲 React 比较好的书,诶,发现两本,很快搞到资源了

  • 《React 精髓》

个小时能把

  • 《深入 React 技术栈》

个小时能把

浅读这两本之后会写一篇博客,对里面知识和作者想法进行梳理

开始大致过一遍这两本书的内容

总结下阅读这两本书的感觉

两本书写的都很好,而且它们刚好侧重点不同,前者侧重于里面核心api的设计思想和理念,后者侧重于实际开发中的一些技术难点

之后会有精读的文章出来,请持续关注…

深入源码(11:00 – 11:30)

阅读 React 源码(15分钟)

进入 github 仓库 https://github.com/facebook/react 头一眼看到全是代码很懵,不知道看啥好

首先来搜一些目录结构图,我们要知道

  • 哪个版本?
  • 哪里上手?
  • 我们应该看什么?
  • 这些目录都是做什么用的?
  • 需要掌握哪些东西?
  • 看完后有什么收获?

之后会有深入研究的文章出来,请持续关注

阅读 antd 源码 (15分钟)

现在还有点时间,我们跑去 antd 的github里面,看看它的各个组件是怎么写的 尝试自己写一个 dialogtable 以后再做

差不多粗略看完源码我们对 React 有些许了解了

现在开始了解一下它的相关生态了

逛逛生态圈(11:30 – 12:00)

组件库(10分钟)

  • antd

个小时能把

  • antd pro

个小时能把

  • 其他组件库

状态管理(10分钟)

  • flux

  • redux

  • saga

  • react-redux

  • dva https://dvajs.com/

个小时能把

React and redux based, lightweight and elm-style framework. D.Va拥有一部强大的机甲,它具有两台全自动的近距离聚变机炮、可以使机甲飞跃敌人或障碍物的推进器、 还有可以抵御来自正面的远程攻击的防御矩阵。

Dva 是基于 React + Redux + Saga 的最佳实践沉淀

app.model({
  namespace: 'count',
  state: {
    record: 0,
    current: 0,
  },
  reducers: {
    add(state) {
      const newCurrent = state.current + 1;
      return { ...state,
        record: newCurrent > state.record ? newCurrent : state.record,
        current: newCurrent,
      };
    },
    minus(state) {
      return { ...state, current: state.current - 1};
    },
  },
  effects: {
    *add(action, { call, put }) {
      yield call(delay, 1000);
      yield put({ type: 'minus' });
    },
  },
  subscriptions: {
    keyboardWatcher({ dispatch }) {
      key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) });
    },
  },
});

  • immutable(不变性的库)

路由(3分钟)

  • react-router(这个同Vue Router 区别不大)

node 相关(4分钟)

  • Umi JS(一个插件,构建,测试,打包工具集)

个小时能把

  • SSR

后续会着重研究SSR相关原理和实践

其他(3分钟)

  • Jest (单元测试相关 facebook 出版,react 默认)

两个优势,第一个是一次安装全部拥有, 二是比之前 Karma 系列多了一个快照测试,主要是用了比对dom结构,进行回归测试相关

  • 其他UI框架

    自制简单开源 React UI 计划中…

后记

惨痛经历:本来写了 3400 多字,有次突然发现少了一些篇章,然后急忙按撤回,撤回最后成了全空状态。自动保存后,草稿全被覆盖,丢失了,后来又重新写的,悲惨。

提醒大家如果想写博客,一定不要在线写,直接本地写,写完复制到在线,加图片后直接发布。

感谢阅读

感谢你能阅读到这个地方,还没放弃…

作者能力有限,里面或许有些许文字错误,和一些技术上错误的理解,欢迎在评论中指出改正

本篇文章只是记录了本人 3 个小时学习 React 的经历,可能对那些学习的比较零散而没有章法的人有一些帮助,通过分享出一个学习路线,让他们系统性的构建知识。要想真正掌握 React 里面的相关技术还是要花很长时间的。

如果你想去深入研究下它的话,希望你能看完本篇文章后,可以花更多时间去琢磨里面的一些点和程序的设计。

最后希望各位的技术都有长足的进步,而不是仅仅局限在一些工作的业务上。

下期预告

找工作结束了,下周大概就会正式入职了,写作博客的时间会相对比较少,但是还是希望多多去研究学习,总结一下学到的点,分享给大家。

下期博文的话,内容大概是关于 Vue 的一些思考和在工作中运用的实践总结(时间待定)

还有就是关于上一期后面面试题的研究,也会慢慢研究,之后会出一个系统性的总结(待定)

原创声明

本文完全原创,全部是经过作者3个小时的学习,和一天多思考总结出来的成果。允许转载,但需标明作者和原文链,谢谢!

[issue评论区](https://github.com/suoyuesmile/suo-blog/issues/38)

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:3个小时能把 React 学到哪种程度?

链接:https://www.javascriptc.com/3774.html

« 2020 Chrome 81 发布 !消灭混合内容的最后一步~
从 javascript 事件循环看 Vue.nextTick 的原理和执行机制»
Flutter 中文教程资源

相关推荐

QR code