知识点【JavaScript模块化】
JavaScript模块化历程
JavaScript发展变迁大概是一下几个步骤:
工具(浏览器兼容)
组件(功能模块)
框架(功能模块组织)
应用(业务模块组织)
但是经过了长长的后天努力过程JavaScript不断被类聚抽象,以更好的组织业务逻辑。从另一个角度而言,他也道出了JavaScript先天就缺乏的一项功能:模块
虽然W3C组织对HTML5规范推进以及各大厂对规范的大力支持,但是对于JavaScript本身而言,它的规范依然是薄弱的,它还有以下缺陷:
没有标准模块系统
标准库少(ECMAScript仅定义了部分核心库)
没有标准接口(JavaScript几乎没有定义过如WEB服务器数据库操作之类的标准统一接口)
缺乏包管理系统
CommonJS规范
CommonJS规范的提出,主要为了弥补JavaScript没有标准的缺陷,以达到想Python、Ruby和Java具备开发大型应用的基础能力,而不是停留在小脚本程序的阶段。
它的使用和定义也非常简单
CommonJS的模块规范
1 模块定义
// example.js
var x =5;
var addx = function (value){
return value + x;
}
module.exports.x = x;
module.exports.addx = addx;
定义关键字是exports这个module是什么意思呢:在CommonJS定义了每个文件都一个模块,也就是说module代表文件本身
2 模块引用
var example = require('./example.js'); console.log(example.x); //
console.log(example.addx(1)); //
CommonJS模块特点如下:
所有代码都运行在模块作用域,不会污染全局作用域。
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
模块加载的顺序,按照其在代码中出现的顺序。
3 看图
用一个图很明显的看出它们之间的引用关系
模块的实现
在Node中引入模块,需要经历3个步骤
1、路径分析
2、文件定位
3、编译执行
在Node中模块分为两类:一类是Node提供的模块,称为核心模块;另一类是用户编写的模块称为文件模块。
ES6 Module
ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。
// CommonJS模块
let { stat, exists, readFile } = require('fs'); // 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
上面代码的实质是整体加载fs
模块(即加载fs
的所有方法),生成一个对象(_fs
),然后再从这个对象上面读取3个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。
ES6 模块不是对象,而是通过export
命令显式指定输出的代码,再通过import
命令输入。
与CommonJS思想类似
1 模块定义
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
上面代码是profile.js
文件,保存了用户信息。ES6 将其视为一个模块,里面用export
命令对外部输出了三个变量。
export
的写法,除了像上面这样,还有另外一种。
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958; export {firstName, lastName, year};
通常情况下,export
输出的变量就是本来的名字,但是可以使用as
关键字重命名。
function v1() { ... }
function v2() { ... } export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};
注意规范:
export
命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
// 报错
export 1; // 报错
var m = 1;
export m;
上面两种写法都会报错,因为没有提供对外的接口。第一种写法直接输出1,第二种写法通过变量m
,还是直接输出1。1
只是一个值,不是接口。正确的写法是下面这样。
// 写法一
export var m = 1; // 写法二
var m = 1;
export {m}; // 写法三
var n = 1;
export {n as m};
function
和class
的输出,也必须遵守这样的写法。
// 报错
function f() {}
export f; // 正确
export function f() {}; // 正确
function f() {}
export {f};
另外,export
语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。
2 模块引用
// a.js
let a = 18;
let funcA = function () {
console.log('module A')
}; // 提供一个对外的借口并输出a变量和funcA方法
export {a, funcA}
引用
import {a, funcA} from "./a"; // 通过import 引入里面的变量和方法
console.log(a);
console.log(funcA());
引用的时候可以设置别名
import {a as aValue, funcA} from "./a"; // 通过import 引入里面的变量和方法
console.log(aValue);
console.log(funcA());
3 export default命令
从前面的例子可以看出,使用import
命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。
为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default
命令,为模块指定默认输出。
本质上,export default
就是输出一个叫做default
的变量或方法,然后系统允许你为它取任意名字
// export-default.js
export default function () {
console.log('foo');
}
然后你可以随便命名
// import-default.js
import customName from './export-default';
customName(); // 'foo'
但是正常引入模块的情况下你必须指定你要引入的变量或者方法,像下面一样:
import {funcA} from "./a.js"; // 这里你在引用的时候你必须知道a.js中定义的方法的名字
比较下正常和export default不同
// 第一组
export default function crc32() { // 输出
// ...
} import crc32 from 'crc32'; // 输入 // 第二组
export function crc32() { // 输出
// ...
}; import {crc32} from 'crc32'; // 输入
Module 的加载实现
上面是模块的语法,接下来看看如何在浏览器和Node中加载ES6模块
浏览器加载ES6模块
在 HTML 网页中,浏览器通过<script>
标签加载 JavaScript 脚本
<!-- 页面内嵌的脚本 -->
<script type="application/javascript">
// module code
</script> <!-- 外部脚本 -->
<script type="application/javascript" src="path/to/myModule.js">
</script>
上面代码中,由于浏览器脚本的默认语言是 JavaScript,因此type="application/javascript"
可以省略。
同样如果你的目录结构是这样的:
你在本地安装了node_js模块vue的话同样可以直接通过script标签引用
浏览器加载ES6模块
浏览器加载 ES6 模块,也使用<script>
标签,但是要加入type="module"
属性。
<script type="module" src="./foo.js"></script>
浏览器对于带有type="module"
的<script>
,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>
标签的defer
属性。
<script type="module" src="./foo.js"></script>
<!-- 等同于 -->
<script type="module" src="./foo.js" defer></script>
ES6 模块也允许内嵌在网页中,语法行为与加载外部脚本完全一致。
<script type="module">
import utils from "./utils.js"; // other code
</script>
参考资料:http://es6.ruanyifeng.com/#docs/module
知识点【JavaScript模块化】的更多相关文章
- 实现一个JavaScript模块化加载器
对任何程序,都存在一个规模的问题,起初我们使用函数来组织不同的模块,但是随着应用规模的不断变大,简单的重构函数并不能顺利的解决问题.尤其对JavaScript程序而言,模块化有助于解决我们在前端开发中 ...
- Javascript模块化编程(三):require.js的用法
Javascript模块化编程(三):require.js的用法 原文地址:http://www.ruanyifeng.com/blog/2012/11/require_js.html 作者: 阮一峰 ...
- Javascript模块化编程(二):AMD规范
Javascript模块化编程(二):AMD规范 作者: 阮一峰 原文地址:http://www.ruanyifeng.com/blog/2012/10/asynchronous_module_d ...
- Javascript模块化编程(一):模块的写法
Javascript模块化编程(一):模块的写法 作者: 阮一峰 原文链接:http://www.ruanyifeng.com/blog/2012/10/javascript_module.html ...
- Javascript模块化编程(二):AMD规范(转)
这个系列的第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块. (接上文) 七.模块的规范 先想一想,为什么模块很重要? 因为有了模块,我们就可以更方便地使用别人的代码,想要 ...
- Javascript模块化编程(一):模块的写法(转)
随着网站逐渐变成"互联网应用程序",嵌入网页的Javascript代码越来越庞大,越来越复杂. 网页越来越像桌面程序,需要一个团队分工协作.进度管理.单元测试等等......开发者 ...
- Javascript模块化规范
Javascript模块化规范 一.前端js模块化由来与演变 CommonJS 原来叫 ServerJS,推出 Modules/1.0 规范后,在 Node.js 等环境下取得了很不错的实践.09年下 ...
- Javascript模块化开发,使用模块化脚本加载工具RequireJS,提高你代码的速度和质量。
随着前端JavaScript代码越来越重,如何组织JavaScript代码变得非常重要,好的组织方式,可以让别人和自己很好的理解代码,也便于维护和测试.模块化是一种非常好的代码组织方式,本文试着对Ja ...
- Javascript 模块化开发上线解决方案
最近又换部门了,好频繁地说...于是把这段时间搞的小工具们简单整理了一下,作了一个小的总结.这次用一个简单业务demo来向大家介绍一下Javascript模块化开发的方式和自动化合并压缩的一些自己的处 ...
随机推荐
- jqgrid 设置隔行换色
有时,为美观效应,需要设置jqgrid隔行换色.jqgrid提供altRows属性来配置 启动隔行换色:altRows: true,//隔行换色 $("#filterGrid"). ...
- php实现远程网络文件下载到服务器指定目录(方法二)
<?php // maximum execution time in seconds set_time_limit (24 * 60 * 60); //if (!isset($_POST['su ...
- Liunx-cp命令
1. 复制当前目录的test文件夹 到/201904 目录 出现如下截图问题是因为test目录下还有文件,所以得加-r,使用递归拷贝.我现在用这个命令拷贝文件都加-r了,不管有文件还是没文件 2.复制 ...
- PostgreSQL索引页
磨砺技术珠矶,践行数据之道,追求卓越价值 [作者 高健@博客园 luckyjackgao@gmail.com] 本页目的,是起到索引其他所有本人所写文档的作用: 分类一:PostgreSQL基础 ...
- JS 获取每月有几周(每周五到周四算作一周)
原文地址:https://caochangkui.github.io/data-week/ 将每周五至周四算作一周,计算每月有几周,并获取到每周的起始时间. 日期格式化 Date.prototype. ...
- Java设计模式之适配器设计模式(项目升级案例)
今天是我学习到Java设计模式中的第三个设计模式了,但是天气又开始变得狂热起来,对于我这个凉爽惯了的青藏人来说,又是非常闹心的一件事儿,好了不管怎么样,目标还是目标(争取把23种Java设计模式接触一 ...
- Linux 僵尸进程
Linux 允许进程查询内核以获得其父进程的 PID,或者其任何子进程的执行状态.例如,进程可以创建一个子进程来执行特定的任务,然后调用诸如 wait() 这样的一些库函数检查子进程是否终止.如果子进 ...
- Salesforce随笔: 解决被指定给Chatter相关用户的RecordType无法被删除的问题
被指定给以下三组用户的RecordType无法在对应的Profile里取消占用: Chatter External User Chatter Free User Chatter Moderator U ...
- Unity 音频合并
将多个音频组合起来进行播放 代码如下: ; [SerializeField] AudioClip s1; [SerializeField] AudioClip s2; [SerializeField] ...
- yocto-sumo源码解析(五): bitbake/lib/bb/main.py
续前面分析,就该对bitbake_main()这个函数进行分析了,这个函数位于bitbake/lib/bb/main.py. 1. 检测主机操作系统是否为linux并且/dev/shm是否存在,pyt ...