对于gh-framework

gh-framework旨在解决vue2环境下(思维可用于任何框架我的项目,不局限于vue2)的前端工程化问题,它将封装vue我的项目中罕用的工具库和配置文件并将其可移植化,例如axiosconstants(常量)directives(指令)services(数据申请层)config(配置文件)mixin(全局混入)utils(工具集)context(上下文)。本计划自己已在5+我的项目上利用,包含一个大型前端我的项目。

github地址(示例代码):https://github.com/cong1223/gh-framework

个性

  1. 高度封装:高度封装我的项目常用工具和配置,不写反复代码。
  2. 疾速移植化:封装一次,其余类型我的项目可复制粘贴,疾速聚拢反复代码,依据业务需要小局部批改即可,如果后端返回数据格式统一,那么services都不须要批改即可利用。
  3. 不具备破坏性:新我的项目如果想尝试这套解决方案,那么能够移植次计划,并且对你原先的我的项目不具备破坏性,能够同时兼容。
  4. 疾速开发体验:一次封装,永恒劳碌,辞别繁琐的导入/导出,this.万物。

适用人群

  1. 对前端工程化具备强烈学习趣味的初中级前端程序员;
  2. 前端我的项目中表演captain角色的程序员;
  3. 疾速交付型守业程序员;
  4. 兼职接单程序员;

我的项目构造介绍

疏忽了vue我的项目根本我的项目构造文件

|-- node_mudules|-- public|-- src    |-- assets // 动态资源文件夹    |-- config // 配置文件文件夹    |-- const // 常量配置文件夹    |-- framework // gh-framework 文件夹        |-- directives // 全局指令文件夹        |-- mixin // 全局mixin混入文件夹        |-- plugins // framework 外围工具集的配置入口        |-- utils // 全局工具集文件夹        |-- ui // 全局通用ui组件文件夹        |-- config.js // 文件名映射配置文件(重要)        |-- index.js // 导出为vue能装置的framework插件(封装为install函数)    |-- services // 数据申请层文件夹|-- .browserslistrc|-- .eslintrc.js|--    .gitignore|-- babel.config.js|-- package.json|-- README.md|-- yarn.lock

framework

framework文件夹就是gh-framework的核心思想所在,高度聚拢我的项目公共代码,应用变得能够疾速移植化,一个我的项目做完了,能够立马复制framework文件夹到另外一个新的我的项目,惟一不同的就是业务代码局部。此文件夹能够依据本人的业务需要持续扩大其余通用逻辑,封装办法参考directivesutils等。

directives

全局指令封装集文件夹,将我的项目中罕用指令对立治理,全局装置。

文件夹我的项目构造如下:

|-- directives    |-- debounce.js // 避免按钮在短时间内被屡次点击,应用防抖函数限度规定工夫内只能点击一次    |-- loadmore.js // element-ui下拉框下拉更多    |-- draggable.js // 实现一个拖拽指令,可在页面可视区域任意拖拽元素    |-- copy.js // 复制粘贴指令    |-- emoji.js // 不能输出表情和特殊字符,只能输出数字或字母等    |-- lazyload.js // 实现一个图片懒加载指令,只加载浏览器可见区域的图片    |-- longpress.js // 实现长按,用户须要按下并按住按钮几秒钟,触发相应的事件    |-- permission.js // 权限指令,对须要权限判断的 Dom 进行显示暗藏    |-- watermark.js // 给整个页面增加背景水印    |-- // 更多其余指令...    |-- index.js // 对立进口
以上更多通用指令请在示例我的项目中查看获取

debounce.js

// 避免按钮在短时间内被屡次点击,应用防抖函数限度规定工夫内只能点击一次const debounce = {  inserted: function (el, binding) {    let timer    el.addEventListener('keyup', () => {      if (timer) {        clearTimeout(timer)      }      timer = setTimeout(() => {        binding.value()      }, 1000)    })  }}export default debounce

loadmore.js

