212019.4

2019免费版权(可商用)的web设计素材资源指北

2091 年 4 月 18 日,对视觉中国的处罚决定出炉,天津市网信办对网站运营主体汉华易美(天津)图像技术有限公司作出从重罚款的处罚。当天盘后,视觉中国(000681)发布公告称行政处罚金额为:30 万元

商业版权是商业公司赚钱的重要途径,一方面通过维权收取版权费,另一方面保护自己的设计成果。但是对于 web 设计者、开发者而言,版权是挡住创意的重要一步。对于创意的作者,在快速实现自己的想法时,如何绕过版权,快速实现创意呢?比较理想的是选择比较合理的开源产品来使用,通过遵守开源协议来使用该产品,以此绕过版权问题。但是,对于 web 设计素材而言,则没有那么容易,特别是对于 web 开发者而言,即使网上找到一些满意的素材,也不敢随意在自己的产品中使用。那么,究竟应该如何选择和寻找适合我们需要的 web 设计素材呢?

常见版权形式

有很多种版权协议形式,但是,对于使用者而言,一定要认真阅读素材供应商提供的版权文件。国外的大部分素材网站都会提供 pdf 的 license 文档,如果你的项目中使用了它们的素材,应该下载这个 pdf 文件,放在你的代码库旁边一起管理。常见的版权方式有:

  • 未经授权不允许使用
  • 个人免费使用,商业未经授权不允许使用
  • 有限使用(attribute),通常需要你在你的产品显著位置注明素材来源及作者
  • 素材版权购买,一般素材平台拥有销售版权的权利,素材作者将作品托管在平台,由平台统一售卖
  • 按等级打包收费,例如素材作者方将素材打包为不同的包,每个包包含的素材数量不同,钱越多数量越多
  • 终身授权与包时授权,通常由素材作者自己建立网站进行销售,目前较多是按年和按月授权
  • 会员制,会员无限授权,通过注册会员,按周期缴会员费,会员可无限使用素材,会员过期不续费将失去该权利
  • 免费版权使用,可商用
  • 开源免费,遵守开源协议

从上面的这些版权方式你可以发现,其实网上很多声称“免费下载”的素材是存在坑的,因为“免费下载”并不代表你可以“免费使用”,下载的目的可能是学习和演示,而非使用在你的产品中,因此,免费下载一定与使用没有任何关系,而且通常是供应商设下的陷阱。在下载时,读者一定要阅读清楚素材的版权文件,而不能直接将下载的素材使用在自己的产品中。

最理想的使用方式是完全免费(可商用)的版权协议,没有任何限制,随意使用。但是这样会存在一个问题,就是使用者在免费获得素材之后,又为素材附加了自己的使用协议,向后面的使用者收费。因此,比较合理的是基于使用协议的免费可商用版权,即限定使用者必须遵循的条件,从而避免上述情况发生,而这种协议在开源领域最为广泛,因此,越来越多的编辑性素材创作者开始考虑开源自己的作品,例如下面会提到的思源家族字体

图片

图片的版权是最为复杂的。主要针对两类图片:摄影作品,绘制作品。故名思议,摄影作品是不可能开源的,而且由于不同平台限制,以及摄影师对自身影响力传播的需要,摄影作品大部分面临版权问题,即使摄影师自己,也面临被平台限制的问题。而绘制作品主要是基于 PS 等绘图软件创作的成果,这类作品大部分都包含了比较重的创作成本,因此,有偿使用也是情理之中。大部分作品主要被放在素材平台上进行统一的版权销售,因此,我们需要注意。我们在绕过平台直接向作者购买图片时,也要注意作者是否和平台签订了排他性协议,如果平台强制要求作品只能由平台方进行版权销售,那么也要规避这类风险。

我所找到的可商用的免费图片平台包括:

pexels-免费高清素材下载网站

pexels 是非常知名的素材查找网站,很多设计师都会定期去查看。它上面提供大量素材,而且由于它上面的素材都是由其他创作者上传的,而非它自己收集的,并且遵循免费原则,因此,它上面的素材都可以免费使用。

