前端理论面试--VUE

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. 父组件通过标签定义参数传值
    子组件通过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>
       `
   })

路由之间跳转

声明式(标签跳转) 编程式( 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

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

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

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 可以包含任意异步操作。

不用Vuex会带来什么问题?

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

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

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

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

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

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

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

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

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

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

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

请列举出3个Vue中常用的生命周期钩子函数

created: 实例已经创建完成之后调用,在这一步,实例已经完成数据观测, 属性和方法的运算, watch/event事件回调. 然而, 挂载阶段还没有开始, $el属性目前还不可见
mounted: el被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。
activated: keep-alive组件激活时调用

什么是vue生命周期

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

vue生命周期的作用是什么

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

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

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

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

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

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

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

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

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

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

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

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

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

为什么使用key?

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

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

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

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会带来速度上质的变化么?实际上更可能的是使其更慢了!
但是对于多数的单页面应用来说,它还是会带来提升的。

推荐阅读更多精彩内容

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