zoukankan      html  css  js  c++  java
  • 知识点【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);  // 5
    console.log(example.addx(1)); // 6

    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};

    functionclass的输出,也必须遵守这样的写法。

    // 报错
    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

  • 相关阅读:
    小甲鱼系列→第一章.基础知识
    FusionCharts-堆栈图、xml格式、刷新数据、添加事件link、传参
    FireBug提示:本页面不包含 JavaScript,明明是包含js的。
    Angular-Chart.js 初接触;;;
    错误 Metadata file 'C:CommoninDebugCommon.dll' could not be found
    UML--PowerDesigner使用小结
    java8入门 错误:找不到或者无法加载主类
    “基础提供程序在Open上失败”
    设计模式--目录开篇
    020医疗项目-模块二:药品目录的导入导出-介绍药品表
  • 原文地址:https://www.cnblogs.com/luotianshuai/p/7677691.html
Copyright © 2011-2022 走看看