阅读量:0
面试题 81 . 请简述Vue更新数组时触发视图更新的方法?
参考回答:
push();pop();shift();unshift();splice();sort();reverse()
面试题 82 . 简述如何使用Vue-router实现懒加载的方式?
参考回答:
vue异步组件 vue异步组件技术 ==== 异步加载 vue-router配置路由 , 使用vue的异步组件技术 , 可以实现按需加载 。但是,这种情况下一个组件生成一个js文件 /* vue异步组件技术 */ { path: '/home', name: 'home', component: resolve => require(['@/components/home'],resolve) },{ path: '/index', name: 'Index', component: resolve => require(['@/components/index'],resolve) },{ path: '/about', name: 'about', component: resolve => require(['@/components/about'],resolve) } es提案的import() 路由懒加载(使用import) // 下面2行代码,没有指定webpackChunkName,每个组件打包成一个js文件。 /* const Home = () => import('@/components/home') const Index = () => import('@/components/index') const About = () => import('@/components/about') */ // 下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。把组件按组分块 const Home = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/home') const Index = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/index') const About = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '@/components/about') { path: '/about', component: About }, { path: '/index', component: Index }, { path: '/home', component: Home } webpack的require,ensure() vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。 /* 组件懒加载方案三: webpack提供的require.ensure() */ { path: '/home', name: 'home', component: r => require.ensure([], () => r(require('@/components/home')), 'demo') }, { path: '/index', name: 'Index', component: r => require.ensure([], () => r(require('@/components/index')), 'demo') }, { path: '/about', name: 'about', component: r => require.ensure([], () => r(require('@/components/about')), 'demo-01') }
面试题 83 . Vue中delete和Vue.delete删除数组的区别 ?
参考回答:
delete只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。Vue.delete 直接删除了数组 改变了数组的键值
面试题 84 . 说明对于Vue e m i t 、 emit 、 emit、on 、 o n c e 、 once 、 once、off理解?
参考回答:
$emit 触发当前实例上的自定义事件(并将附加参数都传给监听器回调) $on 监听实例上自定义事件并调用回调函数,监听emit触发的事件 $once 监听一个自定义事件,但是只触发一次,在第一次触发之后移除监听器。 $off 用来移除自定义事件监听器。如果没有提供参数,则移除所有的事件监听器;如果只提供了事件,则移除该事件所有的监听器;如果同时提供了事件与回调,则只移除这个回调的监听器。 这四个方法的实现原理是:通过对vue实例挂载,然后分别使用对象存储数组对应的函数事件,其中emit通过循环查找存储的数组中对应的函数进行调用,once只匹配一次就就结束,on是将对应的函数存储到数组中,off是删除数组中指定的元素或者所有的元素事件。具体可以参考文章:VUEemit实现
面试题 85 . 请说明Vue中 r o o t 、 root、 root、refs、$parent的使用 ?
参考回答:
$root 可以用来获取vue的根实例,比如在简单的项目中将公共数据放再vue根实例上(可以理解为一个全局 store ),因此可以代替vuex实现状态管理; $refs 在子组件上使用ref特性后,this.属性可以直接访问该子组件。可以代替事件emit 和$on 的作用。 使用方式是通过ref特性为这个子组件赋予一个ID引用,再通过this.$refs.testId获取指定元素。 注意:$refs只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问$refs。 $parent $parent属性可以用来从一个子组件访问父组件的实例,可以替代将数据以 prop 的方式传入子组件的方式;当变更父级组件的数据的时候,容易造成调试和理解难度增加;
面试题 86 . Vue路由的使用以及如何解决在router-link上添加点击事件无效的情况 ?
参考回答:
使用@click.native。原因:router-link会阻止click事件,.native指直接监听一个原生事件
面试题 87 . 简述v-el 作用是什么以及Vue的el属性和$mount优先级?
参考回答:
v-el 提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例。 new Vue({ router, store, el: '#app', render: h => h(App) }).$mount('#div') /*当出现上面的情况就需要对el和$mount优先级进行判断, el的优先级是高于$mount的,因此以el挂载节点为准*/
面试题 88 . SPA首屏加载速度慢的怎么解决?
参考回答:
请求优化:CDN 将第三方的类库放到 CDN 上,能够大幅度减少生产环境中的项目体积,另外 CDN 能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。 缓存:将长时间不会改变的第三方类库或者静态资源设置为强缓存,将 max-age 设置为一个非常长的时间,再将访问路径加上哈希达到哈希值变了以后保证获取到最新资源,好的缓存策略有助于减轻服务器的压力,并且显著的提升用户的体验 gzip:开启 gzip 压缩,通常开启 gzip 压缩能够有效的缩小传输资源的大小。 http2:如果系统首屏同一时间需要加载的静态资源非常多,但是浏览器对同域名的 tcp 连接数量是有限制的(chrome 为 6 个)超过规定数量的 tcp 连接,则必须要等到之前的请求收到响应后才能继续发送,而 http2 则可以在多个 tcp 连接中并发多个请求没有限制,在一些网络较差的环境开启 http2 性能提升尤为明显。 懒加载:当 url 匹配到相应的路径时,通过 import 动态加载页面组件,这样首屏的代码量会大幅减少,webpack 会把动态加载的页面组件分离成单独的一个 chunk.js 文件 预渲染:由于浏览器在渲染出页面之前,需要先加载和解析相应的 html、css 和 js 文件,为此会有一段白屏的时间,可以添加loading,或者骨架屏幕尽可能的减少白屏对用户的影响体积优化 合理使用第三方库:对于一些第三方 ui 框架、类库,尽量使用按需加载,减少打包体积 使用可视化工具分析打包后的模块体积:webpack-bundle- analyzer 这个插件在每次打包后能够更加直观的分析打包后模块的体积,再对其中比较大的模块进行优化 提高代码使用率:利用代码分割,将脚本中无需立即调用的代码在代码构建时转变为异步加载的过程 封装:构建良好的项目架构,按照项目需求就行全局组件,插件,过滤器,指令,utils 等做一 些公共封装,可以有效减少我们的代码量,而且更容易维护资源优化 图片懒加载:使用图片懒加载可以优化同一时间减少 http 请求开销,避免显示图片导致的画面抖动,提高用户体验使用 svg 图标:相对于用一张图片来表示图标,svg 拥有更好的图片质量,体积更小,并且不需要开启额外的 http 请求 压缩图片:可以使用 image-webpack-loader,在用户肉眼分辨不清的情况下一定程度上压缩图片
面试题 89 . 简述Vue初始化过程中(new Vue(options))都做了什么?
参考回答:
处理组件配置项;初始化根组件时进行了选项合并操作,将全局配置合并到根组件的局部配置上;初始化每个子组件时做了一些性能优化,将组件配置对象上的一些深层次属性放到 vm.$options 选项中,以提高代码的执行效率; 初始化组件实例的关系属性,比如 p a r e n t 、 parent、 parent、children、 r o o t 、 root、 root、refs 等 处理自定义事件 调用 beforeCreate 钩子函数 初始化组件的 inject 配置项,得到 ret[key] = val 形式的配置对象,然后对该配置对象进行响应式处理,并代理每个 key 到 vm 实例上 数据响应式,处理 props、methods、data、computed、watch 等选项 解析组件配置项上的 provide 对象,将其挂载到 vm._provided 属性上 调用 created 钩子函数 如果发现配置项上有 el 选项,则自动调用 $mount 方法,也就是说有了 el 选项,就不需要再手动调用 $mount 方法,反之,没提供 el 选项则必须调用 $mount 接下来则进入挂载阶段 // core/instance/init.js export function initMixin (Vue: Class) { Vue.prototype._init = function (options?: Object) { const vm: Component = this vm._uid = uid++ // 如果是Vue的实例,则不需要被observe vm._isVue = true if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options) } else { vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) } if (process.env.NODE_ENV !== 'production') { initProxy(vm) } else { vm._renderProxy = vm } vm._self = vm initLifecycle(vm) initEvents(vm) callHook(vm, 'beforeCreate') initInjections(vm) // resolve injections before data/props initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, 'created') if (vm.$options.el) { vm.$mount(vm.$options.el) } } }
面试题 90 . Vue3.0 里为什么要用 Proxy API替代 defineProperty API?
参考回答:
1.defineProperty API 的局限性最大原因是它只能针对单例属性做监听。 Vue2.x中的响应式实现正是基于defineProperty中的descriptor,对 data 中的属性做了遍历 + 递归,为每个属性设置了 getter、setter。这也就是为什么 Vue 只能对 data 中预定义过的属性做出响应的原因。 2.Proxy API的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作, 这就完全可以代理所有属性,将会带来很大的性能提升和更优的代码。 Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。 3.响应式是惰性的。 在 Vue.js 2.x 中,对于一个深层属性嵌套的对象,要劫持它内部深层次的变化,就需要递归遍历这个对象,执行 Object.defineProperty 把每一层对象数据都变成响应式的,这无疑会有很大的性能消耗。 在 Vue.js 3.0 中,使用 Proxy API 并不能监听到对象内部深层次的属性变化,因此它的处理方式是在 getter 中去递归响应式,这样的好处是真正访问到的内部属性才会变成响应式,简单的可以说是按需实现响应式,减少性能消耗
面试题 91 . 简述Proxy 与 Object.defineProperty 优劣对比?
参考回答:
1.Proxy 可以直接监听对象而非属性; 2.Proxy 可以直接监听数组的变化; 3.Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的; 4.Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改; 5.Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利; 6.Object.defineProperty 的优势如下: 兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。
面试题 92 . Vue中data的属性可以和methods中方法同名吗,为什么?
参考回答:
可以同名,methods的方法名会被data的属性覆盖;调试台也会出现报错信息,但是不影响执行; 原因:源码定义的initState函数内部执行的顺序:props>methods>data>computed>watch //initState部分源码 export function initState (vm: Component) { vm._watchers = [] const opts = vm.$options if (opts.props) initProps(vm, opts.props) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { initData(vm) } else { observe(vm._data = {}, true /* asRootData */) } if (opts.computed) initComputed(vm, opts.computed) if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) } }
面试题 93 . 请解释 vm.$set(obj, key, val) ?
参考回答:
由于 Vue 无法探测对象新增属性或者通过索引为数组新增一个元素,所以这才有了 vm. s e t ,它是 V u e . s e t 的别名。 v m . set,它是 Vue.set 的别名。 vm. set,它是Vue.set的别名。vm.set 用于向响应式对象添加一个新的 property,并确保这个新的 property 同样是响应式的,并触发视图更新。 为对象添加一个新的响应式数据:调用 defineReactive 方法为对象增加响应式数据,然后执行 dep.notify 进行依赖通知,更新视图 为数组添加一个新的响应式数据:通过 splice 方法实现
面试题 94 . Vue中created与mounted区别 ?
参考回答:
在created阶段,实例已经被初始化,但是还没有挂载至el上,所以我们无法获取到对应的节点,但是此时我们是可以获取到vue中data与methods中的数据的; 在mounted阶段,vue的template成功挂载在$el中,此时一个完整的页面已经能够显示在浏览器中,所以在这个阶段,可以调用节点了; //以下为测试vue部分生命函数,便于理解 beforeCreate(){ //创建前 console.log('beforecreate:',document.getElementById('first'))//null console.log('data:',this.text);//undefined this.sayHello();//error:not a function }, created(){ //创建后 console.log('create:',document.getElementById('first'))//null console.log('data:',this.text);//this.text this.sayHello();//this.sayHello() }, beforeMount(){ //挂载前 console.log('beforeMount:',document.getElementById('first'))//null console.log('data:',this.text);//this.text this.sayHello();//this.sayHello() }, mounted(){ //挂载后 console.log('mounted:',document.getElementById('first'))// console.log('data:',this.text);//this.text this.sayHello();//this.sayHello() }
面试题 95 . 简述Vuex和单纯的全局对象有什么区别?
参考回答:
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。 不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
面试题 96 . 为什么 Vuex 的 mutation 中不能做异步操作?
参考回答:
每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现 time-travel 了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难 Mutation 必须是同步函数一条重要的原则就是要记住 mutation 必须是同步函数。为什么?请参考下面的例子: mutations: { someMutation (state) { api.callAsyncMethod(() => { state.count++ }) }} 现在想象,我们正在 debug 一个 app 并且观察 devtool 中的 mutation 日志。 每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。 然而,在上面的例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。 在组件中提交 Mutation 你可以在组件中使用 this.$store.commit('xxx') 提交mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为store.commit 调用(需要在根节点注入 store)。 import { mapMutations } from 'vuex'export default { // ... methods: { ...mapMutations([ 'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')` // `mapMutations` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为`this.$store.commit('incrementBy', amount)` ]), ...mapMutations({ add: 'increment' // 将 `this.add()` 映射为`this.$store.commit('increment')` }) } }
面试题 97 . Vue 3.0 所采用的 Composition Api 与 Vue 2.x使用的Options Api 有什么区别?
参考回答:
Options Api 包含一个描述组件选项(data、methods、props等)的对象 options; API开发复杂组件,同一个功能逻辑的代码被拆分到不同选项 ; 使用mixin重用公用代码,也有问题:命名冲突,数据来源不清晰; Composition Api vue3 新增的一组 api,它是基于函数的 api,可以更灵活的组织组件的逻辑。 解决options api在大型项目中,options api不好拆分和重用的问题
面试题 98 . 请问什么是SSR ,它主要解决什么问题?
参考回答:
Server-Side Rendering 我们称其为SSR,意为服务端渲染指由服务侧完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程; 解决了以下两个问题: seo:搜索引擎优先爬取页面HTML结构,使用ssr时,服务端已经生成了和业务想关联的HTML,有利于seo 首屏呈现渲染:用户无需等待页面所有js加载完成就可以看到页面视图(压力来到了服务器,所以需要权衡哪些用服务端渲染,哪些交给客户端) 缺点 复杂度:整个项目的复杂度 性能会受到影响 服务器负载变大,相对于前后端分离务器只需要提供静态资源来说,服务器负载更大,所以要慎重使用
面试题 99 . 简述父组件给子组件props传参,子组件接收的6种方法 ?
参考回答:
1. data中 变量 = this.props里面的数据 2. watch监听 赋值 3. mounted 渲染完成后调用一个函数 进行赋值 4. vuex 5. computed 计算属性,用法和watch类似,computed是同步,watch可以异步 6. 父组件v-if 触发渲染和销毁,子组件触发传参
面试题 100 . 简述MINI UI是什么?怎么使用?说出至少三个组件的使用方法?
参考回答:
//基于vue框架的ui组件库很多,这里主要简单阐述一下组件的使用方法。 基于vue前端的组件库。使用npm安装,然后import样式和js; vue.use(miniUi)全局引入。在单个组件局部引入; import {Toast} form ‘mini-ui’; 组件一: Toast(‘登录成功’) 组件二: mint-header; 组件三:mint-swiper