关于vue项目一些总结
前言:框架vue2.x,构建工具webpack4.x,node.js 8.11x,系统window,ES6,Axios1:vue项目目录文件结构备注:在初始化项目后src目录下components只用来存放项目公共组件,不用来存放页面其他组件2:基于初始化得项目结构,添加开发所需要得必要文件,所有业务代码统一放在src目录下,对于项目层级的划分可以:抽离所有的api接口统一...
前言:框架vue2.x,构建工具webpack4.x, node.js 8.11x, 系统window,ES6, Axios
1:vue项目目录文件结构
备注:在初始化项目后src目录下components只用来存放项目公共组件,不用来存放页面其他组件
2:基于初始化得项目结构,添加开发所需要得必要文件,所有业务代码统一放在src目录下,对于项目层级的划分可以:
- 抽离所有的api接口统一管理,每个模块的api也单独管理
- 所有单页早src下新建views管理所有页面
- 项目使用的所有公共方法,以及相关的工具,统一在src下新建utils文件夹管理
- 项目所需要用到的字体统一在src下新建fonts管理
- 项目所需要的主题统一在src下新建theme管理
- 公共css文件在src下新建style管理
3:编码命名
1.驼峰命名法(camelCase): 又称为小驼峰命名法,除第一个单词的首字母小写,其他单词的首字母均大写。
2. 帕斯卡命名法(PascalCase): 又称为大驼峰命名法,所有单词的首字母均大写。常用于类名、命名空间
3. 下划线命名法(Underline): 多个单词之间以下划线连接,大写常用于常量,小写常用于文件命名。
4. 短横线命名法(kebab-case): 多个单词之间以短横线(-)连接,单词全部小写。
5. 匈牙利命名法(Hungarian): 通过在变量名前面加上相应的小写字母的符号标识作为前缀,标识除变量的作用域、类型等。广泛应用于Windows编程中。
1:命名
1:变量使用Camel(驼峰)命名法,例如
var loadingModules = {}
2:函数使用Camel命名法,例如
function stringFormat() {}
3:常量使用全部字母大写,单词间下划线分割,例如
var HTML_ENTRY = {}
4:建议boolean类型的变量使用 is 或者 has 开头,例如
var isTrue = false;
var hasMoreInfo = true
5:项目文件夹的命名一般用小写,例如
notice
6:在html中 对于class的命名统一使用短横线命名法,例如
<div class="head-wrapper"></div>
7:在组件中,对于组件name的命名(子组件),统一采取短横线命名法,例如
export default {
name: 'fatherName-childrenName'
}
4:代码规范
1:代码规范(css)(基于scss)
备注:关于缩进问题,无论是编辑器还是eslint规范(编辑器的格式化会基本达到标准的规范),还是个人习惯,都没有太大影响,保持自己的习惯就行,这里只是大致做一些基本的规范
1:对于超长的样式,在样式值得空格或者逗号后换行,建议按照逻辑分组,例如
// 不同属性值按照逻辑分组
background:
transparent url(xxxx)
no-repeat 0 0;
2:scss得嵌套层级3层为宜,例如
.wrapper {
width: 120px;
.header-wrapper {
height: 120px;
.title {
font-size: 16px;
}
}
}
3:当一个rule包含多个selector时,每个声明得选择器必须独占一行,例如
.container,
.wrapper,
.comment {
font-size: 12px
}
4:属性选择器中值必须时双引号包围,例如
article[character="wanggang"] {
font-size: 12px;
}
5:属性值必须领起一行,例如
.wrapper {
margin: 0;
padding: 0
}
6:属性值必须以分号结尾,例如
.wrapper {
font-size: 10px;
}
7:建议对于z-index进行分层,对于文档流外的绝对定位的视觉层进行管理。
8:当css的属性中 url函数中路劲不加引号,例如
body {
// good
background: url(path)
// bad
background: url("path")
}
9:关于scss混合,定义全局公用的css方法,通过@include调用,避免重复写css代码片段,例如
//全局css方法
@mixin radius ($radius:5px) {
border-radius: $radius;
height: 12px;
width: 120px;
}
//组件引用
.wrapper {
@include: radius(10px);
}
2:javascript(vue)基本规范
<script>
export default {
// 组件名称
name: 'name',
// 引入组件
components: {
xxx
},
// 接受数据
props: {
xxx
},
// 过滤器
filter: {
xxx: { xxx }
},
// 混入
mixins: [ xxx ],
// 组件实例数据
data() {
return {
xxx
}
},
// 计算属性
computed: {
xxx
},
// 监听属性变化 执行异步或者是开销比较大的操作
watch: {
xxx
},
// 渲染函数 jsx
render(h) => {
xxx
},
mounted() {
//页面初始化方法
},
methods: {
//实例方法集合
}
}
</script>
以上并没有列出所有的生命周期,只是针对于现有项目大多数情况下通用的一种情况,原则上,将生命周期函数尽量放下面,而将,mixin,data, computed, fliter,components, props等尽量放在上面,以便能够快速读懂组件。
2:关于组件划分原则( 原则上每个组件只做一件事情,组件(模块)不仅仅是为了复用,个人理解有以下几方面:)
- 组件化是对实现的分层,是更加有效,
- 组件化是对资源的重组和优化。使得项目资源趋于合理
- 组件划有利于单元测试
- 组件划对于重构也是非常的友好
组件的的设计原则一般有以下情况,可根据实际的业务做以调整(不死板),大的方向基本不变化
- 单一职责原则
- 追求短小精悍
- 避免太多参数(重要)
- 避免暴露组件内部实现(重要)
- 避免直接操作DOM
- 入口检查参数的有效性,出口检查返回的正确性(重要**重要)
3:公共方法的封装
前面有说到公共方法可以在utils中新建文件夹统一管理,然后再调用的地方调用,还将方法挂载到Vue的实例上,在组件中调用,例如:
// 示例代码 ① 在utils中统一管理实例代码
/**
* 让时间戳按照指定的日期格式显示的方法
* @author wanggang
* @param { String } 格式字符串
* @return { String } 返回生成的日期时间字符串
* @example
* let d = +new Date();
* let pattern = "YYYY-MM-DD";
* return formatDate(d, pattern);
*/
export function formatDate(timestamp,pattern) {
/*
* eg:pattern="YYYY-MM-DD hh:mm:ss";
*
*/
var d = new Date(timestamp);
var o = {
"W+" : d.getDay() == 0 ? 7 : d.getDay(), //week
"M+" : d.getMonth() + 1, //month
"D+" : d.getDate(), //day
"d+" : d.getDate(), //day
"h+" : d.getHours(), //hour
"m+" : d.getMinutes(), //minute
"s+" : d.getSeconds(), //second
"q+" : Math.floor((d.getMonth() + 3) / 3), //quarter
"S" : d.getMilliseconds(), //millisecond
"f+" : d.getMilliseconds()
}
if (/(Y+)/.test(pattern) || /(y+)/.test(pattern)) {
pattern = pattern.replace(RegExp.$1, (d.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(pattern)) {
pattern = pattern.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
}
}
return pattern;
}
备注:对于相对复杂的公共方法,原则上建议 使用标准的多行注释代码块,包括基本的方法名:@parmas(参数,类型),@return (返回值,类型),@example(使用案例),至于创建时间,更新时间,邮箱,作者等可忽略
// 示例代码 ② 在utils中统一管理实例代码
/**
* 生成随机数的方法
* @param { Number } min 生成随机数的最小值
* @param { Number } max 生成随机数的最大值
* @return { Number } 返回生成的随机数
*/
export function getRandom() {
return Math.floor(Math.random() * (max - min + 1) + min);
}
备注: 对于一般性的方法,应具备 @params 和 @return,并且和值对应的类型
// 示例代码 ③ 在utils中统一管理实例代码
/*** 返回字节数 ***/
export function byteLength(value) {
return value.replace(/[^\x00-\xff]/g, "**").length;
}
备注: 对于相对简单的方法 可以做一行注释(多上注释的规则)
// 示例代码 ① 挂载在vue原型上
Vue.prototype.getWindowHeight = function() {
return window.innerHeight || document.documentElement.height || document.body.clientHeight
}
组件中以 this.getWindowHeight()调用。不过建议以统一在utils中统一管理,需要使用的时候引入相关的方法。尽量避免过多使用this,和过多在prototype上挂载方法属性等。
4:关于请求(Axios)(http.js)
// 请求库
import axios from 'axios';
import { Message } from 'element-ui';
// 设定超时
axios.default.timeout = 10000;
// post请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
// 请求拦截器 对request
axios.interceptors.request.use(config => {
// 发起请求之前判断 仅仅为示例代码
// 假设token存在localStorage中
// 如果token存在,在每次得请求header上带上token
// token存在,但是token已经过期,在响应拦截器中对返回得状态进行判断
const token = window.localStorage.token;
token && (config.headers.Authorization = token)
return config
}, err => {
return Promise.reject(err);
});
// 请求拦截器 对response
axios.interceptors.response.use(response => {
if(response.status === 200){
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
}, err => {
if(err.response){
let status = err.response.status;
switch(status) {
// 401未登陆
// 未登陆 则条状到登陆页面,并且携带当前页面得path
// 登陆成功后 返回当前页面
case 401:
router.replace({
path: '/login',
query: {
redirect: router.currentRouter.fullPath
}
});
break;
// 403 token过期
// 登陆过期 对用户进行提示
// 登陆成功返回当前页面
// 跳转登陆页
case 403:
Message({
message : '登陆过期,请重新登陆',
duration: 2000
})
break;
case 404:
Message({
message: '网络请求不存在',
duration: 2000
})
break;
// 其他错误统一抛出,如果要对其中得返回码进行其他操作,在视情况在加case 进行操作
default:
Message({
message : error.response.message,
durantion : 2000
})
}
}
return Promise.reject(err);
})
// 请求封装
/**
* GET 封装
* @param url
* @param data
* @return { Promise }
*
*/
export function get(url, params = {}) {
return new Promise((resolve, reject) => {
axios.get(url, {
params: params
})
.then((res) => {
resolve(res.data)
})
.catch((err) => {
reject(err)
})
})
}
/**
* POST 封装
* @param url
* @param data
* @return { Promise }
*
*/
export function post(url, params = {}) {
return new Promise((resolve, reject) => {
axios.post(url, params)
.then((res) => {
resolve(res.data)
})
.catch((err) => {
reject(err)
})
})
}
/**
* PATCH 封装
* @param url
* @param data
* @returns { Promise }
*
*/
export function patch(url, params = {}) {
return new Promise((resolve, reject) => {
axios.patch(url, params)
.then((res) => {
resolve(res.data)
})
.catch((err) => {
reject(err)
})
})
}
/**
* put 封装
* @param url
* @param data
* @returns { Promise }
*
*/
export function put(url, params = {}) {
return new Promise((resolve, reject) => {
axios.put(url, params)
.then((res) => {
resolve(res.data)
})
.catch((err) => {
reject(err)
})
})
}
/**
* deltet 封装
* @param url
* @param data
* @returns { Promise }
*
*/
export function deleteItem(url, params = {}) {
return new Promise((resolve, reject) => {
axios.delete(url, params)
.then((res) => {
resolve(res.data)
})
.catch((err) => {
reject(err)
})
})
}
接口管理(示例)
在src目录下新建文件夹api
// 文件夹
src
-api
-setting
-xxx
-xxx
// 在api下对每个模块新建文件夹统一管理
// 以setting模块为例,如下:
/**
* setting 模块接口
*/
// 引入对应的方法
import { get, post, put, deleteItem, patch } from '../../utils/http'
/**
* 接口配置列表 interfaceList
*/
export const interfaceList = (params) => get('/setting/api', params)
/**
* 获取属性列表 getAttributes
*/
export const getAttributes = (params) => get('/pdm/attribute', params)
......
在组件中调用如下:
<script>
// 代码仅作为例子
import { getAttributes } from 'api/setting/index.js'
export default {
data() {
return {
dataAttr : [],
arr : ['category_attributes','attribute_values'],
defaultPropsAttr : {
children : "children",
label : "value"
},
}
},
mounted() {
let params = {
id : 1,
value: '小蓝瓶'
}
getAttributes(params).then( res => {
if(res.success){
this.dataAttr = res.data.map(this.deepMap)
}
}).then(() => {}).then(() => {})
},
methods: {
deepMap(v, index) {
let keys = this.arr.find(key => v[key]);
let val = v[keys]
if(!val)
return {
...v,
value:v.valus
}
return {
...v,
value: v.name
children: val.map(this.deepMap)
}
}
}
}
</script>
备注:1:以上代码仅仅为示例代码
2:在模块中引入对应的方法,方法接受参数(对象),将对象传入,then回调中获取结果,
3:如果再then回调中有比较多的业务代码,原则上,写在独立的方法中,如上述案例,
4:如果有多层then回调,原则上应该将then中的逻辑都写在独立的方法中
更多推荐
所有评论(0)