GithubHelp home page GithubHelp logo

blog's Introduction

EggJS 核心开发者,开源爱好者,目前就职于阿里巴巴游戏事业群,广州。

ID: 天猪 / atian25 , 微博: @天猪TZ , 知乎: @liuyong25

Keyword: 前端 / 技术控 / Egg / Node.js / Vue / 80后奶爸

1. 我的开源项目

Egg - 企业级的 Node.js 框架:

2. 主要文章目录

全部博客内容参见 issues

3. 归档

3. 推荐博客/文章

blog's People

Contributors

atian25 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blog's Issues

使用 [email protected] 断点调试 egg cluster

前言

上一次的折腾参见: #14

配置

在刚发布的 vscode 1.6 release note 中, 可以解决该问题, 具体步骤:

  • 在项目中按 F5 生成 .vscode/launch.json (如果之前有, 先删除)
  • 修改生成的文件的 configurations[0] 节点, 增加以下信息
  "runtimeArgs": [
     "--nolazy", "--debug"
  ],
  "port": 5859,

image

  • 再次 F5 重启

遗留问题

  • 在 egg-development 自动重启后, 还不能自动重连
  • 但从上一次折腾中的收获, 应该是可以写个插件搞定这个事
  • 先研究下.

奇思妙想

github.io导航

  • 玉伯用的404.html导航, 可以优化为: issue web hook -> update source/mapping.json -> url rewrite

站点收集

量身打造angular开发解决方案(3) - 开发规范和部署规范

量身打造angular开发解决方案(3) - 开发规范和部署规范

解耦开发规范和部署规范是前端开发体系的设计重点。

0.开发概念定义

ngfis-module

术语概念 描述
生态模块 生态环境中的第三方组件,包括JS模块、CSS模块。
工程框架模块 项目内部的公共模块(framwork)。
工程业务模块 项目内部的业务模块(modules)。
非模块化资源 并不是所有的开发资源都是模块化的,如页面资源, 模块化框架本身等。

我们的文件后缀名约定如下:

文件类型 后缀 描述
脚本文件 *.js , *.coffie
样式文件 *.css, *.sass
模板文件 *.tpl.html 不会发布,需手动内嵌到JS。
测试脚本 *.spec.js 单元测试,仅在自动化测试时发布。
内嵌内容 *.inline.* 有些文件希望分开编写,但发布的时候自动合并。
调试代码 *.inject.js 在开发期自动插入到页面, 提供一些API的mock。
(针对HybridApp

1.开发目录设计

- project
  - component_modules         **存放生态模块**
  - components                **存放工程模块**
    - app                       - angular主入口
    - framework                 - 工程框架模块
    - modules                   - 工程业务模块
  - views                     **存放页面以及非模块化资源**
    - index.html                - HTML文件
    - lib                       - 存放其他非模块化类库
  - server                    NodeJS服务端程序
  - fis-conf.js               FIS的配置文件
  - bower.json                生态模块配置文件
  - package.json              项目配置文件
  • 模块化资源通过require('modules/menu')的方式调用,而views目录下的非模块化资源需要自行通过script/link引入。
  • 模块化资源(app, framework, modules等)采用organize by feature的部署规范,
    一个模块的JS、CSS、HTML,还有单元测试,都维护在一起。它们的子目录规范如下:
- modules                   - 业务模块目录
  - menu                       - 具体的业务模块
    - menu.js                    - 该模块入口 (同模块名, 视为模块入口)
    - menu.css                   - 该模块样式 (同模块名, 会自动依赖)
    - menu.tpl.html              - 该模块模板文件 (不会发布, 需手动内嵌到JS)
    - menu-xx.png                - 图片文件
    - menu.spec.js               - 该模块的单元测试
    - menu-helper.inline.js      - 该脚本不会发布, 需手动内嵌到JS

2.部署目录设计

  • 按版本部署,采用非覆盖式发布。
  • 以下文件不会发布:
    • *.tpl.html (需内嵌到js文件,后文描述)
    • *.inline.* (会被内嵌到其他文件)
    • *.spec.js (仅在自动化测试时发布)
    • *.inject.js (仅在本地调试时嵌入)
    • 其他配置文件
  • ⚠️ 注意:该目录一般在临时文件目录, 而不在项目目录, 否则fis会读取该目录的文件,影响编译速度。

目录结构如下:

- release
  - public
    - 项目名称
      - 版本号
        - lib             **大部分资源都会部署到这个目录下**
          - angular         - 生态模块
          - framework        - 项目公共模块
          - modules         - 项目业务模块
            - menu            - 具体模块名
          - scrat           - 非模块化资源, view目录下的lib
        - index.html
  - server                **NodeJS服务端程序**
  - test                  **测试文件**

3. 设计工具,完成开发目录和部署目录的转换

所谓构建,其核心任务就是将文件按照某种规则进行分类(以文件后缀分类,以模块化/非模块化分类,以前端/后端代码分类),然后针对不同的文件做不同的构建处理。

准备了下示例项目:https://github.com/ng-workflow/ngfis-showcase

同时,也准备了工具项目:https://github.com/ng-workflow/ngfis

我们通过fis的配置文件来映射目录转换,参见ngfis/config/default.jsroadmap.path配置, 关键配置如下:

roadmap: {
  path: [
    //tpl.html文件不发布
    {
      reg: /.*\.tpl\.html$/,
      release: false
    },
    //component工程模块下的JS文件, 发布到public/lib目录下
    {
      reg : /^\/components\/(.*\.js)$/i,
      id : '$1',
      isMod : true,
      useHash : false,
      url : '${urlPrefix}/${name}/${version}/lib/$1',
      release : '/public/${name}/${version}/lib/$1'
    },
    //view下的非模块化资源, 发布到public目录
    {
      reg : /^\/views\/(.*\.(?:html?|js))$/,
      useCache : false,
      isViews : true,
      url : '${urlPrefix}/${name}/${version}/$1',
      release : '/public/${name}/${version}/$1'
    },
  ]

在示例项目下执行 ngfis release -d ../dist 即可看到发布效果,so easy~

ngfis-release

No API is the best API - 抛弃 should/expect/chai 吧

如果你的 node 没有写单元测试,那可以跳过本文了。

1. 缘起

在我们日常的单元测试中,常用的断言库有:

user.should.have.property('name', 'tz');
user.enabled.should.ok;

expect(5).to.be.a('number');
expect(window).not.to.be.an(Image);

存在什么问题呢?

  • 复杂的 API,每次使用时,都需要去翻文档,用自然语言描述测试真的好么?
  • 经常怀疑人生user.enabled.should.ok; 这句到底有没有执行? 还是只是取值?
  • 一脸懵逼,反馈信息不足,往往我们还需要加 log 再跑一次,如果在 ci 上看日志就懵逼了。
require('should');
const expect = require('expect.js');
const assert = require('assert');

describe('test/showcase.test.js', () => {
  const arr = [ 1, 2, 3 ];

  it('should.js', () => {
    arr[1].should.eql(10);
  });

  it('expect.js', () => {
    expect(arr[1]).to.eql(10);
  });

  it('assert', () => {
    // 用原生的话, 得到的提示更是一脸懵逼
    assert(arr[1] === 10);
  });
});

// output:
1) test/showcase.test.js should.js:
     AssertionError: expected 2 to equal 10
     ...
2) test/showcase.test.js expect.js:
     Error: expected 2 to sort of equal 10
     ...
3) test/showcase.test.js assert:
     AssertionError: false == true
     ...

2. 曙光

egg 的开发中, 我们发现了不一样的它:

Power Assert in JavaScript.

Provides descriptive assertion messages through standard assert interface.

No API is the best API.

https://github.com/power-assert-js/power-assert

简单的说,它的优点是:

  • 没有 API 就是最好的 API,不需要任何记忆,只需 assert 即可。
  • 强大的错误信息反馈
  • 强大的错误信息反馈
  • 强大的错误信息反馈
const assert = require('power-assert');

describe('test/showcase.test.js', () => {
  const arr = [ 1, 2, 3 ];

  it('power-assert', () => {
    assert(arr[1] === 10);
  });
});

// output:
4) test/showcase.test.js power-assert:

      AssertionError:   # test/showcase.test.js:6

  assert(arr[1] === 10)
         |  |   |
         |  2   false
         [1,2,3]

  [number] 10
  => 10
  [number] arr[1]
  => 2

image

3. 使用

在线尝试:https://azu.github.io/power-assert-demo/

安装依赖:

$ npm i mocha power-assert intelli-espower-loader --save-dev

配置 npm scripts

{
  "scripts": {
    "test": "mocha -r intelli-espower-loader test/**/*.test.js",
  },
  "devDependencies": {
    "power-assert": "^1.4.2",
    "intelli-espower-loader": "^1.0.1",
  }
}

编写测试:

// 简单的 require, 使用者无感知
// 下面的代码没写错, 无需 `require('power-assert')`, loader 会自动替换
const assert = require('assert');

describe('test/showcase.test.js', () => {
  const arr = [ 1, 2, 3 ];

  it('power-assert', () => {
    // 完全兼容 node 原生的 assert API, 直接自由使用
    assert(arr[1] === 10);
  });
});

执行测试:

$ npm test

4. 其他

  • mocha 需要引入 intelli-espower-loader,主要是把代码转译,参见作者的 slide .
  • 转译之后, 你甚至完全无感知, require('assert') 都不需要改.
  • 因为转译,所以不能用原生的 assert 了, 否则会遇到如下错误

5. 补充

Rethink: Serverless For Frontend

背景

大概在两个月前,为了给团队小伙伴分享我们目前做的一些事的价值,写了一篇 『Serverless For Frontend 前世今生』

在那篇文章中,主要讲了进入每个阶段的缘由和产出的东西,但对演进背后的源动力的思考和阐述有所欠缺。

在之后这段时间,一方面跟内外的同学交流,一方面在实践中去复盘,于是有了本文: Rethink: Serverless For Frontend。本文主要是对 Slide 的一些提炼,完整的 Slide 参见文末。

为什么分享

熟悉我的人会知道,我很喜欢在知乎专栏写一些科普向的文章,也非常喜欢交流。

那是因为我也曾经在小公司迷茫了多年,也作为一个小人物亲身经历了前端在蛮荒期的开疆拓土。在我有幸加入阿里,能更深入的参与到这一进程时,我却发现前端的贫富差距正在拉大,因此我希望自己能做些什么。

改编自 @fouber 的 Slide,提提逼格,哈哈。

但在此次的分享 Slide 中,我并不打算分析技术细节,因为那毫无意义,这是当时 Topic 的大纲。

image

前端演化的内在动力

前面提到,那篇文章仅仅是记录,发出去后,有很多反馈来迫使我思考:

『从前端工程化到走向云端,从 BFF 到现在的 SFF,这一切的内在推动力是什么呢?』

image

在跟玉伯、平侠老师的交流后,我得到了第一个问题的答案:

image

是的,前端最早来源于 艺术与代码之间的 Gap,前端这个岗位就是 设计师研发 之间的桥梁。而发展到现在,『大前端』已经在涉猎到每一个屏幕或者说人机交互界面上,包括:PC、手机、平板、手表、大屏、嵌入式设备。

那第二个问题,前端一路开疆拓土,到处跟客户端、后端『抢活』,是 KPI 的原因么?

于是我把文中的演化过程重新进行的梳理,得到了第二个答案:

