React基础

技术胖的前端路线图

红色:精通,且要有对应的实战经验
蓝色:熟练,达到基础练习量
灰色:了解,课程听完即可
黑色:视频学习地址
开源地址:http://gitee.com/jishupang/web_tlas
Simple React Snippets插件:react代码的快速生成器
科学上网?在谷歌浏览器中安装React Developer Tools插件
Easy Mock:如果没有后端数据,可以使用Easy Mock给自己造一个数据

一、npm安装项目依赖

// 安装node
// 判断node与npm有没有安装成功和版本号
node -v
npm -v
// 安装react脚手架
npm install -g create-react-app
// 创建react项目
// 方法一:使用cmd,先使用mkdir创建文件夹,在里面运行下面这条命令
// 方法二:使用vscode里面的terminal运行以下命令
create-react-app 项目名
// 运行react项目
npm run start
// 安装axios
npm install axios   // 安装到项目目录中,且不添加任何依赖
npm install -g axios  // 安装到全局,npm安装目录下的perfix
npm install -save axios  // 安装到项目目录中,并在package.json中的dependencies(生产环境:已发布)中添加依赖(相应的版本)
npm install -save-dev axios  // 安装到项目目录中,并在package.json中的dev-dependencies(开发环境中:测试,项目管理)中添加依赖(相应的版本)
npm install react-transition-group --save  // 安装动画库

二、项目目录

// 项目目录
src/index.js   // 项目的入口文件
src/App.js :   // 使用模块化编程进行的一个方法模块
src/serviceWorker.js  // 移动端开发的,PWA使用这个文件会离线浏览的功能。

三、特殊写法

// {Component}是一种解构赋值的的写法,相当于 Component = React.Componment
import React,{Component} from 'react'
// 相当于
import React from 'react'
const Component = React.Component()
// 虚拟DOM:快速反应动作,不会占用页面的渲染机制
// jsx语法,在一个 js 组件中,遇到 < > 就当成html来解析,遇到  {} 就当做js来解析
// 使用ReactDOM方式创建元素并挂载到页面中
var child1 = React.createElement('li',null,'JSPang.com')
var child2 = React.createElement('li',null,'I love React')
var root = React.createElement('ul',{className:'my-list',child1,child2})

// 使用jsx方式创建类组件并挂载到对应的位置
class App extends Component{
  // 类组件渲染到页面的内容
  render() {
    return (
      // jsx的语法:遇到 < > 就当成html来解析,遇到  {} 就当做js来解析
      <ul className='my-list'>
        <li>JS1</li>
        <li>JS2</li>
      </ul>
    )
  }
}

四、生命周期

定义:在某一时刻可以自动执行的函数

React基础_第1张图片

// 初始周期
构造函数constructor(set props&state)
// 挂载周期
// componentWillMount 是组件挂载前执行的(可用于 打印日志 和 查询数据库 操作)
// render 是state和props发生改变时会进行的渲染
// componentDidMount 是组件挂载完成后执行的(也可用于 打印日志 和 查询数据库 操作)
componentWillMount --> render --> componentDidMount
// 组件更新周期
// componentWillReceiveProps : 当子组件接受props时执行,当在顶层组件时是不会执行的(组件第一次存在于dom中,函数不会被执行,如果已经存在于dom中,函数才会被执行)
// shouldComponentUpdate : 组件(props或state)更新之前进行执行的,必须返回一个boolean值,返回true会继续向下执行其他生命周期,返回false则不会向下执行了
// componentWillUpdate : 在shouldComponentUpdate后执行,render前执行
// render : state和props发生改变时会进行的渲染
// componentDidUpdate : 改变完成后执行
props : componentWillReceiveProps -- shouldComponentUpdate -- componentWillUpdate -- render -- componentDidUpdate
states : shouldComponentUpdate -- componentWillUpdate -- render -- componentDidUpdate
// 组件删除周期
componentWillUnmount : 组件被删除时执行

五、在实践中学一些注意事项

5.1.public/index.html
DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React Apptitle>
  head>
  <body>
    <noscript>如果js代码受到阻止或没有跑成功会显示的内容noscript>
    
    <div id="root">div>
    <div>knb dmb d ddiv>
  body>
