使用 ESLint 规范你的代码

在一些多人参与的项目开发中, 每个人代码习惯不同, 对于缩进, 有人习惯 TAB, 有人习惯两个空格, 有人习惯四个空格. 对于分号, 有人不加, 有人习惯加, 而习惯加分号的人又会经常漏加分号. 试想一个项目代码如果由各种群魔乱舞的风格组成, 这必然会带来一定的视觉障碍(强迫症的人看了受不了=.=), 并且显得代码质量低下.

即使是个人, 偶尔也会写出一些不太规范的代码, 比如变量声明了未使用, 比如使用了 == 等等, 更不要说各种代码格式如操作符左右空格, if ... else 的格式, 末行换行等等. 特别是像有强迫症的我, 有时候写的过程可能会不小心遗漏或写错, 但发现的时候又会去改过来. 如果能在书写过程中检查出不规范的地方提示岂不是最好?

ESLint 是一个插件化的 JavaScript 代码检查工具, 可以用于检查常见的 JavaScript 代码错误, 规范 JavaScript 代码的书写, 可以有效控制代码的质量.

eslint.jpg

ESLint 规则

ESLint 使用非常简单, 安装好 ESLint 后, 可以自定义一套规则文件或者使用开源的代码风格规范, 像 Google, Airbnb 都有开源的 JavaScript 代码书写规范. 可以直接使用它们的, 也可以自己配置.

具体的用法可以参考官方文档, 其实蛮简单的.

这里列出了 ESLint 的各种规则, 可以参考下这里的说明.

以下是我个人配置的规则

- 阅读剩余部分 -

拥抱 vue 和 vuex

最近一段时间写了两个玩意, 一个是基于 PEG.js 的 XML Parser, 前面有一条博客说了 PEG.js 这东西, 事后自己也模仿着写出了这个 XML 解析器, 感觉并不难, 写着玩玩而已.

另外, 最近花时间特别多的另一件事就是写了一个 RSS 订阅器. 一开始写这个订阅器, 心想上一个项目代码不忍直视, 感觉自己需要写一些能拿出手的代码, 加上学校课程刚好要求做一些东西, 以及自己最近迷上了使用 RSS 订阅器这个东西(这么多理由=.=), 于是就自己动工开搞.

目前订阅器已经基本完工了, RSS 订阅器的网址是 www.enjoyrss.com, 项目开源在 Github 上面, 对于对 RSS 有兴趣或者想学习 Vue2, Vuex 或 Angular1 的人可能会有一些帮助. 额对了, 还有就是后端用的是 Koa2, 前后端鉴权专门在上一篇博客提了下, 想了解 Koa2 的人也可以看下.

数据流动问题

其实每次做一个新的东西的时候, 我都会尽量尝试去使用各种新的技术和用法. 这样才能学到更多的东西. 在这个 RSS 订阅器中, 一开始我只是把自己认为的各种 Angular 最佳实践在项目中都运用了下, 想写出能够体现自己 Angular 水平的代码, 为此前面还写了一篇博客说一些我认为的哪些算是最佳实践. 但其实, 对 Angular 使用已经相当熟悉的我, 并没有在这一次中收获什么新的知识. 要说有, 大概就是前面那博客提到的一些 Angular 最新版本的一些新特性例如 Component 之类的吧, 然而自己并没有花时间去看.

在这个项目中, 另外的一点感受就是 Angular 的跨组件通信难题. 确切的说我觉得这个项目并不适合使用 Angular 来写. 例如

website.png

左边有一个订阅源栏, 它的未读数量要相应右侧的点击文章, 标记全部已读等事件. 它的订阅源列表也要对右侧的订阅和取消订阅事件做出相应. 为了缩减频繁的跨组件通信, 我将下方状态栏直接拆分成三条, 由各自的组件提供其状态栏覆盖原默认只有背景色的状态栏. 但跨组件通信仍然存在, 单单左侧面板就存在着五个事件监听.

