技术, Docker, 前端

前端跨项目组件化及基于Docker的快速部署方案

2018年04月14日凌晨 天气🌧

最近静下心来写了几个项目,花了些时间重新整理了整套组件化方案和部署方案,记录一下。

跨项目组件化

前端的组件化不用多说了,发展到现在,无论是React的还是Vue,都提供了相当方便的组件化实现。在日常项目中,有些组件其实是可以跨多个项目使用的,将这些组件抽离出来作为单独项目,并复用到其他项目中去,一来可以避免重复造轮子,加快开发速度,二来维护效率也高,一些bugfix或者新特性直接在组件中更新,项目中只需要更新引用版本号即可,方便快捷。

跨项目的组件化方式也很多,开发阶段可以用npm link,相当于在主项目的node_modules目录中创建了一个链向组件项目的软链,方便是挺方便,但是有几个问题。一是Eslint的目录递归检查是基于最终实际目录的,也就是说虽然Eslint默认排除node_modules目录,但它依然会对该目录中的软链项目进行检查,一旦组件项目的Eslint规则和主项目的Eslint不一致的话,主项目Eslint就没法通过,这个比较蛋疼,就得临时禁用Eslint或者修改组件项目的规则。作为组件项目应该保证少依赖,而且要服务多个项目,没办法保证匹配各个项目的Eslint规则。第二个问题是,通过npm link实现的依赖,不会体现在package.json中,如果通过Docker去部署,在Docker上是不知道你这个软链的,即便能够把软链写进去,在Docker中构建的时候,由于目录问题,也不能保证可以把组件项目文件拷过去。因此,在生产中,npm link这个方案是没办法用的。

之前在国资的时候,采用的是通过组件项目的git地址来定位包,使用起来也很方便。如下:

"dependencies": {
    "example-component": "git+ssh://git@xxx.com/example-component.git#v1.0.0"
}

npm支持通过tag来定位版本,组件项目发布的时候打一个tag,对应的在项目中更新最后的版本号重新安装就可以完成升级了。由于之前没有用Docker部署,所以也没发现有什么问题。用了Docker的话,就会有些小问题。一般为了精简,都会采用基于Alpine的基础镜像,我目前的前端项目都是基于node:8-alpine来构建的。要知道,Alpine镜像本身只有4.8M,node:8-apline也只有20几兆,非常精简。但是通过上面的这种方式,需要依赖git,而Alpine显然是没有安装git的,也没有必要为了部署专门去安装一个git。

于是便有了第三种方案,基于Nexus的npm repository方案。把包发布到npm上不太现实,大部分公司项目还是希望私有,和maven一样,Nexus也支持npm。创建一个hosted类型的npm仓库,例如npm-hosted,具体教程自行谷歌。但是我们又不希望给该仓库增加过多的压力,不想把所有npm或者yarn默认的registry改为Nexus,没必要,因为即便改为Nexus,在国内的网络环境,还是proxy到https://registry.npm.taobao.org上去了,而且会在Nexus上留下大量缓存,也经过了两层的下载,我尝试过,很蛋疼,在Nexus没有缓存第一次去下载的时候,还会有很多失败。后来发现npm也支持直接通过tgz文件的方式来引用,这样就好办了。

首先执行npm adduser --registry=https://myregistry.example.com,输入Nexus上具有上传npm包权限的用户名和密码,会在本地记录该用户的登录认证。然后在组件项目的package.json中加入:

"publishConfig": {
    "registry": "https://myregistry.example.com/repository/npm-hosted/"
}

然后执行npm publish,组件项目就会被上传到Nexus了。

在具体项目中,需要用到改组件的时候,在package.json中这样引用:

"dependencies": {
    "vue-footer": "https://myregistry.example.com/repository/npm-hosted/example-component/-/example-component-1.0.0.tgz"
}

或者直接npm install https://myregistry.example.com/repository/npm-hosted/example-component/-/example-component-1.0.0.tgz --save

组件更新通过修改package.json里的版本号,然后npm publish,然后具体项目中修改最后的版本号,重新安装即可。

