# react_rapprand
**Repository Path**: rapprand/react_rapprand
## Basic Information
- **Project Name**: react_rapprand
- **Description**: react练习项目 凑字数凑字数凑字数
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2024-02-18
- **Last Updated**: 2025-06-23
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# ① Javascript⭐
## 1,六种数据类型
- 字符串型(String)
- 数值型(Number)
- 布尔型(Boolean)
- undefined型(Undefined)
- null型(Null)
- 除上五种之外的都算Object
null:变量未指向任何内存
undefined:变量未定义或未赋值
## 2, 类型判断
### 2.1、typeof
typeof:用于检验基本数据类型和函数,并返回一个字符串,无法检测null
````js
console.log( typeof typeof(undefined) ); // string
````
基础
````js
console.log(typeof 123); // number
console.log(typeof '123'); // string
console.log(typeof true); // boolean
console.log(typeof null); // object
console.log(typeof undefined); // undefined
````
进阶
````js
console.log(typeof(NaN)); // number 三种都属于 特殊的数字类型,NaN非法数字
console.log(typeof(Number.MIN_VALUE)); // number
console.log(typeof(Infinity)); // number
console.log(typeof(window)); // object
console.log(typeof(document)); // object
console.log(typeof(null)); // object
console.log(typeof(eval)); // function 本身是函数
console.log(typeof(Date)); // function
console.log(typeof(sss)); // undefined 未定义的变量返回undefined
console.log(typeof(undefined)); // undefined
//
let fun = ()=>{
return undefined
}
console.log(typeof fun); // function
//
console.log(typeof function(){}); // function
//
let a
console.log(a);// undefined 声明但未赋值
````
### 2.2、 instanceof
原型链
## 3,类型转换
### 3.1、 转换为string
toString():转化为字符串,null和undefined不能用
String():一个函数,null和undefined可以使用
\+ "" : 好像比较万能
```js
console.log(NaN.toString()); //NaN
/* console.log(null.toString());
console.log(undefined.toString()); */
console.log(String(111));
console.log(String(null)); //null
console.log(String(undefined));// undefined
console.log(false.toString()); //false
console.log(true+""); // true
```
### 3.2、 转换为数字
Number():任意类型 => 数字
```js
console.log(Number('123')); // 123 纯数字字符串 => 数字
console.log(Number('123的')); // NaN 非纯数字字符串 => NaN
console.log(Number(true)); // 1
console.log(Number(false)); // 0
console.log(Number(null));// 0
console.log(Number(undefined)); // NaN
console.log(Number({}));// NaN
```
parseInt:字符串 => 整数
parseFloat:字符串 => 浮点数
这两个从前到后截取字符串数字,直到第一个不是数字为止
```js
console.log(parseInt('12.1')); // 12
console.log(parseFloat('12.1')); // 12.1
console.log(parseInt('12@34')); // 12 截取
console.log(parseInt('dadasd')); // NaN 一个数字都没是NaN
```
\+:
```js
console.log(+true);//数字1
console.log(true + 1); // 数字2
console.log(+null); // 0
console.log(+undefined); // NaN
```
### 3.3、 转换为布尔
Boolean():
```js
console.log(Boolean(123)); // true
console.log(Boolean(0)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean('')); //false
console.log(Boolean('aaa')); // true
console.log(Boolean({})); // true 空对象是true
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false 但undefined是false
console.log(Boolean(function(){})); // true
```
## 4,运算符
省略........
简单写法
```js
let a = 1,
b = 2,
c = 3
console.log(a, b, c);
```
## 5,循环语句
### 5.1、 for循环
break:结束所有循环
```js
for (let a = 0; a < 3; a++) {
console.log('a', a);
for (let b = 0; b < 3; b++) {
if (b === 1) break; //往后不走了
console.log('b', b);
}
}
```
continue:结束本次循环
```js
for (let a = 0; a < 3; a++) {
console.log('a', a);
for (let b = 0; b < 3; b++) {
if (b === 1) continue; //不显示b=1
console.log('b', b);
}
}
```
循环标识:标记一个循环
```js
markA: for (let a = 0; a < 3; a++) {
console.log('a', a);
for (let b = 0; b < 3; b++) {
if (b === 0) break markA;//终止A循环
console.log('b', b);
}
}
markA: for (let a = 0; a < 3; a++) {
console.log('a', a);
for (let b = 0; b < 3; b++) {
if (b === 0) continue markA;//继续A循环
console.log('b', b);
}
}
```
## 6,原型
### 6.1 实例化对象/构造函数
类创建对象和构造函数创建对象时,类会把函数挂载到原型上
```js
class Myfun2 {
constructor(name, age) {
this.name = name
this.age = age
}
introduce() {
console.log('introduce');
}
}
function Myfun1(name, age) {
this.name = name
this.age = age
this.introduce = () => {
console.log('introduce');
}
}
let jojo = new Myfun1('jojo', 16)
let dio = new Myfun2('dio', 17)
console.log(jojo);
console.log(dio);
```

### 6.2 new的执行过程
就是将所有对象的方法集成到类上
```js
// 1、创建一个类
class Myfn {
constructor(name, age) {
this.name = name
this.age = age
}
// 2、类中的方法会添加到实例对象的原型中(重要)
introduce() {
console.log('introduce');
}
}
// 3、这些对象可以使用这个类中的所有方法
let dio = new Myfn('dio', 17)
let jojo = new Myfn('jojo', 17)
console.log(dio);
console.log(jojo);
```
### 6.3原型链
```js
// 爷爷
class Grand {
/* constructor(name){
this.name = name
} */
testFun() {
console.log('祖传');
}
}
// 爸爸
class Father extends Grand {
constructor(name) {
super()
this.name = name
}
}
// 孙子
let jojo = new Father('jojo')
// 孙子可以使用爷爷的能力
jojo.testFun()
console.log(jojo.__proto__.__proto__.testFun);
// 但是他自己是没有这个能力的
console.log(jojo.hasOwnProperty('testFun'));// false
// 能力来源于祖先
console.log('testFun' in jojo);// false
// 最后,原型链尽头是null
```

