😉 如何读一个vue组件的源码 __Vue.js
发布于 3 年前 作者 banyungong 1301 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

🧭 寻龙点穴

寻龙分金看缠山,一重缠是一重关,关门如有八重险,不出阴阳八卦行!

要想看懂一个vue组件库源码,我们首先要找到它的入口所在,那么在vue组件库中,一般都是从哪里入手呢,别急,下面我们就一一道来。

首先,在我们观察了大多数项目后(譬如elementui, iview等),就会神奇的发现,每个项目里最最核心的代码基本都放到了一个名为src的文件夹内,所以在找入口这里,我觉得大家可以不必大费周章,直接把注意力集中在src文件夹内部即可

进入到src文件夹内部,我们会看到,里面的最外层总是会有一个index.js或者main.js,如果你看到了这个文件,恭喜你,这个入口已经被你找到了,接下来就可以从这个入口开始愉快的盗墓,啊不,读源码之旅了。

🤿 实战演练

光说不练假把式,下面我们就实打实的来读一个组件的源码来练练手。首先我们可以在github搜索vue-org-tree,懒得搜索的话可以直接点该链接 vue-org-tree ,进去后我们会看到 QQ截图20200803170351.png

结构不是特别复杂,我们首先进入src目录,然后可以看到里面有components(不知道为啥,现在很多编辑器包括github在你的子文件夹只有一个时会默认加个斜杠放到当前文件夹后面),styles两个文件夹,以及一个main.js文件,我们首先看一下main.js里面的内容,可以发现内容很简单,就是单纯的导出一个引入过来的东西。 QQ截图20200803170742.png

就好像吴邪盗斗,啊不,探墓一样,我们走进main.js这个房间,房间的种种线索指引我们继续往components/org-tree文件夹里进入,这里提醒下,由于org-tree是个文件夹,如果引入文件写到文件夹就不写了,不是作者懒,而是可以默认指向文件夹内部的index.js,毕竟有了捷径,谁不愿意去试试呢? 1.png

上面就是main.js指引我们来的第二层墓室,啊不,js文件,我们可以看到,这个墓室似乎又有线索指引我们往别的墓室前往,但是这个墓室不像上一个墓室空空荡荡的,这个里面是有东西的

可以看到,这个墓室一共暴露了两个财宝给外面,一个是默认暴露export default,一个是具名暴露,如果想要使用具名暴露,就必须知道这个名字才行,不叫对名字是不能开启这间法宝的哦!再深究一下代码,我们会发现这两个暴露的财宝之间是有关联的,这个具名插槽虽然名字怪异——叫install,但是它本身是被直接绑定到了默认导出的install属性上的,那么这种情况其实是为了满足一个Vue.use的使用,这里就不做深究了,有兴趣可以去看下官网的解释 Vue.use ,至于下面的if判断,相信看了代码你应该就知道什么意思了,实质都是为了执行install里面的内容,这里我们有必要看下install里面的内容,可以看到,install里面加入了重复注册的判断,然后在每次注册时,都会把一个install的属性设为true绑定到这个函数上,禁止套娃,虽然有点绕口,但是确确实实是这样,然后可以看到最重要的一句话,Vue.component,在这里,注册了一个名字叫做OrgTree.name的全局组件

接着,我们按照指引又来到了第三层墓室,来到这里我们首先要带着上一层墓室的疑问,那个注册的全局组件到底叫什么?是张三?还是李四? 2.png

看到了name: ‘Vue2OrgTree’,至此,尘埃落定,这个组件如果要用的话一定是叫Vue2OrgTree,在这里有必要提醒下第一次读源码的小白,这里面当让也包括我了,就是我们使用一个vue组件时,只需要关注接口props,事件events和方法methods即可,有时候方法都不会暴露,但是此时我们时在读源码,所以要认清自己的方向,可以看到,该组件出了name属性外,就只有compoents和props两个别的属性了,props暂且不管,因为外部文档已经讲的清除,这里我们主要看这个components,可以看到,这个components的写法有点不一样,不是普通的组件,而是直接用的render函数来编写的,之前在vue官网看,好像说是用render函数编写的组件不用二次转化,效率可能会高一点,另外就是完全的编程式编写组件,让你编写组件更自由,与之而来的则是难度的提升,因为很多东西都要新学才行。那么我么顺着指引继续进入第四层的墓室吧!

由于第四层是主墓室,宝贝较多,我们需要分次查看。 carbon 4.png 在看这个源码之前,大家可以先了解下display: table以及display: table-cell,为了方便可以直接跳转这个例子,display: table & display: table-cell。探险也是要有个先后的,初步应该是由主到次的,由于上一个墓室的指引告诉我们这个墓室的引入是默认引入,所以我们首先关注export default,我们可以发现export default导出的是一个函数,这个函数就叫render并且它本身也是被具名导出的,看一下这个函数render,很明显,这个是vue规定的render函数默认的格式。

加入看过了codepen上的例子,相信你会发现这个组件的实现原理也不是太难,而且为了和树的无穷嵌套相匹配,他起的class名称也很好辨识,分别是org-tree-node,org-tree-node-label,org-tree-node-children,我们可以形象化一下,把org-tree-node类比成一个人,那么org-tree-node-label就好比是他的名字,org-tree-node-children就好比是他的儿子,当然,作为树,他只有一个根节点,也就是只有一个祖先,所以大致的结构就是这样的 3.png

此时我们环顾整个主墓室,就会发现,还真的有这三个class对应的render函数,分别就叫做renderNode,renderLabel和renderChildren这几函数都有共同的参数h,data,context,h我们先不提,它是vue官方的createElement函数,具体细节可以去看官网,我们主要看下这个data,从代码里我们可以看出,这个data其实就是props.data,也就是data借口传过来的值,而data具体的值就是如下图所示 4.png 是一个lable,id,expand,children的嵌套对象,而props.props.children我们也可以知道,是 5.png 也就是字符串children,所以我们可以知道,这里的children就是data.children,然后会进行叶子结点判断,判断的一局就是是否有儿子节点,如果没有,则为叶子节点,然后对其添加is-leaf这个class,如果全局开启折叠并且当前的data的expand属性为false,即不展开,那么就要给当前节点加入collapsed这class,这只会针对非叶子节点使用,然后给子节点数组传入Label组件,判断如果总的接口传值是非折叠的或者当前的expand为true,那么就要往子节点数组继续push Children组件,最后,作为render函数的返回值,我们需要返回createElement函数即可,至此,该组件讲解暂时告一段落,后续有什么细节还会补充。

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 白石桥的锁匠 原文链接:https://juejin.im/post/6856694222506950670

回到顶部