vue 全域性 prefix_微前端vue+react(qiankun)
阿新 • • 發佈:2021-02-13
技術標籤:vue 全域性 prefix
新建專案
mkdir qiankun-m
- 建立.gitignore檔案
node_modules/
建立主專案(vue)
vue create main
- 安裝QianKun
yarn add qiankun
- 建立子專案配置檔案(micro-app.ts)
/** * 路由變化, 獲取子專案字首 * @param {string} prefix * @return {*} */ const getActiveRule = (prefix: string) => { return (location: any) => location.pathname.startsWith(prefix) } /** * 自專案配置列表 * @param {any} props * @return {*} */ const microApps = (props: any) => { const el = document.createElement('div') document.body.append(el) return [ { name: 'vue', entry: '//localhost:8081/', // 子專案入口 activeRule: getActiveRule('/vue'), container: el, // 子專案渲染到的節點 props: props // 傳參 }, { name: 'react', entry: '//localhost:3000/', activeRule: getActiveRule('/react'), container: el, props: props } ] } export default microApps
- 建立全域性store,用於主專案, 子專案的資訊互動(store/global.ts)
import { initGlobalState, MicroAppStateActions } from 'qiankun' ... export const state: RecodeType = { user: { userName: '', avatar: '', mobile: '' } } const localCacheUser = getLocalstore('user') if (localCacheUser) { Object.assign(state.user, localCacheUser) } export const actions: MicroAppStateActions = initGlobalState(state) /** * 監聽全域性狀態改變 * state: 變更後的狀態 * prev 變更前的狀態 */ actions.onGlobalStateChange((state: RecodeType, prev: RecodeType) => { console.log('主專案監聽', state, prev) }) /** * 設定使用者狀態 * @param {UserType} _user * @return {*} */ export function setGlobalUser (_user: UserType) { const { user } = state state.user = Object.assign(user, _user) actions.setGlobalState(state) } /** * 獲取使用者狀態資訊 * @param {*} * @return {*} */ export function getGlobalUser (): UserType { return state.user }
- 更改入口檔案(main.ts)
... import microApps from './micro-app' import { registerMicroApps, start } from 'qiankun' ... // 註冊子應用 registerMicroApps(microApps(state), { beforeLoad: app => { console.log('before load app.name====>>>>>', app.name) return Promise.resolve() }, beforeMount: [ app => { console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name) return Promise.resolve() } ], afterMount: [ app => { console.log('[LifeCycle] after mount %c%s', 'color: green;', app.name) return Promise.resolve() } ], afterUnmount: [ app => { console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name) return Promise.resolve() } ] } ) // 開始 start() new Vue({ router, store, render: h => h(App) }).$mount('#base')
- 更新、監聽全域性狀態(router.ts)
...
import { setGlobalUser, getGlobalUser } from '../store/global'
...
const routes: Array<RouteConfig> = [
...
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
/**
* 登陸請求處理
* @param {*} async
* @return {Promise<string>}
*/
const loginRequest = async (to: Route): Promise<string> => {
const user: UserType = await getUser()
if (!user || !user.mobile) {
return Promise.resolve('/login')
}
const res: any = await Instance.post('/user/mobileDingLogin', {
phoneNumber: user ? user.mobile : ''
})
const { detail } = res
detail.mobile = detail.phoneNumber
setGlobalUser(detail)
setLocalStore('user', detail)
return Promise.resolve((to.path as string))
}
/**
* 處理登入邏輯
* @param {to} Route 前往的路由
* @return {Promise<string>}
*/
const handlerLogin = async (to: Route): Promise<string> => {
const user = getGlobalUser()
if (!user.phoneNumber) {
return await loginRequest(to)
}
return Promise.resolve(to.path as string)
}
router.beforeEach(async (to, from, next) => {
const path = await handlerLogin(to)
if (path === '/login' && to.name !== 'Login') {
next({
path
})
return
}
next()
})
export default router
建立子專案(VUE)vue-demo
vue create vue-demo
- 建立public-path.ts, 使用者處理路徑問題(圖片等靜態資源)
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
- 修改入口檔案main.ts
...
import './public-path'
...
const render = () => {
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/vue' : '/',
mode: 'history',
routes
})
instance = new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
}
export async function bootstrap () {
console.log('vue app bootstraped')
}
/**
* @param props
* 從生命週期 mount 中獲取通訊方法,使用方式和 master 一致
*/
export async function mount (props: any) {
// console.log('props from main app', props)
Object.assign(user, props.user)
props.onGlobalStateChange((state: RecodeType, prev: RecodeType) => {
// state: 變更後的狀態; prev 變更前的狀態
console.log('子元件監聽掛載全域性變數改變', state, prev)
})
render()
}
export async function unmount () {
if (instance) {
instance.$destroy()
}
instance = null
router = null
}
// 本地除錯
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
- 新建vue.config.js
const { name } = require('./package');
...
const writeFile = generateWriteFile()
module.exports = {
configureWebpack: config => {
return {
output: {
// 把子應用打包成 umd 庫格式
library: `${name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`,
},
module: {
rules: [
{
enforce: 'pre',
test: /.(js|ts|vue)$/,
loader: 'eslint-loader',
options: {
fix: true
}
}
]
}
}
},
devServer: {
hot: true,
disableHostCheck: true,
port: 8081,
overlay: {
warnings: false,
errors: true,
},
headers: {
'Access-Control-Allow-Origin': '*', // 跨域
}
}
}
建立子專案(react) react-demo
npx react-create-app react-demo
yarn 調出配置檔案
yarn eject
- 修改config/webpackDevServer.config.js
...
module.exports = function (proxy, allowedHost) {
return {
headers: {
'Access-Control-Allow-Origin': '*', // 允許跨域
},
...
};
修改config/webpack.config.config.js
...
return {
...
output: {
// The build folder.
...
// 微應用配置
library: `${appPackageJson.name}-[name]`,
libraryTarget: 'umd'
},
...
};
- 新建public-path.js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
- 更新main.js
...
import './public-path'
...
/**
* bootstrap 只會在微應用初始化的時候呼叫一次,下次微應用重新進入時會直接呼叫 mount 鉤子,不會再重複觸發 bootstrap。
* 通常我們可以在這裡做一些全域性變數的初始化,比如不會在 unmount 階段被銷燬的應用級別的快取等。
*/
export async function bootstrap() {
console.log('react app bootstraped');
}
/**
* 應用每次進入都會呼叫 mount 方法,通常我們在這裡觸發應用的渲染方法
*/
export async function mount(props) {
ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root'));
}
/**
* 應用每次 切出/解除安裝 會呼叫的方法,通常在這裡我們會解除安裝微應用的應用例項
*/
export async function unmount(props) {
ReactDOM.unmountComponentAtNode(props.container ? props.container.querySelector('#root') : document.getElementById('root'));
}
/**
* 可選生命週期鉤子,僅使用 loadMicroApp 方式載入微應用時生效
*/
export async function update(props) {
console.log('update props', props);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
測試
- 分別啟動三個專案
訪問主專案main專案 http://localhost:8080/
訪問react-demo專案 http://localhost:8080/react
訪問vue-demo專案 http://localhost:8080/vue