SVG服务器渲染

这个月几乎全部精力都在完成公司图表库的Server-side Rendering,摆脱echarts投入d3的怀抱,旨在毫秒级的性能改善和预备以后反爬虫的可能性,不过性能表现的提升还得跟踪一段时间才能得到毕竟准确的结果。

Canvas和SVG的选择

Canvas似乎是现在的主流选择,Flipboard甚至不惜用Canvas代替DOM来改善交互,表现大家自有定论。但在服务器端渲染,Canvas不具备交互的条件,所以SVG是唯一的选择。

虚拟DOM的那些事儿

其实在服务器端渲染需要解决的一个最大的问题,便是虚拟DOM的操作了。虚拟DOM这个概念最近肯定是耳熟能详,为什么浏览器里需要一个虚拟DOM——还不是嫌DOM操作慢。而服务器端也面临同样的问题,有PhantomJS和JSDOM这样的完全模拟出浏览器环境进行DOM操作,但性能完全不能如意啊。
个人在做这个SVG服务器渲染的时候一开始是使用JSDOM(D3官方推荐),但渲染一个折线图(X轴50个点以内)大约需要80ms以上。这样的速度我们来进行SSR就是得不偿失了。所以我尝试了好几种方案:

  • cheerio:性能非常好,但仅仅提供简单的DOM树形结构,但不包含任何DOM方法,无法对节点进行操作。
  • minidom:提供了DOM2大部分方法,但仅限于HTML文档,缺少D3需要的createElementNS。
  • xmldom:提供了DOM2大部分方法,性能能够满足要求。但缺少D3需要的Selectors API。
    于是我给xmldom打了一个补丁,叫xmldom-qsa,让其DOM节点包含querySelector等方法。这样就可以在服务器端完美地调用各种D3的API进行SVG绘图。粗略测试它的性能大概是JSDOM的4倍左右。

事件循环

我们知道JavaScript的一大特点就是单线程,而这个线程中拥有唯一的一个事件循环。JavaScript代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。

事件循环是什么

“任务队列”是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在”任务队列”中添加一个事件,表示相关的异步任务可以进入”执行栈”了。主线程读取”任务队列”,就是读取里面有哪些事件。

“任务队列”中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入”任务队列”,等待主线程读取。

主线程从”任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
事件循环的过程就是等待一个消息:

1
2
3
while(queue.waitForMessage()){
queue.processNextMessage();
}

在浏览器里,当一个事件出现且有一个事件监听器被绑定时,消息会被随时添加。如果没有事件监听器,事件会丢失。所以点击一个附带点击事件处理函数的元素会添加一个消息。

Javascript中的设计模式

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。我们写代码总是会无意识的接触到别人的设计模式,例如每种语言都有自己的监听机制,这么看来其实设计模式从我们学编程起就一直伴随左右了。其实说到设计模式就离不开经验,当我们不断在一门语言里实践的过程中,总会用自己的理解组织代码。例如我每次重新写一个功能,都会总结哪里可以抽出来复用,哪里改动之后可以作为插件供其他人使用,这样就无意识的使用了一种设计模式。
使用设计模式都是为了让代码好维护,可读性更高,调用更合理。尤其是面向对象程序设计里,要遵循一些原则才具有良好的设计。

设计原则

面向对象程序设计有几个原则:单一职责原则 (Single Responsiblity Principle SRP)、里氏代换原则(Liskov Substitution Principle,LSP)、依赖倒转原则(Dependency Inversion Principle,DIP)、接口隔离原则(Interface Segregation Principle,ISP)、最小知识原则(Principle of Least Knowledge,PLK,也叫迪米特法则)、开闭原则(Open Closed Principle,OCP)。开闭原则具有理想主义的色彩,它是面向对象设计的终极目标。其他几条,则可以看做是开闭原则的实现方法。
我先简单介绍以下这几个原则的意思:

  • 单一职责原则:一个类只负责一项职责。在JS里即一个对象只做一件事情。
  • 里氏代换原则:类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。
  • 依赖倒转原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
  • 接口隔离原则:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
  • 最小知识原则:一个对象应该对其他对象保持最少的了解。也就是对象之间耦合度要小。
  • 开闭原则:一个软件实体如类、模块和函数应该可以扩展,但不可以修改。

Webpack模块化

Webpack模块化的实践

Webpack 是德国开发者 Tobias Koppers 开发的模块加载器,在 Webpack 当中, 所有的资源都被当作是模块, js, css, 图片等等,对应各种不同文件类型的资源, Webpack 有对应的模块 loader,Webpack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。

一步一步来配置

它的配置文件是 webpack.config.js.

1
2
3
4
5
6
7
8
9
10
// webpack.config.js
module.exports = {
entry: {
bundle1: './main1.js',
bundle2: './main2.js'
},
output: {
filename: '[name].js'
}
};

Vue实践

Vue和一些7788

最近公司的朋友都很疑惑,前端这阵子真的是日新月异,我们还用着jquery,underscore和knockout这些“小玩具”好像步入了中年一样。没办法啊,现在的前端大兴土木搞组件,搞模块化,我们也得跟,不过老项目总有钉子户,不能一口啃下来,所以就出了一个兼容并包的demo。

简单的模板

初始化这些到处可以搜到的东西就不重复写了。
直接上手写组件。

1
2
3
4
5
6
7
8
9
Vue.component('section1',{
template:'<section><h2>{{title}}</h2><div>{{message}}</div></section>',
data:function(){
return {
title:'示例1:独立组件',
message:'data必须是function'
}
}
});