react入门

react库

​ 用于构建用户界面的JavaScript库

​ 注意!!! React是一个库不是一个框架

react库的特点:

  • 声明式:
  • 组件化:
  • 一次学习,随处编写:版本的变动较小

react的生态现在是阿里做的

一、创建项目

  1. 通过script标签引入,在学习时一般直接引用cdn引入

  2. 通过react的脚手架,创建项目进行开发,部署

    // 1.安装脚手架
    cnpm installl -g create-react-app
    // 2.创建项目
    create-react-app 01helloworld
    // 开发时运行的环境
    cd  01helloworld
    npm start
    

二、React的渲染

使用jsx的写法,可创建js元素

注意:jsx的元素,必须要有一个根元素

let root = <h1>这是根元素</h1>

使用函数的方式创建元素

let desc = "这是根组件";
function App(){
    reture (
        <h1>{desc}</h1>
    )
}
export.default App

三、React组件传值

关于组件的创建

  • 组件的创建第一种方法:函数式组件
function App () {
    return (
        <h1> 这是一个组件 </h1>
    )
}
export default App
  • 组件的创建第二种方法:变量式创建
const app = new App({
  desc:'类组件是继承React.Component的'
}).render();
render(app,document.querySelector('#root'))

定义类的方式

import { render } from "react-dom";
class App extends React.Component{
  render() {
    // props是render函数传进来的参数的,是默认存在,不需要实际声明
    return (<div>
      <h1>组件1</h1>
      <h3>{this.props.desc}</h3>
    </div>)
  }
}
render(<App desc='这是类组件'/>,document.querySelector('#root'));

注意!!!这是React16之后的写法,在这之前的js还没有扩展出类写法,因此在这之前一般采用以下的写法:

const App = React.createClass({
  render(){
    return <h1>xxxxxxx</h1>
  }
});
render(<App desc='这是类组件'/>,document.querySelector('#root'));

四、JSX

JSX的优点:

  1. ​ JSX执行更快,编译为JavaScript代码时进行优化
  2. ​ 类型更安全,编译过程中能即时发现错误
  3. ​ JSX编写模板更加简单快速(相对而言)

注意!!!

  1. ​ Jsx与Vue类似,必须要有一个根节点
  2. ​ 正常的普通HTML必须小写,大写会默认认为是组件

JSX内部的表达式:{}

  1. 可以使用大多数的JS表达式:如: 变量,函数,对象,条件运算符,三元运算符
  2. 可以直接使用HTML,不需要进行任何处理
  3. 在使用类名的时候尽量使用className,因为class是React的内置关键字

总结:

  1. ​ JSX由HTML的元素构成
  2. 中间如果需要加入变量,需要用括号括起来
  3. 可以使用表达式,表达式中可以使用JSX对象
  4. 属性和对象内容都是用括号插入内容

五、JSX-Style样式

1.class中,不可以存在多个class属性

<div class='abc' class={'active'}></div>

2.style样式中,如果存在多个单词时,第二个单词开始必须首字母大写,或者用-引起来,否则会报错

let exampleStyle = {
  background:'skyblue',
  borderBottom:'5px solid #ccc'
  background-image:
    "url('https://www.baidu.com/img/pc_1c6e30772d5e4103103bd460913332f9.png')"  
};

3. 多个类共存的操作

let ele4 = (
  <div>
    <span>横着1</span>
    <span>竖着2</span>
  </div>
);
let DOMElement = (
  // 样式的类名要写className,因为class在React中是关键字
  // 后面可以使用变量,也可以使用字符串
  <div className={color}>
    <h1>hello world</h1>
    <h2 className={classList.join(' ')}>{1+1}</h2>
    <h3>{str+time}</h3>
    <h3>{str+localTime()}</h3>
    <h3 style={exampleStyle}>{temp>37.2? <button>隔离</button>:'不隔离'}</h3>
  </div>
);

4. 关于JSX的注释

{/* 必须在表达式内书写注释,否则报错 */}

六、条件循环

1. 条件判断

2. 循环

1. 简单的循环方式
let arr = ['芜湖','起飞','南通','哈哈哈哈'];
// 循环渲染
class ParentCom extends React.Component{
  constructor(props){
    super(props)
  }
  render() {
    let res = [];
    arr.forEach((item,index)=>{
      let temp = <li key={index}>{item}</li>;
      res.push(temp)
    });
    // 这里的循环方式也可以使用map的方法
    return (
      <div>
        <ul>
          {res}
        </ul>
      </div>
    )
  }
}
2. 简化后的循环方式

