写过自定义指令吗?原理是什么

答复范例

  1. Vue有一组默认指令,比方v-modelv-for,同时Vue也容许用户注册自定义指令来扩大Vue能力
  2. 自定义指令次要实现一些可复用低层级DOM操作
  3. 应用自定义指令分为定义、注册和应用三步:
  4. 定义自定义指令有两种形式:对象和函数模式,前者相似组件定义,有各种生命周期;后者只会在mounted和updated时执行
  • 注册自定义指令相似组件,能够应用app.directive()全局注册,应用{directives:{xxx}}部分注册
  • 应用时在注册名称前加上v-即可,比方v-focus
  • 我在我的项目中罕用到一些自定义指令,例如:
  • 复制粘贴 v-copy
  • 长按 v-longpress
  • 防抖 v-debounce
  • 图片懒加载 v-lazy
  • 按钮权限 v-premission
  • 页面水印 v-waterMarker
  • 拖拽指令 v-draggable
  • vue3中指令定义产生了比拟大的变动,次要是钩子的名称放弃和组件统一,这样开发人员容易记忆,不易犯错。另外在v3.2之后,能够在setup中以一个小写v结尾不便的定义自定义指令,更简略了

根本应用

当Vue中的外围内置指令不可能满足咱们的需要时,咱们能够定制自定义的指令用来满足开发的需要

咱们看到的v-结尾的行内属性,都是指令,不同的指令能够实现或实现不同的性能,对一般 DOM元素进行底层操作,这时候就会用到自定义指令。除了外围性能默认内置的指令 (v-modelv-show),Vue 也容许注册自定义指令

// 指令应用的几种形式://会实例化一个指令,但这个指令没有参数 `v-xxx`// -- 将值传到指令中`v-xxx="value"`  // -- 将字符串传入到指令中,如`v-html="'<p>内容</p>'"``v-xxx="'string'"` // -- 传参数(`arg`),如`v-bind:class="className"``v-xxx:arg="value"` // -- 应用修饰符(`modifier`)`v-xxx:arg.modifier="value"` 

注册一个自定义指令有全局注册与部分注册

// 全局注册注册次要是用过Vue.directive办法进行注册// Vue.directive第一个参数是指令的名字(不须要写上v-前缀),第二个参数能够是对象数据,也能够是一个指令函数// 注册一个全局自定义指令 `v-focus`Vue.directive('focus', {  // 当被绑定的元素插入到 DOM 中时……  inserted: function (el) {    // 聚焦元素    el.focus()  // 页面加载实现之后主动让输入框获取到焦点的小性能  }})// 部分注册通过在组件options选项中设置directive属性directives: {  focus: {    // 指令的定义    inserted: function (el) {      el.focus() // 页面加载实现之后主动让输入框获取到焦点的小性能    }  }}// 而后你能够在模板中任何元素上应用新的 v-focus property,如下:<input v-focus />

钩子函数

  1. bind:只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置。
  2. inserted:被绑定元素插入父节点时调用 (仅保障父节点存在,但不肯定已被插入文档中)。
  3. update:被绑定于元素所在的模板更新时调用,而无论绑定值是否变动。通过比拟更新前后的绑定值,能够疏忽不必要的模板更新。
  4. componentUpdated:被绑定元素所在模板实现一次更新周期时调用。
  5. unbind:只调用一次,指令与元素解绑时调用。

所有的钩子函数的参数都有以下:

  • el:指令所绑定的元素,能够用来间接操作 DOM
  • binding:一个对象,蕴含以下 property

    • name:指令名,不包含 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2
    • oldValue:指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用。无论值是否扭转都可用。
    • expression:字符串模式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"
    • modifiers:一个蕴含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
    • vnodeVue 编译生成的虚构节点
    • oldVnode:上一个虚构节点,仅在 updatecomponentUpdated 钩子中可用

