[VUE篇]
一. vue的优点
- 轻量级框架,大小只有几十kb,中文文档,简单易学
- 组件化,数据双向绑定,数据操作更加方便,可以封装组件,重复使用。
- 虚拟dom: dom的操作本身是很耗时的,vue不在使用原生dom操作,代码更加简洁易读。
二. 组件传值
1. 父子组件传值:
父组件向子组件传值一般实在父组件上通过属性绑定,然后有子组件通过==props==接收父组件传过来的值
子组件向父组件传值:由子组件通过$==emit==()向父组件发送数据,然后由都组件通过事件绑定方式接收子组件的数据
2. 兄弟组件传值:
==事件总线方式==:创建一个事件总线,由一个组件A向组件B发射数据,再有B组件监听得到A组件传递过来的数据。
- 创建一个事件总线:
import Vue from 'vue'
export const EventBus = new Vue()
- A组件向B组件发送数据
emit() {
//表单发生变化,就像兄弟组件发射变化后的表单
EventBus.$emit('searchform', {
searchform: this.searchform, // data中定义的属性
searchInput: this.value // data中定义的属性
})
},
- B组件接受A组件数据
EventBus.$on('searchform', data => {
this.searchform = data.searchform
this.searchInput = data.searchInput
})
三. VUE基础用法
1. v-show和v-if指令的共同点和不同点
共同点: 都是控制元素的显示隐藏
不同点: 实现的方式不一样,v-show是通过控制css样式display:none来隐藏元素,v-if是通过动态添加删除元素来实现显示隐藏
总结: 频繁的操作显示和隐藏应该使用v-show,因为v-if频繁操作会消耗性能。
2. 如何让CSS只在当前组件中起作用?
在组件中的style前面加上scoped
3. keep-alive的作用是什么?
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
4. 说出几种vue当中的指令和它的用法?
- v-model双向数据绑定;
- v-for循环;
- v-if/ v-show 显示与隐藏;
- v-on事件;v-once: 只绑定一次。
5. 为什么使用key?
vue的核心思想是数据驱动视图,避免直接操作dom元素来改变视图中的数据,所有vue中就有了虚拟dom作为数据和视图之间的桥梁,==key值就是给dom元素添加一个唯一标识==,方便diff算法有效快速的找到对应的节点,从而达到==更新虚拟dom的效果==
6. 分别简述computed和watch的使用场景
- computed计算属性: 当一个属性受到多个属性影响的时候就可以使用computed将其作为计算属性,比如: 购物车商品结算的时候
- watch监听属性: 当一个属性可以影响多个属性的时候可使用watch做为监听,当这个属性发生变化的时候可以做一些动作。比如:监听输入框中的内容
7. vue组件中data为什么必须是一个函数?
因为JavaScript的特性所导致,在component中,data必须以函数的形式存在,不可以是对象。
组建中的data写成一个函数,数据以函数返回值的形式定义,这样每次复用组件的时候,都会返回一份新的data,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱。而单纯的写成对象形式,就是所有的组件实例共用了一个data,这样改一个全都改了。
8. 为什么不建议v-if和v-for同时使用
因为v-for的优先级对于v-if,这意味着 v-if 将分别重复运行于每个 v-for 循环中。所以,不推荐v-if和v-for同时使用。
如果v-if和v-for一起用的话,vue中的的会自动提示v-if应该放到外层去。
9. 如何获取dom
在组件上添加ref="domname"属性,然后通过this.$refs.domname获取dom
四. MVC, MVP, MVVM
-
在web发展早期阶段,前端代码和后端代码柔和在一起,维护起来十分不便,前端代码也不好书写和调试,于是逐渐形成了mvc,mvp,mvvm等框架设计规范。
-
三者都是一种框架设计规范,目标都是为了解决model层和view层的耦合问题。
-
MVC模式:出现较早应用于后端,在前端也有所应用,优点是分层清晰,缺点: 数据混乱,controller层臃肿,灵活性低。
-
MVP模式:是由mvc模式进化而来,由p--->presenter作为中间层,解决了model和view之间耦合的问题,负责传递model和view之间的通信,但是由于presenter层过于臃肿,导致维护困难
-
MVVM模式:是在mvc模式基础上进行了再次划分层次,增加了VM-->viewmodel视图模型层,将数据解析,封装的工作交由vm去执行,controller层只需要劫持vm层解析和封装好的数据交给model层保存或者给view层进行渲染。
1. MVC模式:
model(模型):在程序中负责存放数据的部分 view(视图): 在程序中负责显示数据给用户的部分 controller(控制层): 在程序中负责处理数据,和业务逻辑部分,向model发送数据,获取用户输入的数据,或者向view层提供数据部分
斯坦福大学公开课上的这幅图来说明,这可以说是最经典和最规范的MVC标准

