notebooks
  • notebooks
  • _planning
    • 2022 OKR
    • basketball
    • swimming
  • communication
    • Dubbo
    • Kafka
    • Messaging
    • RPC
    • Thrift
  • computation
    • map-reduce
  • cs-basic-knowledge
    • computer-architecture
    • data-structure-and-algorithms
    • networks
    • os
  • devops
    • Docker
    • Linux
    • Prometheus
    • operations
    • security
    • trouble-shooting
  • distributed-knowledge
    • Zookeeper_CMD
    • distributed-system
  • game-engine
    • Unity
  • others
    • appium使用
  • protocols
    • http(s)协议
    • 官方链接
    • sip
  • storage
    • Elasticsearch
    • GuavaCache
    • MySQL
    • MySQL_CMD
    • NoSQL
    • Redis
    • Redis_CMD
  • system-design
    • system-design
  • tools
    • Git
    • IDEA
    • Mac
    • VScode
    • Vim
  • _working
    • doc-template
      • backend-design-review
      • correction-of-error
      • service-review
    • process
      • domain-backup
      • oncall
  • blogs
    • history
      • 8088/8086微处理器
      • 8088/8086指令系统
      • CSS-DOM
      • CSS定位
      • CSS工作原理
      • CSS控制背景
      • CSS浮动布局
      • CSS盒模型
      • Chrome开发者工具使用方法
      • DOM
      • Django Model模型层学习
      • Django-REST-framework Serializers学习
      • Django-REST-framework Views和ViewSets学习
      • Django View视图层学习
      • Gvim下Emmet安装及使用教程
      • HTTP协议简介
      • HashMap原理初探
      • JavaScript简史
      • JavaScript语法
      • Java内存模型和GC机制
      • Java基础——Lambda学习
      • Java基础——方法引用
      • Java基础——枚举类型
      • Java类加载机制
      • KMP算法
      • Kafka学习
      • Linux下用命令行编译Java程序
      • MathJax简介和基本用法
      • Python实现常见数据结构
      • Python装饰器总结
      • TCP协议的三次握手和四次挥手
      • Thrift学习
      • asyncio学习
      • markdown的常用语法
      • 修改hosts文件实现翻墙
      • 充实文档的内容
      • 关系数据库
      • 关系数据库标准语言SQL(一)
      • 关系数据库标准语言SQL(二)
      • 关系数据理论
      • 关系查询处理和查询优化
      • 内联元素和块级元素
      • 剑指offer算法题练习
      • 动态创建标记
      • 图形化用户界面
      • 在Eclipse中使用Maven构建Java Web项目
      • 增加微博秀遇到的一些问题
      • 处理机调度
      • 如何用github和hexo搭建个人博客
      • 存储管理
      • 存储系统的层次结构
      • 学习模仿lionhit网站首页的过程总结
      • 实用的GitHub小技巧
      • 并发控制
      • 循环与分支程序设计
      • 指令系统的设计
      • 指令级并行及其开发——硬件方法
      • 搭建自己的VPN服务器
      • 操作系统用户界面
      • 数据库安全性
      • 数据库完整性
      • 数据库恢复技术
      • 数据库绪论
      • 数据库编程
      • 数据库设计
      • 数据抽象
      • 文件系统
      • 文法和语言
      • 最佳实践
      • 案例研究:JavaScript图片库
      • 案例研究:图片库改进版
      • 汇编语言程序格式
      • 汇编语言程序设计基础知识
      • 流水线技术
      • 深度优先搜索和广度优先搜索
      • 牛客网——网易2017秋招编程题集合
      • 用JavaScript实现动画效果
      • 第一篇博客
      • 经典排序算法总结(Java实现)
      • 经典查找算法总结(Java实现)
      • 综合示例
      • 编译原理引论
      • 背包、队列和栈
      • 虚拟机安装Linux系统及常用软件
      • 计算机操作系统绪论
      • 计算机系统结构的基础知识
      • 设备管理
      • 设计模式之代理模式
      • 设计模式之单例模式
      • 设计模式之工厂模式
      • 设计模式之策略模式
      • 设计模式之观察者模式
      • 词法分析
      • 进程管理
      • 闭包
      • 阻止Google自动跳转到香港服务器的方法
      • 项目部署过程
  • programming-language
    • C#
      • C#
    • C&C++
      • C
    • C&C++
      • C++
    • Java
      • GoogleGuice
    • Java
      • JVM
    • Java
      • Java
    • Java
      • Maven
    • Java
      • Mybatis
    • Java
      • Spring知识
    • Java
      • SpringBoot
    • Java
      • Tomcat
    • Python
      • Python
    • Shell
      • Shell
  • wheels
    • dcc
      • 产品调研
      • 方案设计
    • red-envelope
      • 方案设计
    • short-url
      • 短链接服务
    • sso
      • 方案设计
