React 入门

React 背景知识

React 是一个用于构建用户界面的 JavaScript 库,主要用于构建 UI,而不是一个 MVC 框架,但可以使用 React 作为 MVC 架构的 View 层轻易的在已有项目中使用,它是一个用于构建用户界面的 JavaScript 库,起源于 Facebook 的内部项目,用来架设 Instagram 的网站,于 2013 年 5 月开源。

React 特点

  • 1.声明式设计 −React采用声明范式,可以轻松描述应用。

  • 2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。

  • 3.灵活 −React可以与已知的库或框架很好地配合。

  • 4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。

  • 5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。

  • 6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

环境准备

方式一:在浏览器中编写代码

  • 直接在浏览器选项卡中使用codepen编写

方法二:直接使用 Staticfile CDNReact CDN

  • 直接使用 Staticfile CDN 的 React CDN 库,地址如下:




  • 官方提供的 CDN 地址:




  • 使用实例如下,直接打开index.html页面会输出 Hello, world!:



    
    Hello React!
    
    
    



方式三:搭建本地开发环境,通过 npm 使用 React

  • 环境要求: Node>= 10.16 和 npm >= 5.6

  • 使用 create-react-app 快速构建 React 开发环境

    $ cnpm install -g create-react-app
    $ create-react-app my-app
    $ cd my-app/
    $ npm start   // 运行本地服务
    

在浏览器中打开 http://localhost:3000/ 就能看到运行结果。

  • 项目的目录结构如下:
my-app/
  README.md
  node_modules/
  package.json
  .gitignore
  public/
    favicon.ico
    index.html
    manifest.json
  src/
    App.css
    App.js
    App.test.js
    index.css
    index.js
    logo.svg

manifest.json 指定了开始页面 index.html,一切的开始都从这里开始,所以这个是代码执行的源头。

React 元素渲染

ReactDOM.render()

ReactDOM.render 是 React 的最基本方法,用于将模板转为 HTML 语言,并插入指定的 DOM 节点。

ReactDOM.render(
  

Hello, world!

, document.getElementById('example') );

上面代码将一个 h1 标题,插入 example 节点.

将元素渲染到 DOM 中

首先我们在一个 HTML 页面中添加一个 id=“example” 的,如下:

React 开发应用时一般只会定义一个根节点。

要将React元素渲染到根DOM节点中,我们通过把它们都传递给 ReactDOM.render() 的方法来将其渲染到页面上:

const element = 

Hello, world!

; ReactDOM.render( element, document.getElementById('example') );

更新元素渲染

React 元素都是不可变的。当元素被创建之后,你是无法改变其内容或属性的。

目前更新界面的唯一办法是创建一个新的元素,然后将它传入 ReactDOM.render() 方法,比如下面这个定时器:

function tick() {
  const element = (
    

Hello, world!

现在是 {new Date().toLocaleTimeString()}.

); ReactDOM.render( element, document.getElementById('example') ); } setInterval(tick, 1000);

ReactDOM.render调用函数及参数传递

  • ReactDOM.render内调用 已有函数与常规的JS有很大的区别:
    • 函数引用直接包含在中,而不是()
    • 多个参数是以key、value时直接传递的,而不是对象格式的date数据
    • 示例如下:
 ReactDOM.render(
      < functionname  property1 = {property1_value}  property2 = {property2_value} />,
       document.getElementById('example')
   );

以下实例用一个函数来表示前面的定时器封装:

function Clock(props){
    return (
        

Hello, {props.name} ~

现在是北京时间 {props.date.toLocaleTimeString()}

) } function tick() { ReactDOM.render( < Clock date={new Date()} name={'world'} id={'world'}/>, document.getElementById('example') ); } setInterval(tick, 1000);

值得注意的是 React DOM 首先会比较元素内容先后的不同,而在渲染过程中只会更新改变了的部分。

创建React.Component 的 ES6 类

需要注意的是在 render() 方法中,需要使用this.props 替换 props,示例:

class Clock extends React.Component{
    render(){
        return(
            

Hello, {this.props.name} ~

现在是北京时间 {this.props.date.toLocaleTimeString()}

) } } function tick() { ReactDOM.render( < Clock date={new Date()} name='world' id='world'/>, {/* new Date() 这样的value,需要使用花括号来包含,仅字符串的值则不需要 */} document.getElementById('example') ); } setInterval(tick, 1000);

React JSX

js和jsx的区别

