React学习
介绍
React 是一个用于构建用户界面的 JavaScript 库。
React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。
React 拥有较高的性能,代码逻辑非常简单。
特点
1.声明式设计 −React采用声明范式,可以轻松描述应用。
2.高效 −React通过对DOM的模拟【采用虚拟DOM】,最大限度地减少与DOM的交互。
3.灵活 −React可以与已知的库或框架很好地配合。
4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
创建新的 React 应用
- 确保你安装了较新版本的 Node.js。
- 按照 Create React App 安装指南创建一个新的项目:
npx create-react-app my-app
- npm start
create-react-app 是来自于 Facebook,通过该命令我们无需配置就能快速构建 React 开发环境。
create-react-app 自动创建的项目是基于 Webpack + ES6 。
教程
React 可以在浏览器运行,也可以在服务器运行,服务器的用法与浏览器差别不大。
react.js引用
有两种方式:本地文件 或者引用CDN链接:
- 下载文件到本地
在react官网下载 react.js脚本 的方法介绍
1、访问react的github官方页面
访问地址为:http://react-cn.github.io/react/downloads.html
2、点击Download页面中的"Download Starter Kit"按钮,进行下载
HTML模板
使用 React 的网页源码,结构大致如下:
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/babel.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> // ** Our code goes here! ** </script> </body> </html>
最后一个 <script>
标签的 type
属性为 text/babel
。这是因为 React 独有的 JSX 语法,跟 JavaScript 不兼容【React 使用 JSX 来替代常规的 JavaScript,但jsx使用的是ES6b标准,而目前很多浏览器仍然只支持ES5,所以我们就需要将jsx转成普通js】。
凡是使用 JSX 的地方,则<script>的type属性="text/babel"
。
上面代码一共用了三个库: react.js
、react-dom.js
和 Browser.js
,它们必须首先加载。其中,
react.js
是 React 的核心库,react-dom.js
是提供与 DOM 相关的功能,babel.js
的作用是将 JSX 语法转为 JavaScript 语法,这一步很消耗时间,实际上线的时候,应该将它放到服务器完成。
babel src --out-dir build
上面命令可以将 src
子目录的 js
文件进行语法转换,转码后的文件全部放在 build
子目录。
Babel
参考 什么是 Babel?
使用:https://unpkg.com/@babel/standalone/babel.min.js (用法)
ReactDOM.render()
ReactDOM.render 是 React 的最基本方法,用于将模板转为 HTML 语言,并插入指定的 DOM 节点。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <script src="../build/react.min.js"></script> <script src="../build/react-dom.min.js"></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example') ); </script> </body> </html>
JSX 简介
JSX 是一个jacascript的语法扩展。
特点:
HTML 语言直接写在 JavaScript 语言之中,不加任何引号,它允许HTML与JS的混写。
JSX 的基本语法规则:遇到 HTML 标签(以 <
开头),就用 HTML 规则解析;遇到代码块(以 {
开头),就用 JavaScript 规则解析。
JSX 允许直接在模板插入 JavaScript 变量;允许使用Javascript表达式,表达式写在{}中;在 JSX 中不能使用 if else 语句,但可以使用 conditional (三元运算) 表达式来替代。
<script type="text/babel"> var names=['peter','yong','kate'] ReactDOM.render( <div> { names.map(function(name,index){ return <div key={index}>Hello ,{name} ! </div> }) } </div>, document.getElementById('example') ); </script>
我们不需要一定使用 JSX,但它有以下优点:
- JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
- 它是类型安全的,在编译过程中就能发现错误。
- 使用 JSX 编写模板更加简单快速。
独立文件
JSX代码可以放在一个独立的文件.js中,在html中引入即可
<script type="text/babel" src="helloworld_react.js"></script>
组件
React 的核心是组件。v16.8 版本之前,组件的标准写法是类(class)。
React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件。React.createClass 方法就用于生成一个组件类,所有组件类都必须有自己的 render
方法,用于输出组件。
注意,组件类的第一个字母必须大写,否则会报错,比如HelloMessage
不能写成helloMessage
。另外,组件类只能包含一个顶层标签,否则也会报错。
用法:
组件的用法与原生的 HTML 标签完全一致,可以任意加入属性,比如 <HelloMessage name="John">
,就是 HelloMessage
组件加入一个 name
属性,值为 John
。组件的属性可以在组件类的 this.props
对象上获取,比如 name
属性就可以通过 this.props.name
读取。
下面是一个简单的组件类。
var HelloMessage = React.createClass({ render: function() { return <h1>Hello {this.props.name}</h1>; } }); ReactDOM.render( <HelloMessage name="John" />, document.getElementById('example') );
注意:添加组件属性,有一个地方需要注意,就是 class
属性需要写成 className
,for
属性需要写成 htmlFor
,这是因为 class
和 for
是 JavaScript 的保留字。
组件除了有render方法外,还有:
- PropTypes属性:验证别人使用组件时,提供的参数是否符合要求;
- getDefaultProps方法:可以用来设置组件属性的默认值。
组件类的几个缺点。
- 大型组件很难拆分和重构,也很难测试。
- 业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。
- 组件类引入了复杂的编程模式,比如 render props 和高阶组件。
函数组件
组件的最佳写法应该是函数,而不是类。
React 早就支持函数组件,下面就是一个例子:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
但是,这种写法有重大限制,必须是纯函数,不能包含状态,也不支持生命周期方法,因此无法取代类。
React Hooks 的设计目的,就是加强版函数组件,完全不使用"类",就能写出一个全功能的组件。
更多参考 React Hooks
this.props.children
this.props
对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children
属性。它表示组件的所有子节点
<body> <div id="example"></div> <script type="text/babel"> var NodeList=React.createClass({ render:function(){ return ( <ol> { React.Children.map(this.props.children , function(child){ return <li> {child}</li>; }) } </ol> ); } }); ReactDOM.render( <NodeList> <span>hello</span> <span>world</span> </NodeList>, document.getElementById('example') ) </script> </body>
获取真实的DOM节点
组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。
根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。
但是,有时需要从组件获取真实 DOM 的节点,这时就要用到 ref
属性:
<script type="text/babel"> var MyComponent=React.createClass({ handleClick:function(){ this.refs.myTextInput.focus(); alert(this.refs.myTextInput.value); }, render:function(){ return ( <div> <input type='text' ref='myTextInput'/> <input type='button' value='Focus the text input' onClick={this.handleClick}/> </div> ); } }); ReactDOM.render( <MyComponent />, document.getElementById('example') ) </script>
组件 MyComponent
的子节点有一个文本输入框,用于获取用户的输入。这时就必须获取真实的 DOM 节点,虚拟 DOM 是拿不到用户输入的。为了做到这一点,文本输入框必须有一个 ref
属性,然后 this.refs.[refName]
就会返回这个真实的 DOM 节点。
this.state
组件免不了要与用户互动,React 的一大创新,就是将组件看成是一个状态机,一开始有一个初始状态,然后用户互动,导致状态变化,从而触发重新渲染 UI
注意:由于 this.props
和 this.state
都用于描述组件的特性,可能会产生混淆。一个简单的区分方法是,this.props
表示那些一旦定义,就不再改变的特性,而 this.state
是会随着用户互动而产生变化的特性。
<script type="text/babel"> var LikeButton=React.createClass({ getInitialState:function() { return {liked: false}; }, handleClick:function(){ this.setState({liked: !this.state.liked}); }, render:function(){ var text =this.state.liked ? 'like':'haven\'t liked'; return ( <p onClick={this.handleClick}> You {text} this. Click to toggle. </p> ); } }); ReactDOM.render( <LikeButton />, document.getElementById('example') ) </script>
表单
用户在表单填入的内容,属于用户跟组件的互动,所以不能用 this.props
读取。
文本输入框的值,不能用 this.props.value
读取,而要定义一个 onChange
事件的回调函数,通过 event.target.value
读取用户输入的值。
<script type="text/babel"> var Input=React.createClass({ getInitialState:function() { return {value: 'hello !'}; }, handleChange:function(){ this.setState({value: event.target.value}); }, render:function(){ var value =this.state.value; return ( <div> <input type='text' value={value} onChange={this.handleChange} /> <p> {value}</p> </div> ); } }); ReactDOM.render( <Input />, document.getElementById('example') ) </script>
组件的生命周期及Ajax
组件的数据来源,通常是通过 Ajax 请求从服务器获取,可以使用 componentDidMount
方法设置 Ajax 请求,等到请求成功,再用 this.setState
方法重新渲染 UI
<script type="text/babel"> var UserGist=React.createClass({ getInitialState:function() { return { username:'', LastGistUrl:'' }; }, componentDidMount:function(){ $.get(this.props.source,function(result){ var lastGist = result[0]; console.log(lastGist); if(this.isMounted()){ this.setState({ username:lastGist.owner.login, lastGistUrl:lastGist.html_url }); } }.bind(this)); }, render:function(){ return ( <div> {this.state.username}'s last gist is <a href={this.state.lastGistUrl}> here </a> </div> ); } }); ReactDOM.render( <UserGist source='https://api.github.com/users/octocat/gists' />, document.getElementById('example') ) </script>
React Hooks
React v16.8 版本引入了全新的 API,叫做 React Hooks,颠覆了以前的用法。
React 的两套 API
以前,React API 只有一套,现在有两套:类(class)API 和基于函数的钩子(hooks) API
任何一个组件,可以用类来写,也可以用钩子来写。下面是类的写法:
class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
再来看钩子的写法,也就是函数:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
官方推荐使用钩子(函数),而不是类。因为钩子更简洁,代码量少,用起来比较"轻",而类比较"重"。而且,钩子是函数,更符合 React 函数式的本质。
类和函数的差异
严格地说,类组件和函数组件是有差异的。不同的写法,代表了不同的编程方法论。
- 类(class)是数据和逻辑的封装。 也就是说,组件的状态和操作方法是封装在一起的。如果选择了类的写法,就应该把相关的数据和操作,都写在同一个 class 里面。
- 函数一般来说,只应该做一件事,就是返回一个值。 如果你有多个操作,每个操作应该写成一个单独的函数。而且,数据的状态应该与操作方法分离。根据这种理念,React 的函数组件只应该做一件事情:返回组件的 HTML 代码,而没有其他的功能。
这种只进行单纯的数据计算(换算)的函数,在函数式编程里面称为 "纯函数"(pure function)。
副效应是什么?
如果纯函数只能进行数据计算,那些不涉及计算的操作(比如生成日志、储存数据、改变应用状态等等)应该写在哪里呢?
函数式编程将那些跟数据计算无关的操作,都称为 "副效应" (side effect)。如果函数内部直接包含产生副效应的操作,就不再是纯函数了,我们称之为不纯的函数。
纯函数内部只有通过间接的手段(即通过其他函数调用),才能包含副效应。
钩子(hook)的作用
一句话,钩子(hook)就是 React 函数组件的副效应解决方案,用来为函数组件引入副效应。 函数组件的主体只应该用来返回组件的 HTML 代码,所有的其他操作(副效应)都必须通过钩子引入。
React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。 React Hooks 就是那些钩子。你需要什么功能,就使用什么钩子。React 默认提供了一些常用钩子,你也可以封装自己的钩子。
由于副效应非常多,所以钩子有许多种。React 为许多常见的操作(副效应),都提供了专用的钩子。React 约定,钩子一律使用use
前缀命名,便于识别。
useState()
:保存状态useContext()
:保存上下文useRef()
:保存引用- ......
上面这些钩子,都是引入某种特定的副效应,而 useEffect()
是通用的副效应钩子 。找不到对应的钩子时,就可以用它。其实,从名字也可以看出来,它跟副效应(side effect)直接相关。
useEffect()
1、用法
useEffect()
本身是一个函数,由 React 框架提供,在函数组件内部调用即可。
举例来说,我们希望组件加载以后,网页标题(document.title
)会随之改变。那么,改变网页标题这个操作,就是组件的副效应,必须通过useEffect()
来实现。
import React, { useEffect } from 'react'; function Welcome(props) { useEffect(() => { document.title = '加载完成'; }); return <h1>Hello, {props.name}</h1>; }
上面例子中,useEffect()
的参数是一个函数,它就是所要完成的副效应(改变网页标题)。组件加载以后,React 就会执行这个函数。
useEffect()
的作用就是指定一个副效应函数,组件每渲染一次,该函数就自动执行一次。组件首次在网页 DOM 加载后,副效应函数也会执行。
2、useEffect的第二个参数
有时候,我们不希望useEffect()
每次渲染都执行,这时可以使用它的第二个参数,使用一个数组指定副效应函数的依赖项,只有依赖项发生变化,才会重新渲染。
useEffect(() => { document.title = `Hello, ${props.name}`; }, [props.name]);
只有该变量(props.name)发生变化时,副效应函数才会执行。
3、useEffects的用途
只要是副效应,都可以使用useEffect()
引入。它的常见用途有下面几种。
- 获取数据(data fetching)
- 事件监听或订阅(setting up a subscription)
- 改变 DOM(changing the DOM)
- 输出日志(logging)
useState 状态钩子
useState()
用于为函数组件引入状态(state)。纯函数不能有状态,所以把状态放在钩子里面。
import React, { useState } from "react"; export default function Button() { const [buttonText, setButtonText] = useState("Click me, please"); function handleClick() { return setButtonText("Thanks, been clicked!"); } return <button onClick={handleClick}>{buttonText}</button>; }
useState()
这个函数接受状态的初始值,作为参数,上例的初始值为按钮的文字。该函数返回一个数组,数组的第一个成员是一个变量(上例是buttonText
),指向状态的当前值。第二个成员是一个函数,用来更新状态,约定是set
前缀加上状态的变量名(上例是setButtonText
)。
useContext 共享状态钩子
如果需要在组件之间共享状态,可以使用useContext()
。
useReducer():action 钩子
React 本身不提供状态管理功能,通常需要使用外部库。这方面最常用的库是 Redux。
Redux 的核心概念是,组件发出 action 与状态管理器通信。状态管理器收到 action 以后,使用 Reducer 函数算出新的状态,Reducer 函数的形式是(state, action) => newState
。
报错
1、create-react-app failed with code 1
是说Files\nodejs\node_cache\_npx\1452
这个路径下没有package.json
文件
然后node安装路劲是:D:\Program Files\nodejs\node_cache ,这里可以看出是空格导致的,
解决方法,路径重设:
npm config set prefix "D:\Program~1\nodejs\node_global"
npm config set cache "D:\Program~1\nodejs\node_cache"
参考:npx create-react-app xxx创建项目报错的解决办法