细细品一品VUE

1.更改数据后,页面会立刻重新渲染吗?

数据变化,页面就会重新渲染。VUE更新DOM操作是异步执行的,只要侦听到数据变化就会开启一个异步队列,同步执行栈执行完毕后会执行异步执行栈,队列中微任务先执行。


2.如何在更改数据后,看到渲染后页面上的值?

利用vm.$nextTickVue.nextTick
页面重新渲染,DOM更新后,会立刻执行vm.$nextTick
△两者的区别vue.nextTick内部函数的this指向window,vm.$nextTick内部函数的this指向vue实例对象。

nextTick是怎样实现的?

nextTick是在下次DOM更新循环结束之后执行延迟回调,在Vue更新Dom操作是异步执行的,分为宏任务和微任务,微任务会先执行,在nextTick实现中,会先判断是否执行微任务,不支持的话,才会执行宏任务,他根据执行环境分别尝试采用Promise,MutationObserve,setImmedate,如果以上都不行采用setTimeout.

  if(typeof Promise !== 'undefined') {
    // 微任务
    // 首先看一下浏览器中有没有promise
    // 因为IE浏览器中不能执行Promise
    const p = Promise.resolve();

  } else if(typeof MutationObserver !== 'undefined') {
    // 微任务
    // 突变观察
    // 监听文档中文字的变化,如果文字有变化,就会执行回调
    // vue的具体做法是:创建一个假节点,然后让这个假节点稍微改动一下,就会执行对应的函数
  } else if(typeof setImmediate !== 'undefined') {
    // 宏任务
    // 只在IE下有
  } else {
    // 宏任务
    // 如果上面都不能执行,那么则会调用setTimeout
  }


3.插值表达式{{ }}中可以写什么?

1.将vue中的数据写在插值表达式中
2.直接填写数据值(数字,布尔值,字符串,undefined,null,数组,对象)
3.写表达式,运算表达式,逻辑表达式,三元表达式

不能写什么?
不可以写语句,流程控制。

记住:插值表达式中,可以写:data、js数据、表达式,其他的想都不要想。
注意,只要插值表达式中使用了数据,必须在data中声明过,否则会报错(有一种可能不报错,是写了在原型链上找不到的,之为undefined,就不报错啦);


4.除了未被声明,未被渲染的数据外,还有什么数据更改后不会渲染页面?

1)利用索引直接设置一个数组项的时候
2)修改数组长度
3)添加删除对象

那如何响应式的更新数组和对象?

更改数组:


1. 利用数组变异方法:push、pop、shift、unshift、splice、sort、reverse
2. 利用vm.$set/Vue.set实例方法
3. 利用vm.$set或Vue.set删除数组中的某一项

更改对象:

vm.$set是Vue.set的别名
使用方法:Vue.set(object, propertyName, value),也就是这个意思:Vue.set(要改谁,改它的什么,改成啥)
vm.$delete是Vue.delete的别名
使用方法:Vue.delete(object, target),也就是这个意思:Vue.delete(要删除谁的值,删除哪个)

为什么不会被渲染上页面?

vue 2.x利用object.defineProperty实现响应式

const data = {
  name: 'shanshan',
  age: 18,
  shan: {
    name: 'shanshan',
    age: 18,
    obj: {}
  },
  arr: [1, 2, 3]
}

const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop', 'shift', 'unshift' ,'sort', 'splice', 'reverse'].forEach(method => {
  arrayMethods[method] = function () {
    arrayProto[method].call(this, ...arguments);
    render();
  }
})

function defineReactive (data, key, value) {
  observer(value);
  Object.defineProperty(data, key, {
    get () {
      return value;
    },
    set (newVal) {
      if(value === newVal) {
        return;
      }
      value = newVal;
      render();
    }
  })
}

function observer (data) {
  if(Array.isArray(data)) {
    data.__proto__ = arrayMethods;
    return;
  }

  if(typeof data === 'object') {
    for(let key in data) {
      defineReactive(data, key, data[key])
    }
  }
}

