ajax请求

1.请求基本步骤

<body><form action="###">手机号:<input type="text" name="phone" placeholder="手机号" id="phone"><br>密码:<input type="text" name="pass" id="pass"><br><button id="btn">登录</button></form><script>const phone=document.getElementById('phone')const pass=document.getElementById('pass')const btn=document.getElementById('btn')btn.onclick=function(){//1.1利用ajax核心对象xmlHttpRequest创造实例对象//1.2 利用open方法打开路径//1.3 利用send方法发送请求参数//1.4 利用readystatechange事件监听readyState请求状态码的变化return false}</script>
</body>

1.1 利用ajax核心对象xmlHttpRequest创造实例对象

因为XMLHttpRequest的原型对象中包含很多方法用来支持ajax请求

const xhr=new XMLHttpRequest()

注意:在与form表单标签使用时,要注意form表单的默认请求事件 return false

1.2 利用open方法打开路径

open的3个参数:

​ 请求方式:get/post

​ 请求地址:

​ 1、get请求的地址中包含查询字符段(请求参数)因为get请求默认读取缓存,所以可以用时间戳拼接到查询字符串后面,保证请求地址不同

​ 2、post请求参数在请求体中

​ 是否异步:false/true

//get请求
xhr.open('get',`/login?phone=${phone.value}&pass=${pass.value}&_=${Date.now()}`,true)
//post请求
xhr.open('post','/login',true)

1.3 利用send方法发送请求参数

send 参数:

​ 1.如果是get请求,因为请求参数都在请求地址里面,所以可以不填参数,也可以填写null

​ 2.如果是post请求,里面填写的是要发送的请求体

​ send发送数据的时候需要设置请求头,告诉服务端我发送的数据是什么类型

​ - 如果send中数据格式是url查询字符串格式,请求头的content-type :application/x-www-form-urlencoded

​ - 如果send中数据格式是json字符串格式,请求头的content-type:application/json

//get请求
xhr.send() 或 xhr.send(null)
//post请求
xhr.setRequestHeader('Content-Type','application/json;charset=utf-8')
xhr.send(JSON.stringfy{phone:phone.value,pass:pass.value})

1.4 利用readystatechange事件监听readyState请求状态码的变化

readyState请求状态码(0-4):

​ 0 : 表示没有发送

​ 1 : 表示启动了open方法

​ 2 : 表示启动了send方法

​ 3 : 表示开始部分接受数据

​ 4 : 表示成功了

status 响应状态码(200-299)

responseText 接受其他文本类响应(json)

responseXml 接受xml响应

xhr.onreadystatechange=function(){console.log('readyState'+xhr.readyState)if(xhr.readyState===4&&xhr.status>=200&&xhr.status<=299){console.log('请求发送成功了')const resData=JSON.parse(xhr.responseText)//app.js 设置了响应成功code :200if(resData.code===200){return alert("登录成功,您的用户名是" + resData.data.username)}alert('登录失败')
}

2.jQuery中ajax

2.1 一级封装

$(function(){$('#btn').click(function(){$.ajax({url:'/login',//请求地址type:'get' /'post' , //请求方式cache:false,//是否缓存data:{//请求参数//数据:如果是get请求 则帮我们拼接在url上,如果是post请求 会转成字符串phone:$('#phone').value,pass:$('#pass').value},dataType: "json", //预期的响应数据的格式headers: {}, //请求头timeout: 2000, //设置超时时间sucess(val){//成功的回调函数,参数就是得到的数据console.log(val)}})return false })  })

2.2 二级封装

$(function(){$('#btn').click(function(){//get请求$.get('/login',{//请求参数//数据:如果是get请求 则帮我们拼接在url上,如果是post请求 会转成字符串phone:$('#phone').value,pass:$('#pass').value},(val)=>{//成功的回调函数,参数就是得到的数据console.log(val)},'json')//post请求$.post('/login',{//请求参数//数据:如果是get请求 则帮我们拼接在url上,如果是post请求 会转成字符串phone:$('#phone').value,pass:$('#pass').value},{//请求参数//数据:如果是get请求 则帮我们拼接在url上,如果是post请求 会转成字符串phone:$('#phone').value,pass:$('#pass').value},(val)=>{//成功的回调函数,参数就是得到的数据console.log(val)},'json')return false})
})

2.3 三级封装

$(function(){$('#btn').click(function(){$.getJSON('/login',{ //请求参数phone:$('#phone').value,pass:$('#pass').value},(val)=>{console.log(val)})})
})

Promise

1. Promise基本介绍

1.1 Promise构造函数

  1. Promise对象是一个异步编程的解决方案,可以将异步代码操作以同步的流程表达出来,避免了层层嵌套的回调函数(俗称‘回调地狱’)

  2. Promise上有then,catch,finally三个原型方法

  3. Promise上有all,allSettled,race,any,reject,resolve 六个静态方法

  4. Promise是处理异步代码的,本身Promise自己的回调函数的执行是同步的

1.2 Promise基本使用

  1. Promise实例化的接收一个回调函数作为参数

  2. Promise回调函数接受两个参数分别是reslove和reject(这两个都是函数)

  3. Promise中书写异步代码,在异步代码成功的时候,可以调用reslove函数,在异步代码失败的时候可以调用reject函数

  4. Promise的返回值会根据resolve和reject的调用决定状态,如果没有reject或resolve,则会返回pending状态的promise实例对象,如果是同步代码错误,则直接返回失败的实例对象promise,值为错误信息
    resolve和reject接受参数,分别是成功的值和失败的值,会传递给Promise的实例对象

  5. Promise实例对象的状态只能由pending变为其他状态

1.3 Promise实例化对象

  1. promiseState属性:当前promise的状态

    • 默认是pending状态,代表异步代码正在执行中

    • fulfilled/resolved:代表异步代码执行成功并调用了resolve函数

    • rejected: 代表异步代码执行失败并调用了reject函数

  2. PromiseResult属性:当前promise执行的结果

    • 如果异步代码执行成功,值是resolve函数中传递值

    • 如果异步代码执行失败,值是reject函数中的错误信息

2.Promise原型方法

const p1=new Promise((resolve,reject)=>{try{throw new Error('A有错误')setTimeout(()=>{throw new Error('A异步有错误')console.log('A打印成功')resolve('A')})}catch(e){reject(e.message)}
})
注意:try...catch...只能捕获同步错误,异步里面的错误不能到catch里面,会直接报错
同时同步代码里面有错误后,同步代码后面的代码不会执行,会直接进入catch里面
try...catch...捕获错误后,就不会直接报错 就是会进入catch,我们可以通过error.message获取错误信息,但是不会直接直接在页面里报错

2.1 then()方法

p1.then((value)=>{//若调用then方法的promise实例状态为成功,则进入这个回调函数,value是promise实例的值console.log(value) //A
},(reason)=>{//若调用then方法的promise实例状态为失败,则进入这个回调函数,reason是promise实例的传入的错误信息console.log(reason)//e.message的具体信息
})

2.1.1 then方法基本介绍

  1. then方法是供Promise的实例对象调用,能够通过实例对象的状态执行下一步操作
  2. then方法可以接受2个参数,都是回调函数,分别处理成功状态和失败状态的的promise实例对象
  3. then方法中回调函数都是异步进行的
  4. then方法中回调函数的参数就是成功或者失败的promise实例的

2.1.2 then方法返回值

  1. 默认返回一个成功状态的promise对象,值为返回的值
  2. 如果then中出现报错且没有解决,则返回一个失败状态的promise对象,值为错误信息
  3. 如果then返回一个promise对象,则返回值与这个promise对象挂钩
  4. 如果then中没有处理调用then方法的promise实例对应状态的回调函数时候,会发生值穿透现象

(值穿透现象:返回一个promise对象,promise对象的内容和调用方法的promise实例一样)

2.2 catch()方法

p1.catch(reson=>{//catch只处理调用该方法的失败状态下的promise实例,如果是成功状态的promie实例,会发生值的穿透//若调用then方法的promise实例状态为失败,则进入这个回调函数,reason是promise实例的传入的错误信息console.log(reson)//e.message的具体信息
})

2.2.1 catch方法基本介绍

  1. 和then的第二个回调函数作用一致
  2. 不能和then的第二个回调函数同时使用

2.2.2 catch返回值:规律与then方法一致

2.3 finally()方法

p1.finally(()=>{//调用的promise实例状态为成功或失败都可以进入这个回调函数console.log(reson)//undefinedconsole.log(value)//undefined
})

2.3.1 finally方法基本介绍

  1. 无论调用方法的promise实例状态是成功与否,都会进入finally的回调函数
  2. finally的回调函数不接受任何参数

2.3.2 finally返回值:

  1. 默认情况下直接穿透

  2. 如果finally中出现异常,则会返回一个失败的promise对象,值为异常信息

  3. 如果finally中返回promise实例

    • 返回的promise实例为成功,则无需理会,直接穿透
    • 返回的promise实例为失败,则finally返回这个失败的promise实例

2.4 案例练习

3.Promise静态方法

    const p1 = new Promise((resolve, reject) => {console.log("开始请求A");try {// throw new Error("A错误")setTimeout(() => {console.log("A成功");resolve("A")}, 2000)} catch (e) {reject(e.message)}})const p2 = new Promise((resolve, reject) => {console.log("开始请求B");try {// throw new Error("B错误")setTimeout(() => {console.log("B成功");resolve("B")}, 1000)} catch (e) {reject(e.message)}})const p3 = new Promise((resolve, reject) => {console.log("开始请求C");try {// throw new Error("C错误")setTimeout(() => {console.log("C成功");resolve("C")}, 1500)} catch (e) {reject(e.message)}})const allResult = Promise.all([p1, p2, p3]);//传入的参数必须是iterableconst allResult = Promise.allSettled([p1, p2, p3]);const allResult = Promise.race([p1, p2, p3]);const allResult = Promise.any([p1, p2, p3]);const allResult = Promise.resolve([p1, p2, p3]);const allResult = Promise.reject([p1, p2, p3]);console.log("allResult", allResult);

3.1 all()方法

  1. 返回一个promise对象
  2. 如果参数中promise对象状态都是成功的,则返回一个成功的promise对象,值为参数中所有promise对象的值组成的数组
  3. 如果参数中有任何一个promise对象状态是失败的,则返回一个失败的promise对象,值为参数中监听到的最先执行失败的promise对象的错误信息(reject中的值)

3.2 allSettled()方法

  1. 返回一个promise对象
  2. 主要是为了检测所有的promise对象是否执行结束(无论成功还是失败)
  3. allSettled方法永远返回成功的promise状态,值是一个数组,数组的每一个值是监听的promise对象的状态和值组成的新对象

3.3 race()方法

  1. 返回一个promise对象
  2. 返回的promise对象与参数中第一个改变状态的promise对象挂钩

3.4 any()方法

  1. 返回一个promise对象
  2. 返回的promise对象与参数中执行最快且成功promise对象挂钩
  3. 如果参数中所有promise对象的状态都为失败,返回一个错误状态的promise对象,值为新的错误类型AggregateError:All promises were rejected

3.5 resolve()方法

  1. 快速的得到一个成功的promise对象,值为resolve的参数(也可以快速的把一个值包装到成功promise对象中,就可以继续then异步处理)
  2. 当resolve的参数是一个promise对象的时候,resolve的返回值就和这个promise对象挂钩

3.6 reject()方法

  1. 快速的得到一个失败的promise对象,值为reject的参数(也可以快速的把一个错误包装到失败promise对象中,就 可以继续catch异步处理)
  2. 当reject的参数是一个promise对象的时候,返回一个失败状态的promise对象,值为传入的参数promise对象

async&await

1.generator

      // generator函数 *号  yield关键字function* fn() {console.log("123");yield console.log("1");// 一个yield分号结束之前,都属于yieldyield console.log("2");yield console.log("3");yield console.log("4");yield console.log("5");}const res = fn();console.log(res); //res中的原型链上有next方法 返回一个迭代器对象(iterator)document.onclick = function () {res.next();};
    function* getData() {yield setTimeout(() => {console.log("要1");res.next();}, 2000);yield setTimeout(() => {console.log("要2");res.next();}, 1000);yield setTimeout(() => {console.log("要3");res.next();}, 1500);yield setTimeout(() => {console.log("要4");res.next();}, 1500);yield setTimeout(() => {console.log("要5");}, 1000);}const res = getData(); //先调用generator函数 得到一个对象document.onclick = function () {// 点击文档后开始调用getData 每次点击会重新调用,重新开始// const res = getData();res.next();};

2.async&await

// 最终结果顺序:1 3 2async function fn(){await console.log(1);// await语法语句后面的代码是异步代码console.log(2);}fn()console.log(3);
      // 最终结果顺序:3 2 1async function fn() {// await后面的代码 没有等待前面计时器异步代码的执行await setTimeout(() => {console.log(1);}, 2000);// await语法语句后面的代码是异步代码console.log(2);}fn();console.log(3);
// 结果顺序:3 1 2async function fn() {// await 后面等待promise对象 因为promise对象有异步代码成功结束节点提示// 如果接受到的是reject失败结果,await语句后面的代码不会执行 会报错// 如果接受到的是resolve成功结果,await语句后面代码正常执行,await返回值是promise实例对象的值(resolve传的值)const p=await new Promise((resolve, reject) => {setTimeout(() => {console.log(1);resolve('这是异步代码结果');reject()}, 2000);});console.log(p);//这是异步代码结果 console.log(2);}fn();console.log(3);

async函数返回值

  1. async函数返回一个promise对象
  2. 默认返回成功状态的promise对象,值为函数返回值(与await没有关系)
  3. 当async函数内部出现错误时,返回失败状态的promise对象,值为错误信息
  4. 当async函数内的await等到了一个失败状态的promise实例对象,则函数返回值与这个promise对象挂钩
  5. await返回值:成功的promise对象的值
async function getA() {// throw new error('错啦')const p=await new Promise((resolve, reject) => {//  throw new Error('错啦')try {console.log("开始请求A");// throw new Error("A错啦");setTimeout(() => {console.log("A开始执行");// throw new Error("A错啦");console.log("A成功啦");resolve("A成功");}, 1000);} catch (e) {reject(e.message);}});console.log('B');console.log('p',p);return 1;}const res = getA();console.log("async函数的返回值", res);

3.案例练习

3.1 getData案例

3.1.1 普通版

async function getData(){console.log('开始请求A数据');const A=await new Promise((resolve,reject)=>{try{setTimeout(()=>{console.log("A成功了");resolve({name:'laowang'})})}catch(e){reject(e.message)}})console.log('开始请求B数据');const B=await new Promise((resolve,reject)=>{try{setTimeout(()=>{console.log("B成功了");resolve({age:18})})}catch(e){reject(e)}})console.log('开始请求C数据');const C=await new Promise((resolve,reject)=>{try{setTimeout(()=>{console.log("C成功了");resolve(Object.assign(A,B,{gender:'nan'}))})}catch(e){reject(e)}})console.log(C)}getData()

3.1.2 函数封装版

function getName() {return new Promise((resolve, reject) => {try {setTimeout(() => {resolve({name: "zs",});}, 2000);} catch (e) {reject(e.message);}});
}function getAge() {return new Promise((resolve, reject) => {try {setTimeout(() => {resolve({age: 18,});}, 1500);} catch (e) {reject(e.message);}});
}function getGender(name, age) {return new Promise((resolve,reject) => {try {setTimeout(() => {resolve(Object.assign(name, age, { gender: "男" }));}, 1000);} catch (e) {reject(e.message);}});
}async function getData() {const name = await getName();const age = await getAge();const data = await getGender(name, age);console.log(data);
}
getData()

3.2 写入文件案例

3.2.1 普通版

const fs = require("fs");
const path = require("path");
const filePath = path.join(__dirname, "./a.txt");async function writeWordsInTxt() {const fd = await new Promise((resolve, reject) => {fs.open(filePath, "a", (err, fd) => {if (err) return reject(err.message);resolve(fd);});});await new Promise((resolve, reject) => {fs.write(fd, "你好呀", (err) => {if (err) return reject(err.message);resolve();});});await new Promise((resolve, reject) => {fs.close(fd, (err) => {if (err) return reject(err.message);resolve();});});
}writeWordsInTxt()

3.2.2 函数封装版

const fs = require("fs");
const path = require("path");
const filePath = path.join(__dirname, "./a.txt");function open() {return new Promise((resolve, reject) => {fs.open(filePath, "a", (err, fd) => {if (err) return reject(err.message);resolve(fd);});});
}function write(fd) {return new Promise((resolve, reject) => {fs.write(fd, "封装好啦", (err) => {if (err) return reject(err.message);resolve();});});
}function close(fd) {return new Promise((resolve, reject) => {fs.close(fd, (err) => {if (err) return reject(err.message);resolve;});});
}async function writeWordsInFile() {const fd = await open();await write(fd);close(fd);
}writeWordsInFile();

3.2.3 promisify版

const fs = require("fs");
const path = require("path");
const filePath = path.join(__dirname, "./a.txt");
const { promisify } = require("util");
const open = promisify(fs.open);
const write = promisify(fs.write);
const close = promisify(fs.close);(async function () {const fd = await open(filePath, "a");await write(fd,'promisify完成啦');close(fd);
})();

axios

1.axios基础请求

使用:

<button id="get">get-得到用户信息</button><button id="post">post-得到用户信息</button><button id="put">put-得到用户信息</button><button id="del">delete-删除用户信息</button><script>const oGet = document.getElementById("get");const oPost = document.getElementById("post");const oPut = document.getElementById("put");const oDel = document.getElementById("del");//  get请求oGet.onclick =async function () {try{const re=await axios({url: "/user",method: "GET",// get请求发送的数据 1.拼接到url上 2.使用params配置// params有2种格式 1.对象格式 2.查询字符串格式params:{userID:001},timeout:1000,});console.log('get',re);}catch(e){console.log(e.message);}};//  post请求oPost.onclick =  async function () {try{const re=await axios({url: "/login",method: "POST",// post请求 请求体参数用data发送,data是一个对象类型data:{phone:'1300000001',pass:'11111'},timeout:1000,});console.log('post',re);}catch(e){console.log(e.message);}};// put请求oPut.onclick = async function () {try{const re=await axios({url: "/update",method: "put",//put请求同post请求一样data:{userID:'zs',age:18},timeout:1000,});console.log('put',re);}catch(e){console.log(e.message);}};//  delete请求oDel.onclick = async function () {try{const re=await axios({url: "/deleteuser",method: "Delete",// delete请求同get请求一样params:{userID:'zs',},timeout:1000,});console.log('del',re);}catch(e){console.log(e.message);}};</script>

2.axios别名请求

<button id="get">get-得到用户信息</button><button id="post">post-得到用户信息</button><button id="put">put-得到用户信息</button><button id="del">delete-删除用户信息</button><script>const oGet = document.getElementById("get");const oPost = document.getElementById("post");const oPut = document.getElementById("put");const oDel = document.getElementById("del");//  get请求oGet.onclick = async function () {try {const re = await axios.get("/user", {params: {userID: 001,},timeout: 1000,});// 将查询字符串写在请求地址上// const re=await axio.get('/user?userid=001',{//   timeout: 1000,// })console.log("get", re);} catch (e) {console.log(e.message);}};//  post请求oPost.onclick = async function () {try {const re = await axios.post("/login", {data: {phone: "1300000001",pass: "11111",},timeout: 1000,});console.log("post", re);} catch (e) {console.log(e.message);}};// put请求oPut.onclick = async function () {try {const re = await axios.put("/update", {data: {userID: "zs",age: 18,},timeout: 1000,});console.log("put", re);} catch (e) {console.log(e.message);}};//  delete请求oDel.onclick = async function () {try {// const re = await axios.delete("/deleteuser", {//   params: {//     userID: "zs",//   },//   timeout: 1000,// });const re=await axios.delete('/deleteuser?userid=zs',{timeout: 1000,})console.log("del", re);} catch (e) {console.log(e.message);}};</script>

3.axios配置默认值

 // axios全局默认配置(配置默认基础路径)axios.defaults.baseURL = "/api";

4.创建axios实例

     const oGet = document.getElementById("get");const oPost = document.getElementById("post");const oPut = document.getElementById("put");const oDel = document.getElementById("del");// axios全局默认配置(配置默认基础路径)// axios.defaults.baseURL = "/api";// 创建实例 (创建一个副本)// const myReq = axios.create();// 创建实例,并传入配置项const myReq = axios.create({// 配置当前实例的基础路径baseURL: "/api",// 配置当前实际的超时时间timeout: 1000,// 配置当前实例的请求头headers: {"hello": "world",},});// 创造另一个实例,并传入配置项const yourReq = axios.create({// 配置当前实例的基础路径baseURL: "/sss",// 配置当前实际的超时时间timeout: 1000,// 配置当前实例的请求头headers: {"isAction": "no",},});const oGet = document.getElementById("get");const oPost = document.getElementById("post");const oPut = document.getElementById("put");const oDel = document.getElementById("del");// axios全局默认配置(配置默认基础路径)// axios.defaults.baseURL = "/api";// 创建实例 (创建一个副本)// const myReq = axios.create();// 创建实例,并传入配置项const myReq = axios.create({// 配置当前实例的基础路径baseURL: "/api",// 配置当前实际的超时时间timeout: 1000,// 配置当前实例的请求头headers: {"hello": "world",},});// 创造另一个实例,并传入配置项const yourReq = axios.create({// 配置当前实例的基础路径baseURL: "/sss",// 配置当前实际的超时时间timeout: 1000,// 配置当前实例的请求头headers: {"isAction": "no",},});//  get请求oGet.onclick = async function () {try {const re = await myReq.get("/user", {params: {userID: 001,},});console.log("get", re);} catch (e) {console.log(e.message);}};

5.axios拦截器

 // 配置请求拦截器myReq.interceptors.request.use((config) => {// 请求拦截器中拦截的是axios请求的配置项console.log("请求配置项", config);// 拦截配置项后,要把配置项return出去,否则请求无法进行return config;},(error) => {// 进入这个函数,说明请求未到达服务器,请求就有错,此时可以放回一个失败的promise对象,同时错误原因也返回出去,await等到这个失败的promise对象,可以知道请求有问题return Promise.reject(error);});// 配置响应拦截器myReq.interceptors.response.use((response) => {console.log("response", response);return response;},(error) => {//如果请求出现错误,则我们希望进入发请求的时候catch的异常处理//在发请求阶段得到响应后异常处理怎么进去呢?await等的时候失败的promise对象//所以我们可以把error包装成一个失败的promise对象,返回出去交给请求的地方处理console.log("响应拦截器失败处理函数");// 进入这个函数,说明请求到达服务器,回来的时候发现有错,返回这个失败的promise对象,可以知道请求有问题return Promise.reject(error);});

6.axios拦截器的应用

      // 配置请求拦截器myReq.interceptors.request.use((config) => {// 请求拦截器中拦截的是axios请求的配置项console.log("请求配置项", config);// 应用1:此处进度条开始NProgress.start()return config;},(error) => {// 响应有问题,进度条也可以结束NProgress.done()// 进入这个函数,说明请求未到达服务器,请求就有错,此时可以放回一个失败的promise对象,同时错误原因也返回出去,await等到这个失败的promise对象,可以知道请求有问题return Promise.reject(error);});// 配置响应拦截器myReq.interceptors.response.use((response) => {console.log("response", response); //response是个对象 其中data是response回来的数据// 1.响应拦截器用法1:进度条执行结束 NProgress// 此处进度条结束NProgress.done()// 应用2:对响应的数据进行处理 server可以根据我们传过去的信息判断符不符合要求,response根据符不符合要求返回对应状态 我们可以通过判断状态 看是否提供对应页面//比如登录-无论登录成功还是失败,只要把结果响应回来了都算响应成功//但是对于我们来说,只有登录成功才叫响应成功,如果登录失败,我们就把他交给catch处理// 拦截配置项后,要把配置项return出去,否则请求无法进行if(response.data.code !== 200){return Promise.reject({message:response.data.msg})}return response.data;},(error) => {//如果请求出现错误,则我们希望进入发请求的时候catch的异常处理//在发请求阶段得到响应后异常处理怎么进去呢?await等的时候失败的promise对象//所以我们可以把error包装成一个失败的promise对象,返回出去交给请求的地方处理console.log("响应拦截器失败处理函数");// 进入这个函数,说明请求到达服务器,回来的时候发现有错,返回这个失败的promise对象,可以知道请求有问题return Promise.reject(error);});

7.axios取消请求

      // 取消axios请求 首先要拿到cancelToken构造函数,cancelToken构造函数在axios上// 注:cancelToken构造函数只能在axios拿 不能从axios实例上拿const CancelToken = axios.CancelToken;// 定义一个全局变量,用来保存局部中得等到取消请求的函数let cancel=null;oCancel.onclick=function(){// 启动cancel函数// 函数里可以传入参数 请求理由cancel()}

跨域

1.JSONP

<button id="get">get-得到用户信息</button><script>const oGet = document.getElementById('get');//回调函数,用来jsonp发请求的时候 携带回调函数给服务端 用来服务端调用function callback(value) {alert("我是callback:" + value)}let oScript = null;//get请求oGet.onclick = async function () {if (oScript) {oScript.remove()}//因为跨域原因无法直接请求,但是script标签的src可以跨域,我们选择创建一个script标签,帮助我们发送请求oScript = document.createElement("script");//把请求地址赋值给script标签的src属性oScript.src = "http://192.168.20.82:8888/user?userID=001&cb=callback";//把script标签插入页面中,则直接会把请求发送document.body.appendChild(oScript)}</script>//服务器部分
app.get("/user", (req, res) => {const {userID,//拿到客户端发送的回调函数名称cb} = req.query;console.log("请求进来了", userID);//模拟客户端需要的数据const userToken = "abcdefghijk";//设置响应的数据类型是js类型res.set("Content-Type", "application/javascript;charset=utf-8")//给客户端的script标签响应一个js代码(js代码是调用了客户端发来的回调函数,并传入了数据作为实参)return res.send(`${cb}('${userToken}')`);})

2.cors

//get请求oGet.onclick = async function () {try {//get请求参数放在配置中写const re = await axios.get("http://192.168.20.82:8888/user", {params: {userID: "001"}})console.log("最终的数据是1", re);} catch (e) {console.log("请求出现异常,异常信息是", e);}}
//服务器部分
app.get("/user", (req, res) => {const {userID} = req.query;//获取当前的origin地址const nowOrigin = req.headers.origin;//设置白名单
const allowOrigin = ["http://127.0.0.1:5501", "http://127.0.0.1:5505", "http://127.0.0.1:5500", "http://127.0.0.1:5504"]//判断当前的origin地址是否在白名单中 如果在 则设置跨域if (allowOrigin.includes(nowOrigin)) {//设置cors跨域res.set("Access-Control-Allow-Origin", nowOrigin)}if (userID === "001") {return res.json({code: 200,msg: "ok",data: {username: "laoli"}});}res.json({code: 201,msg: "用户id出错",data: null})
})

3.代理

缓存

1.强制缓存

强制缓存:

  1. 强制缓存是向浏览器缓存查找请求结果,并根据请求结果来决定我们是否可以使用缓存的过程

  2. 简单来讲,就是浏览器直接使用自己的缓存,不进行任何的请求

  3. 强制缓存的设置过程

    • 客户端请求的时候,需要携带 Cache-Control请求头字段,值是 max-age=XXXX(秒数)

    • 服务端响应的时候,也需要携带 Cache-Contorl的响应头字段,值是max-age=XXXX(秒数)

    • 当下次再次请求的时候,判断自己是否符合强制缓存条件,如果符合,则直接读取缓存,如果不符合,则会走协商缓存

///服务器部分
app.get("/img", (req, res) => {const filePath = path.resolve(__dirname, "./01.jpg");const rs = fs.createReadStream(filePath);res.set("Cache-Control", "max-age=1000")rs.pipe(res)
})

2.协商缓存

协商缓存:

  1. 客户端向服务端发送请求,请求某一个资源文件
  2. 服务端向客户端响应当前的文件,并且在响应头中添加两个字段,分别是文件的唯一标识(eTag)和当前被请求文件的最后一次修改时间(last-modified)
  3. 客户端接收到响应,还要处理关于协商缓存的信息,把文件的唯一标识和最后一次修改时间保存下来,并且还要修改字段名,把eTag更名为if-none-match,把last-modified更名为if-modified-since
  4. 客户端再次请求资源,会携带if-none-match和if-modified-since字段
  5. 服务端接收到请求后,会把if-none-match和自己的eTag进行比较,把if-modified-since和自己的last-modified进行比较,如果都相同,则直接响应304状态码,要求读取缓存。否则响应数据,并携带最新的eTag和last-modified
//服务器部分
app.get("/axios", async (req, res) => {console.log(1);const filePath = path.resolve(__dirname, "axios.min.js");//获取文件的唯一标识const Etag = etag(filePath);//获取文件的最后修改时间 fs.stat可以得到文件的详情const stat = promisify(fs.stat);const fileStat = await stat(filePath)console.log(fileStat.mtime.toGMTString());const lastModified = fileStat.mtime.toGMTString();//获取请求对象携带的if-modified-since if-none-matchconsole.log(req.headers);const ifNoneMatch = req.headers["if-none-match"];const ifModifiedSince = req.headers['if-modified-since'];//把请求携带的信息和服务端文件的信息对比,如果有一个不一致,则重新响应文件if (ifNoneMatch !== Etag || ifModifiedSince !== lastModified) {//当读取新资源的时候,需要重新设置文件唯一标识 和最后修改时间的 响应头res.set("etag", Etag);res.set("last-modified", lastModified);return res.sendFile(filePath);}//如果上边的判断不成立,则需要读取缓存res.status(304).send();
})

3.压缩

app.get("/", async (req, res) => {const filePath = path.resolve(__dirname, "./index.html")const rs = fs.createReadStream(filePath);//查看支持的压缩格式const accpetEncoding = req.headers['accept-encoding'];console.log(accpetEncoding)//根据客户端的支持的格式来进行不同的压缩if (accpetEncoding.includes("gzip")) {//zlib.createGzip()//创建一个gzip压缩的盒子,能够被流式写入const gzipFile = rs.pipe(zlib.createGzip()) //返回一个gizp压缩格式的可读流//告诉客户端我响应的压缩格式是什么res.set("content-encoding", "gzip")//把压缩好的文件写入到响应中return gzipFile.pipe(res) //22.1kb}//根据客户端的支持的格式来进行不同的压缩if (accpetEncoding.includes("deflate")) {//zlib.createDeflate()//创建一个gzip压缩的盒子,能够被流式写入const gzipFile = rs.pipe(zlib.createDeflate()) //返回一个gizp压缩格式的可读流//告诉客户端我响应的压缩格式是什么res.set("content-encoding", "deflate")//把压缩好的文件写入到响应中return gzipFile.pipe(res) //22.1kb}//没有压缩的响应rs.pipe(res) //99.1kb
})

事件轮询机制

1. Global对象

2. nodejs事件轮询机制

3. 浏览器事件轮询机制

回调函数:将函数A作为函数B的参数,并且函数A在函数B内进行调用

3.1 代码分类

  • 初始化代码(同步代码):设置定时器、绑定事件,发送ajax等等
  • 回调执行代码(异步代码):定时器回调函数,事件回调函数,ajax回调函数

3.2 轮询机制

  • 浏览器先执行同步代码,再执行异步代码。
  • 在执行同步代码的时候,把异步代码交给浏览器的管理模块进行管理(事件管理模块、ajax管理模块,定时器管理模块)。
  • 当异步代码的回调函数需要执行的时候,会把回调函数放在回调队列(任务队列)中等待执行。
  • 当同步代码执行完毕之后,主线程会去任务队列中轮询,并将任务队列中的任务(回调函数)取出来执行,主线程不断重复这一步,因此叫做事件轮询。

4. 宏任务和微任务

  1. 异步代码有优先级关系。有的优先级高先执行,有的优先级低后执行。分为宏任务(macrotask )和微任务(microtask )

  2. 微任务是js自身发起的: Promise.then/catch/fanally,await语句后的内容,process.nextTick,queueMicrotask

  3. 宏任务是宿主发起的:包括整体代码script,setTimeout,setInterval等等

  4. js引擎执行异步代码。会优先执行微任务,再执行宏任务

  5. 过程如下:

    5.1 执行栈选择最先进入队列的宏任务(一般都是script),执行其同步代码直至结束;

    5.2 检查微任务队列中是否存在微任务,有则会执行至微任务队列为空;

    5.3 执行宏任务中的异步代码

    5.4 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中

    5.5 当前宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)

    5.6 当开始下一个宏任务(从事件队列中获取)

模块化

1.原始

2.commonJS模块化

3.ES6模块化规范

webpack

1.基础配置板块

五大’护法’

  1. entry:入口起点(entry point)

    • 打包时,第一个被访问的源码文件,指示webpack应该使用哪个模块(webpack中一切都是模块),来作为构建其内部依赖图的开始

    • 默认是src/index.js(可以通过配置文件指定)

    • webpack可以通过入口,加载整个项目的依赖

  2. output:出口

    • 打包后,输出的文件名称
    • 默认是dist/main.js(可以通过配置文件指定)
  3. loader :加载器

    • loader让webpack能够去处理那些非JavaScript文件(webpack自身只能解析JavaScript)
  4. plugins:插件

    • 实现loader之外的其他功能(打包优化和压缩等)
    • 是Webpack的支撑,用来实现丰富的功能
  5. mode :模式

    • 生产模式 production
    • 开发模式 development

2.使用webpack配置文件

2.1打包详细配置

2.1 .1打包CSS

2.1.1.1 基本打包
  1. 打包CSS包含:打包逻辑、打包LESS、打包成独立的CSS文件、添加样式前缀、格式校验、压缩CSS
  2. css-loader:将CSS转换为JS(将css输出到打包后的js文件中)
  3. style-loader:把包含CSS内容的JS代码,挂载到页面的style标签中
  4. 需要在入口文件引入CSS(import’./css/main.css’)
  5. 加载器配置:use:[‘style-loader’,‘css-loader’]
  6. 安装 npm i css-loader style-loader -D
rules[{//匹配后缀名test:/\.css$/i,use:[//use中loader加载是有顺序的,先上后下,注意有的loader需要按照顺序书写'style-loader','css-loader']}
]
2.1.1.2 打包less
  1. less-loader:打包less的加载器

  2. 匹配后缀名

  3. 加载器配置:use:[‘style-loader’,‘css-loader’,‘less-loader’]

  4. 安装 npm i less less-loader -D

rules: [{test: /\.less$/i,use: [//use中loader加载是有顺序的,先下后上,注意有的loader需要按照顺序书写"style-loader",'css-loader','less-loader']
}]

React

1.基本使用

1.1 相关js库

  1. react.js:React核心库。
  2. react-dom.js:提供操作DOM的react扩展库。
  3. babel.min.js:解析JSX为JS的库。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><!-- react的核心包 包含了react的核心内容 --><script src="./js/react.development.js"></script><!-- react中处理虚拟DOM和diffing算法的工具 --><script src="./js/react-dom.development.js"></script><!-- react中使用的jsx语法,babel用来编译jsx语法 --><script src="./js/babel.min.js"></script></head><body><div id="app"></div><!-- 因为script标签中写的是jsx语法,所以我们要一如babel并且设置script标签的type为text/babel --><script type="text/babel">// 定义一个虚拟DOMconst vDOM = <h1>hello world</h1>;// 使用ReactDOM的render方法 把虚拟DOM渲染在真实DOM中ReactDOM.render(vDOM, document.getElementById("app"));</script>                     </body>
</html>

1.2 js方式创建虚拟DOM

<!-- 此处用js写虚拟DOM所以不用定义type,默认就是text/javascript -->
<script>// 利用js创建一个虚拟DOM// const vDOM = React.createElement(//   "div",//   {//     className: "box",//     // data-index:1,//     // dataIndex: 1,//     "data-index": 1,//   },//   "欢迎"// );// 嵌套const vDOM = React.createElement("div",{className: "box",// data-index:1,// dataIndex: 1,"data-index": 1,},React.createElement("h1",null,React.createElement("span", null, "title")));// 使用ReactDOM的render方法 把虚拟DOM渲染在真实DOM中ReactDOM.render(vDOM, document.getElementById("app"));

1.3 jsx方式创建虚拟DOM

  <style>.red {color: red;}</style></head><body><div id="app"></div>
<!-- 因为script标签中写的是jsx语法,所以我们要一如babel并且设置script标签的type为text/babel --><script type="text/babel">const vDOM = (<div><h3>boring</h3><ul><li className="red">boring1</li><li>boring2</li><li>boring3</li></ul></div>);// 使用ReactDOM的render方法 把虚拟DOM渲染在真实DOM中ReactDOM.render(vDOM, document.getElementById("app"));

1.4 虚拟DOM与真实DOM

虚拟DOM:

  1. 本质就是Object类型的对象(一般对象)

  2. 虚拟DOM比较“轻”,真实DOM比较“重”,虚拟DOM是react内部在用的,无需真实DOM身上那么多属性

  3. 虚拟DOM早晚会被react转变成真实DOM,呈现在页面上

<script type="text/babel" >//1.创建一个虚拟DOMconst VDOM = <h1>Hello,React</h1>console.log(VDOM) //输出的是虚拟DOM,本质就是Object类型的一般对象const TDOM = document.getElementById('test')console.log(TDOM) //输出的是真实DOMdebugger;//2.将虚拟DOM准备真实DOM渲染到页面ReactDOM.render(VDOM,document.getElementById('test'))
</script>

1.5 React JSX语法

js表达式:有返回值 js语句:没有返回值

  1. 定义虚拟DOM时,不要写引号

  2. 标签结构中要混入js表达式,需要用{}做分割,即:{js表达式}

  3. 指定标签的类名时,用className

  4. 行内样式,要用style={{}} ,外面一层{}是表示js区域,里面的{}表示对象,里面填写键和键值,像font-size这种属性,要转为fontSize

  5. 只能有一个根标签

  6. 标签必须闭合

  7. 标签首字母:

    • 如果标签首字母小写:则该标签转为html中同名元素,若若html中无该元素,则报错。

    • 如果标签首字母大写:则表明是React去渲染的对应组件,若没有定义过该组件,则报错。

1.6 JSX中的js区域{}里面值表现

jsx中{}得到不同类型的值的反应

  1. string和number:直接插入

  2. null,undefined,true,false:直接为空

  3. 数组:把数组的值按顺序放入虚拟DOM中

  4. 对象:对象不允许作为jsx插值中的值

  <script type="text/babel">const arr = ["Angular", "React", "Vue"];const vDOM = (<div><h1>前端框架</h1><ul>{arr.map((item, index) => {//每一个虚拟DOM变化的地方都有一个key值,以便以后改变的时候有对比参照return <li key={index}>{item}</li>;})}</ul></div>);ReactDOM.render(vDOM, document.getElementById("box"));

2.React中组件

2.1 函数式组件

如果构造函数实例化后,其返回值:

  1. 如果构造函数里面没有return,或者return了一个基本数据类型,则函数返回一个实例化对象
  2. 如果构造函数内部返回了一个复杂数据类型,则函数实例化后,则函数返回这个复杂数据
<script type="text/babel">//1.声明一个函数式组件function Dome(){console.log(this);//undefinedreturn <h1>我是用函数定义的组件</h1>}//2.渲染组件到页面  ReactDOM.render(<Dome/>,getElementById('app'))
</script>

函数式组件定义

  1. 定义组件函数时,函数名首字母必须大写
  2. 该组件函数必须有返回值,返回值一般为虚拟DOM
  3. 渲染函数组件到页面中时,函数组件必须放到自结束标签中,<函数名 />

​ 函数式组件的特点

  1. 里面的this为undefined(在babel严格模式下)
  2. 没有生命周期
  3. 没有状态

使用函数组件的过程:

  1. 寻找组件申明,确认组件类型为函数组件
  2. 把函数组件给调用(一定不要自己调用函数,而是使用组件调用的方式,否则将不具备任何组件特征)
  3. 组件调用返回一个虚拟DOM,放到设置的位置中
  4. 将虚拟DOM转为真实DOM,并渲染到页面中

2.2 类式组件

2.1.1 类的基本知识

公有方法和属性:设置给实例化对象的属性和方法

私有方法和属性:声明在构造函数中的变量或函数

静态方法和属性:js中无需实例化就可以获得的的属性和调用的方法 就是给构造函数自己的属性和方法

//创建一个Person类
class Person{//构造器方法或函数constructor(name,age){//构造器中的this指向类的实例对象//公有属性this.name=name;this.age=age}//一般方法 //公有方法speak(){//speak方法放在了Person类的原型对象上,供实例使用//通过Person实例调用speak时,speak中的this就是Person实例console.log(`我叫${this.name},年龄是${this.age}`)}
}
//创建一个Person的实例对象
const p1=new Person('tom',18)
const p2=new Person('jerry',20)//创建一个Student类,继承于Person类
class Student extends Person{//若和Person类父类属性一致,不需要写构造器,也可以正常继承constructor(name,age,gender){//super方法,将与Person类父类属性一致的属性作为参数super(name,age)this.gender=gender}//重写从父类继承过来的方法speak(){console.log(`这是Student的原型对象,我叫${this.name},年龄是${this.age},gender是${this.gender}`)}
}
const s1=new Student('zs',21,'nan')
s1.speak();

2.1.2 类式组件

class Demo extends React.Component{render(){return <h1>我是标题</h1>}
}
ReactDOM.render(<Demo/>,document.getElementById('app'))

类式组件的定义:

  1. 使用class声明一个类组件,继承React.Component
  2. 在类组件中必须要有一个render方法,用来返回一个虚拟DOM

render方法:

  1. render方法定义在类组件中,属于类组件原型对象上的方法
  2. 当类组件被使用时,render方法就会被调用

使用类组件的过程:

  1. 寻找组件申明,确认组件类型为类组件
  2. 把类组件实例化,得到一个实例化对象(组件实例)
  3. 组件实例会调用类组件原型对象上的render方法,render方法返回一个虚拟DOM,放到设置的位置中
  4. 将虚拟DOM转为真实DOM,并渲染到页面中

3. 组件实例三大核心属性一:state

3.1 state基本写法

组件实例重要属性-state

  1. state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
  2. state代表的是当前组件的状态,状态中包含了当前组件需要的数据
  3. 如果虚拟DOM使用了状态中的数据,则修改状态中的数据,虚拟DOM会随之更新
  4. state设置给实例对象的,state需要定义在类的constructor中 this.state={XXX:XXX}

3.2 修改state的方法

方法:

  1. state不能直接修改,需要使用组件实例的setState方法
  2. setState接受的参数是一个对象,将来会合并给原有的state中

练习:


3.3 数量加减的练习


4. React绑定事件

4.1 基础绑定事件

class Demo extends React.Component{render(){return <button onClick={this.printWeather}>点击<button/>}//事件函数,位于Demo原型对象上printWeather(){console.log('今天天气好')console.log('this',this)//this为undefined}}ReactDOM.render(</Demo>,document.getElementById('app'))

事件定义:

  1. 命名采用小驼峰方法命名,放在绑定的标签中
  2. 被绑定的函数位于jsx的js{}区域
  3. 一般函数为原型上或实例上方法,需要用this.方法名调用,this不能漏了

这个基础绑定事件里面,有一个问题就是:React里面默认事件调用函数里面的this为undefined

4.2 绑定事件进阶

4.2.1 方法一

class Demo extends React.Component{render(){this.printWeather=this.printWeather.bind(this)return <button onClick={this.printWeather}>点击<button/>}//事件函数,位于Demo原型对象上printWeather(){console.log('今天天气好')console.log('this',this)//this为调用的实例对象}}ReactDOM.render(</Demo>,document.getElementById('app'))

4.2.1 方法二

class Demo extends React.Component{constructor(){super()//不可少,少了无法得到thisthis.printWeather=this.printWeather.bind(this)}render(){return <button onClick={this.printWeather}>点击<button/>}//事件函数,位于Demo原型对象上printWeather(){console.log('今天天气好')console.log('this',this)//this为调用的实例对象}}ReactDOM.render(</Demo>,document.getElementById('app'))

4.3 绑定事件和state简写

constructor函数的两个用法:

  1. 需要实例化的时候传参
  2. this指向组件实例

但是我们在未来react中基本不用实例化传参,直接在class语法中给一个变量赋值,这个变量就属于组件实例的属性

class Demo extends React.Component{//直接写在类class里面的属性,是实例的属性state={project:'js',}//事件函数的简写位置printWeather=this.printWeather.bind(this)render(){return <button onClick={this.printWeather}>点击<button/>}//事件函数,位于Demo原型对象上printWeather(){console.log('今天天气好')console.log('this',this)//this为调用的实例对象}}ReactDOM.render(</Demo>,document.getElementById('app'))

4.4 真正绑定事件写法

4.4.1 方法一


4.4.2 方法二


4.5 React事件的event事件对象

js事件:

  1. onput事件:当表单内容发生改变就会触发
  2. onchange事件:当表单内容发生改变并失去焦点的时候触发
  3. React的onChange事件是当前表单内容发生改变就会触发

React事件对象:

  1. 可以直接获取事件对象
  2. 不能使用return false来阻止默认事件,需要直接使用preventDefault()来阻止默认事件
  3. e.target可以获取当前的元素,就可以不用使用ref
      class App extends React.Component {render() {return (<div><a href="http://www.baidu.com" onClick={(e)=>{e.preventDefault()}}></a><button onClick={e=>{console.log(e.target.textContent)}}>点击可以获取内容</button><input type="text" onChange={e=>{console.log(e.target.value)}} /></div>);}}ReactDOM.render(<App/>,document.getElementById('app'))

5. 组件实例三大核心属性二:props

5.1 基础的传值方式

props:

  1. 主要用于外部向组件内部传值使用
  2. 写法就是在组件标签上书写属性
  3. 接受props的组件,可以在组件实例上访问到props对象
  4. 不要修改props的值,props是只读的
class Myself extends React.Component{//组件实例内部,可以通过this.props拿到从外部传过来的值,不要修改props的值,props是只读的render(){console.log(this.props)//看下图1let {name,age,gender}=this.props.mes;//如果真的要修改值,则可以结构出来变量,然后对变量进行修改name+='~';return (<ul><li>我的名字是{name}</li><li>我的性别是{age}</li><li>我的年龄是{gender}</li></ul>)}
}
class App extends React.Component{state={persons:{[name:'zs',age:20,gender:'n'],[name:'ls',age:21,gender:'w'],[name:'ws',age:23,gender:'w'],}}render(){const {persons}=this.state;return (<div><Myself mes={persons[0]}/><Myself mes={persons[1]}/><Myself mes={persons[2]}/></div>)}
}
ReactDOM.render=(<App/>,document.getElementById('app'))

this.props 对象:(图一)

5.2 props传值

 class Myself extends React.Component {render() {console.log(this);console.log(this.props);const { name, age, gender } = this.props.mes;return (<ul><li>姓名是:{name}</li><li>年龄是:{age}</li><li>性别是:{gender}</li></ul>);}}class App extends React.Component {state = {persons: [{ name: "zs", age: 20, gender: "nan" },{ name: "ls", age: 18, gender: "wn" },{ name: "wl", age: 25, gender: "nan" },],};render() {const { persons } = this.state;return (<div>{persons.map((item, index) => {//每一个虚拟DOM变化的地方都有一个key值,以便以后改变的时候有对比参照return <Myself mes={item} key={index} />;})}</div>);}}

5.3 props批量传值

{…obj} 在jsx中书写时,可以展开对象为key-value(jsx+react实现的,不是js语法)

 class Myself extends React.Component {render() {console.log(this);console.log(this.props);const { name, age, gender } = this.props;return (<ul><li>姓名是:{name}</li><li>年龄是:{age}</li><li>性别是:{gender}</li></ul>);}}class App extends React.Component {state = {persons: [{ name: "zs", age: 20, gender: "nan" },{ name: "ls", age: 18, gender: "wn" },{ name: "wl", age: 25, gender: "nan" },],};render() {const { persons } = this.state;return (<div>{persons.map((item, index) => {// {...obj}在jsx中书写时,可以展开对象为key-value(jsx+react实现,不是js语法)// 后面再给组件添加属性,也没有影响,this.props会直接展开为一个对象return <Myself {...item} index={index} key={index} />;})}</div>);}}

5.4 配置props限制

使用:

  1. 引入第三方包
  2. 设置为要传入值的构造函数的静态属性 class类组件里面可以static配置或外面构造函数的静态属性
      class Myself extends React.Component {static propTypes = {name: PropTypes.string.isRequired,age: PropTypes.number,gender: PropTypes.string,gender: PropTypes.oneOf(["nan", "wn"]),};render() {console.log(this);const { name, age, gender } = this.props;return (<ul><li>姓名是:{name}</li><li>年龄是:{age}</li><li>性别是:{gender}</li></ul>);}}//   Myself.propTypes={//     name:PropTypes.string.isRequired,//     age:PropTypes.number,//     gender:PropTypes.string,//     gender:PropTypes.oneOf(['nan','wn'])//   }class App extends React.Component {state = {persons: [{ name: "zs", age: 20, gender: "nan" },{ name: "ls", age: 18, gender: "wn" },{ name: "wl", age: 25, gender: "nan" },],};render() {const { persons } = this.state;return (<div>{persons.map((item, index) => {return <Myself {...item} key={index} />;})}</div>);}}

5.5 函数式组件的props写法

过程:

  1. 判断组件类型,确认为函数式组件
  2. 判断为函数式组件后,则调用函数
  3. 将组件中的属性合并到一个对象传给函数作为参数
function Myself({ name, age, gender }) {return (<ul><li>姓名是:{name}</li><li>年龄是:{age}</li><li>性别是:{gender}</li></ul>);};Myself.propTypes = {name: PropTypes.string.isRequired,age: PropTypes.number,gender: PropTypes.string,};const persons = [{ name: "zs", age: 20, gender: "nan" },{ name: "ls", age: 18, gender: "wn" },{ name: "wl", age: 25, gender: "nan" },];function App() {return (<div>{persons.map((item, index) => {let { name, age, gender } = item;return (<Myself name={name} age={age} gender={gender} key={index} />);})}</div>);};ReactDOM.render(<App />, document.getElementById("app"));

6. 组件实例三大核心属性三:refs

使用情景:

  1. 管理焦点,文本选择或媒体播放
  2. 触发强制动画
  3. 集成第三方DOM库

6.1 refs的字符串方式-方法一:

基本步骤:字符串方式

  1. refs主要是React用来操作DOM的
  2. ref的字符串方法,只需要在备货的DOM上设置ref属性,值为一个自定义名称XXX 类似于设置ID给标签
  3. 在当前组件的其他位置可以通过this.refs.XXX(设置的名称)获取当前DOM
      class App extends React.Component {render() {console.log(this);return (<div><input type="text" ref="oIpt" /><button onClick={this.printMsg}>打印输出内容</button></div>);}printMsg = () => {console.log(this.refs.oIpt.value);console.log(this.refs);};}ReactDOM.render(<App />, document.getElementById("app"));

6.2 refs的回调函数方式-方法二

基本步骤:回调形式

  1. 给被获取的DOM设置ref属性
  2. ref属性的值是一个函数(箭头函数)
  3. 当读取到ref属性时,React会将ref属性的回调函数调用,并传递当前的DOM为实参
  4. 把这个接受实参的形参赋值给实例对象的一个属性,这个属性就是被获取的DOM
      class App extends React.Component {render() {console.log(this);return (<div><input type="text" ref={(c) => (this.oIpt = c)} /><button onClick={this.printMsg}>打印输出内容</button></div>);}printMsg = () => {console.log(this.oIpt);console.log(this.oIpt.value);console.log(this.refs);};}ReactDOM.render(<App />,document.getElementById("app"));

6.3 refs的创建容器方式-方法三

基本步骤:创建容器方式

  1. 首先使用React.createRef()方法创建一个容器
  2. 把这个容器给到要被获取的DOM节点的ref属性上
  3. 通过this.容器.current获取到当前的DOM
      class App extends React.Component {//设置一个ref的容器oIpt = React.createRef();render() {console.log(this);return (<div><input type="text" ref={this.oIpt} /><button onClick={this.printMsg}>打印输出内容</button></div>);}printMsg = () => {console.log(this.oIpt);console.log(this.oIpt.current.value);console.log(this.refs);};}ReactDOM.render(<App />, document.getElementById("app"));

7.React收集表单数据

7.1 非受控表单

     class Demo extends React.Component {render() {return (<form action="###">用户名:<input type="text" ref={(c) => (this.user = c)} /><br />密码:<input type="text" ref={(c) => (this.pass= c)} /><br /><button onClick={this.login}>登录</button></form>)}login = (e) => {e.preventDefault();console.log(`用户名是${this.user.value},密码是${this.pass.value}`);};}

7.2 受控表单

 class Demo extends React.Component {state = {user: "",pass: "",};render() {return (<form action="###">用户名:<input type="text" onChange={this.setUser} /><br />密码:<input type="text" onChnage={this.setPass} /><br /><button onClick={this.login}>登录</button></form>);}setUser = (e) => {this.setState({ user: e.target.value });};setPass = (e) => {this.setState({ user: e.target.value });};login = (e) => {e.preventDefault();const { user, pass } = this.state;console.log(`用户名是${user},密码是${pass}`);};}

7.3 受控表单高阶写法

class Demo extends React.Component {state = {user: "",pass: "",};render() {return (<form action="###">用户名:<input type="text" onChange={this.setFormData("user")} /><br />密码:<input type="text" onChange={this.setFormData("pass")} /><br /><button onClick={this.login}>登录</button></form>);}setFormData = (type) => {return (e) => {this.setState({ [type]: e.target.value });};};login = (e) => {e.preventDefault();const { user, pass } = this.state;console.log(`用户名是${user},密码是${pass}`);};}

7.4 高阶函数和函数柯里化

高阶函数:如果有函数A,A只需满足以下2个条件中任意一个,A就是高阶函数

  1. 若A函数接受的参数是一个参数,例如数组相关方法,promise,setTimeout
  2. 若A函数调用后,返回值任然是一个函数,例如防抖节流函数

函数柯里化:通过函数调用,继续返回函数的方式,实现多次接受参数的函数形式

      // 函数柯里化function add(a) {return function (b) {return a + b;};}add(1)(2);

8. React组件生命周期

8.1 生命周期引入

 <div id='app'></div><script type='text/babel'>class App extends React.Component{state = {opacity : 1}componentDidMount(){let {opacity} = this.stateconsole.log(opacity);this.timer = setInterval(()=>{opacity -= 0.1;if(opacity<=0){opacity = 1}this.setState({opacity})},200)}render(){const {opacity} = this.state;return (<div><p style={{opacity:opacity}}>React 太简单了,都是对勾</p>  <button onClick={()=>{ReactDOM.unmountComponentAtNode(document.getElementById('app'))}}>从入门到放弃</button></div>)}componentWillUnmount(){clearInterval(this.timer)}}ReactDOM.render(<App/>,document.getElementById('app'))

8.2 生命周期挂载流程

初始化阶段由ReactDOM.render()触发—初次渲染

若触发流程5,组件卸载不再呈现在页面中

  1. React组件挂载流程1-构造器constructor()
  2. React组件挂载流程2-React组件即将挂载 componentWillMount()
  3. React组件挂载流程3-渲染render()
  4. React组件挂载流程4-React组件已经挂载完成componentDidMount()
  5. React组件挂载流程5-React组件即将卸载componentWillUnmount() ,由ReactDOM.unmountComponentAtNode()触发,参数为组件卸载的父节点
 class App extends React.Component {// 挂载流程1 :constructor构造器constructor() {super();console.log("constructor 执行了");}// 挂载流程2 :React组件即将挂载componentWillMount() {console.log("componentWillMount执行了");}// 挂载流程3:React组件渲染render() {console.log("render 执行了");return (<div><p>更新阶段 由组件内部this.setSate()或父组件重新render触发</p><buttononClick={() => {ReactDOM.unmountComponentAtNode(document.getElementById("app"));}}>从入门到放弃</button></div>);}// 挂载流程4:React组件已经完成挂载componentDidMount() {console.log(" componentDidMount执行了");}// 挂载流程5:React组件即将卸载 由ReactDOM.unmountComponentAtNode()触发,参数为组件从哪里卸载componentWillUnmount() {console.log(" componentWillUnmount执行了");}}ReactDOM.render(<App />, document.getElementById("app"));

8.3 生命周期更新流程

由组件内部this.setSate()或父组件重新render触发

  1. React组件更新流程1-阀门,可以控制state更新后,render函数是够渲染 shouldComponentUpdate() 必须返回布尔值 return true (渲染)/false(不渲染);此处若是false,后面的流程2-4不会触发执行

  2. React组件更新流程2-组件将要更新 componentWillUpdate() , this.forceUpdate()可以触发强制更新

    如果流程触发了强制更新,前面流程1-shouldComponentUpdate() 不管返回什么,都不会生效执行

  3. React组件更新流程3-渲染render()

  4. React组件更新流程4-组件完成更新 componentDidUpdate()

  5. React组件更新流程5-React组件即将卸载componentWillUnmount(),由ReactDOM.unmountComponentAtNode()触发,参数为组件卸载的父节点

class App extends React.Component {// 挂载流程1 :constructor构造器constructor() {super();console.log("constructor 执行了");}state = {count: 0,};// 挂载流程2 :React组件即将挂载componentWillMount() {console.log("componentWillMount执行了");}// 挂载流程3:React组件渲染render() {console.log("render 执行了");let { count } = this.state;return (<div><h1>{count}</h1><p>更新阶段 由组件内部this.setSate()或父组件重新render触发</p><button onClick={() => {this.setState({count:++count})}}>+</button><br /><button onClick={() => {this.forceUpdate()}}>强制更新</button><buttononClick={() => {ReactDOM.unmountComponentAtNode(document.getElementById("app"));}}>从入门到放弃</button></div>);}// 挂载流程4:React组件已经完成挂载componentDidMount() {console.log(" componentDidMount执行了");}//更新流程2:阀门,可以控制state更新后,render函数是否渲染shouldComponentUpdate() {console.log("---componentShouldUpdate执行了---");return true;}// 更新流程3:即将更新 this.forceUpdate()方法可以要求强制更新componentWillUpdate() {console.log("---componentWillUpdate执行了---");}// 更新流程4:更新已完成 componentDidUpdate(){console.log("---componentDidUpdate执行了---");}// 挂载流程5:React组件即将卸载 由ReactDOM.unmountComponentAtNode()触发,参数为组件从哪里卸载componentWillUnmount() {console.log(" componentWillUnmount执行了");}}ReactDOM.render(<App />, document.getElementById("app"));

8.4 生命周期父传子更新流程

子中更新流程新增:componentWillReceiveProps()

  1. 初次渲染过程,父与子正常渲染,子渲染流程是不会执行上面的生命函数的;
  2. 后面父组件只要重新渲染,无论是否接受父组件的props都要执行这个生命周期函数,而且所有更新流程也要执行
 class App extends React.Component {state = {weather: "hot",count: 0,};render() {let { weather ,count } = this.state;return (<div><p>{weather},{count}</p><button onClick={()=>{this.setState({weather:'cool'})}}>改变天气</button><br/><button onClick={()=>{this.setState({count:++count})}}>改变count</button><p>我是父组件 我下边是子组件</p><Son /></div>);}}class Son extends React.Component{render (){return (<div><button onClick={()=>{ReactDOM.unmountComponentAtNode(document.getElementById('app'))}}>卸载组件</button></div>)}componentWillMount(){console.log("即将渲染了");}componentDidMount(){console.log("已经渲染了");}//此步骤:子组件渲染流程的时候是不执行的// 父组件只要重新渲染,无论是否接受父组件的props都要执行这个生命周期函数componentWillReceiveProps(){console.log('即将接受props');}shouldComponentUpdate(){console.log("---shouldComponentUpdate---");return false;}componentWillUpdate(){console.log("---componentWillUpdate---");}componentDidUpdate(){console.log('---componentDidUpdate---');}componentWillUnmount(){console.log('---componentWillUnmount---');}}ReactDOM.render(<App />, document.getElementById("app"));

9.虚拟DOM和DOM Diffing算法

Diffing算法

 /* //旧<li key=0>laowang input</li><li key=1>laozhang input</li><li key=2>laojun input</li>//新<li key=4>laoyang input</li><li key=0>laowang input</li><li key=1>laozhang input</li><li key=2>laojun input</li>*/class App extends React.Component{state = {person:[{id:"001",name:"laowang"},{id:"002",name:"laozhang"},{id:"003",name:"laojun"}]}render(){const {person}  = this.statereturn (<div><button onClick={this.addPerson}>添加</button> <ul>{person.map((item,index)=>{return <li key={item.id}>{item.name} <input type="text"/></li>})}   </ul></div>)}addPerson = () => {const {person} = this.state;person.unshift({id:"004",name:"laoyang"})this.setState({person})}}ReactDOM.render(<App/>,document.getElementById('app'))

10.脚手架

10.1 useState

步骤:

  1. const [要存的变量名,修改变量的方法名]=useState(变量内容)
  2. 用useContext暴露达到传递的目的

10.2 useContext

步骤:

  1. 父组件 createContext
  2. 利用createContext()创造对应实例组件对象,同时将其暴露出去
  3. 将要传数据的子组件放在<实例组件对象名.Provider><实例组件对象名.Provider/>组件标签中
  4. 该组件标签上有一属性value,可以填写要传给子组件的数据 value={{}}
  5. 子组件里引入useContext ,用useContext(实例组件对象名),接收数据

10.3 useEffect

用法:

  1. useEffect通用副作用函数,可以取代生命周期中的componentDidMount、componentDidUpdate、componentWillUnmount
  2. useEffect接受的第一个参数是函数,如果没有第二个参数的情况下,无论是更新还是初始挂载都会执行这个函数
  3. useEffect接受第二个参数是数组,可以限定数组中哪一个状态变化的时候,再执行useEffect的函数,如果是空数组,则无论状态怎么更新,都不会再次执行
  4. 可以设置多个useEffect,也建议不同的功能有不同的useEffect
  5. useEffect返回一个函数,当这个组件被卸载之前,会调用这个函数

10.4 React-Router-DOM旧版本

10.4.1 基础配置

  • 在 App 组件外层嵌套一个 BrowserRouter 组件
  • 所有导航使用 Link 组件 内部是 to 属性定义路由地址
  • 路由展示区域使用 Route 组件,path 用来对应路径 component 属性用来确定组件

10.4.2 NavLink

  • 当导航组件点击需要切换样式的时候,可以给导航使用 NavLink 组件
  • NavLink 组件提供 activeClassName 属性,里边放的是获取焦点后应该加载的类

10.4.3 自定义 Link 组件

  • 自定一个 Link 组件,通过 props 传值进即可
  • 组件的标签内的内容 也是可以在 props.children 中得到的

10.4.4 Switch 组件

  • 可以把多个 Route 放在一个 Switch 组件中,此时只要匹配到一个 Route,则退出整个 Switch
  • 可以拥有多个 Switch

10.4.5 严格匹配

  • 在 Route 组件上有一个 exact 属性,值是布尔值,负责开启严格匹配
  • 严格匹配:(to:/home/news) (path:/home) 此时是无法匹配的
  • 我们一般不开启严格匹配,否则二级路由无法访问

10.4.6 Redirect

  • 重定向
  • 当所有的 Route 没有完成匹配的时候,则自动走 Redirect 组件,Redirect 组件把地址重定向到我们预设的一个默认地址

10.4.7 组件划分

  • 路由组件(pages)和一般组件(components)
  • 一般组件的 props 就是我们传的值
  • 路由组件的 props 是由 history location match 等对象组成的一个对象,提供了很多操作路由的方法

10.4.8 二级和三级路由

  • 正常向一级一样书写即可
  • 路由切换的时候 默认是组件创建和销毁

10.4.9 params 路由传参

  • 路由导航书写接受参数的变量 /user/:id/:say
  • Link 中的路径中拼接需要数据 /user/001/hello
  • 在路由组件中 使用 props.match.params 得到想要的数据

10.4.10 search 路由传参

  • 路由导航正常书写路径
  • 在 Link 的路径中拼接数据为查询字符串
  • 在路由组件中 使用 props.location.search 得到想要的查询字符串
  • 通过 qs 工具 转为对象

10.4.11 state 路由传参

  • 路由导航的 to 属性写成对象 {pathname:路由路径 state:数据}
  • 在路由组件中 通过 props.location.state 得到想要的数据

10.4.12 编程式路由导航

  • Link 默认转为 a 标签,很多时候不用 a 标签,或者不用点击等情况下需要路由跳转
  • props.history.push(path,state)进行编程式路由导航

10.4.13 replace

  • 声明式路由导航中 使用Link组件的 replace属性为boolean值 来进行确定是否替换历史记录
  • 编程式路由导航中 使用props.history.push/replace方 法来确定是否留下历史记录

10.4.14 前进后退历史纪录

  • 使用props.history.go/goBack/goForward

10.4.15 withRouter

  • 可以让一般组件拥有路由组件的props对象
  • 暴露组件的时候 直接把组件名放在 withRouter方法中暴露出去

10.5 React-Router-DOM v6 版本

10.5.1 BrowserRouter

  • BrowserRouter组件最好放在最顶层所有组件之外,这样能确保内部组件使用 Link 做路由跳转时不出错(相比较 v5 没有变化)

10.5.2 Switch 重命名为 Routes

10.5.3 基础写法

  • Link 组件用来定义选项
  • Route 组件用来定义响应(route 内部加载的组件使用 element 属性 值为对应的组件标签)
  • NavLink组件styleclassName可以接收一个函数,函数接收一个isActive参数,可根据该参数调整样式
<NavLinkto="/about"className={({ isActive }) => {return isActive ? "list-group-item active" : "list-group-item";}}>About</NavLink><NavLinkto="/home"className={({ isActive }) => {return isActive ? "list-group-item active" : "list-group-item";}}>Home</NavLink><Routes><Route path="/" element={<Home />} /><Route path="/home" element={<Home />} /><Route path="/about" element={<About />} /></Routes>

10.5.4 路由传参

  • params 传参

    • 传值方式不变
    • 接收值的方式
  import { useParams } from "react-router-dom";console.log(useParams());
  • search 传参

    • 传值方式不变
    • 接收值的方式
   import { useLocation } from "react-router-dom";console.log(useLocation());
  • state 传参

    • 使用编程式路由导航
import { useNavigate } from "react-router-dom";<buttononClick={() => {to("/list", { state: { say: "byebye" } });}}></button>;

​ 接收值的方式

import { useLocation } from "react-router-dom";console.log(useLocation());
  • 二级路由

    • 在一级路由的 Route 中书写 path 为:<Route path="/about/*" element={<About />} />
    • 在二级路由的时候
 <h3>我是About的内容</h3><Link to="/about/hello">hello</Link><Link to="/about/world">world</Link><Routes><Route path="hello" element={<Hello />}></Route><Route path="world" element={<World />}></Route></Routes>

正则:

正则对象调用test方法,传入字符串,返回布尔值 (看符不符合正则要求)

字符串:

字符串调用match方法,传入正则规则,返回符合要求的字符串组成的数组

调用replace方法,替换关键字

正则表达式方法

字符串方法

数组方法

对象方法

ajax、promise、react、缓存笔记记录相关推荐

  1. React学习笔记(上)

    一.React文件的创建(不使用脚手架进行文件的创建过程) 1.1.这里的React文件是一个html文件,或者是一个js文件,需要安转相关依赖 React:是React的核心包 React-dom: ...

  2. react组件卸载调用的方法_好程序员web前端培训分享React学习笔记(三)

    好程序员web前端培训分享React学习笔记(三),组件的生命周期 React中组件也有生命周期,也就是说也有很多钩子函数供我们使用, 组件的生命周期,我们会分为四个阶段,初始化.运行中.销毁.错误处 ...

  3. React 打怪笔记

    介绍 本文为学习react中的记录. Tips: 当组件的props或state有变化,执行render函数. 无论是使用函数或是类来声明一个组件,它决不能修改它自己的props React 可以将多 ...

  4. ES6基础5(Promise)-学习笔记

    文章目录 ES6基础5(Promise)-学习笔记 Promise 三个状态 状态转换 手写Promise源码 同步异步概念 jquery中 串行并行 async-await 微任务 宏任务 ES6基 ...

  5. React入门笔记(一)

    React入门笔记 1.引言 2.什么是React? 3.React基本的使用 4.JSX--react的语法糖 5.理解虚拟Dom 6.手配React项目 7.参考文章 8.视频教程地址 1.引言 ...

  6. React入门笔记(一) — 深入理解JSX

    <一>.理解JSX   JSX即Javascrpt XML- - 一种在React组件内部构建标签的类XML语法.   JSX并不是新语言,也没有改变JavaScript的语法,只是一种基 ...

  7. Examination:《计算机系统与网络技术》计算机三级考试重点笔记记录

    Examination:<计算机系统与网络技术>计算机三级考试重点笔记记录 目录 1.十进制转2进制 一.选择题 2.网友总结 二.综合题 三.应用题 1.根据IP地址.子网掩码地址,求出 ...

  8. 【方向盘】达到Linux第三阶段的常用命令笔记记录---Part Ⅱ

    实现自己既定的目标,必须能耐得住寂寞单干 本文已被https://yourbatman.cn收录:女娲Knife-Initializr工程可公开访问啦:程序员专用网盘https://wangpan.y ...

  9. react学习笔记1--基础知识

    什么是react A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES[React是一个用于构建用户界面的JavaScript库.] React之所以快, ...

最新文章

  1. 智能家居正是扎根好时节 蓄积且待春雨
  2. 同步等待异步操作,为什么Wait()在这里冻结程序
  3. linux 打开端口1935,CentOS服务器开放端口
  4. 经典C语言程序100例之六七
  5. Linux系统网络基础知识及配置
  6. 如何识别交换机的性能优劣?
  7. 《C++程序设计原理与实践》读书笔记(二)
  8. 【PAT - 甲级1005】Spell It Right (20分) (递归输出,水题)
  9. 解决PhoneGap在Android手机上的全屏问题
  10. HTTP权威指南-学习笔记(三)HTTP方法,状态码
  11. php更新用户数据为空,php - 使用PHP更新数据库,而没有来自HTML表单的空值 - SO中文参考 - www.soinside.com...
  12. Eclipse创建web项目
  13. 巧用QQ文件中转站在办公室与住所间作大文件传递
  14. 工业镜头视场、倍率、焦距之间的关系
  15. 国内打开Cousera方法
  16. ERD Commander 2005 使用教程
  17. 创业者2012必看十大文章(10)
  18. CDS ORF 启动子 终止子 转录因子 基因结构 UTR
  19. iOS开发之录屏时如何使系统录不到敏感信息
  20. 一个计算机爱好者的不完整回忆(十)插播游戏

热门文章

  1. 如何测试微信的点赞功能
  2. uni 登录token方法_uni-app 中保持用户登录状态
  3. 想做吃鸡游戏么兄弟?98K轻量物理了解一下
  4. VirtualBox 调整屏幕分辨率
  5. 30000台苹果电脑遭恶意软件入侵,包括最新的M1系列
  6. js find(),findIndex()方法的使用
  7. javaSwing ATM
  8. WINDOWS下多个桌面切换
  9. jquery日历插件daterangepicker全面详解汇总
  10. 【云原生】docker+k8微服务容器化实战