美文网首页前端专刊Web前端之路让前端飞
服务端打包vue工具—服务端

服务端打包vue工具—服务端

作者: layah | 来源:发表于2019-07-03 15:07 被阅读0次

背景


基于前后端分离且没有固定运维人员的情况下,对于不熟悉前端打包的其他开发人员,对整个打包流程一知半解,即使你手把手的教操作、写文档还是弥补不了,为此开发了一个在服务端打包的傻瓜式流程工具。

思路


需求其实不复杂,只需要把在本地开发打包的流程用代码的形式运行到服务端即可,这里涉及到node相关的文件操作及子进程的应用。

本篇文章内容过长,请耐心阅读!

技术栈


服务端:Egg

之所以选择Egg是折服于其MVC的框架设计,很方便

前端:vue 、ant-design-vue

老vue玩家 + 尝鲜UI库 ,体验一番element-ui和 ant-design-vue的不同之处

服务端


由于接口太多,这里只拿一个接口为例,大同小异。

请求全部项目列表:

  • service层:
async getprojects(token){
   const {ctx} = this;
   return await this.curl(
       `/api/user/projects?page=1&pageSize=1000&type=joined`,
         { 
           method: 'GET',
          }
      )
 }
  • controller层:
async getprojects(){
  const { ctx, service } = this;
  const data = await service.coding.index.getprojects(this.token);
  this.send(data);
}
  • router层:
router.get('/getprojects',index.getprojects);
主要逻辑

检查服务端是否安装git 和 yarn

// 前置依赖及变量
const { execSync } = require('child_process');
let _hasYarn;
let _hasGit;
  • 检查是否安装yarn
module.exports.hasYarn = function hasYarn(){
    if (_hasYarn != null) {
        return _hasYarn
    }
    try {
        execSync('yarnpkg --version', { stdio: 'ignore' })
        return (_hasYarn = true)
      } catch (e) {
        return (_hasYarn = false)
      }
}
  • 检查是否安装git
module.exports.hasGit = function hasYarn(){
    if (_hasGit != null) {
        return _hasGit
    }
    try {
        execSync('git --version', { stdio: 'ignore' })
        return (_hasGit = true)
      } catch (e) {
        return (_hasGit = false)
      }
}
  • 设置淘宝源
const execa = require('execa');
const local = 'http://39.106.109.144:4000/' // 这里是本地搭建的npm仓库,可换成淘宝镜像源

module.exports = async function setOrigin() {
    const {stdout} = await execa('yarn',['config', 'set', 'registry',local]);
    return stdout;
}
  • clone项目及安装依赖

这一步的逻辑主要为一下几步步:
1.如果目录文件下存在该工程即直接运行下一步操作(安装依赖);
2.如果没有直接clone下来
3.当前项目分支是否为选择分支,是:直接安装依赖;否:切换分支

代码如下:

const execa = require('execa');
const fs = require('fs');
const path = require('path');

// 克隆项目
function gitClone(project, branch,gitUrl){
    const workSpce = path.resolve(__dirname, WORK_SPACE);
    return execa.sync('git',['clone', gitUrl],{
            cwd: workSpce,
            stdio: 'inherit'
        })
};

// 安装并更新依赖
function installOrUpdate(projectPath, project, branch, gitUrl){
    const exists= fs.existsSync(projectPath);
    if(!exists){
        gitClone(project, branch, gitUrl)
      }else{
        execa.sync('git',['pull'],{
            cwd: projectPath
        });
    }  
    return upgrade(projectPath, branch)
};

// 检查分支并更新安装依赖包
 function upgrade( projectDir, branch = "master") {
    const currentBrach = getGitCurrentBranch(projectDir);
    if(currentBrach !== branch){
        execa.sync('git',['checkout', branch],{
            cwd: projectDir
        });
    }
    return  execa('yarn',['--production=false','--force'],{
        cwd: projectDir
    });
};

// 查看项目当前分支
function getGitCurrentBranch(projectDir){
   return execa.sync('git',['symbolic-ref', '--short', '-q', 'HEAD'],{
        cwd: projectDir
    }).stdout;
};