既然这里提到了jsx,那就顺便了解一下两者的区别:

  • JS:即JavaScript,一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于浏览器客户端的脚本语言。
  • JSX: 即JavaScript XML——一种在React组建内部构建标签的类XML语法。(增强React程序组件的可读性)。Javascript和XML结合的一种格式。React发明了JSX,利用HTML语法来创建虚拟DOM。当遇到<,JSX就当HTML解析,遇到{就当JavaScript解析。

使用 jsx

React 实例

ReactDOM.render(
    

React JSX

欢迎学习 React jsx

这是一个很不错的 JavaScript 库!

, document.getElementById('example') );

jsx独立文件

  • 创建jsx独立文件:例如我们创建一个 helloworld_react.js 文件,代码如下:
ReactDOM.render(
	

hello, world

, document.getElementById('example') )
  • 然后在 HTML 文件中引入该 JS 文件(注意:script type 为 text/babel):

  	

注意:script 标签的 type 属性为 text/babel 。这是因为 React 独有的 JSX 语法,跟 JavaScript 不兼容。凡是使用 JSX 的地方,都要加上 type="text/babel"

JavaScript 表达式

  • 在 JSX 中使用 JavaScript 表达式。表达式写在花括号 {} 中:
ReactDOM.render(
    

{1+1}

, document.getElementById('example') );
  • 条件判断语句:在 JSX 中不能使用 if else 语句,但可以使用 conditional (三元运算) 表达式来替代
ReactDOM.render(
 	

{i == 1 ? 'True!' : 'False'}

, document.getElementById('example') )

样式

React 推荐使用内联样式。我们可以使用 camelCase 小驼峰 语法来设置内联样式. React 会在指定元素数字后自动添加 px ,如font-size要写成fontSize:

let myStyle = {
	fontSize: 100,
	color: '#FF0000'
};
ReactDOM.render(
	

hello, world {1+1}

, document.getElementById('example') )

注释

注释需要 写在花括号{} 中,实例如下:

ReactDOM.render(
	

hello, world {1+1} {/*这是注释部分*/}

, document.getElementById('example') )

数组

JSX 允许在模板中插入数组,数组会自动展开所有成员:

let arr = [
	

React教程

,

学的不仅是React,学的是方法。

, ]; ReactDOM.render(
{arr}
, document.getElementById('example') )

React 组件

  • 组件类的第一个字母必须大写,否则会报错

实例解析:

function HelloMessage(props) {
	return 

Hello {props.name}!

; } const element = ; ReactDOM.render( element, document.getElementById('example') );

注意:

  • 原生 HTML 元素名以小写字母开头,而自定义的 React 类名以大写字母开头,比如 HelloMessage 不能写成 helloMessage。除此之外还需要注意组件类只能包含一个顶层标签,否则也会报错。
  • 在添加属性时, class 属性需要写成 className ,for 属性需要写成htmlFor,这是因为 class 和 for 是 JavaScript 的保留字

复合组件

直接看例子吧:

function Name(props){
	return 

网站名称:{props.name}

} function Url(props){ return

网站地址:{props.url}

} function Nickname(props){ return

网站小名:{props.nickname}

} function App(){ return (
) } ReactDOM.render( , document.getElementById('example') );

React.createClass方法

React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件。React.createClass 方法就用于生成一个组件类

let HelloMessage = React.createClass({
	render: function (){
		return 

hello, {this.props.name}!

} }) ReactDOM.render( , document.getElementById('example') );

注:react最新版本弃用了 React.createClass这个函数,可以使用15.x版本尝试。

React State(状态)

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。

将生命周期方法添加到类中

在具有许多组件的应用程序中,在销毁时释放组件所占用的资源非常重要。

  • 挂载:每当 Clock 组件第一次加载到 DOM 中的时候,我们都想生成定时器,这在 React 中被称为挂载
  • 卸载:同样,每当 Clock 生成的这个 DOM 被移除的时候,我们也会想要清除定时器,这在 React 中被称为卸载

我们可以在组件类上声明特殊的方法,当组件挂载或卸载时,来运行一些代码:


class Clock extends React.Component {
	constructor(props) {
		super(props)
		this.state = {date: new Date()}
	}

	componentDidMount(){
		let _this = this;
		this.timerID = setInterval(function (){
			_this.tick()
		}, 1000)
	}

	componentWillUnmount() {
		clearInterval(this.timerID)
	}

	tick(){
		this.setState({
			date: new Date()
		})
	}

	render(){
		return(
			

Hello, world!

现在是北京时间:{this.state.date.toLocaleTimeString()}

) } } ReactDOM.render( , document.getElementById('example') );

实例解析:

  • componentDidMount()componentWillUnmount() 方法被称作生命周期钩子。
  • 在组件输出到 DOM 后会执行 componentDidMount() 钩子,我们就可以在这个钩子上设置一个定时器。
  • 通过调用 setState(),React 知道状态已经改变,并再次调用 render() 方法来确定屏幕上应当显示什么。 这一次,render() 方法中的 this.state.date将不同,所以渲染输出将包含更新的时间,并相应地更新 DOM。
  • this.timerID 为定时器的 ID,我们可以在 componentWillUnmount()钩子中卸载定时器。

数据自顶向下流动

父组件或子组件都不能知道某个组件是有状态还是无状态,并且它们不应该关心某组件是被定义为一个函数还是一个类。
这就是为什么状态通常被称为局部或封装。 除了拥有并设置它的组件外,其它组件不可访问。
以下实例中 FormattedDate 组件将在其属性中接收到 date 值,并且不知道它是来自 Clock 状态、还是来自 Clock 的属性、亦或手工输入

function FormattedDate(props){
	return 

现在是北京时间:{props.date.toLocaleTimeString()}

} class Clock extends React.Component { constructor(props) { super(props) this.state = {date: new Date()} } componentDidMount(){ let _this = this; this.timerID = setInterval(function (){ _this.tick() }, 1000) } componentWillUnmount() { clearInterval(this.timerID) } tick(){ this.setState({ date: new Date() }) } render(){ return(

Hello, world!

) } } function App(){ return(
) } ReactDOM.render( , document.getElementById('example') );

这里我还没有完全理解它的含义!

React Props

stateprops 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。

基本用法

前面的示例中都已经涉及到Props的使用,这里不再举例,只再提示一点普通类和ES6 class使用上的差别:

  • 普通的类:使用props.name 方式访问属性
  • ES6 类:使用this.props.name方式访问属性

State 和 Props

例:在父组件中设置 state, 并通过在子组件上使用 props 将其传递到子组件上。在 render 函数中, 我们设置 name 和 site 来获取父组件传递过来的数据

class Book extends React.Component{
	constructor(props) {
		super(props)

		this.state = {
			name: 'react',
			time: '2013年5月',
		}
	}

	render() {
		return (
			
) } } class Name extends React.Component { render(){ return(

Book name: {this.props.name}

) } } class Time extends React.Component { render(){ return(

Book create time: {this.props.time}

) } } ReactDOM.render( , document.getElementById('example') )

Props 验证

React.PropTypes 在 React v15.5 版本后已经移到了 prop-types 库。 React v15.5 版本以上使用时需要引入:

Props 验证使用 propTypes,它可以保证我们的应用组件被正确使用,React.PropTypes 提供很多验证器 (validator) 来验证传入数据是否有效。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。

React 16.4 实例:

var title = "react learner";
class MyTitle extends React.Component {
	render() {
		return (
			

Hello, {this.props.title}

); } } MyTitle.propTypes = { title: PropTypes.string }; ReactDOM.render( , document.getElementById('example') );

更多验证器说明如下

MyComponent.propTypes = {
    // 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的
   optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,
 
    // 可以被渲染的对象 numbers, strings, elements 或 array
    optionalNode: React.PropTypes.node,
 
    //  React 元素
    optionalElement: React.PropTypes.element,
 
    // 用 JS 的 instanceof 操作符声明 prop 为类的实例。
    optionalMessage: React.PropTypes.instanceOf(Message),
 
    // 用 enum 来限制 prop 只接受指定的值。
    optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
 
    // 可以是多个对象类型中的一个
    optionalUnion: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.number,
      React.PropTypes.instanceOf(Message)
    ]),
 
    // 指定类型组成的数组
    optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
 
    // 指定类型的属性构成的对象
    optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
 
    // 特定 shape 参数的对象
    optionalObjectWithShape: React.PropTypes.shape({
      color: React.PropTypes.string,
      fontSize: React.PropTypes.number
    }),
 
    // 任意类型加上 `isRequired` 来使 prop 不可空。
    requiredFunc: React.PropTypes.func.isRequired,
 
    // 不可空的任意类型
    requiredAny: React.PropTypes.any.isRequired,
 
    // 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
    customProp: function(props, propName, componentName) {
      if (!/matchme/.test(props[propName])) {
        return new Error('Validation failed!');
      }
    }
  }
}

