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微前端项目的通信方案

  • 相关阅读:
    剑指offer——最小的K个数和数组中第K大的元素
    Leetcode刷题指南链接整理
    160. Intersection of Two Linked Lists
    100. Same Tree
    92. Reverse Linked List II
    94. Binary Tree Inorder Traversal
    79. Word Search
    78,90,Subsets,46,47,Permutations,39,40 DFS 大合集
    0x16 Tire之最大的异或对
    0x16 Tire
  • 原文地址:https://www.cnblogs.com/lodadssd/p/14426020.html
Copyright © 2011-2022 走看看