您的位置:时时app平台注册网站 > web前端 > 前面多少个模块化开采简单介绍【彩世界网址】

前面多少个模块化开采简单介绍【彩世界网址】

2019-11-08 03:49

webpack如何识别CommonJs模块

webpack打包后输出文件的基本结构是下面这个样子的:

(function(modules) { // webpackBootstrap
    // 模块缓存对象
    var installedModules = {};

    // webpack内部的模块引用函数
    function __webpack_require__(moduleId) {

        // 加载入口JS

        // 输出
        return module.exports;
    }

    // 挂载模块数组
    __webpack_require__.m = modules;
    // ...
    // 在__webpack_require__挂载多个属性

    // 传入入口JS模块ID执行函数并输出模块
    return __webpack_require__(__webpack_require__.s = 0);
});
// 包含所有模块的数组
([
    /* id为0 */
    (function(module, exports) {
        console.log('1')
    })
]);

简化以后实际上就是一个自执行函数:

(function(modules){
    return __webpack_require__(0);
}([Module0,Module1...]))

可以看到__webpack_reqruie__( )这个方法的参数就是模块的唯一ID标识,返回值就是module.exports,所以webpack对于CommonJs规范是原生支持的。

AMD演示:模块/scripts/helper/util.js

define(function(require, factory) {
    'use strict';
    return {
        foo: function(){
            console.log("hello util.js");
        }
    };  
});

前端标准的模块化规范

  1. AMD - requirejs
  2. CMD - seajs

二. webpack与模块化

webpack默认支持的是CommonJs规范,毕竟它是nodejs支持的模块管理方式,而没有node哪来的webpack。但同时为了扩展其使用场景,webpack在版本迭代中也加入了对ES harmony规范和AMD规范的兼容。

按需加载问题

  • 如果只使用某个库的其中一个功能, 不得不把整个库引入

回顾Node模块

webpack4.0各个击破(5)—— Module篇

webpack作为前端最火的构建工具,是前端自动化工具链最重要的部分,使用门槛较高。本系列是笔者自己的学习记录,比较基础,希望通过问题 解决方式的模式,以前端构建中遇到的具体需求为出发点,学习webpack工具中相应的处理办法。(本篇中的参数配置及使用方式均基于webpack4.0版本

彩世界网址 1

使用webpack对脚本进行合并是非常方便的,因为webpack实现了对各种不同模块规范的兼容处理,对前端开发者来说,理解这种实现方式比学习如何配置webpack更为重要,本节的内容实用性较低。
彩世界网址 2

UMD(Universal Module Definition)

对象的导出

1. export default{
        add(){}
 }
2. export fucntion add(){} 相当于 将add方法当做一个属性挂在到exports对象

webpack如何识别ES Harmony模块

对于ES Harmony规范不熟悉的可以查看《ES6 Module语法》一文。

先使用import命令加载一个CommonJs规范导出的模块,查看打包后的代码可以看到模块引用的部分被转换成了下面这样:

__webpack_require__.r(__webpack_exports__);
/* harmony import */ 
var _components_component10k_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./components/component10k.js");
/* harmony import */
var _components_component10k_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_components_component10k_js__WEBPACK_IMPORTED_MODULE_0__);

简化一下再来看:

__webpack_require__.r(__webpack_exports__);
var a = __webpack_require__("./components/component10k.js");
var b = __webpack_require__.n(a);

这里涉及到两个工具函数:

彩世界网址 3

这个方法是给模块的exports对象加上ES Harmony规范的标记,如果支持Symbol对象,则为exports对象的Symbol.toStringTag属性赋值Module,这样做的结果是exports对象在调用toString方法时会返回'Module'(笔者并没有查到这种写法的缘由);如果不支持Symbol对象,则将exports.__esModule赋值为true。

另一个工具函数是:

彩世界网址 4

传入了一个模块,返回一个getter方法,此处是一个高阶函数的应用,实现的功能是当模块的__esModule属性为真时,返回一个getDefault( )方法,否则返回getModuleExports( )方法.

回过头再来看上面的简化代码:

// 添加ES Harmony规范模块标记
__webpack_require__.r(__webpack_exports__);
// a实际上得到了模块通过module.exports输出的对象
var a = __webpack_require__("./components/component10k.js");
// 根据a的模块化规范类型返回不同的getter函数,当getter函数执行时才会真正得到模块对象
var b = __webpack_require__.n(a);