pexels 高清图片素材网站

Unsplash-免费高清可商用摄影图片资源

unsplash 也是非常知名的摄影图片素材网站。它和 pexels 的不同在于,它上面有大量的人物摄影作品,而且同样支持免费商用,因此,可以作为需要模特出镜时的图片素材来源。

unsplash 高清人物摄影图片素材网站

pixabay-免费高清矢量图片资源

pixabay 也是一个知名的免费版权的图片资源网站,和前面两个不同的是,它还提供非常丰富的矢量图(插画),甚至还有视频资源可以下载。

pixabay 丰富的摄影、插画、视频素材网站

gratisography-免费高清创意图片资源

gratisography 也是一个知名的素材资源网站,也是免费版权的。不同于前面的网站,它上面的素材很多是经过处理好的,带有风格的成品。

gratisography 风格化高清图片资源网站

可以看出,大部分免费版权的网站提供的主要内容还是摄影作品,毕竟创作过程更快,可短时间内创作多张图片,但是,对于提供可编辑的插画而言,几乎都不感兴趣,毕竟成本很高。

字体

字体则是除了图片之外更容易发生版权纠纷的设计元素。因为几乎所有的设计作品中,一定会需要文字来进行适当的阐释,从而吸引用户的目光。对于出版物上的字体,实际上风险很大。前段时间爆出的微软雅黑版权问题,再度引发了行业对字体版权的重视。之所以字体版权维护更容易,是因为字体开发方可以在字体设计中刻意的加入特殊设计风格,因为字体都要透过系统输入来呈现出来,要修改系统中的字体非常麻烦,很难对所有字都进行重新创作,维权容易。因此,设计师在挑选字体时,一定要使用有版权,或免费版权的字体。目前我收集到的免费可商用版权字体有:

站酷系列字体 5 款

前几年在一次字体版权问题爆发之后由站酷网发布的完全免费可商用的“酷站高端黑体”打开了完全免费商用字体的大门,随后几年,酷站发布了:站酷快乐体、站酷酷黑体、站酷意大利体、站酷小薇 logo 体。加上之前的酷站高端黑体一共 5 款完全免费的字体。而且由于字体开源,还有人在快乐体基础上进行了修改,产生了其他衍生字体。

方正系列字体 4 款

方正的 4 款字体方正黑体、方正书宋、方正仿宋、方正楷体可以免费用于商业用途,但免费并不代表不需要授权,你需要向方正公司申请商业授权使用(意思是,对方公司可能拒绝你的申请),这样它可以限制你的使用范围,防止字体被用于不恰当的场景。

思源系列字体 4 款

Adobe 在线字体库 Typekityu 与谷歌一起正式发布了新款开源字体:思源黑体(Source Han Sans)。随后,Adobe 和 Goole 又在思源黑体基础上,开发了宋体版,即思源宋体

虽然由两家公司合作开发,但各自对这款字体的称呼却不一样,Adobe 称之为 Source Han Sans(思源黑体),而谷歌则称它 Noto Sans CJK(CJK:Chinese, Japanese, Korean),并纳入了 Noto pan-Unicode 字体家族。Noto 是 Google 发起的一项字体项目,遵循 SIL 字体版权协议。思源黑体支持以下字体粗细:ExtraLight、Light、Normal、Regular、Medium、Bold 和 Heavy。

在思源黑体基础上思源柔黑体思源真黑体这两款字体也被做出来,并且也是免费商用的。

文泉驿字体系列 5 款

文泉驿是一个开源汉字字体项目,由旅美学者房骞骞(FangQ)于 2004 年 10 月创建,集中力量解决 GNU/Linux 高质量中文字体匮乏的状况。目前,文泉驿已经开发并发布了第一个完整覆盖 GB18030 汉字(包含 27000 多个汉字)的多规格点阵汉字字库,第一个覆盖 GBK 字符集的开源矢量字库(文泉驿正黑),并提供了目前包含字符数目最多的开源字体——GNU Unifont——包含绝大多数中日韩文相关的符号。

