1.聊聊你对vue的理解
vue是一个渐进式的JS框架。他易用,灵活,高效; 可以把一个页面分隔成多个组件;当其他页面有类似功能时,直接让封装的组件进行复用; 他是构建用户界面的声明式框架,只关心图层;不关心具体是如何实现的
2.Vue 的生命周期是什么?
3.Vue 的各个生命阶段是什么?
4.Vue 组件间的参数传递方式?
5.computed 和 watch 和methods的差异?
(4)methods在重新渲染的时候每次都会被重新的调用;
6.vue-router 中的导航钩子函数
7.$route 和 $router 的区别?
8.vue 常用的修饰符?
.prevent:拦截默认事件.passive:不拦截默认事件.stop:阻止事件冒泡.self:当事件发生在该元素而不是子元素的时候会触发.capture:事件侦听,事件发生的时候会调用- once: 该修饰符表示绑定的事件只会被触发一次
9.vue 中 key 值的作用?
10.谈谈你对 keep-alive 的了解?
- 一般结合路由和动态组件一起使用,用于缓存组件;
- 提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;
- 对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。
11.vue 中 mixin 和 mixins 区别?
12.说说你对 SPA 单页面的理解,它的优缺点分别是什么?
SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。
优点:
- 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
- 基于上面一点,SPA 相对对服务器压力小;
- 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
缺点:
- 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
- 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
- SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
13.v-show 与 v-if 有什么区别?
v-show只是在display: none和display: block之间切换,只需要切换CSS,DOM还是一直保留着,v-show在初始渲染时有更高的开销,但是切换开销很小,更适合频繁切换的场景
v-if涉及到vue底层的编译,当属性初始为false时组件不会被渲染,直到条件为true,并且切换条件时会触发销毁/挂载组件,切换时开销更高,更适合不经常切换的场景
14.Class 与 Style 如何动态绑定?
v-bind:class="{'orange': isRipe, 'green': isNotRipe}"v-bind:class="[class1, class2]"v-bind:style="{color: color, fontSize: fontSize+'px'}"Class 可以通过对象语法和数组语法进行动态绑定:
- 对象语法:
<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
data: {
isActive: true,
hasError: false
}
- 数组语法:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
Style 也可以通过对象语法和数组语法进行动态绑定:
- 对象语法:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
activeColor: 'red',
fontSize: 30
}
- 数组语法:
<div v-bind:style="[styleColor, styleSize]"></div>
data: {
styleColor: {
color: 'red'
},
styleSize:{
fontSize:'23px'
}
}15.怎样理解 Vue 的单向数据流?
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。
有两种常见的试图改变一个 prop 的情形 :
- 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。 在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
- 这个 prop 以一种原始的值传入且需要进行转换。 在这种情况下,最好使用这个 prop 的值来定义一个计算属性
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}16.Vue与Angular以及React的区别?
(版本在不断更新,以下的区别有可能不是很正确。我工作中只用到vue,对angular和react不怎么熟) 1.与AngularJS的区别 相同点: 都支持指令:内置指令和自定义指令;都支持过滤器:内置过滤器和自定义过滤器;都支持双向数据绑定;都不支持低端浏览器。
不同点: AngularJS的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观;在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢;Vue.js使用基于依赖追踪的观察并且使用异步队列更新,所有的数据都是独立触发的。
2.与React的区别 相同点: React采用特殊的JSX语法,Vue.js在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用;中心思想相同:一切都是组件,组件实例之间可以嵌套;都提供合理的钩子函数,可以让开发者定制化地去处理需求;都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载;在组件开发中都支持mixins的特性。 不同点: React采用的Virtual DOM会对渲染出来的结果做脏检查;Vue.js在模板中提供了指令,过滤器等,可以非常方便,快捷地操作Virtual DOM。
17.直接给一个数组项赋值,Vue 能检测到变化吗?
由于 JavaScript 的限制,Vue 不能检测到以下数组的变动:
- 当你利用索引直接设置一个数组项时,例如:
vm.items[indexOfItem] = newValue - 当你修改数组的长度时,例如:
vm.items.length = newLength
为了解决第一个问题,Vue 提供了以下操作方法:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// vm.$set,Vue.set的一个别名
vm.$set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
为了解决第二个问题,Vue 提供了以下操作方法:
// Array.prototype.splice
vm.items.splice(newLength)18.Vue 的父组件和子组件生命周期钩子函数执行顺序?
Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:
-
加载渲染过程
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
-
子组件更新过程
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
-
父组件更新过程
父 beforeUpdate -> 父 updated
-
销毁过程
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
19.在哪个生命周期内调用异步请求?
可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。但是本人推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
- 能更快获取到服务端数据,减少页面 loading 时间;
- ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
20.v-model 的原理?
我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text 和 textarea 元素使用 value 属性和 input 事件;
- checkbox 和 radio 使用 checked 属性和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
以 input 表单元素为例:
<input v-model='something'>
相当于
<input v-bind:value="something" v-on:input="something = $event.target.value">
如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:
父组件:
<ModelChild v-model="message"></ModelChild>
子组件:
<div>{{value}}</div>
props:{
value: String
},
methods: {
test1(){
this.$emit('input', '小红')
},
},21.你使用过 Vuex 吗?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
(1)Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
(2)改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
主要包括以下几个模块:
- State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
- Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
- Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
- Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
- Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
22.使用过 Vue SSR 吗?说说 SSR?
Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
即:SSR大致的意思就是vue在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的html 片段直接返回给客户端这个过程就叫做服务端渲染。
23.vue-router 路由模式有几种?
vue-router 有 3 种路由模式:hash、history、abstract,对应的源码如下所示:
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
其中,3 种路由模式的说明如下:
-
hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;
-
history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
-
abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.
24.能说下 vue-router 中常用的 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);
window.history.replaceState(null, null, path);
history 路由模式的实现主要基于存在下面几个特性:
- pushState 和 repalceState 两个 API 来操作实现 URL 的变化 ;
- 我们可以使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染);
- history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染)。
25.什么是 MVVM?
即Model-View-ViewModel。
M - Model,Model 代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑
V - View,View 代表 UI 组件,它负责将数据模型转化为 UI 展现出来
VM - ViewModel,ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步 View 和 Model 的对象,连接 Model 和 View
26.Vue 是如何实现数据双向绑定的?
Vue 数据双向绑定主要是指:数据变化更新视图,视图变化更新数据,如下图所示:
即:
- 输入框内容变化时,Data 中的数据同步变化。即 View => Data 的变化。
- Data 中的数据变化时,文本节点的内容同步变化。即 Data => View 的变化。
其中,View 变化更新 Data ,可以通过事件监听的方式来实现,所以 Vue 的数据双向绑定的工作主要是如何根据 Data 变化更新 View。
27.vue-router hash 模式和 history 模式有什么区别?
28.你有对 Vue 项目进行哪些优化?
(1)代码层面的优化
- v-if 和 v-show 区分使用场景
- computed 和 watch 区分使用场景
- v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
- 长列表性能优化
- 事件的销毁
- 图片资源懒加载
- 路由懒加载
- 第三方插件的按需引入
- 优化无限列表性能
- 服务端渲染 SSR or 预渲染
(2)Webpack 层面的优化
- Webpack 对图片进行压缩
- 减少 ES6 转为 ES5 的冗余代码
- 提取公共代码
- 模板预编译
- 提取组件的 CSS
- 优化 SourceMap
- 构建结果输出分析
- Vue 项目的编译优化
(3)基础的 Web 技术的优化
-
开启 gzip 压缩
-
浏览器缓存
-
CDN 的使用
-
使用 Chrome Performance 查找性能瓶颈
29.vue路由传参的三种基本方式
方法一
this.$router.push({
path:`/home/${id}`,
})
路由配置
{
path:"/home/:id",
name:"Home",
component:Home
}
在Home组件中获取参数值
this.$route.params.id
复制代码方法二
通过name来匹配路由,通过param来传递参数
this.$router.push({
name:'Home',
params:{
id:id
}
})
用params传递参数,不使用:/id
{
path:'/home',
name:Home,
component:Home
}
Home组件中获取参数
this.$route.params.id
复制代码方法三
path+query;query传递的参数会通过?id = xxx展示
this.$router.push({
path:'/home',
query:{
id:id
}
})
路由配置
{
path:'/home',
name:Home,
component:Home
}
获取参数的方法
this.$route.query.id30.说出至少4中vue指令以及他的用法
v-text
- 解释:更新元素的 textContent
<h1 v-text="msg"></h1>
v-html
- 解释:更新元素的 innerHTML
<h1 v-html="msg"></h1>v-bind
- 作用:当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM
- 语法:
v-bind:title="msg" - 简写:
:title="msg"
v-on
- 作用:绑定事件
- 语法:
v-on:click="say"orv-on:click="say('参数', $event)" - 简写:
@click="say" - 说明:绑定的事件从
methods中获取
v-model
- 作用:在表单元素上创建双向数据绑定
- 说明:监听用户的输入事件以更新数据
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>v-if 和 v-show
- 条件渲染
v-if:根据表达式的值的真假条件,销毁或重建元素v-show:根据表达式之真假值,切换元素的 display CSS 属性
提升用户体验:v-cloak
- 这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。
- 防止刷新页面,网速慢的情况下出现{{ message }}等数据格式
<div v-cloak>
{{ message }}
</div>
31.什么是RESTful API,然后怎么使用?、
RESTful是一个api的标准,无状态请求。请求的路由地址是固定的。 restful:给用户一个url,根据method不同在后端做不同处理:比如post 创建数据,get 获取数据,put和patch修改数据,delete删除数据
32.vue响应式数据原理
核心点:Object.defineProperty
默认Vue在初始化数据时,会给data中的属性使用Object.defineProperty重新定义所有属性,当页面到对应属性时,会进行依赖收集(收集当前组件中的watcher)如果属性发生变化会通知相关依赖进行更新操作
33.vue中是如何实现检测数组的变化
Observer 对数组做了单独的处理,对数组的方法进行编译,并赋值给数组属性的 __proto__ 属性上,因为原型链的机制,找到对应的方法就不会继续往上找了。编译方法中会对一些会增加索引的方法(push,unshift,splice)进行手动 observe。34.为什么Vue采用异步渲染
因为如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染,所以为了性能考虑,Vue会在本轮数据更新后,再去异步更新数据
35.Vue优点
- 轻量级框架
- 只关注视图层,是一个构建数据的视图集合,大小只有几十kb
Vue.js通过简洁的API提供高效的数据绑定和灵活的组件系统- 简单易学
- 国人开发,中文文档,不存在语言障碍,易于理解和学习
- 双向数据绑定
- 通过
MVVM思想实现数据的双向绑定,让开发者不用再操作dom对象,有更多的时间去思考业务逻辑 - 组件化
Vue.js通过组件,把一个单页应用中的各种模块拆分到一个一个单独的组件(component)中,我们只要先在父级应用中写好各种组件标签(占坑),并且在组件标签中写好要传入组件的参数(就像给函数传入参数一样,这个参数叫做组件的属性),然后再分别写好各种组件的实现(填坑),然后整个应用就算做完了- 虚拟DOM
- 把最终的
DOM操作计算出来并优化,由于这个DOM操作属于预处理操作,并没有真实的操作DOM,所以叫做虚拟DOM。最后在计算完毕才真正将DOM操作提交,将DOM操作变化反映到DOM树上 - 视图,数据,结构分离
- 使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作
- 运行速度更快
- 像比较与
react而言,同样都是操作虚拟dom,就性能而言,vue存在很大的优势
36.Proxy与Object.defineProperty()的对比
Proxy的优点:
- 可以直接监听对象而非属性,并返回一个新对象
- 可以直接监听数值的变化
- 可以劫持整个对象,并返回一个新对象
Proxy的缺点:
Proxy是es6提供的新特性,兼容性不好,所以导致Vue3一致没有正式发布让开发者使用
Object.defineProperty的优点:
- 兼容性好,支持IE9
- IE9以下的版本不兼容
Object.defineProperty的缺点:
- 无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应
- 只能劫持对象的属性,我们需要对每个对象的每个属性进行遍历
37.React和Vue的区别
· 模板 vs jsx
Vue鼓励你去写近似常规HTML的模板,写起来很接近标准HTML元素,只是多了一些属性。
Vue鼓励你去使用HTML模板进行渲染。 React推荐你所有的模板通用JavaScript的语法扩展---JSX来书写
· 状态管理 VS 对象属性
1、如果熟悉React就会知道应用中的状态是关键的概念,有一些配套框架被设计为
管理一个大的state对象,state对象在React应用中是不可变的,它意味着不能被直接改变,
需要使用setState( )方法去更新状态
2、在Vue中,state对象并不是必须的,数据由data属性在Vue对象中进行管理。
data参数就是应用中数据的保存者
·数据流不同
1、Vue可以实现双向数据绑定(父子组件之间props可以双向绑定,组件与DOM之间可以通过v-model实现双向绑定)
2、React一直不支持双向绑定,提倡的是单向数据流
· 框架本质不同
1、Vue本质是MVVM框架,由MVC发展而来
2、React是前端组件化框架,由后端组件化发展而来38.Vue中相同逻辑如何抽离
Vue.mixin用法给组件每个生命周期,函数等都混入一些公共逻辑
39.SPA首屏加载慢如何解决
- 使用路由懒加载
- 使用SSR渲染
- 优化webpack打包体积
- 图片使用CDN加速
40.什么是虚拟DOM
Virtual dom, 即虚拟DOM节点。它通过JS的Object对象模拟DOM中的节点,然后再通过特定的render方法将其渲染成真实的DOM节点。虚拟DOM就是一个普通的JavaScript对象,包含了tag、props、children三个属性。
41.简述Vue中diff算法原理
- 先同级比较,在比较子节点
- 先判断一方有儿子一方没儿子的情况
- 比较都有儿子的情况
- 递归比较子节点
(大家要是不懂也可以看下这篇文章)对Dom-Diff的理解
42.第一次页面加载会触发哪几个钩子
beforeCreate, created, beforeMount, mounted
43.为什么v-for和v-if不能连用
v-for 和 v-if 处于同一个节点时,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。如果要遍历的数组很大,而真正要展示的数据很少时,这将造成很大的性能浪费。
这种场景建议使用 computed,先对数据进行过滤44.什么时候需要使用beforeDestroy
- 可能在当前页面中使用了
$on方法,那需要在组件销毁前解绑 - 清除自己定义的定时器
- 解绑事件的绑定
srcollmousemove....
45.谈谈你对作用域插槽的理解
单个插槽:
当子组件模板只有一个没有属性的插槽时, 父组件传入的整个内容片段将插入到插槽所在的 DOM 位置, 并替换掉插槽标签本身
命名插槽:
solt元素可以用一个特殊的特性name来进一步配置如何分发内容。
多个插槽可以有不同的名字。 这样可以将父组件模板中 slot 位置,
和子组件slot元素产生关联,便于插槽内容对应传递
作用域插槽:
作用域插槽scoped slots。可以访问组件内部数据的可复用插槽(reusable slot)。在父级中,具有特殊特性 slot-scope 的
46.组件中的data为什么是函数
47.Vue的渲染过程
1、把模板编译为render函数
2、实例进行挂载, 根据根节点render函数的调用,递归的生成虚拟dom
3、对比虚拟dom,渲染到真实dom
4、组件内部data发生变化,组件和子组件引用data作为props重新调用render函数,生成虚拟dom, 返回到步骤3
48.路由的跳转方式?
- router-link标签会渲染为a标签,咋填template中的跳转都是这种;
- 另一种是编程式导航 也就是通过js跳转 比如 router.push('/home')。
49.Watch中的deep:true是如何实现的
当用户指定了watch中的deep属性为true时,如果当前监控的值是数组类型,会对对象中的每一项进行求值,此时会将当前watcher存入到对应属性的依赖中,这样数组中的对象发生变化时也会通知数据更新
50.action和mutation区别
mutation是同步更新数据(内部会进行是否为异步方式更新数据的检测)action异步操作,可以获取数据后调用mutation提交最终数据
51.Vue3.0你知道有哪些改进
Vue3采用了TS来编写- 支持
Composition API Vue3中响应式数据原理改成proxyvdom的对比算法更新,只更新vdom的绑定了动态数据的部分
52.为什么使用异步组件
在大型应用中,功能不停地累加后,核心页面已经不堪重负,访问速度愈来愈慢。为了解决这个问题我们需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块,从而提高页面加载速度
53.vue实现路由懒加载的方式
1.结合vue的异步组件和Webpack的代码分割功能可以实现路由组件的懒加载。打包后,每一 个组件生成一个js文件。举例如下:{
path: '/Demo',
name: 'Demo',
//打包后,每个组件单独生成一个chunk文件
component: reslove => require(['../views/Demo'], resolve)
}2. 动态import语法
//指定了相同的webpackChunkName,会合并打包成一个文件。
const Demo = () => import(/*webpackChunkName:'Home'*/ '../views/Demo')
const Demo1 = () => import(/*webpackChunkName:'Home'*/ '../views/Demo1') 3. vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载这种 情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。
//这种情况下,多个路由指定相同的chunkName,会打包成一个文件
const Demo = r => require.ensure([], () => r(require('../views/Demo')), 'Demo')
const Demo1 = r => require.ensure([], () => r(require('../views/Demo1')), 'Demo')
54.vue封装组件的思路
第一步:在components目录新建你的组件文件(smithButton.vue),script一定要export default {
第二步:在需要用的页面(组件)中导入:import smithButton from ‘../components/smithButton.vue’
第三步:注入到vue的子组件的components属性上面,components:{smithButton}
第四步:在template视图view中使用,<smith-button> </smith-button>
问题有:smithButton命名,使用的时候则smith-button
55.$nextTick是什么?
vue实现响应式并不是数据发生变化后dom立即变化,而是按照一定的策略来进行dom更新。
$nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM
56.vue.js的两个核心是什么?
数据驱动、组件系统
57.axios的特点有哪些?
1、axios是一个基于promise的HTTP库,支持promise的所有API;
2、它可以拦截请求和响应;
3、它可以转换请求数据和响应数据,并对响应回来的内容自动转换为json类型的数据;
4、它安全性更高,客户端支持防御XSRF;
58.vue 的双向绑定的原理是什么
vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
具体步骤:
第一步:需要 observe 的数据对象v进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter
第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,
第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
59.如果要在组件内部中进行强制刷新
调用this.$forceUpdate()强制重新渲染组件
60.说说vue-router完整的导航解析流程是什么?
1.导航被触发
2.在即将离开的组件里调用beforeRouteLeave守卫
3.调用全局前置守卫beforeEach守卫
4.在重用的组件里调用beforeRouteUpdate守卫 / 调用路由配置的beforeEnter守卫
5.解析异步路由组件
6.在被激活的组件里调用beforeRouteEnter
7.调用全局的beforeResolve守卫
8.导航被确认
9.调用全局的 afterEach 钩子
10.触发DOM更新
11.用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。
61.vue中深度监听对象的几种方式
1.直接监听对象
watch:{
obj:{ //监听的对象
deep:true, //深度监听设置为 true
handler:function(newV,oldV){
console.log('watch中:',newV)
}
}
}2.监听对象下的其中一个属性
data () {
return {
obj:{
name:'老蒋坏得很',
age:18
}
}
},
watch:{
'obj.name':{
deep:true,
handler:function(newV,oldV){
console.log('watch中:',newV)
}
}
}
3.computed配合watch实现单个属性的深度监听
data () {
return {
obj:{
name:'老蒋坏得很',
age:18
}
}
},
computed:{
name(){
return this.obj.name;
}
},
watch:{
name(newval,oldval){
console.log('新:',newval);
console.log('旧:',oldval)
}
62.$nextTick有什么作用?
Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。
$nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM
new Vue({
methods: {
example: function () {
modify data this.message = 'changed'
// DOM is not updated yet
his.$nextTick(function () {
// DOM is now updated
// `this` is bound to the current instance
this.doSomethingElse()
})
}
}
}) 参考:www.jianshu.com/p/a7550c0e1…
63.微任务与宏任务
浏览器中的事件循环 eventLoop,分为同步执行栈和异步队列,首先会执行同步的任务,当同步任务执行完之后会从异步队列中取异步任务拿到同步执行栈中进行执行。
在取异步队列时,还会有一个区分,就是区分微任务和宏任务。
setTimeout(function() {
console.log(4);
}, 0);
new Promise(function(reslove) {
console.log(1);
reslove();
}).then(function(data) {
console.log(3);
});
console.log(2);我们这次来看一下上面的题,promise 中是同步任务,promise 的 .then 中是异步任务,并且是微任务。使用 setTimeout 是宏任务,即使是延时为 0,也是宏任务。
所以上面的执行顺序就是先将 setTimeout 加入到异步队列的宏任务池中,然后执行 promise 中的console.log(1),再将 promise 的.then 加到异步队列中微任务池中,再执行同步任务console.log(2),当同步任务都执行完之后,去微任务中的任务,执行console.log(3),微任务执行完之后取宏任务,执行console.log(4)。所以顺序就是:1,2,3,4。
扩展:
将这道面试题进行一些改造:
setTimeout(function() {
console.log(4);
}, 0);
new Promise(function(reslove) {
console.log(1);
setTimeout(function() {
reslove('done');
}, 0);
reslove('first');
}).then(function(data) {
console.log(data);
});
console.log(2);这个时候就会输出:1,2,first,4,没有输出 done,有些人可能会想,应该输出 1,2,first,4,done,这个时候你就要知道,当使用 reslove 之后,promise 的状态就从 pedding 变成了 resolve,promise 的状态更改之后就不能再更改了,所以 reslove('done') 就不会执行了。
当我们把 reslove('done') 改成 console.log('done') 的时候,他就会输出 1,2,first,4,done 了。
64.vue中是否可以监控到数组下标的变化
Object.defineProperty 有一个缺陷是无法监听数组
无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。所以vue才设置了7个变异数组(push、pop、shift、unshift、splice、sort、reverse)的 hack 方法来解决问题。
65.Vue项目中实现用户登录及token验证
在前后端完全分离的情况下,Vue项目中实现token验证大致思路如下:
1、第一次登录的时候,前端调后端的登陆接口,发送用户名和密码
2、后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token
3、前端拿到token,将token存储到localStorage和vuex中,并跳转路由页面
4、前端每次跳转路由,就判断 localStroage 中有无 token ,没有就跳转到登录页面,有则跳转到对应路由页面
5、每次调后端接口,都要在请求头中加token
6、后端判断请求头中有无token,有token,就拿到token并验证token,验证成功就返回数据,验证失败(例如:token过期)就返回401,请求头中没有token也返回401
7、如果前端拿到状态码为401,就清除token信息并跳转到登录页面
一、调登录接口成功,在回调函数中将token存储到localStorage和vuex中
<template>
<div>
<input type="text" v-model="loginForm.username" placeholder="用户名"/>
<input type="text" v-model="loginForm.password" placeholder="密码"/>
<button @click="login">登录</button>
</div>
</template>
<script>
import { mapMutations } from 'vuex';
export default {
data () {
return {
loginForm: {
username: '',
password: ''
}
};
},
methods: {
...mapMutations(['changeLogin']),
login () {
let _this = this;
if (this.loginForm.username === '' || this.loginForm.password === '') {
alert('账号或密码不能为空');
} else {
this.axios({
method: 'post',
url: '/user/login',
data: _this.loginForm
}).then(res => {
console.log(res.data);
_this.userToken = 'Bearer ' + res.data.data.body.token;
// 将用户token保存到vuex中
_this.changeLogin({ Authorization: _this.userToken });
_this.$router.push('/home');
alert('登陆成功');
}).catch(error => {
alert('账号或密码错误');
console.log(error);
});
}
}
}
};
</script>store文件夹下的index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
// 存储token
Authorization: localStorage.getItem('Authorization') ? localStorage.getItem('Authorization') : ''
},
mutations: {
// 修改token,并将token存入localStorage
changeLogin (state, user) {
state.Authorization = user.Authorization;
localStorage.setItem('Authorization', user.Authorization);
}
}
});
export default store;
二、路由导航守卫
router文件夹下的index.js
import Vue from 'vue';
import Router from 'vue-router';
import login from '@/components/login';
import home from '@/components/home';
Vue.use(Router);
const router = new Router({
routes: [
{
path: '/',
redirect: '/login'
},
{
path: '/login',
name: 'login',
component: login
},
{
path: '/home',
name: 'home',
component: home
}
]
});
// 导航守卫
// 使用 router.beforeEach 注册一个全局前置守卫,判断用户是否登陆
router.beforeEach((to, from, next) => {
if (to.path === '/login') {
next();
} else {
let token = localStorage.getItem('Authorization');
if (token === 'null' || token === '') {
next('/login');
} else {
next();
}
}
});
export default router;三、请求头加token
// 添加请求拦截器,在请求头中加token
axios.interceptors.request.use(
config => {
if (localStorage.getItem('Authorization')) {
config.headers.Authorization = localStorage.getItem('Authorization');
}
return config;
},
error => {
return Promise.reject(error);
});四、如果前端拿到状态码为401,就清除token信息并跳转到登录页面
localStorage.removeItem('Authorization');
this.$router.push('/login');66.setTimeout、Promise、Async/Await 的区别
setTimeout
console.log('script start') //1. 打印 script start
setTimeout(function(){
console.log('settimeout') // 4. 打印 settimeout
}) // 2. 调用 setTimeout 函数,并定义其完成后执行的回调函数
console.log('script end') //3. 打印 script start
// 输出顺序: script start -> script end -> settimeoutPromise
console.log('script start') // 1111111
let promise1 = new Promise(function (resolve) {
console.log('promise1') // 222222
resolve()
console.log('promise1 end') // 333333
}).then(function () {
console.log('promise2')
})
setTimeout(function(){
console.log('settimeout')
})
console.log('script end')
// 输出顺序: script start -> promise1 -> promise1 end -> script end-> promise2 -> settimeoutasync/await
async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start');
async1();
console.log('script end')
// 输出顺序:script start -> async1 start -> async2 -> script end -> async1 end67.请说出vue.cli项目中src目录每个文件夹和文件的用法
- assets文件夹是放静态资源;
- components是放组件;
- router是定义路由相关的配置;
- view视图;
- app.vue是一个应用主组件;
- main.js是入口文件
68.Vue怎么重置data
使用Object.assign(),vm.$data可以获取当前状态下的data,vm.$options.data可以获取到组件初始化状态下的data。
Object.assign(this.$data, this.$options.data())复制代码69.组件中写name选项有什么作用
- 项目使用keep-alive时,可搭配组件的name进行缓存过滤。
- DOM做递归组件时需要调用自身name
- vue-devtools调试工具里显示的组件名称是由vue中组件name决定的
70.axios的拦截
//响应拦截
axios.interceptors.response.use(function(response){
//对响应数据做点什么
return response.data
},function(error){
//对错误响应做点什么
return Promise.reject(error)
})//请求拦截
axios.interceptors.request.use(function(config){
//在发送请求之前做些什么
return config
},function(error){
//对请求错误做些什么
return Promise.reject(error)
})71.vue怎么兼容IE
使用babel-polyfill插件
72.route-link和a标签的区别
<router-link> 组件支持用户在具有路由功能的应用中 (点击) 导航。 通过 to 属性指定目标地址,默认渲染成带有正确链接的 <a> 标签,可以通过配置 tag 属性生成别的标签.。
通过router-link进行跳转不会跳转到新的页面,也不会重新渲染,它会选择路由所指的组件进行渲染,避免了重复渲染的“无用功”。
总结:对比<a>,router-link组件避免了不必要的重渲染,它只更新变化的部分从而减少DOM性能消耗
73.vue3.0中compostionAPI更新了那些内容
- reactive API
- ref API
- watch API变化
- computed API变化
- 生命周期钩子变化
- TypeScript和JSX支持