除了 el 之外,其它参数都应该是只读的,切勿进行批改。如果须要在钩子之间共享数据,倡议通过元素的 dataset 来进行

<div v-demo="{ color: 'white', text: 'hello!' }"></div><script>    Vue.directive('demo', function (el, binding) {    console.log(binding.value.color) // "white"    console.log(binding.value.text)  // "hello!"    })</script>

利用场景

应用自定义组件组件能够满足咱们日常一些场景,这里给出几个自定义组件的案例:

  1. 防抖
// 1.设置v-throttle自定义指令Vue.directive('throttle', {  bind: (el, binding) => {    let throttleTime = binding.value; // 防抖工夫    if (!throttleTime) { // 用户若不设置防抖工夫,则默认2s      throttleTime = 2000;    }    let cbFun;    el.addEventListener('click', event => {      if (!cbFun) { // 第一次执行        cbFun = setTimeout(() => {          cbFun = null;        }, throttleTime);      } else {        event && event.stopImmediatePropagation();      }    }, true);  },});// 2.为button标签设置v-throttle自定义指令<button @click="sayHello" v-throttle>提交</button>
  1. 图片懒加载

设置一个v-lazy自定义组件实现图片懒加载

const LazyLoad = {    // install办法    install(Vue,options){       // 代替图片的loading图        let defaultSrc = options.default;        Vue.directive('lazy',{            bind(el,binding){                LazyLoad.init(el,binding.value,defaultSrc);            },            inserted(el){                // 兼容解决                if('InterpObserver' in window){                    LazyLoad.observe(el);                }else{                    LazyLoad.listenerScroll(el);                }            },        })    },    // 初始化    init(el,val,def){        // src 贮存实在src        el.setAttribute('src',val);        // 设置src为loading图        el.setAttribute('src',def);    },    // 利用InterpObserver监听el    observe(el){        let io = new InterpObserver(entries => {            let realSrc = el.dataset.src;            if(entries[0].isIntersecting){                if(realSrc){                    el.src = realSrc;                    el.removeAttribute('src');                }            }        });        io.observe(el);    },    // 监听scroll事件    listenerScroll(el){        let handler = LazyLoad.throttle(LazyLoad.load,300);        LazyLoad.load(el);        window.addEventListener('scroll',() => {            handler(el);        });    },    // 加载实在图片    load(el){        let windowHeight = document.documentElement.clientHeight        let elTop = el.getBoundingClientRect().top;        let elBtm = el.getBoundingClientRect().bottom;        let realSrc = el.dataset.src;        if(elTop - windowHeight<0&&elBtm > 0){            if(realSrc){                el.src = realSrc;                el.removeAttribute('src');            }        }    },    // 节流    throttle(fn,delay){        let timer;         let prevTime;        return function(...args){            let currTime = Date.now();            let context = this;            if(!prevTime) prevTime = currTime;            clearTimeout(timer);            if(currTime - prevTime > delay){                prevTime = currTime;                fn.apply(context,args);                clearTimeout(timer);                return;            }            timer = setTimeout(function(){                prevTime = Date.now();                timer = null;                fn.apply(context,args);            },delay);        }    }}export default LazyLoad;
  1. 一键 Copy的性能
import { Message } from 'ant-design-vue';const vCopy = { //  /*    bind 钩子函数,第一次绑定时调用,能够在这里做初始化设置    el: 作用的 dom 对象    value: 传给指令的值,也就是咱们要 copy 的值  */  bind(el, { value }) {    el.$value = value; // 用一个全局属性来存传进来的值,因为这个值在别的钩子函数里还会用到    el.handler = () => {      if (!el.$value) {      // 值为空的时候,给出提醒,我这里的提醒是用的 ant-design-vue 的提醒,你们随便        Message.warning('无复制内容');        return;      }      // 动态创建 textarea 标签      const textarea = document.createElement('textarea');      // 将该 textarea 设为 readonly 避免 iOS 下主动唤起键盘,同时将 textarea 移出可视区域      textarea.readOnly = 'readonly';      textarea.style.position = 'absolute';      textarea.style.left = '-9999px';      // 将要 copy 的值赋给 textarea 标签的 value 属性      textarea.value = el.$value;      // 将 textarea 插入到 body 中      document.body.appendChild(textarea);      // 选中值并复制      textarea.select();      // textarea.setSelectionRange(0, textarea.value.length);      const result = document.execCommand('Copy');      if (result) {        Message.success('复制胜利');      }      document.body.removeChild(textarea);    };    // 绑定点击事件,就是所谓的一键 copy 啦    el.addEventListener('click', el.handler);  },  // 当传进来的值更新的时候触发  componentUpdated(el, { value }) {    el.$value = value;  },  // 指令与元素解绑的时候,移除事件绑定  unbind(el) {    el.removeEventListener('click', el.handler);  },};export default vCopy;
  1. 拖拽
<div ref="a" id="bg" v-drag></div>  directives: {    drag: {      bind() {},      inserted(el) {        el.onmousedown = (e) => {          let x = e.clientX - el.offsetLeft;          let y = e.clientY - el.offsetTop;          document.onmousemove = (e) => {            let xx = e.clientX - x + "px";            let yy = e.clientY - y + "px";            el.style.left = xx;            el.style.top = yy;          };          el.onmouseup = (e) => {            document.onmousemove = null;          };        };      },    },  }

原理

  • 指令实质上是装璜器,是 vueHTML 元素的扩大,给 HTML 元素减少自定义性能。vue 编译 DOM 时,会找到指令对象,执行指令的相干办法。
  • 自定义指令有五个生命周期(也叫钩子函数),别离是 bindinsertedupdatecomponentUpdatedunbind

原理

  1. 在生成 ast 语法树时,遇到指令会给以后元素增加 directives 属性
  2. 通过 genDirectives 生成指令代码
  3. patch 前将指令的钩子提取到 cbs 中,在 patch 过程中调用对应的钩子
  4. 当执行指令对应钩子函数时,调用对应指令定义的办法

说一下Vue的生命周期

Vue 实例有⼀个残缺的⽣命周期,也就是从开始创立、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是Vue的⽣命周期。

  1. beforeCreate(创立前):数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能拜访到data、computed、watch、methods上的办法和数据。
  2. created(创立后) :实例创立实现,实例上配置的 options 包含 data、computed、watch、methods 等都配置实现,然而此时渲染得节点还未挂载到 DOM,所以不能拜访到 $el 属性。
  3. beforeMount(挂载前):在挂载开始之前被调用,相干的render函数首次被调用。实例已实现以下的配置:编译模板,把data外面的数据和模板生成html。此时还没有挂载html到页面上。
  4. mounted(挂载后):在el被新创建的 vm.$el 替换,并挂载到实例下来之后调用。实例已实现以下的配置:用下面编译好的html内容替换el属性指向的DOM对象。实现模板中的html渲染到html 页面中。此过程中进行ajax交互。
  5. beforeUpdate(更新前):响应式数据更新时调用,此时尽管响应式数据更新了,然而对应的实在 DOM 还没有被渲染。
  6. updated(更新后) :在因为数据更改导致的虚构DOM从新渲染和打补丁之后调用。此时 DOM 曾经依据响应式数据的变动更新了。调用时,组件 DOM曾经更新,所以能够执行依赖于DOM的操作。然而在大多数状况下,应该防止在此期间更改状态,因为这可能会导致更新有限循环。该钩子在服务器端渲染期间不被调用。
  7. beforeDestroy(销毁前):实例销毁之前调用。这一步,实例依然齐全可用,this 仍能获取到实例。
  8. destroyed(销毁后):实例销毁后调用,调用后,Vue 实例批示的所有货色都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。

另外还有 keep-alive 独有的生命周期,别离为 activateddeactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 activated 钩子函数。

Vue 中 computed 和 watch 有什么区别?

计算属性 computed

 (1)**反对缓存**,只有依赖数据发生变化时,才会从新进行计算函数; (2)计算属性内**不反对异步操作**; (3)计算属性的函数中**都有一个 get**(默认具备,获取计算属性)**和 set**(手动增加,设置计算属性)办法; (4)计算属性是主动监听依赖值的变动,从而动静返回内容。

侦听属性 watch

 (1)**不反对缓存**,只有数据发生变化,就会执行侦听函数; (2)侦听属性内**反对异步操作**; (3)侦听属性的值**能够是一个对象,接管 handler 回调,deep,immediate 三个属性**; (3)监听是一个过程,在监听的值变动时,能够触发一个回调,并**做一些其余事件**。

v-if和v-show的区别

  • 伎俩:v-if是动静的向DOM树内增加或者删除DOM元素;v-show是通过设置DOM元素的display款式属性管制显隐;
  • 编译过程:v-if切换有一个部分编译/卸载的过程,切换过程中适合地销毁和重建外部的事件监听和子组件;v-show只是简略的基于css切换;
  • 编译条件:v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始部分编译; v-show是在任何条件下,无论首次条件是否为真,都被编译,而后被缓存,而且DOM元素保留;
  • 性能耗费:v-if有更高的切换耗费;v-show有更高的初始渲染耗费;
  • 应用场景:v-if适宜经营条件不大可能扭转;v-show适宜频繁切换。

对 React 和 Vue 的了解,它们的异同

相似之处:

  • 都将注意力集中放弃在外围库,而将其余性能如路由和全局状态治理交给相干的库;
  • 都有本人的构建工具,能让你失去一个依据最佳实际设置的我的项目模板;
  • 都应用了Virtual DOM(虚构DOM)进步重绘性能;
  • 都有props的概念,容许组件间的数据传递;
  • 都激励组件化利用,将利用分拆成一个个性能明确的模块,进步复用性。

不同之处 :

1)数据流

Vue默认反对数据双向绑定,而React始终提倡单向数据流

2)虚构DOM

Vue2.x开始引入"Virtual DOM",打消了和React在这方面的差别,然而在具体的细节还是有各自的特点。

  • Vue声称能够更快地计算出Virtual DOM的差别,这是因为它在渲染过程中,会跟踪每一个组件的依赖关系,不须要从新渲染整个组件树。
  • 对于React而言,每当利用的状态被扭转时,全副子组件都会从新渲染。当然,这能够通过 PureComponent/shouldComponentUpdate这个生命周期办法来进行管制,但Vue将此视为默认的优化。

3)组件化

React与Vue最大的不同是模板的编写。

  • Vue激励写近似惯例HTML的模板。写起来很靠近规范 HTML元素,只是多了一些属性。
  • React举荐你所有的模板通用JavaScript的语法扩大——JSX书写。

具体来讲:React中render函数是反对闭包个性的,所以import的组件在render中能够间接调用。然而在Vue中,因为模板中应用的数据都必须挂在 this 上进行一次直达,所以 import 一个组件完了之后,还须要在 components 中再申明下。 4)监听数据变动的实现原理不同

  • Vue 通过 getter/setter 以及一些函数的劫持,能准确晓得数据变动,不须要特地的优化就能达到很好的性能
  • React 默认是通过比拟援用的形式进行的,如果不优化(PureComponent/shouldComponentUpdate)可能导致大量不必要的vDOM的从新渲染。这是因为 Vue 应用的是可变数据,而React更强调数据的不可变。

