由于JavaScript缺乏模块体系,在ES6之前,社区指定了一些模块加载方案,比如node.js所遵循的commonJS规范。而在ES6在语言层面上实现了模块功能,完全可以取代现有的commonJS等规范,成为浏览器、服务器通用的模块解决方案。本篇博客目录如下:
1.如何使用exports
2.如何使用module.exports
3.exports与module.exports的区别
4.使环境支持module语法
5.export与export default的基本用法
在es6还没有出现的日子里,通过commonJS实现模块加载会接触到两个方法,exports与module.exports。我们先来看exports,在node.js中,我们通常会以exports.name=something
的形式来使用exports
//导出文件mout.js
function outputA(){
console.log("this is output A");
}
function outputB(){
console.log("this is output B");
}
exports.outputA=outputA;
exports.outputB=outputB;
//导入文件module.js
let mod=require("./mout.js");
console.log(mod);
这时会输出
如果你想在module.js中使用outputA或outputB函数,就得
let {outputA,outputB}=require("./mout.js");
//{}中变量名必须与mout.js导出函数名相同
outputA();
outputB();
上面的写法也等同于
let _mout=require("./mout.js");
//变量a,b的变量名可以随意
let a=_mout.outputA;
let b=_mout.outputB;
a();
b();
在commonJS中还有一种方法可以导出模块。我们通常以module.exports=something
的形式使用它
//还是那个导出文件
function outputA(){
console.log("this is output A");
}
function outputB(){
console.log("this is output B");
}
module.exports=outputA;
module.exports=outputB;
//还是那个导入文件
let mod=require("./mout.js");
console.log(mod);
这个时候我们会发现输出是这样子的
虽然我们在mout.js中module.exports两次,但输出却告诉我们只有最后一次module.exports是有效的,第一次module.exports被覆盖掉了。如果我们想在module.js中使用outputB只需mod()
即可。
所以,当我们需要将模块定义为一个类时,使用module.exports是唯一的选择。
其实,module.exports 初始值为一个空对象 {},require() 返回的是 module.exports 而不是 exports,exports只不过是指向的 module.exports 的引用。
赋给exports的所有属性或方法最终都赋值给了module.exports。但,这是有前提条件的,那就是module.exports是空对象,如果module.exports具有了一些属性或方法,那么赋给exports的所有属性或方法都会被忽略掉,譬如
//还是TA
function outputA(){
console.log("this is output A");
}
function outputB(){
console.log("this is output B");
}
exports.outputA=outputA;
module.exports=outputB;
//是TA是TA就是TA
let mod=require("./mout.js");
console.log(mod);
这样子,输出的就是酱紫的
而且无论exports在module.exports前还是后,结果都是module.exports生效,而exports看起来并没有发挥什么卵用。
通过上面的推导,我们可以猜测给module.exports添加属性等同于于给exports添加属性
//。。。。。
function outputA(){
console.log("this is output A");
}
function outputB(){
console.log("this is output B");
}
module.exports.outputA=outputA;
module.exports.outputB=outputB;
//。。。。。
let mod=require("./mout.js");
console.log(mod);
非常遗憾的是,到目前为止,node(v9.3.0)尚不支持module语法,但不久后的将来是一定能够实现的。为了让node等环境支持module语法,我们需要用到babel或者类似于babel的工具对代码进行编译。下面以babel为主进行讲解。
首先需要说明的是,如果你通过babel-node等工具试图直接在命令行中运行es6代码可能会出现错误。这是因为
Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。而且像import和export这两个命令现在在任何浏览器中都是不支持的, 同时babel也无法将其转换为浏览器支持的ES5, 原因在于:babel只是个翻译,假设a.js 里 import 了 b.js, 对a.js进行转码,只是翻译了a.js,并不会把b.js的内容给读取合并进来, 如果想在最终的某一个js里,包含 a.js,b.js 的代码,那就需要用到打包工具
接下来就以webpack为例展示如何进行转码
首先,你得有个目录,并且你安装了node与npm
进入到目录中,运行npm init
init完了之后,执行npm install webpack --save-dev --no-optional
接下来,你需要安装babel-loader与babel-core
npm install --save-dev --no-optional babel-loader babel-core
然后根据你的需求选择转码规则,目前有这些
我们使用latest吧,npm install --save-dev --no-optional babel-preset-latest
之后我们还会用到path,所以一并安装上npm install --save-dev --no-optional path
目前为止,目录下的webpack.config.js应该是酱紫的
这一堆工作做完之后,我们还需要在根目录下创建一个webpack配置文件——webpack.config.js,具体内容如下
const path=require("path");
module.exports = {
entry: __dirname + "/app/import.js",//入口文件
output: {
path: __dirname + "/dist",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
},
module:{
loaders:[
{
test:/\.js$/,
loader:'babel-loader',//-loader不可省略
include:path.resolve(__dirname,'/app'),//babel只处理指定目录
options:{
presets:['latest']
}
}
]
}
}
其中,options字段值得我们注意。一般来说babel有三种配置方式:
通过webpack.config.js loaders中的options字段
通过package.json中的babel字段
通过.babelr文件
三种方式具体的配置内容是一模一样的,我比较喜欢第一种
这样子一通折腾下来后,我们在app下创建两个文件import.js与export.js。
//import.js
import {alias,outputB} from "./export.js"
console.log(alias);
console.log(outputB);
//export.js
function outputA(){
console.log("this is output A");
}
function outputB(){
console.log("this is output B");
}
export {outputA as alias,outputB}
之后在根目录下运行webpack
,等待打包完成之后,在dist目录下就会出现bundle.js,我们通过html引用也好,还是直接node运行都可以,输出结果是
普通的、老老实实的、朴实无华的操作
function outputA(){
console.log("this is output A");
}
function outputB(){
console.log("this is output B");
}
export {outputA,outputB}
import {outputA,outputB} from "./export.js"
outputA()
在引入文件中,如果需要使用export.js中的函数,用户必须先知道它的名字,如果用户不想翻文档或者怎么滴,这样子就不太方便,所以就有了export default
function outputA(){
console.log("this is output A");
}
export default outputB;
import alias from "./export.js"
console.log(alias);
alias();
注意,在import的时候,我们已经不用使用{}了
模块整体加载
如果我们需要加载模块的全部接口,那么只需
function outputA(){
console.log("this is output A");
}
function outputB(){
console.log("this is output B");
}
export {outputA,outputB}
import * as alias from "./export.js"
export时使用别名
function outputA(){
console.log("this is output A");
}
function outputB(){
console.log("this is output B");
}
export {outputA as alias,outputB}
接下来,我们进行一些比较骚气的操作
function outputA(){
console.log("this is output A");
}
function outputB(){
console.log("this is output B");
}
export {outputA as alias,outputB}
export default outputB;
这段代码既有export又有export default
我们先
import * as x from "./export.js"
console.log(x);
这是,console出来的x是
这里我们可以看出export default的本质是输出一个叫做default的方法,系统允许我们对它取任意名称
接下来,酱紫
import x from "./export.js"
console.log(x);
输出结果是
也就是说,如果import时变量只有一个,而且外面没有{},那么该变量接受的就是export default。如果import时变量放在{}里,那么就会拿到对应的export。我们可以用这种方式同时接受export与export default
import x,{alias,outputB} from "./export.js"