目前主要的字体:文泉驿点阵宋体、文泉驿正黑、文泉驿点阵正黑、文泉驿等宽正黑、文泉驿微米黑、文泉驿 Unibit

明体系列 4 款

来自台湾的最活跃的字型社团「字嗨」管理员 But Ko ,释出三款改造自思源宋体的繁体中文字体,开放给需要的使用者免费下载!三款字体分别为:源样明体、源流明体及源云明体台湾明体cwTeXMing是一款旧字形外观,在一定程度上仿照古籍〈康熙字典〉字体。

台湾教育部字体 2 款

来自台湾教育部发布的两款字体,台湾教育部国字隶书台湾教育部标准字,都是繁体字,而且因为台湾仍然沿袭古体字,所以在一些输入上和大陆不同,你必须输入繁体才能正常显示对应的字,否则会显示方块。

其他字体

此外,除了上述这些字体之外,还有一些其他的字体也是商用免费的。例如刻石錄系列字体(刻石錄I.钢笔鶴体、刻石錄I.明体、刻石錄I.颜体)、cwtex-q-fonts、明朝体系列 3 款(花园明朝体、装甲明朝体和源界明朝体)等,但由于这些字体的制作方并没有特别出名,因此也常被忽略。有人整理了所有可以免费商用的字体,并且注明了版权协议:

在实际设计中,对于只需要对少数字进行使用时,可以模仿字体的思想,自己通过钢笔工具自己构造自己的文字。

图标

图标(icon)也是 web 设计的重头戏,但是要设计一套图标非常麻烦,而且不同的项目都要重新做一套图标,或者一个设计师对接不同项目时,要反复的拷贝自己以前的设计进行细节修改,会折磨设计师的思想。对于快速迭代的项目,挑选可商用的免费版权图标也是一个需要注意的问题。虽然网络上看到的图标(例如 Facebook 的 icon,例如一些常用的标志的 icon)看上去一致,但是实际在技术层面,作者都作了防盗版标记,因此,不可以随便乱用,两个相同的图标(可能长的一模一样)也可能存在陷阱,因此,一定要从授权网站下载授权图标。

我所知道的收刮免费商用版权的图标来源有:

undraw

undraw 一个提供完全免费的 SVG/PNG 图片素材的站点,网站由 KaterinaLimpitsouni 创建创办,该网站所有的素材图片遵循 MIT 协议许可(开源协议),网站不断更新 SVG 图像素材,并完全免费使用。

Flat Icon Design

一个日本的免费扁平化矢量图标网站,总数量大约有 500 个左右,设计风格简约,最重要的是免费可商用(在它的搜索框旁边已显著注明了可免费商用),下载格式包括:AI、EPS、JPEG、PNG、SVG。

Linea

Linea 由国外团队开发的一套黑白图标,有700+个,下载下来之后,有不同的分类。

Evil Icons

Evil Icons 是由外国的两名开发者联合设计师一起发布的库,同时支持图标的免费使用。你可以通过各种库安装工具下载它,也可以在github上下载源码自己编译。它是基于MIT开源协议完全免费的。

 

免费的 icon 字体库

包括bootstrap, font awesome 在内的一些库都提供了免费的 icon 字体版本。

Iconfinder

iconfinder 是一个图标平台,你可以在上面检索它所提供的图标,它提供了免费和付费的图标,付费图标需要支付后才可以下载,而免费的图标可以用于商业(在下载时可以看到旁边注明)。

虽然有很多提供免费下载图标的网站,例如阿里巴巴的 fonticon、easyicon 等网站,但是它们因为没有具体明确的版权信息,你不知道下载的 icon 是否经过了授权,因此,不建议直接使用从这类网站下载的 icon。

小结