5)高阶组件

react能够通过高阶组件(HOC)来扩大,而Vue须要通过mixins来扩大。

高阶组件就是高阶函数,而React的组件自身就是纯正的函数,所以高阶函数对React来说大海捞针。相同Vue.js应用HTML模板创立视图组件,这时模板无奈无效的编译,因而Vue不能采纳HOC来实现。

6)构建工具

两者都有本人的构建工具:

  • React ==> Create React APP
  • Vue ==> vue-cli

7)跨平台

  • React ==> React Native
  • Vue ==> Weex

如何从实在DOM到虚构DOM

波及到Vue中的模板编译原理,次要过程:

  1. 将模板转换成 ast 树, ast 用对象来形容实在的JS语法(将实在DOM转换成虚构DOM)
  2. 优化树
  3. ast 树生成代码

参考 前端进阶面试题具体解答

过滤器的作用,如何实现一个过滤器

依据过滤器的名称,过滤器是用来过滤数据的,在Vue中应用filters来过滤数据,filters不会批改数据,而是过滤数据,扭转用户看到的输入(计算属性 computed ,办法 methods 都是通过批改数据来解决数据格式的输入显示)。

应用场景:

  • 须要格式化数据的状况,比方须要解决工夫、价格等数据格式的输入 / 显示。
  • 比方后端返回一个 年月日的日期字符串,前端须要展现为 多少天前 的数据格式,此时就能够用fliters过滤器来解决数据。

