微前端 —— menu&&project1(React)
前言
微前端 —— 理论篇
微前端 —— portal项目
上一篇中,我们完成了portal项目的搭建,算是完成了整个微前端架构的一半工程了。现在开始新建我们的业务小应用。
Menu项目
menu项目是作为页面的菜单显示的,主要用于路由的控制。
项目的结构如下:
接下来就开始实现它吧。
- 新建项目文件夹menu,在根目录执行
npm init -y
- 安装相关依赖,由于react相关的几个依赖已经在
portal
项目中抽离出来,因此我们这不需要安装了。
package.js
文件内容如下:
{ "name": "menu", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack-dev-server --config ./webpack.dev.js --port 8235", "build": "webpack --config ./webpack.config.js -p" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "antd": "^3.20.7", "babel-plugin-import": "^1.12.0", "copy-webpack-plugin": "^5.0.4", "react-router": "4.3.1", "react-router-dom": "4.3.1", "single-spa-react": "2.8.1", "@reach/router": "1.2.1" }, "devDependencies": { "@babel/core": "7.0.0", "@babel/plugin-proposal-class-properties": "7.0.0", "@babel/plugin-proposal-decorators": "7.1.0", "@babel/plugin-proposal-object-rest-spread": "7.0.0", "@babel/plugin-syntax-dynamic-import": "^7.0.0", "@babel/preset-env": "7.0.0", "@babel/preset-react": "7.0.0", "autoprefixer": "9.1.5", "babel-core": "6.26.3", "babel-loader": "8.0.0", "clean-webpack-plugin": "0.1.19", "css-loader": "1.0.0", "postcss-loader": "3.0.0", "style-loader": "0.23.0", "webpack": "4.17.1", "webpack-cli": "3.1.0", "webpack-dev-server": "^3.1.14" } }
新建src文件夹,在src文件夹下创建components文件夹,在components文件夹中新建文件Menu.js
,内容如下:
import React from 'react' import {Menu, Icon} from 'antd' import { Link } from '@reach/router' const MenuItem = Menu.Item export default class Menu_ extends React.Component { render () { return ( <div style={menuStyle}> <h1>menu!</h1> <Menu> <MenuItem key="1"> <Icon type="pie-chart"/> <Link to='/' style={{display: 'inline'}}>主页</Link> </MenuItem> <MenuItem key="2"> <Icon type="desktop"/> <Link to='/page1' style={{display: 'inline'}}>页面1</Link> </MenuItem> <MenuItem key="32"> <Icon type="desktop"/> <Link to='/page2' style={{display: 'inline'}}>页面2</Link> </MenuItem> <MenuItem key="4"> <Icon type="desktop"/> <Link to='/page3' style={{display: 'inline'}}>页面3</Link> </MenuItem> <MenuItem key="5"> <Icon type="desktop"/> <Link to='/page4' style={{display: 'inline'}}>页面4</Link> </MenuItem> </Menu> </div> ) } } const menuStyle = { display: 'inline-block', position: 'fixed', width: '300px', height: '100%' }
现在我们已经实现了菜单的页面代码。在src目录下新建文件root.component.js
,引入导出Menu组件
import React from 'react' import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom' import Menu from './components/Menu' export default class Root extends React.Component { state = { hasError: false } componentDidCatch (error, info) { this.setState({hasError: true}) } render () { return ( <Menu /> ) } }
在src目录下新建文件set-public-path.js
// For lazy loading within an application to work you need to set webpack's public path // basically webpack's internal module system always looks for code-splits (modules) at the root export default function setPublicPath() { return Promise.all([getUrl()]).then(values => { const [url] = values const webpackPublicPath = url.slice(0, url.lastIndexOf('/') + 1) __webpack_public_path__ = webpackPublicPath return true }) } function getUrl () { return window.System.resolve('@portal/menu') }
在src目录下新建文件Menu.js
,用于组件的注册
import React from 'react' import ReactDOM from 'react-dom' import singleSpaReact from 'single-spa-react' import { property } from 'lodash' import setPublicPath from './set-public-path.js' const reactLifecycles = singleSpaReact({ React, ReactDOM, loadRootComponent: () => import(/* webpackChunkName: "people-app" */'./root.component.js').then(property('default')), domElementGetter, }) export const bootstrap = [ () => { return setPublicPath() }, reactLifecycles.bootstrap, ] export const mount = [ reactLifecycles.mount, ] export const unmount = [ reactLifecycles.unmount, ] export const unload = [ reactLifecycles.unload, ] function domElementGetter() { let el = document.getElementById("menu"); if (!el) { el = document.createElement('div'); el.id = 'menu'; document.body.appendChild(el); } return el; }
最后编写webpack配置文件,在项目根目录下创建文件webpack.config.js
、webpack.dev.js
webpack.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: path.resolve(__dirname, 'src/menu.js'), output: { filename: 'menu.js', library: 'menu', libraryTarget: 'amd', path: path.resolve(__dirname, 'build/menu'), }, 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')], use: [ 'style-loader', { loader: 'css-loader', options: { modules: true, 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: [ new CleanWebpackPlugin(['build/menu']), new CopyWebpackPlugin([ {from: path.resolve(__dirname, 'src/Menu.js')} ]), ], devtool: 'source-map', externals: [ /^@portal\/*/, /^lodash$/, /^single-spa$/, /^rxjs\/?.*$/, /^react$/, /^react\/lib.*/, /^react-dom$/, /.*react-dom.*/, ], };
公共依赖就别单独打包了
webpack.dev.js
/* eslint-env node */ const config = require('./webpack.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;
menu项目的源码大概就这些
3.project1
project1项目也是通过react框架实现的,主要是实现了不同路由匹配不同页面,最后运行打包的时候,暴露出一个js文件,共portal项目调用。详细的就不阐述了,跟menu项目大同小异,也就只是页面代码不一样。
微前端 —— 理论篇
微前端 —— portal项目
微前端 —— project2项目(VUE)