高级Web前端工程师必会面试题,这里只是整理一些范围知识点,并没有特别具体的面试题目,只要把这些知识点搞明白了,面试题都不是问题。
另外整理了一套 前端面试集锦,内容涉及前端各个方面,并且每道题目基本都有对应得答案解析,是你面试必备宝典。
骚年,加油!高薪等你。
参考:https://www.cnblogs.com/Renyi-Fan/p/12173366.html#_label3
弹性盒布局 flex
盒子是并列的,可以设置指定宽度,轻松实现两栏,三栏布局,经典的圣杯布局和双飞翼布局就是通过flex来实现的。
但是,flexbox 布局方式对浏览器的支持不太友好,有一些兼容性问题,但是,这应该是未来发展的趋势。
浮动 float浮动
float布局是目前各大网站用的最多的一种布局方式了。
通过给元素设置float属性来实现元素浮动,浮动的元素是脱离文档流的,但是不脱离文本流。
特点:
1) 对自身:a、float元素可以形成块,可以让行内元素变成块元素,也拥有宽和高;b、浮动元素的位置尽量靠上;c、设置float:left 或 float:right,如果这一行满足不了浮动元素的宽度,则会被挤到下一行。
2) 对兄弟:a、不影响其他块元素的位置;b、影响其他块元素的文本
3) 对父元素:高度塌陷
高度塌陷:(解决方案)
响应式
最简单的方式是加上一个 meta 标签, ,其中 width = device-width 这一句的意思是让页面的宽度等于屏幕的宽度。
rem 是指html的font-size的大小, 根据rem来计算各个元素的宽高,然后在配合media query 就可以实现自适应
@media query 语法
@media screen and (max-width: 360px) {
html {
font-size: 12px; }
}
参考https://www.cnblogs.com/liuXiaoDi/p/12261100.html
transform
:元素本身不定义边框,给伪元素定义一个 1px 边框,并且根据像素比值(dpr => device-pixel-ratio)设置缩放比例文档:day0220–BFC.note
链接:http://note.youdao.com/noteshare?id=f0a95534319d2cd8f49dbe9b6f5240eb&sub=B77453B2104943B09697AF70C4CE68A5
.call()
和.apply()
将父类构造函数引入子类函数(在子类函数中做了父类的复制)
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
:用于修改某些操作的默认行为,也可以理解为在目标对象之前架设一层拦截,外部所有的访问都必须先通过这层拦截,因此提供了一种机制,可以对外部的访问进行过滤和修改。这个词的原理为代理,在这里可以表示由它来“代理”某些操作,译为“代理器”。
var proxy = new Proxy(target, handler);
Proxy对象的所有用法,都是通过这种形式。不同的只是handle参数的写法。其中new Proxy用来生成Proxy实例,target是表示所要拦截的对象,handle是用来定制拦截行为的对象。Reflect
:是一个全局的普通的对象,原型是Object。
Promise
:是一个专门解决异步回调地狱的问题。
所谓 Promise,简单点来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,promise是一个对象,它可以从获取异步操作的消息,promise提供了统一的API,各种异步操作都可以用同样的方法进行处理。
Generator
:与平常的函数不同,它可以理解为是一个分布执行的函数,返回值是一个遍历器。
async/await
:回调地狱的终极解决方案,使用它可以把异步代码写的看起来像同步代码。
Decorator
:修饰器,是一个函数,用来修饰类的行为。不过目前主流浏览器都没有很好的支持,我们需要用babel来转换为浏览器能识别的语言。
Class
:类,通过class关键字可以定义类。
参考文档
https://www.jianshu.com/p/b6b42fd3f80e
浏览器从服务器那收到的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
从而改变页面内容。参考文档:Cookie、Session和Token认证详解
参考文档:前端常见浏览器跨域请求解决方案
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);
});
fetch规范与jQuery.ajax()主要有两种方式的不同:
1. 当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。
2. 默认情况下,fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于用户 session,则会导致未经认证的请求(要发送 cookies,必须设置 credentials 选项)。
在进程启动时,Node便会创建一个类似于while(true)的循环,每执行一次循环体的过程我们称为Tick。每个Tick的过程就是查看是否有事件待处理。如果有就取出事件及其相关的回调函数。然后进入下一个循环,如果不再有事件处理,就退出进程。
中间件主要是指封装所有Http请求细节处理的方法。一次Http请求通常包含很多工作,如记录日志、ip过滤、查询字符串、请求体解析、Cookie处理、权限验证、参数验证、异常处理等,但对于Web应用而言,并不希望接触到这么多细节性的处理,因此引入中间件来简化和隔离这些基础设施与业务逻辑之间的细节,让开发者能够关注在业务的开发上,以达到提升开发效率的目的。
Koa 与 Express 比较
1. 核心模块,也叫内置模块、原生模块
// 方式一:module.exports 导出
module.exports = {
say: sayName
}
// 方式二:exports 导出
exports.say = sayName
参考文章
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
所有的子组件都可以将这个实例作为一个全局 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
选项 或者
元素来定义模板。<my-component inline-template>
// ...
</my-component>
元素中,并为其带上 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分开打包。render: function(createElement) {
return createElement(
'h' + this.level, // tag name 标签名称
this.$slots.default // 组件的子元素存储在组件实列 $slots.default 中。
)
},
vue中提供了一种混合机制–mixins,用来更高效的实现组件内容的复用。
组件在引用之后相当于在父组件内开辟了一块单独的空间,来根据父组件props过来的值进行相应的操作,单本质上两者还是泾渭分明,相对独立。
而mixins则是在引入组件之后,则是将组件内部的内容如data等方法、method等属性与父组件相应内容进行合并。相当于在引入后,父组件的各种属性方法都被扩充了。
作用:多个组件可以共享数据和方法,在使用mixin的组件中引入后,mixin中的方法和属性也就并入到该组件中,可以直接使用。钩子函数会两个都被调用,mixin中的钩子首先执行。
参考文章
流程图
可以做异步操作,但是会违背 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的值