假设您想要在没有合适后端的情况下,在应用程序中实现一个粗略的搜索功能。你可以通过 lunr 完成它并生成一个静态搜索索引服务。
关键的问题是索引会根据内容的数量而变化。好消息是应用在最开始的时候不需要搜索索引,您可以在用户输入搜索字段时开始加载索引。
我们把索引的加载移动到性能更可接受的地方。初始搜索将比后续搜索慢,您应该显示一个加载指示符。但从用户的角度来看,这是可以接受的。Webpack 的代码拆分可以做到这一点。
使用代码拆分实现搜索
要实现代码拆分,您需要决定将拆分点放在何处,然后在那里通过 promise
加载代码:
import("./asset").then(asset => ...).catch(err => ...)
美妙的是,这可以在出现问题(网络故障等)时进行错误处理,并提供恢复的机会。您还可以使用 Promise
基础实用程序 Promise.all
来编写更复杂的查询。
现在,您需要检测用户何时点击搜索元素,然后加载数据(除非已经加载了数据),然后对其执行搜索逻辑。考虑下面的 React 实现:
App.js
import React from "react";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
index: null,
value: "",
lines: [],
results: [],
};
}
render() {
const { results, value } = this.state;
return (
<div className="app-container">
<div className="search-container">
<label>Search against README:</label>
<input
type="text"
value={value}
onChange={e => this.onChange(e)}
/>
</div>
<div className="results-container">
<Results results={results} />
</div>
</div>
);
}
onChange({ target: { value } }) {
const { index, lines } = this.state;
// 更新搜索框的值
this.setState(() => ({ value }));
// 搜索行或索引,如果行和索引存在的话
if (lines && index) {
return this.setState(() => ({
results: this.search(lines, index, value),
}));
}
// 如果索引不存在,我们就加载它
// 你应该在这里添加一个加载提示图标
// 因为索引的加载可能会花费一些时间
loadIndex()
.then(({ index, lines }) => {
// 现在搜索索引
this.setState(() => ({
index,
lines,
results: this.search(lines, index, value),
}));
})
.catch(err => console.error(err));
}
search(lines, index, query) {
// 搜索索引并匹配 README 行
return index
.search(query.trim())
.map(match => lines[match.ref]);
}
}
const Results = ({ results }) => {
if (results.length) {
return (
<ul>
{results.map((result, i) => <li key={i}>{result}</li>)}
</ul>
);
}
return <span>No results</span>;
};
function loadIndex() {
// 关键在于这个地方,通过 `import` 告诉 Webpack 在这里拆分代码
// 并且动态加载我们的搜索索引
// 注意,对于一些老的浏览器以及 IE,你需要提供 promise.all 兼容代码
return Promise.all([
import("lunr"),
import("../search_index.json"),
]).then(([{ Index }, { index, lines }]) => {
return {
index: Index.load(index),
lines,
};
});
}
在该示例中,webpack 对 import
进行静态检测。它可以基于此分割点生成单独的包。鉴于它依赖于静态分析,您无法在这种情况下进行生成 loadIndex
,也不能将搜索索引路径作为查询参数。
总结
除了搜索之外,该方法也可以与路由一起使用。当用户进入路由时,您可以加载生成视图所需的依赖项。或者,您可以在用户滚动页面到特定的位置时开始加载依赖项。import
提供了强大的功能,让您的应用程序保持精简。
你可以找到一个 lunr、React 和 Webpack 的三者结合使用的完整的例子。基本的想法是一样的,但有功能更加丰富。
回顾一下:
- 如果您的数据集很小且是静态的,那么客户端搜索是一个不错的选择。
- 您可以使用 lunr 等解决方案索引内容,然后对其执行搜索。
- Webpack 的代码拆分功能非常适合按需加载搜索索引。
- 代码拆分可以与 React 等 UI 解决方案结合使用,以实现整个用户界面。