React04 - react ajax、axios、路由和antd UI

news2024/10/21 4:47:24

文章目录

  • react与ajax
  • react与axios
      • react中跨域解决方法
      • 使用axios携带参数发送请求
      • 父子组件间的传值(props)
      • 根据请求结果展示不同的页面效果
      • 兄弟组件间通信(消息订阅与发布)
      • fetch发送请求
  • 路由
      • 前端路由和后端路由
      • 路由原理与基本使用
      • react-router-dom@5
        • BrowserRouter和HashRouter
        • NavLink
        • Switch
        • 路由的模糊匹配和严格匹配
        • Redirect重定向
        • 嵌套路由(二级路由)
        • 向路由组件传递参数
        • 编程式路由导航
        • withRouter
  • antd UI

react与ajax

react不包含发送ajax请求的代码,需要集成第三方ajax库或者自己封装。
常用的ajax请求库:jQuery、axios。对比axios,jQuery会更多的直接操作DOM,不适用react框架,更倾向于选择axios作为react的ajax请求库。

react与axios

axios是一个轻量级的ajax请求库,有promise风格,可以用在浏览器和node服务器两个端口。

react中跨域解决方法

在项目目录终端中输入npm add axios,引入第三方库。

// App.js
import React,{Component} from 'react'
import axios from 'axios'

export default class App extends Component{
  getStudentData = ()=>{
    axios.get('http://localhost:5000/student').then(
        response => {console.log('success',response.data)},
        error =>{console.log('fail',error)}
        )
  }
  render(){
    return (
      <div>
        <button onClick={this.getStudentData}>点击获取数据</button>
      </div>
    )
  }
}

使用nodejs创建一个服务器,向端口5000发送请求。

const express = require('express')
const app = express()
app.use((req,res,next)=>{
  console.log('请求服务器了')
  next()
})
app.get('/student',(req,res)=>{
  const students = [
    {id:'001',name:'tom',age:18}.
    {id:'002',name:'jery',age:18}.
    {id:'003',name:'tony',age:18}.
  ]
  res.send(students)
})
app.listen(5000,err => {
  if(!err) console.log('服务器启动成功,请求地址http://localhost:5000/students')
})

前端项目运行在3000端口,点击按钮,会发生跨域问题,无法返回数据。
解决跨域配置代理方法一,在package.json文件内加入proxy属性,规定跨域地址。

"proxy":http://localhost:5000

在组件请求方法中修改请求路径:

getStudentData = ()=>{
    axios.get('http://localhost:3000/student').then(
        response => {console.log('success',response.data)},
        error =>{console.log('fail',error)}
        )
  }

这样当3000端口发送请求时,先查看当前端口是否有请求资源,若有,直接返回数据,若没有,会向5000端口请求资源,然后返回。

解决跨域配置代理方法二,在src文件夹下新建一个名为setupProxy.js的文件,react在编译的时候会把他交给webpack,然后转为node去做处理。在这个文件里,要使用commonjs编写,commonjs是前端模块化的一种规范,node使用的也是commonjs编写的。该方法适用于要从多个服务器获取资源。

// setupProxy.js
const proxy = require('http-proxy-middleware')
moudle.exports = function(app){
  app.use(
    proxy('/api1',{ // 请求前缀,请求路径中有该前缀则触发此代理配置
      target:'http://localhost:5000', // 请求转发路径
      changeOrigin:true, // 控制服务器收到的请求头中Host值,相当于欺骗服务器,该请求来自本服务器。前端请求为localhost:3000,但当前端发起请求时,5000端口的服务器会知道是请求头localhost:5000的请求。不写这一行代码时,5000端口的服务器会知道是请求头localhost:3000的请求。
      pathRewrite:{'^/api1':''} // 重写请求路径,将请求路径中的/api1字段替换为空字符串,避免服务器收到/api1开头的路径,导致请求返回404
    }),
    proxy('/api2',{
      target:'http://localhost:5001',
      changeOrigin:true,
      pathRewrite:{'^/api2':''}
    }),
  )
}

以上代码中规定了不同服务器请求路径的前缀(api1/api2),组件中的请求方法也要对应修改。

