收藏
回答

npm require 浅拷贝

问题模块 框架类型 问题类型 终端类型 微信版本 基础库版本
框架 小程序 Bug 工具 6.5.3 2.0.0

- 当前 Bug 的表现(可附上截图)


- 预期表现


- 复现路径


- 提供一个最简复现 Demo


小程序会在每个npm包的入口文件前加入一段模块打包代码,类似webpack的,下面是其中 __REQUIRE__ 函数的实现

var __REQUIRE__ = function (modId, source) {
  if (!__MODS__[modId])
    return require(source);
 
  if (!__MODS__[modId].status) {
    var m = { exports: {} };
    __MODS__[modId].status = 1;
    __MODS__[modId].func(__MODS__[modId].req, m, m.exports);
 
    if (typeof m.exports === "object") {
      Object.keys(m.exports).forEach(function (k) {
        __MODS__[modId].m.exports[k] = m.exports[k];
      });
 
      if (m.exports.__esModule)
        Object.defineProperty(__MODS__[modId].m.exports, "__esModule", { value: true });
 
    } else {
      __MODS__[modId].m.exports = m.exports;
    }
  }
 
  return __MODS__[modId].m.exports;
};

可以看到,这里判断了如果一个模块导出的是一个对象,就会对其做一层的拷贝,这不是浅拷贝,浅拷贝是直接赋值。这一层的拷贝会导致引用出问题

以下是webpack的require实现

function __webpack_require__(moduleId) {
  /******/
  /******/                // Check if module is in cache
  /******/                if (installedModules[moduleId]) {
  /******/                        return installedModules[moduleId].exports;
    /******/
  }
  /******/                // Create a new module (and put it into the cache)
  /******/                var module = installedModules[moduleId] = {
  /******/                        i: moduleId,
  /******/                        l: false,
  /******/                        exports: {},
  /******/                        hot: hotCreateModule(moduleId),
  /******/                        parents: (hotCurrentParentsTemp = hotCurrentParents, hotCurrentParents = [], hotCurrentParentsTemp),
  /******/                        children: []
    /******/
  };
  /******/
  /******/                // Execute the module function
  /******/                modules[moduleId].call(module.exports, module, module.exports, hotCreateRequire(moduleId));
  /******/
  /******/                // Flag the module as loaded
  /******/                module.l = true;
  /******/
  /******/                // Return the exports of the module
  /******/                return module.exports;
  /******/
}

可见并没有做一层的拷贝。

最后一次编辑于  07-12  (未经腾讯允许,不得转载)
邀请回答
复制链接收藏关注问题回答

3 个回答

  • June
    June
    07-12

    这个拷贝当初是为了兼容有弱循环依赖的包而加的,可以试试工具的 nightly 版本,加了 getter 和 setter,应该仍然有拷贝,不过对于运行时修改 module.exports 应该也会同步过去。


    另外这里仍有一个已知问题,就是原型链没有被拷贝过去,这个修复近期也会跟 nightly 版本出去。

    07-12
    赞同
    回复 2
    • 七世
      七世
      07-12
      为什么官方不用webpack或者browserify用的打包器呢?
      07-12
      赞同
      回复
    • June
      June
      07-12回复七世
      里面的实现太重了,其实如果用webpack,完全可以自己打包的,只需要遵守工具这边的目录规则就行(即打到 miniprogram_npm 目录)。
      07-12
      赞同
      回复