浏览器背景
浏览器的历程
js
javaScript大家最熟悉不过啦,他是通用的浏览器脚本语言,简称js,不过还有一个名词叫EcmaScript,他们之间是什么关系呢?
原来EcmaScript是制定规范,javaScript是实现,ecma全称为 europe computer manufactures association即欧洲电脑制造商协会
自从浏览器诞生到现在,浏览器脚本语言一直是Js的天下-如上图的js诞生历程,从诞生之日起就确定了他是前端开发的唯一标准,这一切都得归功于布兰登·艾奇,js发明时吸收了以下几个语言的特点
- 基本语法、数据结构
- java、C
- 函数的用法
- scheme
- 原型链继承
- self语言
而且js是单线程模型,在任何时刻js的代码只有一处在执行,这也为后面的异步通信服务端的语言nodeJs奠定了语法的基础
js = ecma规范 + webApi(dom + bom)
DOM(document object model):文档对象模型,提供操作页面元素的方法和属性,如新增文字、图片,编辑文字,图片等
BOM(browser object model);浏览器对象模型,提供一些属性和方法可以操作浏览器,如关闭页面、刷新页面、前进后退等
js包管理---模块化
因为js没有模块的功能,更没有一个广泛能应用的标准库,所以诞生了诸多的js模块管理工具
由于js是前端语言,运行在客户端,不像服务端语言一样,服务端的模块化都在本地计算机缓存、文件系统中,而浏览器客户端都是在网络中,所以前端js的模块化比起后端模块的设计要考虑的更多
commonjs、requireJS(AMD)、seaJS(CMD)、webpack、ES6(Module)等,他们之前的关系是什么呢?哪种才是大众主流的呢?
commonJs
比较著名的实现者:nodeJs
最早的模块规范定制者,这个规范已经有很多版本和具体实现
commonJs前身叫serverJs,初衷是为了打破js只能在浏览器中运行的局面,构建JavaScript能在桌面环境、服务端运行的生态系统
这个项目最开始是由 Mozilla 的工程师 Kevin Dangoor 在2009年1月创建的,当时的名字是 ServerJS
2009年8月,这个项目改名为 CommonJS,以显示其 API 的更广泛实用性
该规范的主要内容是,模块必须通过module.exports
导出对外的变量或接口,使用者通过require('模块名')
来导入其他模块的输出到当前模块作用域中
规范例子
1 | // moduleA.js |
由于commonJs的规范在服务端大方光彩,但是到了客户端浏览器的表现就很一般了,因为commonJs是同步加载,对于服务端加载的资源都是在缓存或者本地文件中,耗时基本可忽略
但是在浏览器端可能会造成阻塞(取决于网络的好坏)白屏时间过长,用户体验不好,所以产生了AMD和CMD
AMD-requireJS
客户端-浏览器专用的包管理
异步模块定义Asynchronous Module Definition,使浏览器避免假死长时间白屏的出现
比较著名的实现着: requireJS ,其他如curl 、Dojo 、Nodules等。。。
使用方式如下
1 | // 三个参数,第一个为模块名(可选,不指定默认为文件名称), |
AMD的规范中,如果要依赖其他模块则必须先等待其他模块加载完,才会进行回调
CMD-seaJS
客户端-浏览器专用的包管理 CMD为通用模块定义Common Module Definition,是国内阿里团队发展出来的,区别于AMD不同之处就是尽可能的懒加载其他模块 比较著名的实现着: seaJS
使用方式如下
1 | // 定义模块 myModule.js |
CMD兼容AMD的语法所以大体使用上大差不大,只不过CMD推崇的是as lazy as possible
ES6-Module
在ES6之前,JavaScript中并没有在语言标准中提供模块定义规范,而在非语言层面,开源社区制定了模块定义规范,主要有CommonJS和AMD以及CMD,而这些模块的引入还不能做到静态化的引入, 什么意思呢?比如需要引入某个模块当中的其中一个方法,那么就得需要加载完该模块后才能引入其中的方法, 而且模块越来越多的情况下,ecma规范也应该统一这种局面了
在ES6中,定义了import和export两个关键字语法
- export
关键字定义导出对象,这个关键字可以无限次使用 - import
关键字引入导入对象,这个关键字可导入任意数量的模块
模块结构可以做静态分析。这使得在编译时就能确定模块的依赖关系,以及输入和输出的变量 每一个模块只加载一次(是单例的), 若再去加载同目录下同文件,直接从内存中读取 import 是静态执行,所以无关写在哪个位置,为了可读性,规范写在文件开头
具体使用如下
1 | // test.js |
export default 命令 该命令只能在一个文件(模块)中,使用一次
1 | // xxx.js |
webpack
由于es6语法的横空出世,有些浏览器还没有普及,为了广泛的兼容做适配,webpack又出现了
德国人Tobias,一个写java不写web页面的程序员,2013年3月10号,发明了当代 web 开发的基石
灵感来自于Google当时的项目GWT(Google Web Toolkit)把java转换成javascript的项目,GWT里面有个feature叫 code splitting(就是webpack的主要特性)Code Splitting 是什么以及为什么
在以前,为了减少 HTTP 请求,通常地,我们都会把所有的代码都打包成一个单独的 JS 文件。但是,如果这个 JS 文件体积很大的话,那就得不偿失了
这时,我们不妨把所有代码分成一块一块,需要某块代码的时候再去加载它;还可以利用浏览器的缓存,下次用到它的话,直接从缓存中读取。很显然,这种做法可以加快我们网页的加载速度,美滋滋!
所以说,Code Splitting 其实就是把代码分成很多很多块( chunk )咯于是,他给当时用nodeJs做前端的项目库modules-webmake(同commonjs的规范)提了一个issue
但是由于没有采用,于是他自己fork了一份modules-webmake代码,起名叫webpack,便开始了新框架的研发
具体诞生细节可看知乎文章
webpack做的事情很多,不仅仅适配代码,还可以节省资源压缩代码,解析项目中的扩展语言如less,sass,输出浏览器可以识别的语言以及压缩图片文字等。。。
总计webpack就是一系列的前端代码构建工具,webpack基于nodeJs
nodejs
作为服务端语言,主要的要求是性能高,延迟低,尤其是io方面的,因为服务端主要面向的是提供服务,避免不了流量的冲击,而面向高性能服务端的开发,需要对内核、 io、 多路复用、 select、 pull、 epoll等技术肯定要了如指掌
在2009年,Ryan Dahl(瑞安·达尔)用C++工作时,发现这些技术组合搞起来简直麻烦的要死,于是在想办法提高工作效率的驱动下,开发出了nodeJs,但是为什么nodeJs和javaScript语法那么的像呢?
因为本身就不是一个从0到1的过程,本身就是想解决io高性能方面的问题,如果要设计一门语言,不仅有投入成本、学习成本、就连推广成本也无法估量,所以Ryan Dahl想,有没有是 单线程的、用的比较广的、学习成本低的语言呢,改造起来又不是特别大,扩展性比较高的语言呢?
JavaScript是单线程模型,浏览器发起的ajax又是非阻塞的,这就导致了js的io只能是异步,而其他语言有同步io,大家写同步io习惯了改异步io又懒得改,所以瑞安·达尔这时候和javaScript就不谋而合了,打算把他改造为一个异步非阻塞的io的服务端语言
这样会前端js的程序员稍微加以学习NodeJs,就是名副其实的全栈程序员了
NPM(node package manage)实践了CommonJS的包规范---全球最大的模块仓库
因为nodeJs的npm包管理的出现,开源的、第三方的、框架等都聚集到了一块,规范了代码的生产
前端框架形形色色、琳琅满目,因为node和js语法大差不差,所以前端开发者都会用到npm包管理,使其前端大规模协作开发成为可能
你也可以去npm官方注册一个账号并发布自己的代码,供大家使用
nodeJs主要帮助开发着在无需关系io、内核等方面的技术问题时,能简单的开发出一套高性能io的代码。减少开发者工作量的同时,大大提高服务端的性能
他的原理主要就是用回调+函数式的编程(事件驱动)构成便捷的异步的io,提供便捷的同时也带来了回调地狱的痛点(debug时很头疼)
nodejs = io.js,io是2014年由于内部分叉,所独立出来的项目,后期内部和解又合并到一起了