$scope.$on('EXPAND', () => vm.expand = !vm.expand)
$scope.$on('FOLD', () => vm.expand = false)
$scope.$on('ADD_FEED', (event, data) => {
  if (vm.feeds.default) {
    vm.feeds.default.push(data)
  } else {
    vm.feeds['default'] = [data]
  }
})
$scope.$on('DELETE_FEED', (event, data) => {
  vm.feeds = _.mapObject(vm.feeds, feeds => feeds = _.filter(feeds, feed => feed.feed_id !== data.feed_id))
})
$scope.$on('READ_POST', (event, data) => {
  vm.feeds = _.mapObject(vm.feeds, feeds => _.each(feeds, feed => feed.feed_id === data ? feed.unread-- : ''))
})

我需要监听折叠事件, 这个动作在其他组件被触发. 需要监听添加订阅源和取消订阅源事件, 并修改订阅源列表. 需要监听已读事件, 并对相应订阅源的未读文章数做减1操作...

在有些应用场景, 存在着需要大量父子组件通信, 兄弟组件通信, 以及没有父子和兄弟关系的组件之间的通信的行为, 这种时候, Angular 虽然也能解决, 但是不得不说 Angular 这种频繁的跨组件通信很容易产生问题, 特别是当一个组件可以被多个组件修改的时候.

- 阅读剩余部分 -

关于前后端分离鉴权的思考

前后端分离项目的 Token 存储问题由来已久,有的人存 Cookie 有的人存 LocalStorage 或 SessionStorage。在之前的个人项目中,曾经花时间去研究这个问题,以下分享下我的看法。

如何安全的传输用户 token

这是最传统也是最简单的方式了,前端登录,后端根据用户信息生成一个 token,并保存这个 token 和对应的用户 id,接着把 token 传给用户,存入浏览器 cookie,之后浏览器请求带上这个 cookie,后端根据这个 cookie 来标识用户。

flow-cookie-session.jpg

但这样做问题就很多,如果我们的页面出现了 XSS 漏洞,由于 cookie 可以被 JavaScript 读取,XSS 漏洞会导致用户 token 泄露,而作为后端识别用户的标识,cookie 的泄露意味着用户信息不再安全。

尽管我们通过转义输出内容,使用 CDN 等可以尽量避免 XSS 注入,但谁也不能保证在大型的项目中不会出现这个问题。另外,后端每次都需要根据 token 查出用户 id,这就增加了数据库的查询和存储开销。

在设置 cookie 的时候,其实你还可以设置 httpOnly 以及 secure 项。设置 httpOnly 后 cookie 将不能被 JS 读取,浏览器会自动的把它加在请求的 header 当中,设置 secure 的话,cookie 就只允许通过 HTTPS 传输。

secure 选项可以过滤掉一些使用 HTTP 协议的 XSS 注入,但并不能完全阻止。

httpOnly 选项使得 JS 不能读取到 cookie,那么 XSS 注入的问题也基本不用担心了。但设置 httpOnly 就带来了另一个问题,就是很容易的被 XSRF,即跨站请求伪造。当你浏览器开着这个页面的时候,另一个页面可以很容易的跨站请求这个页面的内容。因为 cookie 默认被发了出去。

CSRF.jpg

看起来我们不能兼顾。确实,光依靠这一个 token 我们没办法兼顾这两点。既然一个不够,那就两个。于是有了 XSRF-TOKEN,它和作为用户令牌的 token 类似,也是服务器生成的一个散列值。我们把 token 通过 httpOnly 发回去,把 XSRF-TOKEN 直接发回去。我们可以无视 httpOnly 的 cookie 因为我们没法操纵它,但对于这个 XSRF-TOKEN,我们就可以在我们网站的每个请求中都加入到 header 里面去。而服务端就需要检查这个 header 的 XSRF-TOKEN 是否真实有效。

- 阅读剩余部分 -

Angular 实践总结

搞了快一年的 Angular,Angular 真的是一个非常强大非常齐全非常好用的框架,而且拥有强大的社区支持,虽然踩了很多坑,但是仍然无悔学习了这样一个框架。Angular 2 已经正式发布了,我也打算学 Angular2 去了,现在手头上还有一个用 Angular 的项目,这个项目我是想把我学到的很多 Angular 的最佳实践都用进去,借此我就想干脆也写几篇博客总结下好了,我写的比较散,想到什么就说什么。