web 设计本身是一件耗时耗力的创造性过程,不能因为在自己的作品中使用了未授权的素材而给自己带来长久的风险,开发者在快速开发自己的产品,设计师在设计自己的作品时,都应该避免一些陷阱,不要被“免费”二字所欺骗,一定要拿到具体的版权协议,并将所用到的素材所对应的版权协议版本和自己的作品放在一起,以证明自己使用了经过授权的素材。

本文所提供的这些资源,它们都提供比较直接的免费可商用版权,而没有刻意设置陷阱。其中几个是分了免费和收费的,但是都比较直接的,没有陷阱,收费和免费区分的非常清晰。你在使用时,也可以根据自己的需要进行付费购买,毕竟别人的设计成果还是需要金钱的支持才能激发他们继续提供更好的作品。

22:17:48 , , , 已有7条回复
  1. 最后我来推荐一个有良心的收费字体:喜鹊造字。

    比起方正的收费标准来说,喜鹊的收费简直是企业福音了。

    https://www.luckytype.com/
    #769 Betty 2019-04-22 19:39 回复
  2. 你确定没有收广告费?
    #770 回复给#769 否子戈 2019-04-22 22:23 回复
  3. 哈哈,因为公司付费买这些喜鹊字体时,都在感慨价格实惠。
    方正的报价简直了,而且还不是买了能随便用哦。
    记得站酷的报价我们之前也是去了解过,也是令人告辞。


    Unsplash,我以前在它上面不定期找壁纸来用。还蛮喜欢其风格的。
    #771 Betty 2019-04-22 22:36 回复
  4. 字体研发本身就是比较费钱的,所以做一套字体,拿来卖,收盗版费,基本上也是行业规则。
    有兴趣可以研究一下手写体,用画板写出来,然后通过软件自己分析,得到一套字体。
    #772 回复给#771 否子戈 2019-04-22 22:55 回复
  5. "有兴趣可以研究一下手写体,用画板写出来“

    别说,X 久前起就想过给自己弄个数位板之类的,但是又怕自己三分钟热度。
    虽然冤枉钱从来没少花,但……总有更吸引我花冤枉钱的点……
    #777 回复给#772 Betty 2019-05-01 23:31 回复
  6. 希望能够转载
    #851 zhoulujun 2019-10-10 16:06 回复
  7. 不允许转载哦
    #853 回复给#851 否子戈 2019-10-13 09:12 回复

Unsplash

可免费商用的图片素材供应商。
https://unsplash.com/

思源宋体

思源宋体、思源黑体这两款字体都是开源免费(可商用)的高质量字体,由adobe发布,值得收藏。
https://source.typekit.com/source-han-serif/cn/

012019.4

JavaScript 算法

242019.3

手机端真机调试

https://segmentfault.com/a/1190000018613578

212019.3

文件名绝对不能使用驼峰命名方式,原因很简单,不同操作系统对大小写敏感程度不同,导致修改文件名之后无法被识别,git不对这些文件进行任何处理,但是你在代码里面又是使用新的文件名。等你部署的时候,代码报错说找不到这些文件。正确的文件名命名方式是使用-或_连接单词,但是也又一些命名为了省空间不使用连接符,全部小写,这样会导致一些单词连在一起之后,有歧义,而且说实话,工程项目,有的时候为了提升团队协作的整体效率而牺牲小部分性能,也是可以接受的,成熟的现代编程,会平衡代码开发时和运行时的体验,而非一味追求运行时体验。

15:24:15 已有0条回复
202019.3

js数据模型驱动器

在一次表单重构中,我逐渐意识到,在数据和UI分开的前端开发模式中,UI和数据越来越需要解耦,UI要完完全全和数据本身无关,做到无状态,做到完全靠数据驱动,并且提供响应接口(更新UI的接口)。而数据层面,如果考虑到自身将被用于渲染,那么就要配置一个驱动器,将原始的数据结构转化为便于UI使用的数据结构,这就是独立于UI的数据模型驱动器。