//  更新依赖核心代码
 function upgrade( projectDir, branch = "master") {
    const currentBrach = getGitCurrentBrach(projectDir);
    if(currentBrach !== branch){
        execa.sync('git',['checkout', branch],{
            cwd: projectDir
        });
    }
    return  execa('yarn',['--production=false','--force'],{
        cwd: projectDir
    });
};

// 安装依赖
function install(project, branch, gitUrl){
    const workSpce = path.join(__dirname, WORK_SPACE, project);
   return installOrUpdate(workSpce,project,branch,gitUrl)
};

// clone项目
function clone(project, branch,gitUrl){
    return  install(project, branch,gitUrl)
}

  • 项目打包
// 执行打包
function build(project, port){
    const workSpce = path.join(__dirname, WORK_SPACE, project);
   return cloudBuild(workSpce, port)
}

// port为页面中iframe的端口
function cloudBuild(workSpce, port = '7777'){
    return execa('npm',['run', 'build', `--env.SERVER_URL=${port}`],{
        cwd: workSpce
    });
}
  • 下载打包成果zip包,依赖adm_zip模块
const adm_zip = require('adm-zip');

// 根据项目vue.config配置读取项目打包成果对应的文件,并压缩为zip包
function download(projectDir){
    const workSpce = path.join(__dirname, WORK_SPACE, projectDir);
    const output = require(workSpce+'/vue.config.js').outputDir;
    const resolve = path.resolve(workSpce, output);
    const zip = new adm_zip();  
    zip.addLocalFolder(resolve);  
    zip.writeZip(`${resolve}.zip`); 
    return {
        file:fs.createReadStream(`${resolve}.zip`),
        name: `${output}.zip`
    };
}
返回相应执行进度给前端

仅仅展示安装依赖的相关逻辑,打包逻辑与此一样

const { install } = require('./install')
 
class InstallEvent {
    constructor(){
        this.message = '';
        this.finish = false
    }
   
    get output(){
        return this.message;
    }

   
    handleInstall(project, branch, gitUrl){
        const {stdout, stderr} = install(project, branch, gitUrl); // 执行上面安装依赖js

        stdout.on('data', (buffer)=>{
            const msg = buffer.toString();
            if(!msg || msg === '')return;
            this.message += msg;
        });

        stdout.on('end', (buffer)=>{
            this.finish = true
        });

        stderr.on('data', (buffer) =>{
            this.message += buffer.toString();
        });

        stderr.on('end', (buffer)=>{
            this.finish = true
        });
    }
}

module.exports = new InstallEvent()

  • 在相应的controller层编写相应逻辑
const eventer = require('../../helper/installEvent')
async cloudInstall() {
        const { ctx } = this;
        const { branch, project, gitUrl} = ctx.request.body;
        this.send({
            data:{
                data:{
                    step:3,
                    message: '正在下载'
                },
                code:0
            }
        });
        eventer.message = '';
        eventer.finish = false
       await eventer.handleInstall(project, branch, gitUrl);
    }

返回执行安装进度,前端通过定时请求执行

async getInstallMessage(){
        const message = eventer.message;
        this.send({
            data:{
                data:{
                    step:3,
                    message: message,
                    finish: eventer.finish
                },
                code:0
            }
        });
    }
  • 在controller层下载zip包
async download(){
        const stream  = download(this.ctx.request.body.project)
        this.ctx.set('Content-Type', 'application/octet-stream');
        this.ctx.set('Content-Disposition',`attachment; filename=${stream.name}`);
        this.ctx.body = stream.file;
    }

最后


挂载相应接口

router.post('/clone',appLication.clone);
router.post('/install',appLication.cloudInstall);
router.get('/getInstallMessage',appLication.getInstallMessage);
router.post('/download',appLication.download);

PS:欢迎大家关注我的公众号【前端专刊】,一起加油吧~


前端专刊

相关文章

网友评论

    本文标题:服务端打包vue工具—服务端

    本文链接:https://www.haomeiwen.com/subject/tzmqhctx.html