react零基础入门到项目实战-01

本文详细介绍了React的基础知识,包括React与传统MVC的区别、虚拟DOM的概念和优势、创建React应用的脚手架create-react-app以及项目目录结构。重点讲解了JSX语法、组件的创建(类组件、函数组件和嵌套组件)、样式处理、事件处理、状态管理和数据流。此外,还通过多个实战案例,如todoList和影院查询,深入探讨了React的使用技巧和最佳实践。

React与传统MVC的关系

在这里插入图片描述


react的特性

在这里插入图片描述

  • 声明式:我只告诉你我想的东西是什么,具体怎么实现不需要我去实现;只需要根据react模板写好的规则去书写即可,而react怎么把数据映射到视图上的、怎么渲染到页面上的不需要我们去关心;将来数据发生了改变,react怎么去更新模板不需要我们去关心,我只负责改变数据即可;我们只关注数据层的改变
  • DOM操作频繁十分影响前端性质,虚拟DOM可以减少DOM的交互
  • JSX扩展文件名:将HTMLCSSJS合并在一个组件中,然后react处理器解析JSX文件
  • 单向数据流:所有的掌控权都在顶层:顶层可以直接传数据给底层,而底层想要改变什么需要请示顶层授权;数据从父组件流向子组件,如果数据改变,需要从子组件传向父组件,父组件更新完毕后再次流向子组件

虚拟DOM

  • 传统DOM
    在这里插入图片描述
    如果减少一个DOM,底下的DOM需要往上移,必须经历文档重排回流的过程,浏览器负荷大,性能差
  • 虚拟DOM
    在这里插入图片描述
    虚拟DOM其实就是用JS对象把数据显示出来,然后比较前后两个JS对象的DOM树节点
    比如说,原始数据为[11, 22, 33]
    虚拟DOM就是将其转为JS对象{type: 'li', text: 11} {type: 'li', text: 22} {type: 'li', text: 33}
    更新后的虚拟DOM为{type: 'li', text: 11} {type: 'li', text: 33},即删除掉了第二条数据
    然后两个虚拟DOM对比(diff算法,最小代价进行对比),第一条数据与第二条数据节点都没有改变,第二条数据节点不同,仅把第二条数据节点转换为补丁,更新到真正的DOM中即可

也可称为virtual dom vdom vnode


脚手架create-react-app

在这里插入图片描述
在这里插入图片描述


react项目目录结构

在这里插入图片描述


编写第一个react应用程序

在这里插入图片描述
src文件夹下面新建index.js文件,为项目的入口文件(这是react脚手架本身就配置好的),里面的代码自动跑;
同理public文件夹下的index.html为入口html文件,其中最主要的就是挂载的id为root的div
在这里插入图片描述

import React from 'react';
import ReactDom from 'react-dom';

ReactDom.render(<h1>你好react</h1>, document.getElementById("root"));

感觉将html与js混合写在js文件里面
这就是JSX语法(js+xml)


JSX语法

在这里插入图片描述
源码如下:

import React from 'react';
import ReactDom from 'react-dom';

ReactDom.render(<div>
    你好react
</div>, document.getElementById("root"));

下面这一段代码使用React.createElement,与上面这段代码的效果一致:

ReactDom.render(React.createElement("div", {
   
   
    id: 'id',
    className: 'class'
}, '111111'), document.getElementById('root'))

在这里插入图片描述
Babel的JSX编译器,实际上就是帮我们把第一段代码(类似HTML的JSX结构)编译成第二段代码(js的对象结构),只有这样浏览器才能认识这段js语句
在这里插入图片描述


组件的创建——类组件

在这里插入图片描述

首先先复习es6的类如何使用

// 类用大写字母
class Test {
   
   
    // 构造器函数
    constructor() {
   
   
        // 成员属性
        this.a = 1
    }
    // 成员函数
    testA() {
   
   
        console.log('testA')
    }
}

// 继承类
class ChildTest extends Test {
   
   
    testB() {
   
   
        console.log('testB')
    }
}

var obj = new Test()
obj.testA()
console.log(obj.a)

var childObj = new ChildTest()
childObj.testA()
console.log(childObj.a)

然后在index.js里面import这个js文件即可

import './components/classComp';

然后开始写类组件,可以复用

import React from 'react';

// 继承react的组件类
class App extends React.Component {
   
   
    render() {
   
   
        return (
            <div>HELLO REACT COMPONENT</div>
        )
    }
}

