欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2023-12-22 22:09:58
...

新建项目

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();

测试

  • 分别启动三个项目

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

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

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

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

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

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

vue 全局 prefix_微前端vue+react(qiankun)
相关标签: vue 全局 prefix

上一篇:

下一篇: