JavaScript中的模块化

目录

 

1ES6之前的模块化策略

2ES6的模块化


1ES6之前的模块化策略

模块化可以减少全局变量的使用,方便代码的维护和书写。

JavaScript中的模块化_第1张图片

这二者的区别主要在于:CommonJS对模块的加载时采用的是同步(synchronous)的方式,而AMD对模块的加载采用的是异步(Asynchronous)的方式。因为在服务器端,加载的时延主要是对硬盘的读写,而在客户端,加载的时延主要的是网络传输的时延,而网络常常是不稳定的,所以如果在客户端采用同步的方法,会影响浏览器的渲染。

1.1AMD规范

  • 仅仅需要全局环境下定义require 与define ,不需要其他的全局变量
  • 模块实现中声明依赖,以来的加载与执行由加载器操作
  • 提供了打包工具自动分析依赖并合并
define(function(require) {
    //通过相对路径获得依赖模块
    const module1 = require("./module1");
    //模块暴露
    return function() {
        ....    
    };
});

当前有两个库实现了AMD规范:require.js和curl.js。

(1)require.js的使用:http://www.ruanyifeng.com/blog/2012/11/require_js.html (转自  阮一峰的网络日志)

require.js解决的问题就是当引入多个script时,多个js文件同步加载影响浏览器渲染以及它们之间依赖关系复杂的问题。

其中解决  影响浏览器渲染  的问题,平常的方法:

方法一:放在body的底部加载,这样页面渲染完毕才会加载js文件

方法二:指定js文件的属性

1.2CommonJS的使用

Node.js 正是使用的是CommonJS规范,典型的CommonJS规范的模块引入与暴露过程:

//模块引入
const bar = require("./bar");
//模块暴露
module.exports = function () {
    //....
}

2 ES6的模块化

实际上require的模块化加载时运行时加载,即只有运行到这个地方才能确定相应的依赖关系,也即“运行时(动态)加载”。而ES6在编译时便能确定相应的依赖关系,也即“编译时(静态)加载”,效率要高(因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化")。

这其中主要是export和import的使用。

(1)export的使用

  • export用来声明模块对外的接口
//向外声明方法接口
export var meth1 = function(){
    console.log("this is a module");
}
//向外声明变量接口
export var variable = "111";
//用类似对象的方法,向外声明一个接口,里边可以有方法也可以有属性
function foo(){
    console.log("this is a module, too");
}
var abc = "123";
export {abc, foo};
  • 可以用as作为输出(对外)的变量重命名
var a = "name1";
export {a as b};

实际上,在这里就可以看出,export的变量或者方法的名称,必须与import的变量或者方法名称单向对应(import引入的变量或者方法,必须能在export中找到,而export里边的变量不一定都要被import到),否则这里为什么还要as来重命名呢,岂不是多次一举。再者如果不是对应的,如何找到import引入的到底是哪个变量或者方法。

  • export 表现的是一个接口,按照java上说的接口应该也是一个对象,所以
var m = 1;
export {m}; //正确
export m; //错误
export 2;  //错误
  • export 输出接口的变量,与其值是动态绑定的关系,可以实时获取模块内部的数据。这与export、import的“静态编译”的特点相对应

注意:CommonJS中的模块输出的是一个值得拷贝,而ES6模块输出的是值得引用,因此取值时,只是产生一个只读的引用,只有真正用到才回去模块里取值,不会有缓存值。

(2)import的使用

  • 基本用法
import {变量名(对应export里边的名称)} from "导入模块的路径名"

注意:路径名可以只是模块名而不带有路径,但是必须有相应的配置文件。

  • import会声明提前,本质是“编译时加载”(代码运行之前就已经加载了)
foo();  //可以运行
import {foo} from "./module.js"  
  • import和export一样,不能与 表达式和变量 产生联系。因为“编译时加载”,只有运行才会做出if里边的判断,这显然是不符合逻辑的。
if(x === 1){
    import {fpp}  from "./module";
}
  • 整体加载。除了可以加载某个特定的变量或者方法,还可以将整个方法和变量加载到某个对象上。如下,
import * as obj form "./module";
console.log(obj.abc);
console.log(obj.xyz);

(3)default的使用

export default 后边可以加匿名函数、非匿名函数,但这在外部看来都是匿名函数。

var m = 1;
export default m;
//export default var m = 1; 是错误的
//export var m = 1;  是正确的

export default function(){
    console.log("this is an anonymous function!");
}

function foo(){
    console.log("this is not an anonymous function");
}
export default foo;

实际上,export 就是输出了一个叫做default 的变量或者方法,然后系统允许你为它取名字。

所以

export default foo;
//等价于
export {foo as default};


import abc from "./module";
//等价于
import {default as abc} from "./module";

(3)模块的循环加载

  • CommonJS的循环加载
  • ES6模块的循环加载

(4)如何使用模块

如果要使用的常量非常多或者方法是使用非常频繁,可以建立一个专门的constants目录或者utils目录,将常量写在constants.js或者utils.js的文件中,如果常量或者方法的数量很多,可以再在constants目录或者utils目录下建立子目录,然后将这些文件输出的常量或者方法,合并在一个index.js的文件中,到时外边的文件要加载只加载index.js就可以了。

加载的东西较少时:

JavaScript中的模块化_第2张图片

加载的东西较多是:

JavaScript中的模块化_第3张图片

(5)缺陷

固然inport、export编译时加载具有众多优良的特性,但是

if(condition1){
    //加载模块1
}else if(condition2){
  //加载模块2
}

这种动态加载模块的写法是会报错的,因此有一些真的需要动态加载的场景可能必须要靠require解决

3前端组件化

JavaScript中的模块化_第4张图片

react 推荐通过webpack 或者 browserify 进行应用的构建,搭配对应的loader 或者 plugin 可以实现通过 JavaScript 入口文件统一管理依赖资源。从整体上,这是一个典型的“但JavaScript入口组件”方案。

你可能感兴趣的:(前段学习,js学习)