6

image.png

Preface

I don’t know if my friends have such experience. Everything is normal when debugging a local development project. Once it is released online, various strange problems will occur. The reasons are also various. The environment variables are different, the host environment is different, and the interface returns. Different data formats, code logic issues, etc.

Some errors can be found in the development and testing process, and some errors will not be found until they arrive online. At this time, waiting for user feedback is too passive. Therefore, a monitoring system that collects and captures code errors is particularly important. .

The purpose of this series of articles is not to allow everyone to build a front-end code monitoring system immediately. It is to introduce the common ways of capturing code errors in the browser environment, and to create a front-end plug-in that collects code errors in a well-known way. Finally Then briefly introduce how to quickly locate the error after collecting the error information.

Common error types

Before starting our subject, we first need to understand the common types of errors.

Code writing error

This type of error is the easiest to eliminate, we often only need to use the editor's automatic detection, or eslint tslint and other third-party tools, in the development stage can be resolved.

Of course, if some students use a text editor to write code, please download a vscode to experience a better coding environment while accepting my knees.

function foo()
cont bar = "string"

Code execution error

This can be said to be the most encountered in normal development. For example, in the following example, I believe that many students use vue or react to obtain list data through the interface. If the back-end students do not return [] for empty data, they return If you use null or undefined , there will be problems with the front-end display.

Some students will also say that ts Dafa is good, and the interface definition is fragrant (but in fact, this does not solve the problem of inconsistent interface return), and if you encounter a situation where the any . .

undefined.map((item) => console.log(item));

Other types of errors, such as the use of an undefined variable, can be avoided in the development phase.

console.log(helloWorld);

Asynchronous code reject

I do not know little friends using promise when there is no catch reject habits, I believe most people are there except me (against their will), that for not actively catch of reject How should we caught and handled?

const promise = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error("promise reject"));
    }, 1000);
  });
// 未使用 catch 捕获错误
promise().then(console.log);

Resource loading error

Resource loading error is also a bald problem, if it is only image it is okay, if it is important js , css resource loading error. . .

<!-- 你说这个资源能加载?嗯?你有问题,小老弟 -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>

Catch code errors

vue . Some students may say, "Oh, I usually use react . I can't make mistakes in such a simple scenario."

Shhh, hold back your words, the frameworks throw errors in those simple ways.

In addition, the following content has detailed code and comments. clone code locally. For specific instructions, see README.md project branch.

example address

git clone -b demo/catch-code-error https://github.com/jsjzh/tiny-codes.git

Code execution error

In peacetime, when writing code, if you encounter may perform error logic, we usually use try catch , wrapped, and then do the treatment alone, but for some unexpected errors, such as the above interfaces return; there If an error occurs in the asynchronous code, the error try catch cannot be caught by . At this time, window.onerror will come in handy.

We can understand this way, try catch used to capture expected errors, and window.onerror used to capture unexpected errors, that is, the bottom line strategy.

/**
 * message {String}: 错误信息
 * fileName {String}: 发生错误的文件
 * col {Number}: 错误代码的行
 * row {Number}: 错误代码的列
 * error {Error}: Error 对象
 */
window.onerror = (message, fileName, col, row, error) => {
  console.log("message: ", message);
  console.log("fileName: ", fileName);
  console.log("col: ", col);
  console.log("row: ", row);
  console.log("error.name: ", error.name);
  console.log("error.message: ", error.message);
  console.log("error: ", error);
};

Active try catch

In the code below, we took the initiative to try catch an error in 0607aff028e1c6, which means that we know that there may be errors in the logic of try catch . In fact, this error situation may not need to be reported because we have already done the corresponding error handling.

ps: if it thinks still need to report an error, you can catch increase paragraph throw error logic, so that errors can be thrown, is window.onerror capture.
// 同步执行代码,通过 try catch 可以捕获错误
try {
  console.log(helloWorld);
} catch (error) {
  // 在这里做一些自定义处理
  // ...
  // 若做了自定义处理后,仍旧需要上报错误,增加 throw error
  // throw error;
}

Unexpected error

In the following situation, we did not expect an error to be reported, and the error will be directly captured window.onerror

ps: Here, the process object is accessed in the browser environment, which is a global variable only available in the node
// 未使用 try catch 捕获错误
// 说明我们未预料到这段代码可能出错
console.log(process);

image.png

Asynchronous code execution error

If there is a code execution error in the asynchronous logic code, we try catch . At this time, there are two ways. The first is to wrap the asynchronously executed function with try catch . We will not give an example of this. The second method The error is captured window.onerror

const promise = () =>
  new Promise(() => {
    setTimeout(() => {
      console.log(helloWorld);
    }, 1000);
  });

try {
  promise().then();
} catch (error) {
  // 无法捕获到错误 error
  console.log("can't catch error");
}

image.png

As you can see in the picture above, the error was captured window.onerror

Asynchronous code reject

Saying asynchronous code reject ago, we have to have a consensus, promise of catch not be captured code execution error, only through reject() of throwing an exception, will only be promise.catch captured, what does that mean? Look at the code below.

