JavaScript ES6中 module , import和export
2024-01-24 21:54 youxin 阅读(176) 评论(0) 编辑 收藏 举报
假如你想直接在 html 的 script 里面使用 import,你会遇到以下这两个问题:
- 需要给 script 标签添加 type='module' 属性
- 会遇到跨域问题,不单独启用一个服务器无法解决
如果不启动一个 server,访问 js 用的协议是 file,不在浏览器跨域允许的协议中。因此无法做到拿到 js 文件,源代码如下。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>hello world</div>
<script type='module'>
import obj from './a.js'
console.log(obj)
</script>
</body>
</html>
a.js
import {b} from './b.js'
var c = 20;
const a=5;
console.log('[a]',a)
export default {a,b}
解决的办法是,我们需要搭一个 node 服务器,以 express 为例,新建 server.js 文件。
var express = require("express");
var app = express();
app.use(express.static("./")).listen(3000);
在命令行输入
node server.js 跑起服务器,访问 localhost:3000,此时,跨域问题被解决。
或者安装一个插件:VSCode的一个扩展Live Server 。在
index.html
页面右键选择Open with Live Server
在script标签中写js代码,或者使用src引入js文件时,默认不能使用module形式,即不能使用import导入文件,但是我们可以再script标签上加上type=module属性来改变方式。
深入了解JavaScript Module
在其他编程语言中,我们可以通过划分模块,来组织庞大复杂的项目,而JS一开始并没有模块的概念,因为一开始JS的脚本就很简单。
后来随着JS的发展,前端要开发的项目越来越复杂,也越来越大型,因此需要更好的代码组织方式,于是JS也引入了模块功能,比如AMD,UMD,CommonJS等等不同的模块加载方式。
而在ES6规范中,也引入了官方的模块加载方式,也就是我们下面要讨论的知识。
什么是模块
在JS中,一个文件或者脚本就是一个模块,模块可以声明哪些变量或函数供外部使用,也可以引入其他模块的变量与函数为自己所用,在模块中,使用export
标记了可以从当前模块外部访问的变量和函数,使用import
从其他模块导入所需要的功能,如:
hello.js
//声明当前模块可以被其他模块使用的变量
export function sayHello() {
alert('Hello');
}
main.js
//导入其他模块的变量
import {sayHello} from './hello.js';
alert(sayHello);
sayHello();
在浏览器中使用模块
在支持ES6规范的浏览器中,可以使用用script type="module"></script>
标签可以声明模块或者导入其他模块,如:
<!doctype html>
<script type="module">
import {sayHi} from './say.js';
document.body.innerHTML = sayHi('John');
</script>
其实,目前前端项目开发中,并不会使用上面这种在浏览器中使用模块的方式,我们一般是创建一个工程项目,再通过webpack这类的构建工具解析各个模块的导出与依赖,最终打包上线的。
模块的特性
现代模式
模块的代码始终默认使用现代模式use strict
,例如对一个未声明的变量赋值或者重复声明变量都将产生错误。
<script type="module">
test = 5; // 未声明就使用,报错
let a = 1;
let a = 2;//重复声明,报错
</script>
作用域
每个模块都有自己的顶级作用域,也就是说,一个模块中的顶级作用域变量和函数在其他模块中是不可见的,如:
<script type="module">
let a = 10;
</script>
<script type="module">
alert(a)//报错,a is not defined
</script>
上面的示例中,如果把type=module
去掉,变成非模块代码,则可以正常运行。
模块代码解析
如果同一个模块被导入多次,那么它的代码只在第一次导入时便解析运行,比如下面的示例中,只会弹窗一次:
hello.js
alert(1);
index.html
<script type="module" src="./hello.js"></script>
<script type="module" src="./hello.js"></script>
this的值
在JS模块中,this
关键字的值undefined
,非模块脚本的顶级 this
是全局对象,比如浏览器的window
。
非模块代码
<script>
alert(this)
</script>
模块代码
<script type="module">
alert(this)//undefined
</script>
export
export命令用于模块内部导出可供外部使用的变量,导出的方式有:
在声明前导出
可以在变量或函数声明加上export命令,便可以直接导出该变量或函数,如:
export function sayHello(){
return "Hello";
}
export let a = 1;
导出与声明分开
也可以在声明之后,再使用export命令进行导出,不过这时候,导出的变量需要用花括号{}
包括起来,如:
//声明
function sayHello(){
return "Hello";
}
//导出
export {sayHello};//正确
//export sayHello;错误
也可以同时导出多个变量,如:
function sayHello(){
return "Hello";
}
const username='test';
export {username,sayHello}
默认导出
如果直接使用export导出,则使用者必须知道模块导出的变量名称,否则就无法加载,因为使用者需要先了解你的模块代码,但如果想让使用者直接使用而不用了解你的代码,则可以使用export default来指定模块的默认输出。
hello.js
export default function(){
return "hello"
}
main.js
import sayHi from './hello.js';//导出默认输出时,可以自定义变量的名称
sayHi();
导出时给个别名
在导出的时候,可以使用关键字as给导出的变量重新命名,如:
function sayHello(){
return "Hello";
}
export {sayHello as sayHi}
import
import命令用于在当前模块中导出其他模块的可用变量与函数。
整体导入
如果要导入一个模块的全部变量与函数,可以使用*
导出全部,但这种不按需导入的方式,可以会导入很多你不需要的东西,因此一般不推荐使用:
import * from './hello.js'
另外,可以使用as关键字给整体导出起一个别名,如:
import * as say from './hello.js';
say.hello();
按需导入
如果我们只需要导入其他模块的部分功能,则可以使用花括号{}
来指定要导出的变量与函数,如:
import {sayHi,sayHello} from './hello.js';
导入时给个别名
在导入的时候,也可以使用关键字as
给导入的变量与函数起个别名,如:
import {sayHi as hi, sayBye as bye} from './hello.js';
导入模块的默认导出变量时,则不需要使用花括号,如:
import sayHi from './sayHi.js'; //sayHi.js模块中有使用export default导出的变量或函数
小结
JS模块可以让我们把一个很大的前端项目拆分一个个JS小文件,然后再通过构建工具将这些文件组织起来打包运行,因此JS模块的使用,也是前端程序员的必须掌握的知识,希望通过这篇文件能让你对这方面的知识有更加深入的了解。
链接:https://juejin.cn/post/7023218660630069279
export default
:
- 用于导出模块的默认值。
- 可以在一个模块中只有一个
export default
。 - 在导入时,可以使用任意名称来引用默认导出的值。
- 导入时可以省略花括号
{}
。
示例:
// 模块A.js
const name = 'John';
export default name;
// 模块B.js
import myName from './A.js';
console.log(myName); // 输出: 'John'
使用export输出值,而不是使用export default输出对象
export default输出一个对象
- 在a.js中输出一个对象,属性value的值为10
// a.js
export default {
value: 10,
}
- 在b.js中引用a.js中输出的对象,并输出一个打印a对象的函数
// b.js
import defA from './a';
export default function logA() {
console.log(defA);
}
- 在c.js中引用a.js中输出的对象与b.js中的方法,设置a对象的value为20后调用logA方法
// c.js
import defA from './a';
import logA from './b';
defA.value = 20;
logA();
// {value: 20}
- 可以看到打印出来的a对象的value值为20,这说明a提供的对象中的属性值被修改了。
import的值是只读的
- 在a.js中输出一个变量a,值为10
// a.js
export let a = 10;
- 在b.js中获取a.js中输出的a,并将其赋值为20
// b.js
import { a } from './a';
a = 20;
// ReferenceError: a is not defined
- 在经过webpack打包后在浏览器中将报出引用错误。
- 换一种方式看看能不能修改a的值,在c.js中用import as来获取a.js输出的值,再修改a的值
// c.js
import * as A from './a';
A.a = 20;
// TypeError: Cannot set property a of #<Object> which has only a getter
- 可以看到,经过webpack打包之后,依然不能如愿修改a的值。
当前脚本内可以修改输出的值
- a.js中输出a为10,并在两秒后修改变量a的值为20
// a.js
export let a = 10;
setTimeout(function () {
console.log('两秒后修改a的值为20');
a = 20;
}, 2000);
- b.js中获取a的值,分别在获取后就打印和获取三秒后打印
import { a } from './a';
console.log(a);
setTimeout(function () {
console.log(a);
}, 3000);
// b.js:10
// a.js: 两秒后修改a的值为20
// b.js: 20
- 可以看到当前脚本内可以修改输出的值,并且会在修改后应用到所有引用该值的地方。
补充说明
- export或export default一个对象时,对象的属性在外部脚本中都是可以修改的。
- export default的值在外部脚本中也是不能修改的,只是属性可以被修改。
对我的指导
为了保证数据的稳定性,应当尽量使用export输出多个变量的值,而不是使用export default输出一个包含多个属性的对象。
链接:https://juejin.cn/post/7053619070905614349
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。