React 事件处理

  • React 事件绑定属性的命名采用驼峰式写法,而不是小写。

    
    
  • 在 React 中另一个不同是你不能使用返回 false 的方式阻止默认行为, 你必须明确使用 preventDefault

    function ActionLink() {
      function handleClick(e) {
        e.preventDefault();
        console.log('链接被点击');
      }
     
      return (
        
          点我
        
      );
    }
    

向事件处理程序传递参数

通常我们会为事件处理程序传递额外的参数。例如,若是 id 是你要删除那一行的 id,以下两种方式都可以向事件处理程序传递参数:



通过 bind 方式向监听函数传参,在类组件中定义的监听函数,事件对象 e 要排在所传递参数的后面,例如:

class Popper extends React.Component{
    constructor(){
        super();
        this.state = {name:'Hello world!'};
    }
    
    preventPop(name, e){    //事件对象e要放在最后
        e.preventDefault();
        alert(name);
    }
    
    render(){
        return (
            

hello

{/* 通过 bind() 方法传递参数。 */} Click
); } }
  • 补充:bind()是Function对象内建的方法,它们的第一个参数都是用来更改调用方法中this的指向

React 条件渲染

元素变量

添加元素变量帮助你有条件的渲染组件的一部分,而输出的其他部分不会更改。

