Hello, I’m red rabbit front the front end of the studio architect, ready to share a company project under the front end of the architectural design, to share the contents of the following is about how I to implement support for the business system, but the content is very complex, suitable for students in different stages of the watch, how to share, you have what idea can leave a message.

I will elaborate from the following points:

1. Problems with the original project design

2. How to optimize the architecture

3. How to implement the plug-in

Original project design problems

This is also A common problem in front end, mainly for small and medium-sized companies. For this branch, Internet resources are limited, so the development quality is not uniform. For example, at the beginning of the project, company A started with A vue create XXX or creation-React-app. Then the next step, the whole family is installed, a front-end project is set up, of course, there is no problem with this development method, after all, both vuE-CLI and create-React-app are official written development tools that can be applied to most projects. It’s been proven by a lot of groups, but not in the project I’m working on.

The original code of the project I took over was built with create-React-app (students using VUE should not worry, because the following content is not targeted at the framework type). Once the project can run the business normally, there is no problem, but when it is combined with the business type, there will be problems.

Graph one:

Refer to Figure 1. The business system at the bottom is the original project, which is actually a management system created using create-react-app. Therefore, there is nothing to say about the project structure. At the beginning, the project was only used within the company. However, with the development of business, for example, customer company B needs to purchase or enter into our business system, then the system cannot serve only one company. Besides B, there will even be C or D companies, which has a taste of SAAS

Before I took over have been developed to a service, the two companies as two independent companies must differ in terms of business, the demand for some function module is different also, so I need within the project ADAPTS to different company, the current implementation scheme is through the domain name and user information, this way, can only say that eke out, if the C, D companies access to come in, The result would be unmaintainable.

Actually here you should think of the solution, for every customer to create a separate project is finished, then the common parts of the component to pull away, and, indeed, this is a very perfect is very general, but can not so simple to implement, not taken would be a need to change multiple projects at the same time, After all, business-related components change with the business.

How to Optimize the Architecture

Based on the above analysis, we can conclude two points: first, an application framework is needed for unified project management; second, unified project dependency management, such as third-party package version and so on. So there are the following application framework design

Figure 2:

Let’s call it eBuild for now, but here’s a brief explanation of what each module does.

core

Core mainly contains some necessary third-party packages, the list is as follows:

React // react-router-dom // react-router-dom // mobx // redux class // vuex mobx-react class // react-classnames // style handlingCopy the code

This is to unify the third-party package version management, and the project does not need to install each package separately when relying on it, just need NPM install@ebuild /core, simple, convenient and practical.

In addition to the third party package above, there is also an important createApp module. Before designing this module, our entry file code looked like this

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

ReactDOM.render(
    <App />.document.getElementById('root'))Copy the code

I want every single page application to have a global app that contributes the associated context. Here’s how to implement createApp

import { createApp } from '@xsbuild/core'
import routes from './router'

const app = createApp({
  el: '#root'
})

app.setRouter(routes)

app.render(props= > {
  return <app.rootView {. props} / >
})

Copy the code

For those of you who think this code looks like Vue, there are a lot of differences, which I’ll explain later, but it’s worth mentioning that this is a “multi-page application framework”.

CreateApp partial code

class Application extends ApplicationRender {
	constructor(options) {
		super(options);
	}
	setMeta(data) {}
	removeMeta() {}
	_setMetaByLocal(data) {}
	setRouter(routes) {}
	setSubRouter(routes) {}
	setStore(store) {}
	setRedirectWith401(callback) {}
	render(view) {
		if(typeof view === "function") {
			super.render(view); }}}function createApp(opts) {
	return newApplication({ ... opts }); }export default createApp;
Copy the code

CLI

The CLI is used to manage projects

  1. ebuild project
  2. ebuild app
  3. ebuild create:plugin
  4. ebuild use:plugin

These commands are similar to those used by vue create or create-react-app to generate projects directly, but each command has a different function and the implementation scheme is yeoman.

Project implementation code:

