最后更多分享:前端字节跳动真题解析
如果需要绑定多个不同类型的事件的话,可以以对象的键值对形式,比如在v-on上绑定一个click和mouseover事件,我们可以使用v-on=“{click:方法名,mouseover:方法名}”
如果需要绑定多个相同类型的事件的话,直接以逗号分隔方法名,比如绑定click事件,我们可以写@click="方法1,方法2"
5、 客户端渲染和服务端渲染的区别
客户端渲染和服务端渲染
服务器端通过页面模板和数据生成HTML页面,返回给 客户端完整的html,这样浏览器就可以直接显示了
客户端渲染:后端提供一些api使得前端可以获取json数据,然后前端拿到json数据之后再在前端进行html页面拼接,然后展示在浏览器上
两者本质区别
客户端渲染和服务器端渲染的最重要的区别就是究竟是谁来完成html文件的完整拼接
优缺点分析
客户端:
优点:客户端渲染的优点就是网络传输的数据量比较小,减少了服务端的压力,前后端分离,前后端人员可以分工合作,代码耦合度低,并且前端人员可以有更多的选择性,比如使用单页面,可以有更好的用户体验,
缺点:就是不利于seo的优化,首次加载可能会比较慢,因为要加载很多js,css文件
服务端:
优点:不占用前端的资源 ,页面渲染快,并且有利于seo的优化
缺点:前后端不分离,开发时的耦合度很高,分工不明确,降低开发效率,用户量大的时候,对服务器的负荷也会增大
6、 类组件和函数组件有什么区别
类组件可以使用其他特性,如状态 state 和生命周期钩子。
当组件只是接收 props 渲染到页面时,就是无状态组件,就属于函数组件
函数组件的性能比类组件的性能要高, 因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件。
函数组件没有this,没有生命周期,没有状态state,
类组件有this,有生命周期,有状态state。
7、什么是高阶组件、受控组件、非受控组件?
高阶组件
是个函数,输出结果是个新组件,可以对输入的组件进行加工,并返回一个具有特定功能的组件。
受控组件
相当于input中的value值通过state值获取,onChange事件改变state中的value值。实现了双向绑定,任意一方的数据发生变化,另一方也会随之改变 。
非受控组件
不需要设置对应的state属性,可通过ref来直接操作真实的dom。
8、vuex和redux的区别
表面区别就是vuex是通过将store注入到组件实例中,通过dispatch和commit来维护state的状态,并可以通过mapstate和this.$store来读取state数据。而redux则是需要通过connect将state和dispatch连接来映射state并操作state。redux没有commit,直接通过dispatch派发一个action来维护state的数据。并且只能通过reducer一个函数来操作state。
rudex使用的是不可变数据;vuex是可变的数据。
rudex每次都是返回一个新的state;而vuex是直接改变state。
9、http的缓存机制
关于HTTP的缓存机制来说,这些缓存策略都会体现在HTTP的头部信息的字段上,这些策略会根据是否需要重新向服务器发起请求可以分为强缓存和协商缓存两大类。
强缓存: 请求某个资源文件时,服务端就会在response
header中对该资源文件做缓存配置:cache-control,常见的设置是max-age public private
no-cache no-store immutable等。当用户打开某页面,浏览器会判断缓存是否过期,没有过期就会从缓存中读取数据。
协商缓存: 协商缓存就是需要客户端和服务器两端进行交互的;每次请求回来的response
header中的etag和last-modified;下次请求带上,服务端会进行标识和对比,如果资源更新了就会返回新的资源和对应的etag和last-modified;反之资源没有变。
10、什么是数组扁平化,实现扁平化的方法有哪些?
数组扁平化,就是将一个复杂的嵌套多层的数组,一层一层的转化为层级较少或者只有一层数组。
arr.flat(Infinity)
底层原理:通过foreach遍历和递归的方式进行一层一层的遍历。
arr.toString.split(“,”)
reduce和递归来实现
foreach遍历和递归
11、什么是回流和重绘
回流:当rendertree的一部分或者全部元素因改变了自身的宽高,布局,显示或隐藏,或元素内部的文字结构发生变化,导致需要重新构建页面的时候,回流就产生了。
重绘: 当一个元素自身的宽高,布局,及显示或隐藏没有改变,而只是改变了元素的外观风格的时候,就产生了重绘。
结论:回流必定触发重绘,而重绘不一定触发回流。
12、webpack中在使用babel插件处理js代码的时候,为什么使用polyfill,如何使用polyfill ?
因为在使用preset_env 处理js代码时,无法将所有的ES6的语法全部转换成ES5语法,就比如promise、array.from以及实例方法都无法转换,这个时候需要加入垫片。
在入口文件引入@babel/polyfill ,会污染全局环境
在配置文件中的entry中写 ,也会污染全局环境
可以配置@babel/preset-env useBuiltIns接收3个参数
entry:不管代码 有没有用到,只要目标浏览器不支持的都会引入对应的polyfill;自动引入polyfill模块
usage: 根据代码的使用情况,按需引入;自动引入polyfill模块
false:不会自动引入polyfill模块
corejs 3.0以后的版本; 如果参数为entry,则需要在入口文件中引入两个包
13、map和forEach的区别?
map和forEach都是对数组的遍历,但是他们是有区别的,forEach()方法不会返回执行结果,而是undefined。也就是说,forEach()会修改原来的数组。而map()方法会得到一个新的数组并返回。
例如,在react经常需要使用数据进行渲染元素的时候,那么这时候,我们就需要使用map了,返回一个新的元素的数组进行渲染。而forEach就是不需要返回新的数据,需要在原数组的每一项上修改的时候,我们经常会用到。
14、redux的工作流程?
简明来说,首先创建一个store分别管理相应的reducer,在组件内部派发action之后,看是否需要中间件进行处理,如果是ajax等异步操作,可以放入thunk或者saga等中间件,进行处理之后然后传到reducer之中,如果组件里面需要数据,可以通过connect高阶组件或者getState()获取reducer里面的数据。
15、容器组件和UI组件的区别
容器组件里面有逻辑操作,可以提供数据和行为给UI组件,并且容器组件时有状态的
UI组件时无状态的,只可以通过props去接受数据,主要用来展示组件内容,是无状态的
16、react中context的使用?
context,通过createContext创建一个context,在所有组件的最外层包裹一个provider,然后通过给provider绑定数据以及方法,然后后代组件可以通过tatic contextType 或者consumer来获取context里面的值,如果是consumer的话,那么就是使用一个回调函数,回调函数的参数就是context的值
17、React路由加载的几种方式?
路由组件的加载一共有三种方式,component\render\children
component可以直接引入组件名,也可以通过一个回调函数去引入一个组件
render只可以通过回调函数引入组件
children和render一样也是通过一个回调函数组件,但是children引入的组件,无论路径是否匹配都显示引入的组件,最后通过match对象的match属性去对应相应的路由,常用于高亮效果等!
18、提供网页加载速度的方式?(优化)
1、首先可以减少http的请求
2、首页可以使用服务端渲染
3、懒加载图片
4、添加Expires请求头,缓存http访问的组件,下次访问的时候减少不必要的http请求,提高加载速度。
5、使用CDN,减小服务器负担
6、启用GZIP压缩,压缩必要资源,从而给用户发送最小的HTML文件和CSS/JS等资源
19、div+css 的布局较 table 布局有什么优点?
改版的时候更方便 只要改 css 文件。
页面加载速度更快、结构化清晰、页面显示简洁。
表现与结构相分离。
易于优化(seo)搜索引擎更友好,排名更容易靠前。
a> div+css布局的好处:
1.符合W3C标准,代码结构清晰明了,结构、样式和行为分离,带来足够好的可维护性。
2.布局精准,网站版面布局修改简单。
3.加快了页面的加载速度(最重要的)。
4.节约站点所占的空间和站点的流量。
5.用只包含结构化内容的HTML代替嵌套的标签,提高另外搜索引擎对网页的搜索效率。
b> table布局的好处(table布局也不是一点用的没有,这点是毋庸置疑的)
1.容易上手。
2.可以形成复杂的变化,简单快速。
3.表现上更加“严谨”,在不同浏览器中都能得到很好的兼容。
20、vue路由懒加载方式;
方法一 resolve
这一种方法较常见。它主要是使用了resolve的异步机制,用require代替了import,实现按需加载
方法二 官网方法
vue-router在官网提供了一种方法,可以理解也是为通过Promise的resolve机制。因为Promise函数返回的Promise为resolve组件本身,而我们又可以使用import来导入组件。
方法三 require.ensure
这种模式可以通过参数中的webpackChunkName将js分开打包。
component: resolve => require.ensure([], () => resolve(require(’@/components/’+componentName)), ‘webpackChunkName’)
结合 Vue 的异步组件和 Webpack 的代码分割功能,把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件
21、redux工作流程;
首先,用户发出 Action。然后,Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action。 Reducer 会返回新的 State 。State 一旦有变化,Store 就会调用监听函数。listener可以通过store.getState()得到当前状态。如果使用的是 React,这时可以触发重新渲染 View。
22、Es6 module和commonjs的区别;
CommonJS和ES6 Module是目前使用较为广泛的模块标准。它们的主要区别在于
1.前者建立模块依赖关系是在运行时,后者是在编译时;
2.在模块导入方面,CommonJS导入的是值拷贝,ES6 Module导入的是只读的变量映射;
3.ES6 Module通过其静态特性可以进行编译过程中的优化,并且具备处理循环依赖的能力。
23、hash、chunkhash、contenthash三者区别
一、hash(所有文件哈希值相同,只要改变内容跟之前的不一致,所有哈希值都改变,没有做到缓存意义)
二、chunkhash(同一个模块,就算将js和css分离,其哈希值也是相同的,修改一处,js和css哈希值都会变,同hash,没有做到缓存意义)
三、contenthash(只要文件内容不一样,产生的哈希值就不一样)
24、React 中 keys 的作用是什么?
Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性。
25、React 新老版生命周期函数
老版生命周期函数:
新版生命周期函数:
Render函数靠mouting时触发,如果在里面调用状态会造成死循环
componentWillMount 相当于vue中的beforemount,在dom渲染之前触发的
componentDidMount 在dom渲染完之后,是一个创建时期调用的钩子
componentWillReceiveProps(props.state) 检测父组件给的信息,一有变化就会run
componentWillUnmount 在组件被卸载时调用
shouldComponentUpdata 此钩子新老版生命周期函数中都存在,主要用于在渲染之前,判断是否执行渲染,相当于一个开关,当执行结果为false时不会触发后面的render函数。那么我们就可以利用此钩子去做一些优化的操作,例如我们都知道父组件渲染时会带着子组件一起渲染,但有的时候子组件的数据并没有发生变化但还是会带着被渲染一次,就造成了不必要的性能浪费,这个时候在shouldComponentUpdata中加一层判断的话就可以解决这个问题。(当然,在创建组件时如果将继承的Component改成PureComponent,可帮忙做上面的优化)
getDerivedStateFromProps 这是一个新增的钩子,此钩子的处罚比较频繁,当组件被传入值时,不管相不相等都会执行,数据更新时也会执行(不管自身还是父组件传来的)
this.forceupdate() 强制渲染
getSnapshotBeforeUpdate在DOM渲染之前获得一个快照
26、从启动webpack构建到输出结果经历了一系列过程,它们是什么
- 解析webpack配置参数,合并从shell传入和webpack.config.js文件里配置的参数,生产最后的配置结果。
- 注册所有配置的插件,好让插件监听webpack构建生命周期的事件节点,以做出对应的反应。
- 从配置的entry入口文件开始解析文件构建AST语法树,找出每个文件所依赖的文件,递归下去。
- 在解析文件递归的过程中根据文件类型和loader配置找出合适的loader用来对文件进行转换。
- 递归完后得到每个文件的最终结果,根据entry配置生成代码块chunk。
输出所有chunk到文件系统。
27、vue react 怎么检测数据变化的
Vue
vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调
React
react状态变化只能通过setState,调用setState就会更新状态重新渲染dom
28、instanceof 的原理是什么?
实际上这里我们可以自己去手写一个Instanceof去分析
function myInstanceof(left, right) {
let prototype = right.prototype
left = left.__proto__
while (true) {
if (left === null || left === undefined)
return false
if (prototype === left)
return true
left = left.__proto__
}
}
上述代码思路:
首先获取类型的原型
然后获得对象的原型
然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null
29、React中怎么让setState同步更新?
回调,promise,async+await
回调
setState函数的第二个参数允许传入回调函数,在状态更新完毕后进行调用,例如下面的这个例子
this.setState({
data: '666'
}, () => {
console.log('加载完成')
});
promise
利用promise的特性,可以使用promise封装一个函数去一劳永逸的解决这个问题
setStateAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
async+await
相当于promise的优雅版,我们可以利用await"等一等"的特性去让程序等待await后面的函数做完以后再执行下面的代码(当然,await必须要配合async使用),例如下面的小例子
async myfun(){
await this.setState({
data: '666'
});
}
30、什么是immutable, 为什么要使用它
什么是 Immutable Data
1.Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象
2.Immutable 实现的原理是 Persistent Data Structure (持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变
TIP:当然,通过这一点,我们可以去做时间旅行,也就是以前调用之前存在过的旧数据。
3.同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗, Immutable 使用了 Structural Sharing···· (结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
TIP:例如有下图的一个数据结构(灵魂画手就是我了):
例如我们现在数据C2发生改变,那么他只会影响到B2与A,其他不会受到影响的数据直接复制过来就好了无需重新进行操作,性能有所提升。
关于具体的优缺点可以看下图
31、Vue 常用的修饰符
.lazy:v-model 在每次 input 事件触发的时候都会去进行数据同步,添加 lazy 修饰符后就转变为 change 事件触发时再进行数据同步。
.number:可以自动将用户输入的值转为数值类型
.trim:可以自动过滤用户输入的守卫空白字符
事件修饰符:
.stop:阻止冒泡, event.stopPropagation() 或 cancelBubble
prevent:拦截默认事件,等同于 event.preventDefault()
.passive:不拦截默认事件告诉浏览器,不用查询了,我们没用preventDefault阻止默认动作。与 prevent 冲突,不能同时使用。
.capture:事件在捕获阶段执行
.self:只会触发自身事件,不包含子元素的事件
.once:事件只会触发一次
32、函数节流和防抖
- 节流:
说白了就是减少执行频率,自定义一个时间间隔,如果多次去触发一个函数让这个函数每隔这个时间就执行一次,在时间间隔内触发这个方法的均无视。最常见的像scroll这种操作,没必要执行频率那么高,十分影响性能,搞个几百毫秒执行一次就够意思的了,当然具体执行时间间隔还得视业务需求而定 - 防抖:
说白了就是你还在操作我就不执行它,等你停下来我才执行。还是拿滚动scroll来说事,你监听了文档的scroll,但希望在滚动过程中不执行定义的方法,等停下来才执行,那就是典型的防抖
33、map,filter,reduce 各自有什么用
- map:就是遍历原数组,执行一些操作后放入新数组,最后返回新数组。有三个参数分别是,当前项、索引、原数组
- filter:过滤器,取出数组中符合要求的项放入新数组。在遍历数组的时候将返回值为 true 的元素放入新数组,我们可以利用这个函数删除一些不需要的元素。
- reduce:reduce 就是一个累加器,reduce 为数组的每一项都执行一次回调函数,我们可以对每一项都进行操作,可以接受四个参数分别是上一次回调返回的结果、当前项、当前索引,原数组。
map 为迭代器,通常用于处理数据结构的所有数据,并进行数据结构的重构;reduce 为累加器,虽然 reduce 也可以完成 map 的一些工作但是并不提倡这么做。
34、js 的基本数据类型判断方法
- typeof:
其右侧跟一个一元表达式,并返回这个表达式的数据类型。返回的结果用该类型的字符串(全小写字母)形式表示,包括以下 8 种:number、boolean、symbol、string、object、undefined、function、bigInt 等。
但是 typeof 有一定的缺陷。typeof NaN 的返回值是 number,typeof [] 返回值是 object,typeof null 结果也是 object。所以用typeof除了string和boolean其他的都不太靠谱 - instanceof:
一般用来判断引用数据类型的判断,比如 Object、Function、Array、Date、RegExp等等。实现原理主要就是判断右边变量的 prototype 在不在左边变量的原型链上,判断对象和构造函数在原型链上是否有关系,如果有关系,返回真,否则返回假。所以使用 instanceof 来判断类型的话只能知道是不是要判断的这个类型,如果不是就不能知道其准确的类型。 - Object.prototype.toString.call:
toString 是 Object 原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString 运行时 this 指向的对象类型, 返回的类型格式为 [object,xxx],xxx 是具体的数据类型,包括 String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument…
35、 Vue 中 computed 和 watcher 的区别
watcher 顾名思义就是监听数据变化,它监听的数据来自 props、data、computed 的数据。他有两个参数分别是 newValue 和 oldValue。watcher 默认是浅监听,如果想要深度监听的话可以加一个deep:true;
computed 用于处理复杂的逻辑运算,比较适合对多个变量或者对象进行处理后返回一个结果值
computed 支持缓存,只有依赖数据发生改变,才会重新进行计算;watcher 不支持缓存,数据变,直接会触发相应的操作
computed 不支持异步,当computed内有异步操作时无效,无法监听数据的变化。watch 支持异步。
36、 React state 和 props 有什么不同
- props 是一个从外部传进组件的参数,主要作为就是从父组件向子组件传递数据,它具有可读性和不变性,只能通过外部组件主动传入新的 props 来重新渲染子组件,否则子组件的 props 以及展现形式不会改变。
- state 是组件自身的状态,的主要作用是用于组件保存、控制以及修改自己的状态的一些数据,它只能在 constructor 中初始化,它算是组件的私有属性,不可通过外部访问和修改,只能通过组件内部的 this.setState 来修改,修改 state 属性会导致组件的重新渲染。
state 是组件自己管理数据,控制自己的状态,可变;
props 是外部传入的数据参数,不可变;
37、git 怎么删除远程和本地分支
删除本地分支:git branch -D 分支名
删除远程分支:git branch origin -D 分支名
38、 闭包是什么?闭包的 this 指向?
闭包是指有权访问另外一个函数作用域中的变量的函数。一般是在一个函数内返回一个函数,这个被返回的函数就是一个闭包函数。
this对象是在运行时基于函数的执行环境绑定的,谁调用this就是谁。一般的闭包我们调用第一层时是像全局返回一个函数,所以当再调用第二层时里边的this就是指向全局。
因为闭包并不属于这个对象的属性或方法。所以在闭包中的this是指向window的
39、 webpack 中 loader 和 plugin 的区别
- loader 用于加载某些资源文件。因为 webpaack 只能理解 JavaScript 和 JSON 文件,对于其他资源例如 css 、图片、jsx 、视频等等是没有办法加载的,就需要对应的 loader 将资源转化加载进来。字面意思三个可以看出,loader 适用于加载的,它作用域一个个文件上。
- plugin 用于拓展 webpack 的功能。针对是loader结束后,webpack打包的整个过程。它并不直接操作文件,而是基于事件机制工作。目的在于解决loader无法实现的其他事,从打包优化和压缩。
40、 vue 中的 key 有什么作用?不加会怎样?
key 就是给每个节点一个唯一的标识,key是给每一个vnode的唯一id,可以依靠key,更准确, 更快的拿到oldVnode中对应的vnode节点。
如果不加 key ,在更新的时候 vue 会去比较所有的项,然后把全部的元素进行替换,造成性能的降低。
41、React 的路由懒加载怎么实现?
使用传统的 import 的方式。可以在函数内 return 一个 import, 添加 webpackChunkName 的注释,这样 webpack 打包时就会将对应的文件单独打包,当函数执行时再去加载这个包。
return import( /\* webpackChunkName: "lodash" \*/ 'lodash').then(\_ => {}
使用 React.lazy 实现。导入时调用 React.lazy 并给他一个函数参数,函数中返回 import React.lazy(() => import(“./XXX”))。配合 Suspense 可以实现在模块未加载完成的时候给用户一个提示。
42、cookie storage 的区别?什么时候使用
- cookie 数据始终携带在请求中,storage 不会主动的发送给服务器。
- cookie 数据一般限制为大小不超过 4k ,sessionStore 和 localStorage 虽然也有大小限制,但是比 cookie 大的多,可以达到 5M 或更大。
- 有效期不同,sessionStorage 仅在当前窗口关闭前有效,不能持久保存,localStorage 不会主动失效,可以保存持久数据。 cookie 在设置的过期时间之前一直有效
- 作用域不同。sessionStorage 在不同的窗口中是不共享的,即使是打开的同一个页面也不会共享。localStorage 对所有同源的窗口都是共享的。cookie 也是对同源的窗口共享
使用场景:
- cookie 比较小所以不会存大量的数据,一般当你有一些信息在每次请求中都需要携带的时候才会放到 cookie 比如登录信息、设置信息等。
- localStorage 一般用于前端缓存一些数据,这些数据只在前端使用不在后端使用,比如一些网站的编辑器自动保存草稿的功能就可以保存在 localStorage 中。
43、vue组件中data为什么必须是一个函数?
最后:
总结来说,面试成功=基础知识+项目经验+表达技巧+运气。我们无法控制运气,但是我们可以在别的地方花更多时间,每个环节都提前做好准备。
面试一方面是为了找到工作,升职加薪,另一方面也是对于自我能力的考察。能够面试成功不仅仅是来自面试前的临时抱佛脚,更重要的是在平时学习和工作中不断积累和坚持,把每个知识点、每一次项目开发、每次遇到的难点知识,做好积累,实践和总结。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】