Vue
大约 39 分钟
面试题标题前 Emoji 含义
⭐ 表示重点问题。------------------------(必须熟练掌握)
✔️ 表示普通问题。---------------------- (内容不会轻易变动)
❓ 表示此问题还需完善。-----------------(后面会对内容进行完善)
❗ 表示此问题已经过时。-----------------(过时问题不会被剔除,一直存在)
✔️ 1、怎样理解 vue 单项数据流
⭐ 2、v-for 循环为什么一定要绑定 key ?
页面上的标签都对应具体的虚拟dom对象(虚拟dom就是js对象), 循环中 ,如果没有唯一key,页面上删除一条标签, 由于并不知道删除的是那一条! 所以要把全部虚拟dom重新渲染, 如果知道 key为x标签被删除掉, 只需要把渲染的dom为x的标签去掉即可!
21、key 的作用
第一种答法
key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度,key具有唯一性。
让每一个元素有一个唯一的识别身份,主要是为了vue精准的追踪到每一个元素,更高效的更新虚拟DOM。
专业回答:
1.key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch(打补丁)过程中通过key可以精确判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。
2.另外,若不设置key还可能在列表更新时引发一些隐蔽的bug
3.vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为让vue可以区分他们,否则vue只会替换其内部属性而不会触发过渡效果。
第二种答法:
1、key的作用是为了更高效的更新虚拟DOM;
2、vue在patch过程中判断两个节点是否相同key是一个必要条件,渲染一组列表时,key往往是唯一标识,所以如果不定义key的话,vue只能认为比较的两个节点是同一个,哪怕它们实际上不是,这导致了频繁更新元素,使得整个patch过程比较低效,影响性能;
3、实际使用中在渲染一组列表时key必须设置,而且必须是唯一标识,应该避免使用数组索引作为key,这可能导致一些隐蔽的bug;vue中在使用相同标签元素过渡切换时,也会使用key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果;
4、从源码中可以知道,vue判断两个节点是否相同时主要判断两者的key和元素类型等,因此如果不设置key,它的值就是undefined,则可能永远认为这是两个相同节点,只能去做更新操作,这造成了大量的dom更新操作,明显是不可取的。
⭐ 3、Vue 生命周期有哪些?

vue实例从创建到销毁的过程就是生命周期。
也就是从开始创建、初始化数据、编译模板、挂在dom->渲染、更新->渲染、准备销毁、销毁在等一系列过程
vue的声明周期常见的主要分为4大阶段8大钩子函数
另外三个生命周期函数不常用
keep-alive主要用于保留组件状态或避免重新渲染。
activated只有在 keep-alive 组件激活时调用。
deactivated只有在 keep-alive 组件停用时调用。
errorCapured 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
一、创建前 / 后
在 beforeCreate 生命周期函数执行的时候,data 和 method 还没有初始化
在 created 生命周期函数执行的时候,data 和 method 已经初始化完成
二、渲染前/后
在 beforeMount 生命周期函数执行的时候,已经编译好了模版字符串、但还没有真正渲染到页面中去
在 mounted 生命周期函数执行的时候,已经渲染完,可以看到页面
三、数据更新前/后
在 beforeUpdate 生命周期函数执行的时候,已经可以拿到最新的数据,但还没渲染到视图中去。
在 updated 生命周期函数执行的时候,已经把更新后的数据渲染到视图中去了。
四、销毁前/后
在 beforeDestroy 生命周期函数执行的时候,实例进入准备销毁的阶段、此时 data 、methods 、指令等还是可用状态
在 destroyed 生命周期函数执行的时候,实例已经完成销毁、此时 data 、methods 、指令等都不可用
✔️ 4、第一次加载页面会触发哪几个钩子函数?
✔️ 5、虚拟 dom 实现原理
https://www.cnblogs.com/gaosong-shuhong/p/9253959.html
虚拟DOM是什么:用JS对象的形式,表示DOM和DOM之间的嵌套关系.Vue在这个Virtual DOM 上实现了一个 diff 算法,当要重新渲染组件的时候,会通过 diff 寻找到要变更的 DOM 节点,再把这个修改更新到浏览器实际的 DOM 节点上,所以使用虚拟DOM性能会比原生 DOM 快很多。
patch函数
patch函数的执行分为两个阶段,两次传递的参数都是两个
第一阶段为虚拟dom的第一次渲染,传递的两个参数分别是放真实DOM的container和生成的vnode,此时patch函数的作用是用来将初次生成的真实DOM结构挂载到指定的container上面。
第二阶段传递的两个参数分别为vnode和newVnode,此时patch函数的作用是使用diff算法对比两个参数的差异,进而更新参数变化的DOM节点;
可以发发现h函数和patch函数在cnabbdom中实现vdom到真实DOM的转化起到了至关重要的作用,那么还有一个很重要的环节,patch函数中是怎么样实现对比两个vnode从而实现对真实DOM的更新的呢,这里还要提一下snabbdom的另外一个核心算法,即diff算法。
⭐ 6、vue 常见指令
⭐ 7、vue 双向数据绑定(v-model )的原理?
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 变更的双向绑定效果。

