source-map-webpack-plugin插件:
产出自定义的webpack插件,用途:主要用于打包生成代码时,生成source-map文件并把source-map文件通过http模块发送到对应的服务器上(若有需要,可发送到服务器后,删除打包后的source-map)
const path = require('path');const http = require('http');const fs = require('fs');class SourceMapUpload {options: any;constructor(options) {this.options = options;}apply(compiler) {compiler.hooks.done.tap('source-map-webpack-upload', async (compilation) => {const sourceJsPath = path.join(compilation.compilation.outputOptions.path,'umi.js.map',);await this.upload(this.options.url, sourceJsPath);});}upload(url, filePath) {return new Promise((resolve) => {const req = http.request(`${url}?name=${path.basename(filePath)}`, {method: 'POST',headers: {'Content-Type': 'application/octet-stream',Connection: 'keep-alive',},});let fileSteam = fs.createReadStream(filePath, {highWaterMark: 1024 * 10,});fileSteam.pipe(req, { end: false });fileSteam.on('end', () => {req.end();resolve(true);});});}}module.exports = SourceMapUpload;
Umi使用source-map-plugin:
const sourceMapUpload = require('./source-map-webpack-upload');import { defineConfig } from 'umi';export default defineConfig({nodeModulesTransform: {type: 'none',},routes: [{ path: '/', component: '@/pages/index' },{ path: '/test', component: '@/pages/test' },],devtool: 'source-map',fastRefresh: {},chainWebpack: (config) => {if (process.env.NODE_ENV === 'production') {config.plugin('source-map-webpack-upload').use(sourceMapUpload, [{url: 'http://localhost:7001/upload',},]);}},});
异常监听:
// 通过image中的src上报异常信息window.addEventListener('error', (e) => {let str = window.btoa(JSON.stringify({lineno: e?.lineno,colno: e?.colno,error: { stack: e?.error?.stack },message: e?.message,filename: e?.filename,}),);new Image().src = `http://localhost:7001/error?loginfo=${str}`;});// error不能监听promise中reject状态,所以需要监听unhandledrejection来处理promise中reject状态window.addEventListener('unhandledrejection', (e: any) => {// 自行处理});
Node后端:
"use strict";const Controller = require("egg").Controller;const path = require("path");const fs = require("fs");const sourceMap = require("source-map");class UploadController extends Controller {async getError() {const { ctx } = this;const { loginfo } = ctx.query;let obj = JSON.parse(Buffer.from(loginfo, "base64").toString("utf-8"));let fileName = obj.filename.split("/").pop() + ".map";let mapPath = fs.readFileSync(path.join(__dirname, "../../source-map", fileName),"utf-8");var consumer = await new sourceMap.SourceMapConsumer(mapPath);let result = consumer.originalPositionFor({line: obj.lineno,column: obj.colno});obj.lineno = result.line;obj.column = result.column;ctx.body = {success: true,data: { source: result.source, line: 4, column: 14, error: obj.error }};}}module.exports = UploadController;
