前端理论面试--VUE

Vue 3.0 性能提升主要是通过哪几方面体现的?

vue2在初始化的时候,对data中的每个属性使用definepropery调用getter和setter使之变为响应式对象。如果属性值为对象,还会递归调用defineproperty使之变为响应式对象。
vue3使用proxy对象重写响应式。proxy的性能本来比defineproperty好,proxy可以拦截属性的访问、赋值、删除等操作,不需要初始化的时候遍历所有属性,另外有多层属性嵌套的话,只有访问某个属性的时候,才会递归处理下一级的属性。
源码体积的优化

分别简述computed和watch的使用场景

答:computed:
    当一个属性受多个属性影响的时候就需要用到computed
    最典型的栗子: 购物车商品结算的时候
watch:
    当一条数据影响多条数据的时候就需要用watch
    栗子:搜索数据

1.vue双向绑定的原理(详细链接)

VUE实现双向数据绑定的原理就是利用了 Object.defineProperty() 这个方法重新定义了对象获取属性值(get)和设置属性值(set)的操作来实现的。
它接收三个参数,要操作的对象,要定义或修改的对象属性名,属性描述符。重点就是最后的属性描述符。属性描述符是一个对象,主要有两种形式:数据描述符和存取描述符。这两种对象只能选择一种使用,不能混合两种描述符的属性同时使用。上面说的get和set就是属于存取描述符对象的属性
代码演示:defineProperty的用法var obj = { };var name;//第一个参数:定义属性的对象。//第二个参数:要定义或修改的属性的名称。//第三个参数:将被定义或修改的属性描述符。

Object.defineProperty(obj, "data", {//获取值
get: function () {return name;},//设置值
set: function (val) {name = val;console.log(val)}})//赋值调用
setobj.data = 'aaa';//取值调用
getconsole.log(obj.data);
// 代码演示:defineProperty的双向绑定
var obj={};
Object.defineProperty(obj, 'val',{set:function (newVal) {
document.getElementById("a").value=newVal==undefined?'':newVal;
document.getElementById("b").innerHTML=newVal==undefined?'':newVal;}});
document.getElementById("a").addEventListener("keyup",function (e) {obj.val = e.target.value;})

1.Vue中双向数据绑定是如何实现的?
答:vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;
核心:关于VUE双向数据绑定,其核心是 Object.defineProperty()方法。

2.组件之间的传值?

  1. 父组件通过标签定义参数传值
    子组件通过props方法接受数据
  2. 子组件通过$parent获取父组件的属性和方法
    父组件通过$children获取父组件的属性和方法
    注:组件嵌套时候等不到你想要的结果
    用 $children放回的是集合,而没有规定的顺序。
  3. 子组件通过$emit方法定义点击‘’方法名‘’,父组件通过@‘方法名’触发父组件的方法
    父组件通过$refs获取通过ref绑定的组件节点,然后可以调用子组件的属性和方法
  4. 组件和组件之间通过vue路由传参
  5. vuex 处理组件之间的数据交互
    如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候才有上面这一些方法可能不利于项目的维护,vuex的做法就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。
  6. 前方高能
  7. $attrs$listeners
    如果父组件A下面有子组件B,组件B下面有组件C,这时如果组件A想传递数据给组件C怎么办呢?
    如果采用prop方法,我们必须让组件A通过prop传递消息给组件B,组件B在通过prop传递消息给组件C;要是组件A和组件C之间有更多的组件,那采用这种方式就很复杂了。
Vue.component('C',{
       template:`
           <div>
               <input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)"> </div>
       `,

       methods:{
           passCData(val){
               //触发父组件A中的事件
             this.$emit('getCData',val)
           }
       }
   })

   Vue.component('B',{
       data(){
           return {
               mymessage:this.message
           }
       },
       template:`
           <div>
               <input type="text" v-model="mymessage" @input="passData(mymessage)"> 
               <!-- C组件中能直接触发getCData的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners >属性 -->
               <!-- 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) -->
               <C v-bind="$attrs" v-on="$listeners"></C>
           </div>
       `,
       props:['message'],//得到父组件传递过来的数据
       methods:{
           passData(val){
               //触发父组件中的事件
               this.$emit('getChildData',val)
           }
       }
   })
   Vue.component('A',{
       template:`
           <div>
               <p>this is parent compoent!</p>
               <B :messagec="messagec" 
                   :message="message" 
                   v-on:getCData="getCData" 
                   v-on:getChildData="getChildData(message)"></B>
           </div>
       `,
       data(){
           return {
               message:'hello',
               messagec:'hello c' //传递给c组件的数据
           }
       },
       methods:{
           getChildData(val){
               console.log('这是来自B组件的数据')
           },
           //执行C子组件触发的事件
           getCData(val){
               console.log("这是来自C组件的数据:"+val)
           }
       }
   })
   var app=new Vue({
       el:'#app',
       template:`
           <div>
               <A></A>
           </div>
       `
   })
  1. provide和inject
    父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。
