对于gh-framework
gh-framework
旨在解决vue2
环境下(思维可用于任何框架我的项目,不局限于vue2)的前端工程化问题,它将封装vue
我的项目中罕用的工具库和配置文件并将其可移植化,例如axios
、constants(常量)
、directives(指令)
、services(数据申请层)
、config(配置文件)
、mixin(全局混入)
、utils(工具集)
、context(上下文)
。本计划自己已在5+我的项目上利用,包含一个大型前端我的项目。
github地址(示例代码):https://github.com/cong1223/gh-framework
个性
- 高度封装:高度封装我的项目常用工具和配置,不写反复代码。
- 疾速移植化:封装一次,其余类型我的项目可复制粘贴,疾速聚拢反复代码,依据业务需要小局部批改即可,如果后端返回数据格式统一,那么
services
都不须要批改即可利用。 - 不具备破坏性:新我的项目如果想尝试这套解决方案,那么能够移植次计划,并且对你原先的我的项目不具备破坏性,能够同时兼容。
- 疾速开发体验:一次封装,永恒劳碌,辞别繁琐的导入/导出,
this
.万物。
适用人群
- 对前端工程化具备强烈学习趣味的初中级前端程序员;
- 前端我的项目中表演captain角色的程序员;
- 疾速交付型守业程序员;
- 兼职接单程序员;
我的项目构造介绍
疏忽了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
文件夹到另外一个新的我的项目,惟一不同的就是业务代码局部。此文件夹能够依据本人的业务需要持续扩大其余通用逻辑,封装办法参考directives
、utils
等。
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
把services
、const
、config
独自提到src
下也依据理论状况而定,思考到这三个文件夹跟理论业务非亲非故,所以独自提出来,只有在framework下配置好理论门路关联起来就ok。
前端人肯定要摒弃的陋习,vue
文件中大量操作model
,甚至在template
中写大量的数据处理逻辑来渲染相干数据,这会让你的我的项目变得越来越简单且不可保护,并且会生成更多的冗余代码。
vue我的项目四点忠告:
- template中尽量不要写表达式,巧用computed
- view与model拆散,vue文件尽量写ui相干代码,数据处理(后端无奈解决的数据须要前端解决的)在service中解决
- 常量日常化,尽量不要呈现魔法变量,这会让我的项目变得越来越不可保护
- 文件夹层级明显
文件夹我的项目构造如下:
|-- 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等工具联合,能够实现一个编码、打包、部署联合一体的残缺前端工程。