Web前端最新#稳定性-异常上报 前端异常埋点系统初探(1),2024年最新面试心得体会2000字

Vue

  • 什么是MVVM?

  • mvvm和mvc区别?它和其它框架(jquery)的区别是什么?哪些场景适合?

  • 组件之间的传值?

  • Vue 双向绑定原理

  • 描述下 vue 从初始化页面–修改数据–刷新页面 UI 的过程?

  • 虚拟 DOM 实现原理

  • Vue 中 key 值的作用?

  • Vue 的生命周期

  • Vue 组件间通信有哪些方式?

  • vue 中怎么重置 data?

  • 组件中写 name 选项有什么作用?

  • Vue 的 nextTick 的原理是什么?

  • Vuex 有哪几种属性?

    开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

还不了解的同学可以阅读以下文章:

前端-埋点-理念-通识-浅谈

大数据时代数据的重要性不言而喻,而其中最重要的就是用户信息的采集。埋点,无论是项目后期的复盘,还是明确业务价值,还是产品价值的挖掘,都具备很重要的意义。

前端异常捕获

在ES3之前js代码执行的过程中,一旦出现错误,整个js代码都会停止执行,这样就显的代码非常的不健壮。从ES3开始,js也提供了类似的异常处理机制,从而让js代码变的更健壮,程序执行的过程中出现了异常,也可以让程序具有了一部分的异常恢复能力。js异常的特点是,出现不会导致JS引擎崩溃,最多只会终止当前执行的任务。

回归正题,我们该如何在程序异常发生时捕捉并进行对应的处理呢?在Javascript中,我们通常有以下两种异常捕获机制。

基本的try…catch语句

function errFunc() {

// eslint-disable-next-line no-undef

error;

}

function catchError() {

try {

this.errFunc();

} catch (error) {

console.log(error);

}

}

catchError()

复制代码

能捕捉到的异常,必须是线程执行已经进入 try catch 但 try catch 未执行完的时候抛出来的,以下都是无法被捕获到的情形。

  1. 异步任务抛出的异常(执行时try catch已经从执行完了)

  2. promise(异常内部捕获到了,并未往上抛异常,使用catch处理)

  3. 语法错误(代码运行前,在编译时就检查出来了的错误)

  • 优点:能够较好地进行异常捕获,不至于使得页面由于一处错误挂掉

  • 缺点:显得过于臃肿,大多代码使用try ... catch包裹,影响代码可读性。

面试官:请用一句话描述 try catch 能捕获到哪些 JS 异常

全局异常监听window.onerror

window.onerror 最大的好处就是同步任务、异步任务都可捕获,可以得到具体的异常信息、异常文件的URL、异常的行号与列号及异常的堆栈信息,再捕获异常后,统一上报至我们的日志服务器,而且可以全局监听,代码看起来也简洁很多。

  • 缺点:
  1. 此方法有一定的浏览器兼容性

  2. 跨域脚本无法准确捕获异常,跨域之后window.onerror捕获不到正确的异常信息,而是统一返回一个Script error,可通过在<script>使用crossorigin属性来规避这个问题

image.png

window.addEventListener(‘error’, function() {

console.log(error);

// …

// 异常上报

});

throw new Error(‘这是一个错误’);

复制代码

Promise内部异常

前文已经提到,onerror 以及 try-catch 也无法捕获Promise实例抛出的异常,只能最后在 catch 函数上处理,但是代码写多了就容易糊涂,忘记写 catch。

如果你的应用用到很多的 Promise 实例的话,特别是在一些基于 promise 的异步库比如 axios 等一定要小心,因为你不知道什么时候这些异步请求会抛出异常而你并没有处理它,所以最好添加一个 Promise 全局异常捕获事件 unhandledrejection

window.addEventListener(“unhandledrejection”, e => {

console.log(‘unhandledrejection’,e)

});

复制代码

vue工程异常

window.onerror并不能捕获.vue文件发生的获取,Vue 2.2.0以上的版本中增加了一个errorHandle,使用Vue.config.errorHandler这样的Vue全局配置,可以在Vue指定组件的渲染和观察期间未捕获错误的处理函数。这个处理函数被调用时,可获取错误信息和Vue 实例。

//main.js

import { createApp } from “vue”;

import App from “./App.vue”;

let app = createApp(App);

app.config.errorHandler = function(e) {

console.log(e);

//错误上报…

};