### 6.4 call、apply、bind
基础 **call是函数中的一个方法**
```js
// 1、call是函数中的一个方法, class不能用吧,class的function在原型里
function Myfun() {
console.log(this);
}
// 2、使用call可以立即调用这个函数,并把函数的this换成目标对象
Myfun.call( {name: 'jojo'} )
```
```js
let dog = {
name: 'dog',
Fun: function () {
console.log(this.name);
}
}
let cat = {
name: 'cat'
}
// cat调用了dog里的方法
dog.Fun.call(cat)
```
call apply bind 传参方式不同
call 和 apply 就是传参方式不同,都会立即执行函数
call 和 bind 就是是否立即执行函数,bind不会执行函数,但是会返回函数
```js
dog.Fun.call(cat, 'a1', 'a2')
dog.Fun.apply(cat, ['a1', 'a2'])
let cat2 = dog.Fun.bind(cat, 'a1', 'a2')
```
## 7,数组
### 7.1、数组方法
push():向数组的末尾添加一个或**多个**元素,并返回**数组的新的长度**
unshift():向数组的头部添加一个或**多个**元素,并返回**数组的新的长度**
pop():删除数组最后一个元素并返回**这个元素**
shift():删除第一个元素,并返回**这个元素**
slice(初始索引,结束索引):**提取数组元素并返回**,不写结束索引则截取到最后,初始索引为负数则倒着截取
splice(初始索引,删除数量):删除数组元素并返回,**改变原数组**
split():将字符串拆成数组
arr.concat(数组、元素、数组......):连接数组或字符串,不改变原数组
join():连接数组转为字符串,连接符默认“,”
sort( (a,b)=>(a-b) ),数组排序,影响原数组,return (a-b)或(b-a)决定排序方法
indexOf(目标元素,规定检索的位置):查找第一个数组或字符串下标
进阶:
```js
// 手动实现sort 传入函数,返回数组
Array.prototype.mySort = function (callback) {
// 谁调这个方法 this就指向谁
if (typeof callback !== 'function') throw new Error('请传入函数')
for (let i = 0; i < this.length - 1; i++) {
for (let j = 0; j < this.length - i; j++) {
if (callback(this[j], this[j + 1]) > 0) {
let temp = this[j];
this[j] = this[j + 1];
this[j + 1] = temp;
}
}
}
return this;
}
let arr = [3, 1, 2]
console.log(arr.mySort((a, b) => (a - b)));
```
#### forEach():
通常代替for循环使用
````js
arr.forEach(function (元素, 下标, 数组) {
console.log(元素 + " #### " + 下标 + " #### " + 数组);
});
````
forEach终止循环可以使用try catch
````js
try {
[1, 2, 3, 4].forEach((item, index) => {
if (item === 3) {//错误条件
throw new Error("myError")
}
console.log(index, item);
})
} catch (e) {
if (e.message !== 'myError') {//不是我的错误则重新抛出
throw e
}
}
````
## 8,Data对象
```js
var date = new Date();
console.log(date);
console.log(date.getFullYear());//获取当前日期对象的年份(四位数字年份)
console.log(date.getMonth());//获取当前日期对象的月份(0 ~ 11)
console.log(date.getDate());//获取当前日期对象的日数(1 ~ 31)
console.log(date.getHours());//获取当前日期对象的小时(0 ~ 23)
console.log(date.getMinutes());//获取当前日期对象的分钟(0 ~ 59)
console.log(date.getSeconds());//获取当前日期对象的秒钟(0 ~ 59)
console.log(date.getMilliseconds());//获取当前日期对象的毫秒(0 ~ 999)
```
## 9,Math对象
````js
/*固定值*/
console.log("PI = " + Math.PI);
console.log("E = " + Math.E);
console.log("===============");
/*正数*/
console.log(Math.abs(1)); //可以用来计算一个数的绝对值
console.log(Math.ceil(1.1)); //可以对一个数进行向上取整,小数位只有有值就自动进1
console.log(Math.floor(1.99)); //可以对一个数进行向下取整,小数部分会被舍掉
console.log(Math.round(1.4)); //可以对一个数进行四舍五入取整
console.log("===============");
/*负数*/
console.log(Math.abs(-1)); //可以用来计算一个数的绝对值
console.log(Math.ceil(-1.1)); //可以对一个数进行向上取整,小数部分会被舍掉
console.log(Math.floor(-1.99)); //可以对一个数进行向下取整,小数位只有有值就自动进1
console.log(Math.round(-1.4)); //可以对一个数进行四舍五入取整
console.log("===============");
/*随机数*/
//Math.random():可以用来生成一个0-1之间的随机数
//生成一个0-x之间的随机数:Math.round(Math.random()*x)
//生成一个x-y之间的随机数:Math.round(Math.random()*(y-x)+x)
console.log(Math.round(Math.random() * 10)); //生成一个0-10之间的随机数
console.log(Math.round(Math.random() * (10 - 1) + 1)); //生成一个1-10之间的随机数
console.log("===============");
/*数学运算*/
console.log(Math.pow(12, 3)); //Math.pow(x,y):返回x的y次幂
console.log(Math.sqrt(4)); //Math.sqrt(x) :返回x的平方根
````
## 10,DOMApi
### 10.1、查找dom
document.getElementById(*id*)
document.getElementsByTagName(*name*) 标签名
document.getElementsByClassName(*name*) 类名
document.querySelector(*CSS选择器*)
document.querySelectorAll(*CSS选择器*)
### 10.2、获取元素 属性
元素节点.innerText: 获取 HTML 元素的 inner Text。
元素节点.innerHTML:获取 HTML 元素的 inner HTML。
元素节点.属性:获取 HTML 元素的属性值。
元素节点.getAttribute(*attribute*):获取 HTML 元素的属性值。
元素节点.style.样式: 获取 HTML 元素的行内样式值。
### 10.3、改变元素属性
元素节点.innerText = *new text content*:改变元素的 inner Text。
元素节点.innerHTML = *new html content*:改变元素的 inner HTML。
元素节点.属性 = *new value*: 改变 HTML 元素的属性值。
元素节点.setAttribute(*attribute*, *value*):改变 HTML 元素的属性值。
元素节点.style.样式 = *new style*:改变 HTML 元素的行内样式值。
### 10.4、事件绑定
addEventListner和on区别是 前者可以多次绑定同一个事件
*element*.addEventListener(*event*, *function*, *useCapture*) 事件名,函数,指定事件是否在捕获或冒泡阶段执行(基本false)
## 11,BOMApi
### 11.1、window对象
window.alert:警告框
window.confirm:确认框
setTimeout:延时器
setInterval:定时器
innerHeight:浏览器窗口的内高度
innerWidth:浏览器窗口的内宽度
open(URL,name,specs,replace):打开新的窗口
close() :关闭当前窗口
open():打开新窗口
moveTo(x,y):移动窗口
resizeTo(宽,高):设置窗口尺寸
### 11.2、Navigator对象
navigator.userAgent,输出浏览器信息
```js
var ua = navigator.userAgent;
if (/firefox/i.test(ua)) {
alert("你是火狐浏览器");
} else if (/chrome/i.test(ua)) {
alert("你是谷歌浏览器");
} else if (/msie/i.test(ua)) {
alert("你是IE5-IE10浏览器");
} else if ("ActiveXObject" in window) {
alert("你是IE11浏览器");
}
```
### 11.3、Location对象
封装了浏览器地址栏信息
console.log(location); //输出location对象
console.log(location.href); //输出当前地址的全路径地址
console.log(location.origin); //输出当前地址的来源
console.log(location.protocol); //输出当前地址的协议
console.log(location.hostname); //输出当前地址的主机名
console.log(location.host); //输出当前地址的主机
console.log(location.port); //输出当前地址的端口号
console.log(location.pathname); //输出当前地址的路径部分
console.log(location.search); //输出当前地址的?后边的参数部分
location = "https://www.baidu.com"; || location.href = "https://www.baidu.com"; 修改地址
location.assign("https://www.baidu.com"); 跳转
location.reload(true); 重新加载当前页面,并清空缓存
location.replace("https://www.baidu.com"); 替换当前页面,无历史记录
### 11.4、History对象
浏览器前后翻页
history.back(),返回上一个页面
history.forward(),去下一个页面
history.go(数字),根据传入的数字跳转(-2,-1,,1,2)
### 11.5、Screen 屏幕硬件信息
。。。。。。
## 12,js高级语法
### 12.1、Exception 报错
try-catch 捕捉异常代码
```js
try {
// 可能发生异常的代码
} catch (error) {
// 发生错误执行的代码
} finally {
// 无论是否出错都会执行的代码
}
```
throw主动抛出错误
```js
throw new TypeError("您输入的是一个非法数字!")
```
自定义error
````js
// 自定义error
function MyError(message){
this.message = '自定义错误提示'
this.name = '自定义错误名'
}
MyError.prototype = new Error
try {
throw new MyError
} catch (error) {
console.log(error);
}
````
### 12.2、JSON
浏览器和服务器交换数据只能用json
### 12.3、AJAX
AJAX可以使用少量数据完成网络请求,无需重新加载页面
ajax的核心是 XHLHttpRequest 对象
`axios`是一个基于`XMLHttpRequest`的发送`AJAX`请求的库
#### AJAX五种状态:
0:未初始化
1:已open,未send
2:请求已接收
3:正在处理请求
4:请求已完成
状态改变会触发onreadysatechange事件
#### get请求:
````js
// 1、创建
let ajax = new XMLHttpRequest()
// 2、设置请求类型额url
ajax.open("get", "aaa.json")//同目录json文件
// 3、发送请求
ajax.send()
// 4、注册事件
ajax.onreadysatechange = function () {
if (ajax.readyState == 4 && ajax.status == 200) {
console.log(ajax.responseText);//输入响应的内容
}
}
````
#### opst:
```js
let ajax = new XMLHttpRequest()
ajax.open("post", "aaa.json")
ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded")//post请求设置请求头
ajax.onreadysatechange = function () {
console.log(ajax.readyState);
if (ajax.readyState == 4 && ajax.status == 200) {
console.log(ajax.responseText);
}
}
ajax.send('bookname=水浒传&author=施耐庵&publisher=北京图书出版社')
```
#### 封装:
```js
var Ajax = {
get: function (url, fn) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200 || xhr.status == 304) {
fn.call(this, xhr.responseText);
}
};
xhr.send();
},
post: function (url, data, fn) {
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
fn.call(this, xhr.responseText);
}
};
xhr.send(data);
}
};
// 演示GET请求
Ajax.get("users.json", function (response) {
console.log(response);
});
// 演示POST请求
Ajax.post("users.json", "", function (response) {
console.log(response);
});
```
#### 跨域 & cors:
浏览器的一种保护机制,非同源请求 返回的数据 会被拦截
同源:协议、域名、端口号
cors方案,可以让返回的数据校验通过浏览器
cors方案只能解决同个服务器下的跨域问题
**简单请求和预检请求:**
简单请求,为get、head、post,且请默认请求头,会带上 origin+域,询问服务器是否通过,服务器返回Access-Control-Allow-Origin进行匹配(这里不能是\**,*代表全部允许)
预检请求(preflight),除简单请求外都是预检请求,先发送预检请求询问服务器是否通过,后续再发送真实请求(和简单请求一样)
### 12.4、Cookie
只能存储4kb,明文存储,会被添加到HttpRequest请求中
使用window.document.cookie来操作cookie
document.cookie 将以字符串的方式返回所有的 cookie
```js
document.cookie = "username=zhangsan; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/";
```
存储的键值对;到期时间(默认关闭浏览器清除);cookie路径(默认当前页面)
再设置cookie会修改cookie
### 12.5、WebStorage
#### LocalStorage
存储5mb,用于存储token
window.localStorage 永久存储在本地
语法:
保存单个数据:localStorage.setItem(key,value);
读取单个数据:localStorage.getItem(key);
删除单个数据:localStorage.removeItem(key);
删除所有数据:localStorage.clear();
获取某个索引的key:localStorage.key(index);
#### sessionStorage
当前页面关闭后消失
语法:
保存单个数据:sessionStorage.setItem(key,value);
读取单个数据:sessionStorage.getItem(key);
删除单个数据:sessionStorage.removeItem(key);
删除所有数据:sessionStorage.clear();
获取某个索引的key:sessionStorage.key(index);
## 13,ES6 新特性
### 13.1、let和var
区别是var有变量提升
### 13.2、解构赋值
数组解构
```js
let arr = ['jojo', 'dio']
let [jojo, dio] = arr
console.log(jojo, dio);
```
对象解构
```js
let obj = {
name: 'jojo',
age: 18
}
let { name, age } = obj
console.log(name, age);
```
模板字符
```js
let text = '1111'
console.log(`22${text}22`);
```
### 13.3、迭代器
```js
const banji = {
name: "五班",
stus: [
"张三",
"李四",
"王五",
"小六"
],
[Symbol.iterator]() {
//索引变量
let index = 0;
let _this = this;
return {
next: function () {
if (index < _this.stus.length) {
const result = { value: _this.stus[index], done: false };
//下标自增
index++;
//返回结果
return result;
} else {
return { value: undefined, done: true };
}
}
};
}
}
//遍历这个对象
for (let v of banji) {
console.log(v);
}
```
### 13.4、Promise
```js
let promise = new Promise((resolve, reject) => {
if (true) {
resolve('可以传参')
} else {
reject()
}
})
promise.then((val) => val + '成功',(val)=>'拒绝').catch(()=>"失败")
```
三种状态:PromiseState
pending:待定
fulfilled:操作完成
rejected:操作失败
方法:
Promise.resolve():转化为成功结果的promise对象
Promise.reject():转化为失败结果的promise对象
Promise.all():在**所有**传入的 Promise 都被兑现时兑现;在**任意一个** Promise 被拒绝时拒绝
Promise.allSettled():在**所有**的 Promise 都被敲定时兑现
Promise.any():在**任意一个** Promise 被兑现时兑现;仅在**所有**的 Promise 都被拒绝时才会拒绝
Promise.race():在**任意一个** Promise 被敲定时敲定。换句话说,在**任意一个** Promise 被兑现时兑现;在**任意一个**的 Promise 被拒绝时拒绝
promise链:
```js
let promise = new Promise((resolve) => {
resolve('成功')
})
promise.then(val => val).then(val => {
console.log(val);
return val
}).then(val => {
console.log(val);
return new Promise(()=>{}) //返回padding状态的promise对象,可以终止promise链
}).then(val => {
console.log(val);
return val
})
```
进阶
```html
```
### 13.5、async await
axync的返回值:默认是一个promise对象
```js
async function fn() {
// ① return 123 // 返回非promise则函数返回"fulfilled"promise对象
return new Promise((resolve, reject) => {// ② 返回rejected的promise对象则函数返回"rejected"promise对象
reject()
})
//③ throw "error"; 同②
}
let fnSave = fn()
console.log(fnSave); //
```
axync 可以不加await
### 13.6、symbol和set
symbol是一种唯一值,是一种新的数据类型
let s2 = Symbol("张三");
Map:它类似于对象,也是键值对的集合。但是“键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。
set,新的数据结构,类似数组,但值是唯一的
```js
//创建一个空集合
let s = new Set();
//创建一个非空集合
let s1 = new Set([1, 2, 3, 1, 2, 3]);
//集合属性与方法
//返回集合的元素个数
console.log(s1.size);
//添加新元素
console.log(s1.add(4));
//删除元素
console.log(s1.delete(1));
//检测是否存在某个值
console.log(s1.has(2));
//清空集合
console.log(s1.clear());
```
### 13.7、对象方法
Object.is:对比两个对象是否全等
```js
let obj1 = {
name:'jojo',
age:12
}
let obj2 = {
name:'jojo',
age:12
}
let obj3 = obj1
console.log(Object.is(obj1,obj2));//false
console.log(Object.is(obj1,obj3));//true
```
Object.assign:对象的合并,后边的对象会把前边对象的相同属性和方法覆盖,没有的属性和方法会合并,二维对象无法深拷贝
```js
const config1 = {
host: "localhost",
port: 3306,
name: "zhangsan",
pass: "root",
test1: "test1"
};
const config2 = {
host: "127.0.0.1",
port: 3309,
name: "lisi",
pass: "root",
test2: "test2"
}
console.log(Object.assign(config1, config2));
```
- Object.setPrototypeOf:设置原型对象
- Object.getPrototypeof:获取原型对象
```js
const school = {
name: "MySchool"
};
const cities = {
xiaoqu: ["北京", "上海", "深圳"]
};
Object.setPrototypeOf(school, cities);
console.log(Object.getPrototypeOf(school));
console.log(school);
```
## 14、ES7、8、9、10、11部分特性
async await
Object.keys:返回一个数组,包含该对象所有键
Object.values:返回一个数组,包含该对象所有值
```js
//声明对象
const person = {
name: "张三",
age: 20
};
//获取对象所有的键
console.log(Object.keys(person));
//获取对象所有的值
console.log(Object.values(person));
```
flat 平铺数组
```js
const arr1 = [1, 2, 3, 4, [5, 6]];
console.log(arr1.flat());
const arr2 = [1, 2, 3, 4, [5, 6, [7, 8, 9]]];
console.log(arr2.flat());
console.log(arr2.flat(1));//参数为深度是一个数字
console.log(arr2.flat(2));//参数为深度是一个数字
```
## 15、笔试题
### 1、数组扁平化
```js
// (将一个多维数组变为一个一维数组。例如,将数组[1, 2, [3, [4, 5]], [6, 7]]扁平化处理后输出[1, 2, 3, 4, 5, 6, 7];)
let setArr = (arr) => {
let newArr = []
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
newArr = newArr.concat(setArr(arr[i]))
} else {
newArr.push(arr[i])
}
}
return newArr
}
console.log(setArr([1, [2, [3, [4, [5, [6, [7, 8]]]]]]]));
```
### 2、将两个数组合并且有序排列
```js
//给定两个从小到大排好序的数组,亲,请你把它两个合并成新的数组,合并后的结果依然有序。
// 如:给定数组:[1,3,7,15,20]和数组:[-5,0,2,8,9,12]。那么结果是:[-5,0,1,2,3,7,8,9,12,15,20]
const setArr = (arr1,arr2)=>{
let newSrr = []
newSrr = [].concat(arr1,arr2)
newSrr.sort((a,b)=>a-b)
return newSrr
}
console.log(setArr([1,3,7,15,20],[-5,0,2,8,9,12]));
}, [])
```
### 3、求两个数的交集
```js
//如:给定数组 [12,23,34,45]和数组 [21,23,45,100],那么交集是[23,34];
const setArr = (arr1,arr2)=>{
return arr1.filter(item=>arr2.includes(item))
}
console.log(setArr([12,23,34,45],[21,23,45,100]));
}, [])
```
### 4、冒泡排序
```js
// 请编写函数,将data数组中所有对象按照value从小到大排列,不能使用sort函数
let arr = [4, 3, 9, 7, 0, 8, 6, 5, 1, 2];
for (let i = 0; i < arr.length - 1; i++) {//确定轮数
for (let j = 0; j < arr.length - i; j++) {
console.log(1);
if (arr[j] < arr[j - 1]) {
[arr[j - 1], arr[j]] = [arr[j], arr[j - 1]]
}
}
}
console.log("最终排序:" + arr)
```
### 5、求两个数的最大公约数
a%b=0时,b就是最大公约数
```js
let fn = (n1, n2) => {
// 排序
let arr = [n1, n2]
arr.sort((a, b) => a - b)
// 递归
let num = arr[1] % arr[0]
if (num) {//不为零
return fn(arr[0], num)
} else {
return arr[0]
}
}
console.log(fn(99, 810));
```
# ② React⭐
### 1、React运行逻辑
````html
Document
````
### 2、react虚拟dom
````react
// 可能相当于vue template标签
let ele = (
hellow
233
)
````
## 一,类组件
````react
````
### 1、 类组件组件通信
#### 1.1 父传子
父传子为单向数据流,props 里的数据不能直接修改(传递对象.属性似乎只能修改子组件的传参值,父组件的依然没变)
````react
````
#### 1.2 子传父
不止传function,后面ref也可以
````react
````
#### 1.3 构造器 与 props
```react
class Welcome extends React.Component {
//this.props 在构造器中使用,需要 super(props)
//构造比创建对象先执行,如果不使用 super(props) 将无法获取到传的数据
constructor(props) {
super(props);
console.log(this.props.msg) // React
}
render() {
return (
Welcome to {this.props.msg}
)
}
}
```
#### 1.4 props默认值
````react
````
#### 1.5 props 类型限定
限定类型需要借助第三方库 prop-types
````react
//
//
````
### 2、类事件绑定
#### 2.1 事件函数传参
````react
````
### 3、响应式数据
#### 3.1 基础state
````react
````
#### 3.2 setState()
react18版本后,setState“批处理事件”在异步事件中也生效!(本身setState只在react原生事件中为批处理,但原生js如定时器中为同步代码),除非使用ReactDOM.flushSync,但不推荐使用
````react
````
#### 3.3 shouldComponentUpdate钩子
当setState执行的时候,无论数据是否改变都会执行render函数,造成性能浪费
在shouldComponentUpdate钩子中 return true 改为 false 就可以不执行render
shouldComponentUpdate函数两个形参,props和state
````react
````
#### 3.4 PureComponent 纯净组件
由于shouldComponentUpdate太麻烦,直接用PureComponent代替Component被继承就好了
````react
class Page1 extends React.PureComponent {
state = {
name: 'jojo',
}
hClick() {
this.setState({
name: 'jojo',
})
}
render() {
console.log('执行了render');
return (
姓名:{this.state.name}
)
}
}
````
### 4、react中js部分的数据处理
#### 4.1 immutable.js
使用immutable.js代替深拷贝,防止数据过深不好复制
````js
npm install immutable
````
### 5、Ref
#### 5.1 React.createRef 获取元素对象
简单粗暴 和vue差不多
````react
````
````react
````
#### 5.2 ref回调函数获取原生dom
````react
````
#### 5.3 React.CreateRef获取子组件原生的dom 骚操作
也就是props+ref的结合体,vue似乎还方便点
````react
````
### 6、受控组件和非受控组件
类似vue的v-model
#### 6.1 受控组件
````react
class Page1 extends React.PureComponent {
state = {
text: ""
}
inputChange = (e) => {
// 2、设置元素的change事件
this.setState({
text: e.target.value
})
}
render() {
return (
{/* 1、给元素绑定一个value值 */}
{/*
*/}
{this.state.text}
)
}
}
````
#### 6.2 非受控组件
````
看不懂
````
### 7、常用生命周期
````react
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
class Page1 extends React.PureComponent {
ref1 = React.createRef()
state = {
text: 'old'
}
constructor(props) {
super(props)
console.log('1、初始化construtor');
}
componentDidMount() {
console.log('3、组件挂载后,可以获取dom元素', this.ref1);
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('6、组件更新前,不常用', prevProps, prevState)
return prevState.text
}
componentDidUpdate() {
console.log('4、组件更新后,可以获取dom元素', this.ref1);
}
componentWillUnmount() {
console.log('5、组件卸载前,可以获取dom元素', this.ref1);
}
hClickUpdate = () => {
this.setState({
text: 'new'
})
}
hClickDelete = () => {
root.unmount()
}
render() {
console.log('2、render函数');
return (
{this.state.text}
)
}
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
### 8、“插槽”
react中没有插槽这一概念,直接通过props传jsx
#### 8.1 简单插槽
双标签后直接用 this.props.children 获取
````react
let app = document.querySelector('#app');
let root = ReactDOM.createRoot(app);
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
class Page1 extends React.PureComponent {
render() {
return (
{this.props.children}
)
}
}
let ele = (
solt内容
)
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
root.render(ele);
````
#### 8.2 复杂插槽
````react
let app = document.querySelector('#app');
let root = ReactDOM.createRoot(app);
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
class Page1 extends React.PureComponent {
render() {
return (
{this.props.aaa}
{this.props.bbb}
)
}
}
let aaa = aaa
let bbb = bbb
let ele = (
)
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
root.render(ele);
````
#### 8.3 render&props模式
子组件共享父组件的部分jsx代码,也算是一种插槽吧
````react
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
class Page2 extends React.PureComponent {
render() {
return (
{/* 2、子组件使用父组件返回的jsx */}
{this.props.render(123, 323)}
)
}
}
class Page1 extends React.PureComponent {
render() {
return (
{/* 1、父组件给子组件一个"返回jsx的函数",默认取名render */}
(
Page1收到page2的值:x: {x} ,y: {y} 。
)
} />
)
}
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
#### 8.4 HOC模式 高阶函数玩法
这个函数的作用是用来处理组件!
函数名一般以 with 为前缀
函数的形参是class组件,并且函数返回一个class组件
````react
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
function withClass(TheClass) {
return class extends React.Component {
// 在这里执行某些操作,可以处理传给组件的props
render() {
return (
)
}
}
}
class OldPage1 extends React.Component {
render() {
return (
{this.props.text}
)
}
}
let Page1 = withClass(OldPage1)
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
## 9、跨组件通信
子组件有两种接收方式
````react
let app = document.querySelector('#app');
let root = ReactDOM.createRoot(app);
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let MyContext = React.createContext()//1、创建context
class Page2 extends React.Component {
render() {
return (
)
}
}
class Son1 extends React.Component {
// 3、后代组件1,通过MyContext.Consumer加回调函数的方式获取参数
render() {
return (
{(value) => 'Son1 ' + value}
)
}
}
class Son2 extends React.Component {
// 4、后代组件2,通过 static contextType = MyContext 和 this.context方式获取参数
static contextType = MyContext
render() {
return (
{
"Son2 " + this.context
}
)
}
}
class Page1 extends React.Component {
msg = "sadasdasdasdasd"
render() {
return (
{/* 2、根组件通过MyContext.Provider组件设置传递的参数 */}
)
}
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let ele = (
)
root.render(ele);
````
## 二,HOOK与函数组件
### 1、组件通信
#### 1.1 默认值
````js
````
### 2、类型限定
````js
````
### 3、绑定事件
````js
function Page1(props) {
const hClick = ()=>{
console.log(23333);
}
return (
)
}
````
### 4、点标记的组件写法
就是把组件存储在对象里
````js
let app = document.querySelector('#app');
let root = ReactDOM.createRoot(app);
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let Page = {
Page1: class extends React.Component {
render() {
return (
Page1
)
}
},
Page2: function () {
return (
Page2
)
}
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let ele = (
)
root.render(ele);
````
### 5、响应式数据
#### 5.1 useState
````js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
const { useState } = React
function Page1() {
console.log('触发useState函数组件会重新触发');
const [count, setCount] = useState(0)
function hClick() {
console.log('触发useState函数组件会重新触发');
setCount(count + 1)
}
return (
)
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
#### 5.2 useState自动批处理
flushSync可以单独进行一次批处理
正常绑定数据改变只会重新执行一次函数组件,也就是count1、2、3都改变后再执行render函数,这就是自动批处理
但是单独的一个flushSync并传入setCount,函数组件会重新执行一次,然后执行两次render函数
`````js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
const { useState } = React
const { flushSync } = ReactDOM
function Page1(props) {
let [count1, setCount1] = useState(0)
let [count2, setCount2] = useState(0)
let [count3, setCount3] = useState(0)
function hClick() {
flushSync(() => {
setCount1(count1 + 1)
})
flushSync(() => {
})
setCount2(count2 + 1)
setCount3(count3 + 1)
}
console.log('render一次');
return (
{count1}
{count2}
{count3}
)
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
`````
#### 5.3 重复使用setCount1
````js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
const { useState } = React
const { flushSync } = ReactDOM
function Page1(props) {
let [count, setCount] = useState(0)
function hClick() {
// 1、只会执行最后一个
/* setCount(count + 1)
setCount(count + 2)
setCount(count + 3) */
// 2、全部使用箭头函数全部会执行
setCount((count) => count + 1)
setCount((count) => count + 2)
setCount((count) => count + 3)
}
return (
)
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
#### 5.4 惰性初始值
采用传入函数的方法
````js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
const { useState } = React
function Page1(props) {
let initCount = () => {
console.log('只执行一次');
return 233
}
let [count, setCount] = useState(() => initCount());
function handleClick() {
setCount(count + 1)
}
return (
{count}
)
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
### 6、useEffect
#### 6.1 通常用法
````js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
const { useState, useEffect, useLayoutEffect } = React
function Page1(props) {
let [count1, setCount1] = useState(1);
let [count2, setCount2] = useState(2);
useLayoutEffect(() => {
console.log('1、渲染到屏幕更新之间执行,用来处理样式,同步代码');
})
useEffect(() => {
console.log('2、mounted,渲染完后调用,异步代码');
return () => {
console.log('3、初次不会执行,更新后执行');
}
}, [count1])//数组中的值是被侦听的值
function handleClick() {
setCount1(count1 + 1)
}
return (
{count1}
{count2}
)
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
### 7、Dom操作
#### 7.1 函数方式获取dom
````js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
const { useState, useEffect, useLayoutEffect } = React
function Page1() {
function elementFunc(ele) {
console.log('ref加函数获取元素', ele);
}
return (
函数
)
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
#### 7.2 useRef获取dom
````js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
const { useState, useEffect, useLayoutEffect, useRef } = React
function Page1() {
let myRef = useRef()
useEffect(() => {
console.log('useRef获取函数', myRef.current);
})
return (
函数
)
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
### 8、useContext组件传值
````js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
const { createContext, useContext } = React
let MyContext = createContext('无MyContext.Provider时的默认值')
function Page1() {
return (
)
}
function Page2() {
let value = useContext(MyContext)
return (
)
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
### 9、 组件性能优化 memo
````js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
const { useEffect, useState, memo } = React
function Page1() {
const [count, setCount] = useState(0)
function hClick() {
setCount(count + 1)
// setCount(count)
//函数组件更新useState,如果数值不变,也不会触发更新
}
useEffect(() => {
console.log('父组件渲染');
})
return (
)
}
//用memo包裹的子组件类似于PureComponent,父组件更新时,子组件数据若不更新,则不会更新
//也可以 export default memo(Page2)
const Page2 = memo(
function () {
useEffect(() => {
console.log('子组件渲染');
})
return (
Page2
)
}
)
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
### 10、useMemo 计算优化
````js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let { useState, useMemo } = React;
//useMemo返回一个记忆值,当某个值需要被其他state值计算得出时,为了避免重复计算,使用useMemo
//useMemo返回一个值, 而useCallback返回的是函数,也可以使用 useMemo(() => function () { }, [])
function Page1(props) {
let [count, setCount] = useState(16);
function handleClick() {
setCount(count + 1);
}
let adult = useMemo( ()=> {
return count >= 18 ? '成年' : '未成年'
}, [count >= 18])//第二个值类似useEffect的第二个值
return (
年龄:{count}
满18?{adult}
)
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
### 11、useReducer ( useState替代版)
这是个useState的使用
````js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let { useState } = React;
function Page1() {
let [count, setCount] = useState(0)
return (
{count}
)
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
当计算对象包含多个子值计算复杂,或者计算对象依赖上一个state时,推荐使用useReducer
useReducer是一个函数范式,当所需计算满足这种函数逻辑时使用,其实useState也能实现,其实只是区分逻辑罢了。
基础用法
````js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let { useReducer } = React;
const countReduce = (state, action) => {
//reduce函数(纯函数)就是这种 “输入确定则输出确定” 的概念的函数
//根据action的type判断state值
switch (action.type) {
case 'add':
return state + 1
break;
case 'decrease':
return state - 1
break;
default:
return state
break;
}
}
function Page1() {
// 1、定义 并使用useReducer接收两个参数,一个reduce函数,一个被reduce函数处理的初始值
let [state, dispatch] = useReducer(countReduce, 0)
// 2、使用时调用dispatch传指令
return (
{state}
)
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
改为对象
`````js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let { useReducer } = React;
let countObj = {
count: 0,
outher: '用不到的参数'
}
const countReduce = (state, action) => {
//reduce函数就是这种 “根据action的type判断state值” 的概念的函数
console.log(2333, state, action);
switch (action.type) {
case 'add':
return { ...state, count: state.count + 1 }
break;
case 'decrease':
return { ...state, count: state.count - 1 }
break;
default:
return state
break;
}
}
function Page1() {
let [state, dispatch] = useReducer(countReduce, countObj)
return (
{state.count}
)
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
`````
### 12、自定义hook
````react
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
const { useState, useEffect } = React
let useMyHook = () => {
let [x, setX] = useState(0)
let [y, setY] = useState(0)
useEffect(() => {
window.addEventListener('mousemove', (e) => {
setX(e.clientX)
setY(e.clientY)
})
return () => {
(e) => {
setX(e.clientX)
setY(e.clientY)
}
}
})
return { x, y }
}
function Page1() {
// 格式是以use开头并且写在组件最上方,类似类组件的高阶函数
const { x, y } = useMyHook()
return (
)
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
````
### 13、 use Hook 大汇总(二次总结)
#### 13.1、useState()
同一批useState只会让函数组件重新执行一次
```js
let Page1 = () => {
const [count, setCount] = React.useState(1)
setCount(2) //报错,死循环
setCount(2)
return (
{count}
)
}
```
useState批处理,类似异步,同层级只会生效最后一个
```js
React.useEffect(() => {
console.log('useEffect', count);//4
}, [count])
let hClick = () => {
setCount(2)
setCount(3)
setCount(4)
console.log(count);//1
}
```
除非传入箭头函数
```js
React.useEffect(() => {
console.log('useEffect', count);//4
}, [count])
let hClick = () => {
setCount((count) => count + 1)
setCount((count) => count + 1)
setCount((count) => count + 1)
}
```
使用useEffect、或者setCount函数形式获取新值
```js
React.useEffect(() => {
console.log('useEffect', count);//4
}, [count])
let hClick = () => {
setCount((count) => count + 1)
setCount((count) => count + 1)
setCount((count) => count + 1)
setCount((count) => {
console.log(count); //4
return count
})
}
```
useState有缓存功能
```js
const [count, setCount] = React.useState(1)
let num = 1
let hClick = () => {
setCount((count) => {
num += 1
console.log(count, num);//num一直是2
return count += 1
})
}
```
#### 13.2、useEffect()
异步代码
```js
React.useEffect(()=>{
// 获取state数据、dom元素、发送网络请求
},[]) // 空数组只初次渲染执行,传入state则只根据传入的state执行更新
```
useEffect第一个参数,可以再return一个**函数**,在组件销毁时执行一次,在后续useEffct执行时提前于执行
用于清除定时器、移除监听事件等功能,不然多次渲染后会出现多个副作用
```js
React.useEffect(() => {
let timer = setInterval(() => {console.log(2333);}, 1000); //开启定时器
return () => {
clearTimeout(timer) // 移除定时器
}
}, [count])
```
return函数的执行时机:111,click,222,111,click,222,111
**在下一次effect时先执行**,会保存旧的上一次的state值
```js
React.useEffect(() => {
console.log(111);
return () => {
console.log(222,count);//既可以清除上一次定时器,也可以获取旧的state值
}
}, [count])
let hClick = () => {
setCount((count) => {
return count += 1
})
}
```
#### 13.3、useRef()
useRef,和let相比,但是有缓存功能,组件刷新后依旧绑定同个内存地址,类似useEffect第一个参数
```js
let Page1 = () => {
console.log('渲染');
let timer = React.useRef(null)
function hBtn() {
clearInterval(timer.current)
timer.current = setInterval(() => {
console.log('timer',timer);
}, 1000);
}
return (
)
}
```
useRef适合将dom保存以变量的形式
```js
let Page1 = () => {
let btn = React.useRef()
function hBtn(){
console.log(btn.current);
console.dir(btn.current);
}
return (
)
}
```
临时使用dom则使用回调函数的形式
```js
let Page1 = () => {
function hBtn(e){
console.log(e);
}
return (
)
}
```
配合**forwardRef**获取子组件的元素,子组件用**forwardRef**包裹,props后的的参数就是ref
```js
let Page2 = React.forwardRef((props, page2Ref) => {
return (
)
})
let Page1 = () => {
let page2Dom = React.useRef()
function hBtn() {
console.dir(page2Dom.current);
}
return (
)
}
```
进阶,子组件向父组件自由传值,使用**forwardRef**+**useImperativeHandle**
```js
let Page2 = React.forwardRef((props,page2Dom) => {
React.useImperativeHandle(page2Dom,()=>{
return{
a:'aaa',
b:document.querySelector('p')
}
})
return (
)
})
let Page1 = () => {
// 获取子元素的dom
let page2Dom = React.useRef()
let hClick = () => {
console.log(page2Dom.current);
}
return (
)
}
```
#### 13.4、useContext()
正常父子组件通信:
```js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let Page3 = ({ count, setCount }) => {
return
子组件 - {count}
】
}
let Page2 = ({ count, setCount }) => {
return
}
let Page1 = () => {
const [count, setCount] = React.useState(0)
return
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
```
改造后
```js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let MyProvider = React.createContext() // 1、使用createContext创建context,通常单独放一个文件,用的时候引入
let Page3 = () => {
const { count, setCount } = React.useContext(MyProvider)//3、孙组使用useContext件获取
return
子组件 - {count}
}
let Page2 = ({ count, setCount }) => {
return
}
let Page1 = () => {
const [count, setCount] = React.useState(0)
// 2、将创建的context包裹住子组件,使用value传参
return
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
```
#### 13.5、useCllback()
父组件传递给子组件(带memo)函数,父组件改变子组件也会改变,父组件定义函数时需要使用useCallback,也要写第二个参数
```js
let Page1 = () => {
const [num, setNum] = React.useState(0)
let fn = React.useCallback(()=>{},[])//不触发[]内条件则子组件不会重新渲染
return
num:{num}
}
```
#### 13.6、useMemo()
作用是组件重新渲染时,部分代码不执行,而去缓存中获取
useMemo接受一个函数,和一个变量数组,useMemo返回第一个函数中的return值,**第二个数组中的数据如果不改变则启动缓存**
```js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let Page1 = () => {
const [num, setNum] = React.useState(0)
React.useMemo(()=>{
// 缓存区代码
console.log('useMemo');
},[])
return
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
```
#### 13.7、useReducer()
基础使用:封装state数据的操作方法
```js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let reducer = (state, action) => {
switch (action.type) {
case 'add':
return state + action.payload
case 'ace':
return state - action.payload
default:
console.error('cerror');
}
}
let Page1 = () => {
const [count, dispatch] = React.useReducer(reducer, 0)
return
{count}
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
```
封装自定义Hook(对useReducer进行二次封装)
```js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
// 定义了num的初始值和所有能进行的操作
function numFn() {
return React.useReducer((state, action) => {
switch (action.type) {
case 'add':
return state + action.payload
case 'ace':
return state - action.payload
default:
throw '传参错误'
}
}, 0)
}
let Page1 = () => {
const [num, setNum] = numFn()
return
num:{num}
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
```
#### 13.8、React.memo 组件缓存
通常react父组件重绘会触发子组件重绘,用memo包一下子组件就行了
```js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let Page2 = React.memo(() => {
console.log('子组件执行');
return
})
let Page1 = () => {
const [num, setNum] = React.useState(0)
return
num:{num}
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
```
但是当父组件给子组件传递变量时,就会触发子组件重新渲染,需要使用useCallback、useMemo解决
```js
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
let Page2 = React.memo(() => {
console.log('子组件执行');
return
})
let Page1 = () => {
const [num, setNum] = React.useState(0)
let fn = ()=>{}
return
num:{num}
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
```
#### 13.9、useTransition
被startTransition包裹的state**不会优先渲染**,会被打断执行
isPadding会返回当前渲染状态,true时渲染完
```js
let Page1 = () => {
const [iptValue, setIptValue] = React.useState('')
const [isPadding, startTransition] = React.useTransition()
let iptChange = (e) => {
startTransition(() => {
setIptValue(e.target.value)
})
}
return (
{isPadding ?
加载中。。。
: "加载完成"}
{
Array(9999).fill('233').map(item =>
{iptValue}
)
}
)
}
```
### 14、受控组件
是否受控于state
受控组件:组件的状态受到setState影响
```js
let Page1 = () => {
const [num,setNum]=React.useState("0")
function iptChange(e){
console.log(e.target.value);
setNum(e.target.value)
}
return (
)
}
```
非受控组件:组件的状态不受setState影响
```js
let Page1 = () => {
return (
)
}
```
## 三、React路由
### 1、下载
````bash
npm install react-router-dom
````
### 2、路由出口
````react
import { Component } from "react";
import { Outlet } from 'react-router-dom'
class App extends Component {
render() {
return (
{/* 路由出口 */}
)
}
}
export default App;
````
### 3、路由配置
**createHashRouter**
````js
import {createHashRouter } from "react-router-dom";
import App from "../App";
import Home from "../views/Home";
import About from "../views/About";
/* 配置路由表写法:[{ path, element, children }] 推荐
配置路由组件写法: */
const routes = [
{
path: '/',
element: ,
Children: [
{
path: '',
element: ,
},
{
path: 'about',
element: ,
},
]
}
]
// 还有一种路由表的组件写法,不推荐使用
// const routes = createRoutesFromElements(
// }>
// } />
// } />
//
// )
// history 路由
// const router = createBrowserRouter(routes)
// hash 路由
const router = createHashRouter(routes)
export default router
````
````jsx
import { Outlet, NavLink, useNavigate } from 'react-router-dom'
import { showMenuList } from './router/menu'
console.log(showMenuList);
function App() {
return (
{
showMenuList.map((item) => {
/* NavLink是在Link基础之上增加了一些样式属性
认匹配成功时,NavLink就会添加上一个动态的active类名 */
return (
{item.name}
)
})
}
{/* 路由出口 */}
)
}
export default App
````
### 4、 路由重定向 Navigate
路由列表里这么改,可以精准定向
````jsx
import { Navigate } from 'react-router-dom';
let menuList = [
{
path: 'page1',
element: ,
},
{
path: 'page2',
// 这里可以用函数判断用户是否登录,如果未登录,重定向到login
element: ,
},
]
````
### 5、全局引入
****
```js
import React from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider } from 'react-router-dom';
import router from './router'
import store from './store';
import { Provider } from "react-redux";
ReactDOM.createRoot(document.getElementById('root')).render(
)
```
## 四、Redux
### 1、下载reduxjs/toolkit
```js
npm i @reduxjs/toolkit
npm i react-redux
```
### 2、纯redux写法
src/store/user.js ,引入并使用createSlice({name: '切片名', initialState: {值 }, reducers: {修改值的方法})
```js
import { createSlice } from "@reduxjs/toolkit";
// 切片
const userSlice = createSlice({
name: 'user-slice',
initialState: {//初始数据
name: 'jojo',
gender: '男',
age: 18
},
reducers: {//操作数据的方法
// 1、修改名称
setName(state, action) {//action.type action.payload ,类似useReducer
return {
...state,
name:action.apyload
}
}
}
})
export default userSlice
```
### 3、使用toolkit
可以在reducer方法里,直接修改state,方便点
```js
import { createSlice } from "@reduxjs/toolkit";
// 切片
const userSlice = createSlice({
name: 'user-slice',
initialState: {//初始数据
name: 'jojo',
gender: '男',
age: 18
},
reducers: {//操作数据的方法
// 1、修改名称
setName(state, action) {//action.type action.payload ,类似useReducer
state.name = action.payload
}
}
})
export default userSlice
```
### 4、第一步,创建切片
```js
import { createSlice } from "@reduxjs/toolkit";
// 切片
const userSlice = createSlice({
name: 'user-slice',
initialState: {//初始数据
name: 'jojo',
gender: '男',
age: 18
},
reducers: {//操作数据的方法
// 1、修改名称
setName(state, action) {//action.type action.payload ,类似useReducer
state.name = action.payload
},
// 2、修改性别
setGender(state,action){
state.gender = action.payload
},
// 2、修改年龄
setAge(state,action){
state.age = action.payload
},
}
})
export default userSlice
```
### 5、第二步,封装store
```js
import { configureStore } from "@reduxjs/toolkit";
import userSlice from "./user";
import goodsSlice from "./goods";
import bookSlice from "./book";
const store = configureStore({
reducer:{
user:userSlice.reducer,
goods:goodsSlice.reducer,
book:bookSlice.reducer,
}
})
export default store
```
### 6、第三步,全局引入
使用Provider包裹app
```js
import React from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider } from 'react-router-dom';
import router from './router'
import store from './store';
import { Provider } from "react-redux";
ReactDOM.createRoot(document.getElementById('root')).render(
)
```
### 7、第四步、调用
使用**useSelector**和 **useDispatch**
前者用回调函数获取redux寸的
```js
const book = useSelector(state => state.book)
```
useDispatch则需要传入reducer类型的形参
```js
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
export default function Page1() {
const book = useSelector(state => state.book)
const disptch = useDispatch()
return (
{JSON.stringify(book)}
)
}
```
## 五、React18
react18的底层逻辑改了,由**同步**改成**并发**
如两个state一起执行,会同步执行,数据多了就很慢
并发的话,如果当前state渲染比较重要,会优先执行,其他的渲染会被打断
1、在js原生方法中,useState的多个setState也只会触发一次更新
2、并发渲染,渲染的底层机制改变了,渲染可中断、终止,使得开发者可以决定渲染的优先级
故有了新的hook:useTransition
useTransition:
3、suspense:可以理解会catch promise错误,拿到promise自动.then触发render
4、
5、虽然官方推荐升级,但是使用了第三方ui库就容易出问题,上面的功能其实18之前就有了,18算正式推出
# ③ Canvas⭐
## 一、基础
### 1、代码模板
```html
```
### 2、API汇总
#### 2.1、前置:
```js
document.getElementById("canvas").getContext('2d') // 创建2d画笔 ⭐
ctx.beginPtch() //重开一笔
```
#### 2.2、预设
```js
ctx.strokeStyle() //设置笔触颜色
ctx.fillStyle() //设置填充色
ctx.font="30px Arial"; //设置字体属性,字体也能被填充改色之类的,不列举了
```
#### 2.3、绘制
```js
cxt.moveTo(x,y) //画笔坐标
ctx.lineTo(x,y) //画笔目标点
ctx.rect(x,y,宽,高) //创建矩形
ctx.fillRect(x,y,宽,高) //绘制填充的矩形
ctx.strokeRect(x,y,宽,高); //rect+stroke
ctx.clearRect(x,y,宽,高) //切割矩形
```
#### 2.4、收尾
```js
ctx.fill() // 填充(油漆桶,线条不用闭合)
ctx.closePath() // 闭合线条
ctx.stroke() //填充线条
```
### 2.5、其他
````js
canvas.toDataURL(type, encoderOptions) // (默认为`image/png`格式,图片的质量)用于将canvas对象转换为base64位编码
ctx.translate(0,0) // 改变坐标轴中心位置
requestAnimationFrame() //浏览器动画帧,浏览器在下一次重绘前调用传入的函数
````
### 3、练习
#### 3.1、画板
```html
```
#### 3.2、时钟
# ④Vue3⭐
## 一、常用api
### 1、ref
**响应式数据 .value使用**,可以深层响应
```js
{{ count }}
```
ref原理
```js
// 伪代码,不是真正的实现
const myRef = {
_value: 0,
get value() {
// 执行操作后返回
return this._value
},
set value(newValue) {
this._value = newValue
// 修改后执行渲染
}
}
```
ref获取子组件的api时,子组件需要用defineExports暴露出去
### 2、reactive
只能作用于对象或数组,不能作用于普通类型,而且不能给native变量重新赋值
```js
{{ count }}
```
### 3、计算属性
有缓存功能,是只读属性
```js
{{ count }}
```
高级用法 改为可写属性
```js
{{ aaa }}
{{ bbb }}
{{ ccc }}
```
### 4、props emit
withDefaults可以给peops提供默认值
```js
export interface Props {
msg?: string
labels?: string[]
}
const props = withDefaults(defineProps(), {
msg: 'hello',
labels: () => ['one', 'two']
})
```
使用defineProps和defineEmits
```js
const props = defineProps<{
foo: string
bar?: number
}>()
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
// 3.3+:另一种更简洁的语法
const emit = defineEmits<{
change: [id: number] // 具名元组语法
update: [value: string]
}>()
```
### 5、pinia
# ⑤TypeScript⭐
## 一、类型 type
```js
```
## 二、断言 as
断定必然是某种类型
```js
```
## 三、接口 interface
通常用来定义对象
```js
```
## 四、泛型 <>
```js
function myFn(aaa: T, bbb: T): T[] {
return [aaa, bbb]
}
myFn(1,"2") //报错,因为类型不是统一的T
```
```js
function myFn(aaa: T, bbb: T): T[] {
return [aaa, bbb]
}
myFn(1, "2")
```
# ⑥小程序常见问题⭐
## 一、小程序生命周期
onLaunch 初始化完成执行
onShow 小程序启动,或从后台进入前台
onHide 小程序进入后台
onError 小程序出错时
App跟组件内使用
```js
App({
onLaunch: function(options) {},
onShow: function(options) {},
onHide: function() {},
onError: function(msg) {},
globalData: 'I am global data'
})
```
onLaunch 和 onShow会携带参数
1. apiCategory: "default"
2. mode: "default"
3. path: "pages/index/index" //**打开路径**
4. query: {} // 打开小程序的**页面参数query**
5. referrerInfo: {} //当场景为由从另一个小程序或公众号或App打开时,返回此字段,携带来源的appid等信息
6. scene: 1001 // 打开小程序的**场景值**,详细场景值请参考小程序官方文档
```javascript
App({
onLaunch: function(options) { console.log(options) },
onShow: function(options) { console.log(options) }
})
```
## 二、场景值
表示用户从什么场景进入小程序
scene
## 三、全局数据
```js
// app.js
App({
globalData: 'I am global data' // 全局共享数据
})
// 其他页面脚本other.js
var appInstance = getApp()
console.log(appInstance.globalData) // 输出: I am global data
```
## 四、页面构造器
前面的App是全局构造器
单个页面使用Page()
```js
Page({
data: { text: "This is page data." }, //页面的初始数据
onLoad: function(options) { }, // 页面加载,触发时机早于onShow和onReady,获取页面传参
onShow: function() { }, // 页面显示
onReady: function() { }, // 页面加载完成
onHide: function() { }, // 页面隐藏
onUnload: function() { }, // 页面卸载
onPullDownRefresh: function() { }, // 页面下拉
onReachBottom: function() { },// 页面上拉触底
onShareAppMessage: function () { },// 点击转发
onPageScroll: function() { } // 页面滚动
})
```
## 五、路由相关
三者会触发前后两个页面的一些钩子
navigateTo页面推入,像扑克牌一样在最上方加一张牌,最多加十张
```js
wx.navigateTo({ url: 'pages/detail/detail?id=1&other=abc' }) //携带参数跳转到指定页面,可以用onLoad获取
```
navigateBack,拿走最上方一张牌
```js
wx.navigateBack()
```
redirectTo,替换最上面一张牌
```js
wx.redirectTo({ url: 'pageE' })
```
## 六、响应式数据
setDate,在Page的原型中,用this调用,将数据异步传给渲染层,第二个回调函数参数在渲染完成后调用
this.setData(data, callback)
js文件
```js
Page({
/**
* 页面的初始数据
*/
data: {
count: 0,
},
onLoad: function (options) {
},
hClick: function () { //点击事件
console.log(2333, this.data.count);
this.setData({
count: this.data.count + 1
})
}
})
```
wxml文件
```js
{{count}}
```
## 七、网络请求
```js
wx.request({
url: 'test.php',
data: {},
header: { 'content-type': 'application/json' },
success: function(res) {
// 收到https服务成功后返回
console.log(res.data)
},
fail: function() {
// 发生网络错误等情况触发
},
complete: function() {
// 成功或者失败后触发
}
})
```
## 八、登录模块
1、wx.login(),获取微信登录code,wx.login有效期五分钟
2、code发送到后端,后端使用{code,appId,AppSecret(密钥)}获取session_key、用户openId(用户唯一标识)
3、后端用openId操作自己系统的id,生成SessionId给前端
4、后续的小程序请求都带上SessionId,类似token
其中openId和用户id通常绑定使用
session_key是后端和微信间的参数,sessionId是前后端的参数
```js
Page({
tapLogin: function() {
wx.login({ // 1、第一次请求获取code
success: function(res) {
if (res.code) {
wx.request({
url: 'https://test.com/login',
data: { // 2、第二次请求使用code到后端获取openId、sessionId、unionId
username: 'zhangsan', // 用户输入的账号
password: 'pwd123456', // 用户输入的密码
code: res.code
},
success: function(res) {
// 登录成功
if (res.statusCode === 200)
console.log(res.data.sessionId)// 服务器回包内容
}
}
})
} else {
console.log('获取用户登录态失败!' + res.errMsg)
}
}
});
}
})
```
## 九、性能优化
1、精简代码的wxml和wxss结构
2、把静态资源使用cdn分发出去
3、图片压缩
4、分包**subPackages**:app.json
```json
{
"pages":[ // 只加载主包,减少启动时间
"pages/index",
"pages/logs"
],
"subPackages": [ // packageA”和“packageB”两个子目录下的所有文件
{
"root": "packageA",
"pages": [
"pages/cat",
"pages/dog"
]
}, {
"root": "packageB",
"pages": [
"pages/apple",
"pages/banana"
]
}
]
}
```
## 十、小程序支付
# ⑦项目中遇到的困难
## 一、转ES5
大屏设备内核太老,不支持ES6,又不能/让在原有的项目上打包配置,只能把打完的build包二次使用babel.js打包为ES5解决兼容问题
## 二、深度复杂json数据
项目还要不断更新,而且不能使用后端接口字段存储,只能拼命地写很多for循环,为了性能,判断很多情况增删改查
## 三、跨域
CORS在浏览器中的实现是通过在请求和响应中添加相应的HTTP头部字段来完成的。常见的CORS相关头部字段包括:
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Allow-Credentials
Access-Control-Expose-Headers
## 四、前端支付
1、前端把商品用户购买的商品信息发给后端,skuid、商品id、数量
2、后端生成一个**订单id**给前端
3、前端支付时,给后端发送 **订单编号、支付方式**
4、后端把支付地址给前端,前端跳转支付平台
5、支付结果会被**支付宝传给后端**,后端把结果和回调地址发给前端,前端进行展示
(后端也能重定向支付宝平台,不一定前端打开支付宝)
### 1、微信支付
#### 1.1、微信支付商户账号
商户获取**密钥**和**商户证书**
前端:
将商品id、skuid传给后端,生成一个订单
前端选择支付方式传给后端,后端使用用户id、订单id、支付方式调用第三方生成订单
后端传给前端支付参数,表示可以支付后前端对接第三方
前端调取接口的url链接打开支付页面
### 2、小程序支付
先调后端生成订单号,再调用payment去支付
## 五、前端加密
# ⑧网络协议
## 一、websocket
简单代码,如果连接意外中断可以设置定时器启动
```js
let ws = new WebSocket("wss://example.com/socketserver")
// 1、建立连接事件
ws.open = function () {
console.log('连接成功');
ws.send(JSON.stringify({ id: "send发送数据" }))
}
// 2、接收消息事件
ws.onmessage = function (e) {
console.log("接收到的数据", e.data);
}
// 3、错误事件
ws.onerror = function (e) {
console.log("报错", e);
}
// 4、关闭连接
ws.onclose = function () {
console.log("已关闭");
}
```
# ⑨webpack >= 4.0.0
## 一、简介
在项目入口,根据js文件相互依赖关系,构建成若干模块(*bundles*捆)
## 二、基础概念
### 1、entry
构建依赖树的入口
默认./src/index.js
### 2、output
默认值是 `./dist/main.js`,其他生成文件默认放置在 `./dist` 文件夹中
output.filename:
output.path:
### 3、loader(装载机)
webpack只能理解js和json文件
**loader让webpack能识别其他类型文件**
#### 3.1 test
test:要被转换的文件
#### 3.2 use
use:转换时使用的loader
#### 3.3 示例
module.rules
把除了js、json文件之外的文件都在这里使用loader注册一下,打包才能引入
````js
const path = require('path');
module.exports = {
output: {
filename: 'my-first-webpack.bundle.js',
},
module: {
// require或import文件时,后缀为txt的文件,先使用raw-loader进行转换!
rules: [{ test: /\.txt$/, use: 'raw-loader' }],//正则不加引号
},
};
````
比如先下载cssloader :npm install --save-dev style-loader css-loader
再去配置文件引入
````js
module: {
rules: [
{
//所有以 .css 结尾的文件,都将被提供给 style-loader 和 css-loader
test: /\.css$/i,//正则表达式确定应该查找哪些文件
use: ["style-loader", "css-loader"],//对这些文件是用什么样的loader
},
],
},
````
就可以在js文件中引入css了:import "./style.css";
打包后,css会使用js语法伴随着js文件一起引入
### 4、plugin(插件)
比loader作用多,可用于**打包优化**、**资源管理**、**环境变量**
````js
const HtmlWebpackPlugin = require('html-webpack-plugin');//插件以require的形式导入
const webpack = require('webpack'); // 用于访问内置插件
module.exports = {
module: {
rules: [{ test: /\.txt$/, use: 'raw-loader' }],
},
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
````
### 5、mode(模式)
`development`, `production` 或 `none` 之中的一个
```javascript
module.exports = {
mode: 'production',
};
```
6、