gulp部署前端项目,以及.env文件的使用

755 阅读4分钟

本文介绍如何使用gulp作为工具帮我们自动化部署前端代码到后端服务器,以及如何使用.env文件来管理环境变量,管理不同环境的账密。

1 背景

在开发过程中,经常会需要把代码部署到开发或者测试环境,如果每次都需要手动打包好再拷贝到服务器,那就太麻烦了。先说一下部署的过程:连接服务器,然后把我们的代码上传到服务器指定位置。建一套部署工具,自动实现打包部署,可以省很多事,还不容易出错。

2 实现简易部署

2.1 安装依赖

npm i gulp gulp-sftp-up4 --save-dev

2.2 配置gulp

首先,在项目根目录下新建gulp.js,新建一个test-deploy的gulp task

const gulp = require("gulp");
const sftp = require("gulp-sftp-up4");

gulp.task("test-deploy", function() {
  return gulp.src("dist" + "/**").pipe(
    sftp({
      host: "服务IP",
      user: "账户",
      pass: "密码", 
      port: "端口 默认值是22",
      remotePath: `文件放置的路径`
    })
  );
});

2.3 配置命令

在package.json中的scripts下添加命令 "test-deploy": "vue-cli-service build && gulp test-deploy"

2.4 测试部署

在控制台输入 npm run test-deploy 即可自动打包,部署,没出意外的话,可以在服务器对应的目录下看到刚刚上传的文件。没有线上服务器的话,可以用虚拟机试试。

简易的部署就到这里结束了。

在上面的部署中,我们把服务器的账户和密码都写在了gulp.js中,这个文件会被提交到git,这样是非常不安全的,所以接下来会介绍如何使用.env文件管理环境变量和账密,这适合用于有多个环境的管理,如果觉得太麻烦的话,也可以直接用 .ftppass 文件,它是gulp-sftp的配置文件。

3 .env文件的使用

3.1 新增.env文件

在根目录下新增 .env.dev 和.env.prod 两个环境文件,文件中会放除了账号密码之外的其他环境配置,比如

HOST = 192.168.2.88
REMOTE_PATH = /web-dev

3.2 新增.env.local文件

在根目录下新增 .env.dev.local 和.env.prod.local .local文件中的配置会和之前的配置做一个Object.assign的操作,.local文件用来存放账密,.local文件不会被提交到git上,而是在本地存放(为了示例完整,我会放上github),现在在本地可以看到四个.env文件

0b3b5d71d89d57eb297aeb0e06091fc.png

3.3 新增env.config.js

根目录下新建一个文件夹builder,新建文件env.config.js,里面的代码如下

const glob = require("glob");
const fs = require("fs");
const dotenv = require("dotenv");
const gutil = require("gulp-util");

const argv = Object.assign({ mode: null }, gutil.env);

//列出.local文件,用于第一步询问需要部署哪个环境
const listLocal = glob.sync(".env.*.local").map(i => {
  const [lenPrefix, lenSuffix] = [".env.".length, ".local".length];
  return i.substr(lenPrefix, i.length - lenSuffix - lenPrefix);
});

//把env文件中的设置的参数放入process.env
const setEnv = mode => {
  let envSetting = getEnv(mode);
  Object.keys(envSetting).forEach(
    _key => (process.env[_key] = envSetting[_key])
  );
};

//合并.local文件, 比如合并 .env.dev 和 .env.dev.local, .local文件的配置会覆盖前面的
const getEnv = mode => {
  let data = [`.env.${mode || ""}`, `.env.${mode || ""}.local`];
  console.log("data11", data);
  data = data.filter(i => fs.existsSync(i));
  console.log("data", data);
  data = data.map(i => dotenv.config({ path: i }).parsed);
  return Object.assign.apply(this, data);
};

module.exports = { mode: argv.mode, listLocal, getEnv, setEnv };

3.4 修改task deploy方法

