Vue面试题的自我总结

1. 说说你对Vue的理解?

Vue是一个单页面应用的Web框架,也是一个渐进式的MVVM框架。什么是渐进式:强制主张最少,相比于传统的非常多的html文件,Vue只有一个单页面,只是不同页面需要路由去控制。其中的组件化系统、全局状态管理工具、客户端路由等都是传统开发比不了的。

2. 什么是双向绑定?/ 双向绑定的原理是什么?

什么是双向绑定:单向绑定相当于一个使用js代码更新model层,view层相对应的就自动更新,双向绑定就是加上改变view层的数据,同时model层的数据也同时改变,这就是双向绑定。

双向绑定的原理:相当于MVVM,M-model:应用的数据及逻辑,v-view: 页面上显示的数据,vm-viewModel: 核心,关联数据和视图,两部分组成:

(1)监听器(observer)(2)解析器(compiler)

viewModel主要职责:

(1)  数据变化后更新视图

(2)  视图变化后更新数据

3. v-if 和 v-show 的区别

(1) v-if是添加一个节点,v-show是给元素添加一个display:none。

(2) v-if会触发生命周期,v-show不会触发。

(3) v-if有更高的性能消耗,v-show有更高的初始性能消耗。

(4) 频繁的使用建议使用v-show,较少使用建议使用v-if。

4. Vue实例挂载过程中发生了什么?

(1)  new Vue()的时候会调用_init() 方法。

    (1)$set、$get、$delete、$watch等方法。

    (2)$on、$emit等事件。

    (3)$_update、$forceUpdate、$destroy等生命周期

(2) 调用$mount进行页面的挂载。

(3) 执行render生成虚拟DOM。

(4) _update将虚拟DOM生成真实DOM结构,渲染到页面上。

5.说说你对Vue生命周期的理解?

一:什么是Vue的生命周期:
	Vue 实例从开始创建、初始化数据、编译模板、挂载Dom和渲染、更新和渲染、卸载等一系列过程,这是 Vue 的生命周期。

二:生命周期的过程:

(1) beforeCreate -> created: 初始化vue实例,初始化默认的生命周期钩子和事件,比如data,methods都还未初始化,不能调用。

(2) created: data,methods,watch等都初始化完成,都可以使用,但是vm.$el还未创建。

(3) beforeMount: 已获取到vm.$el,并且vm.el已初始化完成,但并未挂载到el上。

(4) beforeMount -> Mounted: vm.el已挂载完,生成的DOM结构替换el。

(5) Mounted: 已完成vm.el的挂载和渲染,可对DOM进行操作。

(6) beforeUpdate: 数据的view层并未更新,而model层的数据已经更新。

(7) Updated: 完成视图层view的更新。

(8) beforeDestroy: 实例被销毁前调用,实例中所有属性和方法都可以调用。

(9) Destroyed:所有属性和方法都不可用,组件已被销毁。

三:数据请求放在在created还是mouted?
(1)因为如果放在mounted请求有可能导致页面闪动,因为DOM解构已经生成并且渲染,所有放在created里面,因为当前页面DOM结构并未生成,不会发生该情况。

6.为什么vue中的v-if与v-for不能一起使用?

(1)因为v-for的优先级大于v-if,在循环里面每次循环都会去判断一次,会带来性能方面的浪费。
(2)解决办法:在外嵌套一层template模板使用v-if。内层使用v-for进行循环。

7.SPA(单页应用)首屏加载速度慢怎么解决?

一:加载慢的原因
	(1)网络延时问题
	(2)加载资源太大
	(3)资源重复请求
	(4)加载脚本,渲染堵塞
二:解决方案
	(1)减小入口文件 --> 路由懒加载
	(2)静态资源本地缓存 --> 合理利用localStorage、采用http缓存,设置Cache-Control等请求头、采用Service Worker离线缓存
	(3)UI框架按需加载 --> element-UI
	(4)组件重复打包 --> 在webpach的config文件修改CommonsChunkPlugin的配置。
		--> minChunks: 3 // 表示把使用三次或以上的抽离出来,放进公共依赖组件、避免重复加载组件。
	(5)图片资源的压缩  --> 使用工具压缩大小、使用雪碧图等方式减轻http请求压力

