写过自定义指令吗?原理是什么
答复范例
Vue
有一组默认指令,比方v-model
或v-for
,同时Vue
也容许用户注册自定义指令来扩大Vue能力- 自定义指令次要实现一些可复用低层级
DOM
操作 - 应用自定义指令分为定义、注册和应用三步:
- 定义自定义指令有两种形式:对象和函数模式,前者相似组件定义,有各种生命周期;后者只会在
mounte
d和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-model
和 v-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 />
钩子函数
bind
:只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置。inserted
:被绑定元素插入父节点时调用 (仅保障父节点存在,但不肯定已被插入文档中)。update
:被绑定于元素所在的模板更新时调用,而无论绑定值是否变动。通过比拟更新前后的绑定值,能够疏忽不必要的模板更新。componentUpdated
:被绑定元素所在模板实现一次更新周期时调用。unbind
:只调用一次,指令与元素解绑时调用。
所有的钩子函数的参数都有以下:
el
:指令所绑定的元素,能够用来间接操作 DOMbinding
:一个对象,蕴含以下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
钩子中可用
除了 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.设置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>
- 图片懒加载
设置一个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;
- 一键 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;
- 拖拽
<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; }; }; }, }, }
原理
- 指令实质上是装璜器,是
vue
对HTML
元素的扩大,给HTML
元素减少自定义性能。vue
编译DOM
时,会找到指令对象,执行指令的相干办法。 - 自定义指令有五个生命周期(也叫钩子函数),别离是
bind
、inserted
、update
、componentUpdated
、unbind
原理
- 在生成
ast
语法树时,遇到指令会给以后元素增加directives
属性 - 通过
genDirectives
生成指令代码 - 在
patch
前将指令的钩子提取到cbs
中,在patch
过程中调用对应的钩子 - 当执行指令对应钩子函数时,调用对应指令定义的办法
说一下Vue的生命周期
Vue 实例有⼀个残缺的⽣命周期,也就是从开始创立、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是Vue的⽣命周期。
- beforeCreate(创立前):数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能拜访到data、computed、watch、methods上的办法和数据。
- created(创立后) :实例创立实现,实例上配置的 options 包含 data、computed、watch、methods 等都配置实现,然而此时渲染得节点还未挂载到 DOM,所以不能拜访到
$el
属性。 - beforeMount(挂载前):在挂载开始之前被调用,相干的render函数首次被调用。实例已实现以下的配置:编译模板,把data外面的数据和模板生成html。此时还没有挂载html到页面上。
- mounted(挂载后):在el被新创建的 vm.$el 替换,并挂载到实例下来之后调用。实例已实现以下的配置:用下面编译好的html内容替换el属性指向的DOM对象。实现模板中的html渲染到html 页面中。此过程中进行ajax交互。
- beforeUpdate(更新前):响应式数据更新时调用,此时尽管响应式数据更新了,然而对应的实在 DOM 还没有被渲染。
- updated(更新后) :在因为数据更改导致的虚构DOM从新渲染和打补丁之后调用。此时 DOM 曾经依据响应式数据的变动更新了。调用时,组件 DOM曾经更新,所以能够执行依赖于DOM的操作。然而在大多数状况下,应该防止在此期间更改状态,因为这可能会导致更新有限循环。该钩子在服务器端渲染期间不被调用。
- beforeDestroy(销毁前):实例销毁之前调用。这一步,实例依然齐全可用,
this
仍能获取到实例。 - destroyed(销毁后):实例销毁后调用,调用后,Vue 实例批示的所有货色都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。
另外还有 keep-alive
独有的生命周期,别离为 activated
和 deactivated
。用 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中的模板编译原理,次要过程:
- 将模板转换成
ast
树,ast
用对象来形容实在的JS语法(将实在DOM转换成虚构DOM) - 优化树
- 将
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
这七种,只有这些办法执行改了数组内容,我就更新内容就好了,是不是很好了解。
- 是用来函数劫持的形式,重写了数组办法,具体呢就是更改了数组的原型,更改成本人的,用户调数组的一些办法的时候,走的就是本人的办法,而后告诉视图去更新。
- 数组里每一项可能是对象,那么我就是会对数组的每一项进行观测,(且只有数组里的对象能力进行观测,观测过的也不会进行观测)
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 与父组件的交互如下:
- 父组件将
searchText
变量传入custom-input 组件,应用的 prop 名为value
; - 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这种手动优化的生命周期.
父子组件生命周期调用程序(简略)
渲染程序:先父后子,实现程序:先子后父
更新程序:父更新导致子更新,子更新实现后父
销毁程序:先父后子,实现程序:先子后父