静态网页,指的是全部由html、css、js等文件组成的页面,一经发布出来,就已经不会再发生变化了(除非有人手动修改),静态网页是完全运行在客户端的。
与之相对应的便是动态网页,这种网页即使在没有人修改过网页内容的情况下,也会随着用户、时间、地点等因素而返回不同的内容,因此被称为是“动态”的。常见的有jsp、asp、php等等,此外使用异步请求向服务器获取数据也属于动态网页的范畴。
相比于静态网页,动态网页可以实现更丰富的功能,如登陆、注册、内容管理、数据统计、数据查询等等,这些功能都有一个共同点,即它们都是以数据库为基础来实现的。
至此我们了解到,动、静态网页的最主要区别,其实是页面内容有没有受到数据库的影响,而不是页面有没有在“动”(如gif、视频、动画效果等等)。
看到这里,可能会有读者疑惑:
既然静态页面不经过数据库,那要怎么实现搜索功能?
确实,在日常的生活和开发当中,大多数的搜索功能,都是使用异步请求来向服务端获取数据的,都是要经过数据库的,百度、谷歌搜索、淘宝上搜索商品、搜索WebAPI等等,但实际上,这只是搜索功能实现的方式之一,它并不是搜索的唯一方式,我们姑且把这种方式称为“动态搜索”。
如果有接触过一些静态博客项目的话,如WordPress、Hexo等,应该都知道这些系统都是支持文章搜索功能的。
以上的搜索功能没有经过服务器数据库。
虽然很想这么说,但是有多少人会信?
举一个更直观的例子,如果使用Vue框架开发过项目的话,应该都接触过ElementUI这个经典的UI库,其中就包含了一些具有搜索功能的组件,如Select、Cascader、Input,虽然我们一般都是将这些组件配合API来异步获取&渲染数据的,但实际上,即使没有API的支持,它也能够完成搜索功能,下面以Select组件为例。
提供给组件的数据
搜索数据
这个组件的搜索功能,其实跟前面提到的WordPress、Hexo系统的文章搜索功能是类似的,都是通过匹配固定的数据来返回搜索结果,需要注意的是,这一过程中,并没有数据库的参与,所以,它们都属于“静态搜索”。
现在我们应该了解到,静态搜索,就是把数据和搜索逻辑都放在前端,由前端来完成从输入关键字到返回数据的全搜索过程,这就是它的原理。
其实无论静态搜索,还是动态搜索,都是通过某些算法来匹配数据返回搜索结果的,只不过是两者搜索逻辑的执行位置和数据存放位置不同而已。
所以,要完成这个静态搜索功能,有两个地方需要我们去准备,它们分别是负责检索数据的逻辑和用于检索的数据。
实现搜索逻辑
根据上面所举的Select组件例子,我们很容易想到,这个功能应该要满足这样的需求:
从搜索input表单中获取出文本,并将其和数据的某些字段匹配,如果匹配成功就把该条数据返回,最后将返回的数据渲染在搜索结果面板上。
首先准备需要的HTML结构
样式就不贴了,可能每个人做出来的效果都不一样,能用就行。
以下是渲染出来的效果。
定义搜索方法
此处的代码实现了一个简单的逻辑,即如果数据的title字段包含有输入的文本value的话,就将其返回。
// 跟日常开发一样,我们可以先忽略后端的数据,随便编一些就好了。
let dataList = [
{
fileName: "http:///11.html",
tag: "测试标签1,测试标签11",
title: "测试标题1",
categoryName:"测试分类1"
},
{
fileName: "http:///22.html",
tag: "测试标签2",
title: "测试标题2",
categoryName:"测试分类2"
},
{
fileName: "http:///33.html",
tag: "测试标签3",
title: "测试标题3",
categoryName:"测试分类3"
}
];
function search(value) {
let r = [];
for (let item of dataList || []) {
if (item.title.includes(value)) {
r.push(item);
}
}
return r;
}
定义渲染数据方法
在该方法中执行搜索方法,如果有数据返回,则将数据用字符串拼接为一个ul列表,并将其设置为dom节点ele的html内容,否则返回找不到数据的提示信息。
let render = function(val, ele, ) {
let list = search(val);
let html = "";
if (list.length > 0) {
html += "<ul>";
for (let item of list) {
let href = item.fileName;
html += "<li class='item' title='"+ item.title +"'>";
html += "<a class='hover-1' href='"+ href +"'>" + item.title + "</a>";
html += "</li>"
}
html += "</ul>";
}
else {
html += "<div>搜索不到数据:|</div>";
}
ele.innerHTML = html;
}
为input搜索框添加事件侦听器
监听input输入事件和失去焦点事件,同时,为了尽可能地优化性能,我们还可以用防抖函数对主要函数进行包装,来处理频繁执行的问题。
该侦听器主要的功能为,监听输入事件,并获取其中的文本,如果不为空,则执行渲染方法,并显示搜索结果面板,否则隐藏搜索结果面板。
let searchInput = document.querySelector("#search-input");
let searchResult = document.querySelector("#search-result");
let content = searchResult.querySelector('.content');
// 防抖
function debounce(fn, delay = 500) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
let wrap = debounce(function() {
let value = searchInput.value;
if (value) {
render(value, content);
searchResult.style.display = "block";
}
else {
if (searchResult.style.display === 'block') {
searchResult.style.display = "none";
}
}
});
searchInput.addEventListener('input', wrap);
searchInput.addEventListener('focus', wrap);
完成以上的代码后,基本的搜索功能已经实现了。
上述的代码是连贯的,都在同一个文件内,按顺序摆放就行,分开只是为了让它们看起来更方便
此外我们还可以为匹配命中的字符添加高亮效果,将搜索逻辑部分代码修改如下。
function search(value) {
let r = [];
for (let item of dataList || []) {
let reg = new RegExp('(' + value + ')', 'i');
if (reg.test(item.title)) {
// 将匹配命中的字符用span标签包装,并添加样式
let title = item.title.replace(reg, "<span style='color: var(--c-1);'>$1</span>");
let _item = {
fileName: item.fileName,
tag: item.tag,
title: title,
categoryName: item.categoryName,
original: item.title // 由于替换后的title并不是纯文本,所以把原数据备份,后续可能会使用到
};
r.push(_item);
}
}
return r;
}
查看运行效果。
只差最后一步
完成以上的代码编写后,只差准备数据这一步了。
这一步是异常简单的,你甚至可以不用写逻辑代码,自己手动输入一份格式正确的数据(即包含标题等信息,上述的例子中只用到了title这一字段),或者也可以像我一样,把从数据库查询出来的记录按照正确的格式直接写进一个js文件里,然后再通过script标签加载,相当的简单粗暴。
数据库查询的记录如下。
这里使用的是java语言,其他语言也是类似的,其他语言请自行实现
private void writeDataListToJsFile(List<Article> articleList) {
List<Map<String, Object>> dataList = new ArrayList<>();
articleList.forEach(item -> {
Map<String, Object> map = new HashMap<>();
map.put("title", item.getTitle());
map.put("fileName", FolderName.ARTICLE_FOLDER + File.separator + item.getId() + ".html");
map.put("categoryName", item.getCategoryName());
map.put("tag", item.getTag());
dataList.add(map);
});
String outputPath = appConfig.getOutputPath();
try {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(outputPath + File.separator + FolderName.JS_FOLDER + File.separator + "dataList.js"));
String str = "let dataList = " + JSON.toJSONString(dataList) + ";";
bufferedWriter.write(str);
IOUtil.close(bufferedWriter);
} catch (IOException e) {
e.printStackTrace();
throw new CustomException("将数据写入文件失败");
}
}
然后在html文件中,使用script标签加载该js文件(注意要在搜索逻辑之前加载)。
最终的效果如下。
至此,这个简单版的静态搜索功能就算完成了,如果需要更复杂的搜索逻辑和更多的匹配字段,比方说,同时根据标题、标签、分类进行匹配,可以在此基础上自行拓展。
当然,该功能缺点也是有的,比如说,当数据量很大的时候,查询速度会变得很慢,以及加载网页的速度也会变慢,会严重影响用户的网站体验,这也是为什么常用异步请求搜索数据的原因之一,但对于个人博客项目那点体量的数据而言,无需担心这个问题。
以上就是本篇文章的所有内容了,希望对你有所帮助。