micro-zoe / micro-app

A simple, efficient and powerful micro front-end framework. 一款简约、高效、功能强大的微前端框架
https://micro-zoe.github.io/micro-app/
MIT License
5.53k stars 563 forks source link

MicroApp 子应用增加 sourceMap ,行号对应不上的问题 #935

Open WeirShi opened 11 months ago

WeirShi commented 11 months ago

问题描述

问题的具体描述 通过MicroApp开发的微应用,通过主应用加载子应用后,js内容有变化,导致sourcemap行列对不上

WeirShi commented 11 months ago

直接加载子应用的js

{(self["webpackJsonp_custom_iwork_admin"] = self("webpack]sonp_custom_iwork_admin"
59158: function(e) {
e.exports = {
"base-tabs": "base-tabs___1Txxa"
}
9420: function(e){
e.exports = {
addressManage: "addressManage__3MoiK",
topCard: "topCard
_3VNHp"
}
7541: function(e, t, a) {
"use strict";
a.d(t, {
I: function(){
}
return n
}};
98677: function(e, t, a) {
"use strict";
a.d(t,{
gp: function(){
},
return o
_N:function(){
},
return d
p6: function(){
},
return s
y3:function(){
3
return c
}};
a(83589);
var n = a(89856
WeirShi commented 11 months ago

通过主应用加载子应用后的js, 会多几行proxyWindow._MICRO_APP_WINDOW_相关的

(function anonymous() {
;(function(proxyWindow)
with (proxyWindow._MICRO_APP_WINDOW_) {
(function(window, self, globalThis, Array, Object, String,Boolean, Ma
;(self["webpackJsonp_custom_iwork_admin"] = self["webppackJsonp_cus
59158: function(e){
e.exports = {
"base-tabs": "base-tabs___1Txxa"
}
},
9420: function(e){
e.exports = {
addressManage: "addressManage__3MoiK",
topCard: "topCard__3VNHP"
}
},
7541: function(e, t, a) {
"use strict";
a.d(t, {
I: function(){
3
return n
});

},
98677: function(e,t, a) {
"use strict";
a.d(t,{
gp:function(){
},
return o
_N:function(){
},
return d
p6: function(){
return s
bailicangdu commented 11 months ago

确实存在这个问题,因为包裹了函数,导致位置偏移

WeirShi commented 11 months ago

所以目前有什么解决方案吗???或者有规划在未来的版本中会解决这个问题吗?

lkiarest commented 6 months ago

+1,希望可以解决

ArayZou commented 6 months ago
const webpack = require('webpack')
const { RawSource, SourceMapSource } = require('webpack-sources')

class BatchReplacePlugin {
  constructor(options) {
    this.options = options;
  }
  apply(compiler) {
    const { Compilation } = compiler.webpack;
    compiler.hooks.compilation.tap('BatchReplacePlugin', (compilation) => {
      if (Compilation && Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE) {
        compilation.hooks.processAssets.tapAsync(
          {
            name: 'BatchReplacePlugin',
            stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE,
          },
          (assets, callback) => {
            Object.keys(assets).forEach((filename) => {
              if (this.shouldReplace(filename)) {
                const asset = assets[filename];
                const originalSource = asset.source();
                const originalSourceMap = asset.map();
                const newSource = this.replaceContent(originalSource);

                if (originalSourceMap) {
                  compilation.updateAsset(
                    filename,
                    new SourceMapSource(newSource, filename, originalSourceMap)
                  );
                } else {
                  // 如果不存在SourceMap,则只更新源文件
                  compilation.updateAsset(filename, new RawSource(newSource));
                }
              }
            });
            callback();
          }
        )
      }
    })
  }
  shouldReplace(filename) {
    return filename.indexOf('.js') > -1;
  }
  replaceContent(source) {
    const { find, replace } = this.options;
    if (!source) return source
    return source.replace(find, replace);
  }
}

new webpack.BannerPlugin({
  banner: `(function anonymous(\n) {\n;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(window,self,globalThis,document,Document,Array,Object,String,Boolean,Math,Number,Symbol,Date,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,navigator,undefined,location,history){;`,
  raw: true,
  test: /\.js($|\?)/i
}),
new BatchReplacePlugin({
  find: `(function anonymous(\n) {\n;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(window,self,globalThis,document,Document,Array,Object,String,Boolean,Math,Number,Symbol,Date,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,navigator,undefined,location,history){;`, // 需要查找的内容
  replace: '' // 替换后的内容
})

本地开发解决方案👆,根据自己的配置调整头部的代码 线上还是有问题,可能需要考虑子工程单独渲染和调试

ArayZou commented 4 months ago

线上和本地都可以work的方案:

线上文件的TerserPlugin配置增加preamble

new TerserPlugin({
    terserOptions: {
      ecma: 5,
      compress: {
        ecma: 5
      },
      output: {
        comments: false,
        ascii_only: true,
        // 新增这里的配置,在每个文件新增首行
        preamble: '\n'
      }
    },
    // parallel: true,
    parallel: 4,
    extractComments: false
  }),

替换plugin逻辑

const { RawSource, SourceMapSource } = require('webpack-sources')

class BatchReplacePlugin {
  constructor(options) {
    this.options = options;
  }

  apply(compiler) {
    const { Compilation } = compiler.webpack;

    compiler.hooks.compilation.tap('BatchReplacePlugin', (compilation) => {
      if (Compilation && Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE) {
        compilation.hooks.processAssets.tapAsync(
          {
            name: 'BatchReplacePlugin',
            stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE,
          },
          (assets, callback) => {
            Object.keys(assets).forEach((filename) => {
              if (this.shouldReplace(filename)) {
                const asset = assets[filename];
                const originalSource = asset.source();
                const originalSourceMap = asset.map();
                const newSource = this.replaceContent(originalSource);

                if (originalSourceMap) {
                  compilation.updateAsset(
                    filename,
                    new SourceMapSource(newSource, filename, originalSourceMap)
                  );
                } else {
                  // 如果不存在SourceMap,则只更新源文件
                  compilation.updateAsset(filename, new RawSource(newSource));
                }
              }
            });
            callback();
          }
        )
      }
    })
  }

  shouldReplace(filename) {
    return filename.indexOf('.js') > -1;
  }

  replaceContent(source) {
    const { find, replace } = this.options;
    if (!source) return source
    return source.replace(find, replace);
  }
}

const MICRO_APP_HEADER_JS = `(function anonymous(\n) {\n;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(window,self,globalThis,document,Document,Array,Object,String,Boolean,Math,Number,Symbol,Date,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,navigator,undefined,location,history){;`

module.exports = {
  BatchReplacePlugin,
  MICRO_APP_HEADER_JS
}

本地webpack配置

const webpack = require('webpack')
const { BatchReplacePlugin, MICRO_APP_HEADER_JS } = require('./batchReplacePlugin')
plugins: [
    new webpack.BannerPlugin({
      banner: `${MICRO_APP_HEADER_JS}`,
      raw: true,
      test: /\.js($|\?)/i
    }),
    new BatchReplacePlugin({
      find: MICRO_APP_HEADER_JS, // 需要查找的内容
      replace: '' // 替换后的内容
    })
  ]

线上webpack配置

const webpack = require('webpack')
const { BatchReplacePlugin, MICRO_APP_HEADER_JS } = require('./batchReplacePlugin')

plugins: [
    new webpack.BannerPlugin({
      banner: `${MICRO_APP_HEADER_JS}\n`,
      raw: true,
      test: /\.js($|\?)/i
    }),
    new BatchReplacePlugin({
      find: `${MICRO_APP_HEADER_JS}\n`, // 需要查找的内容
      replace: '' // 替换后的内容
    })
  ]

思路是在打包文件增加空的首行,在生产SourceMap前先模拟把相关js代码注入后的状态生产SourceMap,然后生成SourceMap后再把模拟注入的js移除。

qinbinhua601 commented 3 months ago

参考garfishjs中sourcemap 对应错误,提供一个现状下已经接入micro-app的子应用的work around的可能思路 (只适合线上临时debug):

  1. 代码里配置可以动态micro-app设置inline属性,记录在localStorage或者cookie等都可以,有标记就用inline加载micro-app
  2. 子应用打包的时候在压缩代码以后,添加一行banner注释(强制让行列跟最后inline以后执行的代码一致),这样sourcemap和最后包裹代码以后执行的sourcemap是一致的。如下图1、图2

图1:打包以后的js 2024-06-22 at 13 43

图2:实际执行的js 2024-06-22 at 13 45

  1. 最后的sourcemap效果 2024-06-22 at 13 47
754442658 commented 2 weeks ago

这个怎么解决啊