前端工程化
组件化
更多关注的UI部分,每个组件有独立的HTML、css、js代码。
可以根据需要把它放在页面的任意部位,也可以和其他组件一起形成新的组件。一个页面是各个组件的结合,可以根据需要进行组装。
模块化
一、模块化:
侧重的功能的封装,主要是针对Javascript代码,隔离、组织复制的javascript代码,将它封装成一个个具有特定功能的的模块。
模块可以通过传递参数的不同修改这个功能的的相关配置,每个模块都是一个单独的作用域,根据需要调用。
一个模块的实现可以依赖其它模块。
二、不引入的缺点--为什么引入
index.html中完成某一功能,以下代码
<script src="util.js"></script>
<script src="a-util.js"></script>
<script src="a.js"></script>
(1)需引用的js文件中的代码中的函数必须是全局变量,才能暴露给适用方,造成全局变量污染(被其他覆盖)
(2)引用顺序不能变,按script标签书写顺序加载
(3)文件依赖关系靠开发者主观解决
(4)代码耦合度过高:代码重复率过高,则会导致本地资源包过大。
三、优点
(1)解决命名冲突问题()
(2)避免过多的文件依赖
(3)模块版本管理
(4)提高代码的可维护性,代码方便重用,别人开发的模块直接拿过来就可以使用,不需要重复开发法类似的功能。
(5)前端性能优化(可实现异步加载模块)
(6)跨环境共享模块(可实现模块的跨服务器和浏览器共享)
四、发展史:
(1)普通的函数封装
(2)封装成对象
(3)使用自执行函数,避免变量污染(function())()
(4)通过多级命名空间,但是代码变得繁琐。如:
app.util.modA = xxx; // 定义
app.tools.modA = xxx;
app.tools.modA.format = xxx;
app.tools.modA.format(); // 使用
五、几种常用的 Common JS AMD CMD ES6
Common JS (同步加载)
服务器端的模块化规范
Node JS 基于其实现
使用:
- 定义模块:module.exports属性表示当前模块对外暴露的接口,其他文件加载该模块,实际上就是读取module.exports变量;
// 导出
module.exports = {
add:function(x,y){}
};
- 加载模块:一个文件就是一个模块,require方法用来加载模块,该方法读取一个文件并执行。(同步加载)
// 导入
var math=require("module");
math.add(2,3)
优点:
简单容易使用
服务器端模块便于复用
缺点:
不适应浏览器--所以有了AMD
因为require同步加载,会造成浏览器假死。
为什么浏览器不能使用同步加载,服务端可以?
对于服务器端而言,所有模块放在本地硬盘,可以同步加载完成,等待时间就是硬盘读取的时间,但是,对于浏览器端,模块放在服务器端,等待时间取决于网速的快慢,可能要等待很长时间,浏览器处于“假死”状态
AMD(异步加载)
浏览器端的模块化规范
require.js基于其实现
使用:
- 定义模块:define([module], function(module){});module为本模块依赖的模块;回调函数通过return将想暴露的暴露出去
- 加载模块:require([module], function(module){});
优点:
- 适合在浏览器环境中异步加载模块
- 可以并行加载多个模块
缺点:
- 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不顺畅
- 不符合通用的模块化思维方式,是一种妥协的实现
RequireJS执行流程:
- require函数检查依赖的模块,根据配置文件,获取js文件的实际路径
- 根据js文件实际路径,在dom中插入script节点,并绑定onload事件来获取该模块加载完成的通知。
- 依赖script全部加载完成后,调用回调函数
CMD(同用模块定义)
浏览器端的的模块化规范
Sea.js基于其实现
使用:
- 定义模块:
- 加载模块:
define(function(require, exports, module){
var a = require('a');
a.foo();
};
AMD和CMD
- AMD和CMD最大的区别是对依赖模块的执行时机处理不同,而不是加载的时机或者方式不同,二者皆为异步加载模块;
特点:
依赖前置--js可以方便知道依赖模块是谁,立即加载
提前执行依赖的模块---执行顺序不一定是先1后2
API 一个当多个使用
特点:
依赖就近---需要使用把模块变为字符串解析一遍才知道依赖了那些模块
延迟执行依赖的模块,----只有在用到某个模块的时候再去require。
API 严格区分,职责单一,一个模块只做一件事。
ES6模块
设计思想:
尽量的静态化、使得编译时就能确定模块的依赖关系,以及输入和输出的变量(CommonJS和AMD模块,都只能在运行时确定这些东西)。
浏览器和服务器的模块规范
使用:
导出:
var a=()=>{};
var b=23;
export {
a as A,
b
}
导入:
Import {a,b} from ‘react’
优点:
- 容易进行静态分析
- 面向未来的 EcmaScript 标准
缺点:
- 原生浏览器端还没有实现该标准
- 全新的命令字,新版的 Node.js才支持。
回答“require与import的区别”
- require使用与CommonJs规范,import使用于Es6模块规范;所以两者的区别实质是两种规范的区别;
- require 是赋值过程并且是运行时才执行, import 是解构过程并且是编译时执行。require可以理解为一个全局方法,所以它甚至可以进行下面这样的骚操作,是一个方法就意味着可以在任何地方执行。而import必须写在文件的顶部。
var a = require(a() + '/ab.js')
- require的性能相对于import稍低,因为require是在运行时才引入模块并且还赋值给某个变量,而import只需要依据import中的接口在编译时引入指定模块所以性能稍高
- 在commom.js 中module.export 之后 导出的值就不能再变化,但是在es6的export中是可以的