面试题

165 阅读15分钟

vue双向绑定原理

Observer(数据监听器) Watcher(订阅者) Compile(指令解析器

通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,
最终 利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化->视图更新。
在 初始化vue实例时,遍历data这个对象,给每一个键值对利用Object.definedProperty 对data的键值对新增getset方法,
利用了事件监听DOM的机制,让视图去改变数据。

底层实现原理:vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter和getter,在数据变动时发布消息给订阅者,触发相应的监听回调

MVVM&&MVC

1、mvvm各部分的通信是双向的,而mvc各部分通信是单向的
2、mvvm是真正将页面与数据逻辑分离放到js里去实现,而mvc里面未分离。

MVVM: 包括view视图层、model数据层、viewmodel层。各部分通信都是双向的。
      采用双向数据绑定,View的变动,自动反映在 ViewModel,反之亦然。其中ViewModel层

MVC: 包括view视图层、controller控制层、model数据层。各部分之间的通信都是单向的。
      View 传送指令到 ControllerController 完成业务逻辑后,要求 Model 改变状态Model 将新的数据发送到 View页面

Vue 中的 key 到底有什么用

key 是给每一个 vnode 的唯一 id,依靠 key,我们的 diff 操作可以更准确、更快速
diff 算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的 key 与旧节点进行比对,从而找到相应旧节点.

VUE组件间通信

props和$emit
bus传值
vuex
children和parent
provide和inject

vue生命周期

vue.js的生命周期一共有10个:
    beforeCreate:实例初始化之前,this指向创建实例,不能访问到data、method上订单方法和数据
    created:实例创建完成,可访问data、computed、watch、method上的方法和数据,未挂载到DOM,不能操作dom,不能访问到$el属性,$ref属性内容为空数组
    beforeMount:在挂载开始之前被调用,beforeMount之前,会找到对应的template,并编译成render函数
    mounted:实例挂载到DOM上,此时可以通过DOMAPi获取到DOM节点,$ref属性可以访问
    beforeUpdate:响应式数据更新时调用,发生在虚拟DOM打补丁之前
    updated:虚拟DOM重新渲染和打补丁之后调用,组件DOM已经更新,可执行依赖于DOM的操作
    activated:keep-alive开启时调用
    deactivated:keep-alive关闭时调用
    beforeDestroy:实例销毁之前调用。实例仍然完全可用,this仍能获取到实例
    destroy:实例销毁后调用,调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁

keep-alive

keep-alive 是 Vue 内置的一个组件,他可以使被包含的组件保留状态,避免重新渲染 ,有以下特性:

1、一般结合路由和动态组件一起使用,用于缓存组件。

2、提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹 配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高。

3、对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated, 当组件被移除时,触发钩子函数 deactivated。
。

vue的diff算法

Virtual Dom 根据真实DOM生成的一个Virtual DOM,当Virtual DOM某个节点的数据改变后生成一个新的Vnode,然后Vnode和oldVnode作对比,发现有不一样的地方就直接修改在真实的DOM上,然后使oldVnode的值为Vnode.

注意:在采取diff算法比较的时候,只会在同层级进行,不会跨层级比较。

vue-router导航守卫

1. 全局守卫钩子 
2. 独享守卫钩子 
3. 路由组件守卫钩子

二、全局守卫钩子(在路由切换全局执行)

全局守卫钩子函数有三种:

const router = new VueRouter({....})
// 全局前置守卫
router.beforeEach((to, from, next) => {
	// do something
})
// 全局解析守卫
router.beforeResolve((to, from, next) => {
	// do something
})
// 全局后置守卫
router.afterEach((to, from) => {
	// do something
})
注意:
	to:route即将进入的路由
	from:route即将离开的路由
	nextfunction这个是必须要调用的
	next()接受参数:
  next():直接执行下一个钩子,如果执行完了导航状态为comfirmed状态
	next(false):中断当前导航,回到from的位置
	next('/hello')或next({path:'/hello'}):路由到任意地址,可以携带参数等
	next(error):会回到router.onError(callback)

三、独享守卫钩子

独享守卫是定义在单独的某一个路由里的

const router = new VueRouter({
	routes: [
		{
			path: '/foo',
			component: Foo,
			beforeEnter: (to, from, next) => {
				// do something
		},
		beforeLeave: (to, from, next) => {
			// do something
		}
	}
]
})

四、路由组件守卫钩子

路由组件即是在vue-router中注册的组件叫路由组件

beforeRouteEnter(to, from, next) {}
beforeRouteUpdate(to, from, next) {}
beforeRouteLeave(to,from, next){}

组件中为什么data是一个函数

因为组件是需要被复用的,所以必须是一个函数,
如果是一个对象,作用域没有分开,子组件 的data属性值会相互影响。
是一个函数的话那么每个实例可以维护一份被返回对象的独立的拷贝,组 件之间的 data 属性值不会互相影响

vuex

Vuex 是一个专为 Vue 开发的状态管理器。

 1. State:定义了应用的状态数据
 2. Getter:在 store 中定义“getter”(可以认为是 store 的计算属性),就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算
 3. Mutation:是唯一更改 store 中状态的方法,且必须是同步函数
 4. Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作
5. Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中

三次握手四次挥手

三次握手:

1、客户端发送一个SYN码给服务器,要求建立数据连接;
2、服务器拿到SYN和自己处理一个SYN(标志);叫SYN+ACK(确认包);发送给客户端,可以建立连接;
3、客户端再次发送ACK向服务器,服务器验证ACK没有问题,则建立起连接;

四次挥手:

1、客户端发送FIN(结束)报文,通知服务器数据已经传输完毕;
2、务器接收到之后,通知客户端我收到了FIN,发送ACK(确认)给客户端,数据还没有传输完成
3、服务器已经传输完毕,再次发送FIN通知客户端,数据已经传输完毕
4、客户端再次发送ACK,进入TIME_WAIT状态;服务器和客户端关闭连接;

建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个 报文里发送给客户端。而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了 但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一 些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分 开发送,从而导致多了一次。

null和undefined的区别

undefined是访问一个未初始化的变量时返回的值,
而null是访问一个尚未存在的对象时所返回的值。

构造函数

构造函数模式的目的就是为了创建一个自定义类,并且创建这个类的实例。构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立的,即实例识别。构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。

另外就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。
function Person(name, age, gender) {    //创建一个构造函数 
        this.name = name
        this.age = age
        this.gender = gender
        this.sayName = function () {    //定义构造函数中的一个方法
            alert(this.name);
        }
    }
    var per = new Person("孙悟空", 18, "男");     //调用创建的构造函数,必须用new关键字调用console.log(per)            //当我们直接在页面中打印一个对象时,事件上是输出的对象的toString()方法的返回值//toSting(): 可以把一个Number转换为字符串

原型对象

原型对象:

原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中在JavaScript中。每当定义一个函数数据类型(普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型的值。
原型出现的目的就是为了减少不必要的内存消耗。

image.png

在JavaScript中是使用构造函数来新建一个对象的,每一个构造函数的内部都有一个 prototype 属性,它的属性值是一个对象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法。当使用构造函数新建一个对象后,在这个对象的内部将包含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针被称为对象的原型。一般来说不应该能够获取到这个值的,但是现在浏览器中实现了 __proto__属性来访问这个属性,但是最好不要使用这个属性,因为它不是规范中规定的。ES5 中新增了一个 Object.getPrototypeOf() 方法,可以通过这个方法来获取对象的原型

原型链:

当我们实例化PersonB得到实例化对象,访问实例化对象的属性时会触发get方法,它会先在自身属性上查 找,如果没有这个属性,就会去__proto__中查找,一层层向上直到查找到顶层对象Object,这个查找的过程 就是原型链来。

image.png

闭包

function foo() {
    var a = 1;
    return function() {
        console.log(a);
    }
}

var bar = foo();
bar();

闭包简单来说就是函数嵌套函数,内部函数引用来外部函数的变量,从而导致垃圾回收 机制没有把当前变量回收掉,这样的操作带来了内存泄漏的影响,当内存泄漏到一定程度会影响你的项目运行 变得卡顿等等问题。因此在项目中我们要尽量避免内存泄漏。

this

`this`对象是是执行上下文中的一个属性,它指向最后一次调用这个方法的对象,在全局函数中,`this`等于`window`,而当函数被作为某个对象调用时,this等于那个对象。 在实际开发中,` this  `的指向可以通过四种调用模式来判断。

1.  函数调用,当一个函数不是一个对象的属性时,直接作为函数来调用时,`this`指向全局对象。
1.  方法调用,如果一个函数作为一个对象的方法来调用时,`this`指向这个对象。
1.  构造函数调用,`this`指向这个用`new`新创建的对象。
1.  第四种是 ` apply 、 call 和 bind  `调用模式,这三个方法都可以显示的指定调用函数的 this 指向。`apply`接收参数的是数组,`call`接受参数列表,`` bind`方法通过传入一个对象,返回一个` this ` 绑定了传入对象的新函数。这个函数的  `this`指向除了使用`new `时会被改变,其他情况下都不会改变。

作用域,作用域链

作用域:全局作用域、局部作用域

作用域就是变量与函数的可访问范围

作用域链:

当所需要的变量在所在的作用域中查找不到的时候,它会一层一层向上查找。这种一层一层的关系,就是作用域链。

节流和防抖

防抖:在一定时间内多次点击查询时,他只会在你最后一次点击以后, 采取执行查询操作
节流:当你点击后的一段时间内,再次点击是不启作用的,因为有一个冷却时间

文字、盒子水平垂直居中

行内元素水平垂直居中:

text-align:center /*水平居中*/ 
height = 100px; /*垂直居中 */ 
line-height = 100px;

块级元素水平居中:

父级元素设置position:relative;相对低挡位
儿子元素设置
width: 100px;  
height: 100px;  
position:absolute;  
top:50%;  
left:50%;  
margin-top:-50px;  
margin-right:-50px;  

父级元素设置position:relative;
儿子元素设置
position:absolute;  
top:0;  
bottom:0;  
left:0;  
right:0;  
margin:auto;

React生命周期

1、挂载卸载过程
-   constructor()
-   componentWillMount()
-   componentDidMount()  : ajax请求
2. 更新过程
-   componentWillReceiveProps (nextProps) :接受父组件改变后的props需要重新渲染组件
-   shouldComponentUpdate(nextProps,nextState) :
-   componentWillUpdate (nextProps,nextState)
-   componentDidUpdate(prevProps,prevState)
-   render()
3. 卸载
-   componentWillUnmount ()

Vue和React的区别

1、vue实现了数据和视图的双向绑定,view视图变化时modal数据发生变化,modal数据变化也能实时更新到视图上;react是单向数据流,如果需要双向实现可以借助setState来修改数据,从而更新页面;

vue2和vue3区别

1、vue2和vue3双向数据绑定原理的区别:

vue2 的双向数据绑定是利用ES5 的一个 [API] Object.definePropert()对数据进行劫持 结合 发布订阅模式的方式来实现的
vue3发生了改变,使用proxy替换Object.defineProerty,使用Proxy的优势:
    可直接监听数组类型的数据变化
    性能的提升
    监听的目标为对象本身,不需要像Object.defineProperty一样遍历每个属性,有一定的性能提升
    可直接实现对象属性的新增/删除

2、根节点的不同

<template>             ----->     <template>
    <div>              ----->        <div>  <h1></h1>  </div>
        <h1></h1>      ----->        <div>  <h1></h1>  </div>   
    </div>             ----->     </template>   
</template>            ----->       
                     

3、Composition API (组合api)

<script>
export default {     ---->     export default {
	// 数据      ---->         setup() {
    data() {         ---->           // 数据 和 方法都在setup里面使用
        return {};   ---->         }
    },               ---->     };
    mounted() {},    ---->
    // 方法          ---->
    methods: {},     ---->
    computed: {},    ---->
    components:{}    ---->
};
</script>

4、生命周期的变化

beforeCreate  -> setup()	开始创建组件之前,创建的是data和method
created       -> setup()
beforeMount   -> onBeforeMount	组件挂载到节点上之前执行的函数。
mounted       -> onMounted	组件挂载完成后执行的函数
beforeUpdate  -> onBeforeUpdate	组件更新之前执行的函数。
updated       -> onUpdated	组件更新完成之后执行的函数。
beforeDestroy -> onBeforeUnmount	组件挂载到节点上之前执行的函数。
destroyed     -> onUnmounted	组件卸载之前执行的函数。dszhuoyi
activated     -> onActivated	组件卸载完成后执行的函数
deactivated   -> onDeactivated


5、diff算法

vue2: diff算法就是进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地 方,最后用patch记录的消息去局部更新Dom。vue2 diff算法会比较每一个vnode,而对于一些不参与更新的元素,进行比较是有点消耗性能的
vue3: diff算法在初始化的时候会给每个虚拟节点添加一个patchFlags,patchFlags就是优化的标识。只会比较patchFlags发生变化的vnode,进行更新视图,对于没有变化的元素做静态标记,在渲染的时候直接复用。

6、v-if 和 v-for的优先级

vue2: 我们最好不要把v-if和v-for同时用在一个元素上,这样会带来性能的浪费(每次都要先渲染才会进行条件判断)

v-for 优先于 v-if 生效
<div v-if="index == 1" v-for="(item,index) in arr" :key="index">{{item}}</div>

vue3:
v-if 优先于 v-for 生效
<div v-if="index == 1" v-for="(item,index) in arr" :key="index">{{item}}
</div>

HTTP 和 HTTPS 区别

1.`HTTP` 的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头
2.`HTTP` 是不安全的,而 HTTPS 是安全的
3.`HTTP` 标准端口是80 ,而 HTTPS 的标准端口是443
4.`在OSI` 网络模型中,HTTP工作于应用层,而HTTPS 的安全传输机制工作在传输层
5.`HTTP` 无法加密,而HTTPS 对传输的数据进行加密
6.`HTTP`无需证书,而HTTPS 需要CA机构wosign的颁发的SSL证书

get 和 post 区别

1.GET在浏览器回退不会再次请求,POST会再次提交请求
2.GET请求会被浏览器主动缓存,POST不会,要手动设置
3.GET请求参数会被完整保留在浏览器历史记录里,POST中的参数不会
4.GET请求在URL中传送的参数是有长度限制的,而POST没有限制
5.GET参数通过URL传递,POST放在Request body中
6.GET参数暴露在地址栏不安全,POST放在报文内部更安全
7.GET一般用于查询信息,POST一般用于提交某种信息进行某些修改操作
8.GET产生一个TCP数据包;POST产生两个TCP数据包

webpack

module.exports={
    //入口文件的配置项
    entry:{},
    //出口文件的配置项
    output:{},
    //模块:例如解读CSS,图片如何转换,压缩
    module:{},
    //插件,用于生产模版和各项功能
    plugins:[],
    //配置webpack开发服务功能
    devServer:{}
}
简单描述了一下这几个属性是干什么的。

Css3新特性

1.过渡 transition
2.动画 animation
3.形状转换 transform
4.阴影 box-shadow
5.滤镜 Filter
6.颜色 rgba
7.文字换行 word-wrap
8.渐变:gradient
9.栅格布局 gird
10.弹性布局 flex
等等还多...

前端性能优化

1. 文件采用按需加载等等
2. 图片优化,采用svg图片或者字体图标
3. 组件封装
4. 缓存
5. 减少http请求次数
6. 样式减少style的使用,可以用class