Skip to content
On this page

前端页面优化

为何需要做前端页面优化

  • 加载速度更快
  • 成本更低(人力以及时间的成本),网页制作完成并不等于开发结束
  • 扩展性更强,前端的工作的非独立性

优化的角度

代码语义性(与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元素

页面加载速度

文件合并与压缩

与前端优化的关系:

  • 文件合并:涉及服务器请求次数
  • 文件压缩:涉及文件大小

方法有:

图片与特殊字体

特殊字体

​ 特殊字体是指所有除了电脑自带字体以外的字体。传统特殊字体的实现方法的实现方法是截图法,但是存在文件大小过大的问题,它的局限性存在内容部分无法使用特殊字体。

​ @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级事件绑定方法:

    javascript
    xxx.addEventListener('事件名',函数,true/false)
    xxx.attachEvent('on+事件名',函数)
    

    扩展:addEventListener()的第三个参数

事件委托

目的是减少功能函数的数量,其基本原理为:

  • 采用事件绑定方法给子级或者父级绑定事件
  • 利用事件冒泡的基本原理(从内层到外层事件运行)
  • 检测事件目标对象,执行相应功能函数

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()

Released under the MIT License.