image

前端的出现、前端接管模板层、前端接管粘合层,这一切都是为了更合理的分工,更优更高效的触达用户。

这也是我们的 前端领域价值观,用于指导做什么,不做什么。

前端如何走向云端?

国内大部分团队处于这个阶段,前端工程化已经深入人心且有一定的讨论和**指导(如云龙的前端工程 已经快 5 年了,还是那么的深刻)。

此时更多的是处于 前端工程化 -> 云端 这一阶段,受限于话语权、人才储备、基建能力等,举步维艰。阿里在这一块多走了几年,在众多前辈的努力下,我们也走出了自己的一条路。(如 蚂蚁 Node.js 演化 这一文所述)

每个公司的情况不一样,是无法简单复制的,我建议的演化路线是:

image

BFF 存在的问题

image

在我看来,BFF(Backend For Frontend) 对前端的价值毋庸置疑,但它带来的额外成本也历历在目。

可能前几年在扩张期的时候,这些问题都被掩盖,随着 Egg、TypeScript 等等的出现,有些前端开始飘飘然,觉得前端无所不能,到处挑衅 Java。我只能说,他们对后端一无所知,是前端的猪队友

在我的思考里,目前它所处的阶段,只是一个阶段性不完善的产物,我们还缺乏一个契机。

image

幸运的是,Serverless 终于到来了,它也许是我们所等待的那个契机点:

  • 我们需要 BFF 带来的能力。
  • 但我们不需要 BFF 带来的那些额外的学习成本和运维成本。

如何演进

image

老实说,我也不知道,现在业界对 Serverless 的讨论,大都不是一个概念上的讨论。

就我们而言,我们只会继续遵循我们的 前端领域价值观,继续深耕在自己的领域。因此,我们对 BFF 的概念进行了升级:Serverless For Frontend

image

image

完整的 Slide

Rethink: Serverless For Frontend

个人博客,最终选择。

2018 年更新,此时我会倾向于在 语雀 https://www.yuque.com/egg 来写。

GitHub 的目录导航体验不是很好,知乎的编辑器体验太差,语雀则让我重新找到编写的兴趣。