过滤器是一个函数,它会把表达式中的值始终当作函数的第一个参数。过滤器用在插值表达式 {{ }}v-bind 表达式 中,而后放在操作符“ | ”前面进行批示。

例如,在显示金额,给商品价格增加单位:

<li>商品价格:{{item.price | filterPrice}}</li> filters: {    filterPrice (price) {      return price ? ('¥' + price) : '--'    }  }

Vue中封装的数组办法有哪些,其如何实现页面更新

在Vue中,对响应式解决利用的是Object.defineProperty对数据进行拦挡,而这个办法并不能监听到数组外部变动,数组长度变动,数组的截取变动等,所以须要对这些操作进行hack,让Vue能监听到其中的变动。 那Vue是如何实现让这些数组办法实现元素的实时更新的呢,上面是Vue中对这些办法的封装:

// 缓存数组原型const arrayProto = Array.prototype;// 实现 arrayMethods.__proto__ === Array.prototypeexport const arrayMethods = Object.create(arrayProto);// 须要进行性能拓展的办法const methodsToPatch = [  "push",  "pop",  "shift",  "unshift",  "splice",  "sort",  "reverse"];/** * Intercept mutating methods and emit events */methodsToPatch.forEach(function(method) {  // 缓存原生数组办法  const original = arrayProto[method];  def(arrayMethods, method, function mutator(...args) {    // 执行并缓存原生数组性能    const result = original.apply(this, args);    // 响应式解决    const ob = this.__ob__;    let inserted;    switch (method) {    // push、unshift会新增索引,所以要手动observer      case "push":      case "unshift":        inserted = args;        break;      // splice办法,如果传入了第三个参数,也会有索引退出,也要手动observer。      case "splice":        inserted = args.slice(2);        break;    }    //     if (inserted) ob.observeArray(inserted);// 获取插入的值,并设置响应式监听    // notify change    ob.dep.notify();// 告诉依赖更新    // 返回原生数组办法的执行后果    return result;  });});