​ 使用数据的map方法,对每一项数据按照jsx的形式进行加工,最终得到1个每一项都是jsx对象的数值,

七、组件

注意ReactDom.render()一般只会放一个根组件,但是一个根组件可以加载无数的组件

1. 函数式组件

import {render} from "react-dom";
import React from 'react';
import logo from './logo.svg';
// props是父组件传进来的参数
// 比如<App weather='下雨' />  即: props.weather = '下雨'
function App(props) {
  // 注意,在括号中可以任何有效的JavaScript表达式并用大括号引起来
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <h1>{desc}</h1>
        <h2>{props.desc}</h2>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>

    </div>
  );
}

2. 类组件

import {render} from "react-dom";
import React from 'react';
class App extends React.Component{
  render() {
    // props是render函数传进来的参数的,是默认存在,不需要实际声明
    return (<div>
      <h1>组件1</h1>
      <h3>{this.props.desc}</h3>
    </div>)
  }
}
// 组件的是需要render函数返回模板
// 类组件不用刻意声明形参
// 比如<App weather='下雨' />  即: this.props.weather = '下雨'

3. 复合组件

class ChildCom extends React.Component{
    render(){
        return(
            <h1>hello {this.props.name} !!!</h1>
        )
    }
}
class ParentCom extends React.Component{
    render() {
    // props是render函数传进来的参数的,是默认存在,不需要实际声明
    return (
        <div>
            <h1>组件1</h1>
            <h2> 今天的天气是: {this.props.weather} </h2>      
            <h3>{this.props.desc}</h3>
            <ChildCom name={this.props.name}/>
        </div>)
  }
}
// 复合组件传参的方式
// <App name='wang' desc='这是一个类组件' weather='rain'/>

4. 函数式与类组件的区别

  1. 类组件可以自定义事件,函数式组件不方便设置事件
  2. 内容定死,只传一些简单的参数,就是使用函数式组件,即为静态组件
  3. 函数式组件操作简单,但可支持功能较少
  4. 类组件用于交互和数据修改

注意!!!无论是类组件还是函数式组件的调用都是<ChildCom/> 这种的方式

八、props

1. 父传递子组件

要对子组件进行控制,如:是否显示,则只需要在父组件处传入一个布尔值即可:

// 父组件
class ParentCom extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      isActive:true
    };
    this.changeShow = this.changeShow.bind(this)
  }
  changeShow(){
    this.setState({
      isActive:!this.state.isActive
    })
  }
  render() {
    return (
      <div>
        <button onClick={this.changeShow}>这个是控制子元素显示</button>
        <h1>这是父元素控制子元素的操作</h1>
        <ChildCom isActive={this.state.isActive}/>
      </div>
    )
  }
}

// 子组件
class ChildCom extends React.Component{
  constructor(props){
    super(props)
  }
  render() {
    let ele = null;
    if (this.props.isActive) {
      return (
        <div>
          <h1>这是子组件</h1>
        </div>
      )
    // 需要注意这里无论如何都需要返回一个模板对象
    }else {
      return (
        null
      )
    }
  }
}

2. 子组件向父组件数据传递

// 实现子传父
class ParentCom extends React.Component{
  constructor(props) {
    super(props);
    this.state = {
      childData:'',
    }
  }
  setChildData=(data)=>{
    this.setState({
      childData:data
    })
  };
  render() {
    return (
      <div>
        <h1>这个是子元素传递给父元素的数据:{this.state.childData}</h1>
        {/* 往子组件处传递一个函数,用以控制父组件的内容 */}
        <ChildCom setChildData={this.setChildData}/>
      </div>
    )
  }
}
class ChildCom extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      msg:'hello world'
    };
    // 这里的绑定也可以进行绑定
    this.sendData = this.sendData.bind(this)
  }
  sendData(){
    console.log(this.state.msg);
    this.props.setChildData(this.state.msg)
  }
  render() {
    return (
      <div>
        <button onClick={this.sendData}>传递hello world给父元素</button>
      </div>
    )
  }

}

九、React事件

特点:

  1. react事件,绑定事件的命名,驼峰命名
  2. 传入1个函数,而不是字符串,如果的

事件对象:

​ React返回的事件对象是代理处理后的事件对象,如果想要查看事件对象的具体值,必须之间输出事件对象的属性

​ 原生的JS,可以直接使用return false,来阻止浏览器的默认行为

