1. vue优点
- 轻量级
- 速度快
- 简单易学
- 低耦合
- 可重用性
- 独立开发
- 文档齐全,且文档为中文文档
2. ## vue中组件间传值
prop/$emit 父子组件传值
ref 和 $parent/$children 父子组件传值
eventBus($emit/$on) 父子,隔代,兄弟组件传值
$attrs/$listeners 隔代
provide/inject 隔代
vuex 父子,隔代,兄弟组件传值
3. ## vue生命周期(vue2和3)
vue2:beforeCreate/created、beforeMount/mounted、beforeUpdate/updated、actived/deadctived、beforeDestory/destoryed
vue3:setup、 beforeMount/mounted、 beforeUpdate/updated、 actived/deadctived、 beforeUnmount/unmounted、errorCapurted当捕获一个来自子孙组件的异常时激活
actived:被包含在<keep-alive>中的组件,会多出两个生命周期钩子函数,被激活时执行
Deactivated:比如从A组件切换到B组件,A组件消失时执行
4. ## vue设计模式
mvvm设计模式
- 工厂模式:传入参数即可创建实例;虚拟dom根据参数的不同返回基础标签的vnode和组件vnode。
- 单例模式:整个程序有且只有一个实例。第二次使用同一个类创建新对象的时候,应该得到与第一次创建的对象完全相同的对象。Vuex
- 策略模式:指对象有某个行为,但是在不同的场景中,该行为有不同的实现方案
- 观察者模式:响应式数据原理
- 发布/订阅者模式:vue事件机制,核心就是一对多的关系,一个发布者发起,所有订阅者都会执行。
- 代理模式:它为目标对象创造了一个代理对象,以控制对目标对象的访问。拦截器监听对象属性的增加或删除
- 装饰模式:(@装饰器的用法)在不改变原对象的基础上,通过对其添加属性或方法来进行包装扩展,使得原有对象可以动态具有更多功能。
Actions是一个装饰器,他包裹mutations使之可以异步使用。对于Store对象,使用Action可以异步改变状态;不用Actions也能使用mutations来同步改变状态;使用actions也不会改变state、getter、mutation的用法、结构
- 适配器模式 :作为两个不兼容的接口之间的桥梁
5. ## vue自定义指令怎么写
两种方式:
全局指令:通过Vue.directive()函数注册一个全局的指令
局部指令:通过组建的directives属性,对该组件添加一个局部的指令
创建全局指令:
需要传入指令名称以及一个包含指令钩子函数的对象,该对象的键即钩子函数的函数名,值即函数体,钩子函数可以有多个。
// 注册全局自定义指令 Vue.directive('my-directive', { // 在绑定元素的时候会被调用 bind: function(el, binding, vnode) { // 操作DOM el.style.color = 'red'; // 访问指令的值和参数 console.log('指令的值: ', binding.value); console.log('指令的参数: ', binding.arg); // 访问绑定元素的属性和内容 console.log('绑定元素的属性: ', el.getAttribute('title')); console.log('绑定元素的内容: ', el.innerHTML); }, // 在绑定元素所在的组件的 VNode 更新时调用 update: function(el, binding, vnode) { // 通过比较新旧值来避免不必要的更新 if (binding.value !== binding.oldValue) { // 执行更新的操作 } }, // 在指令与元素解绑时调用 unbind: function(el, binding, vnode) { // 清除元素的样式 el.style.color = ''; } })
然后,你可以在Vue模板中使用自定义指令:
<div v-my-directive="directiveValue" title="Directive Example">Custom Directive Example</div>
创建局部指令:通过在Vue实例中添加directives对象数据注册局部自定义指令。
directives: { focus: { inserted: function(el) { el.focus(); } } }
举例:
图片懒加载:有些网站图片的加载做的非常优雅,在图片未完成加载之前,用随机的背景色占位,图片加载完成之后才直接渲染出来,用自定义指令可以非常方便的实现这个功能。
<div id="app" v-image = "item " v-for="item in imageList"></div> <script> Vue.directive("image", { inserted: function(el,binding){ var color = Math.floor(Math,random()*1000000) el.style.backgroundColor = "#" + color var img = new Image() img.src = binding.vaule img.onload = function(){ el.style.backgroundImage = “url(” + binding.vaule + ")" } } }) new Vue({ el: "#app", data: { imageList: [ { url: "http://consumer-img.huawei.com/content/dam/huawei-cbg-site/greate-china/cn/mkt/homepage/section4/home-s4-p10-plus.jpg" }, { url: "http://consumer-img.huawei.com/content/dam/huawei-cbg-site/greate-china/cn/mkt/homepage/section4/home-s4-watch2-pro-banner.jpg" }, { url: "http://consumer-img.huawei.com/content/dam/huawei-cbg-site/en/mkt/homepage/section4/home-s4-matebook-x.jpg" } ] } }) </script>
应用场景
- 防抖
- 图片懒加载
- 一键复制功能
- …
自定义指令的钩子函数:均为可选
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用(保证父节点存在,但不一定已被插入文档中)。
update:所有组件的VNode更新时调用,但是可能发生在其子VNode更新之前。指令的值可能发生了改变,也可能没有。但似乎你可以通过比较更新前后的值来忽略不必要的模板更新。
componentUpdated:指令所在组件的VNode及其子VNode全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
钩子函数参数(即 el、binding、vnode 和 oldVnode)
- el:指令所绑定的元素,可以用来直接操作 DOM。
- binding:一个对象,包含以下 property:
6. v-show 和 v-if 指令的共同点和不同点
相同点:都可以控制dom元素的显示和隐藏
不同点:v-show只是改变display属性,dom元素并未消失,切换时不需要重新渲染页面
v-if直接将dom元素从页面删除,再次切换需要重新渲染页面
区别点:
v-if 是动态的向 DOM 树内添加或者删除 DOM 元素
v-show 是通过设置 DOM 元素的 display 样式属性控制显隐
v-if 切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件
v-show 只是简单的基于 css 切换
性能消耗
v-if 有更高的切换消耗
v-show 有更高的初始渲染消耗
使用场景
v-if 适合运营条件不大可能改变
v-show 适合频繁切换
7. ## 怎么封装一个良好可复用的组件
在vue中,组件可以通过以下方式创建:
Vue.component('my-component', { // options })
这是一种全局组件注册方式,my-component是组件名称,可以在Vue实例中的模板中使用。
在组建开发中,我们要注意以下几点:
- 设计良好的API,一个良好的API应该遵循简洁性、一致性、可扩展性、易用性的原则。
- 要保证组件的独立性,这样才能更好的被复用。
- 通过渲染函数和JSX(js的语法扩展 js+xml)的方式创建组件,要注意以下几点:只在必要时使用渲染函数和JSX;尽可能地简洁和易于理解;使用模板字符串来定义字符串常量;使用严格相等运算符;通过v-bind来绑定属性。
- 分离组件的结构、样式和行为
- 使用slot分发内容
- 使用props传递数据
- 减少耦合
- 使用scoped css 限制样式作用范围
8. ## Vue的v-for与v-if的优先级,v-for和v-if的区别,为什么不推荐在v-for中使用v-if?如果出现这样的使用场景,你推荐用什么方式取代?
Vue的v-for与v-if的优先级:
v-for优先于v-if,这意味着当v-for和v-if同时存在于同一个元素上时,v-for会优先执行,然后在每一次迭代中,才会应用v-if的条件。
v-for和v-if的区别:
- 作用不同,v-if指令用于条件渲染,只有指令的表达式返回true时,才会显示;而v-for指令基于一个数组来渲染一个列表。
- 优先级不同,v-for高于v-if,在进行if判断的时候v-for是比v-if先进行判断的。
不推荐在v-for中使用v-if的原因有以下几点:
v-for和v-if同时使用,容易带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
也容易造成逻辑混乱,在v-for中嵌套v-if会使得代码的逻辑变得复杂,难以理解和维护。特别是v-for循环较大时,会导致代码的可读性下降。
如果条件出现在循环外部(当需要先v-if再v-for时),可以在外层嵌套template,在这一层进行v-if判断,在内部进行v-for循环。
如果条件出现在循环内部,可以通过计算属性computed提前过滤掉那些不需要显示的项。
9. 如何获取dom
给dom元素加 ref='refname', 然后通过this.$refs.refname进行获取dom元素
10. 说出几种vue当中的指令和它的用法
- v-model :实现数据的双向绑定,可以实时修改数据
v-bind:动态绑定,及时对页面的数据进行更改,常简写为:
- v-bind:class:绑定一个属性
- v-on:用于绑定事件;多个事件绑定: v-on="{事件名:方法名,事件名:方法名,……}"
- v-for:遍历 循环数组或者json v-for="字段名in(of)数组/json"
- v-if:条件渲染
- v-show:条件渲染
- v-hide:隐藏
v-else/v-else-if:和if语句中else if的用法相同,当v-if不成立时执行
v-text:会把解析到为文本完全替换掉标签里的内容
v-html:会将其当html标签解析后输出
v-once:所在节点在初次动态渲染后,就视为静态内容了。
以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。v-pre:跳过Vue在有
v-pre
属性的所在节点的编译过程。
可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译速度。v-clock:防止闪烁
11. vue-loader是什么?它的用途是什么?
vue文件的一个加载器,将template/js/style转换为js模块
用途:js可以写es6、style样式
12. 为什么用key?
给每个dom元素加上key作为唯一标识,diff算法可以正确的识别到这个节点,使页面渲染更加迅速。
13. axios及安装
vue项目中使用ajax时需要axios插件
下载方式 cnpm install axios --save
14. 请说出vue.cli项目中src目录每个文件夹和文件的用法
- components:存放组件
- app.vue 主页面入口
- main.js 主文件入口
- assets 存放静态资源文件
15. Computed和Watch的区别,分别简述computed和watch的使用场景
computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的 属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。
watch 侦听器 : 更多的是观察的作用,无缓存性,类似于某些数据的监听回调,每 当监听的数据变化时都会执行回调进行后续操作。
用官网的一句话来说,所有需要用到计算的都应该使用计算属性。多条数据影响一条数据时使用计算属性,使用场景购物车。
如果是一条数据更改,影响多条数据时,使用watch,使用场景搜索框。
16. ## $nextTick 的使用
在data()中的修改后,页面中无法获取data修改后的数据,使用$nextTick时,当data中的数据修改后,可以实时的渲染页面
17. vue组件中data为什么必须是一个函数?
因为js的特性所导致,在component中,data必须以函数的形式存在,不可以是对象。
组件中的data写成一个函数,数据以函数返回值的形式定义,这样每次复用组件的时候,丢回返回一份新的data,相当于每个组件实例都有自己私有的数据空间,他们值负责各自维护数据,不会造成混乱。而单纯的写成对象形式,就是所有组件实例共用了一个data,这样改一个全部都会修改。
18. 渐进式框架的理解
主张最少,可以根据不同的需求选择不同的层级
19. vue在双向数据绑定是如何实现的?
vue双向数据绑定是通过数据劫持、组合、发布订阅模式的方式来实现的,也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生变化。
核心:关于vue双向数据绑定,其核心是Object.defineProprerty()方法。
20. 单页面应用和多页面应用的区别及缺点
单页面应用(SPA):通俗地说就是只有一个主页面的应用,浏览器一开始就加载所有的js、html、css。所有的页面内容都包含在这个主页面中,但在写的时候,还是分开写,然后再加护的时候有路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多用于pc端。
多页面应用(MPA):就是一个应用中有多个页面,页面跳转时整个页面刷新
单页面的优点:用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小;前后端分离,页面效果会比较酷炫。
单页面的缺点:不利于seo;导航不可用,如果一定要导航需要自行实现前进、后退。初次加载时耗时多,页面不复杂度提高很多。
21. Vue项目中为什么要在列表组件中写key,其作用是什么?
key时给每个vnode的唯一id,可以依靠key更准确,更快的拿到oldVnode中对应的vnode节点。
更准确
因为带key就不是就地复用了,在sameNode函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确。
更快
利用key的唯一生成map对象来获取对应节点,比遍历方式更快。
22. 父组件和子组件生命周期钩子函数的执行顺序是什么?
加载渲染过程
父 beforeUpdate -> 父created -> 父beforeMount -> 子 beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
子组件更新过程
父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
父组件更新过程
父beforeUpdate -> 父updated
销毁过程
父beforeDestory -> 子beforeDestory -> 子destroyed -> 父destroyed
23. vue和jQuery的区别
jQuery是使用选择器($)选取DOM对象,对其进行赋值、取值、事件绑定等操作,其实和原生的html的区别只在于可以更方便的选取和操作DOM对象,而数据和界面是在一起的。比如需要获取label标签的内容:$("label").val();它还是依赖DOM 元素的值。
Vue则是通过Vue对象将数据和View完全分离开来。对数据进行操作不再需要引用相应的DOM对象,可以说数据和View是分离的,他们通过Vue对象这个vm实现相互的绑定,这就是传说的MVVM。
24. delete 和 Vue.delete 删除数组的区别
delete只是被删除的元素变成了empty/undefined 其他元素的键值还是不变。
Vue.delete直接删除了数组,改变了数组的键值。
25. SPA首屏加载慢如何解决?
安装动态懒加载所需的插件,使用CDN资源。
26. vue项目是打包了一个js文件,一个css文件,还是有多个文件?
根据vue-cli脚手架规范,一个js文件,一个css文件。
27. vue更新数组时触发视图更新的方法。
- push() 尾部添加
- pop() 尾部删除
- shift() 头部删除
- unshift() 头部添加
- splice() 删除,添加,替换
- sort() 排序
- reserve() 翻转
28. 什么是vue的生命周期?有什么作用?
每个Vue实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂在到DOM并在数据变化时更新DOM等。同时在这个过程中也会运行一下叫做生命周期钩子的函数,这给了用户在不同阶段添加自己代码的机会。
29. 第一次页面加载会触发哪几个钩子?
beforeCreate、Created、beforeMount,mounted
30. vue获取数据一般在那个周期函数
created、beforeMount、mounted
31. Vue生命周期的理解
总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
生命周期 描述 beforeCreate 组件实例被创建之初,组件的属性生效之前 created 组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用 beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用 mounted el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子 beforeUpdate 组件数据更新之前调用,发生在虚拟 DOM 打补丁之前 updated 组件数据更新之后 activited keep-alive 专属,组件被激活时调用 deadctivated keep-alive 专属,组件被销毁时调用 beforeDestory 组件销毁前调用 destoryed 组件销毁后调用
32.vuex是什么?
vue框架中状态管理。
33. ## vuex有哪几种属性?
state、getter、mutation、action、module
state:基本数据(数据源存放地)
getter:从基本数据派生出来的数据
mutation:提交更改数据的方法,同步
action:像一个装饰器,包裹mutations,使之可以异步。
module:模块化Vuex
34. ## Vue框架的原理,打包知识,项目构建
vue框架的原理:
vue框架是一个基于mvvm模式的前端框架,其核心思想是将视图和状态分离,通过数据驱动的方式将模型和视图箭囊里关联。他的核心原理主要包括以下几个方面:
- 响应式数据:vue通过使用Object.defineProperty()方法来劫持数据的get和set操作,从而实现了数据的响应式。当数据变化时,vue会通知相关的视图进行更新。
- 虚拟dom:vue通过使用虚拟dom来优化页面渲染性能。在组件更新时,vue会先构建一个虚拟dom树,然后通过比较新旧虚拟dom树的差异,最终只对需要更新的部分进行真是dom操作提升页面渲染效率。
- 组件化开发:vue框架将页面拆分成独立的组件,每个组件都有自己的状态和视图,可以互相结合和嵌套。通过组件化开发,可以提高代码的可复用性和可维护性。
- 模板编译:vue框架通过将vue组建的模板编译成渲染函数来实现数据和视图的关联。编译过程会将模板解析成AST(抽象语法树),然后将AST编译成渲染函数,渲染函数会生成虚拟dom。
打包知识和项目构建:
在开发项目时,常常需要进行打包和构建,以便将多个模块或者文件打包成一个或多个最终部署的静态资源文件,常用的打包工具包括webpack和parcel,vue3使用vite打包
- 配置文件:打包工具会使用一个配置文件(如webpack.config.js)来定义项目的构建规则和需要处理的文件,在配置文件中,你可以设置入口文件(entry)、输出文件路径、使用的加载器(loader)和插件(plugin)、开发和生产模式(mode)。
entry:入口文件
output:文件输出的路径
chunk:多个文件组成一个代码块,可以将可执行的门票快和其他所依赖的模块组合成一个 chunk,这就是打包
loader:文件转换器,例如把sass转换为css
plugin:扩展webpack功能的插件
35. ## 二次封装axios的想法和步骤
创建一个新的Axios实例,对其进行定制化设置,如添加默认的请求头,设置超时时间等。
import axios from 'axios'; const http = axios.create({ baseURL: 'https://api.example.com/', // 设置基本URL timeout: 5000, // 设置请求超时时间 headers: { 'Content-Type': 'application/json' // 设置默认的请求头 } }); export default http;
添加拦截器:通过添加请求拦截器和相应拦截器,你可以在请求发送前和响应返回后对数据进行统一处理,如添加认证信息、错误处理等。
import http from './http'; http.interceptors.request.use( config => { // 在发送请求之前做些什么 return config; }, error => { // 对请求错误做些什么 return Promise.reject(error); } ); http.interceptors.response.use( response => { // 对响应数据做些什么 return response; }, error => { // 对响应错误做些什么 return Promise.reject(error); } ); export default http;
封装常用的请求方法:基于新的Axios实例,你可以封装一些常用的请求方法,比如get、post等,来简化代码编写。
import http from './http'; export const get = (url, params) => { return http.get(url, { params }); }; export const post = (url, data) => { return http.post(url, data); };
你可以通过导入封装后的Axios实例或者封装后的请求方法来发送请求。
36. <keep-alive></keep-alive> 的作用是什么?
主要是用于需要频繁切换的组件时 进行缓存,不需要重新渲染页面;
比如:当你勾选了某一选项或填写了数据,切换页面时数据不会消失
37. ## vue和react的区别
- 开发语言:vue使用模板语法,采用基于模板的开发方式,而react使用JSX语法,将组件的结构和逻辑写在JSX中,采用更多的JS开发方式。
- 学习曲线:相对而言,vue的学习曲线较为平缓,上手相对容易,尤其适合前端初学者;而react的学习曲线较为陡峭,需要更深入地理解JS和函数式编程的概念。
- 生态系统:react拥有更丰富和庞大的生态系统,包括redux、react router等成熟的解决方案和插件。vue的生态系统虽然较为年轻,但也逐渐发展壮大。
- 社区支持:react具有庞大的社区支持,有很多活跃的开发者和社区贡献者,能够提供丰富的资源和支持;vue虽然社区规模相对较小,但也有一批活跃的贡献者和社区活动,也能够提供良好的支持。
- 双向数据绑定:vue具有直接的双向数据绑定,视图更新时会自动同步数据的变化;react则采用了单项数据流的原则,数据通过props和state进行子组件向父组件的传递。
- 组件化开发:vue和react都支持组件化开发,但vue更灵活,多数情况下可以使用vue的选项对象来定义组件,而react中的组件开发更加基于JS的类组件和函数式组件。
38. vue全家桶
vue-cli、vuex、vueRouter、Axios
39. vue-cli 工程常用的 npm 命令有哪些?
npm install 下载 node_modules 资源包的命令
npm run dev 启动 vue-cli 开发环境的 npm 命令
npm run build vue-cli 生成 生产环境部署资源 的 npm 命令
npm run build–report 用于查看 vue-cli 生产环境部署资源文件大小的 npm 命令
40. 请说出 vue-cli 工程中每个文件夹和文件的用处?
build 文件夹是保存一些 webpack 的初始化配置。
config 文件夹保存一些项目初始化的配置
node_modules 是 npm 加载的项目依赖的模块
src 目录是我们要开发的目录:
assets 用来放置图片
components 用来放组件文件
app.vue 是项目入口文件
main.js 项目的核心文件
41. vue 常用的修饰符?
事件修饰符
.stop 阻止事件继续传播
.prevent 阻止标签默认行为
.capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
.self 只当在 event.target 是当前元素自身时触发处理函数
.once 事件只会触发一次
.passive 告诉浏览器你不想阻止事件的默认行为
v-model 的修饰符.lazy 通过这个修饰符,转变为在 change 事件再同步
.number 自动将用户输入值转化为数值类型
.trim 自动过滤用户输入的收尾空格
键盘事件修饰符.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
系统修饰符.ctrl
.alt
.shift
.meta
鼠标按钮修饰符.left
.right
.middle
43. vue 事件中如何使用 event 对象?
获取事件对象,方法参数传递 $event 。注意在事件中要使用 $ 符号
<button @click="Event($event)">事件对象</button>
44. vue 中子组件调用父组件的方法?
直接在子组件中通过 this.$parent.event 来调用父组件的方法。
在子组件里用$emit()向父组件触发一个事件,父组件监听这个事件就行了。
父组件把方法传入子组件中,在子组件里直接调用这个方法。
45. vue路由跳转
(一)声明式导航router-link
- 不带参数:
// 注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。
<router-link :to="{name:'home'}">
<router-link :to="{path:'/home'}"> //name,path都行, 建议用name
- 带参数:
<router-link :to="{name:'home', params: {id:1}}">
<router-link :to="{name:'home', query: {id:1}}">
<router-link :to="/home/:id">
//传递对象
<router-link :to="{name:'detail', query: {item:JSON.stringify(obj)}}"></router-link>
(二)this.$router.push()不带参数:
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
query传参
1.路由配置:
name: 'home',
path: '/home'
2.跳转:
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}})
3.获取参数
html取参: $route.query.id
script取参: this.$route.query.id
params传参
1.路由配置:
name: 'home',
path: '/home/:id'(或者path: '/home:id')
2.跳转:
this.$router.push({name:'home',params: {id:'1'}})
注意:
// 只能用 name匹配路由不能用path
// params传参数(类似post) 路由配置 path: "/home/:id" 或者 path: "/home:id"否则刷新参数消失
3.获取参数
html取参:$route.params.id
script取参:this.$route.params.id
直接通过path传参
1.路由配置:
name: 'home',
path: '/home/:id'
2.跳转:
this.$router.push({path:'/home/123'})
或者:
this.$router.push('/home/123')
3.获取参数:
this.$route.params.id
params和query的区别
query类似 get,跳转之后页面 url后面会拼接参数,类似?id=1。
非重要性的可以这样传,密码之类还是用params,刷新页面id还在。
params类似 post,跳转之后页面 url后面不会拼接参数。
(三)this.$router.replace()用法同上
(四)this.$router.go(n)向前或者向后跳转n个页面,n可为正整数或负整数
区别:this.$router.push
跳转到指定url路径,并在history栈中添加一个记录,点击后退会返回到上一个页面
this.$router.replace
跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)
this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数
46. Vue.js 双向绑定的原理
Vue.js 2.0 采用数据劫持(Proxy 模式)结合发布者-订阅者模式(PubSub 模式)的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
Vue.js 3.0, 放弃了Object.defineProperty ,使用更快的ES6原生 Proxy (访问对象拦截器, 也称代理器)
47. 过滤器 (Filter)
在Vue中使用filters来过滤(格式化)数据,filters不会修改数据,而是过滤(格式化)数据,改变用户看到的输出(计算属性 computed ,方法 methods 都是通过修改数据来处理数据格式的输出显示。
使用场景: 比如需要处理时间、数字等的的显示格式;
48. Vue.js页面闪烁
Vue. js提供了一个v-cloak指令,该指令一直保持在元素上,直到关联实例结束编译。当和CSS一起使用时,这个指令可以隐藏未编译的标签,直到实例编译结束。用法如下。
[v-cloak]{
display:none;
}
<div v-cloak>{{ title }}</div>
49. 如何解决数据层级结构太深的问题
在开发业务时,经常会岀现异步获取数据的情况,有时数据层次比较深,如以下代码: span 'v-text="a.b.c.d">, 可以使用vm.$set手动定义一层数据: vm.$set("demo",a.b.c.d)
50. $route和$router的区别
$route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
$router是“路由实例”对象包括了路由的跳转方法,钩子函数等
51. 怎样理解 Vue 的单项数据流
数据总是从父组件传到子组件,子组件没有权利修改父组件传过来的数据,只能请求父组件对原始数据进行修改。这样会防止从子组件意外改变父组件的状态,从而导致你的应用的数据流向难以理解。
注意:在子组件直接用 v-model 绑定父组件传过来的 props 这样是不规范的写法,开发环境会报警告。
如果实在要改变父组件的 props 值可以再data里面定义一个变量,并用 prop 的值初始化它,之后用$emit 通知父组件去修改。
52. 虚拟DOM是什么?有什么优缺点?
由于在浏览器中操作DOM是很昂贵的。频繁操作DOM,会产生一定性能问题。这就是虚拟Dom的产生原因。Vue2的Virtual DOM 借鉴了开源库 snabbdom 的实现。Virtual DOM本质就是用一个原生的JS对象去描述一个DOM节点,是对真实DOM的一层抽象。
优点:
1、保证性能下限:框架的虚拟DOM需要适配任何上层API可能产生的操作,他的一些DOM操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的DOM操作性能要好很多,因此框架的虚拟DOM至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,既保证性能的下限。
2、无需手动操作DOM:我们不需手动去操作DOM,只需要写好 View-Model的 代码逻辑,框架会根据虚拟DOM和数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率。
3、跨平台:虚拟DOM本质上是JavaScript对象,而DOM与平台强相关,相比之下虚拟DOM可以进行更方便地跨平台操作,例如服务器端渲染、weex开发等等。
缺点:
1、无法进行极致优化:虽然虚拟DOM + 合理的优化,足以应对大部分应用的性能需要,但在一些性能要求极高的应用中虚拟DOM无法进行针对性的极致优化。
2、首次渲染大量DOM时,由于多了一层DOM计算,会比innerHTML插入慢。
53. Vuex 页面刷新数据丢失怎么解决?
需要做 vuex 数据持久化,一般使用本地储存的方案来保存数据,可以自己设计存储方案,也可以使用第三方插件。
推荐使用 vuex-persist (脯肉赛斯特)插件,它是为 Vuex 持久化储存而生的一个插件。不需要你手动存取 storage,而是直接将状态保存至 cookie 或者 localStorage中。
54. Vuex 为什么要分模块并且加命名空间?
模块: 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能会变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。
命名空间: 默认情况下,模块内部的 action、mutation、getter是注册在全局命名空间的 — 这样使得多个模块能够对同一 mutation 或 action 做出响应。如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced:true 的方式使其成为带命名的模块。当模块被注册后,他所有 getter、action、及 mutation 都会自动根据模块注册的路径调整命名。
55. 你都做过哪些 Vue 的性能优化?
这里只列举针对 Vue 的性能优化,整个项目的性能优化是一个大工程。
对象层级不要过深,否则性能就会差。
不需要响应式的数据不要放在 data 中(可以使用 Object.freeze() 冻结数据)
v-if 和 v-show 区分使用场景
computed 和 watch 区分场景使用
v-for 遍历必须加 key,key最好是id值,且避免同时使用 v-if
大数据列表和表格性能优化 - 虚拟列表 / 虚拟表格
防止内部泄露,组件销毁后把全局变量和时间销毁
图片懒加载
路由懒加载
异步路由
第三方插件的按需加载
适当采用 keep-alive 缓存组件
防抖、节流的运用
服务端渲染 SSR or 预渲染
56. Vue.set 方法原理
在两种情况下修改 Vue 是不会触发视图更新的。
1、在实例创建之后添加新的属性到实例上(给响应式对象新增属性)
2、直接更改数组下标来修改数组的值。
Vue.set 或者说是 $set 原理如下
因为响应式数据 我们给对象和数组本身新增了__ob__属性,代表的是 Observer 实例。当给对象新增不存在的属性,首先会把新的属性进行响应式跟踪 然后会触发对象 ob 的dep收集到的 watcher 去更新,当修改数组索引时我们调用数组本身的 splice 方法去更新数组。
57. 函数式组件使用场景和原理
函数式组件与普通组件的区别
1、函数式组件需要在声明组件时指定 functional:true
2、不需要实例化,所以没有this,this通过render函数的第二个参数context代替
3、没有生命周期钩子函数,不能使用计算属性,watch
4、不能通过$emit对外暴露事件,调用事件只能通过context.listeners.click的方式调用外部传入的事件
5、因为函数组件时没有实例化的,所以在外部通过ref去引用组件时,实际引用的是HTMLElement
6、函数式组件的props可以不用显示声明,所以没有在props里面声明的属性都会被自动隐式解析为prop,而普通的组件所有未声明的属性都解析到$attrs里面,并自动挂载到组件根元素上(可以通过inheritAttrs属性禁止)
优点:1.由于函数组件不需要实例化,无状态,没有生命周期,所以渲染性要好于普通组件2.函数组件结构比较简单,代码结构更清晰使用场景:
一个简单的展示组件,作为容器组件使用 比如 router-view 就是一个函数式组件。 “高阶组件”—用于接受一个组件为参数,返回一个被包装过的组件。
相关代码如下:
if (isTrue(Ctor.options.functional)) { // 带有functional的属性的就是函数式组件 return createFunctionalComponent(Ctor, propsData, data, context, children); } const listeners = data.on; data.on = data.nativeOn; installComponentHooks(data); // 安装组件相关钩子 (函数式组件没有调用此方法,从而性能高于普通组件)
58. 子组件为何不可以修改父组件传递的 Prop?
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。