data为什么是一个函数?(Vue全解-class2-4th)
结论: 为了保持两个相同组件之间的数据独立性,避免数据共用
完整回答:
1. 组件本身是一个对象,data则是这个对象身上的一个属性;
2. 当组件在被使用的时候,首先它会被当成一个参数传入Vue构造函数以构造一个新的Vue实例,即new Vue(组件对象)
那么假如组件中的data是一个对象的话,如果一个页面中这个组件被多次使用到;
3. 也就是往new Vue()里面传同一个对象至少两次的话,由于data是一个对象,那么data中的数据必然会有一个引用的问题,
也就是说如果对页面中多个相同组件中的任意一个进行数据操作的话,势必会影响了另一个组件;
4. 但假如data是一个方法的话则不要紧,因为通过先调用data方法然后再生成data对象,这样的data数据则是相互独立的
- 引入一个组件的时候,会创建一个对象;挂载这个对象时则会h函数会内部自动为这个对象调用new Vue(demo)创建Vue实例
- 如果我们把同一个组件挂两遍的话,问题就来了
- 如果demo.vue中的data是一个对象的话,那么用new Vue(demo)创建的两个Vue实例所引用的data自然是同一份数据
- 那如果对其中一个组件进行数据操作的话,势必就影响了另一个组件
- 因此,解决方案就是data写成函数,因为这样的话在初始调用demo中的data时会生成一份独立的对象,哪怕你一个页面复用同一个组件也不影响的
数据响应式原理
精简版回答:
- data对象会被Vue监听,所谓的监听就是Vue会将data中的普通属性统统改造成【getter setter属性】
- data对象会被Vue实例vm代理,代理也就是说data对象上的属性被改造成getter setter属性之后会挂载在vm上一份
- 因此呢 每次this.n类似这样的数据修改就都会被Vue监控到,监控原理和eventBus中的on和emit类似,一旦监听到你的数据有所更新便会调用render函数局部更新页面
详实版回答:
- Vue中的数据响应式保证了我们在初始化Vue实例或者组件时当作参数传入的data对象中的属性一旦与视图绑定之后,后续只要操纵实例中的数据便可更新页面数据。
- 这一块的底层原理是用到了es5中的Object.defineProperty,把普通属性重写为与原属性相同名字的【getter setter属性】并配合代理模式(代理模式是Vue源码中的proxy函数,这个函数把接收到的参数都挂在Vue实例自身),它能让Vue实例vm全权拿到了数据的修改权,因此才能在方法中调用this.n修改数据的类似操作。
- 这一块的内容,Vue精妙地处理了传入的对象,如果我们在创建Vue实例的时候,给data传入一个你自定义的对象myData也是可以的,那么通过调试我们可以知道,假如原本这个myData中存在一个n为1,那么在创建Vue实例之前,这仅仅是一个普通属性,而在我们把myData传入Vue实例之后,myData会被Vue调用defineProperty函数去将普通属性n重写为【getter setter属性】然后data才能被Vue纳入监控了。我们都知道调用defineProperty函数创建一个【getter setter属性】时,【getter setter属性】是要基于一个存在的数据源去改写的,因为【getter setter属性】作用就是动态返回给你最新结果,那么如何保证这个数据源不会被肆意篡改呢?也就是安全性问题是如何解决的?设置【getter setter属性】常规的方式有两种,一种是全局上let一个属性用这个属性保存初始值,然后对一个对象调用defineProperty将这个初始值改成对象自身的一个【getter setter属性】,还有一种就是你在对象自身提前创建好一个下划线属性保存初始值,然后调用defineProperty利用这个下划线属性在自身新增一个【getter setter属性】也就是不带下划线的,当然这个下划线属性名字随便起也不一定要带下划线,保证不重名即可,因为defineProperty设置【getter setter属性】时是没办法return属性自身的,不然就进入无限递归了。因此上面两种方式都不好,因为都存在数据源会被轻易篡改的问题,要么就是会在自身多出一个多于属性的问题,那么其实还有一种方式也就是Vue用的,既可以保证数据安全性,又可以避免自身挂载一个多余属性的问题,也就是在函数内部设置一个中间量,getter setter的操作围绕这个中间量即可。Vue的解决方案就是把数据源也就是对象当作函数参数的形式传入,并且Vue构造函数会对myData中每个属性创建一份中间量value,比如你有个属性n,它就会函数内部创建一个中间量nValue,nValue的值也就是n,但因为value是函数内部参数,所以任何人也无法篡改这个值,这就保证了防篡改,同时,Vue利用value这个中间量对数据源中的n改写成【getter setter属性】,利用中间量的好处就在于避免了产生多余属性的问题,最后再把这些【getter setter属性】在Vue实例vm身上再创建一份让vm全权代理,因此this.n才可以操纵数据
computed method watch 区别
1.computed中文叫计算属性,本质是依赖data拼凑一个新的属性,并且我们可以把它当作属性一样在模板中使用, 首次进入页面就会渲染
定义时基础写法是把一个计算属性写成函数的形式,返回值是data对象中几个数据拼凑而成;
定义时高级写法是写作一个对象,其中有两个方法set和get,分别设置就可以把计算属性设置为【getter setter属性】;
一旦与计算属性相关联的属性被修改了,计算属性就会被动更新,但存在缓存机制的,只要数值不变就不会多次调用的。
2.watch中文叫侦察属性,它是对data对象中的数据监听回调,一旦监听到数据发生变化就会执行回调,回调中允许有两个参数 新值和旧值。 watch 默认情况下不会一进页面就渲染,而是需要设置 immediate:true
watch是异步的,如果不想每次值得修改都触发,则需要手动设置判断条件去规避,同时,为了解决异步问题可以使用 this.$nextTick 将任务安排在监听之后执行。保证任务执行顺序。
它有两个高级用法,immediate:true和deep:true,由于watch默认情况是被动执行的,只有页面初始化以后,被监听的数据先发生变化了,它才会被动执行,而页面初始化过程中创建数据从无到有的过程是不算内的,immediate:true 则可以解决这个问题; deep:true 的意思是在一个object中,object.a发生变化可以触发object整体的监听属性!
- methods 首次进入页面就会渲染,之后页面上任意data经改动,都会再次渲染 methods,所以如果计算量大的情况下,method的计算开销太大了
Vue双向绑定
本质就在问
v-model的原理你了解吗 答:Vue中通过v-model实现双向绑定,当我们把数据绑给页面后,修改实例的数据,页面上的数据会自动更新;修改页面上的数据,实例中的数据也会自动更新.v-model其实就是:value与@input两者的语法糖,当v-model作用于input输入框时,原理是监听输入框的input事件,当v-model作用于单选复选下拉框时,原理是监听change事件 扯得更多一点可以说,v-model最初是尤雨溪借鉴angular1中的ngModel,只适用于监听input事件,但是后来尤雨溪发现v-model很好用,于是就利用v-model传数据的原理拓展到不仅仅跨组件间双向数据绑定且不仅仅支持input事件,那就是.sync这个语法糖的来源。
v-model .sync 区别
v-model可用于单组件也可以用于跨组件通信,.sync主要用于跨组件通信- 两者在跨组件通信时都需要子组件触发
$emit操作 - 两者都是语法糖, 在跨组件通信时
v-model="xxx"相当于是@input="xxx=$event" :value="xxx",:xxx.sync相当于是@update:xxx="xxx=$event" :任意一个prop名="xxx" v-model相当于传递prop名仅限于value的值,而使用.sync则可以使用任意的prop名,v-model只能传递一个属性,.sync能用于多个属性
.sync的作用
答:.sync的作用是数据双向绑定,当子组件改变了父组件传来的 prop 的值时,这个变化也会同步到父组件中。
.sync,作为一种语法糖存在,等价于一个父组件上有一个监听器,自动监听子组件定义好的一个自定义事件,这个自定义事件的格式必须是update:propName
v-if v-show 区别
v-if首次加载页面时不一定会加载,它是通过控制dom节点的存在与否来控制元素显示隐藏,因此它的切换有一个加载/卸载的过程,v-show首次加载页面时必定会加载,原理是基于简单的css切换,- 性能消耗:
v-if有更高的切换消耗;v-show有更高的初始渲染消耗; - 如果需要频繁切换,则使用
v-show较好,否则就用v-if多个页面切换,又得保持切换前的状态,用v-show挺好的,但也会损耗性能,所以建议用keep-alive缓存.
什么叫单向数据流
父组件通过prop传递给子组件data,当父组件的data值受到修改会影响子组件,但是子组件如果无法直接修改prop去影响父组件
Vue.use 与 Vue.prototype.$xx
- 如何初始化插件?Vue.use()
- 在使用
饿了么的时候就会用到Vue.use - Vue.use必须在new Vue()之前,
import Vue from 'vue'
import Element from 'element-ui'
Vue.use(Element)
Element是个对象, 上面有version等字段, 关键有个install函数,Vue.use会自动运行这个函数
通常情况下,install函数中会包含很多的Vue.prototype.$xx这句话就是把属性挂载到构造函数Vue的原型对象上去,以便后续在任意组件内部直接调用this.$xx,
如何避免这个属性被篡改?
Object.defineProperty(Vue.prototype, '$xxx', {
writable: false,
value(){
console.log('我是行货!')
}
});
Vue.use的作用是什么?
- 用于安装/初始化 Vue 插件。从而不需要在每个组件文件中
import插件,Vue.use的源码中规定了插件可以是两种类型,对象或者是函数,如果插件是一个对象,则对象中必须包括install方法。如果插件是一个函数,则整个会被当作install方法 - 比如
this.$route install方法中会接收到一个Vue的参数,也就是Vue构造函数
为什么使用axios不需要Vue.use?
- 因为axios本身不是一个标准的
Vue插件,自身没有一个叫install的函数 - 它只是一个同时支持node端和浏览器端的
http库
this.$nextTick有什么用?
异步执行,类似setTimeout, 在更新了数据之后,视图往往不会立刻刷新,如果立刻获取页面上的数据会拿不到,如果希望等到整个视图都渲染完毕再拿到正确的值,那么就可以使用$nextTick