简略来说就是,重写了数组中的那些原生办法,首先获取到这个数组的__ob__,也就是它的Observer对象,如果有新的值,就调用observeArray持续对新的值察看变动(也就是通过target__proto__ == arrayMethods来扭转了数组实例的型),而后手动调用notify,告诉渲染watcher,执行update。

slot是什么?有什么作用?原理是什么?

slot又名插槽,是Vue的内容散发机制,组件外部的模板引擎应用slot元素作为承载散发内容的进口。插槽slot是子组件的一个模板标签元素,而这一个标签元素是否显示,以及怎么显示是由父组件决定的。slot又分三类,默认插槽,具名插槽和作用域插槽。

  • 默认插槽:又名匿名查抄,当slot没有指定name属性值的时候一个默认显示插槽,一个组件内只有有一个匿名插槽。
  • 具名插槽:带有具体名字的插槽,也就是带有name属性的slot,一个组件能够呈现多个具名插槽。
  • 作用域插槽:默认插槽、具名插槽的一个变体,能够是匿名插槽,也能够是具名插槽,该插槽的不同点是在子组件渲染作用域插槽时,能够将子组件外部的数据传递给父组件,让父组件依据子组件的传递过去的数据决定如何渲染该插槽。

实现原理:当子组件vm实例化时,获取到父组件传入的slot标签的内容,寄存在vm.$slot中,默认插槽为vm.$slot.default,具名插槽为vm.$slot.xxx,xxx 为插槽名,当组件执行渲染函数时候,遇到slot标签,应用$slot中的内容进行替换,此时能够为插槽传递数据,若存在数据,则可称该插槽为作用域插槽。