html>
5.2.src/index.js

项目入口文件

import React from 'react'
import ReactDOM from 'react-dom'
import XiaoJieJie from './XiaoJieJie'

// 将组件挂载到public/index.html 下面id="root"的div
ReactDOM.render(<XiaoJieJie/>,document.getElementById('root'))
5.3.src/XiaoJieJie.js

类组件:XiaoJieJie父组件

// {Component}是一种解构赋值的的写法,相当于 Component = React.Componment
import React,{Component,Fragment} from 'react'
import XiaoJieJieItem from './XiaoJieJieItem'    // 引入新的组件
import axios from 'axios'
import './index.css'

// 在js中写html的写法(类组件和函数组件)
// 类组件
class XiaoJieJie extends Component{

    // 初始周期:构造函数(set props&state)
    constructor(props){
        super(props)    // props调用的是父类Component的
        this.state = {
            inputValue : '',
            // react只能遍历数组类型的数据,不能遍历对象类型的数据
            servicelist : ['指甲油','种睫毛']
        }
    }

    // 挂载周期:componentWillMount --> render --> componentDidMount
    // componentWillMount : 组件挂载前执行的(可用于 打印日志 和 查询数据库 操作)
    // render : state和props发生改变时会进行的渲染
    // componentDidMount : 组件挂载完成后执行的(也可用于 打印日志 和 查询数据库 操作)
    // componentWillMount(){
    //     console.log("componentWillMount -- 组件将要挂载到页面时执行")
    // }
    // 用于请求远程数据,使用componentDidMount生命周期比较合适,只加载一次,后续不再进行请求
    componentDidMount(){
        // axios请求远程数据
        // https://web-api.junjin.im/v3/web/wbbr/bgeda是要访问的后台接口
        axios.post('https://baidu.com')
            .then((res)=>{  
                console.log('axios获取数据成功'+JSON.stringify(res))
            })
            .catch((error)=>{
                console.log('axios获取数据失败'+error)
            })
        // console.log("componentDidMount -- 组件挂载到页面后执行")
    }
    // 组件更新周期
    // props : componentWillReceiveProps -- shouldComponentUpdate -- componentWillUpdate -- render -- componentDidUpdate
    // states : shouldComponentUpdate -- componentWillUpdate -- render -- componentDidUpdate
    // componentWillReceiveProps : 当子组件接受props时执行,当在顶层组件时是不会执行的(组件第一次存在于dom中,函数不会被执行,如果已经存在于dom中,函数才会被执行)
    // shouldComponentUpdate : 组件(props或state)更新之前进行执行的,必须返回一个boolean值,返回true会继续向下执行其他生命周期,返回false则不会向下执行了
    // componentWillUpdate : 在shouldComponentUpdate后执行,render前执行
    // render : state和props发生改变时会进行的渲染
    // componentDidUpdate : 改变完成后执行
    // componentWillReceiveProps(){
    //     console.log("componentWillReceiveProps")
    // }
    // shouldComponentUpdate(){
    //     // 必须返回一个boolean值
    //     // 如果return true 就会继续往下执行componentWillUpdate和render了
    //     // 如果return false 就不会往下执行componentWillUpdate和render
    //     return false 
    // }
    // componentWillUpdate(){
    //     console.log("2 -- componentWillUpdate")
    // }
    // componentDidUpdate(){
    //     console.log("4 -- componentDidUpdate")
    // }
    // 组件删除周期
    // componentWillUnmount : 组件被删除时执行
    // 类组件渲染到页面的内容
    // jsx的语法:遇到 < > 就当成html来解析,遇到  {} 就当做js来解析
    render(){
        console.log("3 -- render -- state和props发生改变时会进行的渲染")
        return (
        	// 虚拟DOM只能由一个根标签,且标签必须闭合
            // 可以使用Fragment,也可以使用<>空标签
            <Fragment>
                <div>
                    {/* onChange事件绑定,当向输入框中输入内容时,会调用his.inputChange方法 */}
                    {/* react事件要绑定this(指针+时间绑定问题),bind(this),这样inputChange方法中的this就会指向当前XiaoJieJie组件了,使用箭头函数就不需要考虑绑定问题了 */}
                    <label htmlFor="servicelabel">增加服务:</label>  
                    <input 
                        id="servicelabel"           // label中的htmlFor用来绑定点击被激活的id,点击label就会激活输入框 
                        className="serviceinputCss"    // 引入css,需要使用className="serviceinputCss",不能直接使用class,以免跟类组件的class重名
                        value={this.state.inputValue} 
                        onChange={this.inputChange.bind(this)} 
                        ref = {(input) => {    // 使用ref箭头函数对输入框中的值进行绑定,input相当于输入的内容
                            this.input = input  
                        }}
                    /> 
                    &nbsp;&nbsp;
                    <button onClick={this.addService.bind(this)}>增加服务</button>
                </div>
                {/* 给ul进行ref绑定,这样就可以获取ul这个对象 */}
                <ul ref={(ul) => {this.ul = ul}}>   
                    {
                        // 使用map来进行循环遍历数组
                        // map函数内部使用的是一个箭头函数,前面一个括号是参数,后面一个大括号是执行的函数
                        // item是数组中的每一项,index是每一项的索引值(索引是从0开始的)
                        this.state.servicelist.map((item,index) => {
                            // map循环遍历需要有一个key值,不能只使用索引值,因为index索引值会重复,可以加上item项来减少重复的可能
                            return (  // 只有return加一个小括号,才支持html标签换行
                                // 
  • // key={index+item} // onClick={this.deleteItem.bind(this,index)} // 删除单个服务项,要传递一个index索引项 // dangerouslySetInnerHTML={{__html:item}} // 将每一项都使用dangerouslySetInnerHTML来解析,这样可以将输入html代码解析,显示成对应的样式,如

    烫睫毛

    ,会显示成一个黑体加粗的烫睫毛
    // > //
  • <XiaoJieJieItem key = {index+item} // 用来循环时有一个唯一的key值 serviceitem = {item} // 父组件给子组件传值:属性={值}的方式 index = {index} deleteItem = {this.deleteItem.bind(this)} // 父组件给子组件传方法:属性={this.方法名.bind(this)}的方式 /> ) }) } </ul> </Fragment> ) } // 改变输入框中的值 inputChange(e){ // this.state.inputValue = e.target.value 在react框架中这是一个错误的方法 // 需要使用setState的方法赋值 this.setState({ inputValue : this.input.value // 使用ref箭头函数对输入框中的值进行绑定 // inputValue : e.target.value // e.target.value 获取输入框的内容 }) } // 添加服务按钮 addService(){ // 需要使用setState的方法赋值 if(this.state.inputValue === ''){ alert('您还没有输入任何服务') }else{ // 虚拟DOM,渲染是需要时间的 // this.setState是一个异步的方法,是浏览器在空闲的时候才会执行的内容 this.setState({ // ... :表示扩展运算符,相当于将数组中的每一个值都放到新数组中 // servicelist:['指甲油','种睫毛',this.state.inputValue],用中括号括住表示在数组中再添加一个值 servicelist:[...this.state.servicelist,this.state.inputValue], inputValue:'' },() => { // 如果遇到ref绑定错误或获取数据不准确,填setState的坑,在setState后面提供了一个回调函数,在这里面来进行后续的一些操作 console.log(this.ul.querySelectorAll('li').length) // 使用ref获取ul对象,使用querySelectorAll来获取所有li的子节点 }) } } // 删除单个服务项 deleteItem(index){ // 一个超级大的坑 // 正确写法是获取一个局部的newservicelist,将其赋值给声明的变量中,然后再操作局部数组的值,最后再使用setState来提交赋值 // 错误写法:不能直接操作 this.state.servicelist 中的值,因为后期做性能优化的时候比较麻烦 let newservicelist = this.state.servicelist newservicelist.splice(index,1) // splice方法代表从index索引项开始删除一个值 this.setState({ servicelist:newservicelist }) } } // ES6导出语法 export default XiaoJieJie
    5.4.src/XiaoJieJieItem.js

    类组件:XiaoJieJieItem子组件

    // 拆分组件
    
    // 单向数据流:子组件只能接受父组件传的值,但是不能直接干预父组件,就是不能给父组件直接传值
    // 父组件可以给子组件传值:    
    // 子组件调用父组件传的值:{this.props.serviceitem}
    
    // 解决单向数据流的问题:父组件给子组件传递一个方法,通过子组件调用父组件的方法来解决
    // 父组件给子组件传方法:
    // 子组件调用父组件传的方法:{this.props.deleteItem}
    import React, { Component } from 'react'
    
    // 父组件对子组件传值进行数据校验:PropTypes
    import PropTypes from 'prop-types'
    class XiaoJieJieItem extends Component {
    
        constructor(props){
            super(props)
            // 在构造函数中给函数名进行绑定,方便以后的性能优化,在下面就只需要使用this.handleClick,就不需要再写bind(this)
            this.handleClick = this.handleClick.bind(this)   
        }
    
        // 进行性能优化
        // 如果输入框中的值不等于父组件传的值,就继续执行后续的render等生命周期
        // 如果相等,就说明没有在操作输入框了,就不继续执行后续的生命周期了
        shouldComponentUpdate(nextProps,nextState){
            if( nextProps.serviceitem != this.props.serviceitem){
                return true
            }else{
                return false
            }
        }
    
        // 在接受父组件传来的props时执行
        // componentWillReceiveProps(){
        //     console.log("componentWillReceiveProps")
        // }
        // componentWillUnmount : 组件被删除时执行
        // componentWillUnmount(){
        //     console.log("组件被删除时执行")
        // }
    
        // 函数式编程:在类组件中写方法
        // 好处:代码清晰,前端自动化测试更加方便
        render() { 
            return (
                // 使用 “{this.props.调用子组件时传值的属性}” 来获取父组件给子组件传的值
                <li onClick={this.handleClick}>
                    {this.props.avname}为你服务--{this.props.serviceitem}
                </li>
            );
        } 
        handleClick(){
            // 调用父组件的删除方法
            this.props.deleteItem(this.props.index)
        }
    }
    
    // PropTypes用来对传递的数据进行校验
    // isRequired代表这个数据必须由父组件传递,不传递就会报错
    XiaoJieJieItem.propTypes = {   // 上面的p是小写 propTypes ,下面的P是大写 PropTypes
        avname:PropTypes.string.isRequired,   
        serviceitem:PropTypes.string,  // serviceitem是string类型的
        index:PropTypes.number,        // index是number类型的
        deleteItem:PropTypes.func      // deleteItem是func方法
    }
    // 如果使用了isRequired又不想传值,就需要在子组件中设置一个默认的值
    XiaoJieJieItem.defaultProps = {
        avname:'小敏'
    }
    
    export default XiaoJieJieItem;
    
    5.4.src/Hello.jsx
    import React, { Component } from 'react'
    import './index.css'
    
    class HelloComponent extends Component {
        // 构造器调用一次
        constructor(props) {
            // 在super前不能调用this
            super(props);
            // 解决updateWeather中this的指向问题
            this.updateWeather = this.updateWeather.bind(this)
            this.state={
                content:'前端js框架列表',
                // isHot:true,
                // react只能遍历数组类型的数据,不能遍历对象类型的数据
                list:['Angular','React','Vue'],
            }
        }
        // 可以将state中的值直接写到类组件中
        state={isHot:true}
        // render调用1+n次,第一次调用是初始化调用,n是状态更新的次数 
        render() { 
            // 使用解构赋值的方式先取到isHot
            // const {isHot} = this.state.isHot
            return (
                // 虚拟DOM只能由一个根标签,且标签必须闭合
                <>
                    {/* 
                        给html标签加样式
                        方式一:className,要引入对应的css文件
                        方式二:内联样式,style={{'key':'value'}} 
                    */}
                    <h2 id='atguigu' className='title' style={{'color':'green'}}>
                        <span>{this.state.content}</span>
                        {/* onClick={this.updateWeather} 不能写成 onClick={this.updateWeather()} 等被点击的时候才调用函数,而不是直接调用函数然后进行赋值*/}
                        <span>今天天气很{this.state.isHot?'炎热':'凉爽'}</span><button onClick={this.updateWeather}>点击</button>
                    </h2>
                    <ul>
                        {/* 大括号里面不能写js语句【if,for,switch】,只能写js表达式【表达式都会有一个只,如a,a+b,demo(1),arr.map(),function test(){}】 */}
                        {
                            this.state.list.map((item,index) => {
                                return <li key={item+index}>{item}</li>
                            })
                        }
                    </ul>
                </>
            );
        }
        // updateWeather放在HelloComponent原型上,通过实例调用
        // 由于updateWeather是onCLick的回调,不是通过实例调用的,是直接调用
        // 类中的方法默认开启了局部的严格模式,所以updateWeather中的this为undefined
        // 但当调用时采用bind(this),绑定了this,this就指向HelloComponent组件实例了
        updateWeather(){
            const isHot = this.state.isHot
            // 状态不能直接更改,需要借助setState来修改状态,且更新是一种替换,而不是更新
            this.setState({
                isHot:!isHot
            })
        }
    }
     
    export default HelloComponent;
    
    

    六、动画效果

    import React, { Component } from 'react'
    import {CSSTransition,TransitionGroup} from 'react-transition-group'
    import './index.css'
    
    class Boss extends Component {
        constructor(props) {
            super(props);
            this.state = {
                isShow : true
            }
            this.toToggole=this.toToggole.bind(this)   // 让this指向正确
        }
        render() { 
            return (  
                <>
                    <CSSTransition
                        in={this.state.isShow}   // 开关打开:显示文字
                        timeout={2000}    // 显示出来的时间2s
                        classNames="boss-text"   // 这里是index.css中的样式前缀
                        unmountOnExit    // DOM元素退场时直接给元素删除
                    >
                        {/* 
    Boss级任务 -- 孙悟空
    */
    } <div>Boss级任务 -- 孙悟空</div> </CSSTransition> <div> <button onClick={this.toToggole}>召唤Boss</button> </div> </> ); } toToggole(){ this.setState({ isShow : this.state.isShow ? false : true }) } } export default Boss;
    .serviceinputCss{
      border: 3px solid #ae7000;
    }
    .show{
      /* 
        opacity: 1;
        transition: all 1.5s ease-in;  // 动画形式消出现
      */
      animation: show-item 2s ease-out forwards;
    }
    .hide{
      animation: hide-item 2s ease-out forwards;   /* forwards 可以在动画运行到最后一帧时停止,否则会跳到第一帧 */
    }
    
    /* 使用@keyframes创建动画 */
    @keyframes show-item{
      /* 添加关键帧 */
      0%{
        opacity: 0;
        color: yellow;
      }
      50%{
        opacity: 0.5;
        color: red;
      }
      100%{
        opacity: 1;
        color: green;
      }
    }
    @keyframes hide-item{
      /* 添加关键帧 */
      0%{
        opacity: 1;
        color: yellow;
      }
      50%{
        opacity: 0.5;
        color: red;
      }
      100%{
        opacity: 0;
        color: green;
      }
    }
    
    /* enter是指动画入场的样式 */
    boss-text-enter{
      opacity: 0;
    }
    /* enter-active是指动画入场到结束之前的样式 */
    boss-text-enter-active{
      opacity: 1;
      transition: opacity 2000ms;
    }
    /* enter-done是指动画入场结束后的样式 */
    boss-text-enter-done{
      opacity: 1;
    }
    /* exit是指动画出场的样式 */
    boss-exit-enter{
      opacity: 1;
    }
    /* exit-active是指动画出场到结束之前的样式 */
    boss-text-exit-active{
      opacity: 0;
      transition: opacity 2000ms;
    }
    /* exit-done是指动画出场结束后的样式 */
    boss-text-exit-done{
      opacity: 0;
    }
    

    你可能感兴趣的:(Web前端,react.js)