⭐ 8、v-if 和 v-show 有什么区别?
v-if是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建,操作的实际上是dom元素的创建或销毁。
v-show就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS进行切换它操作的是 display:none/block属性。
一般来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show较好;如果在运行时条件很少改变,则使用v-if较好。
⭐ 9、组件中的 data 为什么要定义成一个函数而不是一个对象?
每个组件都是Vue的实例。组件共享data属性,当data的值是同一个引用类型的值时,改变其中一个会影响其他
⭐ 10、vuex 的五种状态
⭐ 11、路由模式:hash 和 history
✔️ 12、slot 插槽
✔️ 13、router 的区别
✔️ 14、请说出 vue.cli 项目中 src 目录每个文件夹和文件的用法?
✔️ 15、Vuex 的 5 个核心属性是什么?
✔️ 16、vue 中 keep-alive 的作用
⭐ 17、vue 性能优化
✔️ 18、说说你对 MVVM 思想的理解
MVVM 是一种设计模式,vue,angular等框架就是使用的这种设计思想。MVVM是 Model-View-ViewModel的缩写,即模型-视图-视图模型,
模型(Model): 数据保存—存放着各种数据,有的是固定写死的,大多数是从后端返回的数据
视图 (View):用户界面,也就是DOM
视图模型(View-Model):连接View和Model的桥梁,当数据变化时,ViewModel够监听到数据的变化(通过Data Bindings),自动更新视图,而当用户操作视图,ViewModel也能监听到视图的变化(通过DOM Listeners),然后通知数据做改动,这就实现了数据的双向绑定。
vue就是使用这种编程思想,中心思想就是实现了双向绑定。
✔️ 19、数据双向绑定原理
大概理解的意思是:
vue2.0:
vue.js 采用数据劫持结合发布-订阅者模式,通过 Object.defineproperty 来劫持各个属性的setter和getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
vue3.0:
有两种方式
ref: 通过Object.defineProperty()的get与set来实现响应式(数据劫持)。(定义基本数据类型)
reactive: 通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据. (定义复杂数据类型)
vue2.0双向数据绑定的缺陷:
1、vue 实例创建后,如果再添加属性,不能监听
2、Object.defineProperty的一个缺陷是无法监听数组变化。
js实现简单的双向绑定
<body>
<div id="app">
<input type="text" id="txt">
<p id="show"></p>
</div>
</body>
<script type="text/javascript">
var obj = {};
Object.defineProperty(obj, 'txt', {
get: function () {
return obj
},
set: function (newValue) {
document.getElementById('txt').value = newValue;
document.getElementById('show').innerHTML = newValue;
}
})
document.addEventListener('keyup', function (ev) {
obj.txt = ev.target.value;
})
</script>
20、vue 指令有那些 v-if 和 v-show 的区别
Vue的指令有:
v-show(显示隐藏)、v-bind(绑定属性 简写:)、v-if(条件判断)、v-on(事件监听简写@)、
v-text(渲染文本)、v-for(循环)、v-html(渲染文本和标签)、v-model(表单控件,双向绑定)、
v-once(只渲染一次)、v-slot(插槽简写#)、v-cloak(防闪烁)、v-pre(预编译)
v-if和v-show都可以控制元素的显示与隐藏。
区别:
v-if是通过判断条件重建与销毁DOM元素来控制元素的显示隐藏。
v-show是通过CSS的display来控制元素的显示与隐藏。
v-if频繁切换消耗高,v-show初始渲染消耗高。
使用场景:
所以需要频繁切换状态时,使用 v-show
要加快初始化时的渲染速度(组件的重建和销毁时),使用 v-if
⭐ 22、diff 算法
1.diff 算法是虚拟dom生成的必然产物,通过新旧dom比较,将变化的地方更新到真实的dom上
2.当页面的数据发上变化的时候,Diff算法只会比较同一层级的节点
3.在diff算法时key的作用很重要,就是为了高效的更新虚拟DOM
⭐ 23、watch 和 computed 的区别
区别:
1、computed有缓存,watch没有缓存。
2、computed调用时不需要加括号,watch不需要调用。
3、当属性没有发生改变时computed会从缓存中读取值,watch当属性发生改变时会接收到两个值,一个新值、一个旧值。
4、computed中的函数名可以随意命名,watch中的函数名必须时data中的属性名。
5、computed必须有返回值。
如果watch需要监听对象,必须设置深度监听。(deep = true)
应用场景:
a)多个数据变化影响一个数据的变化,使用computed。监听数据变化时候用computed
b)一个数据变化影响多个数据变化,使用watch。增加业务逻辑用watch
computed计算属性就是为了简化template里面模版字符串的计算复杂度、防止模版太过冗余。它具有
缓存特性
computed用来监控自己定义的变量,该变量不在data里面声明,直接在computed里面定义,然后就
可以在页面上进行双向数据绑定展示出结果或者用作其他处理;
watch主要用于监控vue实例的变化,它监控的变量当然必须在data里面声明才可以,它可以监控一个
变量,也可以是一个对象,一般用于监控路由、input输入框的值特殊处理等等,它比较适合的场景是
一个数据影响多个数据,它不具有缓存性
watch:监测的是属性值, 只要属性值发生变化,其都会触发执行回调函数来执行一系列操作。
computed:监测的是依赖值,依赖值不变的情况下其会直接读取缓存进行复用,变化的情况下才
会重新计算。
✔️ 24、vue 自定义指令
在 Vue,除了核心功能默认内置的指令 ( v-model 和 v-show ),Vue 也允许注册自定义指令。它的作用价值在于当开发人员在某些场景下需要对普通 DOM 元素进行操作
自定义指令:全局自定义指令
局部自定义指令
1、可以使用Vue.directive( dir_name , {} ) 来定义全局自定义指令
2、也可以使用 directives{ dir_name : {} } 来定义局部自定义指令
钩子函数 :
bind
只调用一次, 当指令第一次绑定在元素上时.
用来做一次性初始化操作.
inserted
当绑定元素已插入到父节点时调用.
也就是说 必定存在父节点.
但是 它及它的父节点 未必存在文档中.
update
在包含该组件的VNode更新后调用该函数.
可能在其子级更新前调用.
指令的值可能已更改,可能未更改.最好通过判断新旧值来过滤掉不必要的操作.
基本语法
全局定义指令
Vue.directive('xxx-xxx' , function( el , binding ) {
el.style.backgroundColor = binding.value ;
})
局部定义指令
directives:{ //自定义私有指令
'fontweight':{
bind(el,binding){
el.style.fontWeight = binding.value;
},
},
'color-swatch'(el, binding) { //简写
el.style.backgroundColor = binding.value;
}
}
✔️ 25、说说你对生命周期的理解
生命周期钩子函数:在这个生命周期过程中,vue为我们提供了8个常用的钩子函数。
生命周期:
总共分为8个阶段,创建前/后,载入前/后,更新前/后,销毁前/后。
共三个阶段:
1、创建阶段
beforeCreate:
阶段,vue实例的挂载元素$el和数据对象data都为undefined,data和methods还未初始化。
created:
阶段,数据对象data和methods已经初始化,但$el还没有,视图和实例还没有挂载。
beforeMount:
阶段,vue实例的$el和数据对象data都已经初始化,但还是虚拟的dom节点,视图和实例还没有挂载。
mounted:
阶段,模板已经编译好,虚拟dom渲染成真正的dom标签,数据已经渲染到页面,Vue实例已经创建完毕。
2、运行阶段
beforeUpdate:
阶段当数据依赖改变时,对象data中的 数据已经更改(虚拟dom已经重新渲 染),但是还未开始渲染真正dom,页面中的值还是原来的。
updated:
阶段data中的数据更新完毕,页面已经被重新渲染。
3、销毁阶段
beforeDestroy:
阶段实例还是可用的一般在这里做一些善后工作。(例如清除定时器、清除非指令绑定的事件等等。)。
destroyed:
阶段Vue实例被销毁,观察者、子组件、事件监听被清除(页面数据不会消失,只是响应式无效了)。
一般会在created阶段做网络请求,在mounted阶段对第三方插件的初始化。
在destroyed阶段进行清除定时器和一些事件的解绑
vue3.0
✔️ ⭐ 27、组件之间传值
父传子:
父组件发送的形式是用v-bind绑定值到子组件身上。然后子组件用属性props接收。
props验证
父子组件生命周期的执行顺序
子传父:
子组件使用$emit(事件名,需要发送的值)触发自定义事件,父组件使用v-on监听(@事件名=获取值的方法)。
兄弟:
A)、两个都要引入公共js->bus。首先是发送者,bus.$emit(名字,值)。然后是接受者,bus.$on("传过来的那个名字",函数(值)),这里要注意的是接受者的这个监听事件应该是放在钩子mounted事件内。同时注意this指向问题。(可用封装方法)
B)、使用vuex和localStroage进行传值
C)、借助父组件,让两个兄弟组件可以联动,子组件A将值传递给父组件,父组件再将值传递给子组件B
隔代组件:
vuex、本地存储、事件车、$attrs/$listeners、provide / inject都可以实现
✔️ 28、你都封装过什么组件
组件的分类:功能性组件、内容性组件、页面组件
功能性组件:图片轮播、tabbar、tab切换、loading、分页效果等。
页面组件:首页、我的、购物车等
内容性组件:商品列表、商品详情等
✔️ 29、什么是路由
.后端路由:对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源,这个对应关系就是后端中的路由;
因为现在都是前后端分离,所以我们使用的是前端主流的框架,vue框架,vue是单页面应用,为了访问不同的页面和不同的组件,所以需要用路由来进行跳转页面
前端路由:对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;
优点:
1、用户体验好与后台网速没有关系,不需要每次都从服务器全部获取数据,就能快速展现给客户。
2、可以在浏览器中输入指定想要访问的url地址。
3、实现前后端分离,方便开发。有很多框架都带有路由功能模块。
缺点:
1、使用浏览器前进后退按钮时,会重新发送请求。
2、单页面应用中在使用前进后退时无法记住滚动位置。
后端路由:
对于普通的网站,所有的超链接都是url地址,所有url都对应服务器上对应的资源。
浏览器在地址栏中切换不同的url时,每次都向后台服务器发出请求,服务器响应请求,在后台拼接html文件传给前端显示, 返回不同的页面,
意味着浏览器会刷新页面,网速慢的话说不定屏幕全白再有新内容。后端路由的另外一个极大的问题就是 前后端不分离。
优点:
分担了前端的压力,html和数据的拼接都是由服务器完成。
缺点:
当项目十分庞大时,加大了服务器端的压力,同时在浏览器端不能输入制定的url路径进行指定模块的访问。另外一个就是如果当前网速过慢,
那将会延迟页面的加载,对用户体验不是很友好。
30、路由传参
Params传参和query传参
区别:
1、params通过name传参,query通过path传参
2、query相当于get请求,参数会在地址栏显示,而params相当于post请求,参数不会再地址栏显示。
3、用params传参,F5强制刷新参数会被清空,用query,由于参数是用路径传参的所以F5强制刷新也不会被清空。
4、使用这两种方式传参的时候都可通过路由的name进行传参,所以在配置路由的时候需要加个name属性
31、如何配置路由
在我们的项目搭建完成时候会有一个router的文件夹里边有一个index.js文件,在这个文件里进行配置
第一步:引入需要配置路由的组件
第二步:在index.js中有routes数组 在数组里边有对象 每一个对象就是每一条路由(在routes数组添加一个对象)
第三步:在对象内添加path和component。path的值为组件对应的路径,component的值为路径对应的组件。
当然我们也可根据实际情况进行路由属性的添加及使用。
比如:
name、meta、beforeEnter、redirect、params、query
⭐ 32、route 的区别
router为VueRouter的实例,是一个全局路由对象,包含了路由跳转的方法、钩子函数等。
route 是路由信息对象||跳转的路由对象,每一个路由都会有一个route对象,是一个局部对象,包含path,params,hash,query,fullPath,matched,name等路由信息参数。
✔️ 33、路由钩子的理解
导航钩子: 就是路由的生命周期
分为:全局路由守卫、组件内守卫、路由独享守卫
全局的钩子函数
beforeEach() 在路由切换开始时调用
afterEach() 在路由切换离开时调用
路由独享守卫
beforeEnter
组件的钩子函数
beforeRouterEnter
beforeRouterUpdate
beforeRouterLeave
项目中一般使用全局路由守卫的中beforeEach,进行登录拦截。
✔️ 34、路由模式及区别
hash模式
地址栏 URL 中的 # 符号,hash 虽然出现在 URL >中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。但是页面刷新的时候,会向服务器端请求数据。
history模式
HTML5新增的, 改变URL而不刷新页面,地址栏没有#号,在项目打包的时候如果使用history模式,前端的URL必须和实际向后端发起请求的URL一致,后台配置支持,否则就会出现404错误
✔️ 35、路由组件有那些及如何使选中路由高亮显示
router-link router-view
通过linkActiveClass属性设置类名
⭐ 36、对 keep-alive 的理解
keep-alive是Vue的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁
用来缓存组件,避免多次加载相同的组件,减少性能消耗,提高用户体验。
不希望组件被重新渲染影响使用体验, keep-alive生命周期钩子函数activated 和 deactivated,主要用于保留组件状态或避免重新渲染,在组件切换过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性.
vue2.0 如何 使用keep-alive?
<keep-alive>
<router-view></router-view>
</keep-alive>
vue3.0 如何 使用keep-alive?
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
✔️ 37、说说你对 vuex 的理解
vuex的概念:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
vuex的应用场景:组件嵌套太多,组件数据传递不太方便的时候,可以使用vuex,比如购物车、用户信息等一些共享数据。
五个状态分别是:State、Getters、 Mutations、Actions、Modules。
State:存储状态(数据),在根实例中注册了store 后,用 this.$store.state 来访问store里面的状态, 存放数据方式为响应式。
getters:可以认为是 store 的计算属性,它的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改 变才会被重新计算。
mutations:存储同步函数的,更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
actions:存储任意异步操作,通过提交 mutation 间接改变状态。
modules:将 store 分割成模块,每个模块都具有state、mutations、actions、getters、甚至是嵌套子模块
vuex的优点
1.解决了非父子组件的消息传递(将数据存放在state中)
2.减少了AJAX请求次数,有些情景可以直接从内存中的state获取
vuex的缺点
1.刷新浏览器,vuex中的state会重新变为初始状态
解决方案vuex-along ,vuex-persistedstate
https://github.com/robinvdvleuten/vuex-persistedstate
得配合计算属性和sessionStorage来实现
vuex工作的流程:首先通过mapAction异步提交到action,action通过commit操作mutation,mutation会修改state中的值,state渲染页面
取出state中的值
1、可以根据$store.state
2、根据mapState来取值
✔️ 38、nextTick 的作用
nextTick:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM。
vue是依靠数据驱动视图更新的,该更新的过程是异步的。
应用场景:
当数据更新了,在dom中渲染后,自动执行该函数
1、如果要在created()钩子函数中进行的DOM操作,由于created()钩子函数中还未对DOM进行任何渲染,所以无法直接操作,需要通过$nextTick()来完成。
Vue生命周期的created()钩子函数进行的DOM操作要放在Vue.nextTick()的回调函数中。
2、更新数据后,想要使用js对新的视图进行操作时
3、在使用某个第三方插件时 ,希望在vue生成的某些dom动态发生变化时重新应用该插件,也会用到该方法,这时候就需要在 $nextTick 的回调函数中执行重新应用插件的方法。
Vue.nextTick()的回调函数中;
通俗的理解是:更改数据后想立即使用js操作新的视图的时候需要使用nextTick。
✔️ 39、vue 组件 data 为什么要是函数形式而根实例则没有此限制?
1、Vue组件可能存在多个实例,如果使用对象形式定义data,则会导致它们公用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的;采用函数形式定义,在initData时会将其作为工厂函数返回全新data对象,有效规避多实例之间状态污染问题。
2、而在Vue根实例创建过程中则不存在该限制,也是因为根实例只有一个,不需要担心这种情况
如果 data 是个对象,那么整个vue实例将共享一份数据,也就是各个组件实例间可以随意修改其他组件的任意值,这样对于实际开发时很难受的。
但是 data 定义成一个函数,将会 return 出一个唯一的对象,不会和其他组件共享一个对象。
✔️ 40、混入 (Mixin)
简单来说混入和vue的使用方式差不多一样,都可以实现数据存储以及数据共享,我来说下它们的区别
vuex:用来做状态管理的,里面定义的变量在每个组件中均可以使用和修改,在任一组件中修改此变量的值之后,其他组件中此变量的值也会随之修改。
混入 (mixin) : 是一种分发Vue组件中可复用功能的非常灵活的一种方式。混入对象(mixins)是一个js对象,它可以包含我们组件中script项中的任意功能选项,如data、components、created、methods 、computed、watch等等。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
在每个组件中使用,引入组件中之后,各个变量是相互独立的,值的修改在组件中不会相互影响。
分为全局混入和局部混入
// 定义一个全局的Mixin对象
// myMixins.js
export const myMixins = {
components:{},
data() {
return {
age: 18,
}
},
mounted() {
this.getAge()
},
methods: {
getAge() {
// 打印出来data中age
console.log(this.age)
}
}
}
// 需要使用的组件1 使用myMixins.js
<div>
<el-button @click="age++">增加</el-button>
<div>{{age}}</div>
<Son></Son>
</div>
import { myMixins } from "@/mixins/myMixins.js";
import Son from './son'
export default {
mixins: [myMixins],
data() {
return {}
},
}
// 需要使用的组件Son 使用myMixins.js
<div> {{age}}</div>
export default {
mixins: [myMixins],
data() {
return {}
},
}
✔️ 41、vue 中的两个内置组件
transition keep-alive
⭐ 42、vue3.0 和 2.0 的区别
https://blog.csdn.net/qq_39045645/article/details/106372407
https://blog.csdn.net/qq_42308316/article/details/111035113
2020年9月19日凌晨,尤雨溪大大正式发布了 Vue.js 3.0 版本
vue3.0的新特性
1、createApp创建Vue对象
2、新增Composition API,更好的逻辑复用和代码组织。
Composition API 是以函数的形式展示组件属性,所以第一步就是导入我们需要的函数。在我们的例子中,我们用 reactive 创建响应属性,用 computed 创建计算属性。
1)、setup 函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点。
2)、ref ref是来实现响应式数据的方法 ref方法来实现变量的简单监听
3)、reactive vue3中提供的实现响应式数据的方法,reactive参数必须是对象
4)、computed() 函数用来创建计算属性,函数的返回值是一个 ref 的实例、watch() 函数用来监视数据的变化,从而触发特定的操作
5)、生命周期的变化
(onBeforeMount onMount onBeforeUpdate onUpdated onBeforeUnmount onUnmounted onEorrorCaptured)
2、重构响应式系统,使用Proxy替换Object.defineProperty实现数据的双向绑定
Vue2.0使用Object.defineProperty
原理:通过使用 Object.defineProperty 来劫持对象属性的 geter 和 seter 操作,当数据发生改变发出通知
Vue 3.0使用ES6的新特性porxy
原理:通过ES6的新特性proxy来劫持数据,当数据改变时发出通知
Vue2.x版本中的双向绑定不能检测到下标的变化
proxy可以劫持整个对象,并返回一个新对象
3、filter被移除
4、重构virtual dom
5、使用Typrscript替换Flow(React当前还是Flow)
vue2.x数据双向绑定
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="yingaxiang" content="width=device-width, initial-scale=1.0">
6 <title>vue2.x数据双向绑定</title>
7 </head>
8 <body>
9 <div>
10 <input type="text" id="input">
11 <span id="text"></span>
12 </div>
13 </body>
14 </html>
15 <script>
16 var obj = {};
17 Object.defineProperty(obj, 'prop', {
18 get: function () {
19 return val;
20 },
21 set: function (newVal) {
22 val = newVal;
23 document.getElementById('text').innerHTML = val;
24 }
25 });
26 document.addEventListener('keyup', function (e) {
27 obj.prop = e.target.value;
28 });
29 </script>
vue3.0数据双向绑定
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="yingaxiang" content="width=device-width, initial-scale=1.0">
6 <title>vue3.0数据双向绑定</title>
7 </head>
8 <body>
9 <div>
10 <input type="text" id="input">
11 <span id="text"></span>
12 </div>
13 </body>
14 </html>
15 <script>
16 var obj = {};
17 var obj1 = new Proxy(obj, {
18 // target就是第一个参数obj, receive就是返回的obj(返回的proxy对象)
19 get: function (target, key, receive) {
20 // 返回该属性值
21 return target[key];
22 },
23 set: function (target, key, newVal, receive) {
24 // 执行赋值操作
25 target[key] = newVal;
26 document.getElementById('text').innerHTML = target[key];
27 }
28 })
29 document.addEventListener('keyup', function (e) {
30 obj1[0] = e.target.value;
31 });
32 </script>
✔️ 43、图片懒加载原理
在图片没有进入可视区域时,先不给<img>的src赋值,这样浏览器就不会发送请求了,等到图片进入可视区域再给src赋值。
vue-lazyload.js
就是我们先设置图片的data-set属性(当然也可以是其他任意的,只要不会发送http请求就行了,作用就是为了存取值)值为其图片路径,由于不是src,所以不会发送http请求。 然后我们计算出页面scrollTop的高度和浏览器的高度之和, 如果图片距离页面顶端的坐标Y(相对于整个页面,而不是浏览器窗口)小于前两者之和,就说明图片就要显示出来了(合适的时机,当然也可以是其他情况),这时候我们再将 data-set 属性替换为 src 属性即可。
✔️ 46、vue 和 react 的区别
Vue和React的共同点
01. react采用特殊的JSX语法,Vue.js在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定
02. 两者都需要编译后使用
03. 中心思想相同:一切都是组件,组件实例之间可以嵌套
04. 都提供合理的钩子函数,可以让开发者定制化地去处理需求
05. 都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载
Vue和React的不同点
01. Vue性能更好、Vue性能上更有优势,因为 Vue 的 Virtual DOM 实现相对更为轻量一些,整体大小 比react.js更小更轻便
02. react.JS 彩用SX语法,对语大部分工程师入手比较困难,需要要大量的时间去学习适应这种新语法的 形式,vue采用DOM模板的方法,易看易用
React 和 Vue 的区别
**1.监听数据变化的实现原理不同**
Vue 通过 getter/setter 方法以及一些函数的劫持能精确知道数据的变化。
React 默认是通过比较引用方式 diff 算法进行的,若不优化,会导致大量不必要的 VDom 的重新渲染。
2.数据流不同
Vue 实现双向绑定:props 可以双响绑定,组件与 Dom 之间可以通过 v-moudel 绑定。
React 不支持双向绑定:提倡单向数据流,称之为 onChange/setState 模式。
3.组件通信的区别
Vue 中有三种方式可以实现组件通信:
a. 父组件通过 props 向子组件传递数据或者回调,虽然可以传递回调,但是一般只传递数据;
b. 子组件通过事件向父组件发送消息;
c. 通过 Vue2.2 中新增的 provide/inject 来实现父组件向子组件注入数据,可以跨越多个层级。
React 中有三种方式实现组件通信:
a. 父组件通过 props 向子组件传递数据或者回调;
b. 可以通过 context 进行跨层级的通信;
c. React 本身不支持自定义事件,而 Vue 中子组件向父组件传递消息有两种方式:事件和回调函数。
4.模板渲染方式不同
在表层上,模板的语法不同。React 是通过 jsx 来渲染模板,Vue 是通过一种拓展的 html 语法来进行渲染,但其实这只是表面现象,毕竟 React 并不必依赖 jsx.
在深层上,模板的原理不同。本质的区别在于:React 是在组件 js 代码中,通过原生 js 实现模板汇总的常见语法,比如插值、条件、循环等,都是通过 js 语法实现,更加纯粹、原生;而 Vue 是在和组件 js 代码分离的单独模板中,通过指令来实现的,比如条件语句 v-if。
】5.模板渲染过程不同
Vue 可以更快的计算出 Virtual Dom 的差异,这是由于它在渲染过程中,会跟踪每个组件的依赖关系,不需要更新渲染整个组件树。
React 在应用的状态被改变时,全部子组件都会重新渲染。通过 shouldComponentUpdate 这个生命周期函数可以进行控制,但 Vue 将次视为默认的优化。
如果应用中交互复杂,需要处理大量的 UI 变化,那么使用 Virtual Dom 是一个好主意,如果更新不频繁,可以直接操控 DOM。
6.框架本质不同
Vue 本职是 MVVM 框架,有 MVC 发展而来。
React 是前端组件框架,有都断组件演化而来。
✔️ 48、vueX 数据是临时存储的
https://www.cnblogs.com/wang-sai-sai/p/11076833.html
✔️ 49、v-for 和 v-if 的优先级? (能在一行写吗?如果不能?怎么解决?)
答:v-for优先级大于v-if
不能写在一行,
解决办法:可以加template写v-for 下边的div写v-if
✔️ 50、api 接口请求一般放在那个生命周期函数中?
答:我们在做项目的时候,一般放在create生命周期函数中,但是要操作dom的话放在create里不支持,这时候需要放在mounted中
✔️ 51、vue 组件从创建开始到创建结束经历了哪几个生命周期函数?
答:从开始到创建一共经历了四个生命周期
1、beforeCreate
2、beforeCreated
3、beforeMount
4、mounted
✔️ 52、页面中有父子组件,生命周期顺序如何执行?
1、父创建前beforeCreate
2、父创建之后created
3、父挂载之前beforeMount
4、子创建前beforeCreate
5、子创建之后created
6、子挂载之前beforeMount
7、子挂载之后mounted
8、父挂载之后mounted
✔️ 53、子组件销毁过程生命周期
1、父更新之前beforeUpdate
2、子销毁之前beforeDestroy
3、子销毁之后destroyed
4、父更新之后updated
✔️ 54、vue.js 的两个核心
1、数据驱动
2、组件化
✔️ 55、动态路由
动态路由只要是依于vue-router实例router提供的addRoute函数,可以让我们在[路由表中]|(路由配置中),动态添加路由对象的能力,从而达到动态路由的目的.
一般用于作为权限管理的页面访问控制时候使用.
页面访问控制:
1、路由配置静态 > 用户登录 > 返回权限 > 路由守卫 进行拦截9
2、通过动态路由实现页面访问控制:
用户登录 > 返回页面 > addRoute函数 添加到路由表
用户登录成功之后,后台会给我们返回该角色对应的权限以及对应的路径,这时候我们可以直接通过addRote函数把该角色的路径循环追加到我们的路由表中,但是这时候我们发现一个问题,就是页面一刷新数据就会丢失,所以我们选择登录成功之后把数据存放到本地缓存中,然后在我们的路由文件中,在beforeEach的钩子里面把我们存入缓存的数据取出来,然后再通过addRote循环追加到我们的路由表中。这就实现了动态路由
✔️ 56、vue 单页面的优缺点?
Vue单页面应用及其优缺点
缺点:
不支持低版本的浏览器,最低支持到IE9。
不利于SEO的优化(如果要支持SEO,建议通过服务端来进行渲染组件)。
第一次加载首页耗时相对长一些。
不可以使用浏览器导航的前进和后退按钮
优点:
无刷新体验,提升了用户体验。
前端开发不再以页面为单位,更多地采用组件化的思想,代码结构和组织方法更加规范化,便于修改和调整。
API共享,同一套后端程序代码不用修改就可以用于Web页面、手机、平板等多种客户端。
用户体验好、快,内容的改变不需要重新加载整个页面。
vue优点?
答:双向数据绑定,组件化,轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十kb;
简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习;
双向数据绑定:保留了angular的特点,在数据操作方面更为简单;
组件化:保留了react的优点,实现了html的封装和重用,在构建单页面应用方面有着独特的优势;
视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;
虚拟DOM:dom操作是非常耗费性能的, 不再使用原生的dom操作节点,极大解放dom操作,但具体操作的还是dom不过是换了另一种方式;
运行速度更快:相比较与react而言,同样是操作虚拟dom,就性能而言,vue存在很大的优势
✔️ 57、vue 中除了 v-model 还有其他的可以实现数据双向绑定嘛
1、input框的话可以使用 @input事件 输出e.target.value
2、单选框的话可以使用@change事件 也是输出e.target.value
✔️ 58、vue 实例是挂载到哪个标签上的?
vue实例最后会挂载在body标签里面,所以我们在vue中是获取不了body 标签的,如果要使用body标签的话需要用原生的方式获取
✔️ 59、vue-cli 2.0 和 3.0 有什么区别?
3.0 把配置 webpack 的文件隐藏了,如果需要配置它需要创建一个 vue.config.js 文件,3.0 是 2018.10 月
出来的
✔️ 60、什么 vuex ,谈谈你对它的理解?
1. 首先 vuex 的出现是为了解决 web 组件化开发的过程中,各组件之间传值的复杂和混乱的问题
2. 将我们在多个组件中需要共享的数据放到 store 中,
3. 要获取或格式化数据需要使用 getters,
4. 改变 store 中的数据,使用 mutation,但是只能包含同步的操作,在具体组件里面调用的方式
this.$store.commit('xxxx')
5. Action 也是改变 store 中的数据,不过是提交的 mutation,并且可以包含异步操作,在组件中的调
用方式 this.$store.dispatch('xxx') ; 在 actions 里面使用的 commit('调用 mutation')
✔️ 61、vue 中是怎么实现父向子、子向父、兄弟之间的传值的?
父向子传值主要通过的是 props 属性来传值,props 只读
子向父传值 $emit
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>父组件向子组件传值--props</title>
<script src="./js/vue.min.js"></script>
</head>
<body>
<div id="app">
<menu-item title="来自父组件的值"></menu-item>
<!-- 在子组件身上绑定自定义属性来接收父组件data中的数据 -->
<menu-item :tit="title"></menu-item>
</div>
<script>
Vue.component('menu-item',{
props:['tit'], //props用来接收父组件传过来的值
//在props中使用驼峰形式,模版中要改为使用短横线拼接 props里面的值只读,不能修改
//props是单向数据流
data(){
return{
}
},
template:'<div>{{tit}}</div>'
})
var vm=new Vue({
el:'#app',
data:{
title:'我是父组件中的数据'
},
methods:{
}
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
兄弟组件传值 事件总线
<!-- 父组件 -->
<div :style='{fontSize:fontSize+"px"}'>{{pmsg}}</div>
<!-- 子组件 -->
<menu-item :parr="parr" @aas="blune"></menu-item>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
子组件向父组件传值-基本用法
props传递数据原则:单向数据流
*/
Vue.component('menu-item', {
props:['parr'],
data(){
return {
msg1:'这是子组件传递过来的值'
}
},
template: `
<div>
<ul>
<li v-for="(item,index) in parr" :key="index">{{item}}</li>
</ul>
<button @click='dd'>扩大父组件中字体大小</button>
</div>
`,
methods:{
dd(){
this.$emit("aas",this.msg1)
}
}
});
//$emit
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
parr: ['apple','orange','banana'],
fontSize: 10
},
methods: {
blune(message){
this.fontSize+=5;
console.log(message);
}
}
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./js/vue.min.js"></script>
</head>
<body>
<div id="app">
<brother></brother>
<sister></sister>
</div>
<script>
var enveBus = new Vue();
Vue.component('brother', {
data() {
return {
kk: ''
}
},
methods: {
dd() {
enveBus.$emit("bTs", '这是哥哥给妹妹的爱')
}
},
template: `
<div>
<button @click='dd'>这是一个哥哥组件---{{kk}}</button>
</div>
`,
mounted() {
enveBus.$on('asd', (result) => {
this.kk = result;
})
}
});
Vue.component('sister', {
data() {
return {
sis: ''
}
},
template: `
<div>
<button @click="cc">这是一个妹妹组件---{{sis}}</button>
</div>
`,
mounted() {
enveBus.$on('bTs', (message) => {
this.sis = message
})
},
methods: {
cc() {
enveBus.$emit('asd', '这是妹妹对哥哥的爱');
}
}
});
var vm = new Vue({
el: '#app',
不
同
类
型
的
优
缺
点
typeof instanceof constructor Object.prototype.toString.call
优
点
使用简单
能检测出引用
类型
基本能检测所有
的类型(除了
null 和
undefined)
检测出所有的类型
缺
点
只能检测
出基本类
型(出
null)
不能检测出基
本类型,且不
能跨 iframe
constructor 易被
修改,也不能跨
iframe
IE6 下,undefined 和 null 均为
Object
61、vue 有几种插槽
默认插槽,具名插槽,作用域插槽
默认插槽:就是通过替换占位符达到在父组件中更改子组件中内容的效果。
具名插槽:其实就是给子组件中的插槽取一个名字,而父组件就可以在引用子组件的时候,根据这个名字对号入座,将相应内容填充到相应的插槽中。
作用域插槽:通过slot标签来实现作用域插槽