Vue.component('child',{
       inject:['for'],//得到父组件传递过来的数据
       data(){
           return {
               mymessage:this.for
           }
       },
       template:`
           <div>
               <input type="tet" v-model="mymessage"> 
           </div>
   })
   Vue.component('parent',{
       template:`
           <div>
               <p>this is parent compoent!</p>
               <child></child>
           </div>
       `,
       provide:{
           for:'test'
       },
       data(){
           return {
               message:'hello'
           }
       }
   })
   var app=new Vue({
       el:'#app',
       template:`
           <div>
               <parent></parent>
           </div>
       `
   })
  1. v-model
    父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(‘input’,val)自动修改v-model绑定的值
Vue.component('child',{
      props:{
           value:String, //v-model会自动传递一个字段为value的prop属性
       },
       data(){
           return {
               mymessage:this.value
           }
       },
       methods:{
           changeValue(){
               this.$emit('input',this.mymessage);//通过如此调用可以改变父组件上v-model绑定的值
           }
       },
       template:`
           <div>
               <input type="text" v-model="mymessage" @change="changeValue"> 
           </div>
   })
   Vue.component('parent',{
       template:`
           <div>
               <p>this is parent compoent!</p>
               <p>{{message}}</p>
               <child v-model="message"></child>
           </div>
       `,
       data(){
           return {
               message:'hello'
           }
       }
   })
   var app=new Vue({
       el:'#app',
       template:`
           <div>
               <parent></parent>
           </div>
       `
   })

3.路由之间跳转

声明式(标签跳转) 编程式( js跳转)
路由传参:
方案一:

// 直接调用$router.push 实现携带参数的跳转
this.$router.push({
   path: `/describe/${id}`,
})
// 需要对应路由配置如下:
{
    path: '/describe/:id',
    name: 'Describe',
    component: Describe
}

子组件中: 这样来获取参数this.$route.params.id

方案二:
父组件中:通过路由属性中的name来确定匹配的路由,通过params来传递参数。
对应路由配置: 这里可以添加:/id 也可以不添加,不添加数据会在url后面显示,不添加数据就不会显示
(没有添加显示刷新页面数据会丢失)

// 父组件中:通过路由属性中的name来确定匹配的路由,通过params来传递参数。
this.$router.push({
         name: 'Describe',
         params: {
           id: id
         }
})

子组件中: 这样来获取参数this.$route.params.id

方案三:
父组件:使用path来匹配路由,然后通过query来传递参数
这种情况下 query传递的参数会显示在url后面?id=?

this.$router.push({
         path: '/describe',
         query: {
           id: id
         }
})

对应子组件: 这样来获取参数 this.$route.query.id

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

vue框架中状态管理。在main.js引入store,注入。新建一个目录store,….. export 。场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车,数据共享,方法共享

5.vuex有哪几种属性?

有五种,分别是 State、 Getter、Mutation 、Action、 Module

vuex的State特性
A、Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于一般Vue对象里面的data
B、state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
C、它通过mapState把全局的 state 和 getters 映射到当前组件的 computed 计算属性中
vuex的Getter特性
A、getters 可以对State进行计算操作,它就是Store的计算属性
B、 虽然在组件内也可以做计算属性,但是getters 可以在多组件之间复用
C、 如果一个状态只在一个组件内使用,是可以不用getters
vuex的Mutation特性
Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步操作。

6.不用Vuex会带来什么问题?

可维护性会下降,想修改数据要维护三个地方;
可读性会下降,因为一个组件里的数据,根本就看不出来是从哪来的;
增加耦合,大量的上传派发,会让耦合性大大增加,本来Vue用Component就是为了减少耦合,现在这么用,和组件化的初衷相背

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

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

8.如何让CSS只在当前组件中起作用

将当前组件的<style>修改为<style scoped>

9.<keep-alive></keep-alive>的作用是什么?

<keep-alive></keep-alive> 包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。

10.active-class是哪个组件的属性?

vue-router模块的router-link组件。

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

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

vue 生命周期共分为四个阶段

1:实例创建
2:DOM 渲染
3:数据更新
4:销毁实例

共有八个基本钩子函数
1.beforeCreate --创建前
触发的行为:vue 实例的挂载元素$el 和数据对象 data 都为 undefined,还未初始化。
在此阶段可以做的事情:加 loading 事件

2.created --创建后
触发的行为:vue 实例的数据对象 data 有了,$el 还没有
在此阶段可以做的事情:解决 loading,请求 ajax 数据为 mounted 渲染做准备

3.beforeMount --渲染前
触发的行为:vue 实例的$el 和 data 都初始化了,但还是虚拟的 dom 节点,具体的 data.filter 还未替换
在此阶段可以做的事情:。。。