​ 但是因为React代理处理了原生JS使得return false 变为无效,要阻止浏览器的默认行为就使用 e.preventDefault

React事件传参

  1. 使用函数的方式

    <button onClick={(e)=>{this.parentEvent1('msg:hello world',e)}}>点击</button>
    
  2. 不使用箭头函数方式

    <button onClick={function (event) {
              this.parentEvent1('msg:hello world',event)
            }.bind(this)}> 点击 </button>
    

十、组件条件循环渲染

1. 三元运算符

// 组件1
function UserGreet() {
  return (
    <div>
      <h1>你好,尊敬的用户</h1>
    </div>
  )
}
// 组件2
function UserLogin() {
  return (
    <div>
      <h1>你好请登录</h1>
    </div>
  )
}

// 条件渲染的主组件
class ParentCom extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      isLogin:false,
    }
  }
  changeEvent = (e)=>{
    console.log(e);
    this.setState({isLogin:!this.state.isLogin})
  };
  render() {
    let ele = null;
    /*
    条件渲染组件的方式
    if (this.state.isLogin){
      ele = <UserLogin />
    }else {
      ele = <UserGreet />
    }*/
    // 这里可以使用三元运算符
    ele = this.state.isLogin?<UserGreet/>:<UserLogin/>;
    return (
      <div>
        <h1>这是头部</h1>
        {ele}
        <h1>这是尾部</h1>
        <button onClick={this.changeEvent}>点击登录</button>
      </div>
    )
  }
}

2. 列表循环渲染

十一、React制作疫情地图

1.疫情数据的API接口

注意!!! (不允许跨域请求)

https://c.m.163.com/ug/api/wuhan/app/index/feiyan-data-list?t=1580892891419

2.写入一个Json数据文件

3.导入Json数据

import jsonData from "./feiyan";
console.log(jsonData);

4.设置(设计)数据模型

let provinceObj = {
  // "广东省":{
  //   confirm:'',
  //   suspect:'',
  //   heal:'',
  //   deal:''
  // }
};

5.进行数据累加

jsonData.data.list.forEach((item,index)=>{
  // 数据的初始化处理
  if (provinceObj[item.province]===undefined){
    provinceObj[item.province] = {
      confirm:0,
      suspect:0,
      heal:0,
      dead:0,
    }
  }
  // 数据异常处理
  item.confirm = item.confirm?item.confirm:0;
  item.suspect = item.suspect?item.suspect:0;
  item.heal = item.heal?item.heal:0;
  item.dead = item.dead?item.dead:0;
  // 对数据进行累加处理,以获取每个省份的数值
  provinceObj[item.province] = {
    confirm:provinceObj[item.province].confirm+item.confirm,
    suspect:provinceObj[item.province].suspect+item.suspect,
    heal:provinceObj[item.province].heal+item.heal,
    dead:provinceObj[item.province].dead+item.dead,
  }  
});
let provinceList = [];
for (let item in provinceObj) {
  provinceObj[item].province = item;
  provinceList.push(provinceObj[item])
}

6.对数据进行排序

// 对数组进行降序排序
let provinceListSort = provinceList.sort((a,b)=>{
  if (a.confirm > b.confirm) {
    return -1
  }else {
    return 1
  }
});

7.将数据渲染至页面上

import React from 'react';
import ReactDOM from 'react-dom';
class ParentCom extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    return (
      <div>
        <h1>中国病例</h1>
        <ul>
          <li>
            <span>地区</span>
            <span>确诊</span>
            <span>死亡</span>
            <span>疑似</span>
          </li>
          {
            this.props.list.map((item,index)=>{
              return (
                <li key={index}>
                  <span>{item.province}</span>
                  <span>{item.confirm}</span>
                  <span>{item.dead}</span>
                  <span>{item.suspect}</span>
                </li>
              )
            })
          }
        </ul>
      </div>
    );
 }
}
ReactDOM.render(<ParentCom name='wang' list={provinceListSort}/>,document.querySelector('#root'));

十二、React的生命周期

生命周期的3个状态:

  1. mount:挂载
  2. update:数据更新
  3. unMount:卸载

十三、 表单

  1. 必须绑定value值
  2. 必须绑定onChange事件

十四、Ajax与React

本次小案例运用到的是技术有:

  • axios网路请求
  • express后端框架
  • React前端框架
  • AJax异步请求

1.API接口