8.为什么vue中的data属性是一个函数而不是一个对象?

1.当一个组件中data为一个对象时,Vue通过Vue.extend()构成组件实例,相当于构造函数,多次调用,调用的都是相同地址,所有的调用者都会改变,而函数有单独的作用域,地址是不同的,所有data是对象的话会造成变量全局污染。

2.根实例对象data可以是对象也可以是函数(根实例是单例),不会产生数据污染情况

9.Vue给对象新增属性,页面不刷新?

(1).data(){
	return {
		obj : {
			name : "skam", // 这里面的数据是响应式的,vue2是通过Object.defineProperty设置成响应式数据。
			age: 20
		}
	}
}

this.obj.gender = '男';
这时页面不会刷新,因为这种添加属性方式并不是通过Object.defineProperty设置成响应式。
(2).解决方式
(2.1)Vue.set(目标对象,'key', 'value');    // 添加属性较少使用
 (2.2) 新建一个响应式空对象接收。this.newArr = Object.assign(this.arr, {"gender", "男"});  // 添加大量属性使用

10.Vue组件间通信方式有哪些?

(1) props传递(父传子) --> <Father :sendname="name" />  <Child  />中的props属性 --> props: ['sendname'];
(2) $emit传递(子传父) --> <Child /> 中的this.$emit("事件名", 参数)  <Father 事件名="receivedHandle(data)"/>
(3) ref (父使用子的属性和方法) --> <Child ref='foo' />  <Father /> this.$ref.foo 获取子组件的属性和方法
(4) EventBus (兄弟组件通信)  
	新建一个EventBus.js文件,引入-> import vue from 'vue' ,然后暴露实例 export default new Vue;
	在兄弟组件都引入EventBus.js文件,然后<Child1 />中使用bus.$emit('fn', 传递值) <Child2 />中使用bus.$on('fn', 接受值)
	
(5) provid和inject (父传子,父传子孙...) <Father /> provide(){ return { foo: 'foo' } },<Child /> 中的inject:['foo'];
(6) vuex --> 适用场景: 复杂关系的组件数据传递

11.Vue中的$nextTick有什么作用?

{{ num }}
for(let i = 0; i < 1000; i++) num++;
当num发生变化时,vue并不会去立即更新,而是放入一个异步队列中,等待所有循环都执行完毕,DOM才会更新。如果没有$nextTick机制,每次循环页面都会刷新,有机制就只会刷新一次。

12.说说Vue中key的原理

key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点。

13.keep-alive的理解

1.vue的内置组件,能在组件切换中将状态保存在内存中,防止重复渲染。
2.包裹组件时,会缓存不活动的组件实例,不会销毁它。
3.设置props属性
	--> 3.1 include -><keep-alive include="name"><keep-alive/> 名称匹配会被缓存。
	--> 3.2 exclude -><keep-alive exclude="name"><keep-alive/> 名称匹配不会被缓存。
	--> 3.1 max -> 最多缓存几个组件实例

14.Vue的常用修饰符

1.lazy  <input type="text" v-model.lazy="value"> -> 失焦才会触发更新
2.trim  <input type="text" v-model.trim="value"> -> 去掉首空格
3.number  <input type="text" v-model.number="value"> -> 移动端只会弹出数字键盘,web端会把不是数字和后面的所有去掉
4.stop  <div @click="btnFn"><button @click.stop="btnFn">点击</button></div> -> 阻止事件冒泡
5.prevent  <form v-on:submit.prevent="onSubmit"></form> -> 阻止事件默认行为
6.self  <div @click.self="btnFn"></div> -> 只有点击e.target目标元素才会触发函数
7.once	<div @click.once="btnFn"></div> -> 事件只触发一次,第二次不会触发
8.keyCode  <div @keyup.keyCode="btnFn"></div> -> 按键为keyCode就会触发事件
9.left.right.middle  <div @click.left/right/middle="btnFn"></div> -> 鼠标点击左、右、中键就会触发事件