另外由于这次都是用的最新的组件,也遇到了一个很大的坑。

我的项目都是基于Nuxt.js来搞的,Nuxt.js的框架用起来更爽,ssr性能也比vue原生的要高。有一个问题,组件项目在webpack打包的时候,默认是不支持Nuxt的SSR的,用了vue-style-loader之后,里面会有多个地方用到了document。如果需要同时支持Browser和SSR,需要再建一个SSR的webpack config,将target设为node,并且vue-loaderoptions中需要加入optimizeSSR: false,这个是因为尤大在最近的某个版本中针对SSR做了一些优化,但是在Nuxt的SSR中会有些问题,具体可以参见https://github.com/nuxt/nuxt.js/issues/2565 ,找了很久找到了这个issue,追踪了下,尤大貌似在最近的v2.5.17-beta.0 中已经修复了这个问题,具体等release版发布之后再试下。在Nuxt中,创建一个plugin,直接引用生成的SSR版本的文件即可。

import Vue from 'vue'
import ExampleComponent from 'example-component/dist/ssr.js'

Vue.use(ExampleComponent)

nuxt.config.jsplugins中直接加入'~plugins/components-plugin.js'即可。网上大部分解决方案是引用的时候将组件项目设置为ssr: false,其实是治标不治本,放弃了该组件在服务端的渲染,不可取。

基于Docker的快速部署

使用Docker也快1年了,基本上从开始用上Docker之后,就爱不释手了,大大缩短了发布时间,减少了运维成本。

目前我的项目都是部署在阿里云上,基于阿里云的容器集群方案,前面通过SLB,后面横向部署多台机器。不多说,贴下Dockerfile:

FROM node:8-alpine

WORKDIR /app

COPY package.json /app
COPY yarn.lock /app
RUN npm config set registry https://registry.npm.taobao.org && yarn config set registry https://registry.npm.taobao.org && yarn install
COPY . /app
RUN npm run build

EXPOSE 6002
ENV SERVER_ENV $SERVER_ENV
CMD ["npm", "run", "start"]

记得在.dockerignore文件中把node_modules目录加上,在COPY的时候不进行复制,而是在docker环境中重新获取。

在项目的package.json中配置好deploy命令:

"scripts": {
    "deploy": "docker build -t registry.cn-hangzhou.aliyuncs.com/your-name/example-project:$npm_package_version -t registry.cn-hangzhou.aliyuncs.com/your-name/example-project:latest . && docker push registry.cn-hangzhou.aliyuncs.com/your-name/example-project:$npm_package_version && docker push registry.cn-hangzhou.aliyuncs.com/your-name/example-project:latest"
}

在阿里云的容器镜像服务中建好镜像,便可以部署上去了。之前我是直接通过绑定gitlab,在代码更新后,触发阿里云镜像服务在线构建,但是我们采用了私有的npm仓库的话,就比较麻烦了,还得配置Nexus账号,索性直接在本地构建,上传的网速也不是问题。在阿里云的容器服务中创建好应用,设置好触发器,在镜像更新后触发重新部署,就大功告成了。如果有多台机器,在调度配置中配置好“平滑升级”,会在同一服务的多个容器升级的时候,保证当前一批或者一个容器升级或者更新成功(健康检查成功)之后,再来更新或者升级下一批容器,也就是“滚动发布”。之后只需要npm run deploy,喝杯咖啡☕️,就完成了所有的部署工作了。方便、快捷、不易出错。

最近看了篇文章,里面有句话:“这世界上肯定存在让人上瘾的代码”,提出了“技术多巴胺”这个说法。而此刻的我,就仿佛打了几针“技术多巴胺”一样,很兴奋,一点睡意都没有。

您已成功订阅 HADB.ME
真棒!下一步,完成结账以便解锁 HADB.ME
欢迎回来!您已登录成功。
登录失败,请重试。
操作成功!您的账户已全面激活,现在您有所有内容的权限了。
错误!Stripe 结账失败。
成功!您的账单信息已更新。
错误!账单信息更新失败。