在我们的日常开发中,前端打包后的静态资源都放在哪里呢?有没有放到oss上的呢? 我们在做工业品项目的时候,前端打包后的静态资源存放在oss上,文件夹带版本号,这样每次打包上传不同的版本,后端去抓到最新版本的静态资源作部署测试,上线的时候选择最新版本的文件部署。
听着还不错,那么会遇到这两个问题:
- 因为涉及到多人开发项目,每次需要手动更改版本号再上传。
- 如果有人打包后没有提交package.json文件,那么其他人在打包上传的时候会出现版本不正确,覆盖的情况。 带着问题我们就要着手去解决,以我们的PC端的项目为例,采用的脚手架为codmi(npm.m.jd.com/package/@jd…
目前我们的打包上传的流程是:
standard-version && codmi build && codmi upload
standard-version: 是可以本地更新版本号,增加changelog和创建 git tags,我们暂且使用本地版本号更改递增。
当standard-version改变 package.json文件的 version字段,为最新的版本号,之后执行codmi build,这里在项目的根目录的codmi.config.js配置文件里面设置webpack打包配置,读取到最新的版本: require('./package.json').version
,
然后执行codmi upload上传操作。
这就是遇到开头说的问题,多人开发项目,版本容易错乱。所以在执行standard-version之前我们 oss上拿到最新的版本然后再去改变package.json文件。所以就着手开始npm报的开发吧....
- 首先我们先来看一下在oss上我们保存的前端静态资源目录是怎样的:
在这层目录可以看到,在文件夹ispText下面有很多版本文件夹,在这层之前就是bucket空间,所以我们需要知道oss提供的Api 调取当前目录下所有的文件目录,然后进行比较最大的版本出来。先来看下oss的Api,拿到我们需要的方法。
1.bucket.listObject -- 列出 指定Bucket中的Object信息列表 请求时可以通过一些查询条件来限制返回的结果。
{ Name: 'pro-test',
Prefix: null,
Marker: null,
Delimiter: null,
MaxKeys: 2,
HasNext: true,
Contents:
[ { Key: 'pro-a.txt',
LastModified: 'Mon, 11 Sep 2017 17:47:04 GMT',
ETag: '0cc175b9c0f1b6a831c399e269772661',
Size: 1 },
{ Key: 'pro-b.txt',
LastModified: 'Mon, 11 Sep 2017 17:47:15 GMT',
ETag: '92eb5ffee6ae2fec3ad71c777531578f',
Size: 1 } ],
CommonPrefixes: null }
这是方法返回的结果,Contents就是返回的扁平化数据,这个后面会有讲解。
注意:开始之前,我们还是把npm包放到jnpm上(npm.jd.com/)京东的npm包托管。… npm init 初始化,
npm publish`发包,这里就不多赘述,不过在遇到 发包失败,状态码为403的时候,先看看自己的erp是否正常登陆,还有是不是被锁住了,如果锁住了就重新在erp.jd.com重新登录下。
然后我们再看下npm包的结构:
bin文件夹下的cli.js为入口文件:
#!/usr/bin/env node
const ossVersion = require("../oss.js");
var argv = require("yargs").argv;
ossVersion(argv);
/usr/bin/env就是告诉系统可以在PATH目录中查找。 所以配置#!/usr/bin/env node, 就是解决了不同的用户node路径不同的问题,可以让系统动态的去查找node来执行你的脚本文件。
shelljs 只解决了如何调用 shell 命令,而 yargs 模块能够解决如何处理命令行参数。
npm install --save yargs
argv就是在执行命令的会后拿到的参数,这里我们后面会用到,因为可以传参版本号来之定最新版本。
进入主体内容,在根目录的oss.js文件,导出ossVersion方法,参数为argv,这里面存着执行命令带过来的参数。这里我们在头部初始化所需要的变量和方法。
module.exports = async function ossVersion(argv) {
let directory = ""; //指定目录
let versionArr = []; //读取全部目录的版本
let __setting = {}; //保存ossVersion配置文件参数
//添加原型方法去重
Array.prototype.pushNoRepeat = function () {
for (var i = 0; i < arguments.length; i++) {
var ele = arguments[i];
if (this.indexOf(ele) == -1) {
this.push(ele);
}
}
};
Array.prototype.max = function () {
return Math.max.apply({}, this);
};
Array.prototype.min = function () {
return Math.min.apply({}, this);
};
}
然后,我们是需要比较目录最新版本的,那在这之前我们需要获取到当前的全部目录,在oss上需要实例化new Jss()方法。
new Jss(endpoint, accessKey, secretKey, useHttps)
- endpoint 所要连接的 endpoint 地址
- accessKey 字符串 accessKey
- secretKey 字符串 secretKey
- useHttps string 是否使用 https
我们会在项目的根目录存放json格式配置文件,存放关于 oss bucket等信息。所以默认需要把这个配置文件的参数取出来再实例化。
//读取ossVersion.json 配置文件
let readOssVersion = () => {
let _setting = fs.readFileSync("./ossVersion.json", "utf-8");
return JSON.parse(_setting);
};
__setting = readOssVersion();
//初始化Jss oss api实例
var jss = new Jss( __setting.endpoint , __setting.accessKey ,__setting.secretKey,__setting.useHttps);
bucket.listObject就是真正取目录结构的方法,需要传几个参数:
jss.bucket(bucketName)
.listObject(marker, maxKeys, prefix, delimiter)
.then((res) => {
}
- marker 指定的Object的Key的起始标志
- MaxKeys 指定的Object的数量
- Prefix 指定的Object Key的前缀
- delimiter 指定的Delimiter分组符
这里注意一点:marker参数指的是在查询的时候开始的位置,每次查询数量最大会返回1000条,那么如果几千条的话,当前返回结果的最后一个就是下次开始的marker,前提是返回的数据HasNext为true时才会出现。所以这里需要处理一下:
let marker = "";
if (HasNext && JSON.parse(res).Contents.length > 2) {
marker = JSON.parse(res).Contents[JSON.parse(res).Contents.length - 1].Key;
}
然后请求到的数据可以先存到一个数组里面,然后我们再去循环数组找出版本最大的一个:
所有版本存储到versionArr,循环找出最大值,然后递增,修改package.json文件的参数。
JSON.parse(res).Contents.forEach((item, index) => {
if (item.Key.split("/")[0] == directory) {
versionArr.pushNoRepeat(item.Key.split("/")[1]);
}
});
let arr = versionArr.sort(function (a, b) {
var sources = a.split(".");
var dests = b.split(".");
var maxL = Math.max(sources.length, dests.length);
var result = 0;
for (let i = 0; i < maxL; i++) {
......
}
return result;(-1 or 1)
}
找到最新的版本,就需要修改package.json 文件的参数,这里我们是结合standard-version来做的,所以我们只需要把最新的版本给到standard-version的prebump字段,后面的继续打包上传。
定义writePackageJson方法:
function writePackageJson(cbDataPackage, wholeVersion) {
cbDataPackage["standard-version"]["scripts"][
"prebump"
] = `echo ${wholeVersion}`;
fs.writeFile( "./package.json", JSON.stringify(cbDataPackage, "", "\t"), function (err) {
if (err) console.error(err);
console.log( "package.json文件修改完成,prebump修改为:",cbDataPackage["standard-version"]["scripts"]["prebump"]
);
}
);
}
这样的话,主体的逻辑就已经实现了,下面贴一张整体的流程图。
配置文件
我们可以在项目的根目录放上一个配置文件ossVersion.json,里面放上oss相关的参数。
{
"endpoint": "storage.jd.local",
"accessKey": "rm9RhqAtor1UbGMR",
"secretKey": "YwYKZZgvCTgKQUYgBZyWGDYFrflEeIAdabV3OJLs",
"useHttps": "false",
"bucket": "safequkuaifang-musiclib",
"folderName": "ispText",
"directoryType": "ispText"
}
命令
package.json 文件中 script 增加执行命令 -d 指定名录名称 -v 指定版本(需要手动设置版本)。script 脚本命令行使用:
"start": "codmi start",
"build": "codmi build",
"upload": "codmi upload",
"prepub": "ossVersion && standard-version && codmi build && codmi upload",
"preenv": "ossVersion --d=ispText",
以自己的项目为例:
prepub命令在原来的基础上增加了ossVersion命令,默认会拉去最新的版本号,在执行后面的操作。
preenv则可以针对自己选择要指定的版本号进行创建
codmi配置文件的修改
我们项目使用的是codmi的脚手架,配置文件里面有用到ossUpload插件,就可以和版本控制供给结合使用了。需要注意的一点,在配置文件中,ossUpload的配置需要使用 空间文件夹目录字段,此时,需要动态从我们根目录创建的ossVersion.json中获取,这样的话修改了配置文件也可以指定目录。
folderName = `${ossVersion.directoryType}/${version}`;
在我们的日常开发中总会遇到个各种的难点和困难,那就克服,并且找到好的方法去解决。这次的功能介绍就到这里吧,欢迎各位来交流沟通。