• 初识React -- 一篇文章让你会用react写东西


    认识React

    • 声明式设计 :之前我们想要去实现一个动态功能,一般都会去使用ifelse… 声明式设计只需要告诉我要实现什么样的,具体做法React来帮你做,这也是因为React使用事件委托代理的方法处理UI渲染导致我们无法直接操作指定DOM元素,交给React自行处理
    • 高效灵活 :最大限度减少与 DOM 的交互以及与库和框架的很好的配合
    • 组件化:复用
    • 单向响应数据流
    • 虚拟 DOM
    • JSX:JavaScript 语法的扩展

    React!玩起来

    具体安装配置环境我就不做过多的解释了,因为官网写的清清楚楚

    我们直接来使用

    1. 导入react模块:
    import React from "react"
    
    • 1
    1. 导入reactDOM模块:
    import ReactDOM from "react-dom/client"
    )
    
    • 1
    • 2
    1. 插入组件
    // 自react 18之后会和之前的方法不太一样:
    const rootApp = document.querySelector("#root")
    ReactDOM.createRoot(rootApp).render(<section id="div" className="div_box">nihao</section>)
    
    • 1
    • 2
    • 3

    首先root节点是必须要获取的,因为我们的组件是要渲染到root节点中的,那么,root节点是什么?为什么我们要渲染到root节点中?

    让我们来看看下面这张图,你就明白了 😁
    react-html
    我们看到react中的唯一html文件body节点之中只有root这样一个节点,我们之后的组件都会添加到该root节点中

    其实原理是这样的 😐

    ReactDOM.createRoot(document.querySelector("#root")).render(
    	React.createElement(
    		"section",
    		{
    			id: "div",
    			className: "div_box",
    		},
    		"nihao"
    	)
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    等等,为什么HTML元素直接可以写到js文件中使用且不报错???🤔

    其实这是JSX语法,有兴趣的可以专门去了解一下,总之 ------ 好用就行😁


    外部导入组件

    大家一定会问,我感觉这样更乱了,并没有什么所谓的组件化啊🤐

    我们可以用ES6的import语法导入外部模块啊:

    import App from "./base_01/test_01"
    
    const rootApp = document.querySelector("#root")
    ReactDOM.createRoot(rootApp).render(
    	<React.StrictMode>
    		<App />
    	</React.StrictMode>
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    组件我该怎么写?

    现在我已经导入了外部js文件了呀,那么这个js文件我该怎么写呢?

    // 创建组件类时必须导入react核心模块
    import React from "react"
    
    class App extends React.Component {
    	render() {
    		return (
    			<section>
    				<p>Hello React Component</p>
    			</section>
    		)
    	}
    }
    
    // 向外共享
    export default App
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    如果你接触过node,那么这些你很容易理解

    还有一种更爽的写法,我们可以把共享和创建连写,并且不需要继承React的Component类,我们可以使用import语法直接将react的component类导入:

    import React, { Component } from "react"
    
    export default class App extends Component {
    	render() {
    		return (
    			<section>
    				<h1>React很有趣 -- 你也来一起玩吧!</h1>
    			</section>
    		)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    COOL!

    等等,我有一个奇妙的想法,这样是否可以呢?😶:

    import React, { Component } from "react"
    
    class Test extends Component {
    	render() {
    		return <p>你好呀</p>
    	}
    }
    
    export default class App extends Component {
    	render() {
    		return (
    			<section>
    				<h1>React很有趣 -- 你也来一起玩吧!</h1>
    				<Test></Test>
    			</section>
    		)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    OK,这样写完全可以(这就是组件的嵌套)

    我相信大家知道这是ES6的class类,那么我们可不可以使用函数组件呢?它们又有什么区别呢?

    import React from "react"
    
    function App2() {
    	return (
    		<section>
    			<div>GOOD!函数式组件</div>
    		</section>
    	)
    }
    // 函数式组件无状态组件(16.8之前)
    // 16.8之后,有了react hooks -- 有状态性
    export default App2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    所以说函数组件本身是无状态的,比如我们去操作DOM的时候,react函数式组件不会像类组件一样使用this.setState方法去渲染局部UI数据,那么函数式组件就不能使用了吗?并不是,我们还可以使用hooks,可以让react函数式组件具有状态性😊,不过这都是后话了,这篇文章是初识react,所以我们作为简单了解就好

    函数式写起来真的很爽,配合我们的箭头函数,真的飞起😋:

    const Test = () => <div>Hello</div>
    
    • 1

    react样式我该怎么写?

    我们会插入HTML文档了,整体架构出来了,那么样式我们怎么写呢?

    react推荐我们使用行内式,那么我们先来看看外部CSS文件导入该怎么写:

    import "./CSS/test_04.css" // 导入CSS模块 -- webpack的支持(将css文件以style标签的形式插入到文档中)
    
    class Headerbar extends Component {
    	render() {
    		return (
    			<div>
    				<input type="text" id="username"></input>
    			</div>
    		)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这很简单,不需要过多阐述,我们来看看行内式该怎么写:

    class Headerbar extends Component {
    	render() {
    		const user_name = "barve-airpig"
    		
    		// 类中定义对象供样式使用
    		let styleObj = {
    			background: "yellow",
    		}
    		// react模板字符串{}  VUE的插值表达式是双花括号:{{  }}
    		// 原生写法:`${  }`
    		return (
    			<div style={styleObj}>
    				Headerbar {20 + 30} -- {user_name} -- {20 - 20 || "Hello"}
    				<p style={{ fontWeight: "bold" }}>你好</p>
    				<div className="test_box">模块导入外部CSS文件有用吗?</div>
    				<label htmlFor="username">用户名:</label>
    				<input type="text" id="username"></input>
    			</div>
    		)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    这些都很好理解,唯一使我们困惑的就是:htmlFor、className是什么属性,虽然知道这是什么意思,但是react中样式属性都要重新记一遍吗?这可有点不妙啊

    并不是这样,只是部分属性需要我们注意,这也是避免一些问题而修改的


    事件绑定

    终于到了初识react的重头戏了,架构、样式、行为,我们都会了,那么我们的react就入门了

    import React, { Component } from "react"
    
    export default class App extends Component {
    	render() {
    		return (
    			<section>
    			// 方法一:使用箭头函数,点击事件触发事件回调执行代码
    				<button
    					onClick={() => {
    						console.log("Bye addEventListener")
    					}}
    				>
    					点击按钮1
    				</button>
    			// 方法2:点击事件触发事件回调执行当前继承类APP中的handleClick方法(handleClick是普通函数)
    			// 注意这里只有点击才会执行对应函数
    				<button
    					onClick={() => {
    						this.handleClick()
    					}}
    				>
    					点击按钮2
    				</button>
    			// 方法3:点击执行对应处理函数,不过不要加小括号,否则函数会自动执行,下次执行也无效
    				<button onClick={this.handleClick}>点击按钮3</button>
    			// 方法4:点击执行对应箭头函数
    				<button onClick={this.handleClick2}>点击按钮3</button>
    			</section>
    		)
    	}
    
    	handleClick() {
    		console.log("你好!😁")
    	}
    
    	handleClick2 = () => {
    		console.log("react真好玩!")
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    大家看到我列出了四个版本,没有太大的区别,一定想到了我想要说什么,没错,就是this指向问题:

    以上代码输出结果为:
    请添加图片描述

    这当然没有什么问题,但是我们想要知道的是this指向

    import React, { Component } from "react"
    
    export default class App extends Component {
    	render() {
    		return (
    			<section>
    				<input />
    				<button
    					onClick={() => {
    						console.log(this)
    					}}
    				>
    					点击按钮1
    				</button>
    
    				<button
    					onClick={() => {
    						this.handleClick()
    					}}
    				>
    					点击按钮2
    				</button>
    
    				<button onClick={this.handleClick}>点击按钮3</button>
    				<button onClick={this.handleClick2}>点击按钮3</button>
    			</section>
    		)
    	}
    
    	handleClick() {
    		console.log(this)
    	}
    
    	handleClick2 = () => {
    		console.log(this)
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    请添加图片描述
    等等,谁是undefined????

    没错,按钮三的this去执行的函数中找this,但是没想到这是一个普通函数,this没有指向,而其他的都是箭头函数,箭头函数this指向是最近父作用域链的this,那么当然是继承类APP了

    不行,我今天就要用这种方法,那么有没有解决方法😡,哈哈哈,当然有,你还记得我们的ES6 – bind()方法吗,修改this值且不会自动执行,用在这里再合适不过了:

    <button onClick={this.handleClick.bind(this)}>点击按钮3</button>
    
    • 1

    React 事件绑定和原生事件绑定有什么区别呢?

    • 没有绑定在具体的 DOM 节点身上,绑定在具体的 DOM 身上是比较消耗内存的,react 采用的是一种事件委托(时间代理)的方案(模式)

    react没有获取DOM节点-也有事件对象吗?

    • 和普通浏览器一样,事件会被自动传入一个event事件对象,这个对象和普通的浏览器event对象所包含的方法和属性都基本不一致,不同的是 react 中的event对象并不是浏览器提供的,而是它自己内部所构建的,它同样具有event.stopPropagation阻止冒泡以及event.preventDefault阻止默认行为等等这种常用的方法

    ref指令

    相信学习过Vue的同学们对于ref指令并不陌生,其实再react中功能很相像啊

    export default class App extends Component {
    	render() {
    		return (
    			<section>
    				<input ref={"mytext"} />
    
    {/* 避免变量名重复 -- 不建议自己写ref指令 -- refs将被弃用 */}
    
    				<button
    					onClick={() => {
    						console.log("Bye addEventListener", this.refs.mytext.value)
    					}}
    				>
    					点击按钮1
    				</button>
    			</section>
    		)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    给元素一个ref属性值来挂钩,这样的做法对于Vue来说司空见惯了,不过这种方法快被弃用了(react与vscode都提醒我了,我很害怕😂),所以作为了解就好

    接下来才是真正的新方法:

    对了,不推荐上面的写法是因为避免ref指令值重复导致不必要的错误

    export default class App extends Component {
    // 让react为我们创建一个ref值,我们指定名称就好
    	myRef = React.createRef()
    
    	render() {
    		return (
    			<section>
    			// 使用ref值
    				<input ref={this.myRef} />
    
    				<button onClick={() => this.handleClick()}>点击按钮1</button>
    			</section>
    		)
    	}
    
    	handleClick = () => {
    	// 这种获取方式很特殊啊,this.myRef.current.value
    		if (this.myRef.current.value.trim()) console.log(this.myRef.current.value)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    上面我们提到一个非常疑惑的点:

    【这种获取方式很特殊啊,this.myRef.current.value】

    为什么要使用这种方法获取DOM元素呢???

    这很好解释,让我们倒推myRef的属性 :

    console.log(this.myRef.current.value, this.myRef)
    
    • 1

    请添加图片描述

    现在你知道为什么要使用current了吧,current中的input才是真正的input元素,底层实现我们之后再去讨论


    初识react状态

    • 我们都知道在react开发中,我们应该尽量减少DOM操作,那么我们需要改变UI数据,就需要依靠状态

    • 状态是什么?它是一个数据载体,就是组件描述某种显示情况的数据,由组件自己设置和更改,也就是说由组件自己维护,使用状态的目的就是为了在不同的状态下使组件的显示不同,也就是数据更改,视图自动更新

    • 我想大家应该知道Vue的数据驱动视图思想吧,react没有和Vue一样去底层拦截数据,感知不到数据的改变,所以react使用状态来改变数据

    • this.state是纯JavaScript对象 - 在Vue中,data属性是利用Object。defineProperty处理过的,更改data的数据会触发数据的getter和setter,但是react中没有做过这样的处理,如果直接更改的话,react是无法得知的,所以需要使用特殊的方法更改状态setState

    废话不多说,开整代码:

    import React, { Component } from "react"
    
    export default class App extends Component {
    // 定义状态值
    	state = {
    		show: true,
    	}
    
    	render() {
    		return (
    			<section>
    				<h1>React很有趣 -- 你也来一起玩吧!</h1>
    				<button onClick={() => this.likeFunc()}>
    					{this.state.show ? "收藏" : "取消收藏"}
    				</button>
    			</section>
    		)
    	}
    
    	likeFunc = () => {
    	// 我们不能和VUE一样直接修改data值以达到数据驱动视图的效果,我们可以使用setState方法,让react为我们自行修改
    		this.setState({
    			show: !this.state.show,
    		})
    		// 间接修改state状态
    
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    请添加图片描述
    这样我们就可以随心所欲的点击更改UI数据了

    你一定会说,我直接原生写都比这方便,这有什么用???

    我们不如再往后看看react的美妙,它的组件化,它的性能优化… 😉


    关于数据处理的循环渲染

    当后端给我们返回一堆数据我们该怎么循环渲染到UI页面上??

    这是一个非常重要的问题 😎

    但是这同样离不开我们的状态,因为循环渲染必定会用到我们的DOM,如果我们使用原生开发的话,那么你会怎么做?取到数据,使用模板字符串,然后创建元素,循环渲染到UI界面?等等,react不需要我们去自己操作,交给react吧,我们只需要把自己的需求告诉react 😊

    首先让我们定义状态 – list数组中是我们取回来的数据:

    import React, { Component } from "react"
    
    export default class App extends Component {
    	constructor() {
    		super()
    		this.state = {
    			// list: ["学习Vue", "学习react", "摆烂"],
    			list: [
    				{ id: 1, text: "学习Vue" },
    				{
    					id: 2,
    					text: "学习react",
    				},
    				{
    					id: 3,
    					text: "摆烂",
    				},
    			],
    		}
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    然后呢?forEach?还是?好了,不买关子了,我们最好使用map映射,为什么?让我们往下看 😁:

    import React, { Component } from "react"
    
    export default class App extends Component {
    	constructor() {
    		super()
    		this.state = {
    			// list: ["学习Vue", "学习react", "摆烂"],
    			list: [
    				{ id: 1, text: "学习Vue" },
    				{
    					id: 2,
    					text: "学习react",
    				},
    				{
    					id: 3,
    					text: "摆烂",
    				},
    			],
    		}
    	}
    
    	render() {
    		// 列表中必须加key值
    		const newList = this.state.list.map((item, i) => (
    			<li key={item.id}>
    				{i}
    				{item.text}
    			</li>
    		))
    
    		return (
    			<section>
    				<h1>Hello -- Developer!</h1>
    				<ul>{newList}</ul>
    			</section>
    		)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    COOL!炫酷到我不知道该从哪里看起,首先我们map映射出JSX语法的标签,将每一项的索引以及数据动态创建渲染到UI界面,帅呆了 😍

    当然,我发现了其中的一个问题,比如:

    • 为什么要加key值(唯一值)呢?

    这就要提到我们的虚拟DOM了

    什么是虚拟DOM?就是根据状态,react会生成一份虚拟DOM,与真实DOM进行比较,如果状态被更改了,那么虚拟DOM也将更改,使用diff算法进行比较,对比前后修改对应的key值相同的数据,避免了错误更改与性能优化

    假如不涉及到列表的增加删除、重排等,设置成索引也是可以的


    实现一个简单TODOList

    import React, { Component } from 'react';
    
    export default class App extends Component {
      iptRef = React.createRef();
      linkRef = React.createRef();
    
      constructor() {
        super();
        this.state = {
          todolist: [{ id: 1, todolistText: '你好 -- 欢迎使用ToDoList' }],
        };
      }
    
      render() {
        return (
          <section>
            <input ref={this.iptRef} />
            <button onClick={e => this.addList()}>添加</button>
    
            <ul>
              {this.state.todolist.map((list, i) => (
                <li key={list.id}>
                  {/* {list.todolistText} */}
                  {/* 富文本 */}
    
                  <span
                    dangerouslySetInnerHTML={{ __html: list.todolistText }}
                  ></span>
                  <a
                    href="/#"
                    ref={this.linkRef}
                    key={this.iptRef}
                    onClick={e => this.removeList(e, i)}
                  >
                    删除
                  </a>
                </li>
              ))}
            </ul>
    
            {this.state.todolist.length === 0 && <div>暂无待办事件</div>}
          </section>
        );
      }
    
      addList = () => {
        if (this.iptRef.current.value.trim()) {
          // 不建议直接修改状态
          // const newList = this.state.todolist.slice();
          const newList = [...this.state.todolist];
          newList.push({
            id: newList.length + 1,
            todolistText: this.iptRef.current.value.trim(),
          });
    
          this.setState({
            todolist: newList,
          });
    
          this.iptRef.current.value = '';
        }
      };
    
      removeList = (e, i) => {
        // 在 React 中另一个不同是你不能使用返回 false 的方式阻止默认行为, 你必须明确使用 preventDefault
        e.preventDefault();
        const nowLinkList = [...this.state.todolist];
        nowLinkList.splice(i, 1);
    
        this.setState({
          todolist: nowLinkList,
        });
      };
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    组件切换案例

    import React, { Component } from 'react';
    
    import './CSS/选项卡.css';
    import HOME from './选项卡component/HOME';
    import ABOUT from './选项卡component/ABOUT';
    import TALK from './选项卡component/TALK';
    import MINE from './选项卡component/MINE';
    
    export default class App extends Component {
      listRef = React.createRef();
    
      constructor() {
        super();
        this.state = {
          buttonList: [
            {
              id: 1,
              buttonText: 'HOME',
            },
            {
              id: 2,
              buttonText: '关于',
            },
            {
              id: 3,
              buttonText: '聊天',
            },
            {
              id: 4,
              buttonText: '我的',
            },
          ],
          current: 0,
        };
      }
    
      render() {
        return (
          <section>
            {this.state.current === 0 && <HOME></HOME>}
            {this.state.current === 1 && <ABOUT></ABOUT>}
            {this.state.current === 2 && <TALK></TALK>}
            {this.state.current === 3 && <MINE></MINE>}
            <div>
              <ul>
                {this.state.buttonList.map((list, i) => (
                  <li
                    ref={this.listRef}
                    className={this.state.current === i ? 'active' : ''}
                    key={list.id}
                    onClick={() => this.hihgLight(i)}
                  >
                    {list.buttonText}
                  </li>
                ))}
              </ul>
            </div>
          </section>
        );
      }
    
      hihgLight = index => {
        this.setState({
          current: index,
        });
      };
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
  • 相关阅读:
    R3live&Fastlio2
    mysql做查询时,第一次很慢,第二三次就会很快?
    计算机毕业设计Java音乐视频分享网站(系统+程序+mysql数据库+Lw文档)
    uniapp-vue3-微信小程序-按钮组wo-btn-group
    干货分享|腾讯内部项目管理PPT
    Ceph存储池管理
    网页报告不能直接转换成Word、PDF格式怎么办?Spire.doc控件可以轻松解决
    Linux学习第24天:Linux 阻塞和非阻塞 IO 实验(二): 挂起
    爬山算法详解
    信息系统建设和服务能力评估证书CS
  • 原文地址:https://blog.csdn.net/weixin_63836026/article/details/126133129