绕了一圈圈,最终做出了选择

  • Blog是个人的总结和记录, 对于我是倾向于技术方向。(排除简书)
  • 编写时,格式越简单越好,所以必然是markdown。(排除知乎专栏
  • Blog写出来,要有人交流, 才能进步, 之前选择的 hexo 虽然有多说,但是码农不喜欢。
  • gitpress 停止维护, ghost 太折腾。
  • 最终还是选择了 issue 的方式, 也即此处, 虽然几乎没有流量导,不过无所谓, 专心自我总结。
  • segementfault 的印象好了不少, 技术领域,还有人气都很不错。不过还是需要观察观察。如果
    segementfault 能提供一种方式,博主在issue发表, segementfault 自动 git hook 同步, 导点流量啥的, 大家互利。

以下是当时的思考:

选择困难症又开始了。。。

再读读浩哥的: 程序算法与人生选择

1.知乎专栏

  • 主要是写前端方面的, 而知乎的技术氛围不高。
  • 优点是可以多个人一起写。
  • 经历了几次知乎事件,对知乎的运营越来越不放心了。
  • 编辑器很操蛋,无法多行列表,格式化后吞字。
  • angular的seo问题。

2.简书

  • 很漂亮(在移动设备上,但在大屏PC上感觉不咋滴)
  • 用Markdown,喜欢。
  • 但是担心能否走得够远。
  • 自带的富文本编辑器,连列表都没有?

3.SegmentFault

  • 优点是技术专区。
  • 用Markdown,喜欢。
  • 但总体质量目前偏低。

4.github issue

  • 很多人都用它,如玉伯。
  • 用Markdown,喜欢。
  • 但觉得导航不是很好。

5.GitHub page+ hexo

6.gitpress

  • 也是托管在github,直接读取source里面markdown
  • 用Markdown,喜欢。
  • 还是担心个人开发者的维护性问题。
  • 要是同时也能读取issue就好了,那就综合了4和5.
  • 提了个issue: 75team/gitpress#7

7.ghost

  • 基于nodejs的新一代blog
  • 用Markdown,喜欢。
  • 要找个云主机部署,嫌麻烦。

Egg 2.0 正式发布,性能提升 30%,拥抱 Async

image

原文地址:知乎专栏 https://zhuanlan.zhihu.com/p/31640541

很荣幸的宣布,Egg 正式发布 2.0 版本,距离 3.21 发布的 Egg 1.0 版本 仅时隔 8 个月。

image

2.0 特性

  • 基于 Koa 2.x
  • 框架层优化带来 30% 左右的性能提升,不含 Node 8 带来的提升。
  • 平滑升级,保持了对 Egg 1.x 以及 generator function 的兼容。

如何升级

Egg 的理念之一是渐进式增强,故我们为开发者提供渐进升级的体验。

  1. Node.js 使用最新的 LTS 版本(>=8.9.0)。
  2. 修改 package.json 中 egg 的依赖为 ^2.0.0。
  3. 检查相关插件是否发布新版本(可选)。
  4. 重新安装依赖,跑单元测试。

搞定!几乎不需要修改任何一行代码,就已经完成了升级。

这得益于 Egg 对 1.x 的兼容,但为了更好的统一代码风格,以及更佳的性能和错误堆栈,我们建议开发者参考 升级指南 进一步升级。

未来规划

如您所知,Egg 采用的是 『微内核 + 插件 + 上层框架』 模式。

其中微内核经过 3 年 4 个版本,以及在阿里的大规模应用,已经打磨的非常稳定。

接下来我们的重心主要在开发者体验方面的优化,包括:

  • 更好的开发者体验,包括 TypeScript,开发者工具,IDE 工具等方面。
  • 社区扶持
    • 协助业界的前端团队,打造适合特定团队业务场景的上层框架,欢迎勾搭。
    • 分享我们在团队、协作、规范化等方面的经验。
    • 分享在 Docker,GraphQL,SSR 等方面的探索和最佳实践分享。
  • 国际化,官网和文档翻译等。

同时,我们也欢迎社区更多的参与,一起打造更完善的生态。

我们这一年

截止至今天(2017-12-03):

  • GitHub 5.6k star,555 forks,npm 月下载量 11,140 (不含阿里内网数据),官网 PV 近万。
  • 从 1.0 到 2.0,我们一共发布了 18 个版本,处理了 820 个 issue,收到了 500+ (主库 272) 个 Pull Request 。
  • 开发者体验方面的优化包括:断点调试代理单元测试+覆盖率部署工具,TypeScript 支持,VSCode 插件,文档优化等等。

社区方面:

  • 来自 BAT,丁香园,全民直播等多家公司的反馈和插件回馈。
  • 来自 GitHub Dependents 的统计:992 Repositories, 231 Packages
  • NPM 搜索结果超过 400 个。
  • 上层框架:
    • 北斗 - Isomorphic framework for server-rendered React apps
    • avet - A very comfortable framework for writing isomorphic applications
  • 来自 @Sky 对 [WebPack 和 Vue/React SSR 的探索(https://zhuanlan.zhihu.com/easywebpack)。
  • 如果你有好的分享,PR 传送门:awesome-egg

趣味数据:

  • 官网访问量中,Mac 占 47% → 这比例挺高的,看来 Node 程序猿都很幸福。
  • 周末的访问量约为平时的 1/3 → 看来 Node 程序猿周末加班少。(滑稽
  • 官网访问来源:站内,直链,外链各 30% 多,来自搜索引擎的较少。

分享交流:

image

image

写在最后

开源,痛并快乐着。

3

AngularJS性能优化心得

不知不觉,在项目中用angular已经半年多了,踩了很多坑。
趁着放假,把angular的3本书都看了遍,结合这半年的经验,是该做个总结了。
希望可以给大家带来启示,少踩点坑。

本文针对的读者:

  • 具备JavaScript性能优化的相关知识(雅虎14条性能优化原则《高性能网站建设指南》等)
  • 拥有angular实战经验。

脏数据检查 != 轮询检查更新

谈起angular的脏检查机制(dirty-checking), 常见的误解就是认为: ng是定时轮询去检查model是否变更。
其实,ng只有在指定事件触发后,才进入$digest cycle

  • DOM事件,譬如用户输入文本,点击按钮等。(ng-click)
  • XHR响应事件 ($http)
  • 浏览器Location变更事件 ($location)
  • Timer事件($timeout, $interval)
  • 执行$digest()$apply()

concepts-runtime

参考《mastering web application development with angularjs》 P294

$digest后批量更新UI

传统的JS MVC框架, 数据变更是通过setter去触发事件,然后立即更新UI。
而angular则是进入$digest cycle,等待所有model都稳定后,才批量一次性更新UI。
这种机制能减少浏览器repaint次数,从而提高性能。

参考《mastering web application development with angularjs》 P296
另, 推荐阅读: 构建自己的AngularJS,第一部分:Scope和Digest

提速 $digest cycle

关键点

  • 尽少的触发$digest (P310)
  • 尽快的执行$digest

优化$watch

  • $scope.$watch(watchExpression, modelChangeCallback), watchExpression可以是String或Function。
  • 避免watchExpression中执行耗时操作,因为它在每次$digest都会执行1~2次。
  • 避免watchExpression中操作dom,因为它很耗时。
  • console.log也很耗时,记得发布时干掉它。(用grunt groundskeeper)
  • ng-if vs ng-show, 前者会移除DOM和对应的watch
  • 及时移除不必要的$watch。(angular自动生成的可以通过下文介绍的bindonce

    参考《mastering web application development with angularjs》 P303~309

var unwatch = $scope.$watch("someKey", function(newValue, oldValue){
  //do sth...
  if(someCondition){
    //当不需要的时候,及时移除watch
    unwatch();
  }
});
  • 避免深度watch, 即第三个参数为true

    参考《mastering web application development with angularjs》 P313

  • 减少watch的变量长度
    如下,angular不会仅对{% raw %}{{variable}}{% endraw %}建立watcher,而是对整个p标签。
    双括号应该被span包裹,因为watch的是外部element

    参考《mastering web application development with angularjs》 P314
    {% raw %}

<p>plain text other {{variable}} plain text other</p>
//改为:
<p>plain text other <span ng-bind='variable'></span> plain text other</p>
//或
<p>plain text other <span>{{variable}}</span> plain text other</p>

{% endraw %}

$apply vs $digest

  • $apply会使ng进入$digest cycle, 并从$rootScope开始遍历(深度优先)检查数据变更。
  • $digest仅会检查该scope和它的子scope,当你确定当前操作仅影响它们时,用$digest可以稍微提升性能。

    参考《mastering web application development with angularjs》 P308

延迟执行

  • 一些不必要的操作,放到$timeout里面延迟执行。
  • 如果不涉及数据变更,还可以加上第三个参数false,避免调用$apply
  • 对时间有要求的,第二个参数可以设置为0。
$http.get('http://path/to/url').success(function(data){
  $scope.name = data.name;
  $timeout(function(){
    //do sth later, such as log
  }, 0, false);
});

优化ng-repeat

限制列表个数

  • 列表对象的数据转换,在放入scope之前处理。如$scope.dataList = convert(dataFromServer)
  • 可以使用ngInfiniteScroll来做无限滚动。

使用 track by

刷新数据时,我们常这么做:$scope.tasks = data || [];,这会导致angular移除掉所有的DOM,重新创建和渲染。
若优化为ng-repeat="task in tasks track by task.id后,angular就能复用task对应的原DOM进行更新,减少不必要渲染。
参见:http://www.codelord.net/2014/04/15/improving-ng-repeat-performance-with-track-by

使用单次绑定

我们都知道angular建议一个页面最多2000个双向绑定,但在列表页面通常很容易超标。
譬如一个滑动到底部加载下页的表格,一行20+个绑定, 展示个100行就超标了。
下图这个只是一个很简单的列表,还不是表格,就已经这么多个了:
scope-binding-src
但其实很多属性显示后是几乎不会变更的, 这时候就没必要双向绑定了。(不知道angular为何不考虑此类场景)
如下图,改为bindonceangular-once后减少了很多:
scope-binding-once

update:
1.3.0b10开始支持内建单次绑定, {% raw %}{{::variable}}{% endraw %}
设计文档:http://t.cn/RvIYHp9
commit: http://t.cn/RvIYHpC
目前该特性的性能似乎还有待优化(2x slower)

慎用filter

在$digest过程中,filter会执行很多次,至少两次。
所以要避免在filter中执行耗时操作

参考《mastering web application development with angularjs》 P136

angular.module('filtersPerf', []).filter('double', function(){
  return function(input) {
    //至少输出两次
    console.log('Calling double on: '+input);
    return input + input;
  };
});

可以在controller中预先处理

//mainCtrl.js
angular.module('filtersPerf', []).controller('mainCtrl', function($scope, $filter){
  $scope.dataList = $filter('double')(dataFromServer);
});

慎用事件

directive

使用Batarang来分析性能

如何挑选高质量的 Node.js 模块

1. 前言

如图,截止到 2017 年 3 月, Node.js 模块的数量已经超过 43万,这么多模块难免会良莠不齐,
那我们如何高效的从中找到高质量的模块呢?请听下文分解。

modulecounts

2. npms.io

npms.io - which stands for npm search - was built to empower the JavaScript community by providing a better and open source search for node packages.

npms.io

最早了解到该站点,是听小右在微博说的。


选模块就像选对象,下面我们来看看非诚勿扰选优,呸呸呸,是来看官方的评估标准:

default

2.1 质量 Quality

第一个维度是「质量」,毕竟这是一个看脸的时代,第一印象很重要。

相关的因子就在源码中,故计算起来相对容易些,如:

  • 是否有 REAME,LICENSE,.gitignore 等文件。
  • 是否是稳定版本(按 semver 规则为 >= 1.0.0 ),是否声明了 deprecated
  • 是否包含测试?覆盖率如何?测试是否通过?
  • 是否包含过期的依赖?是否有安全隐患?
  • 有没有官方站点?有没有徽章(badge) ?
  • 是否使用了代码风格检查(lint) ?
  • 代码中的 TODO/FIXME 多不多?

2.2 维护状况 Maintenance

第二个维度是「维护状况」,考察的是一个模块的家况,如果我们希望跟它进一步交往,当然需要了解到这个模块的活跃程度,健康度,是否被遗弃了?

  • issue 的比率
  • issue 被处理的效率
  • 最近一个 commit 的时间
  • commit 的频率
  • 发布的频率

2.3 知名度 Popularity

第三个维度是「知名度」

  • GitHub Star 数
  • GitHub Fork 数 (说个题外话,Fork > Star 说明什么?)
  • GitHub Watch 数 (鄙视下那些经常在 GitHub blog 下乱发消息的人)
  • 贡献者数量
  • 被其他模块依赖的数量
  • 下载安装次数
  • 下载增长率

2.4 个人魅力 Personalities

最后一个维度是「个人魅力」

  • 当两个库差不多的情况下,一般选择大神的库,如 tj 、substack、fengmk2。
  • 不过 npms 目前并没有把该属性纳入计算。

2.5 参考资料

3. 写在最后

  • node-modules 是国内很多 Node.js 大神维护的 Group,里面的模块都经受住大规模的应用考虑,标准化和质量都不错,欢迎关注。
  • 本来想写一个 npms 的 CLI 的,结果被人抢先了,npm-compare ,有兴趣的可以玩玩,PS:@fengmk2 可以考虑集成到为 cnpm compare
  • 对于高质量的模块,我们推荐通过 ^ 引入,而不是 ~ 或写死版本,像 eggjs 的依赖都是 ^ 的。想知道为何?开问题邀请 @dead-horse 回答吧。

vscode debug cluster 的一些研究过程流水账

背景

有些同学反馈 vscode 无法 debug egg 应用, 于是分析了下

流水账

启动时卡住

问题

在 vscode 的 debug 界面 launch 时:

node --debug-brk=21904 --nolazy index.js 
Debugger listening on [::]:21904
2016-09-29 08:15:12,277 INFO 52531 [master] =================== egg start =====================
2016-09-29 08:15:12,278 INFO 52531 [master] egg version 0.1.3
2016-09-29 08:15:12,279 INFO 52531 [master] start with options: {"customEgg":"/Users/tz/Workspaces/eggjs/test/showcase/node_modules/.0.1.3@egg","baseDir":"/Users/tz/Workspaces/eggjs/test/showcase","port":7002,"workers":1,"plugins":null,"https":false,"key":"","cert":""}
2016-09-29 08:15:12,282 INFO 52531 [master] Agent Worker:52532 start with ["{\"customEgg\":\"/Users/tz/Workspaces/eggjs/test/showcase/node_modules/.0.1.3@egg\",\"baseDir\":\"/Users/tz/Workspaces/eggjs/test/showcase\",\"port\":7002,\"workers\":1,\"plugins\":null,\"https\":false,\"key\":\"\",\"cert\":\"\"}"]
Debugger listening on [::]:5856

而正常的 egg 控制台启动日志, 除了上面那段外, 还会有以下:

2016-09-29 08:19:59,184 INFO 52849 [egg:watcher:agent] watcher start success
2016-09-29 08:19:59,219 INFO 52848 [master] Agent Worker started (404ms)
2016-09-29 08:19:59,223 INFO 52848 [master] App Worker#1:52850 start, state: none, current workers: ["1"]
Debugger listening on [::]:19883
2016-09-29 08:19:59,897 WARN 52848 [master] App Worker#1:52850 started at 7002, remain 0 (678ms)
2016-09-29 08:19:59,897 INFO 52848 [master] egg started on http://127.0.0.1:7002 (1082ms)

分析 && 处理

  • 看日志是 master 就被挂住了
  • --debug-brk 把 master 进程挂住, 所以 agent 和 worker 都没起来
  • 这个是 vscode 的一个 bug: microsoft/vscode#3201 (comment)
  • 在 index.js 的 startCluster 之前加入以下代码可以临时解决
process.execArgv[0] = process.execArgv[0].replace('-brk', '');

断点不了

问题

  • 可是上面那种方式, attach 的是 master 进程, 并不能断点到 worker.

分析 && 处理

  • 所以只能考虑先在外部启动 npm run dev
  • 然后在 vscode 里面不用 launch 而是 attach 指定端口
  • 然而, ps -ef|grep customEgg 后发现 egg-bin 并没有传递 --debug 给 worker
  • 所以只能先用 node --debug index.js, 晚点再修复下 egg-bin

worker 自动重启怎么办?

问题

  • egg-development 监控到文件改变时, 会自动重启 worker
  • 而根据 node cluster 的机制, 新的 worker 的 debugPort 会 +1
  • vscode 无法自动重连, 必须断开后, 修改 launch.json 的端口, 然后重新 attach
  • 太麻烦了

分析 && 处理

思路:

  • 可以写个 vscode 的插件, 用来启动 egg
  • 此时, 它就是 egg 进程模型里面的 parent, 可以监听到 worker 的重启事件
  • 进而调用 vscode 的 api, 去自动 attach 新端口

目前进展:

遇到问题:

  • 插件设计
    • vscode 同一时刻只能启动一个 debug, 如果期望 egg 也是在 vscode 里面启动
    • 那上面想到的那个插件, 需要写成一个 vscode debugger 插件, 这个有点难度...
  • API
    • vscode.startDebug 这类 API 的文档, 根本不知道去哪里找... google 不到, 源码翻得很累

JD:蚂蚁集团体验技术部 - 前端基础技术

image

团队介绍

我们隶属于 蚂蚁集团体验技术部,负责 前端基础技术,致力于为蚂蚁前端提供 高效省心、稳定可靠 的 Node.js 研发方案及基础设施。

作为一名前端,你是否:

  • 很好奇 玉伯 带领的蚂蚁体验技术部,是如何产出 Ant Design、Egg 等开源项目?
  • 很好奇 Node.js 如何从小工坊走向大规模企业开发?它如何支撑双十一量级的业务?
  • 很好奇前端网红的日常?想了解朴老师 AliNode,苏千 CNPM,天猪 Egg 的实践过程吗?

现在就加入我们:

  • 你将可以参与到蚂蚁 Node.js 基础设施的建设,参与到蚂蚁前端变革式 Node.js 技术栈建设。
  • 你将体验到大规模 Node.js 研发背后的各种黑科技基建,想告诉爸妈蚂蚁森林的背后也有你的一份力么?
  • 你将在实际生产环境中应用业界前沿技术,引领未来。

因为我们是同类人:

  • 我们好奇心强,我们不自我局限,我们除了 Node.js,还深度跨界到上下游的共建,如 Serverless PaaS 、研发平台、前端工程化等。
  • 我们信仰前端,我们相信技术能改变世界,我们建设了多个稳定可靠的基础服务,支撑多年双十一,是蚂蚁大部分前台业务的背后支撑之一。
  • 我们热爱开源,我们崇尚异步协作,我们为社区贡献了 Egg、CNPM 等开源项目,我们有 Ant Design、Umi、AntV 等兄弟团队提供背靠背依靠。

想跟一帮志同道合的人,站在巨人的肩膀上,去探索和引领广州最前沿的前端技术,做一些有意思的事吗?

想了解我们的发展史,传送门:『这些年的体验技术部 · Node.js 基础服务 - 摸爬滚打才不负功名尘土』 https://www.yuque.com/afx/about/nodejs

职位描述

我们不自我局限,可以一杆到底也可以深钻某个领域,包括框架和命令行工具(Node.js)、基础服务(云服务)、基础设施底盘(K8S)等等。

  • O1:打造易用、可共建、可持续的 Chair 框架及工具链生态,降低学习成本及运维成本,持续提升开发者幸福感。
  • O2:提供稳定、可靠、快速、省心的前端基础设施(File、Render、TNPM 等),勇当二号位,助力及协建更接地气的技术产品。
  • O3:深度参与 Node.js 社区生态建设,提供引领时代的最佳实践,培养更多的 Node.js 人才。

Base 地:广州琶洲阿里中心、杭州蚂蚁 A 空间。

职位要求

我们希望你是:

  • 基础踏实,有一技之长,对某个技术领域有自己的见解。
  • 在实际工作中,有较深的 Java 或 Node.js 实践经验,并掌握云服务的日常运维。
  • 聊起技术就两眼发光,饥渴难耐的求知欲。有一定的技术敏感度和视野。
  • 具备良好的学习能力和问题分析能力,关注社区动态及技术发展趋势,拥抱开源。
  • 具有很强的分析和解决复杂问题的能力,有强烈的责任心和使命感,良好的沟通表达能力和团队协作能力。

科普文:为什么不能在服务器上 npm install ?

背景

Node.js 很简单,容易上手。但也因此缺乏不少规范,使用者水平参差不齐。

最近经常看到的一个问题是:很多新手,在部署的时候,是直接在服务器上 npm install ,这是非常不推荐的。

存在的问题

无法确定唯一性

因为安装是有较大的网络耗时的,所以你甚至无法保证集群情况下,两台服务器上npm install 下来的包是一模一样的。

如果某个库刚好更新了,并且它有 BUG,然后你就是百思不得其解:一定概率出某个问题。排查起来简直想死。

当然,很多人为了解决这个问题,就选择「锁版本」这个方案。

鉴于 「锁版本」这个方案属于 「屎色自行车问题」 ,这里不想讨论,我们的观点参见:「知乎专栏 - 死马:为什么我不使用 shrinkwrap(lock)」

上线耗时久,无法快速回滚

上线后,发现线上故障,要快速回滚止血的时候,就懵逼了:

  1. 要等待依赖安装,万一网络有个抖动啥的,妥妥的 P4 故障变为 P0 故障,年终奖没了。
  2. 万一问题是底层依赖导致的,回滚也没用。
  3. npm cache 解决不了问题,如机器扩容的时候。

推荐方案

development lifecycle

其中,关键点是:在构建期就把依赖打包进去。

优点:

  1. 解压即可立刻启动,无需等待网络耗时。
  2. 能保证肯定是可以运行的,因为此时依赖都是确定好的,且经过 CI 单测保障的。
  3. 可以快速回滚,止血。

缺点:

  1. 包体积大(但其实存储不值钱。。。)

流程图

如何实施

那有同学就要问了:我是小公司,不像你们有这些基建可以服务,怎么办?

其实成本真的很低:

  • 代码仓库 GitLab 自带就有 GitLab-CI 了,你只需要写个配置文件,触发自动构建即可。
  • 然后把构建后的文件,找个地方存储,如 OSS 啥的。
  • 服务器部署的话,有运维发布系统最好,没有的话,自己写个 shell 把 OSS 文件下载解压。
  • 当然,很多云服务都支持 Docker 镜像了,那就更简单了。

广告区

写简历的技巧

最近以前公司很多朋友离职,我们搞拨测那批人终于都各奔东西。
整理下简历的一些注意事项,分享给大家。

  • 关键的内容必须在最前面,那些什么未婚/户口啥的,没人关注的,去掉。多从HR的角度思考下,她们很忙的,一目十行的看简历,如果你第一页没有关注的信息,那就没了。
  • 开发人员简历的三宝: stackoverflow,github,blog/知乎
  • 简历建议控制在一张到两张纸,不要用智联的默认模版,格式要转为pdf(有些公司用wps,看某些doc会格式乱)
  • 建议:
    • 第一列只保留名字,手机,邮件,关键词。
    • 求职意向可以不用,或者简单一句话,放到第一列
    • 自我评价要丰富,这是重点,可以把专业技能写到这里
    • 接着是项目经验,挑重点写,项目介绍要简要,关键突出自己在项目中的作用,经验和教训,团队职责
    • 接着是工作经历和教育,也可以简要一点
    • 证书+语言能力
  • 这是我收集的一些「简历模版」,可以参考下: https://huaban.com/boards/3723920
    但是仅供参考,不要用Photoshop来做图片简历,要用word+ppt来组合做,这样HR才能复制剪贴。
  • 一些业界人士的讨论(建议都看一遍):
  • 说在最后的话:
    • 有时候,面试这东西看缘分,在对的时间对的地点遇到对的公司。
    • #面试官说#: 面试其实看缘分,很多时候不是人家不优秀,而是自己短板正好和人家凑一起了 。
    • 「别说你有十年经验,你只是一年经验用了十年而已。」 http://t.cn/zRJhls7

记录一些常见的沟通问题

前言

本 issue 用于汇总一些在社区常见的的沟通问题,方便直接丢链接砸脸。


1. X-Y PROBLEM

https://coolshell.cn/articles/10804.html

1)有人想解决问题 X
2)他觉得 Y 可能是解决 X 问题的方法
3)但是他不知道 Y 应该怎么做
4)于是他去问别人 Y 应该怎么做?

简而言之,没有去问怎么解决问题 X,而是去问解决方案 Y 应该怎么去实现和操作。于是乎:

1)热心的人们帮助并告诉这个人 Y 应该怎么搞,但是大家都觉得 Y 这个方案有点怪异。
2)在经过大量地讨论和浪费了大量的时间后,热心的人终于明白了原始的问题 X 是怎么一回事。
3)于是大家都发现,Y 根本就不是用来解决X的合适的方案。

