source-map-webpack-plugin插件:

产出自定义的webpack插件,用途:主要用于打包生成代码时,生成source-map文件并把source-map文件通过http模块发送到对应的服务器上(若有需要,可发送到服务器后,删除打包后的source-map)

  1. const path = require('path');
  2. const http = require('http');
  3. const fs = require('fs');
  4. class SourceMapUpload {
  5. options: any;
  6. constructor(options) {
  7. this.options = options;
  8. }
  9. apply(compiler) {
  10. compiler.hooks.done.tap('source-map-webpack-upload', async (compilation) => {
  11. const sourceJsPath = path.join(
  12. compilation.compilation.outputOptions.path,
  13. 'umi.js.map',
  14. );
  15. await this.upload(this.options.url, sourceJsPath);
  16. });
  17. }
  18. upload(url, filePath) {
  19. return new Promise((resolve) => {
  20. const req = http.request(`${url}?name=${path.basename(filePath)}`, {
  21. method: 'POST',
  22. headers: {
  23. 'Content-Type': 'application/octet-stream',
  24. Connection: 'keep-alive',
  25. },
  26. });
  27. let fileSteam = fs.createReadStream(filePath, {
  28. highWaterMark: 1024 * 10,
  29. });
  30. fileSteam.pipe(req, { end: false });
  31. fileSteam.on('end', () => {
  32. req.end();
  33. resolve(true);
  34. });
  35. });
  36. }
  37. }
  38. module.exports = SourceMapUpload;

Umi使用source-map-plugin:

  1. const sourceMapUpload = require('./source-map-webpack-upload');
  2. import { defineConfig } from 'umi';
  3. export default defineConfig({
  4. nodeModulesTransform: {
  5. type: 'none',
  6. },
  7. routes: [
  8. { path: '/', component: '@/pages/index' },
  9. { path: '/test', component: '@/pages/test' },
  10. ],
  11. devtool: 'source-map',
  12. fastRefresh: {},
  13. chainWebpack: (config) => {
  14. if (process.env.NODE_ENV === 'production') {
  15. config.plugin('source-map-webpack-upload').use(sourceMapUpload, [
  16. {
  17. url: 'http://localhost:7001/upload',
  18. },
  19. ]);
  20. }
  21. },
  22. });

异常监听:

  1. // 通过image中的src上报异常信息
  2. window.addEventListener('error', (e) => {
  3. let str = window.btoa(
  4. JSON.stringify({
  5. lineno: e?.lineno,
  6. colno: e?.colno,
  7. error: { stack: e?.error?.stack },
  8. message: e?.message,
  9. filename: e?.filename,
  10. }),
  11. );
  12. new Image().src = `http://localhost:7001/error?loginfo=${str}`;
  13. });
  14. // error不能监听promise中reject状态,所以需要监听unhandledrejection来处理promise中reject状态
  15. window.addEventListener('unhandledrejection', (e: any) => {
  16. // 自行处理
  17. });

Node后端:

  1. "use strict";
  2. const Controller = require("egg").Controller;
  3. const path = require("path");
  4. const fs = require("fs");
  5. const sourceMap = require("source-map");
  6. class UploadController extends Controller {
  7. async getError() {
  8. const { ctx } = this;
  9. const { loginfo } = ctx.query;
  10. let obj = JSON.parse(Buffer.from(loginfo, "base64").toString("utf-8"));
  11. let fileName = obj.filename.split("/").pop() + ".map";
  12. let mapPath = fs.readFileSync(
  13. path.join(__dirname, "../../source-map", fileName),
  14. "utf-8"
  15. );
  16. var consumer = await new sourceMap.SourceMapConsumer(mapPath);
  17. let result = consumer.originalPositionFor({
  18. line: obj.lineno,
  19. column: obj.colno
  20. });
  21. obj.lineno = result.line;
  22. obj.column = result.column;
  23. ctx.body = {
  24. success: true,
  25. data: { source: result.source, line: 4, column: 14, error: obj.error }
  26. };
  27. }
  28. }
  29. module.exports = UploadController;