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

凌晨
1891 字
10 分钟
...阅读
...评论
天气🌧

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

跨项目组件化

前端的组件化不用多说了,发展到现在,无论是 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://[email protected]/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,喝杯咖啡 ☕️,就完成了所有的部署工作了。方便、快捷、不易出错。

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

评论区
Copyright © Bean Deng