小马的大前端之路——Node.js初探

欢迎关注富途web开发团队javascript

夜已深,愚人节有没有对中意的女生表白啊。哈哈。。。css

小编在这里先祝福你们。html

这个周末,原本想把最近尚未整理的几篇文章这里好发给你们的。无奈小编周末有点晕,还没整理好。多是周六晚上烤串吃多了。前端

木屋烧烤

最近有一篇是关于module(模块化) & babel的文章不过还没整理出来。那先来一篇node.js热热身。这篇文章里面的不少东西,如今富途前端仍是在用,对后面你们去理解富途前端因该会有帮助。vue

有个好消息是,富途的前端框架已经慢慢的被vue替代了(原来使用的是JQ , angular.js)。接入层node.js也在推动中,对外的服务指日可待。相信喜欢vue.js ,node.js的小伙伴已经蠢蠢欲动了。java

没错,小编就是富途node.js服务推进者之一。node

在富途作前端,不只须要写前端,还须要写node.js服务的,这个你们不用想太多。react

那仍是回归正题,做为第一个吃螃蟹的小编,是如何在工做中学习运用node.js的吧。webpack

注意:如今富途的前端框架已经往vue.js迁移。但不会影响你们理解阅读。nginx

原文章来源于富途WEB博客: 原文连接

正文

一次偶然的机会让我有幸跨越浏览器的鸿沟来真真切切的体验一次Node.js。

首先,我想说:“很荣幸在经历了2个月的努力,第一个Node.js项目落地了”。整个项目作下来,仍是算比较顺畅的。

事情很简单:Node.js作的是接入层。

事出有因

前端的技术革新是突飞猛进的,前端工程化已经离不开Node.js。如今大多数的项目使用的是先后端分离的架构,后端提供接口前端经过接口数据进行数据渲染。可是如今前端的代码逻辑愈来愈复杂,场景也愈来愈多。这套架构是否适合全部的应用场景值得考虑了。大前端的出现,就是一种尝试吧。试图经过Node.js接入来应对各类应用场景。

架构图

无论是我的仍是团队,技术革新是必须的。如今咱们团队面临的问题就是如此,因此必须有人迈出这一步。而我也很幸运的成为第一个吃螃蟹的人。

始做俑者

无论什么技术,无论怎样的优秀,它的运用与否都是要通过慎重考虑的。但,总不能都不用吧。那怎么办呢。找项目试点呗,线上项目运行的好好的确定不能重构,并且人力紧张啊。只能找新项目了。刚巧,公司须要作新的项目,本觉得按老路子先后分离作。可忽然有一天...

组长说:“团队不是要进行技术选型吗?看这个项目使用Node.js作接入层可不可行?“。

通过慎重考虑,我回答说:“能够没问题。”。(管他3721,应了再说。😄)

借我老大的一句话:“技术这东西不落地,说了也白说”。

背景:其实团队对Node.js一直都保持着高度的关注,包括我。以前我一直都有在对Node.js的源码进行解读和研究。基础架构组也一直在进行Node.js技术框架进行调研,但愿打造一套适用于团队开发的集成项目框架。

因此我相信:机会老是会照顾有准备的人的。

就这样个人Node.js之旅就开始了。

万事开头难

虽然我平时可能每天都会用Node.js跑命令,写各类npm包,甚至还写过一些本身的项目。可是要真正的用Node.js来真正开发项目仍是有压力的。由于这种项目技术架构下要求我操心的东西变多了。平时的时候可能我只要写一些前端逻辑代码,作作前端工程化。可是这种架构下,要求我必须去学习和应用我不熟悉的东西。

我大体列了一些大的方向:

  • 1.Node.js接入层的整体架构是怎样的?
  • 2.前端技术用什么?
  • 3.前端工程化如何作?
  • 4.项目如何根据不一样的环境(常有的环境:开发,测试,正式)运行?
  • 5.前端自动化怎么搞?
  • 6.单元测试?
  • 7.编码风格?
  • 8.Node.js如何和服务端对接?
  • 9.日志,上报,登陆服务接入,权限校验等等我应该怎么作?
  • 10.项目如何发布上线?
  • 11.上线了如何保证服务稳定?
  • 12.如何debug问题?

