Vue面经

本文为在整个秋招期间的关于Vue框架常见问题的总结,仅仅代表了自己的一些简单回答,如果有问题欢迎指出,也希望大家多用自己的理解去回答问题


v-if:判断是否为真,然后重组、销毁DOM 节点

v-else:配合v-if 使用

v-on:添加事件

v-for:数据循环

v-bind:class 绑定一个属性

v-model用于表单数据的双向绑定,其实它就是一个语法糖,这个背后就做了两个操作:

1. v-bind绑定一个value属性

2. v-on指令给当前元素绑定input事件

一、对于MVVM的理解

MVVM 是 Model-View-ViewModel 的缩写。

  • Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。
  • View 代表视图模型,它负责将数据模型转化成UI 展现出来。View 层不负责处理状态,View 层做的是 数据绑定的声明、 指令的声明、 事件绑定的声明。
  • ViewModel监听数据的改变和控制视图行为、处理用户交互,它就是一个同步View 和 Model的对象,连接Model和View。

在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 View 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。

ViewModel通过双向数据绑定把View层和Model层连接起来,而View和Model之间的同步工作完全是自动的,无需人为干涉,因此开发者只需要关注业务逻辑,不需要手动操作DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由MVVM来统一管理

MVVM优缺点
  • 优点:
    1)分离视图(View)和模型(Model),降低代码耦合,提高视图或者逻辑的重用性
    2)提高可测试性: ViewModel的存在可以帮助开发者更好地编写测试代码
    3)自动更新dom: 利用双向绑定,数据更新后视图自动更新,让开发者从繁琐的dom操作中解放
  • 缺点:
    1)Bug很难被调试:数据绑定使得一个位置的Bug被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。另外,数据绑定的声明是指令式地写在View的模版当中的,这些内容是没办法去打断点debug的
    2)一个大的模块中model也会很大,虽然使用方便也并保证了数据的一致性,当时长期持有,不释放内存就造成了花费更多的内存
    3)对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高

vue对css的操作可以通过绑定class或者绑定style。


二、Vue与其他框架对比

2.1 Vue框架与jQuery类库的区别

  • Vue直接操作视图层,它通过Vue对象将数据和View完全分离开来了。对数据进行操作不需要引用相应的DOM节点,只需要关注逻辑,完全实现了视图层和逻辑层的解耦;
  • Jquery的操作是基于DOM节点的操作,jQuery是使用选择器($)选取DOM对象,对其进行赋值、取值、事件绑定等操作,其实和原生的js的区别只在于可以更方便的选取和操作DOM对象,而数据和界面是在一起的。它的优势在于良好的封装和兼容,使调用简单方便。

2.2 Vue 与Angular 以及React 的区别?

与AngularJS 的区别:

相同点:都支持指令: 内置指令和自定义指令; 都支持过滤器; 内置过滤器和自定义过滤器; 都支持双向数据绑定; 都不支持低端浏览器

不同点:

  • AngularJS 的学习成本高, 比如增加了Dependency Injection 特性, 而Vue.js 本身提供的API 都比较简单、直观;
  • 在性能上,AngularJS 依赖对数据做脏检查,所以Watcher 越多越慢;Vue.js 使用基本依赖追踪的观察并且使用异步队列更新, 所有的数据都是独立触发的

与React 的区别:

相同点:React 采用特殊的JSX 语法, Vue.js 在组件开发中也推崇编写vue 特殊文本格式, 对文本内容都有一些约定, 两者都需要编译后使用;

中心思想相同:

一切都是组件, 组件实例之间可以嵌套;

都提供合理的钩子函数, 可以让开发者定制化地去处理需求;

都不内置列数Ajax/Route 等功能到核心包, 而是以插件的方式加载;在组件开发中都支持mixins 的特性。

不同点:React 采用的Virtual DOM 会对渲染出来的结果做脏检查;Vue.js 在模板中提供了指令, 过滤器等, 可以非常方便, 快捷地操作Virtual DOM

2.3 vue和原生js相比,解决了什么问题

vue.js是一套构建用户界面的 渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文件组件和 Vue 生态系统支持的库开发的复杂单页应用。

轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十kb。Vue.js通过简洁的API提供高效的数据绑定和灵活的组件系统

简单易学:国人开发,中文文档,不存在语言障碍,易于理解和学习