X-Y Problem 最大的严重的问题就是:在一个根本错误的方向上浪费他人大量的时间和精力!


2. 如何向开源项目提交无法解答的问题

https://zhuanlan.zhihu.com/p/25795393

卖个关子

『欲知后事如何,且听下回分解』

总是留个后手,不要一次性把话说完,让你的问题充满神秘感,充分调动起读者的好奇心。

正确示范:

你:我的代码出错了,不知道该怎么办?
你:我这里有一个问题,有人能帮我解决么?
你:在吗?

上纲上线

『接连便是难懂的话,什么"KPI","绩效","弃坑"之类,引得众人都哄笑起来』

把你的问题拔高一个层次,站在道德高地进行指责,一旦讨论涉及到政治,他们便百口莫辩。

正确示范:

原来大公司团队也就这样啊,都不好好测试的么?就这玩意还好意思拿出来,
就是个 KPI 产物,晋升完就不管了。

错误示范:

这个项目虽然是大公司的产品,在以下方面比起竞品还有劣势,个人不建议使用。

尽情宣泄情绪

『你们把我项目搞挂了,狗屎!』

开源项目导致了你的项目出现 BUG,导致了你周六晚上还要加班,导致了男/女友抱怨你不理他/她,这必须要有人负责。你的工作和生活被他们毁了,也别让他们好过。

正确示范:

这个项目烂透了,用起来全是坑,文档也太简略了,这样做开源真是呵呵了

错误示范:

这个项目有很多细节问题,文档也不完善,请问有改进的计划么?
我收集了以下具体问题,希望持续完善。


3. 屎色自行车棚

https://juejin.im/post/5aa882eaf265da23923607bd

通常用来指 某些简单到所有人都可以发表观点,并且几乎所有人都会去发表观点的问题。

用来代表一些无关紧要但会引起大量争论的问题,如我们软件开发行业:

  • 世界上最好的编程语言是?(或许这个问题已无需争论)
  • React 和 Vue,哪个更好?
  • 世界上最好的编辑器是哪个?Emacs 还是 Vim?
  • 代码中的缩进应该用 Tab 还是空格?
  • 要不要锁版本?
  • ......

类似的问题还有很多,并且通常争论不休。与其参与到其中图个嘴上痛快,不如踏踏实实地关注眼前的问题。愿你能够辨别自行车棚问题、远离自行车棚会议,充实地过好每分每秒。

科普文:服务器上如何 Node 多版本共存

背景

很多公司的服务器环境没有做隔离,就是全局安装一个 Node.js Runtime,一般很少升级。

nvs / nvm 等可以用来切换版本,但无法同时共存。而且一般服务器不允许你随意升级。

因此很多同学都会很痛苦:「都 8012 年了,还是 Node 4.x 甚至 0.x 简直想死!」

时至今天,最好的办法就是 Docker。但奈何很多小公司还处于水深火热之中。

本文将介绍下我们很早前就使用的一套方案,可以完美解决非 Docker 情况下 Node 多版本共存问题。

npm scripts

首先要介绍下 npm scripts ,简单的说,就是可以在 package.json 里面定义脚本。

{
  "name": "egg-showcase",
  "scripts": {
    "start": "node index.js",
    "debug": "egg-bin debug --inspect-brk"
  },
  "devDependencies": {
    "egg-bin": "^4.7.0",
  }
}

如上,定义脚本后即可执行:

$ npm start
$ # 执行并传参,需要多一个 --
$ npm run debug -- --inspect-brk

同学们可能会比较好奇,上面的 egg-bin 是哪来的?这是 npm 的一个很重要的特性:

通过 npm run your-scripts 启动的脚本,会默认把 node_modules/.bin 加到 PATH 环境变量中。

由于,我们的依赖 egg-bin 有定义了 bin 字段 ,因此安装后会软链到 node_modules/.bin ,从而能被寻址并执行。

聪明的同学很快就会想到,如果 Node 的 Runtime 也在这个目录下,会怎么样呢?

Node Runtime

因此,问题就可以转换为:如何把 Node Runtime 打包到项目中?

答案就是在构建期打包进去,参见我们的上一篇文章 『科普文:为什么不能在服务器上 npm install ?』

🙋🙋🙋 老师!打包能否更简单一点呢?能否就像 dependencies 那样定义一下就好了呢?

没问题!方案有几种:

  • 我们很早前写了一个 nodeinstall 库来简化这个步骤:
  • Node 8 后支持了类似的特性

nodeinstall

定义:

{
  "name": "egg-showcase",
  "scripts": {
    "start": "node index.js",
    "debug": "egg-bin debug --inspect-brk",
    "echo": "node -p process.versions"
  },
  "devDependencies": {
    "egg-bin": "^4.7.0",
  },
  "engines": {
    "install-node": "^8.0.0",
    // AliNode 的话用这个
    "install-alinode": "^3.8.0"
  }
}

安装:

$ npm install nodeinstall -g

$ cd path/to/your/project
$ nodeinstall

$ # 验证
$ npm run echo

node: '8.10.0'

就这么简单,从此服务器上只要有一个任意版本的 npm 即可,各项目都可以用自己的 Node 版本,不会互相影响。

如果你是阿里员工,这一步都可以省了,因为 tnpm 内置就支持这个配置,无需单独安装 nodeinstall

但需要注意的是:

  • 必须在跟你线上服务器一样操作系统的 CI 上去打包,否则如果你是在本地 Windows 下打包的,线上却是 Linux,那对应的 Node Runtime 就不对了。
  • 记得写文档
    • 经常有同学会说,我 Node 是 8 的,但为啥不支持 async 。
    • 这时候让他执行 ./node_modules/.bin/node -p process.versions 就知道了。

Node 8 内建支持

另外,除了我们写的这个工具外,在 Node 8 里面,官方终于也支持了类似的功能。

不过它是在 postinstall 里面安装的,没有优先安装 Node ,会导致 Native Addons 依赖有问题(应该先安装 Node,再用这个版本去安装其他依赖)。现在不知道改了没。

挺期待官方完善这个特性的,这样我们的又一个轮子可以完成历史使命,退休了。

写在最后

那如果我有服务器权限,我可以随意升级 Node 版本,就不需要了吧?
我们 Node 工程化的理念是一个包可以快速部署,这样就不依赖 PE 来配置环境了。

如果应用多了,服务器就得根据应用名配置 Node 版本,这样应用升级就得分开两步操作。

当然,最优的解决方案,还是 Docker 化隔离,都是一样的思路,一份产物部署的理念。

广告区

rn模块 npm link

rn模块 npm link 之后 项目重新刷新会报错。unable to resolve module 'ecool/react-native-dllog' from my Project.这是为啥?

量身打造angular开发解决方案(2) - 需求调研

