之前使用Ocean这款Hexo主题时,它的全文检索方式是使用hexo-generator-search这个库生成search.xml
(xml文件会包含整站文章的标题和正文内容)。通过ocean.js
在每个页面中异步加载xml文件,当用户进行搜索行为时,会触发search.js
进行xml内容搜索和展示。
通过search.js进行内容搜索和展示
但这种方式也存在一定弊端,一是资源会重复被加载(虽然目前市面上最新的浏览器已经实现第二次加载从硬盘读取缓存,但部分老旧版本浏览器仍会加载多次);二是这个xml文件体积极大,当网站有几百篇文章时这个体积可能会达到10M以上。
xml文件会被加载多次
当然我们也可以通过对hexo-generator-search进行配置来减少xml文件体积,例如将content
设置为false
就可以仅生成标题和元数据信息,大大减小xml文件体积。
1 2 3 4 5
| search: path: search.xml field: post content: false #将content设置为false,生成的结果仅涵盖标题和其他元信息。 template: ./search.xml
|
那有没有更好的方案?譬如就不要xml文件了,还能不能实现内容搜索?这时我想到了——v2ex。在v2ex的搜索栏中进行搜索,会自动跳转到Google搜索引擎,并以“帖子URL前缀 + 搜索词”的形式搜索内容。这相当于在精确查找Google收录的页面内容。只要搜索引擎会及时收录内容,我们就可以借助该功能实现“站内搜索”啦!
效果截图
说干就干,先写出了第一个版本。回车搜索后就会前往Google进行搜索!但此时发现一个问题:手机上的回车键不生效啊!
调试一番后发现移动端的e.keyCode
是NumpadEnter
,那给判断加上就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <input id="search" name="search" type="search">
<script>
function searchKeyword (keyword) { document.getElementById('search').blur();
const newWindow = window.open('_blank'); newWindow.location = 'https://www.google.com/search?q=' + keyword.split(' ').join('+') + '+site:https://www.youdiandongxi.com'; }
document.getElementById('search').addEventListener('keydown', function (e) { if (e.code === 'NumpadEnter' || e.keyCode === 13) { searchKeyword(e.target.value) } }) </script>
|
然后又想到google可能对大部分访客不友好,那就把bing、baidu都加上。三个拉出来一起跑,哪个速度快用哪个!
判断思路也很简单,同时加载三个网站的favicon图标,哪个先出来用哪个!如果都失效了,就提示用户“搜索暂时不可用”!(逻辑主要在searchKeyword函数的Promise.any部分,以及testWebsiteConnectivity函数测试图片是否可访问)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
function testWebsiteConnectivity (domain) { return new Promise((resolve, reject) => { const img = new Image() img.onload = function () { resolve(domain) } img.onerror = function () { reject(domain) } img.referrerPolicy = 'no-referrer' img.src = domain + 'favicon.ico'
setTimeout(() => { reject(domain) }, 2000) }) }
function searchKeyword (keyword) { const websites = ['https://www.google.com/', 'https://cn.bing.com/', 'https://www.baidu.com/'] const searchPrefix = ['search?q=', 'search?q=', 's?wd=']
const taskList = [] websites.forEach(website => { taskList.push(testWebsiteConnectivity(website)) })
Promise.any(taskList).then(fastDomain => { const index = websites.findIndex((domain) => domain === fastDomain);
document.getElementById('search').blur();
const newWindow = window.open('_blank'); newWindow.location = fastDomain + searchPrefix[index] + keyword.split(' ').join('+') + '+site:<%= config.url %>'; }).catch((e) => { alert('搜索功能暂不可用,请稍后再试!')
document.getElementById('search').blur(); }) }
document.getElementById('search').addEventListener('keydown', function (e) { console.log(e) if (e.code === 'NumpadEnter' || e.keyCode === 13) { searchKeyword(e.target.value) } })
|