在builder目录下,新增task-deploy.js文件

文件中引入了一个prompt包,在我们执行npm deploy之后,会询问要部署的环境,如下图 0775478a483cb84a6b25bdd79bf799b.png

选择好要部署的环境后(chooseEnviroments),就会开启批量部署流程(batchDeploy),根据需要部署的环境,执行打包命令和上传到对应位置,批量部署完成后,则本次部署完成

const { execSync } = require("child_process");
const prompts = require("prompts");
const gulp = require("gulp");
const sftp = require("gulp-sftp-up4");

//deploy task
const taskDeploy = ({ listLocal, setEnv, mode }, _callback) => {
  if (mode !== null) {
    return Promise.resolve()
      .then(() => console.log(`Deploy starting...`))
      .then(() => setEnv(mode)) // 设置环境变量
      .then(() => {
        return buildApp({ mode }); //打包项目
      })
      .then(() => {
        return deploy();
      })
      .catch(_error => console.log(`x ${_error.message && _error}`))
      .finally(_callback);
  } else {
    // 没有指定打包环境,就执行下面这段代码,下面这段代码的实际作用就是根据命令行中的选择,构建出每个环境最终的gulp deploy task命令,
    // 执行命令的时候,因为mode不为空了,就会执行上面if中的代码了
    chooseEnviroments({ envs: listLocal })
      .then(({ environments }) => batchDeploy({ environments }))
      .catch(_error => console.log("error", _error))
      .finally(_callback);
  }
};

//使用prompts,在部署过程中提供友好的交互式命令行提示
const chooseEnviroments = ({ envs }) => {
  return prompts([
    {
      type: "multiselect",
      name: "environments",
      message: "Which environments do you want to deploy?",
      choices: envs.map(i => ({ title: i, value: i })),
      validate: environments =>
        environments.length === 0 ? "environments is required" : true
    }
  ]);
};

//循环获取之前选择要部署的环境,挨个部署这些环境,这里如果不需要分环境打包,可以优化一下不用重复打包
const batchDeploy = ({ environments }) => {
  let envDeploySuccess = [];
  environments.forEach(_env => {
    let cmd = `gulp deploy --mode ${_env}`;

    let execResult = execCommandSync(cmd).toString();
    console.log(execResult);

    if (execResult.includes("Deploy Succeeded")) envDeploySuccess.push(_env);
    console.log("exec", cmd, "done");
  });
};

//执行gulp deploy task
const execCommandSync = _cmd => {
  console.log("exec command:", _cmd);
  try {
    return execSync(_cmd)
      .toString()
      .trim();
  } catch (e) {
    return null;
  }
};

// 使用 vue cli 编译打包项目
const buildApp = ({ environment }) => {
  console.log(`Vue Cli Building for ${environment} ...`);
  let execResult = execCommandSync(
    `vue-cli-service build --mode ${environment}`
  ).toString();
  console.log(execResult);
  return Promise.resolve();
};

//使用sftp上传文件到服务器
const deploy = () => {
  gulp.src("dist" + "/**").pipe(
    sftp({
      host: process.env.HOST,
      user: process.env.USER,
      pass: process.env.PASSWORD,
      port: "22", //默认值就是22
      remotePath: process.env.REMOTE_PATH
    })
  );
};

module.exports = { taskDeploy };

3.5 gulp.js使用 taskDeploy,配置package.json

gulp.js使用 taskDeploy

gulp.task("deploy", _callback => taskDeploy(envConfig, _callback));

配置package.json,在scripts下添加

"deploy": "gulp deploy"

可以为某个环境单独配置一条命令

"deploy-dev": "gulp deploy --mode dev"

结语

利用.env文件,还可以实现一些个性化的部署,比如为某个环境打包专属的element theme。文章到这里就结束了,欢迎在留言区分享一下大家的部署方式,有用的话请点个赞,代码放在了github:github.com/daisygogogo…