Step2-知其然:⼀⽂讲透Obsidian插件DataviewJS
Obsidian&Zettelkasten&LYT构建第⼆⼤脑系列⽂章总⽬录(持续更新)
第⼆步:知其然。作为热⾝,我们还是先看⼀段相对简单      我们接着上⼀篇,Step1-先预热:⼀⽂讲透Obsidian插件DataviewJS,继续讲解第⼆步:知其然。
⼀点的代码:
⽰例代码
这段代码的运⾏结果是:在笔记库vault中,把标签为“book”的笔记出来,然后按照书籍的类型进⾏分组,每组⽤⼀个表格的形式展⽰出来,表格的表头包含“姓名name”、“阅读时间TimeRead”、“评分Rating”,⼏个字段,表格的内容按照书籍评分降序排列。执⾏效果如下:
执⾏结果
分析代码,格式排版很重要,我的习惯是把他们放到代码编辑器⾥进⾏排版,这样便于⾃⼰阅读和理解,这⾥选⽤Sublime Text来编写和阅读代码。具体怎么⽤,后⾯机会单独介绍,排版以后的代码如下:
格式化后的代码--可读性强
这么看顺眼多了,也更好理解。整段代码是嵌⼊在obsidian笔记中的,使⽤“```davaviewjs”做开头,使⽤“```”做结尾,⾥⾯放的是javascript代码,准确的说应该是typerscript代码。因为Dataview的作者使⽤的typerscript来开发的Dataview插件。这个从github上的项⽬⽂件后缀就可以看出来,都是以*.ts结尾的。
typerscript⽂件
代码源⽂件src下⾯包括多个⽬录,随便打开⼀个⾥⾯的⽂件都是以*.ts结尾。这⾥api和data两个⽬录,后⾯我们会重点介绍⾥⾯⼏个关键的源代码⽂件。看源代码可以在浏览器的github⾥打开直接看,不过我强烈建议把整个源码下载到本地,然后⽤Sublime Text导⼊整个项⽬,借助代码编辑器读源代码的效果会⾼出数倍。
❶  ⾸先是⼀个for语句,在typerscript的for循环语句有两种形式,这⾥采⽤的是常⽤的f语法。意思就是在⼀个集合中逐⼀取出⼀个元素进⾏遍历,针对取出的这个元素,做⼀些处理动作。for循环体()⾥的部分是对遍历的集合做⼀个条件限制。
❷  let 是⼀个定义变量的关键词,类似javascript⾥⾯的var,不过let的⽤法更灵活,它可以定义任意类型的变量,⽐如
let aaa : string = "hello"
这⾥就定义了⼀个string字符串类型的变量叫aaa。还可以定义别的,⽐如
let dance = function(name : string) {return "Look! ${name} is dancing!"}
js 二维数组这⾥let就定义了⼀个函数变量dance,这个函数定义⽤到了function关键词,在()⾥定义了⼀个需要输⼊的参数name,⽽且明确name的类型为string字符串,在{}⾥定义了函数要做的动作,return是关键词,表⽰整个函数运⾏完后返回的结果。这⾥函数返回的是⼀个字符串,⼀句话:XXX在跳舞,这⾥的XXX因为在""内,所以使⽤了简写的⽅式,使⽤${}的⽅式直接调⽤了参数name的值,⽐如函数是dance("Jack"),那么结果输出就是:"Look! Jack is dancing!""。当然typerscript在定义函数时有好⼏种不同的⽅式,⽐如刚才的定义也可以写成:
let dance => (name : string): "Look! ${name} is dancing!"
看上去是不是简洁了很多,这是使⽤=>定义函数的⽅式,在typescript的代码⾥⼗分常见,要习惯。其实,typerscript和JavaScript相⽐,代码定义更严格,它是对JavaScript的扩充,针对泛型的使⽤更⾼频,代码更简洁,但有时候过度简洁读起来不是特别好理解。⽐如还是这句,如果⽤泛型来定义可以写成:
let dance<T> => (args : T) : T
这⾥使⽤泛型<T>的好处就是可以不⽤事先确定参数的类型和返回的类型,函数的适⽤范围更灵活,后⾯我们讲解dataviewAPI的部分源码时会经常碰到这样的情况。⼀般我们写程序,⽐如java等,编程的过程都是针对值进⾏编程,typerscript前进了⼀步,它能够针对类型进⾏编程。⽐如还是这个例⼦,
我们可以定义⼀个⾃定义的新类型danceType,让它把这种函数形态给记录下来以后随时使⽤:
type danceType<T> = (args : T) => T
type是⼀个关键词,可以理解为给⼀个⾃定义的数据类型起了⼀个别名,这个就是给⼀个⾃定义的函数类型的⼀个数据类型,起了个别名叫danceType。或者举个简单的例⼦:
type mystr = string
就是给string类型另外起了⼀个别名叫mystr,实际上还是同⼀个类型。要义就是通过这种⽅式对类型进⾏编程了。结合上泛型,就变得⼗分灵活,不过可读性也确实烧脑了。读源码的时候这种类型+泛型的代码很多,需要熟悉和习惯。后⾯我会留⼀些配套的⽂章,供⼤家慢慢学习熟悉。
❸  继续看例⼦:for的()⾥的意思,使⽤let定义了⼀个group变量,因为group放在了f中间,所以group就是of后⾯跟着的那个集合中取出的⼀组元素,确切的说就是
dv.pages("#book").groupBy(p => p.genre)
所代表的⼀组元素集合,是⼀个数据结构。这⾥要弄清含义,就需要看看github上作者给出的DataviewAPI了,⾥⾯给出了常⽤的⼀些函数的说明,位置在⾸页的概述部分:
打开后,可以看到⼀些常⽤的函数:
DataviewAPI页⾯
API中⽂翻译
API英⽂原⽂
我的英⽂不好,所以经常是⽤翻译软件,中英⽂来回切换着看,这样效率更⾼⼀些。
从API⽂档⾥可以看到,dv是在obsidian笔记中调⽤Dataview的⼀个代名词,⼀般的函数都是从dv.XXX开始的。dv.pages(source),这个函数可以根据传⼊参数source的不同返回符合参数条件的笔记集合,主要⽀持两种⽅式:按标签和按笔记所在的⽬录或者路径,这些条件可以通过与或⾮进⾏条件组合。从作者给的例⼦⾥可以看出函数返回的是⼀个笔记的集合pages,⾥⾯会有⼀篇⼀篇的笔记page。实际上从这个API⽂档中我们得到的确切信息不⾜,⽐如返回的结果到底是什么类型,看不出来,只能猜出⼤概的意思是⼀个笔记的集合,但是具体的数据类型并不清楚,这种不清晰会给我们后续理解代码带来很⼤的障碍,编程不是感觉怎么怎么样,⽽且确切的推理和推导过程,计算机代码是严丝合缝的,不是靠猜的。要想做到这点,就需要更进⼀步追踪进去看源代码了,这个我们放到后⾯第三步再说,这⾥先意会吧。其他函数的意思⼤家⾃⼰去看API,不再逐个做解释。
typescript⽀持函数嵌套的写法,前⼀个函数返回的对象可以继续调⽤他所⽀持的函数继续⼀层层调⽤下去,所以会出现很多个函数.函数.函数这样⼀直连续调⽤⼀长段,很正常,习惯就好。在这⾥dv.pages()函数返回笔记页⾯构成的集合对象后,继续调⽤了⼀个groupBy函数进⾏分组,groupBy()内部的写法是⼀个典型的泛型参数传参的写法,这个后⾯我们看源代码的时候在继续深究,这⾥只需
要明⽩,groupBy()函数是将前⾯的笔记集合中的元素,⽤genre分类属性作为对笔记集合的分组条件,这样返回的结果就是按照分类把书籍分成了⼏组,所以for循环的次数,和分组后的组数是⼀致的,看⼀眼for循环体内的句⼦,我们⼤概能猜到,每组书籍应该是以表格形式来呈现的,有⼏组就有⼏张表格。
❹  for循环体内的语句,不展开,使⽤了dv.head()与dv.table()的⽅式来输出三级标题和画表格。这⾥需要重点提两个地⽅:
第⼀是group.key的⽤法。group在前⾯提了,是每次for循环从笔记集合体遍历出来的⼀个笔记⼦集合,由于前⾯⽤了genre属性做分组,分组保存的形式,类似key、value的⽅式,key保存的是分组的genre属性的值,value保存的是某个分组下的笔记的集合。所以,group.key的值就是genre分类,把它当成⼀个三级标题输出。
第⼆个是dv.table()的⽤法,这⾥不展开讲,⽤到了排序和map构造⼆维数组作为填充表格的内容,这个也放到第三步⾥我们看源代码来详细解读。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。