vue2总结 (完整搭建、风格指南融合)

前言/初衷

  • 最近因为遇到了很多规范性问题,感觉到深深的无奈与”愤慨“,但是由于自己的基础知识点还不够扎实,又陷入了”我只知道怎么写好的代码而不知道为什么写好的代码“的纠结,于是我决定开始第三次重新学习vue2(当然这也不会是最后一次)。这也算是对自己所学知识点的整理与归纳吧。
  • 幸运的是:vue2可能就此终结,vue3还远未普及,这篇文章正好应运生于新王登基,旧帝仍在的阶段。这可能会给我的vue2知识总结复盘带来一些所谓”圆满“的意味。
  • 声明:本章仅是我结合vue2风格指南,日常开发习惯,并且仅仅代表我当前阶段对于开发、vue2的理解,之中一定有着各种各样的问题,随着年限的增长与对行业的深耕,思想与思考维度都是时刻改变的。我对成长的恳切淋漓尽致,殷盼着高人与贵人的指点。

我认为规范化的核心:广泛掌握,择优而选

项目搭建

vue安装:
	npm install -g @vue/cli
	vue create 

最好在选择配置时 选择添加路由、vuex等
搭建好环境后的开始是开发公共组件,例如:统一消息提醒模式,统一按钮模式、统一图片上传模式等

项目目录

项目目录的搭建上,我其实是有一些自己的想法的,因为我刚开始接触的是angular7,而angular7对模块化的拆分对我有很多影响,所以在我个人想法中有很多受其影响的部分,也不一定都是对的。再一点是基于我短浅的见识,曾‘有幸‘见过几个格外随意的vue2项目,所以产生了一些对规范化的执念。

下面的目录中,最主要的部分:(我的理解,并不一定好用)

1、将所有的部分,无论组件还是页面都作为文件夹命名,所有最后一层文件夹下都统一为index.vue,这样我们的所有引入都是引入到文件夹,做到统一
2、views目录基于视图:下面区分模块文件夹,每个模块文件夹中有pages和components两个子文件夹,将视图的主页面部分放入pages中,下面所有页面区分文件夹,文件夹下为Index.vue,而views下的components文件夹下是一个一个的此模块下组件。
3、public文件夹:存放公共组件、公共指令、公共管道、公共混入
4、文件命名:大驼峰,与开发者工具对应

├── README.md            项目介绍
├── index.html           入口页面
├── build              构建脚本目录
│  ├── build-server.js         运行本地构建服务器,可以访问构建后的页面
│  ├── build.js            生产环境构建脚本
│  ├── dev-client.js          开发服务器热重载脚本,主要用来实现开发阶段的页面自动刷新
│  ├── dev-server.js          运行本地开发服务器
│  ├── utils.js            构建相关工具方法
│  ├── webpack.base.conf.js      wabpack基础配置
│  ├── webpack.dev.conf.js       wabpack开发环境配置
│  └── webpack.prod.conf.js      wabpack生产环境配置
├── config             项目配置
│  ├── dev.env.js           开发环境变量
│  ├── index.js            项目配置文件
│  ├── prod.env.js           生产环境变量
│  └── test.env.js           测试环境变量
├── package.json          npm包配置文件,里面定义了项目的npm脚本,依赖包等信息
├── src               源码目录  
│  ├── main.js             入口js文件
│  ├── app.vue             根组件
│  ├── public           公共组件目录
│  │  ├── components
		├── ButtonProvide  // 封装公共按钮组件
			└── index.js
		├── MessageTipProvide  // 封装公共提示组件
			└── index.js
│  │  ├── directives
		└── index.js
│  │  ├── filters
		└── index.js
│  │  ├── mixins
		└── index.js
│  ├── assets             资源目录,这里的资源会被wabpack构建
│  │  └── images
│  │    └── logo.png
│  ├── routes             前端路由
│  │  └── index.js
│  ├── store              应用级数据(state)
│  │  ├── modules // 模块化
│   	└── ModuleA
			└──index.js
│		└── ModuleB
			└──index.js
