目录
1. react使用方式
不使用构建工具,直接在现有页面上使用react组件
<!-- ... 其它 HTML ... -->
<!-- 加载 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>
//加入下条代码可使用JSX,该方法会使网站速度变慢,不适用于生产环境。可以使用JSX 预处理器
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 加载我们的 React 组件。-->
<script src="like_button.js"></script>
</body>
安装 JSX 预处理器
- 执行
npm init -y
(如果失败,这是修复办法) - 执行
npm install babel-cli@6 babel-preset-react-app@3
运行 JSX 预处理器
- 创建一个名为 src 的文件夹并执行这个终端命令:
npx babel --watch src --out-dir . --presets react-app/prod
当你编辑带有 JSX 的源文件时,转换过程将自动重新执行。
使用构建工具
- 学习 React 或创建一个新的单页应用,请使用
Create React App
这里是引用
```javascript
npx create-react-app my-app
cd my-app
npm start
```
- 用 Node.js 构建服务端渲染的网站,试试
Next.js
- 构建面向内容的静态网站,试试
Gatsby
- 打造组件库或将 React 集成到现有代码仓库:
Neutrino
把 webpack 的强大功能和简单预设结合在一起。并且包括了 React 应用和 React 组件的预设。Parcel
是一个快速的、零配置的网页应用打包器,并且可以搭配 React 一起工作。Razzle
是一个无需配置的服务端渲染框架,但它提供了比 Next.js 更多的灵活性。
2. JSX
- JavaScript的语法扩展
React.createElement()
的语法糖,简化写法- JSX也是一个表达式,可以在
if
语句和for
循环的代码块中使用 JSX,将 JSX 赋值给变量,把 JSX 当作参数传入,以及从函数中返回 JSX: - 使用引号
""
,来将属性值指定为字符串字面量;使用大括号{}
,来在属性值中插入一个 JavaScript 表达式;(对于同一属性不能同时使用这两种符号) - 使用
camelCase
(小驼峰命名)来定义属性的名称 class
替换为className
;for
替换为htmlFor
- JSX 能防止注入攻击;
React DOM
在渲染所有输入内容之前,默认会进行转义。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止XSS
(cross-site-scripting, 跨站脚本)攻击。
3. 元素渲染
- 元素是构成 React 应用的最小砖块。
- 与浏览器的 DOM 元素不同,React 元素是创建开销极小的普通对象。React DOM 会负责更新 DOM 来与 React 元素保持一致。
- 想要将一个 React 元素渲染到根 DOM 节点中,只需把它们一起传入
ReactDOM.render()
:const element = <h1>Hello, world</h1>; ReactDOM.render(element, document.getElementById('root'));
- React DOM 会将元素和它的子元素与它们之前的状态进行比较,并只会进行必要的更新来使 DOM 达到预期的状态。
4. 组件 & Props
组件,从概念上类似于 JavaScript
函数。它接受任意的入参(即 “props
”),并返回用于描述页面展示内容的 React
元素。
定义组件的两种方式:
- 函数组件
接收唯一带有数据的 “props
”(代表属性)对象与并返回一个React
元素function Welcome(props) { return <h1>Hello, { props.name}</h1>; }
- class组件 (ES6里的class)
class Welcome extends React.Component { render() { return <h1>Hello, { this.props.name}</h1>; } }
注意: 组件名称必须以大写字母开头
当 React
元素为用户自定义组件时,它会将 JSX
所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props
”。
// 这段代码会在页面上渲染 “Hello, Sara”:
function Welcome(props) {
return <h1>Hello, {
props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
- 组件可以在其输出中引用其他组件
Props 的只读性
- 组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的
props
- 所有 React 组件都必须像纯函数一样保护它们的
props
不被更改。
5. State & 生命周期
State
与 props
类似,但是 state
是私有的,并且完全受控于当前组件。
使用state
, 要将函数组件转换成class
组件
案例:定时器
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
date: new Date()};
}
// componentDidMount() 方法会在组件已经被渲染到 DOM 中后运行
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
// 组件被删除的时候, 执行该方法
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {
this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
使用state
的注意事项:
- 不能直接修改
state
的值,要使用setState()
- 构造函数是唯一可以给
this.state
赋值的地方 - State 的更新可能是异步的
出于性能考虑,React
可能会把多个setState()
调用合并成一个调用。
因为this.props
和this.state
可能会异步更新,所以你不要依赖他们的值来更新下一个状态。
解决方法:// 此代码可能会无法更新计数器 this.setState({ counter: this.state.counter + this.props.increment, }); // 可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数 this.setState((state, props) => ({ counter: state.counter + props.increment }));
State
的更新会被合并;可以使用setState()
来单独更新state
里的独立的变量- 数据是向下流动的
组件可以选择把它的state
作为props
向下传递到它的子组件中:
但是子组件本身无法知道传递下来的值是来自于父组件的state
或props
,还是手动输入的。
6. 事件处理
React
事件的命名采用小驼峰式(camelCase
),而不是纯小写。- 使用
JSX
语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。// React中要这样 <button onClick={ activateLasers}> Activate Lasers </button>
- 在 React 中,不能通过返回
false
的方式阻止默认行为// 传统html中阻止默认事件 <a href="#" onclick="console.log('The link was clicked.'); return false"> Click me </a> // react中阻止默认事件,要使用e.preventDefault(); function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('The link was clicked.'); } return ( <a href="#" onClick={ handleClick}> Click me </a> ); }
- 当你使用
ES6 class
语法定义一个组件的时候,通常的做法是将事件处理函数声明为class
中的方法 class
的方法默认不会绑定this
// 1. 可以在构造函数中通过bind()绑定this constructor(props) { // ... this.changeTheme = this.changeTheme.bind(this) } // 2. 可以使用实验性的 public class fields 语法 class LoggingButton extends React.Component { // 此语法确保 `handleClick` 内的 `this` 已被绑定。 // 注意: 这是 *实验性* 语法。 handleClick = () => { console.log('this is:', this); } render() { return ( <button onClick={ this.handleClick}> Click me </button> ); } } // 3. 可以在回调中使用箭头函数: // 注意: 如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染 // 建议使用前2种方法 class LoggingButton extends React.Component { handleClick() { console.log('this is:', this); } render() { // 此语法确保 `handleClick` 内的 `this` 已被绑定。 return ( <button onClick={ () => this.handleClick()}> Click me </button> ); } }
- 向事件处理程序传递参数
// 通过箭头函数实现 // 如果通过箭头函数的方式,事件对象e必须显式的进行传递 <button onClick={ (e) => this.deleteRow(id, e)}>Delete Row</button> // 通过Function.prototype.bind 实现 // 通过 bind 的方式,事件对象e以及更多的参数将会被隐式的进行传递 <button onClick={ this.deleteRow.bind(this, id)}>Delete Row</button>
7. 条件渲染
- 运算符
if
声明一个变量并使用 if 语句进行条件渲染 - 与运算符
&&
true && expression
总是会返回expression
, 而false && expression
总是会返回 false
如果条件是 true,&& 右侧的元素就会被渲染,如果是 false,React 会忽略并跳过它。 - 三目运算符
? :
阻止组件渲染:
让render
方法直接返回 null
,就能不进行任何渲染。
在组件的 render
方法中返回 null
并不会影响组件的生命周期
8. 列表 & Key
- 基础列表组件
function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => <li key={ number.toString()}>{ number}</li> ); return ( <ul>{ listItems}</ul> ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( <NumberList numbers={ numbers} />, document.getElementById('root') );
key
key
帮助React
识别哪些元素改变了,比如被添加或删除- 一个元素的
key
最好是这个元素在列表中拥有的一个独一无二的字符串(如id
) - 当元素没有确定
id
的时候,万不得已你可以使用元素索引index
作为key
如果列表项目的顺序变化,会导致性能变差,还可能引起组件状态的问题 - 如果你选择不指定显式的
key
值,那么React
将默认使用索引用作为列表项目的key
值 - 元素的
key
只有放在就近的数组上下文中才有意义
使用一个组件,把key
放在组件上,而不是组件里面的元素上
一个好的经验法则是:在map()
方法中的元素需要设置key
属性 key
只是在兄弟节点之间必须唯一key
会传递信息给React
,但不会传递给你的组件
9. 表单
受控组件
- 在 HTML 中,表单元素(如
<input>
,<textarea>
,<select>
)之类的表单元素通常自己维护state
,并根据用户输入进行更新。 - 而在
React
中,可变状态(mutable state)通常保存在组件的state
属性中,并且只能通过使用setState()
来更新。
我们可以把两者结合起来,使 React
的 state
成为“唯一数据源”。
渲染表单的 React
组件还控制着用户输入过程中表单发生的操作。
被 React
以这种方式控制取值的表单输入元素就叫做“受控组件
”。
<input type="text">
, <textarea>
和 <select>
之类的标签都非常相似,它们都接受一个 value
属性,你可以使用它来实现受控组件。
<input type="text" value={
this.state.value} onChange={
this.handleChange} />
<textarea value={
this.state.value} onChange={
this.handleChange} />
<select value={
this.state.value} onChange={
this.handleChange}>
<option value="grapefruit">葡萄柚</option>
<option value="lime">酸橙</option>
<option value="coconut">椰子</option>
<option value="mango">芒果</option>
</select>
// 将数组传递到 value 属性中,以支持在 select 标签中选择多个选项
<select multiple={
true} value={
['B', 'C']}>
<input type="file">
允许用户从存储设备中选择一个或多个文件,将其上传到服务器,或通过使用 JavaScript
的File API
进行控制。
因为它的 value
只读,所以它是 React
中的一个非受控组件
当需要处理多个 input
元素时,我们可以给每个元素添加 name
属性,并让处理函数根据 event.target.name
的值选择要执行的操作。
使用 ES6 计算属性名称的语法更新给定输入名称对应的 state
值
// 使用了 ES6 计算属性名称的语法
this.setState({
[name]: value
});
// 等同 ES5:
var partialState = {
};
partialState[name] = value;
this.setState(partialState);
Formik :
https://formik.org/
包含验证、追踪访问字段以及处理表单提交的完整解决方案
10. 状态提升
通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去。
11. 组合 vs 继承
- 包含关系
<FancyBorder>
JSX 标签中的所有内容都会作为一个children prop
传递给FancyBorder
组件。因为FancyBorder
将{props.children}
渲染在一个<div>
中,被传递的这些子组件最终都会出现在输出结果中。
function FancyBorder(props) {
return (
<div className={
'FancyBorder FancyBorder-' + props.color}>
{
props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
也可以自行约定输入内容
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{
props.left}
</div>
<div className="SplitPane-right">
{
props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
注意:组件可以接受任意 props
,包括基本数据类型,React
元素以及函数。
如果你想要在组件间复用非 UI 的功能,我们建议将其提取为一个单独的 JavaScript
模块,如函数、对象或者类。组件可以直接引入(import
)而无需通过extend
继承它们。
12. React 哲学
未完待续。。。