https://i.snssdk.com/ugc/hotboard_fe/hot_list/template/hot_list/forum_tab.html?activeWidget=1

获取到首页的信息后查看Ajax的数据请求

可以获取到首页的数据请求

以及

2.建议服务器

  • express --view=ejs react-serve
  • cd react-serve
  • npm install

3.axios的安装

  • npm install axios

十五、React实现todoList

复习一下React的事件机制

涉及到的技术有:Ref以及键盘事件

十六、关于prop-types

1.prop-types介绍

prop-types是用于限制父组件传给子组件的数据类型,以及是否为必须的数据

​ prop-types的报错只是表明与声明的规范不一致导致的报错,并不一定会影响最终的DOM渲染

2. prop-types的安装

​ 自从React V15之后,prop-type就作为单独的插件,需要另外的安装,而且也不是必须使用的插件

安装:

npm i prop-types --save

3.prop-types的使用

// 1. 导入prop-types
// ES6的写法
import PropTypes from 'prop-types';
// ES5的写法
var PropTypes = require('prop-types');

// 2. 创建一个组件
class MyComponent extends React.Component {
  construction(props){
      super(props)
  }  
  render() {
    // ... 写入需要生成的DOM元素
  }
}

// 3. 使用prop-types
MyComponent.propTypes = {
    // 定义类型为字符串
    desc:PropTypes.string,
    // 定义类型为数字类型
    x:PropTypes.number,
    // 定义类型为数字类型,且为必须的数字类型
    y:PropTypes.number.isRequrie
}
// 4. 使用prop-types的默认属性值
MyComponent.defaultProps = {
    y:2
};

当然还有另外一种写法:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      title:this.props.title
    };
    console.log(this.props);
  }
  static propTypes = {
    desc:PropTypes.string,
    x:PropTypes.number,
    y:PropTypes.number.isRequired
  };
  static defaultProps = {
    y:2
  };
  render() {
    return (
      <div>
        <h1>这是{this.state.title}</h1>
        {this.props.children}
        {this.props.x+this.props.y}
      </div>
    );
  }
}

十七、React插槽

​ 组件中写入自定义内容,这些内容可以被识别和控制,

1.原理

插槽的内容会默认传入props.children处,直接调用可以使用this.props.children

组件中的HTML内容直接全部插入,调用{this.props.children}

根据HTML内容的不同插入的内容也不同的,(即对组件插槽进行处理)

// 父组件
<ChildCom>
          <h1 data-position="header">这是放置头部的内容</h1>
          <h1 data-position="main">这是放置主要内容的内容</h1>
          <h1 data-position="footer">这是放置尾部的内容</h1>
</ChildCom>

// 定义子组件
class ChildCom extends React.Component {
  render() {
    let headerCom,mainCom,footerCom;
    this.props.children.forEach((item, index)=>{
      if (item.props["data-position"] === "header"){
        headerCom = item
      } else if (item.props["data-position"] === "main") {
        mainCom = item
      }else {
        footerCom = item
      }
    });
    return (
      <div>
        <div className="header">
          {headerCom}
        </div>
        <div className="main">
          {mainCom}
        </div>
        <div className="footer">
          {footerCom}
        </div>
      </div>
    );
  }
}

十八、 React 路由

1.安装

cnpm i react-router-dom --save

2.路由的三大组件

  1. Router:所有的路由的根组件
  2. Link:用于页面的跳转相当于HTML的A标签
  3. Route:路由根组件下面匹配的组件

3.写一个简单的路由

<Router basename="/admin">
    <div className="nav">
        <Link to="/">Home</Link>
        <Link to="/product">Product</Link>
        <Link to="/me">Me</Link>
    </div>
    {/* 如果需要精确匹配就需要加入exact属性 */}
    <Route path="/" exact component={Home} />
    <Route path="/product" component={Product}/>
    <Route path="/me" component={Me}/>
 </Router>

4.路由传参

<div id="app">
    <h1>外面显示的所有内容:</h1>
    <Router basename="/admin">
        <div className="nav">
            <Link to="/">Home</Link>
            <Link to="/product">Product</Link>
            <Link to={{pathname:'/me',search:'?username=admin',hash:'#abc',state:{msg:'isLogin'}}}>Me</Link>
        </div>
        {/* 如果需要精确匹配就需要加入exact属性 */}
        <Route path="/" exact component={Home} />
        <Route path="/product" component={Product}/>
        <Route path="/me" component={Me}/>
    </Router>
</div>

5.路径切换

