Vue学习-13道Vue经典面试题与知识点小串烧

4 篇文章 0 订阅
4 篇文章 0 订阅
本文深入探讨Vue的面试重点,包括Vue路由的两种模式、组件间通信、MVVM与MVC的区别、Vue生命周期、Vue-Router导航钩子、组件封装、Vue与Jquery对比、v-if与v-show、SEO优化策略等。通过实例解析,助你掌握Vue核心技术。
摘要由CSDN通过智能技术生成

1 Vue路由的两种模式

小白回答:hash模式url带#号,history模式不带#号

大牛解答:hash模式url里面永远带着#号,我们在开发当中默认使用这个模式。那么什么时候要用history模式呢?如果用户考虑url的规范那么就需要使用history模式,因为history模式没有#号,是个正常的url适合推广宣传。当然其功能也有区别,比如我们在开发app的时候有分享页面,那么这个分享出去的页面就是用vue或是react做的,咱们把这个页面分享到第三方的app里,有的app里面url是不允许带有#号的,所以要将#号去除那么就要使用history模式,但是使用history模式还有一个问题就是,在访问二级页面的时候,做刷新操作,会出现404错误,那么就需要和后端人配合让他配置一下apache或是nginx的url重定向,重定向到你的首页路由上就ok啦。

hash与history的区别:

hashhistory
url显示有#,很Low无#,好看
回车刷新可以加载到hash值对应页面一般就是404掉了
支持版本支持低版本浏览器和IE浏览器HTML5新推出的API

hash模式
我们先来认识下这位朋友#,这个#就是hash符号,中文名哈希符或锚点,当然这在我们前端领域姑且这么称呼。

