Use ES6 With Babel6 in Nodejs

发布在 Node.js

目录

  1. 什么是Babel
    1. ES6
    2. Babel
  2. babel5 和 babel6 的区别
  3. Quick Start
    1. 建立空文件夹 babel6
    2. 安装Babel6
    3. require hook
    4. 安装插件
    5. 书写优雅的ES6代码
    6. Run it
  4. 内容解释
    1. .babelrc
      1. “presets”
    2. require hook
    3. CLI
      1. babel
      2. babel-node
      3. babel-doctor
      4. babel-external-helpers
  5. 总结
  6. 更新

什么是Babel

相信很多新手没有听说过BabelES6,如果你是老手的话,那么请自动忽略~

ES6

ES6也就是ECMAScript 6,也就是最新的一代js规范,添加了很多语言的特性,包括模块管理,类,块级作用域等等内容。我最喜欢的就是箭头函数,优雅~

Babel

然而虽然ES6很棒,但是现在几乎没有浏览器或者Node(我记得5.0已经全部支持了es6,可是为啥我试着却不行。。。似乎要开启全部的harmony)能够完全支持es6的代码,那么问题来了,如果我想体验一下es6的代码,怎么办??

一个很简单的思路便是:

我写个程序,将es6代码转换成es5代码进行运行不就好了,很棒

Babel就是干的这个事情。

babel5 和 babel6 的区别

对于Babel来说,现在有了两个版本,一个是5,一个是6,那么两者有什么区别呢?

  • 5对新手更加友好,因为只需要安装一个babel就可以了,而6需要安装比较多的东西和插件才可以。
  • 相比5来说,6将命令行工具和API分开来了,最直观的感觉就是,当你想在代码中运行es6代码的话,需要安装babel-core,而如果你想在终端编译es6或者是运行es6版本的REPL的话,需要安装babel-cli
  • 也许有人问,原先的babel去哪了?是这样的,这个babel的package到了6版本之后虽然还是能安装,但是已经不具有任何的有效的代码了。取而代之的是一段提示文字,提示你需要安装babel-core或者babel-cli。所以你在babel6的情况下,完全不需要安装babel
  • 6将babel插件化,当你第一次安装babel-core并且按照以前的方式来加载require hook的话,你回发现代码无法运行:
    1
    require('babel-core/register');

就是因为babel6整体插件化了,如果你想使用es6语法,需要手动加载相关插件。

这里有一篇文章,建议看一下《The Six Things You Need To Know About Babel 6》


Quick Start

建立空文件夹 babel6

建立空文件夹babel6作为本次的目录,并npm init

安装Babel6

1
npm install babel-core --save

如果觉得慢,可以使用淘宝镜像cnpm
此时,基础的babel6就安装完成了,如果你想安装babel5,那么执行如下的代码

1
npm install babel@5 --save

即可使用babel5,那么在后文的中,统一使用babel6

安装babel6

require hook

安装好之后,问题来了,如何使用呢?

相信使用过coffee的人一定知道register,那么在babel中同样不例外,也可以使用同样的方法。

start.js
1
2
3
require('babel-core/register');

require('./app');

大家可能以为这样我就可以在app.js中优雅的使用es6了,在babel5中确实是这样的,但是在babel6中,却不一样了。

如果你这样写完,并没有任何作用,因为你缺少一个插件。

安装插件

如果想使用es6语法,必须安装一个插件

1
npm install babel-preset-es2015

然后在文件夹下面创建一个叫.babelrc的文件,并写入如下代码:

.babelrc
1
2
3
{
"presets": ["es2015"]
}

下面你就可以很优雅的书写你的es6代码了。
安装es2015插件

书写优雅的ES6代码

下面我们写一段优雅的代码

app.js
1
2
3
4
5
let first = (size, ...args) => [...args].slice(0, size);

export default first;

console.log(first(2,1,2,3));

Run it

直接运行,不说话~~~
Run it


内容解释

.babelrc