拿表单来说,市面上有一些基于jquery的表单框架,虽然说是框架,实际上,还是一套基于js配置的ui组件,并非真正意义上的框架。而我的思考是,我们同一套表单,它背后的业务逻辑、数据提交的验证、编辑时的数据回溯等等问题,也就是脱离了UI层面的其他逻辑问题,都可以抽象出来。现在的开发全部是多端开发,同一个表单,在UI界面上可能不同,使用的UI技术也可能不同,例如在公众号网页和在小程序中,甚至在ReactNative构建的原生app中,他们抛开UI、交互层面的东西,背后的业务逻辑几乎是一摸一样的,为什么不可以共享?而且这三种场景,背后都支持js原生的计算能力和语言特性,所以,很明显是一定可以共享的,所不同的是,在将模型和UI框架进行拼接时,拼接方式不同。

表单模型只是一个例子。这种将模型抽离出UI的方案,适用于大部分带有业务逻辑的中后台应用中,在都使用了web技术的前提下,想多端共享同一套业务逻辑,就应该用这种思路去写。

现在的问题是,有没有一套完整方案(框架),去像React那样,编写模型。我认为还没有,或者说,还没有一个UI框架是基于这种考虑去实现的,因此,也就没有模型框架可以被应用到实际开发中。如果有一种UI框架(特别是React),可以基于“模型-视图”的方式去开发自己,那么就更有利于模型框架的出现。就目前而言,最接近这种形态的是rxjs作为模型框架,react作为UI框架。

01:12:27 已有0条回复
132019.3

第一步,在鼠标setting里面激活secondary click,并选择click on left side。这样就可以左右两个键互换了。接下来就是修改鼠标指针,参考如下步骤。

1. Download Mousecape.zip containing the 0.0.5 pre-release and install.
2. Install the helper tool from the menu bar by clicking Mousecape > Install Helper Tool.
3. From Mousecape preferences (Command-,) select "I am... left handed"
4. From the menu bar click Capes > Dump Cursors.
5. Click the cursor dump to highlight. Command-Enter to apply. Command-R to restore the default cursors.

16:19:27 已有0条回复

npm依赖应该放在哪一个字段?

在一个前端项目中,我们把依赖通过特有的pacakge.json,其中的dependencies字段提供给npm来安装依赖。自从npm升级之后,就增加了依赖的控制,例如增加package-lock.json来锁定开发时使用的依赖版本。而我们的项目,很多会上到CI系统去构建,然后通过CD系统来部署到我们的生产环境中。这就会带来一个问题,我们在开发环境中使用的依赖,应不应该在CI系统中安装,因为大部分CI系统都不具备开发环境那么宽松的编译环境。那么到底要怎么来安排我们的开发环境依赖、CI环境依赖和生产环境依赖?

首先理解三种依赖:

dependencies

本包的依赖。即当另外一个包/项目依赖你这个包的时候,你的包必须依赖的其他包。npm在安装一个包的时候,它不单单只安装了这个包本身,同时还安装了dependencies里面指定的依赖。例如:

npm i databaxe

你会发现,不单单databaxe这个包被安装了,databaxe这个包的package.json里面的dependencies字段里面的包也被安装了(devDependencies里面的包没有被安装,optionalDependencies 也会被安装,但是 optionalDependencies 有一点不同,如果里面的包在安装过程中失败了,那么不会影响安装过程,这个包因为安装失败而被忽略,具体阅读这里)。

由于我们大部分前端项目,在现阶段还是需要通过构建工具构建的,因此,对于一个完整的上线项目而言,dependecies里面只需要放置node端的modules即可,例如express等服务器依赖包,而不需要把jquery等前端代码需要的包放在 dependencies 里面,因为,前端代码的依赖包会被webpack等工具打包进去。这些在构建阶段用到的包,可以放在 devDependencies 里面。

但是,对于一个组件,或者一个第三方前端包,你需要考虑的是,你的包被其他项目引用时,会由其他项目去自己实现构建过程。你的代码里面的依赖可以和该项目其他依赖的依赖共用,例如前面提到的jquery,有可能你的包和别人的包都用到了,是一个公用的依赖,因此,你不应该把jquery的代码打包到你自己发布的包代码中,而是作为依赖的形式,添加到dependencies中去。这样,当别人的项目在构建时,会同时使用你的包、别人的包、jquery去进行构建。这样保证了代码的复用和代码量的减少。