function render () {
  console.log('页面渲染啦');
}

function $set (data, key, value) {
  if(Array.isArray(data)) {
    data.splice(key, 1, value);
    return value;
  }
  defineReactive(data, key, value);
  render();
  return value;
}

function $delete(data, key) {
  if(Array.isArray(data)) {
    data.splice(key, 1);
    return;
  }
  delete data[key];
  render();
}

observer(data);

缺点:

1)需要递归查询
(2)监听不到数组不存在的索引的改变
(3)监听不到数组长度的改变
(4)监听不到对象的增删

简单说一下Vue2.x响应式数据原理

Vue在初始化数据时,会使用Object.defineProperty重新定义data中的所有属性,当页面使用对应属性时,首先
会进行依赖收集(收集当前组件的watcher)如果属性发生变化会通知相关依赖进行更新操作(发布订阅)

那你知道Vue3.x响应式数据原理吗?

Vue3.x改用Proxy替代Object.defineProperty。因为Proxy可以直接监听对象和数组的变化,
并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。

Proxy只会代理对象的第一层,那么Vue3又是怎样处理这个问题的呢?

判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理,
这样就实现了深度观测。
❝
监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?
❞
我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。

再说一下vue2.x中如何监测数组变化?

使用了函数劫持的方式,重写了数组的方法,Vue将data中的数组进行了原型链重写,指向了自己定义的数组原型
方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归
遍历进行监控。这样就实现了监测数组变化。

5.为什么不用index作为数组的key值

因为数组改变的时候页面会重新渲染,Vue会根据key值来判断需不需要移动元素,用索引作key的话,页面重新渲染后,元素的key值会被重新赋值。Vue会对比渲染前后拥有同样key的元素,如果发现有变动,就会再生成一个元素,如果用索引作为key值,假如你reverse之后,所有元素会被重新生成。以前的数据和重新渲染后的数据随着 key 值的变化从而没法建立关联关系. 这就失去了 key 值存在的意义.

6.处理复杂逻辑,计算属性(computed)和方法(method)最本质的区别;

计算属性是基于响应式依赖进行缓存的,计算属性的值一直存在与缓存中(缓存可以减少开销),只要他依赖的data数据不变,每次访问计算属性,会like返回缓存的结果,而不是再次执行函数。而方法是每次触发新的渲染,调用方法钟会再次执行函数。


7.侦听器(watch) VS 计算属性(computed)

computed:本质是一个具备缓存的watcher,依赖的属性发生变化就会更新视图,适用于多个数据影响一个数据,比较消耗性能的计算场景。当表达式过于复杂时,模板中放过多逻辑会让模板难以维护,可以将复杂的逻辑放入计算属性中。
watch:没有缓存性,更多的是观察作用,监听某些数据,执行回调,深度监听对象的属性的时候(deep:true),便于对对象中的每一个属性进行监听,一个数据影响多个数据。

(1)都可以观察和响应VUE实例的数据变动
(2)watch:一个数据影响多个数据;计算属性:多个数据影响一个数据
(3)侦听器中可以异步执行,计算属性不可以。

8.组件template中的data为什么是一个函数?

return{
data:
}

一个组件被复用多次会创建多个实例。本质上这些实例用的都是同一个构造函数,若data是对象,对象属于引用类型,会影响到所有实例,是函数的话,每个实例可以维护一份被返回的对象的独立拷贝,他可以保证组件不通实例之间的data不冲突。

9.v-text VS Mustache

<span v-text="msg"></span>
<!-- 和下面的一样 -->
<span>{{msg}}</span>

区别:
v-text替换元素中所有的文本,Mustache只替换自己,不清空元素内容
v-text 优先级高于 {{ }}


10.textContent VS innerText