什么是.babelrc文件呢?熟悉linux的同学一定知道,rc结尾的文件通常代表运行时自动加载的文件,配置等等的,类似bashrc,zshrc。同样babelrc在这里也是有同样的作用的,而且在babel6中,这个文件必不可少。

  • 里面可以对babel命令进行配置,以后在使用babel的cli的时候,可以少写一些配置
  • 还有一个env字段,可以对BABEL_ENV或者NODE_ENV指定的不同的环境变量,进行不同的编译操作

“presets”

这个是babel6新加的,就是代表需要启动什么样的预设转码,在babel6中,预设了6种,分别是

  • es2015
  • stage-0
  • stage-1
  • stage-2
  • stage-3
  • react

至于如何安装,请查看balel官网

而且,对.babelrc的设置,你可以存放在package.json中的。如下:

1
2
3
4
5
6
7
{
...
"babel": {
"presets": ["es2015"]
},
...
}

require hook

require hook 的作用就是替换原先的require,以便在加载自动对代码进行编译,运行。

其实这个做的便是重写require.extensions中对应的扩展名的加载程序,并且默认会判断这个文件是否是node_modules中的模块,如果是的话,那么将不会进行转换。否则的话,会进行转换。

CLI

其实babel也可以当做全局变量来使用的

1
npm install babel-cli -g

安装上后,会安装如下四个程序到全局环境中:

  • babel
  • babel-node
  • babel-doctor
  • babel-external-helpers

babel

这个就是编译js文件的全局变量,具体如何使用,大家请参照官网。使用方法和coffee,style,less了类似,就不多讲了

babel-node

这里主要说一下这个东西,就是这个的作用就是提供一个node命令相同的REPL环境,不过这个环境会在执行之前讲代码进行编译。

坑1:上文讲到,babel6默认是无法编译es6文件的,需要你手动安装es2015的preset,同样,全局模式下,也需要这个preset。

那么问题来了,我们怎么安装这个preset呢?global?所以这是一个坑,我在babel的issue中找到这样的一条。作者给出这样的回答:我们处理preset和plugin是依据于输入的文件,而你直接运行CLI是没有输入文件的,也就无法定位preset和plugin的位置。言下之意就是不要全局安装,虽然我们给你了你全局安装的方式。然后作者关闭了issue,表示很无奈。。。。

解决方案1: 经过寻找,找到一种解决方案,就是创建一个空项目,安装babel-preset-es2015,并写入对应的配置进入.babelrc,然后在这个目录下运行babel-node即可正常运行

不过这种方式太麻烦了,所以,如果大家想体验一下es6的REPL的话,建议安装babel5

1
npm install babel@5 -g

babel-doctor

就是检查babel状况的,
主要检查以下几个内容

  • 是否发现了.babelrc配置文件
  • 是否有重复的babel安装包,比如说安装了5和6
  • 所有的babel安装包是否已经升级到了最新版
  • 并且 npm >= 3.3.0

babel-external-helpers

就是讲一些公共的帮助函数提取成一个文件,其实就做了这一个作用。。。

总结

这是我的第一篇关于es6的教程,如果大家有什么不好的地方,请及时想我反馈

更新

2015-11-15 添加了CLI

注释和共享

大学牲&程序猿

我为什么要写这篇文章

我是一名大学牲,你别不相信,在来大学之前,我坚信自己是一个好学生,热爱学习,懂得学习。我之所以没有考到好的学校,不是因为自己能力不行,是因为自己不在意而已。

可事实证明是,虽然高考在某一些方面确实是会扼杀部分的才能,而更多的是能够区分不同的人。

当我作为一个一般的学生进入一般的大学的时候,真正的体会到了那种好大学和坏大学的区别。

好大学,都是一种朴素的情绪,任何事情都是踏踏实实的来做。

或许这篇文章是给我自己的,让自己时刻去警醒。

互联网在技术上的浮躁情绪

