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

  • 相关阅读:
    Linux下sed,awk,grep,cut,find学习笔记
    Python文件处理(1)
    KMP详解
    Java引用详解
    解决安卓中页脚被输入法顶起的问题
    解决swfupload上传控件文件名中文乱码问题 三种方法 flash及最新版本11.8.800.168
    null id in entry (don't flush the Session after an exception occurs)
    HQL中的Like查询需要注意的地方
    spring mvc controller间跳转 重定向 传参
    node to traverse cannot be null!
  • 原文地址:https://www.cnblogs.com/luotianshuai/p/7677691.html
Copyright © 2011-2022 走看看