function UserGreeting(props){
	return 

欢迎回来~

} function GuestGreeting(props){ return

请先登录~

} function Greeting(props){ const isLogging = props.isLogging if(isLogging){ return } return } function LoginButton(props){ return ( ) } function LogoutButton(props){ return( ) } class LoginControl extends React.Component { constructor(props) { super(props) this.handleLoginClick = this.handleLoginClick.bind(this) this.handleLogoutClick = this.handleLogoutClick.bind(this) this.state = {isLoggedIn: true} } handleLoginClick(){ this.setState({isLoggedIn: true}) } handleLogoutClick(){ this.setState({isLoggedIn: false}) } render(){ let logged = this.state.isLoggedIn let button if(logged){ button = }else { button = } return(
{button}
) } } ReactDOM.render( , document.getElementById('example') )

与运算符 &&

可以通过用花括号包裹代码在 JSX 中嵌入任何表达式 ,也包括 JavaScript 的逻辑与 &&,它可以方便地条件渲染一个元素。示例:

function MailBox(props){
	let unreadmessage = props.unreadmessage

	return(
		

Hello!

{ unreadmessage.length >0 &&

您有 {unreadmessage.length} 条未读消息。

}
) } const messages = ['React', 'Re: React', 'Re:Re: React']; ReactDOM.render( , document.getElementById('example') );
  • 在 JavaScript 中,true && expression 总是返回 expression,而 false && expression 总是返回 false
  • 因此,如果条件是 true&& 右侧的元素就会被渲染,如果是false,React 会忽略并跳过它。

三目运算符

用法和 JavaScript 的条件运算符一样:

condition ? true : false。

示例:

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    
{isLoggedIn ? ( ) : ( )}
); }

阻止组件渲染

实现:让 render 方法返回 null 而不是它的渲染结果即可实现。

function WarningBanner(props) {
	if (!props.warn) {
		return null;
	}

	return (
		
警告!
); } 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 (
); } } ReactDOM.render( , document.getElementById('example') );

React 列表 & Keys

实例:使用 map() 方法遍历数组生成了一个 1 到 5 的数字列表(每个列表元素分配一个 key,不然会出现警告 a key should be provided for list items,意思就是需要包含 key:):

