vue总结

178 阅读8分钟

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动

一、理解MVVM

mvvm 是Model-View-ViewModel的缩写Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View代表UI组件,负责将数据模型转化成UI展现出来;ViewModel监听模型数据的改变和控制视图行为、处理用户交互

二、vue的特点

1.轻量级框架 Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合;
2.简单易学中文文档,国人开发;
3.双向数据绑定 声明式渲染是数据双向绑定的主要体现,同样也是 Vue.js 的核心,它允许采用简洁的模板语法将数据声明式渲染整合进 DOM;
4.组件化开发 在一个大型应用中,有必要将整个应用程序划分为组件,以使开发更易管理;
5.视图,数据,结构分离数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作。

三、vue双向绑定的实现

采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

四、v-if VS v-show

v-if是‘真正’的条件渲染,因为它确保在切换过程中条件块内的时间监听器和子组件适当地被销毁和重建;初始条件为true,才会渲染条件块。
v-show不管初始条件是什么,元素总会被渲染,只会基于css(display)进行切换。
因此,如果需要非常频繁地切换则使用v-show较好;运行时条件很少改变,则v-if较好;

五、v-for渲染key的作用

当vue正在更新使用v-for渲染的元素列表时,默认使用‘就地复用’的策略。如果数据项的顺序被改变,vue将不会移动DOM元素来匹配数据项的顺序,而是就地更新每个元素。key是每一个虚拟节点的唯一id,在内存中可以根据key更快拿到旧节点和新节点进行对比进行局部更新(缺点:无法复用),使用key时,会基于key的变化重新排列元素顺序,并且会移除key不存在的元素,如果仅展示可以绑定index,排序绑定key,数据不返回id,可借助shortid。

六、vue生命周期

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数。

  • beforeCreate(创建前)在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。此时不能访问 data、ref,Vue 实例对象上仅有生命周期函数及部分默认事件。
  • create(创建后)在实例创建完成后立即调用。实例已完成一下的配置:数据观察(data observer),property和方法的运算,watch/enent事件回调。挂载阶段还没开始,$el property目前不可用。
  • beforeMount(挂载前)在挂载开始之前被调用:相关的 render 函数首次被调用。 实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。
  • mounted(挂载后)实例被挂载后调用,el被新创建的vm.$el替换。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。
  • beforeUpdate(更新前)数据更新时调用,发生在虚拟DOM打补丁之前。这里适合在更新之前访问现有的 DOM。比如手动移除已添加的事件监听器。
  • Updated(更新后)由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
  • beforeDestroy(销毁前)实例销毁之前调用。在这一步,实例仍然完全可用。
  • Updated(更新后)实例销毁后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。 代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title></title>
  <script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
  <div id="app">
    {{message}}
  </div>
</body>
<script>
  var vm = new Vue({
    el: '#app',
    data: {
      message: 'Vue生命周期'
    },
    beforeCreate: function() {
      console.group('------beforeCreate创建前状态------');
      console.log("%c%s", "color:red" , "el     : " + this.$el); //undefined
      console.log("%c%s", "color:red","data   : " + this.$data); //undefined
      console.log("%c%s", "color:red","message: " + this.message)
    },
    created: function() {
      console.group('------created创建完毕状态------');
      console.log("%c%s", "color:red","el     : " + this.$el); //undefined
      console.log("%c%s", "color:red","data   : " + this.$data); //已被初始化
      console.log("%c%s", "color:red","message: " + this.message); //已被初始化
    },
    beforeMount: function() {
      console.group('------beforeMount挂载前状态------');
      console.log("%c%s", "color:red","el     : " + (this.$el)); //已被初始化
      console.log(this.$el);
      console.log("%c%s", "color:red","data   : " + this.$data); //已被初始化
      console.log("%c%s", "color:red","message: " + this.message); //已被初始化
    },
    mounted: function() {
      console.group('------mounted 挂载结束状态------');
      console.log("%c%s", "color:red","el     : " + this.$el); //已被初始化
      console.log(this.$el);
      console.log("%c%s", "color:red","data   : " + this.$data); //已被初始化
      console.log("%c%s", "color:red","message: " + this.message); //已被初始化
    },
    beforeUpdate: function () {
      console.group('beforeUpdate 更新前状态===============》');
      console.log("%c%s", "color:red","el     : " + this.$el);
      console.log(this.$el);
      console.log("%c%s", "color:red","data   : " + this.$data);
      console.log("%c%s", "color:red","message: " + this.message);
    },
    updated: function () {
      console.group('updated 更新完成状态===============》');
      console.log("%c%s", "color:red","el     : " + this.$el);
      console.log(this.$el);
      console.log("%c%s", "color:red","data   : " + this.$data);
      console.log("%c%s", "color:red","message: " + this.message);
    },
    beforeDestroy: function () {
      console.group('beforeDestroy 销毁前状态===============》');
      console.log("%c%s", "color:red","el     : " + this.$el);
      console.log(this.$el);
      console.log("%c%s", "color:red","data   : " + this.$data);
      console.log("%c%s", "color:red","message: " + this.message);
    },
    destroyed: function () {
      console.group('destroyed 销毁完成状态===============》');
      console.log("%c%s", "color:red","el     : " + this.$el);
      console.log(this.$el);
      console.log("%c%s", "color:red","data   : " + this.$data);
      console.log("%c%s", "color:red","message: " + this.message)
    }
  })