// element-ui下拉框下拉更多const loadmore = {  bind (el, binding) {    // 获取element-ui定义好的scroll盒子    const SELECTWRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')    SELECTWRAP_DOM && SELECTWRAP_DOM.addEventListener('scroll', function () {      const CONDITION = this.scrollHeight - this.scrollTop <= this.clientHeight      if (CONDITION) {        binding.value()      }    })  }}export default loadmore

index.js

import loadmore from './loadmore'import debounce from './debounce'// 自定义指令const directives = {  loadmore,  debounce}export default {  install(Vue) {    Object.keys(directives).forEach((key) => {      Vue.directive(key, directives[key])    })  }}

mixin

全局混入,一些全局的办法和属性能够定义在此文件中,比方路由跳转,封装后能够更灵便的解决门路和参数问题,比方全局的toast、二次弹窗等等。

文件夹我的项目构造如下:

|-- mixin    |-- index.js

index.js

export default {  data() {    return {};  },  computed: {},  created() {  },  mounted() {  },  methods: {    // 二次弹窗, 命令形式调用    $confirmBox(title, message, handleConfirm, handleCancel, options) {      const {        confirmButtonText = "确认",        showCancelButton = true,        // eslint-disable-next-line no-unused-vars        confirmButtonType = "primary"      } = options || {};      return this.$messageBox({        title,        message,        showCancelButton, // 依据业务需要扩大        confirmButtonText, // 依据业务需要扩大        customClass: "zk-confirm-box",        closeOnClickModal: false,        confirmValidate: (action, component, done, instance) => {          // component : 自定义传入的component的组件实例          if (action === "cancel") {            if (handleCancel) {              handleCancel();            }            return done();          } else {            instance.confirmButtonLoading = true;          }          handleConfirm(done, instance);        }      }).catch(() => {      });    },    // 可扩大自定义弹窗内容, 给components传自定义组件即可    $messageBox(      {        component = null,        componentName = "",        confirmData = {},        confirmValidate = () => {        },        ...rest      }    ) {      const h = this.$createElement;      return new Promise((resolve, reject) => {        this.$msgbox({          message: h(component, {            props: { confirmData }          }),          beforeClose: (action, instance, done) => {            const cptInstance = instance.$children.find(child => {              return child.$options.name === componentName;            });            confirmValidate(action, cptInstance, done, instance);          },          ...rest        })          .then(resolve)          .catch(reject);      });    },    // 批改告诉的默认工夫    $toast(type, msg, duration, callback) {      this.$message({ type: type, message: msg, duration: duration || 1500, onClose: callback });    },    getParams(type = "params", key) {      const params = this.$route[type];      if (Object.keys(params).length) {        if (key) {          return params[key];        } else {          return params;        }      } else {        return null;      }    },    goBack() {      this.$router.go(-1);    },    goto(name, params = {}, isReplace) {      params = params || {};      return new Promise((resolve) => {        if (name) {          if (name.indexOf("/") >= 0) {            if (isReplace) {              this.$router.replace({                path: name, params              }, () => {                resolve && resolve();              });            } else {              this.$router.push({                path: name, params              }, () => {                resolve && resolve();              });            }          } else {            if (isReplace) {              this.$router.replace({                name, params              }, () => {                resolve && resolve();              });            } else {              this.$router.push({                name, params              }, () => {                resolve && resolve();              });            }          }        }      });    }  }};

plugins

此文件夹对立收揽了所有framework治理下的"插件",那些咱们封装好了的工具集咱们称为framework的插件集。

文件夹我的项目构造如下:

|-- plugins    |-- axios.js // 封装后的axios插件    |-- config.js // 配置文件插件    |-- const.js // 常量定义插件    |-- request.js // axios的下层申请封装插件    |-- service.js // 接口申请,数据处理层插件    |-- storage.js // localStorage插件    |-- utils.js // 自定义封装工具集插件

axios.js

// 依据本人理论业务需要配置,此配置文件仅供参考import axios from 'axios'import Cookies from 'js-cookie'import {  Message,  MessageBox} from 'element-ui'const service = axios.create({  timeout: 6000,  headers: {    'X-User-Agent': 'boss',    'X-Ent': '0'  }})service.interceptors.request.use(config => {  if (!config.url.includes('hzzk-portal/sys/login') && Cookies.get('pro__Access-Token')) {    config.headers['X-Access-Token'] = Cookies.get('pro__Access-Token');  }  return config;}, err => {  return Promise.reject(err);})// http response 拦截器service.interceptors.response.use(  response => {    const { data } = response;    if (data.code === 200) {      return Promise.resolve(data);    } else if (/500/.test(data.code)) {      if (data.code === 50002) {        Cookies.remove('pro__Access-Token');        Message({          message: data.message,          type: 'error',          duration: 2 * 1000,          onClose: () => {            location.replace('/');          }        })      } else {        Message({          message: data.message,          type: 'error',          duration: 2 * 1000        })        return Promise.reject(data)      }    } else if (/400/.test(data.code)) {      if (data.code === 40009) {        // 权限有余        Message({          message: data.message,          type: 'error',          duration: 2 * 1000        })      } else {        Message({          message: data.message,          type: 'error',          duration: 2 * 1000        })        return Promise.reject(data)      }    } else if (/300/.test(data.code)) {      Message({        message: data.message,        type: 'info',        duration: 2 * 1000      })      return Promise.reject(data)    } else {      return Promise.reject(new Error(data.message || 'Error'))    }  },  error => {    // 判断申请异样信息中是否含有超时timeout字符串    if (error.message.includes('timeout')) {      Message({        message: '申请超时',        type: 'error'      })    }    // token生效重定向至登陆页    if (error.response.data.status === 500 && error.response.data.message === 'Token生效,请从新登录') {      if (error.response.data.message === "Token生效,请从新登录") {        MessageBox.alert('Token生效,请从新登录', '提醒', {          confirmButtonText: '从新登录',          callback: () => {            Cookies.remove('pro__Access-Token');            location.replace('/')          }        });      }    } else if (error.response.status === 500) {      // 间接捕捉http申请的错误码, 而不是后端的返回体里的错误码      Message({        message: '服务器异样',        type: 'error',        duration: 2 * 1000      })    } else {      Message({        message: error.message,        type: 'error',        duration: 2 * 1000      })    }    return Promise.reject(error)  },)export default service

config.js

import config from '../config'let constant = {};for (let i in config.config) {  let file = config.config[i];  constant[i] = require('../../config/' + file).default; // 具体门路依据你理论我的项目中config所在门路配置,这里配置实用于我以后我的项目所配置的文件夹门路}export default constant;

const.js

import config from '../config'const consts = {};for (const i in config.const) {  const fileName = config.const[i];  consts[i] = require('../../const/' + fileName).default; // 具体门路依据你理论我的项目中const所在门路配置}export default consts;

context.js

import storage from './storage'export default {  // 获取用户  user() {    return storage.getItem('userInfo') || {}  },  // 设置用户  setUser(user) {    storage.setItem('userInfo', user)  },  curEnterpriseId() {    return this.user().curEnterpriseId;  }}

request.js

// url配置依据本人理论我的项目进行配置import config from './config';import axios from './axios';const URI = config.uri;export default {  // 污浊的的get  getRequest(url, params = {}, base = 'BASE_URL') {    return new Promise((resolve, reject) => {      axios.get(URI[base] + url, {        params: params      }).then(res => {        resolve(res);      }).catch(err => {        reject(err)      })    })  },  postRequest(url, params = {}, base = 'BASE_URL') {    return new Promise((resolve, reject) => {      axios.post(URI[base] + url, params)        .then(res => {          resolve(res);        })        .catch(err => {          reject(err)        })    });  },  arcApi(url, params = {}, method = 'get', base = 'HZZK_ARC_API') {    if (method === 'post') {      return this.postRequest(url, params, base);    } else {      return this.getRequest(url, params, base);    }  },  bossApi(url, params = {}, method = 'get', base = 'BASE_URL') {    if (method === 'post') {      return this.postRequest('/boss' + url, params, base);    } else {      return this.getRequest('/boss' + url, params, base);    }  },  sysApi(url, params = {}, method = 'get', base = 'BASE_URL') {    if (method === 'post') {      return this.postRequest('/sys' + url, params, base);    } else {      return this.getRequest('/sys' + url, params, base);    }  }}

services.js

import config from '../config'const service = {};for (const i in config.service) {  const fileName = config.service[i];  Object.defineProperty(service, i, {    get() {      return Reflect.construct(require('../../services/' + fileName).default, []); // 实例化类    }  });}export default service;

storage.js

export default {  getItem(k) {    const jsonStr = window.localStorage.getItem(k.toString());    return jsonStr ? JSON.parse(jsonStr) : null;  },  setItem(k, value) {    value = JSON.stringify(value);    try {      window.localStorage.setItem(k, value);    } catch (e) {      this.removeItem(k);    }  },  removeItem(k) {    window.localStorage.removeItem(k);  },  clear() {    window.localStorage.clear();  },  key(index) {    return window.localStorage.key(index);  },  getItemByIndex(index) {    const item = {      keyName: '',      keyValue: ''    };    item.keyName = this.key(index);    item.keyValue = this.getItem(item.keyName);    return item;  }}

utils.js

import config from '../config'const utils = {};for (const i in config.utils) {  const fileName = config.utils[i];  utils[i] = require('../utils/' + fileName).default; // 具体门路依据你理论我的项目中const所在门路配置}export default utils;

utils

封装的公共工具集

文件夹我的项目构造如下:

|-- utils    |-- array.js    |-- index.js    |-- //更多自定义封装的工具...

array.js

export default {  /**   * 依据数组中对象的某个属性值进行去重   * @param arr: 须要去重的数组   * @param key: 依据对象中的哪个属性进行去重   * @returns {*}   */  unique(arr, key) {    const res = new Map();    return arr.filter((a) => !res.has(a[key]) && res.set(a[key], 1))  }  // ===== 更多工具函数 =====}

index.js

import array from './array'export {  array,  // more util func}

ui

全局专用ui组件

文件夹我的项目构造如下:

|-- ui    |-- components // 搁置组件文件夹        |-- scroll-view // 组件名称            |-- index.vue // 以后组件入口文件        |-- index.js // 对立组件进口

scroll-view/index.vue

<!-- 滚动加载组件 --><template>  <div id="scroll-view">    <slot></slot>    <p v-if="loading">加载中...</p>    <p v-if="noMore">没有更多了</p>  </div></template><script>export default {  props: {    // 列表的总页数    pages: {      type: [String, Number],      default: 0    }  },  data() {    return {      page: 1,      currLength: 0, // 以后列表长度      loading: false    }  },  computed: {    noMore () {      return this.currLength >= this.total;    }  },  mounted() {    const ScrollView = document.querySelector('#scroll-view');    ScrollView.addEventListener("scroll", (event) => {      const scrollDistance =        event.target.scrollHeight -        event.target.offsetHeight -        event.target.scrollTop;      if (this.loading) return;      if (this.page < this.pages && scrollDistance <= 0) {        this.loading = true;        this.$emit('load', ++this.page, () => {          this.loading = false;        })      }    });  },  methods: {    refresh() {      this.page = 1;      this.$emit('load', this.page, () => {        this.loading = false;      });    }  }}</script><style scoped lang="scss"> #scroll-view {   width: 100%;   height: 100%;   overflow: auto;   p {     line-height: 1.5em;     font-size: 14px;     color: #5e6d82;     text-align: center;     padding: 16px 0;   }   &::-webkit-scrollbar {     display:none   } }</style>

components/index.js

import ZkScrollView from './zk-scroll-view'export {  ZkScrollView,  // more components}

config.js

所有framework下的插件文件名的映射配置文件,这里配置的映射名能够应用this拜访,辞别繁琐的import

export default {  const: {    account: 'AccountConstants', // 通过this.const.account拜访到AccountConstants.js文件    // more constants file map  },  service: {    user: 'UserService', // 通过this.service.user拜访到UserService.js文件    enterprise: 'EnterpriseService', // 通过this.service.enterprise拜访到EnterpriseService.js文件    // more service file map  },  utils: {    array: 'array', // 通过this.service.user拜访到UserService.js文件    // more utils file map  },  config: {    uri: 'uri',// 通过this.config.uri拜访到config/uri.js文件    // more config file map  }};

index.js

封装framework的install办法,以供vue在main.js中装置它,并且挂载文件到Vue.prototype,以便全局通过this拜访到framework下的私有插件。

import storage from './plugins/storage.js';import Const from './plugins/const';import service from './plugins/service';import config from './plugins/config';import mixin from './mixin';import utils from './plugins/utils';import context from './plugins/context';import { ZkScrollView } from './zk-ui/components';import Directives from './directives'export default {  install(Vue, option) {    Vue.prototype.storage = storage;    Vue.prototype.config = config;    Vue.prototype.context = context;    Vue.prototype.const = Const;    Vue.prototype.$const = Const; // 标签中不能应用const关键字,而js中拜访的是this作用域下的const字段    Vue.prototype.service = service;    Vue.prototype.utils = utils;    Vue.mixin(mixin);    Vue.use(Directives);    Vue.component('zk-scroll-view', ZkScrollView);  }}

services

servicesconstconfig独自提到src下也依据理论状况而定,思考到这三个文件夹跟理论业务非亲非故,所以独自提出来,只有在framework下配置好理论门路关联起来就ok。

前端人肯定要摒弃的陋习,vue文件中大量操作model,甚至在template中写大量的数据处理逻辑来渲染相干数据,这会让你的我的项目变得越来越简单且不可保护,并且会生成更多的冗余代码。

vue我的项目四点忠告:

  1. template中尽量不要写表达式,巧用computed
  2. view与model拆散,vue文件尽量写ui相干代码,数据处理(后端无奈解决的数据须要前端解决的)在service中解决
  3. 常量日常化,尽量不要呈现魔法变量,这会让我的项目变得越来越不可保护
  4. 文件夹层级明显

文件夹我的项目构造如下:

|-- services    |-- abstract        |-- BaseService.js // service文件中不能通过this获取framework下的插件,这里对立在基类外面援用,使其在service文件中也能通过this获取插件。    |-- UserService.js    |-- EnterpriseService.js    |-- // 更多业务Service

BaseService.js

import request from "../../framework/plugins/request";import storage from "../../framework/plugins/storage";import service from "../../framework/plugins/service";import utils from "../../framework/plugins/utils";import config from "../../framework/plugins/config";import Const from "../../framework/plugins/const";import context from "../../framework/plugins/context";import dayjs from "dayjs";export default class BaseService {  constructor() {    this.request = request;    this.storage = storage;    this.service = service;    this.utils = utils;    this.$dayjs = dayjs;    this.const = Const;    this.config = config;    this.context = context;  }  /**   * 刷选出接口返回的有用数据(data),异样捕捉解决   * @param promise   * @param isTotal, 是否返回全副的json数据   * @returns {Promise<T>}   */  output(promise, isTotal = false) {    return new Promise((resolve, reject) => {      promise.then((resp) => {        if (!resp) {          reject();        } else {          if (resp.success) {            if (resp.code === 200) {              resp = resp.result;              if (isTotal) {                resolve(resp);              } else {                resolve((resp && resp.list) || (resp && resp.data) || resp);              }            } else {              reject(resp);            }          } else {            reject(resp);          }        }      }, (resp) => {        reject(resp);      });    });  }

举个例子,编写业务service

UserService.js

import BaseService from "./abstract/BaseService";/** * 用户治理 */export default class UserService extends BaseService {  // eslint-disable-next-line no-useless-constructor  constructor() {    super()  }  /**   * 登录   * @returns {Promise<T>}   */  login(username, password) {    const params = {      username,      password    };    return super.output(this.request.postRequest('/sys/login', params), true);  }  /**   * 通用短信验证码   * @returns {Promise<T>}   */  getSmsCode(params) {    return super.output(this.request.postRequest('/sys/sms', params), true);  }  /**   * 重置明码   * @returns {Promise<T>}   */  resetPassword(params) {    return super.output(this.request.postRequest('/sys/user/findBackPassword', params), true);  }}

EnterpriseService.js

import BaseService from "./abstract/BaseService";/** * 组织架构治理 */export default class EnterpriseService extends BaseService {  // eslint-disable-next-line no-useless-constructor  constructor() {    super()  }  /**   * 获取企业成员   * @param enterpriseId   * @param page   * @param pageSize   * @param option   * @param realName: 成员名字,搜寻用   * @returns {Promise<T>}   */  async getEnterpriseUserList(enterpriseId, realName, page = 1, pageSize = 50) {    let params = {      enterpriseId,      option: 0,      realName,      page,      pageSize    };    params = this.utils.obj.deleteEmptyProperty(params);    const result = await super.output(this.request.getRequest('/structure/queryUser', params), true);    // 数据逻辑解决在service中解决后返回给View页面应用    if (result && result.list && result.list.user) {      result.list.user.forEach(item => {        if (item.createTime) {          item.createTime = this.$dayjs(item.createTime).format(            "YYYY-MM-DD HH:mm:ss"          )        }        item.statusText = this.const.account.UserStatus.getCnameByValue(item.status);      })    }    return result;  }}

const

常量定义文件所在目录

文件夹我的项目构造如下:

|-- const    |-- abstract        |-- BaseConstant.js    // 封装常量类工具    |-- TemplateConstants.js    |-- // more constants files

BaseConstant.js

/** * 枚举常量根底类 * @Author 王聪 * @cdate 2018-01-20 14:35 */export default class BaseConstant {  constructor(name, value, cname, desc) {    this._name = name;    this._value = value;    this._cname = cname;    this._desc = desc;  }  name() {    return this._name;  }  cname() {    return this._cname;  }  value() {    return this._value;  }  numberValue() {    return parseInt(this.value())  }  desc() {    return this._desc;  }  /**   * 取得所有常量的map   * @returns {{}}   */  static getEnumMap() {    const prototypeMap = Object.getOwnPropertyDescriptors(this);    const enumMap = {};    for (const prototypeName in prototypeMap) {      const val = prototypeMap[prototypeName].value;      if ((val instanceof BaseConstant) && val._name) {        enumMap[val._name] = val;      }    }    return enumMap;  }  /**   * 取得所有常量的数组   * @returns {Array}   */  static getEnumList() {    const prototypeMap = Object.getOwnPropertyDescriptors(this);    const enumList = [];    for (const prototypeName in prototypeMap) {      const val = prototypeMap[prototypeName].value;      if ((val instanceof BaseConstant) && val._name) {        enumList.push(val);      }    }    return enumList;  }  static getValueByName(name) {    const enumMap = this.getEnumMap();    const _enum = enumMap[name];    return _enum ? _enum.value() : null;  }  static getNameByValue(value) {    const enumList = this.getEnumList();    let name = null;    enumList.find((_enum) => {      if (_enum.value() == value) {        name = _enum.name();        return true;      }    });    return name;  }  static getCnameByName(name) {    const enumMap = this.getEnumMap();    const _enum = enumMap[name];    return _enum ? _enum.cname() : null;  }  static getCnameByValue(value) {    const enumList = this.getEnumList();    let cname = null;    enumList.find((_enum) => {      if (_enum.value() === value) {        cname = _enum.cname();        return true;      }    });    return cname;  }  static getCnameByBitValue(value) {    const enumList = this.getEnumList();    const cnameArr = [];    enumList.forEach((_enum) => {      if ((value & _enum.value()) !== 0) {        cnameArr.push(_enum.cname());      }    });    return cnameArr.join(',');  }  /**   * 给饿了么的select组件用   * name为组件的value   * cname为组件的label   * @returns {*}   */  static getSelectOptionsByCnameAndName() {    const enumList = this.getEnumList();    const options = [];    enumList.forEach((_enum) => {      options.push({        value: _enum.name(),        label: _enum.cname()      });    });    return options;  }  static getSelectOptionsByCnameAndNameWithAll(option = { label: '全副', value: '' }) {    const options = this.getSelectOptionsByCnameAndName()    option = !option ? { label: '全副', value: '' } : option    options.unshift(option)    return options;  }  /**   * 给饿了么的select组件用   * value为组件的value   * cname为组件的label   * @returns {*}   */  static getSelectOptionsByCnameAndValue() {    const enumList = this.getEnumList();    const options = [];    enumList.forEach((_enum) => {      options.push({        value: _enum.value(),        label: _enum.cname()      });    });    return options;  }  static getSelectOptionsByCnameAndValueWithAll(option = { label: '全副', value: '' }) {    const options = this.getSelectOptionsByCnameAndValue()    option = !option ? { label: '全副', value: '' } : option    options.unshift(option)    return options;  }  /**   * 查问按位与的位数的值是否在值内   * @param value   * @return boolean   */  isAttrIn(value) {    if (value == null) {      return false;    }    value = parseInt(value)    return (value & this.numberValue()) == this.numberValue();  }  /**   * 原属性中增加属性   * @param attr   * @return   */  addAttr(attr) {    return this.numberValue() | (!attr ? 0 : attr);  }  /**   * 原属性中去掉属性   * @param attr   * @return   */  removeAttr(attr) {    if (!attr || attr <= 0) {      //logger.debug("原属性值 attribute="+attr+",不须要remove");      return 0;    }    return ~this.numberValue() & attr;  }  /**   * 获取属性地位,相当于log2 + 1   * @return   */  getAttrPos() {    return Math.log(this.numberValue()) / Math.log(2) + 1;  }}

举例常量类的编写: TemplateConstants.js

import BaseConstant from './abstract/BaseConstant'export default class TemplateConstants {  /**   * 模板类型   */  static TemplateType = class TemplateType extends BaseConstant {    static SYS_ENG_PROJECT = new BaseConstant("工程项目", '4', '系统工程模板');  }  /**   * 模板角色权限   * @type {TemplateConstants.Role}   */  static PerRole = class PerRole extends BaseConstant {    static MANAGER = new BaseConstant("MANAGER", '1', '职责管理员');    static NORMAL = new BaseConstant("NORMAL", '2', '职责一般人员');    static ADMIN = new BaseConstant("ADMIN", '0', '所有');  }  /**   * 文件夹权限   * @type {TemplateConstants.Role}   */  static PerFolder = class PerFolder extends BaseConstant {    static READ = new BaseConstant("READ", '0', '只读');    static DOWNLOAD = new BaseConstant("DOWNLOAD", '2', '下载');    static WRITE = new BaseConstant("WRITE", '3', '编辑');    static HEAD = new BaseConstant("HEAD", '4', '负责人');  }}

config

我的项目配置文件目录

文件夹我的项目构造如下:

|-- config    |-- uri.js

uri.js

let BASE_URL = ''let HZZK_FT_API = ''let HZZK_ARC_API = ''let HZZK_OCR_API = ''let PERMISSION_API = ''switch (process.env.NODE_ENV) {  case 'development':    PERMISSION_API = 'https://test.xxx.com'    BASE_URL = 'https://test.xxx.com/hzzk-portal'    HZZK_FT_API = 'https://test.xxx.com/hzzk-ft'    HZZK_ARC_API = 'https://test.xxx.com/hzzk-arc'    HZZK_OCR_API = 'https://test.xxx.com/hzzk-ocr'    break  case 'test':    PERMISSION_API = 'https://test.xxx.com'    BASE_URL = 'https://test.xxx.com/hzzk-portal'    HZZK_FT_API = 'https://test.xxx.com/hzzk-ft'    HZZK_ARC_API = 'https://test.xxx.com/hzzk-arc'    HZZK_OCR_API = 'https://test.xxx.com/hzzk-ocr'    break  case 'production':    PERMISSION_API = 'https://hzzk.xxx.com'    BASE_URL = 'https://hzzk.xxx.com/hzzk-portal'    HZZK_FT_API = 'https://hzzk.xxx.com/hzzk-ft'    HZZK_ARC_API = 'https://hzzk.xxx.com/hzzk-arc'    HZZK_OCR_API = 'https://hzzk.xxx.com/hzzk-ocr'    break  case 'local':    PERMISSION_API = 'http://192.168.0.108:20001'    BASE_URL = 'http://192.168.0.108:20001/hzzk-portal'    HZZK_FT_API = 'http://192.168.0.108:20001/hzzk-ft'    HZZK_ARC_API = 'http://192.168.0.108:20001/hzzk-arc'    HZZK_OCR_API = 'http://192.168.0.108:20001/hzzk-ocr'    break}export default { BASE_URL, HZZK_FT_API, HZZK_ARC_API, HZZK_OCR_API, PERMISSION_API }

装置

关上vue入口文件,例如main.js:

import GhFramework from './framework';Vue.use(Framework);

这样,就能齐全利用以上配置所有插件。

利用

举例说完了framework的一个整体框架和各个插件的独立配置外,以下用代码片段展现在我的项目中如何利用这些插件。

  • 自定义指令(directives)
<el-button type="primary" v-copy="content">点击复制</el-button>
  • 全局混入中局部办法的利用

    // 跳转首页this.goto('/');// 返回上一页this.goBack();// showToastthis.$toast("success", "调用胜利!");// $confirmBoxthis.$confirmBox("勾销下载", `确定要勾销该下载工作吗?`, (done, instance) => {  setTimeout(() => {    done();    instance.confirmButtonLoading = false;  }, 1500);});
  • 应用全局公共工具集

    uniqueArr() {  const arr = [{ name: "王", age: 2 }, { name: "叶", age: 4 }, { name: "张", age: 2 }];  console.log(this.utils.array.unique(arr, "age")) // [{ name: "王", age: 2 }, { name: "叶", age: 4 }]},deleteEmptyProperty() {  const params = {    name: '小聪忙',    age: 24,    address: '',    job: undefined,    phone: null  };  console.log(this.utils.obj.deleteEmptyProperty(params))  //{name: '小聪忙',age: 24}}
  • 应用全局ui组件

    <template>  <scroll-view style="height: 300px; background-color: #d0e5f2" :pages="pages"@load="load">    <div v-for="(num, index) in list" :key="index">      {{ num }}        </div>  </scroll-view></template>export default {    data() {    return {      pages: 3, // 总页数      list: []    };  },  methods: {    load(page = 1, next) {      setTimeout(() => {        if (page === 1) {          this.list = Array.from({ length: 100 }, (v, k) => k);        } else {          this.list.push(...Array.from({ length: 100 }, (v, k) => k));        }        next && next();      }, 1000);    }  }}
  • 应用全局配置文件

    // 获取uri配置文件中的BASE_URLconst baseUrl = this.config.uri.BASE_URL;
  • 应用全局常量

    // 获取一般人员角色this.const.template.PerRole.NORMAL.value() // "2"this.const.template.PerRole.NORMAL.name() // "NORMAL"// 获取文件夹权限汇合,实用于element-ui的el-select组件const perOptions = this.const.template.PerRole.getSelectOptionsByCnameAndValue(); // [{label: "只读", value: ""0},{...}]// 依据value获取对应的值的形容信息(例子: 下载权限对应的value是'2')this.const.template.PerFolder.getCnameByValue("2") // "下载"
  • 应用services进行后盾数据申请

    this.service.enterprise.getEnterpriseUserList(  'xxxxx',  this.keywords,  this.page,  this.pageSize).then(res => {  console.log(res)  // TODO: 解决返回数据}).catch(e =>  {  console.log(e)  // TODO: 处理错误返回数据})
  • 应用本地缓存

    // 缓存用户信息// 这里的key在理论我的项目中也要配置化,对立治理const user = { name: "小聪忙", wechat: "YXC19970131" };this.storage.setItem("USER_INFO", user);this.$toast("success", "保留胜利");// 获取用户名const user = this.storage.getItem("USER_INFO");if (user && user.name) {  this.$toast("success", user.name);} else {  this.$toast("error", "暂无用户缓存信息,请先缓存");}

总结

gh-framework就是以封装、疾速移植为目标而诞生的一种工程化思维,能够达到帮忙开发者和小型团队疾速搭建我的项目,复制我的项目,移植我的项目外围代码的目标。其与webpack、gitLab等工具联合,能够实现一个编码、打包、部署联合一体的残缺前端工程。