总结一下,webpack所做的处理相当于对模块增加了代理,如果被加载模块符合ES Harmony规范,则返回module['default'],否则返回module。这里的module泛指模块输出的对象。

再使用import加载一个使用export语法输出的ES Harmony模块,查看打包结果中的模块文件可以看到:

//component10k.js模块文件在main.bundle.js中的内容
__webpack_require__.r(__webpack_exports__);
__webpack_exports__["default"] = (function(){
    Array.from('component10k');
})

可以看到输出的内容直接绑定到了输出模块的default属性上,由于这个模块被打上了__esModule的标记,所以引用它的模块会通过module['default']来取用其内容,也就正好命中了模块的输出内容。

CommonJS

  1. 一个单独的JS文件就是一个模块,每一个模块都是一个单独的作用域
  2. 定义全局函数require,通过传入模块标识来引入其他模块,执行的结果即为别的模块暴漏出来的API
  3. 如果被require函数引入的模块中也包含依赖,那么依次加载这些依赖
  4. 如果引入模块失败,那么require函数应该报一个异常
  5. 模块通过变量exports来向外暴漏API,exports只能是一个对象,暴漏的API须作为此对象的属性。

模块打包器

根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。如何在一个大规模的代码库中,维护各种模块资源的分割和存放,维护它们之间的依赖关系,并且无缝的将它们整合到一起生成适合浏览器端请求加载的静态资源。市面上已经存在的模块管理和打包工具并不适合大型的项目,尤其单页面 Web 应用程序。最紧迫的原因是如何在一个大规模的代码库中,维护各种模块资源的分割和存放,维护它们之间的依赖关系,并且无缝的将它们整合到一起生成适合浏览器端请求加载的静态资源。
这些已有的模块化工具并不能很好的完成如下的目标:

  • 将依赖树拆分成按需加载的块
  • 初始化加载的耗时尽量少
  • 各种静态资源都可以视作模块
  • 将第三方库整合成模块的能力
  • 可以自定义打包逻辑的能力
  • 适合大项目,无论是单页还是多页的 Web 应用

一. 模块化乱炖

彩世界网址 5

脚本合并是基于模块化规范的,javascript模块化是一个非常混乱的话题,各种**【*MD】**规范乱飞还要外加一堆【*.js】的规范实现。现代化前端项目多基于框架进行开发,较为流行的框架内部基本已经统一遵循ES6的模块化标准,尽管支持度不一,但通过构建工具可以解决浏览器支持滞后的问题;基于nodejs的服务端项目原生支持CommonJs标准;而开发中引入的一些工具类的库,热门的工具类库为了能同时兼容浏览器和node环境,通常会使用UMD标准(Universal Module Definition) 来实现模块化,对UMD范式不了解的读者可以先阅读《javascript基础修炼(4)——UMD规范的代码推演》一文,甚至有些第三方库并没有遵循任何模块化方案。如果不借助构建工具,想要对各类方案实现兼容是非常复杂的。

AMD演示:目录结构

彩世界网址 6

source_tree.png


传统非模块化开发有如下的缺点

  1. 命名冲突
  2. 文件依赖

webpack如何识别AMD模块

我们将component10k.js模块改为用AMD规范定义:

define(function(){
    console.log('test');
})

查看经过webpack打包后,这个模块变成了如下的样子:

var __WEBPACK_AMD_DEFINE_RESULT__;
!(__WEBPACK_AMD_DEFINE_RESULT__ = (function(){
    console.log('test');
}).call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));

简化一下:

var result;
!(result=(function(){}).call(...),result!==undefined && module.exports = result);

抽象一下:

var result;
!(expression1,expression2 && expression3)

这里涉及的javascript的基本知识较多,逗号表达式的优先级最低,所以最后参与运算,逗号表达式会从左到右依次执行语句,并返回最后一个表达式的结果,&&为短路运算语法,即前一个条件成立时才计算后面的表达式,赋值语句执行完后会将所赋的值返回。此处外层的!(expression )语法起了什么作用,笔者也没看懂,希望了解的读者多多指教。

所以,webpack对于AMD模块的处理,实际上是加了一层封装,将模块运行的结果挂载到了webpack模块的module.exports对象上。

