听说成年人的自尊都是工资给的,我已经没有自尊两个多月了,哈哈哈
疫情影响很多小伙伴都被破离职了,下面整理了一些常见面试题,可以大家一起分享,程序员小白一个,如有问题,欢迎大佬们随时指点呀~~
1.前端优化可以做什么?
2.http和https的区别?
3.ajax的理解和优缺点?
4.什么是MVVM?
5.v-if和v-show的区别?
6.总结生命周期钩子函数
7.vuex的五个核心概念
8.vue路由钩子函数
9.什么情况会造成跨域,以及怎么解决跨域?
10.hash与history的区别?
11.vue的内容分发
12.深拷贝和浅拷贝的区别?
13.v-text、v-html(重点) 、v-pre 区别及用法
14.vue双向绑定原理
15.for循环结构遍历数组与key的作用分析
16.钩子函数参数
17.如何自定义指令指令?
18.父子组件传值以及非父子组件传值
19.computed计算属性与method方法的区别?
20.渐进增强和优雅降级之间的不同吗?
21.请描述一下cookies,sessionStorage和localStorage的区别?
22.清除浮动
23.什么是闭包,如何使用它,为什么要使用它?
24.JavaScript宿主对象和原生对象的区别?
25.call和.apply的区别是什么?
26.==和===有什么不同?
27.http和https有什么区别?
28.节流防抖
29.es6的新语法你了解哪些?
30.get和post的区别?
一、前端优化可以做什么?
1.减少http请求:
合并js和css文件(使用webpack或其它打包工具打包)
小图片使用雪碧图(需要的图片和ui说下,交给ui做就可以)
使用base64表示简单的图片
2.用内容分发网络cdn
cdn主要用于静态文件,如css,js和flash。
cdn,自动寻找最近的物理机服务器下载web组件
3.简js和css
使用jsmint和gzip精简文件,css精简技术点:#660066 优化 #606 ,提取 css ,js 公共方法,使用0代替0px
4.资源体积减少
gzip压缩
js混淆
css压缩
图片压缩
5.图片加载处理
图片懒加载
首屏加载时进度条的显示
懒加载详细:最初给图片的src设置一个比较简单的图片,然后将图片的真实地址设置给自定义的属性,做一个占位,然后给图片设置监听事件,一旦图片到达视口范围,从图片的自定义属性中获取出真是地址,然后赋值给src,让其进行加载。
首屏加载时进度条的显示:往对于首屏优化后的数据量并不满意的话,同时也不能进一步缩短首屏包的长度了,就可以使用进度条的方式,来提醒用户进行等待。
6.移动端优化
长列表滚动优化
函数防抖和函数节流
使用touchstart、touchend代替click
HTML的viewport设置
长列表滚动:ios尽量局部滚动,Android尽量全局滚动,同时需要给body添加上-webkit-overflow-scrolling: touch来优化移动段的滚动
防抖:止频繁滑动或者重复点击,当调用动作过了n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。
节流:预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期。
touchstart、touchend代替click:click在移动端会有300ms延时,事件执行顺序是touchstart->touchmove->touchend->click
HTML的viewport设置:可以防止页面的缩放,来优化性能。
二、http和https的区别?
1.https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2.http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3.http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4.http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
三、ajax的理解和优缺点?
ajax是无须进行刷新页面就可以请求后台的数据的一种方法,通过XmlHttpRequest对象来向服务器发送异步请求,从服务器中获取数据,然后通过js进行操作dom,以此来更新页面内容。
实现过程:
1.创建一个XmlHttpRequest的对象
2.设置响应HTTP请求的回调函数
3.创建一个HTTP请求,指定响应的请求方法、url、参数等
4.发送HTTP请求
5.获取服务端返回的数据
6.使用js操作dom更新页面的内容
缺点:
1.对搜索引擎不友好
2.要实现Ajax下的前后退功能成本较大
3.跨域问题限制
四、什么是MVVM?
M、V、VM 分别代表什么?
m model
数据层 Vue 中 数据层 都放在 data 里面
v view 视图
Vue 中 view 即 我们的HTML页面
vm (view-model) 控制器 将数据和视图层建立联系
vm 即 Vue 的实例 就是 vm
总结:
1、mvvm是把整个项目划分为三大块
2、vue实例vm是数据模型data和视图模板view的桥梁
3、当数据改变的时候,vm会通知视图进行改变
4、当视图改变的时候,如果用的是v-model,它会通知数据改变
5、总结:mvvm写代码的原则
(1) 只关心数据,不关心视图的DOM结构
(2) 数据和视图是一一对应的
(3) 不要去操作DOM,而是去操作数据
(4) 因为数据是响应式的
(5) 只要数据变了,视图自然就会跟着变
MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑
MVVM模式和以前的MVC模式一样,主要目的是分离(View)和模型(Model),有几大优点:
- 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
- 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
- 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。
- 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
五、v-if和v-show的区别?
v-if 是“真正的”条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下, v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说, v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件不太可能改变,则使用 v-if 较好。
六、总结生命周期钩子函数
1.beforeCreate:在实例初始化之后,**数据观测(data observer) ** 和 event/watcher事件配置 之前被调用,注意是 之前,此时data、watcher、methods统统滴没有。
这个时候的vue实例还什么都没有,但是$route对象是存在的,可以根据路由信息进行重定向之类的操作。
2.created:在实例已经创建完成之后被调用。在这一步,实例已完成以下配置:数据观测(data observer) ,属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el属性目前不可见。
此时 this.$data 可以访问,watcher、events、methods也出现了,若根据后台接口动态改变data和methods的场景下,可以使用。
3.beforeMount:在挂载开始之前被调用,相关的 render 函数 首次被调用。但是render正在执行中,此时DOM还是无法操作的。我打印了此时的vue实例对象,相比于created生命周期,此时只是多了一个$el的属性,然而其值为undefined,
页面渲染时所需要的数据,应尽量在这之前完成赋值。
4.mounted:在挂载之后被调用。在这一步 创建vm.$el并替换el,并挂载到实例上。
此时元素已经渲染完成了,依赖于DOM的代码就放在这里吧~比如监听DOM事件。
5.beforeUpdate:$vm.data更新之后,虚拟DOM重新渲染 和打补丁之前被调用。
你可以在这个钩子中进一步地修改$vm.data,这不会触发附加的重渲染过程。
6.updated:虚拟DOM重新渲染 和打补丁之后被调用。
当这个钩子被调用时,组件DOM的data已经更新,所以你现在可以执行依赖于DOM的操作。但是不要在此时修改data,否则会继续触发beforeUpdate、updated这两个生命周期,进入死循环!
7.beforeDestroy:实例被销毁之前调用。在这一步,实例仍然完全可用。
实例要被销毁了,赶在被销毁之前搞点事情吧哈哈~
8.destroyed:Vue实例销毁后调用。此时,Vue实例指示的所有东西已经解绑定,所有的事件监听器都已经被移除,所有的子实例也已经被销毁。
这时候能做的事情已经不多了,只能加点儿提示toast之类的东西吧。
注:beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed这几个钩子函数,在服务器端渲染期间不被调用。
七、Vuex的五个核心概念
VueX 是一个专门为 Vue.js 应用设计的状态管理架构,统一管理和维护各个vue组件的可变化状态(你可以理解成 vue 组件里的某些 data )。
Vue有五个核心概念,state, getters, mutations, actions, modules。下面将对这个五个核心概念进行梳理。
state => 基本数据
getters => 从基本数据派生的数据
mutations => 提交更改数据的方法,同步!
actions => 像一个装饰器,包裹mutations,使之可以异步。
modules => 模块化Vuex
State
state即Vuex中的基本数据!
state提供唯一的公共数据源,所有共享的数据都要统一放到store的state中进行存储
Vuex使用单一状态树,即用一个对象就包含了全部的状态数据。state作为构造器选项,定义了所有我们需要的基本状态参数。
在Vue组件中获得Vuex属性
我们可以通过Vue的Computed获得Vuex的state,如下:
每当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。
mapState辅助函数
当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键。
当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
对象展开运算符
mapState 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。但是自从有了对象展开运算符,我们可以极大地简化写法:
对象运算符
... 展开运算符(spread operator)允许一个表达式在某处展开。展开运算符在多个参数(用于函数调用)或多个元素(用于数组字面量)或者多个变量(用于解构赋值)的地方可以使用。
展开运算符不能用在对象当中,因为目前展开运算符只能在可遍历对象(iterables)可用。iterables的实现是依靠[Symbol.iterator]函数,而目前只有Array,Set,String内置[Symbol.iterator]方法,而Object尚未内置该方法,因此无法使用展开运算符。不过ES7草案当中已经加入了对象展开运算符特性。
例子:
ES7草案中的对象展开运算符
ES6中还不支持对对象的展开运算符,但是ES7中将支持。对象展开运算符符可以让我们更快捷地操作对象,如下例子:
组件仍然保有局部状态
使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。
如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。
组件访问state中数据的两种方式
第一种方式:
第二种方式:
getters
即从store的state中派生出的状态。
getters接收state作为其第一个参数,接受其他getters 作为第二个参数,如不需要,第二个参数可以省略如下例子:
与state一样,我们也可以通过Vue的Computed获得Vuex的getters。
mapGetters 辅助函数仅仅是将 store 中的 getters 映射到局部计算属性,与state类似
如果你想将一个 getter 属性另取一个名字,使用对象形式:
mutations
提交mutation是更改Vuex中的store中的状态的唯一方法。
mutation必须是同步的,如果要异步需要使用action。
每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,提交载荷作为第二个参数。(提交荷载在大多数情况下应该是一个对象),提交荷载也可以省略的。
你不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:
对象风格的提交方式
我们也可以使用这样包含 type 属性的对象的提交方式。
Mutations 需遵守 Vue 的响应规则
最好提前在你的 store 中初始化好所有所需属性。
当需要在对象上添加新属性时,你应该
使用 Vue.set(obj, 'newProp', 123), 或者
以新对象替换老对象。例如,利用对象展开运算符我们可以这样写state.obj = {...state.obj, newProp: 123 }
mapMutations 辅助函数
与其他辅助函数类似,你可以在组件中使用 this.$store.commit(‘xxx’) 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。
actions
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
我们用如下例子来结束actions:
注意:Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
*分发actions
Action 通过 store.dispatch 方法触发:
*其他与mutations类似的地方
Actions 支持同样的载荷方式和对象方式进行分发:
*mapActions辅助函数
你在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store):
import { mapActions } from 'vuex'
export default {
//..
methods: {
...mapActions(\[
'incrementN' //映射 this.incrementN() 为 this.$store.dispatch('incrementN')
\]),
...mapActions({
add: 'incrementN' //映射 this.add() 为 this.$store.dispatch('incrementN')
})
}
}
Modules
使用单一状态树,导致应用的所有状态集中到一个很大的对象。但是,当应用变得很大时,store 对象会变得臃肿不堪。
为了解决以上问题,Vuex 允许我们将 store 分割到模块(module)。每个模块拥有自己的 state、mutation、action、getters、甚至是嵌套子模块——从上至下进行类似的分割:
模块的局部状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态,对于模块内部的 getter,根节点状态会作为第三个参数:
同样,对于模块内部的 action,context.state 是局部状态,根节点的状态是 context.rootState:
总结下stateMutationActionGetter
state :所有的共享数据都要统一放到store的state中进行存储
mutation : 用于修改store中的数据
action :因为在mutations中不能写异步代码,所以需要action,它是用于处理异步任务的
Getter : 用于对store中的数据进行加工处理新的数据,注意它并不会改变state中的数据,只是起包装作用,相当于计算属性
vuex 是处理复杂的父子通信时,需要用到的技术。
state中可以定义一些常量,getters中写触发state改变的方法,通常用到getters比较少。
还有两个可以存放方法的地方是actions和mutations,一个是异步,一个是同步,我们一般都用mutations存放一些方法,在页面中通过 this.$store.commit()
方法来触发mutations,从而改变state中的值。
八、vue路由的钩子函数
vue-router 提供的导航钩子主要用来拦截导航,让它完成跳转或取消。
路由钩子分为三类: 全局的、单个路由独享的、或者组件级的
全局钩子: 主要包括beforeEach和afterEach,这类钩子主要作用于全局,一般用来判断权限,以及页面丢失时候需要执行的操作。一般写在main.js里面
beforeEach函数有三个参数:
to:router即将进入的路由对象
from:当前导航即将离开的路由
next:Function,进行管道中的一个钩子,如果执行完了,则导航的状态就是 confirmed (确认的);否则为false,终止导航。
可以使用router.beforeEach注册一个全局的 before 钩子
router.beforeEach((to, from, next) => { // ... })
after 钩子没有 next 方法,不能改变导航
router.afterEach(route => { // ...})
在项目中用到:
1.全局的钩子
2.单个路由里面的钩子
主要用于写某个指定路由跳转时需要执行的逻辑
3.组件内的钩子 (用到的不多)
主要包括 beforeRouteEnter和beforeRouteUpdate ,beforeRouteLeave,这几个钩子都是写在组件里面也可以传三个参数(to,from,next),作用与前面类似.
九、什么情况会造成跨域,以及怎么解决跨域?
造成跨域的四字真言:同源策略
跨域问题是这是浏览器为了安全实施的同源策略导致的,同源策略限制了来自不同源的document、脚本,同源的意思就是两个URL的域名、协议、端口要完全相同
解决跨域
1.JSONP
JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。
核心思想:网页通过添加一个