资讯专栏INFORMATION COLUMN

微前端 —— portal项目

shiguibiao / 1378人阅读

摘要:前言微前端理论篇上一篇介绍了微前端的理念,本片将开始介绍项目。先实现公共依赖的引入吧。在上一步我们没有引入的依赖包,是因为的依赖包是作为公共依赖导入的。里面全是我的公共依赖文件在下新建文件夹,新建文件,作为我们整个项目的页面文件。

前言

微前端 —— 理论篇
上一篇介绍了微前端的理念,本片将开始介绍portal项目。

portal项目介绍

        portal项目包括两个功能:
        1. 路由分发与应用加载;
        2. 抽离公共依赖;

目录结构如下:

具体实现

新建一个文件夹,命名随便,我的是portal
        执行npm init -y初始化package.json
        现在我们要做什么呢?肯定是先引入依赖包啊
        webpack打包相关和babel和简单几个需要的loader就行了
        npm i webpack webpack-cli webpack-dev-server style-loader css-loader copy-webpack-plugin clean-webpack-plugin babel-loader babel-core @babel/preset-env @babel/plugin-syntax-dynamic-import @babel/core -D
        由于我们还需要同时运行公共依赖引入和应用加载两个服务,所以再 npm i concurrently -D

在引入所需的依赖之后,开始实现相关功能。先实现公共依赖的引入吧。在上一步我们没有引入single-spa的依赖包,是因为single-spa的依赖包是作为公共依赖导入的。
        先创建一个公共依赖的文件夹,这个文件夹在任何地方都可以,因为我们是通过远程链接引入的,要是你认为的single-spa.js文件可以通过远程cdn或者其他链接取到,可以跳过这一步了,下面是我创建的公共依赖库文件夹。

        里面全是我的公共依赖文件

        
        在portal下新建src文件夹,新建文件index.html,作为我们整个项目的页面文件。代码如下

      
          
            
              
              
              
            
            
              // 此处为从远程导入各个项目的js文件
              // (react、vue项目打包和运行都会产生一个js文件,此文件就是动态渲染页面用的)
              // config.js是portal项目产生的,用来进行路由分发与组件状态管理等
              
              
              
              
              
              
              
              
              
              
          
              
              
          
              
              
              
              
              
              
              

        接下来实现common-deps.js,将我们所有的公共依赖文件统一引入,并命名

      window.SystemJS = window.System

      function insertNewImportMap(newMapJSON) {
        const newScript = document.createElement("script")
        newScript.type = "systemjs-importmap"
        newScript.text = JSON.stringify(newMapJSON)
        const allMaps = document.querySelectorAll("script[type="systemjs-importmap"]")
      
        allMaps[allMaps.length - 1].insertAdjacentElement(
          "afterEnd",
          newScript
        )
      }
      
      const devDependencies = {
        imports: {
          react: "http://localhost:8000/react.development.js",
          "react-dom": "http://localhost:8000/react-dom.development.js",
          "react-dom/server": "http://localhost:8000/react-dom-server.browser.development.js",
          "single-spa": "http://localhost:8000/single-spa.min.js",
          lodash: "http://localhost:8000/lodash.js",
          rxjs: "http://localhost:8000/rxjs.umd.js",
        }
      }
      
      const prodDependencies = {
        imports: {
          react: "http://localhost:8000/react.production.min.js",
          "react-dom": "http://localhost:8000/react-dom.production.min.js",
          "react-dom/server": "http://localhost:8000/react-dom-server.browser.production.min.js",
          "single-spa": "http://localhost:8000/single-spa.min.js",
          lodash: "http://localhost:8000/lodash.min.js",
          rxjs: "http://localhost:8000/rxjs.umd.min.js",
        }
      }
      
      const devMode = true // you will need to figure out a way to use a set of production dependencies instead
      if (devMode) {
        insertNewImportMap(devDependencies)
      } else {
        insertNewImportMap(prodDependencies)
      }  

        公共依赖的抽取代码已经实现了,下面就配置webpack,将这些依赖进行打包,在项目根目录创建 webpack.common-deps.config.jswebpack.common-deps.config.dev.js


        webpack.common-deps.config.js

  const path = require("path")
  const CleanWebpackPlugin = require("clean-webpack-plugin")
  const CopyWebpackPlugin = require("copy-webpack-plugin");
  
  module.exports = {
    entry: "./src/common-deps.js",
    output: {
      filename: "common-deps.js",
      path: path.resolve(__dirname, "build/common-deps"),
      chunkFilename: "[name].js",
    },
    mode: "production",
    node: {
      fs: "empty",
    },
    resolve: {
      modules: [
        __dirname,
        "node_modules",
      ],
    },
    devtool: "sourcemap",
    plugins: [
      new CleanWebpackPlugin(["build/common-deps/"]),
      CopyWebpackPlugin([
        {from: path.resolve(__dirname, "src/common-deps.js")}
      ]),
    ],
    module: {
      rules: [
        {parser: {System: false}},
        {
          test: /.js$/,
          exclude: /(node_modules|bower_components)/,
          use: {
            loader: "babel-loader",
          }
        }
      ]
    }
  }



        webpack.common-deps.config.dev.js

  /* eslint-env node */
  const config = require("./webpack.common-deps.config.js");
  const webpack = require("webpack");
  
  config.plugins.push(new webpack.NamedModulesPlugin());
  config.plugins.push(new webpack.HotModuleReplacementPlugin());
  config.devServer = {
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
  }
  
  config.mode = "development"
  
  module.exports = config;

        上面配置dev-server跨域,很重要!!!否则会因为无法跨域,访问不到js文件


        最后在package.jsonscripts中添加命令

  "scripts": {
      "test": "echo "Error: no test specified" && exit 1",
      "start:common-deps": "webpack-dev-server --config ./webpack.common-deps.config.dev.js --port 8234",
      "build:common-deps": "webpack --config ./webpack.common-deps.config.js -p"
    },

        至此,公共依赖的抽取已经完成,执行npm run start:common-deps,公共依赖就被打包到了portal项目下的build/common-deps/目录,通过import也可以正常导入使用(归功于system)

