This article focuses on some lessons learned in front-end refactoring: Think big, start small! If your project uses front-end templates, back-end routing + rendering, this article may not be appropriate. This article focuses on SPA-type front-end applications. Creating a front-end application generally involves a number of processes
Note: It may be a mistake to put testing after development, and front-end developers need to pay attention to testing as well
This article focuses on some of the details of the development phase. A project run typically involves the following steps
1. What do you usually do to start an application
A. Load monitoring
Monitoring is the last link, but it needs to be used as soon as the application is initialized
b. tracking
Burial points are often used as an important basis for business analysis,
Rule 1: The default value is greater than the configured value
We should use a uniform approach, such as event broker, to send data to the necessary burial points
const eventWhiteList = ['InputItem-module'.'AuthButton-module']
window.addEventListener("mouseup".function (e) {
if(e.target) {
/ / find the classlist
const classList = e.target.classList;
let moduleClassName = null
for(let i=0; i< classList.length; i++) {
if(classList[i].indexOf("-module__") > 0) {
moduleClassName = classList[i]
break; }}if(moduleClassName && !new RegExp("^"+eventWhiteList.join(") | (") +")").test(moduleClassName)) {
// Intercepts the hash portion of the class in addition to the identifying content
moduleClassName = moduleClassName.replace(/ ___. + /."")
let innerText = e.target.innerText || ""
Tracking.trackEvent('click', {'lmt-track-id': moduleClassName, 'activity': innerText.substr(0.30})}}}, {capture: true})
Copy the code
This is a default event sending code based on the taro Settings
As mentioned above, we need to find some regular behaviors according to our own usage framework, analyze the behaviors, extract the parts that can be identified as tracking, and create data
2. What is a global interceptor
Mainly used to deal with some interception judgments that must be made before route initialization
3. Initialize the route
Why list route initializers separately? Is it just a route definition? Of course not!
Because of spA-like applications, routing is brought to the front, so we need to consider some side effects of route switching
Principle 2: Page isolation
Back-end route switching often drives front-end page refresh, and front-end route adjustment is only the change of address.
We need to destroy the page information at the necessary nodes to prevent side effects on the next page
Important: the destruction of timer must be noted
AbortController is AbortController. AbortController is AbortController. AbortController is AbortController. Add a signal to resolve and reject, and programmatically set it so that no response is processed if signal is triggered
For example, director based routing, we can write this
// routeConfig is the list of routes defined
var r = new director.Router (routeConfig).configure ({
html5history:!!!!! routerInc["settings"] ["enableHistory"].run_in_init: true.convert_hash_in_init: true.before: function () {
// Clean up the previous page
if (routerInc && routerInc.page ()) {
routerInc.page ().isLoading (true);
routerInc.page ().cancelXhr();
routerInc.page ().modal.distoryModal();
routerInc.clearPage();
}
varcurUrl = !! routerInc["settings"] ["enableHistory"]?window.location.href.replace(window.location.protocol+"/ /"+window.location.host,"") : (this.delimiter+this.getRoute().join(this.delimiter);
logger.debug ("all--------route---before");
return ic.doInterceptor(curUrl, routerInc);
},
notfound: function () {
// There is no route
// In case you cannot return to the previous step, change to replace the current page directly
try{
routerInc.page ().notFound();
}catch (e){
routerInc.redirect ("/notfound"); logger.error(e); }}});Copy the code
In the code above, there is an ic.doInterceptor, which is a filter calling class that does some synchronous filtering in routes
As shown in the figure above, the login page is directly redirected from a URL. These functions can be implemented using filters or responsibility chains
Note: Design patterns provide targets for refactoring behavior
4. Go to the initialization page
In this section, we should focus on the development of the business. In this phase, we usually focus on the following points
Principle 3: Focus on the business
A. Parameter transfer
Parameters such as /pay/:type type are route parameters
②. Search parameters such as /pay/online? orderid=xxxx
③. Parameter transfer between components uses memory to transfer parameters, which is mainly used to solve the situation of large data volume
B. Parameter security
Some pages will directly display the contents of the search on the page, such as some results page, accept the parameters of the background, display to the user
This is where YOU need to prevent XSS
Refer to the queryString in Get URL
C. Prevent user serial numbers
When a user logs in to different accounts on the same browser’s multi-tab page, it needs to be prompted
For details, see How to avoid the problem of string users caused by TAB login in multiple browsers in SPA applications
D. Consistency of ajax or FETCH behavior
There are three main categories: loading, caching, and result transformation
Generic content should be encapsulated in the framework, and uniform coding behavior is always more difficult than framework implementation
There is usually such logic. I judge whether to enter page B through the interface on page A and enter directly without preventing users from manually entering the address of page B. We will conduct interface judgment on both pages A and B
This is where the ability to cache for a few seconds is very useful
5. Page-level filters
It is used to filter asynchronously before page objects are initialized. The filtering process is performed according to the service objects on the current page
You can use the Promise implementation, and the reference implementation is shown below
/** * defines a public filter * for handling some common page blocking logic * @param resolve * @param Rejected */
var filter = function (resolve, reject) {
let excutors = []
// Define your filters in the following order
new Filter1(excutors, promiseQueue);
new Filter2(excutors, promiseQueue);
new Filter3(excutors, promiseQueue);
promiseQueue(excutors).then((a)= > {
resolve(true)
}).catch(cb= > {
reject(cb)
})
}
function promiseQueue (executors, letGo) {
return new ES6Promise((resolve, reject) = > {
if (!Array.isArray(executors)) {
executors = Array.from(executors)
}
if (executors.length <= 0) {
return resolve([])
}
var res = []
executors = executors.map((x, i) = >() = > {var p = typeof x === 'function' ? new ES6Promise(x) : ES6Promise.resolve(x)
p.then(response= > {
res[i] = response
if (i === executors.length - 1) {
resolve(res)
} else {
executors[i + 1]()
}
}, reject)
})
executors[0] ()if (letGo) {
resolve(true)}}}Copy the code
6. Configuration management
Configuration management is an elegant and indispensable feature when we launch a feature that is full of uncertainty or complexity, changes based on the environment, or otherwise requires configuration of switch variables
Rule 4: Configure more than hard code
7. The mock configuration
We define a set of flexible mock mechanisms based on YAPI and configuration management. First, all API definitions of the project have a constant class that integrates mock by defining a unified API access method
var getApi = function(url, obj){
var path = $.extend({}, apis, mock)[url] || url;
if(obj){
for(var o in obj){
path = path.replace(":"+o,obj[o]); }}/ / handle the mock
if(settings.mockConfig && settings.mockConfig.enable && $.inArray(url, mockPaths || []) >= 0){
path = (mockPrefix||"") + path
}
return path;
}
Copy the code
And then with nginx or Node forward