1.29 如何实现SEO优化
SEO主要分为内部和外部两个方向。
一、内部优化
- META 标签优化:例如:TITLE,KEYWORDS,DESCRIPTION (TDK)等的优化
- 内部链接的优化,包括相关性链接(Tag 标签),锚文本链接,各导航链接,及图片链接
- 网站内容更新:每天保持站内的更新(主要是文章的更新等)
- 服务器端渲染(SSR)
二、外部优化
- 外部链接类别:博客、论坛、B2B、新闻、分类信息、贴吧、知道、百科、相关信息网等尽量保持链接的多样性
- 外链运营:每天添加一定数量的外部链接,使关键词排名稳定提升。
- 外链选择:与一些和你网站相关性比较高,整体质量比较好的网站交换友情链接,巩固稳定关键词排名
1.30 SEO是什么?
SEO(Search Engine Optimization),汉译为搜索引擎优化。
搜索引擎优化是一种利用搜索引擎的搜索规则来提高目前网站在有关搜索引擎内的自然排名的方式。
SEO是指为了从搜索引擎中获得更多的免费流量,从网站结构、内容建设方案、用户互动传播、页面等角度进行合理规划,使网站更适合搜索引擎的索引原则的行为。
1.31 DNS 预解析是什么?怎么实现?
DNS优化
在介绍dns-prefetch
之前,先要提下当前对于DNS优化主流方法。
一般来说,一次DNS解析需要耗费 20-120ms,所以为了优化DNS,我们可以考虑两个方向:
- 减少DNS请求次数
- 缩短DNS解析时间
dns-prefetch
什么是dns-prefetch?
dns-prefetch
(DNS预获取)是前端网络性能优化的一种措施。它根据浏览器定义的规则,提前解析之后可能会用到的域名,使解析结果缓存到系统缓存中,缩短DNS解析时间,进而提高网站的访问速度。
为什么要用dns-prefetch?
每当浏览器从(第三方)服务器发送一次请求时,都要先通过DNS解析将该跨域域名解析为 IP地址,然后浏览器才能发出请求。
如果某一时间内,有多个请求都发送给同一个服务器,那么DNS解析会多次并且重复触发。这样会导致整体的网页加载有延迟的情况。
我们知道,虽然DNS解析占用不了多大带宽,但是它会产生很高的延迟,尤其是对于移动网络会更为明显。
因此,为了减少DNS解析产生的延迟,我们可以通过dns-prefetch预解析技术有效地缩短DNS解析时间。
<link rel="dns-prefetch" href="https://baidu.com/">
dns-prefetch背后原理
当浏览器访问一个域名的时候,需要解析一次DNS,获得对应域名的ip地址。 在解析过程中,按照:
- 浏览器缓存
- 系统缓存
- 路由器缓存
- ISP(运营商)DNS缓存
- 根域名服务器
- 顶级域名服务器
- 主域名服务器
的顺序逐步读取缓存,直到拿到IP地址。dns-prefetch
就是在将解析后的IP缓存在系统中。
这样,dns-prefetch
就有效地缩短了DNS解析时间。因为,在本地操作系统做了DNS缓存,使得DNS在解析的过程中,提前在系统缓存中找到了对应IP。
这样一来, 后续的解析步骤就不用执行了,进而也就缩短了DNS解析时间。
假如浏览器首次将一个域名解析为IP地址,并缓存至操作系统,那么下一次DNS解析时间可以低至0-1ms。
倘若结果不缓存在系统,那么就需要读取路由器的缓存,进而后续的解析时间最小也要约15ms。
如果路由器缓存也不存在,则需要读取ISP(运营商)DNS缓存,一般像taobao.com
、baidu.com
这些常见的域名,读取ISP(运营商)DNS缓存需要的时间在80-120ms,如果是不常见的域名,平均需要200-300ms。
一般来说,大部分的网站到运营商这块都能找到IP。
那也就是说,dns-prefetch
可以给DNS解析过程带来15-300ms的提升,尤其是一些大量引用很多其他域名资源的网站,提升效果就更加明显了
浏览器DNS缓存与dns-prefetch
现代浏览器为了优化DNS解析,也设有了浏览器DNS缓存。
每当在首次DNS解析后会对其IP进行缓存。至于缓存时长,每种浏览器都不一样,比如Chrome的过期时间是1分钟,在这个期限内不会重新请求DNS。
Tip:
每当Chrome浏览器启动的时候,就会自动的快速解析浏览器最近一次启动时记录的前10个域名。所以经常访问的网址就不存在DNS解析的延迟,进而打开速度更快。
而dns-prefetch 相当于在浏览器缓存之后,在本地操作系统中做了DNS缓存,个人理解,为的是给浏览器缓存做保障,尽量让DNS解析出本地,以此来做了又一层DNS解析优化。
一般来说,DNS在系统的缓存时间是大于浏览器的。
浏览器与系统DNS缓存时间
TTL(Time-To-Live),就是一条域名解析记录在DNS服务器中的存留时间
- 浏览器DNS缓存的时间跟DNS服务器返回的TTL值无关, 它的缓存时间取决于浏览器自身设置。
- 系统缓存会参考DNS服务器响应的TTL值,但是不完全等于TTL值。
国内和国际上很多平台的TTL值都是以秒为单位的,很多的默认值都是3600,也就是默认缓存1小时。
dns-prefetch
缺点
dns-prefetch
最大的缺点就是使用它太多。
过多的预获取会导致过量的DNS解析,对网络是一种负担。
最佳实践
请记住以下三点:
1.dns-prefetch
仅对跨域域上的 DNS查找有效,因此请避免使用它来指向相同域。这是因为,到浏览器看到提示时,您站点域背后的IP已经被解析。
2.除了link 还可以通过使用 HTTP链接字段将 dns-prefetch
(以及其他资源提示)指定为 HTTP标头:
Link: <https://fonts.gstatic.com/>; rel=dns-prefetch
3.考虑将 dns-prefetch 与 preconnect(预连接)提示配对。
由于dns-prefetch 仅执行 DNS查找,不像preconnect 会建立与服务器的连接。
如果站点是通过HTTPS服务的,两者的组合会涵盖DNS解析,建立TCP连接以及执行TLS握手。将两者结合起来可提供进一步减少跨域请求的感知延迟的机会。如下所示:
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin> <link rel="dns-prefetch" href="https://fonts.gstatic.com/">
Note: 如果页面需要建立与许多第三方域的连接,则将它们预先连接会适得其反。 preconnect
提示最好仅用于最关键的连接。对于其他的,只需使用 <link rel="dns-prefetch">
即可节省第一步的时间DNS查找。
1.32 indexDB怎么使用
IndexedDB 是一种浏览器本地数据库技术,与传统的关系型数据库或非关系型数据库不同,它主要目的是为 web 应用提供一个在客户端存储数据的机制,从而允许用户离线操作或缓存大量数据的情况下,得到更好的性能和可靠性。
IndexedDB 使用了 key-value 的方式来进行数据存储。每个 IndexedDB 数据库由多个对象仓库(Object Store)组成,每个对象仓库包含了对应的数据记录集合。以下是基本的使用步骤:
1.打开数据库 可以通过 IndexedDB 对象及其 open 方法打开一个指定名称的数据库,并指明需要创建或升级数据库时相应声明该数据库的配置信息,如版本号、对象仓库和索引等。
javascript复制代码// 前提条件:浏览器支持 IndexedDB let request = indexedDB.open("myDatabase", 2); request.onerror = function(event) { console.log("打开数据库失败"); }; request.onupgradeneeded = function(event) { let db = event.target.result; let objectStore = db.createObjectStore("customers", { keyPath: "id" }); objectStore.createIndex("name", "name", { unique: false }); objectStore.createIndex("email", "email", { unique: true }); }; request.onsuccess = function(event) { let db = event.target.result; }
2.创建对象仓库 在打开 IndexedDB 数据库后,可以通过调用 createObjectStore()
方法来创建对象仓库。您可以指定主键和索引,以便在查询数据库时更有效地操作数据。
javascript复制代码let objectStore = db.createObjectStore("customers", { keyPath: "id" }); objectStore.createIndex("name", "name", { unique: false }); objectStore.createIndex("email", "email", { unique: true });
3.添加数据记录 使用事务(transaction)来添加或更新数据记录。在事务内调用应用的 add()
函数提交要存储的数据,并指定其属于哪个对象仓库。需要注意,IndexedDB 中所有操作都是异步的,并且受到浏览器安全机制的限制。
javascript复制代码let customerData = [ { id: 1, name: "John Doe", email: "john@doe.com" }, { id: 2, name: "Jane Doe", email: "jane@doe.com" } ]; let transaction = db.transaction(["customers"], "readwrite"); let objectStore = transaction.objectStore("customers"); customerData.forEach(function(customer) { let request = objectStore.add(customer); request.onsuccess = function(event) { console.log("新增数据成功"); }; });
4.读取数据记录 读取数据文件与添加数据文件类似,同样通过开启事务和打开对应对象仓库,然后使用 openCursor()
或 get()
等方法遍历并获取其中的数据记录。
javascript复制代码let transaction = db.transaction(["customers"]); let objectStore = transaction.objectStore("customers"); let request = objectStore.get(1); request.onerror = function(event) { console.log("读取数据失败"); }; request.onsuccess = function(event) { if (request.result) { console.log("读取到的数据为:" + JSON.stringify(request.result)); } else { console.log [Network Error]
1.33 HTML5 有哪些 drag 相关的 API ?
dragstart:事件主体是被拖放元素,在开始拖放被拖放元素时触发。
darg:事件主体是被拖放元素,在正在拖放被拖放元素时触发。
dragenter:事件主体是目标元素,在被拖放元素进入某元素时触发。
dragover:事件主体是目标元素,在被拖放在某元素内移动时触发。
dragleave:事件主体是目标元素,在被拖放元素移出目标元素是触发。
drop:事件主体是目标元素,在目标元素完全接受被拖放元素时触发。
dragend:事件主体是被拖放元素,在整个拖放操作结束时触发。
1.34 浏览器乱码的原因是什么?如何解决?
产生乱码的原因:
网页源代码是gbk
的编码,而内容中的中文字是utf-8
编码的,这样浏览器打开即会出现html
乱码,反之也会出现乱码;
html
网页编码是gbk
,而程序从数据库中调出呈现是utf-8
编码的内容也会造成编码乱码;
浏览器不能自动检测网页编码,造成网页乱码。
解决办法:
- 使用软件编辑HTML网页内容;
- 如果网页设置编码是
gbk
,而数据库储存数据编码格式是UTF-8
,此时需要程序查询数据库数据显示数据前进程序转码;- 如果浏览器浏览时候出现网页乱码,在浏览器中找到转换编码的菜单进行转换。
1.35 js和css是如何影响DOM树构建的?
CSS不会阻塞DOM的解析,但是会影响JAVAScript的运行,javaSscript会阻止DOM树的解析,最终css(CSSOM)会影响DOM树的渲染,也可以说最终会影响渲染树的生成。
接下来我们先看javascript对DOM树构建和渲染是如何造成影响的,分成三种类型来讲解:
JavaScript脚本在html页面中
<html> <body> <div>1</div> <script> let div1 = document.getElementsByTagName('div')[0] div1.innerText = 'time.geekbang' </script> <div>test</div> </body> </html>
两段div中间插入一段JavaScript脚本,这段脚本的解析过程就有点不一样了。
当解析到script脚本标签时,HTML解析器暂停工作,javascript引擎介入,并执行script标签中的这段脚本。
因为这段javascript脚本修改了DOM中第一个div中的内容,所以执行这段脚本之后,div节点内容已经修改为time.geekbang了。脚本执行完成之后,HTML解析器回复解析过程,继续解析后续的内容,直至生成最终的DOM。
html页面中引入javaScript文件
//foo.js let div1 = document.getElementsByTagName('div')[0] div1.innerText = 'time.geekbang'
<html> <body> <div>1</div> <script type="text/javascript" src='foo.js'></script> <div>test</div> </body> </html>
这段代码的功能还是和前面那段代码是一样的,只是把内嵌JavaScript脚本修改成了通过javaScript文件加载。
其整个执行流程还是一样的,执行到JAVAScript标签时,暂停整个DOM的解析,执行javascript代码,不过这里执行javascript时,需要现在在这段代码。这里需要重点关注下载环境,因为javascript文件的下载过程会阻塞DOM解析,而通常下载又是非常耗时的,会受到网络环境、javascript文件大小等因素的影响。
优化机制:
谷歌浏览器做了很多优化,其中一个主要的优化就是预解析操作。当渲染引擎收到字节流之后,会开启一个预解析线程,用来分析HTML文件中包含的JavaScript、CSS等相关文件,解析到相关文件之后,会开启一个预解析线程,用来分析HTML文件中包含的javascprit、css等相关文件、解析到相关文件之后,预解析线程会提前下载这些文件。
再回到 DOM 解析上,我们知道引入 JavaScript 线程会阻塞 DOM,不过也有一些相关的策略来规避,比如使用 CDN 来加速 JavaScript 文件的加载,压缩 JavaScript 文件的体积。
另外,如果 JavaScript 文件中没有操作 DOM 相关代码,就可以将该 JavaScript 脚本设置为异步加载,通过 async 或 defer 来标记代码,使用方式如下所示:
<script async type="text/javascript" src='foo.js'></script> <script defer type="text/javascript" src='foo.js'></script>
async和defer区别:
async:脚本并行加载,加载完成之后立即执行,执行时机不确定,仍有可能阻塞HTML解析,执行时机在load事件派发之前。
defer:脚本并行加载,等待HTML解析完成之后,按照加载顺序执行脚本,执行时机DOMContentLoaded事件派发之前。
html页面中有css样式
//theme.css div {color:blue}
<html> <head> <style src='theme.css'></style> </head> <body> <div>1</div> <script> let div1 = document.getElementsByTagName('div')[0] div1.innerText = 'time.geekbang' // 需要 DOM div1.style.color = 'red' // 需要 CSSOM </script> <div>test</div> </body> </html>
该示例中,JavaScript 代码出现了 div1.style.color = ‘red’ 的语句,它是用来操纵 CSSOM 的,所以在执行 JavaScript 之前,需要先解析 JavaScript 语句之上所有的CSS 样式。所以如果代码里引用了外部的 CSS 文件,那么在执行 JavaScript 之前,还需要等待外部的 CSS 文件下载完成,并解析生成 CSSOM 对象之后,才能执行 JavaScript 脚本。
而 JavaScript 引擎在解析 JavaScript 之前,是不知道 JavaScript 是否操纵了 CSSOM的,所以渲染引擎在遇到 JavaScript 脚本时,不管该脚本是否操纵了 CSSOM,都会执行CSS 文件下载,解析操作,再执行 JavaScript 脚本。所以说 JavaScript 脚本是依赖样式表的,这又多了一个阻塞过程。
总结:通过上面三点的分析,我们知道了 JavaScript 会阻塞 DOM 生成,而样式文件又会阻塞js的执行。
1.36 表单中readonly和disabled的区别
共同点:能够使用户不能改变表单中的内容
不同点:
1、readonly只对input和textarea有效,但是disabled对所有的表单元素都是有效的,包括radio、checkbox
2、readonly可以获取到焦点,只是不能修改。disabled设置的文本框无法获取焦点
3、如果表单的字段是disabled,则该字段不会发送(表单传值)和序列化
1.37 主流浏览器及内核
Google chrome:webkit/blink
safari:webkit
IE:trident
firefox:gecko
Opera:presto/webkit/blink
1.38 说说你对以下几个页面生命周期事件的理解:DOMContentLoaded,load,beforeunload,unload
HTML 页面的生命周期包含三个重要事件:
DOMContentLoaded —— 浏览器已完全加载 HTML,并构建了 DOM 树,但像 和样式表之类的外部资源可能尚未加载完成。
load —— 浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等。
beforeunload/unload —— 当用户正在离开页面时。
每个事件都是有用的:
DOMContentLoaded 事件 —— DOM 已经就绪,因此处理程序可以查找 DOM 节点,并初始化接口。
load 事件 —— 外部资源已加载完成,样式已被应用,图片大小也已知了。
beforeunload 事件 —— 用户正在离开:我们可以检查用户是否保存了更改,并询问他是否真的要离开。
unload 事件 —— 用户几乎已经离开了,但是我们仍然可以启动一些操作,例如发送统计数据。
DOMContentLoaded 和脚本
当浏览器处理一个 HTML 文档,并在文档中遇到
因此,DOMContentLoaded 肯定在下面的这些脚本执行结束之后发生。
此规则有两个例外:
- 具有 async 特性(attribute)的脚本不会阻塞 DOMContentLoaded,稍后 我们会讲到。
- 使用 document.createElement(‘script’) 动态生成并添加到网页的脚本也不会阻塞 DOMContentLoaded。
DOMContentLoaded 和样式
外部样式表不会影响 DOM,因此 DOMContentLoaded 不会等待它们。
但这里有一个陷阱。如果在样式后面有一个脚本,那么该脚本必须等待样式表加载完成。原因是,脚本可能想要获取元素的坐标和其他与样式相关的属性。因此,它必须等待样式加载完成。
当 DOMContentLoaded 等待脚本时,它现在也在等待脚本前面的样式。
浏览器内建的自动填充
Firefox,Chrome 和 Opera 都会在 DOMContentLoaded 中自动填充表单。
例如,如果页面有一个带有登录名和密码的表单,并且浏览器记住了这些值,那么在 DOMContentLoaded 上,浏览器会尝试自动填充它们(如果得到了用户允许)。
因此,如果 DOMContentLoaded 被需要加载很长时间的脚本延迟触发,那么自动填充也会等待。你可能在某些网站上看到过(如果你使用浏览器自动填充)—— 登录名/密码字段不会立即自动填充,而是在页面被完全加载前会延迟填充。这实际上是 DOMContentLoaded 事件之前的延迟。
window.onload
当整个页面,包括样式、图片和其他资源被加载完成时,会触发 window 对象上的 load 事件。可以通过 onload 属性获取此事件。
window.onunload
当访问者离开页面时,window 对象上的 unload 事件就会被触发。我们可以在那里做一些不涉及延迟的操作,例如关闭相关的弹出窗口。
有一个值得注意的特殊情况是发送分析数据。
假设我们收集有关页面使用情况的数据:鼠标点击,滚动,被查看的页面区域等。
自然地,当用户要离开的时候,我们希望通过 unload 事件将数据保存到我们的服务器上。
有一个特殊的 navigator.sendBeacon(url, data) 方法可以满足这种需求,详见规范 https://w3c.github.io/beacon/。
它在后台发送数据,转换到另外一个页面不会有延迟:浏览器离开页面,但仍然在执行 sendBeacon。
当 sendBeacon 请求完成时,浏览器可能已经离开了文档,所以就无法获取服务器响应(对于分析数据来说通常为空)。
还有一个 keep-alive 标志,该标志用于在 fetch 方法中为通用的网络请求执行此类“离开页面后”的请求。你可以在 Fetch API 一章中找到更多相关信息。
如果我们要取消跳转到另一页面的操作,在这里做不到。但是我们可以使用另一个事件 —— onbeforeunload。
window.onbeforeunload
如果访问者触发了离开页面的导航(navigation)或试图关闭窗口,beforeunload 处理程序将要求进行更多确认。
如果我们要取消事件,浏览器会询问用户是否确定。
总结
页面生命周期事件:当 DOM 准备就绪时,document 上的 DOMContentLoaded 事件就会被触发。在这个阶段,我们可以将 JavaScript 应用于元素。
诸如 <script>...</script> 或 <script src="..."></script> 之类的脚本会阻塞 DOMContentLoaded,浏览器将等待它们执行结束。
图片和其他资源仍然可以继续被加载。
当页面和所有资源都加载完成时,window 上的 load 事件就会被触发。我们很少使用它,因为通常无需等待那么长时间。
当用户想要离开页面时,window 上的 beforeunload 事件就会被触发。如果我们取消这个事件,浏览器就会询问我们是否真的要离开(例如,我们有未保存的更改)。
当用户最终离开时,window 上的 unload 事件就会被触发。在处理程序中,我们只能执行不涉及延迟或询问用户的简单操作。正是由于这个限制,它很少被使用。我们可以使用 navigator.sendBeacon 来发送网络请求。
1.39 使用input标签上传图片时,怎样触发默认拍照功能?
capture 属性用于指定文件上传控件中媒体拍摄的方式。
可选值:
- user 前置
- environment 后置
- camera 相机
- camcorder 摄像机
- microphone 录音
<input type='file' accept='image/*;' capture='camera'>
给input上传多张照片
<input type="file" name="files" multiple/>
1.40 如何禁止input展示输入的历史记录?
在输入input时会提示原来输入过的内容,还会出现下拉的历史记录,禁止这种情况只需在input中加入: autocomplete=“off”
<input type="text" autocomplete="off" />
autocomplete 属性是用来规定输入字段是否启用自动完成的功能。
1.41 iconfont是什么?有什么优缺点?
什么是 IconFont
顾名思义,IconFont 就是字体图标。严格地说,就是一种字体,但是,它们不包含字母或数字,而是包含符号和字形。您可以使用 CSS 设置样式,就像设置常规文本一样,这使得 IconFont 成为 Web 开发时图标的热门选择。
优点
可以方便地将任何 CSS 效果应用于它们。
因为它们是矢量图形,所以它们是可伸缩的。这意味着我们可以在不降低质量的情况下伸缩它们。
我们只需要发送一个或少量 HTTP 请求来加载它们,而不是像图片,可能需要多个 HTTP 请求。
由于尺寸小,它们加载速度快。
它们在所有浏览器中都得到支持(甚至支持到 IE6)。
不足
不能用来显示复杂图像
通常只限于一种颜色,除非应用一些 CSS 技巧
字体图标通常是根据特定的网格设计的,例如 16x16, 32×32, 48×48等。如果由于某种原因将网格系统改为25×25,可能不会得到清晰的结果