设置文本替换时,两者都会把指定节点下的所有子节点也一并替换掉。
textContent 会获取所有元素的内容,包括 <script><style>元素,然而 innerText 不会。
innerText 受 CSS 样式的影响,并且不会返回隐藏元素的文本,而textContent会。
由于 innerText 受 CSS 样式的影响,它会触发重排(reflow),但textContent 不会。
innerText 不是标准制定出来的 api,而是IE引入的,所以对IE支持更友好。textContent虽然作为标准方法但是只支持IE8+以上的浏览器,在最新的浏览器中,两个都可以使用。
综上,Vue这里使用textContent是从性能的角度考虑的。


11.阻止默认事件

注意
使用修饰符时,顺序很重要。相应的代码会以同样的顺序产生。因此,
v-on:click.prevent.self 会阻止所有的点击的默认事件
v-on:click.self.prevent 只会阻止对元素自身点击的默认事件
不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。


12.v-model原理

双向数据绑定,本质是一个语法糖,可以看成是input+value方法的语法糖。可以根据model属性的prop和event属性来进行自定义。

VUE事件绑定原理:原生事件绑定是通过addeventlistener绑定给真实元素。
组件事件绑定是通过Vue自定义的$on实现的

手写v-model
步骤:(1)通过绑定的数据给元素设置value(2)触发input事件,去更改数据的值(3)
更改数据后,同步input的value值。

Vue.directive('mymodel',{
    bind(el,binding,vnode){
        const vm = vnode.context;
        const{value,expression} = binding;
        el.value = value;
        el.oninput = function(e){
            const inputVal = el.value;
            vm[expression] = inputVal;
        }
    },
    update(el,binding){
        const {value} = binding;
        el.value = value
    }
})

13.key值的唯一性

key就是在更新组件时,判断两个节点是否相同,相同就复用,不同就创建新的删除旧的。(强制更新组件,避免原地复用带来的副作用)

不用key
就地复用节点,a.key和b.key都是undefined,所以不会重新创建和删除节点,只会在节点的属性层面上进行比较和更新。
无法维持组件的状态:动态效果啊开关等状态
性能下降:要复用很多节点,顺序和原来的完全不同,创建和删除节点的数量就会比带key的时候增加很多。

不建议将数组的索引作为key值:当改变数组时,页面会重新渲染,Vue会根据key值来判断要不要移动元素。例如使用reverse当页面重新渲染时,当使用数组的索引作为key值时,页面重新渲染后,元素的key值会重新被赋值。Vue会比对渲染前后拥有同样key的元素,发现有变动,就会再生成一个元素,如果用索引作key值得话,那么此时,所有的元素都会被重新生成。

14.v-for和v-if不能一起使用

v-for 比 v-if 具有更高的优先级,也许会在重新渲染的时候遍历整个列表.
当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级,通过v-if 移动到容器元素,不会再重复遍历列表中的每个值。取而代之的是,我们只检查它一次,且不会在 v-if 为否的时候运算 v-for。

15. .v-model VS .sync

Vue 1.x :.sync可以在子组件中修改父组件的状态,会造成整个状态的变换很难追溯。
vue 2.0 : 移除
vue 3.0 : 回归,语法糖和v-model实现原理一样
(1)两个都是用来实现双向数据传递的,都是语法糖,最终通过prop + 事件来达成目的。
(2)当一个组件对外只暴露一个受控状态,并且都符合统一标准的时候,用v-model来处理,.sync更灵活,双向数据传递都可以使用。


16.keep-alive

包裹动态组件的时候,会缓存不活动的组件实例,而不是销毁它们。keep-alive是一个抽象组件;他自身不会渲染一个DOM元素,也不会出现在父组件中。
常用的两个属性:include/exclude 允许组件有条件进行缓存。
两个生命周期:actived/deactiveted 得知当前组件是否处于活跃状态。

运用了LRU算法。


17.组建通信

