【七日打卡】 Vue项目架构团队自动化资产生成的管理思考__Vue.js__JavaScript
发布于 3 年前 作者 banyungong 1056 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

主题列表:juejin, github, smartblue, cyanosis, channing-cyan, fancy, hydrogen, condensed-night-purple, greenwillow, v-green, vue-pro, healer-readable, mk-cute, jzman, geek-black, awesome-green, qklhk-chocolate

贡献主题:https://github.com/xitu/juejin-markdown-themes

theme: awesome-green highlight:

本文已授权掘金开发者社区公众号独家使用,包括但不限于编辑、标注原创等权益。

简介

在之前总结我对Vue项目团队开发的一些基本配置封装分享文章中,我们已经对大部分的常见封装都做出了处理,从API约定式Model数据层,这类行为都赋予了项目更加自动化的处理方式,使团队前端工程师不需要关注生成,而只需要注意代码结果代码质量,从而得到更多实践上得意义。

同时,对于代码质量的管理在【七日打卡】重构几次代码后,我总结了一些前端代码优化实践中,我们也提到了一些代码的实践优化案例,以及许许多多的条件约束都是为开发者养成好的编码习惯的同时进行探索,为成为更好的前端工程师养成基本习惯。

在这篇文章中。我主要想分享的就是在开发时,项目脚手架和项目架构能为开发人员做什么处理,是不是能够给与一些实质性的帮助来提升开发的效率和容错率。

本文内容阅读并不是很困难,没有涉及太深入的一些技术体系,适合大部分前端进行阅读和探究思考。

什么是【资产】

对于我们开发者来说,平时所构建的代码就是个人资产,但是大多时候,资产的管理对于团队来说是非常糟糕的,对于资产的管理来说,很多小公司项目都是临时产生的,每一个项目都属于从0开始。那么,每一次所有的配置,封装都不一样。对于团队来说,所有的项目都没有统一性,新入职的同学需要耗费非常久的时间去学习不同的配置风格,加上老板如果没耐心,需要快速的投入生产,那么这个项目最终其实难逃重构的命运。

因此,对于团队公共资产的管理就变得尤为重要,其中框架的更新肯定会带动资产的更新是必然的趋势,所以这就需要团队在维护资产的方面有一定的经验能力了,毕竟资产维护是脱离于项目之外的工作量但是,收益方却是整个部门的前端团队。

Plop微型脚手架能干什么?

在这里,我是通过Plop来管理日常资产的,对于我们日常写的组件,方法库来说,这类固定资产是必须进行管理的。举个例子,我写了一个微信小程序处理登录的逻辑块,我将它抽离成一个单独的切片方法。那么,下一次我写小程序是不是只需要将逻辑方法复制过来进行调用就完成了?

Plop是一个非常小巧的Sub Generator,对我们来说,将其集成到我们项目的脚手架中充当一个调度者。它需要调度的就是我们自身团队维护起来的【开发资产】,如:UI DesginUtils, Api约定式生成, 抽象代码模板…等等功能的调度生产,大幅度的减少开发者日常CV,插件的一些工作。

  • 创建对应的UI资产组件
  • 创建对应的资产方法
  • 创建开发时的API接口函数
  • 创建开发目录结构
  • 创建开发模板代码

Plop如何实现这些功能?

在构建时,我们需要做的就是配置setGenerator,通过命名空间来产生不同的流程方式,决定我们当前需要处理的行为和选择。

plop.setGenerator('test', createPageTemplate)
plop.setGenerator('api', createApiTemplate)

当我们需要创建一个页面的时候,需要运行plop命令就可以进行对应的页面和组件,而不必像之前一样,先创建文件,然后创建对应的资源,甚至于,我们还可以进行其他模式的配置,如下图中,你是否需要Modlsass,scss的选择。

一个简单的setGenerator

一个基本的setGenerator做的东西可多可少,取决于开发者需要让其干什么。学习过C语言的同学都应该知道命令行界面的基本操作吧,通过scanf来进行输入,根据输入的内容产生对应的逻辑出来,其实这个就和脚手架很像了,只不过脚手架做的东西更多,除了输入框之外,还有列表选择器…等等其他组件元素的存在。

那么,我们来分析以下最基本的页面容器的创建,是怎么进行的吧:

在一个一个简单的setGenerator中,包含以下4个配置关键字。

comopnent:''
description: 'this is a skeleton plopfile',
prompts: [], // array of inquirer prompts
actions: []  // array of actions
  • comopnent:作为setGenerator的命名空间名
  • description:作为描述,主要用来描述当前需要处理的行为
  • prompts:作为步骤条提示,可以提示和中断来与用户操作,引导用户进行配置使用。
  • actions: 作为操作行为,当用户通过提示步骤配置后用来执行相应的动作时产生。

