现在的前端开发, 不仅仅是完成浏览的基本需求,并且通常是一个单页面应用,每一个视图通过异步的方式加载,这导致页面初始化和使用过程中会加载越来越多的 JavaScript 代码. 如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,就需要一个模块化系统
把函数作为模块 缺陷: 污染全局变量 模块成员之间没什么关系 面向对象思想 并使用立即执行函数 实现闭包 避免了变量污染 同时同一模块内的成员也有了关系 在模块外部无法修改我们没有暴露出来的变量、函数 这就是简单的模块
模块的加载和传输,我们首先能想到两种极端的方式,一种是每个模块文件都单独请求,另一种是把所有模块打包成一个文件然后只请求一次。显而易见,每个模块都发起单独的请求造成了请求次数过多,导致应用启动速度慢;一次请求加载所有模块导致流量浪费、初始化过程慢。这两种方式都不是好的解决方案,它们过于简单粗暴。
分块传输,按需进行懒加载,在实际用到某些模块的时候再增量更新,才是较为合理的模块加载方案。要实现模块的按需加载,就需要一个对整个代码库中的模块进行静态分析、编译打包的过程。
在上面的分析过程中,我们提到的模块仅仅是指 JavaScript 模块文件。然而,在前端开发过程中还涉及到样式、图片、字体、HTML 模板等等众多的资源。如果他们都可以视作模块,并且都可以通过require
的方式来加载,将带来优雅的开发体验,那么如何做到让 require
能加载各种资源呢?在编译的时候,要对整个代码进行静态分析,分析出各个模块的类型和它们依赖关系,然后将不同类型的模块提交给适配的加载器来处理。Webpack 就是在这样的需求中应运而生。
<script>
的书写顺序进行加载服务器端的 Node.js 遵循 CommonJS 规范,该规范的核心思想是允许模块通过 require
方法来同步加载所要依赖的其他模块,然后通过 exports
或 module.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);
优点:
缺点:
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 = ...
})
优点:
缺点:
5.UMD
6.ES6 模块
ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。
import "jquery";
export function doStuff() {}
module "localModule" {}
优点:
缺点:
实现:
从前有两个规范,一个是 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
模块化规范
Webpack 对 CommonJS 的 AMD 的语法做了兼容, 方便迁移代码
不过实际上, 引用模块的规则是依据 CommonJS 来的
require('lodash') // 从模块目录查找
require('./file') // 按相对路径查找
AMD 语法中, 也要注意, 是按 CommonJS 的方案查找的
define (require, exports. module) ->
require('lodash') # commonjs 当中这样是查找模块的
在一开始,我们先讲一下它和以往我们所用的模块管理工具有什么不一样。在最开始的阶段,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。
browserify 的出现非常棒,但 webpack 更胜一筹!
我们来看看 webpack 支持哪些功能特性:
1. export default{
add(){}
}
2. export fucntion add(){} 相当于 将 add 方法当做一个属性挂在到 exports 对象
如果导出的是: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'
CommonJS - Node.js
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);
##回顾 webpack
根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。如何在一个大规模的代码库中,维护各种模块资源的分割和存放,维护它们之间的依赖关系,并且无缝的将它们整合到一起生成适合浏览器端请求加载的静态资源。市面上已经存在的模块管理和打包工具并不适合大型的项目,尤其单页面 Web 应用程序。最紧迫的原因是如何在一个大规模的代码库中,维护各种模块资源的分割和存放,维护它们之间的依赖关系,并且无缝的将它们整合到一起生成适合浏览器端请求加载的静态资源。 这些已有的模块化工具并不能很好的完成如下的目标:
Webapck 和其他模块化工具有什么区别呢?
require("./templates/" + name + ".jade")
。CommonJS 和 AMD 是用于 JavaScript 模块管理的两大规范,前者定义的是模块的同步加载,主要用于 NodeJS ;而后者则是异步加载,通过 requirejs 等工具适用于前端。随着 npm 成为主流的 JavaScript 组件发布平台,越来越多的前端项目也依赖于 npm 上的项目,或者 自身就会发布到 npm 平台。因此,让前端项目更方便的使用 npm 上的资源成为一大需求。 web 开发中常用到的静态资源主要有 JavaScript、CSS、图片、Jade 等文件,webpack 中将静态资源文件称之为模块。webpack 是一个 module bundler(模块打包工具),其可以兼容多种 js 书写规范,且可以处理模块间的依赖关系,具有更强大的 js 模块化的功能。Webpack 对它们进行统 一的管理以及打包发布
1. 对 CommonJS、AMD、ES6 的语法做了兼容 2. 对 js、css、图片等资源文件都支持打包 3. 串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性,例如提供对 CoffeeScript、ES6 的支持 4. 有独立的配置文件 webpack.config.js 5. 可以将代码切割成不同的 chunk,实现按需加载,降低了初始化时间 6. 支持 SourceUrls 和 SourceMaps,易于调试 7. 具有强大的 Plugin 接口,大多是内部插件,使用起来比较灵活 8.webpack 使用异步 IO 并具有多级缓存。这使得 webpack 很快且在增量编译上更加快
1
zjp 2018-11-26 12:13:31 +08:00 via Android
楼主三小时三个贴,居然不贴个博客或者公众号啥的,太奇怪了:doge:
|
2
codermagefox 2018-11-26 12:17:05 +08:00
浅谈模块化开发:后端玩剩下的东西,包装个概念又拿出来卖
|
3
yuduxyz 2018-11-26 12:31:04 +08:00
@codermagefox 前端在这块确实落后了,但是发展也总是要有个过程的
|
4
codermagefox 2018-11-26 13:47:13 +08:00
@yuduxyz #3 发展归发展,吹捧归吹捧.我现在比较烦的就是一大堆吹捧概念的,本来稳步发展是一点毛病没有的,硬是有一大堆人跑出来把那些概念吹捧成什么多么🐂🍺的创新,搞得好像前端天下第一.技术狂热没问题,暴露无知给人看笑话就很有问题了..虽然我也是这么过来的.
所以看到这种,只能一笑置之了.李姐万岁嘛. |
5
geshansuiyue 2018-11-26 14:00:10 +08:00
复制粘贴这么多意义在哪。
|
6
majun OP @zjp 最近公司比较闲所以... 博客 http://www.needqq.com 不过好久没管了
|
7
majun OP @geshansuiyue 归纳梳理,不过是早期整理的,还在继续完善中...然后发现 V2EX 不能编辑???我的锅 让半成品的东西让大佬们看见了
|