VUE

295 阅读8分钟

VUE

一. MVVM模型和MVC模型区别

  1. MVVM模型

1.定义

MVVM 是Model-view-ViewModel 的简写,即模型-视图-视图模型

模型: 指的是后端传递的数据

视图: 指的是所看到的页面

视图模型: mvvm 模型的核心,它是连接view 和model 的桥梁。它有两个方向:

  • 一是:将模型转化成视图,即将后端传递的数据转化成所看到的页面,实现的方式是:数据绑定。
  • 二是:将视图转化成模型,即将所看到的页面转化成后端的数据,实现的方式是:DOM 事件监听

这两个方向都实现的,我们称之为数据的双向绑定。

  1. ViewModel 观察者(obsever)

在MVVM 的框架下视图和模型是不能直接通信的。它们通过ViewModel 来通信,ViewModel 通常要实现一个obsever 观察者,当数据发生变化,ViewModel 能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel 也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。并且MVVM中View 和 ViewModel 可以相互通信。

  1. MVVM 流程图

  2. MVC

  3. 定义

MVC 是Model-View-Controller 的简写,即模型-视图-控制器

模型和视图和MVVM中一样

控制器: Controller 指的是页面业务逻辑

  1. MVC 模型的目的

将M 和 V 的代码分离

  1. MVC 是单向通信

就是View 和 Model 必须通过Controller 来承上启下

  1. MVVM 和 MVC 两者的区别

并不是VM 完全取代了C,ViewModel 存在目的在于抽离Controller 中展示的业务逻辑,而不是替代Controller,其它视图操作业务等还是应该放在Controller中实现。也就是说MVVM实现的是业务逻辑组件的重用。

二. 在MVVM框架下的VUE介绍

  1. Vue中三者含义

Vue是基于MVVM模型实现的一套框架

在vue中:

Model: 指的是js 中的数据,如对象、数组

View: 指的是页面视图

ViewModel: 指的是vue 实例对象

  1. 为什么说Vue是一个渐进式的JavaScript框架
  • 一:如果你已经有一个现成的服务端应用,你可以将Vue 作为该应用的一部分嵌入其中,带来更加丰富的交互体验
  • 二:如果你希望将更多业务逻辑放在前端来实现,那么Vue的核心库及其生态系统也可以满足你的各式需求(core+vuex+vue-route)。和其他前端框架一样,Vue允许你将一个网页分割成复用的组件,每个组件都包含属于自己的HTML、CSS、JavaScript 用来渲染网页中相应的地方
  • 三:如果我们构建一个大型的应用,在这一点上,我们可能需要将东西分割成为各自的组件和文件,vue有一个命令行工具,使快速初始化一个真实的工程变得非常简单

总结:以上这三个例子,是一步步递进的,也就是说对Vue的使用可大可小,它都会有相应的方式来整合到你的项目中

  1. Vue最独特的特性:响应式系统

Vue 是响应式的,也就是说当我们的数据变更时,Vue 会帮你更新所有网页中用到它的地方。

  1. 深入响应式原理

建议看文档:cn.vuejs.org/v2/guide/re…

  1. 如何追踪变化
  • 一:当你把一个普通的JavaScript对象传入Vue实例作为data 选项,Vue 将遍历此对象所有属性,并使用Object.defindProperty 把这些属性全部转为getter/setter
  • 二:这些getter/setter 对用户来说是不可见的,但是在内部它们让Vue 能够追踪依赖,在属性被访问和修改时通知变更
  • 三:每个组件实例都对应一个watcher 实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的setter 触发时,会通知watcher,从而使它关联的组件重新渲染
  1. 检测变化的注意事项

受现代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(),用原对象与要混合进去的对象的属性一起创建一个新的对象。

  1. 异步更新队列

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 钩子函数

四、组件通信

  1. 组件通信分为

  2. 父子组件通信

父组件通过props 传递数据给子组件,子组件通过emit 发送事件传递数据给父组件,典型的单向数据流

  1. 兄弟组件通信

对于这种情况可以通过查找父组件中的子组件实现,也就是 this.parent.clildren ,在$children 中可以通过组件 name查询到需要的组件实例,然后进行通信

  1. 跨多层级组件通信

provide / inject

// 父组件
exprot default {
	provide: {
		data: 1
	}
}

// 子组件
export default {
	inject: ['data'],
	mounted() {
		// 无论跨几层都能获得父组件的data 属性
		console.log(this.data) // 1
	}
}
  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 区别

  1. mixin

用于全局混入,会影响到每个组件实例,通常插件都是这样做初始化的

Vue.mixin({
    beforeCreate() {
        // ...逻辑
        // 这种方式会影响到每个组件的beforeCreated 钩子函数
    }
})
  1. mixins

最常使用的扩展组件的方式,如果多个组件中有相同的业务逻辑,就可以就这些逻辑剥离出来,通过mixins混入代码,如:上拉、下拉加载数据等等

七、computed 和 watch 区别

  1. computed

computed 是计算属性,依赖其他属性计算值,并且computed 的值有缓存,只有当计算值变化才会返回内容

  1. watch

watch 监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作

  1. 总结

一般来说需要依赖别的属性来动态获得值的时候可以使用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 组件包裹需要保存的组件