第二章 - 04: Javascript 模块化管理的来世今生

模块管理这个概念其实在前几年前端度过了刀耕火种年代之后就一直被提起,那么我们有思考过这个模块管理具体指的是什么东西?什么样子的展现形式?历史由来?现在是什么样的一个状态?

直接回想起来的就是 cmd amd commonJS 这三大模块管理的印象。但是大家清楚 cmd amd commonJS 为什么会出现么?接下来,我们就一起来瞅瞅这具体是啥情况。

感觉自己在每一个阶段,对于同一个技术的理解都不一样。

一、什么是模块化开发

为了让一整个庞大的项目看起来整整齐齐,规规整整而出现的模块化管理,我们常见的三种模块化管理的库: requireJS、seaJS、commonJS规范 ( 注意,这里的commonJS不是一个库,而是一种规范) 逐渐的在我们日常的开发中出现。 同时依赖于 这三类规范而出现的一些构建工具,但最后都败给了 webpack 。这是一篇介绍 webpack 基本入门的文章,可以结合着这篇文章来进行解读。

《前端之路》之 webpack 4.0+ 的应用构建

1-1、模块化第一阶段

在这个阶段中,我们常常会把非常多复杂的功能 封装成一个个的函数:

  1. function f1() {
  2. // todo
  3. }
  4. function f2() {
  5. // todo
  6. }
  7. .
  8. .
  9. .

但是当 整个项目变大了以后,就会遇到很多问题,都是定义的全局变量,形成了比较严重的污染,还有可能会出现因为重命名导致的各种问题。所以这些是需要进化的。所以就会进入到模块化的第二阶段: 对象。

1-2、封装到对象

到了第二个阶段为了避免全局变量的污染,我们会将单个模块封装到一个对象内部。如下:


  1. const module = {
  2. _number 10,
  3. f1: () => {
  4. console.log(123)
  5. },
  6. f2: () => {
  7. console.log(456)
  8. },
  9. .
  10. .
  11. .
  12. }

这样我们就没个模块定义一个对象,在需要的时候直接调用就好了,但是这样也会存在一个问题

这样写的话会暴露全部的对象内部的属性,内部状态可以被外部改变. 例如:

  1. module._number = 100

如果我们支付相关模块这样子来写的话。我们随意的来改变支付的金额,那样就会出现比较危险的情况。

1-3、 对象的优化

后来,聪明的人类就想到了利用 立即执行函数 来达到 不暴露私有成员的目的

  1. const module2 = (function() {
  2. let _money = 100
  3. const m1 = () => {
  4. console.log(123)
  5. }
  6. const m2 = () => {
  7. console.log(456)
  8. }
  9. return {
  10. f1: m1,
  11. f2: m2
  12. }
  13. })()

通过立即执行函数,让外部根本没有时间从外部去修改内部的属性,从而达到一定的防御作用。

以上就是模块化开发的基础中的基础。 没有库,没有规范,一切的维护都是靠人力,一切的创新,都是来源于 解放生产力。

二、模块化管理的发展历程

2-1、CommonJS

CommonJS 的出发点: JS 没有完善的模块系统,标准库较少,缺少包管理工具。(虽然这些东西,在后端语言中已经是 早就被玩的不想玩的东西了)

伴随着 NodeJS 的兴起,能让 JS 可以在任何地方运行,特别是服务端,以达到 也具备 Java、C#、PHP这些后台语言具备开发大型应用的能力,所以 CommonJS 应运而生。

2-1-1、 CommonJS常见规范

  • 一个文件就是一个模块,拥有单独的作用域
  • 普通方式定义的 变量、函数、对象都属于该模块内
  • 通过 require 来家在模块
  • 通过 exportsmodule.exports 来暴露模块中的内容

我们通过编写一个 Demo 来尝试写这个规范

Demo 1 : 通过 module.exports 来导出模块

  1. // module.js
  2. module.exports = {
  3. name: "zhang",
  4. getName: function() {
  5. console.log(this.name);
  6. },
  7. changeName: function(n) {
  8. this.name = n;
  9. }
  10. };
  11. // index.js
  12. const module = require("./module/index");
  13. console.log(module) // {name: "zhang", getName: ƒ, changeName: ƒ} "commons"

Demo 2 : 通过 exports 来导出模块

  1. // module1.js
  2. const getParam = () => {
  3. console.log(a);
  4. };
  5. let a = 123;
  6. let b = 456;
  7. exports.a = a;
  8. exports.b = b;
  9. exports.getParam = getParam;
  10. // index.js
  11. const module1 = require("./module/index1");
  12. consoel.log(module1, "commons1") // {a: 123, b: 456, getParam: ƒ} "commons1"

