background

  • Technical Documentation: Qiankun
  • Reason for using VUe3 on the basis of the original project, it is more convenient to develop, but the complete reconstruction is a lot of work
  • Original projects: client, ant-design-Pro framework based on UMI and Roadhog; Server: an egg
  • Project expectation: the original business theme content will not be affected, the new function will be realized by VUe3 +antProComponents(vuE3 version has only one proLayout component temporarily)+ TS, and the head and menu navigation bar will use the newly developed content of VUe3

Project preparation

  1. Project structure: The original project structure was client(Ant-Design-Pro) and Server (Egg). The core of Qiankun is to register micro applications in the main application. Therefore, it plans to build a new directory client-main as the main application and Client as the micro application.

  2. To build client-main, vuE3 was directly selected using VUE-CLI, and the required tools were installed: TS, Router, and Ant-Design-Vue.
  3. The directory structure in client is temporarily left untouched

Basic building

  • Documentation references the Main application and the React microapplication

  • The main application

    1. Install qiankun

    $YARN add Qiankun # or NPM I Qiankun -S

    1. Register microapplications in the main application
    import { registerMicroApps, start } from "qiankun";
    registerMicroApps([
        {
          name: "kol-mis-client".entry:
            process.env.NODE_ENV === "development" ? "//localhost:8000" : "/client".container: "#qiankun-container".activeRule: () = > true,
        },
    ])
    start();
    Copy the code

    3. For the explanation of the properties of the registered micro-application, see the official documents

  • Micro application

    1. routing

    At first, the hash routing mode was used, and the history mode was officially recommended. In practice, it was found that there were some problems in the hash mode when configuring the application route, so the micro application route was changed to the History mode

    // import {createHashHistory} from 'history';
    // user BrowserHistory
    import { createBrowserHistory } from 'history';
    // 1. Initialize
    const app = dva({
      // history: createHashHistory(),
      history: createBrowserHistory(),
    });
    Copy the code
    1. Register the Qiankun hook function (Ant-Design-Pro rendering differs from the original React rendering, so this content is also slightly different from the official documentation).
      function render(props) {
        const { container } = props;
        app.start(container ? container.querySelector('#root') : document.querySelector('#root'));
      }
      if (!window.__POWERED_BY_QIANKUN__) {
        render({});
      }
      export async function bootstrap() {
        console.log('[react16] react app bootstraped');
      }
    
      export async function mount(props) {
        console.log('[react16] props from main framework', props);
        render(props);
      }
    
      export async function unmount(props) {
        console.log('Microservices Uninstallation')
        const { container } = props;
        container.querySelector('#root').innerHTML = ' '
      }
    Copy the code
    1. Webpack configuration

    The packaging tool of Ant-Design-Pro is Roadhog, and roadhog webpack configuration is encapsulated by them, providing users with simple and direct configuration, but this configuration does not support the configuration required by Qiankun. The workaround is that Roadhog also supports webpack.config.js for custom configuration, but there may be hidden problems, so it is not officially recommended. (Temporarily normal)

    const { name } = require('./package.json');
    
    export default function (config, env) {
      const newConfig = {
        plugins: [],
        ...config,   
        output: {
            ...config.output,
            library: `${name}-[name]`.libraryTarget: 'umd'.jsonpFunction: `webpackJsonp_${name}`,},devServer: {headers : {
                'Access-Control-Allow-Origin': The '*',},historyApiFallback : true.hot : false.watchContentBase : false.liveReload : false,}};// merge or override
       return newConfig;
     }
    Copy the code

Controls the display of main and micro applications

  • The main application needs to create a label to hold the content of the child application. I put the label in Layout

    <div id="qiankun-container"></div>

    The master application determines whether to inject content into the child application container by registering the microapplication activeRule property to determine which routes are for the child application and which are for the master application. But the activeRule attribute I passed in above is :() => true, which means that any route will load the content of the child application. This problem is solved in the sub-application. Load the following code in layout in the child application to return empty content if no valid route is loaded

       <Switch>
          {redirectData.map(item= > (
            <Redirect key={item.from} exact from={item.from} to={item.to} />
            ))}
          {getRoutes(match.path, routerData).map(item= > (
            <AuthorizedRoute
              key={item.key}
              path={item.path}
              component={item.component}
              exact={item.exact}
              authority={item.authority}
              redirectPath="/exception/403"
            />
            ))}
          <Redirect exact from="/" to={bashRedirect} />
          <Route render={Empty} /> // Empty is a file that is introduced with an Empty label
       </Switch>
    Copy the code

Problem solving

  1. Error with push/replace State DOMException Error with push/replace State: Failed to execute ‘replaceState’ on ‘History’: A history state object with URL ‘http://localhost:8080undefined/’ cannot be created in a document with origin ‘http://localhost:8080’ and URL ‘http://localhost:8080/koluser/certify-detail/1893/1’. Due to the difference between the react-router and vue-router processing, some content is lost when the page is jumped. Solutions also looked up some information, the final solution

    // The main application uses nested routines
    router.beforeEach((to, from, next) = > {
      if (!window.history.state.current) window.history.state.current = to.fullPath;
      if (!window.history.state.back) window.history.state.back = from.fullPath;
      // Manually change the state of history
      return next();
    });
    Copy the code
  2. The main application style and child application style separation problem, mainly because the main application and child application use the Ant series, the style of the main application will be overwritten by the style of the application, in fact, there are official solutions to this problem. Note: Adding a prefix to ANTD is supported only in 3.26.20 or later.

  3. Project deployment

    1. The directory structure after packaging



      The contents of client project are packaged into the client folder under public, and the contents of client-mian are packaged directly under public.

    2. Client packaging code has been slightly modified: package destination folder and publicPath

        publicPath: process.env.NODE_ENV === "development" ? "/" : '/client'.//1. PublicPath is very important because the contents of the client package are no longer in the root directory
        //2. The value of publicPath is the same as that of entry for registering microapplications
        outputPath: path.resolve(__dirname, '.. /server/app/public/client'),
      Copy the code
    3. Configuration of srver (Egg)

      //1. Config.default.ts for the template engine
        config.view = {
          root: [
            path.join(appInfo.baseDir, "app/view"),
            path.join(appInfo.baseDir, "/app/public"),
          ].join(","),
          // '${appinfo.basedir}/app/view,${appinfo.basedir}/app/public',// This is not possible on Windows
          defaultExtension: "ejs".mapping: {
            ".html": "ejs".".ejs": "ejs",}};Copy the code
      / / 2. The configuration of a single route pair /client
      router.get('/client', controller.client.test);
      import { Controller } from "egg";
      //3.ClientController /client.ts
      export default class ClientController extends Controller {
        public async test() {
          const { ctx } = this;
          console.log('test')
          return await ctx.render("/client/index.html");// Important: Only when publicPath is configured during packaging can resources under /client/index.html be successfully accessed}}Copy the code
      //4. Router-ts is processed for all routes
      router.get(The '*', controller.home.index); // Page refresh will no longer be 404
      //5
      import { Controller } from "egg";
      export default class HomeController extends Controller {
        public async index() {
          try {
            // return await ctx.render("auth.ejs");
            return await ctx.render("index.html");
          } catch (e) {
            console.log(e); }}}Copy the code

conclusion

The above is the main content of the practice. I don’t remember some small problems. I can comment on any problems and recall the solutions. Mainly view official documents, what problems to solve what problems.