export default App

在index.js中引入App后,把它当标签用即可

import React from 'react';
import ReactDom from 'react-dom';
import App from './components/classComp';

ReactDom.render(<App></App>, document.getElementById('root'))

在这里插入图片描述
快捷键:输入rcc加回车,自动生成class组件

import React, {
   
    Component } from 'react'

export default class nestComp extends Component {
   
   
    render() {
   
   
        return (
            <div>
                
            </div>
        )
    }
}


组件的创建——函数组件

拓展:JSX必须有一个父元素包裹住里面的内容
在这里插入图片描述

function App () {
   
   
    return <div>functional component</div>
}

export default App

下段代码与上面代码效果一模一样,注意:如果return后面想要换行,那么必须包上括号

function App () {
   
   
    return (
        <div>functional component</div>
    )
}

export default App

在react版本16.8之前,函数式组件是没有状态state的,因此使用的比较少;16.8版之后,函数式组件也引入了react hooks


组件的创建——嵌套

在这里插入图片描述
父子组件,兄弟组件

以下抽取组件,注意三个子组件的三种不同写法,都可以

import React, {
   
    Component } from 'react'

class Navbar extends Component {
   
   
    render() {
   
   
        return (
            <div>navbar</div>
        )
    }
}

function Swiper () {
   
   
    return (
        <div>swiper</div>
    )
}

const Tabbar = () => <div>tabbar</div>

export default class nestComp extends Component {
   
   
    render() {
   
   
        return (
            <div>
                <Navbar></Navbar>
                <Swiper></Swiper>
                <Tabbar></Tabbar>
            </div>
        )
    }
}

注意,此时要想在Navbar里面再嵌套一个子组件,应该如下嵌套:

class Navbar extends Component {
   
   
    render() {
   
   
        return (
            <div>
                navbar
                <Child></Child>
            </div>
        )
    }
}

组件的样式(行内样式)

react中的一对大括号为模板语法规则,里面的内容会按照js指令执行

import React, {
   
    Component } from 'react'

export default class styleComp extends Component {
   
   
    render() {
   
   
        let myName = 'corn6'

        return (
            <div>
                {
   
   10 + 20}-{
   
   myName}
                {
   
   10 > 20 ? 'aaa' : 'bbb'}
            </div>
        )
    }
}

在这里插入图片描述
注意,react中组件的行内样式style,只能为对象形式,如下:

import React, {
   
    Component } from 'react'

export default class styleComp extends Component {
   
   
    render() {
   
   
        let obj = {
   
   
            background: 'yellow'
        }

        return (
            <div>
                {
   
   /* 此处obj外面包裹一个大括号,是因为要解析为变量 */}
                <div style={
   
   obj}>111</div>
            </div>
        )
    }
}

因此看到下面这段代码,不要理解为双大括号;里面一层括号是因为react中组件的行内样式style,只能为对象形式;而外面一层括号就是react的模板,用于解析js变量

<div style={
   
   {
   
   background: 'red'}}>22222</div>
let obj = {
   
   
            // js中的变量,不出现连字符-,改为驼峰写法
            backgroundColor: 'yellow',
            fontSize: '30px'
        }

在这里插入图片描述
官方推荐的就是行内样式!!


组件的样式(class)

在这里插入图片描述
className声明好类名

<div className='active'>33333</div>
<div id='myapp'>44444</div>

新建一个css文件,写样式

.active {
   
   
    background: blue;
}

#myapp {
   
   
    background: green;
}

回到组件的js文件,import css文件

import '../css/styleComp.css'

之所以可以在js文件里面直接引入css文件,是因为背后有脚手架封装的webpack的支持

拓展,编译过后,实际上就是把css作为内部样式插入到head标签里面了,这是webpack帮我们做的,如下图
在这里插入图片描述


for关键字改为htmlFor

跟上述class改为className一样的道理,因为它们原先都是js中的关键字,没写清楚的话浏览器还需要费时间去解析

<label htmlFor='username'>用户名:</label>
<input type='text' id='username'></input>

事件处理4形式

import React, {
   
    Component } from 'react'

export default class methodComp extends Component {
   
   
    render() {
   
   
        return (
            <div>
                <input></input>
                <button onClick={
   
   
                    () => {
   
   
                        console.log('click')
                    }
                }>Add</button>

                <button onClick={
   
    this.handleClick }>Add2</button>

                <button onClick={
   
    this.handleClick3 }>Add3</button>

                <button onClick={
   
    
                    () => {
   
   
                        this.handleClick()
                        this.handleClick3()
                    }
                 }>Add4</button>
            </div>
        )
    }

    handleClick() {
   
   
        console.log("click2")
    }

    handleClick3 = () => {
   
   
        console.log("click3")
    }
}

注意onClick后面的事件需要用一个大括号包裹,并且推荐使用箭头函数写,没有this的问题

注意第二三种,调用时不要加()

注意第四种,调用时要加(),并且在逻辑很复杂的情况下,使用第四种调用方法可维护性更高;并且后续需要传入参数的时候,只能使用第四种!


各种形式事件处理this指向问题

首先讲清楚js改变this指向的3种方法

/*
    js改变this指向的3种方法:
    call,
    apply,
    bind
*/

var obj1 = {
   
   
    name: 'obj1',
    // this => 谁调用我指向谁
    // 此时this指向obj1
    getName() {
   
   
        console.log(this.name)
    }
}

var obj2 = {
   
   
    name: 'obj2',
    getName() {
   
   
        console.log(this.name)
    }
}

obj1.getName()
// call用于修正this指向,
// 首先找到getName方法,然后强行让getName方法的this改为指向obj2
// call:改变this指向,同时自动函数
obj1.getName.call(obj2)
// apply同call:改变this指向,同时自动函数
obj1.getName.apply(obj2)
// bind:仅改变this指向,要想执行函数,手动添加()
obj1.getName.bind(obj2)()

在这里插入图片描述
然后声明一个状态state,观察各种形式如何获取到这个state

import React, {
   
    Component } from 'react'

export default class methodComp extends Component {
   
   
    a = 100

    render() {
   
   
        return (
            <div>
                <input></input>
                {
   
   /* 第一种,函数里面的this,跟外面render()的this保持一致 */}
                <button onClick={
   
   
                    () => {
   
   
                        console.log('click1', this.a)
                    }
                }>Add</button>

                {
   
   /* this指向谁:谁调用我我就指向谁
                第二种,handleClick被react调用,this指向undefined
                总之,不会指向App实例
                因此,想要在这种调用方法里面访问到App的state,需要改变this的指向
                使用.bind(this),让handleClick方法里面的this,指向外面的这个this.handleClick的this即可
                */}
                <button onClick={
   
    this.handleClick.bind(this) }>Add2</button>

                {
   
   /* 
                第三四种,又是箭头函数
                根本不关心谁调用了我,永远保持跟外部作用域相同
                */}
                <button onClick={
   
    this.handleClick3 }>Add3</button>

                <button onClick={
   
    
                    () => {
   
   
                        this.handleClick()
                        this.handleClick3()
                    }
                 }>Add4</button>
            </div>
        )
    }

    handleClick() {
   
   
        console.log("click2", this.a)
    }

    handleClick3 = () => {
   
   
        console.log("click3", this.a)
    }
}

因此不推荐第二种写法,其他三种都可以

总结如下:
在这里插入图片描述


react事件绑定与原生事件绑定的区别

react并不会真正地绑定事件到每一个具体的元素上,而是使用事件代理的模式:将事件绑定在根节点上,然后模拟一套冒泡机制实现,好处是占用内存很小,因为不需要将事件绑定到具体某个元素上


event事件对象

在这里插入图片描述

// 事件对象evt不是我们传的,而是react内部构建好的
    handleClick3 = (evt) => {
   
   
        console.log("click3", this.a, evt)
    }

打印出来如下:
在这里插入图片描述
同样有阻止冒泡,默认行为evt.target


ref的应用

在这里插入图片描述

import React, {
   
    Component } from 'react'

export default class methodComp extends Component {
   
   
    a = 100

    render() {
   
   
        return (
            <div>
                {
   
   /* ref,起引用的作用
                后续想要拿到input,可以直接用this.refs.mytext拿到整个input标签
                */}
                <input ref='mytext'></input>
                <button onClick={
   
   
                    () => {
   
   
                        console.log('click1', this.refs.mytext, this.refs.mytext.value)
                    }
                }>Add</button>
            </div>
        )
    }
}

在这里插入图片描述
然而以上直接给ref一个字符串值的写法不被提倡,以下写法更为标准:
调用React.createRef方法赋给一个状态变量,再把这个变量绑到ref上,由于变量无法重复声明,因此比字符串方法更安全

import React, {
   
    Component } from 'react'

export default class methodComp extends Component {
   
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值