1.组件传参
1. props emit
2.$children $parent
3. provide inject provide: { foo: 'bar'}, inject: ['foo'],
4.ref refs
5.eventBus const EventBus = new Vue()
6. vuex
7.localStorage /sessionStorage
8.$attrs $listeners 子组件( inheritAttrs: false, this.$attrs)
父子组件通信: props; $parent / $children; provide / inject ; ref ; $attrs / $listeners
兄弟组件通信: eventBus ; vuex
跨级通信: eventBus;Vuex;provide / inject 、$attrs / $listeners
2.vue的组件有两种:局部组件和全局组件
其中全局组件的调用方式有三中:
a. Vue.component(component.name, component);
b.指令的方式,指令的组件中不需要定义props
import messageOriginal from '...'
let ConstructMessage = Vue.extend(messageOriginal)
const Instance= {
install:(Vue)=>{
Vue.directive('message',{
bind:(el,binding)=>{
let instanceMessage = new ConstructMessage({
data:{
info:el.getAttribute('message-info'),
fullscreen:!!binding.modifiers.fullscreen
}
})
Vue.nextTick(()=>{
el.appendChild(instanceMessage.$el)
})
}
})
}
}
Vue.use(Instance)
使用:
<div v-message.fullscreen="true" :messageInfo="hello"></div>
fullscreen是组件中定义的参数
c.vue的prototype的继承的方式
import messageOriginal from '...'
let ConstructorMessage = Vue.extend(messageOriginal)
const Intance = (options)=>{
let instanceMessage = new ConstrctorMessage({data:{...options}})
instanceMessage.$mount()
document.appendChild(instanceMessage.$el)
return instanceMessage
}
Vue.prototype.$message = Instance
3. v-model 和 .sync 的区别
v-model 是一个语法糖 value /@input
<myComponent v-model='value'></myComponent>
// 编译后
<myComponent :value="value" @input="(e)=>value = e.target.value"></myComponent>
.sync
<myComponent :value.sync="value"></myComponent>
//编译后
<myComponent :value="value" @update="(val)=>value = val"
本质上都是一样的监听触发事件
区别:
v-model默认对应的是input、textarea。。。组件中不可以有多个
.sync 组件中可以有多个属性使用.sync修饰符
v-model更多的是操作结果,是双向数据绑定的结果
.sync 更多的是一种状态的改变..loading状态,菜单收起还是展开...
v-model 可以在model 属性中进行自定所以prop和event
model: { prop: 'checked', event: 'change' },
4.使用div实现v-model
<div contenteditable="true" v-text="text" @input="inputHandler"></div>
export defalut{
props:{
value:[String,Number]
},
data(){
return {
text: this.value
}
},
methods:{
inputHandler(e){
if(e.target.innerText === this.value) return
this.text= e.tartet.innerText
this.$emit('input',e.target.innerText)
}
}
}
//使用组件
<myInput v-model="value"></myInput>
5. data数据重新初始化
//初始化 data 中的 footData 数据
Object.assign(this.$data.formData, this.$options.data().formData);
//初始化全部的 data数据
Object.assign(this.$data, this.$options.data());
6.动态组件和异步组件
1.动态组件是使用 is来绑定组件
<component v-bind:is="currentTabComponent"></component>
<!-- 失活的组件将会被缓存!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
可以使用keep-alive 将组件进行缓存保存组件中状态
2. 异步组件
我们全局注册组件时一般的方式:
import ExampleComponent from "..."
Vue.component('example-component', ExampleComponent)
1.通过返回回调函数实现异步组件
import ExampleComponent from "..."
Vue.component('example-component',function(resolve,reject){
resolve(ExampleComponent)
})
Vue只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染 (按需加载组件)
2. 使用webpack的code-spliting功能实现
Vue.component('example-conponent',function(resolve){
// 这个特殊的 require 语法将会告诉webpack
// 自动将构建的代码切割成多个包,这些包会通过Ajax请求加载
require(['./components/exampleComponents.vue'],resolve)
})
3.结合 promise
// 这个动态导入会返回一个promise对象
Vue.component('example-component', ()=>import('./components/exampleComponents.vue'))
局部注册的时候也可以直接返回promise的函数
7.Vue directive
一个指令定义对象可以提供5个钩子函数(均为可选)
- bind: 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
- inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)
- update: 所在组件的VNode更新时调用,但是可能发生在其子VNode更新之前。指令的值可能发生了改变,也可能没有,但是你可以通过比较更新前后的值来忽略不必要的模板更新
- componentUpdated:指令所在组件的Vnode及其子VNode全部更新后调用
- unbind: 只调用一次,指令与元素解绑时调用
钩子函数参数:
-
el:指令所绑定的元素,可以用来直接操作DOM
-
binding: 一个对象,包含一下property:
-
name:指令名,不包括v-前缀
-
value: 指令的绑定值,例如:v-my-directive="1+1" 中,绑定值为2
-
oldValue: 指令绑定的前一个值,仅在update 和componentUpdated钩子中可用,无论值是否改变都可用
-
expression:字符串形式的指令表达式。例如v-my-directive="1+1",表达式为“1+1”
-
arg: 传递给指令的参数,可选,例如v-my-directive:foo中,参数为“foo”
-
modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar中,修饰对象为{foo:true,bar:true}
-
vnode:Vue编译生成的虚拟节点
-
oldVnode:上一个虚拟节点,仅在update和componentUpdated钩子中可用
Vue.directive('my-directive',{ bind:function(el,binding,vnode){}, inserted:function(){}, update:function(){}, componentUpdated:function(){}, unbind:function(){} })
8.组件中对props中的参数校验规则复杂,vue提供的无法满足请使用validator
props:{
age:{
required:true,
validator:function(value){
return value >=0 && value<=128
}
}
}
9.双向数据绑定
基于数据劫持的双向数据绑定离不开Proxy与Object.defineProperty等方法对对象/对象属性的劫持,实现一个完整的双向数据绑定需要三个要点;
- 利用Proxy或Object.defineProperty生成的Observer针对对象/对象的属性进行劫持,在属性发生变化后通知订阅者
- 解析器Compile解析模板中Directive(指令),收集指令依赖的方法和数据,等待数据变化然后进行渲染
- Watcher属于Observer和Compile桥梁,它将收到的Observer产生的数据变化,并根据Compile提供的指令进行试图渲染,使得数据变化促使视图变化。
10.MVC和MVVM原理
MVVM和MVC是一种软件架构模式
vue是MVVM中一种在前端层面上的实现。
MVC模式原理,MVC即Model-View-Controller,模型,视图和控制器
Model
- 程序需要操作的数据来源。通常是从数据库、网络请求的数据
- 负责提供数据
View
- 程序用来展示内容的界面,通常是UI组件
- 负责展示数据
Controller
- 程序用于处理Model数据业务逻辑并将结果输送给View的中间层
- 负责处理业务逻辑
优点
- 耦合性低:视图层和业务层分离
- 重用性高:多个View能共享一个Model
- 部署快:责任分离,各司其职,职责清晰
- 可维护性高
缺点:
- Controller角色比重太大,虽然担任控制器的职责,但在开发中做了很多view的操作,VC耦合性较高
MVVM原理
MVVM模式利用了一个工具数据绑定实现了其中的VM。将数据和View绑定在一起,这样一来,数据发生改变,View会即时更新。
Model
- 负责提供实体模型,提供数据
View
- UI组件,这一块只包含UI相关代码,不含有业务逻辑代码
ViewModel
- 只负责业务逻辑
- 将Model提供的实体数据和View中真正展示数据的UI组件通过数据绑定在一起
- VM绑定在一起,Model一改变,View自动更新
优点
- 双向绑定技术,Model变化,view和ViewModel自动更新
- 模块之间职责更清晰
- 控制器功能大都到了view上,大大减轻压力
缺点
- 大项目中Model会很大,长期占有不能释放内存
- view的重用性降低,MVVM中一个View绑定一个Model,不同模块的View都不同,重用困难
11.样式穿透
在网页检查页面元素的时候发现自己写的样式不生效,原因是因为中scoped的问题导致,所以我们需要用到样式穿透:
-
外层类 >>> 想要修改的类名 {}
-
外层类 /deep/ 想要修改的类名{}
-
::v-deep 想要修改的类名 {}
12.Object.defineProperty()和Proxy
对于Object.defineProperty的缺陷
- vue实例创建后,再向其上添加属性,不能监听
当创建一个vue实例时,将遍历所有DOM对象,并为每个数据属性添加了get和set,get和set允许vue观察数据的更改并触发更新。但是,如果你在vue实例化后添加或删除一个属性,这个属性不会被vue处理,改变set和get
可以使用Vue.set(obj,key,value)的方式创建一个响应式属性
2.数组
vue重构了push,pop,shift,unshift,splice,sort,reverse的方法
Ⅰ. Object.defineProperty只能劫持对象的属性,而Proxy是直接代理对象
由于Object.defineProperty只能对属性进行劫持,需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历。而Proxy直接代理对象,不需要遍历操作
Ⅱ. Object.defineProperty对新增属性需要手动进行Observe
由于Object.defineProperty劫持的是对象的属性,所以新增属性时,需要重新遍历对象,对其新增属性在使用Object.defineProperty进行劫持。
也正是因为这个原因,使用Vue给Data中的数组或者对象新增属性时,需要使用vm.$set才能保证新增的属性也是响应式的
13. vue.$nextTick执行原理
由于js是单线程,js设计者把任务分为同步任务和异步任务,同步任务都在主线程上排队执行,前面任务没有执行完成,后面的任务会一直等待;异步任务则是挂在一个任务队列里,等待主线程所有任务执行完成后,通知任务队列可以把可执行的任务放到主线程执行。异步任务放到主线程执行完成后,又通知任务队列把下一个异步任务放到主线程中执行。这个过程一直持续,直到异步任务执行完成,这个持续重复的过程叫做event loop。
浏览器: 宏任务执行完成后执行浏览器渲染。微任务执行在宏任务之前。
nextTick使用mutationObserver渲染微任务,
nextTick执行顺序:宏任务->浏览器渲染->微任务(nextTick)->宏任务->浏览器渲染
14. computed和watch的区别
计算属性computed和监听器watch都可以观察属性的变化从而做出响应,不同的是:
计算属性computed:
- 支持缓存,只有依赖的数据发生变化,才重新计算
- 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
- computed属性值会默认走缓存,计算属性是基于它们响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据计算得到的值
- 如果computed属性值是一个函数,那么默认走get方法;函数的返回值就是属性的属性值;在computed中,属性都有一个get和一个set方法
监听器watch:
-
不支持缓存,数据发生变化,直接会触发相应的操作
-
watch支持异步
-
监听的函数接受两个参数,第一个参数是最新的值,第二个参数是输入之前的值
-
监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据放生变化时,触发其他操作
-
immediate:组件加载立即触发回调函数执行
-
deep:深度监听,为了发现对象内部值的变化,复杂类型的数据时使用注意:deep无法监听到数组的变动和新增,只有以响应式的方式触发才会被监听到。
watch: { a: function (val, oldVal) { console.log('new: %s, old: %s', val, oldVal) }, // 方法名 b: 'someMethod', // 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深 c: { handler: function (val, oldVal) { /* ... */ }, deep: true }, // 该回调将会在侦听开始之后被立即调用 d: { handler: 'someMethod', immediate: true }, // 你可以传入回调数组,它们会被逐一调用 e: [ 'handle1', function handle2 (val, oldVal) { /* ... */ }, { handler: function handle3 (val, oldVal) { /* ... */ }, /* ... */ } ], // watch vm.e.f's value: {g: 5} 'e.f': function (val, oldVal) { /* ... */ } }