devDependencies

本包的开发依赖。当你在开发一个包的时候,你可能需要用到一些用于开发的依赖,例如webpack,babel等。这些依赖只会在开发构建阶段(也包括你上到CI系统进行构建时)用到,而不会在生产环境用到。npm在安装一个包时,这个包pacakge.json里面的devDependencies里面的包不会被同时安装。但你在把这个包的源码克隆到本地,并准备开发时,你在项目目录下执行

npm i

你就会发现,这些包被安装了。而且,这些包里面的package.json里面的规定,也完全和本文所讲的规则一致。也就是说,虽然这些包是放在devDependencies里面,但是,它们被安装时,它们自己package.json里面的devDependencies里面的包不会被安装,因为,这些是它们自己的开发依赖。当它们被安装时,是被当作一个正式的第三方产品来使用。

optionalDependencies

本包的可选依赖。当你在开发一个包的时候,你可能希望有些包是可选的,比如,一个包由AB两位同学在开发。其中A开发时由于自己的习惯,使用到了一个包,但是B同学没有这个习惯,他不希望自己在开发时这个多余的包被下载浪费时间,这个时候,这个包应该被放在optionalDependencies里面。npm在安装一个包的时候,该包的optionalDependencies里面的依赖也不会被安装。但是,在开发时,你执行

npm i

这些包还是会被安装。你可能会问,那它和devDependencies有啥区别?区别在于,你可以使用参数让它们不被安装:

npm i --no-optional

通过这个参数,在执行npm i的时候,optionalDependencies就不会被安装了。

不同的开发环境如何正确选择一个包应该放在哪里?

  • 但凡用于构建的、测试的、质量检查的,都放到devDependencies里面
  • 但凡仅用于本地开发,例如预览等,都放到optionalDependencies里面

例如,我们经常会在本地开发时,起一个devserver来对开发结果进行预览,但是在上到CI系统时,这个功能根本不会用到,所以,我们把这些依赖放到optionalDependencies里面,而在CI系统构建脚本中,一定要加上--no-optional参数。

所以,最终总结下来,我们大致可以这么认为:dependencies是给生产环境用的,devDependencies是给CI构建用的,optionalDependencies是给本地开发用的。虽然这个理解有偏差,但对于对这个知识点刚刚了解的同学来说,非常有用。

13:46:41 已有0条回复
062019.3

创建可缓存计算过程的函数

受reselect启发,实际上,对于某类特定函数——输入相同的情况下,输出一定相同的纯函数,实际上,对于相同的输入参数,没有必要进行多次计算,而是可以将结果缓存起来,在识别到相同的参数时,直接取出该参数对应的结果即可。因此,我编写了computex:

/**
 * 创建一个可缓存的纯函数。
 * 使用场景:纯函数,相同的参数永远得到相同的结果。
 * @param {*} fn 原始纯函数
 * @param {*} expire 缓存过期时间
 * @example
 * const fn = computex((state) => {
 *   return state.name + ':' + state.age
 * })
 * var a1 = fn({})
 * var a2 = fn({})
 * // a1 === a2 // 因为参数相同,所以结果使用了缓存,因此也相同(引用相同)
 */
export function computex(fn, expire = 60000) {
  const cache = {}

  // 创建一个循环任务,检查缓存是否过期,如果过期,则删除缓存,释放内存
  const recycle = () => {
    const keys = Object.keys(cache)
    keys.forEach((key) => {
      const { time } = cache[key]
      if (time + expire <= Date.now()) {
        delete cache[key]
      }
    })
    setTimeout(recycle, 1000)
  }

  // 如果expire设置为0,则缓存永久生效
  if (expire) {
    recycle()
  }

  return function(...args) {
    const hash = getObjectHash(args)
    if (hash in cache) {
      const item = cache[hash]
      return item.result
    }

    const result = fn.apply(this, args)
    const time = Date.now()
    cache[hash] = { result, time }

    return result
  }
}