使用单次绑定或单向绑定

从 Angular 1.3 开始就有了 once-time binding,Angular 1.5 开始支持了指令和组件的 one-way binding。看看他们的用法:

单次绑定很简单,加上:: 就可以了。

<p>{{::vm.title}}</p>

An expression that starts with :: is considered a one-time expression. One-time expressions will stop recalculating once they are stable, which happens after the first digest if the expression result is a non-undefined value (see value stabilization algorithm below).

当 digest 结束之后并且单次绑定的值不为 undefined 时,这个值将不再被监听。官方称这个算法为 Value stabilization algorithm 还解释了一下。看到这个,再想起 Angular 源码那注释,觉得真的业界良心啊。想具体了解下单次绑定,出门右转 -> docs.angularjs

在视图进行单向绑定也很简单,使用 ng-bind 就可以了,这里主要说的是指令和组件中的单向绑定。

其实 Angular1 还是不断再发展,现在最新的是 1.5.9,加了很多新的东西,也做了很多性能优化。单向绑定也是 1.5 之后才引入的新东西。1.5 也引入了一个新的东西叫 component,和 directive 差不多,具体我没用过我也不太了解,不过写法简洁了很多。单向绑定主要就是用于 component 和 directive 的。

随便找个比较简单的例子来说下,

- 阅读剩余部分 -

Angular1 之折腾记

前几天 Angular2 正式发布了,虽然他也在我的学习计划里面,但我并没有把他应用在我最近开展的一个项目中。最近在写一个 Rss 订阅器,基于 Angular1 和 Koa2(总感觉两个有点不搭 =.= ),主要是不想在这个项目花太长时间,再者我还想集我目前掌握的所有技术之大成写一个能拿的出手的项目,所以就没有选择 Angular2 了。至于 Koa2,其实很早就想学了,只是之前一直在忙别的。

angular-resource 介绍

今天捣鼓 Angular 的 Resource 功能,前端后端都掌握在自己的手上时去用 Angular 的 Resource 特别舒服,大大减少了代码量,特么强大。

(function() {
    angular
        .module('app')
        .factory('Post', $resource => {
            return $resource('/api/feed/:feed_id/post/:id', {id: '@_id'}, {
                update: {method: 'PUT'},
                get: {method: 'GET', cache: true}
            });
        })
}());

在上面我定义了一个 Post 资源,一旦创建完成后,他就自动拥有了以下方法。

{
    'get': {method: 'GET'},
    'save': {method: 'POST'},
    'query': {method: 'GET', isArray: true},
    'remove': {method: 'DELETE'},
    'delete': {method: 'DELETE'}
}

有些 IE 浏览器可能不支持 delete,这时候可以使用 remove
我们还可以自定义或者修改里面的方法,比如我上面中就自定义了一个 update 方法以及给 get 方法开启了缓存。

- 阅读剩余部分 -

提升网站加载速度的 N 个方法

Web 这几年的一个变化之一估计就是各种优化小技巧不断涌出...自己也琢磨和尝试了不少优化,毕竟自己项目的网页首屏加载也是一度接近 2M 的。以下针对 HTTP1 和 HTTP1.1,在 HTTP2 中,很多最佳实践都适得其反了。

减少文件传输数量

现在前端代码发布上线的时候一般都会进行压缩,混淆,合并等操作,他们起到了减少文件体积和数量以及混淆代码降低可读性的作用。

浏览器针对同一域名的并发请求数目是有限制的,而在 HTTP1 和 HTTP1.1 中每传输一个资源就得建立一条连接。因此当网站的请求资源数量过多时,会导致后面资源请求的阻塞,也会导致频繁的连接建立和关闭带来的开销。一般浏览器的并发请求数量在4-8之间。因此我们针对同一域名的资源不宜过多,否则就会导致后面资源的阻塞。