量身打造angular开发解决方案(2) - 需求调研

兵马未动粮草先行,我们的第一步工作就是分析需求:我们需要怎么样的一款工具?

1. 需求调研

根据我们团队的实践,还有在社区的调研,该前端集成解决方案应该具备:

  1. 模块化开发。最好能像写NodeJS一样写JS,很舒服。
  2. 性能要好。模块那么多,得能按需加载,请求不能多。还要能支持ngRouteui-router
  3. 组件化开发,organize by feature,一个组件的JS、CSS、模板最好都在一个目录维护,维护方便。
  4. 也许哪天我就不爱你了呢? 所以不能对Angular进行太耦合的hack,要原汁原味。
  5. 依赖注入声明的写法太烦了,能否自动帮我生成?
  6. JS/CSS/图片的混淆压缩应该都没问题吧。
  7. 图片base64嵌入。有些小图需要压缩并嵌入到页面、JS或者CSS中使用。
  8. 测试不能停,要能与ci平台集成, 支持karmaprotractor
  9. 开发体验要好。文件监听,浏览器自动刷新(livereload)一个都不能少。
  10. 我们用NodeJS作为服务器,本地要能预览,最好再能抓取线上数据,方便调试。
  11. 业界那么多优秀类库, 当然要支持bower组件仓库的安装啦。
  12. yeoman用过吧? 对, 脚手架功能也不能少了。
  13. CoffeeScript,Sass之类的有意思,也给我来一个。
  14. 顺便再提供个打包功能吧

回头看看这个列表,我怎么有点腿发软了。。。

2. 需求分析