可能还有不少不少须要处理的问题可是这已经能够看出一下端倪了。瞬间感受我懂的只有冰山一角。代码码的再漂亮感受也无力。要求的再也不是单一的编码能力,而是大局观,思惟角度的转变。

但无论怎样,新建git仓库开始搞呗。

如何获得一个合适的项目架构

这个确实是个问题,架构设计的合不合理。会影响到后期编码是否能够作到快速开发,还会影响后期的功能迭代和维护。

那么问题来了,我是预先设计仍是预先编码?

这里我选择了先编码,而后重构。

背景:由于上文已经说过,基础架构组已经有一个简单的Node.js集成框架,它是不完整的,可是它够简单。也就是说我在这上面重构出本身的项目架构是彻底没有问题的。

你可能会以为仍是要预先设计啊?

说的是侧重点不同,侧重于编码实现,将这个项目跑起来,而后经过重构去寻找出合适的项目架构。

对于先编码仍是设计这个问题我借用重构里面的是一句话:

“重构改变了预先设计的角色。若是没有重构,你就必须保证预先作出的设计是正确无误,这压力太大了。这意味着若是未来须要对原始设计作任何修改,代价都将很是昂贵。所以你须要把更多的精力放在预先设计上,以免往后的修改。若是选择重构,问题的重点就转变了。你任然作预先设计,可是没必要必定要找出证正确的解决方案,此刻的你只须要获得一个合理的解决方案就够了。“ --摘自《重构-改善既有代码的设计》

把一个简单的解决方法重构成一个灵活的解决方法有多难?答案是:“至关容易”。 --摘自《重构-改善既有代码的设计》

实在不明白我推荐你去看看《重构-改善既有代码的设计》这本书。

因此我将侧重点放在了预先编码上,让后在整个项目demo跑起来以后再去寻找合适的架构。一个合理的架构体系就是把代码放到它应该出现的位置上去。代码是具备流失性的,就比如一个房间历来不整理的话,就会变的脏乱不堪。重构就是将代码再次整理将它放回原位。

目录图

技术框架选型考虑

技术框架的选择会影响着项目的整体架构,编码,产出效益,以及后期人员维护的成本。

首先我想说:“无论前端仍是后端用什么框架我以为仍是要站在团队的角度上去考虑这个问题,毕竟这不是我的的项目。总不能说我不在就没人能维护这个项目吧”。

Node.js后端

koa2。为何没有使用koa或者express等框架,或者为何团队不本身开发。

Node.js v8LTS 已经快要来临。koa已经升级到了koa2版本,没有必要再用旧的express太老了。koa2在这两年已经锋芒毕露,现阶段团队没有必要花费不少的人力去搞一套本身的框架,能够转变思惟在koa2的基础上作一个集成的适合团队项目使用的框架。

基于这个基础架构团队使用koa2做为主框架使用在现阶段是最合适的。特别是在Node.js v7.6+ 原生支持了asyncawait语法。

前端框架

jQuery的王朝已经渐渐被瓦解。angular.js,react和vue三足鼎立的时代已经到来。再次基于团队的现状,选择了最有优点的angular.js v1.x。

在这里我并无说其余框架很差的意思,彻底是基于团队现状的考虑,以及当前框架是否能够帮助我高效的完成开发的一种考虑。假若有一天我以为angular.js已经不适合现阶段项目开发需求,我会责无旁贷的提出个人疑问。

好比:项目须要咱们考虑加速页面渲染时,要考虑服务器渲染;服务器压力山大时,考虑先后端分离。同构做为最合适的编码方式react和vue都是不错的选择。

框架没有对与错,只有合不合适。

webpack2 做为当红炸子鸡,我也是优先考虑的。至于为何没有选webpack3嘛。。。

实际上是这样的,我也有实际的去使用webpack3来作过测试,就是这个项目。个人衡量标准就是压缩要比如今的要小。最后没有达到预期效果因此没有进行合并。

