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 应用

  1. 确保你安装了较新版本的 Node.js
  2. 按照 Create React App 安装指南创建一个新的项目:npx create-react-app my-app
  3. npm start

create-react-app 是来自于 Facebook,通过该命令我们无需配置就能快速构建 React 开发环境。

create-react-app 自动创建的项目是基于 Webpack + ES6

教程

React 入门实例教程

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>
View Code

获取真实的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>
View Code

组件 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>
View Code

组件的生命周期及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>
View Code

React Hooks

参考: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创建项目报错的解决办法

 

posted @ 2021-06-24 16:29  peterYong  阅读(64)  评论(0编辑  收藏  举报