中臺項目-微前端qiankun-umimax

學習視頻🔊

基礎: 黑馬前端基于qiankun搭建微前端項目實戰教程_嗶哩嗶哩_bilibili

路由、部署配置注意:qiankun+vite微前端上線注意事項,base公共路徑設置_嗶哩嗶哩_bilibili

微前端

什么是微前端?

微前端是將前端應用分解成一系列更小、更易管理的獨立部分的架構方案。類似于微服務,每個微應用可以由不同團隊獨立開發、測試、部署。

微前端的好處?

  1. 技術棧無關

主應用和子應用可以使用不同的技術棧

允許漸進式技術棧升級

降低全局技術升級的風險

  1. 獨立開發部署

各團隊可以獨立開發、測試、部署

有哪些微前端方案?

下面介紹有哪些主流的微前端方案。

1、qiankun(螞蟻金服,后續也采用這種)

優點:

  • 基于 single-spa 封裝

  • 完善的沙箱機制

  • 開箱即用的 API

  • 中文社區活躍

適用場景:

  • 大型中臺系統

  • 需要多團隊協作的項目

2、single-spa

優點:

  • 最早的微前端框架

  • 靈活性高

  • 社區成熟

缺點:

  • 配置較復雜

  • 需要自己實現樣式隔離

3、Module Federation(來自Webpack 5,中文:模塊聯邦,可以用來做遠程組件)

優點:

  • Webpack 原生支持

  • 真正的運行時模塊共享

  • 構建時優化

適用場景:

  • 新項目

  • 需要精細化控制模塊共享

4、micro-app(京東)

優點:

  • 使用簡單

  • 基于 Web Components

  • 性能好

適用場景:

  • 對性能要求高的項目

  • 喜歡簡單配置的團隊

5、Iframe

優點:

  • 具有天然的隔離屬性,js沙箱、樣式隔離等都很好。

缺點:

  • UI不同步,比如在iframe中添加蒙層彈框,只會在iframe中顯示,不是全屏的。

  • 慢,每次進入會重新加載,多個iframe時瀏覽器容易卡死。

基礎改造

🌈🌈🌈

基座改造🚩

一、Antd Pro基座改造(umi系)🤠

接下來對中臺(antd Pro 創建的項目,作為基座)進行改造。

1、安裝@umijs/plugin-qiankun

【umimax已內置】

pnpm i @umijs/plugin-qiankun

2、注冊子應用

config/config.ts配置:

在config的qiankun.master.apps數組中 注冊子應用

