发布的npm包ES6导入失效,模块创建遇技术问题
i.default.capitalize is not a function错误 我之前也碰到过类似的问题,咱们一步步拆解排查,应该能很快解决:
一、先确认模块1的导出是不是正确的
首先得看看你模块1里的script.js是怎么导出方法的。如果是想让别人用默认导入调用capitalize,那代码得写成这样:
// 模块1的script.js class MyUtils { capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } } // 导出实例,这样别人导入后直接就能调用方法 export default new MyUtils();
或者直接导出包含方法的对象也可以:
export default { capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } };
要是你用的是命名导出(比如export const capitalize = () => {...}),那模块2里就得用import { capitalize } from '你的模块名'来导入,不能用默认导入再去调用xxx.capitalize,不然肯定找不到方法。
二、检查gulp打包配置有没有“破坏”模块结构
很多时候报错是因为gulp打包时把ES6模块转成了其他格式,导致导出的结构不对。
1. 看看Babel的配置
如果你用了Babel转译,得确保它没有把ES6模块改成CommonJS。在gulp的babel任务里要这么配置:
// gulpfile.js里的babel处理任务 .pipe(babel({ presets: [ ['@babel/preset-env', { modules: false, // 关键!不转译模块语法,保留ES6的export/import targets: { browsers: ['last 2 versions'] } }] ] }))
要是把modules设成了commonjs,那导出会变成module.exports,这时候ES6默认导入拿到的是一个带default属性的对象,调用的时候得写xxx.default.capitalize,这显然不是咱们想要的,所以一定要保留ES6模块格式。
2. 瞅一眼打包后的文件
打开模块1打包好的script.min.js,最后应该能看到类似export default xxx;的代码。如果是module.exports = xxx;,那说明打包把模块转成CommonJS了,这时候要么改打包配置,要么模块2里用const MyUtils = require('你的模块名')来导入。
三、模块2的导入和调用方式要对应
如果模块1是默认导出的实例/对象,那模块2里得这么写:
// 模块2的script.js import MyUtils from '你的模块名'; // 直接调用方法就行 console.log(MyUtils.capitalize('hello'));
要是模块1是命名导出,那导入的时候就得用大括号把方法名包起来:
import { capitalize } from '你的模块名'; console.log(capitalize('hello'));
另外,模块2如果也是用gulp打包,别只用babel+uglify,最好用webpack-stream或者rollup-stream这类工具,因为普通的gulp任务不会处理node_modules里的模块导入,浏览器识别不了import语句,就会把它转成奇怪的变量(比如报错里的i),自然找不到方法。
四、模块1的package.json配置不能少
模块1的package.json里一定要加这两个关键字段:
{ "name": "你的模块名", "version": "1.0.0", "main": "script.min.js", // 指向打包后的文件 "type": "module" // 告诉Node.js这是ES6模块 }
没加"type": "module"的话,Node会默认把文件当成CommonJS处理,就算你写了ES6导出,导入的时候结构也会乱。如果想同时支持CommonJS和ES6导入,可以加exports字段:
{ "exports": { ".": { "import": "./script.min.js", "require": "./script.cjs" } } }
这样不管别人用import还是require都能正常用。
五、本地调试更高效,别总npm publish
调试的时候不用每次都发布到npm,用本地链接就行:
# 在模块1的目录里执行 npm link # 在模块2的目录里执行 npm link 你的模块名
这样模块2直接用本地的模块1代码,改完就能测试,省得来回发布麻烦。
内容的提问来源于stack exchange,提问作者David Vielhuber