Powered by GitBook
On this page
  • 导读
  • 平稳退化
  • 分离JavaScript
  • 向后兼容
  • 性能考虑
  1. blogs
  2. history

最佳实践

导读

平稳退化:确保网页在不支持JavaScript的情况下也能正常工作。 分离JavaScript:把网页的结构和内容与JavaScript脚本的动作分开。 向后兼容性:确保老版本的浏览器不会因为你的JavaScript脚本而死掉。 性能考虑:确保脚本执行的性能最优。

平稳退化

如果正确地使用了JavaScript脚本,就可以让访问者在他们的浏览器不支持JavaScript的情况下仍能顺利地浏览你的网站。这就是所谓的平稳退化(graceful degradation),就是说,虽然某些功能无法使用,但最基本的操作仍能顺利完成。

分离JavaScript

例如,如何用下面给这条语句来表明“当这个链接被点击时,它将调用popUp()函数”的意思呢?

<a href="http://www.example.com/" class="popUp">Example</a>

获取这个事件的元素:

var links=document.getElementsByTagName("a");
for (var i=0;i<links.length;i++){
		if(links[i].getAttribute("class")=="popUp"){
				links[i].onclick=function(){
						popUp(this.getAttribute("href"));
						return false;
					}
			}
	}

以上代码将把调用popUp()函数的onclick事件添加到有关的链接上。只要把它们存入一个外部JavaScript文件,就等于是把这些操作从HTML文档里分离出来了。而这就是“分离JavaScript”的含义。 还有个问题需要解决:如果把这段代码存入外部JavaScript文件,它们将无法正常运行。因为这段代码的第一行是:

var links=document.getElementsByTagName("a");

这条语句将在JavaScript文件被加载时立刻执行。如果JavaScript文件是从HTML文档<head>部分用<script>标签调用的,它将在HTML文档之前加载到浏览器里。同样,如果<script>标签位于文档底部</body>之前,就不能保证哪个文件最先结束加载(浏览器可能一次加载多个)。因为脚本加载时文档可能不完整,所以模型也不完整。没有完整的DOM,getElementsByTagName等方法就不能正常工作。必须让这些代码在HTML文档全部加载到浏览器之后马上开始执行。 文档将被加载到一个浏览器窗口里,document对象又是window对象的一个属性。当window对象触发onload事件时,document对象已经存在。 将上述代码打包在preparelLinks函数里,并把这个函数添加到window对象的onload事件上去。这样一来,DOM就可以正常工作了:

window.onload=prepareLinks;
function prepareLinks(){
	var links=document.getElementsByTagName("a");
        for (var i=0;i<links.length;i++){
		if(links[i].getAttribute("class")=="popUp"){
				links[i].onclick=function(){
						popUp(link[i].getAttribute("href"));
						return false;
					}
			}
	}
function popUp(winURL){
		window.open(winURL,"popUp","width=320,height=480");  //三个参数可选:网页URL、新窗口名字、新窗口属性
	}

}

向后兼容

由于某些原因,你的网站访问者可能未启用JavaScript功能。此外,不同浏览器对JavaScript的支持程度也不一样。绝大多数现代的浏览器对DOM的支持都非常不错,但比较古老的浏览器却可能无法理解DOM提供的方法和属性。因此,即使某位用户在访问你的网站时使用的是支持JavaScript的浏览器,某些脚本也不一定能正常工作。

对象检测

针对这一问题的最简单解决办法就是:检测浏览器对JavaScript的支持程度。这种检测称为对象检测。

if (method){
	statements
	}

例如,如果有一个使用了getElementById()方法的函数,就可以在调用getElementById()方法之前先检查用户所使用的浏览器是否支持这个方法。在使用对象检测时,一定要删掉方法名后面的圆括号,如果不删掉,测试的将是方法的结果,无论方法是否存在。

function myFunction(){
	if(document.getElementsById){
			statements using getElementById
		}
	}

因此,如果某个浏览器不支持getElementById()方法,它就永远也不会执行使用此方法的语句。 这个解决方案的唯一不足是,如此编写出来的函数会增加一对花括号。如果需要在函数里检测多个DOM方法和/或属性是否存在,这个函数中最重要的语句就会被深埋在一层又一层的花括号里。而这样的代码往往很难阅读和理解。 我们可以换种角度:

if(!getElementById) return false;