其中computex本身是一个函数,它接收一个函数,返回一个函数,返回的函数是接收到函数的复刻版,运行它得到的结果和运行传入函数是一样的。这样就可以做到缓存计算过程。

/**
 * 创建一个在同一个同步进程中只获取一次的函数。
 * 使用场景:反复调用一个函数,而该函数在本批次运行时,实际执行结果应该相同。
 * @param {*} fn
 * @param {number} expire 缓存过期时间,根据实际的情况来看,如果一个运算比较大,可以设大一点
 * @example
 * const get = getx(() => Date.now())
 * var a1 = get()
 * var a2 = get()
 * // a1 === a2 // 在同步进程中,直接使用缓存
 */
export function getx(fn, expire = 10) {
  var iscalling = false
  var cache = null
  var timer = null

  return function() {
    clearTimeout(timer)
    timer = setTimeout(() => {
      iscalling = false
      cache = null
    }, expire)

    if (iscalling) {
      return cache
    }

    iscalling = true

    const result = fn.call(this)
    cache = result
    return result
  }
}

getx是另外一个逻辑。当在同一个同步进程中,我们可能会反复调用一个函数,而这些函数在这一次反复调用时,实际上是执行了同一个过程,所以,得到的结果也是一样的,但是在执行过程中消耗了资源。因此,我们可以将这些结果缓存起来,在这一个同步进程结束后再清理掉即可。

/**
 * 创建一个缓存Promise的异步函数
 * @param {*} fn
 * @param {*} expire 缓存的过期时间,不设置的时候,当Promise结束缓存就会被清空,而如果设置了expire,完全按照expire清空缓存,而不会依赖Promise的结束
 */
export function asyncx(fn, expire = 0) {
	const cache = {}

	// 创建一个循环任务,检查缓存是否过期,如果过期,则删除缓存,释放内存
	const recycle = () => {
		const keys = Object.keys(cache)
		keys.forEach((key) => {
			const { time } = cache[key]
			if (time + expire <= Date.now()) {
				delete cache[key]
			}
		})
		setTimeout(recycle, 1000)
	}

	// 如果expire设置为0,则缓存永久生效
	if (expire > 0) {
		recycle()
	}

	return function(...args) {
		const hash = getObjectHash(args)
		if (hash in cache) {
			const item = cache[hash]
			return item.deferer
		}

		const deferer = new Promise((resolve, reject) => {
			Promise.resolve().then(() => fn.apply(this, args)).then(resolve).catch(reject).then(() => {
				if (expire > 0) {
					return
				}
				delete cache[hash]
			})
		})
		const time = Date.now()
		cache[hash] = { deferer, time }

		return deferer
	}
}

而对于基于Promise的异步操作,则通过asyncx来实现缓存。缓存的并非Promise最终返回的结果,而是缓存Promise本身,在一个Promise进行中的时候,如果再次调用这个函数,得到的会是未结束的Promise,而非重新发起一个Promise。这有助于解决短时间内反复请求某个api接口的情况。

20:27:44 已有2条回复
  1. hello,有个问题要请教一下你,之前你说过网易云的播放器音质挺烂,你说过用了什么耳机再来用苹果的,iphone的音质很差。 我想要你帮我推荐一个手机耳机,或者说是便捷式耳机。不不太想要太大,如果说在体验上很好也是可以接受的。


    一直关注你。
    #735 苦瓜 2019-03-08 21:48 回复
  2. 谢谢关注,其实我对耳机要求不高,所以没有太多研究。你可以试试airpod,时尚感比较强,如果是追求音质,还是推荐索尼的耳机,如果希望续航长一点,可以考虑项圈的,我买的JBL虽然音质我还觉得不错,但是续航弱。
    #742 回复给#735 否子戈 2019-03-11 10:15 回复