基于模块联邦的微前端实现方案

news2024/9/23 17:23:38

一、 微前端应用案例概述

当前案例中包含三个微应用,分别为 Marketing、Authentication 和 Dashboard

  1. Marketing:营销微应用,包含首页组件和价格组件

  2. Authentication:身份验证微应用,包含登录组件

  3. Dashboard:仪表盘微应用,包含仪表盘组件
    在这里插入图片描述

容器应用、营销应用、身份验证应用使用 React 框架,仪表盘应用使用 Vue 框架。

在这里插入图片描述

二、 Marketing - 应用初始化

  1. 创建应用结构
├── public
│ └── index.html
├── src
│ ├── bootstrap.js
│ └── index.js
├── package-lock.json
├── package.json
└── webpack.config.js
<!-- index.html -->
<title>Marketing</title>
<div id="dev-marketing"></div>
// index.js
import("./bootstrap")
// bootstrap.js
console.log('Hello')
  1. 配置 webpack
const HtmlWebpackPlugin = require("html-webpack-plugin")

module.exports = {
mode: "development",
devServer: {
port: 8081 ,
// 当使用 HTML5 History API 时, 所有的 404 请求都会响应 index.html 文件
historyApiFallback: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-react", "@babel/preset-env"],
// 1. 避免 babel 转义语法后 helper 函数重复
// 2. 避免 babel polyfill 将 API 添加到全局
plugins: ["@babel/plugin-transform-runtime"]
}
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html"
})
]
}
  1. 添加启动命令
"scripts": {
"start": "webpack serve"
}

三、Marketing - 创建挂载方法

// bootstrap.js
import React from "react"
import ReactDOM from "react-dom"

function mount(el) {
ReactDOM.render(<div>Marketing works</div>, el)
}

if (process.env.NODE_ENV === "development") {
const el = document.querySelector("#dev-marketing")
if (el) mount(el)
}

export { mount }

四、Marketing - 创建路由

  1. 在 src 文件夹中创建 components 文件夹用于放置页面组件

  2. 在 src 文件夹中创建 App 组件,用于编写路由

// App.js
import React from "react"
import { BrowserRouter, Route, Switch } from "react-router-dom"
import Landing from "./components/Landing"
import Pricing from "./components/Pricing"

export default function App() {
return (
<BrowserRouter>
<Switch>
<Route path="/pricing" component={Pricing} />
<Route path="/" component={Landing} />
</Switch>
</BrowserRouter>
)
}
// bootstrap.js
import App from "./App"
function mount(el) {
ReactDOM.render(<App />, el)
}

五、Container - 应用初始化

  1. 创建应用结构 (基于 Marketing 应用进行拷贝修改)
├── public
│ └── index.html
├── src
│ ├── bootstrap.js
│ └── index.js
├── package-lock.json
├── package.json
└── webpack.config.js
  1. 修改 index.html
<title>Container</title>
<div id="root"></div>
  1. 修改 App.js
import React from "react"

export default function App() {
return <div>Container works</div>
}
  1. 修改 bootstrap.js
if (process.env.NODE_ENV === "development") {
const el = document.querySelector("#root")
if (el) mount(el)
}
  1. 修改 webpack.config.js
module.exports = {
devServer: {
port: 8080
}
}

六、Container 应用加载 Marketing

  1. Marketing 应用配置 ModuleFederation
const ModuleFederationPlugin =
require("webpack/lib/container/ModuleFederationPlugin")

new ModuleFederationPlugin({
name: "marketing",
filename: "remoteEntry.js",
exposes: {
"./MarketingApp": "./src/bootstrap"
}
})
  1. Container 应用配置 ModuleFederation
const ModuleFederationPlugin =
require("webpack/lib/container/ModuleFederationPlugin")
new ModuleFederationPlugin({
name: "container",
remotes: {
marketing: "marketing@http://localhost:8081/remoteEntry.js"
}
})
  1. 在 Container 应用中新建 MarketingApp 组件,用于挂载 Marketing 应用
// Container/components/MarketingApp.js
import React, { useRef, useEffect } from "react"
import { mount } from "marketing/MarketingApp"

export default function MarketingApp() {
const ref = useRef()
useEffect(() => {
mount(ref.current)
}, [])
return <div ref={ref}></div>
}
  1. 在 Container 应用中的 App 组件中渲染 Marketing 应用的