父子:(1)prop:父组件传递给子组件时;
(2)$emit,$on:子组件传递给父组件时;
(3)ref:父组件中访问子实例的数据;$ref只会在组件渲染完成之后生效
(4)provide & inject :祖先组件提供数据(provide),子组件按需注入(inject),会将组件的阻止方式耦合在一起,使组件重构困难,不推荐。
兄弟组件:(1)eventBus(事件总线)
(1)$attrs:祖先组件 ->子孙组件【撰写基础组件】
(2)$listenner:子孙组件中执行祖先组件的函数【监听器】
(3)$root:子组件中访问根实例
跨级组建通信:$attr .$listener provide inject
父传子: this. r e f s . x x x 子 传 父 : t h i s . refs.xxx 子传父: this. refs.xxx:this.parent.xxx

还可以通过 e m i t 方 法 出 发 一 个 消 息 , 然 后 emit方法出发一个消息,然后 emiton接收这个消息


18.Vue生命周期

(1)beforeCreate:new vue()之后触发的第一各钩子,当前阶段data,methods,computed,watch上的数据和方法都不能访问。vue实例的挂载元素 e l 和 数 据 对 象 d a t a 都 为 u n d e f i n e d , 还 未 初 始 化 。 在 c r e a t e d 阶 段 , v u e 实 例 的 数 据 对 象 d a t a 有 了 , el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了, eldataundefinedcreatedvuedatael还没有。
(2)created:实例创建完成之后发生,完成了数据观测,可以使用数据,更改数据,在这里更改数据不会触发update,可以做初始数据的获取,无法与Dom进行交互(非想交互可以通过vm.$nextTick来访问).在beforeMount阶段,vue实例的 e l 和 d a t a 都 初 始 化 了 , 但 还 是 挂 载 之 前 为 虚 拟 的 d o m 节 点 , d a t a . m e s s a g e 还 未 替 换 。 在 m o u n t e d 阶 段 , v u e 实 例 挂 载 完 成 , d a t a . m e s s a g e 成 功 渲 染 。 ( 3 ) b e f o r e M o u n t : 发 生 在 挂 在 之 前 , 在 这 之 前 ‘ t e m p l a t e ‘ 模 板 已 导 入 , 渲 染 函 数 编 译 。 D O M 穿 件 完 成 , 即 将 开 始 渲 染 , 此 时 可 以 更 改 数 据 , 不 会 触 发 u p d a t e 。 ( 4 ) m o u t e d : 挂 载 完 成 之 后 发 生 , 真 实 D O M 挂 载 完 毕 , 数 据 完 成 双 向 绑 定 , 可 以 访 问 到 D O M 节 点 , 使 用 ‘ el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。 (3)beforeMount:发生在挂在之前,在这之前`template`模板已导入,渲染函数编译。DOM穿件完成,即将开始渲染,此时可以更改数据,不会触发update。 (4)mouted:挂载完成之后发生,真实DOM挂载完毕,数据完成双向绑定,可以访问到DOM节点,使用` eldatadomdata.messagemountedvuedata.message3beforeMounttemplateDOM穿update4moutedDOM访DOM使refs`属性。
(5)beforeUpdate:更新之前,响应式数据发生更新,虚拟dom重新渲染之前被触发,可以在这个阶段进行更改数据,不会被渲染。
(6)Updated:更新完成之后,dom已经完成更新,避免在此进行更改数据,可能会导致无限循环。
(7)beforeDestory:实例销毁前,实例被使用,收尾(清楚定时器)
(8)destroyed:实例销毁之后,只剩dom空壳,组件被拆解,数据绑定被卸除,监听被移除,子例销毁。
△除了beforeCreate和Create钩子之外,其他钩子均在服务器端渲染期间不被调用。
updated不要修改data里面赋值的数据,否则会导致死循环。

①beforeCreate钩子调用在initState之前,initState作用是对props,methods,data,computed,watch等做初始化处理。
②mount组件核心是先实例化一个渲染watcher在他的回调函数中调用了updatacomponent方法,在执行vm.render()函数渲染虚拟节点之前,把虚拟节点patch到真实Dom后,执行mouted钩子。
③接口请求一般放在mounted中,但是服务端渲染时不支持mounted,需要放到eated中。

(1)、什么是vue生命周期

答: Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。

(2)、vue生命周期的作用是什么

答:它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。

