Hello React(无构建版本)
前言
本文记录了自己体验和认识React
的笔记和体会:
- 通过在网页上开发无构建版本的
React
组件的例子 - 通过井字棋例子了解
React
组件是怎么创建与其主要的几个特性。
一、体验React(无构建版本)
在网页中添加React
- 加载React
- 加载React组件
- 将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'
);
}
}
效果:
官方文档:在网站中添加 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
井字棋例子

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={()=>{}}
这里我也犯了类似错误,特别记一下,需要使用箭头函数来进行事件处理
最后
近几年我接触的项目大多基于Vue
框架,相比这次React
入门教程中看到的,我的感受是
React JSX
模版里写法比较接近原生JS,没有像Vue一样直接加在元素上的指令(v-if等)- React 通过数据不可变性监听
state
数据的变化,Vue是通过对数据劫持监听 - Vue是双向数据绑定的,而React是单项的
PS:此文是基于入门教程自己的笔记和体会,也许文字描述不一定是准确的,有建议和问题望提出,谢谢!~
转载自:https://juejin.cn/post/7152735039514476557