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