浏览器背景

浏览器的历程 浏览器历史


js

js二十年的历程
ecma5 = es5。2009年12月发布5.0,2011年6月发布5.1,成为ISO国际标准
ecma6 = es6。2015年6月正式发布(简称ES2015)

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是前端语言,运行在客户端,不像服务端语言一样,服务端的模块化都在本地计算机缓存、文件系统中,而浏览器客户端都是在网络中,所以前端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
2
3
4
5
6
7
8
// moduleA.js
module.exports = function( value ){
return value * 2;
}

// moduleB.js
var multiplyBy2 = require('./moduleA');
var result = multiplyBy2(4);

由于commonJs的规范在服务端大方光彩,但是到了客户端浏览器的表现就很一般了,因为commonJs是同步加载,对于服务端加载的资源都是在缓存或者本地文件中,耗时基本可忽略
但是在浏览器端可能会造成阻塞(取决于网络的好坏)白屏时间过长,用户体验不好,所以产生了AMD和CMD

AMD-requireJS

客户端-浏览器专用的包管理
异步模块定义Asynchronous Module Definition,使浏览器避免假死长时间白屏的出现
比较著名的实现着: requireJS ,其他如curl 、Dojo 、Nodules等。。。

使用方式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
// 三个参数,第一个为模块名(可选,不指定默认为文件名称),
// 第二个参数为当前模块依赖的模块(可选,不指定默认为["require", "exports", "module"])
// 第三个参数为当前模块的定义,参数为依赖的模块(如有有依赖其他模块的话)
// define(id?, dependencies?, factory);
define('myModule', ['jquery'], function($) {
// $ 是 jquery 模块的输出
$('body').text('hello world');
});

// 使用,等模块加载完时,会进行回调
require(['myModule'], function(myModule) {
//...
});

AMD的规范中,如果要依赖其他模块则必须先等待其他模块加载完,才会进行回调

CMD-seaJS

客户端-浏览器专用的包管理 CMD为通用模块定义Common Module Definition,是国内阿里团队发展出来的,区别于AMD不同之处就是尽可能的懒加载其他模块 比较著名的实现着: seaJS

使用方式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义模块  myModule.js
define(function(require, exports, module) {
var $ = require('jquery.js'); // 同步
$('div').addClass('active');

// 也可以写成异步加载
require.async('jquery.js', function ($) {
$('div').addClass('active');
});
});

// 加载模块
seajs.use(['myModule.js'], function(my){
// ...
});

CMD兼容AMD的语法所以大体使用上大差不大,只不过CMD推崇的是as lazy as possible

ES6-Module

在ES6之前,JavaScript中并没有在语言标准中提供模块定义规范,而在非语言层面,开源社区制定了模块定义规范,主要有CommonJS和AMD以及CMD,而这些模块的引入还不能做到静态化的引入, 什么意思呢?比如需要引入某个模块当中的其中一个方法,那么就得需要加载完该模块后才能引入其中的方法, 而且模块越来越多的情况下,ecma规范也应该统一这种局面了

在ES6中,定义了import和export两个关键字语法

  • export
    关键字定义导出对象,这个关键字可以无限次使用
  • import
    关键字引入导入对象,这个关键字可导入任意数量的模块

    import 后面直接跟模块,为静态导入,不能写在if块中
    import('模块'), 异步动态的导入模块,并返回promise,可写在if块中

模块结构可以做静态分析。这使得在编译时就能确定模块的依赖关系,以及输入和输出的变量 每一个模块只加载一次(是单例的), 若再去加载同目录下同文件,直接从内存中读取 import 是静态执行,所以无关写在哪个位置,为了可读性,规范写在文件开头

具体使用如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// test.js
let myName = "Tom";
let myfn = function(){
return "My name is" + myName + "! I'm '" + myAge + "years old."
}
let myClass = class myClass {
static a = "yeah!";
}
export { myName, myAge, myfn, myClass }

// 直接静态导入
import { myName as mmmyName, myfn, myClass } from "./test.js";
// 或者异步导入(可写在if块中)
import('./test.js').then(function({myfn}) {
console.log(myfn())
})
console.log(myfn());// 输出: My name is Tom! I'm 20 years old.
console.log(mmmyName);// 自定义导入的别名
console.log(myClass.a );// yeah!

export default 命令 该命令只能在一个文件(模块)中,使用一次

1
2
3
4
5
6
// xxx.js
var a = "123";
export default a; // 仅有一个

import b from "./xxx.js"; // 使用任意变量接收
console.log(b); // 输出 123

webpack

由于es6语法的横空出世,有些浏览器还没有普及,为了广泛的兼容做适配,webpack又出现了
webpack是一个打包工具,他可以集成很多插件,默认内置只能打包js

德国人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做的事情很多,引入你想要的插件,不仅仅打包代码,还可以引入babel做代码适配(es6兼容es5),还可以节省资源压缩代码,解析项目中的扩展语言如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年由于内部分叉,所独立出来的项目,后期内部和解又合并到一起了

具体可参考nodeJs学习