# 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); ``` ![image-20240728174333758](C:\Users\冠翎\AppData\Roaming\Typora\typora-user-images\image-20240728174333758.png) ### 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 ``` ![image-20240728200819964](C:\Users\冠翎\AppData\Roaming\Typora\typora-user-images\image-20240728200819964.png) ### 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 (
{count}
) } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ ```` #### 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 (

Page2

{value}

) } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ ```` ### 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 (

Page1

) } //用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 (
Page1

x:{x},y:{y}

) } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ ```` ### 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 (

p1

p2

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

p

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

父组件 - {count}

} let Page1 = () => { const [count, setCount] = React.useState(0) return

根组件 - {count}

} /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ ``` 改造后 ```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

父组件 - {count}

} let Page1 = () => { const [count, setCount] = React.useState(0) // 2、将创建的context包裹住子组件,使用value传参 return

根组件 - {count}

} /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ ``` #### 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 ``` ref原理 ```js // 伪代码,不是真正的实现 const myRef = { _value: 0, get value() { // 执行操作后返回 return this._value }, set value(newValue) { this._value = newValue // 修改后执行渲染 } } ``` ref获取子组件的api时,子组件需要用defineExports暴露出去 ### 2、reactive 只能作用于对象或数组,不能作用于普通类型,而且不能给native变量重新赋值 ```js ``` ### 3、计算属性 有缓存功能,是只读属性 ```js ``` 高级用法 改为可写属性 ```js ``` ### 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、