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 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概念使用,记住下面两点即可:
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开发中最为常用,最为基础的组件。下面逐一进行介绍。
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)
设置文字&文字大小&颜色&行数限制&文本对齐
效果如下:
图片也是UI部分开发最为重要的组件之一。在能看图随看文字的年代,图片是页面展示的重中之重!Flutter同样原生提供了Image组件。下面重点介绍一下几个重点:
怎样设置图片显示的缩放方式呢?
Flutter中的图片缩放是fit字段来控制的。这是对最终图片展示效果影响很大的一个参数,也是容易出错的点。下面逐个分析一下flutter Image组件提供的缩放方式。
缩放属性值在BoxFit枚举中
下面列出的图片是flutter官方对各种缩放做的图片示例。基本上都表述很清楚了,就整理出来供大家查阅。
怎样从各种来源加载图片?
默认的Image组件不能直接显示图片,他需要一个ImageProvider来提供具体的图片资源的(即Image中的image字段需要赋值)。咋一看这确实非常麻烦,但是实际上ImageProvider并不需要完全重新自己实现。在Image类中目前提供了一下几个实现好的ImageProvider,基本能满足常见的需求。
Image同样支持GIF图片
网络请求Image是大家最常见的操作。这里重点说明两个点:
ImageCache是ImageProvider默认使用的图片缓存。ImageCache使用的是LRU的算法。默认可以存储1000张图片。如果觉得缓存太大,可以通过设置ImageCache的maximumSize属性来控制缓存图片的数量。也可以通过设置maximumSizeBytes来控制缓存的大小(默认缓存大小10MB)。
如果想要使用cdn优化,可以通过url增加后缀的方式实现。默认实现中没有这个点,但是考虑到cdn优化的可观收益,建议大家利用好这个优化。
在实际开发中,考虑到图片加载速度可能不能达到预期。所以希望能增加渐入效果&增加placeHolder的功能。Flutter同样提供的这样的组件——FadeInImage。
FadeInImage也提供了从多种渠道加载图片的能力。这块跟上面所说差异不大。这里不再赘述。
Flutter的设计思想就是完全的widget化。这也就是说连最基本的padding,Center都是widget。设想一下如果每次写view,连padding,Center都要自己包一个组件是一种怎样的体验?作为一个工程师,别给只给我谈思想,实际操作的操作效率也同样非常重要。flutter 官方也意识到了这个问题,他们从实际编写效率的角提供了一个友好高效的封装,这就是Container!首先没有任何疑问,Container 本身也是一个widget。但是他却提供了对基础widget的封装,提高了UI基础装饰能力的表达效率。Container类似于android中的ViewGroup。
相信大部分的属性大家都会感觉非常亲切,结合代码注释都比较容易理解,这里就不再赘述。其中需要重点解释一下的是:Decoration和BoxConstraints。
Decoration是对Container进行装饰的描述。其概念类似与android中的shape。一般实际场景中会使用他的子类BoxDecoration。BoxDecoration提供了对背景色,边框,圆角,阴影和渐变等功能的定制能力。
需要注意几个点:
BoxConstraints其实是对Container组件大小的描述。BoxConstraints属性比较简单。如果不太清楚可以研究一下盒子模型。这里有个点需要重点说明一下:
手势操作是最常见的UI交互操作。在Flutter中手势识别也是一个widget!这点对新人来说又是一个新鲜的地方。通常来说可以通过GestureDetector类来完成点击事件的处理。使用时只需要将GestureDetector包裹在目标widget外面,再实现对应事件的函数即可。从点击到长按,从缩放到拖动,这个类基本上都有相应的实现。具体可以参见组件文档。
页面布局应该是UI编写最为根本的知识,其主要的描述的是父子组件子子组件之间的位置关系。首先我们理解一下官方文档的逻辑:
将布局分为单孩子和多孩子是Flutter布局的一大特色。这点对native研发同学来说会比较新鲜。单孩子组件主要继承自SingleChildRenderObjectWidget。这些组件能提供丰富的装饰能力(例如container),也能提供部分特定的布局能力(例如center)。多孩子组件继承自MultiChildRenderObjectWidget,能提供更加丰富的布局能力(Flex,Stack,flow),但几乎没有装饰的能力。下面介绍几个重点布局:
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居中。两者都居中才是容器的完全居中。这点是客户端同学可能会容易弄混的地方。重点关注一下。
ok,看完这些知识,我们实际需求角度实际操作几个case来熟悉一下Flex。
在实际开发中,还是需要在一些Widgets的上面再覆盖上新的Widgets。这时候就需要层式布局了。这种布局在Native上,以android为例,类似于relativeLayout 或者FrameLayout。在Flutter中使用的是Stack。
实际使用中Stack中的子Widgets分为两种:
对于non-positioned children, 我们通过控制Stack的alignment属性来控制对齐方式。Positioned的布局方式类似于H5&weex中的position布局中的absolute布局方式。通过设置距离父组件上下左右的距离,Positioned对象能在Stack布局中更加灵活的控制view的展现方式。
当你看完Flutter Widge文档的时候,我们突然发现一个略显尴尬的问题:组件是否显示怎么控制?貌似所有的组件中都没有这个属性!这不坑了,咋办?
目前看方法无非如下几个:
核心将该真实widget或者widget树从renderTree中移除。
具体到实践级别主要分为两个:
在父容器的children字段的list中,删除掉对应的cell。
Offstage 是一个widget。Offstage的offstage属性设置为true,那么Offstage以及他的child都将不会被绘制到界面上。
sample code如下:
设置widget的透明度,使之不可见。但是这样的方法是副作用的。因为这个对应的widget树是已经经过了完整的layout&paint过程,成本高。同时设置透明度本身也要耗费一定的计算资源,造成了二次浪费。需要注意的是即便变透明了,占据的位置还在。大家慎重选择使用。
sample code如下:
visibility的控制还是比较麻烦的。这是Flutter设计上不符合正常习惯的一个点,需要大家重点关注。
widget是immutable的,发生变化的时候需要重建,所以谈不上状态。StatefulWidget 中的状态保持其实是通过State类来实现的。State拥有一套自己的生命周期,下面做一个简单的介绍。
生命周期状态图如下:
几个注意点
需要指出的是如果想要知道App的生命周期,那么需要通过WidgetsBindingObserver的didChangeAppLifecycleState 来获取。通过该接口可以获取是生命周期在AppLifecycleState类中。常用状态包含如下几个:
一个实际场景中的例子:
在不考虑suspending的情况下:从后台切入前台生命周期变化如下:
从前台压后台生命周期变化如下:
Dart语言对大部分开发者而言是很陌生的一种语言。google为啥会选择如此'冷门'的语言来开发flutter?主要原因如下:
个人认为是两个主要的点:
可能刚开始接触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上。这么做有两个好处:
StatelessWidget是状态不可变的widget。初始状态设置以后就不可再变化。如果需要变化需要重新创建。StatefulWidget可以保存自己的状态。那问题是既然widget都是immutable的,怎么保存状态?其实Flutter是通过引入了State来保存状态。当State的状态改变时,能重新构建本节点以及孩子的Widget树来进行UI变化。注意:如果需要主动改变State的状态,需要通过setState()方法进行触发,单纯改变数据是不会引发UI改变的。
更多资讯,尽在阿里云科技快讯~
来科技快讯看新闻鸭~
快点关注我认识我爱上我啊~~~
*请认真填写需求信息,我们会在24小时内与您取得联系。