15.什么是虚拟DOM?为什么需要虚拟DOM?

1.是对真实DOM的抽象,以js对象(虚拟节点)作为基本的树,用对象的属性来描述节点,通过一系列操作使这棵树映射到真实环境
2.在js对象中,虚拟DOM表现为一个Object对象,并且最少包含标签(tag)、属性(attr)、子对象元素(children),不同框架不同区别。
3.目的:更好的将虚拟的节点渲染到页面视图中,虚拟DOM对象的节点与真实DOM的属性一一对应。Vue可以对抽象树进行新增、修改、删除的操作,经过diff算法得出最小单位,最后渲染到页面,减少了DOM操作,提高了性能。

为什么需要虚拟DOM? 
-> 原因:操作DOM是非常慢的,大部分的性能问题,基本上都是操作ODM引起的,频繁操作可能会影响页面卡顿,非常影响用户的体验。
例子:原生api与vue同时更新十次数据。
-> 原生会直接渲染十次DOM节点。
-> Vue使用虚拟DOM,不会立即操作DOM,将这10次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,避免大量的无谓计算。
虚拟DOM的优势:(1)diff算法,减少js操作真实DOM带来的性能消耗。(2)抽象了原本的渲染过程,实现了跨平台能力,不仅仅浏览器的DOM,安卓和ios的原生组件、小程序、GUI等。

16.说说Vue中的diff算法

1.diff算法是一种比较同层树节点的高效算法
2.特点
	(1)只会比较同层级,不会跨层级比较
	(2)循环从两端向中间比较。 -> 旧节点的末端与新节点的始端
3.总结:深度优先,同层比较。

17.Vue项目怎么划分结构和划分组件比较合理呢?

1.为什么要划分?
	(1)项目结构清晰会提高开发效率,熟悉项目的各种配置开发效率会更高。
	(2)划分项目结构的基本原则。
		-> 文件夹和文件夹内部文件的语义一致性。如pages,只包含路由模块,不该有其他模块,好处:一眼就能看到项目的路由有哪些。
		-> 单一入口/出口。如:把所有请求接口的函数都放在一个js文件内,使用时只需要导入同一个文件。好处:无论你的模块文件夹内部有多乱,外部引用的时候,都是从一个入口文件引入,这样就很好的实现了隔离,如果后续有重构需求,你就会发现这种方式的优点
		-> 就近原则,紧耦合的文件应该放到一起,且应以相对路径引用。 好处:提高移植性。
		-> 公共的文件应该以绝对路径的方式从根目录引用。如:更改公共组件目录结构,只需要全局搜索替换。
		-> /src 外的文件不应该被引入。好处: 方便划分项目代码文件和配置文件

18.跨域问题

1.JSONP
	(1)核心: 动态添加<script>标签来调用服务器提供的js脚本。
	(2)原理: 利用script的src属性跨域引用文件,实现跨域请求,前端提前定义一个callback函数,服务端把所有json数据放在				  callback里面并执行函数,浏览器在解析script标签把数据当作参数,传入callback函数里。
	(3)缺点: 只支持get请求。
2.cors
	(1)使用:后端实现cors就可以实现完整的跨域。以下node为例子:
	app.use(function (req, res, next) {
        res.set({
            "Access-Control-Allow-Origin": "www.a.com", // 请求的URL
            "Access-Control-Allow-Credentials": true, // 对于跨域 XMLHttpRequest或 Fetch 请求,浏览器不会发送身份凭证信													    息。如果要发送凭证信息,需要设置为true。
            "Access-Control-Allow-Headers": "Content-Type, Content-Length, Authorization, 'Origin', Accept, X-				Requested-With",  // 请求允许的请求头
            "Access-Control-Allow-Methods": "*", // 请求允许的方法
            "Access-Control-Max-Age": 86400 // 指定预检请求的缓存时间
        })
        next();
    });