然后哈希符后面的值,我们称之为哈希值。OK,接下来我们继续分析他的原理。路由的哈希模式其实是利用了window可以监听onhashchange事件,也就是说你的url中的哈希值(#后面的值)如果有变化,前端是可以做到监听并做一些响应(搞点事情),这么一来,即使前端并没有发起http请求他也能够找到对应页面的代码块进行按需加载。

后来人们给他起了一个霸气的名字叫前端路由,成为了单页应用标配。

history模式
我们先介绍一下H5新推出的两个神器:pushState与replaceState

具体自行百度,简而言之,这两个神器的作用就是可以将url替换并且不刷新页面,好比挂羊头卖狗肉,http并没有去请求服务器该路径下的资源,一旦刷新就会暴露这个实际不存在的“羊头”,显示404。

那么如何去解决history模式下刷新报404的弊端呢,这就需要服务器端做点手脚,将不存在的路径请求重定向到入口文件(index.html),前后端联手,齐心协力做好“挂羊头卖狗肉”的完美特效。

总之,pushState方法不会触发页面刷新,只是导致history对象发生变化,地址栏会有反应。

history模式下,build之后本地 index.html 打开是无效的。

hash模式下,build之后本地 index.html 打开正常!

2 Vue组件之间如何传值

  1. 父组件向子组件传值:

    父组件:

    <template>
      <div class="parentOne">
        <children-item :content="item" v-for="item in list" :key="item.id"></children-item>
      </div>
    </template>
    
    <script>
      import ChildrenItem from "../children/ChildrenOne.vue"
      export default {
        name: "ParentOne",
        data() {
          return {
            list: [
              { id: "001", title: "这是父组件第一项内容" },
              { id: "002", title: "这是父组件第二项内容" },
              { id: "003", title: "这是父组件第三项内容" }
            ]
          }
        },
        components: {
          "children-item": ChildrenItem
        }
      }
    </script>
    
    <style>
    </style>
    

    **在父组件中使用子组件,并且在子组件中添加需要传递的值:content="item"**

    子组件:

    <template>
      <div class="ChildrenOne">
        <ul>
          <li v-for="childItem in content">{{childItem}}</li>
          <li>{{message}}</li>
        </ul>
      </div>
    </template>
    <script>
      export default {
        name: 'ChildrenOne',
        props: ["content"],
        data() {
          return {
            message: this.content.id
          }
        }
      }
    </script>
    <style></style>
    

    在子组件中添加props数组,接收从父组件中传递过来的content值,然后就可以在上边的template中像使用data中的数据一样使用props中的接收值,从而实现父组件向子组件的传值

  2. 子组件向父组件传值:

    子组件向父组件传值这一个技术点有个专业名词,叫做“发布订阅模式”,很明显在这里子组件为发布方,而父组件为订阅方

    子组件:

    <template>
      <div class="ChildrenTwo">
        <ul>
          <li v-for="childItem in content" @click="ChildrenOnclick()">{{childItem}}</li>
        </ul>
      </div>
    </template>
    
    <script>
      export default {
        name: 'ChildrenTwo',
        props: ["content", "index"],
        data() {
          return {
          }
        },
        created() {
          console.log(this.index);
        },
        methods: {
          ChildrenOnclick() {
            console.log(this.index);
            // 发布订阅模式
            this.$emit("delete", this.index)
          }
        }
      }
    </script>
    
    <style>
    </style>
    

    在该子组件中中触发li的click事件,调用ChildrenOnclick方法,在该方法中通过使用$emit方法定义了一个delete方法,并传递index值给父组件

    父组件:

    <template>
      <div class="parentTwo">
        <children-item 
        	:content="item" 
        	:index="index" 
        	v-for="(item,index) in list" 
        	:key="item.id" 
        	@delete="handleParentClick"
        >
        </children-item>
      </div>
    </template>
    
    <script>
      import ChildrenItem from "../children/ChildrenTwo.vue"
      export default {
        name: "ParentTwo",
        data() {
          return {
            list: [
              { id: "001", title: "这是父组件第一项内容" },
              { id: "002", title: "这是父组件第二项内容" },
              { id: "003", title: "这是父组件第三项内容" }
            ]
          }
        },
        methods: {
          handleParentClick(index) {
            this.list.splice(index, 1);
          }
        },
        components: {
          "children-item": ChildrenItem
        }
      }
    </script>
    
    <style>
    </style>
    

    在父组件使用子组件时,使用子组件中自定义的delete方法实现接收到子组件传递过来的值@delete="handleParentClick",调用handleParentClick方法将传递的值作为参数传入父组件

  3. 兄弟(或者没有关系)组件的组件传值:

    1. 建立中转站:bus.js

    2. 在需要引用的地方引入bus.js

      import bus from '@/bus.js'
      

    兄弟组件2:

    <template>
        <div id="two">
          <h3>小弟组件</h3>
          <p>
            <button @click="say">给大哥说话</button>
          </p>
        </div>
    </template>
    
    <script>
    // 引入bus.js进来
    import bus from '@/bus.js'
    
    export default {
      name: 'Two',
      methods: {
        // 实现给大哥传递数据的
        say () {
          // 让bus调用自己的事件
          bus.$emit('receive', '1000元保护费')
        }
      }
    }
    </script>
    
    <style lang="less" scoped>
    #two{
      width: 300px;
      height: 100px;
      border:1px solid greenyellow;
    }
    </style>
    
    

    在兄弟组件2小弟组件中,通过click事件调用say方法,在say方法里再调用bus.$emit,向receive事件传递值1000元保护费

    兄弟组件1:

    <template>
        <div id="one">
          <h3>大哥组件</h3>
          <span>接收小弟的礼物:{{money}}</span>
        </div>
    </template>
    
    <script>
    // 引入bus.js进来
    import bus from '@/bus.js'
    
    export default {
      name: 'One',
      // 在created中给bus绑定事件,时机最靠前,随时可以响应使用
      data () {
        return {
          // 接收小弟来到数据
          money: ''
        }
      },
      created () {
        // this:组件实例对象
        // 注意:设置为箭头函数
        bus.$on('receive', val => {
          // val:是其他应用处给传递的数据
          // 把获得的数据赋予money
          this.money = val
        })
      }
    }
    </script>
    
    <style lang="less" scoped>
    #one{
      width: 300px;
      height: 100px;
      border:1px solid red;
      margin-bottom:20px;
    }
    </style>
    
    

    在兄弟组件1大哥组件中,在created()方法里调用bus.$on,执行receive方法,接收小弟组件传递过来的值(1000元保护费),然后进行其他操作,这样就完成了兄弟组件之间的值传递

    提一笔:只要没有直接套用关系的组件都是兄弟

3 MVC与MVVM的区别

MVVM与MVC最大的区别就是:MVVM实现了View和Model的自动同步,也就是当Model的属性改变时,我们不用再自己手动操作Dom元素,来改变View的显示,而是改变属性后该属性对应View层显示会自动改变

进一步理解

一、MVC

MVC允许在不改变视图的情况下改变视图对用户输入的响应方式,用户对View的操作交给了Controller处理,在Controller中响应View的事件调用Model的接口对数据进行操作,一旦Model发生变化便通知相关视图进行更新。

? 如果前端没有框架,只使用原生的html+js,MVC模式可以这样理解。将html看成view;js看成controller,负责处理用户与应用的交互,响应对view的操作(对事件的监听),调用Model对数据进行操作,完成model与view的同步(根据model的改变,通过选择器对view进行操作);将js的ajax当做Model,也就是数据层,通过ajax从服务器获取数据。

二、MVVM

MVVM与MVC最大的区别就是:它实现了View和Model的自动同步,也就是当Model的属性改变时,我们不用再自己手动操作Dom元素,来改变View的显示,而是改变属性后该属性对应View层显示会自动改变。

