likes
comments
collection
share

Hello React(无构建版本)

作者站长头像
站长
· 阅读数 49

前言

本文记录了自己体验和认识React的笔记和体会:

  • 通过在网页上开发无构建版本的React组件的例子
  • 通过井字棋例子了解React组件是怎么创建与其主要的几个特性。

一、体验React(无构建版本)

在网页中添加React

  1. 加载React
  2. 加载React组件
  3. 将React组件渲染到指定dom容器内
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>hello react</title>
</head>

<body>
    <div id="like_button_container"></div>

    <!-- 加载 React。-->
    <!-- 注意: 部署时,将 "development.js" 替换为 "production.min.js"。-->
    <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>

    <!-- 加载我们的 React 组件。-->
    <script src="like_button.js"></script>

    <script type="text/javascript">
        const domContainer = document.querySelector('#like_button_container');
        ReactDOM.render(e(LikeButton), domContainer);
    </script>
</body>

</html>

like_button.js

'use strict';

const e = React.createElement;

class LikeButton extends React.Component {
    render() {
        return e(
            'button', {
                onClick: () => this.setState({
                    liked: true
                })
            },
            'Like'
        );
    }
}

效果: Hello React(无构建版本) 官方文档:在网站中添加 React

使用JSX(可选)

上面例子里LikeButton类里render方法 return 的是e(),也可如下用JSX编写

// 显示一个 "Like" <button>
return (
  <button onClick={() => this.setState({ liked: true })}>
    Like
  </button>
);

快速尝试JSX

如果想直接在上述例子里面使用JSX,需要加上babel和声明script是text/babel类型的

    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    <script src="like_button.js" type="text/babel"></script>

    <script type="text/babel">
        const e = React.createElement;
        const domContainer = document.querySelector('#like_button_container');
        ReactDOM.render(e(LikeButton), domContainer);
    </script>

PS:这种引入方式不适用于生产环境

在项目中添加JSX

安装

  • 步骤 1:  执行 npm init -y
  • 步骤 2:  执行 npm install babel-cli@6 babel-preset-react-app@3

运行 JSX 预处理器

在当前目录创建一个src文件夹,将写有JSX语法的js放进去

  • 执行 npx babel --watch src --out-dir . --presets react-app/prod

这个命令创建一个监听器,会监听src里面的源文件,如果带有JSX的文件有更新就会预处理这些文件(如src/like_button.js),生成转换处理后的like_button.js。(包含ES6转换成ES5)

src/like_button.js

'use strict';
class LikeButton extends React.Component {
    render(){
        return <button onClick={() => {this.setState({liked: true})}}> Like </button>
    }
}

预处理后的like_button.js

'use strict';

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var LikeButton = function (_React$Component) {
    _inherits(LikeButton, _React$Component);

    function LikeButton() {
        _classCallCheck(this, LikeButton);

        return _possibleConstructorReturn(this, (LikeButton.__proto__ || Object.getPrototypeOf(LikeButton)).apply(this, arguments));
    }

    _createClass(LikeButton, [{
        key: 'render',
        value: function render() {
            var _this2 = this;

            return React.createElement(
                'button',
                { onClick: function onClick() {
                        _this2.setState({ liked: true });
                    } },
                ' Like '
            );
        }
    }]);

    return LikeButton;
}(React.Component);

二、认识React

在上述无构建版本上,参考“认识React”里井字棋,写了一个简版的例子

官方文档:入门教程: 认识 React

井字棋例子

Hello React(无构建版本)

game.js源文件

'use strict';

const e = React.createElement;

// class Square  extends React.Component{
//     render() {
//        return <button className="square" onClick={()=>{this.props.onClick(this.props.i)}}>{this.props.val}</button>
//     }
// }

function Square(props){
    return <button className={"square " + (props.val == 'X'?'isX':'')} onClick={()=>{props.onClick(props.i)}}>{props.val}</button>
}

class Board extends React.Component{

    constructor(props) {
        super(props);
    }
    
    renderSquare(i){
        return  <Square onClick={(i)=>{this.props.onClick(i)}} i={i} val={this.props.squares[i]} ></Square>
    }
    
    render() {
        console.log('Board','render');
        const sq = this.props.squares;
        return <div>
            <div className="row">
                {this.renderSquare(0)}
                {this.renderSquare(1)}
                {this.renderSquare(2)}
            </div> 
            <div className="row">
                {this.renderSquare(3)}
                {this.renderSquare(4)}
                {this.renderSquare(5)}
            </div>
            <div className="row">
                <button className={"square " + (this.props.squares[6] == 'X'?'isX':'')} onClick={()=>{this.props.onClick(6)}}> {this.props.squares[6]}</button>
                <button className={"square " + (this.props.squares[7] == 'X'?'isX':'')} onClick={()=>{this.props.onClick(7)}}> {this.props.squares[7]}</button>
                <button className={"square " + (this.props.squares[8] == 'X'?'isX':'')} onClick={()=>{this.props.onClick(8)}}> {this.props.squares[8]}</button>
            </div>
        </div>
    }
}