(3)、vue生命周期总共有几个阶段

答:可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后

(4)、第一次页面加载会触发哪几个钩子

答:第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子

(5)、DOM 渲染在 哪个周期中就已经完成

答:DOM 渲染在 mounted 中就已经完成了。

(6)、简单描述每个周期具体适合哪些场景

答:生命周期钩子的一些使用方法:

beforecreate : 可以在这加个loading事件,在加载实例时触发
created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
mounted : 挂载元素,获取到DOM节点
updated : 如果对数据统一处理,在这里写上相应函数
beforeDestroy : 可以做一个确认停止事件的确认框
nextTick : 更新数据后立即操作dom

19.什么是虚拟DOM

MVC MVP架构模式希望可以从代码组织方式来降低维护复杂引用程序的难度,DOM还是要操作只是将数据,操作,视图分离了。所以有了MVVM:只要在模板中声明试图组件是和什么数据绑定的,双向绑定引擎就会在状态更新的时候自动更新视图。MVVM会降低我们的维护状态,大大减少了代码中的试图更新逻辑。

什么是虚拟DOM?
创建虚拟DOM就是为了更高效,频繁的更新DOM。是一个常规的JS对象。

原理:

用新渲染的对象树去和旧的对比,记录这两棵树的差异。记录下来的不同就是我们需要对页面真正的DOM操作,然后把它们应用到真正的DOM树上,页面就变更了。这样就可做到:视图的结构整个全新渲染了,最后操作DOM的时候只变更有不同的地方。

即Virtual Dom算法:
用JS对象结构表示DOM树结构,然后用这个树构建一个真正的DOM树,插到文档中;当状态变更的时候,重新构造一颗新的对象树,用新的树和旧的树进行比较,记录两棵树的差异,把差异应用到真正的DOM树上,视图就更新了。

虚拟DOM本质上是在JS和DOM上做了一个缓存。

虚拟DOM核心:diff算法

在实际的代码中,会对新旧两棵树进行深度的遍历,给每一个节点进行标
记。然后在新旧两棵树的对比中,将不同的地方记录下来。
function diff(oldTree, newTree) {
 var index = 0 // 当前节点的标志
 var patches = {} // 记录每个节点差异的地方
 dfsWalk(oldTree, newTree, index, patches)
 return patches
}
function dfsWalk(oldNode, newNode, index, patches) {
 // 对比 newNode 和 oldNode 的差异地方进行记录
 patches[index] = [...]
 diffChildren(oldNode.children, newNode.children, index, patches)
}
function diffChildren(oldChildren, newChildren, index, patches) {
 let leftNode = null
 var currentNodeIndex = index
 oldChildren.forEach((child, i) => {
 var newChild = newChildren[i]
 currentNodeIndex = (leftNode && leftNode.count) // 计算节点的
标记
 ? currentNodeIndex + leftNode.count + 1
 : currentNodeIndex + 1
 dfsWalk(child, newChild, currentNodeIndex, patches) // 遍历子节
点
 leftNode = child
 })
}
同理,有 p 是 patches[1], ul 是 patches[3],以此类推
patches 指的是差异变化,这些差异包括:
1、节点类型的不同,
2、节点类型相同,但是属性值不同,文本内容不同。
所以有这么几种类型:
var REPLACE = 0, // replace 替换
 REORDER = 1, // reorder 父节点中子节点的操作
 PROPS = 2, // props 属性的变化
 TEXT = 3 // text 文本内容的变化
