加速网站访问速度

分析

在发布的时候使用 qiniu nodejs sdk 将编译后的资源全部上传到七牛云,如果使用 history 路由,index.html 得使用 nginx 或者其它 web 服务器进行部署

安装 qiniu nodejs sdk

yarn add qiniu -D
// or
npm install qiniu -D

创建 qiniu.upload.js 用于上传资源文件

放在项目根路径下

const qiniu = require("qiniu");
const glob = require("glob");
const mime = require("mime");
const path = require("path");
const crypto = require("crypto");
const fs = require("fs");
// 编译后的目录
const distDir = "./dist";
const isWindow = /^win/.test(process.platform);
let pre = path.resolve(__dirname, distDir + "/") + (isWindow ? "\\" : "");
pre = pre.replace(/\\/g, "/");

const files = glob.sync(`${path.join(__dirname, distDir + "/**/*.*")}`);

const bucket = "";
const accessKey = "";
const secretKey = "";

const mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
const config = new qiniu.conf.Config({
  // bucket 区域
  zone: qiniu.zone.Zone_z2
});
const formUploader = new qiniu.form_up.FormUploader(config);
const bucketManager = new qiniu.rs.BucketManager(mac, config);

// 上传文件列表
async function uploadFiles(files) {
  files.map(async file => {
    const key = getFileKey(pre, file);
    let md5 = await getFileMd5(file);
    let info = await getQiniuFileInfo(bucket, key);
    const fileExist =
      info && info.respInfo && info.respInfo.data && info.respInfo.data.md5;
    if (fileExist && info.respInfo.data.md5 === md5) {
      console.log(`文件已存在且没有改变,跳过上传 key: ${key} md5: ${md5}`);
    } else {
      const changeStr =
        fileExist && info.respInfo.data.md5 !== md5
          ? "文件发生改变重新上传,"
          : "未存在的文件,";
      try {
        await uploadFIle(key, file);
        console.log(`${changeStr}上传成功 key: ${key}`);
      } catch (e) {
        console.log(`${changeStr}上传失败 key: ${key} error:`, e);
      }
    }
  });
}

// 封装单文件上传
function uploadFIle(key, localFile) {
  const extname = path.extname(localFile);
  const mimeName = mime.getType(extname);
  const putExtra = new qiniu.form_up.PutExtra({ mimeType: mimeName });
  const putPolicy = new qiniu.rs.PutPolicy({
    // 覆盖上传
    scope: bucket + ":" + key
  });
  const uploadToken = putPolicy.uploadToken(mac);
  return new Promise((resolve, reject) => {
    formUploader.putFile(uploadToken, key, localFile, putExtra, function(
      err,
      respBody,
      respInfo
    ) {
      if (err) {
        reject(err);
      } else {
        if (respInfo.statusCode === 200) {
          resolve({ respBody, respInfo });
        } else {
          reject(respInfo);
        }
      }
    });
  });
}

// 获取七牛文件信息
function getQiniuFileInfo(bucket, key) {
  return new Promise(resolve => {
    bucketManager.stat(bucket, key, function(err, respBody, respInfo) {
      resolve({ respBody, respInfo });
    });
  });
}

// 获取文件的 key
function getFileKey(pre, file) {
  if (file.indexOf(pre) > -1) {
    const key = file.split(pre)[1];
    return key.startsWith("/") ? key.substring(1) : key;
  }
  return file;
}

// 获取文件 md5
function getFileMd5(file) {
  return new Promise(reslove => {
    let md5sum = crypto.createHash("md5");
    let stream = fs.createReadStream(file);
    stream.on("data", function(chunk) {
      md5sum.update(chunk);
    });
    stream.on("end", function() {
      let fileMd5 = md5sum.digest("hex");
      reslove(fileMd5);
    });
  });
}

(async () => {
  console.time("上传文件到 cdn");
  await uploadFiles(files);
  console.timeEnd("上传文件到 cdn");
})();

修改 vue.config.js 的 publicPath

const cdnDomain = 'cdn地址'
module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? cdnDomain : '/'
}

上传

方式一:手动执行命令

node qiniu.upload.js

方式二:编译正式环境的时候一并执行

"scripts": {
  "build": "vue-cli-service build && node qiniu.upload.js"
},

写的不好,仅供参考