preface

I don’t know if you have encountered such a heart-piercing scene in the development process: the project runs perfect locally and passes the self-test perfectly! He could not help raising his mouth confidently with the test students said: “brother! Leave work early today. There will be no serious bugs on this project.”

And then I went back to my seat to launch my test suit, and I was just about to pack up and go home, and I just had to check everything was working, Nani! The resulting page is blank, and the console does not throw any valuable exception messages! Refresh, clear the cache, republish and try, blank again! What? Suddenly feel face some pain hot! There are ten thousand animals in my chest!

In my years of work, no matter in the development of simple active page or complex dynamic form interaction, I have always encountered some baffling bugs (maybe because I am too busy), and repeatedly encountered the same type of bugs, such as cross-domain exceptions. The author once encountered 6 cross-domain bugs in one project! Can you imagine how I must have felt?

After a large number of BUG beatings, I decided to reflect on the past, corrected, spent time to sort out and review the bugs encountered, summed up a set of BUG solving thinking model, as long as there is a BUG, I can quickly solve the problem as long as I take a seat.

Since the author’s main technology stack is VUE, this article involves a large number of VUe-related exceptions. The following content does not strictly distinguish between errors and exceptions. If I feel misled, I’m sorry! I will try my best to improve my cognitive level. (smiling. JPG)

Common Front-end exceptions

Common front-end exception classification

  • JS syntax errors and exceptions
  • AJAX request exception
  • Static resource loading is abnormal
  • Abnormal Promise
  • The Iframe abnormal
  • Cross-domain Script error
  • Crash and jam
  • Vue abnormal

This section lists common JS syntax errors and exceptions, AJAX request span exceptions, and Vue exceptions.

JS syntax errors and exceptions

RangeError: Flags an error that is triggered when a value is set outside the corresponding range

Eg: Uncaught RangeError: Maximum Call stack size exceeded eg: Recursive call without termination until browser stack burst throws an exception

function recursion(){
 console.log(1)
 recursion()
}
recursion()
Copy the code

ReferenceError: A reference type error that occurs when a variable that does not exist is referenced

eg:

console.log(d) 
Copy the code

Uncaught ReferenceError: d is not defined

SyntaxError: indicates a SyntaxError

