首页 > 作文

【JS基础】一文看懂前端模块化规范

更新时间:2023-04-03 13:48:19 阅读: 评论:0

前言

前端的模块化之路经历了漫长的过程,想详细了解的小伙伴可以看浪里行舟大神写的前端模块化详解(完整版),这里根据大佬们写的文章,做了汇总和整理,希望读完的小伙伴能有些收获。

什么是模块

中华人民共和国户口登记条例 将一个复杂的程序依四川外国语学校据一定的规则(规范)封装成几个块(文件), 并进行组合在一起 块的内部数据与实现是私有的,只是向外部暴露一些接口(方法)与外部其它2018年大事件模块通信

commonjs

node 应用由模块组成,采用 commonjs 模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。在服务器端,模块的加载是运行时同步加载的;在端,模块需要提前编译打包处理。

commonjs规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。

基本语法:

暴露模块:module.exports = value 或 exports.xxx = value 引入模块:require(xxx),如果是第三方模块,xxx为模块名;如果是自定义模块,xxx为模块文件路径

但是,commonjs有一个重大的局限使得它不适用于浏览器环境,那就是require操作是同步的。这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于”假死”状态。

因此,浏览器端的模块,不能采用”同步加载”(synchronous),只能采用”异步加载”(asynchronous),这就是amd规范诞生的背景。

amd

特点:非同步加载模块,允许指定回调函数,浏览器端一般采用amd规范

代表作:require.js

用法:

//定义没有依赖的模块define(function(){   return 模块})//定义有依赖的模块define(['module1', 'module2'], function(m1, m2){   return 模块})//引入使用模块require(['module1', 'module2'], function(m1, m2){   //使用m1/m2})

cmd

特点:专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行

代表作:a.js

用法:

//定义没有依赖的模块define(function(require, exports, module){  exports.xxx = value  module.exports = value})//定义有依赖的模块define(function(require, exports, module){  //引入依赖模块(同步)  var module2 = require('./module2')    //引入依赖模块(异步)    require.as什么食物可以化痰ync('./module3', function (m3) {    })  //暴露模块  exports.xxx = value})//引入使用模块define(function (require) {  var m1 = require('./module1')  var m4 = require('./module4')  m1.show()  m4.show()})

cmd与amd区别

amd和cmd最大的区别是对依赖模块的执行时机处理不同,而不是加载的时机或者方式不同,二者皆为异步加载模块。

amd依赖前置,js可以方便知道依赖模块是谁,立即加载;

而cmd就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病cmd的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略。

一句话总结:
两者都是异步加载,只是执行时机不一样。amd是依赖前置,提前执行,cmd是依赖就近,延迟执行。

umd

umd是amd和commonjs的糅合:

amd模块以浏览器第一的原则发展,异步加载模块。

commonjs模块以服务器第一原则发展,选择同步加载,它的模块无需包装(unwrapped modules)。

这迫使人们又想出另一个更通用的模式umd (u货轮niversal module definition)。希望解决跨平台的解决方案。

umd先判断是否支持node.js的模块(exports)是否存在,存在则使用node.js模块模式。

在判断是否支持amd(define是否存在),存在则使用amd方式加载模块。

(function (window, factory) {    if (typeof exports === 'object') {             module.exports = factory();    } el if (typeof define === 'function' && define.amd) {             define(factory);    } el {             window.eventutil = factory();    }})(this, function () {    //module ...});

es6模块化

es6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。commonjs 和 amd 模块,都只能在运行时确定这些东西。比如,commonjs 模块就是对象,输入时必须查找对象属性。

es6 module默认目前还没有被浏览器支持,需要使用babel,在日常写demo的时候经常会显示这个错误:

es6模块使用import关键字导入模块,export关键字导出模块:

/** 导出模块的方式 **/var a = 0;export { a }; //第一种   export const b = 1; //第二种   let c = 2;export default { c }//第三种 let d = 2;export default { d as e }//第四种,别名/** 导入模块的方式 **/import { a } from './a.js' //针对export导出方式,.js后缀可省略import main from './c' //针对export default导出方式,使用时用 main.cimport 'lodash' //仅仅执行lodash模块,但是不输入任何值

命名式导出与默认导出

export {<变量>}这种方式一般称为 命名式导出 或者 具名导出,导出的是一个变量的引用
export default这种方式称为 默认导出 或者 匿名导出,导出的是一个

举例:

// a.jslet x = 10let y = 20ttimeout(()=>{    x = 100    y = 200},100)export { x }export default y// b.jsimport { x } from './a.js'import y from './a.js'ttimeout(()=>{    console.log(x,y) // 100,20},100)

es6 模块与 commonjs 模块的差异

① commonjs 模块输出的是一个值的拷贝,es6 模块输出的是值的引用。

commonjs 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。而且,commonjs 模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,返回的都是第一次运行结果的缓存,除非手动清除缓存。

?es6 模块的运行机制与 commonjs 不一样,js 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用,等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,es6 的import有点像 unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,es6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

② commonjs 模块是运行时加载,es6 模块是编译时输出接口。

commonjs 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。

例如:

// commonjs模块let { stat, exists, readfile } = require('fs');// 等同于let _fs = require('fs');let stat = _fs.stat;let exists = _fs.exists;let readfile = _fs.readfile;

上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取 3 个方法。因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。

es6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。通过export命令显式指定输出的代码,import时采用静态命令的形式。即在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”或者“静态加载”。

// es6模块import { stat, exists, readfile } from 'fs';

上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。即 es6 可以在编译时就完成模块加载,效率要比 commonjs 模块的加载方式高。当然,这也导致了没法引用 es6 模块本身,因为它不是对象

由于 es6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 javascript 的语法,比如引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。

除了静态加载带来的各种好处,es6 模块还有以下好处:

不再需要umd模块格式了,将来服务器和浏览器都会支持 es6 模块格式。目前,通过各种工具库,其实已经做到了这一点。 将来浏览器的新api 就能用模块格式提供,不再必须做成全局变量或者navigator对象的属性。 不再需要对象作为命名空间(比如math对象),未来这些功能可以通过模块提供。

总结

commonjs规范主要用于服务端,加载模块是同步的,这并不适合在浏览器环境,因为同步意味着阻塞加载,浏览器资源是异步加载的,因此有了amd、cmd解决方案。 amd规范在浏览器环境中异步加载模块,而且可以并行加载多个模块。不过,amd规范开发成本高,代码的和书写比较困难,模块定义方式的语义不顺畅。 cmd规范与amd规范很相似,都用于浏览器编程,依赖就近,延迟执行,可以很容易在node.js中运行。不过,依赖spm打包,模块的加载逻辑偏重。 es6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 commonjs 和 amd 规范,成为浏览器和服务器通用的模块解决方案。

以上是本篇文章的内容,欢迎大家提出自己的想法,我们一起学习进步,与君共勉。

参考资料

前端模块化详解(完整版)
ecmascript 6 入门
近一万字的es6语法知识点补充
彻底搞清楚javascript中的require、import和export
commonjs,amd,cmd,es6,require 和 import 详解

本文发布于:2023-04-03 13:48:17,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/c6a4c2497ef88d2b4d45381982d54117.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:【JS基础】一文看懂前端模块化规范.doc

本文 PDF 下载地址:【JS基础】一文看懂前端模块化规范.pdf

标签:模块   加载   浏览器   对象
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图