function NumberList(props) {
	const numbers = props.numbers;
	const listItems = numbers.map(function (item){
		return 
  • {item}
  • }); return (
      {listItems}
    ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( , document.getElementById('example') );

    Keys

    • 用途:在 DOM 中的某些元素被增加或删除的时候帮助 React 识别哪些元素发生了变化
    • 值:一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串

    用keys提取组件

    • 元素的 key 只有在它和它的兄弟节点对比时才有意义。
    function ListItem(props) {
      // 对啦!这里不需要指定key:
      return 
  • {props.value}
  • ; } function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => // 又对啦!key应该在数组的上下文中被指定 ); return (
      {listItems}
    ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( , document.getElementById('example') );

    元素的 key 在他的兄弟元素之间应该唯一

    • 数组元素中使用的 key 在其兄弟之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的键。
    function Blog(props) {
      const sidebar = (
        
      {props.posts.map((post) =>
    • {post.title}
    • )}
    ); const content = props.posts.map((post) =>

    {post.title}

    {post.content}

    ); return (
    {sidebar}
    {content}
    ); } const posts = [ {id: 1, title: 'Hello World', content: 'Welcome to learning React!'}, {id: 2, title: 'Installation', content: 'You can install React from npm.'} ]; ReactDOM.render( , document.getElementById('example') );
    • key 会作为给 React 的提示,但不会传递给你的组件。如果您的组件中需要使用和 key 相同的值,请将其作为属性传递:
    const content = posts.map((post) =>
      
    );
    

    在 jsx 中嵌入 map()

    • JSX 允许在大括号中嵌入任何表达式,比如像这样:
    function NumberList(props) {
      const numbers = props.numbers;
      return (
        
      {numbers.map((number) => )}
    ); }

    React 组件 API

    设置状态:setState

    setState(object nextState[, function callback])
    
    • 参数说明
      • nextState,将要设置的新状态,该状态会和当前的state合并
      • callback,可选参数,回调函数。该函数会在setState设置成功,且组件重新渲染后调用。

    合并nextState和当前state,并重新渲染组件。setStateReact事件处理函数中和请求回调函数中触发UI更新的主要方法。

    • 补充:
      • setState()并不会立即改变this.state,而是创建一个即将处理的statesetState()并不一定是同步的,为了提升性能React会批量执行state和DOM渲染。
      • setState()总是会触发一次组件重绘,除非在shouldComponentUpdate()中实现了一些条件渲染逻辑。

    替换状态:replaceState

    replaceState(object nextState[, function callback])
    
    • nextState,将要设置的新状态,该状态会替换当前的state。
    • callback,可选参数,回调函数。该函数会在replaceState设置成功,且组件重新渲染后调用。

    replaceState()方法与setState()类似,但是方法只会保留nextState中状态,原state不在nextState中的状态都会被删除。

    设置属性:setProps

    setProps(object nextProps[, function callback])
    

    替换属性:replaceProps

    replaceProps(object nextProps[, function callback])
    

    强制更新:forceUpdate

    forceUpdate([function callback])
    

    获取DOM节点:findDOMNode

    DOMElement findDOMNode()
    

    判断组件挂载状态:isMounted

    bool isMounted()
    
    • 返回值:true或false,表示组件是否已挂载到DOM中

    React 组件生命周期

    组件的生命周期可分成三个状态:

    • Mounting:已插入真实 DOM
    • Updating:正在被重新渲染
    • Unmounting:已移出真实 DOM

    生命周期的方法有:

    • componentWillMount 在渲染前调用,在客户端也在服务端。

    • componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。

    • componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。

    • shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
      可以在你确认不需要更新组件时使用。

    • componentWillUpdate 在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。

    • componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。

    • componentWillUnmount 在组件从 DOM 中移除之前立刻被调用。

    React AJAX

    componentDidMount() {
        this.serverRequest = $.get(this.props.source, function (result) {
          var lastGist = result[0];
          this.setState({
            username: lastGist.owner.login,
            lastGistUrl: lastGist.html_url
          });
        }.bind(this));
      }
    

    React 表单与事件

    • 使用 onChange 事件来监听 input 的变化,并修改 state。
    class HelloMessage extends React.Component {
      constructor(props) {
          super(props);
          this.state = {value: 'Hello Runoob!'};
          this.handleChange = this.handleChange.bind(this);
      }
     
      handleChange(event) {
        this.setState({value: event.target.value});
      }
      render() {
        var value = this.state.value;
        return 

    {value}

    ; } } ReactDOM.render( , document.getElementById('example') );

    React Refs

    React 支持一种非常特殊的属性 Ref ,你可以用来绑定到 render() 输出的任何组件上。

    这个特殊的属性允许你引用 render() 返回的相应的支撑实例( backing instance )。这样就可以确保在任何时间总是拿到正确的实例。

    class MyComponent extends React.Component {
      handleClick() {
        // 使用原生的 DOM API 获取焦点
        this.refs.myInput.focus();
      }
      render() {
        //  当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs
        return (
          
    ); } } ReactDOM.render( , document.getElementById('example') );

    参考

    • React 教程
    • React 组件的API(组件实例)介绍
    • React 生命周期方法可以参考官方文档
    • React 入门实例教程

    你可能感兴趣的:(React,react)