3.proxy
	(1)网络代理
	(2)三种方式
		(2.1)使用vue-cli的项目中在vue.config.js文件中添加。
                 module.exports = {
                    devServer: {
                        host: '127.0.0.1',
                        port: 8084,
                        open: true,// vue项目启动时自动打开浏览器
                        proxy: {
                            '/api': { // '/api'是代理标识,用于告诉node,url前面是/api的就是使用代理的
                                target: "http://xxx.xxx.xx.xx:8080", //目标地址,一般是指后台服务器地址
                                changeOrigin: true, //是否跨域
                                pathRewrite: { // pathRewrite 的作用是把实际Request Url中的'/api'用""代替
                                    '^/api': "" 
                                }
                            }
                        }
                    }
                }
		(2.2)后端配置代理,以express框架为例
                 var express = require('express');
                 const proxy = require('http-proxy-middleware')
                 const app = express()
                 app.use(express.static(__dirname + '/'))
                 app.use('/api', proxy({ target: 'http://localhost:4000', changeOrigin: false
                                      }));
                 module.exports = app
		(2.3)配置nginx代理
                 server {
                    listen    80;
                    # server_name www.josephxia.com;
                    location / {
                        root  /var/www/html;
                        index  index.html index.htm;
                        try_files $uri $uri/ /index.html;
                    }
                    location /api {
                        proxy_pass  http://127.0.0.1:3000;
                        proxy_redirect   off;
                        proxy_set_header  Host       $host;
                        proxy_set_header  X-Real-IP     $remote_addr;
                        proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
                    }
                }

19.Vue3与Vue2改变了哪些?Vue3的新增特性?

1.Vue3介绍 -->
	(1)速度更快 --> 编译模板的变化、更高效的组件初始化、重写了虚拟DOM、ssr速度提升、update性能提升。
	(2)体积更小 --> webpack的tree-shaking功能,可以将无用模块“剪辑”,仅打包需要的
	(3)更易维护 --> 可与option API一起使用、灵活的逻辑组合和复用、更好的Typescript支持:vue3是基于TS写的
	(4)更接近原生 --> 可以自定义渲染API
	(5)更易使用 --> 响应式API暴露出来、轻松识别组件重新渲染原因
2.Vue3新增特性 -->
	(1)framents 组件支持多个根节点
	(2)teleport 通过Teleport,我们可以在组件的逻辑位置写模板代码,然后在 Vue 应用范围之外渲染它
	(3)createRenderer 能够构建自定义渲染器,我们能够将 vue 的开发模型扩展到其他平台
	(4)composition API 组合式API,更加容易维护我们的代码,将相同功能的变量进行一个集中式的管理
		--> 超链接图片 --> https://static.vue-js.com/6f67a590-5088-11eb-85f6-6fac77c0c9b3.png

20.什么是SSR?Vue中对SSR的解释?

1.由服务器完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程。
2.Vue SSR的解释:
	(1)是一个在SPA上进行改良的服务端渲染、
	(2)需要在客户端激活才能实现交互
	(3)Vue SSR将包含两部分:服务端渲染的首屏,包含交互的SPA
3.解决了什么?
	(1)SEO:搜索引擎优先爬取页面HTML结构,使用ssr时,服务端已经生成了和业务想关联的HTML,有利于SEO
	(2)首屏呈现渲染:用户无需等待页面所有js加载完成就可以看到页面视图(压力来到了服务器,所以需要权衡哪些用服务端渲染,哪些		   交给客户端)

21.Vuex是什么?Vuex有几种属性,它们存在的意义分别是什么?