例如测试浏览器是否支持getElementById和getElementsByTagName:

if(!getElementById || !getElementsByTagName) return false;

虽然只是一条简单的if语句,但它可以确保那些“古老的”的浏览器不会因为我的脚本代码而出问题。这么做是为了让脚本有良好的向后兼容性。因为在给网页添加各有关行为时始终遵循了“渐进增强”的原则,所以可以确切地知道我添加的那些功能都能平稳退化,我的网页在那些“古老的”浏览器里也能正常浏览。那些只支持一部分JavaScript功能但不支持DOM的浏览器仍可以访问我的网页的内容。

浏览器嗅探技术

“浏览器嗅探”指通过提取浏览器供应商提供的信息来解决向后兼容性问题。

性能考虑

尽量少访问DOM和尽量减少标记

访问DOM的方式对脚本性能会产生非常大的影响。以下面代码为例:

if(document.getElementsByTagName("a").length>0){
		var links=document.getElementsByTagName("a");
		for(var i=0;i<links.length;i++){
				//对每个链接做点处理
			}
	}

虽然这段代码可以运行,但它不能保持最优的性能。**不管什么时候,只要是查询DOM中的某些元素,浏览器都会搜索整个DOM树,从中查找可能匹配的元素。**这段代码使用了两次getElementsByTagName方法去执行相同的操作,浪费了一次搜索。更好的办法是把第一次搜索的结果保存在一个变量中,然后在循环里重用该结果:

var links=document.getElementsByTagName("a");
if(links.length>0){
		for(var i=0;i<links.length;i++){
				//对每个链接做点处理
			}
	}

这样一来,代码功能没有变,但搜索DOM的次数由两次降低到了一次。 在多个函数都会取得一组类似元素的情况下,可以考虑重构代码,把搜索结果保存在一个全局变量里,或者把一组元素直接以参数形式传递给函数。 另一个需要注意的地方,就是要尽量减少文档中的标记数量。过多不必要的元素只会增加DOM树的规模,进而增加遍历DOM树以查找特定元素的时间。

合并和放置脚本

包含脚本的最佳方式就是使用外部文件。不过下面这种方式最好不要出现:

<script src="script/functionA.js"></script>
<script src="script/functionB.js"></script>
<script src="script/functionC.js"></script>
<script src="script/functionD.js"></script>

推荐做法是把functionA.js、functionB.js、functionC.js、functionD.js合并到一个脚本文件中。这样,就可以减少加载页面时发送的请求数量。而减少请求数量通常都是在性能优化时首先要考虑的。 脚本在标记中的位置对页面的初次加载时间也有很大影响。传统上,我们都把脚本放在文档的<head>区域,这种放置问题有一个问题。位于<head>块中的脚本会导致浏览器无法并行加载其他文件(如图像或其他脚本)。一般来说,根据HTTP规范,浏览器每次从同一个域名中最多只能同时下载两个文件。而在下载脚本期间,浏览器不会下载其他任何文件,即使是来自不同域名的文件也不会下载,所有其他资源都要等脚本加载完毕后才能下载。 把所有的<script>标签都放到文档的末尾,</body>标记之前,就可以让页面变得更快。即使这样,在加载脚本时,window对象的load事件依然可以执行对文档进行的各种操作。

压缩脚本

所谓压缩脚本,指的是把脚本文件中不必要的字节,如空格和注释,统统删除,从而达到“压缩”文件的目的。 例如以下代码:

function showPic(whichpic){
	var source = whichpic.getAttribute("href");
	var placeholder = document.getElementById("placeholder");
	placeholder.setAttribute("src",source);
	
	var text=whichpic.getAttribute("title");
	var description=document.getElementById("description");
	description.firstChild.nodeValue=text;
	}

压缩之后的代码就会变成下面这样:

function showPic(whichpic){var source = whichpic.getAttribute("href");var placeholder = document.getElementById("placeholder");placeholder.setAttribute("src",source);var text=whichpic.getAttribute("title");var description=document.getElementById("description");description.firstChild.nodeValue=text;}

精简后的代码虽然不容易看懂,却能大幅减少文件大小。多数情况下,你应该有两个版本,一个是工作副本,可以修改代码并添加注释;另一个是精简副本,用于放在站点上。通常,为了与非精简版本区分开,最好在精简副本的文件名中加上min字样:

<script src="script/scriptName.min.js"></script>

推荐的代码压缩工具:

Previous文法和语言Next案例研究:JavaScript图片库

Last updated 3 years ago

Douglas Crockford的JSMin
雅虎的YUI Compiler
谷歌的Closure Compiler