针对该问题,我们可以采用合并文件,将资源分到不同域名,缓加载资源,提前加载资源,缓存等手段。具体如下:

  1. 合并文件以减少并发请求数量
    合并文件也不能简单粗暴的合并为一个,对于长时间不会改变的文件我们要单独合并出来,这个文件是可以进行长期缓存的,而一些变动较为频繁的我们就不应该和上面的这些文件合并在一起,并且他们也不应该设置过激的缓存策略。
  2. 将次要文件延迟加载,比如 Google Analysis
    一些无关痛痒的文件可以放到页面最尾部,这是最佳实践,这里特别想提一下 async 和 defer,他们并没有对文件的请求产生影响,只是影响了执行的过程,所以我们不应该使用 async 或者 defer 方法来优化。
    async-defer.jpg
    使用 async,会立即开始并行加载,加载完成后会进行执行并阻塞主渲染进程。
    使用 defer,会立即开始并行加载,但会延迟到最后才执行。
  3. 分散资源到不同域名
    比如图片有专门的域名(img.xxx.com)来存储。一些资源可以考虑第三方 CDN 比如 Bootcss 的 CDN,因为这类 CDN 使用较广,有可能用户浏览器已经有过缓存,这就避免了再次请求和加载,同时也减轻了服务器压力。
  4. 使用雪碧图(css sprites)来合并小图片
    这个优化技术其实挺常见,将图片合并为一个后使用 background-image 和 background-position 等来控制显示雪碧图的哪一部分就好了,据说还可以自动生成雪碧图自动定位。
  5. 利用 200 缓存
    这是一个比较极端的缓存方式,200 缓存时浏览器不发出网络请求,直接调用本地缓存,这需要强制浏览器使用本地缓存。我们可以使用 Expires 标志。即给出日期时间,超出该时间后则认为是过时,浏览器才会重新发起请求。这个具体细节我还不太了解。过后补充。
  6. 使用懒加载(lazy load)
    很多网站特别是有大量图片的网站都会使用该技术。当用户下滑页面时,才开始加载下面的图片。一来减少了页面加载的请求数和加载时间,二来也减少用户流量。不过可能有人会说这样体验不太好,好在业内有人把这个技术做到了堪称极致的地步,就是预先加载一个高度压缩的原图,然后淡出原图。大家应该有体验到类似的技术,就不多说了。
  7. 使用预加载技术(prefetch)
    这个技术知道的人可能不多,MDN 上面是这样解释的:

    页面资源预加载(Link prefetch)是浏览器提供的一个技巧,目的是让浏览器在空闲时间下载或预读取一些文档资源,用户在将来将会访问这些资源。一个Web页面可以对浏览器设置一系列的预加载指示,当浏览器加载完当前页面后,它会在后台静悄悄的加载指定的文档,并把它们存储在缓存里。当用户访问到这些预加载的文档后,浏览器能快速的从缓存里提取给用户。

    不过资源预加载其实使用的并不多,可能是因为技术本身不成熟,浏览器支持不够等原因。目前没有发现有哪个网站使用了这个技术。感兴趣的自己去了解下,这里不多阐述这个技术。

  8. 集中加载资源
    额,这个名字是我自己起的,姑且我认为也是一种优化手段,主要针对的时 SPA。比如 Angular 搭建的 SPA。Angular 提供了 templateCache 这个模块。这个在前面的博客中已经介绍过,简单说就是一个数组,我们把模板全部都预先放入这个数组中。Angular 在请求页面的时候会先检查 templateCache 是否已经缓存了,如果有则直接调用这个缓存的模板,否则发出网络请求获取该模板,同时会放入 templateCache 中缓存。有人可能会问那不是增加了首屏加载的体积大小了吗?的确,但比起用户每点击一个新的页面就发起一个请求而言,这种方式无疑会更适合不是吗?并且如果你的文件确实太大了,那你应该考虑下你是否充分利用了指令功能。

减少文件大小

除了减少文件数量,减少文件大小也同样重要,不过比起合并文件这样简单的减少文件数量的操作,减少文件大小就没来的那么简单了。常用的方法如下:

- 阅读剩余部分 -

This page loaded in 0.001151 seconds