app.mount(“#app”);

复制代码

Vue项目JS脚本错误捕获

综上,可以将几种方式有效结合起来,笔者这里是在vue-cli框架中做的处理,其余类似:

import { createApp } from “vue”;

import App from “./App.vue”;

let app = createApp(App);

window.addEventListener(

“error”,

(e) => {

console.log(e);

//TODO:上报逻辑

return true;

},

true

);

// 处理未捕获的异常,主要是promise内部异常,统一抛给 onerror

window.addEventListener(“unhandledrejection”, (e) => {

throw e.reason;

});

// 框架异常统一捕获

app.config.errorHandler = function(err, vm, info) {

//TODO:上报逻辑

console.log(err, vm, info);

};

app.mount(“#app”);

复制代码

sourcemap

生产环境下所有的报错的代码行数都在第一行了,为什么呢?

通常在该环境下的代码是经过webpack打包后压缩混淆的代码,否则源代码泄漏易造成安全问题,在生产环境下,我们的代码被压缩成了一行。而保留了sourcemap文件就可以利用webpack打包后的生成的一份.map的脚本文件就可以让浏览器对错误位置进行追踪了,但这种做法并不可取,更为推荐的是在服务端使用Node.js对接收到的日志信息时使用source-map解析,以避免源代码的泄露造成风险

image.png

vue.config.js配置里通过属性productionSourceMap: true可以控制webpack是否生成map文件

webpack自定义插件实现sourcemap自动上传

为了我们每一次构建服务端能拿到最新的map文件,我们编写一个插件让webpack在打包完成后触发一个钩子实现文件上传,在vue.config.js中进行配置

调整 webpack 配置

//vue.config.js

let SourceMapUploader = require(“./source-map-upload”);

module.exports = {

configureWebpack: {

resolve: {

alias: {

“@”: resolve(“src”),

},

},

plugins: [

new SourceMapUploader({url: “http://localhost:3000/upload”})

],

}

//   chainWebpack: (config) => {},

}

复制代码

//source-map-upload.js

const fs = require(“fs”);

const http = require(“http”);

const path = require(“path”);

class SourceMapUploader {

constructor(options) {

this.options = options;

}

/**

* 用到了hooks,done表示在打包完成之后

* status.compilation.outputOptions就是打包的dist文件

*/

apply(compiler) {

if (process.env.NODE_ENV == “production”) {

compiler.hooks.done.tap(“sourcemap-uploader”, async (status) => {

// console.log(status.compilation.outputOptions.path);

// 读取目录下的map后缀的文件

let dir = path.join(status.compilation.outputOptions.path, “/js/”);

let chunks = fs.readdirSync(dir);

let map_file = chunks.filter((item) => {

return item.match(/.js.map$/) !== null;

});

// 上传sourcemap

while (map_file.length > 0) {

let file = map_file.shift();

await this.upload(this.options.url, path.join(dir, file));

}

});

}

}

//调用upload接口,上传文件

upload(url, file) {

return new Promise((resolve) => {

let req = http.request(${url}?name=${path.basename(file)}, {

method: “POST”,

headers: {

“Content-Type”: “application/octet-stream”,

Connection: “keep-alive”,

},

});

let fileStream = fs.createReadStream(file);

fileStream.pipe(req, { end: false });

fileStream.on(“end”, function() {

req.end();

resolve();

});

});

}

}

module.exports = SourceMapUploader;

复制代码

错误上报

两种方式:

  1. img标签 这种方式无需加载任何通讯库,而且页面是无需刷新的,相当于get请求,没有跨域问题。缺点是有url长度限制,但一般来讲足够使用了。

  2. ajax 与正常的接口请求无异,可以用post

这里采用第一种,通过动态创建一个img,浏览器就会向服务器发送get请求。将需要上报的错误数据放在url中,利用这种方式就可以将错误上报到服务器了。

确定上报的内容,应该包含异常位置(行号,列号),异常信息,在错误堆栈中包含了绝大多数调试有关的信息,我们通讯的时候只能以字符串方式传输,我们需要将对象进行序列化处理。

  1. 将异常数据从属性中解构出来,存入一个JSON对象

  2. 将JSON对象转换为字符串

  3. 将字符串转换为Base64

后端接收到信息后进行对应的反向操作,就可以在日志中记录。

1621581164(1).png

function uploadErr({ lineno, colno, error: { stack }, message, filename }) {

let str = window.btoa(

JSON.stringify({

lineno,

colno,

error: { stack },

message,

filename,

})

);

let front_ip = “http://localhost:3000/error”;

new Image().src = ${front_ip}?info=${str};

}

复制代码

后端服务

用koa搭一个简单后台服务,代码比较简单,按功能拆开来讲

上传文件接口

文件流写入:

router.post(“/upload”, async (ctx) => {

const stream = ctx.req;

const filename = ctx.query.name;

let dir = path.join(__dirname, “source-map”);

//判断source文件夹是否存在

if (!fs.existsSync(dir)) {

fs.mkdirSync(dir);

}

let target = path.join(dir, filename);

const ws = fs.createWriteStream(target);

stream.pipe(ws);

});

复制代码

错误日志

使用log4js记录我们的错误日志,这个也是非常流行的日志插件了,直接贴代码。

log4js-node

const path = require(‘path’)

const log4js = require(‘log4js’);

log4js.configure({

appenders: {

info: {

type: “dateFile”,

filename: path.join(__dirname, ‘logs’, ‘info’, ‘info’),

pattern: “yyyy-MM-dd.log”,

encoding: ‘utf-8’,

alwaysIncludePattern: true,

},

error: {// 错误日志

type: ‘dateFile’,

filename: path.join(__dirname, ‘logs’, ‘error’, ‘error’),

pattern: ‘yyyy-MM-dd.log’,

encoding: ‘utf-8’,

alwaysIncludePattern: true

}

},

categories: {

default: { appenders: [‘info’], level: ‘info’ },

info: { appenders: [‘info’], level: ‘info’ },

error: { appenders: [‘error’], level: ‘error’ }

}

});

/**

* 错误日志记录方式

* @param {*} content 日志输出内容

*/

function logError(content) {

const log = log4js.getLogger(“error”);

log.error(content)

}

/**

* 日志记录方式

* @param {*} content 日志输出内容

*/

function logInfo(content) {

最后更多分享:前端字节跳动真题解析

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

r’, ‘error’),

pattern: ‘yyyy-MM-dd.log’,

encoding: ‘utf-8’,

alwaysIncludePattern: true

}

},

categories: {

default: { appenders: [‘info’], level: ‘info’ },

info: { appenders: [‘info’], level: ‘info’ },

error: { appenders: [‘error’], level: ‘error’ }

}

});

/**

* 错误日志记录方式

* @param {*} content 日志输出内容

*/

function logError(content) {

const log = log4js.getLogger(“error”);

log.error(content)

}

/**

* 日志记录方式

* @param {*} content 日志输出内容

*/

function logInfo(content) {

最后更多分享:前端字节跳动真题解析

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • [外链图片转存中…(img-LdS6vcFa-1715898537437)]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值