getStudentData = ()=>{
    axios.get('http://localhost:3000/api1/student').then(
        response => {console.log('success',response.data)},
        error =>{console.log('fail',error)}
        )
  }

使用axios携带参数发送请求

const value = 10;
axios.get('http://localhost:3000/search/users?q=${value}')
     .then(
         res => {console.log('success',res.data);},
         err => {console.log('fail',err)}
     )

父子组件间的传值(props)

案例:App包含Search和List两个子组件,在Search组件中输入数据,点击按钮请求数据,将请求返回的数据给List组件,用于展示列表。

// 父组件中
export default class App extends Component{
  state = {users:[]}
  // 获取子组件Search返回的数据,保存在state中
  saveUsers = users => {
    this.setState({users})
  }
  render(){
    return (
      <div>
        <Searrch saveUsers={this.saveUsers}/>
        <List list={this.state.users}/>
      </div>
    ) // 使用props传递函数和值给子组件
  }
}
// 子组件Search中
export default class Search extends Component{
  search = () => {
    // 使用ref获取输入框的值
    const value = this.inputValue.value;
    axios.get('http://localhost:3000/search/users?q=${value}').then(
      res => {
        // 将接口返回的数据通过props传递给父组件
        this.props.saveUsers(res.data)
      },
      err => {console.log('ereoe',err)}
    )
  }
  render(){
    return (
      <div>
        <input ref={c => this.inputValue = c} type="text" />
        <button onClick={this.search}>点击获取数据</button>
      </div>
    )
  }
}
// 子组件Lisr中
export default class List ectends Component{
  render(){
    return (
      <div>
        this.props.list.map(item => {
          return (
            <li key={item.id}>{item.name}</li>
          )
        })
      </div>
    ) // 使用props获取父组件传递的数据
  }
}

根据请求结果展示不同的页面效果

发送请求前的页面,发送请求时的页面,请求成功的页面,请求失败的页面。

// jsx使用嵌套三元表达式
beforeSend ? <h1>Welcome</h1> :
  loading ? <h1>wait loading</h1> :
     err ? <h1>something wrong</h1> :
         <h1>response data</h1>

兄弟组件间通信(消息订阅与发布)

消息发布:把消息给别的兄弟组件,
消息订阅:可以接收别的兄弟组件发布的消息,
注意要在组件的componentWillUnmount中取消订阅

import PubSub from 'pubsub-js'
// 订阅,在接收消息的组件中使用
this.token = PubSub.subscribe('MY TOPIC',(msg,data)=>{
  console.log(msg,data); // msg是消息订阅名称,data是发布传送的数据
});
// 取消订阅,在接收消息的组件中适用
componentWillUnmount(){
  PubSub.unsubscribe(this.token)
}
// 发布,在发送消息的组件中使用
PubSub.publish('MY TOPIC','hello');

fetch发送请求

原生xhr对象=>jQuery(回调地狱)=>axios(promise)
fetch是windows对象自带的方法,也能发送请求,也有promise风格,适用IE8+浏览器。

// 原生xhr对象发送请求
var xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.resonseType = 'json';
xhr.onload = function(){
  console.log(xhr.response);
}
xhr.onerror = function(){
  console.log('error');
}
xhr.send();
// fetch发送请求
fetch(url).then( //进行服务器连接
  response => {
    console.log('联系服务器成功');
    return response.json(); // 返回成功,值是一个promise对象
  },
  error => {
    console.log('联系服务器失败',error);
    return new Promise(()=>{}); // 设置空promise对象,以防联系服务器失败依旧走下面的then,显示获取数据成功,undefined
  }
).then( // 前一个then中成功的回调返回的promise对象,继续操作
  response => {console.log('获取数据成功',response)}, // response是数据
  error =>{console.log('获取数据失败',error)}
)

// 优化写法
fetch(url).then( 
  response => {
    console.log('联系服务器成功');
    return response.json(); 
  }
).then( 
  response => {console.log('获取数据成功',response)}
).catch(
  error => {console.log('请求出错',error)}
)

// 优化写法 await跟promise对象搭配使用,await适用外部要包含async函数
async()=>{
  try{
    const response = await fetch(url);
    const data = await response.json();
    console.log(data)
  }catch(error){
    console.log('请求出错',error)
  }
}

