background
When I joined a new company after the New Year, I vaguely remember that the interviewer told me that the company’s technology stack was React. However, I found that I was mainly responsible for the project using jQuery after I joined the company (it seems that the project was bought at the beginning, and it was one of the major profitable projects). This project designed a set of MVC framework based on jQuery, but there is no document for this framework, I don’t know whether it was lost in the process of repeated handover. The DESIGN of the MVC framework itself is fine, but it is not suitable for building large applications (perhaps not expected to be as large as it is now, plus the rapid growth of the front-end three over the years, DOM manipulation is clearly not suitable for the current scenario). At the same time, there was no internal specification to constrain the code, and the project became a typical Stonehenge application.
The company itself is mainly engaged in e-commerce business, so this project must distinguish between different platforms. As we all know, there are so many e-commerce platforms now, which has spawned many different projects, such as ABCDE. As a result, there is a lot of redundant code left in the project. In addition, tools like ESLint are not used to standardize the code format, and there are few code comments, which increase the maintenance difficulty of the whole project. Since it is a business project, the business requirements will never stop, that is to say, it is impossible to stop the business iteration and give you a few months to refactor all the projects, which will cause more losses than I can afford. This is where the need for refactoring comes in. After all, refactoring can help improve work efficiency and reduce labor costs, and it won’t scare off new employees. Although it is not up to me to decide whether to reconstruct or not, I can promote the reconstruction through communication with the supervisor. The supervisor seems to have no better plan for the reconstruction, otherwise, I would not have left it untouched for so long. Based on some practical problems mentioned above, a modular segmentation and continuous reconstruction scheme is needed. In line with the principle of pursuing the right position, I came up with two plans:
- By custom
webpack plugin
In this case, the old and new projects are in the same folder. After the reconstruction is completed, some fragments of the old projects are easy to be generated. - By starting a third project to achieve the aggregation of the resource files of the new project and the old project, the new and old projects can be better separated without additional fragments.
In fact, I wanted to implement it through plan 1 at first, but during the implementation process, on the one hand, I found that there was very little content about plugin on the official website, especially the hooks parameter, which was not specified at all, so I was forced to shed tears without technology. On the other hand, I find that if you write old and new projects together, there may be some fragments of code or fragments of files (especially remnants of the old project) during the refactoring process, which in my opinion is an incomplete refactoring because there are traces of the old project. So when I found that all options were not a good option, I chose the second option, which is now completed, and I’ll call it the loading center for now.
Git address: click here to jump to
Load center
The loading center is responsible for loading and aggregating various projects (or resources), and can be used not only for aggregating old and new projects, but also for aggregating sub-projects. Those of you who have learned about the micro front end at this time should easily think that this is actually a way to realize the micro front end, but my scheme is lighter and more suitable for the scene of project reconstruction (please allow me to feel so shameless). In terms of concrete implementation, there is also a reference to the implementation of the micro front-end framework Qiankun, feeling that qiankun should be a relatively good design of a micro front-end scheme, from cloud users affirmation. After a brief introduction, we are going to talk about some specific use methods.
File directory
-- config config folder -- dist package directory -- loaders custom loader for automatic loading of config contents -- SRC internal implementation, Ejs -- index.js -- package.json -- webpack.config.jsCopy the code
Using a configuration
In the config folder, write the configuration file of the project we need to load. This file should be exported as an object. For the specific configuration properties, see the sample file old.js in the config folder. Comments have been written full, just follow the configuration. If you want to load any new projects or resources, just add a new configuration file under the Config folder. If root is specified, make sure that index.ejs has a corresponding element.
export default {
// The project name, as the unique identifier of the current configuration item, can not be specified, because this configuration item is not actually needed, more for the purpose of comment
name: 'old'.// Mandatory, specify the type to load, be resource or project, enumeration values: project, resource
type: 'project'.// Specify a root element to which the HTML elements of the current project will be loaded
root: '#oldApp'.// Mandatory, load priority, small number indicates high priority
priority: 1.// This parameter is mandatory when type === project, because a layer of proxy is required to obtain the main page content of the target project by making a cross-domain request
proxyUrl: './getOldHtml/index.html'.// Optional, valid when type === resource, js file to load
scripts: [],
// Optional, valid when type === resource, style file to load
styles: [],
// Optional. Resources that match the regular match will not be loaded
exceptResourceRule: /^a.js$/.// This parameter is optional. If specified, the prefix will be added to all resource files (not a fully linked file address, i.e. files that do not contain the domain). Generally, it is the proxy address
prefixUrl: './getOldHtml'.// The current project js file is loaded before the hook
beforeLoad() {
// ...
},
// All js files of the current project (excluding inline js) are loaded with post-loading hooks
afterLoad() {
// ...
},
// Optional, routing rule. Root element is displayed only under the current route
// If not specified, the root element will be displayed in any case
// If this parameter is specified, the root element will be displayed only if the route matches, and display: None will be set to the root element if the route does not match
routesRule: /^printBatch$/.// In sandbox mode, this configuration item will run the matching js files under the current project configuration under a separate Window object
// In general, it is not recommended to use it. If you use something other than global variables, it may cause some unexpected errors
// We are considering whether we can maintain a set of properties that access the real window object when accessing certain properties under window
useSandbox: false.// Set useSandbox to true
sandboxRule: /1.js/,}Copy the code
EventBus
Mount two properties under the current window: window. $SyncEventBus and Window. $AsyncEventBus. These are two event bus systems. The difference is that the event can be fired synchronously or asynchronously. The following methods are provided:
On (eventName, FN, isMulityListers: Boolean): Listens for an event. The third parameter indicates that the event can have multiple listeners
Emit (EventName, ParAM): Triggers an event
off(eventname, … Fn): Unlisten some functions
Once (eventname, fn): an event is triggered only once. After the event is triggered, the eventname is removed. Multiple listener functions are not allowed
OffAll (eventName): Cancels all listener functions under the name of the current event
Clear (): Removes all event listening
As for this event system, my design suggests a one-to-one relationship, that is, one event name corresponds to one response function. This is designed to reduce the number of hidden bugs. Imagine A situation where program A listens for an event in A component. The event name is event and the response function is fnA. Program B also listens for events in another component, and the response function is fnB. In fact, this is just an occasional naming conflict, but it’s easy to have hidden bugs because both fnA and fnB are triggered, and if it’s a problem that isn’t intuitively obvious, it can be difficult to rule it out. Through this one – to – one approach, this situation will be fundamentally ruled out. But what if I need a one-to-many relationship? It doesn’t matter. When the event is listening, the third argument passed in is true which is a one-to-many relationship.
request
When the loading center obtains the page of the target project, it uses FETCH. If it thinks there may be compatibility problems, it can use traditional Ajax instead and modify the SRC/Request file.
Introduction of the principle
Obtain the HTML file of the target by making an HTTP request. Then extract THE JS and CSS resources in the HTML file through import-html-entry and introduce them into the new project. For CSS, this is done by creating link tags that are inserted into the document in turn. For js files, because of sandbox processing, the string content of the object file is first obtained through HTTP request, and then created and executed through new Function(). When sandbox mode is needed, the window is changed by creating a self-executing anonymous function.
(function(window) {
// Insert the requested js content here
})(newWindow)
Copy the code
Routing control is relatively simple, because the hash route currently used by the company only supports hash mode. The root element is displayed only for matched routes, and display: None is set to display if not matched.
Specific code implementation, or recommended to go to Github view: click this jump, the amount of code is not a lot, notes are also written quite a lot, should be easier to understand, HHH.
Simple summary
The specific direction of reconstruction is to first reconstruct the basic modules (such as project infrastructure and user authorization), and then start from modules with relatively small business volume and gradually expand to large modules. Because reconstruction takes a long time, this solution does not cause service content to be left behind and synchronizes service advancement and reconstruction. However, I think what’s really needed is not refactoring, but a sense of how to write good code. Otherwise, maybe in a year or two it will become a boulder app again?
If you think it’ll help, just give it a thumbs up. If you have a better solution for refactoring, please share it.