</script>
</html>

七、vue 指令

v-textv-htmlv-showv-ifv-forv-on 缩写:@v-bind缩写::v-modelv-slot...

八、计算属性和侦听器

计算属性

计算属性写在vue实例中的computed参数中,是一个函数,返回计算出来的值。模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:

<div id="example">
  {{ message.split('').reverse().join('') }}
</div>

里是想要显示变量 message 的翻转字符串。当你想要在模板中的多处包含此翻转字符串时,就会更加难以处理。 所以,对于任何复杂逻辑,你都应当使用计算属性。

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})
//结果
Original message: "Hello"
Computed reversed message: "olleH"
计算属性vs方法
<p>Computed reversed message: "{{ reversedMessage()}"</p>
// 在组件中
methods: {
  reversedMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

可以将同一个函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于他们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。如果你不希望有缓存,请用方法来替代

计算属性vs侦听属性

Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。

  <div id="app">
    姓:<input v-model="firstName" /><br/>
    名:<input v-model="lastName" /><br/>
    <div>{{fullName}}</div>
  </div>
  var vm = new Vue({
    el: '#app',
    data: {
      firstName: '',
      lastName: ''
    },
    computed: {
      fullName: function () {
        //计算属性的一个方法,方法的返回值作为属性值
        return this.firstName + ' ' + this.lastName;
      }
    }
  })

侦听器

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的

  <div id="app">
    姓:<input v-model="firstName" /><br/>
    名:<input v-model="lastName" /><br/>
    <div>fullName:{{fullName}}</div>
    <div>count:{{count}}</div>
  </div>
  var vm = new Vue({
    el: '#app',
    data: {
      firstName: '',
      lastName: '',
      count:0
    },
    computed: {
      fullName: function () {
        //计算属性的一个方法,方法的返回值作为属性值
        return this.firstName + ' ' + this.lastName;
      }
    },
    watch:{
      fullName:function(){
        this.count++
      }
    }
  })

其实监听器的作用是,监听某个数据的变化,一旦这个数据发生变化我就可以在监听器中做相应的业务处理。

watch的高级用法

使用watch时有一个特点,就是当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行。如果我们需要在最初绑定值的时候也执行函数,则就需要用到immediate属性

handler方法和immediate属性
    <p>FullName: {{person.fullName}}</p>
    <p>FirstName: <input type="text" v-model="person.firstName"></p>
  const vm = new Vue({
    el: '#app',
    data: {
      firstName: 'Bella',
      lastName: 'Anna',
      fullName: ''
    },
    person: {
       handler(newName, oldName) {
          console.log(newName)
          this.person.fullName = newName.firstName + '+++' + this.person.lastName;
        },
        // 代表在wacth里声明了firstName这个方法之后立即先去执行handler方法
        immediate: true,
        //deep:true
      }
    }
  })

上述代码可以看出拿不到初始化和变化的值,immediate: true,就有初始值。

当父组件向子组件动态传值时,子组件props首次获取到父组件传来的默认值时,也需要执行函数,此时就需要将immediate设为true
之前我们写的 watch 方法其实默认写的就是这个handler,Vue.js会去处理这个逻辑,最终编译出来其实就是这个handler。 immediate:true代表如果在 wacth 里声明了 viewDetials之后,就会立即先去执行里面的handler方法,如果为 false就跟我们以前的效果一样,不会在绑定的时候就执行

deep属性

deep,默认值为false,代表是否深度监听。

可以看出随着input输入值的变化发生实时改变。 不设置immediate和deep深度监听,input框实时变化的值是得不到的,而且初始化的值也不会显现,加上immediate:true,初始化的值会出现,但是在input框中改变值,不会发生改变,只有加上deep:true,span里面的值才会随着input框里面的值变化而实时改变。

补充

持续更新 未完