webpack 优点

  • 可以兼容多模块风格,无痛迁移老项目
  • 一切皆模块,js/css/图片/字体/音视频 等都是模块, 都可被打包
  • 配合插件/加载器可以进行各种操作: 转译, 代码检查, 压缩等等
  • 静态解析,按需打包,动态加载,
  • 支持抽离公共模块
  • 支持进行代码分隔, 按需下载
  • 扩展性强,插件机制强大, 生态完善
  • 强大的webpack-dev-server: 检测代码改变, 进行代码热重载, 无需浏览器刷新

CommonJS

服务器端的 Node.js 遵循 CommonJS规范,该规范的核心思想是允许模块通过 require 方法来同步加载所要依赖的其他模块,然后通过 exportsmodule.exports 来导出需要暴露的接口。

require("module");
require("../file.js");
exports.doStuff = function() {};
module.exports = someValue;

// moduleA.js
module.exports = function( value ){
    return value * 2;
}

// moduleB.js
var multiplyBy2 = require('./moduleA');
var result = multiplyBy2(4);

优点:

  • 服务器端模块便于重用
  • NPM 中已经有将近20万个可以使用模块包
  • 简单并容易使用

缺点:

  • 同步的模块加载方式不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的
  • 不能非阻塞的并行加载多个模块

目前模块化的解决方案

  1. CommonJS -- Node.js
  2. AMD -- RequireJS
  3. CMD -- SeaJS
  4. UMD
  5. ES6 Module

模块系统

Q1

期望的模块系统

模块的加载和传输,我们首先能想到两种极端的方式,一种是每个模块文件都单独请求,另一种是把所有模块打包成一个文件然后只请求一次。显而易见,每个模块都发起单独的请求造成了请求次数过多,导致应用启动速度慢;一次请求加载所有模块导致流量浪费、初始化过程慢。这两种方式都不是好的解决方案,它们过于简单粗暴。

分块传输,按需进行懒加载,在实际用到某些模块的时候再增量更新,才是较为合理的模块加载方案。要实现模块的按需加载,就需要一个对整个代码库中的模块进行静态分析、编译打包的过程。

在上面的分析过程中,我们提到的模块仅仅是指JavaScript模块文件。然而,在前端开发过程中还涉及到样式、图片、字体、HTML 模板等等众多的资源。如果他们都可以视作模块,并且都可以通过require的方式来加载,将带来优雅的开发体验,那么如何做到让 require 能加载各种资源呢?在编译的时候,要对整个代码进行静态分析,分析出各个模块的类型和它们依赖关系,然后将不同类型的模块提交给适配的加载器来处理。Webpack 就是在这样的需求中应运而生。

SeaJS

SeaJS集各家所长, 融合了太多的东西,已经无法说它遵循哪个规范了,所以干脆就自立门户,起名曰CMD(Common Module Definition)规范

AMD

define(id?, dependencies?, factory),它要在声明模块的时候指定所有的依赖 dependencies,并且还要当做形参传到 factory 中,对于依赖的模块提前执行,依赖前置。

define("module", ["dep1", "dep2"], function(d1, d2) {
  return someExportedValue;
});
require(["module", "../file"], function(module, file) { /* ... */ });

一些用例:
定义一个名为 myModule 的模块,它依赖 jQuery 模块:

define('myModule', ['jquery'], function($) {
    // $ 是 jquery 模块的输出
    $('body').text('hello world');
});
// 使用
define(['myModule'], function(myModule) {});

注意:在 webpack 中,模块名只有局部作用域,在 Require.js 中模块名是全局作用域,可以在全局引用。
定义一个没有 id 值的匿名模块,通常作为应用的启动函数:

define(['jquery'], function($) {
    $('body').text('hello world');
});

依赖多个模块的定义:

define(['jquery', './math.js'], function($, math) {
    // $ 和 math 一次传入 factory
    $('body').text('hello world');
});

模块输出:

define(['jquery'], function($) {

    var HelloWorldize = function(selector){
        $(selector).text('hello world');
    };

    // HelloWorldize 是该模块输出的对外接口
    return HelloWorldize;
});

在模块定义内部引用依赖:

define(function(require) {
    var $ = require('jquery');
    $('body').text('hello world');
});

优点:

  • 适合在浏览器环境中异步加载模块
  • 可以并行加载多个模块

