webpack的模块
前端工程化分为四个方面: 模块化,组件化,规范化和自动化
模块化
模块化是解决一个复杂问题的时候自顶向下把系统划分成为若干模块的过程,每个模块完成一个特定的子功能,所有模块按照某种方法组装起来,称为一个整体。
文件划分方式
// module-a.js
function foo() {
console.log("moduleA#foo")
}
// module-b.js
var date = "something"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"
<title>Stage 1</title>
</head>
<body>
<scrip src="module-a.js"></scrip>
<script src="module-b.js"></script>
<script>
foo()
console.log(data)
data = "other"
</script>
</body>
</html>
缺点
- 模块直接在全局工作,大量模块成员污染全局作用域
- 没有私有空间,所有模块内的成员都可以在模块外部被访问或者修改
- 一旦模块增多,容易出现命名冲突
- 无法管理模块和模块之间的依赖关系
- 在维护的过程中也很难分辨每个成员所属的模块
命名空间方式
// module-a.js
window.moduleA = {
method1: function () {
console.log("module#method1")
}
}
// module-b.js
window.moduleB = {
data: 'something',
method1: function () {
console.log('moduleb#method1')
}
}
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script src="module-a.js"></script>
<script src="module-b.js"></script>
<script >
moduleA.method1()
moduleB.method2()
moduleA.data="foo"
</script>
</body>
</html>
解决了命名冲突的问题,但是其他的问题依然存在
IIFE 立即执行函数表达式
// moduleA
;(function () {
var name = "module-a"
function method1() {
console.log(name+"#method1")
}
window.moduleA = {
method1: method1
}
})
// moduleb
;(function () {
var name = 'module-b'
function method1() {
console.log(name+"#mothod1")
}
window.moduleB = {
method1: method1
}
})
// 模块依赖处理
;(function ($) {
var name = "method-a"
function method1() {
console.log(name+'#method1')
$('body').animate({margin: '200px'})
}
window.moduleA = {
method1: method1
}
})(jQuery)
解决了上面的方式中的缺点,并且还提供了一个选项,方便传参
commonjs
模块化规范的出现 一个统一的模块化标准规范 一个可以自动加载模块的基础库
Commonjs规范是nodejs中所遵循的模块规范,该规范约定一个文件就是一个模块,每个模块都有单独的作用域,通过module.exports导出成员,然后通过require函数载入模块
适用于服务端,即nodejs。
- 所有的代码都运行在模块作用域,不会污染全局作用域
- 模块可以多次加载,但是只会在第一次加载的时候运行一次,然后运行结果就被缓存,以后再加载的话,就直接读取缓存结果,要想让模块再次运行,必须清除缓存。
- 模块加载的顺序,按照其再代码中出现的顺序
- commonjs是模块化的社区标准,nodejs是commonjs模块化规范的实现,对模块的加载是同步的,只有引入的模块加载玩不,才会执行后面的操作。
在node服务端应用中,模块化在本地,加载快,同步问题不大,但是在浏览器中,体验会很差
AMD
AMD是专门为浏览器环境设计的,定义了一套异步加载标准来解决同步的问题
define(id?: string, dependencies?: String[], factory: Function|Object)
- id: 模块的名字,字符串可选
- dependencies: 指定了所要依赖的模块列表,是一个数组,也是可选的参数,每个依赖的模块的输出将作为参数一次传入factory中,如果没有指定dependencies,默认值是[“require”,“exports”,“module”]
- factory包裹了模块的具体实现,可以为函数或者对象,如果是函数,返回值就是模块的输出接口或者值
// 不仅是加载模块,还可以用来定义模块
define('myModule', ["jquery"], function($) {
$('body').text('isboyjc')
})
// 用来加载模块
require(['myModule'], function (myModule) {
// todo
})
define(['jquery'], function($) {
$('body').text('isboyjc')
})
define(['jquery', './math.js'], function($, math) {})
define(['jquery'], function($) {
var writeName = function(selector){
$(selector).text('isboyjc')
}
// writeName 是该模块输出的对外接口
return writeName
})
define(function(require) {
// 引入依赖
var $ = require('jquery')
$('body').text('isboyjc')
})
define(['jquery','./module2.js'], function ($, module2) {
return {
start: function () {
$('body').animate({margin: '200px'})
module2()
}
}
})
本质上是使用动态创建script标签方式加载,在依赖加载完毕之后在执行,省去开发手动书写script标签还需要关注加载顺序的烦恼。
后来推出了es modules规范
于是在node.js环境中,准寻commonjs规范来组织模块,在浏览器环境中,遵循esmodule模块
CMD规范
结合了commonjs和amd。专门用于浏览器的异步加载
在cmd中,一个模块就是一个文件,define是一个全局函数,用来定义模块,define接受factory参数,factory可以是一个函数,也可以是一个对象或字符串
推崇延迟加载。
define({'name': 'hello world'})
define(`my name is {{name}}`)
// require是一个方法,接受模块表示作为唯一参数,用来获取其他模块提供的接口
// export是一个对象,用来向外提供模块接口
// module是一个对象,上面存储了与当前模块相关联的属性和方法
define(function (require, exports, module) {
var a = require('./a')
a.dosomething()
var b = require('./b')
b.dosomething()
// 异步引入
require.async('./b', function (b) {
})
// 条件引入
if(status) {
var c = require('./c')
}
exports.aaa = 'hhh'
})
ES Module
var foo = 'es module'
export {foo}
import { foo } from './module'
console.log(foo)
模块打包工具 webpack
使用的ES modules模块系统本身就存在环境兼容问题,不仅仅是js需要模块化,html和css也需要模块化
但是模块化的方式划分出来的模块文件很多,前端应用运行在浏览器中,每个文件都需要单独从服务器中请求回来。零散的模块文件会导致浏览器频繁的发送网络请求,影响用户体验
模块的管理,这里面涉及到了npm, yarn, pnpm,先不说
为了解决性能加载的问题,就诞生了webpack,webpack的工作流程可以理解为合并,在分割
合并:为了解决项目中依赖文件过多,而导致 HTTP 请求过多的问题,webpack 会把所有资源文件视为模块,分析模块之间的依赖关系,并把所有的依赖打包成一个或多个 bundle.js 文件。
分割:合并打包会带来一个问题——单文件过大,导致加载时间过长。为了解决这个问题,Webpack 引入了代码分割的概念。代码分割允许你将一个大的 bundle 文件分割成多个小文件,这些小文件在需要时才会被加载。这提高了应用程序的加载性能,因为浏览器只需要下载当前页面所需的代码块,而不是整个应用的所有代码。Webpack 提供了不同的代码分割策略,如按路由、按组件或按异步加载。
同时有可能要将es6编译为es5的,解决兼容问题
将散落的文件打包到一起,可以解决浏览器多次请求文件的问题
支持不同种类的前端资源模块