Giter VIP home page Giter VIP logo

commonjs's Introduction

首先, 我们知道,Javascript 是没有模块的概念,只有服务器端才有模块的概念。

image

一开始前端的工作内容不多,html中如图中引入js文件,有很多缺点:

必须按顺序引入,如果 1.js中要用到jquery,那就将jquery.js放到1.js上方。

同步加载各个js,只有1.js加载并执行完,才去加载2.js。

各个js文件可能会有多个window全局变量的创建,污染。

......还有很多缺点

总之,上面的结构,在前端内容越来越多,尤其ajax的趋势、前后端分离、越来越注重前端体验,js文件越来越多且互相引用更复杂的情况下,真心乱套了,所以需要有一个新的模块化工具。 我们当然是希望像现在这样,文件之间互相 import、export 就行了,但遗憾的是,这是es6配合node的用法,需要服务端做支撑处理文件,而一开始仅通过静态文件去模块化。

AMD and CMD

详解 Javascript模块化编程(一):模块的写法 Javascript模块化编程(二):AMD规范 Javascript模块化编程(三):require.js的用法

AMD(Asynchronous Module Definition, 异步模块定义) 是 RequireJS 在推广过程中对模块定义的规范化产出。 CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。 CommonJS 是 BravoJS 在推广过程中对模块定义的规范化产出。 还有不少⋯⋯这些规范的目的都是为了 JavaScript 的模块化开发,特别是在浏览器端的。目前这些规范的实现都能达成浏览器端模块化开发的目的

区别:

  1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。

不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。

CMD 推崇 as lazy as possible.2. CMD 推崇依赖就近,AMD 推崇依赖前置。

看代码:

// CMD

define(function(require, exports, module) {  
var a = require('./a')   
a.doSomething()  
// 此处略去 100 行  

var b = require('./b') // 依赖可以就近书写   
b.doSomething()   
// ... 
})

代码在运行时,首先是不知道依赖的,需要遍历所有的require关键字,找出后面的依赖。具体做法是将function toString后,用正则匹配出require关键字后面的依赖。显然,这是一种牺牲性能来换取更多开发便利的方法。

而AMD是依赖前置的,换句话说,在解析和执行当前模块之前,模块作者必须指明当前模块所依赖的模块,表现在require函数的调用结构上为:

// AMD 默认推荐的是
define(['./a', './b'], function(a, b) {  // 依赖必须一开始就写好    
a.doSomething()    
// 此处略去 100 行    

b.doSomething()    
...

})

amd 原理

无论是传统的script 加载还是 require.js 目的都是让浏览器加载到响应的文件;

分析实现原理: 可能1: 编码时的html文件和运行时的html文件是两个文件,即通过某些工具复制并修改了html。可惜修改文件需要服务端程序去做,而require.js只是个js文件,所以不是这个原理。这在node下webpack可以轻松的实现。 可能2: 既然可能1是不对的,那么说明了,浏览器运行的html文件和编码时的html文件是一模一样的。所以只剩下第二条路了,就是运行时由js代码去修改html文档~

commonjs's People

Watchers

 avatar  avatar

commonjs's Issues

require命令

1.1 基本用法

Node使用CommonJS模块规范,内置的require命令用于加载模块文件。

require命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错。

1.2 模块的缓存

第一次加载某个模块时,Node会缓存该模块。以后再加载该模块,就直接从缓存取出该模块的module.exports属性。

如果想要多次执行某个模块,可以让该模块输出一个函数,然后每次require这个模块的时候,重新执行一下输出的函数。

所有缓存的模块保存在require.cache之中,如果想删除模块的缓存,可以像下面这样写。

// 删除指定模块的缓存
delete require.cache[moduleName];

// 删除所有模块的缓存
Object.keys(require.cache).forEach(function(key) {
  delete require.cache[key];
})

注意,缓存是根据绝对路径识别模块的,如果同样的模块名,但是保存在不同的路径,require命令还是会重新加载该模块。

1.3 require.main

require方法有一个main属性,可以用来判断模块是直接执行,还是被调用执行。

直接执行的时候(node module.js),require.main属性指向模块本身。

require.main === module
// true

module对象

function Module(id, parent) {
  this.id = id;
  this.exports = {};
  this.parent = parent;
  // ...

每个模块内部,都有一个module对象,代表当前模块。它有以下属性。

  • module.id 模块的识别符,通常是带有绝对路径的模块文件名。
  • module.filename 模块的文件名,带有绝对路径。
  • module.loaded 返回一个布尔值,表示模块是否已经完成加载。
  • module.parent 返回一个对象,表示调用该模块的模块。
  • module.children 返回一个数组,表示该模块要用到的其他模块。
  • module.exports 表示模块对外输出的值。

1.1 module.exports属性

module.exports属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取module.exports变量。

1.2exports变量

为了方便,Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令。

var exports = module.exports;

造成的结果是,在对外输出模块接口时,可以向exports对象添加方法。

exports.area = function (r) {
  return Math.PI * r * r;
};

exports.circumference = function (r) {
  return 2 * Math.PI * r;
};

注意,不能直接将exports变量指向一个值,因为这样等于切断了exports与module.exports的联系。

ES6的模块加载和CommonJS的区别

1、ES6模块的设计**,是尽量的静态化,使得编译时就能确定模块的依赖关系(“静态优化”),以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西(“运行时加载”)。

2、ES6模块不是对象,而是通过export命令显式指定输出的代码,输入时也采用静态命令的形式。
CommonJS和AMD模块是一个对象。

3、ES6模块加载的机制,与CommonJS模块完全不同。CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用。

CommonJS模块输出的是被输出值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

例如:
a.js 和 b.js 同时引用 common.js

//  common.js

let common = {
    a: ''
}
export default common;

如果采用 import 的方式:

// a.js
import A from '../common'

A.a = 'footer';
console.log(A.a);    //  'footer'

// b.js
import A from '../common'

setTimeout(() => {
    console.log(A.a)  // 'footer'
}, 1000)

若采用CommonJS 方式;

// a.js
var A = require('../common')

A.a = 'footer';
console.log(A.a);    //  'footer'

// b.js
var A = require('../common')

setTimeout(() => {
    console.log(A.a)  // undifinde
}, 1000)

4、处理“循环加载”的方法是不一样的,返回的结果也不一样。

CommonJS模块的重要特性是加载时执行,即脚本代码在require的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。

ES6模块是动态引用,如果使用import从一个模块加载变量,那些变量不会被缓存,而是成为一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。

CommonJS 模块的特点

  • 所有代码都运行在模块作用域,不会污染全局作用域。
  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
  • 模块加载的顺序,按照其在代码中出现的顺序。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.