1.css禁用鼠标事件
.disabled{
pointer-events: none;
cursor: default;
opacity:0.6;
}
复制代码
2.get/post的理解和他们之间的区别
http
超文本传输协议(HTTP)的设计目的是保证客户机与服务器之间的通信。 HTTP 的工作方式是客户机与服务器之间的请求-应答协议。 web 浏览器可能是客户端,而计算机上的网络应用程序也可能作为服务器端。
http方法:
HEAD: 与 GET 相同,但只返回 HTTP 报头,不返回文档主体 PUT: 上传指定的 URI 表示 DELETE: 删除指定资源 OPTIONS: 返回服务器支持的 HTTP 方法 CONNECT: 把请求连接转换到透明的 TCP/IP 通道 POST: 向指定的资源提交要被处理的数据
在这里小编建了一个前端学习交流扣扣群:132667127,我自己整理的最新的前端资料和高级开发教程,如果有想需要的,可以加群一起学习交流
// 查询字符串(名称/值对)是在 POST 请求的 HTTP 消息主体中发送的
POST /test/demo_form.asp HTTP/1.1
Host: w3schools.com
name1=value1&name2=value2
复制代码
GET: 从指定的资源请求数据
GET和POST的区别
GET 请求可被缓存 GET 请求保留在浏览器历史记录中 GET 请求可被收藏为书签 GET 请求不应在处理敏感数据时使用 GET 请求有长度限制(2048字符),IE和Safari浏览器限制2k;Opera限制4k;Firefox,Chrome限制8k GET 请求只应当用于取回数据
POST 请求不会被缓存 POST 请求不会保留在浏览器历史记录中 POST 不能被收藏为书签 POST 请求对数据长度没有要求
3.实现条纹网格的方式
nth-child(even/odd)
//odd表示基数,此时选中基数行的样式,even表示偶数行
.row:nth-child(odd){
background:#eee;
}
复制代码
nth-of-type(odd)
.row:nth-of-type(odd){
background:#eee;
}
复制代码
渐变实现linear-gradient
.stripe-bg{
padding: .5em;
line-height:1.5em;
background: beige;
background-size: auto3em;
background-origin: content-box;
background-image:linear-gradient(rgba(0,0,0,.2)50%, transparent0);
}
复制代码
4.js求平面两点之间的距离
// 数据可以以数组方式存储,也可以是对象方式
leta = {x:'6', y:10},
b = {x: 8, y: 20};
functiondistant(a,b){
letdx = Number(a.x) - Number(b.x)
letdy = Number(a.y) - Number(b.y)
returnMath.pow(dx*dx + dy*dy, .5)
}
复制代码
5.css禁止用户选择
body{
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
复制代码
6.数组去重
// indexOf实现
var array = [1,1,'1'];
functionunique(array){
var res = [];
for(var i =0,len= array.length; i
var current = array[i];
if(res.indexOf(current) ===-1) {
res.push(current)
}
}
returnres;
}
console.log(unique(array));
// 排序后去重
var array = [1,1,'1'];
functionunique(array){
var res = [];
var sortedArray = array.concat().sort();
var seen;
for(var i =0,len= sortedArray.length; i
// 如果是第一个元素或者相邻的元素不相同
if(!i || seen !== sortedArray[i]) {
res.push(sortedArray[i])
}
seen = sortedArray[i];
}
returnres;
}
console.log(unique(array));
// filter实现
var array = [1,2,1,1,'1'];
functionunique(array){
var res = array.filter(function(item, index, array){
returnarray.indexOf(item) === index;
})
returnres;
}
console.log(unique(array));
// 排序去重
var array = [1,2,1,1,'1'];
functionunique(array){
returnarray.concat().sort().filter(function(item, index, array){
return!index || item !== array[index -1]
})
}
console.log(unique(array));
// Object键值对
var array = [{value:1}, {value:1}, {value:2}];
functionunique(array){
var obj = {};
returnarray.filter(function(item, index, array){
console.log(typeof item + JSON.stringify(item))
returnobj.hasOwnProperty(typeof item + JSON.stringify(item)) ?false: (obj[typeof item + JSON.stringify(item)] =true)
})
}
console.log(unique(array)); // [{value:1}, {value:2}]
// ES6 Set实现
var unique = (a) => [...new Set(a)]
复制代码
7.什么是CDN和CDN的好处
CDN:CDN是将源站内容分发至最接近用户的节点,使用户可就近取得所需内容,提高用户访问的响应速度和成功率。解决因分布、带宽、服务器性能带来的访问延迟问题,适用于站点加速、点播、直播等场景。 好处: 1、多域名加载资源 一般情况下,浏览器都会对单个域名下的并发请求数(文件加载)进行限制,通常最多有4个,那么第5个加载项将会被阻塞,直到前面的某一个文件加载完毕。 因为CDN文件是存放在不同区域(不同IP)的,所以对浏览器来说是可以同时加载页面所需的所有文件(远不止4个),从而提高页面加载速度。
2、文件可能已经被加载过并保存有缓存 一些通用的js库或者是css样式库,如jQuery,在网络中的使用是非常普遍的。当一个用户在浏览你的某一个网页的时候,很有可能他已经通过你网站使用的CDN访问过了其他的某一个网站,恰巧这个网站同样也使用了jQuery,那么此时用户浏览器已经缓存有该jQuery文件(同IP的同名文件如果有缓存,浏览器会直接使用缓存文件,不会再进行加载),所以就不会再加载一次了,从而间接的提高了网站的访问速度
3、高效率 你的网站做的再NB也不会NB过百度NB过Google吧?一个好的CDNs会提供更高的效率,更低的网络延时和更小的丢包率。
4、分布式的数据中心 假如你的站点布置在北京,当一个香港或者更远的用户访问你的站点的时候,他的数据请求势必会很慢很慢。而CDNs则会让用户从离他最近的节点去加载所需的文件,所以加载速度提升就是理所当然的了。
5、使用情况分析 一般情况下CDNs提供商(如百度云加速)都会提供数据统计功能,可以了解更多关于用户访问自己网站的情况,可以根据统计数据对自己的站点适时适当的做出些许调整。
6、有效防止网站被攻击 一般情况下CDNs提供商也是会提供网站安全服务的
8.圣杯布局和双飞翼布局
参考连接
9.正则表达式匹配手机号
functioncheckPhone(){
if(!(/^1[345678]\d{9}$/.test(phone))){
alert("手机号码有误,请重填");
returnfalse;
}
}
复制代码
10.如何提高首频加载速度
1.js外联文件放到body底部,css外联文件放到head内 2.http静态资源尽量用多个子域名 3.服务器端提供html和http静态资源时最好开启gzip 4.在js,css,img等资源响应的http headers里设置expires,last-modified 5.尽量减少http requests的数量 6.js/css/html/img资源压缩 7.使用css spirtes,可以减少img请求次数 8.大图使用lazyload懒加载 9.避免404,减少外联js 10.减少cookie大小可以提高获得响应的时间 11.减少dom elements的数量 12.使用异步脚本,动态创建脚本
11.浏览器内核(渲染引擎)
IE/360/搜狗浏览器: Trident Chrome/Safari/Opera: WebKit(KHTML的一个开源的分支) (虽然我们称WebKit为浏览器内核,但不太适合直接称渲染引擎,因为WebKit本身主要是由两个引擎构成的,一个正是渲染引擎“WebCore”,另一个则是javascript解释引擎“JSCore”,它们均是从KDE的渲染引擎KHTML及javascript解释引擎KJS衍生而来。) (在13年发布的Chrome 28.0.1469.0版本开始,Chrome放弃Chromium引擎转而使用最新的Blink引擎(基于WebKit2——苹果公司于2010年推出的新的WebKit引擎),Blink对比上一代的引擎精简了代码、改善了DOM框架,也提升了安全性。) (为了减少研发成本,Opera在2013年2月宣布放弃Presto,转而跟随Chrome使用WebKit分支的Chromium引擎作为自家浏览器核心引擎) Firefox/SeaMonkey: Gecko
12.浏览器渲染过程及优化建议
1)解析: 一个是HTML/SVG/XHTML,事实上,Webkit有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。 CSS,解析CSS会产生CSS规则树。 Javascript,脚本,主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree. 2)渲染:浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意: Rendering Tree 渲染树并不等同于DOM树,因为一些像Header或display:none的东西就没必要放在渲染树中了。 CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element。也就是DOM结点。也就是所谓的Frame。 然后,计算每个Frame(也就是每个Element)的位置,这又叫layout和reflow过程。 3)绘制:最后通过调用操作系统Native GUI的API绘制。
减少reflow和repaint 1)不要一条一条地修改DOM的样式。还不如预先定义好css的class,然后修改DOM的className。
把DOM离线后修改。如: 使用documentFragment 对象在内存里操作DOM 先把DOM给display:none(有一次reflow),然后你想怎么改就怎么改。比如修改100次,然后再把他显示出来。 clone一个DOM结点到内存里,然后想怎么改就怎么改,改完后,和在线的那个的交换一下。 3)不要把DOM结点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写这个结点的属性 4)为动画的HTML元件使用fixed或absoulte的position,尽量使用transfoem,那么修改他们的CSS是不会reflow的 5)尽量少使用table布局。因为可能很小的一个小改动会造成整个table的重新布局
13. 页面导入样式时,使用link和@import有什么区别?
(1)link属于XHTML标签,除了加载CSS外,还能用于定义RSS, 定义rel连接属性等作用;而@import是CSS提供的,只能用于加载CSS; (2)页面被加载的时,link会同时被加载,而@import引用的CSS会等到页面被加载完再加载; (3)import是CSS2.1 提出的,只在IE5以上才能被识别,而link是XHTML标签,无兼容问题; (4)link支持使用js控制DOM去改变样式,而@import不支持;
14. 简述一下你对HTML语义化的理解?
用正确的标签做正确的事情。 html语义化让页面的内容结构化,便于对浏览器、搜索引擎解析; 即使在没有样式CSS情况下也以一种文档格式显示,并且是容易阅读的; 搜索引擎的爬虫也依赖于HTML标记来确定上下文和各个关键字的权重,利于SEO; 使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。
15. 请描述一下 cookies,sessionStorage 和 localStorage 的区别?
cookie是网站为了标示用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)。 cookie数据始终在同源的http请求中携带(即使不需要),记会在浏览器和服务器间来回传递。 sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。
存储大小: cookie数据大小不能超过4k。 sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。
有期时间: localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据; sessionStorage 数据在当前浏览器窗口关闭后自动删除。 cookie 设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭
16. iframe有那些缺点?
*iframe会阻塞主页面的Onload事件; *搜索引擎的检索程序无法解读这种页面,不利于SEO; *iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。 使用iframe之前需要考虑这两个缺点。如果需要使用iframe,最好是通过javascript 动态给iframe添加src属性值,这样可以绕开以上两个问题。
17. vue父子组件生命周期的顺序及作用?
(图片来自vue官网,更多vue学习推荐去官网查看详细文档)
18. 网页验证码是干嘛的,是为了解决什么安全问题?
区分用户是计算机还是人的公共全自动程序。可以防止恶意破解密码、刷票、论坛灌水; 有效防止黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试。
19. 介绍一下标准的CSS的盒子模型?低版本IE的盒子模型有什么不同的?
(1)有两种, IE 盒子模型、W3C 盒子模型; (2)盒模型: 内容(content)、填充(padding)、边界(margin)、 边框(border); (3)区 别: IE的content部分把 border 和 padding计算了进去;
20. position的值relative和absolute定位原点是?
absolute 生成绝对定位的元素,相对于值不为 static的第一个父元素进行定位。 fixed (老IE不支持) 生成绝对定位的元素,相对于浏览器窗口进行定位。 relative 生成相对定位的元素,相对于其正常位置进行定位。 static 默认值。没有定位,元素出现在正常的流中(忽略 top, bottom, left, right z-index 声明)。 inherit 规定从父元素继承 position 属性的值。
21. 经常遇到的浏览器的兼容性有哪些?原因,解决方法是什么,常用hack的技巧 ?
png24位的图片在iE6浏览器上出现背景,解决方案是做成PNG8.
浏览器默认的margin和padding不同。解决方案是加一个全局的*{margin:0;padding:0;}来统一。
IE6双边距bug:块属性标签float后,又有横行的margin情况下,在ie6显示margin比设置的大。 浮动ie产生的双倍距离 #box{ float:left; width:10px; margin:0 0 0 100px;} 这种情况之下IE会产生20px的距离,解决方案是在float的标签样式控制中加入 ——_display:inline;将其转化为行内属性。(_这个符号只有ie6会识别) 渐进识别的方式,从总体中逐渐排除局部。 首先,巧妙的使用“\9”这一标记,将IE游览器从所有情况中分离出来。 接着,再次使用“+”将IE8和IE7、IE6分离开来,这样IE8已经独立识别。 css .bb{ background-color:red;/所有识别/ background-color:#00deff\9; /IE6、7、8识别/ +background-color:#a200ff;/IE6、7识别/ _background-color:#1e0bd1;/IE6识别/ }
IE下,可以使用获取常规属性的方法来获取自定义属性, 也可以使用getAttribute()获取自定义属性; Firefox下,只能使用getAttribute()获取自定义属性。 解决方法:统一通过getAttribute()获取自定义属性。
IE下,even对象有x,y属性,但是没有pageX,pageY属性; Firefox下,event对象有pageX,pageY属性,但是没有x,y属性。
解决方法:(条件注释)缺点是在IE浏览器下可能会增加额外的HTTP请求数。
Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示, 可通过加入 CSS 属性 -webkit-text-size-adjust: none; 解决。
超链接访问过后hover样式就不出现了 被点击访问过的超链接样式不在具有hover和active了解决方法是改变CSS属性的排列顺序: L-V-H-A : a:link {} a:visited {} a:hover {} a:active {}
22. CSS优化、提高性能的方法有哪些?
关键选择器(key selector)。选择器的最后面的部分为关键选择器(即用来匹配目标元素的部分); 如果规则拥有 ID 选择器作为其关键选择器,则不要为规则增加标签。过滤掉无关的规则(这样样式系统就不会浪费时间去匹配它们了); 提取项目的通用公有样式,增强可复用性,按模块编写组件;增强项目的协同开发性、可维护性和可扩展性; 使用预处理工具或构建工具(gulp对css进行语法检查、自动补前缀、打包压缩、自动优雅降级);
23. 如何修改chrome记住密码后自动填充表单的黄色背景 ?
input:-webkit-autofill,textarea:-webkit-autofill,select:-webkit-autofill{
background-color:rgb(250,255,189);/* #FAFFBD; */
background-image: none;
color:rgb(0,0,0);
}
复制代码
24. 让页面里的字体变清晰,变细用CSS怎么做?
-webkit-font-smoothing: antialiased;
复制代码
25. 让overflow:scroll平滑滚动?
-webkit-overflow-scrolling: touch;
复制代码
26. 如何将浮点数点左边的数每三位添加一个逗号,如12000000.11转化为『12,000,000.11』?
functioncommafy(num){
returnnum && num
.toString()
.replace(/(\d)(?=(\d{3})+\.)/g,function($1, $2){
return$2+',';
});
}
复制代码
27. Javascript作用链域?
全局函数无法查看局部函数的内部细节,但局部函数可以查看其上层的函数细节,直至全局细节。 当需要从局部函数查找某一属性或方法时,如果当前作用域没有找到,就会上溯到上层作用域查找, 直至全局函数,这种组织形式就是作用域链。
28. 谈谈This对象的理解。
this总是指向函数的直接调用者(而非间接调用者); 如果有new关键字,this指向new出来的那个对象; 在事件中,this指向触发这个事件的对象,特殊的是,IE中的attachEvent中的this总是指向全局对象Window;
29. eval是做什么的?
它的功能是把对应的字符串解析成JS代码并运行; 应该避免使用eval,不安全,非常耗性能(2次,一次解析成js语句,一次执行)。 由JSON字符串转换为JSON对象的时候可以用eval,var obj =eval('('+ str +')');
30. 什么是window对象? 什么是document对象?
window对象是指浏览器打开的窗口。 document对象是Documentd对象(HTML 文档对象)的一个只读引用,window对象的一个属性。
31. ["1", "2", "3"].map(parseInt) 答案是多少?
["1", "2", "3"].map(parseInt) 答案也就是:[1, NaN, NaN]
32. 什么是闭包(closure),为什么要用它?
闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。
33. javascript 代码中的"use strict";是什么意思 ? 使用它区别是什么?
使JS编码更加规范化的模式,消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为。 默认支持的糟糕特性都会被禁用,比如不能用with,也不能在意外的情况下给全局变量赋值; 全局变量的显示声明,函数必须声明在顶层,不允许在非函数代码块内声明函数,arguments.callee也不允许使用; 保证代码运行的安全,限制函数中的arguments修改; 提高编译器效率,增加运行速度;
34. 如何判断一个对象是否属于某个类?
if(a instanceof Person){
alert('yes');
}
// 判断对象类型最好的方式
// 对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其他对象,则需要通过 call /apply 来调用才能返回正确的类型信息。
Object.prototype.toString.call('') ;//[object String]
Object.prototype.toString.call(1) ;//[object Number]
Object.prototype.toString.call(true) ;//[object Boolean]
Object.prototype.toString.call(Symbol());//[object Symbol]
Object.prototype.toString.call(undefined) ;//[object Undefined]
Object.prototype.toString.call(null) ;//[object Null]
Object.prototype.toString.call(new Function()) ;//[object Function]
Object.prototype.toString.call(new Date()) ;//[object Date]
Object.prototype.toString.call([]) ;//[object Array]
Object.prototype.toString.call(new RegExp()) ;//[object RegExp]
Object.prototype.toString.call(new Error()) ;//[object Error]
Object.prototype.toString.call(document) ;//[object HTMLDocument]
Object.prototype.toString.call(window) ;//[object global] window 是全局对象 global 的引用
复制代码
35. new一个对象的过程
1、创建一个空对象,并且this变量引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到this引用的对象中。
3、新创建的对象由this所引用,并且最后隐式的返回this。
复制代码
36. Ajax 解决浏览器缓存问题
1、在ajax发送请求前加上 anyAjaxObj.setRequestHeader("If-Modified-Since","0")。
2、在ajax发送请求前加上 anyAjaxObj.setRequestHeader("Cache-Control","no-cache")。 3、在URL后面加上一个随机数: "fresh=" + Math.random();。 4、在URL后面加上时间戳:"nowtime=" + new Date().getTime();。 5、如果是使用jQuery,直接这样就可以了 $.ajaxSetup({cache:false})。这样页面的所有ajax都会执行这条语句就是不需要保存缓存记录。
37. 如何解决跨域问题?
jsonp、 iframe、window.name、window.postMessage、服务器上设置代理页面
38. 模块化开发怎么做
立即执行函数,不暴露私有成员
varmodule1 = (function(){
var_count =0;
varm1 =function(){
//...
};
var m2 = function(){
//...
};
return {
m1 : m1,
m2 : m2
};
})();
复制代码
39. AMD(Modules/Asynchronous-Definition)、CMD(Common Module Definition)规范区别?
对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。 2. CMD 推崇依赖就近,AMD 推崇依赖前置。
// CMD
define(function(require, exports, module){
var a =require('./a')
a.doSomething()
// 此处略去100行
var b =require('./b') // 依赖可以就近书写
b.doSomething()
// ...
})
// AMD 默认推荐
define(['./a','./b'],function(a, b){ // 依赖必须一开始就写好
a.doSomething()
// 此处略去100行
b.doSomething()
// ...
})
复制代码
40. requireJS的核心原理是什么?(如何动态加载的?如何避免多次加载的?如何 缓存的?)
参考链接
41. 如何实现一个模块加载器
参考连接
42. 列举几条 JavaScript 的基本代码规范
(1)不要在同一行声明多个变量 (2)如果你不知道数组的长度,使用 push (3)请使用 ===/!== 来比较 true/false 或者数值 (4)对字符串使用单引号 ''(因为大多时候我们的字符串。特别html会出现") (5)使用对象字面量替代 new Array 这种形式 (6)绝对不要在一个非函数块里声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但是它们解析不同 (7)不要使用全局函数 (8)总是使用 var 来声明变量,如果不这么做将导致产生全局变量,我们要避免污染全局命名空间 (9)Switch 语句必须带有 default 分支 (10)使用 /**...*/ 进行多行注释,包括描述,指定类型以及参数值和返回值 (11)函数不应该有时候有返回值,有时候没有返回值 (12)语句结束一定要加分号 (13)for 循环必须使用大括号 (14)if 语句必须使用大括号 (15)for-in 循环中的变量应该使用 var 关键字明确限定作用域,从而避免作用域污染 (16)避免单个字符名,让你的变量名有描述意义 (17)当命名对象、函数和实例时使用驼峰命名规则 (18)给对象原型分配方法,而不是用一个新的对象覆盖原型,覆盖原型会使继承出现问题
43. DOM操作——怎样添加、移除、移动、复制、创建和查找节点?
1)创建新节点 createDocumentFragment() //创建一个DOM片段 createElement() //创建一个具体的元素 createTextNode() //创建一个文本节点 (2)添加、移除、替换、插入 appendChild() removeChild() replaceChild() insertBefore() //在已有的子节点前插入一个新的子节点 (3)查找 getElementsByTagName() //通过标签名称 getElementsByName() //通过元素的Name属性的值(IE容错能力较强,会得到一个数组,其中包括id等于name值的) getElementById() //通过元素Id,唯一性
44. jquery实现原理
参考链接
45. jquery中如何将数组转化为json字符串,然后再转化回来?
jQuery中没有提供这个功能,所以你需要先编写两个jQuery的扩展:
$.fn.stringifyArray =function(array){
returnJSON.stringify(array)
}
$.fn.parseArray =function(array){
returnJSON.parse(array)
}
然后调用:
$.fn.stringifyArray(array)
复制代码
46. jquery的$.extend深复制和浅复制原理
// 浅复制
$ = {
extend :function(target, options){
for(nameinoptions) {
target[name] = options[name];
}
returntarget;
}
};
// 深复制
$ = {
extend :function(deep, target, options){
for(nameinoptions) {
copy = options[name];
if(deep && copy instanceof Array) {
target[name] = $.extend(deep, [], copy);
}elseif(deep && copy instanceof Object) {
target[name] = $.extend(deep, {}, copy);
}else{
target[name] = options[name];
}
}
returntarget;
}
};
复制代码
47. jquery.extend 与 jquery.fn.extend的区别?
jquery.extend 为jquery类添加类方法,可以理解为添加静态方法
jquery.fn.extend: 源码中jquery.fn = jquery.prototype,所以对jquery.fn的扩展,就是为jquery类添加成员函数 使用: jquery.extend扩展,需要通过jquery类来调用,而jquery.fn.extend扩展,所有jquery实例都可以直接调用。
48. 如何判断当前脚本运行在浏览器还是node环境中?(阿里)
this=== window ?'browser':'node';
复制代码
49. 实现一个页面操作不会整页刷新的网站,并且能在浏览器前进、后退时正确响应。给出你的技术实现方案?
参考链接
50. 把 Script 标签 放在页面的最底部的body封闭之前 和封闭之后有什么区别?浏览器会如何解析它们?
之前推荐的方法(已过时):之前解决这个问题的方法是把script标签放到body标签之后 ,这确保了解析到