0基础快速入门WebPack(1)——引言:简述模块化思想

1. 重点提炼

模块化

  • commonjs - node.js
  • amd/cmd - require.js / sea.js
  • umd - 同构
  • esm - ECMAScript Module

2. webpack 与 模块化

2.1 模块化

模块化已经是现代前端开发中不可或缺的一部分了。

把复杂的问题分解成相对独立的模块(把一个大的项目中功能进行拆解,然后分门别类的存放起来,用文件的方式组织起来),这样的设计可以降低程序复杂性(一个好的文件/模块组织管理方式带来的好处),提高代码的重用,也有利于团队协作开发与后期的维护和扩展。

相当于现在有100个不一样的东西,如果把这些东西都统一放在一个位置的话,如果我们需要某个东西,从这100个东西里去找回非常麻烦,如果按照不同的功能,把这些东西分门别类放在不同的位置,并打上标签,这样的话,就很好找了,因此一个好的文件/模块组织管理方式能够帮我们降低程序复杂性、后期维护和扩展等。

如封装函数、面向对象和模块化都有很多相似之处,无非就是把问题简单话而已。实际开发过程中,后端开发一上手就需要掌握模块化,而前端模块化是近些年来火起来了的。

ECMAScript2015 开始引入了模块的概念,我们称为:ECMAScript Module,简称:ESM

2.2 模块化的核心

  • 独立的作用域

    • 随着项目的复杂,所产生的数据可能产生很多干扰。像原生js,无论拆分多少都是依靠script标签引入的,如果不做处理,引进来的函数与变量则都是全局的。不同文件之间是相互影响的,其数据、对象、变量等等都是相互影响的,因此首要解决的问题就是模块作用域,即拆出去的代码都必须有独立作用域。
  • 如何导出模块内部数据

    • 如果把代码拆到一个个模块中去了,这个模块又相对是独立的,那如何把这个独立模块的数据导出去呢(供其他模块使用)?
  • 如果导入外部模块数据

    • 考虑导入其他模块导出的数据

2.3 ESM

ECMAScript2015/ECMAScript6 开始,JavaScript 原生引入了模块概念,而且现在主流浏览器也都有了很好的支持

2.3.1 独立模块作用域

一个文件就是模块,拥有独立的作用域,且导出的模块都自动处于 严格模式下,即:'use strict'

2.3.2 导出模块内部数据

使用 export 语句导出模块内部数据

// 导出单个特性(单个数据)
export let name1, name2,, nameN;
export let name1 =, name2 =,, nameN;
export function FunctionName(){...}
export class ClassName {...}

// 导出列表
export { name1, name2,, nameN };

// 重命名导出
export { variable1 as name1, variable2 as name2,, nameN };

// 默认导出
export default expression;
export default function () {}
export default function name1() {}
export { name1 as default,};

// 模块重定向导出(导入一个模块,把这个模块原封不动地导出,一般作统一导出入口的时候用这种方式)
export * from;
export { name1, name2,, nameN } from;
export { import1 as name1, import2 as name2,, nameN } from;
export { default } from;

2.3.3 导入外部模块数据

导入分为两种模式

  • 静态导入
  • 动态导入

静态导入(用的较多)

在浏览器中,import 语句只能在声明了 type="module" 的 script 的标签中使用。

注意import语句必须在模块的最开始。