好吧,在瓶神的鼓励下, 我们试试看吧,先把上面的需求整理下:

  • 规范
    • 开发规范
      • 模块化开发,JS模块化,CSS模块化,像NodeJS一样编码
      • 组件化开发,js、css、tpl维护在一起,organize by feature
      • 按需加载,支持ngRouteui-router
      • 尽量原生化的编写Angular
    • 部署规范
      • 采用NodeJS后端,基本部署规范应该参考express项目部署
      • 按版本号做非覆盖式发布
  • 框架
    • JS模块化框架,符合CommonJS规范
    • 支持请求合并
    • 支持按需加载
    • 支持通过localstorage等方式缓存资源
  • 工具
    • 自动生成Angular的依赖注入声明。
    • 支持js、css、图片压缩
    • 允许图片压缩后以base64编码形式嵌入到css、js或html中
    • ci平台``集成, 支持karma单元测试,protractor`端到端测试。
    • 文件监听、浏览器自动刷新
    • 本地预览、数据模拟
    • 提供类似yeoman的脚手架, 生成项目框架,包括Controller
    • 支持CoffeeScript,Sass的编译
    • 提供打包功能
  • 仓库
    • 支持bower模块安装和使用

3. 小结

一开始的时候,我总期望在scrat的基础上去修订,可是浪费很多时间后才发现, 即使团队的规范看起来差异不大,但还是有很多不同的应用场景,不能一刀切。因为,团队自身的前端解决方案是需要量身定制的。

本文实现的ngfis是一个基于fis之上的angular方向的解决方案, 只包含了最核心的一些需求,因此我们可以在这之上再去架构自己的方案, 如我们团队的larva就是ngfis+hybrid app+uae的结合体。

JD - 阿里游戏前端组 - 广州

岗位描述

作为一名前端,你是否:

  • 很好奇 V8 的各种黑科技?
  • 很好奇 Node 如何从小工坊走向大规模企业开发?它如何支撑双十一量级的业务?
  • 很好奇 Egg 团队基于 RFC 的团队异步协作模式?
  • 很好奇前端网红的日常?想了解朴老师 AliNode,苏千 CNPM,天猪 Egg,云风的游戏引擎,张云龙前端工程化的实践过程吗?

现在就加入我们:

  • 你将可以参与到 Egg 等阿里开源项目的研发。
  • 你将体验到大规模 Node 研发背后的各种黑科技基建。
  • 你将可以跟浏览器内核同学深度交流。
  • 你将在实际生产环境中应用业界前沿技术,引领未来。

岗位要求

我们希望你是:

  • 不甘于只做切图仔。
  • 聊起技术就两眼发光,饥渴难耐的求知欲。有一定的技术敏感度和视野。
  • 有一技之长,对某个技术领域有自己的见解(Node or 炫酷动效 or 小游戏)。
  • 具备良好的学习能力和问题分析能力,关注社区动态及技术发展趋势,拥抱开源。
  • 了解常见的前端技术:Vue / React / 工程化 / 性能优化 / Node / 前端动效 。
  • 工作年限不强制,即使毕业一两年,只需有对应的技术产出。

团队介绍

我们是阿里游戏大前端团队,Leader 是 Egg 的核心开发者 - 天猪,坐标广州。

负责阿里游戏各业务线的研发与支撑,既有面向千万用户的游戏平台,也有支持业务快速发展的中后台产品,当然还有正处风口的小游戏。

想跟一帮志同道合的人,站在巨人的肩膀上,去探索和引领广州最前沿的前端技术,做一些有意思的事吗?

联系方式

image

简历投递: liuyong.ly3#alibaba-inc.com

请忽略

升级到vscode1.32.1后,debug启动时代码卡住,无法启动

只有升级到vscode1.32.1才会有问题,周围同事不升级可以启动,升级后也启动不了了...

启动日志

image

debug配置

image

有趣的收集

收集我遇到的有意思的前端类库

基础库

框架

数据库

数据模拟

爬虫

Bootstrap模板

Logger

editor

angular modules

tool

奇奇怪怪

语法解析

你所不知道的模块调试技巧 - npm link

1. 背景

node 应用开发中,我们不可避免的需要使用或拆分为 npm 模块,经常遇到的一个问题是:

新开发或修改的 npm 模块,如何在项目中试验?

新同学一般会有以下几种方式:

为了方便示范,我们假设项目是 my-project, 需要用到一个独立的 my-utils 模块

1.1 发布一个 beta 版本

  • 优点:你高兴就好。
  • 缺点: 无趣+无趣+无趣,麻烦+麻烦+麻烦。

1.2 直接用相对路径安装

$ cd path/to/my-project
$ npm install path/to/my-utils
  • 优点:简单明了
  • 缺点: 调试过程中往往需要微调,此时需要切换到 my-utils 目录修改,然后反复重新 install,很麻烦

1.3 使用软链

$ cd path/to/my-project/node_modules
$ ln -s path/to/my-utils my-utils
  • 优点:软链后,两边修改直接同步
  • 缺点: 指令操作麻烦,不同操作系统语法不一样

2. 正解 - npm link

但其实 npm 本身已经对此类情况提供了专门的 npm link 指令。

相关文档: https://docs.npmjs.com/cli/link

下面我们简单介绍下用法:

$ cd path/to/my-project
$ npm link path/to/my-utils

简单的替换一个单词,就搞定了,cool~

如果这两种的目录不在一起,那还有一种方法:

$ # 先去到模块目录,把它 link 到全局
$ cd path/to/my-utils
$ npm link
$
$ # 再去项目目录通过包名来 link
$ cd path/to/my-project
$ npm link my-utils

该指令还可以用来调试 node cli 模块,譬如需要本地调试我们的 egg-init,可以这样:

$ cd path/to/egg-init
$ npm link
$ # 此时全局的 egg-init 指令就已经指向你的本地开发目录了
$ egg-init # 即可

想去掉 link 也很简单:

$ npm unlink my-utils

3. 写在最后

  • 该方法只是为了最后一步调试,模块本身的正确性,应该更多的通过单元测试来保证。
  • 单元测试相关内容,可以参见:单元测试

关于如何更改egg调试时候的端口

您好,我现在有一个后端项目用的egg,我想用vscode调试,但是前端监听后端使用的端口是8080,默认egg调试的端口是7001,那么我如何更改这个端口呢?

量身打造angular开发解决方案

量身打造angular开发解决方案

不知不觉,我们团队实践AngularJS已经一年多了, 一直使用grunt来作为工作流,但它存在不少问题。

最近在反复拜读 @fouber「前端工程系列文章」, 发现神器 FIS,并希望借此机会,量身打造了一款Angular前端工具。

可惜FIS本身的文档还不够完善,内部实现有很多黑盒,于是本文作为踩坑实践后的总结,希望能对后来者有所帮助。

本文假定读者是有前端开发经验的工程师,对angular有一定的实践经验,且仔细阅读过「前端工程系列文章」

1.为何选择FIS?

FIS是一个构建系统内核,很好的抽象了前端集成解决方案所需的通用工具需求。

它补全了前端语言缺少的三种语言能力

  • **资源定位的能力:**使用开发路径进行资源定位,项目发布后转换成部署路径
  • **依赖声明的能力:**声明一个资源依赖另一个资源的能力
  • **资源嵌入的能力:**把一个资源的编译内容嵌入到另一个文件中

每个团队都有自己的应用场景,不能一刀切,所以我们需要量身定制。
FIS本身并不与任何后端语言绑定。只有基于FIS实现的具体解决方案才会有具体的规范和技术选型。

因此FIS是一个很好的起点,目前已有的定制方案:

解决方案 描述
fis-plus 适合百度的业务场景,后台为PHP
scrat 适合UC大导航的业务场景,后台为NodeJS
spmx 简单实现对seajs的场景
jello Java + Velocity
ng-fis 针对Angular的场景,即本文实现的方案。
(而我们团队的项目则是基于ng-fis之上进行再次封装)

2.目录

VSCode 调试 Egg 完美版 - 进化史

背景

VSCode 早期版本,对 Node Cluster 的调试支持一直不是很友好,譬如:

  • 开发期重启进程后,不支持重新 attach。
  • Cluster 重启后 debugPort 会自增,VSCode 也不支持 attach 新端口。
  • Egg 的 多进程模型 多了 Agent 处理公关事务,在开发期也有 3 个进程(master, agent, worker)。

那些折腾过的历史

黑暗时代

早在 2016 年时就开始的折腾:#14#15 ,没有太好的办法。

青铜时代

然后 @okoala 写了 egg-development-proxyworker

主要思路是在 agent 里面启动一个 socket proxy 来动态代理 worker 的调试端口。
很巧妙的解决了自动重启后调试端口变化问题,但缺点是要开发者手动安装插件,并配置 vscode。

此时只能说达到可用的阶段。

黄金时代

接着,我写了 egg-bin debug 把 proxy 功能内置了,实现原理参见当时的 RFC 提案

并且提供了 vscode-eggjs 扩展来方便配置。

解决了:

  • 自动 attach 重启后的 worker 新端口
  • 自动生成 launch.json

对于一般应用开发者基本上已经非常易用了,但还存在以下问题:

  • vscode 的 launch.json 对同时 attach 多个的支持不是很友好,虽然有 compounds
  • 默认只 attach worker,并且不支持启动期的断点,如果要 brk 的话要手动 attach 3 次,非常麻烦。

而今天,[email protected] 正式支持了 Automatically attach debugger to Node.js subprocesses

因此我们之前的做法可以大幅简化了,没解决的问题也基本解决了,可以称为 完美版 了。

完美版的人生

文档已经更新:使用 VSCode 进行调试

安装 vscode-eggjs,并初始化调试配置(如果之前有则需删除 launch.json 文件)

image

然后简单的一个 F5 搞定~

简析

// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch Egg",
      "type": "node",
      "request": "launch",
      "cwd": "${workspaceRoot}",
      "runtimeExecutable": "npm",
      "windows": { "runtimeExecutable": "npm.cmd" },
      // 启动我们的 egg-bin debug 并默认是 brk
      "runtimeArgs": [ "run", "debug", "--", "--inspect-brk" ],
      // 日志输出到 Terminal,否则启动期的日志看不到
      "console": "integratedTerminal",
      "protocol": "auto",
      // 进程重启后自动 attach
      "restart": true,
      // 因为无需再 proxy,故改回原来的 9229 端口
      "port": 9229,
      // 自动 attach 子进程 
      "autoAttachChildProcesses": true
    }
  ]
}

其他

vscode 扩展生成的配置里面,还支持了单元测试的断点,配置如下:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Egg Test",
      "runtimeExecutable": "npm",
      "runtimeArgs": [
        "run",
        "test-local",
        "--",
        "--inspect-brk"
      ],
      "protocol": "auto",
      "port": 9229,
      "autoAttachChildProcesses": true,
      "disableOptimisticBPs": false,
    }
  ]
}

完美版.最终版1

VSCode 1.22 支持了 Automatically Attach to Node.js processes,也就是如果你开启了这个的话,无需配置什么 launch.json,直接在 terminal 执行 npm run debug --inspect-brk 就会自动 attach 了。

补充

Egg 的调试,跟 Node 没啥区别,因此一定要了解 Node 的基础调试知识。

其中,--inspect-brk 是指在进程第一行就暂停,等待 attach,因此:

  • master 先启动,在第一行会停住,需要你 attach master,才会往下走
  • 接着 master 启动 agent,也是在第一行停住,需要 attach agent 才会往下走
  • 最后 agent 启动完成后,worker 才开始启动,一样也是在第一行停住,需要 attach agent 才会往下走

上面这几个 attach,由于上面我们提到的 VSCode 的支持,只需要开启配置,即可无感知的一键 attach。
虽然如此,但作为开发者,大家还是需要理解 Node 的调试原理。

知乎: 如何评价阿里开源的企业级 Node.js 框架 egg?

搬自我在知乎的问答: https://www.zhihu.com/question/50526101/answer/144952130

谢朴老师邀请。

利益相关: egg 团队成员,jsconf china 2016 的 egg topic 的演讲者。

本文较长,包含以下内容,比较忙的同学可以跳阅:

  • Node.js 在阿里的定位
  • 我们面对的挑战与机遇
  • egg 在阿里的定位 / 发展史 / 开发者
  • egg 的设计理念和特点介绍
  • 我个人在参与 egg 开发过程中的收获

1. Node.js 在阿里

阿里是业界最早的一批使用 Node.js 来做线上大流量应用的公司,早在 2011 年的就已经开始在生产环境中使用。

众所周知,在阿里的技术栈中, Java 是最最核心的,那 Node.js 扮演怎么样的一个角色呢?

  • 基础设施大部分采用 Java/C++ 实现,变化较少,有事务要求的 Business Services 通常使用 Java 实现。
  • 而 Node.js 则替代过去 PHP/Java Web 的场景,用在需要快速迭代,需求变化非常快的用户侧。

据不完全统计,目前阿里 Node.js 的开发者几百号人,线上的应用也非常之多,仅次于 Java 应用,光对外服务的进程数就超过 1w+。

2. 我们面对的挑战与机遇

Node.js 的使用是越来越多了,但整个基建和生态却跟不上:

  • Node.js 开发者越来越多,但是真正涉足基础技术的人员还是那么少,那么分散。
  • 出现非常多的重复性技术问题和重复建设,每个团队一套轮子。
  • 非常多不合理地使用 Node.js 进行 Web 开发,也没有一套统一的规范可以参考。
  • 越来越多的 Node.js 应用出现,需要保证高可用。

面对上述的挑战,阿里的 Node.js 先驱者们,做了非常多的探索和努力,如:

  • 朴灵的 alinode 就是一套运行时环境和服务平台,帮助开发者分析性能问题,快速定位疑难杂症。
  • 苏千的 cnpm / tnpm 提供 npm 国内镜像加速服务,以及阿里内部私有库服务。
  • 死马等很多同学贡献的几百块个 Node.js 基础类库,还有各种服务的 Node.js 版 sdk。
  • 还有包括整个工作流程上的内部系统和工具支撑,阿里的 Node.js 基建真的很不错。
  • node 的 collaborator 有 5 个国人, 其中 4 个在我们这边.

也正是因为他们一代代的努力,Node.js 在阿里才能落地生根,才有今天这繁荣。

对这块有兴趣的同学,可以开个问题邀请苏千/死马等人讲讲他们当年在阿里的开荒史。

3. egg 在阿里的定位

egg 也是这一时代洪流中的新生一员,它面向的领域是:企业级的 web 基础框架

  • 2013 年蚂蚁的 chair 框架,可以视为 egg 的前身。
  • 2015 年 11 月,在苏千的召集下,阿里各 BU 的前端骨干齐聚黄龙,闭门共建。
  • 2016 年初,各 BU 的基础 web 框架完成升级,在同一套规范的基础上进行差异化定制。
  • 2016 年中,广泛使用在绝大部分阿里的前端 Node.js 应用。
  • 2016 年 09 月,在 JSConf China 2016 上亮相并宣布开源。
  • 2017 年初,官网文档 https://eggjs.org/ 亮相,并发布 [email protected] 版本。

egg 目前是阿里 Node.js 应用的核心基础设施,担心是 KPI 产物的同学,可以放宽心了。

有哪些人参与到 egg 的开发和维护中?

  • 核心开发者中,贯高(popomore, 小 substack),苏千(Python发烧友),死马等人,是开源社区的资深人士,每个人维护的 npm 模块都有几百个,同时也是 cnpm 和 koa 的核心开发者。
  • 我们甚至还有 2 位前端安全方向的专家,负责 egg-security 等类库。
  • 阿里各 BU 的前端活跃开发者,光框架和插件的维护者就超过 40 位。
  • 外部开发者也不少,甚至还有几位硅谷华人开发者在帮我们翻译文档。

同学们也不用担心 egg 只适合阿里或电商类应用:

  • 蚂蚁金服,天猫,UCWeb,村淘,神马等 BU 的业务场景千差万别,但他们的基础框架都是在 egg 之上扩展的,遵循的是同一套规范。
  • egg 的设计机制,使得我们在遵循同一套规范的同时,完美的达成生态共建和差异化定制的平衡点。
  • egg 里面只有我们对企业级应用的最佳实践,而并没有耦合任何阿里的具体业务逻辑。

image

4. egg 的设计理念

Think about future, Design with flexibility, But only implement for production.

4.1 约定优于配置

一个大规模团队的基础框架,最重要的是需要遵循一定的约束和约定。

没有约定的团队,沟通成本是非常高的,比如有人会按目录分栈而其他人按目录分功能,开发者认知不一致很容易犯错。通过约定可以减少开发人员的学习成本,开发人员不再是『钉子』,可以流动起来。

egg 最核心的东西,其实就是一套约定和规范,这个规范不仅仅是开发目录的约定,还包括了开发过程中,从提案讨论,编码风格,code review 等等方方面面的规范化。

其实大家的基础框架用不用 egg 真的无所谓,最重要是有一套适合团队的约定。

egg 给社区最有价值的回馈是:

当然,我们推荐基于 egg 来定制上层框架:

  • egg 采用微核 + 插件体系,本身大部分功能由插件提供,高度灵活,功能强大。
  • egg 提供了完善的加载机制实现 - loader,也是整个框架的灵魂,并且支持非常多的扩展方式。
  • 约定不等于扩展性差,相反 egg 有很高的扩展性。
  • 对于团队架构师或技术负责人,它足够的 optional,只需简单的做加法,组装插件即可。
  • 对于团队开发人员,它又足够的强约束,保证不会出现千人千面的代码风格和设计。
  • 某种意义上来讲,egg 也是渐进式的框架,参见 egg 文档 - 渐进式开发

4.2 插件机制

插件机制是 egg 的一大特色,它不但可以保证框架核心的足够精简、稳定、高效,还可以促进业务逻辑的复用,生态圈的形成。

经典范例如 egg-security,就集合了阿里集团的多年安全经验积累,具体可以看下 egg 文档 - 安全

同时,差异化定制不意味着没有约定,它只是下层插件实现的差异化,而上层开发体验是一致的:

  • 如 egg-view-nunjucks / egg-view-react 这类插件,遵循 模板插件开发规范 的同时,又不限制具体模板引擎选项。仅需安装不同的 view 插件, 上层开发体验是一致的。
  • egg 文档 - 插件 中提到的 mysql 等的实例化方式,只要是开发**性的模式,都会被不断的总结和沉淀下来成为约定。
  • egg-tracer / egg-userrole 这些约定,等等,无处不在。

4.3 框架机制

上面提到的插件机制,很灵活,但是对于企业级应用来说,却还不够。

如果你的团队遇到过:

  • 维护很多个项目,每个项目都需要复制拷贝诸如 gulpfile.js / webpack.config.js 之类的文件。
  • 每个项目都需要使用一些相同的类库,相同的配置。
  • 在新项目中对上面的配置做了一个优化后,如何同步到其他项目?

如果你的团队需要:

  • 统一的技术选型,比如数据库、模板、前端框架及各种中间件设施都需要选型,而框架封装后保证应用使用一套架构。
  • 统一的默认配置,开源社区的配置可能不适用于公司,而又不希望应用去配置。
  • 统一的部署方案,通过框架和平台的双向控制,应用只需要关注自己的代码。
  • 统一的代码风格,框架不仅仅解决代码重用问题,还可以对应用做一定约束,作为企业框架是很必要的。egg 在 koa 基础上做了很多约定,框架可以使用 Loader 自己定义代码规则。

为此,egg 为团队架构师和技术负责人提供 框架定制 的能力,框架是一层抽象,可以基于 egg 去封装上层框架,并且 egg 支持多层继承。

+-----------------------------------+--------+
|      app1, app2, app3, app4       |        |
+-----+--------------+--------------+        |
|     |              |  framework3  |        |
+     |  framework1  +--------------+ plugin |
|     |              |  framework2  |        |
+     +--------------+--------------+        |
|                   egg             |        |
+-----------------------------------+--------|
|                   koa                      |
+-----------------------------------+--------+

这样,整个团队就可以遵循统一的方案,并且在项目中可以根据业务场景自行使用插件做差异化,当后者验证为最佳实践后,就能下沉到框架中,其他项目仅需简单的升级下框架的版本即可享受到。

4.4 质量保障和技术支撑

  • egg 所有的核心代码和插件,都有 93+% 的覆盖率的单元测试。参见 egg 文档 - 单元测试
  • 规范的 github pull request 协作模式,每一次提交都会经过自动化集成测试,以及 2+ 个核心开发者的 review。
  • 严格遵循 semver 版本发布规则,可以放心的使用 ^ 引入我们的模块。
  • 提供了 egg-bin,npminstall 等等开发期的辅助功能,让开发者更愉悦的进行开发。
  • 内网版的 egg 是继承于社区版的,大部分问题,我们能第一时间感知到并及时修复。
  • 技术支撑方面,在前面第三节里面也提到了,我们有非常多且活跃的开发者,以及内部的广泛使用,并不会没人维护。
  • node 的 collaborator 有 5 个国人, 其中 4 个在我们这边.

4.5 其他

与其他框架的对比

其实不是一个层面的,sails , hapi 这些框架,通过 egg + 对应的插件封装成上层框架,就一样了。

egg 与 koa 的关系

  • koa 是一个非常优秀的框架,然而对于企业级应用来说,它还比较基础,而 egg 选择了 koa 作为其基础框架,在它的模型基础上,进一步对它进行了一些增强。
  • egg 升级到 koa2 的成本?
    • egg 的核心开发者即为 koa 的核心开发者
    • 非常之低, 具体参见我们的升级计划

5. 我个人在参与 egg 开发过程中的收获

回顾这几年,我个人感觉是非常幸运的,13 年的时候,跟着云龙做前端工程化,15 年则是参与到 egg 的整个开发过程中。

在这旅途中,我熟悉了堪称教科书式的基于 Git Pull Request 的异步硬盘式协作模式;我学习到不少大规模应用中的经验总结;这一段经历让我受益良多,永远无法忘怀,在无数个大半夜,一帮人还在 issue 上讨论的热火朝天。

很幸运自己能参与到阿里的 Node.js 发展中搬一两块砖,这里的基建和生态真的非常完善:

  • 私有库有苏千的 tnpm
  • 性能分析有朴老师的 alinode (egg 开源前还用它发现了 node set header 提升 10x 速度的一个坑,对这个有兴趣的,开问题邀请朴老师回答吧~)
  • 各种后端服务都有死马他们维护的对应的 SDK 接入
  • 还有非常多的数据上报,监控等等工作流上的辅助工具和系统。
  • 还有玉伯团队的三年规划,佩服到五体投地。

有好奇心的同学,在杭州的,不妨亲自进去看看,不信,你看叔叔 @徐飞 。

而在广州的同学,也可以过来 UC 这边体验下,我们的内部交流通道非常顺畅。https://www.zhihu.com/question/55271199/answer/143741434

最后,回过头来看,我个人是挺感慨的,这么短时间,完全没有政治命令,大家主动拥抱共建,对于阿里这样如此大规模的多部门的公司,真可谓奇迹。

国内的开发者真的不用妄自菲薄,这几年,越来越多的国内框架如 ant design,element,weex,macaca 等等,正走出国门,拥抱世界。

以上

--

FIS使用问题记录

  • commander插件不能继承
  • 插件机制不支持类似karmainline
  • 调试不方便
  • 模板替换机制?

当 Egg 遇到 TypeScript,收获茶叶蛋一枚

slice

前言

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.

TypeScript 的静态类型检查,智能提示,IDE 友好性等特性,对于大规模企业级应用,是非常的有价值的。详见:TypeScript体系调研报告

然而,此前使用 TypeScript 开发 Egg ,会遇到一些影响 开发者体验 问题:

  • Egg 最精髓的 Loader 自动加载机制,导致 TS 无法静态分析出部分依赖。
  • Config 自动合并机制下,如何在 config.{env}.js 里面修改插件提供的配置时,能校验并智能提示?
  • 开发期需要独立开一个 tsc -w 独立进程来构建代码,带来临时文件位置纠结以及 npm scripts 复杂化。
  • 单元测试,覆盖率测试,线上错误堆栈如何指向 TS 源文件,而不是编译后的 js 文件。

本文主要阐述:

  • 应用层 TS 开发规范
  • 我们在工具链方面的支持,是如何来解决上述问题,让开发者几乎无感知并保持一致性。

具体的折腾过程参见:[RFC] TypeScript tool support


快速入门

通过骨架快速初始化:

$ npx egg-init --type=ts showcase
$ cd showcase && npm i
$ npm run dev

上述骨架会生成一个极简版的示例,更完整的示例参见:eggjs/examples/hackernews-async-ts


目录规范

一些约束:

  • Egg 目前没有计划使用 TS 重写。
  • Egg 以及它对应的插件,会提供对应的 index.d.ts 文件方便开发者使用。
  • TypeScript 只是其中一种社区实践,我们通过工具链给予一定程度的支持。

整体目录结构上跟 Egg 普通项目没啥区别:

  • typescript 代码风格,后缀名为 ts
  • typings 目录用于放置 d.ts 文件(大部分会自动生成)
showcase
├── app
│   ├── controller
│   │   └── home.ts
│   ├── service
│   │   └── news.ts
│   └── router.ts
├── config
│   ├── config.default.ts
│   ├── config.local.ts
│   ├── config.prod.ts
│   └── plugin.ts
├── test
│   └── **/*.test.ts
├── typings
│   └── **/*.d.ts
├── README.md
├── package.json
├── tsconfig.json
└── tslint.json

Controller

// app/controller/home.ts
import { Controller } from 'egg';

export default class HomeController extends Controller {
  public async index() {
    const { ctx, service } = this;
    const page = ctx.query.page;
    const result = await service.news.list(page);
    await ctx.render('home.tpl', result);
  }
}

Router

// app/router.ts
import { Application } from 'egg';

export default (app: Application) => {
  const { router, controller } = app;
  router.get('/', controller.home.index);
};

Service

// app/service/news.ts
import { Service } from 'egg';

export default class NewsService extends Service {
  public async list(page?: number): Promise<NewsItem[]> {
    return [];
  }
}

export interface NewsItem {
  id: number;
  title: string;
}

Middleware

// app/middleware/robot.ts

import { Context } from 'egg';

export default function robotMiddleware() {
  return async (ctx: Context, next: any) => {
    await next();
  };
}

因为 Middleware 定义是支持入参的,第一个参数为同名的 Config,如有需求,可以用完整版:

// app/middleware/news.ts

import { Context, Application } from 'egg';
import { BizConfig } from '../../config/config.default';

// 注意,这里必须要用 ['news'] 而不能用 .news,因为 BizConfig 是 type,不是实例
export default function newsMiddleware(options: BizConfig['news'], app: Application) {
  return async (ctx: Context, next: () => Promise<any>) => {
    console.info(options.serverUrl);
    await next();
  };
}

Extend

// app/extend/context.ts
import { Context } from 'egg';

export default {
  isAjax(this: Context) {
    return this.get('X-Requested-With') === 'XMLHttpRequest';
  },
}

// app.ts
export default app => {
  app.beforeStart(async () => {
    await Promise.resolve('egg + ts');
  });
};

Config

Config 这块稍微有点复杂,因为要支持:

  • 在 Controller,Service 那边使用配置,需支持多级提示,并自动关联。
  • Config 内部, config.view = {} 的写法,也应该支持提示。
  • config.{env}.ts 里可以用到 config.default.ts 自定义配置的提示。
// app/config/config.default.ts
import { EggAppInfo, EggAppConfig, PowerPartial } from 'egg';

// 提供给 config.{env}.ts 使用
export type DefaultConfig = PowerPartial<EggAppConfig & BizConfig>;

// 应用本身的配置 Scheme
export interface BizConfig {
  news: {
    pageSize: number;
    serverUrl: string;
  };
}

export default (appInfo: EggAppInfo) => {
  const config = {} as PowerPartial<EggAppConfig> & BizConfig;

  // 覆盖框架,插件的配置
  config.keys = appInfo.name + '123456';
  config.view = {
    defaultViewEngine: 'nunjucks',
    mapping: {
      '.tpl': 'nunjucks',
    },
  };

  // 应用本身的配置
  config.news = {
    pageSize: 30,
    serverUrl: 'https://hacker-news.firebaseio.com/v0',
  };

  return config;
};

简单版:

// app/config/config.local.ts
import { DefaultConfig } from './config.default';

export default () => {
  const config: DefaultConfig = {};
  config.news = {
    pageSize: 20,
  };
  return config;
};

备注:

  • TS 的 Conditional Types 是我们能完美解决 Config 提示的关键。
  • 有兴趣的可以看下 egg/index.d.ts 里面的 PowerPartial 实现。
// {egg}/index.d.ts
type PowerPartial<T> = {
  [U in keyof T]?: T[U] extends {}
    ? PowerPartial<T[U]>
    : T[U]
};

Plugin

// config/plugin.ts
import { EggPlugin } from 'egg';

const plugin: EggPlugin = {
  static: true,
  nunjucks: {
    enable: true,
    package: 'egg-view-nunjucks',
  },
};

export default plugin;

Typings

该目录为 TS 的规范,在里面的 \*\*/\*.d.ts 文件将被自动识别。

  • 开发者需要手写的建议放在 typings/index.d.ts 中。
  • 工具会自动生成 typings/{app,config}/\*\*.d.ts ,请勿自行修改,避免被覆盖。(见下文)

现在 Egg 自带的 d.ts 还有不少可以优化的空间,遇到的同学欢迎提 issue 或 PR。


开发期

ts-node

egg-bin 已经内建了 ts-node ,egg loader 在开发期会自动加载 \*.ts 并内存编译。

目前已支持 dev / debug / test / cov

开发者仅需简单配置下 package.json

{
  "name": "showcase",
  "egg": {
    "typescript": true
  }
}

egg-ts-helper

由于 Egg 的自动加载机制,导致 TS 无法静态分析依赖,关联提示。

幸亏 TS 黑魔法比较多,我们可以通过 TS 的 Declaration Merging 编写 d.ts 来辅助。

譬如 app/service/news.ts 会自动挂载为 ctx.service.news ,通过如下写法即识别到:

// typings/app/service/index.d.ts
import News from '../../../app/service/News';

declare module 'egg' {
  interface IService {
    news: News;
  }
}

手动写这些文件,未免有点繁琐,因此我们提供了 egg-ts-helper 工具来自动分析源码生成对应的 d.ts 文件。

只需配置下 package.json :

{
  "devDependencies": {
    "egg-ts-helper": "^1"
  },
  "scripts": {
    "dev": "egg-bin dev -r egg-ts-helper/register",
    "test-local": "egg-bin test -r egg-ts-helper/register",
    "clean": "ets clean"
  }
}

开发期将自动生成对应的 d.tstypings/{app,config}/ 下,请勿自行修改,避免被覆盖。

后续该工具也会考虑支持 js 版 egg 应用的分析,可以一定程度上提升 js 开发体验。

Unit Test && Cov

单元测试当然少不了:

// test/app/service/news.test.ts
import * as assert from 'assert';
import { Context } from 'egg';
import { app } from 'egg-mock/bootstrap';

describe('test/app/service/news.test.js', () => {
  let ctx: Context;

  before(async () => {
    ctx = app.mockContext();
  });

  it('list()', async () => {
    const list = await ctx.service.news.list();
    assert(list.length === 30);
  });
});

运行命令也跟之前一样,并内置了 错误堆栈和覆盖率 的支持:

{
  "name": "showcase",
  "scripts": {
    "test": "npm run lint -- --fix && npm run test-local",
    "test-local": "egg-bin test -r egg-ts-helper/register",
    "cov": "egg-bin cov -r egg-ts-helper/register",
    "lint": "tslint ."
  }
}

Debug

断点调试跟之前也没啥区别,会自动通过 sourcemap 断点到正确的位置。

{
  "name": "showcase",
  "scripts": {
    "debug": "egg-bin debug -r egg-ts-helper/register",
    "debug-test": "npm run test-local -- --inspect"
  }
}

部署

构建

  • 正式环境下,我们更倾向于把 ts 构建为 js ,建议在 ci 上构建并打包。

配置 package.json :

{
  "egg": {
    "typescript": true
  },
  "scripts":  {
    "start": "egg-scripts start --title=egg-server-showcase",
     "stop": "egg-scripts stop --title=egg-server-showcase",
     "tsc": "ets && tsc -p tsconfig.json",
     "ci": "npm run lint && npm run cov && npm run tsc",
     "clean": "ets clean"
  }
}

对应的 tsconfig.json :

{
  "compileOnSave": true,
  "compilerOptions": {
    "target": "es2017",
    "module": "commonjs",
    "strict": true,
    "noImplicitAny": false,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "charset": "utf8",
    "allowJs": false,
    "pretty": true,
    "noEmitOnError": false,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "strictPropertyInitialization": false,
    "noFallthroughCasesInSwitch": true,
    "skipLibCheck": true,
    "skipDefaultLibCheck": true,
    "inlineSourceMap": true,
    "importHelpers": true
  },
  "exclude": [
    "app/public",
     "app/web",
    "app/views"
  ]
}

注意:

  • 当有同名的 ts 和 js 文件时,egg 会优先加载 js 文件。
  • 因此在开发期, egg-ts-helper 会自动调用清除同名的 js 文件,也可 npm run clean 手动清除。

错误堆栈

线上服务的代码是经过编译后的 js,而我们期望看到的错误堆栈是指向 TS 源码。
因此:

  • 在构建的时候,需配置 inlineSourceMap: true 在 js 底部插入 sourcemap 信息。
  • egg-scripts 内建了处理,会自动纠正为正确的错误堆栈,应用开发者无需担心。

具体内幕参见:


插件/框架开发指南

指导原则:

  • 不建议使用 TS 直接开发插件/框架,发布到 npm 的插件应该是 js 形式。
  • 当你开发了一个插件/框架后,需要提供对应的 index.d.ts
  • 通过 Declaration Merging 将插件/框架的功能注入到 Egg 中。
  • 都挂载到 egg 这个 module,不要用上层框架。

插件

可以参考 egg-ts-helper 自动生成的格式

// {plugin_root}/index.d.ts

import News from '../../../app/service/News';

declare module 'egg' {

  // 扩展 service
  interface IService {
    news: News;
  }

  // 扩展 app
  interface Application {

  }

  // 扩展 context
  interface Context {

  }

  // 扩展你的配置
  interface EggAppConfig {

  }

  // 扩展自定义环境
  type EggEnvType = 'local' | 'unittest' | 'prod' | 'sit';
}

上层框架

定义:

// {framework_root}/index.d.ts

import * as Egg from 'egg';

// 将该上层框架用到的插件 import 进来
import 'my-plugin';

declare module 'egg' {
  // 跟插件一样拓展 egg ...
}

// 将 Egg 整个 export 出去
export = Egg;

开发者使用的时候,可以直接 import 你的框架:

// app/service/news.ts

// 开发者引入你的框架,也可以使用到提示到所有 Egg 的提示
import { Service } from 'duck-egg';

export default class NewsService extends Service {
  public async list(page?: number): Promise<NewsItem[]> {
    return [];
  }
}

其他

TypeScript

最低要求 2.8+ 版本,依赖于新支持的 Conditional Types ,黑魔法中的黑魔法。

$ npm i typescript tslib --save-dev
$ npx tsc -v
Version 2.8.1

VSCode

由于 VSCode 自带的 TypeScript 版本还未更新,需手动切换:

F1 -> TypeScript: Select TypeScript Version -> Use Workspace Version 2.8.1

之前为了不显示编译后的 js 文件,会配置 .vscode/settings.json ,但由于我们开发期已经不再构建 js,且 js 和 ts 同时存在时会优先加载 js,因为 建议「不要」配置此项。

// .vscode/settings.json
{
  "files.exclude": {
    "**/*.map": true,
    // 光注释掉 when 这行无效,需全部干掉
    // "**/*.js": {
    //  "when": "$(basename).ts"
    // }
  },
  "typescript.tsdk": "node_modules/typescript/lib"
}

package.json

完整的配置如下:

{
  "name": "hackernews-async-ts",
  "version": "1.0.0",
  "description": "hackernews showcase using typescript && egg",
  "private": true,
  "egg": {
    "typescript": true
  },
  "scripts": {
    "start": "egg-scripts start --title=egg-server-showcase",
    "stop": "egg-scripts stop --title=egg-server-showcase",
    "dev": "egg-bin dev -r egg-ts-helper/register",
    "debug": "egg-bin debug -r egg-ts-helper/register",
    "test-local": "egg-bin test -r egg-ts-helper/register",
    "test": "npm run lint -- --fix && npm run test-local",
    "cov": "egg-bin cov -r egg-ts-helper/register",
    "tsc": "ets && tsc -p tsconfig.json",
    "ci": "npm run lint && npm run tsc && egg-bin cov --no-ts",
    "autod": "autod",
    "lint": "tslint .",
    "clean": "ets clean"
  },
  "dependencies": {
    "egg": "^2.6.0",
    "egg-scripts": "^2.6.0"
  },
  "devDependencies": {
    "@types/mocha": "^2.2.40",
    "@types/node": "^7.0.12",
    "@types/supertest": "^2.0.0",
    "autod": "^3.0.1",
    "autod-egg": "^1.1.0",
    "egg-bin": "^4.6.3",
    "egg-mock": "^3.16.0",
    "egg-ts-helper": "^1.5.0",
    "tslib": "^1.9.0",
    "tslint": "^4.0.0",
    "typescript": "^2.8.1"
  },
  "engines": {
    "node": ">=8.9.0"
  }
}

高级用法

装饰器

通过 TS 的装饰器,可以实现 依赖注入 / 参数校验  / 日志前置处理 等。

import { Controller } from 'egg';

export default class NewsController extends Controller {
  @GET('/news/:id')
  public async detail() {
    const { ctx, service } = this;
    const id = ctx.params.id;
    const result = await service.news.get(id);
    await ctx.render('detail.tpl', result);
  }
}

目前装饰器属于锦上添花,因为暂不做约定。
交给开发者自行实践,期望能看到社区优秀实践反馈,也可以参考下:egg-di

友情提示:要适度,不要滥用。

tegg

未来可能还会封装一个上层框架 tegg,具体 RFC 还没出,还在孕育中,敬请期待。

名字典故:typescript + egg -> ts-egg -> tea egg -> 茶叶蛋

Logo:image.png | left | 225x225


写在最后

早在一年多前,阿里内部就有很多 BU 在实践 TS + Egg 了。

随着 TS 的完善,终于能完美解决我们的开发者体验问题,也因此才有了本文。

本来以为只需要 2 个 PR 搞定的,结果变为 Hail Hydra,好长的 List:[RFC] TypeScript tool support

终于完成了 Egg 2.0 发布时的一大承诺,希望能通过这套最佳实践规范,提升社区开发者的研发体验。

关于在spa中angula-ui-router的路由问题

1)业务描述:

打算把微信服务号的部分页面用SPA模式重构下;

2)目前问题:

A:因为只是要把部分页面重构为SPA,所以就涉及到这个SPA的root或者说base href(应该是锚点)怎么定?

B:其实重构的主要是把原来的a标签中的链接,换成ui-sref的形式;原来我的a标签是一个从后台传过来的具体的URL(http://rostname/wx/consultant/home/p/QzkwODVGWWlTRms9/oeOGnuEEc4g5bj2wMaSRJxjx9b_k/939b888800bf4797ba5583dd7aba2463bffcd4c8/0/),

那么现在我该怎么设置这个ui-sref?

怎么设置$stateProvider.state中的各个值;url,templateUrl等。

Grunt+Livereload 搭建本地前端开发环境

要解决什么问题?

  • 前端开发时,经常需要把静态文件映射成web服务,传统的做法是丢到apache,但太重太不友好了。
  • 开发angular的时候,官方的chrome插件对file:///的支持不好,所以最好在web浏览器里面。
  • 然后还有livereload -- 节省你的F5

主要思路

  • 用nodejs+connectjs搭建静态web服务
  • grunt来做脚本
  • livereload来通知文件变更(不需要chrome livereload插件)
  • yeoman angular自带的grunt脚本太旧太重了,所以自己写个。

再具体点:

  1. grunt-contrib-connect负责启动web服务
  2. connect-livereload负责给middleware,动态在html底部加一条livereload的js
  3. grunt-contrib-watch监控文件变化并通知

具体步骤

安装依赖

  1. 安装nodejshttp://nodejs.org
  2. 安装gruntjs (http://gruntjs.com/) :npm install -g grunt-cli
  3. 初始化package.json: 在项目根目录下,命令行执行npm init,一路回车。
  4. 安装依赖模块: 在项目根目录下,命令行执行npm install --save-dev grunt matchdep grunt-contrib-connect grunt-contrib-watch connect-livereload grunt-open

在根目录放置Gruntfile.js

  1. 注意首字母大写
  2. 修改src:'src/app/'为你的源码目录
  3. 若端口冲突则修改port
/**
 * 自动化脚本定义
 */
module.exports = function (grunt) {
  'use strict';

  //load all grunt tasks
  require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

  //define tasks
  grunt.registerTask('server', ['connect:server', 'open:server', 'watch:server']);

  //env cfg
  var pkg = grunt.file.readJSON('package.json');
  var cfg = {
    src: 'src/app/',
    // Change 'localhost' to '0.0.0.0' to access the server from outside.
    serverHost: '0.0.0.0',
    serverPort: 9000,
    livereload: 35729
  };  

  //grunt config
  grunt.initConfig({
    //======== 配置相关 ========
    pkg: pkg,
    cfg: cfg,

    //======== 开发相关 ========
   //开启服务
    connect: {
      options: {
        port: cfg.serverPort,
        hostname: cfg.serverHost,
        middleware: function(connect, options) {
          return [
            require('connect-livereload')({
              port: cfg.livereload
            }),
            // Serve static files.
            connect.static(options.base),
            // Make empty directories browsable.
            // connect.directory(options.base),
          ];
        }
      },
      server: {
        options: {
          // keepalive: true,
          base: cfg.src,
        }
      }
    },

    //打开浏览器
    open: {
      server: {
        url: 'http://localhost:' + cfg.serverPort
      }
    },

    //监控文件变化
    watch: {
      options: {
        livereload: cfg.livereload,
      },
      server: {
        files: [cfg.src + '/**'],
        // tasks: [''],
      },
    }
  });
};

开始开发吧~

  • 项目根目录下执行 grunt server
  • 将会自动弹出浏览器,访问 localhost:9000 ,如果你端口冲突,在Gruntfile.js里面修改。
  • 试着修改你的源码,保存,然后浏览器就自动刷新了。(别习惯性的按F5哈)
  • 注意:访问的文件必须是HTML,并且有body标签,否则不会插入livereload.js

服务器部署npm下载egg-scripts 出错

node V8.4.0
npm V5.3.0
Linux

hi。大神,您好,向您咨询个问题
服务器部署时候,"egg-scripts": "2.5.0"已经制定版本,但是部署时候还是报错,报npm ERR! egistry.npm.taobao.org/egg-scripts/download/egg-scripts-1.1.1.tgz"}," 为啥没有选择2.5.0的egg-scripts版本?最后我尝试用 package-lock.json 文件锁定版本,但还是报这个错误!

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.