export default defineConfig({qiankun: {master: {apps: [{name: 'sub-umi',//子應用的名稱entry: '//localhost:5175',//子應用的入口地址activeRule: '/qiankun/umi',//子應用的激活規則,指路由sandbox: {strictStyleIsolation: true,//嚴格樣式隔離},},],},},//其他配置
})

3、配置訪問子應用的路由

config/router.tsx :在基座的路由中添加能訪問子應用的路由。

方式1:用microApp屬性指定要渲染的子應用的name。(本次改造采用這種)

  {path: '/qiankun',name: 'qiankun',routes: [{path: '/qiankun/umi',name: 'sub-umi',microApp: 'sub-umi', //和注冊時的name一致microAppProps: {// 子應用自動設置loading// autoSetLoading: true, //可以用autoSetLoading,需要子應用引入antdloader: (loading: boolean) => <Spin spinning={loading} />,},},],},

方式2:使用component屬性指定組件,在組件中使用qiankun提供的 MicoApp組件

// 1、路由{path: '/app1',name: 'sub-app',element:<SubApp/>}// 2、 SubApp組件,用MicroApp組件占位,需要指定name(和注冊時同名)
import React from 'react';
import { MicroApp } from '@umijs/max';
type Props = {};const MicroApp1 = (props: Props) => {return <MicroApp name="sub-app" />;
};export default MicroApp1;

二、非umi系基座改造

1、安裝qiankun
pnpm i qiankun // 或者 yarn add qiankun
2、修改入口文件

在src/index.tsx中增加如下代碼:從qiankun中引入注冊和啟動的函數,注冊子應用并調用start啟動。


import { start, registerMicroApps } from 'qiankun';// 1. 要加載的子應用列表
const apps = [{name: "sub-react", // 子應用的名稱entry: '//localhost:8080', // 默認會加載這個路徑下的html,解析里面的jsactiveRule: "/sub-react", // 匹配的路由container: "#sub-app" // 加載的容器},
]// 2. 注冊子應用
registerMicroApps(apps, { //下面的配置對象可以不寫beforeLoad: [async app => console.log('before load', app.name)],beforeMount: [async app => console.log('before mount', app.name)],afterMount: [async app => console.log('after mount', app.name)],
})start() // 3. 啟動微服務
/*
// 配置qiankun啟動參數
start({// prefetch: true, // 預加載  默認是true,即在主應用加載的時候,加載子應用sandbox: {//沙箱// experimentalStyleIsolation: true, // 實驗性樣式隔離,好像沒用哦strictStyleIsolation: true, // 嚴格樣式隔離},
})
*/

3、注冊子應用路由,提供容器dom

router注冊一下子應用的路由,element設置為null,在跳轉到子應用的路由時,展示id為subApp的div。

//router/index.jsx
const router = [{path: "/",element: <Home />,},{path: "/app1/*",element: null,},
]//App.jsx
function App() {const element = useRoutes(router)return (<div className="main-layout"><nav><Link to="/">首頁</Link><Link to="/app1">子應用1</Link></nav><div className="test-aa">123</div><div className="main-content"><Suspense fallback={<div>Loading...</div>}>{element}{/* 需要子應用的容器 */}<div id="subApp"></div></Suspense></div></div>)
}

一旦瀏覽器的 url 發生變化,便會自動觸發 qiankun 的匹配邏輯。 所有 activeRule 規則匹配上的微應用就會被插入到指定的 container 中,同時依次調用微應用暴露出的生命周期鉤子。

  • registerMicroApps(apps, lifeCycles?)

注冊所有子應用,qiankun會根據activeRule去匹配對應的子應用并加載

  • start(options?)

啟動 qiankun,可以進行預加載和沙箱設置,更多options : API 說明 - qiankun


至此基座基本就改造完成,接下來改造子應用。

子應用改造🤩

子應用改造主要需要注意:

  1. 用umd格式打包,當然,像umi,vite這些插件已經設置了。

一、umi子應用改造

中臺項目的子應用是采用umi進行構建。

  1. 安裝插件

在子應用目錄安裝@umijs/plugins插件,才能在umirc中用qiankun字段。

pnpm i @umijs/plugins
  1. 使用插件

.umirc.ts 中使用上面的插件,這樣就在基座中通過子應用的地址來訪問這個子應用了。


export default defineConfig({base: '/',   // 用qiankun插件后默認base為包名,所以這里重置一下qiankun: { //告訴umi這個項目需要用到qiankunslave: {},},plugins: ['@umijs/plugins/dist/qiankun', '@umijs/plugins/dist/model', '@umijs/plugins/dist/mf'], //plugins使用@umijs/plugins插件,功能分別是支持qiankun、允許useModel、模塊聯邦mf})

  1. 生命周期

如果需要在生命周期中做一些事情,可以在入口文件app.tsx中導出qiankun對象,在對象中的方法寫代碼,qiankun會執行這些生命周期函數。


export const qiankun = {async mount(props: any) {console.log(props)},async bootstrap() {console.log('umi app bootstraped');},async afterMount(props: any) {console.log('umi app afterMount', props);},
};

二、vue3+vite改造

創建子應用

創建子應用,選擇vue3+vite

npm create vite@latest
改造子應用
  1. 安裝vite-plugin-qiankun包,因為qiankun和vite有些問題,需要這個包解決。

pnpm i vite-plugin-qiankun
  1. 修改vite.config.js,使用上面的插件

import qiankun from 'vite-plugin-qiankun';defineConfig({base: '/sub-vue', // 和基座中配置的activeRule一致server: {port: 3002,cors: true,origin: 'http://localhost:3002'},plugins: [vue(),qiankun('sub-vue', { // 配置qiankun插件useDevMode: true})]
})
  1. 修改main.ts

我們需要提供三個必須的生命周期函數,即:bootstrap(只在第一次進入的時候執行)、mount(掛載)、onmount(卸載)

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';let app: any;
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {createApp(App).mount('#app');
} else {renderWithQiankun({// 子應用掛載mount(props) {app = createApp(App);app.mount(props.container.querySelector('#app'));},// 只有子應用第一次加載會觸發bootstrap() {console.log('vue app bootstrap');},// 更新update() {console.log('vue app update');},// 卸載unmount() {console.log('vue app unmount');app?.unmount();}});
}

三、create-react-app改造

1、改造入口文件

代碼如下

  • 導出三個必須的生命周期函數,供qiankun使用。

  • 根據window.__POWERED_BY_QIANKUN__來決定render邏輯

let root: Root// 將render方法用函數包裹,供后續主應用與獨立運行調用
function render(props: any) {const { container } = propsconst dom = container ? container.querySelector('#root') : document.getElementById('root')root = createRoot(dom)root.render(// 可以根據需要指定basename//<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? "/sub-react" : ""}><BrowserRouter><App/></BrowserRouter>)
}// 判斷是否在qiankun環境下,非qiankun環境下獨立運行
if (!(window as any).__POWERED_BY_QIANKUN__) {render({});
}// 各個生命周期
// bootstrap 只會在微應用初始化的時候調用一次,下次微應用重新進入時會直接調用 mount 鉤子,不會再重復觸發 bootstrap。
export async function bootstrap() {console.log('react app bootstraped');
}// 應用每次進入都會調用 mount 方法,通常我們在這里觸發應用的渲染方法
export async function mount(props: any) {render(props);
}// 應用每次 切出/卸載 會調用的方法,通常在這里我們會卸載微應用的應用實例
export async function unmount(props: any) {root.unmount();
}

2、新增public-path.js

動態設置 webpack publicPath,防止資源加載出錯

if (window.__POWERED_BY_QIANKUN__) {// 動態設置 webpack publicPath,防止資源加載出錯// eslint-disable-next-line no-undefwebpack_public_path = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

3、修改webpack配置文件,用umd格式打包

這里用craco修改webpack的配置,所以應該先下載craco。在根目錄下新增craco.config.js文件并新增如下配置

const { name } = require("./package");module.exports = {webpack: (webpackConfig) => {webpackConfig.output = {...webpackConfig.output,library: ${packageName}-[name],libraryTarget: "umd", //主要是這個配置,用umd打包這個項目chunkLoadingGlobal: webpackJsonp_${packageName},filename: "static/js/[name].umd.js",chunkFilename: "static/js/[name].umd.chunk.js",}return webpackConfig}
};

什么是umd?

umd格式_umd 庫格式-CSDN博客


在基座能訪問子應用,即說明配置成功。

說明

一、樣式隔離

使用

子應用之間的樣式隔離qiankun已經實現了,但是基座和子應用之間的樣式隔離沒有實現。

我們可以在基座注冊子應用時 設置 strictStyleIsolation為true 這樣設置主要是對直接設置class時進行隔離(className='test'),在項目中我們一般是 css module 和strictStyleIsolation 一起使用,子應用能加自己的前綴是更好的。

export default defineConfig({qiankun: {master: {apps: [{name: 'sub-umi',entry: '//localhost:5175',activeRule: '/qiankun/umi',sandbox: {strictStyleIsolation: true,//嚴格樣式隔離},},],},},//其他配置
})

strictStyleIsolation原理??

【基于shadow dom來做的樣式隔離】

shadowDOM 的MDN地址如下:

使用影子 DOM - Web API | MDN

strictStyleIsolation 的原理是基于 Web Components 中的 Shadow DOM 技術。讓我詳細解釋一下:

  1. 基本實現原理

function createShadowContainer(container, appName) {// 創建 Shadow DOMconst shadow = container.attachShadow({ mode: 'open' });// 子應用的所有內容都會被放入這個 Shadow DOM 中return shadow;
}

實際效果:

<!-- 普通 DOM 結構 -->
<div id="main"><div id="app1">#shadow-root (open)<!-- 子應用的所有內容都在 Shadow DOM 中 --><style>.title { color: red; }</style><div class="title">我是app1的標題</div></div>
</div>

  1. Shadow DOM 的特性

  • 獨立的 DOM 樹

  • 樣式完全隔離

  • JavaScript 訪問限制

  • 事件局部化

  1. 樣式隔離效果

/* Shadow DOM 內部的樣式 */
.title { color: red; }/* 外部的樣式無法影響到 Shadow DOM 內部 */
#main .title { color: blue; } /* 這個樣式不會影響 Shadow DOM 內的 .title */

主要優點:

  • 完全的樣式隔離

  • 不需要額外的樣式轉換

  • 原生的隔離方案

主要缺點:

  • 一些第三方庫可能無法正常工作

  • 彈窗類組件可能會被限制在 Shadow DOM 內

  • 瀏覽器兼容性問題

二、js沙箱

  1. 在基座中修改window會共享到各個子應用。

  2. 在子應用A修改window不會影響到子應用B。

qiankun中js沙箱的原理

qiankun中的js沙箱是對window進行隔離,主要解決全局變量沖突和全局狀態相互影響的問題。

qiankun提供了三種沙箱模式:

1、在不支持proxy的瀏覽器,提供【快照沙箱】,在進入子應用之前

2、在支持proxy的瀏覽器,用proxy代理window,子應用修改代理后的window。單例就采用legacySandbox沙箱,多例就采用proxySandbox沙箱。

類似代碼:

const proxy = new Proxy(window)
(function(window){//子應用的代碼
})(proxy)

倔金沙箱:?

jhttps://juejin.cn/post/6920110573418086413#heading-12

三、剔除重復依賴

如果基座和子應用使用了項目的庫,可以考慮子應用使用基座的包,從而減少重復加載

有兩種方式:

  1. externals,基座用cdn引入包,子應用相同的cdn設置為ignore。(更推薦用externals

  2. 模塊聯邦,基座將重復包打包至remote.js,子應用不打包重復的包,而是在運行時請求基座的remote.js

externals

流程:

  • 基座:將所有公共依賴配置webpackexternals,并且在index.html使用外鏈引入這些公共依賴

  • 子應用:和主應用一樣配置webpackexternals,并且在index.html使用外鏈引入這些公共依賴,注意,還需要給子應用的公共依賴的加上 ignore 屬性(這是自定義的屬性,非標準屬性),qiankun在解析時如果發現igonre屬性就會自動忽略

以lodahs為例:

基座:

修改config/config.ts文件,在externals中添加lodash,之后在headScripts數組中添加lodash的cdn地址。

// 修改config/config.js
export default defineConfig({/*** @name <head> 中額外的 script* @description 配置 <head> 中額外的 script*/headScripts: [// 解決首次加載時白屏的問題//{ src: '/scripts/loading.js', async: true },//lodash 的cdn{ src: 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js', async: false },],externals: {lodash: '_',//externals 指定lodash和他的全局變量名},
})

umi子應用:

子應用同樣需要在自己的配置文件中添加cdn的lodash,并且需要添加ignore忽略lodash。

<!-- 注意:這里的公共依賴的版本,基座和子應用需要一致 -->


export default defineConfig({// 剔除重復包externals: {lodash: '_',},headScripts: [{src: 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js',async: false,ignore: true, // 子應用需要添加ignore,忽略lodash,使用基座掛載window上的lodash,這樣只需要請求一次},],
})

四、公共組件

我們后續會用pnpm的workspace來做monorepo,這樣在單倉下基座和子應用就能共享組件了,

但是這樣還有一個問題,當公共組件變化,子應用就需要重新打包部署才能得到公共組件的變化,所以可以采用模塊聯邦的方式,讓子應用使用基座的遠程組件,這樣就只需要對基座進行打包部署。

接下來先介紹monorepo改造基座,并舉例在子應用中如何使用workspce的公共組件,最后介紹將公共組件轉為遠程組件使用。

1、monorepo改造

  1. 在基座的根目錄新建pnpm-workspace.yaml 文件,文件內容:

意思是:

  • 會將這三個文件夾下的目錄添加到workspace工作空間中,他們可以相互通過workspce訪問到。

  • 其中app文件夾存放子應用的項目代碼,src/expoese/components文件下存放各種公共組件的代碼。

  • 在umi中要導出遠程組件,需要將組件寫到src/exposes文件夾下,umi自動處理exposes文件夾下的組件。

packages:- 'apps/*'- 'src/exposes/components/*'- 'src/exposes/*'
  1. 新建yaml中涉及的文件夾,將子應用放入apps文件夾中。

至此簡單的monorepo改造完畢

2、添加公共組件

下面演示PureButton這個公共組件的創建和使用。

  1. 前置步驟

在src/exposes/components (看上面,這是一個workspace目錄) 目錄下執行npm init -y,我們會將components目錄作為組件庫的目錄,后續的公共組件都寫在這個目錄下。其package.json類似步驟2(ps:也可以不這么做,也可以直接所有公共組件寫在exposes下,只要組件是個npm包就行。)

{"name": "components","version": "1.0.1","description": "","main": "index.tsx","keywords": [],"author": "","license": "ISC"
}
  1. 在workspce中創建組件包

在src/exposes/components下新建一個PureButton 文件夾,在這個文件夾中執行npm init -y 生成package.json 文件,主要修改文件中以下字段:

  • name(我們可以用@loctek這個前綴,包名用橫線分割,不要用駝峰。)

  • version

  • main(main是這個包的入口)

{"name": "@loctek/pure-button","version": "1.0.0","description": "","main": "index.tsx","types": "index.d.ts","keywords": [],"author": "","license": "ISC","private": true
}
  1. 書寫組件代碼

新建index.tsx,書寫組件的代碼

import styles from './style.module.less';
import type { Props } from './index.d';const PureButton = ({ btnStr = '' }: Props) => {return (<><div className={styles['btn-container']}>{btnStr}</div></>);
};export default PureButton;

  1. 測驗組件是否生效

(這里單純測試workspace是否生效,實際項目我們采用模塊聯邦的方式。)

在本地開發中,我們可以在apps的子應用中,在其package.json中添加上面的包,然后在子項目的目錄執行pnpm i 或者 pnpm i @loctek/pure-button 命令,這樣就會將@loctek/pure-button組件的軟鏈接添加到子項目的node_modules中供子應用使用。

  "dependencies": {//其他包..."@loctek/pure-button": "workspace:*",},


3、利用模塊聯邦使用公共組件

官網地址:

Module Federation 插件

  1. 改造基座,導出遠程組件

在config/config.ts中使用 mf 字段,這樣最終會在打包文件中多出一個remote.js文件。用name起一個名字,remoteHash用于取消打包的hash,library指定打包的文件的模塊

export default defineConfig({// 模塊聯邦mf: {name: 'master',//關閉remote.js的hashremoteHash: false,// qiankun時必須要,window是掛載到window上,默認是varlibrary: { type: 'window', name: 'master' },},//其他配置})
  1. 子應用注冊遠程組件

子應用的.umirc.ts中,1、添加mf插件、2、添加mf字段

mf中的remotes指定訪問的遠程組件地址和他的name,基座最終會被部署到80端口,所以我們的entry就是//localhost:80/remote.js',,remote.js就是會將基座中src/exposes下的文件打包進去。(ps:在本地開發階段可以寫基座項目啟動的地址,比如 //localhost:8080/remote.js )

shared字段填這個子應用用到的遠程包,因為我們會將所有的組件寫入components,所以直接這樣寫好就行了。

  plugins: ['@umijs/plugins/dist/qiankun', '@umijs/plugins/dist/model', '@umijs/plugins/dist/mf'],//添加  @umijs/plugins/dist/mf'mf: {remotes: [{aliasName: 'masterAppXXX',//一個別名name: 'master', // 對應基座應用的 nameentry: '//localhost:80/remote.js', // 基座應用中導出的共享包的入口},],// 聲明共享依賴shared: {'components': {singleton: true,//單例,整個應用只存在一個,防止一個庫加載多個版本。eager: false,//控制共享模塊的加載時機,默認為false:異步加載,實際用的時候加載;為true時指:同步加載,應用啟動就加載,使用于本地開發的時候。requiredVersion: '^1.0.1',},},},
  1. 使用遠程組件

隨便在子應用中找個文件用遠程的PureButton組件

//impoer MasterApp from '別名/包名'
import MasterApp from 'masterAppXXX/components';
//因為是默認導出,所以需要拿到默認導出的東西后解構
const { PureButton} = MasterApp;
export default function Foo(){return (<><PureButton btnStr="555" /></>)
}

五、應用之間的通信

官網地址:

微前端

useModel

基座

在基座中定義了一些model,并且在app.jsx這個入口文件中導出子應用中需要使用的model

入口文件:app.tsx中導出useQiankunStateForSlave函數。

// 子應用(需要子應用是umi項目)獲取主應用的全局狀態,需要在app.tsx導出useQiankunStateForSlave供umi使用
interface QiankunState {site: string;
}
export function useQiankunStateForSlave(): QiankunState {const { site } = useModel('site');return {site,//導出site供子應用使用};
}

子應用

在子應用中用useModel('@@qiankunStateFromMaster') 即可拿到導出的數據


import { useModel } from 'umi';export default function HomePage() {// 獲取主應用的actionsconst model = useModel('@@qiankunStateFromMaster');console.log(model?.site)return <>...<>}

六、keep-alive

菜單切換時會重新加載子應用,我們能手動來加載子應用的顯示隱藏來實現keep-alive的效果,umimax已經為我們提供好了【MicroAppWithMemoHistory】組件,直接用就行。

路由注冊時使用這個組件,routes.ts:

export default [  ...['accountService', 'recordCenter'].map((base) => ({path: `/${base}/*`,component: './MicroAppWrapper',})),
]

組件代碼:

import { useMemo } from 'react';
import { MicroAppWithMemoHistory } from '@umijs/max';const map = {'accountService': 'qijing','recordCenter': 'haitu',
};function MicroAppWrapper() {const info = useMemo(() => {const pathname = window.location.pathname;const base = pathname.split('/')[1];return {name: map[base],pathname,};}, []);return <MicroAppWithMemoHistory name={info.name} url={info.pathname} />;
}export default MicroAppWrapper;

?

nginx部署

官網地址:

入門教程 - qiankun

  • 可以將子應用和基座部署在同一個server,也可以部署在不同的server下。

一、部署在同一個server

部署在同一個server下,

  • 子應用的路由base需要和基座的activeRule保持一致。

  • 子應用的entery則是需要與其nginx路徑一致

基座注冊:

  qiankun: {master: {apps: [{name: 'sub-umi-1', //子應用的名稱entry: '/sub/umi1/', //子應用的入口地址,nginx配置的目錄activeRule: '/app-umi1-history', //子應用的激活規則,指路由sandbox: {strictStyleIsolation: true, //嚴格樣式隔離},},],},},

子應用.umirc.ts

  base: '/app-umi2-history', //供基座訪問的路由前綴,會和activeRule一樣// 用qiankun插件后默認base為包名publicPath: '/sub/umi2/', //資源的前綴,會和nginx中存放的目錄保持一次

二、部署在不同的server

部署在不同的server需要為子應用的server添加跨域

nginx.conf內容:

    # main主應用server {listen       80;server_name  localhost;#  CORS 配置add_header Access-Control-Allow-Origin '*' always;add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS' always;add_header Access-Control-Allow-Headers '*' always;add_header Access-Control-Allow-Credentials 'true' always;if ($request_method = 'OPTIONS') {return 204;}location / {root   html/main;index  index.html index.htm;try_files $uri $uri/ /index.html;}# API 代理配置location ^~ /auth {proxy_pass http://mall-center.dev.springbeetle.top;}location ^~ /perm {proxy_pass http://mall-center.dev.springbeetle.top;}# 處理子應用的代理# location ^~ /qiankun/react {#     proxy_pass http://localhost:5173;# }error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}}# umi子應用server {listen       5175;server_name  localhost;# # 添加5174全局 CORS 配置add_header Access-Control-Allow-Origin '*' always;add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS' always;add_header Access-Control-Allow-Headers '*' always;add_header Access-Control-Allow-Credentials 'true' always;if ($request_method = 'OPTIONS') {return 204;}location / {root   html/qiankun/umi;index  index.html index.htm;try_files $uri $uri/ /index.html;}# API 代理配置# location ^~ /auth {#      proxy_pass http://mall-center.dev.springbeetle.top;# }error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/82132.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/82132.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/82132.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Java學習筆記】代碼塊

代碼塊 介紹&#xff1a;代碼塊又稱為初始化塊&#xff0c;屬于類中的成員&#xff08;即是類的一部分&#xff09;&#xff0c;類似于方法&#xff0c;將邏輯語句封裝在方法體中&#xff0c;通過{}包圍起來 與類方法的不同點 無方法名 無返回類型 無參數 只有方法體&#…

spring boot 2.7集成舊的springfox-boot-starter swagger oas 3.0

舊版本目前已經不維護推薦使用 springdoc-openapi-ui&#xff0c;這里為了演示使用舊的最新依賴 <dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version> </dep…

Linux按鍵驅動測試方式詳細介紹

Linux按鍵驅動測試可采用以下分層方法&#xff1a; 基礎事件檢測 使用輸入子系統調試工具&#xff1a; sudo apt install evtest # 安裝事件測試工具 evtest # 選擇對應設備編號觸發按鍵后觀察終端輸出&#xff0c;正常情況應顯示&#xff1a; Event:…

USB學習【13】STM32+USB接收數據過程詳解

目錄 1.官方的描述2.HAL的流程把接收到的數據從PMA拷貝到用戶自己定義的空間中 3.處理接收到的數據4.最后再次開啟準備接收工作 1.官方的描述 2.HAL的流程 以上的官方說法我們暫時按下不表。 如果接收到數據&#xff0c;會激活中斷進入到USB_LP_CAN1_RX0_IRQHandler&#xff0…

上海內推 | 上海算法創新研究院-上海交大聯合招收空間智能/具身智能算法實習生

最近這一兩周不少公司已開啟春招和實習招聘。 不同以往的是&#xff0c;當前職場環境已不再是那個雙向奔赴時代了。求職者在變多&#xff0c;HC 在變少&#xff0c;崗位要求還更高了。 最近&#xff0c;我們又陸續整理了很多大廠的面試題&#xff0c;幫助一些球友解惑答疑&am…

C語言速成12之指針:程序如何在內存迷宮里找寶藏?

程序員Feri一名12年的程序員,做過開發帶過團隊創過業,擅長Java、鴻蒙、嵌入式、人工智能等開發,專注于程序員成長的那點兒事,希望在成長的路上有你相伴&#xff01;君志所向,一往無前&#xff01; 0. 前言&#xff1a;程序如何在內存迷宮里找寶藏&#xff1f; 想象內存是一個巨…

部署n8n

https://github.com/n8n-io/n8n docker volume create n8n_data docker run -it --rm --name n8n -p 5678:5678 -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n Discover 2192 Automation Workflows from the n8ns Community

ABP VNext + Orleans:Actor 模型下的分布式狀態管理最佳實踐

ABP VNext Orleans&#xff1a;Actor 模型下的分布式狀態管理最佳實踐 &#x1f680; &#x1f4da; 目錄 ABP VNext Orleans&#xff1a;Actor 模型下的分布式狀態管理最佳實踐 &#x1f680;一、引言&#xff1a;分布式系統的狀態挑戰 &#x1f4a1;二、架構圖與技術棧 &am…

構建安全AI風險識別大模型:CoT、訓練集與Agent vs. Fine-Tuning對比

構建安全AI風險識別大模型:CoT、訓練集與Agent vs. Fine-Tuning對比 安全AI風險識別大模型旨在通過自然語言處理(NLP)技術,檢測和分析潛在的安全威脅,如數據泄露、合規違規或惡意行為。本文從Chain-of-Thought (CoT)設計、訓練集構建、以及Agent-based方法與**AI直接調優…

Baklib內容中臺的主要構成是什么?

Baklib內容中臺核心架構 Baklib作為一站式知識管理平臺的核心載體&#xff0c;其架構設計圍繞智能搜索引擎優化技術與多終端適配響應系統展開。通過模塊化內容組件的靈活配置&#xff0c;企業可快速搭建知識庫、FAQ頁面及幫助中心等標準化場景&#xff0c;同時借助可視化數據看…

Ubuntu Desktop 24.04 常用軟件安裝步驟

文章目錄 Ubuntu Desktop 24.04 常用軟件安裝步驟Snipaste F1快捷截圖&#xff08;超方便 | 我6臺電腦每臺都用&#xff09;搜狗輸入法快速瀏覽工具 | 空格鍵快速預覽文件壁紙工具 | varietySSH 工具 | Termius 終端分屏工具 | TmuxCaffeine | 避免息屏小工具 一些設置將啟動臺…

詳細使用@rollup/plugin-inject的方式

rollup/plugin-inject 是一個 Rollup 插件&#xff0c;它允許你在構建時自動注入模塊中的變量引用&#xff0c;避免手動在每個文件中 import。Vite 使用的是 Rollup 構建底層&#xff0c;因此該插件在 Vite 項目中也適用。 一、使用場景 比如你希望在代碼中不手動寫 import { …

Day 0017:Web漏洞掃描(OpenVAS)解析

一、NVT腳本解析&#xff1a;漏洞檢測的“DNA” 1. NVT腳本結構 每個NVT腳本都是一個Lua腳本&#xff0c;包含以下核心模塊&#xff1a; lua -- 示例&#xff1a;檢測Apache HTTPd 2.4.49路徑穿越漏洞&#xff08;CVE-2021-41773&#xff09; script_id "1.3.6.1.4.1.…

【HarmonyOS Next之旅】DevEco Studio使用指南(二十六) -> 創建端云一體化開發工程

目錄 1 -> 創建HarmonyOS應用工程 1.1 -> 新建工程 1.1.1 -> 前提條件 1.1.2 -> 選擇模板 1.1.3 -> 配置工程信息 1.1.4 -> 關聯云開發資源 1.2 -> 工程初始化配置 1.2.1 -> 自動開通云開發服務 1.3 -> 端云一體化開發工程目錄結構 1.3.1…

Python 包管理工具 uv

Python 包管理工具 uv 是由 Astral 團隊&#xff08;知名工具 Ruff 的開發者&#xff09;基于 Rust 開發的新一代工具&#xff0c;旨在通過高性能和一體化設計革新 Python 生態的依賴管理體驗。以下是其核心特性、優勢及使用指南的全面解析&#xff1a; 一、uv 的核心優勢 極致…

何謂第二大腦?讀書筆記

2025/05/11 發表想法 每個人都是矛盾結合體&#xff0c;既想學到新知識、新的能力&#xff0c;又想沒辦法專注的學習&#xff0c;既無法專注有渴望學習新技能&#xff0c;逐漸會產生焦慮、失眠等負面癥狀&#xff0c;這就是現實社會現照&#xff0c;那怎么辦&#xff1f;我們能…

動態防御體系實戰:AI如何重構DDoS攻防邏輯

1. 傳統高防IP的靜態瓶頸 傳統高防IP依賴預定義規則庫&#xff0c;面對SYN Flood、CC攻擊等常見威脅時&#xff0c;常因規則更新滯后導致誤封合法流量。例如&#xff0c;某電商平臺遭遇HTTP慢速攻擊時&#xff0c;靜態閾值過濾無法區分正常用戶與攻擊者&#xff0c;導致訂單接…

為什么在設置 model.eval() 之后,pytorch模型的性能會很差?為什么 dropout 影響性能?| 深度學習

在深度學習的世界里&#xff0c;有一個看似簡單卻讓無數開發者困惑的現象&#xff1a; “為什么在訓練時模型表現良好&#xff0c;但設置 model.eval() 后&#xff0c;模型的性能卻顯著下降&#xff1f;” 這是一個讓人抓耳撓腮的問題&#xff0c;幾乎每一個使用 PyTorch 的研究…

[爬蟲知識] http協議

相關爬蟲專欄&#xff1a;JS逆向爬蟲實戰 爬蟲知識點合集 爬蟲實戰案例 引言&#xff1a;爬蟲與HTTP的不解之緣 爬蟲作用&#xff1a;模擬瀏覽器請求網頁為何要懂HTTP&#xff1a;http是網絡通信的基石&#xff0c;爬蟲抓取數據就是通過HTTP協議進行的&#xff0c;了解http有…

《Spark/Flink/Doris離線實時數倉開發》目錄

歡迎加入《Spark/Flink/Doris離線&實時數倉開發》付費專欄&#xff01;本專欄專為大數據工程師、數據分析師及準備大數據面試的求職者量身打造&#xff0c;聚焦Spark、Flink、Doris等核心技術&#xff0c;覆蓋離線與實時數倉開發的全流程。無論你是想快速上手項目、提升技術…