│  │  └── getters.js  // 公共getters
│  │  └── index.js
│  └── views              页面目录(模块化)
│	├── ModelA
│    ├── pages
│		├── HelloAAA
│			└── index.vue
│		├── HelloBBB
│			└── index.vue
│    └── components
│		├── HelloCCC
│			└── index.vue
│		├── HelloDDD
│			└── index.vue
│		├── HelloEEE
│			└── index.vue
├── static             纯静态资源,不会被wabpack构建。
└── test              测试文件目录(unit&e2e)
  └── unit              单元测试
    ├── index.js            入口脚本
    ├── karma.conf.js          karma配置文件
    └── specs              单测case目录
      └── Hello.spec.js

文件规范:

  1. 所有的组件名都使用***大驼峰多个单词命名*** 例如:AbcInfo.vue,这样符合了vue2的开发者工具命名,也符合了与HTML原生标签区分开来的思想(这种思想始于react的自定义组件命名规则)
  2. 所有的组件引用都使用组件命名引用,单标签大驼峰(虽然我们知道引用时可以重命名,但如果各个都重命名对后期维护的成本消耗式巨大的)例如:AbcInfo.vue , import Abc from “…/Abc.vue”(结合风格指南中提到的,通常我们在写Vue.component()时才会进行小写横线连接的命名)
  3. 文件内容规范:

项目语法规范:

  • 1、插值语法:{{}}、拼接语法:${}
  • 2、指令语法:v-bind 统一采用简写(冒号形式:) v-on 统一采用(@形式)
  • 3、data有两种写法:对象形式、函数形式,统一采用函数形式,以避免对象形式带来的数据关联关系(vue3中也取消了data的对象形式)并且使用函数的简写形式data(){}来定义
  • 4、不要将函数写在data中,data中可以写函数,但是data中的数据会进行数据代理,将函数放在data中会多出来函数的数据代理,是无用的,会导致vue很累

模板语法、计算属性computed、监视watch 的选择

  1. 模板中复杂的数据要放到计算属性中,减少模板计算的付出,计算属性就是用来优化模板中数据改变的监听的,要灵活使用
  2. 所有的计算属性都可以用watch来写,但是当computed可以实现的,选择computed而不是watch
  3. computed是不可以开启异步任务的,但watch可以,所以异步的情况选择watch无可厚非

模板规则、模板属性规则

  1. vue2的template标签下都包含着一个div, 这样的设计实际上也是为了避免一些组件中标签不统一的问题,例如:给组件添加一个@click事件,如果组件下有两个根标签,那这个click事件添加给谁呢,(vue3使用Fragment标签解决了这一问题)
  2. 不影响结构,会清除掉,所以这也就是为什么组件最外层会被template包裹的原因,template不能和v-show一块使用,但是可以与v-if v-for一块使用,所以当我们不想添加额外标签时使用标签
  3. v-for 和v-if不要用在同一个标签上,这里就可以借助template标签
  4. v-for需要配合key=""使用,值为唯一值,当对数据没有增删操作时才可以使用下标作为key,(如果使用下标操作,当我们要去删除数组第一项时,会删除最后一项,这就是因为虚拟dom渲染、diff算法的问题),另外v-for是可以遍历对象的
  5. .number .lazy .trim的使用是通常会遗忘的
  6. v-cloak 可以配合css属性来解决样式的展示,[v-cloak]:{display:none}

样式规则

  • 样式库采用分别暴露的形式:
  • 样式修改原则:优先写外部样式的,不写行内样式(不要让模板变得臃肿,不易于维护)
  • css、less、saas样式的选择:尽量使用less,无论选择哪种结构,整个项目尽量使用统一结构
  • 行内、内联样式、外联样式的选择:在使用组件内的

数据处理规范

  • 数组的处理使用如果想要响应式:push pop shift unshift splice sort reverse,因为vue2重写了此7种方法,使其拥有了响应式
  • 。。。。。太多了容我再想想有没有更好方式整理

