文章目录
Vue面试题
Vue
1.请详细说下你对 vue 生命周期的理解?
2.为什么 vue 组件中 data 必须是一个函数?
3.vue 中 v-if 和 v-show 有什么区别?
4.**computed 和 watch 的区别**
计算属性 computed:
侦听属性 watch:
5.**vue-loader 是什么?使用它的用途有哪些?**
6.**$nextTick 是什么?**
7.**v-for key 的作用**
8.**Vue 的双向数据绑定原理是什么?**
9.**请说下封装 vue 组件的过程**
10.**Vue.js 的 template 编译**
11.**vue 如何监听对象或者数组某个属性的变化**
12.**常用的事件修饰符**
13.**vue 如何获取 dom**
14.**v-on 可以监听多个方法吗?**
15.**assets 和 static 的区别**
16.**slot 插槽**
17.**vue 初始化页面闪动问题**
18. Vue 中操作 data 中数组的方法中哪些可以触发视图更新,哪些不可以,不可以的话有什么解决办法?
19.混入(mixin)
20.computed 中的属性名和 data 中的属性名可以相同吗?
21.怎么强制刷新组件?
22.watch 的属性和 methods 方法能用箭头函数定义吗?
23.给组件绑定自定义事件无效怎么解决?
24.怎么访问子组件的实例或者子元素?
25.怎么在子组件中访问父组件的实例?怎么在组件中访问到根实例?
26.组件会在什么时候下被销毁?
27.is 这个特性你有用过吗?主要用在哪些方面?
28.prop 验证的 type 类型有哪几种?
29.在 Vue 事件中传入 `$event` ,使用 $event.target和 event.currentTarget 有什么区别?
30.使用事件修饰符要注意什么?
31.说说你对 Vue 的表单修饰符.lazy 的理解?
32.v-once 的使用场景有哪些?
33.v-cloak 和 v-pre 有什么作用?
34.怎么使 css 样式只在当前组件中生效?在 style 上加 scoped 属性需要注意哪些?你知道 style 上加 scoped 属性的原理吗?
35.Vue 渲染模板时怎么保留模板中的 HTML 注释呢?
36.Vue 中怎么重置 data?
37.过滤器中可以用 this 吗?
38.Vue在created和mounted这两个生命周期中请求数据有什么区别呢?
39.说说你对keep-alive的理解
40.v-if和v-for的优先级是什么?如果这两个同时出现时,那应该怎么优化才能得到更好的性能?
41.使用v-for遍历对象时,是按什么顺序遍历的?如何保证顺序?
42.key除了在v-for中使用,还有什么作用?
43.使用key要什么要注意的吗?
44.说说组件的命名规范
45.为什么组件中data必须用函数返回一个对象?
46.组件的name选项有什么作用?
47.说下`$attrs`和`$listeners`的使用场景?
48.EventBus注册在全局上时,路由切换时会重复触发事件,如何解决呢?
49.Vue组件里写的原生addEventListeners监听事件,要手动去销毁吗?为什么?
50.Vue组件里的定时器要怎么销毁?
51.Vue中能监听到数组变化的方法有哪些?为什么这些方法能监听到呢?
52.在Vue中哪些数组变化无法监听,为什么,怎么解决?
53.在Vue中哪些对象变化无法监听,为什么,怎么解决?
54.删除对象用delete和Vue.delete有什么区别?
55.``有什么用?
56.Vue怎么定义全局方法
57.Vue怎么改变插入模板的分隔符?
58.Vue变量名如果以_、$开头的属性会发生什么问题?怎么访问到它们的值?
59.怎么捕获Vue组件的错误信息?
60.Vue.observable你有了解过吗?说说看
61.Vue项目中如何配置favicon?
62.怎么修改Vue项目打包后生成文件路径?
63.怎么解决Vue项目打包后静态资源图片失效的问题?
64.怎么解决Vue中动态设置img的src不生效的问题?
65.在Vue项目中如何引入第三方库(比如jQuery)?有哪些方法可以做到?
66.说说你对SPA单页面的理解,它的优缺点分别是什么?
67.SPA单页面的实现方式有哪些?
68.说说你对Object.defineProperty的理解
69.说说你对Proxy的理解
70.Object.defineProperty和Proxy的区别
71.Vue的模板语法用的是哪个web模板引擎的吗?说说你对这模板引擎的理解?
72.你认为Vue的核心是什么?
73.说说你对单向数据流和双向数据流的理解
74.什么是虚拟DOM?
75.Vue中如何实现一个虚拟DOM?说说你的思路
76.Vue为什么要求组件模板只能有一个根元素?
77.axios是什么?怎样使用它?怎么解决跨域的问题?
78.如果想扩展某个现有的Vue组件时,怎么做呢?
79.vue-loader是什么?它有什么作用?
80.你有使用过JSX吗?说说你对JSX的理解?
Vuex
1.什么是Vuex?
2.Vuex解决了什么问题?
3.什么时候用Vuex?
4.怎么引用Vuex?
5.Vuex的5个核心属性是什么?
6.Vuex中状态储存在哪里,怎么改变它?
7.Vuex中状态是对象时,使用时要注意什么?
8.怎么在组件中批量使用Vuex的state状态?
9.Vuex中要从state派生一些状态出来,且多个组件使用它,该怎么做?
10.怎么通过getter来实现在组件内可以通过特定条件来获取state的状态?
11.怎么在组件中批量使用Vuex的getter属性
12.怎么在组件中批量给Vuex的getter属性取别名并使用
13.在Vuex中使用mutation要注意什么。
14.Vuex中action和mutation有什么区别?
15.Vuex中action和mutation有什么相同点?
16.在组件中多次提交同一个action,怎么写使用更方便。
17.Vuex中action通常是异步的,那么如何知道action什么时候结束呢?
18.Vuex中有两个action,分别是actionA和actionB,其内都是异步操作,在actionB要提交actionA,需在actionA处理结束再处理其它操作,怎么实现?
19.有用过Vuex模块吗,为什么要使用,怎么使用。
20.在模块中,getter和mutation接收的第一个参数state,是全局的还是模块的?
21.在模块中,getter和mutation和action中怎么访问全局的state和getter?
22.在组件中怎么访问Vuex模块中的getter和state,怎么提交mutation和action?
23.用过Vuex模块的命名空间吗?为什么使用,怎么使用。
24.怎么在带命名空间的模块内提交全局的mutation和action?
25.怎么在带命名空间的模块内注册全局的action?
26.怎么使用mapState,mapGetters,mapActions和mapMutations这些函数来绑定带命名空间的模块?
27.Vuex插件有用过吗?怎么用简单介绍一下?
28.在Vuex插件中怎么监听组件中提交mutation和action?
29.在v-model上怎么用Vuex中state的值?
30.Vuex的严格模式是什么,有什么作用,怎么开启?
Vue-Router
1.怎么重定向页面?
2.怎么配置404页面?
3.切换路由时,需要保存草稿的功能,怎么实现呢?
4.路由有几种模式?说说它们的区别?
5.讲一下完整的导航守卫流程?
6.讲一下导航守卫的三个参数的含义?
7.在afterEach钩子中可以使用`next()`吗?
8.全局导航守卫有哪些?怎么使用?
9.什么是路由独享的守卫,怎么使用?
10.说说你对router-link的了解
11.怎么在组件中监听路由参数的变化?
12.切换路由后,新页面要滚动到顶部或保持原先的滚动位置怎么做呢?
13.路由组件和路由为什么解耦,怎么解耦?
14.说说active-class是哪个组件的属性?
15.怎样动态加载路由?
16.怎么实现路由懒加载呢?
17.路由之间是怎么跳转的?有哪些方式?
18.如果vue-router使用history模式,部署时要注意什么?
19.route和router有什么区别?
20.Vue路由怎么跳转打开新窗口?
Vue面试题
Vue
1.请详细说下你对 vue 生命周期的理解?
总共分为 8 个阶段创建前 / 后,载入前 / 后,更新前 / 后,销毁前 / 后。
创建前 / 后: 在 beforeCreate 阶段,vue 实例的挂载元素 el 和数据对象 data 都为 undefined,还未初始化。在 created 阶段,vue 实例的数据对象 data 有了,el 为 undefined,还未初始化。
载入前 / 后:在 beforeMount 阶段,vue 实例的 $el 和 data 都初始化了,但还是挂载之前为虚拟的 dom 节点,data.message 还未替换。在 mounted 阶段,vue 实例挂载完成,data.message 成功渲染。
更新前 / 后:当 data 变化时,会触发 beforeUpdate 和 updated 方法
销毁前 / 后:在执行 destroy 方法后,对 data 的改变不会再触发周期函数,说明此时 vue 实例已经解除了事件监听以及和 dom 的绑定,但是 dom 结构依然存在
2.为什么 vue 组件中 data 必须是一个函数?
对象为引用类型,当复用组件时,由于数据对象都指向同一个 data 对象,当在一个组件中修改 data 时,其他重用的组件中的 data 会同时被修改;而使用返回对象的函数,由于每次返回的都是一个新对象(Object 的实例),引用地址不同,则不会出现这个问题。
3.vue 中 v-if 和 v-show 有什么区别?
v-if 和 v-show 看起来似乎差不多,当条件不成立时,其所对应的标签元素都不可见,但是这两个选项是有区别的:
1、v-if 在条件切换时,会对标签进行适当的创建和销毁,而 v-show 则仅在初始化时加载一次,因此 v-if 的开销相对来说会比 v-show 大。
2、v-if 是惰性的,只有当条件为真时才会真正渲染标签;如果初始条件不为真,则 v-if 不会去渲染标签。v-show 则无论初始条件是否成立,都会渲染标签,它仅仅做的只是简单的 CSS 切换。
4.computed 和 watch 的区别
计算属性 computed:
支持缓存,只有依赖数据发生改变,才会重新进行计算
不支持异步,当 computed 内有异步操作时无效,无法监听数据的变化
computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于 data 中声明过或者父组件传递的 props 中的数据通过计算得到的值
如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用 computed
如果 computed 属性属性值是函数,那么默认会走 get 方法;函数的返回值就是属性的属性值;在 computed 中的,属性都有一个 get 和一个 set 方法,当数据变化时,调用 set 方法。
侦听属性 watch:
不支持缓存,数据变,直接会触发相应的操作;
watch 支持异步;
监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
当一个属性发生变化时,需要执行对应的操作;一对多;
监听数据必须是 data 中声明过或者父组件传递过来的 props 中的数据,当数据变化时,触发其他操作,函数有两个参数:
immediate:组件加载立即触发回调函数执行
watch: {
firstName: {
handler(newName, oldName) {
this.fullName = newName + ' ' + this.lastName;
},
// 代表在wacth里声明了firstName这个方法之后立即执行handler方法
immediate: true
}
}
复制代码
1
2
3
4
5
6
7
8
9
10
deep: deep 的意思就是深入观察,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器,但是这样性能开销就会非常大了,任何修改 obj 里面任何一个属性都会触发这个监听器里的 handler
watch: {
obj: {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
deep: true
}
}
复制代码
1
2
3
4
5
6
7
8
9
10
优化:我们可以使用字符串的形式监听
watch: {
'obj.a': {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
// deep: true
}
}
复制代码
1
2
3
4
5
6
7
8
9
10
这样 Vue.js 才会一层一层解析下去,直到遇到属性 a,然后才给 a 设置监听函数。
5.vue-loader 是什么?使用它的用途有哪些?
vue 文件的一个加载器,跟 template/js/style 转换成 js 模块。
6.$nextTick 是什么?
vue 实现响应式并不是数据发生变化后 dom 立即变化,而是按照一定的策略来进行 dom 更新。
nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 nextTick,则可以在回调中获取更新后的 DOM
7.v-for key 的作用
当 Vue 用 v-for 正在更新已渲染过的元素列表是,它默认用 “就地复用” 策略。如果数据项的顺序被改变,Vue 将不是移动 DOM 元素来匹配数据项的改变,而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。key 属性的类型只能为 string 或者 number 类型。
key 的特殊属性主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试修复 / 再利用相同类型元素的算法。使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
8.Vue 的双向数据绑定原理是什么?
vue.js 是采用数据劫持结合发布者 - 订阅者模式的方式,通过 Object.defineProperty () 来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。主要分为以下几个步骤:
1、需要 observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter 这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化
2、compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
3、Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁,主要做的事情是: ①在自身实例化时往属性订阅器 (dep) 里面添加自己 ②自身必须有一个 update () 方法 ③待属性变动 dep.notice () 通知时,能调用自身的 update () 方法,并触发 Compile 中绑定的回调,则功成身退。
4、MVVM 作为数据绑定的入口,整合 Observer、Compile 和 Watcher 三者,通过 Observer 来监听自己的 model 数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化 (input) -> 数据 model 变更的双向绑定效果。
9.请说下封装 vue 组件的过程
首先,组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低、难维护、复用性等问题。
然后,使用 Vue.extend 方法创建一个组件,然后使用 Vue.component 方法注册组件。子组件需要数据,可以在 props 中接受定义。而子组件修改好数据后,想把数据传递给父组件。可以采用 emit 方法。
10.Vue.js 的 template 编译
简而言之,就是先转化成 AST 树,再得到的 render 函数返回 VNode(Vue 的虚拟 DOM 节点),详细步骤如下:
首先,通过 compile 编译器把 template 编译成 AST 语法树(abstract syntax tree 即 源代码的抽象语法结构的树状表现形式),compile 是 createCompiler 的返回值,createCompiler 是用以创建编译器的。另外 compile 还负责合并 option。
然后,AST 会经过 generate(将 AST 语法树转化成 render funtion 字符串的过程)得到 render 函数,render 的返回值是 VNode,VNode 是 Vue 的虚拟 DOM 节点,里面有(标签名、子节点、文本等等)
11.vue 如何监听对象或者数组某个属性的变化
当在项目中直接设置数组的某一项的值,或者直接设置对象的某个属性值,这个时候,你会发现页面并没有更新。这是因为 Object.defineProperty () 限制,监听不到变化。
解决方式:
this.$set (你要改变的数组 / 对象,你要改变的位置 /key,你要改成什么 value)
this.$set(this.arr, 0, "OBKoro1"); // 改变数组
this.$set(this.obj, "c", "OBKoro1"); // 改变对象
复制代码
1
2
3
调用以下几个数组的方法
splice()、 push()、pop()、shift()、unshift()、sort()、reverse()
复制代码
1
2
vue 源码里缓存了 array 的原型链,然后重写了这几个方法,触发这几个方法的时候会 observer 数据,意思是使用这些方法不用我们再进行额外的操作,视图自动进行更新。 推荐使用 splice 方法会比较好自定义,因为 splice 可以在数组的任何位置进行删除 / 添加操作
12.常用的事件修饰符
.stop: 阻止冒泡
.prevent: 阻止默认行为
.self: 仅绑定元素自身触发
.once: 2.1.4 新增,只触发一次
passive: 2.3.0 新增,滚动事件的默认行为 (即滚动行为) 将会立即触发,不能和.prevent 一起使用
.sync 修饰符
从 2.3.0 起 vue 重新引入了.sync 修饰符,但是这次它只是作为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 监听器。示例代码如下:
<comp :foo.sync="bar"></comp>
复制代码
1
2
会被扩展为:
<comp :foo="bar" @update:foo="val => bar = val"></comp>
复制代码
1
2
当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:
this.$emit('update:foo', newValue)
复制代码
1
2
13.vue 如何获取 dom
先给标签设置一个 ref 值,再通过 this.$refs.domName 获取,例如:
<div ref="test"></div>
const dom = this.$refs.test
复制代码
1
2
3
4
14.v-on 可以监听多个方法吗?
是可以的,来个例子:
<input type="text" v-on="{ input:onInput,focus:onFocus,blur:onBlur, }">
复制代码
1
2
15.assets 和 static 的区别
这两个都是用来存放项目中所使用的静态资源文件。
两者的区别:
assets 中的文件在运行 npm run build 的时候会打包,简单来说就是会被压缩体积,代码格式化之类的。打包之后也会放到 static 中。
static 中的文件则不会被打包。
建议:将图片等未处理的文件放在 assets 中,打包减少体积。而对于第三方引入的一些资源文件如 iconfont.css 等可以放在 static 中,因为这些文件已经经过处理了。
16.slot 插槽
很多时候,我们封装了一个子组件之后,在父组件使用的时候,想添加一些 dom 元素,这个时候就可以使用 slot 插槽了,但是这些 dom 是否显示以及在哪里显示,则是看子组件中 slot 组件的位置了。
17.vue 初始化页面闪动问题
使用 vue 开发时,在 vue 初始化之前,由于 div 是不归 vue 管的,所以我们写的代码在还没有解析的情况下会容易出现花屏现象,看到类似于 {{message}} 的字样,虽然一般情况下这个时间很短暂,但是我们还是有必要让解决这个问题的。
首先:在 css 里加上以下代码
[v-cloak] {
display: none;
}
复制代码
1
2
3
4
如果没有彻底解决问题,则在根元素加上 style=“display: none;” :style=“{display: ‘block’}”
18. Vue 中操作 data 中数组的方法中哪些可以触发视图更新,哪些不可以,不可以的话有什么解决办法?
push ()、pop ()、shift ()、unshift ()、splice ()、sort ()、reverse () 这些方法会改变被操作的数组; filter ()、concat ()、slice () 这些方法不会改变被操作的数组,返回一个新的数组; 以上方法都可以触发视图更新。
利用索引直接设置一个数组项,例:this.array[index] = newValue
直接修改数组的长度,例:this.array.length = newLength
以上两种方法不可以触发视图更新;
可以用 this.$set(this.array,index,newValue) 或 this.array.splice(index,1,newValue) 解决方法 1
可以用 this.array.splice(newLength) 解决方法 2
19.混入(mixin)
全局混入在项目中怎么用?
在 main.js 中写入
import Vue from 'vue';
import mixins from './mixins';
Vue.mixin(mixins);
1
2
3
之后,全局混入可以写在 mixins 文件夹中 index.js 中,全局混入会影响到每一个之后创建的 Vue 实例(组件);
局部混入在项目中怎么用
局部混入的注册,在 mixins 文件中创建一个 a_mixin.js 文件,然后再 a.vue 文件中写入
<script>
import aMixin from 'mixins/a_mixin'
export default{
mixins:[aMixin],
}
</script>
1
2
3
4
5
6
局部混入只会影响 a.vue 文件中创建的 Vue 实例,不会影响到其子组件创建的 Vue 实例;
组件的选项和混入的选项是怎么合并的
数据对象【data 选项】,在内部进行递归合并,并在发生冲突时以组件数据优先;
同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用;
watch 对象合并时,相同的 key 合成一个对象,且混入监听在组件监听之前调用;
值为对象的选项【filters 选项、computed 选项、methods 选项、components 选项、directives 选项】将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。
20.computed 中的属性名和 data 中的属性名可以相同吗?
不能同名,因为不管是 computed 属性名还是 data 数据名还是 props 数据名都会被挂载在 vm 实例上,因此这三个都不能同名。
if (key in vm.$data) {
warn(`The computed property "${key}" is already defined in data.`, vm)
} else if (vm.$options.props && key in vm.$options.props) {
warn(`The computed property "${key}" is already defined as a prop.`, vm)
}
1
2
3
4
5
21.怎么强制刷新组件?
this.$forceUpdate()。
组件上加上 key,然后变化 key 的值。
22.watch 的属性和 methods 方法能用箭头函数定义吗?
不可以。this 会是 undefind, 因为箭头函数中的 this 指向的是定义时的 this,而不是执行时的 this,所以不会指向 Vue 实例的上下文。
23.给组件绑定自定义事件无效怎么解决?
加上修饰词.native。
24.怎么访问子组件的实例或者子元素?
先用 ref 特性为子组件赋予一个 ID 引用 <base-input ref="myInput"></<base-input>
比如子组件有个 focus 的方法,可以这样调用 this.$refs.myInput.focus();
比如子组件有个 value 的数据,可以这样使用 this.$refs.myInput.value。
先用 ref 特性为普通的 DOM 元素赋予一个 ID 引用
<ul ref="mydiv">
<li class="item">第一个li</li>
<li class="item">第一个li</li>
</ul>
console.log(this.$refs['mydiv'].getElementsByClassName('item')[0].innerHTML)//第一个li
1
2
3
4
5
25.怎么在子组件中访问父组件的实例?怎么在组件中访问到根实例?
使用 this.$parent 来访问
this.$root
26.组件会在什么时候下被销毁?
没有使用 keep-alive 时的路由切换;
v-if='false';
执行 vm.$destroy();
27.is 这个特性你有用过吗?主要用在哪些方面?
动态组件
<component :is="componentName"></component>, componentName 可以是在本页面已经注册的局部组件名和全局组件名,也可以是一个组件的选项对象。 当控制 componentName 改变时就可以动态切换选择组件。
is 的用法
有些 HTML 元素,诸如 <ul>、<ol>、<table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的。
而有些 HTML 元素,诸如 <li>、<tr> 和 <option>,只能出现在其它某些特定的元素内部。
<ul>
<card-list></card-list>
</ul>
1
2
3
所以上面 <card-list></card-list> 会被作为无效的内容提升到外部,并导致最终渲染结果出错。应该这么写:
<ul>
<li is="cardList"></li>
</ul>
1
2
3
28.prop 验证的 type 类型有哪几种?
String、Number、Boolean、Array、Object、Date、Function、Symbol, 此外还可以是一个自定义的构造函数 Personnel,并且通过 instanceof 来验证 propwokrer 的值是否是通过这个自定义的构造函数创建的。
function Personnel(name,age){
this.name = name;
this.age = age;
}
export default {
props:{
wokrer:Personnel
}
}
1
2
3
4
5
6
7
8
9
29.在 Vue 事件中传入 $event ,使用 $event.target和 event.currentTarget 有什么区别?
$event.currentTarget 始终指向事件所绑定的元素,而 $event.target 指向事件发生时的元素。
30.使用事件修饰符要注意什么?
要注意顺序很重要,用 @click.prevent.self 会阻止所有的点击,而 @click.self.prevent 只会阻止对元素自身的点击。
31.说说你对 Vue 的表单修饰符.lazy 的理解?
input 标签 v-model 用 lazy 修饰之后,并不会立即监听 input 的 value 的改变,会在 input 失去焦点之后,才会监听 input 的 value 的改变。
32.v-once 的使用场景有哪些?
其作用是只渲染元素和组件一次。随后的重新渲染,元素 / 组件及其所有的子节点将被视为静态内容并跳过。故当组件中有大量的静态的内容可以使用这个指令。
33.v-cloak 和 v-pre 有什么作用?
v-cloak:可以解决在页面渲染时把未编译的 Mustache 标签({{value}})给显示出来。
[v-cloak] {
display: none!important;
}
<div v-cloak>
{{ message }}
</div>
1
2
3
4
5
6
v-pre:跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
<span v-pre>{{ this will not be compiled }}</span>
1
34.怎么使 css 样式只在当前组件中生效?在 style 上加 scoped 属性需要注意哪些?你知道 style 上加 scoped 属性的原理吗?
<style lang="less" scoped> </style>
1
如果在公共组件中使用,修改公共组件的样式需要用 /deep/。
vue 通过在 DOM 结构以及 css 样式上加上唯一的标记 data-v-xxxxxx,保证唯一,达到样式私有化,不污染全局的作用。
35.Vue 渲染模板时怎么保留模板中的 HTML 注释呢?
在组件中将 comments 选项设置为 true
<template comments> ... <template>
36.Vue 中怎么重置 data?
Object.assign(this.$data,this.$options.data())
1
37.过滤器中可以用 this 吗?
不可以
38.Vue在created和mounted这两个生命周期中请求数据有什么区别呢?
在created中,页面视图未出现,如果请求信息过多,页面会长时间处于白屏状态,DOM节点没出来,无法操作DOM节点。在mounted不会这样,比较好。
39.说说你对keep-alive的理解
keep-alive是一个抽象组件:它自身不会渲染一个DOM元素,也不会出现在父组件链中;使用keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
其有三个参数
include定义缓存白名单,会缓存的组件;
exclude定义缓存黑名单,不会缓存的组件;
以上两个参数可以是逗号分隔字符串、正则表达式或一个数组,include="a,b"、:include="/a|b/"、:include="['a', 'b']";
匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配;
max最多可以缓存多少组件实例。一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉;
不会在函数式组件中正常工作,因为它们没有缓存实例;
当组件在内被切换,它的activated和deactivated这两个生命周期钩子函数将会被对应执行。
40.v-if和v-for的优先级是什么?如果这两个同时出现时,那应该怎么优化才能得到更好的性能?
当它们处于同一节点,v-for的优先级比v-if更高,这意味着v-if将分别重复运行于每个v-for循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用。
<ul>
<li v-for="item in items" v-if="item.show">{{item}}</li>
</ul>
1
2
3
如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素 (或 <template>)上。
<ul v-if="items.length">
<li v-for="item in items">{{item}}</li>
</ul>
1
2
3
41.使用v-for遍历对象时,是按什么顺序遍历的?如何保证顺序?
按 Object.keys() 的顺序的遍历,转成数组保证顺序。
42.key除了在v-for中使用,还有什么作用?
还可以强制替换元素/组件而不是重复使用它。在以下场景可以使用
完整地触发组件的生命周期钩子
触发过渡
<transition>
<span :key="text">{{ text }}</span>
</transition>
1
2
3
当 text 发生改变时,<span>会随时被更新,因此会触发过渡。
43.使用key要什么要注意的吗?
不要使用对象或数组之类的非基本类型值作为key,请用字符串或数值类型的值;
不要使用数组的index作为key值,因为在删除数组某一项,index也会随之变化,导致key变化,渲染会出错。
例:在渲染[a,b,c]用 index 作为 key,那么在删除第二项的时候,index 就会从 0 1 2 变成 0 1(而不是 0 2),随之第三项的key变成1了,就会误把第三项删除了。
44.说说组件的命名规范
给组件命名有两种方式,一种是使用链式命名my-component,一种是使用大驼峰命名MyComponent,
在字符串模板中<my-component></my-component> 和 <MyComponent></MyComponent>都可以使用,
在非字符串模板中最好使用<MyComponent></MyComponent>,因为要遵循W3C规范中的自定义组件名
(字母全小写且必须包含一个连字符),避免和当前以及未来的 HTML 元素相冲突。
45.为什么组件中data必须用函数返回一个对象?
对象为引用类型,当重用组件时,由于数据对象都指向同一个data对象,当在一个组件中修改data时,其他重用的组件中的data会同时被修改;而使用返回对象的函数,由于每次返回的都是一个新对象(Object的实例),引用地址不同,则不会出现这个问题。
46.组件的name选项有什么作用?
递归组件时,组件调用自身使用;
用is特殊特性和component内置组件标签时使用;
keep-alive内置组件标签中include 和exclude属性中使用。
47.说下$attrs和$listeners的使用场景?
$attrs: 包含了父作用域中(组件标签)不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。 在创建基础组件时候经常使用,可以和组件选项inheritAttrs:false和配合使用在组件内部标签上用v-bind="$attrs"将非prop特性绑定上去;
$listeners: 包含了父作用域中(组件标签)的 (不含.native) v-on 事件监听器。 在组件上监听一些特定的事件,比如focus事件时,如果组件的根元素不是表单元素的,则监听不到,那么可以用v-on="$listeners"绑定到表单元素标签上解决。
48.EventBus注册在全局上时,路由切换时会重复触发事件,如何解决呢?
在有使用$on的组件中要在beforeDestroy钩子函数中用$off销毁。
49.Vue组件里写的原生addEventListeners监听事件,要手动去销毁吗?为什么?
要,不然会造成多次绑定和内存泄露。
50.Vue组件里的定时器要怎么销毁?
如果页面上有很多定时器,可以在data选项中创建一个对象timer,给每个定时器取个名字一一映射在对象timer中,在beforeDestroy构造函数中for(let k in this.timer){clearInterval(k)};
如果页面只有单个定时器,可以这么做。
const timer = setInterval(() =>{}, 500);
this.$once('hook:beforeDestroy', () => {
clearInterval(timer);
})
1
2
3
4
51.Vue中能监听到数组变化的方法有哪些?为什么这些方法能监听到呢?
push()、pop()、shift()、unshift()、splice()、sort()、reverse(),这些方法在Vue中被重新定义了,故可以监听到数组变化;
filter()、concat()、slice(),这些方法会返回一个新数组,也可以监听到数组的变化。
52.在Vue中哪些数组变化无法监听,为什么,怎么解决?
利用索引直接设置一个数组项时;
修改数组的长度时。
第一个情况,利用已有索引直接设置一个数组项时Object.defineProperty()是可以监听到,利用不存在的索引直接设置一个数组项时Object.defineProperty()是不可以监听到,但是官方给出的解释是由于JavaScript的限制,Vue不能检测以上数组的变动,其实根本原因是性能问题,性能代价和获得的用户体验收益不成正比。
第二个情况,原因是Object.defineProperty()不能监听到数组的length属性。
用this.$set(this.items, indexOfItem, newValue)或this.items.splice(indexOfItem, 1, newValue)来解决第一种情况;
用this.items.splice(newLength)来解决第二种情况。
53.在Vue中哪些对象变化无法监听,为什么,怎么解决?
对象属性的添加
对象属性的删除
因为Vue是通过Object.defineProperty来将对象的key转成getter/setter的形式来追踪变化,但getter/setter只能追踪一个数据是否被修改,无法追踪新增属性和删除属性,所以才会导致上面对象变化无法监听。
用this.$set(this.obj,"key","newValue")来解决第一种情况;
用Object.assign来解决第二种情况。
54.删除对象用delete和Vue.delete有什么区别?
delete:只是被删除对象成员变为' '或undefined,其他元素键值不变;
Vue.delete:直接删了对象成员,如果对象是响应式的,确保删除能触发更新视图,这个方法主要用于避开 Vue 不能检测到属性被删除的限制。
55.<template></template>有什么用?
当做一个不可见的包裹元素,减少不必要的DOM元素,整个结构会更加清晰。
56.Vue怎么定义全局方法
有三种
挂载在Vue的prototype上
// base.js
const install = function (Vue, opts) {
Vue.prototype.demo = function () {
console.log('我已经在Vue原型链上')
}
}
export default {
install
}
复制代码
1
2
3
4
5
6
7
8
9
10
//main.js
//注册全局函数
import base from 'service/base';
Vue.use(base);
复制代码
1
2
3
4
5
利用全局混入mixin
用this.$root.$on绑定方法,用this.$root.$off解绑方法,用this.$root.$emit全局调用。
this.$root.$on('demo',function(){
console.log('test');
})
this.$root.$emit('demo');
this.$root.$off('demo');
1
2
3
4
5
57.Vue怎么改变插入模板的分隔符?
用delimiters选项,其默认是["{{", "}}"]
// 将分隔符变成ES6模板字符串的风格
new Vue({
delimiters: ['${', '}']
})
1
2
3
4
58.Vue变量名如果以_、$开头的属性会发生什么问题?怎么访问到它们的值?
以 _ 或 $ 开头的属性 不会 被 Vue 实例代理,因为它们可能和 Vue 内置的属性、API 方法冲突,你可以使用例如 vm.$data._property 的方式访问这些属性。
59.怎么捕获Vue组件的错误信息?
errorCaptured是组件内部钩子,当捕获一个来自子孙组件的错误时被调用,接收error、vm、info三个参数,return false后可以阻止错误继续向上抛出。
errorHandler为全局钩子,使用Vue.config.errorHandler配置,接收参数与errorCaptured一致,2.6后可捕捉v-on与promise链的错误,可用于统一错误处理与错误兜底。
60.Vue.observable你有了解过吗?说说看
让一个对象可响应。可以作为最小化的跨组件状态存储器。
61.Vue项目中如何配置favicon?
静态配置 <link rel="icon" href="<%= BASE_URL %>favicon.ico">, 其中<%= BASE_URL %>等同vue.config.js中publicPath的配置;
动态配置
<link rel="icon" type="image/png" href="">
1
import browserImg from 'images/kong.png';//为favicon的默认图片
const imgurl ='后端传回来的favicon.ico的线上地址'
let link = document.querySelector('link[type="image/png"]');
if (imgurl) {
link.setAttribute('href', imgurl);
} else {
link.setAttribute('href', browserImg);
}
1
2
3
4
5
6
7
8
62.怎么修改Vue项目打包后生成文件路径?
在Vue CLI2中修改config/index.js文件中的build.assetsPublicPath的值;
在Vue CLI3中配置publicPath的值。
63.怎么解决Vue项目打包后静态资源图片失效的问题?
在项目中一般通过配置alias路径别名的方式解决,下面是Vue CLI3的配置。
configureWebpack: {
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
'assets': resolve('src/assets'),
'css': resolve('src/assets/css'),
'images': resolve('src/assets/images'),
}
},
},
1
2
3
4
5
6
7
8
9
10
11
64.怎么解决Vue中动态设置img的src不生效的问题?
因为动态添加src被当做静态资源处理了,没有进行编译,所以要加上require。
<template>
<img class="logo" :src="logo" alt="公司logo">
</template>
<script>
export default {
data() {
return {
logo:require("assets/images/logo.png"),
};
}
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
65.在Vue项目中如何引入第三方库(比如jQuery)?有哪些方法可以做到?
先在主入口页面 index.html 中用 script 标签引入<script src="./static/jquery-1.12.4.js"></script>,如果你的项目中有用ESLint检测,会报'$' is not defined,要在文件中加上/* eslint-disable */
先在主入口页面 index.html 中用 script 标签引入<script src="./static/jquery-1.12.4.js"></script>,然后在webpack 中配置一个 externals,即可在项目中使用。
externals: {
'jquery': 'jQuery'
}
1
2
3
先在webpack中配置alias,最后在main.js中用import $ from 'jquery',即可在项目中使用。
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
'jquery': resolve('static/jquery-1.12.4.js')
}
}
1
2
3
4
5
6
7
在webpack中新增一个plugins,即可在项目中使用
plugins: [
new webpack.ProvidePlugin({
$:"jquery",
jQuery:"jquery",
"windows.jQuery":"jquery"
})
]
1
2
3
4
5
6
7
66.说说你对SPA单页面的理解,它的优缺点分别是什么?
是一种只需要将单个页面加载到服务器之中的web应用程序。当浏览器向服务器发出第一个请求时,服务器会返回一个index.html文件,它所需的js,css等会在显示时统一加载,部分页面按需加载。url地址变化时不会向服务器在请求页面,通过路由才实现页面切换。
优点:
良好的交互体验,用户不需要重新刷新页面,获取数据也是通过Ajax异步获取,页面显示流畅;
良好的前后端工作分离模式。
缺点:
SEO难度较高,由于所有的内容都在一个页面中动态替换显示,所以在SEO上其有着天然的弱势。
首屏加载过慢(初次加载耗时多)
67.SPA单页面的实现方式有哪些?
在hash模式中,在window上监听hashchange事件(地址栏中hash变化触发)驱动界面变化;
在history模式中,在window上监听popstate事件(浏览器的前进或后退按钮的点击触发)驱动界面变化,监听a链接点击事件用history.pushState、history.replaceState方法驱动界面变化;
直接在界面用显示隐藏事件驱动界面变化。
68.说说你对Object.defineProperty的理解
Object.defineProperty(obj,prop,descriptor)方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
obj:要在其上定义属性的对象。
prop:要定义或修改的属性的名称。
descriptor:将被定义或修改的属性描述符。
descriptor属性描述符主要有两种形式:数据描述符和存取描述符。
描述符必须是这两种形式之一;不能同时是两者。
数据描述符和存取描述符共同拥有
configurable:特性表示对象的属性是否可以被删除,以及除value和writable特性外的其他特性是否可以被修改。默认为false。
enumerable:当该属性的enumerable为true时,该属性才可以在for…in循环和Object.keys()中被枚举。默认为false。
数据描述符
value:该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为undefined。
writable:当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为false。
存取描述符
get:一个给属性提供 getter的方法,如果没有getter则为undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。默认为undefined。
set:一个给属性提供 setter的方法,如果没有setter则为undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认为undefined。
定义descriptor时,最好先把这些属性都定义清楚,防止被继承和继承时出错。
function Archiver() {
var temperature = null;
var archive = [];
Object.defineProperty(this, 'temperature', {
get: function() {
console.log('get!');
return temperature;
},
set: function(value) {
temperature = value;
archive.push({ val: temperature });
}
});
this.getArchive = function() { return archive; };
}
var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
69.说说你对Proxy的理解
官方定义:proxy对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。
通俗来说是在对目标对象的操作之前提供了拦截,对外界的操作进行过滤和修改某些操作的默认行为,可以不直接操作对象本身,而是通过操作对象的代理对象来间接来操作对象。
let proxy = new Proxy(target, handler)
1
target 是用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理);
handler 一个对象,其属性是当执行一个操作时定义代理的行为的函数,也就是自定义的行为。
handle可以为{},但是不能为null,否则会报错
Proxy 目前提供了 13 种可代理操作,比较常用的
handler.get(target,property,receiver)获取值拦截
handler.set(target,property,value,receiver)设置值拦截
handler.has(target,prop)in 操作符拦截
let obj = {
a : 1,
b : 2
}
let test = new Proxy(obj,{
get : function (target,property) {
return property in target ? target[property] : 0
},
set : function (target,property,value) {
target[property] = 6;
},
has: function (target,prop){
if(prop == 'b'){
target[prop] = 6;
}
return prop in target;
},
})
console.log(test.a); // 1
console.log(test.c); // 0
test.a = 3;
console.log(test.a) // 6
if('b' in test){
console.log(test) // Proxy {a: 6, b: 6}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
70.Object.defineProperty和Proxy的区别
Object.defineProperty
不能监听到数组length属性的变化;
不能监听对象的添加;
只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。
Proxy
可以监听数组length属性的变化;
可以监听对象的添加;
可代理整个对象,不需要对对象进行遍历,极大提高性能;
多达13种的拦截远超Object.defineProperty只有get和set两种拦截。
71.Vue的模板语法用的是哪个web模板引擎的吗?说说你对这模板引擎的理解?
采用的是Mustache的web模板引擎mustache.js
<script type="text/javascript" src="./mustache.js"></script>
<script type="text/javascript">
var data = {
"company": "Apple",
}
var tpl = '<h1>Hello {{company}}</h1>';
var html = Mustache.render(tpl, data);
console.log(html);
</script>
1
2
3
4
5
6
7
8
9
10
11
72.你认为Vue的核心是什么?
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统。
73.说说你对单向数据流和双向数据流的理解
单向数据流是指数据只能从父级向子级传递数据,子级不能改变父级向子级传递的数据。
双向数据流是指数据从父级向子级传递数据,子级可以通过一些手段改变父级向子级传递的数据。
比如用v-model、.sync来实现双向数据流。
74.什么是虚拟DOM?
虚拟DOM是将状态映射成视图的众多解决方案中的一种,其是通过状态生成一个虚拟节点树,然后使用虚拟节点树进行渲染生成真实DOM,在渲染之前,会使用新生成的虚拟节点树和上一次虚拟节点树进行对比,只渲染不同的部分。
75.Vue中如何实现一个虚拟DOM?说说你的思路
首先要构建一个VNode的类,DOM元素上的所有属性在VNode类实例化出来的对象上都存在对应的属性。例如tag表示一个元素节点的名称,text表示一个文本节点的文本,chlidren表示子节点等。将VNode类实例化出来的对象进行分类,例如注释节点、文本节点、元素节点、组件节点、函数式节点、克隆节点。
然后通过编译将模板转成渲染函数render,执行渲染函数render,在其中创建不同类型的VNode类,最后整合就可以得到一个虚拟DOM(vnode)。
最后通过patch将vnode和oldVnode进行比较后,生成真实DOM。
76.Vue为什么要求组件模板只能有一个根元素?
当前的virtualDOM差异和diff算法在很大程度上依赖于每个子组件总是只有一个根元素。
77.axios是什么?怎样使用它?怎么解决跨域的问题?
axios 是一个基于 promise 的 HTTP 库,先封装在使用。
使用proxyTable配置解决跨域问题。
比如你要调用http://172.16.13.205:9011/getList这个接口
先在axios.create()配置baseURL增加标志
const service = axios.create({
baseURL: '/api',
});
复制代码
service.get(getList, {params:data});
1
2
3
4
5
然后在config/index.js文件中配置
dev:{
proxyTable: {
'/api': {
target: 'http://172.16.13.205:9011', // 设置你调用的接口域名和端口号
secure: false,
changeOrigin: true,// 跨域
pathRewrite: {
'^/api': '' // 去掉标志
}
}
},
}
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
配置后要重新npm run dev
F12中看到请求是http://localhost:8080/api/getList,实际上请求是http://172.16.13.205:9011/getList。
78.如果想扩展某个现有的Vue组件时,怎么做呢?
用mixins混入
用extends,比mixins先触发
用高阶组件HOC封装
79.vue-loader是什么?它有什么作用?
vue-loader是一个webpack的loader,是一个模块转换器,用于把模块原内容按照需求转换成新内容。
它允许你以一种名为单文件组件 (SFCs)的格式撰写 Vue 组件。可以解析和转换 .vue 文件,提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模版 template,再分别把它们交给对应的loader去处理。
80.你有使用过JSX吗?说说你对JSX的理解?
JSX就是Javascript和XML结合的一种格式。React发明了JSX,利用HTML语法来创建虚拟DOM。当遇到<,JSX就当HTML解析,遇到{就当JavaScript解析。
Vuex
1.什么是Vuex?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理插件。它采用集中式存储管理应用的所有组件的状态,而更改状态的唯一方法是提交mutation,例this.$store.commit('SET_VIDEO_PAUSE', video_pause,SET_VIDEO_PAUSE为mutations属性中定义的方法 。
2.Vuex解决了什么问题?
解决两个问题
多个组件依赖于同一状态时,对于多层嵌套的组件的传参将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
来自不同组件的行为需要变更同一状态。以往采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
3.什么时候用Vuex?
当项目遇到以下两种场景时
多个组件依赖于同一状态时。
来自不同组件的行为需要变更同一状态。
4.怎么引用Vuex?
先安装依赖nnpm install vuex --save
在项目目录src中建立store文件夹
在store文件夹下新建index.js文件,写入
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
//不是在生产环境debug为true
const debug = process.env.NODE_ENV !== 'production';
//创建Vuex实例对象
const store = new Vuex.Store({
strict:debug,//在不是生产环境下都开启严格模式
state:{
},
getters:{
},
mutations:{
},
actions:{
}
})
export default store;
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
然后再main.js文件中引入Vuex,这么写
import Vue from 'vue';
import App from './App.vue';
import store from './store';
const vm = new Vue({
store:store,
render: h => h(App)
}).$mount('#app')
1
2
3
4
5
6
7
5.Vuex的5个核心属性是什么?
分别是 state、getters、mutations、actions、modules 。
6.Vuex中状态储存在哪里,怎么改变它?
存储在state中,改变Vuex中的状态的唯一途径就是显式地提交 (commit) mutation。
7.Vuex中状态是对象时,使用时要注意什么?
因为对象是引用类型,复制后改变属性还是会影响原始数据,这样会改变state里面的状态,是不允许,所以先用深度克隆复制对象,再修改。
8.怎么在组件中批量使用Vuex的state状态?
使用mapState辅助函数, 利用对象展开运算符将state混入computed对象中
import {mapState} from 'vuex'
export default{
computed:{
...mapState(['price','number'])
}
}
1
2
3
4
5
6
9.Vuex中要从state派生一些状态出来,且多个组件使用它,该怎么做?
使用getter属性,相当Vue中的计算属性computed,只有原状态改变派生状态才会改变。
getter接收两个参数,第一个是state,第二个是getters(可以用来访问其他getter)。
const store = new Vuex.Store({
state: {
price: 10,
number: 10,
discount: 0.7,
},
getters: {
total: state => {
return state.price * state.number
},
discountTotal: (state, getters) => {
return state.discount * getters.total
}
},
});
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
然后在组件中可以用计算属性computed通过this.$store.getters.total这样来访问这些派生转态。
computed: {
total() {
return this.$store.getters.total
},
discountTotal() {
return this.$store.getters.discountTotal
}
}
1
2
3
4
5
6
7
8
10.怎么通过getter来实现在组件内可以通过特定条件来获取state的状态?
通过让getter返回一个函数,来实现给getter传参。然后通过参数来进行判断从而获取state中满足要求的状态。
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
getTodoById: (state) => (id) =>{
return state.todos.find(todo => todo.id === id)
}
},
});
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
然后在组件中可以用计算属性computed通过this.$store.getters.getTodoById(2)这样来访问这些派生转态。
computed: {
getTodoById() {
return this.$store.getters.getTodoById
},
}
mounted(){
console.log(this.getTodoById(2).done)//false
}
1
2
3
4
5
6
7
8
11.怎么在组件中批量使用Vuex的getter属性
使用mapGetters辅助函数, 利用对象展开运算符将getter混入computed 对象中
import {mapGetters} from 'vuex'
export default{
computed:{
...mapGetters(['total','discountTotal'])
}
}
1
2
3
4
5
6
12.怎么在组件中批量给Vuex的getter属性取别名并使用
使用mapGetters辅助函数, 利用对象展开运算符将getter混入computed 对象中
import {mapGetters} from 'vuex'
export default{
computed:{
...mapGetters({
myTotal:'total',
myDiscountTotal:'discountTotal',
})
}
}
1
2
3
4
5
6
7
8
9
13.在Vuex中使用mutation要注意什么。
mutation 必须是同步函数
14.Vuex中action和mutation有什么区别?
action 提交的是 mutation,而不是直接变更状态。mutation可以直接变更状态。
action 可以包含任意异步操作。mutation只能是同步操作。
提交方式不同,action 是用this.$store.dispatch('ACTION_NAME',data)来提交。mutation是用this.$store.commit('SET_NUMBER',10)来提交。
接收参数不同,mutation第一个参数是state,而action第一个参数是context,其包含了
{
state, // 等同于 `store.state`,若在模块中则为局部状态
rootState, // 等同于 `store.state`,只存在于模块中
commit, // 等同于 `store.commit`
dispatch, // 等同于 `store.dispatch`
getters, // 等同于 `store.getters`
rootGetters // 等同于 `store.getters`,只存在于模块中
}
1
2
3
4
5
6
7
8
15.Vuex中action和mutation有什么相同点?
第二参数都可以接收外部提交时传来的参数。 this.$store.dispatch('ACTION_NAME',data)和this.$store.commit('SET_NUMBER',10)
16.在组件中多次提交同一个action,怎么写使用更方便。
使用mapActions辅助函数,在组件中这么使用
methods:{
...mapActions({
setNumber:'SET_NUMBER',
})
}
1
2
3
4
5
然后调用this.setNumber(10)相当调用this.$store.dispatch('SET_NUMBER',10)
17.Vuex中action通常是异步的,那么如何知道action什么时候结束呢?
在action函数中返回Promise,然后再提交时候用then处理
actions:{
SET_NUMBER_A({commit},data){
return new Promise((resolve,reject) =>{
setTimeout(() =>{
commit('SET_NUMBER',10);
resolve();
},2000)
})
}
}
this.$store.dispatch('SET_NUMBER_A').then(() => {
// ...
})
1
2
3
4
5
6
7
8
9
10
11
12
13
18.Vuex中有两个action,分别是actionA和actionB,其内都是异步操作,在actionB要提交actionA,需在actionA处理结束再处理其它操作,怎么实现?
利用ES6的async和await来实现。
actions:{
async actionA({commit}){
//...
},
async actionB({dispatch}){
await dispatch ('actionA')//等待actionA完成
// ...
}
}
1
2
3
4
5
6
7
8
9
19.有用过Vuex模块吗,为什么要使用,怎么使用。
有,因为使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。所以将 store 分割成模块(module)。每个模块拥有自己的 state、mutations、actions、getters,甚至是嵌套子模块,从上至下进行同样方式的分割。
在module文件新建moduleA.js和moduleB.js文件。在文件中写入
const state={
//...
}
const getters={
//...
}
const mutations={
//...
}
const actions={
//...
}
export default{
state,
getters,
mutations,
actions
}
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
然后再index.js引入模块
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
import moduleA from './module/moduleA'
import moduleB from './module/moduleB'
const store = new Vuex.Store({
modules:{
moduleA,
moduleB
}
})
export default store
1
2
3
4
5
6
7
8
9
10
11
12
20.在模块中,getter和mutation接收的第一个参数state,是全局的还是模块的?
第一个参数state是模块的state,也就是局部的state。
21.在模块中,getter和mutation和action中怎么访问全局的state和getter?
在getter中可以通过第三个参数rootState访问到全局的state,可以通过第四个参数rootGetters访问到全局的getter。
在mutation中不可以访问全局的satat和getter,只能访问到局部的state。
在action中第一个参数context中的context.rootState访问到全局的state,context.rootGetters访问到全局的getter。
22.在组件中怎么访问Vuex模块中的getter和state,怎么提交mutation和action?
直接通过this.$store.getters和this.$store.state来访问模块中的getter和state。
直接通过this.$store.commit('mutationA',data)提交模块中的mutation。
直接通过this.$store.dispatch('actionA,data')提交模块中的action。
23.用过Vuex模块的命名空间吗?为什么使用,怎么使用。
默认情况下,模块内部的action、mutation和getter是注册在全局命名空间,如果多个模块中action、mutation的命名是一样的,那么提交mutation、action时,将会触发所有模块中命名相同的mutation、action。
这样有太多的耦合,如果要使你的模块具有更高的封装度和复用性,你可以通过添加namespaced: true 的方式使其成为带命名空间的模块。
export default{
namespaced: true,
state,
getters,
mutations,
actions
}
1
2
3
4
5
6
7
24.怎么在带命名空间的模块内提交全局的mutation和action?
将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。
this.$store.dispatch('actionA', null, { root: true })
this.$store.commit('mutationA', null, { root: true })
1
2
25.怎么在带命名空间的模块内注册全局的action?
actions: {
actionA: {
root: true,
handler (context, data) { ... }
}
}
1
2
3
4
5
6
26.怎么使用mapState,mapGetters,mapActions和mapMutations这些函数来绑定带命名空间的模块?
首先使用createNamespacedHelpers创建基于某个命名空间辅助函数
import { createNamespacedHelpers } from 'vuex';
const { mapState, mapActions } = createNamespacedHelpers('moduleA');
export default {
computed: {
// 在 `module/moduleA` 中查找
...mapState({
a: state => state.a,
b: state => state.b
})
},
methods: {
// 在 `module/moduleA` 中查找
...mapActions([
'actionA',
'actionB'
])
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
27.Vuex插件有用过吗?怎么用简单介绍一下?
Vuex插件就是一个函数,它接收 store 作为唯一参数。在Vuex.Store构造器选项plugins引入。 在store/plugin.js文件中写入
export default function createPlugin(param){
return store =>{
//...
}
}
复制代码
1
2
3
4
5
6
然后在store/index.js文件中写入
import createPlugin from './plugin.js'
const myPlugin = createPlugin()
const store = new Vuex.Store({
// ...
plugins: [myPlugin]
})
1
2
3
4
5
6
28.在Vuex插件中怎么监听组件中提交mutation和action?
用Vuex.Store的实例方法subscribe监听组件中提交mutation
用Vuex.Store的实例方法subscribeAction监听组件中提交action 在store/plugin.js文件中写入
export default function createPlugin(param) {
return store => {
store.subscribe((mutation, state) => {
console.log(mutation.type)//是那个mutation
console.log(mutation.payload)
console.log(state)
})
// store.subscribeAction((action, state) => {
// console.log(action.type)//是那个action
// console.log(action.payload)//提交action的参数
// })
store.subscribeAction({
before: (action, state) => {//提交action之前
console.log(`before action ${action.type}`)
},
after: (action, state) => {//提交action之后
console.log(`after action ${action.type}`)
}
})
}
}
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
然后在store/index.js文件中写入
import createPlugin from './plugin.js'
const myPlugin = createPlugin()
const store = new Vuex.Store({
// ...
plugins: [myPlugin]
})
1
2
3
4
5
6
29.在v-model上怎么用Vuex中state的值?
需要通过computed计算属性来转换。
<input v-model="message">
// ...
computed: {
message: {
get () {
return this.$store.state.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
30.Vuex的严格模式是什么,有什么作用,怎么开启?
在严格模式下,无论何时发生了状态变更且不是由 mutation函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
在Vuex.Store 构造器选项中开启,如下
const store = new Vuex.Store({
strict:true,
})
1
2
3
Vue-Router
1.怎么重定向页面?
第一种方法:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
1
2
3
4
5
第二种方法:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
1
2
3
4
5
第三种方法:
const router = new VueRouter({
routes: [
{
path: '/a',
redirect: to =>{
const { hash, params, query } = to
if (query.to === 'foo') {
return { path: '/foo', query: null }
}else{
return '/b'
}
}
}
]
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2.怎么配置404页面?
const router = new VueRouter({
routes: [
{
path: '*', redirect: {path: '/'}
}
]
})
1
2
3
4
5
6
7
3.切换路由时,需要保存草稿的功能,怎么实现呢?
<keep-alive :include="include">
<router-view></router-view>
</keep-alive>
1
2
3
4.路由有几种模式?说说它们的区别?
hash: 兼容所有浏览器,包括不支持 HTML5 History Api 的浏览器,例http://www.abc.com/#/index,hash值为#/index, hash的改变会触发hashchange事件,通过监听hashchange事件来完成操作实现前端路由。hash值变化不会让浏览器向服务器请求。
// 监听hash变化,点击浏览器的前进后退会触发
window.addEventListener('hashchange', function(event){
let newURL = event.newURL; // hash 改变后的新 url
let oldURL = event.oldURL; // hash 改变前的旧 url
},false)
1
2
3
4
5
history: 兼容能支持 HTML5 History Api 的浏览器,依赖HTML5 History API来实现前端路由。没有#,路由地址跟正常的url一样,但是初次访问或者刷新都会向服务器请求,如果没有请求到对应的资源就会返回404,所以路由地址匹配不到任何静态资源,则应该返回同一个index.html 页面,需要在nginx中配置。
abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。
5.讲一下完整的导航守卫流程?
导航被触发。
在失活的组件里调用离开守卫beforeRouteLeave(to,from,next)。
调用全局的beforeEach( (to,from,next) =>{} )守卫。
在重用的组件里调用 beforeRouteUpdate(to,from,next) 守卫。
在路由配置里调用beforeEnter(to,from,next)路由独享的守卫。
解析异步路由组件。
在被激活的组件里调用beforeRouteEnter(to,from,next)。
在所有组件内守卫和异步路由组件被解析之后调用全局的beforeResolve( (to,from,next) =>{} )解析守卫。
导航被确认。
调用全局的afterEach( (to,from) =>{} )钩子。
触发 DOM 更新。
用创建好的实例调用beforeRouteEnter守卫中传给 next 的回调函数
beforeRouteEnter(to, from, next) {
next(vm => {
//通过vm访问组件实例
})
},
1
2
3
4
5
6.讲一下导航守卫的三个参数的含义?
to:即将要进入的目标 路由对象。
from:当前导航正要离开的路由对象。
next:函数,必须调用,不然路由跳转不过去。
next():进入下一个路由。
next(false):中断当前的导航。
next('/')或next({ path: '/' }) : 跳转到其他路由,当前导航被中断,进行新的一个导航。
7.在afterEach钩子中可以使用next()吗?
不可以,不接受next的参数。
8.全局导航守卫有哪些?怎么使用?
router.beforeEach:全局前置守卫。
router.beforeResolve:全局解析守卫。
router.afterEach:全局后置钩子。
import VueRouter from 'vue-router';
const router = new VueRouter({
mode: 'history',
base: '/',
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition;
} else {
return { x: 0, y: 0 };
}
}
})
router.beforeEach((to, from, next) => {
//...
next();
})
router.beforeResolve((to, from, next) => {
//...
next();
})
router.afterEach((to, from) => {
//...
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
9.什么是路由独享的守卫,怎么使用?
是beforeEnter守卫
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
1
2
3
4
5
6
7
8
9
10
11
10.说说你对router-link的了解
<router-link>是Vue-Router的内置组件,在具有路由功能的应用中作为声明式的导航使用。
<router-link>有8个props,其作用是:
to
:必填,表示目标路由的链接。当被点击后,内部会立刻把to的值传到 router.push(),所以这个值可以是一个字符串或者是描述目标位置的对象。
<router-link to="home">Home</router-link>
<router-link :to="'home'">Home</router-link>
<router-link :to="{ path: 'home' }">Home</router-link>
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
<router-link :to="{ path: 'user', query: { userId: 123 }}">User</router-link>
复制代码
1
2
3
4
5
6
注意path存在时params不起作用,只能用query
replace:默认值为false,若设置的话,当点击时,会调用router.replace()而不是router.push(),于是导航后不会留下 history 记录。
append:设置 append 属性后,则在当前 (相对) 路径前添加基路径。
tag:让<router-link>渲染成tag设置的标签,如tag:'li,渲染结果为<li>foo</li>。
active-class:默认值为router-link-active,设置链接激活时使用的 CSS 类名。默认值可以通过路由的构造选项 linkActiveClass 来全局配置。
exact-active-class:默认值为router-link-exact-active,设置链接被精确匹配的时候应该激活的 class。默认值可以通过路由构造函数选项 linkExactActiveClass 进行全局配置的。
exact:是否精确匹配,默认为false。
<!-- 这个链接只会在地址为 / 的时候被激活 -->
<router-link to="/" exact></router-link>
复制代码
1
2
3
event:声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组,默认是click。
11.怎么在组件中监听路由参数的变化?
有两种方法可以监听路由参数的变化,但是只能用在包含<router-view />的组件内。
第一种
watch: {
'$route'(to, from) {
//这里监听
},
},
复制代码
1
2
3
4
5
6
第二种
beforeRouteUpdate (to, from, next) {
//这里监听
},
1
2
3
12.切换路由后,新页面要滚动到顶部或保持原先的滚动位置怎么做呢?
滚动顶部
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition;
} else {
return { x: 0, y: 0 };
}
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13.路由组件和路由为什么解耦,怎么解耦?
因为在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性,所有要解耦。
耦合如以下代码所示。Home组件只有在
http://localhost:8036/home/123
1
URL上才能使用。
const Home = {
template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/home/:id', component: Home }
]
})
复制代码
1
2
3
4
5
6
7
8
9
使用 props 来解耦
props为true,route.params将会被设置为组件属性。
props为对象,则按原样设置为组件属性。
props为函数,http://localhost:8036/home?id=123,会把123传给组件Home的props的id。
const Home = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/home/:id', component: Home, props: true},
// 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
{
path: '/home/:id',
components: { default: Home, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
{ path: '/home', component: Home, props: {id:123} },
{ path: '/home', component: Home, props: (route) => ({ id: route.query.id }) },
]
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
14.说说active-class是哪个组件的属性?
<router-link/>组件的属性,设置链接激活时使用的 CSS 类名。默认值可以通过路由的构造选项 linkActiveClass 来全局配置。
15.怎样动态加载路由?
使用Router的实例方法addRoutes来实现动态加载路由,一般用来实现菜单权限。
使用时要注意,静态路由文件中不能有404路由,而要通过addRoutes一起动态添加进去。
const routes = [
{
path: '/overview',
name: 'overview',
component: () => import('@/views/account/overview/index'),
meta: {
title: '账户概览',
pid: 869,
nid: 877
},
},
{
path: '*',
redirect: {
path: '/'
}
}
]
vm.$router.options.routes.push(...routes);
vm.$router.addRoutes(routes);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
16.怎么实现路由懒加载呢?
function load(component) {
//return resolve => require([`views/${component}`], resolve);
return () => import(`views/${component}`);
}
const routes = [
{
path: '/home',
name: 'home',
component: load('home'),
meta: {
title: '首页'
},
},
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
17.路由之间是怎么跳转的?有哪些方式?
声明式 通过使用内置组件<router-link :to="/home">来跳转
编程式 通过调用router实例的push方法router.push({ path: '/home' })或replace方法router.replace({ path: '/home' })
18.如果vue-router使用history模式,部署时要注意什么?
要注意404的问题,因为在history模式下,只是动态的通过js操作window.history来改变浏览器地址栏里的路径,并没有发起http请求,当直接在浏览器里输入这个地址的时候,就一定要对服务器发起http请求,但是这个目标在服务器上又不存在,所以会返回404。
所以要在Ngnix中将所有请求都转发到index.html上就可以了。
location / {
try_files $uri $uri/ @router index index.html;
}
location @router {
rewrite ^.*$ /index.html last;
}
1
2
3
4
5
6
19.route和router有什么区别?
route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。 而router是“路由实例对象”,包括了路由的跳转方法,钩子函数等。
20.Vue路由怎么跳转打开新窗口?
const obj = {
path: xxx,//路由地址
query: {
mid: data.id//可以带参数
}
};
const {href} = this.$router.resolve(obj);
window.open(href, '_blank');
————————————————
版权声明:本文为CSDN博主「萌萌哒の瑞萌萌」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_46232841/article/details/111332225