Vue 单页利用与多页利用的区别

概念:

  • SPA单页面利用(SinglePage Web Application),指只有一个主页面的利用,一开始只须要加载一次js、css等相干资源。所有内容都蕴含在主页面,对每一个功能模块组件化。单页利用跳转,就是切换相干组件,仅仅刷新部分资源。
  • MPA多页面利用 (MultiPage Application),指有多个独立页面的利用,每个页面必须反复加载js、css等相干资源。多页利用跳转,须要整页资源刷新。

说说Vue的生命周期吧

什么时候被调用?

  • beforeCreate :实例初始化之后,数据观测之前调用
  • created:实例创立万之后调用。实例实现:数据观测、属性和办法的运算、 watch/event 事件回调。无 $el .
  • beforeMount:在挂载之前调用,相干 render 函数首次被调用
  • mounted:了被新创建的vm.$el替换,并挂载到实例下来之后调用改钩子。
  • beforeUpdate:数据更新前调用,产生在虚构DOM从新渲染和打补丁,在这之后会调用改钩子。
  • updated:因为数据更改导致的虚构DOM从新渲染和打补丁,在这之后会调用改钩子。
  • beforeDestroy:实例销毁前调用,实例依然可用。
  • destroyed:实例销毁之后调用,调用后,Vue实例批示的所有货色都会解绑,所有事件监听器和所有子实例都会被移除

每个生命周期外部能够做什么?

  • created:实例曾经创立实现,因为他是最早触发的,所以能够进行一些数据、资源的申请。
  • mounted:实例曾经挂载实现,能够进行一些DOM操作。
  • beforeUpdate:能够在这个钩子中进一步的更改状态,不会触发重渲染。
  • updated:能够执行依赖于DOM的操作,然而要防止更改状态,可能会导致更新无线循环。
  • destroyed:能够执行一些优化操作,清空计时器,解除绑定事件。

ajax放在哪个生命周期?:个别放在 mounted 中,保障逻辑统一性,因为生命周期是同步执行的, ajax 是异步执行的。复数服务端渲染 ssr 同一放在 created 中,因为服务端渲染不反对 mounted 办法。 什么时候应用beforeDestroy?:以后页面应用 $on ,须要解绑事件。分明定时器。解除事件绑定, scroll mousemove

MVC 和 MVVM 区别

MVC

MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计榜样

  • Model(模型):是应用程序中用于解决应用程序数据逻辑的局部。通常模型对象负责在数据库中存取数据
  • View(视图):是应用程序中解决数据显示的局部。通常视图是根据模型数据创立的
  • Controller(控制器):是应用程序中解决用户交互的局部。通常控制器负责从视图读取数据,管制用户输出,并向模型发送数据

MVC 的思维:一句话形容就是 Controller 负责将 Model 的数据用 View 显示进去,换句话说就是在 Controller 外面把 Model 的数据赋值给 View。

MVVM

MVVM 新增了 VM 类

  • ViewModel 层:做了两件事达到了数据的双向绑定 一是将【模型】转化成【视图】,行将后端传递的数据转化成所看到的页面。实现的形式是:数据绑定。二是将【视图】转化成【模型】,行将所看到的页面转化成后端的数据。实现的形式是:DOM 事件监听。