双向数据绑定:vue.js通过MVVM思想实现数据的双向绑定,让开发者不用再操作dom对象,有更多的时间去思考业务逻辑。vue的双向绑定原理及一步一步实现MVVM

组件化:Vue.js通过组件,把一个单页应用中的各种模块拆分到一个一个单独的组件(component)

视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作

详细说明:

一、控件跟数据自动绑定

vue.js通过MVVM思想实现数据的双向绑定,让开发者不用再操作dom对象,有更多的时间去思考业务逻辑。

可以直接使用data里面的数据值来提交表单,而不需要再使用$("#myid").val()那一套方法来获取控件的值,对控件赋值也方便很多,只需要改变data的值,控件就会自动改变值。将复杂的界面操作,转化为对数据进行操作。

比如下面的一段代码就可以很简单的实现了select控件的里面的列表的动态管理:

html代码:

<el-select v-model="mType" style="flex: 1;">
  <el-option v-for="(item,index) in enums" :label="item.label" :value="item.value" :key="index"></el-option>
</el-select>

js代码:

data(){
    return{
    mType:'',
    enums:[{value:0,label:'正常'},{value:1,label:'拉黑'}]
  }
}

二、页面参数传递和页面状态管理

页面传值对于vue来说,可供选择的方法非常多。比如使用子组件实现,通过对props属性传值;也可以使用页面url参数的方法传值;或使用vuex全局状态管理的方法页面传值等等。而原生开发的时候,在页面有多个参数的时候,页面传值和初始化,要复杂很多。而vue直接将参数保存在对象里面,直接给子组件的属性或vuex存储一个对象就行了,比如<Ctrl :userinfo=“userinfo” ></Ctyrl> , 这样就可以将userinfo传到自定义组件。

三、模块化开发、无刷新保留场景参数更新

比如一个列表页面里面有添加功能,有修改功能,这时候我们可以通过引用子组件的形式,当子组件内容更新的时候,修改主组件的数据,比如修改了一条数据后,我们需要列表页同时刷新,但我们不希望改变原来列表页的页码和搜索条件。假如你用原生开发来实现这个,需要写很多业务逻辑保存上一个页面的搜索条件和页码这些参数,但假如你用vue开发,将变得非常简单。

四、代码的可阅读性

vue天生具有组件化开发的能力,因此不同的功能基本都是写在不同的模块里面,因此代码的可阅读性非常高。当一个新手接手一个旧项目的时候,基本上可以做到一天就能定位到要修改的代码,进行修改,快速接手项目。

五、基于强大的nodejs,添加新的组件库,基本一句npm命令就能安装,比如当我需要使用axios组件的时候,直接npm install axios安装一下,就可以使用axios这个组件。熟悉maven的同学估计很容易就能理解npm工具。

六、主路由、子路由、主页面、子组件的方式,可以让我们彻底抛弃iframe。写过前端的同学都知道,因为iframe的滚动条、和子页面跟其他页面的交互性这些原因、用户体验还是远远没有单页面架构友好。而且使用vue非常简单方便的实现系统菜单、导航等固定布局。

七、各子组件样式不冲突:各个组件之间,可以使用相同的样式名,但有不同的样式属性。比如组件A和组件B的button都绑定了class=“btn”, 但在两个组件里,我们可以实现两个不同的btn样式属性,互不影响。

vue的不足:

当然,vue也有不足,不足的地方如下:

一、vue是单页面页面,对于搜索引擎不友好,影响seo.因此不适合做公司官网。比如两个vue路由(页面),它的路径是这样的:index.html#aaa 和 index.html#bbb,但对于搜索引擎来说,都是同一个页面,就是index.html。这样搜索引擎就无法收录你的页面。

二、vue门槛较高,使用vue,需要先学习和摸索vue大概3天左右的时候,建议使用vue的时候,不需要看node.js自动帮你生成的js文件。你只需要编写你自己页面的代码就行了。具体nodejs帮你生成的框架代码,其实是不用看的。


三、Vue的生命周期

3.1 什么是Vue生命周期?

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

3.2 Vue生命周期简述

vue的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后。

  1. 创建前/后:在beforeCreated阶段,vue实例的挂载元el还没有。在created 阶段,vue 实例的数据data 有了,el 还没有

  2. 载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。

  3. 更新前/后:当data变化时,会触发beforeUpdate和updated方法。

  4. 销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。

