谈面试前端工程师
谈面试前端工程师
- 打战需要周全的准备,而找一份年薪几十万的工作对于个人来说不亚于一场大战
- 有一天,你若需要招人,则需要在极短的时间内从各方面考核对方是否合适
- 通过面试可以推动自己主动了解行业新技术,尤其是长期呆在一家技术不那么好的公司
- 面试的内容数不胜数(亦或千奇百怪),面试官和面试者的背景也不尽相同,或许我们只能尽力而为。将每次面试当成一次修炼,好好享受
以下是近期朋友去面试前端工程师所遇到的一些问题,整理如下:
vue
v-model 修饰符有哪些
答:.number、.trim、.lazy
Tip: 用法如下:
<input v-model.lazy="msg">
<input v-model.number="age" type="number">
<input v-model.trim="msg">
v-model 的原理
答:v-model 可以在表单或组件上创建双向绑定。v-model 的本质其实是一个语法糖,即名为 value 的 props 以及 input 事件。
- 在组件上使用
<custom-input v-model="count"></custom-input>
等于
<custom-input :value="count" @input='changCount'></custom-input>
- 在表单上使用
<input v-model="message" placeholder="edit me">
等于
<input type="text" :value='message' @input='handleInput' placeholder="edit me">
Tip:更多介绍请看 v-model
v-if 与 v-for 一起使用
答:不推荐同时使用 v-if 和 v-for。若一定要一起使用,可以使用 template,就像这样:
<li v-for="todo in todos" >
<template v-if="!todo.isComplete">
{{ todo }}
</template>
</li>
computed 和 watch 谁先执行
答:从 vue 源码中可知晓,先初始化计算属性,后初始化 watch,笔者做了一个实验,即在一个组件中同时添加 watch、computed,却先执行 watch ,再执行 computed。
计算属性有缓存吗
答:计算属性有缓存。因为假如计算属性需要遍历一个巨大的数组并计算得来,而其他地方也需要使用这个计算属性,没有缓存,将再次计算。
请简单介绍下 vuex
答:在 vue 中我们可以使用 props 和 emit 解决父子组件之间的通信,而非父子之间的通信则可以使用 bus(中央事件总线)。而 Vuex 作为 vue 的一个插件,解决的问题与 bus 类似,更具体些,就是 vuex 能解决多个组件共享状态的需求。
Vuex 的核心概念有 State、Getters、Mutations、Actions:
- state,即 vuex 的数据
- getters,可以认为是 store 的计算属性
- mutations,更改 vuex 中 state(数据)的唯一方式
- actions,类似 mutation,但不能直接修改 state
Tip: 更多介绍请看 vuex基础
vuex重刷还有吗,如何做持久化
答:没有了。可以配合 sessionStorage 做持久化,亦或使用 npm 包(vuex-persistedstate)。
Tip:
// App.vue
created(){
//页面加载时读取 localStorage 里的状态信息
if(localStorage.getItem("userMsg")){
this.$store.replaceState(...)
}
//在页面刷新时保存到 localStorage
window.addEventListener("beforeunload",()=>{
localStorage.setItem(...)
})
}
vue-router 默认是什么模式
答:hash
$router 和 $route 的区别
答:$router
指 router 实例。例如 this.$router.push('/')
;$route
指当前激活的路由信息对象。例如 this.$route.params
javascript
是否了解 es11,?.
和 ??
用过吗
答:有所了解。例如 es11 中有 BigInt、可选链操作符( ?. )、空值合并操作符(??)。
?. 和 ?? 用法如下:
const obj = {
a: 1,
b: {
c: 2
}
};
// ?. 操作符的功能类似于 . 链式操作符,不同之处在于,
// 在引用为空 null 或者 undefined 的情况下不会引起错误,该表达式短路返回值是 undefined。
console.log(obj.d?.c) // undefined
console.log(obj.b?.c) // 2
console.log(obj?.someNonExistentMethod?.()) // undefined
// 当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数
console.log(null ?? 'default string') // default string
// || vs ??
console.log(0 || 42) // 42
console.log(0 ?? 42) // 0
Tip:有关 ES2020、ES2021、ES2022 等特性可以查阅:kangax es2016plus、babel 插件列表。
什么是事件循环
答:js 是单线程的语言,同一时刻只能执行也给任务,只有第一个任务完成,才能接着做第二个任务,如果上一个任务是计算量很大,cup 很忙导致无法接着做下一个任务,这种情况没问题,但如果上一个任务是只是在等待(如 ajax 请求),cup 很闲而导致下一个任务也只能跟着一起等待,那么就不好了,所以需要事件循环这个机制来解决此问题。
事件循环机制如下:
- 所有同步任务都在主线程上执行,形成一个
执行栈
- 除了主线程,还有一个
任务队列
,只要异步任务有了结果就会往任务队列中放一个事件 - 当执行栈为空,
主线程
就会去读任务队列,放入执行栈中执行 - 如此循环
Tip:关于事件循环更详细的介绍,有多种不同版本,笔者倾向于下面这种
主线程:
同步代码1->同步代码2->...->同步代码N
事件队列:
- 宏任务队列:事件1->事件2->...->事件N
- 微任务队列: 事件1->事件2->...->事件N
①,执行栈中所有同步任务执行完毕
②,执行完微任务队列中所有事件对应回调
③,取出宏任务队列中一个事件,将对应回调加入执行栈
依次循环①②③①②③...
宏任务和微任务都有哪些
答:
- 微任务:Pormise.then、MutationObserver
- 宏任务:setTimeout、setInterval、requestAnimationFrame
什么时候用 promise,什么时候用 async
答:async/await
的本质是生成器和执行器,而执行器是通过 Promise 实现。所以 async 也就是 Promise 的语法糖,并且是一个实现了固定功能的语法糖。
所以,就灵活程度来讲,Promise 可能更好。
所以,这两个东西就不是替换关系,而是有他们各自使用的场景。
以下是我的一些个人习惯:
- 只有一个异步请求,并且需要处理错误情况,倾向 Promise
- 逻辑比较复杂,比如异步嵌套、条件、中间值等等,使用 async
- async 有时会比 Promise 更容易调试
Tip:详细介绍请看 这里
给数组增加一个方法,只能存入唯一的值
答:
Array.prototype.uniquePush = function (...values) {
values.forEach(v => {
if (!this.includes(v)) {
this.push(v)
}
})
}
let array = [1, 2, 3]
array.uniquePush(4, 3)
// array: [ 1, 2, 3, 4 ]
console.log('array: ', array)
扁平化嵌套数组
例如有这么一个数组:
const arr = [1, 2, [3, 4, 5, [6, 7, 8], 9], 10, [11, 12]]
将其扁平化(层级不限)成:
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]
可提供多种方式
答:
- 使用数组的 flat() 方法
const flated1 = arr.flat(Infinity)
console.log('flated1: ', flated1);
- 使用递归
function flat(arr, result = []) {
arr.forEach(item => {
let wrap = Array.isArray(item) ? flat(item) : [item]
result.push(...wrap)
})
return result
}
const flated2 = flat(arr)
console.log('flated2: ', flated2);
- 使用 toString() 方法
const flated3 = arr.toString().split(',').map(item => Number(item))
console.log('flated3: ', flated3)
Tip:Array.prototype.toString() 返回一个表示数组及其元素的字符串。并会调用每项的 toString() 方法:
let obj = {
toString() {
return 'i am obj'
}
}
let arr2 = ['a', 3, obj]
// a,3,i am obj
console.log(arr2.toString())
请完成 getTree() 方法
// 将数据转成树的结构
function getTree(arr) {
}
// parent 为 -1 的是根节点。只有一个根节点
let flatTree = [
{ name: 'b', id: 2, parent: 1 },
{ name: 'a', id: 1, parent: -1 },
{ name: 'c', id: 3, parent: 1 },
{ name: 'd', id: 4, parent: 3 },
{ name: 'e', id: 5, parent: 3 },
]
let tree = getTree(flatTree)
// {"name":"a","id":1,"parent":-1,"children":[{"name":"b","id":2,"parent":1},{"name":"c","id":3,"parent":1,"children":[{"name":"d","id":4,"parent":3},{"name":"e","id":5,"parent":3}]}]}
console.log(JSON.stringify(tree))
答:
function getTree(arr) {
arr.forEach(item => {
if (item.parent === -1) {
return
}
let pId = item.parent
let parentNode = arr.find(item => item.id === pId)
if (!parentNode.children) {
parentNode.children = []
}
parentNode.children.push(item)
})
const root = arr.find(item => item.parent === -1)
return root
}
刷新页面 sesstionStorage 会清空吗?
答:不会
刷新页面,将在控制台依次输出1、2、3...;若用新的 tab 访问,又会依次输出1、2、3...
let msg = sessionStorage.getItem('a')
if (!msg) {
sessionStorage.setItem('a', '1')
} else {
sessionStorage.setItem('a', (+msg) + 1)
}
console.log(sessionStorage.getItem('a'));
Tip: 更多介绍请看 sessionStorage
new Date('abc') 会如何
答:控制台输出 Invalid Date
async 中的 await 后面如果报错,会发生什么
答:会中断 await 后续代码的执行,async 方法会立即结束。
Tip:
- 不明白题意,请看如下代码
async function foo() {
// obj 是一个不存在的变量
await obj
return 1
}
foo().then(v => {
console.log(v)
}).catch(v => {
console.log(`catch, ${v}`)
})
// 输出:catch, ReferenceError: obj is not defined
- 为什么会这样可以查看:async
class 的构造函数能否使用 async
答:不能。
请看示例:
class Dog {
async constructor() {
this.name = 'apple'
}
async say() {
console.log(this.name);
}
}
new Dog().say()
// Class constructor may not be an async method
若用数组来实现栈、队列,需要用到数组的什么方法
答:栈是先进后出,所以会用到 push 、pop;而队列是先进先出,故用到 push、shift
如何解决 0.1 + 0.2 != 0.3
答:此问题的出现是因为浮点数不能精确地用二进制表示所有小数,会出现一些意外的结果,比如:
let a = 0.1
let b = 0.2
let c = 0.3
// 0.30000000000000004
console.log(a + b)
有三种方法可以解决:
- Number.EPSILON
- 转为整数再比较。即将数字提升 10 的 N 次方
- 使用 toFixed()
请看示例:
function equal(a, b) {
return Math.abs(a - b) < Number.EPSILON
}
let isEqual = equal(a + b, c)
// isEqual: true
console.log('isEqual: ', isEqual)
let seed = Math.pow(10, 10)
let isEqual2 = (a * seed + b * seed) === c * seed
// isEqual2: true
console.log('isEqual2: ', isEqual2);
let isEqual3 = parseFloat((a + b).toFixed(10)) === c
// isEqual3: true
console.log('isEqual3: ', isEqual3)
http
什么是协商缓存、什么是强制缓存
答:缓存的作用很好理解,通过复用以前的资源,可以提高网站的性能,提升用户体验。
通常第一次获取资源后,会根据返回的信息(respone header)得知如何缓存,例如做强缓存、协商缓存或不做缓存。
强缓存的特性是,下次直接从缓存中读取,不会发送到服务器,返回的状态码是 200(from cache)
协商缓存的特性,也是直接从缓存中读取,不过得先到服务器那里询问一下缓存资源是否可用,返回的状态码是 304(not modified)
与强缓存相关的 http 头有 cache-control
和 Expires
。比如Cache-control: max-age=N
的头,相应的缓存的寿命就是N,对于不含这个属性的请求,则会去查看是否包含 Expires
属性。
与协商缓存相关的是 Etag
和 Last-Modified
。比如,用户再次访问给定的URL(设有ETag字段),若资源过期了,客户端就发送值为ETag的If-None-Match
header字段。Last-Modified 由于精确度比 ETag 要低,所以这是一个备用机制
Tip: 对于单页面,我们可以通过更新页面(例如 index.html)中引用资源的路径,让浏览器重新加载资源,更多介绍请看 MDN HTTP 缓存、静态资源的缓存。
描述下浏览器输入 url 回车后的整个过程
答:
大概流程:
- 首先拿到服务器 ip(即域名解析)
- 接着和服务器建立连接,传递数据(即相互通信)
- 服务器处理请求
- 浏览器接收响应
更具体一些:
域名解析
拿到服务器 ip 需要用到 域名解析(将好记的域名解析成 ip)。
解析流程大致如下图所示:
- 客户端:我要访问
www.163.com
,请告诉我 ip 本地 dns 服务器
:缓存里找不到,去 dns 根服务器询问dns 根服务器
:这个域名由 .com 负责.com 域服务器
:163.com 域名应该知道163.com 域服务器
:ip 是1.1.1.xx
- 本地 dns 服务器取得
www.163.com
对应的 ip,写入缓存,返回 ip 给客户端
Tip:
- 真实的解析过程情况会更多,比如会搜索系统中 hosts 文件。笔者很久以前用过一个叫 SwitchHosts(能管理、切换多个 hosts) 的工具,开发中用于切换不同的环境,比如开发环境、测试环境等等。
- dns(即域名系统),用于管理有意义的域名和ip的对应关系
相互通信
主要是涉及 TCP/IP协议(一个协议族,能够在多个不同网络间实现信息传输,也是 Internet 最基本的协议)。
大致流程如下:
- 浏览器发起 http 请求
- 经由应用层
- 然后到传输层,其中 tcp 会使用三次握手建立连接,后面则会使用四次握手中断连接
- 在往下就是网络层,比如 ip 寻址
- 最后是网络接口层,这里有传输数据的物理媒介
- 服务端接收则会反过来,依次是网络接口层,网络层,传输层,应用层
Tip:不同种类的应用程序会根据自己的需要来使用应用层的不同协议,比如浏览器在这里会使用 http 协议。
浏览器接收响应
大致流程如下:
- 浏览器接收到资源后,会根据响应内容做不同的处理
- 比如响应码是 301 则永久重定向,如果是 304 则去读缓存
- 比如资源使用 gzip 压缩了,就得解压
- 比如资源类型是视频(
Content-Type:video/mp4
),浏览器则会播放 - 比如是否对资源做缓存
Tip:有关构建 dom 树、预加载扫描器、构建 cssom树、回流、重绘等可以看 这里
零碎
- 如果输入的不是合法的 url,而是关键字,那么浏览器就会去搜索该关键字
- 如果之前已经访问过,那么会先走强缓存,如果过期就走协商缓存
- 可能还会涉及 sts,例如通过 http 而非 https 来访问淘宝、百度、京东的首页,则会发生一个 307 的重定向,并转到 https 上来
应用层的协议有哪些?例如 http 就属于应用层
答:应用层协议 还有 dns,ftp、smtp、snmp、telnet。
使用 tcp 传输的应用层协议有哪些?
答:http、ftp、telnet、smtp
Tip:如何验证“smtp”是 tcp,百度百科 SMTP 即可知晓。
使用 udp 传输的应用层协议有哪些?
答:dns、snmp、tftp
三次握手、四次挥手是针对 tcp,亦或 udp 吗?
答:三次握手、四次挥手只针对 tcp。
Tip:使用 tcp 的应用层协议,例如 http、ftp等,都会有三次握手、四次挥手
什么是三次握手、为什么又是四次挥手(亦称四次握手)?
答:
三次握手建立连接如下:
- 客户端发送报文(例如
x
)给服务端 - 服务端接收后,回复报文给客户端,例如
y x+1
- 客户端接收到后,再次发送报文给服务端,例如
y+1
- 这样,双方都知道对方接收并理解了自己(例如发送
x
,收到x+1
),于是可以开始通信
四次挥手过程如下:
- 客户端执行”主动关闭“,会给服务端发送
FIN
。表明客户端不再发送数据,但仍可以接收数据 - 服务端接收
FIN
后执行“被动关闭”,发送ACK
表明我已知晓,但我还有数据得处理 - 等服务端不再发送数据,就给客户端发送
FIN
,表明对端可以关闭 - 客户端收到
FIN
,发送ACK
给服务端。 - 主要由于有数据可能需要处理,不能即使关闭,所以这里使用四次。若服务端将
FIN
和ACK
合并成一次,则可变成三次挥手
Tip:更多介绍请看 TCP
https 也有三次握手、四次挥手吗?
答:是的。因为 https 建立在 http 基础上。
https 使用对称加密还是非对称加密
答:传输时使用对称加密。
Tip:SSL 初始化
时会用到非对称加密。
请描述一下 https 连接通信的过程
答:
由于 https 相对 http 多了一层,即安全层,所以这个过程会稍微复杂一些。
在 https 中,客户端首先打开一条到 Web 服务器端口 443
(https 默认端口。http 的是 80
),先建立 TCP 连接,之后客户端和服务端就会初始化 SSL 层(术语 SSL 来表示 SSL 或者 TLS),对加密参数进行沟通,并交换密钥。等到 SSL 初始化完成,就可以开始正式通信。客户端将请求报文发送给安全层,在发送给 tcp 之前,会先对其进行加密;服务端也在 SSL 层上发送 HTTP 响应,然后在 TCP 中发送已加密的响应。完成双方通信后,SSL关闭通知,TCP 连接关闭。
SSL 初始化大致流程:
- 发送客户端支持的加密协议给服务端
- 服务端筛选合适的加密协议,返回证书给客户端。证书中携带公钥,服务端自己保留私钥
- 客户端根据根证书验证证书合法性,倘若非法,浏览器则警告提醒;客户端生成
对称密钥
(即:加密和解密使用相同的密钥),通过公钥加密,发送给服务器(除了对称密钥,应该还有其他信息) - 服务端使用私钥解密,获取对称密钥,使用对称密钥加密数据
- 客户端使用对称密钥解密数据,确认服务端已正确接收对称密钥
tip:
- 所有连网的应用都会提供端口
- HTTPS (安全的HTTP)是 HTTP 协议的加密版本。它通常使用 SSL (en-US) 或者 TLS 来加密客户端和服务器之间所有的通信 —— mdn https
- 传输层安全性协议(英语:Transport Layer Security,缩写:
TLS
)及其前身安全套接层(英语:Secure Sockets Layer,缩写:SSL
)是一种安全协议,目的是为互联网通信提供安全及数据完整性保障 - http 传输分层,从上至下为:应用层(http)、传输层(tcp)、网络层(ip)、数据链路层。而 https 较 http 多一层,即安全层
- https 传输分层,从上至下为:应用层(http)、安全层(SSL or TLS)、传输层(tcp)、网络层(ip)、数据链路层
- 加密算法和解密算法统称为
密码算法
密钥
是使用密码算法中输入的一段参数。同一个明文在相同的密码算法和不同的密钥下会产生不同的密文。根据密钥的使用方法,可分为对称加密和公钥加密对称加密
,加密和解密使用相同的密钥公钥加密
,分为加密密钥(或称公钥
)和解密密钥(私钥
),其中任何人都可以获取公钥,而私钥只能自己使用
webpack
简单说一下 tree-shaking (树摇)
答:用于描述移除 JavaScript 上下文中的未引用代码
Tip: 更多细节请看 tree-shaking
说一下你知道的 loader
答:由于 webpack 只能识别 javascript,其他资源都需要相应的 loader 来做翻译。
与css相关的有:css-loader、style-loader、less-loader、sass-loader、postcss-loader
配合图片的有:url-loader、file-loader
与 svg 相关的有:svgo-loader
与 js 有关的有:babel-loader、ts-loader
与 vue 相关的有:vue-loader、vue-style-loader
Tip:以下是相应 loader 的简易介绍:
css-loader
,将 css 文件翻译成 webpack 能识别style-loader
,将 css 注入 dom。less-loader
,用于将 less 转为 css。类似的有 sass-loader、stylus-loaderpostcss-loader
,webpack 它用来使用 postcssurl-loader
,将图片转为 base64file-loader
,配合 url-loader 可以生成图片label-loader
,用于在 webpack 中使用 babelts-loader
,与 typescript 相关svgo-loader
,与 svg 相关,用于 svg 优化vue-loader
,它允许你以一种名为单文件组件 (SFCs)的格式撰写 Vue 组件vue-style-loader
,是一个基于 style-loader 的 fork,作为依赖项包含在 vue-loader 中,无需下载 1. vue-style-loader 即可使用。
请说一说 loader 和 plugin
答:
webpack 没有特殊配置只能识别 javascript,而前端还有css、图片等其他资源。
loader 用于对模块的源代码进行转换,可以理解成”翻译官“,比如我们需要使用 css,那么可以用 css-loader,如果需要使用图片,我们可以使用 url-loader 和 file-loader,如果需要将箭头函数翻译成普通函数,可以使用 babel-loader。loader 本质上是导出为函数的 JavaScript 模块。
而插件的目的是用于解决 loader 无法是现实其他事,plugin 强调一个事件监听能力,能在 webpack 内部监听一些事件,并且能改变一些文件打包后输出的结果。比如我们可以使用 html-webpack-plugin 来帮我们创建 html 并自动引用打包后的资源。plugin 本质上是一个具有 apply 方法的类。
Tip:有关 loader 和 plugin 的本质介绍可以查看 这里。
请说一下 vue-loader
答:vue-loader 是一个 webpack 的 loader,它允许我们能以单文件组件的格式编写 vue 组件。同时它也提供了一些其他特性,比如:提供热重载、提供 scoped css、允许为 vue 组件的每个部分使用其他的 webapck loader(如 style 部分使用 sass,template 部分使用 pug)。总之,webpack 配合 vue-loader 能帮助我们编写 vue 应用。
Tip: 更多介绍请看 vue-loader
其他
能否说说 lodash.js 的深拷贝
答:没看过 lodash.js,但我用过 jQuery 的 extend 方法,如果要深拷贝,则传 true 给第一个参数
深度作用选择器
修改 element-ui 样式,比如将表格组件的表头改为红色,通常是不起作用,就像下面这样:
<template>
<el-table></el-table>
</template>
<style scoped>
.el-table thead {
color: red;
}
</style>
我们可以怎么做?
答:可以使用 深度作用选择器。即三个大于号(>>>),就像这样:
<style scoped>
>>> .el-table thead {
color: red;
}
</style>
子域名跨域 Document.domain
不修改后端服务器,只在前端设置,如何让 a.baidu.com 和 b.baidu.com 能相互访问
答:可以使用 Document.domain
平时你是如何封装 axios
答:会简单封装下 axios。比如创建一个 request.js 的文件,里面返回一个 axios 实例,再做一下请求拦截和响应拦截。请求拦截器可以统一给所有请求传递一些信息给服务器,比如 token,在响应拦截中,比如返回 code 不是2000,则抛出错误,比如是 3000 就说明需要重新登录等等。
如何将二进制数据显示成图片
答:或许可以利用 URL、Blob。就像这样:
var blob = new Blob(...);
var url = URL.createObjectURL(blob)
imageElement.setAttribute('src', url)
一般通过什么途径来学习
答:比较杂,比如:腾讯 Web 前端、百度前端、淘宝前端、凹凸实验室、奇舞团,或者 github(看开源项目)、npm、babel、掘金、简书、博客园、纸质书、线上视频(如 慕课网、bilibili、极客学院)、技术相关的官网(如 node、express、webpack)、mdn、维基百科等等
简单谈一下跨域
答:跨域方式有很多,但现在主流的有两种,一种是开发环境和生成环境都使用 cors(跨域资源共享),一种是开发环境用 proxy,而生产环境用 nginx。
推荐的是 cors,它能决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应。前端无需变化,工作量在后端。而后端的开发环境和生成环境是一套代码,所以也很方便使用。如果后端不愿意,前端就用 proxy + nginx。
Tip:更多细节可以查看 这里
请说一下 bfc(块级格式化上下文)
答:bfc 有个特性,如果一个元素具有 bfc,那么内部子元素在怎么折腾,都不会影响外部元素。
所以 bfc 元素不会有 margin 重叠。bfc 元素也能用来清除浮动的影响,子元素浮动会导致父元素高度塌陷,则会影响后面布局,这有悖于前面的特性。
创建 bfc 有多种方式,比如 overflow:auto/hidden
、display:table-cell
是否用过 chrome 中 Performance
答:用过。通常在无痕模式下进行来分析性能,比如可以通过勾选内存
其来分析内存使用情况,或使用调用树
来找出花费时间较长的函数等。
是否了解过微服务、微前端
答:微前端首次提出是在 2016 年底,它将微服务这个应用于服务端的技术扩展到前端领域。
微前端背后的想法是:将网站或 web 应用程序视为由独立团队负责的子应用(或模块、功能)的组合。
微前端的核心是拆和合。为前端框架有 single-spa、qiankun。
Tip:更多介绍请看 初步认识微前端
是否自己写过 npm 包
答:写过。
Tip:如何发布一个 npm 包请看 这里
前端要不要学习后端
答:了解和掌握后端会更好。
例如:
- 不会后端,纯前端能干的事太少,比如在某些小公司或it部门很小的大公司
- 一个产品,前端只是一部分,如果不会后端,就不能把握住整个产品,和后端沟通也不会很顺利
所以学习后端:
- 能更快的找到问题的根源并提出解决方案
- 更高效的沟通和协作
- 能独立完成一些简单任务,节约时间并提高团队生产力
- 拓宽职业发展道路
结束
在罗列一点剩余的题目:
- 介绍一下 vue,就当我是 vue 小白
- 是否了解 csrf,能否简单谈一下
- 如果做单页面应用的首页优化
- 是否了解 vue 中的 $listeners、$attrs
- 请介绍一下 http1 和 http2 的关系和区别
- 比如让你制定项目规范,你会从什么方面着手
- 请说说你知道的 http 头部(headers)
- 请说说你知道的 http 状态码
- 是否使用过 corss-env
把心放平,轻装上阵,祝君好运。
出处:https://www.cnblogs.com/pengjiali/p/15695628.html
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。