缺点:

  • 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不顺畅
  • 不符合通用的模块化思维方式,是一种妥协的实现

4.CMD

define(function(require, exports, module) {
  var $ = require('jquery');
  var Spinning = require('./spinning');
  exports.doSomething = ...
  module.exports = ...
})

优点:

  • 依赖就近,延迟执行
  • 可以很容易在 Node.js 中运行

缺点:

  • 依赖 SPM 打包,模块的加载逻辑偏重

5.UMD

6.ES6模块

ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。

import "jquery";
export function doStuff() {}
module "localModule" {}

优点:

  • 容易进行静态分析
  • 面向未来的 EcmaScript 标准

缺点:

  • 原生浏览器端还没有实现该标准
  • 全新的命令字,新版的 Node.js才支持

实现:

  • Babel

ES6 Module 基本用法

  • 浏览器使用: 在入口JS文件加上type="module"就可以在该文件内使用ES6 Module 语法

    <script src="scripts/main.js" type="module"></script>
    

现在的前端开发

现在的前端开发, 不仅仅是完成浏览的基本需求,并且通常是一个单页面应用,每一个视图通过异步的方式加载,这导致页面初始化和使用过程中会加载越来越多的 JavaScript 代码. 如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,就需要一个模块化系统

参考

  • http://es6.ruanyifeng.com/#docs/module
  • http://web.jobbole.com/83761/
  • http://requirejs.org/docs/start.html

功能特性

browserify的出现非常棒,但webpack更胜一筹!

我们来看看webpack支持哪些功能特性:

  • 支持CommonJs和AMD模块,意思也就是我们基本可以无痛迁移旧项目。
  • 支持模块加载器和插件机制,可对模块灵活定制。特别是我最爱的babel-loader,有效支持ES6。
  • 可以通过配置,打包成多个文件。有效利用浏览器的缓存功能提升性能。
  • 将样式文件和图片等静态资源也可视为模块进行打包。配合loader加载器,可以支持sass,less等CSS预处理器。
  • 内置有source map,即使打包在一起依旧方便调试。
  • 看完上面这些,可以想象它就是一个前端工具,可以让我们进行各种模块加载,预处理后,再打包。之前我们对这些的处理是放在grunt或gulp等前端自动化工具中。有了webpack,我们无需借助自动化工具对模块进行各种处理,让我们工具的任务分的更加清晰。

自执行函数

// b.js
var a = 1;
// a.js
(function(args){
  var a = 2;
  console.log(a); // 2
  console.log(window.a); // 1
}).call(context,args);

服务器端的模块化规范

CommonJS - Node.js

AMD演示:主页文件/project.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Page Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script data-main="scripts/main"src="scripts/require.js"></script>
</head>
<body>

</body>
</html>

回顾ES6模块

前端模块化开发简介


历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。


回顾webpack

依赖顺序&&重复引入问题

  • a.js 依赖 b.js 如何保证顺序?

  • a.js, b.js 都依赖 c.js, 如何保c不被重复引入?


script

  • 全局作用域下容易造成变量冲突
  • 文件只能按照 <script> 的书写顺序进行加载
  • 开发人员必须主观解决模块和代码库的依赖关系
  • 在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪

模块化与打包工具

由于模块化方案多样, 且浏览器支持不一, 再加上上述模块化方案仅仅支持JavaScript本身, 对 于复杂的前端应用来说远远不够用, 因此出现了各种打包工具来解决这些问题

  • 早期打包工具

    1. r.js -- RequireJS提供的打包工具,仅仅支持RequireJS
    2. SPM -- SeaJS提供的打包工具,仅仅支持SeaJS
    3. browserify -- 让浏览器使用Node.js的NPM模块
    4. gulp/grunt/fis -- 前端自动化构建, 用来测试,压缩,检错,合并前端代码, 不支持模块化(类似Maven/Gradle)
  • 现代打包工具

    1. webpack -- 高度可配置的静态资源打包器, 有着强大的插件和生态
    2. rollup -- 小巧高效的前端资源打包器, 适合用来编写库或框架
    3. parcel -- 后起之秀, 极速零配置Web应用打包工具

Webpack 的特点