如果节点类型不同,就说明是需要替换,例如将 div 替换成 section,就记录
渡一教育
9
下差异:
patches[0] = [{
 type: REPLACE,
 node: newNode // section
},{
 type: PROPS,
 props: {
 id: 'container'
 }
}]
在标题二中构建了真正的 DOM 树的信息,所以先对那一棵 DOM 树进行
深度优先的遍历,遍历的时候同
patches 对象进行对比,找到其中的差异,然后应用到 DOM 操作中。
function patch(node, patches) {
 var walker = {index: 0} // 记录当前节点的标志
 dfsWalk(node, walker, patches)
}
function dfsWalk(node, walker, patches) {
 var currentPatches = patches[walker.index] // 这是当前节点的差异
 var len = node.childNodes
 ? node.childNodes.length
 : 0
 for (var i = 0; i < len; i++) { // 深度遍历子节点
 var child = node.childNodes[i]
 walker.index++
 dfsWalk(child, walker, patches)
 }
 if (currentPatches) {
 applyPatches(node, currentPatches) // 对当前节点进行 DOM 操作
 } }
// 将差异的部分应用到 DOM 中
function applyPatches(node, currentPatches) {
 currentPatches.forEach((currentPatch) => {
 switch (currentPatch.type) {
 case REPLACE:
 var newNode = (typeof currentPatch.node === 'string')
 ? document.createTextNode(currentPatch.node)
 : currentPatch.node.render()
 node.parentNode.replaceChild(newNode, node)
 break;
 case REORDER:
 reorderChldren(node, currentPatch.moves)
 break
 case PROPS:
 setProps(node, currentPatch.props)
 break
 case TEXT:
 if (node.textContent) {
 node.textContent = currentPatch.content
 } else {
 node.nodeValue = currentPatch.content
 }
 break
 default:
 throw new Error('Unknown patch type ' + 
currentPatch.type)
 }
 })
}

20.Vue模板编译原理

就是将template转化为render函数的过程。
会经历:生成抽象语法树过程
首先解析模板,生成抽象语法树,对模板进行解析,遇到标签文本的时候会执行相应的钩子函数进行相关。
Vue数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有些数据首次渲染后就不会再变化,对应的DOM也不会变化,优化的过程就像是深度遍历的抽象语法树,按照相关节点对树的节点进行标记,这些被标记的节点就可以跳过对她们的对比,对运行时的模板起到很大的优化作用。编译的最后一步是将优化后的抽象语法树转换成为可执行代码。

21.VUE中的diff算法

diff是一种优化手段,将前后两个模块进行差异对比。

为什么要用diff算法?
真实dom开销很大,改变页面中的数据,如果直接渲染到真实DOM中会引起整棵树的重绘,要做一个最少重绘。

虚拟DOM和真实DOM的区别?
虚拟DOM是将真实的DOM数据抽离出来,以对象的形式模拟树形结构

diff是如何比较的
对操作前后的dom树同一层的节点进行对比,如果节点类型不同,干掉前面的节点,再创建并插入新的节点,不会在比较这个节点以后的子节点,如果节点类型相同,会重新设置节点的属性,从而实现节点更新。

key可以表示组件的唯一性,作了唯一标识,diff就可以正确的识别而不是一个一个的更新节点;

diff算法的核心

(1)两个相同的组件产生类似的DOM结构,不同的组件产生不同的DOM结构
(2)同一层级的一组节点,他们可以通过唯一id进行区分

22.数据绑定Proxy与defineProperty优劣对比

proxy的优势:
(1)直接监听对象而不是属性
(2)直接监听数组变化
(3)有多达13种拦截方式(apply,ownkey…)
(4)proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而object.defineProperty只能遍历对象属性直接修改。

object.defineProperty:兼容性好,支持IE9,而Proxy存在浏览器兼容性的问题(3.0用)

23.Vue filter过滤器,可以在不改变数据前提下,修改数据渲染样式。

Vue.filter('toMoney',(value,time) => {
    console.log(value.time)
    return 'xxx'
});
// 使用
{{money | toMoney}}

24.单页面的理解SPA

仅在web页面初始化的时候加载相应的HTML,JS和css,一旦页面加载完成,SPA不会因为用户的操作而进行页面重新加载或跳转,取而代之的是利用路由机制实现HTML的内容的变换,UI与用户的交互,避免页面的重新加载。

优点:
用户体验好,块,内容加载不需要重新加载整个页面,避免了不必要的跳转和重复渲染,SPA相对于服务器压力小,前后端职责分明,架构清晰,前端负责交互逻辑,后端负责数据处理。

