React课堂笔记2
一、JSX
1.1、什么是JSX
JSX = JavaScript XML,这是React官方发明的一种JS语法(糖)
概念:JSX是 JavaScript XML(HTML)的缩写,表示在 JS 代码中书写 HTML 结构
设想如下变量声明:
const element= <h1>Hello, world!</h1>;
这个有趣的标签语法既不是字符串也不是 HTML。
它被称为 JSX,是一个 JavaScript 的语法扩展。我们建议在 React 中配合使用 JSX,JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模板语言,但它具有 JavaScript 的全部功能。
JSX 可以生成 React “元素”。
1.2、为什么使用 JSX?
React 认为渲染逻辑本质上与其他 UI 逻辑内在耦合,比如,在 UI 中需要绑定处理事件、在某些时刻状态发生变化时需要通知到 UI,以及需要在 UI 中展示准备好的数据。
React 并没有采用将标记与逻辑分离到不同文件这种人为的分离方式,而是通过将二者共同存放在称之为“组件”的松散耦合单元之中,来实现关注点分离。后面章节深入学习组件。如果你还没有适应在 JS 中使用标记语言,这个会议讨论应该可以说服你。
React 不强制要求使用 JSX,但是大多数人发现,在 JavaScript 代码中将 JSX 和 UI 放在一起时,会在视觉上有辅助作用。它还可以使 React 显示更多有用的错误和警告消息。
浏览器默认是不支持JSX的,所以jsx语法必须使用@babel/preset-react进行编译,编译的结果React.createElement()这种Api的代码。
示例:<div>Hello</div> ==>@babel/preset-react==> React.createElement('div',{},'Hello')
在React开发中,JSX语法是可选的(也就是你可以不使用JSX)。如果不使用JSX语法,React组件代码将变得特别麻烦(难以维护)。所以几乎所有React开发都用的是JSX语法。
JSX是Javascript的一种语法拓展
JSX是JavaScript XML简写,表示在JavaScript中编写XML格式代码(也就是HTML格式)
优势:
- 声明式语法更加直观
- 与HTML结构相同
- 降低了学习成本、提升开发效率
注意:JSX 并不是标准的 JS 语法,是 JS 的语法扩展,浏览器默认是不识别的,脚手架中内置的 @babel/plugin-transform-react-jsx 包,用来解析该语法
1.3、JSX语法
1.3.1、基本语法
JSX的基本语法和XML语法相同,都是使用成对的标签构成一个人树状结构的数据,例如
看图,一个实例,可以把let name= "朱小婷" , const vnode = <h2>Hello {name}! </h2> 改成
const vnode=React.createElement("h2",{},"朱小婷");
1.3.2、使用map函数循环
index.js文件
import React from 'react'; import ReactDOM from 'react-dom/client'; // 1、创建虚拟DOM节点 // const vnode = <h2>Hello React Cli!</h2> const vnode=React.createElement("h1",{},"Hello React!!!"); // 2、创建根节点 const root = ReactDOM.createRoot(document.querySelector("#root")); // 3、将虚拟dom节点挂载到根节点上 root.render(vnode); // map let arr1=[1,3,5,7,9]; // arr1.map((item) => console.log(item)); let arr2=arr1.map((item) => item*2);//map做循环用,产生新数组 console.log(arr2); // 索引值carr就是前面的arr2,一般嘛不会用,还有index的this,所以选择不加进去 arr2.map((item,index,carr) => console.log(index + "->"+ item));
1.3.3、JSX条件渲染
目标任务:
能够在JSX中实现条件渲染
作用:根据是否满足条件生成HTML结构,比如Loading效果
实现:可以使用 三元运算符
或 逻辑与(&&)运算符
案例:
// 来个布尔值 const flag = true function App() { return ( <div className="App"> {/* 条件渲染字符串 */} {flag ? 'react真有趣' : 'vue真有趣'} {/* 条件渲染标签/组件 */} {flag ? <span>this is span</span> : null} </div> ) } export default App
1.3.4、JSX样式处理
-
行内样式 - style - 更优写法
const styleObj = { color:red } function App() { return ( <div className="App"> <div style={ styleObj }>this is a div</div> </div> ) } export default App
-
类名 - className(推荐)
app.css .title { font-size: 30px; color: blue; } app.js import './app.css' function App() { return ( <div className="App"> <div className='title'>this is a div</div> </div> ) } export default App
-
类名 - className - 动态类名控制
import './app.css' const showTitle = true function App() { return ( <div className="App"> <div className={ showTitle ? 'title' : ''}>this is a div</div> </div> ) } export default App
1.3.5、注意事项
- JSX必须有一个根节点,如果没有根节点,可以使用
<></>
(幽灵节点)替代 - 所有标签必须形成闭合,成对闭合或者自闭合都可以
- JSX中的语法更加贴近JS语法,属性名采用驼峰命名法
class -> className
for -> htmlFor
- JSX支持多行(换行),如果需要换行,需使用
()
包裹,防止bug出现
三、组件 Component
如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展,但如果,我们将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。如果我们将一个个功能块拆分后,就可以像搭建积木一下来搭建我们的项目。
3.1、SPA
SPA指的是Single Page Application,就是只有一张Web页面的应用。单页应用程序 (SPA) 是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。 浏览器一开始会加载必需的HTML、CSS和JavaScript,所有的操作都在这张页面上完成,都由JavaScript来控制。因此,对单页应用来说模块化的开发和设计显得相当重要。
单页Web应用,顾名思义,就是只有一张Web页面的应用。浏览器一开始会加载必需的HTML、CSS和JavaScript,之后所有的操作都在这张页面上完成,这一切都由JavaScript来控制。因此,单页Web应用会包含大量的JavaScript代码,复杂度可想而知,模块化开发和设计的重要性不言而喻。
速度:更好的用户体验,让用户在web app感受native app的速度和流畅
MVVM:经典MVVM开发模式,前后端各负其责
ajax:重前端,业务逻辑全部在本地操作,数据都需要通过AJAX同步、提交
路由:在URL中采用#号来作为当前视图的地址,改变#号后的参数,页面并不会重载
优点:
1.分离前后端关注点,前端负责View,后端负责Model,各司其职;
2.服务器只接口提供数据,不用展示逻辑和页面合成,提高性能;
3.同一套后端程序代码,不用修改兼容Web界面、手机;
4.用户体验好、快,内容的改变不需要重新加载整个页面
5.可以缓存较多数据,减少服务器压力
6.单页应用像网络一样,几乎随处可以访问—不像大多数的桌面应用,用户可以通过任务网络连接和适当的浏览器访问单页应用。如今,这一名单包括智能手机、平板电脑、电视、笔记本电脑和台式计算机。
缺点:
1.SEO问题
2.刚开始的时候加载可能慢很多
3.用户操作需要写逻辑,前进、后退等
4.页面复杂度提高很多,复杂逻辑难度成倍
3.2、什么是组件
组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思。
组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。
组件它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构:
3.3、组件定义
组件是 React的核心慨念,定 React应用程序的基石。组件将应用的UI拆分成独立的、可复用的模块,React 用任厅止定田一个一个组件搭建而成的。
定义一个组件有两种方式,便用ES 6 class(类组件)和使用函数(函数组件)。我们先介绍使用class定义组件方式。
3.3.1、使用class定义组件
1、新建一个项目文件
打开命令提示符,输入指令,命名为demo02,等待时间有点长,因为文件大
2、用编辑器打开项目,删掉src下的原始文件,后续自己添加
3、组件的index.js文件
3.3.2、组件props
做一个例子BBS项目:
先创建一个PostItem组件
import React, { Component } from 'react' export class PostItem extends Component { // 写一个构造器 constructor (props){ super (props); this.state={vote:0}; } clickHandle = ()=>{ this.setState ({vote:this.state.vote+1}) } render() { // 展开解构 const {title,author,date} =this.props; return ( <li> {/* 这里写的是导出的模板样式规格,第一行是标题,第二是名字,地上是时间,然后数据有多少条就展示多少条 */} <div>{title}</div> <div>创建人:<span>{author}</span></div> <div>创建时间:<span>{date}</span></div> <div> <button onClick={this.clickHandle}>点赞{this.state.vote}</button> </div> </li> ) } } export default PostItem
接着使用PostList渲染出来:数据本来是需要后台获取的,这里先定义数据,
import React, { Component } from 'react' import PostItem from './PostItem' const data =[ {title:"外交部回应美国以涉俄因素制裁中企",author:"小明",date:"2023-2-19"} , {title:"东部战区回应美军机穿航台湾海峡",author:"小军",date:"2023-3-1"} , {title:"通报“保洁员捡2万报警后被开除”",author:"小红",date:"2023-3-1"} , ] export class PostList extends Component { render() { return ( <div> <h2>帖子列表</h2> {/* 开始循环遍历,上面的数据定义好 */} {/* 渲染出一个Item子组件 */} {data.map((item) => ( <PostItem title={item.title} author={item.author} date={item.date}></PostItem> ))} </div> ) } } export default PostList最后
去index.js文件挂载出来 ,点赞功能是后面加的
3.5、组件state与setState与状态
3.5.1、state
组件的 state是组件内部的状态,state的变化最终将反映到组件UI的上。我们在组件的构造方法constructor中通过this.state定义组件的初始状态,并通过调用 this.setState 方法改变组件状态(也是改变组件状态的唯一方式),进而组件UI也会随之重新渲染。
示例:
import React, { Component } from 'react' // rcc快捷键 export default class Counter extends Component { // 构造函数,用户初始化当前组件 constructor(props){ // 初始化属性值 super(props); // 定义当前组件的状态对象 this.state ={ n:100, }; } render() { return ( <div> <h2>计数器</h2> {/* 然后把值显示在这里 */} <button>{this.state.n}</button> </div> ) } }
结果:
需要写一个单击事件,单击加1,
试过使用state的话,会有黄线报错,建议使用setState。
代码实例:Counter.js
import React, { Component } from 'react' // rcc快捷键 export default class Counter extends Component { // 构造函数,用户初始化当前组件 constructor(props){ // 初始化属性值 super(props); // 定义当前组件的状态对象 this.state ={ n:100, }; }; // 写个单击事件函数 clickHandler = () => { // 需要点击一次就加1, // this.state.n++; 报黄线 //因为this.state对象是只读的,不允许直接修改,使用setstate修改 //当调用setState时会更新UI,render()被调用 // 更新状态,设置新的状态对象 this.setState({n:this.state.n+1}); } render() { console.log("reder") return ( <div> <h2>计数器</h2> {/* 然后把值显示在这里 */} {/* 第二步写单击事件 */} <button onClick={this.clickHandler}>{this.state.n}</button> </div> ) } }
button可以替换
<button onClick={ () => { this.setState({n:this.state.n+1}) }}> {this.state.n} </button>
结果一样
总结:写构造函数。第一步不能少(初始化属性值)
案例:
代码:PostItem.js组件,上面有插入PostList.js
import React, { Component } from 'react' export class PostItem extends Component { // 写一个构造器 constructor (props){ super (props); this.state={vote:0}; } clickHandle = ()=>{ this.setState ({vote:this.state.vote+1}) } render() { // 展开解构 const {title,author,date} =this.props; return ( <li> {/* 这里写的是导出的模板样式规格,第一行是标题,第二是名字,地上是时间,然后数据有多少条就展示多少条 */} <div>{title}</div> <div>创建人:<span>{author}</span></div> <div>创建时间:<span>{date}</span></div> <div> <button onClick={this.clickHandle}>点赞{this.state.vote}</button> </div> </li> ) } } export default PostItem
效果:
3.5.2、指定函数中的this
示例代码:
import React, { Component } from 'react' // rcc快捷键 export default class Counter extends Component { // 构造函数,用户初始化当前组件 constructor(props){ // 初始化属性值 super(props); // 定义当前组件的状态对象 this.state ={ n:100, }; // 指定clickHandler函数中的this为当前组件,并上生成新的函数 this.clickHandler=this.clickHandler.bind(this); } // 不使用箭头函数,就变一个普通函数 clickHandler () { debugger; this.setState({n:this.state.n+1}); } render() { console.log("reder") return ( <div> <h2>计数器</h2> {/* 然后把值显示在这里 */} {/* 第二步写单击事件 */} <button onClick={this.clickHandler}> {this.state.n} </button> </div> ) } }
点击按钮毫无反应。原因是this没有值:undefined
如果上面是一个函数,下面又作为一个事件,所以怎么把这个this指回组件本身
3.5.3、添加bind
bind()
方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
// 指定clickHandler函数中的this为当前组件,并上生成新的函数 this.clickHandler=this.clickHandler.bind(this);
3.5.4、bind函数的作用
打开第一节的demo1项目在根目录创建bindDemo.html文件
示例代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> const obj1 = { x: 100, getX() { console.log(this.x); //输出100 } } obj1.getX(); let fun1 = obj1.getX; fun1(); //你猜输出多少,一开始我猜输出100!!,错,输出undefined (如果定义一个x=400.那就不输出undefined,输出400) // 因为obj1调用了x,x是谁调用它就就给谁,所以给了obj1 100,但是fun1没有调用x, fun1()输出undefined // 接下来在定义 const obj2 = { x: 200, } const obj3 = { x: 300, } // 将getX中的this指向obj2对象,并生成新的函数fun2 let fun2 = obj1.getX.bind(obj2); //obj1指向obj2.obj2有x,就直接把值取出来了 fun2();//输出200 let fun3 = obj1.getX.bind(obj3); fun3();//输出300 </script> </body> </html>
结果:
1、语法
fun.bind(thisArg[, arg1[, arg2[, ...]]])
2、参数
thisArg
当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new
操作符调用绑定函数时,该参数无效。
arg1, arg2, ...
当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
3、返回值
返回由指定的this值和初始化参数改造的原函数拷贝
4、描述
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call
属性)。当新函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。
新函数具有下列内部属性:
- [BoundTargetFunction] - 封装的函数对象;
- [BoundThis] - 在调用包装函数时作为此值传递的值。
- [BoundArguments] - 元素被用作对包装函数的调用的第一个参数的值列表。
- [call] - 执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个this值和一个包含通过调用表达式传递给函数的参数的列表。
当调用绑定函数时,它调用[BoundTargetFunction]上的内部方法[Call ],并使用以下参数Call(boundThis, args)。其中,boundThis是[BoundThis],args是[BoundArguments]后跟函数调用传递的参数。
绑定函数也可以使用new
运算符来构造:这样做就好像目标函数已被构建一样。所提供的this
值将被忽略,同时为仿真函数提供前置参数。
1、重新绑定this,用函数的解决方案,还有一种方法,下面用Counter2.js示例
简化方法代码:
import React, { Component } from 'react' export default class Counter2 extends Component { // 使用state属性初始化状态对象 state ={ n:990 } // 箭头函数中的this始终子项组件本身 clickHandler =()=>{ this.setState({n:this.state.n+1}); } render() { return ( <div> <h2>计数器</h2> <button onClick={this.clickHandler}> {this.state.n} </button> </div> ) } }
3.6、componentDidMount 生命周期
UI被渲染了两次
代码:
import React, { Component } from 'react' export default class Timer extends Component { constructor(props){ super(props); console.log("构造函数"); this.state = { time: new Date().toLocaleString() }; }; // DOM挂载完成后 componentDidMount(){ console.log("DOM挂载完成后"); this.Timer=setInterval(() => { this.setState({time:new Date().toLocaleString()}); }, 1000); } render() { console.log("渲染UI"); return <div>当前时间:{this.state.time}</div>; } }
结果:
这章有整理的不规范,所以在换下章。