前言
有了之前的基础,我们现在可以讲讲一个成熟的脚手架是怎么做了。vue-cli作为vue的脚手架,给如此多的前端开发者使用,已经算是成熟了吧。
这里我们参考vue-cli的源码,基于rollup和typescript一步步搭建。
开始
以下我们的命令仍然是ds~,模板是ds-cli-lib-template
目录结构
1 | ├─ bin # 打包文件目录 |
编写构建配置
现如今,webpack用来开发应用(热更新hmr,代码拆分等),rollup用来开发类库(简单易上手,打包后代码能读懂,至于其他的特性webpack基本已支持)。
现在来明确我们的需求
- 使用typescript编写模块代码
- 打包成umd模块规范的代码
- 可以引用commonjs规范的包(因为历史原因,大多数包都不是ES模块规范)
- 压缩打包代码,减少体积
1 | //rollup.config.js |
npm脚本命令(“scripts”字段)1
2
3
4{
"clean": "rm -rf ./bin && mkdir bin",
"build": "npm run clean && rollup --config"
}
编写入口文件
是一些非常基础的东西,我们一般不放很复杂的逻辑在入口文件里。
1 | const cmd = require('commander'); |
我们打包到bin文件夹下后,配置一下package.json的bin字段为bin/ds.js,然后发布npm试一下命令。如果失败,请重新审视上述流程。
初始化模板
我们对模板的要求
- 文件目录必须含有template文件夹,并且所需模板文件放在该目录下
- 文件名命名规范是ds-cli-‘name’-template,方便脚手架拉取
- 可用meta.js提高自定义程度(所谓动态化模板)
期望命令
ds init
流程
1 | if(当前目录下构建){ |
动态模板
大家也看到了,其实最重要的就是generate函数~
- 而如果我们去掉这一步generate模板的话,其实就是相当于下载一个静态模板,如果我们对于用户自定义没要求的话,其实可以跳过这一步。
generate函数里面用到了metalsmith,这个就相当于我们之前用的gulp,通过不断地编写中间件来优化打包后的结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function generate(){
const opts = getOptions(name, templatePath) as meta; // 获取meta.js配置,存到opts里
// 我们把所需文件放在源文件的template目录下,其他一些如测试放在外面。初始化一下metalsmith
const metalsmith = Metalsmith(path.join(templatePath, 'template')) //我们约定,将模板所有文件放在ds-cli-lib-template/template里
//中间件
metalsmith.use(askQuestions(opts.prompts)) // 询问问题,将信息存metalsmith.metadata()
.use(filterFiles(opts.filters)) // 通过问题交互过滤掉不需要的文件
.use(renderTemplateFiles()); // 模板里面可以使用handlebar语法,作为占位符,我们这里重新渲染模板文件
// 源目录打包到目标目录to
metalsmith.clean(false)
.source('.')
.destination(to)
.build((err, files) => {
done(err);
});
}我们在模板(如ds-cli-lib-template)目录下需要构造meta.js,自定义我们所需的字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31module.exports={
//会通过中间件把这些字段存在metalsmith.metadata(),方便接下来的中间件调用
prompts:{
//格式可以参考https://github.com/SBoudrias/Inquirer.js/#question
name: {
type: 'string',
required: true,
message: 'Project name',
},
author: {
type: 'string',
message: 'Author',
},
description: {
type: 'string',
required: false,
message: 'Project description',
default: '构建一个lib',
},
lint: {
"type": "confirm",
"message": "是否用tslint"
},
},
filters: {
//当上面prompts的lint为false的时候,就过滤掉文件
"tslint.json": "lint",
"tsconfig.json": "lint"
}
}