MVVM 与 MVC 最大的区别就是:它实现了 View 和 Model 的主动同步,也就是当 Model 的属性扭转时,咱们不必再本人手动操作 Dom 元素,来扭转 View 的显示,而是扭转属性后该属性对应 View 层显示会主动扭转(对应Vue数据驱动的思维)

整体看来,MVVM 比 MVC 精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不必再用选择器操作 DOM 元素。因为在 MVVM 中,View 不晓得 Model 的存在,Model 和 ViewModel 也察看不到 View,这种低耦合模式进步代码的可重用性

留神:Vue 并没有齐全遵循 MVVM 的思维 这一点官网本人也有阐明

那么问题来了 为什么官网要说 Vue 没有齐全遵循 MVVM 思维呢?

  • 严格的 MVVM 要求 View 不能和 Model 间接通信,而 Vue 提供了$refs 这个属性,让 Model 能够间接操作 View,违反了这一规定,所以说 Vue 没有齐全遵循 MVVM。

$nextTick 是什么?

Vue 实现响应式并不是在数据产生后立刻更新 DOM,应用 vm.$nextTick 是在下次 DOM 更新循环完结之后立刻执行提早回调。在批改数据之后应用,则能够在回调中获取更新后的 DOM

那vue中是如何检测数组变动的呢?

数组就是应用 object.defineProperty 从新定义数组的每一项,那能引起数组变动的办法咱们都是晓得的, pop push shift unshift splice sort reverse 这七种,只有这些办法执行改了数组内容,我就更新内容就好了,是不是很好了解。

  1. 是用来函数劫持的形式,重写了数组办法,具体呢就是更改了数组的原型,更改成本人的,用户调数组的一些办法的时候,走的就是本人的办法,而后告诉视图去更新。
  2. 数组里每一项可能是对象,那么我就是会对数组的每一项进行观测,(且只有数组里的对象能力进行观测,观测过的也不会进行观测)

vue3:改用 proxy ,可间接监听对象数组的变动。

vue如何监听对象或者数组某个属性的变动

当在我的项目中间接设置数组的某一项的值,或者间接设置对象的某个属性值,这个时候,你会发现页面并没有更新。这是因为Object.defineProperty()限度,监听不到变动。

解决形式:

  • this.$set(你要扭转的数组/对象,你要扭转的地位/key,你要改成什么value)
this.$set(this.arr, 0, "OBKoro1"); // 扭转数组this.$set(this.obj, "c", "OBKoro1"); // 扭转对象
  • 调用以下几个数组的办法
splice()、 push()、pop()、shift()、unshift()、sort()、reverse()

vue源码里缓存了array的原型链,而后重写了这几个办法,触发这几个办法的时候会observer数据,意思是应用这些办法不必再进行额定的操作,视图主动进行更新。 举荐应用splice办法会比拟好自定义,因为splice能够在数组的任何地位进行删除/增加操作

vm.$set 的实现原理是:

  • 如果指标是数组,间接应用数组的 splice 办法触发相应式;
  • 如果指标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式解决,则是通过调用 defineReactive 办法进行响应式解决( defineReactive 办法就是 Vue 在初始化对象时,给对象属性采纳 Object.defineProperty 动静增加 getter 和 setter 的性能所调用的办法)

v-model 能够被用在自定义组件上吗?如果能够,如何应用?

能够。v-model 实际上是一个语法糖,如:

<input v-model="searchText">

实际上相当于:

<input  v-bind:value="searchText"  v-on:input="searchText = $event.target.value">

用在自定义组件上也是同理:

<custom-input v-model="searchText">

相当于:

<custom-input  v-bind:value="searchText"  v-on:input="searchText = $event"></custom-input>