const Generator = require("yeoman-generator");
const path = require("path")

class ProjectGenerator extends Generator {
    constructor(args, opts) {
		super(args, opts);
		this._setupGenerator()
	}

	_setupGenerator() {
		this.argument("name", {
			type: String.required: false.description: "Project Name"
		})

		this.option("description", {
			type: String.description: "Project Description"}); }async setOptions() {
		this.projectInfo = {};
		["name"."description"].forEach(v= > {
			if (this.options[v]) {
				this.projectInfo[v] = this.options[v]
			}
		});
	}

	promptProjectName() {
		const prompts = [
			{
				type: "input".name: "name".message: "Project Name :".when: this.options.name == null.default: "xsbuild-start"
			},
			{
				type: "input".name: "description".message: "Project Description:.when: this.options.description == null.default: this.options.name || ' ',},];return this.prompt(prompts).then(props= > {
			Object.assign(this.projectInfo, props);
		});
	}

	scaffold() {
		this.destinationRoot(this.projectInfo.name);

		this.fs.copyTpl(
			path.resolve(__dirname, '.. /templates/project/**'),
			this.destinationPath(""),
			{
				project: this.projectInfo,
			},
			{},
			{
				processDestinationPath: destPath= > destPath,
				globOptions: {
					dot: true.nobrace: true.noext: true,}},)this.fs.move(
            this.destinationPath(`package.json.ejs`),
            this.destinationPath(`package.json`));this.fs.move(
            this.destinationPath(`.gitignore.ejs`),
            this.destinationPath(`.gitignore`)); }async end() {
		this.log();
		this.log(`The ${this.projectInfo.name}Project created successfully);
		this.log();
		this.log("Next step :");
		this.log();
		this.log("$ cd " + this.projectInfo.name);
		this.log(`npm install or yarn install`);
		this.log(`npm run start`);
		this.log(); }}module.exports = ProjectGenerator
Copy the code

CLI-SERVER

Cli-server implementation is a little more complicated, mainly because it is between Webpack and Vite. I finally choose Vite. First of all, I will tell you why I choose Vite, actually the word “fast”, Vite is 10-100 times faster than WebPack + Babel. Specific students can refer to the explanation of vite official document, but many are introduced here, but fast to fast but the downside is obvious, with relatively less people temporarily pit should be less, and less solution, simply webpack plug-ins are brought the box, but when I use vite implement this framework, you’ve wrote four plug-ins.

Back to the CLI-server, its main function is to manage the development,build,test, and deployment scripts. Here I implement dev,build,test, and deploy respectively, and read a custom configuration file in the project as a dependency, such as a configuration file named ebuild.config.js. Here are some explanations of the configuration items

module.exports = {
    // Head configuration for all pages
	appGlobal: {
		title: "--title--".links: [].scripts: [].meta: [{name: "description".value: "--description--"}],},// The development directory where the apps are stored
    appRoot: "pages"
    // Multi-page app configuration, where each item is a single page app
	apps: {
		index: {
			title: "--index--",}},/ / the plugin
	plugins: {},
    // The original Vite configuration item
	viteConfig(source) {
		return{... source }; }};Copy the code

other

Note that helper is just a repository for common utility functions.

How to implement plug-ins

We have a unified application framework, but the problem we discussed at the beginning seems to be unresolved, that is, the problem of components, application framework is only convenient for us to manage multiple projects, but some specific business components in it is another matter, here we will try to find a solution according to the actual situation.

Since our project needs to adapt to multiple customers, after the multi-page design, is it possible to take out a large function module and make it into an independent page? Then, it can be installed into the project when needed, which can be granulated, disassembled and configurable, so we have the following plug-in design ideas.

Figure 3:

The plug-in is developed separately and released to the NPM warehouse after development. Any project that needs to be used can be directly installed and used by modifying the configuration. The effect is as follows:

Figure 4:

This layout as an independent component, other functional modules removed into plug-in development, the above is the realization of the whole architecture.

Final project code address: github.com/hsian/ebuil…