3.3 Vue生命周期钩子函数及对应的操作

第一次页面加载会触发 beforeCreate, created, beforeMount, mounted 这几个钩子函数;DOM 渲染在 mounted 中就已经完成了。官方实例的异步请求是在mounted生命周期中调用的,而实际上也可以在created生命周期中调用。

beforeCreate(创建前) :数据观测和初始化事件还未开始

created(创建后):完成数据观测,属性和方法的运算,初始化事件,el属性还没有显示出来。

beforeMount(挂载前) :在挂载开始之前被调用,相关render函数首次被调用。实例完成以下配置:编译模板,把data里面的数据和模板生成html,但是没有挂载html到页面中。

mounted(挂载后):挂载到实例之后被调用,实例完成以下配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。

beforeUpdate(更新前):在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。

updated(更新后):在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。

beforeDestroy(销毁前): 在实例销毁之前调用。实例仍然完全可用。

destroyed(销毁后): 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。

activited:keep-alive专属,组件被激活时调用

deactivated:keep-alive专属,组件被激活时调用

3.4 Vue 的父组件和子组件生命周期钩子函数执行顺序?

Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:

  • 加载渲染过程

    父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted

  • 子组件更新过程

    父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated

  • 父组件更新过程

    父 beforeUpdate -> 父 updated

  • 销毁过程

    父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

3.5 其他问题

1. 在哪个生命周期内调用异步请求?

可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。但是本人推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:

  • 能更快获取到服务端数据,减少页面 loading 时间;
  • ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;

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

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

3. DOM 渲染在那个周期中已完成?

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

4. 在什么阶段才能访问操作DOM?

在钩子函数 mounted 被调用前,Vue 已经将编译好的模板挂载到页面上,所以在 mounted 中可以访问操作 DOM。vue 具体的生命周期示意图可以参见如下,理解了整个生命周期各个阶段的操作,关于生命周期相关的面试题就难不倒你了。

5. 父组件可以监听到子组件的生命周期吗?

比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,可以通过以下写法实现:

// Parent.vue

以上需要手动通过 $emit 触发父组件的事件,更简单的方式可以在父组件引用子组件时通过 @hook 来监听即可,如下所示:

//  Parent.vue

当然 @hook 方法不仅仅是可以监听 mounted,其它的生命周期事件,例如:created,updated 等都可以监听。


四、Vue实现数据双向绑定的原理:Object.defineProperty()

vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。

要想实现mvvm,主要包含两个方面,视图变化更新数据,数据变化更新视图.

  • 1)view变化更新data:可以通过事件监听实现
  • 2)data变化更新view:通过Object.defineProperty( )对属性设置一个set函数,当属性变化时就会触发这个函数,所以我们只需要将一些更新的方法放在set函数中就可以实现data变化更新view了.
  • 3)具体过程:
    首先要对数据进行劫持监听,所以要设置一个监听器Observer,用来监听所有的属性,当属性变化时,就通知订阅者Watcher,看是否需要更新。属性可能是多个,所以会有多个订阅者,故需要一个消息订阅器Dep来专门收集这些订阅者,并在监听器Observer和订阅者Watcher之间进行统一的管理。因为在节点元素上可能存在一些指令,所以还需要一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令初始化成一个订阅者Watcher,并替换模板数据并绑定相应的函数,这时候当订阅者Watcher接受到相应属性的变化,就会执行相对应的更新函数,从而更新视图.

总结:

1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。

2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。

3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。

img

五、Vue组件如何通信

  • 1、父组件与子组件传值

    父组件传给子组件:子组件通过props方法接受数据;

    子组件传给父组件: 在子组件中,通过$emit()来触发事件;在父组件中,通过v-on来监听子组件事件。

  • 2、非父子组件间的数据传递,兄弟组件传值

    通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级.当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。

    • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
    • 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

六、Vue-CLI是什么?

CLI是Command-Line Interface, 翻译为命令行界面, 但是俗称脚手架。Vue CLI是一个官方发布 vue.js 项目脚手架。使用 vue-cli 可以快速搭建Vue开发环境以及对应的webpack配置

Vue.js提供一个官方命令行工具,可用于快速搭建大型单页应用(在一个完成的应用或者站点中,只有一个完整的HTML页面,这个页面有一个容器,可以把需要加载的代码(以组件的方式)插入到该容器中)。