显然,custom-input 与父组件的交互如下:

  1. 父组件将searchText变量传入custom-input 组件,应用的 prop 名为value
  2. custom-input 组件向父组件传出名为input的事件,父组件将接管到的值赋值给searchText

所以,custom-input 组件的实现应该相似于这样:

Vue.component('custom-input', {  props: ['value'],  template: `    <input      v-bind:value="value"      v-on:input="$emit('input', $event.target.value)"    >  `})

Vue的基本原理

当一个Vue实例创立时,Vue会遍历data中的属性,用 Object.defineProperty(vue3.0应用proxy )将它们转为 getter/setter,并且在外部追踪相干依赖,在属性被拜访和批改时告诉变动。 每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会告诉watcher从新计算,从而以致它关联的组件得以更新。

形容下Vue自定义指令

在 Vue2.0 中,代码复用和形象的次要模式是组件。然而,有的状况下,你依然须要对一般 DOM 元素进行底层操作,这时候就会用到自定义指令。
个别须要对DOM元素进行底层操作时应用,尽量只用来操作 DOM展现,不批改外部的值。当应用自定义指令间接批改 value 值时绑定v-model的值也不会同步更新;如必须批改能够在自定义指令中应用keydown事件,在vue组件中应用 change事件,回调中批改vue数据;

(1)自定义指令根本内容

  • 全局定义:Vue.directive("focus",{})
  • 部分定义:directives:{focus:{}}
  • 钩子函数:指令定义对象提供钩子函数

    o bind:只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置。

    o inSerted:被绑定元素插入父节点时调用(仅保障父节点存在,但不肯定已被插入文档中)。

    o update:所在组件的VNode更新时调用,然而可能产生在其子VNode更新之前调用。指令的值可能产生了扭转,也可能没有。然而能够通过比拟更新前后的值来疏忽不必要的模板更新。

    o ComponentUpdate:指令所在组件的 VNode及其子VNode全副更新后调用。

    o unbind:只调用一次,指令与元素解绑时调用。

  • 钩子函数参数
    o el:绑定元素

    o bing: 指令外围对象,形容指令全副信息属性

    o name

    o value

    o oldValue

    o expression

    o arg

    o modifers

    o vnode 虚构节点

    o oldVnode:上一个虚构节点(更新钩子函数中才有用)

(2)应用场景

  • 一般DOM元素进行底层操作的时候,能够应用自定义指令
  • 自定义指令是用来操作DOM的。只管Vue推崇数据驱动视图的理念,但并非所有状况都适宜数据驱动。自定义指令就是一种无效的补充和扩大,不仅可用于定义任何的DOM操作,并且是可复用的。

(3)应用案例

高级利用:

  • 鼠标聚焦
  • 下拉菜单
  • 绝对工夫转换
  • 滚动动画

高级利用:

  • 自定义指令实现图片懒加载
  • 自定义指令集成第三方插件

Vue为什么没有相似于React中shouldComponentUpdate的生命周期?

考点: Vue的变动侦测原理

前置常识: 依赖收集、虚构DOM、响应式零碎

根本原因是Vue与React的变动侦测形式有所不同

React是pull的形式侦测变动,当React晓得发生变化后,会应用Virtual Dom Diff进行差别检测,然而很多组件实际上是必定不会发生变化的,这个时候须要用shouldComponentUpdate进行手动操作来缩小diff,从而进步程序整体的性能.

Vue是pull+push的形式侦测变动的,在一开始就晓得那个组件产生了变动,因而在push的阶段并不需要手动管制diff,而组件外部采纳的diff形式实际上是能够引入相似于shouldComponentUpdate相干生命周期的,然而通常正当大小的组件不会有适量的diff,手动优化的价值无限,因而目前Vue并没有思考引入shouldComponentUpdate这种手动优化的生命周期.

父子组件生命周期调用程序(简略)

渲染程序:先父后子,实现程序:先子后父

更新程序:父更新导致子更新,子更新实现后父

销毁程序:先父后子,实现程序:先子后父