// Container/App.js
import React from "react"
import MarketingApp from "./components/MarketingApp"

export default function App() {
return <MarketingApp />
}

七、共享库设置

在 Container 应用和 Marketing 应用中都使用了大量的相同的代码库,如果不做共享处理,则应用中相 同的共享库会被加载两次。

"dependencies": {
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router-dom": "^5.2.0"
}

在这里插入图片描述

在 Container 应用和 Marketing 应用的 webpack 配置文件中加入以下代码

const packageJson = require("./package.json")

new ModuleFederationPlugin({
shared: packageJson.dependencies
})

在这里插入图片描述

八、路由配置

容器应用路由用于匹配微应用,微应用路由用于匹配组件。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
容器应用使用 BrowserHistory 路由,微应用使用 MemoryHistory 路由。

  1. 为防止容器应用和微应用同时操作 url 而产生冲突,在微前端架构中,只允许容器应用更新 url,应用不允许更新 url,MemoryHistory 是基于内存的路由,不会改变浏览器地址栏中的 url。

  2. 如果不同的应用程序需要传达有关路由的相关信息,应该尽可能的使用通过的方式,memoryHistory 在 React 和 Vue 中都有提供。
    在这里插入图片描述

更新现有路由配置

  1. 容器应用的路由配置
// Container/App.js
import { Router, Route, Switch } from "react-router-dom"
import { createBrowserHistory } from "history"

const history = createBrowserHistory()

export default function App() {
return (
<Router history={history}>
<Switch>
<Route path="/">
<MarketingApp />
</Route>
</Switch>
</Router>
)
}
  1. Marketing 应用的路由配置
// Marketing/bootstrap.js
import { createMemoryHistory } from "history"

function mount(el) {
const history = createMemoryHistory()
ReactDOM.render(<App history={history} />, el)
}
// Marketing/app.js
import { Router, Route, Switch } from "react-router-dom"

export default function App({ history }) {
return (
<Router history={history}>
<Switch>
<Route path="/pricing" component={Pricing} />
<Route path="/" component={Landing} />
</Switch>
</Router>
)
}
  1. 添加头部组件
import Header from "./components/Header"

export default function App() {
return <Header />
}

九、微应用和容器应用路由沟通

  1. 微应用路由变化时 url 地址没有被同步到浏览器的地址栏中,路由变化也没有被同步到浏览器的历史记录中。当微应用路由发生变化时通知容器应用更新路由信息 (容器应用向微应用传递方法)。
// Container/components/MarketingApp.js
import { useHistory } from "react-router-dom"

const history = useHistory()

mount(ref.current, {
onNavigate({ pathname: nextPathname }) {
const { pathname } = history.location
if (pathname !== nextPathname) {
history.push(nextPathname)
}
}
})
// Marketing/bootstrap.js
function mount(el, { onNavigate }) {
if (onNavigate) history.listen(onNavigate)
}
  1. 容器应用路由发生变化时只能匹配到微应用,微应用路由并不会响应容器应用路由的变化。当容器应用路由发生变化时需要通知微应用路由进行响应 (微应用向容器应用传递方法)
// Marketing/bootstrap.js
function mount(el, { onNavigate }) {
return {
onParentNavigate({ pathname: nextPathname }) {
const { pathname } = history.location
if (pathname !== nextPathname) {
history.push(nextPathname)
}
}
}
}
// Container/components/MarketingApp.js
const { onParentNavigate } = mount()
if (onParentNavigate) {
history.listen(onParentNavigate)
}

十、Marketing 应用本地路由设置

目前 Marketing 应用本地开发环境是报错的,原因是本地开发环境在调用 mount 方法时没有传递第二个参数,默认值就是 undefined, mount 方法内部试图从 undefined 中解构 onNavigate,所以就报错了。

解决办法是在本地开发环境调用mount 方法时传递一个空对象。

if (process.env.NODE_ENV === "development") {
if (el) mount(el, {})
}

如果当前为本地开发环境,路由依然使用 BrowserHistory,所以在调用 mount 方法时传递defaultHistory 以做区分。

// Marketing/bootstrap.js
if (process.env.NODE_ENV === "development") {
if (el) mount(el, { defaultHistory: createBrowserHistory() })
}

