整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

FlexSearch.js:快速、零依赖的Javas

FlexSearch.js:快速、零依赖的Javascript全文搜索库

句话介绍

Web端最快且最具内存灵活性的全文搜索库,零依赖性。

Github地址:https://github.com/nextapps-de/flexsearch

github截图

中文翻译介绍

在原始搜索速度方面,FlexSearch优于每一个搜索库,并提供灵活的搜索功能,如多字段搜索,语音转换或部分匹配。 根据使用的选项,它还提供最高内存效率的索引。 FlexSearch引入了一种新的评分算法,称为“上下文索引”,基于预先评分的词典字典体系结构,与其他库相比,实际执行的查询速度提高了1,000,000倍。 FlexSearch还为您提供非阻塞异步处理模型以及Web工作者,以通过专用平衡线程并行地对索引执行任何更新或查询。

安装

可以到官网下载经过压缩的js文件或者使用cdn,也可以使用npm安装

//使用最新版:
<script src="https://cdn.jsdelivr.net/gh/nextapps-de/flexsearch@master/dist/flexsearch.min.js"></script>
//或者特定版
<script src="https://cdn.jsdelivr.net/gh/nextapps-de/flexsearch@0.3.51/dist/flexsearch.min.js"></script>
  • npm安装
npm install flexsearch

用法

  • 创建一个索引
var index=new FlexSearch();
//或者
var index=FlexSearch.create();
//或者给定一个默认值
var index=new FlexSearch("speed");
//自定义配置
var index=new FlexSearch({
 // default values:
 encode: "balance",
 tokenize: "forward",
 threshold: 0,
 async: false,
 worker: false,
 cache: false
});
//在或者
var index=new FlexSearch("memory", {
 encode: "balance",
 tokenize: "forward",
 threshold: 0
});
  • 将文本添加到索引

Index.add(id, string)

index.add(10025, "John Doe");
  • 搜索

Index.search(string | options, <limit>, <callback>)

index.search("John");
  • 限制数量
index.search("John", 10);
  • 异步搜索
//基于回调函数
index.search("John", function(result){
 
 // array of results
});
//基于Promise
index.search("John").then(function(result){
 
 // array of results
});
//es6写法
async function search(query){
 const result=await index.search(query);
 
 // ...
}
  • 自定义搜索
index.search({
 query: "John",
 limit: 1000,
 threshold: 5, // >=threshold
 depth: 3, // <=depth
 callback: function(results){
 // ...
 }
});
//或者
index.search("John", {
 limit: 1000,
 threshold: 5,
 depth: 3
 
}, function(results){
 
 // ....
});
  • 分页
var response=index.search("John Doe", {
 limit: 5,
 page: true
});
index.search("John Doe", {
 limit: 10,
 page: response.next
});//下一页
  • 建议

获取查询建议

index.search({
 query: "John Doe",
 suggest: true
});

启用建议后,将填写所有结果(直到限制,默认为1000),并按相关性排序相似的匹配。

  • 更新

Index.update(id, string)

index.update(10025, "Road Runner");
  • 移除

Index.remove(id)

index.remove(10025);
  • 重置索引
index.clear();
  • 销毁
index.destroy();
  • 重新初始化

Index.init(<options>)

//使用相同配置重新初始化
index.init();
//使用新的配置重新初始化
index.init({
 /* options */
});
//重新初始化会销毁旧的索引
  • 获取长度
var length=index.length;
  • 获取寄存器
var index=index.index;

寄存器的格式为“@”+ id

请不要手动修改寄存器,用作只读即可

  • 添加自定义匹配器

FlexSearch.registerMatcher({REGEX: REPLACE})

  • 为所有实例添加全局匹配:
FlexSearch.registerMatcher({
 '?': 'a', // replaces all '?' to 'a'
 'ó': 'o',
 '[?úù]': 'u' // replaces multiple
});
  • 为特定实例添加私有匹配:
index.addMatcher({
 '?': 'a', // replaces all '?' to 'a'
 'ó': 'o',
 '[?úù]': 'u' // replaces multiple
});
  • 添加定制编码

通过在索引创建/初始化期间传递函数来分配自定义编码