可以到看,其实Plop的行为不算复杂,大多数都是配置性的工作。因此,在项目周期淡季可以挑选时间做资产的生成模式。下面是一个普通的setGenerator,本身没有太多的行为去

页面是如何生成的?

我们通过一个基本的目录生成到路由插入的示例来进行一个Demo演示吧。下面就是一个基本的演示,主要实现的功能就是项目目录自动构建的同时插入路由,一键创建页面级别的模板。如下图,可以看到moreTest页面生成后,会自动在routes.js下面自动插入一个路由声明,达到我们一键生成的目的。

安装plop

可以选择在全局安装,或者仅当前项目安装的形式,通过包管理器安装plop的运行依赖。

# shell
yarn global add plop

新增启动命令

通过yarn new module的命令就是通过script来添加的,名字可以随便取,主要是执行Plop的脚本。

// package.json
"scripts": {
  "build": "vue-cli-service build",
  "lint": "vue-cli-service lint",
  "dev": "vue-cli-service serve --mode dev",
  "doc": "jsdoc -r -c conf.json README.md",
  "new": "plop"
},

创建plopfile

plopfile.js就是作为plop配置的entry文件,主要就是加载不同的setGenerator。由于我们的setGenerator可能会是大批量的,建议拆分成一个目录做这件事情。我个人目前是所有设置类型的操作都放在了.setting目录下,方便管理。

在这里其实相当于一个文件的聚合,主要用来声明对应的配置和setGenerator Name。主要的代码就是各个模块不同的action了。

const createPageTemplate = require('./src/.setting/plop/page')
const createApiTemplate = require('./src/.setting/plop/api')

function cli (plop) {
  plop.setGenerator('page', createPageTemplate)
  plop.setGenerator('api', createApiTemplate)
}

module.exports = cli

prompts

在写prompts之前,可以先阅读文档,查看prompts中的配置属性,方便更好的去实现对应的提示操作和选项。

  • type: 主要的类型存在:input, number, confirm, list, rawlist, expand, checkbox, password, editor
  • name: 当前类型存放的变量名,可以通过名称拿到对应的操作值
  • message: 提示信息,主要是呈现给用户展示
  • default: 当前选项的默认值
  • choices:多个选项,一般用于列表展示。
  • 其他:查看Plop文档,不然别人说我水字数:#Plop Types

在上面的话,我将一些基本的参数都说明了下对应的作用,那么就可以开始写对应的prompts了,如下代码,就创建了一个inputlistconfirm提示组件。让用户进行更好的操作。

prompts: [
    {
      type: 'input',
      name: 'pageName',
      message: '请输入页面文件夹名称'
    },
    {
      type: 'list',
      name: 'cssType',
      default: 'scss',
      choices: ['手动', 'sass', 'scss'],
      message: '请选择需要创建的sass类型?'
    },
    {
      type: 'confirm',
      name: 'isModel',
      default: true,
      message: '请选择需要创建Model?'
    },
  ],

action

如果说输入的组件完成了,这个时候我们就可以在action,中拿到对应的值,这些值都是用户操作过后的一些值,我们可以通过这个去进行action行为的添加。

常见的action动作有以下几种。

  • add: 该操作用于将文件添加到项目中。path 属性是一个处理句柄模板,将用于按名称创建文件。文件内容将由或属性确定。
  • addMany: 该操作可用于通过单个操作将多个文件添加到项目中, add的加强版。
  • modify: 该操作可以使用两种方法。可以使用 属性查找/替换位于指定文件中的文本,也可以使用函数来转换文件内容。
  • append: 它用于在特定位置将数据追加到文件中, 插入文件数据的作用。

同时常见的属性有:

  • type: 上面action的动作类型
  • path: 需要操作的路径
  • templateFile: 模板路径
  • data: 带入hbs模板中的数据,在模板中可以获取。

可以看到,在下面我拿到了用户输入的操作值,根据操作值的情况,进行了action的操作,这里为了直观,并没有使用addMany来创建文件,而是一步步来进行。如下图和代码,我们可以产生对应的文件目录,文件名,和模板代码。这里其实不难理解,无非就是通过不同的判断,添加了几个action,最后返回出去了集合数组。

