data的值 如何初始化vue_Vue相关知识点总结(上)

本文详细介绍了Vue的生命周期,包括beforeCreate、created、beforeMount、mounted等阶段,阐述了数据如何通过Object.defineProperty实现双向绑定,并讲解了计算属性、侦听器、组件通信以及slot插槽的工作原理和应用场景,帮助理解Vue的响应式系统和组件交互机制。
摘要由CSDN通过智能技术生成

考点一:生命周期

new Vue()之后。 Vue 会调用_init函数进行初始化,也就是这里的init过程,它会初始化生命周期、事件、 props、 methods、 data、 computed 与 watch 等。其中最重要的是通过Object.defineProperty设置settergetter函数,用来实现「响应式」以及「依赖收集」。

vue生命周期总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。

  • beforeCreate (创建前)在beforeCreat阶段,vue实例的挂载元el还没有。vue实例的挂载元素$el和数据对象 data都是undefined, 还未初始化;
  • created (创建后) 完成了 data数据初始化, el还未初始化;
  • beforeMount (载入前) vue实例的$eldata都初始化了, 相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上;
  • mounted (载入后) 在el 被新创建的 vm.$el替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
  • beforeUpdate (更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
  • updated (更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。当data变化时,会触发beforeUpdate和updated方法;
  • beforeDestroy (销毁前) 在实例销毁之前调用。实例仍然完全可用。
  • destroyed (销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。

考点二:双向绑定

把一个普通 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty() 把这些属性全部转为 getter/setter。

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

  • 当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 都加上 setter和getter 这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
  • compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
  • Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
    1、在自身实例化时往属性订阅器(dep)里面添加自己
    2、自身必须有一个update()方法
    3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
  • MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

考点三:计算属性和侦听器

1.计算属性

1.1计算属性例子:

<body>
<div id="root">
  姓:<input v-model="firstName" type="text">
  名:<input v-model="lastName" type="text">
  <div>{{fullName}}</div>
</div>
  
<script>
  new Vue({
    el: "#root",
    data: {
      firstName: '',
      lastName: ''
    },
    computed: {
      fullName: function() {
        return this.firstName + "" + this.lastName
      }
    }
  })
</script>
</body>

c1e4eea5f1ca556f5afdd68c95c48479.png

https://jsbin.com/lixuwujaqe/edit?html,output

1.2 关于计算属性是否会影响原值的面试题:

输入框b的值是由输入框a的值+1计算得到的,若认为更改b 的值后a的值会相应变化吗?

<body>
<div id="vue_det">
    <input type="text" v-model="a">
    <input type="text" v-model="b" @input="handleInput">
     <button @click="handlePrint">打印a、 b的值a:{{a}} b: {{b}}</button>
</div>
<script type="text/javascript">
    var vm = new Vue({
        el: '#vue_det',
        data: {
            a: 1
        },
        computed: {
          b: function() {
            return (this.a + 1)
          }
        },
        methods: {
            handleInput() {
              console.log(this.a, 'a')
              console.log(this.b, 'b')
            },
            handlePrint() {
              console.log(this.a, 1)
              console.log(this.b, 2)
            }
        }
    })
    
</script>
</body>

运行结果为不会。

ff55b3e28d7227557bc383fa30afd2f6.png

http://js.jirengu.com/falubuyefu/1/edit

2.侦听器

侦听器例子:

<body>
  <div id="root">
    姓:<input v-model="firstName" type="text">
    名:<input v-model="lastName" type="text">
    <div>{{fullName}}</div>
    <span>{{count}}</span>
  </div>

  <script>
    new Vue({
      el: "#root",
      data: {
        firstName: '',
        lastName: '',
        count: 0
      },
      computed: {
        fullName: function() {
          return this.firstName + "" + this.lastName
        }
      },
      watch: {
        firstName: function() {
           this.count++
        },
        lastName: function() {
           this.count++
        }
      }
    })
  </script>
</body>

f7f015e2af953a5b897558dc21cbdeef.png

当然,也可以侦听计算属性,效果是一样的:https://jsbin.com/pusuhuviba/edit?html,output

f6f326273ecbf9045d014b41905a1168.png

考点四:组件通信

先由“组件”开始讲,庞大的大型项目可以细分拆成很多部分,每一部分就是一个小型的组件,这样维护每个组件就相对简单很多。实际上,每一个组件又是一个new Vue()产生的实例。任何一个Vue项目都是由千千万万个 Vue的实例组成的。

例子1:一个vue实现的Todo: https://jsbin.com/maxuziwaka/edit?html,output

56dd8101eb5ca7f182baf998b3332575.png
<body>
<div id="root">
  <div>
    <input v-model="inputValue" type="text">
    <button @click="handleSubmit">提交</button>
  </div>
  <ul>
    <todo-item 
      v-for="(item, index) of list"
      :key="index"
      :content="item"
    >
    </todo-item>
  </ul>
</div>
<script>
  // 这种方式定义的是全局组件
  Vue.component('todo-item', {
    props: ['content'],
    template: '<li>{{content}}</li>'
  })
  
  new Vue({
    el: "#root",
    data: {
      inputValue: '',
      list:[]
    },
    methods: {
      handleSubmit: function() {
        this.list.push(this.inputValue)
      }
    }
  })
</script>
</body>

或者也可以定义成局部组件:

<body>
<div id="root">
  <div>
    <input v-model="inputValue" type="text">
    <button @click="handleSubmit">提交</button>
  </div>
  <ul>
    <todo-item 
      v-for="(item, index) of list"
      :key="index"
      :content="item"
    >
    </todo-item>
  </ul>
</div>
  
<script>
  // 这种方式定义的是局部组件
  var TodoItem = {
    props: ['content'],
    template: '<li>{{content}}</li>'
  }
  
  new Vue({
    el: "#root",
    components: {
      'todo-item': TodoItem
    },
    data: {
      inputValue: '',
      list: []
    },
    methods: {
      handleSubmit: function() {
        this.list.push(this.inputValue)
      }
    }
  })
</script>
</body>

如图:https://jsbin.com/revatudoho/edit?html,output

c9eda3480166d0921d54d60d436c36d1.png

一、父子组件之间通信 1.子组件通过props获取父组件的传值,父组件通过v-on监听子组件使用$emit触发的自定义事件。
1.1 父组件通过属性的方式向子组件传递值,如上文中例子1的:content="item"的写法。

1.2 子组件要向父组件通信的话需要使用发布-订阅模式:

例如:子组件里的每一项li添加一项点击就删除该行的功能,则需要传递被点击行的序号信息给父组件。https://jsbin.com/munodusaji/edit?html,console,output

<body>
<div id="root">
  <div>
    <input v-model="inputValue" type="text">
    <button @click="handleSubmit">提交</button>
  </div>
  <ul>
    <todo-item 
      v-for="(item, index) of list"
      :key="index"
      :content="item"
      :index="index"
      @delete="handleDelete"
    >
    </todo-item>
  </ul>
</div>
<script>
  // 这种方式定义的是全局组件
  Vue.component('todo-item', {
    props: ['content', 'index'],
    template: '<li @click="handleClick">{{content}}</li>',
    methods: {
      handleClick: function() {
        this.$emit('delete', this.index)
      }
    }
  })
  
  new Vue({
    el: "#root",
    data: {
      inputValue: '',
      list:[]
    },
    methods: {
      handleSubmit: function() {
        this.list.push(this.inputValue)
      },
      handleDelete: function(index) {
        console.log(index)
        this.list.splice(index, 1)
      }
    }
  })
</script>
</body>

9ce54aa620420b89fb44cbc029375155.png

写在vue-cli搭建的项目里看起来这种父子通信的结构可能更为直观一些:

b33bca32194bcdc6442089d622088f20.png

“TodoList“是父组件(即左编辑栏内),其内部用到了“<todo-item></todo-item>”的子组件,子组件是“TodoItem”(即右编辑栏内)。子组件通过props获取父组件的传值content和index,父组件通过v-on事件监听子组件使用$emit自定义的方法delete。

9f1ca88f275aac43164cf5e8f883a9a2.png

2. 父子组件采用$parent/$children通信。

dd320386ed3a21a9b5acbbf46feb39ea.png

见Vue文档

例子2.1

父组件想要拿到某个子组件内的值realIndex(即在0开头的index的基础上自己加工+1后的子组件的值)要通过$children选取子组件实例。

90192e5a4699f15a12cdb164d26713ee.png

891811a4769284a5624a0597a5a894b7.png

而某个子组件想要获取其父组件的值需要使用$parent得到其父组件实例。

ddce8bc0b77cf10ed547fda5ff035ce1.png

a24b2fb0ad3432de2c5f022defdaab22.png

3.ref方法

官方文档API和应用如下:

7af0d7118e465cd7336fef4f75d11b8a.png

6fab69cadcc466b18542dba1f7c5dc5b.png

例子3.1 比如父组件TodoList.vue想要得到子组件<todo-item>的相关信息可以通过ref特性为这个子组件赋予一个ID引用,然后可以使用 this.$refs.ID来访问这个子组件实例。

注:当refv-for一起使用的时候,你得到的引用将会是一个包含了对应数据源的这些子组件的数组。

46d246c23734686d3070ace53afffa85.png

02861b6c4dbc2e78b1d5aea6011bf692.png

4. slot方法

<slot>插槽可用于父组件向子组件传递HTML等,子组件内的数据想要传递到父组件作用域内使用则需要借助“作用域插槽”来在子组件文件内的<slot>元素上绑定一个特性即“插槽prop”。

详见本文第五节“slot插槽”

二、 跨级通信——爷孙组件典型

例子二 如下图,我们在刚才的项目里主App文件(实际上我们改叫做TodoList.vue了)里新插入里一个子组件<Advertise>,其内部又引入一个可以选择广告服务类型的<JoinKind>子组件(它相对于<TodoList>来说是其孙组件)。

// advertise.vue

<template>
<div class="footer">

  <p>广告位长期招租哦 </p>
  <p>{{contact}}</p>
  <join-kind></join-kind>

</div>
</template>

<script>
import joinKind from "./kind.vue";

export default {
  name: 'Advertise',
  components: {
      'join-kind': joinKind
  },
  data() {
      return {
          contact: '800-***-8820'
      }
  },
  methods: {
  }
}
</script>

21ee5fb4b745b19cfd2a16a05fa36661.png

1. provide/inject 方法

依赖注入不论子组件嵌套得有多深,只要调用了inject那么就能注入provide中的数据,而不局限于只能从当前父组件的props属性中得数据。provideinject主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。并且这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。

614157631ce8eecfc16e08e0cce2219d.png

549e9d969875ae1333475bbe9e8846e3.png

"依赖注入"简单来说就是:父组件中通过 provide来提供变量,然后在子组件或者孙组件等想要获取的地方中通过inject来注入变量 。

3c4aba5fc931c83260932f02d5d9aded.png

2999aa66f34441b85ecd93aed04e349b.png

2. $attrs/$listeners 方法

8453b7463b9660474084305c983dd9a9.png

$attrs$listeners属性像两个收纳箱,一个负责收纳属性,一个负责收纳事件。可以使用v-bind="$attrs"将父组件中不被认为props特性绑定的属性传递给子组件(这样孙组件就能在子组件得到的属性上拿到父组件数据)。

4d5b8dfb29dc34223124f82ee6ed9317.png

80b4c93b28c043d7de375de4f818545f.png

53e9eacc51d1a4667e6969f3f3b11e63.png

还可以使用v-on="$listeners"将父组件中非.native的子组件绑定事件传递给子组件(这样孙组件就能在子组件得到的属性上调用父文件里的事件)。

6b328a964bad709a8a72c18e0467bdcb.png

bb4d30ff6dd3c8fb63368dbfddc657a2.png

a569a563900d977d5b244099e0378713.png

附注:$attrs通常配合 inheritAttrs 选项一起使用,当我们在组件上赋予了一个非Prop 声明时,编译之后的代码会把这些个属性都当成原始属性对待,添加到 html 原生标签上,看上面的代码编译之后的样子:(子组件文件里我们写里inheritAttr:false了故子组件在DOM里没编译出那些属性,但孙组件里编译出来了)

df087b326035a7306db55f0efc0fc2fa.png

我们现在在孙组件里也加上inheritAttrs: false则孙组件也不会露出那些属性了:

1558422956474430e9e5b3829a3d7515.png

84ec97550b806c1e55b21a925b039967.png

e62512d516a74664ba97b6819e344c3d.png

3. 事件总线eventbus 方法

在官网上有个藏得很深的使用例子

1f0c99bd945b942ff008a40a8f577f63.png

7fcf3b3b74f719eabe1833cafd71907e.png

(1)首先需要创建一个事件总线并将其导出, 以便其他模块可以使用或者监听它.

// eventBus.js
import Vue from 'vue'

export const EventBus = new Vue()

(2)假设你有两个兄弟组件:additionNumshowNum, addtionNum.vue 中通过EventBus.$emit()发送事件,showNum.vue 中通过EventBus.$on()接收事件。这样就实现了在组件additionNum.vue中点击相加按钮, 在showNum.vue中利用传递来的num展示求和的结果。

3143938697db6919d629cc29d50b74e2.png

ee13b39e743bd714aac4b8145432d810.png

若需要移除事件的监听:EventBus.$off('addition', {})即可。

4. Vuex全局状态管理

在main.js引入store,注入,新建一个目录store,….. export 等

相关使用实例请见下一篇文章:Vuex使用解析


考点五:slot 插槽

e2582e53f67d0b8df01760d937327db9.png

想象这样的场景: 正常情况下<Child><span>HelloWorld</span></Child>写在组件 Child标签里的span标签会被组件的<template></template>里内容替换覆盖掉,当你想要在父文件的<Child>组件标签内传进内容时就会用到插槽。

例如:<advertise></advertise>是在父组件TodoList.vue里调用的子组件,想要在该文件内往子组件标签插进文字或者HTML是无法直接实现的,

6c4814f26d89c47487d1195a59317fd9.png

e0e5030f16f3df74aa527f6694549f78.png

而写成这样:在子组件的文件里用slot标签“占位”一下,就能起到替换你想要内容的作用。

872f30e057762e49438930ce313f3630.png

45efe7fb1e981bc69469ba66ef609071.png

插槽可通俗理解成“占坑”,即在组件模板中提前占座,待使用组件标签时其内的内容就会自动落座替换坑位(即对应<slot></slot>的位置)。可以通过slot插槽向组件内部指定位置传递内容。父组件向子组件传递“标签”可以通过slot完成。

在slot标签添加样式无效。拥有命名的插槽不能被不含slot属性的标签内容替换,会显示slot的默认值(具名slot具有对应性)。例如:

4a7b7e4c6d36ecd2cf4b0f2fe7d7489a.png

99dea114fd669f0bcf637ae840e7d4ab.png

(一)关于具名插槽:有时我们需要多个插槽,对于这样的情况,<slot>元素有一个特殊的特性:name。一个不带name<slot>出口会带有隐含的名字“default”。在向具名插槽提供内容的时候,我们可以在一个<template>元素上使用v-slot指令,并以v-slot的参数的形式提供其名称,现在<template>元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有v-slot<template>中的内容都会被视为默认插槽的内容。组件中有多个命名的slot插槽时,可以实现父组件对子组件的指定位置显示内容或传参。

例如:下面的2和4均属于“任何没有被包裹在带有v-slot的<template>中的内容都会被视为默认插槽(<slot></slot>)的内容。”

05f8881e4370516143c5aa4f38856a64.png

d51693c4fccc7c96f9af57dd91cad41b.png

(二)关于作用域插槽

5d5d5b1f2c9e29d1cb676f994cb0ba62.png

简单说就是子组件模版内的数据在父文件里是获取不到的,因为作用域不同。现在想要在父级作用域中使用子组件作用域的数据就必须借助“作用域插槽”——比如将子组件内某个量maneger作为<slot></slot>的一个特性绑定上去:<slot name="name" v-bind:person="maneger">{{person}}</slot>,在父级作用域里我们可以给v-slot带一个值来定义我们提供的插槽prop的名字:

<advertise>
      <template v-slot:name="childProps">
        <i>{{childProps.person}}</i>
      </template>
</advertise>

例如:

da321bf8c74244e9651e3bcdba88ec40.png

39764d0c890178cd76b7f78001cfb1b0.png

总结:<slot>插槽可用于父组件向子组件传递HTML等,子组件内的数据想要传递到父组件作用域内使用则需要借助“作用域插槽”来在子组件文件内的<slot>元素上绑定一个特性即“插槽prop”。


本文参考:

如何在Vue面试环节,证明自己值月薪15K?_慕课手记​www.imooc.com
b974faa6acca974693ec2904a96be1f5.png
vue.js2.5基础入门视频教程-慕课网​www.imooc.com
b974faa6acca974693ec2904a96be1f5.png
vue学习之父组件与子组件之间以及兄弟组件之间的通信​blog.csdn.net baozi:Vue开发模式:发布-订阅模式​zhuanlan.zhihu.com
Vue 组件间通信六种方式(完整版)​juejin.im vue中8种组件通信方式, 值得收藏!​juejin.im 关于vue.js中slot的理解 - 云+社区 - 腾讯云​cloud.tencent.com 【Vue-slot】组件间的通信——插槽占位slot​blog.csdn.net Vue中slot的使用(通俗易懂)​blog.csdn.net Vue.js 父子组件通信的1212种方式​juejin.im 剖析 Vue.js 内部运行机制掘金小册​juejin.im 公司要求会使用框架vue,面试题会被问及哪些?_慕课手记​www.imooc.com
b974faa6acca974693ec2904a96be1f5.png
公司要求会使用框架vue,面试题会被问及哪些?_慕课手记​www.imooc.com
b974faa6acca974693ec2904a96be1f5.png
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值