lasy load图片的实现
无意中看到了这篇关于使用LQIP(Low Quality Image Placeholders) 原文链接,方案实现图片加载优化方案。在此实践一把。
1. 方案实现
- 页面初始化时,img元素初始化时,src使用低质量的图片,显示出图片的大概轮廓
- 页面滚动到当前图片位置,后台启动加载原图
- 原图加载完成,替换掉之前的src显示出原图
监听页面是否滚动到图片位置使用的IntersectionObserver,减少使用scroll过程造成的页面卡顿。
2. 代码结构
index.tmpl
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>lasyload and LQIP</title> <meta name="viewport" id="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <link rel="shortcut icon" href="/favicon.ico"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="format-detection" content="telephone=no"> </head> <body> <p>You may need to install go and Primitive</p> <p> This is long article. <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> <img src="./images/1.jpg" class="big-pic" alt=""> <br/><br/><br/> next line <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> <img src="./images/2.jpg" class="big-pic" alt=""> <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 3th picture <br/> <br/> <img src="./images/3.jpg" class="big-pic" alt=""> </p> <script type='text/javascript' src='./bundle.js'></script> </body> </html>
lib/index.js
function replaceSrc (changes) { changes.forEach(change => { if (change.intersectionRatio <= 0) return let item = change.target let src = item.getAttribute('data-src') let img = new Image() img.onload = function () { item.setAttribute('src', src) } img.src = src // observer.unobserve(item) }) } module.exports = function (selector) { let els = document.querySelectorAll(selector) let observer = new IntersectionObserver(replaceSrc.bind()); [].forEach.call(els, (item) => { observer.observe(item) }) }
index.js
let dealPic = require('../lib/index')
dealPic('.big-pic')
各文件关系是:
- index.tmpl是HTML模板,编译之后生成index.html
- lib/index.js 用于监听元素是否达到了页面位置
- index.js用于生成bundle.js
3. 构建的实现
首先需要安装go和primitive. primitive库: https://github.com/fogleman/primitive
安装primitive时有些网站被墙了,所以要下载安装包安装。网上搜索到了一个第三方下载地址:https://www.golangtc.com/download/package
按提示下载解压至go目录下的src,然后执行
go install github.com/fogleman/primitive
安装完成环境。
安装npm库依赖包:
npm i glob sqip browserify
使用glob检索所有的图片,sqip用于转换图片为最小格式的svg,browserify为了使用require模块。
使用正则检测出html模板中所有的img, 匹配已经转为svg的文件,文件相同,使用base64格式替换掉原src,新加data-src为原src, 设置width height等。最后输出文件为index.html
构建的代码如下:
const sqip = require('sqip') const glob = require('glob') const path = require('path') const fs = require('fs') function getSvgList (folder) { return new Promise((resolve, reject) => { glob(folder + '/**/*.jpg', {}, function (err, files) { if (err) { reject(err) } let list = [] files.forEach(file => { const result = sqip({ filename: file, numberOfPrimitives: 10, }) list.push({ file: path.join(__dirname, file), result, }) }) resolve(list) }) }) } function replaceHtml (html, list) { if (!path.isAbsolute(html)) { html = path.join(__dirname, html) } let str = fs.readFileSync(html, 'utf-8') let htmlPath = path.dirname(html) const REG = /(<img .*?src=\")(.*?)\"( .*?>)/g let imgSrc = REG.exec(str) while (imgSrc) { let src = imgSrc[2] let file if (path.isAbsolute(src)) { file = path.join(__dirname, src) } else { file = path.join(htmlPath, src) } list.forEach(item => { if (item.file === file) { var imgInfo = item.result.img_dimensions str = str.replace(imgSrc[0], function (str) { return imgSrc[1] + 'data:image/svg+xml;base64,' + item.result.svg_base64encoded + `" data-width="${imgInfo.width}" data-height="${imgInfo.height}"` + ` data-src="${src}"` + imgSrc[3] }) } }) imgSrc = REG.exec(str) } fs.writeFileSync('./example/index.html', str) } getSvgList('./example/images/') .then(list => { replaceHtml('./example/index.tmpl', list) })
4. 执行构建并检测结果
在npm中加入scripts:
{ "build": "npm run sqip & browserify example/index.js -o example/bundle.js", "sqip": "node sqip.js" }
执行build后,example文件目录下会生成index.html。开启web服务器(我是使用的自己写的xmocker),访问example/index.html,使用throttle进行查看效果。
5. 总结
方案只是针对普通Html,对于其他模块,应该有现成的方案。访问体验确实好了很多。
附:代码地址 https://github.com/wenlonghuo/code-test/tree/master/001_lasyload