Webapck 和其他模块化工具有什么区别呢?

  1. 代码拆分
    Webpack 有两种组织模块依赖的方式,同步和异步。异步依赖作为分割点,形成一个新的块。在优化了依赖树后,每一个异步区块都作为一个文件被打包。
  2. Loader
    Webpack 本身只能处理原生的 JavaScript 模块,但是 loader 转换器可以将各种类型的资源转换成 JavaScript 模块。这样,任何资源都可以成为 Webpack 可以处理的模块。
  3. 智能解析
    Webpack 有一个智能解析器,几乎可以处理任何第三方库,无论它们的模块形式是 CommonJS、 AMD 还是普通的 JS 文件。甚至在加载依赖的时候,允许使用动态表达式 require("./templates/" name ".jade")
  4. 插件系统
    Webpack 还有一个功能丰富的插件系统。大多数内容功能都是基于这个插件系统运行的,还可以开发和使用开源的 Webpack 插件,来满足各式各样的需求。
  5. 快速运行
    Webpack 使用异步 I/O 和多级缓存提高运行效率,这使得 Webpack 能够以令人难以置信的速度快速增量编译。

CMD(Common Module Definition)

CommonJS 与 AMD 支持

Webpack 对 CommonJS 的 AMD 的语法做了兼容, 方便迁移代码

不过实际上, 引用模块的规则是依据 CommonJS 来的

require('lodash') // 从模块目录查找
require('./file') // 按相对路径查找

AMD 语法中, 也要注意, 是按 CommonJS 的方案查找的

define (require, exports. module) ->
  require('lodash') # commonjs 当中这样是查找模块的 

模块化所解决的问题

  1. 模块作用域: 安全的包装一个模块的代码--不污染模块外的任何代码
  2. 模块唯一性: 唯一标识一个模块--避免重复引入
  3. 模块的导出: 优雅的把模块的API暴漏给外部--不增加全局变量
  4. 模块的引入: 方便的使用所依赖的模块

对象的导入

如果导出的是:export default{ add(){}}
那么可以通过  import obj from './calc.js'
如果导出的是:
export fucntion add(){} 
export fucntion substrict(){} 
export const PI=3.14
那么可以通过按需加载 import {add,substrict,PI} from './calc.js'

A2

  • 检查<head>标签,确保依赖顺序

  • 将所有文件按依赖顺序合并


CommonJs与AMD

在一开始,我们先讲一下它和以往我们所用的模块管理工具有什么不一样。在最开始的阶段,Js并没有这些模块机制,各种Js到处飞,得不到有效妥善的管理。后来前端圈开始制定规范,最耳熟能详的是CommonJs和AMD。

CommonJs是应用在NodeJs,是一种同步的模块机制。它的写法大致如下:

var firstModule = require("firstModule");
//your code...
module.export = anotherModule

AMD的应用场景则是浏览器,异步加载的模块机制。require.js的写法大致如下:

define(['firstModule'], function(module){
   //your code...
   return anotherModule
})

其实我们单比较写法,就知道CommonJs是更为优秀的。它是一种同步的写法,对Human友好,而且代码也不会繁琐臃肿。但更重要的原因是,随着npm成为主流的JavaScript组件发布平台,越来越多的前端项目也依赖于npm上的项目,或者自身就会发布到npm平台。所以我们对如何可以使用npm包中的模块是我们的一大需求。所以browserify工具就出现了,它支持我们直接使用require()的同步语法去加载npm模块。

当然我们这里不得不说的是,ES2015(ES6)里也有了自己的模块机制,也就是说ES6的模块机制是官方规定的,我们通过babel(一种6to5的编译器)可以使用比较多的新特性了,包括我们提到的模块机制,而它的写法大致如下:

import {someModule} from "someModule";
// your codes...
export anotherModule;

当然上面的写法只是最基本的,还有其他的不同加载模块的写法,可以看一下阮一峰老师的ECMAScript 6 入门或者babel的相关文档Learn ES2015。

同时兼容AMD, CommonJS的模块化定义

  • 写法丑陋复杂, 但是能够支持多种规范

    (function (root, factory) {
    if (typeof define === 'function' && define.amd) {
      define(['jquery'], factory); // AMD
    } else if (typeof exports === 'object') {
      module.exports = factory(require('jquery'));// CommonJS
    } else {
      root.returnExports = factory(root.jQuery); // 浏览器全局变量
    }
    }(this, function ($) {
    function foo(){$()};
    return foo;
    }));
    

