React :用于构建用户界面的 JavaScript 库
声明式编程:
声明式编程是目前整个大前端开发的模式:Vue、React、Flutter、SwiftUI
它允许我们只需要维护自己的状态,当状态改变时,React可以根据最新的状态去渲染我们的UI界面
组件化开发:将复杂的界面拆分成一个个小的组件
开发Web页面
ReactNative,用于开发移动端跨平台
ReactVR,用于开发虚拟现实Web应用程序
开发React必须依赖三个库:
react:包含react所必须的核心代码
react-dom:react渲染在不同平台所需要的核心代码
babel:将jsx转换成React代码的工具
react包中包含了react web和react-native所共同拥有的核心代码
在React的0.14版本之前是没有react-dom这个概念的,所有功能都包含在react里
原因就是react-native
react-dom针对web和native所完成的事情不同
web端:react-dom会将jsx最终渲染成真实的DOM,显示在浏览器中
native端:react-dom会将jsx最终渲染成原生的控件(比如Android中的Button,iOS中的UIButton)
babel : 是目前前端使用非常广泛的编译器、转移器。
Babel,又名 Babel.js
当下很多浏览器并不支持ES6的语法,通过Babel工具,将ES6转成大多数浏览器都支持的ES5语法
在react中,直接编写jsx(JavaScript XML)的语法,并且让babe转换成React.createElement
可不使用babel,但是要用 React.createElement 来编写源代码,它编写的代码非常的繁琐和可读性差
ps : 后续脚手架中使用
crossorigin属性 :目的是为了拿到跨域脚本的错误信息
ps : 也可以下载依赖包到本地,然后本地引入
react
https://unpkg.com/react@18/umd/react.development.js
react-dom
https://unpkg.com/react-dom@18/umd/react-dom.development.js
babel
https://unpkg.com/babel-standalone@6/babel.min.js
script : 添加 type="text/babel",作用是可以让babel解析jsx的语法
ReactDOM.createRoot函数:用于创建一个React根,之后渲染的内容会包含在这个根中
root.render函数 : 要渲染的根组件
Document
Document
暂时使用类的方法封装组件
定义一个类(类名大写,组件的名称是必须大写的,小写会被认为是HTML元素)
继承自React.Component
实现当前组件的render函数
render当中返回的jsx内容,就是之后React渲染的内容
在组件中的数据,可以分成两类:
参与界面更新的数据:当数据变量时,需要更新组件渲染的内容
不参与界面更新的数据:当数据变量时,不需要更新将组建渲染的内容
参与界面更新的数据也可以称之为是参与数据流,这个数据是定义在当前对象的state中
可以通过在构造函数中 this.state = {定义的数据}
当数据发生变化时,可以调用 this.setState 来更新数据,并且通知React进行update操作
在进行update操作时,就会重新调用render函数,并且使用最新的数据,来渲染界面
React 事件的命名采用小驼峰式(camelCase),而不是纯小写 => onClick
需要通过{}传入一个事件处理函数,这个函数会在事件发生时被执行
"react-html-demo": {
"prefix": "react-html",
"body": [
"",
"",
" ",
" ",
" ",
" ",
" Document ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
"",
""
],
"description": "react-html-demo"
}
JSX : React - JSX
JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML
因为看起就是一段XML语法
用于描述我们的UI界面,并且其完成可以和JavaScript融合在一起使用 => html in js
不同于Vue中的模块语法,不需要学习模块语法中的一些指令
比如v-for、v-if、v-else、v-bind
React认为渲染逻辑本质上与其他UI逻辑存在内在耦合 => html、css、js分不开,干脆写到一起
比如UI需要绑定事件(button、a原生等等)
比如UI中需要展示数据状态
比如在某些状态发生改变时,又需要改变UI
他们之间是密不可分,所以React没有将标记分离到不同的文件中,而是将它们组合到了一起
这个地方就是组件(Component)
1. JSX的顶层只能有一个根元素
所以我们很多时候会在外层包裹一个div元素(或者使用后面我们学习的Fragment)
2. 为了方便阅读,通常在jsx的外层包裹一个小括号()
这样可以方便阅读,并且jsx可以进行换行书写
3. JSX中的标签可以是单标签,也可以是双标签
如果是单标签,必须以/>结尾
4. JSX中注释的写法 => [ .jsx文件中 -- ctrl + / ] 即可
render() {
const { counter } = this.state;
return (
{
// 注释的写法1
}
{/* 注释的写法2 */}
当前计数 : {counter}
);
}
当变量是Number、String、Array类型时,可以直接显示
class App extends React.Component {
constructor() {
super();
this.state = {
// Number/String/Array => 直接显示
num: 10,
str: 'abc',
arr: [1, 2, 3],
};
}
render() {
// 直接显示
const { num, str, arr } = this.state;
return (
{/* Number/String/Array => 直接显示,其中Array会依次拿出元素遍历出来 */}
{num}
{str}
{arr}
);
}
}
当变量是null、undefined、Boolean类型时,内容为空
ps : 如果希望可以显示null、undefined、Boolean,那么需要转成字符串
class App extends React.Component {
constructor() {
super();
this.state = {
// null/undefined/boolean => 页面不显示
aa: null,
bb: undefined,
cc: true,
dd: false
};
}
render() {
// 页面不显示
const { aa, bb, cc, dd } = this.state;
return (
{/* null/undefined/boolean => 界面上不显示,也不会报错*/}
{aa}
{bb}
{cc}
{dd}
{/* 可以转成字符串显示出来 */}
{aa + ''}
{String(bb)}
{cc.toString()}
);
}
}
Object对象类型不能作为子元素(not valid as a React child)
class App extends React.Component {
constructor() {
super();
this.state = {
// Object类型,不能作为子元素显示,报错
obj: {
name: 'coder',
age: 18
}
};
}
render() {
// Object类型,不能作为子元素显示,报错
const { obj } = this.state;
return (
{/* Object类型,不能作为子元素显示,报错 */}
{/* {obj}
*/}
{/* Object中的属性可以 */}
{obj.name}
{obj.age}
);
}
}
render() {
const firstName = 'coder';
const lastName = 'star';
return (
{1 + 2}
{firstName + lastName}
);
}
render() {
const boo = true;
const age = 18;
return (
{age >= 18 ? '成年' : '未成年'}
{boo ? 'true' : 'false'}
);
}
// 抽离出来,在函数中做复杂的操作
getName = () => {
return 'abc' + 'sdfs' + 'we';
};
render() {
return (
{this.getName()}
);
}
render() {
const title = 'title';
const imgUrl = 'https://n.sinaimg.cn/sinakd10111/533/w533h800/20200713/6340-iwhseit8630287.jpg';
return (
);
}
绑定class属性 : 最好使用className
render() {
const isActive = false;
return (
111
{/* 动态绑定class */}
222
);
}
render() {
const isActive = true;
const classList = ['a', 'b'];
isActive && classList.push('active');
return (
111
{/* 动态绑定class */}
222
);
}
脚手架中使用该插件 => npm insatall classnames
render() {
const style = { color: 'blue', fontSize: '26px' };
return (
{/* 1. 不支持 */}
{/* 111
*/}
{/* 2. { {中间是对象} } */}
111
{/* 3. { {中间是对象} } */}
222
);
}
事件绑定中的this默认情况下是undefined
很奇怪,居然是undefined
因为在正常的DOM操作中,监听点击,监听函数中的this其实是节点对象 -比如说是button对象
这次因为React并不是直接渲染成真实的DOM,我们所编写的button只是一个语法糖,它的本质React的Element对象
那么在这里发生监听的时候,react在执行函数时并没有绑定this,默认情况下就是一个undefined
onClick = {this.fn} => 这个时候函数还没有执行,调用的时候可能是默认调用
this问题
因其内部很可能是默认调用,如果不传递this进去,在严格模式下,方法内部的this就为undefined,所以通过bind绑this进去,不管后面怎么调用,this都为绑定的this
使用箭头函数 : 箭头函数中没有this,自动去上层作用域中找到this
class App extends React.Component {
constructor() {
super();
}
btnClick(e) {
console.log('btnClick', this, e);
}
render() {
return (
{/* 方式一 : 有事件参数,没有this */}
{/* 方式二 : 有事件参数,有this */}
{/* 方式三 : 须手动传递下事件参数,有this */}
);
}
}
class App extends React.Component {
constructor() {
super();
}
/**
* 记住顺序,event是在最后
* bind的时候把前两个参数传递过来了
* 调用的时候,event才传过来
*/
btnClick(name, age, event) {
console.log('btnClick', name, age, event);
}
render() {
return (
{/* 方式一 : 传递参数需要记住顺序 */}
{/* 方式二 : 传递参数时,很清晰 */}
);
}
}
Document
适合 逻辑较多 的情况
适合 逻辑比较简单 的情况
适合如果条件成立,渲染某一个组件;如果条件不成立,什么内容也不渲染
使用for循环
render() {
// 1. 创建元素数组
const elList = [];
for (let i = 0; i < this.state.list.length; i++) {
const movies = this.state.list[i];
// 2. 创建li元素
const el = {movies} ;
// 3. 推入元素数组中
elList.push(el);
}
return (
// 4. 直接放入数组
{elList}
);
}
使用map循环 : 展示列表最多的方式就是使用数组的map高阶函数
render() {
return (
// 循环
{this.state.list.map((item) => {
return - {item}
;
})}
);
}
实际上,jsx 仅仅只是 React.createElement(component, props, ...children) 函数的语法糖
所有的jsx最终都会被转换成React.createElement的函数调用
createElement需要传递三个参数:
参数一:type
当前ReactElement的类型
如果是标签元素,那么就使用字符串表示 “div”
如果是组件元素,那么就直接使用组件的名称
参数二:config
所有jsx中的属性都在config中以对象的属性和值的形式存储
比如传入className作为元素的class
参数三:children
存放在标签中的内容,以children数组的方式进行存储
如果是多个元素,React内部有对它们进行处理
Document
通过 React.createElement 最终创建出来一个 ReactElement对象
React利用ReactElement对象组成了一个JavaScript的对象树
JavaScript的对象树就是虚拟DOM(Virtual DOM)
1. 在更新数据时不会把整一段全部重新渲染
会在新旧虚拟dom之间进行diff算法计算,然后再重新渲染有差别的部分
2. 跨平台,使用React-native原生开发
虚拟DOM就是js对象,ReactElement对象
可根据环境不同,桥接到环境时,生成不同的东西
web端 => div、button IOS、Android端 => 原生控件、view、...
3. 从命令式编程转到了声明式编程的模式
虚拟DOM帮助我们从命令式编程转到了声明式编程的模式
React官方的说法:
Virtual DOM 是一种编程理念
在这个理念中,UI以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的JavaScript对象
可以通过root.render让 虚拟DOM 和 真实DOM同步起来,这个过程中叫做协调
这种编程的方式赋予了React声明式的API :
只需要告诉React希望让UI是什么状态
React来确保DOM和这些状态是匹配的
不需要直接进行DOM操作,可以从手动更改DOM、属性操作、事件处理中解放出来
脚手架 : 让项目从搭建到开发,再到部署,整个流程变得快速和便捷
现在比较流行的三大框架都有属于自己的脚手架:
Vue的脚手架:@vue/cli
Angular的脚手架:@angular/cli
React的脚手架:create-react-app
网址 : Node.js
npm install create-react-app -g
// 查看安装的版本
create-react-app --version
项目名称不能包含大写字母
create-react-app 项目名称
npm run start
如果希望看到webpack的配置信息,
可以执行命令 =>package.json文件中的一个脚本:"eject": "react-scripts eject"
这个操作是不可逆的
将src下的所有文件都删除
将public文件下出列favicon.ico和index.html之外的文件都删除掉
/**
* 入口文件
*/
// react18 从这里引入ReactDOM
import ReactDOM from 'react-dom/client';
import App from './App.jsx';
const root = ReactDOM.createRoot(document.querySelector('#root'));
/**
* => 这里实际上就是App类的实例,相当于创建了一个实例
*/
root.render( );
/**
* 内部有单独导出Component
* import React from 'react';
* import { Component } from 'react';
* 所以可以合并
*/
import React, { Component } from 'react';
class App extends React.Component {
constructor() {
super();
this.state = {};
}
render() {
return (
title
);
}
}
// 导出类
export default App