gulp 工做流处理,没毛病。这里可能会有的让人疑惑,为何使用了webpack2 还要使用gulp?为何2个都要用?

其实对于这2个组件,它们没有绝对的对立关系。在这里它们是相辅相成的。

总的前端框架:angular.js v1.x + webpack2 + gulp。

babel用来编译前端代码。

项目使用的主要框架,如图:

主要框架图

前端工程化

项目的整体架构和前端技术框架的选型势必会对前端工程化产生深远的影响。前端代码放到哪里,webpack打包如何作,产出文件放到哪里。gulp须要作哪些事情,多仍是少,烦不烦琐。这种种问题都会对你项目的架构作出挑战。这也就是我为何先编码而后经过重构来调整项目架构的缘由之一。假如你预先就把项目的整体架构规定死了,那么后期你的编码就会想尽办法的去套这个项目架构,写出来的代码可想而知——必定是不尽人意的。

那么第一个问题就来了。

本身编写的anglaur.js部分的源码放到哪里

对于这个问题,在使用Node.js开发初期,我就对基础的架构作出了建议:前端源码不能放到服务器静态资源目录。只有打包后的文件才会放到静态资源文件目录,除非该文件能够直访问。

这就意味着,我须要寻找一个文件目录来放置前端源代码。最合理的位置就是于服务器目录平级放置。

webpack

经过webpack的编译打包,将文件保存到静态资源目录。我这里把因此和代码相关的打包和编译任务都交给了webpack,其中还包含公共文件的提取,版本控制,压缩,以及模版文件注入。

webpack

如何进行版本控制

版本控制用的比较多的就2种:基于文件和基于hash。

基于文件就比如,每次打包的时候都会生成不一样文件名的文件。有利于在线上跑多个版本的功能。

基于hash就意味着线上这个功能的文件永远就只有一个,没法进行全量灰度。

这里有个问题就是:基于文件的版本控制,难点就在于打包后的.js.css文件名是不可控的,因此,并不能把引入的js或css文件路径写死在html模版文件里面。因此经过webpack打包的时候,我须要指定模版文件是哪个,经过webpack的模版文件注入插件完成js或css文件路径的引入。

其它方式;经过在webpack打包完成以后,将返回值种的hash参数保存下来。这样也能够完成基于文件的版本控制。

gulp的工做流

gulp结合webpack的应用如鱼得水,webpack打包任务是gulp任务流里最重要的一环。考虑到打包编译,都交给webpack作了。那gulp所要作的就是保证前端各个任务正确的执行。包括什么时候执行webpack打包,完成打包之后作什么。

gulp

前端自动化

这里的自动化可能与你在别的地方所说的自动化可能有分歧。这里的前端自动化主要指的是在前端代码如何完成自动化打包编译。其实项目中能够进行自动化的流程有不少,我在项目里接入的是jenkins,主要用来自动完成前端打包编译,而后经过zip命令对webpack打包编译后的全部文件进行打包成.zip文件。由于打包后的文件不入库。

这里有疑惑是正常的。首先为何不把webpack打包后生成的文件归入git版本库?

道理很简单,git版本库里面的任意一个文件产生变化,就会有下一个版本号产生。webpack每次打包编译就势必会产生文件变化,若是把打包文件归入版本库就必须提交文件,从而产生版本号。也就是说我本地提交一次代码到git库后,jenkins会进行打包,而后打包文件又必须提交回git库,这样就至关于每次提交代码否会产生2次提交记录(一次我本身的提交,一次jenkins完成自动化打包后的提交。)。因此为了避免让jenkins完成打包后向git代码库提交文件,所要作的就是把webpack打包后产生的文件都移除版本库。

但问题没有这么简单,webpack打包不归入版本库,发布的时候,这些webpack打包后产生文件怎么发布。这里解决方案就是经过把全部和webpack打包相关的文件用zip命令打包成一个${commitId}.zip包(commitId 是git每次提交参数的能够经过bash获取:commitId=$(git rev-parse HEAD))。这样发布的时候就能够经过commitId找到${commitId}.zip这个压缩包,而后解压它到指定位置便可。

为何有2个打包任务?