在 mount 方法内部判断 defaultHistory 是否存在,如果存在就用 defaultHistory,否则就用MemoryHistory。

// Marketing/bootstrap.js
function mount(el, { onNavigate, defaultHistory }) {
const history = defaultHistory || createMemoryHistory()
}

十一、Authentication 应用初始化

  1. 下载应用依赖 cd auth && npm install

  2. 拷贝 src 文件夹并做如下修改

// bootstrap.js
if (process.env.NODE_ENV === "development") {
const el = document.querySelector("#dev-auth")
}
// App.js
import React from "react"
import { Router, Route, Switch } from "react-router-dom"

export default function App({ history }) {
return (
<Router history={history}>
<Switch>
<Route path="/auth/signin" component={Signin}></Route>
</Switch>
</Router>
)
}
  1. 拷贝 public 文件夹,并修改 index.html
<div id="dev-auth"></div>
  1. 拷贝 webpack.config.js 文件并进行修改
module.exports = {
devServer: {
port: 8082
},

plugins: [
new ModuleFederationPlugin({
name: "auth",
exposes: {
"./AuthApp": "./src/bootstrap"
}
})
]
}
  1. 添加应用启动命令
// package.json
"scripts": {
"start": "webpack serve"
}
  1. 修改 publicPath 更正文件的访问路径
// webpack.config.js
module.exports = {
output: {
publicPath: "http://localhost:8082/"
}
}
  1. 更正其他微应用的 publicPath
// Container/webpack.config.js
output: {
publicPath: "http://localhost:8080/"
}
// Marketing/webpack.config.js
output: {
publicPath: "http://localhost:8081/"
}

十二、Container 应用加载 AuthApp

  1. 在 Container 应用的 webpack 中配置添加 AuthApp 的远端地址
// Container/webpack.config.js
remotes: {
auth: "auth@http://localhost:8082/remoteEntry.js"
}
  1. 在 Container 应用的 components 文件夹中新建 AuthApp.js,并拷贝 MarketingApp.js 中的内容进行修改
import { mount } from "auth/AuthApp"
export default function AuthApp() {}
  1. 在 Container 应用的 App.js 文件中配置路由
<BrowserRouter>
<Switch>
<Route path="/auth/signin">
<AuthApp />
</Route>
<Route path="/">
<MarketingApp />
</Route>
</Switch>
</BrowserRouter>
  1. 解决登录页面点击两次才显示的 Bug当点击登录按钮时,容器应用的路由地址是 /auth/signin,加载 AuthApp,但是 AuthApp 在首次加载时默认访问的是 /,因为在使用 createMemoryHistory 创建路由时没有传递初始参数,当再次点击登录按钮时,容器应用通知微应用路由发生了变化,微应用同步路由变化,所以最终看到了登录页面。

解决问题的核心点在于微应用在初始创建路由对象时应该接收一个默认参数,默认参数就来自于容器应用。
在这里插入图片描述

// auth/bootstrap.js
function mount(el, { onNavigate, defaultHistory, initialPath }) {
createMemoryHistory({
initialEntries: [initialPath]
})
}
// container/src/components/AuthApp.js
mount(ref.current, {
initialPath: history.location.pathname
})

十三、按照上述方法修正 MarketingApp

懒加载微应用

目前所有的微应用都会在用户初始访问时被加载,这样会导致加载时间过长,解决办法就是懒加载微应用。

// Container/app.js
import React, { lazy, Suspense } from "react"
import Progress from "./components/Progress"
const MarketingApp = lazy(() => import("./components/MarketingApp"))
const AuthApp = lazy(() => import("./components/AuthApp"))

function App () {
return (
<Suspense fallback={<Progress />}>
<Switch>
<Route path="/auth/signin">
<AuthApp />
</Route>
<Route path="/">
<MarketingApp />
</Route>
</Switch>
</Suspense>
)
}
import React from "react"
import { makeStyles } from "@material-ui/core/styles"
import LinearProgress from "@material-ui/core/LinearProgress"
const useStyles = makeStyles(theme => ({
root: {
width: "100%",
"& > * + *": {
marginTop: theme.spacing( 2 )
}
}
}))

export default function Progress() {
const classes = useStyles()
return (
<div className={classes.root}>
<LinearProgress />
</div>
)
}

设置登录状态