公共依赖抽取实现、配置完毕之后,开始利用single-spa构建我们的路由分发系统


        在src中创建文件activityFns.js,实现路由的正确匹配,内容如下:

  export function prefix(location, ...prefixes) {
      return prefixes.some(
        prefix => (
          location.href.indexOf(`${location.origin}/${prefix}`) !== -1
        )
      )
    }
    // return true 则加载 false则不加载
    // 这里的menu是菜单,按理应该一直加载出来的,因此return true
    export function menu(location) {
      return true
    }
  
    export function project1(location) {
      return prefix(location, "", "page1", "page2")
    }
  
    export function project2(location) {
      return prefix(location, "page3", "page4")
    }
 

        在src中创建文件config.js,通过single-spa实现路由的注册

  import * as isActive from "./activityFns.js"
  import * as singleSpa from "single-spa"
  
  singleSpa.registerApplication("menu", () => SystemJS.import("@portal/menu"), isActive.menu)
  singleSpa.registerApplication("project1", () => SystemJS.import("@portal/project1"), isActive.project1)
  singleSpa.registerApplication("project2", () => SystemJS.import("@portal/project2"), isActive.project2)
  singleSpa.start()
 
 

        配置webpack打包,在项目根目录创建文件webpack.config.config.js,内容如下:

  /* eslint-env node */
  const webpack = require("webpack")
  const path = require("path");
  const CleanWebpackPlugin = require("clean-webpack-plugin");
  const CopyWebpackPlugin = require("copy-webpack-plugin");
  
  module.exports = {
    entry: "./src/config.js",
    output: {
      filename: "config.js",
      library: "config",
      libraryTarget: "amd",
      path: path.resolve(__dirname, "build"),
    },
    mode: "production",
    module: {
      rules: [
        {parser: {System: false}},
        {
          test: /.js?$/,
          exclude: [path.resolve(__dirname, "node_modules")],
          loader: "babel-loader",
        },
        {
          test: /.css$/,
          exclude: [path.resolve(__dirname, "node_modules"), /.krem.css$/],
          use: [
            "style-loader",
            {
              loader: "css-loader",
              options: {
                localIdentName: "[path][name]__[local]",
              },
            },
            {
              loader: "postcss-loader",
              options: {
                plugins() {
                  return [
                    require("autoprefixer")
                  ];
                },
              },
            },
          ],
        },
        {
          test: /.css$/,
          include: [path.resolve(__dirname, "node_modules")],
          exclude: [/.krem.css$/],
          use: ["style-loader", "css-loader"],
        },
      ],
    },
    resolve: {
      modules: [
        __dirname,
        "node_modules",
      ],
    },
    plugins: [
      CopyWebpackPlugin([
        {from: path.resolve(__dirname, "src/index.html")},
        {from: path.resolve(__dirname, "src/styles.css")},
      ]),
      new CleanWebpackPlugin(["build"]),
    ],
    devtool: "source-map",
    externals: [
      /^lodash$/,
      /^single-spa$/,
      /^rxjs/?.*$/,
    ],
  };

        上面的配置中,最后的externals属性值配置不需要webpack打包的依赖,因为这些在公共依赖中已经打包好了,不需要多带带打包


        在项目根目录创建文件webpack.config.config.dev.js,内容如下:

  const config = require("./webpack.config.config.js")
  const webpack = require("webpack")
  
  config.plugins.push(new webpack.NamedModulesPlugin());
  config.plugins.push(new webpack.HotModuleReplacementPlugin());
  config.devServer = {
    contentBase: "./build",
    historyApiFallback: true,
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
    proxy: {
      "/common/": {
        target: "http://localhost:8234",
        pathRewrite: {"^/common" : ""}
      }
    }
  }
  
  config.mode = "development"
  
  module.exports = config;

        同样配置跨域
        在package.json中添加命令,最终内容如下

  {
    "name": "portal",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
      "test": "echo "Error: no test specified" && exit 1",
      "start": "concurrently "npm run start:config" "npm run start:common-deps"",
      "start:config": "webpack-dev-server --config ./webpack.config.config.dev.js --port 8233",
      "start:common-deps": "webpack-dev-server --config ./webpack.common-deps.config.dev.js --port 8234",
      "build": "npm run build:config && npm run build:common-deps",
      "build:config": "webpack --config ./webpack.config.config.js -p",
      "build:common-deps": "webpack --config ./webpack.common-deps.config.js -p"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
      "@babel/core": "^7.4.3",
      "@babel/plugin-syntax-dynamic-import": "7.0.0",
      "@babel/preset-env": "^7.4.3",
      "babel-core": "6.26.3",
      "babel-loader": "8.0.0",
      "clean-webpack-plugin": "0.1.19",
      "concurrently": "^4.1.1",
      "copy-webpack-plugin": "4.5.2",
      "css-loader": "1.0.0",
      "style-loader": "0.23.0",
      "webpack": "4.17.1",
      "webpack-cli": "3.1.0",
      "webpack-dev-server": "^3.1.14"
    }
  }

        至此,整个portal项目就搭建完毕了,下一篇,我们实现menu项目和project1项目


        项目源码
        Portal源码

        微前端 —— 理论篇
        微前端 —— menu&&project1(React)
        微前端 —— project2项目(VUE)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/106341.html