更新dom规范

  1. n e x t T i c k 的 使 用 : t h i s . nextTick 的使用:this. nextTick使this.nextTick(()=>{})
    vue在执行代码时遵从的原则是:将代码执行完才修改页面dom,所以在下一行代码需要上一行代码执行dom操作完成后才运行时,可以使用$nextTick
  2. dom没更新是否因为响应式缺失:优先处理响应式缺失的问题,$set的添加最后考虑
    vue2无法检测数据变化有四种情况:对象中属性的新增与删除,数组索引位置数据变化,通过length改变数组
    • 数组使用vue重写的7种改变数组的方法(来替代数组length变化与索引位置数据的变化)
    • 对象的属性添加或删除使用应尽量避免,无法避免则使用this.$set(obj,“属性”,“属性值”) 来替换
  3. 强制更新 f o r c e U p d a t e ( ) 极 少 情 况 需 要 强 制 更 新 , 尽 量 不 使 用 即 使 更 新 也 指 定 区 域 更 新 : ‘ t h i s . forceUpdate() 极少情况需要强制更新,尽量不使用 即使更新也指定区域更新:`this. forceUpdate()使使this.refs.table.$forceUpdate()`

自定义指令规范

  • 指令名称统一使用全小写命名,多个单词使用 短杠连接(-)
  • 通常在我们没有特殊需求时,通常以第三种方式编写指令,因为通常指令创建的目标是解决一系列问题,我们也要用 ”一个指令去解决一系列问题“ 的思想来编写指令,所以我们尽量放入公共指令中,一般写成函数形式,结合特殊情况才会将生命周期拆分来写。
1种:组件中
 directives:{  
 	// 函数形式为bind和update的合并写法,缺少了inserted
	big(el,binding){
		// el: 元素本身
		// binding 传入值的集合
	}
 }2种:组件中
 directives:{
	abc:{
		bind(){}   // 指令与元素成功绑定时调用
		inserted(){}  // 指令所在元素插入到页面时
		update(){}  // 指令所在的模板被重新解析时
		componentUpdated(){}// 指令所在组件的 VNode 及其子 VNode 全部更新后调用
		unbind(){} // 解绑时调用
	}
}3种:公共
Vue.directive('abc'(el, binding, vnode)=>{
	// el: 元素本身
	// binding 传入值的集合
	// vnode:vue生成的虚拟节点
})4种:公共
Vue.directive('abc'{
	bind(){}
	inserted(){}
	update(){}
})

自定义管道规范

自定义管道是我特别喜欢写的,但是我很不理解为什么vue3开始将管道剔除了,用computed和methods的使用来替代管道

  • 使用小驼峰命名自定义filter
  • 使用模板中: {{msg | filterName('参数1','参数2')}}
  • 组件中创建filter:
filters:{
	filterName(item) {
		return item+10;
	},
},
  • 公共filter: 一般的日期处理建议放到其中
在这里插入代码片
Vue.filter('dataTime', (data) => {
    if (!data) return '';
    return moment(Number(data)).format(
        "YYYY-MM-DD HH:mm:ss"
    );
});

mixin混入规范

混入的使用如果不规范,将会使代码的维护变得困难。

  • 混入文件中,使用分别导出 export const handle = {}
  • 混入文件和原组件都有的属性,以原组件为主,但是生命周期钩子将会都执行,并且混入的在前,原组件在后

父子组件间传值规范

父->子:
通常使用第一种

  1. :aaa=”传的值“ props:{aaa:{}} prop中的数据采用结合风格指南中的例子:通常使用第二个。
    另外props使用时,组件内时不允许修改值的,虽然检测方式是浅层检测,可以修改对象内的值,但是为了规范,制定不修改props的原则

  2. ref方式 this.$refs.组件内定义好的属性

props: {
  status: {
    type: String,
    required: true,
    validator: function (value) {
      return ['syncing'].indexOf(value) !== -1
    }
    default(){
   		return ""
   	}
  }
}
props: {
  status: {
    type: String,
    required: true,
    default:""
  }
}

子->父:
通常我们使用第二种方式:自定义事件

  1. props方式 传递一个函数给子组件: :addTodo=“addTodo” 通过props接收此函数在需要时调用
  2. 自定义是事件方式 v-on的方式 也就是简写@addTodo=“addTodo” 子组件通过this.$emit(“addTodo”,data)发射事件
  3. ref方式 this. r e f . s t u d e n t R e f . ref.studentRef. ref.studentRef.on(“事件名”,this.getStrdent) 灵活性更强一些 注意后面的触发方法可以在methods中定义,也可以写成箭头函数的形式,但是不能写成function的形式,因为事件会将this付给后面的回调函数,里面的this会指向子组件的vc