import defaultExport from "module-name";
import * as name from "module-name";
import { export } from "module-name";
import { export as alias } from "module-name";
import { export1 , export2 } from "module-name";
import { foo , bar } from "module-name/path/to/specific/un-exported/file";
import { export1 , export2 as alias2 , [...] } from "module-name";
import defaultExport, { export [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name";

注意:静态导入方式不支持延迟加载(即不能根据条件,动态地导入模块),import 必须这模块的最开始,即不能在如下函数中导入。

document.onclick = function () {

    // import 必须放置在当前模块最开始加载
    // import m1 from './m1.js'

    // console.log(m1);

}

动态(延迟)导入

此外,还有一个类似函数的动态 import(),它不需要依赖 type="module" 的 script 标签。

关键字 import 可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个 promise。因此可以在上面的函数中导入了。

import('./m.js')
  .then(m => {
    //...
});
// 也支持 await
let m = await import('./m.js');

通过 import() 方法导入返回的数据会被包装在一个对象中,即使是 default 也是如此

js当中,最早的时候,模块化的概念出现在后端。

js除了能在浏览器上运行以外,其实早期的时候尝试用它做一些后端事情,当然现在比较有名的是nodejs,它也是基于ecmscript出来的。

前端浏览器的js和后端node的语言相同的地方,在于底层的ecmscript是类似的,只不过基于ecmscript扩展出来的是不一样的,js前端扩展出来的是针对浏览器的一些apinodejs扩展出来的是针对服务端的一些api

2.4 模块化的向下兼容

  • CommonJS
  • AMD
  • UMD
  • ESM

无论是那种模块化规范,重点关注

  • 独立模块作用域
  • 导出模块内部数据
  • 导入外部模块数据

2.4.1 CommonJS

在早起前端对于模块化并没有什么规范,反而是偏向服务端的应用有更强烈的需求,CommonJS规范就是一套偏向服务端的模块化规范,NodeJS 就采用了这个规范。但这个规范中很多约定并不适合前端,首先模块的加载机制不适合前端,因为它是通过读文件系统,而前端加载机制采用的是http协议。还有io、以及同步加载模式(浏览器端是难以实现的)等。因此早期的CommonJS规范,只适用于后端,不能移植到前端上的。

独立模块作用域

一个文件就是模块,拥有独立的作用域

导出模块内部数据

通过 module.exportsexports 对象导出模块内部数据

// a.js
let a = 1;
let b = 2;

module.exports = {
  x: a,
  y: b
}
// or
exports.x = a;
exports.y = b;

导入外部模块数据

通过 require 函数导入外部模块数据

// b.js
let a = require('./a');
a.x;
a.y;

2.4.2 AMD

因为CommonJS规范一些特性(基于文件系统,同步加载),它并不适用于浏览器端,所以另外定义了适用于浏览器端的规范 。这些规范实际都是虚拟的,而实实在在的是实现这种规范的语言或者框架,如实现CommonJSnodejs,实现AMDrequireJS框架(库),它可以实现前端的模块化加载。

AMD(Asynchronous Module Definition)

官网

浏览器并没有具体实现该规范的代码,我们可以通过一些第三方库来解决。

2.4.2.1 requireJS

requirejs

// 1.html
<script data-main="scripts/main" src="https://cdn.bootcss.com/require.js/2.3.6/require.min.js"></script>

独立模块作用域

(闭包,就相当于一种伪模块,函数作用域)通过一个 define 方法来定义一个模块,并通过该方法的第二个回调函数参数来产生独立作用域,严格而说一个define就是一个模块。

// scripts/Cart.js
define(function() {
  // 模块内部代码
})

导出模块内部数据

通过 return 导出模块内部数据

// scripts/Cart.js
define(function() {
  return class Cart {
    add(item) {
      console.log(`添加商品:${item}`)
    }
  }
})

导入外部模块数据

通过前置依赖(把依赖信息都写在前面,并且前置加载)列表导入外部模块数据。

// scripts/main.js
// 定义一个模块,并导入 ./Cart 模块(第一个参数),模块导出的变量传给对应的函数参数
define(['./Cart'], function(Cart) {
  let cart = new Cart()
  cart.add({name: 'iphoneXX', price: 1000000})
})

2.4.3 requireJS的 CommonJS 风格

require.js 也支持 CommonJS 风格的语法

导出模块内部数据

// scripts/Cart.js
define(['require', 'exports', 'module'], function(require, exports, module) {
  class Cart {
    add(item) {
      console.log(`添加商品:${item}`)
    }
  }
  exports.Cart = Cart;
})
// 忽略不需要的依赖导入
define(['exports'], function(exports) {
  class Cart {
    add(item) {
      console.log(`添加商品:${item}`)
    }
  }
  exports.Cart = Cart;
})
// 如果是依赖的导入为:require, exports, module,也可以省略依赖导入声明
define(function(require, exports, module) {
  class Cart {
    add(item) {
      console.log(`添加商品:${item}`)
    }
  }
  exports.Cart = Cart;
})

导入外部模块数据

// scripts/main.js
define(['./Cart'], function(Cart) {
  let cart = new Cart()
  cart.add({name: 'iphoneXX', price: 1000000})
})

不过其实国内流行的是CMD规范,国际上不太通用,而是AMD规范,国内现在UMD用的比较多。

2.4.4 UMD

严格来说,UMD 并不属于一套模块规范(而是一种兼容处理方式:处理差异化/代码同构,即前后端代码同构,一套代码既可以用在前端又可以用在后端,也可理解为某个操作字符串的函数,前后端都可以去调用),它主要用来处理 CommonJSAMDCMD 的差异兼容,是模块代码能在前面不同的模块环境下都能正常运行。

// 函数自执行
// 模块化工厂函数
(function (root, factory) {
  	if (typeof module === "object" && typeof module.exports === "object") {
        // Node, CommonJS-like
        module.exports = factory(require('jquery'));
    }
    else if (typeof define === "function" && define.amd) {
      	// AMD 模块环境下
        define(['jquery'], factory);
    }
}(this, function ($) { // $ 要导入的外部依赖模块
    $('div')
    // ...
    function b(){}
    function c(){}

    // 模块导出数据
    return {
        b: b,
        c: c
    }
}));


(后续待补充)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值