脚手架构建
// 1.安装 vue-cli 脚手架构建工具npm install --global vue-cli// 2.构建于 webpack 模板的一个新项目,填写相关项目信息vue init webpack my-project// 3.安装项目依赖npm install
项目目录
按如下文件目录搭建项目框架
src 主要源码目录|-- assets 静态资源,统一管理|-- components 公用组件,全局组件|-- javascript JS相关操作处理|-- ajax axios封装的请求拦截|-- filters 全局过滤器|-- utils 全局封装的工具类|-- datas 模拟数据,临时存放|-- router 路由,统一管理|-- store vuex, 统一管理|-- views 视图目录|-- order 视图模块名|-- |-- orderList.vue 模块入口页面|-- |-- orderDetail.vue 模块入口页面|-- |-- components 模块通用组件文件夹
UI 框架选择
- PC 端:依次推荐使用 antDesign, iView
- 移动端:依次推荐使用 vant,vux
css 预处理器
推荐使用 less,scss , 可在 common.css 设置全局样式,如
- 常用样式设置原子类名
- 主题颜色, UI 设计规范等样式
全局组件公共样式
.colorTheme {color: #40a9ff !important;}.fl {float: left;}.fr {float: right;}.clearfix:after {clear: both;content: '';display: block;width: 0;height: 0;visibility: hidden;}
移动端适配
以蓝湖 750px 设计稿。
- 使用 rem 适配,代码书写 px,用 px2rem-loader 将 px 转化为 rem。
const px2remLoader = {loader: 'px2rem-loader',options: {remUnit: 100 //1rem=多少像素 这里的设计稿是750px。}}
拆分路由
在 Vue 项目中使用路由,相信大家都已经很熟悉怎么使用了,要新增一个页面的话,需要到路由配置中配置该页面的信息。
但是如果页面越来越多的话,那么如何让我们的路由更简洁呢?
根据不同的业务模块进行路由拆分,在每个子模块导出一个路由配置数组,如 userCard.js 导出会员卡模块的路由,order.js 导出订单模块的路由
在路由根目录在 index.js 中导入所有子模块const routes = [{path: '/userCardList',component: function(resolve) {require(['@/view/userCard/userCardList'], resolve)}},{path: '/userCardEdit',component: function(resolve) {require(['@/view/userCard/userCardEdit'], resolve)}}]
axios 请求封装import Vue from 'vue'import Router from 'vue-router'import userCard from '.userCard'import order from './order'let routes = [...userCard, ...order]Vue.use(Router)export default new Router({mode: 'hash',routes: routes})
设置请求拦截和响应拦截
封装 get 和 post 请求方法const PRODUCT_URL = 'https://test-o2o-store-all.iauto360.cn'const MOCK_URL = 'http://39.104.49.240:19090'let http = axios.create({baseURL: process.env.NODE_ENV === 'production' ? PRODUCT_URL : MOCK_URL})// 请求拦截器http.interceptors.request.use(config => {// 设置token,Content-Typevar token = sessionStorage.getItem('UserLoginToken')config.headers['token'] = tokenconfig.headers['Content-Type'] = 'application/json;charset=UTF-8'// 请求显示loading效果if (config.loading === true) {vm.$loading.show()}return config},error => {vm.$loading.hide()return Promise.reject(error)})// 响应拦截器http.interceptors.response.use(res => {vm.$loading.hide()// token失效,重新登录if (res.data.code === 401) {// 重新登录}return res},error => {vm.$loading.hide()return Promise.reject(error)})
把 get,post 方法挂载到 vue 实例上。function get(url, data, lodaing) {return new Promise((resolve, reject) => {http.get(url).then(response => {resolve(response)},err => {reject(err)}).catch(error => {reject(error)})})}function post(url, data, loading) {return new Promise((resolve, reject) => {http.post(url, data, { loading: loading }).then(response => {resolve(response)},err => {reject(err)}).catch(error => {reject(error)})})}export { get, post }
工具类函数封装// main.jsimport { get, post } from './js/ajax'Vue.prototype.$http = { get, post }
添加方法到 vue 实例的原型链上
在 main.js 通过 vue.use()注册export default {install (Vue, options) {Vue.prototype.util = {method1(val) {...},method2 (val) {...},}}
命名规范import utils from './js/utils'Vue.use(utils)
——让团队当中其他人看你的代码能一目了然
- 文件夹和文件命名以业务或者模块名字为主,驼峰式命名。
- 组件命名遵循以下原则,使用驼峰命名(
carLib)进行组件声明,使用短横线分隔命名(<car-lib></car-lib>)进行使用。 - 当项目中需要自定义比较多的基础组件的时候,比如一些 button,input,icon,建议以一个统一的前缀如 Base 开头,这样做的目的是为了方便查找。
- method 方法命名使用驼峰式,动词+名词,如 getData, submitForm
- 变量命遵循语义化原则,使用驼峰式。
编码规范
vue 风格推荐
Prop 定义应该尽量详细。
使用 v-for 必须加上 key 值// badprops: ['status']// goodprops: {status: String}// betterprops: {status: {type: String,required: true,validator: function (value) {return ['syncing','synced','version-conflict','error'].indexOf(value) !== -1}}}
不要把 v-if 和 v-for 同时用在同一个元素上。<!-- bad --><ul><li v-for="todo in todos">{{ todo.text }}</li></ul><!-- good --><ul><li v-for="todo in todos" :key="todo.id">{{ todo.text }}</li></ul>
组件的 data 必须是一个函数<!-- bad --><ul><li v-for="user in users" v-if="shouldShowUsers" :key="user.id">{{ user.name }}</li></ul><!-- good --><ul v-if="shouldShowUsers"><li v-for="user in users" :key="user.id">{{ user.name }}</li></ul>
组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。// badVue.component('some-comp', {data: {foo: 'bar'}})// goodVue.component('some-comp', {data: function() {return {foo: 'bar'}}})
指令缩写// bad{{fullName.split(' ').map(function (word) {return word[0].toUpperCase() + word.slice(1)}).join(' ')}}// good// 在模板中{{ normalizedFullName }}// 复杂表达式已经移入一个计算属性computed: {normalizedFullName: function () {return this.fullName.split(' ').map(function (word) {return word[0].toUpperCase() + word.slice(1)}).join(' ')}}
<!-- bad --><input v-bind:value="newTodoText" :placeholder="newTodoInstructions" v-on:input="onInput" /><!-- good --><input :value="newTodoText" :placeholder="newTodoInstructions" @input="onInput" />
关于组件内样式
为组件样式设置作用域
若要改变第三方组件库的样式,需要加上顶级作用域。/* bad */<style>.btn-close {background-color: red;}</style>/* good */<style scoped>.button-close {background-color: red;}</style>
关于组件结构/* bad */.ivu-input {width: 254px !important;}/* good */.customerForm .ivu-input {width: 254px !important;}/* .customerForm为当前组件的顶级dom */
组件结构遵循从上往下 template,script,style 的结构。
script 部分各方法成员遵循以下顺序放置。 ```protobuf<template><div></div></template><script>export default {}</script><style lang="scss" scoped></style>
- name
- components
- props
- data
- methods
- computed
- watch
- created
- mounted
update ``` 关于注释规范
以下情况需要加注释,以方便代码维护和他人理解公共组件使用说明
- 各组件中重要函数或者类说明
- 复杂的业务逻辑处理说明
- 特殊情况的代码处理说明,对于代码中特殊用途的变量、存在临界值、函数中使用的 hack、使用了某种算法或思路等需要进行注释描述。
- 多重 if 判断语句
其他规范
建议不再使用双引号,静态字符串使用单引号,动态字符串使用反引号衔接。
使用数组展开操作符 … 复制数组。// badconst foo = 'jack'const bar = foo + ',前端工程师'// goodconst foo = 'jack'const bar = `${foo},前端工程师`
使用数组对象解构赋值// badconst len = items.lengthconst itemsCopy = []let ifor (i = 0; i < len; i += 1) {itemsCopy[i] = items[i]}// goodconst itemsCopy = [...items]
使用对象属性速记语法const arr = [1, 2, 3, 4]// badconst first = arr[0]const second = arr[1]// goodconst [first, second] = arr
百度统计const name = 'Luke'const age = 20// badconst obj = {name: name,age: age}// goodconst obj = {name,age}
在 index.html 添加百度统计脚本
在全局路由守卫添加需要统计的页面var _hmt =_hmt ||[](function() {var hm = document.createElement('script')hm.src = 'https://hm.baidu.com/hm.js?d1dbfd0ce8ca70cf72828b2844498250'var s = document.getElementsByTagName('script')[0]s.parentNode.insertBefore(hm, s)})()
router.afterEach(to => {if (window._hmt) {window._hmt.push(['_trackPageview', '/#' + to.fullPath])}})