缺点:
初次加载耗时多,为了实现单页面web应用功能及显示效果,需要在加载页面的时候将js,css同一加载,部分页面按需加载,

25.对于MVVM的理解?

MVVM 是 Model-View-ViewModel 的缩写。
Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。
View 代表UI 组件,它负责将数据模型转化成UI 展现出来。
ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View 和 Model的对象,连接Model和View。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。

26.Vue的路由实现:hash模式 和 history模式

hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。

history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”

27.vue路由的钩子函数

首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。

beforeEach主要有3个参数to,from,next:

to:route即将进入的目标路由对象,

from:route当前导航正要离开的路由

next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转。

28.vuex是什么?怎么使用?哪种功能场景使用它?

只用来读取的状态集中放在store中; 改变状态的方式是提交mutations,这是个同步的事物; 异步逻辑应该封装在action中。
在main.js引入store,注入。新建了一个目录store,…… export 。
场景有:单页应用中,组件之间的状态、音乐播放、登录状态、加入购物车
图片描述

state
Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
mutations
mutations定义的方法动态修改Vuex 的 store 中的状态或数据。
getters
类似vue的计算属性,主要用来过滤一些数据。
action
actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action。

const store = new Vuex.Store({ //store实例
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit(‘increment’)
}
}
})
modules
项目特别复杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。

const moduleA = {
state: { … },
mutations: { … },
actions: { … },
getters: { … }
}
const moduleB = {
state: { … },
mutations: { … },
actions: { … }
}

const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
})

29.vue-cli如何新增自定义指令?

1.创建局部指令

var app = new Vue({
el: ‘#app’,
data: {
},
// 创建指令(可以多个)
directives: {
// 指令名称
dir1: {
inserted(el) {
// 指令中第一个参数是当前使用指令的DOM
console.log(el);
console.log(arguments);
// 对DOM进行操作
el.style.width = ‘200px’;
el.style.height = ‘200px’;
el.style.background = ‘#000’;
}
}
}
})
2.全局指令

Vue.directive(‘dir2’, {
inserted(el) {
console.log(el);
}
})
3.指令的使用