非父子组件间传值

  1. 简单兄弟组件:状态提升:数据存放位置提升到统一父组件中,然后向下传,(并不常用)
  2. 路由传参,通过路由传递参数 (跳转页面 传少量值时,优先使用)
  3. 全局事件总线(推荐):利用VueComponent.prototype.__proto__ ==Vue.prototype 将全局事件绑定到一个固定属性上( b u s ) , 在 所 有 组 件 中 就 都 可 以 找 到 bus),在所有组件中就都可以找到 busbus了,注意:销毁是必要的
new Vue({
	el:"#app",
	render:h=>h(app),
	// vm要在new Vue 创建后才有,但是创建完成后意味着代码执行完成,所以要放到new Vue()  的beforeCreate() 声明周期中,刚创建完成时调用
	beforeCreate(){
		Vue.prototype.$bus = this // 安装全局事件总线  $bus就是当前应用的vm
	}
})`
this.$bus.$on() // 监听
this.$bus.$emit() // 调用
beforeDestroy(){  // 销毁绑定的事件
	this.$bus.$off("名称")  
}
  1. 消息的订阅与发布:需要安装插件 推荐:pubsub-js 使用publish
    subscribe发布与订阅消息(当项目中很多处简单的传递时可以使用,如果少有需要的功能没有必要使用),另外:也需要组件销毁时解除绑定pubsub.unsubscribe(this.publd)

  2. vuex (vue风格指南推荐,优先级优于全局事件总线/消息订阅与发布,但不优于路由传参)是否是多于两处需要使用此数据,并且此数据为全局的关键数据,存储的意义大不大

vue动画、过渡规范

css3提供了优秀的动画写法,vue也提供了动画的标签与解决方案,说实话我也没搞懂两者到底使用哪一个会比较好?所以仅供参考,留待之后的学习(也像echart和v-chart到底选择哪一个,原因是什么我还没有理解到位)

对于toC端的项目,对样式的要求严格,并且需求量大,我们统一采用成型的第三方动画库: animate.style

npm install animate.css
// 基本使用:
name="库中的name"
enter-active-class=""
leave-active-class=""

对于toB端的项目,如果对样式的要求停留在能看就行,但是最好有:

配置代理服务器规范

axios规范

插槽规范

插槽的选择:组件的封装过程中,需要不同的组件内部展现形式,而且这种展现是随机的。(类似于elementui中使用来重新定义组件内容),我们没办法在组件内通过v-if列举所有可能,并且这也是不好的,所以与其列举不如放开组件内的一个位置,让外部传入的内容添加到此处。更灵活,更易用。
三种插槽方式:建议统一使用具名插槽,当需要子传父值的时候选择作用域插槽

  1. 默认插槽:
标签体内直接写内容:<hello><img></hello>
hello组件中:<slot>等待数据加载...</slot>定义插入位置,slot标签体中的内容放的为默认值
样式:可以放在slot里也可以写在slot外,这样也增加了维护的难度,所以不建议使用默认插槽
  1. 具名插槽
放的位置增加name:
	<slot name="img"></slot>
使用插槽增加slot:
	<hello>
		<img slot="img">
	</hello>
建议使用<template>包裹:(vue2.6后可使用v-slot:img代替slot="img",只用于template标签)
	<hello>
		<template slot="img">
			<img/>
		</ template>
	</hello>
  1. 作用域插槽:反向传输数据给父组件,可以在父组件中使用插槽时调用子组件的数据
在这里插入代码片
放的位置:通过games将数据传递
	<slot :games="games"></slot>
使用插槽:通过scope获取到数据  或者写成slot-scope
	<hello>
		<template scope="data">
			{{data.games}}  // data:{games:""}
		</template>
	</hello>

路由规范

route:路由 每个路由的详情与参数
router:路由器,整个应该只有一个路由器

路由配置概览

// 使用导航
<router-link  to="/aaa" active-class="active"></router-link>
// 展示位置
<router-view></router-view>
export default new VueRouter({
	mode:'history',// 通常web应用我们都会使用history模式而不是hash模式,让后端处理识别路由
	routes:[
		{
			name:"about",
			path:"/about",
			meta:{isAuth:false}, // 元信息,可以配置标志位用于路由守卫中做权限处理等问题
			// 统一按照vue-router官网建议,使用懒加载方式配置
			component:()=>import('路径'),
			children:[
				{
					path:'child1',
					component:()=>import('')
				}
			]
		}
	]
})

路由守卫 3种守卫 1种周期

  1. 全局前置路由守卫
    初始化的时候被调用、每次路由切换之前被调用

// 接收三个参数:to 去哪的路由对象,from来自哪的路由对象,next() 放行
router.beforeEach((to,from,next)=>{
	next() //必须通过next() 才能跳转到下一步
})
  1. 后置路由守卫
    路由切换后执行:两个参数
    用途:可以用于切换页面标签 meta:{title:“主页”},无需在前置路由守卫中判断是否跳转
router.afterEach((to,from)=>{
	document.title = to.meta.title || "首页"  //并且需要把配置中的title也修改
})
  1. 独享路由守卫
    某一个路由独享的
beforeEnter((to,from,next)=>{})
  1. 组件内路由生命周期
通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from,next){}
刷新路由:
beforeRouteUpdate(to,from,next){}
离开组件时调用:
beforeRouteLeave(to,form,next){}

vuex规范

vuex风格指南:

应该优先通过 Vuex 管理全局状态,而不是通过 this. r o o t 或 一 个 全 局 事 件 总 线 。 通 过 t h i s . root 或一个全局事件总线。 通过 this. root线this.root 和/或全局事件总线管理状态在很多简单的情况下都是很方便的,但是并不适用于绝大多数的应用。

  • 是否是多于两处需要使用此数据,并且此数据为全局的关键数据
  • 路由传参、全局事件总线传递是否优于vuex

store中的属性命名:mutations方法中的参数统一使用全大写字母命名,其他方法中的方法名、属性名统一按照小驼峰命名

注意区分项目大小:不是只有一两个页面的直接无脑选2,便于扩展

1、当项目很小,没有模块化vuex时:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    // 存放字段(使用小驼峰命名)
    state = {
        userName: ''
    },

    mutations = {
        // 全大写命名,接收两个参数(state对象,改变的值)
        SET_USER_NAME: (state, value) => {
            state.userName = value;
        }
    },

    // 重复使用的API调用
    actions = {
        // context,上下文中是一个简化的store,里面包含着commit的上下文方法,所需的数据
        // context.commit("方法",value),利用commit调用mutations里面的方法,如果不需要其他方法可以结构赋值 {commit}
        setUserName(context, value) {
            context.commit("SET_USER_NAME", value)
        }
    },

    // vuex的计算属性,可以调用里面的方法二次更新state数据(state,其他的getters)
    getters = {
        setUserData(state, getters) {
            return state.userName + "很帅";
        }
    }
})

调用时:

// 获取数据
this.$store.state.userName 
// 直接修改数据
this.$store.conmmit("SET_USER_NAME",value) 
// 通过action修改数据
this.$store.dispatch("setUserName",value)
// 通过getter获取修改后的数据
this.$store.getters.getData();

2、当项目很大时,使用模块化的vuex存储数据

// 模块化 配置store的index.js
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import system from './modules/system' // system模块

Vue.use(Vuex)


const store = new Vuex.Store({
    modules: {
        system, // 模块化命名
    },
    getters // 公共getters
})

export default store

modules目录下每个模块vuex文件中:

const state = {};

const getters = {};

const mutations = {};

const actions = {};

export default {
    namespaced: true,  // 开启命名空间
    state,
    mutations,
    actions,
    getters
};

调用时:
直接的方式

// 获取数据
this.$store.state.模块名.userName 
// 直接修改数据
this.$store.conmmit("模块名/SET_USER_NAME",value) 
// 通过action修改数据
this.$store.dispatch("模块名/setUserName",value)
// 通过getter获取修改后的数据
this.$store.getters["模块名/getData"];

统一化的方式:

// 数据获取: 放在computed中  this.a调用
...mapState('a', ['a','b','c'])    模块名,[字段名]形式
...mapGetters('b',['a','b'])
// 数据提交:放在methods, this.a(传参) 调用
...mapMutations('a',['a','b'])
...mapActions('b',['a','b'])  
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值