PHP的全栈之路3

死磕React 第一回

上帝视角

官网当然是我们最佳的学习资料,但我记得一句话,好老师从来都不是按照课本顺序来讲义的,所以对于官网上的文档,我觉得最佳的学习顺序也并非从头看到尾,我们首先要开启上帝视角

为什么要有react呢,他解决了一类什么问题?
React 理念这篇官网的文章让我找到了一些答案,水平有限,我只读懂了文章里的第一步、第二步,后面就开始晕了,建议第一次接触的人也不要急于全部搞懂,这篇文章给我两个很重要的信息。

  • 前端分层解决方案——组件
    第一次让我见识到,原来前端不再是像以前那样,写一大串html,来完成整个页面的静态编码,而是将页面切割成不同的部分,用组件来表示页面的元素
  • 组件的实现方式——react
    react是什么呢?我认为它就是组件的一种实现方式之一,组件是一种思想,落实到编码实现上,就是react了,react也就显的不再那么高深了,由此我还能猜想到Vue和Angular无非就是组件的另外两种实现方式而已

了解概念

接下来就要撸一堆概念了,建议撸完本文后,可以官网上撸概念

组件

将UI切分成一些的独立的、可复用的部件,这样你就只需专注于构建每一个单独的部件,这样的部件称为组件,在React中有其特有的语法表示,组件从概念上看就像是函数,它可以接收任意的输入值(称之为“props”),并返回一个需要在页面上展示的React元素

函数定义/类定义组件

定义一个组件最简单的方式是使用JavaScript函数:

function Welcome(props) {
  return 

Hello, {props.name}

; }

它接收一个单一的“props”对象并返回了一个React元素,可能你会对这句话里的propsReact元素理解起来困难,没关系,就将其当成PHP里的参数和返回值好了。

你也可以使用 ES6 class 来定义一个组件:

class Welcome extends React.Component {
  render() {
    return 

Hello, {this.props.name}

; } }

上面两个组件在React中是相同的,我就当第一种是过程编程,第二种是对象编程吧,这样有助于我记忆,毕竟利用学过的知识来映射新的知识,这样能更清楚的记住

组合组件

组件可以在它的输出中引用其它组件,这就可以让我们用同一组件来抽象出任意层次的细节。在React应用中,按钮、表单、对话框、整个屏幕的内容等,这些通常都被表示为组件。
我们可以创建一个App组件,用来多次渲染Welcome组件:

function Welcome(props) {
  return 

Hello, {props.name}

; } function App() { return (
); } ReactDOM.render( , document.getElementById('root') );

警告:组件的返回值只能有一个根元素。这也是我们要用一个

来包裹所有元素的原因。

元素

上面我们提到一个组件会返回一个React元素,所谓元素是构成 React 应用的最小单位,元素用来描述我们在屏幕上看到的内容:

const element = 

Hello, world

;

与浏览器的 DOM 元素不同,React 当中的元素事实上是普通的对象,React DOM 可以确保 浏览器 DOM 的数据内容与 React 元素保持一致

元素渲染到 DOM 中

首先我们在一个 HTML 页面中添加一个 id="root" 的

:

在此 div 中的所有内容都将由 React DOM 来管理,所以我们将其称之为 “根” DOM 节点。

我们将 元素传入一个名为 ReactDOM.render() 的方法来将其渲染到页面上:

const element = 

Hello, world

; ReactDOM.render( element, document.getElementById('root') );

Props&纯函数

上文中我们搁置了对Props的解释,只是暂且看作PHP函数的参数来理解,实际上也是如此。

对于函数声明的组件,props就是外部使用组件时,传入组件的参数

// Welcome组件通过props.name拿到调用者传入的数据
function Welcome(props) {
  return 

Hello, {props.name}

; } // 组件App 返回一个react元素,元素中使用了Welcome组件,并将name属性传入组件 function App() { return (
); } // 渲染App组件到DOM中 ReactDOM.render( , document.getElementById('root') ); /*output

Hello, Sara

*/

对于类声明的组件,props就是类的一个属性,通过这个属性能取到外部使用组件时,传入组件的一些必要数据