actions: data => {
    const { pageName, cssType, isModel } = data
    console.log(data)
    const actions = []
    const name = '{{pageName}}'
    const path = `src/${PAGE_PATH}/{{ pageName }}/`
    if (pageName) {
      actions.push({
        type: 'add',
        path: `${path}/index.vue`,
        templateFile: 'src/.setting/template/view/page.hbs',
        data: {
          name: pageName,
        }
      })
    }
    if (cssType !== '手动') {
      actions.push({
        type: 'add',
        path: `${path}/index.${cssType}`,
        templateFile: 'src/.setting/template/view/css.hbs'
      })
    }
    
    if (isModel) {
      actions.push({
        type: 'add',
        path: `${path}/model.js`,
        templateFile: 'src/.setting/template/view/model.hbs',
        data: {
          name: pageName,
        }
      })
    }
    actions.push({
      type: 'append',
      path: 'src/router/routes.js',
      pattern: /\[/,
      templateFile: 'src/.setting/template/router/config.hbs',
      data: {
        name: pageName,
      }
    })

    return actions
  }

hbs模板

可能大家发现了,我们的模板是hbs的,它是一个单纯的模板引擎,作用相比于现在的模板来说虽然优势不明显,但是我们仅仅只是需要一些模板的编写,所以hbs完全是能够满足我们的一些基本需求的。我们需要创建action中对应声明的hbs模板文件,来让plop创建或者插入到我们声明的path当中去。

<template>
  <div>
    <p>页面创建成功,当前页面名称 -- {{ name }}</p>
    <p>页面创建成功,当前页面路径 -- @/views/{{ name }}</p>
  </div>
</template>

<script>
import { useServices } from 'framework'
const api = useServices()
export default {
  name: '{{name}}',
  data: () => {}
}
</script>

效果

至此完成,我们的一个基本目录添加就完成了,下面看一下效果吧。看看最终插入到视图上面的内容是不是我们想要的内容吧。

代码=>

const PAGE_PATH = 'views'

module.exports = {
  description: '创建文件,注意必须符合项目约束规范',
  prompts: [
    {
      type: 'input',
      name: 'pageName',
      message: '请输入页面文件夹名称'
    },
    {
      type: 'list',
      name: 'cssType',
      default: 'scss',
      choices: ['手动', 'sass', 'scss'],
      message: '请选择需要创建的sass类型?'
    },
    {
      type: 'confirm',
      name: 'isModel',
      default: true,
      message: '请选择需要创建Model?'
    },
  ],
  actions: data => {
    const { pageName, cssType, isModel } = data
    console.log(data)
    const actions = []
    const name = '{{pageName}}'
    const path = `src/${PAGE_PATH}/{{ pageName }}/`
    if (pageName) {
      actions.push({
        type: 'add',
        path: `${path}/index.vue`,
        templateFile: 'src/.setting/template/view/page.hbs',
        data: {
          name: pageName,
        }
      })
    }
    if (cssType !== '手动') {
      actions.push({
        type: 'add',
        path: `${path}/index.${cssType}`,
        templateFile: 'src/.setting/template/view/css.hbs'
      })
    }
    
    if (isModel) {
      actions.push({
        type: 'add',
        path: `${path}/model.js`,
        templateFile: 'src/.setting/template/view/model.hbs',
        data: {
          name: pageName,
        }
      })
    }
    actions.push({
      type: 'append',
      path: 'src/router/routes.js',
      pattern: /\[/,
      templateFile: 'src/.setting/template/router/config.hbs',
      data: {
        name: pageName,
      }
    })

    return actions
  }
}

历史文章回溯

总结

plop可维护比较多的模板类资产,随着我们的setGenerator越来越多,我们的资产库就越来越大,那么除了维护这一部分的代码外,对脚手架的设计也需要明细,尽量能够在界面上提供更好的元素描述,让团队新人开发者或者说入门开发者能够凭借描述来使用对应的资产,而不是在翻阅文档或者是找资产提供人进行询问。

资产中的设计资产也同样重要,我们可以尝试和UI还有交互共同维护一套跨部分和业务的资产体系,其实涉及的设计体系交互操作动画模式都是资产,Plop只能为开发带来共同的资产维护,但是团队的资产也可以是来源于外方的。

当积累的资产到达一定的积累时,在制作新的产品,开发的效率会大大提高,相比于从01,更多的是直接集成资产来进行开发,这个时候大部分设计交互动画基础方法组件,都已经在资产库中存在,我们只需要将其提取出来,然后在页面上进行调用,我们可以有更多的时间方法代码的圈复杂度和逻辑优化上面。

如果本文对你的有一定的帮助,那么可以点个赞支持下作者哦。

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: wangly19 原文链接:https://juejin.im/post/6918720683807604744

回到顶部