首先我们要知道webpack是什么?
webpack是一个现代JavaScript应用程序的静态模块打包工具。当webpack处理应用程序时,它会在内部构建一个依赖图,此依赖图会映射项目所圃的每个模块,并生成一个或多个bundle包! webpack本身是基于node.js开发的!(比如说:有多个js文件,我们需要构建这些js文件的依赖关系,读取这些js文件中的代码,最后合并在一起 放在一个新的js文件中,最后压缩)
我们为什么要学习webpack?(webpack的好处)
- 代码转换: TypeScript编译成JavaScript、LESS/SCSS编译成CSS、ES6/7编译为ES5、虚拟DOM编译为真实的DOM等等.。
- 文件优化:压缩JS、CSS、HTML代码,压缩合并图片,图片BASE64等
- 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码等
- 模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。
- 自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器
- 代码校验: Eslint代码规范校验和检测、单元测试等
- 自动发布:自动构建出线上发布代码并传输给发布系统
- .........
webpack的模块化编程历史
-
单例设计模式
-
AMD「require.js」
-
CommonJS
-
CMD「sea.js」
-
ES6Module
单例设计模式:最早期的模块开发管理方案
首先要知道单例设计模式
单例模式的好处:
@1:它是被闭包 包起来的 可以防止全局变量的污染。它保证了各个模块之间方法/属性的私有化
@2:可以把闭包中某些属性和方法 暴露出去 供别的模块使用,可以实现模块间方法的调用
假设说有两个模块 :A模块和B模块 A 模块中有一个方法求和,B模块中有一个方法求平均数
单例设计模式A模块js代码:
let AModule=(function(){
let name="A";
const sum=function sum(...args){
if(args.length===0)return 0;
if(args.length===1)return args[0];
return args.reduce((result,item)=>{
return result+item;
},0)
};
return {
name,
sum
}
})();
单例设计模式B模块js代码:
let BModule = (function () {
let name = 'B';
const average = function average(...args) {
args.sort((a, b) => a - b);
args.pop();
args.shift();
if (args.length === 0) return 0;
let total = AModule.sum(...args);
return (total / args.length).toFixed(2);
};
return {
name,
average
};
})();
我们可以发现一个特点 B模块一定依赖于A 模块 他们之间存在依赖关系
我们需要把所有的模块合并在一起 必须先导入A 模块,因为B模块依赖于A模块
创建一个入口文件:main.js,在入口文件中拿到所有的模块拿来用
console.log(AModule.sum(10, 20, 30, 40, 50));
console.log(BModule.average(10, 20, 30, 40, 50));
在单例设计模式中的HTML 引入的js顺序是这样的
<script src="A.js"></script>
<script src="B.js"></script>
<script src="main.js"></script>
但是存在一个弊端 js文件过多 我们需要自己构建依赖关系,梳理起来比较困难 ,比较恶心。所以诞生了新的开发管理规范
AMD[require.js]//导入框架:
相对于单例设计模式来说,在单例模式的基础上加入模块之间的依赖关系的处理。这种模式叫做AMD模式
先在HTML中导入require.min.js文件 我们使用AMD需要用到require插件。main.js 作为入口文件
<script src="require.min.js"></script>
<script src="main.js"></script>
我们先要拿require进行全局配置。在main.js中
require.config({
//全局配置
baseUrl: 'lib',//所有的模块都去lib文件夹找
});
require(['A', 'B'], function (AModule, BModule) {
console.log(AModule.sum(10, 20, 30, 40, 50));
console.log(BModule.average(10, 20, 30, 40, 50));
});
A模块js代码:使用 define 定义模块
define(function () {
const sum = function sum(...args) {
if (args.length === 0) return 0;
if (args.length === 1) return args[0];
return args.reduce((result, item) => {
return result + item;
}, 0);
};
return {
sum
};
});
B模块js代码:
define(['A'], function (AModule) { //数组中存放 所有依赖的模块 正常情况下应该是'lib/A.js'但是已经指定了公共的文件夹(lib)所以写成A .js可以忽略不写
const average = function average(...args) {
args.sort((a, b) => a - b);
args.pop();
args.shift();
if (args.length === 0) return 0;
let total = AModule.sum(...args);
return (total / args.length).toFixed(2);
};
return {
average
};
});
注意:定义模块期间,指定需要的依赖 AModule存储A模块导出的内容
/特点:所需要的依赖,需要“前置导入” 所有的依赖都需要在数组中导入
AMD模块开发思想,解决了模块之间依赖的问题。但是存在所有依赖的模块都需要前置导入!浏览器不支持AMD思想,需要我们导入插件:require.min.js
CommonJS:[不支持浏览器端 ,Node.js默认的模块规范就是CommonJS]《常用》
CommonJS实现模块的导入和导出:main.js作为入口文件
let sum = require('./A');
console.log(sum(10, 20, 30, 40));
let B = require('./B');
console.log(B.average(10, 20, 30, 40));
注意: CommonJS模块规范
CommonJS模块规范导出「多个」
module.exports = {
sum
};
导入
let A = require(模块地址);
A.sum(...);
---
CommonJS模块规范导出「一个」
module.exports = sum;
导入
let sum = require(模块地址);
A模块js代码:
const sum = function sum(...args) {
if (args.length === 0) return 0;
if (args.length === 1) return args[0];
return args.reduce((result, item) => {
return result + item;
}, 0);
};
module.exports = sum;
B模块js代码:
let sum = require('./A');//... 模块地址需要是相对地址(./ OR ../ OR /),这样才是导入自己写的模块;如果不写相对地址,只写模块名字,则是导入Node内置模块或者安装的第三方模块!
const average = function average(...args) {
args.sort((a, b) => a - b);
args.pop();
args.shift();
if (args.length === 0) return 0;
let total = sum(...args);
return (total / args.length).toFixed(2);
};
module.exports = {
average
};
CommonJS规范支持“按需导入” 什么时候用,什么时候导入 比AMD思想用起来更加方便
HTML部分
<script src="main.js"></script>
CMD「sea.js」
CMD 是淘宝中的“玉伯”,自己写了个 sea.js 插件,把CommonJS规范,搬到浏览器端也能用」
ES6Module:《常用》
特点:不导入任何插件 也可以在浏览器端 像 CommonJS 规范一样,去管理模块化开发
ES6Module模块规范:
导出:export 导入:import
@1: export let xxx = xxx;(导出多个)
@2:export const xxx = function xxx(){};(导出多个)
@3:export function xxx() { };
............
导出多个内容:[内部也是导出一个对象:把变量名作为对象的属性名,变量值作为属性值]
不支持的语法(导出对象)export {x:10};
支持的语法export default {x:10};
导入:
import {xxx} from '地址'; //直接解构赋值
import * as AAA from '地址'; //导入所有导出的内容,并且赋值给AAA这个对象
-----------------------------------
export default 值
export default {...};
export default function xxx(){};
export default xxx;
导出的还是个对象,对象中有一个叫做“default”的属性,属性值是导出的这个值;所以一个模块中,export default 只能出现一次;export default 导出的内容,在导入的时候,不能直接的解构赋值;
import xxx from '地址'; //直接获取导出的default属性值
导入时候的特点:
@1 必须放在代码最前面 “前置导入”
@2 不能省略后缀名「但是在webpack中可以省略」
@3 必须设定好相对路径
main.js中
import sum from './A.js';
import { average } from './B.js';
console.log(sum(10, 20, 30, 40, 50));
console.log(average(10, 20, 30, 40, 50));
HTML中 : 想要开启管理规范需要 设置 type="module" 就可以在JS文件中使用ES6Module模块规范 。但是有个前题就是页面的预览需要基于 http或者https 协议
<script type="module" src="main.js"></script>
A模块js代码:
const sum = function sum(...args) {
if (args.length === 0) return 0;
if (args.length === 1) return args[0];
return args.reduce((result, item) => {
return result + item;
}, 0);
};
export default sum;
B模块js代码:
import sum from './A.js';
export const average = function average(...args) {
args.sort((a, b) => a - b);
args.pop();
args.shift();
if (args.length === 0) return 0;
let total = sum(...args);
return (total / args.length).toFixed(2);
};