React笔记(一)
React 介绍
- React 是一个用于构建用户界面的渐进式 JavaScript 库
- 本身只处理 UI
- 不关系路由
- 不处理 ajax
- React主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。
- 数据驱动视图
- React 由 Facebook 开发
- 第一个真生意义上把组件化思想待到前端开发领域
- angular 早期没有组件化思想
- 后来也被 Vue 学习借鉴了
React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。
由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。
这个项目本身也越滚越大,从最早的UI引擎变成了一整套前后端通吃的 Web App 解决方案。衍生的 React Native 项目,目标更是宏伟,希望用写 Web App 的方式去写 Native App。如果能够实现,整个互联网行业都会被颠覆,因为同一组人只需要写一次 UI ,就能同时运行在服务器、浏览器和手机(参见《也许,DOM 不是答案》)。
-
数据驱动视图
-
组件化
-
路由
-
React
- 对技术要求比较高
- 编程性更好一些,更底层,更灵活
- 可玩儿性更高
-
Vue 7.5 w
React 特点
- 组件化
- 高效
- 虚拟 DOM
- Vue 2 也是虚拟 DOM
- 虚拟 DOM 更高效
- 灵活
- 渐进式,本身只处理 UI ,可以和你的其它技术栈组合到一起来使用
- 声明(配置)式设计
data
响应式数据mathods
处理函数- 这样做的好处就是按照我们约定好的方式来开发,所有人写出来的代码就像一个人写的
state
- 方法就是类成员
- 也有特定的组件生命钩子
- JSX
- 一种预编译 JavaScript 语言,允许让你的 JavaScript 和 HTML 混搭
- 模板中就是 JavaScript 逻辑
- 单向数据流
- 组件传值
- 所有数据都是单向的,组件传递的数据都是单向
- Vue 也是单向数据流
- 没有双向数据绑定
React 的发展历史
- Facebook 内部用来开发 Instagram
- 2013 年开源了 React
- 随后发布了 React Native
- React 开源协议
- 知乎专栏 -React 的许可协议到底发生了什么问题?
- 知乎 - 如何看待 Facebook 计划将 React 改为 MIT 许可证?
- 阮一峰 - 开源许可证教程
- 阮一峰 - 如何选择开源许可证
- React 最后架不住社区的压力,最后还是修改了许可协议条款。我分享,我骄傲。
React 与 Vue 的对比
技术层面
- Vue 生产力更高(更少的代码实现更强劲的功能)
- React 更 hack 技术占比比较重
- 两个框架的效率都采用了虚拟 DOM
- 性能都差不多
- 组件化
- Vue 支持
- React 支持
- 数据绑定
- 都支持数据驱动视图
- Vue 支持表单控件双向数据绑定
- React 不支持双向数据绑定
- 它们的核心库都很小,都是渐进式 JavaScript 库
- React 采用 JSX 语法来编写组件
- Vue 采用单文件组件
template
script
style
开发团队
- React 由 Facebook 前端维护开发
- Vue
- 早期只有尤雨溪一个人
- 由于后来使用者越来越多,后来离职专职开发维护
- 目前也有一个小团队在开发维护
社区
- React 社区比 Vue 更强大
- Vue 社区也很强大
Native APP 开发
- React Native
- 可以原生应用
- React 结束之后会学习
- Weex
- 阿里巴巴内部搞出来的一个东西,基于 Vue
相关资源链接
- React 官网
- 官方教程
- 连字游戏
- 官方文档
- 基础教程
- 高级教程
- API 参考文档
- React - GitHub
- 阮一峰 - React 技术栈系列教程
- [阮一峰 - React 入门实例教程](http://www.ruanyifeng.com/blog/2015/03/react.html
- awesome react
- awesome-react-components
EcmaScript 6 补充
class
React 核心概念
组件化
虚拟 DOM
起步
https://reactjs.org/docs/hello-world.html
初始化及安装依赖
$ mkdir react-demos
$ cd react-demos
$ npm init --yes
$ npm install --save babel-standalone react react-dom
Hello World
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo - Hello World</title>
<script src="../node_modules/babel-standalone/babel.min.js"></script>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
ReactDOM.render(
<h1>Hello, react!</h1>,
document.getElementById('root')
)
</script>
</body>
</html>
JSX
HTML 语言直接写在 JavaScript 语言中,不加任何引号,这就是 JSX 语法。它允许 HTML 与 JavaScript 的混写。
环境配置
- 非模块化环境
babel-standalone
- 模块化环境
babel-preset-react
- Babel REPL 赋值查看编译结果
基本语法规则
-
必须只能有一个根节点
-
遇到 HTML 标签 (以
<
开头) 就用 HTML 规则解析- 单标签不能省略结束标签。
-
遇到代码块(以
{
开头),就用 JavaScript 规则解析 -
JSX 允许直接在模板中插入一个 JavaScript 变量
- 如果这个变量是一个数组,则会展开这个数组的所有成员添加到模板中
-
单标签必须结束
/>
基本语法:
const element = <h1>Hello, world!</h1>;
在 JSX 中嵌入 JavaScript 表达式
- 语法
- 如果 JSX 写到了多行中,则建议包装括号避免自动分号的陷阱
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);
ReactDOM.render(
element,
document.getElementById('root')
);
在 JavaScript 表达式中嵌入 JSX
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
JSX 中的节点属性
- 动态绑定属性值
class
使用className
tabindex
使用tabIndex
for
使用htmlFor
普通的属性:
const element = <div tabIndex="0"></div>;
在属性中使用表达式:
const element = <img src={user.avatarUrl}></img>;
声明子节点
如果标签是空的,可以使用 />
立即关闭它。
const element = <img src={user.avatarUrl} />;
JSX 子节点可以包含子节点:
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
JSX 自动阻止注入攻击
const element = <div>{'<h1>this is safe</h1>'}</div>
在 JSX 中使用注释
写法一:
{
// 注释
// ...
}
写法二(单行推荐):
{/* 单行注释 */}
写法三(多行推荐):
{
/*
* 多行注释
*/
}
JSX 原理
Babel 会把 JSX 编译为 React.createElement()
函数。
- 每个 React 元素都是一个真实的 JavaScript 对象
下面两种方式是等价的:
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
// Note: this structure is simplified
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world'
}
};
列表循环
JSX 允许直接在模板插入 JavaScript 变量。如果这个变量是一个数组,则会展开这个数组的所有成员。
var arr = [
<h1>Hello world!</h1>,
<h2>React is awesome</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
综上所述,我们可以这样:
var names = ['Alice', 'Emily', 'Kate'];
ReactDOM.render(
<div>
{
names.map(function (name) {
return <div>Hello, {name}!</div>
})
}
</div>,
document.getElementById('example')
);
DOM Elements
参考文档:https://reactjs.org/docs/dom-elements.html
列表渲染
参考文档:https://reactjs.org/docs/lists-and-keys.html
语法高亮
http://babeljs.io/docs/editors
条件渲染
参考文档:https://reactjs.org/docs/conditional-rendering.html
示例1:
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
// Try changing to isLoggedIn={true}:
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);
示例2:
function LoginButton(props) {
return (
<button onClick={props.onClick}>
Login
</button>
);
}
function LogoutButton(props) {
return (
<button onClick={props.onClick}>
Logout
</button>
);
}
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button = null;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
示例3(行内判断):
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
示例4(if-else):
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
);
}
示例5(阻止组件渲染):
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true}
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(prevState => ({
showWarning: !prevState.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Hide' : 'Show'}
</button>
</div>
);
}
}
ReactDOM.render(
<Page />,
document.getElementById('root')
);
事件处理
参考文档:https://reactjs.org/docs/handling-events.html
示例1
<button οnclick="activateLasers()">
Activate Lasers
</button>
<button onClick={activateLasers}>
Activate Lasers
</button>
示例2
<a href="#" onclick="console.log('The link was clicked.'); return false">
Click me
</a>
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
示例3(this 绑定问题)
class Toggle extends React.Component {
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);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
箭头函数:
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
// Warning: this is *experimental* syntax.
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
更简单的方式:
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}
示例4(传递参数)
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
Class 和 Style
class:
<div className="before" title="stuff" />
classNames:
style:
<div style={{color: 'red', fontWeight: 'bold'}} />
表单处理
参考文档:https://reactjs.org/docs/forms.html
组件
React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件。
组件规则注意事项
- 组件类的第一个首字母必须大写
- 组件类必须有
render
方法 - 组件类必须有且只有一个根节点
- 组件属性可以在组件的
props
获取- 函数需要声明参数:
props
- 类直接通过
this.props
- 函数需要声明参数:
-
函数式组件(无状态)
- 名字不能用小写
- React 在解析的时候,是以标签的首字母来区分的
- 如果首字母是小写则当作 HTML 来解析
- 如果首字母是大小则当作组件来解析
- 结论:组件首字母必须大写
类方式组件(有状态)
class ShoppingList extends React.Component {
render() {
return (
<div className="shopping-list">
<h1>Shopping List for {this.props.name}</h1>
<ul>
<li>Instagram</li>
<li>WhatsApp</li>
<li>Oculus</li>
</ul>
</div>
);
}
}
// Example usage: <ShoppingList name="Mark" />
本质:
return React.createElement('div', {className: 'shopping-list'},
React.createElement('h1', /* ... h1 children ... */),
React.createElement('ul', /* ... ul children ... */)
);
组件传值 Props
EcmaScript 5 构造函数:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
EcmaScript 6 Class:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
this.props.children
参考文档:https://reactjs.org/docs/react-api.html#reactchildren
this.props
对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children
属性。
它表示组件的所有子节点。
this.props.children
的值有三种可能:如果当前组件没有子节点,它就是 undefined
;如果有一个子节点,数据类型是 object
;如果有多个子节点,数据类型就是 array
。所以,处理 this.props.children
的时候要小心。
React 提供一个工具方法 React.Children
来处理 this.props.children
。我们可以用 React.Children.map
来遍历子节点,而不用担心 this.props.children
的数据类型是 undefined
还是 object
。
组件状态 State
参考文档:https://reactjs.org/docs/state-and-lifecycle.html
组件生命周期
参考文档:https://reactjs.org/docs/state-and-lifecycle.html
完整生命周期 API:https://reactjs.org/docs/react-component.html#the-component-lifecycle
PropTypes 类型校验
参考文档:https://reactjs.org/docs/typechecking-with-proptypes.html
组件的属性可以接受任意值,字符串、对象、函数等等都可以。有时,我们需要一种机制,验证别人使用组件时,提供的参数是否符合要求。
示例:
import PropTypes from 'prop-types';
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Greeting.propTypes = {
name: PropTypes.string
};
Default Prop Values
参考文档:https://reactjs.org/docs/typechecking-with-proptypes.html#default-prop-values
示例:
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
// Specifies the default values for props:
Greeting.defaultProps = {
name: 'Stranger'
};
// Renders "Hello, Stranger":
ReactDOM.render(
<Greeting />,
document.getElementById('example')
);
或者:
class Greeting extends React.Component {
static defaultProps = {
name: 'stranger'
}
render() {
return (
<div>Hello, {this.props.name}</div>
)
}
}
和服务端交互
组件的数据来源,通常是通过 Ajax 请求从服务器获取,可以使用 componentDidMount
方法设置 Ajax 请求,等到请求成功,再用 this.setState
方法重新渲染 UI 。
获取真实 DOM 节点
参考文档:https://reactjs.org/docs/refs-and-the-dom.html
组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。
但是,有时需要从组件获取真实 DOM 的节点,这时就要用到 ref
属性。
示例:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// Explicitly focus the text input using the raw DOM API
this.textInput.focus();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in an instance field (for example, this.textInput).
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
TodoMVC
开始
下载模板:
git clong https://github.com/tastejs/todomvc-app-template.git --depth=1 todomvc-react
安装依赖:
cd todomvc-react
npm install
安装 react
开发环境依赖:
npm install --save babel-standalone react react-dom
React 其它
React DevTools
https://github.com/facebook/react-devtools