再上图中,model层和view层中间是双黄线隔离,但是这部意味着,model和view层是不能交互的,而是不应该,没人能阻止你在写代码的时候在一个M里面去写V,但是一旦你这样做了,那么你就违背了MVC的规范,你就不是在使用MVC了。
总结:M和V之间的关系应该是一种同步的关系,也就是,不管任何时刻,只要M的值发生改变,V的显示就应该发生改变(显示最新的M的内容)。所以我们可以关注M的值改变,而不用关心M的网络请求是否结束了。实际上C根本不知道M从哪去拿的数据,C的责任是负责把M最新的数据赋值给V。所以C应该关注的事件是:M的值是否发生了变化或者view中是否有事件传递过来。
2. mvp模式:
MVP架构模式是Model(模型)、View(视图)、Presenter(表示器)组成。

MVP架构模式最主要是针对Android的MVC架构模式进行改进的,MVP与MVC最不同的一点是M与V是不直接关联的也是就Model与View不存在直接关系,这两者之间间隔着的是Presenter层,其负责调控View与Model之间的间接交互。
3. mvvm模式:
MVVM是Model(数据层)、ViewController/View(视图层)、ViewModel(数据模型)组成。
MVVM架构模式最主要是针对前端和iOS的MVC架构模式进行改进的,减轻Controller层或者View层的压力,实现更加清晰化代码。通过对ViewModel层的封装:封装业务逻辑处理,封装网络处理、封装数据缓存等,让逻辑处理分离出来,并且不需要处理Model数据,使得Controller层或者View层结构简单,条理清晰。
五. vue如何实现数据双向绑定
vue实现数据双向绑定的原理使用过数据劫持和发布-订阅者模式来实现的
实现步骤:
-
实现一个监听者Observer来劫持需要监听的所有属性,一旦有属性发生变化,就通知订阅者watcher
/* 劫持一个属性需要用到 defineProperty(obj,key,val)方法,在JS中每个数据都有两个属性风别是get()和set(),get用来通知属性被读取或者被使用了, set用来通知数据被修改了。 */ let car = { brand: 'BWM', price: '5000' } function defineProperty(obj, key, val) { // val是指对应键的值 if (arguments.length === 2) { // 如果只传了两个参数处理 val = obj[key] } Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { console.log(key + '属性被读取了') return val }, set(newVal) { console.log(key + '属性被修改了') val = newVal } }) } defineProperty(car, 'price') // 开始劫持属性 -
实现一个订阅者watcher来接受属性发生变化的通知,并且执行相应的动作达到更新视图的目的
/* 订阅者(watcher): 即被观察者的依赖,使用或者读取了某个属性的集合,当属性发生变化,被修改后,就会通知订阅者发生需要发生变化了,订阅者发生了变化,视图也就更新。 1. 如何获取这些订阅者(一个视图下会有多个订阅者) 2. 如何通知这些订阅者 3. 如何管理这些订阅者 */ // 1. 收集订阅者: // 在劫持属性的时候,我们知道,当属性被读取或者被修改的时候,会触发数据本身携带的get和set属性,订阅者肯定读取了某个属性的值,所以在get中我们可以收集这些订阅者 // 2. 通知订阅者: // 当属性的值被修改的时候,会触发数据的set属性,这时候应该通知订阅者,数据发生了变化。 function defineReactive (obj,key,val) { if (arguments.length === 2) { val = obj[key] } if(typeof val === 'object'){ new Observer(val) } const dep = new Dep() //实例化一个依赖管理器,生成一个依赖管理数组dep Object.defineProperty(obj, key, { enumerable: true, configurable: true, get(){ dep.depend() // 在getter中收集依赖 return val; }, set(newVal){ if(val === newVal){ return } val = newVal; dep.notify() // 在setter中通知依赖更新 } }) } // 3. 管理订阅者: // 应为在一个视图中会有很多的订阅者,将它们集中管理,便于维护,在vue源码中是将它们集中放在一个叫dep的类中: 这个类中会对订阅者执行一些增删改查的动作 export default class Dep { constructor () { this.subs = [] } addSub (sub) { this.subs.push(sub) } // 删除一个依赖 removeSub (sub) { remove(this.subs, sub) } // 添加一个依赖 depend () { if (window.target) { this.addSub(window.target) } } // 通知所有依赖更新 notify () { const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } } /** * Remove an item from an array */ export function remove (arr, item) { if (arr.length) { const index = arr.indexOf(item) if (index > -1) { return arr.splice(index, 1) } } } // Vue中还实现了一个叫做Watcher的类,在之后数据变化时,我们不直接去通知依赖更新,而是通知依赖对应的Watch实例,由Watcher实例去通知真正的视图 export default class Watcher { constructor (vm,expOrFn,cb) { this.vm = vm; this.cb = cb; this.getter = parsePath(expOrFn) this.value = this.get() } get () { window.target = this; const vm = this.vm let value = this.getter.call(vm, vm) window.target = undefined; return value } update () { const oldValue = this.value this.value = this.get() this.cb.call(this.vm, this.value, oldValue) } } /** * Parse simple path. * 把一个形如'data.a.b.c'的字符串路径所表示的值,从真实的data对象中取出来 * 例如: * data = {a:{b:{c:2}}} * parsePath('a.b.c')(data) // 2 */ const bailRE = /[^\w.$]/ export function parsePath (path) { if (bailRE.test(path)) { return } const segments = path.split('.') return function (obj) { for (let i = 0; i < segments.length; i++) { if (!obj) return obj = obj[segments[i]] } return obj } } -
实现一个解释器compile,可以扫描和解析每个节点的相关指令,并更具初始化模板数据以及初始化相对应的订阅者
六. 虚拟DOM
1. 什么是虚拟DOM
所谓的虚拟dom就是一个用js对象描述的一个dom节点
<div class="a" id="b">我是内容</div> // 真实的dom节点
// 用js对象描述的dom节点
{
tag:'div', // 元素标签
attrs:{ // 属性
class:'a',
id:'b'
},
text:'我是内容', // 文本内容
children:[] // 子元素
}
我们把一个真实的dom节点用js对象描述出来,这个js对象我们就称之为虚拟DOM
2. 为什么需要虚拟DOM
vue的核心是数据驱动视图,当数据发生变化,视图就要随之更新。更新视图必然需要操作dom节点,然而操作dom节点是非常消耗性能的,为了尽量减少操作dom,于是创建的虚拟dom。
当初始化数据的时候,我们创建一个虚拟的dom,当数据发生变化的时候,我们先找到对应的虚拟dom中哪写数据发生了变化,也就是视图哪些地方需要更新,只需要更新需要更新的地方,没有变化的地方则不需要更新,这样就大大减少了dom的操作。也就是用JS的计算性能换取了操作dom消耗的性能
如何计算哪写数据发生了变化,主要通过 DOM-Diff 算法计算出来 (算法具体,不做深究)
七. vue生命周期