zoukankan      html  css  js  c++  java
  • 基于qiankun从零搭建微前端项目

    微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。

    一、什么是微前端

    “微前端”一词最早在2016年提出,它将后端微服务的概念扩展到前端世界。简单来说,就是将大型的wen渐进式项目拆分成一个个小型工程,即微应用。每个微应用都能独立开发、部署、运行,然后由主应用将所有微应用整合在一起,实现所有页面的展示和交互。

    二、为什么用微前端

    在开发toB项目尤其是中后台项目时,以往的趋势都是搭建一个功能强大且完善的单页应用程序。但是中后台项目生命周期较长(一般3年+),这期间由于参与的人员、团队的增多、变迁,单页应用就会从一个普通应用演变成一个巨石应用。随之而来就导致项目无法维护尾大不掉。这类问题在企业级 Web 应用中尤其常见。

    巨石应用往往有以下几个弊端:

    • 项目引用包太多,打包上线巨慢;
    • 首屏请求的接口越来越多,页面资源繁杂,导致首屏加载缓慢;
    • 项目初期野蛮生长过度,代码难以维护,产品迭代举步维艰;
    • toB应用对于客户定制化需求很难满足;

    巨石应用发展后期都逃不过重构这一条路,一旦重构导致整个项目功能停止更新迭代,对公司发展来说成本巨大。


     微前端架构旨在解决单体应用长时间迭代后,导致不可维护的问题。其具备以下几个核心价值:

    • 技术栈无关
      • 主框架不限制接入应用的技术栈,微应用具备完全自主权
    • 独立开发、独立部署
      • 微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
    • 增量升级
      • 在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
    • 独立运行时
      • 每个微应用之间状态隔离,运行时状态不共享

     这其中的技术难点就是如何将不同技术栈的微应用整合在一起,保持微应用独立运行的同时又能实现跨应用通信。qiankun微前端框架提供了一套简单、无痛的微前端架构解决方案。下面我们一步一步讲解使用qiankun如何从零开始搭建微前端项目。

    三、主应用

    主应用与技术栈无关,我们可以使用Vue、React、Angular、JQuery甚至ES5语法进行搭建。主应用的目的如下:

    1. 注册微应用;
    2. 为每个微应用提供dom容器;
    3. 启动主应用;

    下面我们以vue为主应用一步一步进行讲解,先使用vue-cli3构建项目。

    3.1 安装qiankun

    $ yarn add qiankun

    3.2 注册微应用

    在入口文件main.js中添加如下代码:

    import { registerMicroApps, start, setDefaultMountApp } from 'qiankun'
    
    /**
     * step1 初始化应用
     */
    new Vue({
        render: h => h(App),
    }).$mount('#mainapp-container')
    
    /**
     * step2 注册微应用
     */
    registerMicroApps(
        [
            {
                name: 'vue-app',
                entry: '//localhost:8101',
                container: '#subapp-container',
                activeRule: '/vue',
            },
            {
                name: 'react-app',
                entry: '//localhost:8102',
                container: '#subapp-container',
                activeRule: '/react17',
            },
        ],
        {
            beforeMount: [
                app => {
                console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name);
                },
            ],
            afterUnmount: [
                app => {
                console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name);
                },
            ]
        }
    )
    
    /**
     * step3 设置默认进入微应用
     */
    setDefaultMountApp('/vue')
    
    /**
     * step4 启动
     */
    start()

    注意step1中我们将主应用挂载到自己的dom容器#mainapp-container中,step2中微应用运行的容器#subapp-container我们定义在主应用的根组件App.vue中。

    3.3 启动配置

    所有的微应用都保存在根目录projects中,之后会讲解如何配置不同技术栈的微应用。等所有微应用都配置好后,就需要一键启动。

    $ yarn add npm-run-all

    安装npm-run-all支持同时运行多个npm项目,然后修改package.json配置:

      "scripts": {
        "mainapp:install": "npm-run-all --parallel install:*",
        "mainapp:start": "npm-run-all --parallel start:*",
        "install:main": "yarn",
        "start:main": "yarn start",
        "install:vue": "cd projects/vue && yarn",
        "start:vue": "cd projects/vue && yarn start",
        "install:react17": "cd projects/react17 && yarn",
        "start:react17": "cd projects/react17 && yarn start",
        "start": "vue-cli-service serve",
        "serve": "vue-cli-service serve",
        "build": "vue-cli-service build",
        "lint": "vue-cli-service lint"
      },

    按照下面命令就能启动微前端项目:

    $ yarn install
    $ yarn mainapp:install
    $ yarn mainapp:start

    四、微应用

    微应用不用额外安装qiankun即可接入主应用,大致分为下面几个步骤:

    1. 入口js文件平级目录下增加public-path.js文件;
    2. 入口js文件中引入public-path.js,修改并导出qiankun定义的三个钩子函数:bootstrap、mount、unmount;
    3. 使用history路由,并且路由base值要和activeRule匹配规则保持一致;
    4. 修改webpack配置,允许开发环境跨域及umd打包;

    下面我们以react17+typescript作为技术栈构建微应用,一步一步进行讲解,首先使用create-react-app构建项目。

    $ yarn create react-app react17 --template typescript

    4.1 在src下增加public-path.js

    if (window.__POWERED_BY_QIANKUN__) {
        // eslint-disable-next-line no-undef
        __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }

    注意注释掉的那一行代码必须加上,否则项目有eslint报错无法启动。

    4.2 修改入口文件

    修改src下index.tsx文件

    import './public-path';
    
    function render(props: any) {
        const { container } = props;
        ReactDOM.render(<App />, container ? container.querySelector('#root') : document.querySelector('#root'));
    }
    
    // 单独启动
    if (!(window as any).__POWERED_BY_QIANKUN__) {
        render({});
    }
    
    // 修改、导出微应用钩子
    export async function bootstrap() {
        console.log('[react16] react app bootstraped');
    }
    
    export async function mount(props: any) {
        console.log('[react16] props from main framework', props);
        render(props);
    }
    
    export async function unmount(props: any) {
        const { container } = props;
        ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
    }

    4.3 路由配置

    在组件内部配置路由时,使用history路由并且设置base值。

    import React, { lazy, Suspense } from 'react'
    import { BrowserRouter as Router, Link, Route, Switch } from 'react-router-dom'
    
    const Home = lazy(() => import('../components/Home'))
    const About = lazy(() => import('../components/About'))
    
    const Layout: React.FC = () => {
        return (
            <Router basename={(window as any).__POWERED_BY_QIANKUN__ ? '/react17' : '/'}>
                <nav>
                    <Link to="/">Home</Link>
                    <Link to="/about">About</Link>
                </nav>
                <Suspense fallback={null}>
                    <Switch>
                        <Route path="/" exact component={Home} />
                        <Route path="/about" component={About} />
                    </Switch>
                </Suspense>
            </Router>
        )
      }
    
      export default Layout

    4.4 配置webpack

    安装插件@rescripts/cli

    yarn add @rescripts/cli

    在根目录下新增.rescriptsrc.js文件:

    const { name } = require('./package');
    module.exports = {
        webpack: config => {
            config.output.library = `${name}-[name]`;
            config.output.libraryTarget = 'umd';
            config.output.jsonpFunction = `webpackJsonp_${name}`;
            config.output.globalObject = 'window';
            return config;
        },
        devServer: _ => {
            const config = _;
            config.headers = {
                'Access-Control-Allow-Origin': '*',
            };
            config.historyApiFallback = true;
            config.hot = false;
            config.watchContentBase = false;
            config.liveReload = false;
            return config;
        },
    };

    修改package.json启动文件:

    -   "start": "react-scripts start",
    +   "start": "rescripts start",
    -   "build": "react-scripts build",
    +   "build": "rescripts build",
    -   "test": "react-scripts test",
    +   "test": "rescripts test",
    -   "eject": "react-scripts eject"

    4.5 启动端口配置

    根目录下增加.env配置端口信息,保证微应用启动端口和主应用中注册的该微应用的入口entry保持一致。

    SKIP_PREFLIGHT_CHECK=true
    BROWSER=none
    PORT=8102
    WDS_SOCKET_PORT=8102

    到这里以React作为技术栈的微应用就配置成功了。Vue、Angular所需要的配置都一样,实现起来大同小异。

    完整微前端项目代码在github上。

    五、后记

    当然一个优秀的项目最终是要服务于产品,为客户提供便利为公司带来价值,不能为了炫技而本末倒置。微前端项目启动初期,需要和后端协商好如何依据业务模块进行拆分确定接口url,后期上线部署也需要运维支持。这样才能保证项目能健壮发展下去。

    系列相关文章:

    基于qiankun从零搭建微前端项目

    基于qiankun微前端项目的通信方案

  • 相关阅读:
    《深入理解Java虚拟机》笔记--第四章、虚拟机性能监控与故障处理工具
    《深入理解Java虚拟机》笔记--第三章 、垃圾收集器与内存分配策略
    《深入理解Java虚拟机》笔记--第二章、Java内存区域与内存溢出异常
    Netty并发优化之ExecutionHandler
    Netty框架入门
    123
    如何清理任务栏程序预览的历史记录?
    三个安装,看VIP电影
    WIN 10 COM surrogate 关闭
    微软官方win10系统安装u盘启动盘制作
  • 原文地址:https://www.cnblogs.com/lodadssd/p/14426020.html
Copyright © 2011-2022 走看看