vue 全局 prefix_微前端vue+react(qiankun)

本文介绍了如何使用QianKun搭建一个包含Vue和React子项目的微前端应用。主要内容包括新建项目、创建Vue主项目和Vue、React子项目,配置全局Store进行信息交互,处理子项目的public-path以及启动和测试各项目。
摘要由CSDN通过智能技术生成

新建项目

mkdir qiankun-m
  • 创建.gitignore文件
node_modules/

创建主项目(vue)

vue create main
  1. 安装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();

测试

  • 分别启动三个项目

3958cf3941589c30924be7a74c1fc553.png

访问主项目main项目 http://localhost:8080/

1335b9c85bd01a3cb57ad0fb382d9c73.png

访问react-demo项目 http://localhost:8080/react

544e66a58e7fbf3a6ac19fd9bddfc893.png

访问vue-demo项目 http://localhost:8080/vue

d3028079735ff13b22792d048532f17e.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值