1.Vuex是一种状态管理模式,存在的目的是共享可复用的组件状态。
2.有五种,分别是 State、 Getter、Mutation 、Action、 Module
	(1)State 单一状态树,相当于data。
	(2)Getter 相当于computed,对State中的数据进行过滤计算。
	(3)mutation 相当于methods,对State中的数据进行状态改变。
	(4)Action 类似于mutation,不是直接改变状态,而是提交mutation,用来解决异步操作。
	(5)Module 将 store 分割成模块(module),每个模块拥有自己的 state、mutation、action、getter,将Vuex模块化的对象,目		的是更好的维护

22.Vuex中的辅助函数怎么使用?

1.vuex 中的 mapState、mapGetters、mapMutations、mapActions 等辅助函数是我们经常使用到的。
2.如何使用?
	(1)mapState:把state属性映射到computed身上。
		computed:{
          ...Vuex.mapState({
            input:state=>state.inputVal,
            n:state=>state.n
          })   
        }

	(2)mapAcions:把actions里面的方法映射到methods中
    	methods:{
            ...Vuex.mapActions({
                add:"handleTodoAdd",    //val为actions里面的方法名称
                change:"handleInput"     
            })
        }
		等价于
        methods: {
            handleInput(e){           
                let val = e.target.value;
                this.$store.dispatch("handleInput",val )
            },
            handleAdd(){
                this.$store.dispatch("handleTodoAdd")
            }
        }

	(3)mapMutations:把mutations里面的方法映射到methods中
    	methods:{
            ...Vuex.mapMutations({
                handleAdd:"handlMutationseAdd"
            })
        }

	(4)mapGetters:把getters属性映射到computed身上
    	 computed:{
            ...Vuex.mapGetters({
                NumN:"NumN"
            })
        }

23.自定义指令是什么?有哪些应用场景?

1.vue中指令比如v-on、v-for等等,自定义指令就是自己封装的指令。
2.注册方式
	(1)全局注册
		// 注册一个全局自定义指令 `v-focus`
        Vue.directive('focus', {
          // 当被绑定的元素插入到 DOM 中时……
          inserted: function (el) {
            // 聚焦元素
            el.focus()  // 页面加载完成之后自动让输入框获取到焦点的小功能
          }
        })
	(2)局部注册
        directives: {
          focus: {
            // 指令的定义
            inserted: function (el) {
              el.focus() // 页面加载完成之后自动让输入框获取到焦点的小功能
            }
          }
        }
	 (3)使用:<input v-focus />
	(4)封装自定义指令  --> 时间
    	(4.1)在src下建立一个direactives文件下,然后定义一个index.js出口文件。
			index.js -> 
                import registerFormatTime from './formatTime'
                export default function registerDireactives (app) {
                    registerFormatTime(app)
                }
		(4.2)在direactives文件下建立formatTime.js文件为自定义时间指令。
			formatTime.js ->
                import dayjs from 'dayjs'
                export default function (app) {
                    let formatString = "YYYY-MM-DD HH:mm:ss"
                    app.directive("format-time", {
                        created(el, bindings){
                            if(bindings.value) formatString = bindings.value
                        },
                        mounted(el) {
                            const textContent = el.textContent
                            let timestamp = parseInt(textContent)
                            if(textContent.length === 10) timestamp = timestamp * 1000
                            el.textContent = dayjs(timestamp).format(formatString)
                        },
                    })
                }
		(4.3)在main的入口文件中注册
                main.js ->
                    import registerDireactives from './direactives/index'
				   registerDireactives(app)
		(4.4)在组件中使用    <div v-format-time="'YYYY/MM/DD HH:mm:ss'">{{ format }}</div>
3.应用场景 -> 防抖、图片懒加载等。

24. vue-loader做了哪些事情?

1.配置各种loader主要是把.vue文件中的 template、script、style切割,根据不同loader分别解析,最终为浏览器识别的html文件。
2.css scoped -> css 样式只作用于当前组件中的元素,他会给标签生成唯一标识,例如-> data-v-9ea40744

