视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
好搜移动端页面研究_html/css
2020-11-27 16:30:30 责编:小采
文档


首先声明,我并不是好搜员工,研究源码只是爱好,并没有别的意思,下面的代码是在 pc-chrome模拟手机访问时截取的 m.so.com,并自己适当的修改了些变量名(因为源码被混淆、压缩了),只是学习用,如果有冒犯贵司之处,还请【联系我】 ,我会第一时间删除~

先看整个无缓存时的源码:源码部分变量我修改过,注释是我根据上下文添加的~

html360搜索,SO靠谱wpo={p:"m_so",start:+(new Date),page:"home"} MSO = {}; SOH = {}; ENV = {}; ENV.android = '6_0'; ENV.webp = '1'; ENV.domain = 'so.com'; ENV.abv = 'a'; ENV.nat = '1'; ENV.brandname = '360'; ENV.sitename = '360搜索';
输出不同的html片段)有耦合 -->(function (e, document) { // 查找id元素 function getId(e) { return document.getElementById(e) } // 给cookie上打个标识,来设置浏览器不支持缓存 function setNotSupportLS() { setCookie("stc_nls", 1, 1) } /** * 读取ls的数据 */ function i(n, r) { var i = ""; // 容错,主要是怕读取出错 try { i = LS[n] || "", // 如果小于99则认为是error,则清空ls版本号 i.length < 99 && (setCookie(r, 0), // 设置页面不可见,然后强制刷新下页面 document.documentElement.style.display = "none", l(), e.onbeforeunload = null, location.reload(!0)) } catch (s) { // 出错时清空版本号 clearLS() } return i } /** * 设置ls数据 */ function setLS(key, t) { try { // 先设置数据到ls // 再读取设置后的内容是否跟源数据相等,如果不相等则清空ls版本,这里主要处理可能ls超大设置失败 LS[key] = t, t !== LS[e] && clearLS() } catch (n) { clearLS() } } /** * 获取cookie */ function o(e) { var n = document.cookie.split("; "); for (var r = 0, i = n.length, s; r < i; r++) { s = n[r].split("="); if (s[0] === e) return s[1] } return "" } /** * 轻量级设置cookie */ function setCookie(key, value, r) { // 默认60天 r = r || 60, // 如果没有值则为-1,也就是清空 value || (r = -1), // 过期时间,单位为天 r = (new Date(+(new Date) + r * 800000)).toGMTString(); // 生成cookie var i = key + "=" + value + "; path=/; expires=" + r; // 如果是https则加密cookie location.protocol.indexOf("https") > -1 && (i += "; Secure"), // 写入吧,少年 document.cookie = i } /** * 读取id=e的元素内容,写入ls * @param {string} e ls里的key名 * @param {string} t 元素id名 */ function html2ls(e, t) { // 如果元素存在,就获取内容,并去两端空格 var r = getId(t) && getId(t).innerHTML.trim(); setLS(e, r) } /** * 读取ls数据,并生成标签到页面中 */ function ls2html(e, n, r) { var s = i(e, r), o = document.createElement(n); o.innerHTML = s, document.head.appendChild(o) } /** * 清除cookie里的ls版本号 */ function clearLS() { var e = /(?:;)?stc_[^=]*=[^;]*;?/g, n = document.cookie.match(e) || [], r = n.length; while (r) --r, setCookie(n[r].split("=")[0], 0) } /** * 更新版本号,这个很牛b */ function updateVersion(e, t, n) { var r = o(e).split(""), i = !1; // cookie里是以2位版本号紧邻存储,比如: // a文件的一位版本是a,二位版本是b: xxoo=ab // b文件的一位版本是1,二位版本是2: xxoo=ab12 // // 后端在判断是否有缓存时应该也是这样循环,判断b文件版本对否时循环1,然后判断紧邻的版本是不是2,如果是则认为有缓存,否则认为缓存失败 // // 这样的好处是cookie值非常的小,因为最多2个版本,比md5要小 // 但由于位数,和只能用 数字+字母+部分符号 做为版本 可能文件数量上有些问题,但对于移动端来说应该完全足够 for (var s = 0, a = r.length; s < a; s += 2) // 如果查找到该标识则设置 if (r[s] === t) { r[s + 1] = n, i = !0; break } // 如果找了一圈发现没有标识说明没有设置过cookie则直接添加2位版本号 i || r.push(t, n), // 写入吧 setCookie(e, r.join("")) } var noop = function () {}, LS, d = 0, // 先全部设置空方法 v = e.LS = { html2ls: noop, ls2html: noop, updateVersion: noop }; // 尝试设置ls,如果失败则打上浏览器不支持ls的标识 stc_nls try { LS = localStorage, v.html2ls = html2ls, v.ls2html = f, v.updateVersion = c } catch (m) { setNotSupportLS() }})(this, document)输出html真实代码,并设置到ls里,更新版本号,这里是 !U -->LS.html2ls("stc_home_next_css","stc_home_next_css");LS.updateVersion('stc_ls_p_s','!','U')(function (e, t, n) { var r = alert, i = encodeURIComponent, // ... e.onerror = function (e, t, n, r, i) { for (var o = 0; o < u.length; ++o) if (s(u[o], e)) return; setTimeout(function () { if (c > 4 || l[e]) return; p({ code: "notice", msg: e }), l[e] = 1, ++c }, 1e3) }, e.alert = function (e) { p({ code: "warning", msg: "alert " + e }), !arguments.length && (e = ""), r(e) }})(this, ENV)try { // 设置页面样式,应该有版本区分 document.documentElement.className += " w-" + (localStorage.home_w || 0)}catch (e) {}// 速度打点wpo.head = +(new Date) - wpo.start
...
// 这里引用了es6-promise库,可见用了很多promise的api// 这里引用了zepto库和一些常用模块// 这里写了个monitor,主要是日志上报模块LS.html2ls("stc_home_next_base","stc_home_next_base");LS.updateVersion('stc_ls_p_s','T','R')// 这里是webpack.js// 这里是webpack打包后的一些常用模块,应该是so的公用lib:模板引擎、 能用滚动事件、解析url参数、webp图片适配(切图)、图片延迟加载(跟dom有耦合)、节流、https代理(但我试了几个图片发现不管用,应该做了域名白名单或者我姿势不对)// 这里是基于webpack生成的MSO.observer,看代码应该是整个页面的事件驱动,应该有些固定的勾子,比如scroll、show之类,好处是事件统一接管,提供页面hook// 下面有一个牛b的东西登场:window._loader,看意思是个加载器,但她不只是加载器,在代码大概意思是:// 在使用时 _loade.use(uri),然后会首先判断该uri模块有没有被执行:// 如果执行过:则直接执行回调// 如果未执行过:// 读取ls里是否有该uri:// 有:// 判断版本:// 匹配:读取ls并执行回调// 不匹配:执行加载远程操作// 没有:// 直接加载文件,文件的内容类似jsonp,加载成功后会执行回调,并把文件md5+内容写入ls以方便后续使用// 再往下就是些逻辑代码不说了

总结

开发模式

看代码应该是使用的 es6开发,使用 webpack来打包成浏览器端可运行版本,这样开发效果很高,但感觉也并没有使用 es6的全部特性,因为全部特性需要 runtime环境,而这个环境的 shim不小,我看有 promise-shim,感觉应该只用了部分功能,然后转换。但在一定的程度上也可以提高开发效率~

localStorage的应用

使用 localStorage来缓存静态文件的手法很常见,但 so的方式很新颖:

  1. cookie里使用2位版本号存放,这样很能减少 cookie的体积,由其是在流量很大的站
  2. 常用模块也使用 localStorage来存在,如果不存在则异步请求,这样也很省流量呢,比如iscroll、城市code数据,你懂的~

之前写的一个: 设计localStorage自动更新

事件管理

整个页面统一由 MSO.observer接管,公用事件统一 trigger(在这里叫 publish),我相信 so内部肯定有这方面的文档,并且有公用事件详细的说明~

比如页面加载完成有 load事件、搜索框聚焦时有 search:focus事件,这样的事件 hook可以很好的使不同模块之间的通信和判断

其他的一些

比如容错做的很好,不至于你关了 cookie或者 ls就报错,还有使用了 webp,并且我看上面有 window.ENV变量,应该是 server端判断了ua信息输出的

当然还有很多有特我的发现~

下载本文
显示全文
专题