如何能够引入刚下载的第三方模块?
发布于 3 年前 作者 linzb93 4609 次浏览 来自 问答

我想实现一个函数,当目标模块已下载时,直接通过 require 引入,否则去npm下载,完成后再引入。 原先的代码是这样的:

const execa = require('execa');
const requireDynamic = async moduleName => {
    try {
        return require(moduleName);
    } catch {
        await execa('npm', ['install', moduleName]);
        return require(moduleName);
    }
};

但是这样会报错,提示模块不存在。 而我在模块安装结束后用下面这段代码确认模块已经安装,模块文件夹是存在的:

const fs = require('fs-extra');
const path = require('path');
console.log(fs.pathExists(path.resolve(process.cwd(), 'node_modules', moduleName)));

我怀疑模块下载成功后没能立即允许引入,所以开了定时器setInterval 来确认什么时候可以引入,但是一直都不可以。 后来我先找到这个模块的package.json ,然后在main字段找到模块的入口文件,引入这个文件,是可以的。

const execa = require('execa');
const path = require('path');
const requireDynamic = async moduleName => {
    try {
        return require(moduleName);
    } catch {
        await execa('npm', ['install', moduleName]);
        const modulePath = path.resolve(process.cwd(), 'node_modules', moduleName);
        const { main } = require(path.resolve(modulePath, 'package.json'));
        return require(path.resolve(modulePath, main));
    }
};

我就是没明白,npm模块从下载,解压,还要经过什么流程,才可以被正常引入。希望有好心人解答下,谢谢了。

7 回复

目测 require 缓存造成的,catch 语句里面先删除一次缓存试试

await execa('npm', ['install', moduleName]);
delete require.cache[moduleName];
return require(moduleName);

@xiaoxiaojx 不是,删除缓存后还是会提示模块不存在。

webstorm里可以看到require实现的源码 楼主可以试试看

@xiaoxiaojx 我找到解决方案了。 delete require.cache[] 数组里面填写的不能是moduleName,因为之前require失败,缓存里面找不到moduleName对应的地址。 应该改成path.resolve(process.cwd(), 'node_modules', moduleName)就可以成功删除了。

其实没必要这样操作,如果你的目的只是不想多下载一次,可以使用pnpm,每个版本的包只会下载一次。写成这样,如果原理了解不够透彻或者测试不够充分,容易留下隐患

@zurmokeeper 是有些依赖不是跨平台的,在另一个平台安装会报错。所以这些依赖的安装就不会通过先写入package.json再install。

回到顶部