Demo 3 : 同时存在 exports 和 module.exports 来导出模块

  1. // module2.js
  2. let a = 123;
  3. const getSome = () => {
  4. console.log("yyy");
  5. };
  6. const getA = () => {
  7. console.log(a);
  8. };
  9. exports.getSome = getSome;
  10. module.exports = getA;
  11. // index.js
  12. const module2 = require("./module/index2");
  13. consoel.log(module2, "commons2") // function getA() {...}

总结 : 通过这样的一个对比的例子就可以比较清晰的对比出 exports 和 module.exports 的区别:

1、当 exports 和 module.exports 同时存在的时候,module.exports 会盖过 exports

2、当模块内部全部是 exports 的时候, 就等同于 module.exports

3、最后 我们就可以认定为 exports 其实就是 module.exports 的子集。

以上就是我们对于 CommonJS 的一个初级的介绍。

还有一个硬性的规范,这里我们只是做一下列举,就不做详细的 Demo 演示了。

2-1-2、 CommonJS 规范 --- 加载、作用域

所有代码都运行在模块作用域,不会污染全局作用域;模块可以多次加载,但只会在第一次加载的时候运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果;模块的加载顺序,按照代码的出现顺序是同步加载的;

2-1-3、 CommonJS 规范 --- __dirname、__filename

__dirname代表当前模块文件所在的文件夹路径,__filename代表当前模块文件所在的文件夹路径+文件名;

以上就是关于 CommonJS 规范 相关的介绍,更下详细的 文档,可以查阅 CommonJS 规范 官方文档。

2-2、CommonJS 与 ES6(ES2015) 的 import export

在 ES2015 标准为出来之前,最主要的是CommonJS和AMD规范。上文中我们已经介绍了 CommonJS 规范(主要是为了服务端 NodeJS 服务),那么当 ES6标准的出现,为浏览器端 模块化做了一个非常好的补充。

这里,我们还是比较详细的介绍下 ES6 的 import export 的系列特性。

2-2-1、 ES6 的 export

export用于对外输出本模块(一个文件可以理解为一个模块)变量的接口

Demo 1 export { xxx }

  1. // export/index.js
  2. const a = "123";
  3. const fn = () => window.location.href;
  4. export { fn };
  5. // show/index.js
  6. const ex = require("./export/index");
  7. import x from "./export/index";
  8. import { fn } from "./export/index";
  9. console.log(ex, "export1"); // {fn: ƒ, __esModule: true} "export1"
  10. console.log(x, "export-x"); // undefined "export-x"
  11. console.log(fn, "export-fn"); // function() { return window.location.href; } "export-x"

Demo 2 export default xxx

  1. // export/index1.js
  2. const a = "123";
  3. const fn = () => window.location.href;
  4. export default fn;
  5. // show/index1.js
  6. const ex1 = require("./export/index1");
  7. import x from "./export/index1";
  8. console.log(ex1, "export1");
  9. // {default: ƒ, __esModule: true} "export1"
  10. console.log(x, "export2");
  11. // ƒ fn() {return window.location.href;} "export2"

通过 Demo 1 和 Demo 2 我们可以很好的 对比了下 export 和 export default 的区别

export 可以导出的是一个对象中包含的多个 属性,方法。

export default 只能导出 一个 可以不具名的 对象。

import {fn} from './xxx/xxx' ( export 导出方式的 引用方式 )

import fn from './xxx/xxx1' ( export default 导出方式的 引用方式 )

同时,我们发现 可以直接使用 require 来引用

这个也能引用其实有点神奇的。但是功能和 import 一样。(原因就是我这里起了 webpack 的 server 相关)

2-2-2、 ES6 的 import

这里就同上面 Demo 中的例子一样了得出的结论就是

import {fn} from './xxx/xxx' ( export 导出方式的 引用方式 )

import fn from './xxx/xxx1' ( export default 导出方式的 引用方式 ,可以不用在意导出模块的模块名)

总结: 之前对于 模块的导出、引用 的概念都比较的魔幻,这次通过知识的梳理终于搞清楚了。