const promise = () =>
  new Promise(() => {
    setTimeout(() => {
      console.log(helloWorld);
    }, 1000);
  });

promise()
  .then()
  // 无法捕获到 helloWorld 的错误
  .catch(() => {
    console.log("can't catch error");
  });

image.png

So what error can promise of catch The answer is reject , as follows.

const promise2 = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      // 通过 reject 抛出错误
      reject(new Error("promise reject"));
    }, 1000);
  });

promise2()
  .then()
  .catch((error) => {
    // 能够捕获到错误
    console.log("error.name: ", error.name);
    console.log("error.message: ", error.message);
    console.log("error: ", error);
  });

image.png

The above content is just to reach a consensus between us. Next, let's talk about how to capture catch that is reject and pass window.onunhandledrejection .

ps: One thing needs to be reminded. For custom errors thrown, try to use new Error(...) , so that we can not only get the complete error information, but also the file name, error line and error column, which is the so-called stack information .

ps2: If it is promise.catch captured by reject , it will not be window.onunhandledrejection . If you still want to report this error, you can use throw error , which will be captured window.onerror

window.onunhandledrejection = (promiseRejectEvent) => {
  // Error 对象,由 reject(new Error()) 生成
  const reason = promiseRejectEvent.reason;
  if (reason) {
    console.log("reason.name: ", reason.name);
    console.log("reason.message: ", reason.message);
    console.log("reason.stack: ", reason.stack);
  }
};

const promise2 = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      // 通过 reject 抛出错误
      reject(new Error("promise reject"));
    }, 1000);
  });

// 未使用 catch,错误将被 window.onunhandledrejection 捕获
promise2().then();

image.png

Resource loading error

Regarding resource loading errors, whether it is js , css , image , we can monitor and capture errors window.addEventListener("error")

However, the resource loading error caused by background-image: url(./error.png) new Image().src = "./error.png" cannot be captured by this method.

ps: If a friend knows any way to catch these loading errors, please tell me that I will update it in the article after my self-test is successful.
window.addEventListener(
  "error",
  (sourceErrorEvent) => {
    const targetElement =
      sourceErrorEvent.target || sourceErrorEvent.srcElement;
    const url = targetElement.src || targetElement.href;

    console.log("sourceError: ", url);
  },
  true
);
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<script src="./error.js"></script>
<link rel="stylesheet" href="./error.css" />
<img src="./error.png" />

image.png

Unexpected little problem

I believe that with the above three solutions, we have been able to capture most of the errors. However, when debugging, we found that when the loaded js resource is cross-domain, window.onerror will only collect error information Script error. , as shown in the figure below.

image.png

Why is this happening? Of course it is the strategy of the browser. . . But we also have corresponding solutions.

ps: For the html tag resources, please refer to MDN's crossOrigin attribute description.

Back to the question, we only need to do two things, first:

Add a crossOrigin="anonymous" attribute to js resource loaded across domains

<script src="http://127.0.0.1:7002/index.js" crossorigin="anonymous"></script>

second:

Ensure that the cross-domain resource server sets the Access-Control-Allow-Origin: hosts header, and your browser sends Origin: host in its list. If it is troublesome, the server can directly set Access-Control-Allow-Origin: * .

ps: In fact, it is not recommended to set it to * . If you use cookie , this will cause cookie not work properly. You need to set some other headers to make cookie take effect.
Of course, if the resources are configured in a unified CDN, then the header can be configured separately for the static resource server.

ps2: You don’t need to actively add the Origin header, this is the browser’s spontaneous behavior

# Response Header
...
Access-Control-Allow-Origin: http://127.0.0.1:7001
...

# Request Header
...
Origin: http://127.0.0.1:7001
...

image.png

As above, we can get the detailed information of the error.

Just a little emphasis, the above examples can be found in the following examples.

example address

git clone -b demo/catch-code-error https://github.com/jsjzh/tiny-codes.git

Afterword

At this point, the correct posture for catching code errors (1) is over. When writing the article, I learned a lot of points that I would not usually pay attention to. I also have some insights about the magical front-end code monitoring system.

Of course, some students have to say, isn’t sentry fragrant? Why take time and effort to mess around with yourself? Regarding this point, I think that if we are just to solve the problem, it is naturally best to use a ready-made solution. But if you don't toss, what is the difference with salted fish? (Escape

There are two more chapters following the article. In the second chapter, I will build a plug-in that collects front-end error messages. In the third chapter, I will provide some immature solutions for how to locate problems after code compression that is very common now.

In addition, the writing is poor and the plan is naive. If the judges have any comments or suggestions, please leave a message in the comment area and we will discuss and discuss together.

Footer

Code is life, and I am happy with it.

Technology is constantly changing
Mind is always online
The front end is long
See you next time

by --- Crotch Trio

I am here gayhub@jsjzh welcome everyone to come and play with me.

Welcome friends to add me directly, pull you into the group and do things together, remember to note where you saw the article.

image.png

ps: If the picture is invalid, you can add me wechat: kimimi_king

裤裆三重奏
788 声望936 粉丝

认真的灵魂会发光