AMD、CMD、webpack的区别:

从前有两个规范,一个是AMD 一个是CMD
RequireJS是AMD规范的实现,SeaJS是CMD规范的实现,
一个主张提前加载依赖,一个主张延迟加载依赖
后来出现了 commonjs规范
webpack就是支持commonjs规范的
目前可以说是主导了前端构建格局。

CommomJS是服务端规范,node就是采用这个规范,他是同步加载,毕竟服务端不用考虑异步。
它是对通用的JavaScript模式的标准化尝试,它包含有 AMD 定义
AMD是异步加载模块的缩写,使用require引入模块,提倡依賴前置。
CMD与AMD其实挺接近的,还因为有sea.js,中文资料还是比较亲切的。
还有就是AMD和CommomJS的中间者UMD
Webpack其实就是一个打包工具,他的思想就是一切皆模块,css是模块,js是模块,图片是模块。并且提供了一些列模块加载(各种-loader)来编译模块。官方推荐使用commonJS规范,但是也支持CMD和AMD

无论是node应用模块,还是webpack 配置 ,均是采用CommonJS模块化规范

ES6 Module 浏览器支持情况

彩世界网址 7

caniuse.png


webpack是什么?

CommonJS和AMD是用于JavaScript模块管理的两大规范,前者定义的是模块的同步加载,主要用于NodeJS;而后者则是异步加载,通过requirejs等工具适用于前端。随着npm成为主流的JavaScript组件发布平台,越来越多的前端项目也依赖于npm上的项目,或者 自身就会发布到npm平台。因此,让前端项目更方便的使用npm上的资源成为一大需求。
web开发中常用到的静态资源主要有JavaScript、CSS、图片、Jade等文件,webpack中将静态资源文件称之为模块。 webpack是一个module bundler(模块打包工具),其可以兼容多种js书写规范,且可以处理模块间的依赖关系,具有更强大的js模块化的功能。Webpack对它们进行统 一的管理以及打包发布

比较

  • AMD推崇(但不强制)依赖前置,在定义模块的时候就要用require声明其依赖的
  • CMD推崇(但不强制)就近依赖,只有在用到某个模块的时候再去require
  • CMD不需要AMD那样的回调写法, 可以像CommonJS一样的同步写法(但加载其实还是异步的)
  • AMD模块是提前执行的, 而CMD模块默认是延迟执行的
  • 由于延迟加载, CMD用户体验稍差

为什么使用 webpack?

  1. 对 CommonJS 、 AMD 、ES6的语法做了兼容
  2. 对js、css、图片等资源文件都支持打包
    3. 串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性,例如提供对CoffeeScript、ES6的支持
  3. 有独立的配置文件webpack.config.js
  4. 可以将代码切割成不同的chunk,实现按需加载,降低了初始化时间
  5. 支持 SourceUrls 和 SourceMaps,易于调试
  6. 具有强大的Plugin接口,大多是内部插件,使用起来比较灵活
    8.webpack 使用异步 IO 并具有多级缓存。这使得 webpack 很快且在增量编译上更加快

AMD演示


Node模块化相关的规则

  1. 如何定义模块:一个js文件就是一个模块,模块内部的成员都是相互独立
  2. 模块成员的导出和引入:
    exports与module的关系:module.exports = exports = {};
    模块成员的导出最终以module.exports为准
    如果要导出单个的成员或者比较少的成员,一般我们使用exports导出;如果要导出的成员比较多,一般我们使用module.exports的方式;这两种方式不能同时使用
var sum = function(a,b){
    return parseInt(a)   parseInt(b);
}
// 方法1
// 导出模块成员
exports.sum = sum;
//引入模块
var module = require('./xx.js');
var ret = module.sum(12,13);

// 方法2
// 导出模块成员
module.exports = sum;
//引入模块
var module = require('./xx.js');
module();

// // 方法1
// exports.sum = sum;
// exports.subtract = subtract;
// 
// var m = require('./05.js');
// var ret = m.sum(1,2);
// var ret1 = m.subtract(1,2);
// console.log(ret,ret1);
// 
// // 方法2
// module.exports = {
//     sum : sum,
//     subtract : subtract,
//     multiply : multiply,
//     divide : divide
// }
// 
// var m = require('./05.js');
// console.log(m);

