前言:框架vue2.x,构建工具webpack4.x, node.js 8.11x, 系统window,ES6, Axios
1:vue项目目录文件结构
在这里插入图片描述

备注:在初始化项目后src目录下components只用来存放项目公共组件,不用来存放页面其他组件

2:基于初始化得项目结构,添加开发所需要得必要文件,所有业务代码统一放在src目录下,对于项目层级的划分可以:

  1. 抽离所有的api接口统一管理,每个模块的api也单独管理
  2. 所有单页早src下新建views管理所有页面
  3. 项目使用的所有公共方法,以及相关的工具,统一在src下新建utils文件夹管理
  4. 项目所需要用到的字体统一在src下新建fonts管理
  5. 项目所需要的主题统一在src下新建theme管理
  6. 公共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:关于组件划分原则( 原则上每个组件只做一件事情,组件(模块)不仅仅是为了复用,个人理解有以下几方面:)

  1. 组件化是对实现的分层,是更加有效,
  2. 组件化是对资源的重组和优化。使得项目资源趋于合理
  3. 组件划有利于单元测试
  4. 组件划对于重构也是非常的友好

组件的的设计原则一般有以下情况,可根据实际的业务做以调整(不死板),大的方向基本不变化

  1. 单一职责原则
  2. 追求短小精悍
  3. 避免太多参数(重要)
  4. 避免暴露组件内部实现(重要)
  5. 避免直接操作DOM
  6. 入口检查参数的有效性,出口检查返回的正确性(重要**重要)

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中的逻辑都写在独立的方法中
Logo

前往低代码交流专区

更多推荐