该工具提供开箱即用的构建工具配置,带来现代化的前端开发流程。只需几分钟即可创建并启动一个带热重载、保存时静态检查以及可用于生产环境的构建配置的项目。

img

src文件夹内容

assets文件夹是放静态资源;components是放组件;router是定义路由相关的配置;view视图;app.vue是一个应用主组件;main.js是入口文件等等


七、Vuex

官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化

状态管理到底是什么?

状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。

那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?等等,如果是这样的话,为什么官方还要专门出一个插件Vuex呢?难道我们不能自己封装一个对象来管理吗?当然可以,只是我们要先想想VueJS带给我们最大的便利是什么呢?没错,就是响应式。如果你自己封装实现一个对象能不能保证它里面所有的属性做到响应式呢?当然也可以,只是自己封装可能稍微麻烦一些。不用怀疑,Vuex就是为了提供这样一个在多个组件间共享状态的插件,用它就可以了。

但是,有什么状态时需要我们在多个组件间共享的呢?

如果你做过大型开放,你一定遇到过多个状态,在多个界面间的共享问题。比如用户的登录状态、用户名称、头像、地理位置信息等等。比如商品的收藏、购物车中的物品等等。这些状态信息,我们都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的。

(1)Vuex 的五大属性:

Vuex 有五个核心概念:state、getters、mutations、actions、modules

  • State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
  • Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
  • Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
  • Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
  • Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。

(2)不用Vuex 会带来什么问题?

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

总结:

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。

(1)Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

(2)改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。


八、Vue-router

**SPA( single-page application )**仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。

8.1 Vue-router是什么

这里的路由并不是指我们平时所说的硬件路由器,这里的路由就是SPA(单页应用)的路径管理器。再通俗的说,vue-router就是WebApp的链接路径管理系统。

vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。传统的页面应用,是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。路由模块的本质 就是建立起url和页面之间的映射关系

至于我们为啥不能用a标签,这是因为用Vue做的都是单页应用,就相当于只有一个主的index.html页面,所以你写的<a></a>标签是不起作用的,你必须使用vue-router来进行管理。

8.2 Vue的路由实现:hash模式 和 history模式(Vue 的两种状态)

**hash模式:**在浏览器中符号“#”,#以及#后面的字符称之为hash,用 window.location.hash 读取。特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。

**history模式:**history采用HTML5的新特性;且提供了两个新方法: pushState()replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。

8.3 hash 和 history 路由模式实现原理

(1)hash 模式的实现原理

早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站,它的 location.hash 的值为 ‘#search’:

https://www.word.com#search

hash 路由模式的实现主要是基于下面几个特性:

  • URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;
  • hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换;
  • 可以通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用 JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值;
  • 我们可以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。

(2)history 模式的实现原理

HTML5 提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:

window.history.pushState(null, null, path);

history 路由模式的实现主要基于存在下面几个特性:

  • pushState 和 repalceState 两个 API 来操作实现 URL 的变化 ;
  • 我们可以使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染);
  • history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染)。

8.4 vue-router 路由模式

(1)vue-router 有 3 种路由模式:hash、history、abstract,根据mode 参数来决定采用哪一种方式

