题目描述
简单描述一下你所了解的 JS 模块化
答案解析
这道题目主要考察对 JS 模块化发展历程的了解, 以及发展中出现的一些规范和技术的掌握.
模块化是每一种语言膨胀的毕竟之路, JS 也不例外, 从原来的不支持模块化, 到现在支持模块化, 经历了一个曲折的过程.
JS 模块化产生的原因
以前 JS 本身没有模块化的概念和相关 API, 开发者一般都是在 html 中引入多个 script 标签, 业务逻辑复杂时, 就会带来很多问题, 比如:
全局作用域下容易造成变量冲突
文件只能按照 script 的书写顺序进行加载
开发人员必须主观解决模块和代码库的依赖关系
在大型项目中各种资源难以管理, 长期积累的问题导致代码库混乱不堪
模块化的作用
它可以帮助开发者拆分和组织代码, 方便复用功能, 降低项目开发的复杂度, 提高可维护性.
模块化方案及规范
一, 闭包
在模块化规范形成之前, JS 开发者通常利用闭包来解决全局作用域的污染问题, 一般是通过自定义暴露行为来区分私有成员和公有成员.
- let myModule = (function (Windows) {
- let name = '前端名狮' // private
- // public
- function setName(str) {
- name = str
- }
- // public
- function getName() {
- return name
- }
- return { setName, getName } // 暴露行为
- })(Windows)
二, CommonJS
JS 一开始只能运行于浏览器中, 所以一直被诟病. 为了突破 JS 的运行环境, CommonJS 规范出现了, 它定义了一些规范和 API(主要指非浏览器端的), 使 JS 语言拥有了类似 Python,Java 等后端编程语言的能力. 这样的话, 开发者可以使用 CommonJS API 编写应用程序, 然后这些应用可以运行在不同的 JavaScript 解释器和不同的主机环境中.
2009 年, 美国程序员 Ryan Dahl 创造了 node.JS 项目, 将 JavaScript 语言用于服务器端编程. 这标志 "Javascript 模块化编程" 正式诞生. Node.JS 是 CommonJS 规范的实现, webpack 也是以 CommonJS 的形式来书写的.
node 模块化示例:
- // math.JS 文件
- let math = {
- add: function(a, b) {
- console.log(a + b);
- }
- }
- module.exports = math;
- // App.JS 文件
- let math = require('./math.js');
- math.add(1, 1);
三, AMD && CMD
基于 commonJS 规范的 Node.JS 出来以后, 服务端的模块概念已经形成, 如果将其应用到浏览器端, 会出现什么问题呢?
- let math = require('./math.js');
- math.add(1, 1);
上面代码执行 require 的时候, 需要等 math.JS 文件加载完成, 才能继续执行, 如果加载时间过长, 页面就会出现 "假死" 状态.
这对服务器端不是一个问题, 因为所有的模块都存放在本地硬盘, 可以同步加载完成, 等待时间就是硬盘的读取时间. 但是, 对于浏览器, 这却是一个大问题, 因为模块都放在服务器端, 文件的加载需要通过 HTTP 请求, 等待时间取决于网速的快慢, 可能要等很长时间.
CommonJS 是主要为了 JS 在后端的表现制定的, 是不适合前端的 , 所以出现了 AMD,CMD,UMD 等等一系列可以在浏览器等终端实现的异步加载的模块化方案, 我们最熟悉的 require.JS 就是 AMD 的产物, SeaJS 是 CMD 的产物.
- // AMD
- require(['a', 'b'], function(math) {
- a.doSomething();
- b.doSomething();
- });
- // CMD
- define(function(require, exports, module) {
- var a = require('./a')
- a.doSomething();
- ...
- var b = require('./b') // 依赖可以就近书写
- b.doSomething();
- })
AMD 推崇依赖前置, CMD 推崇依赖就近.
上面这句话的意思就是: CMD 加载完某个依赖模块后并不执行, 只是下载而已, 在所有依赖模块加载完成后进入主逻辑, 遇到 require 语句的时候才执行对应的模块, 这样模块的执行顺序和书写顺序是完全一致的.
四, ES6 Module
ES6 的模块化已经不是规范了, 而是 JS 语言的特性. 随着 ES6 的推出, AMD 和 CMD 也随之成为了历史. ES6 模块与模块化规范相比, 有两大特点:
模块化规范输出的是一个值的拷贝, ES6 模块输出的是值的引用.
模块化规范是运行时加载, ES6 模块是编译时输出接口.
模块化规范输出的是一个对象, 该对象只有在脚本运行完才会生成. 而 ES6 模块不是对象, ES6 module 是一个多对象输出, 多对象加载的模型. 从原理上来说, 模块化规范是匿名函数自调用的封装, 而 ES6 module 则是用匿名函数自调用去调用输出的成员.
注意: 目前的浏览器几乎都不支持 ES6 的模块机制, 所以我们要用 Babel 把 ES6 的模块机制转换成 CommonJS 的形式, 然后使用 Browserify 或者 Webpack 这样的打包工具把他们打包起来. 达到模块化加载效果类似于 SeaJS 代码
展望
1. Http 2.0 对 JS 模块化的推动
JS 模块化定义的再美好, 浏览器端的支持粒度永远是瓶颈, http 2.0 正是考虑到了这个因素, 大力支持了 ES 2015 模块化规范.
随着 HTTP/2 流行起来, 请求和响应可以并行, 一次连接允许多个请求, 对于前端来说, 不再需要在开发和上线时再做编译这个动作.
ES2015 Modules 也只是解决了开发的问题, 由于浏览器的特殊性, 还是要经过繁琐打包的过程, 等 Import,Export 和 HTTP 2.0 被主流浏览器支持, 那时候才是彻底的模块化.
前端复杂度不断提高, 促使着模块化的改进, 代理 (浏览器, node) 的支持程度, 与前端特殊性(流量, 缓存) 可能前端永远也离不开构建工具, 新的标准会让这些工作做的更好, 同时取代, 增强部分特征, 前端的未来是更加美好的, 复杂度也更高.
2. HTML,CSS 模块化
文章中的 JS 的模块化还不等于前端工程的模块化, Web 界面是由 HTML,CSS 和 JS 三种语言实现, 不论是 CommonJS 还是 AMD 包括之后的方案都无法解决 CSS 与 HTML 模块化的问题.
HTML 与 CSS 模块化问题正是以后的方向. 一句话, 模块化仍在路上.
关注我
来源: http://www.jianshu.com/p/4cf556ea83c4