本文探讨在 ES6 模块系统中,使用 export default 导出对象时,通过 import { host } from './config' 进行解构赋值导致获取 undefined 的原因。主要涉及 Webpack 构建后模块结构变为 a.default,以及 Babel 版本对语法处理的差异。文章提供了通过添加默认导入或修改导出方式来解决该兼容性问题的具体方案。
ES6 解构赋值基础与模块导出
解构赋值
假设存在如下 config 对象:
const config = {
host: 'localhost',
port: 80
}
若需获取其中的 host 属性,可直接进行解构操作。
let { host } = config
将代码拆分为独立模块。
上述两段代码若置于同一文件中运行无异常。然而,在实际项目中,config 对象往往多处复用,因此将其单独存放于 config.js 文件中更为合理。
// config.js
export default {
host: 'localhost',
port: 80
}
在 app.js 中引入 config.js 模块。
// app.js
import config from './config'
let { host } = config
console.log(host) // => localhost
console.log(config.host) // => localhost
上述常规引用方式运行正常,但若在 import 语句中尝试使用解构赋值,情况会有所不同。
// app.js
import { host } from './config'
console.log(host) // => undefined
import 解构失败的原因分析
问题所在
import { host } from './config'
该代码语法层面无误。在早期使用 antd-init 创建的项目中,此类写法能够正常运行。然而,后续使用 vue-cli 和 create-react-app 创建的项目中,采用相同代码却无法正确获取 host 属性值。
// config.js
export default {
host: 'localhost',
port: 80
}
// app.js
import { host } from './config'
console.log(host) // => undefined
babel 对 export default 的处理机制
通过搜索 'es6 import 解构失败',找到相关文章《ES6的模块导入与变量解构的注意事项》。经分析,Webpack 和 Babel 转换后的模块结构发生了变化。
在 ES6 标准中,常规变量解构语法如下:
const a = { b: 1 }
const { b } = a
可直接通过解构赋值获取对象的同名属性,其效果等同于直接访问属性。
const b = a.b
ES6 模块导入语法提供了类似的解构形式:
import { resolve } from 'path'
若项目使用 Webpack 构建,需注意此处解构与普通变量解构存在差异。例如在 a.js 中定义以下代码:
export default {
b: 1
}
若依据普通变量解构规则导入该模块,即采用如下形式:
import { b } from './a'
这将导致错误,变量 b 无法被正确导出。主要原因在于 Webpack 构建过程的特性。使用模块导入时,Webpack 构建后的结果中,
import { b } from './a'
变量 b 实际上位于 a.default 属性下,而非直接位于 a 上。
a.default.b
由此可见,变量 b 存在于 a.default 中,而非 a 本身,因此解构结果为 undefined。若需正确解构,必须在模块内部显式导出变量,即:
export const b = 1
如此处理后,构建后的代码中变量 b 将直接位于 a 上,而非 a.default 下,从而支持正确解构。
因此,
export default {
host: 'localhost',
port: 80
}
构建后的代码结构变为:
module.exports.default = {
host: 'localhost',
port: 80
}
由于变量 host 位于 default 属性中,直接解构取不到值属于正常现象。那么,为何使用 antd-init 创建的项目却能成功获取呢?
解决 import 解构问题的方案
解决方案
再次搜索 GitHub 相关讨论发现,import 语句中的“解构赋值”并非真正的解构赋值,而是命名导入(named imports)。尽管语法相似,但二者存在本质区别,例如:
import { host as hostName } from './config' // 解构赋值中不能用 as
let obj = {
a: {
b: 'hello',
}
}
let {a: {b}} = obj // import 语句中不能这样子写
console.log(b) // => helllo
这种写法在 Babel 6 之前被允许,但在 Babel 6 之后不再支持。
// a.js
import { foo, bar } from "./b"
// b.js
export default {
foo: "foo",
bar: "bar"
}
因此,在 import 语句中增加一行默认导入:
import b from './b'
let { foo, bar } = b
或者采用以下写法:
// a.js
import { foo, bar } from "./b"
// b.js
let foo = "foo"
let bar = "bar"
export { foo, bar }
或者:
// a.js
import { foo, bar } from "./b"
// b.js
export let foo = "foo"
export let bar = "bar"
antd-init 项目使用了 babel-plugin-add-module-exports 插件,因此即使使用 export default 也能正常工作。
以上内容为本文全部总结,希望对各位的学习有所帮助,也欢迎大家继续支持本站。