而在我们的大学,到处都充斥着一种浮躁的情绪。在计算机方面:

  • 比谁懂得多,你懂mysql,不行我也要懂。你会nodejs,我也去学。他会前端,我也去学。他会Python,我也去学,结果到头来,你确实是知道了很多,但是不得不说,你仅仅是懂,如果让你去仔细写一个项目的话,根本不行。
  • 在我们社团中,虽然我很尊重我们的技术副站长,可是总是充斥着一种如果你不学会前端,后端,运维,装逼,瞎扯,看B站,几乎都不好意思出现在社团中。而且,不管你懂得深不深,只要你懂,你就可以在社团中瞎扯了。虽然我不反对对全栈技术的支持,但是我反对这种只为了广度,而没有深度的学习。今天看着他们搭建服务器,讲想看看mysql,明天看着他们写api,就想学学node,后天看着前端妹子漂亮,然后就去写HTML,可是最后呢?你知道了很多,但是如果给你一周的时间,你能完成什么东西呢?

    是的,你必须要有坚实方向感,如果你是iOS工程师,先干好自己的事情,然后再去学习其他的。如果你是前端开发工程师,你要着眼于前端,而其他内容都是你所需要了解即可。不要因为这一刻我买了苹果手机,坚定了写iOS程序的愿望,然后看着舍友都会写前端,然后就果断跳去前端的阵营。
    记住,学精不等于学会

  • 但是,在我们社团中,还充斥这一种学会等于学精的感觉。我很讨厌。

  • 其次体现在创业上,很多项目都是一时兴起,后面全都靠的是摸索。

这种浮躁情绪在大学生中的体现

或许这种体现,就是浅尝辄止,无法深入下去。对于我而言,我发现我很多东西都是接触过,但是没有深入研究过。而这种感觉总让我感觉不舒服,难受。我宁愿没有接触过那些东西。

如果解决这种浮躁情绪

或许这种情绪的来源在于大家对自己地位的在意。比如说,当我自己接触过之后,就可以和他们聊起来,当时自己接触的非常浅,每当有别人来尝试这个东西的时候,就会担心他学的比自己好,自己原本厉害的形象被损毁,然后不得不再去寻找其他的可以装逼的技术。从而在这里产生了一个恶性循环。让自己越来越深的陷入了这个循环中。无法自拔。

如何更好的在这种环境中生活

留给自己的话,认真,认真,认真对待每一件事情。
你会成功的!加油。

假设自己已经学通了,所有的部分,而且很深刻,还会担心别人超过自己么?自己之所以担心别人超过自己,都是因为对自己的不自信,相信自己,找到你和别人的不足,努力去奋斗。保持一种学习的心态,不要浅尝辄止。而且要学会和别人分享。自己总是觉得别人没有走自己的老路,觉得不舒服。可是,还没有想好如何安慰自己这个心态。。。

注释和共享

Nodejs EventEmitter 解读

发布在 Node.js