路由的切换,默认采用的push方法,可以前进后退

replace:进入的该路由之后,就会将该路由设置为默认首页,此时不可以前进或者后退

<Router basename="/admin">
    <div className="nav">
        <Link replace to={{pathname:'/me',search:'?username=admin',hash:'#abc',state:{msg:'isLogin'}}}>Me</Link>
    </div>
    <Route path="/me" component={Me}/>
</Router>

6.动态路由

<Router basename="/admin">
    <div className="nav"> 
        <Link to="/news/123">News</Link>
    </div>
    <Route path="/news/:id" component={News}/>
    {/* this.props.match.params.id 获取动态路由的信息 */}
</Router>

7.路由重定向

// 当访问到LoginInfo组件的是否,该组件会跟条件重定向至不同的内容
function LoginInfo(props) {
  // props.loginState = "success";
  // props.loginState = "fail";
  console.log(props);
  if (props.location.state.loginState === "success"){
    return (
      <Redirect to="/admin" />
    )
  }else {
    return  (
      <Redirect to="/login" />
    )
  }
}

8.路由函数式导航

class ChildCom extends React.Component {
  toIndex = ()=>{
    console.log(this.props);
    this.props.history.push("/",{msg:'从childCom发给首页的数据'})
      {/* 首页接收数据是以 this.props.location.state.msg */}  
  };
  render() {
    return (
      <div>
        <button onClick={this.toIndex}>跳转至首页</button>
      </div>
    );
  }
}

9.Switch组件

让switch组件内容的route只匹配1次,只要匹配到了内容,便不会往下匹配

<Router>
    <Switch>
        {/* 此时不会显示abc2页面 */}
        <Route path="/abc" exact component={()=>(<h1>这是abc1页面</h1>)} />
        <Route path="/abc" exact component={()=>(<h1>这是abc2页面</h1>)} />
    </Switch>
</Router>

十九、Redux状态管理树

​ 解决React数据管理(状态管理),用于中大型项目,数据比较庞大,且组件间的数据交互多的情况下使用

Redux的特点:

  1. Store:数据仓库,保存数据的地方
  2. state:state是一个对象,数据仓库的所有数据都放到state里面
  3. action:与Vue有点区别,action就是一个动作用于触发数据改变的方法,不是用于Ajax异步请求
  4. dispatch:将action动作触发成方法
  5. Reducer:是一个函数,通过获取动作,改变数据生成一个新的状态state从而改变页面

1.安装Redux

cnpm install redux --save

2.初始化仓库

// 1.导入redux

// 2.创建store仓库
const store = createStore(reducer);

// 3.初始化仓库数据以及方法
const reducer = function (state = {num: 0}, action) {
  console.log(action); // 输出{type: "add"}
  switch (action.type) {
    case "add":
      state.num++;
      break;
    case "decrement":
      state.num--;
      break;
  }
  return {...state}
};
// 4.调用数据
function add() {
  // 通过仓库的方法进行修改数据
  store.dispatch({type: "add"});
  // console.log(store.getState());
}
function decrement() {
  // 通过仓库的方法进行修改数据
  store.dispatch({type:"decrement"})
}
// 获取数据的方式
let state = store.getState()
// 5.修改视图--监听数据的变化重新渲染视图
store.subscribe(()=>{
        ReactDOM.render(<App />, document.getElementById('root'));
    }
)

3.React-redux

安装:

cnpm i react-redux --save

概念:

  1. Province

使用:

  1. 初始化仓库数据

    function reducer(state=0,action) {
      switch (action.type) {
        case "add":
          state.num++;
          break;
        case "decrement":
          state.num--;
          break;
        default:
          break;
      }
      return {...state}
    }
    
  1. 实例化数据仓库

    const store = createStore(reducer);
    
  1. 数据的获取--映射函数

    // 设置action
    let addAction = {
        type:"add"
    }
    
    // 将state映射到props函数中
    function mapStateToProps(state) {
      return {
        value:state.num,
      }
    }
    // 将修改state数据的方法,映射到props,默认会传入store里的dispatch方法
    function mapDispatchToProps(dispatch) {
      return {
        onAddClick:()=>{
          dispatch(addAction)
        },
     onDecrementClick:()=> dispatch({type:"decrement"})
      }
    }
    
  1. 将两个映射函数结合成一个新的App函数

    const App = connect(
      mapStateToProps,
      mapDispatchToProps
    )(Counter)
    
  2. 渲染组件

    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    )
    
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。