10道常见的全栈工程师面试题
英文原文链接:"10 Essential Full Stack Interview Questions"
1. 编程时候关于SEO搜索引擎优化,有哪些关键点需要考虑?
- 对所有图片添加适当的
alt
标签 - 添加正确的HTML标签保证页面结构,比如
<h1>
/<h2>
/<h3>
和<p>
- 连接公司网站到其社交网站
- 添加XML sitemap
- 避免有坏掉的链接
- 使用友好的链接URL(人类可读的)
- 添加robots.txt (此文件是为搜索引擎爬虫准备的,可以告诉爬虫哪些页面或文件可爬哪些不可以)
- 集成Google Analytics(or alternative)
- 添加网站的图标favicon,为不同设备浏览器做优化处理更加
- 保证网站加载速度
- 避免有JavaScript错误
- 优化资源(包括JS文件打包压缩最小化)
- 为每个页面单独设置不超过70字符的标题
- 每个页面添加meta描述
- 保证有足够的内容和相关关键词(搜索引擎会因为你的大部分页面只有一句话而对你的网站做降级惩罚)
- 采用合适的浏览器缓存处理
- 避免W3C验证错误
2. 如果你想写一个endpoint检查一个文件资源是否存在,你会采取什么路径和方法?
这个问题目的在于测试应聘者的对于RESTful API的了解是否标准。最常见的坑是创建endpoint的时候采用描述性动词在路径中,比如:
GET /users/search
GET /posts/create
按照标准,一个RESTful路径应该只包含名词——使用的方法决定采取什么行动。比如:
POST /users
(创建一个用户)PUT /users/{id|slug}
(替代一个用户)PATCH /users/{id|slug}
(更新用户的一部分)DELETE /users/{id|slug}
(删除用户)GET /users/{id|slug}
(获取一个用户)
判断某资源是否存在经常会被用到,但是大多实现并不符合行业标准,标准的实现方法举一个例子为:
HEAD /users/{id|slug}
这个方法会使用最少的资源宽带,因为它并不返回data,只会返回一个200
(资源存在)或者404
(资源不存在) HTTP状态码。
3. 列出优化网站提高其高效性(efficient)于可拓展性(scalable)的方法
优化一个网站是一门艺术,但不少人对其并不了解。以下的列表内的内容列举条目越多越表示应聘者越有可能在编写代码的时候就开始考虑实现而不是事后再返回来添加优化。
- 优化所有资源(图片、文件)
- 将所有资源安置于一个单独的、无cookie的域名,采用CDN最佳。
- 避免行内JavaScript和CSS
- 开启gzip
- 保证代码被压缩最小化
- 推迟JS的分析
- 为响应式图片添加
srcset
- 采用合适的浏览器缓存处理
- 减少DNS查表
- 避免重复代码
- 开启HTTP Keep-Alive (
Keep-Alive
是一个通用消息头,允许消息发送者暗示连接的状态,还可以用来设置超时时长和最大请求数。) - 根据设备和浏览器采用不同大小的图片 (serve scaled images)
- 合适的地方采用image sprites
- 优先采用异步资源
- 尽早具体说明字符集于服务器端
- 避免CSS的
@import
- 具体说明一个缓存验证
- 最小化请求的体积
- 避免bad requests和404
- 具体说明图片尺寸
- 尽量少量的HTTP请求,比如请求尽量少的外部资源
- 尽量少采用图片,多采用CSS
- 保证无W3C验证错误
4. 参考下面一个数据库table的设计,这里面有什么设计缺陷?应该如何改进?
CREATE TABLE `notifications` (
`id` INT NOT NULL AUTO_INCREMENT,
`type` INT(8) NOT NULL,
`notifiable_id` INT unsigned NOT NULL,
`notifiable_type` VARCHAR(10) NOT NULL,
`relation_id_1` INT unsigned,
`relation_type_1` VARCHAR(10),
`relation_id_2` INT unsigned,
`relation_type_2` VARCHAR(10),
`updated_at` TIMESTAMP NOT NULL,
`created_at` TIMESTAMP NOT NULL,
PRIMARY KEY (`id`)
);
这里面最关键的问题是关于object_id_X
和object_type_X
域的设计。在一个表格中存在递增的域名是个糟糕的设计,他们其实可以被存放在另一个表格中:
Notification table:
CREATE TABLE `notifications` (
`id` INT NOT NULL AUTO_INCREMENT,
`type` INT(8) NOT NULL,
`notifiable_id` INT unsigned NOT NULL,
`notifiable_type` VARCHAR(10) NOT NULL,
`updated_at` TIMESTAMP NOT NULL,
`created_at` TIMESTAMP NOT NULL,
PRIMARY KEY (`id`)
);
Notification Relations table:
CREATE TABLE `notification_relations` (
`notification_id` INT unsigned NOT NULL,
`relation_id` INT unsigned NOT NULL,
`relation_type` VARCHAR(10) NOT NULL,
PRIMARY KEY (`notification_id`)
);
5. 一个常见的问题是当集成第三方的服务,使自己的API请求不得不等待其回应,用户因此也将需要更久的等待时间。我们如何避免这个问题?
最有效率的方式是使用队列(queues)。
当我们的API收到一个请求,一个单独的job会被创建并加入到队列中。这个job将会被独立运行并反馈到被请求的endpoint,这将会让服务器无延迟反馈。
有不少提供队列服务的运营商,其中比较知名的有:
- Amazon SQS
- Redis
- Beanstalkd
6. 如何避免一个机器爬虫爬你的公开的API?
既然是公开的API,从技术角度讲是无法避免被爬数据的。但一些技术手段可以阻挡大部分人爬、机爬。一个有效的方法是设置“服务限流”(也叫throttling——节流阀)。
限流可以限定一个设备在限定的时间段内只可请求限定的次数。超过请求次数后,会发送一个429 Too Many Attempts
HTTP错误。
注意:追踪多IP的设备很重要,因为此IP也许并非此设备专属,屏蔽掉这个IP可能会导致使用这个IP的所有其他用户失去请求权限。
其他可采用但并不完美的方案有:
- 根据UA用户代理(User Agent String)屏蔽请求,但这个很容易能被绕过
- 在应用的前端生成一个暂时的session访问密码,但这个并不安全,在前端存储一个密码会有可能被反工程,进而让任何人都有机会破解生成可用的访问码。
7. 一个用户试图创建一个已存在的资源,比如试图用注册过的邮箱再次注册。我们应该采用什么HTTP状态码?
尽管标准答案有争议,但广泛被接受的是采用一个409 Conflict
HTTP状态码。采用422 Unprocessable Entity
也是可接受的。
也有人会说400 Bad Request
也可以,但不建议采用,因为按照常规这个代码表示的是“服务器并不懂这个请求”,在这个例子里面似乎并不正确。
8. 一个响应式设计网页中需要展示一个图片,如何保证页面加载尽量小的图片?
关键属性为srcset
和sizes
。使用这两个属性可以保证同一个img元素根据设备展现不同大小的图片。
<img src="https://example.com/images/image.png" srcset="https://example.com/images/image-1024.png 1024w, https://example.com/images/image-512.png 512w" sizes="100vw">
任何建议采用JavaScript来修改图片源都有风险,因为这预示着应试者没有注意最新的CSS特性支持和行业最佳方案。
9. 有一个登录界面只有一个登录表格,请问如何保证这个表格横纵都居中?
技术层面有很多解决方案,但行业内目前最流行的“正确”方式是采用display: flex
和margin: auto
。
也有采用position: absolute
, top: 50%
, margin-top: -Xpx
的,但自从引进了display: flex
,现在这个方案已经不是最佳方案了。
10. 数据库SQL语句,SELECT的掌握
写一个SELECT语句,获取 posts
内所有 user_id
为某值的数据,并且包括所有post的 post_likes
数。
表格结构如下:
CREATE TABLE `posts` (
`id` INT NOT NULL AUTO_INCREMENT,
`text` TEXT,
`user_id` INT unsigned NOT NULL,
`updated_at` TIMESTAMP NOT NULL,
`created_at` TIMESTAMP NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `post_likes` (
`post_id` INT unsigned NOT NULL,
`user_id` INT unsigned NOT NULL,
`created_at` TIMESTAMP NOT NULL
);
需要注意,你必须一条语句内就实现。有多个解决方案,其中一个为:
SELECT
posts.*,
COUNT(post_likes.user_id) AS likes
FROM
posts
LEFT JOIN
post_likes
ON posts.id = post_likes.post_id
WHERE posts.user_id = 'XXX'
GROUP BY posts.id