初识React -七个知识点 + 俩个案例 - 让你入门React

文章目录

  • 认识React
  • React!玩起来
    • 外部导入组件
    • 组件我该怎么写?
    • react样式我该怎么写?
    • 事件绑定
    • ref指令
    • 初识react状态
    • 关于数据处理的循环渲染
    • 实现一个简单TODOList
    • 组件切换案例

认识React

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

React!玩起来

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

我们直接来使用

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

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

让我们来看看下面这张图,你就明白了
初识React -七个知识点 + 俩个案例 - 让你入门React_第1张图片
我们看到react中的唯一html文件body节点之中只有root这样一个节点,我们之后的组件都会添加到该root节点中

其实原理是这样的

ReactDOM.createRoot(document.querySelector("#root")).render(
	React.createElement(
		"section",
		{
			id: "div",
			className: "div_box",
		},
		"nihao"
	)
)

等等,为什么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>
)

组件我该怎么写?

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

// 创建组件类时必须导入react核心模块
import React from "react"

class App extends React.Component {
	render() {
		return (
			<section>
				<p>Hello React Component</p>
			</section>
		)
	}
}

// 向外共享
export default App

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

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

import React, { Component } from "react"

export default class App extends Component {
	render() {
		return (
			<section>
				<h1>React很有趣 -- 你也来一起玩吧!</h1>
			</section>
		)
	}
}

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>
		)
	}
}

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

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

import React from "react"

function App2() {
	return (
		<section>
			<div>GOOD!函数式组件</div>
		</section>
	)
}
// 函数式组件无状态组件(16.8之前)
// 16.8之后,有了react hooks -- 有状态性
export default App2

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

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

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

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>
		)
	}
}

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

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>
		)
	}
}

这些都很好理解,唯一使我们困惑的就是: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真好玩!")
	}
}

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

以上代码输出结果为:
初识React -七个知识点 + 俩个案例 - 让你入门React_第2张图片

这当然没有什么问题,但是我们想要知道的是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)
	}
}

初识React -七个知识点 + 俩个案例 - 让你入门React_第3张图片
等等,谁是undefined????

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

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

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

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>
		)
	}
}

给元素一个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)
	}
}

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

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

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

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

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

请添加图片描述

现在你知道为什么要使用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状态

	}
}

初识React -七个知识点 + 俩个案例 - 让你入门React_第4张图片
这样我们就可以随心所欲的点击更改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: "摆烂",
				},
			],
		}
	}
}

然后呢?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>
		)
	}
}

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,
    });
  };
}


组件切换案例

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,
    });
  };
}

你可能感兴趣的:(React,react.js,javascript,前端,前端框架,reactjs)