十、vue如何自定义一个过滤器? html代码:
{{msg| capitalize }}
JS代码:
var vm=new Vue({
el:"#app",
data:{
msg:’’
},
filters: {
capitalize: function (value) {
if (!value) return ‘’
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
})
全局定义过滤器

Vue.filter(‘capitalize’, function (value) {
if (!value) return ‘’
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
过滤器接收表达式的值 (msg) 作为第一个参数。capitalize 过滤器将会收到 msg的值作为第一个参数。

30.vue.js的两个核心是什么?

答:数据驱动、组件系统

31.vue常用的修饰符?

答:.prevent: 提交事件不再重载页面;.stop: 阻止单击事件冒泡;.self: 当事件发生在该元素本身而不是子元素的时候会触发;.capture: 事件侦听,事件发生的时候会调用

32.怎么定义 vue-router 的动态路由? 怎么获取传过来的值

答:在 router 目录下的 index.js 文件中,对 path 属性加上 /:id,使用 router 对象的 params.id 获取。

33.vue-router有哪几种导航钩子?

答:三种,一种是全局导航钩子:router.beforeEach(to,from,next),作用:跳转前进行判断拦截。第二种:组件内的钩子;第三种:单独路由独享组件

34.scss是什么?安装使用的步骤是?有哪几大特性?

答:预处理css,把css当前函数编写,定义变量,嵌套。 先装css-loader、node-loader、sass-loader等加载器模块,在webpack-base.config.js配置文件中加多一个拓展:extenstion,再加多一个模块:module里面test、loader

35.scss是什么?在vue.cli中的安装使用步骤是?有哪几大特性?

答:css的预编译。

使用步骤:

第一步:用npm 下三个loader(sass-loader、css-loader、node-sass)

第二步:在build目录找到webpack.base.config.js,在那个extends属性中加一个拓展.scss

第三步:还是在同一个文件,配置一个module属性

第四步:然后在组件的style标签加上lang属性 ,例如:lang=”scss”

有哪几大特性:

1、可以用变量,例如($变量名称=值);

2、可以用混合器,例如()

3、可以嵌套

36.axios是什么?怎么使用?描述使用它实现登录功能的流程?

答:请求后台资源的模块。npm install axios -S装好,然后发送的是跨域,需在配置文件中config/index.js进行设置。后台如果是Tp5则定义一个资源路由。js中使用import进来,然后.get或.post。返回在.then函数中如果成功,失败则是在.catch函数中

37.Vue的双向数据绑定原理是什么?

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

具体步骤:

第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 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变更的双向绑定效果。

ps:1答案同样适合”vue data是怎么实现的?”此面试题。

38.请说下封装 vue 组件的过程?

答:首先,组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低、难维护、复用性等问题。

然后,使用Vue.extend方法创建一个组件,然后使用Vue.component方法注册组件。子组件需要数据,可以在props中接受定义。而子组件修改好数据后,想把数据传递给父组件。可以采用emit方法。

39.请说出vue.cli项目中src目录每个文件夹和文件的用法?

答:assets文件夹是放静态资源;components是放组件;router是定义路由相关的配置;view视图;app.vue是一个应用主组件;main.js是入口文件

40.vue.cli中怎样使用自定义的组件?有遇到过哪些问题吗?

答:第一步:在components目录新建你的组件文件(smithButton.vue),script一定要export default {

第二步:在需要用的页面(组件)中导入:import smithButton from ‘…/components/smithButton.vue’

第三步:注入到vue的子组件的components属性上面,components:{smithButton}

第四步:在template视图view中使用,
问题有:smithButton命名,使用的时候则smith-button。

41.聊聊你对Vue.js的template编译的理解?

答:简而言之,就是先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点)

详情步骤:

首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 即 源代码的抽象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创建编译器的。另外compile还负责合并option。

然后,AST会经过generate(将AST语法树转化成render funtion字符串的过程)得到render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名、子节点、文本等等)

42.vue为什么不直接操作dom?

答:因为操作dom对象后,会触发一些浏览器行为,比如布局(layout)和绘制(paint)。  paint是一个耗时的过程,然而layout是一个更耗时的过程,我们无法确定layout一定是自上而下或是自下而上进行的,甚至一次layout会牵涉到整个文档布局的重新计算。浏览器的layout是lazy的,也就是说:在js脚本执行时,是不会去更新DOM的,任何对DOM的修改都会被暂存在一个队列中,在当前js的执行上下文完成执行后,会根据这个队列中的修改,进行一次layout。

43.你怎么理解vue是一个渐进式的框架?

答:我觉得渐进式就是不必一开始就用Vue所有的全家桶,可以根据场景,按需使用想要的插件。也可以说就使用vue不需要太多的要求。

44.vue中mixin与extend区别?

答:
全局注册混合对象,会影响到所有之后创建的vue实例,而Vue.extend是对单个实例进行扩展。

mixin 混合对象(组件复用)

同名钩子函数(bind,inserted,update,componentUpdate,unbind)将混合为一个数组,因此都将被调用,混合对象的钩子将在组件自身钩子之前调用

methods,components,directives将被混为同一个对象。两个对象的键名(方法名,属性名)冲突时,取组件(而非mixin)对象的键值对

45.mvvm和mvc区别?它和其它框架(jquery)的区别是什么?哪些场景适合?

mvc和mvvm其实区别并不大。都是一种设计思想。主要就是mvc中Controller演变成mvvm中的viewModel。mvvm主要解决了mvc中大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。

区别:vue数据驱动,通过数据来显示视图层而不是节点操作。
场景:数据操作比较多的场景,更加便捷

46.vue的优点是什么?

低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。

47. v-show和v-if指令的共同点和不同点

v-show指令是通过修改元素的display的CSS属性让其显示或者隐藏
v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果

48.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值