# MVVM
MVVM
是Model-View-ViewModel
缩写,也就是把MVC中的Controller演变成ViewModel
。Model层代表数据模型,View
代表UI组件,ViewModel
是View
和Model
层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。
# Vue 响应式原理
Vue 的响应式原理是核心是通过 ES5 的保护对象的
Object.defindeProperty
中的访问器属性中的 get 和 set 方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM树上。
# 组件通讯
# props
这个是非常常用的属性,就是父传子的属性; props 值可以是一个数组或对象;
// 数组
props:[]
// 对象
props:{
inpVal:{
type:Number, //传入值限定类型
// type 值可为String,Number,Boolean,Array,Object,Date,Function,Symbol
// type 还可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认
required: true, //是否必传
default:200, //默认值,对象或数组默认值必须从一个工厂函数获取如 default:()=>[]
validator:(value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
# $emit 和 $on
触发子组件触发父组件给自己绑定的事件,其实就是子传父的方法
// 父组件
<home @title="title">
// 子组件
this.$emit('title',[{title:'这是title'}])
# vuex
state:定义存贮数据的仓库 ,可通过this.$store.state 或mapState访问
getter:获取 store 值,可认为是 store 的计算属性,可通过this.$store.getter 或
mapGetters访问
mutation:同步改变 store 值,为什么会设计成同步,因为mutation是直接改变 store 值,
vue 对操作进行了记录,如果是异步无法追踪改变.可通过mapMutations调用
action:异步调用函数执行mutation,进而改变 store 值,可通过 this.$dispatch或mapActions
访问
modules:模块,如果状态过多,可以拆分成模块,最后在入口通过...解构引入
# attrs和 listeners
attrs
包含了父作用域中不作为prop
被识别 (且获取) 的 attribute 绑定 (class
和style
除外)。当一个组件没有声明任何prop
时,这里会包含所有父作用域的绑定 (class
和style
除外), 即attrs
可以获取子传父中未在props
定义的值
listeners
包含了父作用域中的 (不含.native
修饰器的)v-on
事件监听器。可以通过v-on="$listeners"
传入内部组件
// 父组件
<father title="这是标题" width="80" height="80" imgUrl="imgUrl"/>
// 子组件
mounted() {
console.log(this.$attrs) //{title: "这是标题", width: "80", height: "80", imgUrl: "imgUrl"}
},
//父组件
<son
name="name"
age="18"
gender="666"
sdf="asd"
@click="isClick"
@change="asd"
v-bind="$attrs"
v-on="$listeners"
></son>
//子组件
mounted() {
console.log(this.$attrs);
console.log(this.$listeners);
this.$listeners.isClick();
this.$listeners.asd();
},
补充: inheritAttrs
默认情况下父作用域的不被认作 props 的 attribute 绑定将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。通过设置 inheritAttrs
到 false
,这些默认行为将会被去掉。
默认值:true
示例
// 父组件调用子组件
<child-component aaa="1111"></child-component>
//子组件
<div class="child">子组件</div>
export default {
inheritAttrs: true,
mounted() {
console.log('this.$attrs', this.$attrs)
}
}
</script>
设置 inheritAttrs: true
(默认)
设置 inheritAttrs: false
不管inheritAttrs
为true
或者false
,子组件中都能通过$attrs
属性获取到父组件中传递过来的属性。
# provide和inject
provide
和inject
主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中; 并且这对选项需要一起使用; 以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效
provide
和inject
绑定并不是可响应的。这是官方刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
//父组件:
provide: { //provide 是一个对象,提供一个属性或方法
foo: '这是 foo',
fooMethod:()=>{
console.log('父组件 fooMethod 被调用')
}
},
// 子或者孙子组件
inject: ['foo','fooMethod'], //数组或者对象,注入到子组件
mounted() {
this.fooMethod()
console.log(this.foo)
}
# parent和children
指定已创建的实例之父实例,在两者之间建立父子关系。子实例可以用
this.$parent
访问父实例,子实例被推入父实例的$children
数组中。
//父组件
mounted(){
console.log(this.$children)
//可以拿到 一级子组件的属性和方法
//所以就可以直接改变 data,或者调用 methods 方法
}
//子组件
mounted(){
console.log(this.$parent) //可以拿到 parent 的属性和方法
}
# $refs
// 父组件
<home ref="home"/>
mounted(){
console.log(this.$refs.home) //即可拿到子组件的实例,就可以直接操作 data 和 methods
}
# $root
当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自己。
// 父组件
mounted(){
console.log(this.$root) //获取根实例,最后所有组件都是挂载到根实例上
console.log(this.$root.$children[0]) //获取根实例的一级子组件
console.log(this.$root.$children[0].$children[0]) //获取根实例的二级子组件
}
# .sync
// 父组件
<home :title.sync="title" />
//编译时会被扩展为
<home :title="title" @update:title="val => title = val"/>
// 子组件
// 所以子组件可以通过$emit 触发 update 方法改变
mounted(){
this.$emit("update:title", '这是新的title')
}
# EventBus
- 声明一个全局Vue实例变量 EventBus , 把所有的通信数据,事件监听都存储到这个变量上;
- 类似于 Vuex。但这种方式只适用于极小的项目
- 原理就是利用on和o**n和emit 并实例化一个全局 vue 实现数据共享
// 在 main.js
Vue.prototype.$eventBus=new Vue()
// 传值组件
this.$eventBus.$emit('eventTarget','这是eventTarget传过来的值')
// 接收组件
this.$eventBus.$on("eventTarget",v=>{
console.log('eventTarget',v);//这是eventTarget传过来的值
})
**注释:**对应的事件名eventTarget必须是全局唯一的
# 路由传参
1.方案一
// 路由定义
{
path: '/describe/:id',
name: 'Describe',
component: Describe
}
// 页面传参
this.$router.push({
path: `/describe/${id}`,
})
// 页面获取
this.$route.params.id
2.方案二
// 路由定义
{
path: '/describe',
name: 'Describe',
component: Describe
}
// 页面传参
this.$router.push({
name: 'Describe',
params: {
id: id
}
})
// 页面获取
this.$route.params.id
3.方案三
// 路由定义
{
path: '/describe',
name: 'Describe',
component: Describe
}
// 页面传参
this.$router.push({
path: '/describe',
query: {
id: id
`}
)
// 页面获取
this.$route.query.id
三种方案对比 方案二参数不会拼接在路由后面,页面刷新参数会丢失
# observable
让一个对象可响应。Vue 内部会用它来处理
data
函数返回的对象。返回的对象可以直接用于渲染函数 (opens new window)和计算属性 (opens new window)内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器,用于简单的场景;
# 生命周期
生命周期 | 详解 |
---|---|
breforeCreate | 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。 |
created | 创建完毕,data中有值,未挂载 |
beforeMount | 在挂载开始之前被调用:相关的 render 函数首次被调用。 |
mounted | 实例被挂载后调用,这时 el 被新创建的 vm.$el 替换了,此时才可以操作DOM |
beforeUpdate | 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。 |
updated | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。该钩子函数不会保证所有的子组件也对会被一起重绘,可以调用$nextTick |
activated | 被 keep-alive 缓存的组件激活时调用。 |
deactived | 被 keep-alive 缓存的组件停用时调用。 |
beforeDestroy | 实例销毁之前调用。在这一步,实例仍然完全可用。 |
destroyed | 实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。 |
# 开发技巧
记录一些平时积累的开发小技巧
# 登录验证
前景介绍:有多个按钮给添加点击事件,但是需要对这些进行登录验证,登录的情况下正常执行事件,未登录则执行跳转事件。
技巧:使用函数闭包,和函数方法apply
<div class="demo" @click="$method(handleClick2, 12, 'zhangsan ')"></div>
handleClick2(type, name) {
console.log("子级点击事件", type);
console.log("名字", name);
console.log(this.info);
},
//注册全局事件
Vue.prototype.$method = function(fn, ...args) {
if (true) {
return fn.apply(this, args);
} else {
console.log("登录失败");
}
};