第一次是webpack打包,前端代码须要打包编译。第二次是文件打包,发布须要,缘由很就是webpack打包文件不入库的解决方法。

因此要求团队中必须会搭建而且有使用过jenkins,这个工具对团队的帮助是很是大的,预先打包文件并缓存,比在发布项目的时候再进行打包要好不少。能够预先发现打包问题及时进行补救,以避免发布时打包出现问题而影响发布进度和线上项目的正常运行。

jenkins

git仓库支持添加hooks。因此能够在git库里添加触发事件。让jenkins自动完成打包。

假若有一天,我须要写单元测试的时候,也能够试着让jenkins帮我跑自动化测试了。这算是我回答了单元测试的问题吗?哈哈哈哈哈哈哈。。。。。。

前端问题基本解决了,如今问题抛到了服务端。

Node.js服务端运行环境配置

写个项目,要跑起来很简单,个人项目入口文件是server/index.js。经过执行以下命令就能够启动:

node server/index.js
复制代码

但有时候,环境并无我想的那么简单。由于项目须要针对不一样的环境运行,因此必需对不一样的运行环境使用不一样的配置文件。这样就须要我在启动Node.js服务的时候,必须携带不一样的参数。因此要求我在编码的时候尽量的作到环境参数的配置化——牵涉到与执行环境有关的参数尽可能进行配置化。

启动

Node.js接入层服务的接入,权限的校验

其实对于一个小白来讲,很担忧的是我如何才能在Node.js里面往真正的服务器发起request请求。我项目站点的登陆服务鉴权如何去作,以及用户登陆了,有没有权限去访问都是个问题。

http服务的接入

经过http模块发起requset请求。其实开始的时候我也是一脸茫然的,如何在接入层请求后端服务,可想而知这是以前做为前端的我历来没有考虑过的。如今回想起来就那么回事。有些事情想着可能很复杂,真正的作起来就好像有种:山重水复疑无路,柳暗花明又一春。的感受。

服务接入

Node.js接入层请求后端服务简单的代码实现:

exports.example = async (ctx)=>{
  let options = {
    port: 80,
    hostname: 'www.test.com',
    method:'GET',
    path:'/api/getuser?token=document.cookie.token'
  };
  let getData = function (){
    return new Promise((resolve , reject)=>{
      let request = http.request(options , (socket)=>{
        let data = '';
        console.log('status: ' , socket.statusCode , socket.headers);
        socket.on('data' , (chunk)=>{
          data += chunk;
        });
        socket.on('end' , ()=>{
          console.log('server call back get data: ' , data);
          return resolve(data);
        });
        socket.on('error' , (e)=>{
          return reject(data);
        });
      });
      request.end();
    });
  }
  ctx.body = await getData();
}
复制代码

这里我没有考虑https的方式,由于https是创建在SSL/TLS之上的,也就是说,须要有私钥和公钥和CA证书才行。CA证书虽然说能够本身颁发但仍是得本机自行安装才有效。对https本身颁发CA证书感兴趣的能够看看这篇文章:HTTPS自签发CA证书

后端服务器(PHP/JAVA...)须要作的就是根据请求参数是否合法已经齐全,而后验证调用者是否有权限使用该功能。这样的案例比比皆是,好比使用第三方服务。

小到Number校验

有可能最简单的参数校验都不知道如何校验。这跟javascript语言以及前端的思惟方式有关。我开始的时候也是这样,感受写起代码来怪怪的。

其实这是一个简单的例子,在前端检验一个Number类型的值是否是有效,我通常是经过:

num = typeof num === 'number' && num === num && num !== Infinity ? num : 0;
复制代码

这种思路和逻辑放在前端彻底是没有问题的,可是在Node.js接入层这么写感受很尴尬。因此要转变个人思惟方式:

num = Number.isFinite(num) ? num : 0;
复制代码

小到参数的校验,我都要认真的考虑。是时候改变本身的思惟方式了,考虑使用JavaScript原生的方式处理会比本身写好不少。

权限的校验

我并不但愿全部的用户都能访问这个项目,即便他已经登陆了也不行。这就是我要解决的问题。