由于每个微应用都有可能用到登录状态以及设置登录状态的方法,所以登录状态和设置登录状态的方法需要放置在容器应用中。

// Container/App.js
export default function App() {
// 存储登录状态
const [status, setStatus] = useState(false)
return <AuthApp setStatus={setStatus} />
}
// Container/AuthApp.js
export default function AuthApp({ setStatus }) {
useEffect(() => {
mount(ref.current, { setStatus })
}, [])
}
// Auth/bootstrap.js
function mount(el, { setStatus }) {
ReactDOM.render(<App setStatus={setStatus} />, el)
}
// Auth/App.js
export default function App({ setStatus }) {
return <Signin setStatus={setStatus} />
}
// Auth/Signin.js
export default function SignIn({ setStatus }) {
return <Button onClick={() => setStatus(true)}>登录</Button>
}

十四、登录状态应用

根据登录状态更改头部组件右侧的按钮文字,如果是 未登录状态 , 显示登录 ,如果是 登录状态 , 显示退出 。

点击退出按钮取消登录状态。

如果登录状态为真,跳转到 Dashboard 应用。

// Container/App.js
export default function App() {
const [status, setStatus] = useState(false)
// 如果登录状态为真,跳转到 Dashboard 应用
useEffect(() => {
if (status) history.push("/dashboard")
}, [status])
return (
<Router history={history}>
{/* 将登录状态和设置登录状态的方法传递到头部组件 */}
<Header status={status} setStatus={setStatus} />
</Router>
)
}
// Container/Header.js
export default function Header({ status, setStatus }) {
// 当点击按钮时取消登录状态
const onClick = () => {
if (status && setStatus) setStatus(false)
}
return <Button to={status? "/" : "/auth/signin"} onClick={onClick}> {status?
"退出" : "登录"}</Button>
}

十五、Dashboard 初始化

  1. 下载依赖 npm install

  2. 新建 public 文件夹并拷贝 index.html 文件

<div id="dev-dashboard"></div>
  1. 新建 src 文件夹并拷贝 index.js 和 bootstrap.js
// bootstrap.js
import { createApp } from "vue"
import Dashboard from "./components/Dashboard.vue"

function mount(el) {
const app = createApp(Dashboard)
app.mount(el)
}

if (process.env.NODE_ENV === "development") {
const el = document.querySelector("#dev-dashboard")
if (el) mount(el)
}

export { mount }
  1. 拷贝 webpack.config.js 文件并做如下修改
const HtmlWebpackPlugin = require("html-webpack-plugin")
const { VueLoaderPlugin } = require("vue-loader")
const ModuleFederationPlugin =
require("webpack/lib/container/ModuleFederationPlugin")
const packageJson = require("./package.json")

module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
publicPath: "http://localhost:8083/",
filename: "[name].[contentHash].js"
},
resolve: {
extensions: [".js", ".vue"]
},
devServer: {
port: 8083 ,
historyApiFallback: true,
headers: {
"Access-Control-Allow-Origin": "*"
}
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif|woff|svg|eot|ttf)$/i,
use: [
{
loader: "file-loader"
}
]
},
{
test: /\.vue$/,
use: "vue-loader"
},
{
test: /\.scss|\.css$/,
use: ["vue-style-loader", "style-loader", "css-loader", "sass-
loader"]
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
plugins: ["@babel/plugin-transform-runtime"]
}
}
}
]
},
plugins: [
new ModuleFederationPlugin({
name: "dashboard",
filename: "remoteEntry.js",
exposes: {
"./DashboardApp": "./src/bootstrap"
},
shared: packageJson.dependencies
}),
new HtmlWebpackPlugin({
template: "./public/index.html"
}),
new VueLoaderPlugin()
]
}
  1. 修改启动命令
"scripts": {
"start": "webpack serve"
}

十六、Container 应用加载 Dashboard

  1. Container 配置 ModuleFedaration
// container/webpack.config.js
remotes: {
dashboard: "dashboard@http://localhost:8083/remoteEntry.js"
}
  1. 新建 DashboardApp 组件
import React, { useRef, useEffect } from "react"
import { mount } from "dashboard/DashboardApp"

export default function DashboardApp() {
const ref = useRef()
useEffect(() => {
mount(ref.current)
}, [])
return <div ref={ref}></div>
}
  1. Container 应用添加路由