25.Computed和watch的区别

1.computed
	(1)computed的属性都有一个set和get方法,当数据变化时,都会调用set方法。
	(2)computed有缓存效果,只有计算的属性发生变化时computed才会重新计算。
	(3)当computed是一个函数时,默认会走get方法,必须要有一个返回值,返回值就是定义函数属性的值。
		computed:{
			fillName () {
				return this.firstName + this.lastName;  // 相当于get方法
			}
		}
	(4)当computed是对象时,需要同时给set和get方法。
		computed: {
            fillName : {
				get () {
                    return this.firstName + this.lastName;
                  },
                  set (val) {  // val -> 当数据发生变化时,传入的新值
					const names = val ? val.split('') : [];
                      this.firstName = names[0];
                      this.lastName = names[1];
                  }
            }
        }
2.watch
	(1)监听data、props、computed的数据变化。
	(2)支持异步,不支持缓存,监听数据发生变化会触发相应操作。
     (3)有两个参数,第一个是新值,第二个是旧值。
    	watch : {
            fillName (newValue, oldValue) {
                console.log(newValue);
                console.log(oldValue);
            }
        }

26.react和vue的区别

1.react是通过jsx语法编写,vue是通过模板引擎。
2.react是函数式的思想,单向流思想, vue是响应式的思想。
3.react的性能优化需要手动修改,vue的性能优化是自动的,但是响应式机制也有问题,当state特别多的时候,watcher会很多,导致卡顿。

27.说说vue的页面渲染流程?

1. _init -> $mount -> vm._render(VNOde) -> vm._update -> patch -> createElm
2. 总结:初始化调用$mount挂在组件,render树通过createElmenet方法构建虚拟节点,构建完成后传给_update函数,patch阶段根据VNOde创建真实节点树并挂载到页面上。

28.vue中history模式和hash模式的区别?

1.hash
	(1)hash模式是把前端路由的路径用'#'拼接在真实路径后面。
		http://localhost:8080/#/index.html
	 (2)优缺点:
	 	(2.1) -> 浏览器兼容性好,IE8都支持。
	 	(2.2) -> 比较丑
2.history
	(1)直接拼接在真实的路径后面 		http://localhost:8080/index.html
	(2)优缺点
		(2.1) 路径比较正规,没有井号
		(2.2) 兼容性不如 hash,且需要服务端支持,否则一刷新页面就404了

29:Vue中的this.$route和this.$router的区别

1.this.$router
	this.$router获取的是router实例,通过 this.$router 访问路由器,相当于获取了整个路由文件,在$router.option.routes中,或		查看到当前项目的整个路由结构 具有实例方法
	// 导航守卫
    router.beforeEach((to, from, next) => {
      /* 必须调用 `next` */
    })
    router.beforeResolve((to, from, next) => {
      /* 必须调用 `next` */
    })
    router.afterEach((to, from) => {})

    // 动态导航到新路由
    router.push
    router.replace
    router.go
    router.back
    router.forward
2.this.$route
	当前激活的路由信息对象。这个属性是只读的,里面的属性是 immutable (不可变) 的,不过可以 watch (监测变化) 它。
    通过 this.$route 访问的是当前路由,获取和当前路由有关的信息
    fullPath: ""  // 当前路由完整路径,包含查询参数和 hash 的完整路径
    hash: "" // 当前路由的 hash 值 (锚点)
    matched: [] // 包含当前路由的所有嵌套路径片段的路由记录 
    meta: {} // 路由文件中自赋值的meta信息
    name: "" // 路由名称
    params: {}  // 一个 key/value 对象,包含了动态片段和全匹配片段就是一个空对象。
    path: ""  // 字符串,对应当前路由的路径
    query: {}  // 一个 key/value 对象,表示 URL 查询参数。跟随在路径后用'?'带的参数
posted @ 2022-04-27 17:05  SKa-M  阅读(190)  评论(0编辑  收藏  举报