var index=new FlexSearch({
 encode: function(str){
 
 // do something with str ...
 
 return str;
 }
});

编码器函数获取一个字符串作为参数,返回修改后的字符串

直接调用自定义编码器:

var encoded=index.encode("sample text");
  • 注册全局编码

FlexSearch.registerEncoder(name, encoder)

所有实例都可以共享/使用全局编码

FlexSearch.registerEncoder("whitespace", function(str){
 return str.replace(/\s/g, "");
});

初始化索引并分配全局编码

var index=new FlexSearch({ encode: "whitespace" });

直接调用全局编码

var encoded=FlexSearch.encode("whitespace", "sample text");
  • 混合/扩展多个编码
FlexSearch.registerEncoder('mixed', function(str){
 
 str=this.encode("icase", str); // built-in
 str=this.encode("whitespace", str); // custom
 
 // do something additional with str ...
 
 return str;
});
  • 添加自定义标记

在创建/初始化期间定义私有自定义标记

var index=new FlexSearch({
 tokenize: function(str){
 return str.split(/\s-\//g);
 }
});
  • 添加特定于语言的词干分析器和/或过滤器

Stemmer: several linguistic mutations of the same word (e.g. "run" and "running")

Filter: a blacklist of words to be filtered out from indexing at all (e.g. "and", "to" or "be")

在创建/初始化期间分配私有自定义词干分析器或过滤器

var index=new FlexSearch({
 stemmer: {
 
 // object {key: replacement}
 "ational": "ate",
 "tional": "tion",
 "enci": "ence",
 "ing": ""
 },
 filter: [ 
 
 // array blacklist
 "in",
 "into",
 "is",
 "isn't",
 "it",
 "it's"
 ]
});

使用自定义词干分析器

var index=new FlexSearch({
 stemmer: function(value){
 // apply some replacements
 // ...
 
 return value;
 }
});

使用自定义过滤器

var index=new FlexSearch({
 filter: function(value){
 
 // just add values with length > 1 to the index
 
 return value.length > 1;
 }
});

或者将词干分析器/过滤器全局分配给一种语言

Stemmer作为对象(键值对)传递,过滤为数组

FlexSearch.registerLanguage("us", {
 stemmer: { /* ... */ },
 filter: [ /* ... */ ]
});

或者使用一些预定义的词干分析器或首选语言的过滤器

<html>
<head>
 <script src="js/flexsearch.min.js"></script>
 <script src="js/lang/en.min.js"></script>
 <script src="js/lang/de.min.js"></script>
</head>
...

现在您可以在创建/初始化期间分配内置词干分析器

var index_en=new FlexSearch({
 stemmer: "en", 
 filter: "en" 
});
var index_de=new FlexSearch({
 stemmer: "de",
 filter: [ /* custom */ ]
});

在Node.js中,只需要语言包文件即可使用它们

require("flexsearch.js");
require("lang/en.js");
require("lang/de.js");

总结

本文知识大致翻译了部分使用方法,更加强大和完整的用法参考官方Github文档,里面有更加详细的用法!

好,本文属于技术类文章,建议有本领域的技术基础者阅读,以便更好地理解。

这篇文章分两个步骤讲解,如下:

第一步:什么是 flex + div 呢?

flex 是CSS的一套布局方案,一个一维布局模型;是Flexible Box的缩写,意思就算“弹性布局”,理解flex概念使用,记住下面两点即可:

  1. flex布局的两根轴线:x轴和y轴(有些会叫主轴与交叉轴、水平线与垂直线,我喜欢用x与y描述更简洁些)。
  2. flex容器和flex子元素(任何一元素标签都可以设置为flex容器,而被设置flex容器包裹在内的子元素,就是flex子元素)。

div 其就是一个普通HTML元素标签,大部分内容都是可以基于这个元素标签封装的,形成一个复用性大,共用性好的新元素标签。

第二步:如何使用 flex + div 来绘制作品,哪些场景的作品可以使用?

首先如何使用,下面我们直接看一个封装好的例子元素标签,给拆解出来讲解如何使用,其他举一反三即可:

图-1-1

这是一个普通货品上架的卡片信息内容,它就是一个flex+div结构绘制,被红线圈出来,都是一个个的flex+div内容布局,外层定义flex容器,内部就是该flex容器子元素内容,这些内容子元素,都flex布局排列x-y轴方向的信息,简单吧!所以几乎大部分的页面内容都可以使用这flex+div方式可以封装你的业务标签组件,以便复用,flex布局元素排列好,设置每个元素的内外边距、样式、字体、颜色这些标签属性,就看到这样一个通用的普通组件标签场景了。

简单吧!这就是一个flex+div绘制互联网作品,商品上架的货品卡片,多个就复用该标签组件形成一个列表展示了。

如下代码标签,就是这个货品使用flex + div方式绘制的,只有你确定了页面内容flex结构层级,即可快速绘制出来:

图-1-2

哪些场景会用到了,网站、小程序、管理系统等这些互联网作品基本都会用它的,按业务需求场景去封装你的作品输出服务。

简单吧!学会flex + div,在你的互联网产品绘制之路,快速便捷的实战,无论是自由原创、还是看图仿制都是可以的,不限制你的想象空间,就像我们画家一样,可以自由创作作品,也可看图,看景创景作品,实际工作是会沟通好场景需求,在去创作,才服务满足顾客需求意向,以更好去交付获得收益。

拜拜!下期再见!

要: 作者:闲鱼技术-意境 Flutter作为一种全新的响应式,跨平台,高性能的移动开发框架。从开源以来,已经得到越来越多开发者的喜爱。闲鱼是最早一批与谷歌展开合作,并在重要的商品详情页中使用上线的公司。一路走来,积累了大量的开发经验。

Flutter作为一种全新的响应式,跨平台,高性能的移动开发框架。从开源以来,已经得到越来越多开发者的喜爱。闲鱼是最早一批与谷歌展开合作,并在重要的商品详情页中使用上线的公司。一路走来,积累了大量的开发经验。虽然越来越多的技术大牛在flutter世界中弄得风声水起,但是肯定有很多的flutter小白希望能快速上手,享受flutter编程的乐趣。本文就是面向刚刚踏上futter的同学,从Flutter体系中最基本的一个概念widget入手学习Flutter。希望能助力每一位初学者。

可能大家要问的第一个问题是为什么从Widget开始?

从flutter的架构图中不难看出widget是整个视图描述的基础。Flutter 的核心设计思想便是

即一切即Widget。在flutter的世界里,包括views,view controllers,layouts等在内的概念都建立在Widget之上。widget是flutter功能的抽象描述。所以掌握Flutter的基础就是学会使用widget开始。

本文会从大家熟悉的UI绘制视角来介绍flutter组件和布局的基础知识。首先罗列了UI开发中最为常用,最为基础的组件。下面逐一进行介绍。

1 组件篇

1.1 Text

Text几乎是UI开发中最为重要的组件之一了,UI上面文字的展示基本上都要靠Text组件来完成。Flutter提供了原生的Text组件。Text的配置属性是很丰富的,属性主要分为两个部分一个是对齐&显示控制相关的在Text类的属性中,另一类是样式相关的属性使用单独的类TextStyle进行控制。跟native控件相比(以android为例),Text的组件基本上提供了同等的能力,并且提供了更加丰富的样式装饰能力。详细的属性可以参考官方文档flutter text.(https://docs.flutter.io/flutter/widgets/Text-class.html?spm=a2c4e.11153940.blogcont634392.6.14f56cd4VVp3Nm)

1.1.1 实践Coding

设置文字&文字大小&颜色&行数限制&文本对齐

效果如下:

1.2 Image

图片也是UI部分开发最为重要的组件之一。在能看图随看文字的年代,图片是页面展示的重中之重!Flutter同样原生提供了Image组件。下面重点介绍一下几个重点:

1.2.1 缩放

怎样设置图片显示的缩放方式呢?

Flutter中的图片缩放是fit字段来控制的。这是对最终图片展示效果影响很大的一个参数,也是容易出错的点。下面逐个分析一下flutter Image组件提供的缩放方式。

缩放属性值在BoxFit枚举中

下面列出的图片是flutter官方对各种缩放做的图片示例。基本上都表述很清楚了,就整理出来供大家查阅。

1.2.2 图片获取

怎样从各种来源加载图片?

默认的Image组件不能直接显示图片,他需要一个ImageProvider来提供具体的图片资源的(即Image中的image字段需要赋值)。咋一看这确实非常麻烦,但是实际上ImageProvider并不需要完全重新自己实现。在Image类中目前提供了一下几个实现好的ImageProvider,基本能满足常见的需求。

Image同样支持GIF图片

网络请求Image是大家最常见的操作。这里重点说明两个点:

  • 缓存

ImageCache是ImageProvider默认使用的图片缓存。ImageCache使用的是LRU的算法。默认可以存储1000张图片。如果觉得缓存太大,可以通过设置ImageCache的maximumSize属性来控制缓存图片的数量。也可以通过设置maximumSizeBytes来控制缓存的大小(默认缓存大小10MB)。

  • CDN优化

如果想要使用cdn优化,可以通过url增加后缀的方式实现。默认实现中没有这个点,但是考虑到cdn优化的可观收益,建议大家利用好这个优化。

1.2.3 FadeInImage

在实际开发中,考虑到图片加载速度可能不能达到预期。所以希望能增加渐入效果&增加placeHolder的功能。Flutter同样提供的这样的组件——FadeInImage。

FadeInImage也提供了从多种渠道加载图片的能力。这块跟上面所说差异不大。这里不再赘述。

1.2.4 实践Coding

  • 从网络获取图片保持图片比例并尽可能大的放入

  • 效果如下:

1.3 Container

Flutter的设计思想就是完全的widget化。这也就是说连最基本的padding,Center都是widget。设想一下如果每次写view,连padding,Center都要自己包一个组件是一种怎样的体验?作为一个工程师,别给只给我谈思想,实际操作的操作效率也同样非常重要。flutter 官方也意识到了这个问题,他们从实际编写效率的角提供了一个友好高效的封装,这就是Container!首先没有任何疑问,Container 本身也是一个widget。但是他却提供了对基础widget的封装,提高了UI基础装饰能力的表达效率。Container类似于android中的ViewGroup。

相信大部分的属性大家都会感觉非常亲切,结合代码注释都比较容易理解,这里就不再赘述。其中需要重点解释一下的是:Decoration和BoxConstraints。

1.3.1 装饰

Decoration是对Container进行装饰的描述。其概念类似与android中的shape。一般实际场景中会使用他的子类BoxDecoration。BoxDecoration提供了对背景色,边框,圆角,阴影和渐变等功能的定制能力。

需要注意几个点:

  • BoxDecoration的image属性相当于设置的是背景图。但是image会绘制在color 和gradient之上。
  • image是需要一个DecorationImage类的实现。DecorationImage的属性和Image组件比较类似,可以复用Image组件中的ImageProvider。

1.3.2 大小

BoxConstraints其实是对Container组件大小的描述。BoxConstraints属性比较简单。如果不太清楚可以研究一下盒子模型。这里有个点需要重点说明一下:

  • 如何表达尽可能大这样的意思?(类似于android中的match_parent)Flutter中可以使用double.infinity来做出类似的表达。

1.3.3 实践Coding

  • 设置边框&padding&margin&圆角&背景图

  • 效果如下:

1.4 手势操作

手势操作是最常见的UI交互操作。在Flutter中手势识别也是一个widget!这点对新人来说又是一个新鲜的地方。通常来说可以通过GestureDetector类来完成点击事件的处理。使用时只需要将GestureDetector包裹在目标widget外面,再实现对应事件的函数即可。从点击到长按,从缩放到拖动,这个类基本上都有相应的实现。具体可以参见组件文档。

2. 布局

页面布局应该是UI编写最为根本的知识,其主要的描述的是父子组件子子组件之间的位置关系。首先我们理解一下官方文档的逻辑:

将布局分为单孩子和多孩子是Flutter布局的一大特色。这点对native研发同学来说会比较新鲜。单孩子组件主要继承自SingleChildRenderObjectWidget。这些组件能提供丰富的装饰能力(例如container),也能提供部分特定的布局能力(例如center)。多孩子组件继承自MultiChildRenderObjectWidget,能提供更加丰富的布局能力(Flex,Stack,flow),但几乎没有装饰的能力。下面介绍几个重点布局:

2.1 Flex

Flutter在布局上也提供了完整的Flex布局能力。但是在Flutter官方文档中Layout Widgets,是看不到任何Flex的影子的。映入眼帘的却是Row,Column,这些是什么鬼?其实不难发现类似Row,Column 这样的组件,他们的基类都是Flex。Row和Column差别是设置了不同的flex-direction。而之所这么设计,是因为Flutter的widget从开始设计之初就考虑到UI布局语义保持的重要性。这块应该部分借鉴了前端的经验,极力避免一个div搞定全部页面的尴尬(当然flutter也可以使用Flex来做同样的事情,但是并不建议这么做)。

Flutter使用的Flex模型基本上跟传统的Css类似。这块前端同学可以快速上手。但是Flex对于客户端同学来说是一种全新的布局方式。Flex的基础知识可以参看flex布局基础。由于篇幅有限这里不展开叙述。这里只重点强调一个点:

如下图flex布局概念如下:

flex通过direction设置了flex的主轴方向即main axis。和主轴垂直的方向叫做cross axis。flex布局中对子布局的控制是从main axis 和cross axis两个方向上进行的。例如居中有main axis居中和cross axis居中。两者都居中才是容器的完全居中。这点是客户端同学可能会容易弄混的地方。重点关注一下。

2.1.1 实践Coding

ok,看完这些知识,我们实际需求角度实际操作几个case来熟悉一下Flex。

  • 居中

  • 效果如下:

  • weight left:right=2:1 通过设置Flexible的flex值大小完成比例设置

  • 效果如下:

2.2 stack

在实际开发中,还是需要在一些Widgets的上面再覆盖上新的Widgets。这时候就需要层式布局了。这种布局在Native上,以android为例,类似于relativeLayout 或者FrameLayout。在Flutter中使用的是Stack。

实际使用中Stack中的子Widgets分为两种:

  • positioned
  • 是包裹在组件Positioned中的组件
  • 可以通过Positioned属性灵活定位
  • non-positioned
  • 没有包裹在Positioned组件中
  • 需要通过父Widget Stack 的属性来控制布局

对于non-positioned children, 我们通过控制Stack的alignment属性来控制对齐方式。Positioned的布局方式类似于H5&weex中的position布局中的absolute布局方式。通过设置距离父组件上下左右的距离,Positioned对象能在Stack布局中更加灵活的控制view的展现方式。

2.2.1 实践Coding

  • 层叠布局

  • 效果如下:

3. Visibility

当你看完Flutter Widge文档的时候,我们突然发现一个略显尴尬的问题:组件是否显示怎么控制?貌似所有的组件中都没有这个属性!这不坑了,咋办?

目前看方法无非如下几个:

3.1 删除法

核心将该真实widget或者widget树从renderTree中移除。

具体到实践级别主要分为两个:

  • 单个组件‘隐藏’自己。在build方法中返回一个空的Container.

  • 多个child

在父容器的children字段的list中,删除掉对应的cell。

3.2 Offstage

Offstage 是一个widget。Offstage的offstage属性设置为true,那么Offstage以及他的child都将不会被绘制到界面上。

sample code如下:

3.3 透明度

设置widget的透明度,使之不可见。但是这样的方法是副作用的。因为这个对应的widget树是已经经过了完整的layout&paint过程,成本高。同时设置透明度本身也要耗费一定的计算资源,造成了二次浪费。需要注意的是即便变透明了,占据的位置还在。大家慎重选择使用。

sample code如下:

visibility的控制还是比较麻烦的。这是Flutter设计上不符合正常习惯的一个点,需要大家重点关注。

4 生命周期

4.1 state 生命周期

widget是immutable的,发生变化的时候需要重建,所以谈不上状态。StatefulWidget 中的状态保持其实是通过State类来实现的。State拥有一套自己的生命周期,下面做一个简单的介绍。

生命周期状态图如下:

几个注意点

  • didChangeDependencies有两种情况会被调用。
  • 创建时候在initState 之后被调用
  • 在依赖的InheritedWidget发生变化的时候会被调用
  • 正常的退出流程中会执行deactivate然后执行dispose。但是也会出现deactivate以后不执行dispose,直接加入树中的另一个节点的情况。
  • 这里的状态改变包括两种可能:1.通过setState内容改变 2.父节点的state状态改变,导致孩子节点的同步变化。

4.2 App生命周期

需要指出的是如果想要知道App的生命周期,那么需要通过WidgetsBindingObserver的didChangeAppLifecycleState 来获取。通过该接口可以获取是生命周期在AppLifecycleState类中。常用状态包含如下几个:

一个实际场景中的例子:

在不考虑suspending的情况下:从后台切入前台生命周期变化如下:

  • AppLifecycleState.inactive->AppLifecycleState.resumed;

从前台压后台生命周期变化如下:

  • AppLifecycleState.inactive->AppLifecycleState.paused;

5 初学者的困惑

5.1 为什么使用dart语言?

Dart语言对大部分开发者而言是很陌生的一种语言。google为啥会选择如此'冷门'的语言来开发flutter?主要原因如下:

  1. dart具有jit&Aot双重编译执行方式。这样就能利用JIt进行开发阶段的hot reload开发,提升研发效率。同时在最终release版本中使用aot将dart代码直接变成目标平台的指令集代码。简单高效,最大限度保障了性能。
  2. dart针对flutter中频繁创建销毁Widget的场景做了专门的gc优化。通过分代无锁垃圾回收器,将gc对性能的影响降至最低。
  3. dart语言在语法上面是类java的,易学易用。

5.2 为什么widget都是immutable?

个人认为是两个主要的点:

  • 提高渲染效率
  • flutte在页面渲染上面的核心思想是simple is fast!将widget设计成immutable,所以在数据变化时,flutter选择重建widget树的方式进行数据更新。采用这样方式的好处是框架不需要关心数据影响的范围,简单高效。缺点就是对GC会造成压力。
  • 组件描述的复用
  • 既然widget都是不可变的。那widget可以以较低成本进行复用。在一个真实的渲染树中可能存在同一个widget渲染树中不同节点的情况。

5.3 widget是view么?

可能刚开始接触flutter的同学最疑惑的一个问题就是widget和view的关系了。那么简单分析一下:

widget是对页面UI的一种描述。他功能类有点似于android中的xml,或者web中的html。widget在渲染的时候会转化成element。Element相比于widget增加了上下文的信息。element是对应widget,在渲染树的实例化节点。由于widget是immutable的,所以同一个widget可以同时描述多个渲染树中的节点。但是Element是描述固定在渲染书中的某一个特定位置的点。简单点说widget作为一种描述是可以复用的,但是element却跟需要绘制的节点一一对应。那element是最终渲染的view么?抱歉,还不是。element绘制时会转化成rendObject。RendObject才是真正经过layout和paint并绘制在屏幕上的对象。在flutter中有三套渲染相关的tree,分别是:widget tree, element tree & rendObject tree。三者的渲染流程如下:

那可能有人会问,为什么需要增增加中间这层的Element tree?

flutter是响应式的框架。在某一时刻页面的布局,可能受不同的输入源的影响。Element这层实际上做了对某一时刻事件的汇总,在将真正需要修改的部分同步到真实的rendObject tree上。这么做有两个好处:

  • 1.不需要直接操作UI,改为通过数据驱动视图。代码表达可以更加精炼。
  • 2.最大层度降低对最终真实视图(rendObject tree)的修改,提高页面渲染效率。

5.4 StatelessWidget 和 StatefulWidget的区别

StatelessWidget是状态不可变的widget。初始状态设置以后就不可再变化。如果需要变化需要重新创建。StatefulWidget可以保存自己的状态。那问题是既然widget都是immutable的,怎么保存状态?其实Flutter是通过引入了State来保存状态。当State的状态改变时,能重新构建本节点以及孩子的Widget树来进行UI变化。注意:如果需要主动改变State的状态,需要通过setState()方法进行触发,单纯改变数据是不会引发UI改变的。


更多资讯,尽在阿里云科技快讯~

来科技快讯看新闻鸭~

快点关注我认识我爱上我啊~~~