在tdesign-vue
的源码,有一个初始化的脚本文件,script/init/index.js
,在这个文件中,就是这个文件让他拥有了动态创建组件和删除组件的能力。
今天通过学习这个工具的源码,来学习一下如何动态创建组件,让我们也可以在项目中使用这个功能。
源码地址:
使用
README.md
中并没有关于这一块的使用说明,我们直接通过源码来看如何使用。
打开script/init/index.js
文件,直接拉到最底下,可以看到下面的代码:
最底下看到直接执行了init()
函数,在init
函数中,看第一行代码就知道这个是运行在node
环境下的,因为process.argv
是node
环境下的全局变量,用来获取命令行参数。
然后再看package.json
文件,可以看到script
中有一个init
命令,这个命令就是用来执行script/init/index.js
文件的。
可以省略
index.js
,因为node
会默认去找index.js
文件
通过这些线索,我们可以知道运行这个脚本的命令是:
效果都是一样的,都是执行script/init/index.js
文件,然后传入两个参数,第一个参数是组件名,第二个参数是del
,如果传入了del
,就会删除组件,如果没有传入del
,就会创建组件。
注意:不能
cd
到script/init
目录下执行,因为这样node
的执行目录就是script/init
目录,而不是项目根目录,所以会找不到src
目录
源码分析
动态创建组件
1. 创建组件
上面我们已经知道了,如果不传入del
,那就现从创建组件开始分析,先简化一下代码:
首先可以看到component
是必传的,如果不传直接退出程序。
indexPath
是src/index.ts
文件的路径;
toBeCreatedFiles
是通过config.getToBeCreatedFiles(component)
获取的,来看看这个函数:
这个在script/init/config.js
文件中;
可以看到这个函数返回了一个对象,这个对象的键是目录,值是文件,这个对象的目的就是用来动态创建组件的。
比如我们传入的组件名是Button
,那么这个函数就会返回一个对象:
继续往下看,addComponent
函数:
这个就是用来给组件创建目录和文件的,首先会遍历toBeCreatedFiles
对象,然后创建目录,然后创建文件。
代码太多,先简化一下:
上面的简化只是将入参固定,移除了最外层的遍历,在外面循环执行我这个简化的函数也是可以的,然后将fs.mkdir
改成了fs.mkdirSync
,这样看着会更清晰一些。
可以看到最后会遍历contents.files
,然后根据item
的类型来创建文件,如果是对象,那么就会调用outputFileWithTemplate
函数,这个函数的作用是根据模板创建文件:
简化代码,将入参固定:
可以看到,这个函数会读取模板文件,然后编译模板,然后传入模板变量,最后创建文件。
_.template
是lodash
的一个方法,传入模板字符串,返回一个编译函数,然后调用这个函数传入模板变量,就可以得到编译后的字符串。
模板文件长这样:
就是通过<%= xxx %>
这种形式来引用模板变量,然后lodash.template
返回的编译函数就会把这些变量替换掉。、
这里面有两个模板变量,component
和upperComponent
;
component
就是组件名
upperComponent
见名知意,就是组件名的首字母大写,是通过getFirstLetterUpper
函数来实现的:
非常简单的一个函数,就是把首字母大写,然后拼接上剩下的字符串。
继续往下就是创建文件了,调用了createFile
函数:
就是调用fs.writeFile
来创建文件,这里面有两个参数,一个是文件路径;
desc
用于打印日志,这里面会打印出文件的描述,然后文件的路径。
回到addComponent
函数,可以看到item
如果不是对象,就直接调用createFile
函数来创建文件,不过内容是空的;
部分代码:
当addComponent
函数执行完毕,就会在node
执行脚本的目录下创建一个src
目录,然后里面会生成一堆文件:
这一步只是创建,接下来就是注册组件了。
2. 注册组件
上面执行完addComponent
函数后,还有一个insertComponentToIndex
函数,这个函数的作用就是把组件注册到index.js
文件中:
第一行就是将首字母大写,刚才已经讲过了,先把代码整理一下:
importPath
是通过getImportStr
函数来获取的,这个函数的作用就是拼接import
语句:
这里会判断index.ts
文件中是否已经存在了该组件,如果存在就不再插入,否则就会在index.ts
文件中插入import
语句和组件名。
然后找到入口文件的import
语句,分号结尾,后面有两个换行符,然后在后面插入import
语句:
然后找到components
对象,然后在后面插入组件名:
最后写入文件,就完成了组件的注册。
删除组件
最开始的init
函数还有一个删除的分支,回顾一下:
上面还是简化后的代码,删除逻辑也一样,两个函数完成,先来看deleteComponent
函数:
这次就不简化代码了,可以自行尝试一下;
先看第一行代码,这里会获取快照文件,通过getSnapshotFiles
函数:
这个快照信息的结构和toBeCreatedFiles
是一样的,然后和toBeCreatedFiles
合并;
然后遍历合并后的对象,如果有deleteFiles
属性,就删除这些文件,否则就删除整个文件夹;
可以看一下配置文件,是没有deleteFiles
属性的,所以这里就是删除整个文件夹;
deleteFolderRecursive
函数是在utils
文件中定义的,代码如下:
这里就是递归删除文件夹,如果是文件就直接删除,如果是文件夹,就递归删除;
fs.unlinkSync
是删除文件,fs.rmdirSync
是删除文件夹;
然后还要删除index.ts
文件中的组件注册信息,这里就是deleteComponentFromIndex
函数:
实现逻辑和注册组件的时候是相同的,只不过注册组件是增加,删除组件就是删除,主要是下面这一段代码:
js
这里是用正则匹配要删除的内容,然后替换为空字符串,就完成了删除组件的操作。
自己动手
上面其实并不是很复杂,我们自己也可以实现一个类似的功能,就比如我们日常开发中,经常会做重复的CRUD
操作,我们可以封装一个脚手架,来帮助我们完成这些重复的操作;
首先分析需求,我们需要做的事情是:
- 有哪些层级的文件夹需要创建;
src/api
:接口层;src/pages
:页面层;
- 每个文件夹下需要创建哪些文件;
src/api
:增删改查
四个接口src/pages
:列表页面,有增删改查
四个功能
- 每个文件需要写入哪些内容;
src/api
:增删改查
四个接口src/pages
:列表页面,有增删改查
四个功能src/router.js
:路由配置
我这里就简单的实现一下:
- template 文件夹
- api.js
- pages.vue
- router.js
上面两个文件是模板文件,router.js
是本来就应该存在的。
现在开始实现:
- init.js
这样我们就可以通过命令行来生成模板文件了。
上面的代码没有经过测试,可能有问题,只是跟着思路写的,正则是测试过的没问题,有一颗开源的心但是懒,愿意可以自己完善一下。
总结
通过tdesign-vue
的动态注入组件的源码,学习到了通过模板文件来生成代码的思路,对于一些重复性的工作,可以通过这种方式来减少重复性的工作,提高效率。
对应动态修改文件内容,也提供了一个思路,通过正则匹配,然后替换,这样就可以动态修改文件内容了。
正则确实有点烧脑,换我的话,我应该会用注释内容当做一个占位符,然后直接找到这个占位符,通过占位符来定位替换的位置,这样就可以减少很多心智上的负担了,缺点就是担心协助被误删。