VUE
一. MVVM模型和MVC模型区别
- MVVM模型
1.定义
MVVM 是Model-view-ViewModel 的简写,即模型-视图-视图模型
模型: 指的是后端传递的数据
视图: 指的是所看到的页面
视图模型: mvvm 模型的核心,它是连接view 和model 的桥梁。它有两个方向:
- 一是:将模型转化成视图,即将后端传递的数据转化成所看到的页面,实现的方式是:数据绑定。
- 二是:将视图转化成模型,即将所看到的页面转化成后端的数据,实现的方式是:DOM 事件监听
这两个方向都实现的,我们称之为数据的双向绑定。
- ViewModel 观察者(obsever)
在MVVM 的框架下视图和模型是不能直接通信的。它们通过ViewModel 来通信,ViewModel 通常要实现一个obsever 观察者,当数据发生变化,ViewModel 能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel 也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。并且MVVM中View 和 ViewModel 可以相互通信。
-
MVVM 流程图
-
MVC
-
定义
MVC 是Model-View-Controller 的简写,即模型-视图-控制器
模型和视图和MVVM中一样
控制器: Controller 指的是页面业务逻辑
- MVC 模型的目的
将M 和 V 的代码分离
- MVC 是单向通信
就是View 和 Model 必须通过Controller 来承上启下
- MVVM 和 MVC 两者的区别
并不是VM 完全取代了C,ViewModel 存在目的在于抽离Controller 中展示的业务逻辑,而不是替代Controller,其它视图操作业务等还是应该放在Controller中实现。也就是说MVVM实现的是业务逻辑组件的重用。
二. 在MVVM框架下的VUE介绍
- Vue中三者含义
Vue是基于MVVM模型实现的一套框架
在vue中:
Model: 指的是js 中的数据,如对象、数组
View: 指的是页面视图
ViewModel: 指的是vue 实例对象
- 为什么说Vue是一个渐进式的JavaScript框架
- 一:如果你已经有一个现成的服务端应用,你可以将Vue 作为该应用的一部分嵌入其中,带来更加丰富的交互体验
- 二:如果你希望将更多业务逻辑放在前端来实现,那么Vue的核心库及其生态系统也可以满足你的各式需求(core+vuex+vue-route)。和其他前端框架一样,Vue允许你将一个网页分割成复用的组件,每个组件都包含属于自己的HTML、CSS、JavaScript 用来渲染网页中相应的地方
- 三:如果我们构建一个大型的应用,在这一点上,我们可能需要将东西分割成为各自的组件和文件,vue有一个命令行工具,使快速初始化一个真实的工程变得非常简单
总结:以上这三个例子,是一步步递进的,也就是说对Vue的使用可大可小,它都会有相应的方式来整合到你的项目中
- Vue最独特的特性:响应式系统
Vue 是响应式的,也就是说当我们的数据变更时,Vue 会帮你更新所有网页中用到它的地方。
- 深入响应式原理
建议看文档:cn.vuejs.org/v2/guide/re…
- 如何追踪变化
- 一:当你把一个普通的JavaScript对象传入Vue实例作为data 选项,Vue 将遍历此对象所有属性,并使用Object.defindProperty 把这些属性全部转为getter/setter
- 二:这些getter/setter 对用户来说是不可见的,但是在内部它们让Vue 能够追踪依赖,在属性被访问和修改时通知变更
- 三:每个组件实例都对应一个watcher 实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的setter 触发时,会通知watcher,从而使它关联的组件重新渲染
- 检测变化的注意事项
受现代JavaScript 的限制(而且Object.也已经被抛弃),Vue 无法检测到对象属性的添加和删除。由于Vue 会在初始化实例时对属性执行getter / setter 转化,所以属性必须在data 对象上存在才能让Vue 将它转换为响应式的。
var vm = new Vue({
data: {
someObj: {
name: 'ben'
}
}
})
vm.age = 18 // 是非响应式的
Vue.set(vm.someObj, 'age', 18)
或
vm.$set(vm.someObj, 'age', 18) // 是响应式的
var someObj = {grade:750,heigth:180}
vm.some = Object.assign(someObj, vm.some)
对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用Vue.set(object, propertyName, value) 或 vm.$set(object, propertyName, value) 方法向嵌套对象添加响应式属性。
有时需要为已有的对象赋值多个新属性,可以使用Object.assign(),用原对象与要混合进去的对象的属性一起创建一个新的对象。
- 异步更新队列
Vue 在更新DOM 时是异步执行的,只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一个事件循环中发生的所有数据变更。如果同一个watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作是非常重要的。然后,在下一个的事件循环“tick” 中,Vue刷新队列并执行实际(已去重的)工作。
<div id="example">{{message}}</div>
var vm = new Vue({
el: '#example',
data: {
message: '123'
}
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
vm.$el.textContent === 'new message' // true
})
或
vm.$nextTick(function () {
this.$el.textContent === 'new message' // true
})
当你设置vm.someData = 'new value',该组件不会立即重新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。如果你想基于更新后的DOM 状态来做点什么,可能有些棘手。为了数据变化之后等待Vue完成更新DOM,可以在数据变化之后立即使用Vue.nextTick(callback),这样回调函数将在DOM 更新完成后被调用。
因为$nextTick() 返回一个Promise 对象,所以可以使用async/await
methods: {
updateMessage: async function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
await this.$nextTick()
console.log(this.$el.textContent) // => '已更新'
}
}
三. 生命周期钩子函数
beforeCreate: 在beforeCreate 钩子函数调用的时候,是获取不到props 和data 中的数据
created: 然后执行created 钩子函数,已经可以访问到之前不能访问的数据,但是这时候组件还没挂载,所以是看不到的
beforeMount: 接着执行beforeMount 钩子函数,开始创建vritual dom(VDOM,虚拟dom)
mounted: 最后执行mounted 钩子,并将vritual dom 渲染为真实dom 并且渲染数据。组件中如果有子组件的话,会递归挂载子组件,只有当所有子组件全部挂载完毕,才会执行根组件的挂载钩子
beforeUpdate: 数据更新前
updated:数据更新后
beforeDestroy: 适合移除事件、定时器等等,否则可能会引起内存泄露的问题。如果有子组件的话,也会递归销毁字组件
destroyed:所有子组件都销毁完毕后才会执行根组件的 destroyed 钩子函数
keep-alive: keep-alive 独有的生命周期,分别为activated 和 deactivated。用keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行activated 钩子函数
四、组件通信
-
组件通信分为
-
父子组件通信
父组件通过props 传递数据给子组件,子组件通过emit 发送事件传递数据给父组件,典型的单向数据流
- 兄弟组件通信
对于这种情况可以通过查找父组件中的子组件实现,也就是 this.clildren ,在$children 中可以通过组件 name查询到需要的组件实例,然后进行通信
- 跨多层级组件通信
provide / inject
// 父组件
exprot default {
provide: {
data: 1
}
}
// 子组件
export default {
inject: ['data'],
mounted() {
// 无论跨几层都能获得父组件的data 属性
console.log(this.data) // 1
}
}
- 任意组件
这种方式可以通过Vuex 或者 Event Bus 解决
五、extend
作用是扩展组件生成一个构造器,通常会与$mount 一起使用
// 创建组件构造器
let Component = Vue.extend({
template: '<div>test</div>'
})
// 挂载到#app 上
new Component().$mount('#app')
// 除了上面的方式,还可以用来扩展已有的组件
let SuperComponent = Vue.extend(Component)
new SuperComponent({
created() {
console.log(1)
}
})
new SuperComponent().$mount('#app')
六、mixin 和 mixins 区别
- mixin
用于全局混入,会影响到每个组件实例,通常插件都是这样做初始化的
Vue.mixin({
beforeCreate() {
// ...逻辑
// 这种方式会影响到每个组件的beforeCreated 钩子函数
}
})
- mixins
最常使用的扩展组件的方式,如果多个组件中有相同的业务逻辑,就可以就这些逻辑剥离出来,通过mixins混入代码,如:上拉、下拉加载数据等等
七、computed 和 watch 区别
- computed
computed 是计算属性,依赖其他属性计算值,并且computed 的值有缓存,只有当计算值变化才会返回内容
- watch
watch 监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作
- 总结
一般来说需要依赖别的属性来动态获得值的时候可以使用computed,对于监听到值的变化需要做一些复杂业务逻辑的情况可以使用watch
vm.$watch('obj', {
// 深度遍历
deep: true,
// 立即触发
immediate: true,
// 执行的函数
handler: function(val, oldVal) {}
})
var vm = new Vue({
data: { a: 1 },
computed: {
aPlus: {
// this.aPlus 时触发
get: function () {
return this.a + 1
},
// this.aPlus = 1 时触发
set: function (v) {
this.a = v - 1
}
}
}
})
八、keep-alive 组件有什么作用
如果你需要在组件切换的时候,保存一些组件的状态防止多次渲染,就可以使用keep-alive 组件包裹需要保存的组件