21.vuex有哪几种属性?
有五种,分别是 State、Getter、Mutation 、Action、Module;
state为单一状态树,在state中需要定义我们所需要管理的数组、对象、字符串等等,只有在这里定义了,在vue.js的组件中才能获取定义对象的状态;
getter类似vue.js的计算属性,当我们需要从store的state中派生出一些状态时,就需要使用getter,getter会接收state作为第一个参数,而且getter的返回值会根据它的依赖被缓存起来,只有getter中的依赖值(state中的某个需要派生状态的值)发生改变的时候才会被重新计算;
更改store中state状态的唯一方法就是提交mutation,每个mutation都有一个字符串类型的事件类型和一个回调函数,我们需要改变state的值就要在回调函数中改变,要执行这个回调函数,就需要执行一个相应的调用方法:store.commit;
action可以提交mutation,在action中可以执行store.commit,而且action中可以有任何的异步操作。在页面中如果我们要用这个action,就需要执行store.dispatch;
当state中很复杂臃肿的时候,module可以将store分割成模块,每个模块中拥有自己的state、mutation、action和getter。
22.不用Vuex会带来什么问题?
(1)可维护性会下降,要想修改数据,得维护三个地方;
(2)可读性会下降,因为一个组件里的数据,看不出来是从哪来;
(3)增加耦合,大量的上传派发,会让耦合性大大的增加,本来Vue用Component就是为了减少耦合,现在这么用,和组件化的初衷相背。
23.Vue前端实现购物车
利用vuex + vant 实现购物车功能,概括有以下步骤:
(1)mock模拟数据;
(2)利用veux操作商品的选中状态;
(3)全选与取消全选;
(4)动态reduce计算价格;
(5)结算取出商品id;
(6)利用manageStatus判断管理状态,管理删除商品;
(7)未删除商品的状态存储。
24.methods、computed、watch的区别
methods是个方法,执行的时候需要事件进行触发;
computed是一个计算属性,是实时响应的,只要data中的属性发生了变化那么就会触发computed,计算属性是基于属性的依赖进行缓存的,methods调用的时候需要加(),而computed调用的时候不需要加();
watch用来监听属性的变化,当值发生变化的时候来执行特定的函数,watch监听属性的时候会有2个参数newVal和oldVal一个新值一个旧值。
25.简述vuex的数据传递流程
当组件进行数据修改的时候我们需要调用dispatch来触发actions里面的方法,actions里面的每个方法中都会 有一个commit方法,当方法执行的时候会通过commit来触发mutations里面的方法进行数据的修改,mutations里面的每个函数都会有一个state参数,这样就可以在mutations里面进行state的数据修改 ,当数据修改完毕后,会传导给页面,页面的数据也会发生改变。
26.组件中写 name 选项有什么作用?
(1)项目使用 keep-alive 时,可搭配组件 name 进行缓存过滤;
(2)DOM 做递归组件时需要调用自身 name;
(3)vue-devtools 调试工具里显示的组见名称是由vue中组件name决定的。
27.<keep-alive></keep-alive>的作用是什么?
<keep-alive></keep-alive> 包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。
28.mint-ui是什么?怎么使用?说出至少三个组件使用方法
基于vue的前端组件库。先npm安装,然后import引入样式和js,vue.use(mintUi)全局引入。在单个组件局部引入:import {Toast} from ‘mint-ui’。
组件一:Toast(‘登录成功’);
组件二:mint-header;
组件三:mint-swiper
29.为什么避免 v-if 和 v-for 用在一起?
当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级,通过v-if 移动到容器元素,不会再重复遍历列表中的每个值。取而代之的是,我们只检查它一次,且不会在 v-if 为否的时候运算 v-for。
30.怎样理解 Vue 的单向数据流?
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行;
这样会防止从子组件意外改变父级组件的状态,从而导致应用的数据流向难以理解;
除此之外,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值,
所以不应该在一个子组件内部改变 prop,如果你这样做了,Vue 会在浏览器的控制台中发出警告;
子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件来修改。
31.Vue 的父组件和子组件生命周期钩子函数执行顺序?
Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:
加载渲染过程
父 beforeCreate --> 父 created --> 父 beforeMount --> 子 beforeCreate --> 子 created --> 子 beforeMount --> 子 mounted --> 父 mounted
子组件更新过程
父 beforeUpdate --> 子 beforeUpdate --> 子 updated --> 父 updated
父组件更新过程
父 beforeUpdate --> 父 updated
销毁过程
父 beforeDestroy --> 子 beforeDestroy --> 子 destroyed --> 父 destroyed
32.在哪个生命周期内调用异步请求?
可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。但是更推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
(1)能更快获取到服务端数据,减少页面 loading 时间;
(2)ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
33.你有对 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 查找性能瓶颈;
34.虚拟 DOM 的优缺点
优点:
保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。
缺点:
无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
35.虚拟 DOM 实现原理
虚拟 DOM 的实现原理主要包括以下 3 部分:
(1)用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
(2)diff 算法 — 比较两棵虚拟 DOM 树的差异;
(3)pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。
36.直接给一个数组项赋值,Vue 能检测到变化吗?
由于 JavaScript 的限制,Vue 不能检测到以下数组的变动:
(1)利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
(2)当你修改数组的长度时,例如: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)
37.在Vue生命周期什么阶段才能访问操作DOM?
在钩子函数 mounted 被调用前,Vue 已经将编译好的模板挂载到页面上,所以在 mounted 中可以访问操作 DOM。
38.Vue-router 原理
vue-router通过hash与history两种方式实现前端路由;更新视图但不重新请求页面是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有两种方式:
(1)hash:利用 URL 中的 hash. 形式上会多个# ;如下,
http://localhost:8080/#/login
hash("#") 的作用是加载 URL 中指示网页中的位置; # 本身以及它后面的字符称之为 hash,可通过 window.location.hash 获取;
hash 虽然出现在 url 中,但不会被包括在 http 请求中,它是用来指导浏览器动作的,对服务器端完全无用,因此改变 hash 不会重新加载页面, 每一次改变 hash(window.localtion.hash)都会在浏览器访问历史中增加一个记录。 利用 hash 的以上特点,就可以来实现前端路由"更新视图但不重新请求页面"的功能了。
(2)history:html5 中新增的方法,形式上比 hash更好看;如下,
http://localhost:8080/login
History interface 是浏览器历史记录栈提供的接口,通过back()、forward()、go()等方法,可以读取浏览器历史记录栈的信息,进行各种跳转操作。
从 HTML5开始,History interface 提供了2个新的方法:pushState()、replaceState() 使得我们 可以对浏览器历史记录栈进行修改,这2个方法有个共同的特点:当调用他们修改浏览器历史栈后,虽然当前url改变了,但浏览器不会立即发送请求该url,这就为单页应用前端路由,更新视图但不重新请求页面提供了基础。
39.router-link 和 $router.push 实现跳转的原理
router-link
默认会渲染为 a 标签,可以通过 tag 属性修改为其他标签,自动为 a 标签添加 click 事件,然后执行 $router.push() 实现跳转;
$router.push
根据路由配置的 mode 确定使用 HTML5History 还是 HashHistory 实现跳转;
HTML5History:调用 window.history.pushState() 跳转;
HashHistory:调用 HashHistory.push() 跳转。
40.promise 和 await/async 区别
区别主要在于按顺序调用多个异步函数时的写法和报错获取,
Promise方式
ajax().then(func1).then(func2).then(func3).then(func4)
await/async方式
async function demo(){ await res = ajax(); await res = func1(res); await res = func2(res); await res = func3(res); await res = func4(res); }
当遇到多个异步函数时 Promise 方式需要很多 .then,这样会导致代码不易读且结构复杂;await/async 方式让异步代码的格式与同步代码一样更易读。
报错读取 Promise 使用 .catch 抓取报错,await/async 使用 try...catch... 方式抓取报错。
41.v-if 和 v-show 的区别
v-if 的开销比 v-show 更大,v-show 有更高的初始化渲染消耗;
一个元素频繁进行隐藏和显示操作时使用 v-show 更加合适;一个元素不频繁进行隐藏和显示操作时使用 v-if 更合适。
42.什么是单页应用?
单页应用的全称是 Single Page Application,简称 SPA,通过路由的变更,局部切换网页内容取代整个页面的刷新操作;
三大框架 React、Vue、Angular 均采用单页应用模式;
优点:用户操作体验好,用户不用刷新页面;局部更新, 对服务器压力小;良好的前后端分离,后端不再负责页面渲染和输出工作。
缺点:首次加载耗时长, 速度慢;SEO不友好, 需要采用 prerender 服务进行完善。
43.Vue的权限管理
整体思路:
后端返回用户权限,前端根据用户权限处理得到左侧菜单;所有路由在前端定义好,根据后端返回的用户权限筛 选出需要挂载的路由,然后使用 addRoutes 动态挂载路由。
具体思路:
(1)路由定义,分为初始路由和动态路由,一般来说初始路由只有 login,其他路由都挂载在 home 路由之下 需要动态挂载;
(2)用户登录,登录成功之后得到 token,保存在 sessionStorage,跳转到 home,此时会进入路由拦截根 据 token 获取用户权限列表;
(3)全局路由拦截,根据当前用户有没有 token 和 权限列表进行相应的判断和跳转,当没有 token 时跳到 login,当有 token 而没有权限列表时去发请求获取权限等等逻辑;
(4)使用 Vuex 管理路由表, 根据 Vuex 动态渲染侧边栏组件。
44.vue 的 watch 是否可以监听数组
能监听
数组的元素增删:例如 push 和 splice 操作;
数组元素内部的变化:必须手动开启 deep:true 配置, 才能监听到。
不能监听
数组中已有值的替换。
45.前端的优化方案
(1)尽量减少闭包的使用;
(2)进行 js 和 css 文件的合并,减少http请求次数,进行可能讲文件压缩,减少请求大小;
(3)使用字体图标和svg图标,代替传统的png格式;
(4)减少DOM操作,主要减少DOM的重绘和重排;
(5)采用图片懒加载,加快页面启动速度,加载页面时先不加载图片,使用一张背景图占位,等页面加载完毕后再加载图片;
(6)尽可能使用事件委托来处理绑定操作,减少DOM的频繁操作(事件委托:为父元素添加事件, 利用冒泡机制,让父元素处理所有子元素的事件);
(7)减少 css 表达式的使用;
(8)减少 css 标签选择器的使用;
(9)页面数据获取方式采用异步和延迟分批加载。