const DashboardApp = lazy(() => import("./components/DashboardApp"))

function App () {
return (
<Route path="/dashboard">
<DashboardApp />
</Route>
)
}
  1. 重启 Container 应用查看效果

十七、Dashboard 路由保护

function App () {
const [status, setStatus] = useState(false)
useEffect(() => {
if (status) history.push("/dashboard")
}, [status])
return (
<Router history={history}>
<Route path="/dashboard">
{!status && <Redirect to="/" />}
<DashboardApp />
</Route>
</Router>
)
}


// Marketing/Landing.js
<Link to="/dashboard">
<Button variant="contained" color="primary">
Dashboard
</Button>
</Link>

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

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

相关文章

B站发帖软件哪个好用?好用的哔哩哔哩发帖工具

B站发帖软件哪个好用?好用的哔哩哔哩发帖工具#发帖软件#哔哩哔哩发帖#视频发布软件 登录成功之后&#xff0c;进入到这样一个界面&#xff0c;默认情况下是这个样子的&#xff0c;我们在这里输入一下我们的一个文件夹的路径&#xff0c;输入到这里&#xff0c;点击添加账号&a…

kettle开发-Day36-循环驱动作业

前言&#xff1a;在日常数据处理时&#xff0c;我们通过变量传参来完成某个日期的数据转换。但可能因程序或者网络原因导致某个时间段的数据抽取失败。常见导致kettle作业失败的原因大概分为三大类&#xff0c;数据源异常、数据库异常、程序异常。因此面对这些异常时&#xff0…

Not available OpenAI s services are not available in your country.

一、准备阶段 1、邮箱账号(qq、网易、谷歌等等) 2、你能够科学上网(下边详细介绍) 3、拥有一个GW手机号&#xff0c;用于接收注册验证码。&#xff08;下边详细介绍&#xff09; 二、开始注册 1、官方注册网址https://beta.openai.com/signup&#xff08;按照步骤注册&am…

RDSDRDSPolarDBPolarDB-X的区别

RDS 阿里云关系型数据库&#xff08;Relational Database Service&#xff0c;简称RDS&#xff09;&#xff0c;是一种稳定可靠、可弹性伸缩的在线数据库服务。 基于阿里云分布式文件系统和高性能存储&#xff0c;RDS支持MySQL、SQL Server、PostgreSQL和PPAS&#xff08;Post…

在VScode终端上创建nuxtjs项目遇到的问题以及使用GitHub遇到的问题和一些个人笔记

文章目录&#x1f4cb;前言&#x1f4bb;关于GitHub打开慢或无法打开的问题&#x1f4bb;克隆GitHub的项目到本地&#x1f4bb;创建nuxtjs项目&#x1f9e9;无法加载文件的报错问题&#x1f9e9;使用vue init nuxt/starter demo出现的问题&#x1f9e9;另一种命令创建nuxtjs项目…

字符编码及转换

什么是字符编码字符编码&#xff08;Character encoding&#xff09;也称字集码&#xff0c;是把字符集中的字符&#xff0c;编码为指定集合中的某一对象&#xff08;例如&#xff1a;比特模式、自然数序列、8位组或者电脉冲&#xff09;&#xff0c;以便文本在计算机中存储或者…

前端控制台出现 红色 的报错信息TypeError: fn.apply is not a function

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 使用vue在原来的系统中 开发新的模块&#xff0c;开发时无意间发现 前端控制台出现 红色 的报错信息&#xff0c;但是页面依然还是正常 加载出来了 控制台一直报错 TypeError: fn.apply is not a fun…

09- 逻辑回归算法 (LogisticRegression) (机器学习)

基本概念: 逻辑回归主要逻辑是通过sigmoid函数进行分类, 当函数结果大于0时赋值1, 小于0时赋值0, 然后根据结果进行分类, 化简后求最小值的过程和线性方程类似, 该函数的特点是:分类算法 模型训练 : lr LogisticRegression() from sklearn.linear_model import LogisticRegr…

力扣39.组合总数

文章目录力扣39.组合总数题目描述方法1&#xff1a;深搜回溯力扣39.组合总数 题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可…

【数据库】MySQL 单表查询,多表查询