4.mounted --渲染后
触发的行为:vue 实例挂载完成,data.filter 成功渲染
在此阶段可以做的事情:配合路由钩子使用

5.beforeUpdate --更新前
触发的行为:data 更新时触发
在此阶段可以做的事情:。。。

6.updated —更新后
触发的行为:data 更新时触发
在此阶段可以做的事情:数据更新时,做一些处理(此处也可以用 watch 进行观测)

7.beforeDestroy —销毁前
触发的行为:组件销毁时触发
在此阶段可以做的事情:可向用户询问是否销毁

8.destroyed —销毁后
触发的行为:组件销毁时触发,vue 实例解除了事件监听以及和 dom 的绑定(无响应了),但 DOM 节点依旧存在
在此阶段可以做的事情:组件销毁时进行提示

14.vue生命周期的作用是什么

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

15.vue生命周期总共有几个阶段

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

16.第一次页面加载会触发哪几个钩子

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

17. DOM 渲染在 哪个周期中就已经完成

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

19.说出至少4种vue当中的指令和它的用法?

v-if:判断是否隐藏;v-else 和 v-else-if 配合使用
v-for:数据循环;
v-bind:class:绑定一个属性;(支持缩写 “:class”)
v-model:实现双向绑定;
v-text: 更新内容;
v-html: 更新元素的 innerHTML;
v-on: 绑定事件监听器。事件类型由参数指定。(支持缩写 “@”)

20.vue-loader是什么?使用它的用途有哪些?

解析.vue文件的一个加载器。
用途:js可以写es6、style样式可以scss或less、template可以加jade等

21.为什么使用key?

当有相同标签名的元素切换时,需要通过 key 特性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。

22.为什么避免 v-if 和 v-for 用在一起

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

23.VNode是什么?虚拟 DOM是什么?

Vue在 页面上渲染的节点,及其子节点称为“虚拟节点 (Virtual Node)”,简写为“VNode”。“虚拟 DOM”是由 Vue 组件树建立起来的整个 VNode 树的称呼。
虚拟 dom 是相对于浏览器所渲染出来的真实 dom 的,在react,vue等技术出现之前,我们要改变页面展示的内容只能通过遍历查询 dom 树的方式找到需要修改的 dom 然后修改样式行为或者结构,来达到更新 ui 的目的。
这种方式相当消耗计算资源,因为每次查询 dom 几乎都需要遍历整颗 dom 树,如果建立一个与 dom 树对应的虚拟 dom 对象( js 对象),以对象嵌套的方式来表示 dom 树,那么每次 dom 的更改就变成了 js 对象的属性的更改,这样一来就能查找 js 对象的属性变化要比查询 dom 树的性能开销小。

可以理解为:用 JavaScript 将DOM节点虚拟化表示,用数据对象来呈现DOM树,虚拟DOM它可以使我们操作这块的数据对象
如果我们使用虚拟DOM,而不是直接在代码中调用类似 .getElementById 的 DOM API 方法,操作就会像改变 JS 对象一样非常的简单省时。

DOM的缺点?
大小 - 其中之一就是更多的功能意味着代码包中更多行的代码。幸运的是,Vue.js 2.0 依旧比较小(当前版本 21.4kb),并且也正在删除很多东西。
内存 -同样,虚拟DOM需要将现有的DOM拷贝后保存在内存中,这是一个在DOM更新速度和内存使用中的权衡。
并不适用所有情况 -如果虚拟DOM可以一次性进行批量的修改是非常好的。但是如果是单独的、稀少的更新呢?这样的任何DOM更新都将会使虚拟DOM带来无意义的预计算。
因此,如果某个项目只有较少数量的节点,那么使用虚拟DOM会带来速度上质的变化么?实际上更可能的是使其更慢了!
但是对于多数的单页面应用来说,它还是会带来提升的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 一、什么是MVVM? MVVM是Model-View-ViewModel的缩写。MVVM是一种设计思想。Model...
    LemonnYan阅读 112,055评论 9 269
  • 这篇笔记主要包含 Vue 2 不同于 Vue 1 或者特有的内容,还有我对于 Vue 1.0 印象不深的内容。关于...
    云之外阅读 4,989评论 0 29
  • 我坐在卧室的沙发上,阵阵凉风吹过,夜晚如此迷人。美好的七月开始了,经过浴血奋战的六月我想告诉你什么呢?告诉你美好在...
    何金卫育儿教育阅读 191评论 0 0
  • 一个人的生活需要双性别上身,修得了水管,换的了灯泡,能下厨,会打扫,搬得了水桶,推得动沙发。 很久没和...
    麾毛杆儿阅读 385评论 0 1
  • 这个一生都没被开过苞的皇后,名叫张嫣。(捂脸,一上来就没把握住尺度。) 她是鲁元公主的女儿、汉惠帝刘盈的外甥女、吕...
    卿疯阅读 565评论 7 15