相关文章

  • 前端 —— 理论篇

    摘要:现在开始从头搭建我们的微前端架构。项目源码微前端项目微前端微前端项目 1. 微前端         基于spa类的单页应用,随着企业工程项目的体积越来越大,开发的功能越来越多,页面也越来越多,项目随之也变得越来越臃肿,维护起来十分困难,往往改一个logo,或者改一个小样式,都要将项目全部重新打包一遍,维护困难,部署也困难。         因此前端在借鉴后端的微服务架构模式后,衍生了...

    wangbinke 评论0 收藏0
  • 前端 —— project2项目(VUE)

    摘要:前言微前端理论篇微前端项目微前端前一篇文章讲解了项目在微前端架构中的应用,本篇为最后一篇,项目在此架构中的应用。项目我们就不自己搭建了,直接使用脚手架生成。 前言         微前端 —— 理论篇        微前端 —— portal项目        微前端 —— menu&&project1(React)        前一篇文章讲解了react项目在single-spa微...

    kumfo 评论0 收藏0
  • 前端改造初探

    摘要:我们继续沿用了原来就有的,借此把融入整个微前端框架,而已经改造的则不需要我们的开发团队,分框架组和各个业务组。项目该项目是整个微前端项目的入口。本坑实践它很大的理由也是用自己的方法初探微前端实践方法的可行性。 在写这篇文章的一个多月前,本坑还不知道微前端是什么,大概从字面上的含义是比较小的前端项目。 本坑开始实践它,是由于工作要求。改造一个运行多年,前端用jsp写的服务平台项目(以下简...

    KunMinX 评论0 收藏0
  • 前端 —— menu&&project1(React)

    摘要:前言微前端理论篇微前端项目上一篇中,我们完成了项目的搭建,算是完成了整个微前端架构的一半工程了。项目项目是作为页面的菜单显示的,主要用于路由的控制。源码地址源码地址项目源码地址微前端理论篇微前端项目微前端项目 前言         微前端 —— 理论篇        微前端 —— portal项目        上一篇中,我们完成了portal项目的搭建,算是完成了整个微前端架构的一半...

    Euphoria 评论0 收藏0
  • ELSE 技术周刊(2017.10.16期)

    摘要:前端中的计算机领域的通常认为起源于。并对其主要内容作了自己的解读。搬到另一个地区会导致名气降低。年度报告,年最受欢迎的编程语言年上最流行的种编程语言及前十最火热的项目排行榜,分别由及登顶。技术周刊由小组出品,汇聚一周好文章,周刊原文。 showImg(https://segmentfault.com/img/bVWHC4?w=1000&h=710); 本期推荐 反击爬虫,前端工程师的脑...

    0xE7A38A 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<