《前端之路》之 Javascript 模块化管理的来世今生的更多相关文章

  1. JS模块化,Javascript 模块化管理的历史

    模块管理这个概念其实在前几年前端度过了刀耕火种年代之后就一直被提起. 直接回想起来的就是 cmd amd commonJS 这三大模块管理的印象.接下来,我们来详细聊聊. 一.什么是模块化开发 为了让 ...

  2. 2.精通前端系列技术之JavaScript模块化开发 seajs(一)

    在使用seajs模块化开发之前,直接在页面引用js会容易出现冲突及依赖相关的问题,具体问题如下 问题1:多人开发脚本的时候容易产生冲突(比如全局参数冲突,方法名冲突),可以使用命名空间降低冲突,不能完 ...

  3. Webpack:前端资源模块化管理和打包工具

    一.介绍: Webpack 是当下最热门的前端资源模块化管理和打包工具.它可以将许多松散的模块按照依赖和规则打包成符合生 产环境部署的前端资源.还可以将按需加载的模块进行代码分隔,等到实际需要的时候再 ...

  4. 关于Javascript模块化和命名空间管理的问题说明

    最近闲下来的时候,稍微想了想这个问题.关于Javascript模块化和命名空间管理 [关于模块化以及为什么要模块化] 先说说我们为什么要模块化吧.其实这还是和编码思想和代码管理的便利度相关(没有提及名 ...

  5. Javascript模块化编程之路——(require.js)

    转自:http://www.ruanyifeng.com/blog/2012/10/javascript_module.html Javascript模块化编程(一):模块的写法 随着网站逐渐变成&q ...

  6. 使用mini-define实现前端代码的模块化管理

    这篇文章主要介绍了使用mini-define实现前端代码的模块化管理,十分不错的一篇文章,这里推荐给有需要的小伙伴. mini-define 依据require实现的简易的前端模块化框架.如果你不想花 ...

  7. 前端常用的库和实用技术之JavaScript 模块化

    模块化概念 AMD是requirejs在推广过程中对模块化定义的规范化产出. 异步加载模块,依赖前置,提前执行 Define定义模块define(['require','foo'],function( ...

  8. Javascript模块化规范

    Javascript模块化规范 一.前端js模块化由来与演变 CommonJS 原来叫 ServerJS,推出 Modules/1.0 规范后,在 Node.js 等环境下取得了很不错的实践.09年下 ...

  9. Javascript模块化开发,使用模块化脚本加载工具RequireJS,提高你代码的速度和质量。

    随着前端JavaScript代码越来越重,如何组织JavaScript代码变得非常重要,好的组织方式,可以让别人和自己很好的理解代码,也便于维护和测试.模块化是一种非常好的代码组织方式,本文试着对Ja ...

随机推荐

  1. jQuery的学习笔记

    JQuery学习笔记 Chapter one初识jQuery 1.2测试jQuery 在jQuery库中,$是jQuery的别名,如:$()相当于jQuery() 注意:在使用JQuery进行开发的时 ...

  2. NSURLSession 所有的都在这里(二)

    前面一篇我们说了什么? 这是这个关于NSURLSession的第二篇文章,第一篇再加上这篇文章,就大概的把NSURLSession的API以及一些简单使用我们也就说的差不多了,这篇文章总结哪些点呢?相 ...

  3. 拿到月薪30K,必选一些Python好书!

    论述: Python是所有编程语言中与人工智能最紧密相连的编程语言,阿尔法狗都在使用的 Python 语言. 教育部早在两个月前(自2018年3月起)就以及公布:大学生全国计算机二级考试中必考Pyth ...

  4. 算法训练 2的次幂表示(蓝桥杯C++写法)

    问题描述 任何一个正整数都可以用2进制表示,例如:137的2进制表示为10001001. 将这种2进制表示写成2的次幂的和的形式,令次幂高的排在前面,可得到如下表达式:137=2^7+2^3+2^0 ...

  5. Java基础:Java的四种引用

    在Java基础:java虚拟机(JVM)中,我们提到了Java的四种引用.包括:强引用,软引用,弱引用,虚引用.这篇博客将详细的讲解一下这四种引用. 1. 强引用 2. 软引用 3. 弱引用 4. 虚 ...

  6. spark SQL随笔

    sparkSQL 1.主要的数据结构 DataFreames 2.开始使用:SQLContext 创建步骤: Val  sc:sparkContext Val  sqlContext=new org. ...

  7. .net 弹出消息框后,页面样式变乱

    点击按钮,执行提交操作,弹出消息框后,页面的样式变乱,解决方法: 首先,确定使用的css样式正确,页面中的宽高值保持规范统一: 然后,弹出框避免使用Response.Write(),如下所示 Resp ...

  8. mysql学习 第二章 数据库的基本操作

    3.1   创建数据库 MySQL安装好之后,首先需要创建数据库,这是使用MySQL各种功能的前提.本章将详细介绍数据的基本操作,主要内容包括:创建数据库.删除数据库.不同类型的数据存储引擎和存储引擎 ...

  9. 写XML

    //创XML建对象 XmlDocument doc = new XmlDocument(); //bool a = false; //声明根节点 XmlElement books; //判断文件是否存 ...

  10. nginx常用配置系列-虚拟主机

    本来准备详尽的出一份nginx配置讲解,但nginx功能配置繁多,平常使用中使用最多的一般有: 1. 虚拟主机配置 2. HTTPS配置 3. 静态资源处理 4. 反向代理 ============= ...