路由

单页面web应用(SPA)整个应用只有一个完整页面,点击页面中的链接不会刷新整个页面,只会局部更新页面,数据都需要通过ajax请求获取,并在前端异步展现。

前端路由和后端路由

一个路由是一个映射关系(key: value),key是路径,value可以是function(后端路由)或component(前端路由)。

// node后端路由,根据请求路径调用函数来请求处理返回数据
router.get("/search/users",function(req,res){ 
  axios.get(url).then(
    res => {},
    err=>{}
  )
}
// 前端路由,根据请求路径返回组件更新页面内容
<Route path="/users" component={User}>

路由原理与基本使用

前端路由是基于浏览器的History工作的,在BOM中的History对象管理页面的访问路径历史记录等。
history对象使用栈存储页面路径,更改路径但是不跳转页面。
锚点跳转也可以更新路径,但不跳转页面。

// 引用history.js
let history = History.createBrowserHistory() // 方法一,直接使用H5推出的history对象的API
// let history = History.createHashHistory() // 方法一,hash值(锚点)
history.push(path)
history.replace(path)
history.goBack()
history.goForward()

react-router包含三个方面的库:web、native(native)、anywhere(能适用任何地方)。
主要学习react-router-dom库(web应用)。

react-router-dom@5

npm add react-router-dom@5  // 下载版本5的路由库

分析页面,找页面的导航区,展示区,不同的展示区就是不同的页面。要实现点击导航区链接改变路径,被路由器检测到路径改变,进行匹配组件,进而更新页面。导航区写路由组件链接,展示区注册路由。

<body>
  <!--原生结构 -->
 <div>
    <span>
      <a href="./about.html">About</a>
    </span>
    <span>
      AboutPage
    </span>
 </div>
</body>

// App.jsx中,使用Link路由组件,类似原生中的a标签 <a href="./about.html">About</a>
import {Link,BrowserRouter} from 'react-router-dom'
import About from './pages/About'
<div>
 <BrowserRouter>
    <span>
      <Link to="/about">About</Link> // (导航区使用路由链接)在react中使用路由链接实现组件切换,Link组件外必须有Router标签
    </span>
    <span>
      <Route path="/about" component={About}> // (内容展示区注册路由)注册路由
    </span>
 </BrowserRouter>
</div>


// 路由组件About.jsx
export default class About extends Component{
  render(){
    return <span>AboutPage</span>
  }
}
BrowserRouter和HashRouter

Router包含BrowserRouter和HashRouter,BrowserRouter适用于请求服务器的情况,路径中没有#,HashRouter适用于不请求服务器的情况,路径中带有#。
BrowserRouter使用的是H5的history API,不兼容IE9以下版本,HashRouter使用的是URL的哈希值。
页面刷新后,BrowserRouter没有任何影响,因为state会保存在history对象中,HashRouter会发生路由state参数丢失。

在使用路由组件时,必须用Router将路由组件包含起来,注册路由也需要使用同一个Router,因此需要包含在同一个Router里。

// App组件的index.js里,将整个App组件使用同一个Router包裹,所有的组件都使用一个Router管理
ReactDom.render(
  <BrowserRouter>
     <App/>
  </BrowserRouter>
,document.getElementById('root'))

// 在App.jsx中只需要写Link和Route
  <Link to="/about">About</Link>
  <Route path="/about" component={About}> 

路由组件和一般组件的最大区别在于,自带了props属性:history, location, match。

NavLink

使用NavLink可以实现路由链接的高亮,通过activeClassName指定自定义样式名。

<NavLink activeClassName="active" to="/abput">About</NavLink>
Switch

Switch组件,避免多个相同路径的路由一直匹配渲染。

// 多个路径相同的组件会一直匹配,About和Detail两个组件的内容都会显示在页面上
<Route path="/about" component={About}> 
<Route path="/about" component={Detail}> 

// 引入Switch组件后匹配到第一个就停止匹配
import {Switch} from 'react-router-dom'
<Switch> // 使用<Switch>包裹所有路由
  <Route path="/about" component={About}>  // 匹配到About组件就停止,页面只显示About组件的内容
  <Route path="/about" component={Detail}> 
</Switch>
路由的模糊匹配和严格匹配
// 无法显示页面
<Link to="/home">Home</Link>
<Route path="/home/a/b" component={Home}/>

// 可以显示页面,模糊匹配,最左逐个匹配home a b,匹配到了home
<link to="/home/a/b">Home</Link>
<Route path="/home" component={Home}/>

// 无法显示页面,严格匹配
<Link to="/home/a/b">Home</Link>
<Route exact={true} path="/home" component={Home}/>
Redirect重定向

当Redirect前面的路由都匹配不上时,走Redirect指定的路径。

import {Redirect} from 'react-router-dom'
<Switch>
  <Route path="/home" component={Home}/>
  <Route path="/about" component={About}/>
  <Redirect to="/home"/> // 当没有点击导航,页面访问根路径“/”的时候,默认到home页面
</Switch>
嵌套路由(二级路由)

路由按照注册的顺序进行匹配,严格匹配会导致无法匹配二级路由

<div>
  <div>
  <Switch>
    <NavLink to="/home">Home</NavLink> // 当开启严格匹配,点击Home组件中的二级路由链接,会跳转到about组件,展示不了Home组件
    <NavLink to="/about">About</NavLink>
    <Redirect to="/about"/> 
  </Switch>
  </div>
  <div>
    <Route path="/home" component={Home}/>
    <Route path="/about" component={About}/>
  </div>
</div>
// Home组件
<div>
  <div>
    <NavLink to="/home/a">Home</NavLink>
    <NavLink to="/home/b">About</NavLink>
  </div>
  <div>
    <Route path="/home/a" component={HomeA}/> // 先匹配/home路由,来到Home页面,才继续匹配/a
    <Route path="/home/b" component={HomeB}/>
  </div>
</div>
向路由组件传递参数

params参数

<Link to="/details/apple/18">详情</Link>
<Route path="details/:name/:price" component={Details} />
this.props.match.params // 获取参数

search参数

import qs from 'querystring'
<Link to="/details?name=apple&price=18">详情</Link>
<Route path="/details" component={Details}/>
qs.parse(this.props.location.search) // 接收参数,获取到的是urlencode编码字符串,需要借助querystring解析

state参数

<Link to={{path:'/details',state:{name:'apple',price:18}}}>详情</Link>
<Route path="/details" component={Details}/>
this.props.location.state
编程式路由导航

路由跳转有replace和push两种方式,默认是push,push方式可以返回,在浏览器路径可以看到,replace是替换之前的路径,不可返回前面的路径。

// 编程式路由导航,使用方法改变路由
const {id,name} = this.props.match.params 
// const {search} = this.props.location
// const {id,name} = qs.parse(search.slice(1))
// const {is,name} = this.props.location.state || {}

replaceShow = (id,name)=>{
  this.props.history.replace('/users/${id}/${name}')
 // this.props.history.replace('/users?id=${id}&name=${name}')
 // this.props.history.replace('/users',{id:id,name:name})
}
pushShow = (id,name)=>{
  this.props.history.push('/users/${id}/${name}')
  // this.props.history.push('/users?id=${id}&name=${name}')
  // this.props.history.push('/users',{id:id,name:name})
}
back = ()=>{
  this.props.history.goBack()
}
forward = ()=>{
  this.props.history.goForward()
}
go = ()=>{
 this.props.history.go(2)  // 前进两页,正数为前进,负数为后退
}

<button onClick={()=>{this.replaceShow(obj.id,obj.name)}}>replace查看</button>
<button onClick={()=>{this.pushShow(obj.id,obj.name)}}>push查看</button>
<button onClick={this.back}>回退</button>
<button onClick={this.forward}>前进</button>
<button onClick={this.go}>跳转</button>
// params
<Link to={'/users/${obj.id}/${obj.name}'}>{obj.name}</Link>
<Route path="/users/:id/:name" component={User}/>

// search
<Link to={'/users/?id=${id}&name=${name}'}>{obj.name}</Link>
<Route path="/users" component={User}/>

// state
<Link to={{pathname:'/users',state:{id:obj.id,name:obj.name}}}>{obj.name}</Link>
<Route path="/users" component={User}/>
withRouter

withRouter可以加工一般组件,让一般组件带有路由组件有的API(history,location,match),返回值是一个新组件。

// Header组件
import {withRouter} from 'react-router-dom'
class Header extends Component{
  back = ()=>{
    this.props.history.goBack()
  }
  render(){
   return (
     <button onClick={this.back}>回退</button>
   )
  }
}
export default withRouter(Header)

//User组件
export default class User extends Component{
  render(){
    return (
     <div>
      <Link to={'/users/${obj.id}/${obj.name}'}>{obj.name}</Link>
      <Route path="/users/:id/:name" component={User}/>
     </div>
    )
  }
}

antd UI

参考ant-Design官网文档

add antd  // 终端引入库
import {Button} from 'antd'  // 引入Button组件样式
import 'antd/dist/antd.css' // 新版可不引入

export default class App extends Component{
  render(){
    return (
      <Button type="primary">按钮</Button>
    )
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2219793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot项目get请求遇到:Message Request header is too large报错。

一、错误日志 HTTP Status 400 – Bad Request Type Exception ReportMessage Request header is too largeDescription The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, inva…

Django学习-ORM_常用字段及字段选项

字段选项&#xff1a; 注意&#xff1a;当我们新增数据的时候&#xff0c;如果没有新增设置了default的字段&#xff0c;此时会使用default设置的默认值填充到新增的数据中

数字后端零基础入门系列 | Innovus零基础LAB学习Day2

今天开始更新数字IC后端设计实现中Innovus零基础Lab学习后续内容。 数字后端零基础入门系列 | Innovus零基础LAB学习Day1 ####LAB5-2 这个章节的目标也很明确——学习掌握工具的一些常用快捷键。 这里只需要掌握以下几个快捷键即可。其他小编我也不会&#xff0c;也用不着。…

【初识数据库】

目录 一、数据库简介 1.什么是数据库 2.数据库与数据结构有啥关系 3.为什么要使用数据库 二、数据库服务器、数据库和表的关系 三、客户端与服务器的通讯方式 1.C/S架构 2.B/S架构 3.命令提示符 4.MySQL架构 一、数据库简介 1.什么是数据库 组织和保存数据的应用程序…

【JavaEE初阶】深入理解网络编程—使用UDP协议API实现回显服务器

前言 &#x1f31f;&#x1f31f;本期讲解关于TCP/UDP协议的原理理解~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话不…

centors7安装docker启动ubuntu

在centors上升级glibc为2.18&#xff0c;使用yum最新源直接安装不行&#xff0c;使用源码安装还不行&#xff0c;因为必须使用2.18版本glic&#xff0c;采用先安装docker&#xff0c;在启动新的Ubuntu&#xff0c;在ubuntu里配置自己的环境 1. centors安装docker 查看docker的…

专业学习|马尔可夫链(概念、变体以及例题)

一、马尔可夫链的概念及组成 &#xff08;一&#xff09;学习资料分享 来源&#xff1a;024-一张图&#xff0c;但讲懂马尔可夫决策过程_哔哩哔哩_bilibili 马尔可夫链提供了一种建模随机过程的方法&#xff0c;具有广泛的应用。在实际问题中&#xff0c;通过转移概率矩阵及初…

NOIP2023题解

T1&#xff1a;词典 有一个好猜的结论&#xff1a;对于一个字符串&#xff0c;若它当中的最小字符大于等于某其他字符串中的最大字符&#xff0c;那么这个字符串一定不可行。 证明也很简单&#xff0c;若最小字符大于最大字符&#xff0c;显然一定不可行。若最小字符等于最大…

常用Python数据分析开源库:Numpy、Pandas、Matplotlib、Seaborn、Sklearn介绍

文章目录 1. 常用Python数据分析开源库介绍1.1 Numpy1.2 Pandas1.3 Matplotlib1.4 Seaborn1.5 Sklearn 1. 常用Python数据分析开源库介绍 1.1 Numpy Numpy(Numerical Python)是Python数据分析必不可少的第三方库&#xff0c;Numpy的出现一定程度上解决了Python运算性能不佳的…

解决VScode(Visual Studio Code)中的乱码问题. 2024-10-13

系统环境: win10 64bit , vscode 1.94.2 1.乱码原因 默认使用utf-8编码,导致非utf-8内容乱码 2.解决乱码问题 1)打开设置 点击左下角的齿轮,然后再点击设置 2)启用编码自动检测 在设置搜索框中输入 编码 ,然后启用 自动猜测编码选项 3.乱码问题解决 重新打开文件后,乱码问…

清空redo导致oracle故障恢复---惜分飞

客户由于空间不足,使用> redo命令清空了oracle的redo文件 数据库挂掉之后,启动报错 Fri Oct 04 10:32:57 2024 alter database open Beginning crash recovery of 1 threads parallel recovery started with 31 processes Started redo scan Errors in file /home/oracle…

各种查询sql介绍

1. 关联查询&#xff08;JOIN&#xff09; 关联查询用于从多个表中检索数据。它基于两个或多个表之间的共同字段&#xff08;通常是主键和外键&#xff09;来组合数据。 内连接&#xff08;INNER JOIN&#xff09;&#xff1a; sql SELECT a.name, b.order_date FROM custome…

IO进程---day5

1、使用有名管道实现两个进程之间的相互通信 //管道文件 #include<myhead.h> int main(int argc, const char *argv[]) {//创建有名管道文件1if(mkfifo("./pipe1",0664)-1){perror("创建管道文件失败");return 0;}if(mkfifo("./pipe2",066…

upload-labs靶场Pass-03

upload-labs靶场Pass-03 分析源码 $is_upload false; $msg null; if (isset($_POST[submit])) {if (file_exists(UPLOAD_PATH)) {$deny_ext array(.asp,.aspx,.php,.jsp);$file_name trim($_FILES[upload_file][name]);$file_name deldot($file_name);//删除文件名末尾的…

Damn-Vulnerable-Drone:一款针对无人机安全研究与分析的靶机工具

关于Damn-Vulnerable-Drone Damn-Vulnerable-Drone是一款针对无人机安全研究与分析的靶机工具&#xff0c;广大研究人员可以利用该环境工具轻松学习、研究和分析针对无人机安全态势。 Damn Vulnerable Drone 基于流行的 ArduPilot/MAVLink 架构&#xff0c;其中故意留下了各种…

PDF 软件如何帮助您编辑、转换和保护文件

如何找到最好的 PDF 编辑器。 无论您是在为您的企业寻找更高效的 PDF 解决方案&#xff0c;还是尝试组织和编辑主文档&#xff0c;PDF 编辑器都可以在一个地方提供您需要的所有工具。市面上有很多 PDF 编辑器 — 在决定哪个最适合您时&#xff0c;请考虑这些因素。 1. 确定您的…

基于 Konva 实现Web PPT 编辑器(三)

完善公式 上一节我们简单讲述了公式的使用&#xff0c;并没有给出完整的样例&#xff0c;下面还是完善下相关步骤&#xff0c;我们是默认支持公式的编辑功能的哈&#xff0c;因此&#xff0c;我们只需要提供必要的符号即可&#xff1a; 符号所表达的含义是 mathlive 的command命…

从0开始深度学习(12)——多层感知机的逐步实现

依然以Fashion-MNIST图像分类数据集为例&#xff0c;手动实现多层感知机和激活函数的编写&#xff0c;大部分代码均在从0开始深度学习&#xff08;9&#xff09;——softmax回归的逐步实现中实现过 1 读取数据 import torch from torchvision import transforms import torchv…

JavaCove部署文档

1. 基础配置 1.1服务器&#xff1a; 2 核 2G 1.2. 一个域名 1.3. 项目地址&#xff1a; gitee:https://gitee.com/guo-_jun/JavaCove github:https://github.com/nansheng1212/JavaCove 2. CentOS 安装 Docker 官方网站上有各种环境下的 安装指南&#xff0c;这里主要介绍…

webpack自定义插件 ChangeScriptSrcPlugin

插件文件 class ChangeScriptSrcPlugin {apply(compiler) {const pluginName "ChangeScriptSrcPlugin";compiler.hooks.compilation.tap(pluginName, (compilation, callback) > {compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync(pluginName,(html…