通过对项目结构 / 开发方式 / 技术架构的研究,再次提出部分能够优化的点。css
刚加入新公司以后,在学习来新公司的开发技术以后,就火烧眉毛的想要将本身过往的开发方式和技巧融合到如今的项目中,下面是针对目前项目的中一些技术点的改进思路html
此方案主要由以下几点组成:前端
方案在不断的编写过程当中,其余内容还需你们共同努力发现,整理。vue
良好的代码编写规范能够大幅度提升项目开发效率,增长代码的可读性/可维护性/可测试性,减小开发/重构等场景的复杂耗时程度。node
代码规范能够划分为构建规范/编写规范等方面,例如项目结构的搭建,说明文档的维护/模块的构建与命名/代码中注释与业务代码编写方式等方面都应该考虑到并设立统一的/较优的规则来进行约束,下面就进行简单说明:ios
项目结构构建过程当中,应该不断维护《目录结构说明文档》,而且创建项目结构版本,如:1.0.0,而且应该标注以下几点:ajax
根据目前已经处于开发过程当中的项目,整理部分规则以下:算法
通常目录命名采用lowercase方式,,目录由多个单词组件则利用"-"链接,如:npm
router,json
store,
components,
store/modules,
utils/dygraph-plugin
views中路由视图目录采用大驼峰形式命名,如:
views/Algorithms
views/Events
views/Monitor
工具模块, 基础业务模块文件采用lowercase方式,目录由多个单词组件则利用"-"链接,如:
bin/babel-external-helpers.js (babel中的命名方式)
store/modules/event.js
组件命名采用大驼峰方式命名,如:
AlgorithmsCenter.vue
AlgorithmsDdetail.vue
具体命名方式能够参考Vue.js风格指南 。
代码编写过程当中应遵照基本的开发规范准则,在这里只是给出部分建议。
BEM(Block-Element-Modifier)结构命名方式
HTML编写DOM结构并标时尽可能遵照BEM命名方式,具体请参考文献:BEM--前端命名规范介绍,BEM —— 源自Yandex的CSS 命名方法论。
注意:不必真的在每一个地方都用上它,当某个节点不属于任何一个BEM范畴的时候,按照常规命名方式就能够。
OOCSS(Object Oriented CSS)编写方式
OOCSS(面向对象CSS编写)并非什么新奇的技术,它想代表的其实就是咱们能够将样式与固定的dom解耦,将一个或多个细小的css样式添加到须要它们的dom节点身上,目的其实仍是为了提升代码的复用率,而且代码可读性也会有很大的提升,在项目中特别推荐使用BEM + OOCSS的开发方式,网上有不少结合使用指南,这里给你们提供一个很是简洁的CSS的组件化方案:OOCSS + BEM。
Javascript规范能够参考Eslint中更多详细的规则,Google Javascript规范也是不错的选择,好比:
1. 拒绝var
2. 使用空格代替tab
3. 优先使用箭头函数
4. 使用模版字符串代替链接字符串
5. ...
复制代码
这里提供给你们来自阿里的Kissy 最佳编码实践。
注释方式
对于注释其实应该更加看重,好的注释能够大幅度提升代码的可读性。
HTML文档中应该对页面每个Block的开始和结束进行注释说明,单独的Element也要标明,例如:
<div class="events-center">
<!-- 头部图表显示 start -->
<div class="top-chart">
<stacked-bar-chart :counts="eventCounts" :color="eventColor" @click="handleBarClick"></stacked-bar-chart>
</div>
<!-- 头部图表显示 end -->
<!-- 搜索与操做模块 start -->
<div class="toolbar horizontal background">
<!-- 搜索功能组件 -->
<cv-search :placeholder="helloWord" v-model="query"></cv-search>
<!-- 设置按钮 -->
<div v-if="!filterToggle" @click="openFilter" class="filter-icon"><icon-settings /></div>
<!-- 模式切换按钮 -->
<div v-if="!filterToggle" @click="switchMode" class="filter-icon"><icon-mode /></div>
<!-- 操做按钮组 start-->
<cv-button v-if="filterToggle" kind="secondary" @click="resetFilter">重置</cv-button>
<cv-button v-if="filterToggle" kind="secondary" @click="cancelFilter">取消</cv-button>
<cv-button v-if="filterToggle" @click="confirmFilter">确认</cv-button>
<!-- 操做按钮组 end-->
</div>
<!-- 搜索与操做模块 end -->
</div>
复制代码
CSS中也应该根据所编写的代码划分结构片断后注释,尽量使用多行注释方式(/**/),以下:
/* css reset start*/
*{
box-sizing: border-box
}
body {
margin: 0;
background-color: #FFF;
font-family: MicrosoftYaHei, PingFangSC, Helvetica, Arial, sans-serif, "宋体";
}
// ...
/* elemnt-ui table 样式重置 */
.el-table tr {
background-color: #f3f3f3;
cursor: pointer;
}
// ...
复制代码
JS中的注释但愿能采用JSDoc推荐的方式,这样更有利于开发到某一阶段时,利用JSDoc工具直接生成关于该JS模块的文档,具体能够参考文档:JSDoc在线文档,JSDoc中文文档
/**
* 为了演示JSDoc的示例模块.
* @module utils/jsdoc
* @see module:main.js
*/
/** 暴露 name */
export const name = 'mixer';
/**
* 错误处理方法.
* @param {object} self - 调用方法的vue实例.
* @param {object} error - 错误对象.
* @param {string} errorMessage - 出错提醒信息.
* @return {string} 处理后的错误信息文本.
* @example
* errorTip(this, e, '登录失败')
*/
export function errorTip (self, error, errorMessage) {
// 处理后的错误文本信息
let message = translate(error.message || error.msg || '出错了,请重试')
console.log(error, message)
// 调用vue实例的消息提示方法
self.$message({
type: 'error',
message: errorMessage || message,
center: true,
duration: (error.message && error.message.indexOf('Network Error') !== -1) ? 8000 : 3000
})
return message
}
/**
* 错误文本信息处理方法.
* @param {string} message - 原始的error对象中的报错信息.
* @return {string} 处理后的错误信息.
*/
function translate (message) {
if (message.indexOf('timeout') !== -1) {
return '请求超时'
} else if (message.indexOf('Network Error') !== -1) {
return '网络错误'
} else {
return '出错了,请重试'
}
}
复制代码
生成文档的方式:
下载jsdoc-to-markdown 能够将文档输出为markdown
npm install --save-dev jsdoc-to-markdown
复制代码
配置scripts
{
"scripts": {
"docs": "jsdoc2md lib/*.js > api.md"
}
}
复制代码
执行任务
npm run docs
复制代码
便可将lib下全部的js文件根据jsdoc规范生成到api.md中。
针对axios进行了更高效的封装,好比设置了拦截器,在数据请求回来后就能够根据请求结果的状态进行失败处理等等。
config/axios.config.js:
/**
* axios 配置模块
* @module config/axios.config
* @see utils/request
*/
import qs from 'qs';
/**
* axios具体配置对象
* @description 包含了基础路径/请求先后对数据对处理,自定义请求头的设置等
*/
const axiosConfig = {
baseURL: process.env.RESTAPI_PREFIX,
// 请求前的数据处理
transformRequest: [function (data) {
return data;
}],
// 请求后的数据处理
transformResponse: [function (data) {
return data;
}],
// 自定义的请求头
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded'
},
// 查询对象序列化函数
paramsSerializer: function (params) {
return qs.stringify(params);
},
// 超时设置s
timeout: 10000,
// 跨域是否带Token 项目中加上会出错
// withCredentials: true,
// 自定义请求处理
// adapter: function(resolve, reject, config) {},
// 响应的数据格式 json / blob /document /arraybuffer / text / stream
responseType: 'json',
// xsrf 设置
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
// 下传和下载进度回调
onUploadProgress: function (progressEvent) {
Math.round(progressEvent.loaded * 100 / progressEvent.total);
},
onDownloadProgress: function (progressEvent) {
Math.round(progressEvent.loaded * 100 / progressEvent.total);
},
// 最多转发数,用于node.js
maxRedirects: 5,
// 最大响应数据大小
maxContentLength: 2000,
// 自定义错误状态码范围
validateStatus: function (status) {
return status >= 200 && status < 300;
},
// 用于node.js
// httpAgent: new http.Agent({ keepAlive: true }),
// httpsAgent: new https.Agent({ keepAlive: true })
};
/** 导出配置模块 */
export default axiosConfig;
复制代码
utils/request.js(目前使用的是restapi.js):
/**
* 业务中使用的ajax请求工具模块
* @module utils/request
* @see main.js
*/
import axios from 'axios';
import config from '../config/axios.config';
import qs from 'querystring'
import Vue from 'vue'
import { errorTip } from 'utils/common'
// 用来调用errorTip的vue实例
const vueInstance = new Vue()
// 构建得的请求对象
const request = axios.create(config);
// 返回状态判断(添加响应拦截器)
request.interceptors.response.use(
res => {
// 若是数据请求失败
if ( res.data.code > 300 ) {
errorTip(vueInstance, res.data)
return Promise.reject(res.data);
}
return res.data.data;
},
error => {
return Promise.reject(error);
}
);
request.interceptors.request.use((config) => {
let allowMethods = ['post'];
if (allowMethods.indexOf(config.method) !== -1) {
config.headers['Content-Type'] = 'application/x-www-form-urlencoded';
config.data = qs.stringify(config.data);
}
return config;
}, (error) => {
return Promise.reject(error);
});
// 对axios的实例从新封装成一个plugin ,方便 Vue.use(xxxx)
export default request;
复制代码
在src开发目录中创建api目录结构,在其中能够集中构建全部请求动做的模块,以便调用。
api/index.js:
/**
* api接口调用维护模块
* @module api
* @see main
*/
import request from 'utils/request'
import Vue from 'vue'
import { errorTip } from 'utils/common'
// 用来调用errorTip的vue实例
const vueInstance = new Vue()
/**
* 分页获取算法列表
* @param {Object} [p] pageInfo 页码相关信息
* @param {number} [p.page] page 页数
* @param {number} [p.size] size 每页数据个数
* @param {string} [p.labels] labels 标签
* @param {string} [p.query] query 查询条件
* @return {Promise} request 请求动做promise
*/
export let apiGetList = ({page = 1, size = 10, labels = 'anomaly', query = ''} = {}) => {
return request.get(`/algorithm?page=${page}&labels=${labels}&size=${size}&query=${query}`).catch(error => errorTip(vueInstance, error))
}
复制代码
利用async await对请求做出优化处理,在须要调用请求的地方利用async/await来规避回调函数的嵌套。
store/modules/setting.js:
import { apiGetList } from 'api'
// ...
actions: {
// 分页获取算法列表 当前写法
getList: ({ commit }, {page = 1, size = 10, labels = 'anomaly', query = ''} = {}) => {
return new Promise((resolve, reject) => {
restapi.request({
method: 'get',
url: `/algorithm?page=${page}&labels=${labels}&size=${size}&query=${query}`,
success: sdata => {
commit('LIST_ALGORITHMS', sdata)
resolve(sdata)
},
error: reject
})
})
},
// 分页获取算法列表 重构后写法
getListTest: async ({ commit }, params) => {
let sdata = await apiGetList(params)
commit('LIST_ALGORITHMS', sdata)
}
// ...
}
复制代码
视图中调用方式基本区别不大,不须要再作请求错误处理。
views/Algorithms/AlgorithmsCenter.vue
methods: {
...mapActions('algorithm', [
'getList',
'getListTest',
'getCount'
]),
// ...
getAlgorithmsList ({start = 1, length = 10} = {}) {
// this.getList({page: Math.ceil(start / length), size: length, labels: this.algoType}).catch(error => errorTip(this, error)) // 当前调用方式
this.getListTest({page: Math.ceil(start / length), size: length, labels: this.algoType})// 重构后调用方式
}
// ...
}
复制代码
mock数据的搭建有利于在先后端开发进度不一样步的状况下进行模拟数据请求,就能够根据请求完成前端的交互逻辑,合理的配置能够在有真实接口后稍做更改就能够调用线上真实接口,处理手法更平滑。
Mock的搭建有不少种方式,在这里推荐使用json-server的使用方式,本质就是利用json-server快捷的启动一个mock服务器,而后在config/index/dev/proxyTable中代理请求到mock服务器便可。
具体配置过程能够参考 json-server配置详解
其实EasyMock这个网站也提供来便利的mock数据的方式,再也不须要本身去搭建mock环境,简直是很是棒了