AMD演示:运行结果

彩世界网址 8

console.png

彩世界网址 9

network.png


什么是模块化

把函数作为模块
缺陷: 污染全局变量 模块成员之间没什么关系
面向对象思想 并使用立即执行函数 实现闭包
避免了变量污染 同时同一模块内的成员也有了关系 在模块外部无法修改我们没有暴露出来的变量、函数
这就是简单的模块

ES6 Module 基本用法

  • 引用模块

    // 从模块中导入指定对象, 支持重命名, main.js
    import { foo, bar as myBar } from './module.js'
    
    // 从模块中导入默认对象(名称可跟原名称不一样)
    import myFoo from './module.js'
    
    // 执行模块, 但不导入任何值
    import './module.js'
    
    // 整体导入
    
    import * as myModule from './module.js'
    
  • 注意: ES6 import导入的模块都是原模块的引用


手动分离所需代码


开始之前

总结

- 加载机制 缺点 评价
CommonJS 同步加载 加载时会阻塞线程,仅适用于后端 NodeJS首创,具有先导意义
AMD 异步加载, 依赖前置 写法冗余,依赖多的时候很痛苦 前端残留势力
CMD 异步加载, 依赖后知 体验略差,需要配合SPM打包工具,配置复杂 被创始人说"已死"的规范
UMD 根据运行环境判断选用合适的方式 写法臃肿难看 前后端跨平台跨平台的解决方案
ESM 编译时静态确定 浏览器支持乏力,需要配合转译或打包工具使用 未来前端模块管理的规范

A3

CommonJS 缺点

  1. 只能在服务端(Node.js)使用, 不能在浏览器直接使用
  2. 模块是同步加载的, 如果加载过慢会阻塞进程

webpack 简介

webpack并不强制你使用某种模块化方案,而是通过兼容所有模块化方案让你无痛接入项目,当然这也是webpack牛逼的地方。 有了webpack,你可以随意选择你喜欢的模块化方案,至于怎么处理模块之间的依赖关系及如何按需打包,webpack会帮你处理好的


如何解决JS全局变量/函数冲突?


AMD(Asyncchronous Module Definition)

  1. 专门为浏览器量身定制,兼容IE6 (在node.js中使用适配器也可以用)
  2. 模块是异步加载的
  3. 用全局函数define来定义模块,用法为:define(id?, dependencies?, factory)
  4. 使用全局函数require来引入模块, 用法为: require(dependencies?, callback)

CommonJS 例子

//a.js
exports.foo = function() {
  console.log('foo')
}

//b.js
var a = require('./a.js')
a.foo()

webpack 简介

彩世界网址 10

webpack.png


Q2

ES6 Module 基本用法

  • 定义模块

    //变量, module.js
    export var bar = 'bar'
    
    // 函数, module.js
    export function foo(){}
    
    // 统一导出&重命名, module.js
    var bar = 'bar'
    function foo(){}
    export { bar as myBar, foo }
    
    // 默认导出, module.js
    function foo(){}
    export default foo
    

AMD缺点

  1. 预下载, 预解释, 带来额外性能消耗
  2. 书写复杂
  3. 回调地狱
define(['a', 'b', 'c', 'd', 'e', 'f', 'g'], function(a, b, c, d, e, f, g){ })

我们先用几个问题, 探讨下, 在没有模块化的情况下, 我们如何解决这些问题


AMD 例子

//a.js
define(function(){
  return {
    hello: function(){
      console.log('hello, a.js')
    }
  }
})
// main.js
require(['a', 'other'], function(a, other){
  a.hello() // hello, a.js
  other.foo()
})

A1

AMD演示:JS入口文件/scripts/main.js

requirejs(["helper/util"], function(util) {
  if(!condition){
    util.foo()
  }
});

ES6 Module -- 面向未来的模块标准

ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案

  • 但是到目前为止, 浏览器对ES6 Module的支持还是相当不完善

ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,从而进行一些优化。CommonJS 和 AMD 模块,都只能在运行时确定依赖

  • 在ES8的stage-3提案中, 出现了动态importimport()方法,它返回一个Promise对象, 允许动态导入模块

Q3

本文由时时app平台注册网站发布于web前端,转载请注明出处:前面多少个模块化开采简单介绍【彩世界网址】

关键词: