前端面试题
js相关
let和const
let es6新增了let声明变量的方式,和var类似
let 变量
const用来声明常量
和var相比 就是 不允许变量提升,存在暂时性死区,不允许重复声明,块级作用域
解构赋值
1 | // 数组,使用的时候名字可以任意 |
模版字符串
1 | //方便了字符串的拼接,使用反引号语法``,反引号内部可以通过${}插入表达式 |
字符串的 includes
1 | //用来查找一个字符串中是否包含另一个字符串,返回值为布尔 |
字符串的 startsWith endsWith
用来判断某字符串是否以某字符串开头 用来判断某字符串是否以某字符串结尾
函数-尖头函数
1 | function fn() {} |
函数-形参默认值 函数-形参解构 函数-形参剩余参数
1 | //函数-形参解构 |
扩展运算符
1 | const a = [1, 2, 3, 4, 5] |
对象简写
1 | const a = 1 |
对象方法简写
1 | const obj = { |
对象key-使用表达式
1 | let name = '张三' |
Object.assign
相当于一个浅拷贝 拷贝的是地址
Object.create
创建一个对象,并将对象的proto指向参数对象
1 | const source = { b: 4, c: 5, obj: { a: 1 } }; |
Promise
ES6模块化: 规范
commonJs模块
导出:module.exports
导入: require()
其实就是把不同的功能 分配不同js文件
web过渡时期的产品
AMD: AMD加载完模块后,就立马执行该模块 代表(require.js)
CMD:CMD加载完某个模块后没有立即执行而是等到遇到require语句的时再执行(Sea.js)
web现在流行es6
1 | // 单纯的运行某个文件 |
默认导出,默认导入
1 | console.log('配置路由') |
注意: ⚠默认导出只能使用一次,所以也就是为什么默认引入可以使用任意名字接收
按需导出,按需导入
1 | export const a = 1 |
注意:⚠导入的名字默认必须要和导出的名字一致,并用花括号括起来
按需导入可以起别名
1 | import { a as a1, b } from './router.js' |
同时引入默认和按需
1 | import user, { b, c } from './router.js' |
注意:⚠语法必须为import 默认导入, {}按需导入 from ‘路径’
全部引入
1 | import * as moduleRouter from './router.js' |
注意:⚠返回值是一个对象,按需导出的变量添加到对象中的key,值会成为对象中的value,默认导出的key固定为default,值为导出的对象
null 和 undefind的区别
简单区分
总的来说 null
和 undefined
都代表空,主要区别在于 undefined
表示尚未初始化的变量的值,而 null
表示该变量有意缺少对象指向。
undefined
- 这个变量从根本上就没有定义
- 隐藏式 空值
null
这个值虽然定义了,但它并未指向任何内存中的对象
- 声明式 空值
vue相关
1.Vue的优点? vue的缺点?
优点: 渐进式,组件化,轻量级,虚拟dom,响应式,单页面路由,数据与视图分开
缺点: 单页面不利于seo,不支持IE8以下,首屏加载时间长
2.为什么说Vue是一个渐进式框架?
渐进式: 就是,你想用啥你就用啥,咱也不强求你。你想用vuex就用,不用也行,你想用component就用,不用也可以,或者你可以选择不用,或者只选几样去用
3. Vue跟React的异同点?
相同点:
- 都使用了虚拟dom
- 组件化开发
- 都是单向数据流(父子组件之间,不建议子修改父传下来的数据) 4.都支持服务端渲染 不同点:
- React的JSx, Vue的template
- 数据变化,React手动(setState),Vue自动(初始化已响应式处理,Object.defineProperty)
- React单向绑定,Vue双向绑定·4.React的Redux,Vue的Vuex
4.MVVM是什么?和MVC有何区别呢?
Mvc
- Model(模型):负责从数据库中取数据.
- View(视图):负责展示数据的地方
- Controller(控制器):用户交互的地方,例如点击事件等等·思想:Controller将Model的数据展示在View 上
MVVM
VM: 也就是View-Model,数据的双向绑定将【模型】转化成【视图】,即后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM事件监听。
区别
整体看来,MVVM 比MVC精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不用再用选择器操作DOM元素。因为在MVVM 中,View不知道 Model的存在,Model和ViewModel也观察不到View,这种低耦合模式提高代码的可重用性 Vue是不是MVVM框架? Vue是MVVM框架,但是不是严格符合MVVM,因为MVVM规定Model和View不能直接通信,而Vue的ref可以做到这点
5. Vue和JQuery的区别在哪?为什么放弃JQuery用Vue?
- jQuery是直接操作DOM,Vue不直接操作DOM,Vue的数据与视图是分开的,Vue只需要操作数据就行
- jQuery的操作DOM行为是频繁的,而Vue利用虚拟DOM的技术,大大提高了更新DOM时的性能
- Vue中不倡导直接操作DOM,开发者只需要把大部分精力放在数据层面上
- Vue集成了一些库,大大提高开发效率,例如Route、Vuex等等
. 为什么data是一个函数 而不是对象
- 之所以data是一个函数,是因为一个组件可能会多处调用,而每一次调用就会执行data函数并返回新的数据对象,这样,可以避免多处调用之间的数据污染。
- 简单理解就是 防止组件被多个页面使用时,造成的变量互相污染
Vue的生命周期,有哪些?
beforeCreate
:实例话Vue,未初始化和响应式数据created
:已初始化和响应式数据,可访问数据beforeMount
:render调用,虚拟DOM生成,未转真实DOMmounted
:真实DOM挂载完成beforeUpdate
:数据更新,新虚拟DOM生成updated
:新旧虚拟DOM进行对比,打补丁,然后进行真实DOM更新beforeDestroy
:实例销毁前,仍可访问数据destroy
:实例销毁,子实例销毁,指令解绑,解绑本实例的事件activated
:keep-alive所缓存组件激活时调用deactivated
:keep-alive所缓存组件停用时调用errorCaptured
:子孙组件的错误捕获,此函数可返回false阻止继续向上传播
. 使用过哪些Vue的修饰符呢?
.once
:事件只触发一次.number
:将v-medol绑定的值转数字.trim
:讲v-model绑定的值首位空格给去掉.stop
:阻止事件冒泡.capture
:事件的捕获.self
:点击事件绑定本身才触发.lazy
:输入框失焦时才会更新v-model的值.prevent
:阻止默认事件.native
:绑定事件在自定义组件上时,确保能执行.left、.middle、.right
:鼠标左中右键的触发passive
:相当于给移动端滚动事件加一个.lazy
camel
:确保变量名会被识别成驼峰命名.sync
:简化子修改父值的步骤
.使用过哪些Vue的内部指令呢?
v-text
:元素的textContentv-html
:元素的innerHTMLv-show
:通过样式display改变显隐,控制的cssv-if
:通过操作DOM改变显隐v-else
:配合v-ifv-else-id
:配合v-elsev-for
:循环渲染v-on
:绑定事件,缩写@v-bind
:绑定变量,缩写:v-model
:双向绑定v-slot
:插槽v-once
:只渲染一次v-pre
:跳过元素编译v-cloak
:隐藏双括号,有值显示
v-if和v-show有何区别?
v-if
:通过操作DOM来控制显隐,适用于偶尔的情况,因为每一次执行它都要生成和销毁,v-if有着更高的切换消耗v-show
:通过改变css样式display属性控制显隐,适用于频繁显隐的情况,不会销毁,v-show在初始渲染消耗更高点- 频繁或者大数量显隐使用v-show ,否则使用v-if
为什么v-if和v-for不建议用在同一标签?
v-for
优先级高于v-if
,每项都通过v-for
渲染出来后再去通过v-if
判断显隐,过程中会增加无用的dom操作,渲染了无用的节点
.组件之间的传值方式有哪些?
- 父传子: 组件使用
props
进行接收 - 子传父: 子组件使用
$emit
+事件对父组件进行传值 - 父子之间通过
$parent
和$chidren
获取实例进而通信 - 使用
$refs
获取组件实例,进而获取数据。使用vuex进行状态管理 - 使用
eventBus
进行跨组件触发事件,进而传递数据 - 使用浏览器本地缓存,例如
localstorage``sessionStorage
路由有哪些模式呢?又有什么不同呢?
- hash模式:通过#号后面的内容的更改,触发hashchange事件,实现路由切换,而不刷新页面
- history模式:通过pushState和replaceState切换url,触发popstate事件,实现路由切换,需要后端配合
路由的钩子函数?
- beforeEach:跳转路由前
- to:将要跳转进入的路由对象
- from:将要离开的路由对象
- next:执行跳转的方法
- afterEach:路由跳转后
- to:将要跳转进入的路由对象 组件内路由钩子
- beforeRouteEnter(to, from, next):跳转到路由渲染组件时触发
- beforeRouteUpdate(to, from, next):跳转到路由且组件被复用时触发
- beforeRouteLeave(to, from, next):跳转到路由且离开组件时触发
如何设置动态class,动态style?
- 动态class对象:
<div :class="{ 'is-active': true, 'red': isRed }"></div>
- 动态class数组:
<div :class="['is-active', isRed ? 'red' : '' ]"></div>
- 动态style对象:
<div :style="{ color: textColor, fontSize: '18px' }"></div>
vuex的有哪些属性?用处是什么?
state
:定义初始状态getter
:从store从取数据mutation
:更改store中状态,只能同步操作action
:用于提交mutation,而不直接更改状态,可异步操作module
:store的模块拆分
watch有哪些属性,分别有什么用?
immediate
:初次加载时立即执行deep
:是否进行深度监听handler
:监听的回调函数
一般在哪个生命周期请求异步数据
- 可以啊钩子函数中的
created
、beforeMount
、mounted
中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。 - 在created中最好
- 能更快获取到服务端数据,减少页面加载时间,用户体验更好;
- SSR不支持 beforeMount 、mounted 钩子函数,放在 created 中有助于一致性。
父子组件生命周期顺序?
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
为什么不建议用index做key,为什么不建议用随机数做key?
- 用index和用随机数都是同理,随机数每次都在变,做不到专一性,也很消耗性能
使用过那些插槽的使用,原理是什么
- 默认插槽 - 又名匿名查抄,当slot没有指定name属性值的时候一个默认显示插槽,一个组件内只有有一个匿名插槽
- 具名插槽:带有具体名字的插槽,也就是带有name属性的slot,一个组件可以出现多个具名插槽。
- 作用域插槽:默认插槽、具名插槽的一个变体,可以是匿名插槽,也可以是具名插槽,该插槽的不同点是在子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件的传递过来的数据决定如何渲染该插槽。 实现原理:当子组件vm实例化时,获取到父组件传入的slot标签的内容,存放在
vm.$slot
中,默认插槽为vm.$slot.default
,具名插槽为vm.$slot.xxx
,xxx 为插槽名,当组件执行渲染函数时候,遇到slot标签,使用$slot
中的内容进行替换,此时可以为插槽传递数据,若存在数据,则可称该插槽为作用域插槽。 。
是否使用过滤器,过滤器的作用,怎么使用
- 顾名思义,过滤器就是用来过滤的数据的,在Vue中使用
filters
来过滤数据,filters
不会修改数据,而是过滤数据,改变用户看到的输出(计算属性computed
,方法methods
都是通过修改数据来处理数据格式的输出显示)。 使用场景: - 需要格式化数据的情况,比如需要处理时间、价格等数据格式的输出 / 显示。
- 比如后端返回一个 年月日的日期字符串,前端需要展示为 多少天前 的数据格式,此时就可以用
fliters
过滤器来处理数据。
使用
- 在插值表达式 \
{{ }}
和v-bind
* 表达式* 中,然后放在操作符“|
”后面进行指示。
自定义指令的钩子函数?
bind
:指令绑定到指定元素时调用,只调用一次inserted
:指定元素插入父节点时调用update
:所在组件的 VNode 更新时调用componnetUpdated
:所在组件以及其子组件 VNode 全部更新后调用unbind
:只调用一次,指令与元素解绑时调用
说说nextTick的用处?
修改数据时不能马上得到最新的DOM信息,所以需要使用nextTick,在nectTick回调中可以获取最新DOM信息
为什么nextTick优先是微任务?
优先是Promise.then
方法,是个微任务,这样可以避免多一次队列,进而少一次UI渲染,节省性能
Vue的SSR是什么?有什么好处?
SSR全称Server Side Render
- 有利于SEO:由于是在服务端,将数据填充进HTML之后再推送到浏览器,所以有利于SEO的爬取
- 首屏渲染快 SSR的缺点:
- 开发条件会受到限制,服务器端渲染只支持beforeCreate和created两个钩子;
- 当需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于Node.js的运行环境;
- 更多的服务端负载。
Vue响应式是怎么实现的?
- 1、劫持:通过
Object.defineProperty
对对象进行递归劫持属性的get、set
- 2、观察者模式:使用
watcher
进行观察数据使用的地方 - 3、发布订阅模式:使用
dep
收集watcher
,数据更改时,通过notify
方法通知dep
里的watcher
去进行相应的更新 - 4、数组:数组没有使用劫持的模式,而是通过重写数组原型上的方法,来实现数组的响应式
为什么只对对象劫持,而要对数组进行方法重写?
数组的元素大概率是成百上千的,所以对数组下标进行劫持的话会非常消耗性能。Vue通过对数组原型上方法的重写,实现数组的响应式
router.push、router.replace、router.go的区别?
router.push
:跳转,并向history栈中加一个记录,可以后退到上一个页面router.replace
:跳转,不会向history栈中加一个记录,不可以后退到上一个页面router.go
:传正数向前跳转,传负数向后跳转
Vue中封装的数组方法有哪些,如何实现页面更新
- 在Vue中,对响应式处理利用的是Object.defineProperty对数据进行拦截,而这个方法并不能监听到数组内部变化,数组长度变化,数组的截取变化等,所以需要对这些操作进行hack,让Vue能监听到其中的变化
- push()
- pop()
- shift()
- unshift()
- solice()
- sort()
- reverse()
请问子组件可以直接改变父组件的数据吗?怎么解决
- 肯定是不可以的,在vue里面为了维护父子组件的单向数据流,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值,如果你去修改,在控制台发出警告
- 可以通过 \
$emit
* 派发一个自定义事件,父组件接收到后,由父组件去修改。*
vue如何监听对象或者数组某个属性的变化
- this.$set(你要改变的数组/对象,你要改变的位置/key,你要改成什么value)
- 调用以下几个数组的方法
1 | splice()、 push()、pop()、shift()、unshift()、sort()、reverse() |
Vue有那些性能优化
(1)编码阶段
- 尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
- v-if和v-for不能连用
- 如果需要使用v-for给每项元素绑定事件时使用事件代理
- SPA 页面采用keep-alive缓存组件
- 在更多的情况下,使用v-if替代v-show
- key保证唯一
- 使用路由懒加载、异步组件
- 防抖、节流
- 第三方模块按需导入
- 长列表滚动到可视区域动态加载
- 图片懒加载
(2)SEO优化
- 预渲染
- 服务端渲染SSR
(3)打包优化
- 压缩代码
- Tree Shaking/Scope Hoisting
- 使用cdn加载第三方模块
- 多线程打包happypack
- splitChunks抽离公共文件
- sourceMap优化
(4)用户体验
- 骨架屏
- PWA
- 还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。