深入浅出JS—24谈谈前端模块化

早期JS中用不到模块化,JS多用来表单验证,没必要放到多个文件中来写;
后来JS需求变得复杂:ajax前后端分离、SPA、前端路由和Node等,使得对模块化的需求愈加强烈。
为此社区中产生了AMD、CMD、CommonJS等模块化规范,随着官方ES6中ES Module的提出,社区规范可以落幕了

什么是模块化

  • 将程序划分为一个个模块,在模块内部可以编写属于自己作用域的代码,不会影响其他模块
  • 模块可以将自己希望暴露的东西暴露给别的模块使用,也可以导入其他模块暴露的变量、函数、对象等
  • 一个独立的js文件就是一个模块

没有模块化带来的问题

在html文件中,<script>标签导入的文件没有独立作用域,会出现变量命名冲突,产生难以察觉的bug,可以采用立即执行函数来包裹每个js文件,将需要暴露的变量作为函数返回值

//a.js:暴露变量a
var moduleA = (function () {
  var a = "aaaaa";

  return {
    a: a,
  };
})();
// index.js:引用a.js中变量a
(function () {
  // 现在有全局变量moduleA
  var a = moduleA.a;
  console.log("why", a);
})();

采用立即执行函数虽然解决了作用域的问题,但是依然存在模块命名冲突的问题,同时还需要记住名字,增加了维护成本

几种模块化的方案

1 CommonJS规范

Node中对CommonJS进行了支持和实现,让我们在开发node的过程中可以方便的进行模块化开发

  • 导出
const name = "xs";
function sum(n1, n2) {
  return n1 + n2;
}

// module.exports指向要导出的对象
module.exports = {
  name,
  sum,
  age: 18,
}

module.exports和exports之间建议使用前者,那么node中要exports何用,主要是为了符合CommonJS规范

  • 导入
const {name, age, sum} = require('文件路径.js')

缺点:
导入文件是同步的,在服务端导入的是本地文件,速度较快;但是在浏览器端,去服务器上请求文件会阻塞之后JS代码的运行,所以CommonJS一般用于浏览器端

2 AMD

Asynchronous Module Definition 异步模块定义

  • 异步加载模块
  • 用于浏览器端
  • 实现的库:requirejs

3 CMD

Common Module Definition 通用模块定义

  • 异步加载模块,吸收了CommonJS关键字
  • 用于浏览器端
  • 实现的库:SeaJS

4 ES Module⭐

4.1 用法

如果js文件中使用了ES Module的模块化规范,在html导入js文件时,必须设置type属性为module

<script src="./main.js" typr="module"></script>

同时,该html文件应以本地服务器的方式从浏览器获取,否则会出现CORS跨域问题。(可以安装VScode插件Live Server)

4.2 export & import

  • 导出:export ***
// 1 分别导出
export const name = "xs";
export function sum(n1, n2) {
  return n1 + n2;
}

// 2 一起导出
const name = "xs";
function sum(n1, n2) { return n1 + n2; }

export{
  name,
  age,
 // name: 'xs 错误,{}不是对象的意思,不能写键值对
}

// 3 默认导出 不能写为export default const foo = 18;
const age = 18;
export default age;
  • 导入:import *** from ***
// 1 直接导入
import {name, sum} from "./file.js";

// 2 导入时起别名
import {name as fName, sum} from "./file.js";

// 3 全部导入
import * as foo from "./file.js"
foo.name // 见名知意:知道是foo导入的name,区分于当前文件中定义的name
foo.sum

// 4 默认导入 在导入时不需要使用 {},并且可以自己来指定名字
import age from "./file.js"
  • 导入+导入 export *** from ***

在开发和封装一个功能库时,通常我们希望将暴露的所有接口放到一个文件中;
这样方便指定统一的接口规范,也方便阅读;这个时候,我们就可以使用export和import结合使用

场景:utils文件夹下有文件:math.js、format.js等,每个文件都提供了一些通用的工具方法
通常在utils文件夹下创建一个index.js文件,导入math.js、format.js中暴露的函数,然后再依次暴露出去。

// index.js

// 导出方式一
import { add, sub } from "./math.js";
import { priceFormat, typeFormat } from "./format.js";

export { add, sub, priceFormat, typeFormat };

// 导出方式二
export { add, sub } from "./math.js";
export { priceFormat, typeFormat } from "./format.js";

