Appearance
前端页面优化
为何需要做前端页面优化
- 加载速度更快
- 成本更低(人力以及时间的成本),网页制作完成并不等于开发结束
- 扩展性更强,前端的工作的非独立性
优化的角度
代码语义性(与SEO相关)
重要性
语义性对SEO以及网站自然排名的影响
网站的权重决定着网站自然排名,搜索引擎优化是优化网站权重,语义性是搜索引擎优化当中的一部分
SEO是什么
SEO,全称是搜索引擎优化(Additional Search Optimization,简称 SEO),是指搜索引擎优化系统借助技术和算法改进,实现网站优化工作目标的一种推广手段。其本质是利用搜索引擎(包括百度、谷歌、搜狗等)搜索用户想要浏览的网页,然后通过技术将其展示给目标用户,让用户找到该页面,并实现转化。
对语义性的要求使得网站发生了什么变化
- 合理的将Table布局转化为DIV+CSS(即HTML+CSS)的变化
- Flash类网站的消亡(只存在一个Object的标签,爬虫无法爬取到网页的真实内容,所以自然而然搜索权重也就降低了)
与前端开发相关的搜索引擎爬虫的评判因素
- 良好的缩进与规范格式
- 良好的扩展性(代码量少)
- 合理的标签语义
- 合理的标签嵌套
- 404页面必不可少
搜索引擎抓取的网站的是资源以及图片!
标签选择
- 代码量/嵌套层数
- 语义性
前端要进行的优化
- title和meta
- 标签语义化
- div、span
- h1~h6、p
- ul、ol、li、dl、dt、dd
- a、img、table
- 特殊属性
- img的alt和title属性
- a的title属性
代码可读性(降低人力维护成本)
标签的嵌套规则
- ins和del(行内元素)可以包含块状元素或行内元素,其他任何行内元素都不允许包含块状元素
- p、h1~h6可以直接包含行内元素和文本信息,但是不允许包含块状元素
- dl元素只允许包含dt和dd,同时dt不能包含块状元素,只允许包含行内元素,dd可以包含任何元素
- 不建议from元素直接包含input元素
- table元素建议直接包含caption、colgroup、col、thead、tbody、tfoot,不建议直接包含tr或者其他任何元素
CSS代码书写规范
- 命名采用更简明有语义的英文单词进行组合
- 针对单词可以进行适当的缩写
- 采用小写字母加中划线的方式进行命名,不使用下划线或大写字母
- CSS代码的书写顺序遵循CSS样式的渲染顺序
- 显示属性——display、float、position等
- 自身属性——width、height、margin、padding、border
- 文本属性——font-size、line-height、text-align等
- 其他(含CSS3等)
JS命名规范与推荐
标识符命名规范
- 区分大小写
- 第一个字符必须是一个字母、下划线(_)或一个美元符号($),其他字符可以是字母、下划线、美元符号或数字
- 不能含有空格,不能以关键字或保留字命名
标识符命名推荐
- 遵循小驼峰命名法(如currentTime),除了第一个首字母之外,其他的单词首字母大写
- 变量或者属性以名词开头,方法或者函数以动词开头
- 常量命名:字母全部大写,多个单词之间使用下划线分隔(如PI_API)
- 构造函数函数名称:首字母大写,遵循大驼峰命名法
合理的注释和空格
在绝大多数的操作符前后,需要添加空格
在数组项的逗号的后面需要加空格
在对象属性的逗号之后;属性名和属性值分开的冒号前后,均需要加空格
函数声明的大括号之前(参数括号之后)需要加空格
函数中,分隔各个参数的逗号之后需要加空格
合理添加注释
行注释一般用于语句的注释以及语句的特殊注释,块状注释针对js文件或者js功能函数
注释符号与注释内容之间加空格
块状注释首行和末行不要写注释内容
代码扩展性与伪元素
前后台数据交互常见问题
代码扩展性不足引发的问题:
图片尺寸与文本溢出
解决办法:
- 设置合理的宽度与高度
- 设置文本超出隐藏 overflow:hidden;
- 文本超出显示为省略号 overflow:hidden;text-overflow:ellipsis;word-break:keep-all;white-space:nowrap;
未合理处理“相似元素的某个不同样式”
解决方法:
- overflow超出隐藏,margin负值的应用,整体向上移动1px
- :first-child等伪类选择器
伪元素
- :before :在元素之前添加内容
- :after:在元素之后添加内容
- 伪元素的常见用途:
- 实现清除浮动
- 实现背景图
用户交互体验
a标签的可触区问题:
- line-height、display:block
- 移动端可以使用a标签嵌套dl、dt元素
页面加载速度
文件合并与压缩
与前端优化的关系:
- 文件合并:涉及服务器请求次数
- 文件压缩:涉及文件大小
方法有:
传统合并:将所有同类型的文件内容放在一个同类型的文件下
使用在线工具进行压缩
webpack进行实现
图片与特殊字体
特殊字体
特殊字体是指所有除了电脑自带字体以外的字体。传统特殊字体的实现方法的实现方法是截图法,但是存在文件大小过大的问题,它的局限性存在内容部分无法使用特殊字体。
@font-face可以实现特殊字体,它的核心语法为:
css
/* 定义 */
@font-face {
font-family: <YourWebFontName>;
src:url('xxx.ttf');
}
/* 使用 */
div {
font-family: <YourWebFontName>;
}
IE9以上直接使用ttf就行,兼容多个浏览器需要进行字体格式转换:Online @font-face generator — Transfonter,@font-face的问题是中文类字体文件较大,于是我们可以使用处理特殊字体的工具fontmin选取需要的几个字体减少体积大小。
特殊字体出现的不同情境与处理方法(比较大小):
- “非数据部分”的特殊字体
- 大量的特殊字体
- 极少量的特殊字体
- “数据部分”,不含中文的特殊字体
- “数据部分”,中文类特殊字体
图片
图片的类型有PNG、JPG、GIF(传统图片类型),有时候我们需要进行图片压缩。
推荐网站:
图片类型还有webp(现代图片类型),webp图片大小都有很大的减少,我们可以通过智图或者iSparta软件转换webp格式图片,srcset可以和webp一起实现全浏览器兼容。
html
<picture>
<source type="images" srcset="test.webp"></source>
<img src="test.png" alt="test" title="test">
</picture>
背景图合并是指将多个图片进行合并一个图片,使用时移动背景图位置即可(使用background-position),可以减少请求次数,可以使用PS或者CSS Sprite进行合并。
文件位置
CSS和JS放置的位置
CSS代码未放置在head标签当中也会生效,JS代码放置在顶部存在潜在隐患
页面回流与重绘
页面回流:当render tree(DOM Tree和样式结构体组合后构建)中的一部分(或全部)因为元素的规模尺寸、布局、隐藏等改变而引起的页面重新渲染(或者叫做重新构建绘制)
页面重绘:当render tree中的一些元素需要更新属性,但这些属性只影响元素的外观、风格,而不会影响到元素的布局,此类页面渲染叫做页面重绘
图片预加载
图片预加载是指当用户界面需要该图片的时候再进行加载,其核心原理为:使用new Image()动态创建img,设置图片的src,并使用onload方法回调预加载完成的事件。
按需加载
根据浏览者的需求进行加载资源。
HTML内容
利用JS在符合某种条件时,将script标签的内容取出,让HTML生效,核心代码为:
html
<script id="hide" type="text/x-template">需要按需加载的HTML代码</script>
图片
利用切换图片标签的src属性,实现图片的按需加载,核心代码为:
html
<img src="" alt="图片01" title="" data-src="http://xxx.com/xx.png">
按照整个屏幕进行加载
将待加载的代码放置于textarea当中,在合适的时候使用DOM处理该内容,核心代码为:
htm
<textarea id="add1">具体代码</textarea>
利用AJAX实现页面的懒加载
在符合某种条件时,触发懒加载;使用AJAX实现前后台数据交互,异步请求数据;使用DOM将AJAX异步获取的数据加载到HTML中。
代码性能优化
标签查找(遍历)次数
获取标签,然后遍历查找标签列表的长度(查找速度较快),如果需要多次使用长度值,可以用一个常量存储起来。
javascript
var test = document.getElementById('test')
var lists = test.getElementsByTagName('li')
var len = lists.length
for(var i = 0;i < len;i++) {
......
}
大量DOM节点插入时的操作方法
获取大量数据信息后,DOM节点的动态创建方法有:
- 每处理一条数据,立即创建一个DOM节点,并将DOM节点放置在DOM树当中
- 将所有的DOM节点处理完毕之后,一次性将DOM放置于DOM树当中(速度会比较快!)
事件绑定
DOM0级事件绑定问题:同标签同类型事件只能绑定一次
DOM2级事件绑定:同标签同类型事件可绑定多次
DOM2级事件绑定方法:
javascriptxxx.addEventListener('事件名',函数,true/false) xxx.attachEvent('on+事件名',函数)
事件委托
目的是减少功能函数的数量,其基本原理为:
- 采用事件绑定方法给子级或者父级绑定事件
- 利用事件冒泡的基本原理(从内层到外层事件运行)
- 检测事件目标对象,执行相应功能函数
CSS3属性实现动画的性能优势
CSS通过left等方向属性能够实现和CSS3的translate属性的动画效果,但是相对于方向属性来说,CSS3的translate属性拥有更好的性能表现以及流畅度。PC端可能不会有很大的区别,移动端建议使用translate属性。
函数缓存
函数缓存主要应用的场景为同参数的多次执行问题,其基本原理为将一个函数对于给定参数的返回值缓存起来,用内存换取性能。
DOM2级事件兼容处理方法
DOM2级事件兼容处理方法有传统实现方法、惰性载入函数以及函数柯里化。
传统实现方法
完成了浏览器的兼容,但是它的每一次调用都要去判断,性能比较差。
javascript
function eHandler(ele, type, ftn) {
if(window.addEventListener) {
ele.addEventListener(type, function(e){
ftn.call(ele, e)
}, false)
} else if(window.attachEvent) {
ele.attachEvent('on'+type, function(e){
ftn.call(ele, e)
})
}
}
eHandler(btn1, 'click', function(){
console.log('元素被点击')
})
惰性载入函数
利用闭包解决传统实现方法的弊端
javascript
function eHandler(ele, type, ftn) {
if (window.addEventListener) {
eHandler = function (ele, type, ftn) {
ele.addEventListener(
type,
function (e) {
ftn.call(ele, e);
},
false
);
};
} else if (window.attachEvent) {
eHandler = function (ele, type, ftn) {
ele.attachEvent("on" + type, function (e) {
ftn.call(ele, e);
});
};
}
// eHandler被覆盖了
return eHandler(ele, type, ftn);
}
eHandler(btn1, "click", function () {
console.log("元素被点击");
});
函数柯里化
利用闭包解决传统实现方法的弊端
javascript
var addEvent = (function () {
if (window.addEventListener) {
return function (ele, type, ftn) {
ele.addEventListener(
type,
function (e) {
ftn.call(ele, e);
},
false
);
};
} else {
return function (ele, type, ftn) {
ele.attachEvent("on" + type, function (e) {
ftn.call(ele, e);
});
};
}
})();
addEvent(btn1, "click", function () {
console.log("元素被点击");
});
图片上传
传统的方法核心原理为:
- 使用file类型的input标签
- 点击后获取图片的路径
- 将图片加载出来
html
<div><img id="pic" src="" alt="" title="" /></div>
<div><input type="file" name="" id="fileBtn" /></div>
<script>
var pic = document.getElementById("pic");
var fileBtn = document.getElementById("fileBtn");
fileBtn.onchange = function (e) {
showPic(e);
};
function showPic(e) {
var newImg = URL.createObjectURL(fileBtn.files[0]);
pic.setAttribute("src", newImg);
}
</script>
但是该方法存在一些问题,在渲染时候文件大小过大就会导致图片加载速度变慢,我们可以使用canvas进行优化。
canvas实现方法的核心原理为:
- 使用file类型的input标签
- 点击后获取图片的路径
- 通过canvas进行图片绘制
- 将canvas绘制的图片加载出来(不加载原图)
html
<div><img id="pic" src="" alt="" title="" /></div>
<div><input type="file" name="" id="fileBtn" /></div>
<script>
var pic = document.getElementById("pic");
var fileBtn = document.getElementById("fileBtn");
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var relPic = new Image();
fileBtn.onchange = function (e) {
showPic(e);
};
function showPic(e) {
var newImg = fileBtn.files[0];
var reader = new FileReader();
var fileType = newImg.type;
reader.readAsDataURL(newImg);
reader.onload = function (e) {
var fileSrc = e.target.result;
relPic.src = fileSrc;
relPic.onload = getSize;
};
function getSize() {
var w = this.width;
var h = this.height;
var imgSize = 400;
var cw = w;
var ch = h;
if (cw >= ch) {
cw = imgSize;
ch = (cw / w) * h;
} else {
ch = imgSize;
cw = (ch / h) * w;
}
canvas.width = cw;
canvas.height = ch;
context.clearRect(0, 0, 400, 400);
context.drawImage(this, 0, 0, cw, ch);
var Img = canvas.toDataURL(fileType, 0.8);
pic.setAttribute("src", Img);
}
}
</script>
面向对象
面向对象对前端页面优化的作用有:
- 避免全局作用域被污染
- 提升代码可读性
- 提升代码复用性
- 减少存储空间(函数)的占用
单例模式
我们可以使用对象来包裹方法以及属性:
javascript
var person = {
name: '李四',
info: function() {...},
...
};
该方法减少了全局作用域被污染的影响,但是没有改变代码的复用性,我们需要使用工厂模式来解决这一问题。
工厂模式
- 入口—函数参数
- 加工—具体函数功能
- 出口—函数返回值
javascript
function User(name) {
var peo = {};
peo.name = "张三";
peo.changeInfo = function() {
...
}
...
return peo;
}
工厂模式解决了代码复用性的问题,但是它在可读性(区分不开函数的类型)以及空间性(每次调用会创建一个空间)上仍存在问题。我们使用构造模式解决可读性的问题,原型模式解决空间性的问题。
构造模式
- new实例化对象
- 将构造函数的作用域赋给新对象(this)
javascript
function User(name) {
this.name = "张三";
this.changeInfo = function() {
...
}
...
}
var peo = new User()
peo.name='李四'
peo.changeInfo()
原型模式
- 多个实例化对象的方法共用同一个空间
- 引用类型变量的相关问题,任意实例化对象的修改都会造成其他实例化对象的修改
javascript
function People() {}
People.prototype.name="张三"
People.prototype.info=function(){
...
}
var peo = new People()
peo.name='李四'
peo.info()
混合模式
- 利用构造模式与原型模式的各自优势
- 构造模式-->利用参数为每个对象创建相应属性
- 原型模式-->使实例化后对象的方法共用一个空间
javascript
function User(name) {
this.name = "张三";
this.changeInfo = function() {
...
}
...
}
People.prototype.friend="张三"
People.prototype.info=function(){
...
}
var peo = new User()
peo.name='李四'
peo.changeInfo()
peo.friend='李四'
peo.info()
幽离知识库