switch (mode) {

其中,3 种路由模式的说明如下:

  • hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;
  • history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
  • abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.

(2)路由之间的跳转:

声明式(标签跳转):<router-view> 标签用于展示路由组件,DOM 节点中使用v-link 进行跳转,或使用router-link 标签编程式(js 跳转)

(3)怎么定义vue-router 的动态路由以及如何获取传过来的动态参数?

在router 目录下的index.js 文件中,对path 属性加上/:id 使用router 对象的params id

(4)Vue 中如何用watch 去监听router 变化

当路由发生变化的时候,在watch 中写具体的业务逻辑

let vm = new Vue({
   el:"#app", data:{}, router, watch:{
       $router(to,from){
           console.log(to.path);
      }
  }
}

8.5 vue-router 有哪几种导航钩子?以及它的参数?

vue-router提供的导航钩子主要用来拦截导航,让它完成跳转或取消

导航钩子的分类:全局守卫、路由独享守卫、局部守卫

1、全局导航钩子

  • router.beforeEach(to, from, next): 路由改变前的钩子
const router = new Router({ ... })
router.beforeEach((to, from, next) => {
  //
})

to: 即将要进入的目标路由对象

from: 当前正要离开的路由,也是一个路由对象

next: 一定要调用该方法来resolve这个钩子

next 方法必须要调用,否则钩子函数无法 resolved

  • router.beforeResolve : 在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,该钩子函数就被调用
  • router.afterEach : 路由改变后的钩子

2、路由独享钩子

可以在路由配置上直接定义 beforeEnter

cont router = new Router({
   routes: [
       {
           path: '/file',
           component: File,
           beforeEnter: (to, from ,next) => {
               // do someting
           }
       }
   ]
});

3、组件内的导航钩子

是指在组件内执行的钩子函数,类似于数组内的生命周期函数,相当于为配置路由的组件添加的生命周期钩子函数。钩子函数按执行顺序包括beforeRouteEnter、beforeRouteUpdate(2.2新增)、beforeRouteLeave三个

  • beforeRouteEnter 在进入当前组件对应的路由前调用
  • beforeRouteUpdate 在当前路由改变,但是该组件被复用时调用
  • beforeRouteLeave 在离开当前组件对应的路由前调用
export default {
    data() { ... },
    beforeRouteEnter(to, from, next) {
        ... ...
    }
}

注意:

beforeRouteEnter 不能获取组件实例 this,因为当守卫执行前,组件实例被没有被创建出来,剩下两个钩子则可以正常获取组件实例 this


九、vue组件中的data为什么是一个函数?

组件是可复用的vue实例,一个组件被创建好之后,就可能被用在各个地方,而组件不管被复用了多少次,组件中的data数据都应该是相互隔离,互不影响的,基于这一理念,组件每复用一次,data数据就应该被复制一次,之后,当某一处复用的地方组件内data数据被改变时,其他复用地方组件的data数据不受影响。

类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。


十、computed和watch有什么区别?

computed: 是计算属性,是依赖vm中data的属性变化而变化的,只有当data中的属性发生改变的时候,下一次获取 computed 的值时才会重新计算 computed 的值;当computed中的函数所依赖的属性没有发生改变,那么调用当前函数的时候会从缓存中读取。

使用场景:当一个值受多个属性影响的时候------------购物车商品结算

data: {
    message: 'Hello'
  },
 computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }

**watch:**监听属性,监听一个值的变化。每当监听的数据变化时都会执行回调进行后续操作;

使用场景:当一条数据的更改影响到多条数据的时候---------搜索框

watch: {
    // 如果 `question` 发生改变,这个函数就会运行
    question: function (newQuestion, oldQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.debouncedGetAnswer()
    }
  },

特殊情况:

(1) watch只会监听数据的值是否发生改变,而不会去监听数据的地址是否发生改变。也就是说,watch想要监听引用类型数据的变化,需要进行深度监听。“obj.name”(){}------如果obj的属性太多,这种方法的效率很低,obj:{handler(newVal){},deep:true}------用handler+deep的方式进行深度监听。

(2) 特殊情况下,watch无法监听到数组的变化,特殊情况就是说更改数组中的数据时,数组已经更改,但是视图没有更新。更改数组必须要用splice()或者 s e t 。 t h i s . a r r . s p l i c e ( 0 , 1 , 100 ) − − − − − 修 改 a r r 中 第 0 项 开 始 的 1 个 数 据 为 100 , t h i s . set。this.arr.splice(0,1,100)-----修改arr中第0项开始的1个数据为100,this. setthis.arr.splice(0,1,100)arr01100this.set(this.arr,0,100)-----修改arr第0项值为100。

(3) immediate:true 页面首次加载的时候做一次监听。

区别总结:

1、功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。

2、是否调用缓存:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。

3、是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。

4、使用场景:computed----当一个属性受多个属性影响的时候,使用computed-------购物车商品结算。watch----当一条数据影响多条数据的时候,使用watch-------搜索框。


computed属性 与 methods方法

在vue.js中,有methods和computed两种方式来动态当作方法来用的。

1.首先最明显的不同 就是调用的时候,methods要加上()

2.我们可以使用 methods 来替代 computed,效果上两个都是一样的,但是 computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值。而使用 methods ,在重新渲染的时候,函数总会重新调用执行

那什么是相关依赖发生改变时才会重新取值呢 比方说 reversedMessage function() 计算属性中调用了 message 变量。就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。而methods是实时的,在重新渲染时,函数总会重新调用执行,不会缓存,(多次输出时间不同)


v-if 与 v-show 的区别

共同点:v-if 和 v-show 都能实现元素的显示隐藏

区别

  • v-show 只是简单的控制元素的 display 属性,而 v-if 才是条件渲染
    v-show 仅是隐藏 / 显示,值为 false 时,该元素依旧存在于 dom 树中。若其原有样式设置了 display: none 则会导致其无法正常显示。
    v-if 才是真实的条件渲染(条件为真,元素将会被渲染,条件为假,元素会被销毁)。v-if 是动态添加,当值为 false 时,是完全移除该元素,即 dom 树中不存在该元素。;
  • v-if 有更高的切换开销,v-show 切换开销小;
    在切换 v-if 块时,Vue.js 有一个局部编译/卸载过程,因为 v-if 之中的模板也可能包括数据绑定或子组件。相比之下,v-show 简单得多——元素始终被编译并保留,只是简单地基于 CSS 切换。
  • v-show 有更高的首次渲染开销,而 v-if 的首次渲染开销要小的多;
    v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——在条件第一次变为真时才开始局部编译(编译会被缓存起来)。

因此,如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好。

  • v-if 有配套的 v-else-if 和 v-else,而 v-show 没有
  • v-if 可以搭配 template 使用,而 v-show 不行

keep-alive作用

keep-alive是一个抽象组件:它自身不会渲染一个DOM元素,也不会出现在父组件链中;使用keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。当然keep-alive不仅仅是能够保存页面/组件的状态这么简单,它还可以避免组件反复创建和渲染,有效提升系统性能。总的来说,keep-alive用于保存组件的渲染状态。

聊聊Keep-alive 的实现原理和缓存策略

原理:keep-alive 的实现正是用到了LRU 策略, 将最近访问的组件push 到this.keys 最后面, this,keys[0] 也就是最久没有被访问到的组件, 当缓存实例超过max 设置值, 删除this.keys[0]

LRU 缓存淘汰算法: LRU 算法根据数据的历史访问记录来进行记录, 其核心思想是”如果数据最近被访问过, 那么将来被访问的几率也更高”。


Vue 中引入组件的步骤

  • 采用ES6 的import…from…语法或CommonJS 的require()方法引入组件

  • 对组件进行注册

    // 注册
    Vue.component('my-component', {
      template: '<div>A custom component!</div>'
    })
    
  • 使用组件<my-component></my-component>


npm和cmpm命令的区别

相信很多人都不太明白 npm 和 cnpm 到底是什么东东, 为啥在国内要用 淘宝镜像使用 cnpm,

(1) 两者之间只是 node 中包管理器的不同哟,

(2) npm是node官方的包管理器。cnpm是个中国版的npm,是淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:

(3)如果因为网络原因无法使用npm下载,那cnpm这个就派上用场了。


Vue 中的虚拟DOM和Key

虚拟DOM

浏览器本身处理DOM也是有性能瓶颈的,尤其是在传统开发中,用JQuery或者原生的JavaScript DOM操作函数对DOM进行频繁操作的时候,浏览器要不停的渲染新的DOM树,渲染真实的DOM开销是很大的,比如有时候我们修改了某个数据,如果直接渲染到真实DOM上会引起整个DOM树的重绘和重排,diff算法可以帮助我们实现只将更改过的数据映射到真实DOM上,从而减少重绘。

在Vue.js 2.0版本中引入了 Virtual DOM的概念,Virtual DOM是将真实的DOM数据抽取出来,以对象的形式模拟树状结构。 这个树形结构包含了整个DOM结构的信息。当虚拟DOM某个节点的数据改变后会生成一个虚拟节点,然后将虚拟节点与上一次渲染视图所使用的旧虚拟节点做对比,找出真正需要更新的节点来进行DOM操作,从而避免操作其他不需要改动的DOM元素。(diff用来实现这一过程,它调用名为patch的函数,比较新旧节点,一层一层对比,一边比较一边给真实DOM打补丁)。

虚拟DOM在Vue.js中主要做了两件事情:

  • 提供与真实DOM节点所对应的虚拟节点VNode
  • 将虚拟节点VNode和旧虚拟节点oldVNode进行对比,然后更新视图

Key的作用

key的作用主要是为了高效的更新虚拟DOM

在diff算法中,key 是给每一个 vnode 的唯一id, 依靠key可以使diff算法在执行时更快的找到对应的节点,从而提高diff速度

更准确: 因为带Key 就不会出现重复现象, 在sameNode 函数a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确,如果不加key 会导致之前节点的状态被保留下来,会产生一系列的bug

**更快速:**key 的唯一性可以被Map 数据结构充分利用,相比于遍历查找的时间复杂度O(n),Map 的时间复杂度仅仅为O(1)


谈一谈nextTick 的原理

Vue 的nextTick 方法的实现原理

  • Vue 用异步队列的方式来控制DOM 更新和nextTick 回调先后执行
  • mictask 因为其高优先级特性, 能确保队列中的微任务在一次事件循环前辈执行完毕
  • 考虑兼容问题做了microtask 向macrotask 的降级方案

详解:

js 执行是单线程的,它是基于事件循环的。主线程的执行过程就是一个tick,而所有的异步结果都是通过“任务队列”来调度。消息队列中存放的是一个个的任务(task)。规范中的task 分为两大类,分别是macro task 和micro task,并且每个macrotask 结束后,都将清空所有micro task。

在浏览器环境中,常见的macro task 有setTimeout、MessageChannel、postMessage、setImmediate。常见的micro task 有MutationObsever 和Promise.then

Vue 在更新DOM 时是异步执行的,只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。在Vue2.5 的源码中,macrotask 降级的方案依次是: setImmediate 、MessageChannel,setTimeout


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

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


Vue 项目优化方式

组件优化-- 提高组件复用性

vue-router 优化— 使用路由懒加载

v-if 和v-show/ computed 和watch 区分使用场景

v-for 遍历必须添加key 值, 同时避免使用v-if

事件销毁— 在声明周期销毁阶段, 销毁一些不必要的数据、定时器等

图片懒加载

第三方插件按需引入

提取公共代码

减少ES6 转为ES5 的冗余代码

Vue 首屏加载过慢怎么优化?

vue 作为一个单页面应用, 如果不对路由进行处理, 在加载首页的时候, 就会将所有组件全部加载, 并向服务器请求数据, 这必将拖慢加载速度

解决方案:

  • vue-router 懒加载:就是按需加载组件, 只有当路由被访问时才会加载对应组件, 而不是在加载首页的时候就加载, 项目越大, 对首页加载的速度提升越明显
  • 使用CDN 加速:在做项目时, 我们会用到很多库,在爱用CDN 加载可以加快加载速度
  • gzip 压缩:方法一: 使用Nginx 反向代理, 配置nginx.conf 文件;方法二: 使用node 压缩, 需要使用compression 库
  • 异步加载组件
  • 服务端渲染:使用pug/jade/ejs/vue 通过应用框架Nuxt 等等都可以实现后端渲染, 并且后端渲染还能对seo 优
    化起到作用

谈谈你对webpack 的看法

webpack 是一个静态模块打包工具,你可以使用webpack 管理你的模块依赖,并编译输出模块们所需要的静态文件。它能够很好地管理、打包web 开发中所用到的HTML、Javascript、CSS 以及各种静态文件(图片、字体等),让开发过程更加高效。

webpack可以递归打包项目中的所有模块,即指定一个入口,分析模块的依赖,然后递归查找所有相关的依赖,最后生成了优化且合并后的静态资源。

Class 与 Style 如何动态绑定?

Class 可以通过对象语法和数组语法进行动态绑定:

  • 对象语法:
<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
  • 数组语法:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>

Style 也可以通过对象语法和数组语法进行动态绑定:

  • 对象语法:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
  • 数组语法:
<div v-bind:style="[styleColor, styleSize]"></div>

怎样理解 Vue 的单向数据流?

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。

这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。

有两种常见的试图改变一个 prop 的情形 :

  • 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。 在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'],
  • 这个 prop 以一种原始的值传入且需要进行转换。 在这种情况下,最好使用这个 prop 的值来定义一个计算属性
props: ['size'],

ass]">


Style 也可以通过对象语法和数组语法进行动态绑定:

- 对象语法:

```xml
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
  • 数组语法:
<div v-bind:style="[styleColor, styleSize]"></div>

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值