权限

权限管理在这里就显得极其重要了。最好的方式就是把权限相关的功能进行服务化。


使命感受才刚刚开始!!!!!

项目的部署上线

能够说我对项目部署和运维基本上是没有经验。可是有一点就是项目上线后的可用率是必需要保证的。不能由于一点小问题,就让服务挂掉,而后还要人屁颠屁颠的从新手动重启吧。也不能说服务器断电了,重启后也要手动启动吧。这一些列的问题都是必须解决的。

pm2

很高效的开发完成了项目后,其实项目的真正使命才要刚刚开始,如何保证服务在线上稳定的运行,保证高可用率。这就须要借助其它组件来完成了。使用pm2管理确实是个好的方案。

  1. 首先经过npm install -g pm2进行安装。

  2. 安装完成了以后,就能够在项目中进行pm2相关配置。

案例:

//test.config.js
'use strict';
//pm2配置文件
module.exports = {
    apps:[{
        name : 'test',
        script: './server/index.js',//应用入口
        cwd: './',
        instances : 1,
        watch : ['server'],
        env: {
            'NODE_ENV': 'development',
        },
        env_production: {
            'NODE_ENV': 'production',
        },
        exec_mode : 'cluster',
        source_map_support : true,
        max_memory_restart : '1G',
        //日志地址
        error_file : '/data/logs/pm2/test_error.log',
        out_file : '/data/logs/pm2/test_access.log',
        listen_timeout : 8000,
        kill_timeout : 2000,
        restart_delay : 10000, //异常状况
        max_restarts : 10
    }]
};
复制代码
  1. 而后就能够经过命令启动:
pm2 start test.config.js
复制代码

nginx

Nginx 是俄罗斯人编写的十分轻量级的 HTTP 服务器,Nginx,它的发音为“engine X”,是一个高性能的HTTP和反向代理服务器。nginx配置也是必不可少的,80端口就一个,因此我须要nginx进行转发。

例以下面的案例:

upstream test_upstream {
    server 127.0.0.1:6666;
    keepalive 64;
}
server{
    listen 80;
    server_name www.test.com;
    client_max_body_size 10M;
    
    index index.html index.htm;
    error_log /data/nginx/log/error_www.test.com.log;
    access_log /data/nginx/log/access_www.test.com.log combined;
 
    location / {
        proxy_store off;
        proxy_redirect off;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Remote-Host $remote_addr;
        proxy_set_header X-Nginx-Proxy true;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_http_version 1.1;
        proxy_pass http://test_upstream/;
        proxy_read_timeout 60s;
    }
}
复制代码

项目启动的端口是本机的6666端口,可是我不可能说访问www.test.com的时候后面还带着端口号吧。这个时候就是nginx发挥做用的时候,访问域名不带端口默认使用80端口,由nginx作反向代理到我服务6666端口。

这里有一点post请求时client_max_body_size参数的设定直接会影响data的大小。

日志,上报,运营维护

项目的健康与否,都会在日志和上报中体现。我只须要天天看看日志,看看视图就能够对当天项目的运行状况作一个大体的了解。若是没有这些辅助的功能,两眼一抹黑,发生啥事都不知道。

编码风格

编码风格方面遵循eslint的语法标准。使用了最新的async/awaitimport语法。

编码

debug代码

Node.js已经支持在chrome中直接调试Node.js代码,只要在启动项目的时候添加--inspact参数。

node --inspect server/index.js
复制代码

debug

复制上面红框的url连接到chrome里面打开,而后点击start后,再访问页面,须要暂停的时候能够点击stop,进行代码分析。

总结

做为一个初学者,我只能说Node.js在作接入层上,确实是能够作到如鱼得水,关键点就是契机。抛开Node.js接入层,前端的工程化是彻底能够作的。可是服务器同构渲染是没有办法作到的,除非与后端同窗配合;使用Node.js接入层,那么前端在处理一些棘手的问题时就会游刃有余,并且后端服务会获得更深一层的保护,不至于说后端服务直面攻击,由于多了一层Node.js接入层在前面。

相关文章
相关标签/搜索