这里我们拿典型的MVVM模式的代表,Vue,Vue实例中的data相当于Model层,而ViewModel层的核心是Vue中的双向数据绑定,即Model变化时VIew可以实时更新,View变化也能让Model发生变化。

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

这里只是简单地说一下区别,详细的可以参考以下文章:

https://www.jianshu.com/p/b0aab1ffad93

https://blog.csdn.net/u013282174/article/details/51220199

https://www.jianshu.com/p/f07cf01056b1

4 Vue的生命周期

啥也先别说,开局一张图:

Vue的生命周期可以分为以下八个阶段:

beforeCreate 实例创建前

created 实例创建完成

beforeMount 挂载前

mounted 挂载完成

beforeUpdate 更新前

updated 更新完成

beforeDestory 销毁前

destoryed 销毁完成

  1. BeforeCreated:

    这个钩子是new Vue()之后触发的第一个钩子,在当前阶段中data、methods、computed以及watch上的数据和方法均不能被访问。

  2. created

    这个钩子在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。可以做一些初始数据的获取,注意请求数据不易过多,会造成白屏时间过长。在当前阶段无法与Dom进行交互,如果你非要想,可以通过vm.$nextTick来访问Dom。

  3. Beforemounted

    这个钩子发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。

  4. mounted

    这个钩子在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$ref属性对Dom进行操作。也可以向后台发送请求,拿到返回数据。

  5. BeforeUpdate

    这个钩子发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。

  6. updated

    这个钩子发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。

  7. BeforeDestroy

    这个钩子发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。

  8. destroyed

    这个钩子发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。

提一笔:

在使用生命周期时有几点注意事项需要我们牢记。

1.第一点就是上文曾提到的created阶段的ajax请求与mounted请求的区别:前者页面视图未出现,如果请求信息过多,页面会长时间处于白屏状态。

2.除了beforeCreate和created钩子之外,其他钩子均在服务器端渲染期间不被调用。

3.上文曾提到过,在updated的时候千万不要去修改data里面赋值的数据,否则会导致死循环。

4.Vue的所有生命周期函数都是自动绑定到this的上下文上。所以,你这里使用箭头函数的话,就会出现this指向的父级作用域,就会报错。

Vue生命周期先写到这儿吧,发现需要写的东西很多,后期专门写一篇Vue生命周期的博文

5 Vue-Router有哪几种导航钩子

全局前置守卫:
    const router = new VueRouter({ ... })

    router.beforeEach((to, from, next) => {
      // ...
    })
全局解析守卫:
你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,
区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

全局后置钩子:
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

router.afterEach((to, from) => {
  // ...
})

路由独享的守卫:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})
组件内的守卫:
你可以在路由组件内直接定义以下路由导航守卫:
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}


6 路由之间怎么跳转

声明式–<router-link :to="{name:'index',query:{id:'xxx',name:'xxx'}}">

编程式–this.$router.push({name:‘组件名’)};

7 Vue全家桶都有哪些

Vue-cli 项目构建工具;vue-router 路由;vuex 状态管理;axios http请求工具;webpack

这题感觉没啥意思,但目录都写好了,不想重新改了…

8 Vue和Jquery的区别有哪些

区别一:
1).jQuery是一个类库,提供了很多的方法,不能算框架,在过去和现在Jquery是最流行的web前端js库,可是现在无论国内还是国外,他的使用率正在渐渐被其他的js库所替代。随着浏览器厂商对H5规范统一遵循以及ECMA6在浏览器端的实现,jquery的使用率会越来越低。
2).vue的介绍:vue是一个刚兴起不久的前端框架,有一套完整的体系,是一个精简的MVVM。从技术角度讲,vue.js专注于MVVM模型的ViewModel层,通过双向数据绑定把view层和Model层连接起来,通过对数据的操作就可以完成对页面视图的渲染。
区别二:
1).jQuery是直接操作DOM的;使用选择器($)选取DOM对象对其进行赋值、取值、事件绑定等操作
和原生的js区别只在于可以更方便的选取和操作DOM对象

? 数据和界面是在一起,比如获取input标签的内容
? 2)vue基于一种MVVM模式,使用数据驱动的方式,通过Vue对象将数据和View完全分离开来
? 对数据进行操作,不在需要引用相应的DOM对象,通过vue对象,将数据和相应的DOM对象相互绑定起来
区别三
? 1).JQuery适用的场景:JQuery侧重样式操作,比如一些H5的动画页面;需要js来操作页面样式的页面

? 2).Vue适用的场景:vue侧重数据绑定,比如复杂数据操作的后台页面;表单填写页面

9 v-if和v-show的区别

v-if是通过动态创建或者移除元素实现动态切换 v-show是通过控制元素的css样式display:none样式实现切换 一般来说,v-if 有更高的切换消耗 而 v-show 有更高的初始渲染消耗。 因此,如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好。