class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = { 
        squares:Array(0).fill(null),
        stepNumber: 0,
        isXNext:true
     };
  }

  handleClick(i){
    console.log('handleClick',i);
    const sq = this.state.squares.slice();
    if (calculateWinner(sq)) {  //如果赢了就不能继续下了
        return; 
    }
    if(sq[i]) return;
    sq[i] = this.state.isXNext?'X':'O';
    
     //只有触发setState才会重新render
    this.setState({
        squares:sq,
        stepNumber:this.state.stepNumber+1,
        isXNext:!this.state.isXNext
    }) 
  }

  reset(){
    this.setState({
        squares:Array(0).fill(null),
        stepNumber:0,
        isXNext:true
    }) 
  }
  
  getTips() {
    const sq = this.state.squares;
    const winner = calculateWinner(sq);

    if (winner) {
      return <div>胜方:{winner}!</div>;
    }
    return <div>下一步出棋者:{this.state.isXNext?'X':'O'}</div>;
  }
  
  render() {
    console.log('Game','render');
    const sq = this.state.squares;

    return <div className="game">
        <div className="gameBorad">
           <div>井字棋(X先)</div>
           <Board squares={sq} onClick={(i)=>{this.handleClick(i)}}></Board>
           <div>已下{this.state.stepNumber}步</div>
           {this.getTips()}
           <button onClick={()=>this.reset()}>重置</button>
        </div>
    </div>
  }
}

// Find all DOM containers, and render Like buttons into them.
document.querySelectorAll('.game')
  .forEach(domContainer => {
    ReactDOM.render(
      e(Game),
      domContainer
    );
  });

function calculateWinner(squares) {
    const lines = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6]
    ];
    for (let i = 0; i < lines.length; i++) {
      const [a, b, c] = lines[i];
      if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
        return squares[a];
      }
    }
    return null;
  }

game.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <title>Add React in One Minute</title>
    <style>
        button.square {
            width: 50px;
            height: 50px;
            color: blue;
        }

        button.square.isX {
            color: red;
        }

        .row {
            display: flex;
        }
    </style>
</head>

<body>

    <div class="game"></div>

    <!-- Load React. -->
    <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>

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

</body>

</html>

体会

React.Component组件

  • render()函数 - 必须的,返回用于渲染的“动态”HTML的React元素
  • this.props - 用于传递数据,props为接收的参数(如vue的props)
  • this.state - 该组件的状态数据(类比vue的data)

state数据 & 不可变性

我一开始尝试直接修改this.state里的数据,没有调this.setState(),视图没有重新渲染,只有调了setState视图才会刷新。

每次在组件中调用 setState 时,React 都会自动更新其子组件。

例子里都没有直接修改this.state的数据,而是直接“拷贝”一个新的数据,即保持"不可变性",文档中提到(不直接修改数据)的好处有以下:

  • 简化复杂的功能(如可以让我们追溯并复用游戏的历史记录)
  • 跟踪数据的变化(容易跟踪数据是否变化,无需遍历对象树)
  • 确定在 React 中何时重新渲染(最主要优势:帮助创建pure components,更轻松的发现数据是否发生变化,从而确定何时渲染)

—— 暂时我还不了解pure components,不过我理解”不可变性”就是为了更轻松更直接的知道数据发生了变化。

函数式组件

组件还有另一种写法,如Square 组件,是一个函数组件,参数为props,函数实现render()方法。

function Square(props) {
  return (
    <button className="square" onClick={props.onClick}>
      {props.value}
    </button>
  );
}

注意事项:onClick={()=>{}}

这里我也犯了类似错误,特别记一下,需要使用箭头函数来进行事件处理 Hello React(无构建版本)

最后

近几年我接触的项目大多基于Vue框架,相比这次React入门教程中看到的,我的感受是

  • React JSX模版里写法比较接近原生JS,没有像Vue一样直接加在元素上的指令(v-if等)
  • React 通过数据不可变性监听state数据的变化,Vue是通过对数据劫持监听
  • Vue是双向数据绑定的,而React是单项

PS:此文是基于入门教程自己的笔记和体会,也许文字描述不一定是准确的,有建议和问题望提出,谢谢!~

转载自:https://juejin.cn/post/7152735039514476557
评论
请登录