Previous articles are recommended
Gitlab-cicd + Docker + Vue + Django
Introduction to Docker, this one is enough
Introduction to Technology Stack
- Micro front-end
- qiankun
- Take a look at my previous introduction and you will understand it at a glance
- Gitlab-ci/CD Here is the knowledge of automated deployment, you can understand
- nginx
It is recommended to cooperate with video explanation for faster understanding
What is a micro front end
The micro front end is a kind of technical means and method strategy that multiple teams jointly build modern Web applications by releasing functions independently.
The micro front-end architecture has the following core values:
-
Technology stack independent main framework does not limit access to the application stack, microapplications have full autonomy
-
Independent development, independent deployment of micro application warehouse independent, the front and back end can be independently developed, after the deployment of the main framework automatically complete synchronization update
-
The incremental upgrade
In the face of a variety of complex scenarios, it is usually difficult to upgrade or reconstruct the existing system completely, and the micro front end is a very good means and strategy to implement the gradual reconstruction
-
Independent run time States are isolated between each microapplication and run time states are not shared
What is qiankun
Qiankun is a productionable micro-front-end framework based on Single-SPA, with JS sandbox, style isolation, HTML Loader, preloading and other capabilities required by micro-front-end systems. The Qiankun can be used with any JS framework and microapplication access is as simple as embedding an IFrame system.
The core design concept of Qiankun
Reference address: qiankun.umijs.org/zh/guide
-
simple
As the main application and micro application can be stack independent, Qiankun is just a library like jQuery for users. You need to call several apis of Qiankun to complete the micro front end transformation of the application. Meanwhile, thanks to THE HTML entry and sandbox design of Qiankun, accessing microapplications is as simple as using IFrame.
-
Decoupling/technology stack independent
The core goal of the micro front end is to break up the boulder application into several loosely coupled micro-applications that can be autonomous. Many of the designs of Qiankun are based on this principle, such as HTML Entry, sandbox and inter-application communication. Only in this way can we ensure that microapplications can be developed independently and run independently.
Why not use Iframe
Reference address: www.yuque.com/kuitos/gky7…
Iframe is pretty much the perfect microfront-end solution, if you leave aside the experience issues.
The most important feature of iframe is that it provides a browser native hard isolation scheme, no matter the style isolation, JS isolation and other problems can be solved perfectly. However, its biggest problem is that its isolation can not be broken, leading to the application context can not be shared, resulting in the development experience, product experience problems.
- The URL is not synchronized. The browser refresh iframe URL status is lost, and the back forward button is unavailable.
- The UI is not synchronized and the DOM structure is not shared. Imagine an iframe with a mask layer in the bottom right corner of the screen, and we want it to be centered in the browser and automatically centered when the browser resize.
- The global context is completely isolated and memory variables are not shared. Iframe internal and external system communication, data synchronization and other requirements, the cookie of the main application should be transparently transmitted to the sub-applications with different root domain names to achieve the effect of free registration.
- Slow. Each child application entry is a process of browser context reconstruction and resource reloading.
Some problems better solve the problem of (1), some problems we can turn a blind eye (question 4), but some problems we are difficult to solve the problem of (3) even unable to solve the problem of (2), and these can’t solve the problem it will bring very serious to product experience problems, finally lead us to abandon the iframe solution.
Core values of the micro front end
www.yuque.com/kuitos/gky7…
Project concept
Before we get into the technical implementation, let’s take a look at what we want to implement.
Microfront diagram
The master application manages login status and displays navigation
Child apps load dynamically based on the click of the main app navigation
Deployment of logic
There are many ways to deploy, but here’s what I tried:
-
Using only one Nginx container, deploy multiple applications by listening on different ports, and add routing proxies to child applications in the ports of the primary application
This approach is the simplest but not suitable for automated deployment of Gitlab-CI/CD, so I will initially test the implementation of the nGINx deployment micro front end
-
Multiple Nginx containers are used, each container exposes a port, and the corresponding routing proxy is added to the child application through the primary application
This approach can be implemented, but multiple ports are exposed on the server, reducing security, and external applications can be accessed directly through the ports
-
With multiple Nginx containers, only the ports of the master application are exposed, and the master application communicates with the child application, which is then accessed through the Nginx proxy
Ideally, only one port is exposed, all agents are in between containers, and are insensitive to the outside world. The following is an illustration of the implementation
qiankun
Install qiankun
$ yarn add qiankun # or NPM I Qiankun-s
Copy the code
Register microapplications in the main application
import { registerMicroApps, addGlobalUncaughtErrorHandler, start } from 'qiankun';
const apps = [
{
name: 'ManageMicroApp'.entry: '/system/'.// Use //localhost for local development
container: '#frame'.activeRule: '/manage',},]/** * Register microapplication * first parameter - registration information for microapplication * second parameter - global lifecycle hook */
registerMicroApps(apps,{
// Qiankun Lifecycle Hook - micro application before loading
beforeLoad: (app: any) = > {
console.log("before load", app.name);
return Promise.resolve();
},
// Qiankun Lifecycle Hook - micro application after mount
afterMount: (app: any) = > {
console.log("after mount", app.name);
return Promise.resolve(); }});/** * Adds a global uncaught exception handler */
addGlobalUncaughtErrorHandler((event: Event | string) = > {
console.error(event);
const { message: msg } = event as any;
// Load failed
if (msg && msg.includes("died in status LOADING_SOURCE_CODE")) {
console.error("Microapplication failed to load. Please check whether the application is running."); }}); start();Copy the code
Once the url of the browser changed after the microapplication information was registered, the matching logic of qiankun would be automatically triggered. All microapplications matched by activeRule rules would be inserted into the specified Container and the lifecycle hooks exposed by the microapplication would be called in turn.
If the microapplication is not directly associated with routing, you can also manually load the microapplication:
import { loadMicroApp } from 'qiankun';
loadMicroApp({
name: 'app'.entry: '//localhost:7100'.container: '#yourContainer'});Copy the code
Micro application
The microapplication does not require any additional dependencies to be installed to access the Qiankun master application.
1. Export the appropriate lifecycle hooks
Microapplications need to export the bootstrap, mount, and unmount lifecycle hooks in their own entry JS (usually the entry JS of your webpack configuration) for the host application to call when appropriate.
import Vue from 'vue';
import VueRouter from 'vue-router';
import './public-path';
import App from './App.vue';
import routes from './routes';
import SharedModule from '@/shared';
Vue.config.productionTip = false;
let instance = null;
let router = null;
// Execute render directly if the child application is running independently
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
/** * Render functions * run in the main application lifecycle hook/run when the child application starts alone */
function render(props = {}) {
// SharedModule is used for communication between the master application and its children
// If the shared passed in is empty, use the child's own shared
// If the shared passed in is not empty, the shared passed in by the main application overloads the shared of the child application
const { shared = SharedModule.getShared() } = props;
SharedModule.overloadShared(shared);
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/manage/' : '/'.mode: 'history',
routes
});
// Mount the application
instance = new Vue({
router,
render: (h) = > h(App)
}).$mount('#app');
}
/** * Bootstrap will only be called once during the micro-application initialization. The next time the micro-application re-enters, the mount hook will be called directly. Bootstrap will not be triggered again. * This is usually where we can initialize global variables, such as application-level caches that will not be destroyed during the unmount phase. * /
export async function bootstrap() {
console.log('vue app bootstraped');
}
/** * The application calls the mount method every time it enters, and usually we trigger the application's render method here */
export async function mount(props) {
console.log('vue mount', props);
render(props);
}
/** * The method that is called each time the application is cut/unloaded, usually here we unload the application instance of the microapplication */
export async function unmount() {
console.log('vue unmount');
instance.$destroy();
instance = null;
router = null;
}
/** * Optional lifecycle hooks that only work when microapplications are loaded using loadMicroApp */
export async function update(props) {
console.log('update props', props);
}
Copy the code
There is also a public-path file referenced in the above code:
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
Copy the code
This is mainly to solve the micro application dynamic loading of scripts, styles, images and other incorrect addresses.
2. Configure the micro-application packaging tool
In addition to exposing the corresponding lifecycle hooks in the code, in order for the main application to correctly identify some of the information exposed by the micro-application, the micro-application packaging tool needs to add the following configuration:
Webpack:
const packageName = require('./package.json').name;
module.exports = {
publicPath: '/system/'.// The package address is based on the entry value registered in the main application
output: {
library: 'ManageMicroApp'.// The library name is the same as the name of the microapplication registered by the main application
libraryTarget: 'umd'.// This option attempts to expose the library to the previous module-defining system, which makes it compatible with CommonJS and AMD, or exposes it as a global variable.
jsonpFunction: `webpackJsonp_${packageName}`,}};Copy the code
Summary of Key points
-
Configuration during registration of the primary application
const apps = [ { name: 'ManageMicroApp'.entry: '/system/'.// http://localhost/system/ will point to the corresponding child application address via the nginx proxy container: '#frame'.activeRule: '/manage',},]Copy the code
When a master application registers a microapplication,
entry
Can be the relative path,activeRule
Can not andentry
Same (otherwise the main app page refresh becomes a micro app) -
Vue route base
router = new VueRouter({ base: window.__POWERED_BY_QIANKUN__ ? '/manage/' : '/'.mode: 'history', routes }); Copy the code
If it is called by the main application then the base of the route is
/manage/
-
Webpack packages the configuration
module.exports = { publicPath: '/system/'};Copy the code
for
webpack
Build microapplications, microapplicationswebpack
packagedpublicPath
Need to be configured as/system/
, otherwise microappliedindex.html
Can request correctly, but micro applicationindex.html
The inside of thejs/css
The path doesn’t take/system/
.
Now that we have the microfront-end configuration done, the next step is to configure nginx.
Nginx configuration in production environment
Let’s suspend the nginx configuration for the main application
server {
listen 80;
listen[: :] :80 default_server;
server_name localhost;
root /usr/share/nginx/html;
location / {
try_files $uri $uri/ /index.html;
index index.html;
}
# The subapplication entry we configured above is /system/, so it will trigger the proxy here, proxy to the corresponding subapplication
location /system/ {
# -e: true as long as filename exists, no matter what type filename is. He took the
if(! -e $request_filename) {proxy_pass http://192.168.1.2; The IP here is the IP of the child application docker container
}
# -f filename True if filename is a regular file
if(! -f $request_filename) {proxy_pass http://192.168.1.2;
}
# docker run nginx does not recognize localhost so this will be reported as 502
# proxy_pass http://localhost:10200/;
proxy_set_header Host $host;
}
location /api/ {
proxy_passhttp://background IP address IP/;proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
Copy the code
Let’s look at what’s applied all at once
server {
listen 80;
listen[: :] :80 default_server;
server_name_2.root /usr/share/nginx/html;
You must add allow cross-domain, otherwise the main application cannot access
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
location / {
try_files $uri $uri/ /index.html;
index index.html;
}
location /api/ {
proxy_passhttp://background IP address IP/;proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
Copy the code
Dockerfile configuration
So let’s look at what’s applied all at once
# Use nginx images directly
FROM nginx
# replace the default conf file configured above
COPY nginx.conf /etc/nginx/nginx.conf
# nginx default directory should be able to see the index.html file
COPY dist/index.html /usr/share/nginx/html/index.html
Note that all resource files must be placed in the system file index.html to load correctly
COPY dist /usr/share/nginx/html/system
Copy the code
Let’s take a look at the main application
Nginx reverse proxy/API / 404 error (unknown)
FROM centos
# to install nginx
RUN yum install -y nginx
Go to /etc/nginx
WORKDIR /etc/nginx
Replace the configuration file
COPY nginx.conf nginx.conf
Go to /usr/share/nginx/html
WORKDIR /usr/share/nginx/html
The main app is packaged normally, so just put the package in it
COPY dist .
Expose port 80
EXPOSE 80
# run nginx
CMD nginx -g "daemon off;"
Copy the code
Gitlab – ci/CD configuration
First look at a draught of application, only say the key
image: node
stages:
- install
- build
- deploy
- clear
cache:
key: modules-cache
paths:
- node_modules
- dist
Installation environment:
stage: install
tags:
- vue
script:
- npm install yarn
- yarn install
Package items:
stage: build
tags:
- vue
script:
- yarn build
Deployment project:
stage: deploy
image: docker
tags:
- vue
script:
Build a mirror of the project from dockerfile
- docker build -t rainbow-system .
Delete the container if it exists
- if [ $(docker ps -aq --filter name=rainbow-admin-system) ]; then docker rm -f rainbow-admin-system; fi
Create a rainbow-net network adapter for the container. Create a rainbow-net network adapter for the container
- docker run -d --net rainbow-net --ip 192.1681.2. --name rainbow-admin-system rainbow-system
Clean up the docker:
stage: clear
image: docker
tags:
- vue
script:
- if [ $(docker ps -aq | grep "Exited" | awk '{print $1 }') ]; then docker stop $(docker ps -a | grep "Exited" | awk '{print $1 }'); fi
- if [ $(docker ps -aq | grep "Exited" | awk '{print $1 }') ]; then docker rm $(docker ps -a | grep "Exited" | awk '{print $1 }'); fi
- if [ $(docker images | grep "none" | awk '{print $3}') ]; then docker rmi $(docker images | grep "none" | awk '{print $3}'); fi
Copy the code
Take a look at the main application again, skip the repetitions and get straight to the point
Deployment project:
stage: deploy
image: docker
tags:
- vue3
script:
- docker build -t rainbow-admin .
- if [ $(docker ps -aq --filter name=rainbow-admin-main) ]; then docker rm -f rainbow-admin-main; fi
Give the container a network card, rainbow-net, and give it an IP address. Then connect to the child application created by --link.
- docker run -d -p 80: 80 --net rainbow-net --ip 192.1681.1. --link 192.1681.2. --name rainbow-admin-main rainbow-admin
Copy the code
The following command is generated for the docker custom nic:
$docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 rainbow-netCopy the code
conclusion
Up to now, we have achieved the automatic deployment of Qiankun + Docker with Gitlab-CI/CD. We encountered many difficulties in the process and came up with a relatively reasonable solution. Welcome to discuss any questions.