10 Vue封装组件的流程

  1. 建立组件模板

  2. 通过props接收外部数据

  3. 在需要的模块中引入该组件,使用该组件,最终在网页中输出组件与数据

    这个在网上没有找到比较好的文章,所以是自己根据自己为数不多的封装小组件的经验写的,如果大家有更好的更规范的流程,可以在下方评论,我及时更新

11 methods和watch,computed的区别

  1. computed–computed是在HTML DOM加载后马上执行的,如赋值;computed计算属性是基于它们的依赖进行缓存的,调用函数时,只要依赖的数据没发生改变时,会直接从缓存中拿数据,不会再次执行函数
  2. methods–methods则必须要有一定的触发条件才能执行,如点击事件;只要发生重新渲染,method 调用总会执行该函数。
  3. watch–检测一个数据的变化;对应一个对象,键是观察表达式,值是对应回调。值也可以是方法名,或者是对象,包含选项。
  4. 执行顺序–先computed再watch,不执行methods;等触发某一事件后,则是:先methods再watch。

12 如何解决SPA应用首屏加载过慢

  • 将全局引入转换为按需引入文件

  • 在 config/index.js 文件中将productionSourceMap 的值设置为false. 不生成映射资源

  • 路由懒加载:懒加载即组件的延迟加载,通常vue的页面在运行后进入都会有一个默认的页面,而其他页面只有在点击后才需要加载出来,使用懒加载可以将页面中的资源划分为多份,从而减少第一次加载的时候耗时

    这种优化,就是将每个组件的js代码独立出来,在使用到这个组件时,才向服务器请求文件,并且请求过一次后就会缓存下来,再次使用到这个组件时,就会使用缓存,不再发送请求

  • 压缩css和js文件

  • 使用cdn托管(就是把原服务器上数据复制到其他服务器上,用户访问时,哪台服务器近访问到的就是哪台服务器上的数据。)

13 Vue项目对SEO不友好的解决方法

开局一张图:

先分析下原因:

1.搜索引擎的基础爬虫的原理就是抓取你的url,然后获取你的html源代码并解析。 而你的页面通常用了vue等js的数据绑定机制来展示页面数据,爬虫获取到的html是你的模型页面而不是最终数据的渲染页面,所以说用js来渲染数据对seo并不友好。

2.seo 本质是一个服务器向另一个服务器发起请求,解析请求内容。但一般来说搜索引擎是不回去执行请求到的js的。也就是说,如果一个单页应用,html在服务器端还没有渲染部分数据数据,在浏览器才渲染出数据,而搜索引擎请求到的html是没有渲染数据的。 这样就很不利于内容被搜索引擎搜索到。 所以服务端渲染就是尽量在服务器发送到浏览器前 页面上就是有数据的。

3.一般的数据逻辑操作是放在后端的。排序这个如果仅仅是几条数据,前后端排序开起来是一样的,如果是有1000条数据,前端要排序就要都请求过来。这样显然是不合理的。

常用的解决方案:

1.页面预渲染

2.服务端渲染

3.路由采用h5 history模式

大厂如何做优化的呢?

1) bilibili做了基本的seo优化,比如

(1)TDK描叙详细。
(2)提升网页加载速度:对外联css,以及js使用了延迟加载以及dns-prefetch,preload。
(3)外联较多,关键词排名高。

2) 掘金网站使用了vue-meta-info 管理网站的meta,应该配合使用了prerender-spa-plugin 对SEO进行了优化

3) Element在logo上加了首页的地址,并且只有logo是放在h1标签中。

4) 有一些流量不太高的网站比如http://www.marshall.edu (Marshall University)做了seo社会化分享优化,在meta信息中出现了property=”og:title”这种新东西;https://we.dji.com/zh-CN/campus (大疆招聘)使用了Nuxt

这些网站中出现率最高的公共组件或公共方法有四个:

1) 面包屑导航
2) Icon
3) 搜索框
4) Button组件

关于收录问题:

搜索引擎判断一个网站权重高低的尺度无非两个:收录和外链。因此百度收录的高低很大程度上影响着网站在百度的排名

目前百度spider抓取新链接的途径有两个:

一是主动出击发现抓取;

二就是从百度站长平台的链接提交工具中获取数据,其中通过主动推送功能“收”上来的数据最受百度spider的欢迎。

对于站长来说,如果链接很长时间不被收录,建议尝试使用主动推送功能,尤其是新网站,主动推送首页数据,有利于内页数据的抓取。

参考文章:

https://blog.csdn.net/codeliuguisheng/article/details/79634422

https://blog.csdn.net/chjj0904/article/details/79388438

@_@

博文参考文章:

https://blog.csdn.net/wang1006008051/article/details/81805932

https://blog.csdn.net/fifteen718/article/details/82529433

https://www.imooc.com/article/257885

https://juejin.im/post/5d1b464a51882579d824af5b

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值