eg:

 if() {Copy the code

Uncaught SyntaxError: Unexpected token ‘{‘

TypeError: indicates an error that occurs when the value type is not the expected type

eg:

console.log(a.c)
Copy the code

Uncaught TypeError: Cannot read property ‘c’ of undefined

AJAX request exception

Ajax requests are divided into stateful exceptions and stateless exceptions: stateful exceptions usually include server exception 5XX, client exception 4XX; Stateless exceptions are generally network exceptions (no response).

When there is an error on the server side, the default return is 500 for the backend students to check the code, and the resource can not locate the browser to return 404, the front end to check whether the path is correct, or according to the agreement to return 401 on behalf of identity authentication expired, and so on, here I focus on the network abnormal without status.

Network exception Rule out network problem request timeout, then encounter the most likely problem is cross-domain problems, let you review the following let me spit rainbow 6 cross-domain exceptions!

Cross request exception

Methods to solve cross-domain request include JSONP, NGINx proxy, CORS, IFrame, etc. Generally, we choose CORS (cross-source resource sharing) to deal with cross-domain, because other things will have certain disadvantages. The following are the problems and solutions encountered when using CORS to solve cross-domain request.

You might think that setting the access-Control-Allow-Origin: http://xxxx (your website’s domain name) header on the server would solve the cross-domain problem, but it’s not that simple.

The withCredentials configuration is abnormal

WithCredentials specifies whether to allow requests to send cookies and HTTP authentication information. When withCredentials = true is configured on the current end, the following exceptions are reported:

Cause: When withCredentials = true is configured on the current end, access-Control-allow-origin cannot be * and must be a specific domain name address.

Note: For withCredentials = true to take effect, the access-Control-allow-credentials: true must be configured on the backend

Custom request header exceptions and contentType exceptions

The reason: Cors policy requests are classified into simple requests and non-simple requests. When the request method is PUT or DELETE, or the content-Type field of the request is Application/JSON, or the request header is customized, Common custom authentication Authorization request headers are complex requests.

The browser will issue a precheck options request and throw an exception if the server does not configure the appropriate request header. For more details, please see Ruan Yifeng’s cross-domain resource sharing CORS.

Solution: Set access-Control-allow-methods: GET,POST,PUT,DELETE access-Control-allow-headers: content-type,Authorization on the server

Earlier versions do not support the wildcard * character

Cause: Wildcard * was not supported until 2017, which caused an exception in earlier versions of browsers.

Solution: Set * to the specific request domain name, request header, or request method.

Repeated proxy exception

Cause: Access-Control-allow-origin: * set twice, probably once in code, and once again in Nginx.

Access-control-allow-origin: * configure access-Control-allow-origin: *

Vue abnormal

Vue exceptions here include VUE, VUE-CLI and VUe-Router bucket related exceptions. There are more bugs than the ones listed below. I will collect more bugs and update them periodically.

Vue – cli exception

  • The background path is incorrect after vuE-CLI2 is released

RootPublicPath: process.env.node_env === ‘production’? ‘./’ : ‘/’ If you publish static resources not in the root directory but in the/XXX/XXX file in the root directory, change the ‘/’ to/XXX/XXX

  • Vue-cli4 package file ES6 is not translated into ES5, resulting in incompatibility issues in earlier versions

The reason: NODE_ENV = ‘test’; NODE_ENV = ‘production’; The CLI only recognizes NODE_ENV = ‘production’ and sets the condition as production environment. .env.test set NODE_ENV = ‘test’ to NODE_ENV = ‘production’

  • Env file custom variables do not take effect

Solution: Custom variables must start with VUE_APP_.

Vue, the router

  • An infinite loop occurs when route. beforeEach is used for route interception. The Maximum Call stack size exceeded

Solution: Before running the next command, check whether the current route is the route to be jumped. If yes, do not run the next command.

router.beforeEach((to, from, next) = > {
  if (localStorage.getItem('token')) { // If you have logged in
    next();
  } else {
    if (to.path === '/login') { // Next ()
      next();
    } else { // Otherwise, go to the login page
      next({
        path: '/login'}); }}});Copy the code
  • Keep – live not to take effect
<keep-alive >
    <router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if=! "" $route.meta.keepAlive"/>
Copy the code

Solution: The route is nested in multiple layers, and the keep-live should be nested in the router-view of the correct component.

  • Include /exclude does not take effect for the keep-alive component
<keep-alive include="a">
<router-view />      
</keep-alive>
Copy the code

Cause: A is the component name, not the route name.

Vue abnormal

  • Properties not declared in a data object cannot be dynamically rendered

Set (this,obj,a,2).

  • Duplicate keys detected: ‘0’. This may cause an update error

Cause: The key value of v-for is duplicate. Solution: Use ID as key.

  • When the child component receives the props object with a new property, the parent component’s object is somehow changed

This. Info = this.childUserInfo. If the info changes, the parent userInfo will change with it. Parse (json.stringfy (this.childUserInfo))

  • Child component Mounted function cannot get object after parent component mounted function changed

Eg:

The parent componentmounted(){
 this.commit('updateUserInfo', {name: XXX})} subcomponentmounted(){
 console.log(this.state.userInfo) // The latest value of userInfo is not available here
}
Copy the code

Component rendering cycle Parent beforeCreate-> Parent created-> parent beforeMount-> child beforeCreate-> child created-> child beforeMount-> Child Mounted -> Parent Mounted Add v-if=” this.state.userinfo.name “to the subcomponent and render the subcomponent after the data has been retrieved.

  • An error occurred with attributes assigned to computed

 

Scenario: If an attribute changes depending on an attribute, the following userName depends on the phone attribute, but userName can be assigned

 computed: {
    userName(){return this.phone}
    }
  
Copy the code

The first response is to add a setter for userName like this:

 computed: {
   userName: {
      get() {
        return this.phone ;
      },
      set(val) {
        this.userName = val; }}}Copy the code

And then tragedy!

Correct posture is: use watch to listen.

  watch:{
    'phone'(val){
     this.userName = val
    }
  }
Copy the code

(VUE exceptions will be updated irregularly…)

Abnormal troubleshooting routine

Common error scenarios and solutions

Scene 1: Somehow the page is missing or the modification doesn’t work. Do not doubt that 100% is the wrong file or wrong service.

Scenario 2: Run the test locally without any exception, and there are various bugs after it goes online. Solution:

  • Whether the empty object is processed;
  • Whether the interface returns a consistent data structure (field name/type);
  • Is the test code not removed (extra return)?
  • Are the tests in place?

Scenario 3: An error occurs after a slight modification, and the error message cannot be located. Solution: whether the execution order is correct, whether to rely on the parameters obtained, can be used to delete the code legal bit problem.

Scenario 4: An IF event cannot be triggered. For example, a pop-up event cannot be triggered. If you are sure that the default if condition is met, remember to look at the if code logic for errors.

Scenario 5: When an online emergency occurs, for example, one page can obtain the route parameter userId, but another page cannot. Solution: Normally, we would compare the two pages of code to find the answer. It would take half a day to find the answer, so we can start at the source, which logic would change the route or which logic would change the userId.

Scenario 6: The local server runs properly, but the test server releases the code successfully but it does not take effect. Don’t suspect that there is a problem with the publishing system. If your code is not written properly, add console.log to try it.

Start troubleshooting by throwing exception information

When we encounter an exception during the operation of the project, we can usually locate the exception location by using the exception information thrown by the browser. For example, vue’s exception is shown in the following figure, and the error file is obviously SignIn. Vue can locate the exception location by clicking the link:



But in a production environment if we configure the productionSourceMap: false, we will see it is exception information

Can click the link to see is compiled and compressed code, very crash there, don’t worry there is a way to solve the following.

Click the format code button on the console,

Something amazing happened! You can easily identify a created hook error by looking at the yellow background exception code and its context code to see which file the exception is in.

conclusion

If there is an exception, first from the error information mining valuable information, if the error information does not help, then you can start from the debugger, and then remove the code, the ultimate method is’ restart ‘.

BUG harvester (exception monitoring)

Exception monitoring is simple, with the following three events capturing more than 70% of exceptions, but writing an error-burying SDK requires consideration of what data to collect and how to integrate it. Refer to article: How to handle front-end exceptions gracefully? , abnormal monitoring, recommend interested students to see the original text.

Simple exception catching

// Catch js exceptions and resource loading exceptions
window.addEventListener('error'.(error) = > {
  console.log('error',error)
  return true
},true) 
// Catch vUE exception
Vue.config.errorHandler = (err, vm, info) = > {
console.log('error',err, vm, info)
}
// Catch the Promise exception
window.addEventListener("unhandledrejection".(e) = >{
  console.log(e.reason)
});
// Create an IMG tag for data reporting, because ajax can also have exceptions.
 report(url, dataStr) {
    if (Math.random() < 0.3) { // Collect only 30%
      new Image().src = `${url}? logs=${dataStr}`; }}Copy the code

Complete anomaly buried point SDK

Monitoring of the base class

// Monitor the base class
export default class BaseMonitor {
  constructor(params) {
    this.category = ' '; / / classification
    this.filename = ' '; // Error file
    this.timeStamp = ' '; // Access the timestamp
    this.userAgent = navigator.userAgent; / / the client
    this.msg = ' '; // Error message
    this.type = ' '; // Error type
    this.postion = ' '; / / position
    this.stack = ' '; / / stack
    this.selector = ' '; / / selector
    this.reportUrl = params.reportUrl;
  }
  report(url, dataStr) {
    try {
      if (Math.random() < 0.3) {
        new Image().src = `${url}? logs=${dataStr}`; }}catch (error) {}
  }
  recordError() {
    let data = this.handleErrorInfo();
    this.report(this.reportUrl, data);
  }
  handleErrorInfo() {
    try {
      let message = ' ';
      if (this.type === 'ajaxError') {
        message = ` categories:The ${this.category}\r\n Exception type:The ${this.type}\r\n Log Message:The ${this.msg}\r\n
      url:The ${this.url}\r\n
      status :The ${this.status}\r\n
      statusText:The ${this.statusText}\r\n Client:The ${this.userAgent}\r\n
      `;
      } else {
        message = ` categories:The ${this.category}\r\n Exception type:The ${this.type}\r\n Log Message:The ${this.msg}The \ r \ n position:The ${this.postion}\r\n File name:The ${this.filename}The \ r \ n the stack:The ${this.stack}\r\n Client:The ${this.userAgent}\r\n
      `;
      }
      this.selector && (message += 'selector:The ${this.selector}`);
      console.log(message);
      return message;
    } catch (err) {
      console.log(err); }}}Copy the code

Get js and resource load exception classes

import BaseError from './base.js';
import getLastEvent from '.. /libs/getLastEvent';
import { getSelector } from '.. /libs/utils';

/* Catch js exceptions and resource loading exceptions */
export default class JsResourceError extends BaseError {
  constructor(params) {
    super(params);
  }
  /* Handle js and resource loading exceptions */
  hanleError() {
    window.addEventListener(
      'error'.(event) = > {
        try {
          console.log('event', event);
          let target = event.target;
          let isElementTarget =
            target instanceof HTMLScriptElement ||
            target instanceof HTMLLinkElement ||
            target instanceof HTMLImageElement;
          if (isElementTarget) {
            // Resource loading error
            this.type = 'resourceError'; // Error type
            this.filename = target.src || target.href;
            this.tagName = target.tagName;
            this.selector = getSelector(target)
          } else {
            let lastEvent = getLastEvent();
            this.msg = event.error.message; // Error message
            this.type = 'jsError'; // Error type
            this.postion = event.lineno + ', ' + event.colno; / / position
            this.filename = event.filename;
            this.stack = event.error.stack;
            this.selector = lastEvent ? getSelector(lastEvent.path) : ' '; / / selector
          }    
          this.category = 'stability';
          this.timeStamp = event.timeStamp; // Access the timestamp
          this.recordError();
        } catch (error) {
          console.log('Resource load collection exception', error);
        }
        if(! event)return;
      },
      true); }}Copy the code

Catch the Promise exception class

import BaseError from './base.js';
import getLastEvent from '.. /libs/getLastEvent';
import { getSelector } from '.. /libs/utils';
/* Catch the Promise exception */
export default class ResourceError extends BaseError {
  constructor(params) {
    super(params);
  }
  /* Handle promise exceptions */
  hanleError() {
    window.addEventListener('unhandledrejection'.(event) = > {
      try {
        console.log('event', event);
        if(! event || ! event.reason) {return;
        }
        let lastEvent = getLastEvent();
        this.category = 'stability';
        this.timeStamp = event.timeStamp; // Access the timestamp
        this.type = 'promiseError'; // Error type
        this.selector = lastEvent ? getSelector(lastEvent.path) : ' '; // selector ERROR';
        let reason=event.reason
        if (typeof reason === 'string') {
          this.msg = reason;
        } else {
          this.msg = reason.message
          this.stack = reason.stack
          // at http://localhost:8080/:22:17
          let matchfile = reason.stack.match(/at\s+(.+):(\d+):(\d+)/)
          this.filename = matchfile[1]
          this.postion = matchfile[2] + ', ' + matchfile[3]}this.recordError();
      } catch (error) {
        console.log('promise anomalies'+ error); }}); }}Copy the code

Catch Ajax exception classes

import BaseError from './base.js';

/* Catch ajax exceptions */
export default class AjaxError extends BaseError {
  constructor(params) {
    super(params);
  }
  /* Handle ajax exceptions */
  hanleError() {
    if (!window.XMLHttpRequest) return;
    let xhrSend = XMLHttpRequest.prototype.send;
    let _handler = (event) = > {
       console.log('ajaxevent',event)
      try {
        this.category = 'stability';
        this.type = 'ajaxError'; // Error type
        this.msg = event.target.response;
        this.url = event.target.responseURL;
        this.status = event.target.status;
        this.statusText = event.target.statusText
        this.recordError();
      } catch (error) {
        console.log('error', error); }}; XMLHttpRequest.prototype.send =function() {
      if (this.addEventListener) {
        this.addEventListener('error', _handler);
        this.addEventListener('load', _handler);
        this.addEventListener('abort', _handler);
      }
      return xhrSend.apply(this.arguments); }; }}Copy the code

Catch vUE exception classes

import BaseError from './base.js';

/* Catch vUE exception */
export default class VueError extends BaseError {
  constructor(params) {
    super(params);
  }
  /* Handle vUE exception */
  hanleError(Vue) {
    if(! Vue)return;
    Vue.config.errorHandler = (error, vm, info) = > {
      let matchfile = error.stack.match(/at\s+(.+):(\d+):(\d+)/);
      try {
        this.category = 'stability';
        this.filename = matchfile[1];
        this.type = 'vueError'; // Error type
        this.msg = info;
        this.postion = matchfile[2] + ':' + matchfile[3];
        this.stack = error.stack;
        this.recordError();
      } catch (error) {
        // console.log('vue error', error);}}; }}Copy the code

Exception Monitoring Class

import AjaxError from './ajaxError.js';
import PromiseError from './promiseError.js';
import JsResourceError from './jsResourceError.js';
import VueError from './vueError.js';
  
  class Monitor {
    constructor() {}
    init(options) {
      console.log('options',options)
      let param = {reportUrl:options.url}
      new PromiseError(param).hanleError();
      new JsResourceError(param).hanleError();
      new AjaxError(param).hanleError();
      options.vue && newVueError(param).hanleError(options.vue); }}export default Monitor
Copy the code

Collect the results

If you want to see the full version, please go to GithubException Monitoring SDKThe following exceptions need to be caught

  • The iframe abnormal
  • Cross-domain Script error
  • Crash and jam

Write in the last

You say strange not strange, write project split in clap BUG heaps appear, but when I want to write this article, those bugs are hidden, sister I spent 3 weeks to finish this article, HMM ~, so children boots, bugs must be recorded, This is great for locating problems quickly next time and for BUG replays!

The other thing I want to say is: If you encounter problems that are not very urgent, be sure to start your own debugger to locate and solve, make good use of the powerful tool Google, if Google does not have the relevant answer, then go to the official website to check documents and read frequently asked questions or go to GitHub to see the issue raised by others, There is usually a windfall! If you don’t, chances are you’re looking in the wrong direction to begin with.

Why say must oneself begin work, because ask others although the problem is solved quickly, but you missed the opportunity of a technical experience, you want to know the distinction between people is probably the distinction of ability to solve a problem! You only have to beat the problems you encounter one by one seriously, and then record, review, then will slowly form their own problem solving thinking model!

Refer to the article

How to handle front-end exceptions gracefully? Common error reporting and exception capture and processing methods of front-end JavaScript exception monitoring