// 导出方式三
export * from "./math.js";
export * from "./format.js";

4.3 静态+动态导入

静态:写在文件开头,在代码执行前JS引擎先进行解析
动态:写进逻辑里,在代码执行时动态导入,使用import函数,返回值是一个promise

let isFlag = true;
if (isFlag){
  import("./module/aaa.js").then((aaa)=>{
	aaa.aaa();
  })
}else{
  import("./module/bbb.js").then((aaa)=>{
	bbb.bbb();
  })
}

4.4 原理

识别到模块化之后,解析有三个阶段

  • 构建阶段
    fetch:获取下载文件
    parse:解析为模块记录
    其中使用module map映射表来管理各依赖模块的信息
    在这里插入图片描述

  • 实例化阶段+ 求值阶段
    实例化阶段:对模块记录进行实例化,并且分配内存空间,解析模块的导入和导出语句,把模块指向对应的内存地址
    求值阶段:运行代码,计算值,并且将值填充到内存地址中
    在这里插入图片描述
    只有导出模块可以更改变量的值,导入模块无法更改变量的值

总结

AMD和CMD现在很少用了,成为时代的眼泪。目前虽然CommonJS还在用,但是ES Module作为JS语言本身提出的规范,势必会一统天下。在当前的webpack开发中,由于webpack内部做了支持,可以让CommonJS和ES Module混用,譬如CJS语法导出,ESModule语法导入,但最好不要混用。
一句话:认准并学好ES Module!

03-19
### IEEE 802.1Q VLAN Tagging Protocol Standard IEEE 802.1Q 是支持虚拟局域网(VLAN)的标准协议之一,通常被称为 Dot1q。该标准定义了一种用于以太网帧的 VLAN 标记系统以及交换机和桥接器处理这些标记帧的操作流程[^2]。 #### 协议结构概述 IEEE 802.1Q 的核心功能在于通过在以太网数据帧中插入特定字段来实现 VLAN 标签的功能。这种标签使得网络设备能够识别哪些流量属于哪个 VLAN,并据此执行转发决策。具体来说: - **Tag Header**: 在原始以太网帧头部增加了一个额外的 4 字节字段作为 VLAN 标签头。这四个字节包含了以下部分: - **Priority Code Point (PCP)**: 使用 3 比特表示优先级级别,范围从 0 到 7,主要用于 QoS 控制。 - **Canonical Format Indicator (CFI)**: 这是一个单比特位,在传统以太网环境中设置为零。 - **VLAN Identifier (VID)**: 使用 12 比特标识具体的 VLAN ID,理论上可以支持多达 4096 个不同的 VLAN(编号从 0 至 4095),其中某些特殊值保留给内部用途或管理目的。 #### 数据包处理机制 当一个带有 VLAN tag 的数据包进入支持 IEEE 802.1Q 的交换机时,它会依据此标签决定如何路由或者过滤该数据流。如果目标端口不属于同一 VLAN,则不会传输至其他无关联的物理接口上;反之亦然——只有相同 VLAN 成员之间才允许互相通信除非经过路由器跨网段访问[^1]。 此外,为了简化管理和配置过程并增强互操作性,还引入了一些辅助性的子协议和服务组件比如 GARP(通用属性注册协议)。GARP 可帮助分发有关 VLAN 成员资格的信息到各个连接节点以便动态调整其行为模式而无需频繁手动干预[^3]。 以下是创建带 VLAN TAG 的 Python 示例代码片段展示如何模拟构建这样的 Ethernet Frame: ```python from scapy.all import Ether, Dot1Q, IP, sendp def create_vlan_packet(src_mac="00:aa:bb:cc:dd:ee", dst_mac="ff:ff:ff:ff:ff:ff", vlan_id=100, src_ip="192.168.1.1", dst_ip="192.168.1.2"): ether = Ether(src=src_mac, dst=dst_mac) dot1q = Dot1Q(vlan=vlan_id) ip_layer = IP(src=src_ip, dst=dst_ip) packet = ether / dot1q / ip_layer return packet packet = create_vlan_packet() sendp(packet, iface="eth0") # Replace 'eth0' with your network interface name. ``` 上述脚本利用 Scapy 库生成包含指定源地址、目的地址及所属 VLAN 编号的数据报文并通过选定的网卡发送出去测试实际效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值