目录

  1. events moudle
  2. Class EventEmitter
    1. EventEmitter Static Method And Property
      1. defaultMaxListeners
      2. usingDomains
      3. init()
      4. listenerCount(emitter, type)
        1. 疑问:为什么要判断原型上是否含有listenerCount方法呢?
  3. EventEmitter Property
    1. _events
    2. _eventsCount
    3. _maxListeners
  4. EventEmitter Method
    1. addListener() = on()
      1. newListener事件是在事件真正添加之前触发的
    2. emit()
      1. handler的执行
      2. 根据参数个数进行加速
    3. once()
      1. 我的疑问:这里我有一点不太理解的地方就是为什么还需要fired进行标记一下呢?
  5. removeListener(type, listener)
    1. 在事件删除之后才调用removeListener事件
  6. removeAllListeners(type)
  7. listeners(type)
  8. listenerCount(type)
  • TODO
    1. domain 部分没有进行解释
  • events moudle

    先说一下网上似乎很多人提供的使用例子来看,都是如下的

    1
    var EventEmitter = require('events').EventEmitter;

    然而在源码当中有这样的一句话

    模块定义events.js:8
    1
    2
    module.exports = EventEmitter;
    EventEmitter.EventEmitter = EventEmitter;

    所以require('events').EventEmitterrequire('events')是一样的。所以以后大家可以直接写

    1
    var EventEmitter = require('events');

    Class EventEmitter

    在源码中,构造函数极其的简单。

    constructorevent.js:5
    1
    2
    3
    function EventEmitter() {
    EventEmitter.init.call(this);
    }

    思路很简单,就是直接调用EventEmitter上的静态方法init进行构造。之后我们会介绍EventEmitter.init方法

    EventEmitter Static Method And Property

    挂在到EventEmitter上的静态属性和方法还是很多的,先说明一下静态属性:

    • defaultMaxListeners
    • usingDomains

    defaultMaxListeners

    顾名思义,默认的最大callback数量,就是如果当前对象没有指定_maxListeners,默认使用的就是这个值,默认是10
    更多请看_maxListeners

    usingDomains

    待定

    init()

    EventEmitter.initevents.js:23
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    EventEmitter.init = function() {
    this.domain = null;
    if (EventEmitter.usingDomains) {
    // if there is an active domain, then attach to it.
    domain = domain || require('domain');
    if (domain.active && !(this instanceof domain.Domain)) {
    this.domain = domain.active;
    }
    }

    if (!this._events || this._events === Object.getPrototypeOf(this)._events) {
    this._events = {};
    this._eventsCount = 0;
    }

    this._maxListeners = this._maxListeners || undefined;
    };

    先不管domain恨死干什么用的,总之init函数给this挂上了以下四个属性:

    listenerCount(emitter, type)

    获取emitter中指定类型的callback数量

    这里有一个比较特殊的地方,就是他对对象是否含有listenerCount方法进行了判断

    listenerCountevents.js:397
    1
    2
    3
    4
    5
    6
    7
    EventEmitter.listenerCount = function(emitter, type) {
    if (typeof emitter.listenerCount === 'function') {
    return emitter.listenerCount(type);
    } else {
    return listenerCount.call(emitter, type);
    }
    };

    疑问:为什么要判断原型上是否含有listenerCount方法呢?

    EventEmitter Property

    _events

    这个属性是一个事件的缓存列表,他的key就是事件名称,value就是事件的回调函数,有如下几种取值:

    • Function
    • Array
      当只有一个回调函数的时候,就直接存储为回调函数本身,否则包装成数组进行存储

    _eventsCount

    顾名思义,就是事件的个数,这里有几个需要注意的就是,这个之有且仅在添加一个没有过的事件的时候才会加一,如果你给一个事件添加了多了callback的话,这个值并不会加一的。

    _maxListeners

    这个就是可以表示一个event的最大callback的个数。如果小于等于这个个数,不会产生任何问题。但是如果大于这个限制,将会弹出警告,说可能会导致内存泄露。
    我猜测可能是因为过多的callback会保存过多的context,从而导致内存泄露。
    敬请更正

    EventEmitter Method

    先从最常用的讲起,就是添加事件侦听喽

    addListener() = on()

    首先大家一定很熟悉on方法,大家一定想不到的是,竟然还有一个叫addListener方法。那么这两个方法有什么区别呢?
    答案就是什么区别都木有~

    而且在定义的时候,定义的是addListener而不是on,从源码中便可以看出来

    addListenerevents.js:191
    1
    2
    3
    4
    5
    6
    7
    8
    EventEmitter.prototype.addListener = function addListener(type, listener) {
    var m;
    var events;
    var existing;
    // .... 此处省略10000行
    }
    // 就是这里,大家看到了么~~
    EventEmitter.prototype.on = EventEmitter.prototype.addListener;

    所以on更多的是一种简化,而正统的却是addListener,但是虽然这个正统,相信也没有几个人来使用吧,毕竟实在是太那啥了,是吧-_-||

    好的下面言归正传,整个添加函数的流程可以看做是这样的(大家可以对照的源码看,我就不在这里把源码全部都粘贴过来了,并且我这里可能为了语句的通顺改变部分代码的执行顺序,大家请自行看出来。。。):

    1. 接受两个参数,分别是type表示事件,就是event,第二个是listener,侦听器,也就是callback
    2. 如果listener不是函数,很明显啊,抛出错误不解释
    3. 检测当前对象上是否存在_events属性,如果不存在创建之,并顺手初始化_eventsCount为0.
    4. eventsthis._events
    5. 如果events中没有typelistener,那么不解释,添加之~即events[type] = listener
    6. 如果events里面有的话,不解释,一个就数组包装一下,两个直接push
    7. 如果type类型的listener超过两个了,那么就检测一下有没有超过长度限制,具体的检测逻辑我就不在这里详细的说明了,总之就是给将检测结果挂到events[type]warned属性上了。
    8. 如果有对应的newListener事件侦听的话,就直接用typelistener.listener?listener.listener:listener触发之
    9. 返回this

    至此函数执行完毕

    那么下一个就讲一下如果触发事件吧

    newListener事件是在事件真正添加之前触发的

    emit()

    很早以前我认为emit只能emit一个参数,到现在我猜明白,想几个就几个,没人限制你。

    emit有一个很好玩的特性就是,如果你emit一个"error",如果没有事件侦听这个error的话,就会直接throw出一个error来,而其他的事件不会又这种效果,这就意味着我们最好要侦听error事件,万一出了一点错误,那将是崩溃的节奏啊~

    源码在events.js:117

    1. 检测type是否为"error"
    2. 如果是,并且没有监听到error,throw出pass进来的错误或者构建一个未捕捉的错误弹出。
    3. 如果没有对应的回调函数的话,返回false
    4. 获取构造函数并赋值给handler,这个值有可能是函数,或者是数组。
    5. 根据参数个数调用对应的函数,顺序依次运行handler
    6. 返回true

    handler的执行

    handler执行的时候,会先将当前队列复制一份,然后再进行执行。并且根据参数个数用call或者apply执行函数。放置在某一次执行期间突然掺入了其他的callback或者删除了callback,从而引发错误。

    我原本以为会使用process.nextTick进行异步的执行,后来一想不对啊,肯定要按照添加的顺序进行执行,所以依次调用。

    根据参数个数进行加速


    fastToRunevents.js:65
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    // 没有参数的调用
    function emitNone(handler, isFn, self) {
    if (isFn)
    handler.call(self);
    else {
    var len = handler.length;
    var listeners = arrayClone(handler, len);
    for (var i = 0; i < len; ++i)
    listeners[i].call(self);
    }
    }
    // 一个参数
    function emitOne(handler, isFn, self) {
    // ...... 此处继续省略10000行
    }
    // 两个参数
    function emitTwo(handler, isFn, self) {
    // ...... 此处继续省略10000行
    }
    // 三个参数
    function emitThree(handler, isFn, self) {
    // ...... 此处继续省略10000行
    }
    // 不定参数
    function emitMore(handler, isFn, self) {
    // ...... 此处继续省略10000行
    }

    源码对不同的情况进行了加速,因为人们大部分情况都是使用0参数或者1参数的,所以对这几种情况进行处理是非常高效的。

    once()

    这个最简单了,就是用一个函数封装一下传入的callback,然后将这个函数,addListener进入到事件中。关于这个封装函数的写法,如下

    g()events.js:253
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var fired = false;
    function g() {
    this.removeListener(type, g);

    if (!fired) {
    fired = true;
    listener.apply(this, arguments);
    }
    }
    g.listener = listener;
    // 挂载原先的callback,用于remove的时候比较使用

    具体的就不多说了,总之就是执行后,先删除这个包装过的callback。

    我的疑问:这里我有一点不太理解的地方就是为什么还需要fired进行标记一下呢?

    removeListener(type, listener)

    删除指定类型的指定callback的事件
    很简单,分为一下几步

    1. 如果listener不是函数,抛出错误
    2. 如果events不存在或者对应的事件不存在,返回this
    3. listevents[type]
    4. 如果list就是那个callback或者list.listener是那个callback,删除
    5. 否则如果这是一个数组,那么找到对应的listener删除,否则返回this
    6. 如果上面有任意一个删除之后_eventsCount为0了,直接重新赋值this._events = {}(其实并不知道这个意义何在)
    7. 如果有任何一个删除,并且有事件侦听到了removeListener,触发之,传递typelistener
    8. 返回this

    在事件删除之后才调用removeListener事件

    removeAllListeners(type)

    根据传递的参数来决定是删除全部的还是只删除指定事件的全部

    1. 确保存在this._events,否则返回this
    2. 如果没有侦听removeListener
      • 如果没有传递type,重新赋值this._eventsthis._eventsCount
      • 如果传递了type,删除type对应的事件,根据情况重新赋值~~~
    3. 如果侦听了removeListener,除了这个本身,依次调用removeAllLiseners()进行删除,最后删除removeListener这个事件
    4. 对于每一个callback,依次调用removeListener方法进行删除
    5. 返回this

    listeners(type)

    获取指定类型的callback,否则为[]

    listenerCount(type)

    获取指定类型的callback的数量

    TODO

    domain 部分没有进行解释

    注释和共享

    目录

    1. HTTP2 简介
    2. 前言
    3. 准备工作
      1. 通过翻墙打开Google.com测试浏览器是否支持http2
      2. Chrome
        1. 通过 net-intervals 查看是否支持
        2. 如果不支持,请尝试打开http2开关
        3. 否则的话,我也不知道了
      3. firefox
      4. 其他浏览器
      5. 建立Nodejs工作目录,并安装依赖
      6. 使用openssl生成密钥和证书
    4. 创建服务器(start-server 分支)
    5. 访问 https://localhost/
    6. 尝试服务器推(server-push 分支)
      1. 建立一个首页 index.html
      2. 在代码中加入服务器推的代码,并返回上面那个主页
      3. 启动服务器,并访问之
      4. 总结
    7. GitHub 源码

    HTTP2 简介

    请大家自行搜索百度 Or Google

    前言

    本文章内部的术语,我不进行大篇幅的解释,如果你不了解的话,请自行搜索,不要说自己懒~

    如果本文章有问题的话,请及时进行评论,并和我联系

    准备工作

    通过翻墙打开Google.com测试浏览器是否支持http2

    Google的官网通过我的观察,几乎已经全部使用了http2,你可以在开发者工具中的 Network 中查看 Protocol 使用的协议。
    查看Google的网络

    这里有一个问题,就是如果你的没有 Protoccol ,这个怎么办?
    你可以在上面单击右键,就会弹出来有哪一些列可以让你选择,然后你选择 Protocol 即可。
    开启Protocol

    Chrome

    通过 net-intervals 查看是否支持

    对于Chrome浏览器,可以在地址栏上输入

    chrome://net-internals
    

    并在左上角的下拉菜单中寻找 HTTP/2 的字样,如果有的话,说明你的浏览器就支持http2,否则就话可能是另外一种情况

    查看是否存在HTTP2

    如果不支持,请尝试打开http2开关

    在Chrome地址栏中输入

    chrome://flags
    

    当前页面搜索 http2 ,找到相应的内容,并开启。然后重启浏览器尝试

    否则的话,我也不知道了

    如果还是前面两种都不可以的话,那么就请自行百度吧。

    firefox

    这个由于我没有firefox的浏览器,就留给读者自行百度了。

    其他浏览器

    同样自行百度吧

    建立Nodejs工作目录,并安装依赖

    浏览器都支持之后,那么就是开始准备我们的工作目录了。创建一个目录,我这里就叫做 try-http2。并安装http2包。

    准备工作

    使用openssl生成密钥和证书

    这里不再重复,请尝试百度,并将证书和密钥拷贝到目录下面

    准备工作结束

    创建服务器(start-server 分支)

    使用http2服务创建服务器和使用https是基本一样的

    index.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    var http2 = require('http2');
    var fs = require('fs');

    var server = http2.createServer({
    key: fs.readFileSync('privatekey.pem'),
    cert: fs.readFileSync('certificate.pem')
    }, function(req, res) {
    res.end('hello world');
    });

    server.listen(443, function() {
    console.log('listen on 443');
    });

    此时在terminal中启动服务器即可

    启动服务器

    记住,这里一定要用sudo来启动,因为在Linux的系统中(Mac也是和Linux相同祖先),普通用户是无法监听到1024端口一下的端口,所以要用sudo来让程序监听443端口,因为https服务就是监听在443端口的

    访问 https://localhost/

    在浏览器中访问上面那个地址,然后在Network中查看Protocol是否为h2

    浏览localhost

    看成功了,已经成功通过http2协议打开了这个网页。

    https error
    你会发现在地址栏上https证书出现了错误,这是因为证书是你自己发布的,Chrome自然是不认识了

    尝试服务器推(server-push 分支)

    说到服务器推,大家一定第一个想到的是WebSocket,但是相比于WebSocket来说,服务器推的东西不是数据,而是网络请求。

    其实他的正规说法是,服务器会推测你需要的东西,在解析HTML前将你需要的文件或者数据给你请求回来。这样,你请求了一次数据,返回了好几个资源

    建立一个首页 index.html

    index.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>haha</title>
    <script src="/client.js"></script>
    </head>
    <body>
    Hello world
    </body>
    </html>

    在代码中加入服务器推的代码,并返回上面那个主页

    index.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    var http2 = require('http2');
    var fs = require('fs');

    var server = http2.createServer({
    key: fs.readFileSync('privatekey.pem'),
    cert: fs.readFileSync('certificate.pem')
    }, function(req, res) {
    var push = res.push('/client.js')
    push.end('alert("this is a server push");');
    fs.createReadStream('index.html').pipe(res);
    });

    server.listen(443, function() {
    console.log('listen on 443');
    });

    启动服务器,并访问之

    看成功执行了client.js中的代码。

    下面我们来看看Network中的请求

    服务推送

    你会发现虽然是请求了两次,但是client.js是服务器推送过来的请求,并不是浏览器去请求的。

    总结

    虽然服务器推跟我们的心里落差较大,但是这并不影响这个技术的实施。

    有了服务器推,可以很好的加快网站的加载速度,完全可以不需要等待整张页面加载完成之后再加载相关的数据。像一些script,CSS之类的可以在渲染页面之前就加载完成。

    但是这个服务器推仍然无法替代WebSocket的地位,因为两个东西本质和目的都是不同的。而且服务器推相比来说,我们能控制的能力更少。

    但是服务器推也有缺点,就是会无故增加浏览网页的流量,对于移动端来说,这将是致命的!

    GitHub 源码

    GitHub

    注释和共享

    Mac 是否区分大小写

    发布在 Mac

    目录

    1. 真的是区分大小写么?
    2. 我想要区分大小写怎么办?

    真的是区分大小写么?

    熟悉过Linux的同学一定知道,在Linux中,文件是区分大小写的。
    而对于相同祖先的Mac OS来说,应该也是如此。大家想当然的认为是这样的。

    可是,事实却不是这样的。

    在Mac OS中,默认是不区分大小写的。
    这个大家可以进行如下的实验

    $ echo a > a
    $ echo A > A
    $ ls
    

    大家可以看一下结果,是不是

    a
    

    这样的么?

    说明Mac OS确实是不区分大小写的。

    我想要区分大小写怎么办?

    前面我说过了,默认是不区分,但是我们可以更改文件分区来区分大小写。

    Mac 分区
    我是用的我的一个移动硬盘来看的分区,所以会在window的格式上有对勾,请不要在意😂

    如图,默认是日志式,你可以选择区分大小写,日志式就可以了

    注释和共享

    目录

    1. 结构更新
    2. 内容更新

    结构更新

    1. 使用了tranquilpeak主题,GitHub
    2. 对主题进行了一些修改,准备pull request
      • 检测toc存在结果存放放post.toc中 _partial/post.ejs line 6
      • 使用toc进行判断 _partial/post/actions line 55
    3. 删除了search菜单
    4. 添加了GitHubCoding,可惜Coding没有对应的图片,等下一版再进行更新

    内容更新

    1. 更新了背景图片,变成了最近刚上映的终结者5创战纪

      吐槽一下这个名字,创战纪让我想起了以前的一个电影,关于电子游戏的,不过我忘了是什么了 -_-||

    注释和共享

    • 第 1 页 共 1 页

    XGHeaven

    一个弱弱的码农


    杭州电子科技大学学生一枚


    Weifang Shandong, China