// Welcome组件通过this.props.name拿到调用者传入的数据
class Welcome extends React.Component {
  render() {
    return 

Hello, {this.props.name}

; } } // 组件App 返回一个react元素,元素中使用了Welcome组件,并将name属性传入组件 function App() { return (
); } // 渲染App组件到DOM中 ReactDOM.render( , document.getElementById('root') ); /*output

Hello, Sara

*/

当然这还不是全部,Props相比一般的参数,具有只读性,也就是其值不会被改变,这里正好引入了纯函数和非纯函数的概念

function sum(a, b) {
  return a + b;
}

类似于上面的这种函数称为“纯函数”,它没有改变它自己的输入值,当传入的值相同时,总是会返回相同的结果

与之相对的是非纯函数,它会改变它自身的输入值:

function withdraw(account, amount) {
  account.total -= amount;
}

React是非常灵活的,但它也有一个严格的规则:
所有的React组件必须像纯函数那样使用它们的props

State&生命周期

应用的界面是随时间动态变化的,State可以在不违反props规则的情况下,根据用户操作、网络响应、或者其他状态变化,使组件动态的响应并改变组件的输出

我们在定义组件的时候,虽然可以通过函数和类声明这两种方式,但是当涉及到局部状态、生命周期钩子等高级用法时,只能通过类的方式来声明组件

官网的例子写的非常详细,细致的说出了其演变,我重新再拜读下官网例举的定时器例子,并试着给代码用1、2、3标注执行的顺序,加上注释

class Clock extends React.Component {
  // 2. 组件被外部调用时,会优先执行构造函数,这里是初始化this.state的地方
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  // 4. 当组件返回的元素输出到屏幕时,调用此钩子设置一个定时器,每秒钟调用一次tick()
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  // 6. 一旦Clock组件被从DOM中移除,会调用这个钩子函数清除定时器
  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  /* 5. 调用tick()方法,通过使用包含当前时间的对象调用setState()来调度UI更新。
        通过调用setState() ,React 知道状态已经改变,并再次调用render()方法
        来确定屏幕上应当显示什么。这一次,render()方法中的this.state.date将不同,
        所以渲染输出将包含更新的时间,并相应地更新DOM。
  */
  tick() {
    this.setState({
      date: new Date()
    });
  }

  // 3. 随后render方法被调用,将Clock组件返回的元素输出到屏幕
  render() {
    return (
      

Hello, world!

It is {this.state.date.toLocaleTimeString()}.

); } } // 1. 渲染Clock组件 ReactDOM.render( , document.getElementById('root') );

我们可以在组件类上声明特殊的方法,当组件挂载或卸载时,来运行一些代码

上例中的componentDidMountcomponentWillUnmount这些方法被称作生命周期钩子,分别在组件输出到 DOM 后、组件从DOM中移除时执行

正确地使用状态

关于 setState() 这里有三件事情需要知道

  1. 不要直接更新状态

例如,此代码不会重新渲染组件:

// Wrong
this.state.comment = 'Hello';

应当使用 setState():

// Correct
this.setState({comment: 'Hello'});

构造函数是唯一能够初始化 this.state 的地方。

  1. 状态更新可能是异步的

React 可以将多个setState() 调用合并成一个调用来提高性能。

因为 this.propsthis.state 可能是异步更新的,你不应该依靠它们的值来计算下一个状态。

例如,此代码可能无法更新计数器:

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

要修复它,请使用第二种形式的 setState() 来接受一个函数而不是一个对象。 该函数将接收先前的状态作为第一个参数,将需要更新的值作为第二个参数:

// Correct
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

上方代码使用了箭头函数,但它也适用于常规函数:

// Correct
this.setState(function(prevState, props) {
  return {
    counter: prevState.counter + props.increment
  };
});
  1. 状态更新合并

当你调用 setState() 时,React 将你提供的对象合并到当前状态。例如,你的状态可能包含一些独立的变量:

  constructor(props) {
    super(props);
    this.state = {
      posts: [],
      comments: []
    };
  }

你可以调用 setState() 独立地更新它们:

  componentDidMount() {
    fetchPosts().then(response => {
      this.setState({
        posts: response.posts
      });
    });

    fetchComments().then(response => {
      this.setState({
        comments: response.comments
      });
    });
  }

这里的合并是浅合并,也就是说this.setState({comments})完整保留了this.state.posts,但完全替换了this.state.comments

你可能感兴趣的:(PHP的全栈之路3)