目录 单表查询 一&#xff0c;创建表worker 1&#xff0c;创建表worker的sql代码如下&#xff1a; 2&#xff0c;向worker表中插入信息 二&#xff0c; 按要求进行单表查询 1、显示所有职工的基本信息。 2、查询所有职工所属部门的部门号&#xff0c;不显示重复的部门号。 …

【自学Python】Python字符大小写判断

大纲 Python字符串是否是小写 Python字符串是否是小写教程 在开发过程中&#xff0c;有时候我们需要判断一个 字符串 是否是小写形式&#xff08;即&#xff0c;所有的字符都是小写字母&#xff0c;不是英文字符的忽略不做判断&#xff09;&#xff0c;在 Python 中&#xff…

SAP ERP系统SD模块常用增强之一:VA01/VA02创建或修改SO的输入检查

在SAP/ERP项目的实施中销售管理模块&#xff08;SD&#xff09;的创建和修改销售订单必定会有输入字段校验检查的需求&#xff0c;来防止业务人员录入错误或少录入数据&#xff0c;SAP公司也考虑到这一点&#xff0c;所以这方面的配置功能也非常强大&#xff0c;通常情况下不需…

GraphQL vs REST API 架构,谁更胜一筹?

GraphQL vs REST API 架构&#xff0c;谁更胜一筹&#xff1f; Michele Moody 平川 万佳 2020 年 1 月 18 日 本文字数&#xff1a;2890 字 阅读完需&#xff1a;约 9 分钟 2015 年&#xff0c;Facebook 开源 GraphQL。此后&#xff0c;它在前端 Web 中大受欢迎。传统的 R…

一篇文章带你学会Ansible的安装及部署

目录 前言 一、什么是Ansible 二、Ansible的工作方式 三、Ansible的安装 四、构建Anisble清单 1、清单书写方式 2、清单查看 3、清单书写规则 4、主机规格的范围化操作 五、ansible命令指定清单的正则表达式 六、 Ansible配置文件参数详解 1、配置文件的分类与优先…

【C++】C/C++内存管理模板初阶

文章目录一、 C/C内存管理1. C/C内存分布2. C内存管理方式3. operator new与operator delete函数4. new和delete的实现原理5. 定位new表达式6. 常见面试题malloc/free和new/delete的区别内存泄漏二、模板初阶1. 泛型编程2. 函数模板3. 类模板一、 C/C内存管理 1. C/C内存分布 …

每天10个前端小知识 【Day 13】

前端面试基础知识题 1. Position&#xff1a;absolute绝对定位&#xff0c;是相对于谁的定位&#xff1f; CSS position属性用于指定一个元素在文档中的定位方式。top&#xff0c;right&#xff0c;bottom 和 left 属性则决定了该元素的最终位置。 absolute的元素会被移出正…

牛客网 NC107 寻找峰值

前言&#xff1a;内容包括四大模块&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路&#xff0c;代码解读 题目&#xff1a; 描述 给定一个长度为n的数组nums&#xff0c;请你找到峰值并返回其索引。数组可能包含多个峰值&#xff0c;在这种情况下&#xff0c;返回…

vue-router路由配置

介绍&#xff1a;路由配置主要是用来确定网站访问路径对应哪个文件代码显示的&#xff0c;这里主要描述路由的配置、子路由、动态路由&#xff08;运行中添加删除路由&#xff09; 1、npm添加 npm install vue-router // 执行完后会自动在package.json中添加 "vue-router…

某游戏辅助功能分析

FPS游戏发展至今&#xff0c;阻挡外挂开发者脚步的往往不是数据和功能开发&#xff0c;而是高难度的检测。 现如今&#xff0c;检测的手段越来越多&#xff0c;也越来越五花八门。列如&#xff1a; 检测参数&#xff0c;检测堆栈&#xff0c;检测注入等等。 CRC是众多检测手段中…

Qt OpenGL(三十九)——Qt OpenGL 核心模式-在雷达坐标系中绘制飞行的飞机

提示:本系列文章的索引目录在下面文章的链接里(点击下面可以跳转查看): Qt OpenGL 核心模式版本文章目录 Qt OpenGL(三十九)——Qt OpenGL 核心模式-在雷达坐标系中绘制飞行的飞机 一、场景 在之前绘制完毕雷达显示图之后,这时候,我们能匹配的场景就更广泛了,比如说…