2016年11月

浅谈 co 库

最近在写一个静态博客框架. 但不同于 hexo 之类的, 框架本身提供了博客书写和管理功能. 后端同样使用了 Koa2 来做,

归功于 Arch, 我已经在使用 Node 7.2 了, 不过好像默认还是不能支持 asyncawait , 经查发现是要输入参数开启的=.=. 不过当时第一时间并没有想说去加参数解决, 而是使用了 co 这个库... 虽然也可以用 babel , 但是通过 babel 运行代码很难调试.

node.png

co 用起来其实和 async 差不多, 甚至一些地方更简洁, 比如他可以 yield 一个 Promise 数组. 而 async 则只能 await 一个 Promise.all. 其实归根就是 co 进行了一个隐性的转换. co 内可以直接 yeild 一个数组或对象, co 会自动把数组或对象里面的所有值尝试转为 Promise 并包装在 Promise.all 中返回.

yield [promise1, promise2, ..., promisen]

async 要处理多个异步的并行操作, 只能把这些操作手动放入 Promise.all 返回. 即:

await Promise.all([promise1, promise2, ..., promisen])

草案还有一个 await* , 这个也是最近才注意到的, 使用 await* 就可以这样写了:

await* [promise1, promise2, ..., promisen]

然而草案并不推荐 await* , 目前 Node 7.2 也不支持 await* , 不过 babel 支持 ~

- 阅读剩余部分 -

正确使用 JS 的 sort 方法

sort() 方法对数组的元素做原地的排序, 并返回这个数组. 默认按照字符串的 Unicode 编码排序. sort 排序可能是不稳定的. 但其实 sort 方法可能并没有你想象的那么简单, 不信的话你耐心往下看看.

默认排序

sort 方法支持传入一个比较函数, 如果不传入则默认按照其字符串的 Unicode 编码排序, 因此默认情况下会出现以下情况.

[5, 1, 4, 11, 42].sort()
// [1, 11, 4, 42, 5]

解决方法就是:

[5, 1, 4, 11, 42].sort((pre, curr) => pre - curr)
// [1, 4, 5, 11, 42]

pre - curr 的值有三种情况

  • 小于 0 时, curr 排在 pre 后面
  • 大于 0 时, curr 排在 pre 前面
  • 等于 0 时, curr 和 pre 的位置不变

另外, 我们也可以对字符串进行排序:

['d', 'c', 'b', 'a'].sort()
// [a, b, c, d]

可能?是不稳定?

因为 ECMAScript 只是制定了 sort 这个方法, 但并没有给出具体的实现方式以及是否需要稳定的要求, sort 的实现就和其他大多数的方法一样由浏览器自行制定. 不同的浏览器或者同个浏览器不同版本上 sort 方法可能是稳定的, 也可能是不稳定的.

这里结合 V8 源码分析下这个 sort 方法的内部调优过程.

在 V8 中, 会通过以下函数方法进入 innerArraySort

function ArraySort(comparefn) {
  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.sort");

  var array = TO_OBJECT(this);
  var length = TO_LENGTH(array.length);
  return InnerArraySort(array, length, comparefn);
}

源码有点长, 可以自己看下 这里

注释已经点明了:

- 阅读剩余部分 -

聊聊 Webpack 使用

老早的时候就听说了 Webpack 这个工具, 当时大概的印象就是类似 Gulp 这样的东西, 并且看起来好像挺复杂的. 直到学习 React 的时候才开始接触 Webpack, 才知道 Webpack 更多的是做模块化的工作. 不过当时也是乱配置一通能用就行=.=.

现在 Vue 标配也是用 Webpack 了. Webpack 其实并没有想象中的那么复杂, 其实最核心的还是 loader 那一块. 这次就主要聊一聊 Webpack. 我用的是 Webpack 最新版本 2.1.0-beta.27.

what-is-webpack.png

Loader

Loader 是 Webpack 的核心, 它会自动查找项目中的我们指定的文件类型, 然后使用我们指定的 Loader 进行处理. 例如:

module: {
  rules: [{
    test:    /\.vue$/,
    loader:  'vue-loader',
    options: {
      loaders: {
        css: ExtractTextPlugin.extract({
          loader:         ['css-loader?minimize', 'postcss-loader'],
          fallbackLoader: 'vue-style-loader'
        })
      }
    }
  }, {
    test:    /\.js$/,
    loader:  'babel-loader',
    exclude: /node_modules/
  }, {
    test:   /\.css$/,
    loader: ExtractTextPlugin.extract({
      loader: ['css-loader?minimize', 'postcss-loader']
    })
  }, {
    test:   /\.(eot|woff|woff2|ttf)([\?]?.*)$/,
    loader: 'file-loader'
  }, {
    test:   /\.(png|jpg|gif|svg|ico)$/,
    loader: 'url-loader?limit=8192',
  }]
},

对于 Vue 文件, 我们要让 vue-loader 来处理, 这里可以先忽略 ExtractTextPlugin 部分, 它作用是提取 CSS 这个在后面会提. 对于 .js 文件, 我们使用 babel-loader 来处理, 我们可以在项目配置一个 .babelrc 文件来指定我们使用的 presets 和 plugins.

- 阅读剩余部分 -

使用 Koa2 开发小结

RSS 订阅器项目是我最近花时间比较多的一个项目了. 在这个项目中我使用了大量的新技术, 很多技术都是我第一次使用. 后端是基于 Koa2 和 Mongoose 的 RESTful API.

在这个项目开展前, 我已经有半年多没接触后端了. 上一次后端还是用 PHP 以及 Laravel 框架开发的 LNMP 架构. 在动工前, 我也没有正式的使用过 Node 以及其部署, 对于 Koa2 的 async await 的异步书写方式也只是久仰大名而已.

这篇博客主要想说一说自己在使用 Node.js 和 Koa2 开发后端过程中的一些总结和收获.

中间件

Koa2 本身是一个非常轻的框架, 我们需要使用大量的中间件去完善它, 例如 koa-bodyparser , koa-etag, koa-router, koa-sslify 等等.

同时, 肯定免不了自己写中间件, 例如我自己就写了 7 个中间件, 分别是处理缓存, 处理 cookies, 强制 www, 配合前端 HTML5Mode, 错误处理, UA 判断, JWT 和 XSRF 处理.

module.exports = function () {
    return async(ctx, next) => {
        if (/^\/(mark|square|feed|feeds|post|posts|me|search)/.test(ctx.request.url)) {
            if (ctx.mobile) {
                await send(ctx, './public/index.html')
            }
            else {
                await send(ctx, './public/pc.html')
            }
        }
        await next()
    }
}

上述就是配合前端 HTML5Mode 以及根据 UA 指向不同入口文件的中间件.

- 阅读剩余部分 -

使用 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 这种频繁的跨组件通信很容易产生问题, 特别是当一个组件可以被多个组件修改的时候.

- 阅读剩余部分 -