赞
踩
高级Web前端工程师必会面试题,这里只是整理一些范围知识点,并没有特别具体的面试题目,只要把这些知识点搞明白了,面试题都不是问题。
最近整理了一套 2021最新最全的前端面试题集锦(持续更新中),内容涉及前端各个方面,并且每道题目基本都有对应得答案解析,是你面试必备宝典。
骚年,加油!高薪等你。
弹性盒布局 flex
盒子是并列的,可以设置指定宽度,轻松实现两栏,三栏布局。
但是,flexbox
布局方式对浏览器的支持不太友好,有一些兼容性问题,但是,这应该是未来发展的趋势。
浮动布局 float
float
布局是目前各大网站用的最多的一种布局方式了。
通过给元素设置float属性来实现元素浮动,浮动的元素是脱离文档流的,但是不脱离文本流。
特点:
float
元素可以形成块,可以让行内元素变成块元素,也拥有宽和高;float:left
或 float:right
,如果这一行满足不了浮动元素的宽度,则会被挤到下一行。overflow:hidden;
。overflow:auto/scroll
或者 display:flex/inline-flex/table
等,也都可以解决高度塌陷,也是因为触发了父元素为 BFC
响应式布局
meta
标签:<meta name="viewport" content="width=device-width, initial-scale=1">
width = device-width
这一句的意思是让页面的宽度等于屏幕的宽度。rem
是指 html
的 font-size
的大小, 根据 rem
来计算各个元素的宽高,然后在配合 media query
就可以实现自适应。
@media query
语法@media screen and (max-width: 360px) {
html { font-size: 12px; }
}
rem
布局
rem
以根元素字体大小作为参照的布局方式,可以实现等比缩放布局,不管内容是多大的,显示的内容是一样的。%
1px
边框的解决方案参考文章:移动端 1px 解决方案(完整版)
dpr
为2或者3,1px边框看起来比真实的1px边框看起来更宽transform
:把原先元素的 border
去掉,然后利用 :before
或者 :after
重做 border
,并 transform
的 scale
缩小0.5 或者 0.33,原先的元素相对定位,新做的 border
绝对定位。
(dpr => device-pixel-ratio)
设置缩放比例:
border-radius
)。但是,代码量也很大,对于已经使用伪类的元素,可能需要多层嵌套。viewport
+ rem
:
meta
设置对应 viewport
的 rem
基准值,这种方式就可以像以前一样轻松愉快的写1px了。border-image
border-image
时,将 border
设计为物理1pxbackground-image
Viewport
)尺寸的增加,系统会自动分为最多12列。详细讲解:深入理解JavaScript的函数作用域
变量对象:如果变量与执行上下文相关,那变量自己应该知道它的数据存储在哪里,并且知道如何访问,这种机制就是变量对象(VO)。
变量对象是一个与执行上下文相关的特殊对象,它存储着在上下文中声明的变量、函数声明、函数形参等内容。
全局对象:是在进入任何执行上下文之前就已经创建了的对象。
全局对象只存在一份,它的属性在程序中任何地方都可以访问,全局对象的生命周期终止于程序退出那一刻。
作用域链与原型链:
详细讲解:
【JavaScript高级】原型与原型链
window
,原型链顶层是Object
。this
指向:取决于被调用的方式:
this
指向 window
,严格模式下,this
是 undefined
;this
指向该对象;call()
、apply()
或者 bind()
方式调用,this
指向被绑定的对象;this
指向实例化出来的新对象;this
,具体来说,箭头函数会继承外层函数调用的 this
绑定。this
指向:外部函数的 this
指向调用他的对象,内部函数的 this
指向了全局对象。__proto__
对象属性):原型为同一个构造函数new
出来的实例对象提供了一个公共的区域来存放共同的属性和方法。
prototype
对象属性,指向另一个对象,prototype
的所有属性和方法,都会被构造函数的实例继承。这就意味着,我们可以把那些不变(公共)的属性和方法,直接定义在 prototype
对象属性上。prototype
就是调用构造函数所创建的那个实例对象的原型。prototype
可以让所有的对象实例共享它所包含的属性和方法。也就是说,不必再构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。__proto__
的内置属性,用于指向创建它的函数对象的原型对象 prototype
。__proto__
)和构造器原型(prototype
)
__proto__
属性,原型链上的对象正是依赖这个属性连结在一起。__proto__
属性所指向的上一个对象,并在那个对象中查找指定的方法或属性,如果不能找到,那就会继续通过这个对象的 __proto__
属性指向的对象进行向上查找,直到这个链表结束。前提:提供父类(继承谁,提供谁的属性)
分类:
1、原型链继承:可以让新实例的原型等于父类的实例
2、构造函数继承:用 .call()
和 .apply()
将父类构造函数引入子类函数(在子类函数中做了父类的复制)
call
多个)3、组合继承(原型链继承 + 构造函数继承):结合了两种模式的优点,传参和复用
4、原型式继承:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了一个可以随意增添属性的实例或对象,Object.create()
就是这个原理
5、寄生式继承:就是给原型式继承外面套个壳子
6、寄生组合继承:(常用)修复了组合继承的问题
apply
或者call
引用另一个构造函数,可传参scroll
事件,滚动监听事件,每隔一段时间计算一次位置信息等input
框实时搜索并发送请求展示下拉列表,每隔1s发送一次请求。(防抖也可以) function throttle(fn, delay) {
let timer;
return function () {
let _this = this;
let args = arguments;
if (timer) {
return;
}
timer = setTimeout(function () {
fn.apply(_this, args);
timer = null; // 在delay后执行完fn之后清空timer,此时timer为假,throttle触发可以进入计时器
}, delay)
}
}
mousemove
、mouseover
鼠标移动事件防抖 function debounce(fn, delay) {
let timer; // 维护一个 timer
return function () {
let args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(()=> {
fn.apply(this, args); // 用apply指向调用debounce的对象,相当于this.fn(args);
}, delay);
};
}
setTimeout
实现clearTimeout
和 setTimeout
实现。clearTimeout
。防抖可以比作等电梯,只要有人进来,就需要再等一会。业务场景有避免触发按钮多次重复提交。timer = timeout; timer = null
。节流可以比作红绿灯,每等一个红灯时间就可以过一批。callback
函数。设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。
subscribe()
接收观察者,使其订阅;unsubscribe()
取消订阅;fire()
触发事件,通知到所有观察者。computed
计算属性来做处理,这个过程就用到了适配器模式。Proxy
, Reflect
, Promise
, Generator
, async
, Decorator
, Class
)Proxy
:用于修改某些操作的默认行为,也可以理解为在目标对象之前架设一层拦截,外部所有的访问都必须先通过这层拦截,因此提供了一种机制,可以对外部的访问进行过滤和修改。这个词的原理为代理,在这里可以表示由它来“代理”某些操作,译为“代理器”。
var proxy = new Proxy(target, handler);
Reflect
:是一个全局的普通的对象,原型是Object。
Promise
:是一个专门解决异步回调地狱的问题。
所谓 Promise,简单点来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,promise是一个对象,它可以从获取异步操作的消息,promise提供了统一的API,各种异步操作都可以用同样的方法进行处理。
Generator
:与平常的函数不同,它可以理解为是一个分布执行的函数,返回值是一个遍历器。
async/await
:回调地狱的终极解决方案,使用它可以把异步代码写的看起来像同步代码。
Decorator
:修饰器,是一个函数,用来修饰类的行为。不过目前主流浏览器都没有很好的支持,我们需要用babel来转换为浏览器能识别的语言。
Class
:类,通过class关键字可以定义类。
浏览器从服务器那收到的HTML,CSS,JavaScript等相关资源,然后经过一系列处理后渲染出来的web页面。
过程:
以上五个步骤并不一定一次性顺序完成,比如DOM或CSSOM被修改时,亦或是哪个过程会重复执行,这样才能计算出哪些像素需要在屏幕上进行重新渲染。而在实际情况中,JavaScript和CSS的某些操作往往会多次修改DOM或者CSSOM。
https://segmentfault.com/a/1190000018181334
早期的路由都是后端实现的,直接根据 url 来 reload 页面,页面变得越来越复杂服务器端压力变大,随着 ajax 的出现,页面实现非 reload 就能刷新数据,也给前端路由的出现奠定了基础。我们可以通过记录 url 来记录 ajax 的变化,从而实现前端路由。
History API(history.pushState 和 history.replaceState)
hash
hashchange
事件中注册 ajax
从而改变页面内容。参考文档:前端常见浏览器跨域请求解决方案
js // 配置 cors 跨域 header("Access-Control-Allow-Origin:*"); header("Access-Control-Request-Methods:GET, POST, PUT, DELETE, OPTIONS"); header('Access-Control-Allow-Headers:x-requested-with,content-type,test-token,test-sessid');
原生xhr:XMLHttpRequest对象
// 兼容处理 if (window.XMLHttpRequest) { // model browser xhr = new XMLHttpRequest() } else if (window.ActiveXObject) { // IE 6 and older xhr = new ActiveXObject('Microsoft.XMLHTTP') } xhr.open('POST', url, true) xhr.send(data) xhr.onreadystatechange = function () { try { // TODO 处理响应 if (xhr.readyState === XMLHttpRequest.DONE) { // XMLHttpRequest.DONE 对应值是 4 // Everything is good, the response was received. if (xhr.status === 200) { // Perfect! } else { // There was a problem with the request. // For example, the response may hava a 404 (Not Found) // or 500 (Internal Server Error) response code. } } else { // Not ready yet } } catch (e) { // 通信错误的事件中(例如服务器宕机) alert('Caught Exception: ' + e.description) } }
jQuery ajax: $.ajax
$.ajax({
type: 'POST',
url: url,
data: data,
dataType: dataType,
success: function () {},
error: function () {}
})
Axios
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'liu',
lastName: 'weiqin'
}
})
.then(res => console.log(res))
.catch(err => console.log(err))
fetch
fetch('http://example.com/movies.json')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson);
});
在进程启动时,Node便会创建一个类似于while(true)的循环,每执行一次循环体的过程我们称为Tick。每个Tick的过程就是查看是否有事件待处理。如果有就取出事件及其相关的回调函数。然后进入下一个循环,如果不再有事件处理,就退出进程。
中间件主要是指封装所有Http请求细节处理的方法。一次Http请求通常包含很多工作,如记录日志、ip过滤、查询字符串、请求体解析、Cookie处理、权限验证、参数验证、异常处理等,但对于Web应用而言,并不希望接触到这么多细节性的处理,因此引入中间件来简化和隔离这些基础设施与业务逻辑之间的细节,让开发者能够关注在业务的开发上,以达到提升开发效率的目的。
Koa 与 Express 比较
Express.js
Express
是基于 callback
来组合业务逻辑。Callback
有两大硬伤,一是不可组合,二是异常不可捕获。Express
的中间件模式虽然在一定程度上解决这两个问题,但没法彻底解决。Koa.js
Express
的下一代,性能比较好;Nest.js
express
中间件;一个js文件就是一个模块
模块内所有的变量均为局部变量,不会污染全局
模块中需要提供给其他模块使用的内容需要导出
导出使用 exports.xxx = xxx
或 module.exports=xxx
或 this.xxx=xxx
其他模块可以使用 require
函数导入
node
实现了 CommonJS
规范,在编写模块时,都有 require
、exports
、module
三个预先定义好的变量可以使用。
require{}
函数的两个作用:
1. 执行导入的模块中的代码;
2. 返回导入模块中的接口;
fs
,http
,path
,url
模块;node.js
的时候,就已经编译成 二进制文件,可以直接加载运行(速度较快);node.exe
这个进程启动的时候就已经默认加载了,所以可以直接使用。.js
,.json
,.node
顺序依次加载相应模块// 方式一:module.exports 导出
module.exports = {
say: sayName
}
// 方式二:exports 导出
exports.say = sayName
require()
加载模块时传入的参数是否以 '/'
或 './'
或 './'
等等这样的路径方式开头(相对路径或绝对路径都可以)require('./test.js')
,require('./test')
.js
、.json
、.node
进行匹配,如果匹配不到,执行第二步package.json
文件(找到该文件后尝试找main
字段中的入口文件)、index.js
、index.json
、index.node
,找不到则加载失败require('http')
、require('fs')
node_modules
目录中是否包含相应的包,如果查找完毕磁盘根目录依然没有,则加载失败参考文章:HTTP协议超级详解
前端面试题之 热门框架Vue 篇
前端面试题之 进阶 Vue3.0 篇
keep-alive
<keep-alive>
元素将其动态组件包裹起来。这样,失活的组件将会被缓存。resolve
回调,这个回调函数会在从服务器得到组件定义的时候被调用,也可以调用 reject(reason)
来表示加载失败。Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 `require` 语法将会告诉 webpack
// 自动将你的构建代码切割成多个包,这些包
// 会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
})
Promise
,使用动态导入Vue.component(
'async-webpack-example',
// 这个动态导入会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
promise
的函数new Vue({
components: {
'my-component': () => import('./my-async-component')
}
})
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
$root
,$parent
,$refs
,依赖注入,循环引用,$forceUpdate
,v-once
)访问根实例 $root
所有的子组件都可以将这个实例作为一个全局 store 来访问或使用。
this.$root.xxx
对于demo或者非常小型的有少量组件的应用来说比较方便,但是在绝大多数情况下,需要使用 Vuex
来管理应用的状态。
访问父级组件实例 $parent
可以用来从一个子组件访问父组件的实例。可以在后期随时触达父级组件,以替代将数据以 prop 的方式传入子组件的方式。
在绝大多数情况下,会使得应用更难调试和理解,尤其是变更了父级组件的数据的时候。
在需要向任意更深层级的组件提供上下文信息时推荐使用依赖注入。
访问子组件实例或子元素 $refs
给子组件添加一个 ref 属性,赋予一个ID引用,就可以使用$refs
来访问这个子组件实例
// 子组件定义ref属性
<base-input ref="usernameInput"></base-input>
// 允许父组件给子组件输入框获取焦点
this.$refs.usernameInput.focus()
当 ref
和 v-for
一起使用的时候,得到的ref会包含对应数据源的这些子组件的数组。
$refs
会在组件渲染完成后生效,并且不是响应式的,应该避免在模板或计算属性中访问 $refs
依赖注入:provide
和 inject
provide
:可以指定想要提供给后代组件的数据/方法provide: function () {
return {
getMap: this.getMap
}
}
inject
:在任何后代组件中,通过 inject 选项接收指定的属性inject: ['getMap']
$parent
:这种用法可以在任意后代组件中访问该定义的属性,而不需要暴露整个组件实例。同时这些组件之间的接口是始终明确定义的,和props
一样。程序化的事件侦听器:
$emit
可以被 v-on
侦听$on(eventName , eventHandler)
侦听一个事件$once(eventName , eventHandler)
一次性侦听一个事件$off(eventName , eventHandler)
停止侦听一个事件循环引用
name
选项来实现。Vue.component
全局注册一个组件时,这个全局的ID会自动设置为该组件的name
选项。模板定义替代品
inline-template
:添加这个属性会将这个组件里面的内容作为模板,但是会让模板的作用域变得难以理解,在组件内优先选择 template
选项 或者 <template>
元素来定义模板。<my-component inline-template>
// ...
</my-component>
<script>
元素中,并为其带上 text/x-template
的类型,然后通过id将模板引用过去。// 定义模板
<script type="text/x-template" id="hello-world-template">
// ...
</script>
// 引用模板
Vue.component('hello-world', {
template: '#hello-world-template'
})
控制更新
$forceUpdate
v-once
创建低开销的静态组件v-once
来使这些内容只计算一次然后缓存起来beforeEach
:全局前置守卫,进入路由前执行beforResolve
:全局解析守卫,在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用afterEach
:全局后置守钩子,导航确认执行时执行,可理解为导航完成时执行beforeEnter
:进入该路由前beforeRouteEnter
:进入组件时,不能获取组件实例this,因为当守卫执行前,组件实例还没被创建beforeRouteUpdate
:组件被复用时调用beforeRouteLeave
:离开组件时导航解析流程
beforeRouteLeave
守卫beforeEach
守卫beforeRouteUpdate
守卫beforeEnter
beforeRouteEnter
beforeResolve
守卫afterEach
钩子beforeRouteEnter
守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入路由懒加载
webpackChunkName
将js分开打包。template
来创建HTML,有的时候需要使用 js 来创建 html,这个时候就要用到 render
渲染函数。render: function(createElement) {
return createElement(
'h' + this.level, // tag name 标签名称
this.$slots.default // 组件的子元素存储在组件实列 $slots.default 中。
)
},
createElement
mixin
vue中提供了一种混合机制–mixins,用来更高效的实现组件内容的复用。
组件在引用之后相当于在父组件内开辟了一块单独的空间,来根据父组件props过来的值进行相应的操作,单本质上两者还是泾渭分明,相对独立。
而mixins则是在引入组件之后,则是将组件内部的内容如data等方法、method等属性与父组件相应内容进行合并。相当于在引入后,父组件的各种属性方法都被扩充了。
作用:多个组件可以共享数据和方法,在使用mixin的组件中引入后,mixin中的方法和属性也就并入到该组件中,可以直接使用。钩子函数会两个都被调用,mixin中的钩子首先执行。
参考文章:Vuex模块化应用实践
流程图
可以做异步操作,但是会违背 flux 思想
new Vue()
之后,Vue会调用 _init
函数进行初始化,也就是进行init 过程,它会初始化生命周期、事件、props、methods、data、computed与watch等。其中重要的是通过 Object.defineProperty
设置 setter
和 getter
函数,用来实现响应式和依赖收集。$mount
会挂载组件,如果是运行时编译,即不存在 render function
,但是存在 template
的情况,需要进行编译的步骤。compile
编译可以分成 parse
、optimize
与 generate
三个阶段,最终需要得到render function
。parse
:用正则解析 template 模板中的指令、class、style等数据,形成 AST(抽象语法树);optimize
:用来标记静态节点,将来更新视图的时候,通过diff算法会跳过静态节点,达到优化的一个目的;generate
:将AST
转换成 render function
字符串,得到结果是 render 的字符串以及 staticRenderFns字符串。render function
被渲染的时候,因为会读取所需对象的值,所以会触发 getter
函数进行依赖收集,依赖收集的目的的将观察者 Watcher
对象存放到当前闭包中的订阅者 Dep
的subs
中。setter
,setter
通知之前依赖收集得到的 Dep中的每一个 Watcher
,告诉他们自己的值改变了,需要重新渲染视图。这时候这些 Watcher
就会开始调用 update
来更新视图。Virtual DOM
其实就是一棵以 js 对象作为基础的树,用对象属性来描述节点,实际上它只是一层对真实DOM的抽象。patch()
方法对比新的 VNode 和 旧的 VNode。通过diff算法得到差异,进行对应修改。{}
,经过react语法的构造,编译转化,最后得到dom元素,插到页面中。createElement
,可以构建一个js对象来描述HTML结构的信息。其中,第一个参数是标签名,第二个参数是对象,包含了所有的属性,第三个是子节点。context,通过createContext创建一个context,在所有组件的最外层包裹一个provider,然后通过给provider绑定数据以及方法,然后后代组件可以通过tatic contextType 或者consumer来获取context里面的值,如果是consumer的话,那么就是使用一个回调函数,回调函数的参数就是context的值
immutable
(什么是immutable
, 为什么要使用,重要的API)文档持续更新中。。。加油骚年!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。