博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
前端如何搭建一个成熟的脚手架
阅读量:4082 次
发布时间:2019-05-25

本文共 5726 字,大约阅读时间需要 19 分钟。

前言

有了之前的基础(),我们现在可以讲讲一个成熟的脚手架是怎么做了。

这里我们参考vue-cli的源码,基于rollup和typescript一步步搭建。vue-cli作为vue的脚手架,给如此多的前端开发者使用,已经算是成熟了吧。

开始

以下我们的命令仍然是~,模板是

目录结构

├─ bin            # 打包文件目录│  ├─ ds.js       # package.json里的bin字段引用文件 ├─ src│  ├─ lib         # 具体命令目录│     ├─ list     # ds list│     ├─ init     # ds init│  ├─ utils       # 工具函数├─ main.ts        # 入口文件├─ typings        # typescript类型文件目录├─ rullup.config.js # rollpu构建配置├── test            #  测试用例复制代码

编写构建配置

现如今,webpack用来开发应用(热更新hmr,代码拆分等),rollup用来开发类库(简单易上手,打包后代码能读懂,至于其他的特性webpack目前基本已支持)。

现在来明确我们的需求

  • 使用typescript编写模块代码
  • 打包成umd模块规范的代码
  • 可以引用commonjs规范的包(因为历史原因,大多数包都不是ES模块规范)
  • 压缩打包代码,减少体积
//rollup.config.jsimport typescript from "rollup-plugin-typescript2";import commonjs from 'rollup-plugin-commonjs'import { uglify } from 'rollup-plugin-uglify'export default {    //入口文件  input: "src/main.ts",  output: [    {      banner: "#!/usr/bin/env node",      /**       * 头部插入这段代码       * */      name: "ds",      file: "bin/ds.js",      //打包成umd模块规范      format: "umd"    }  ],  plugins: [    typescript(),    commonjs({      include: "node_modules/**",      extensions: ['.js', '.ts']    }),    uglify()  ],};复制代码

npm脚本命令("scripts"字段)

{"clean": "rm -rf ./bin && mkdir bin","build": "npm run clean && rollup --config"}复制代码

编写入口文件

是一些非常基础的东西,我们一般不放很复杂的逻辑在入口文件里。

const cmd = require('commander');const config = require('../package.json');//这里cli-init.ts和cli-list.ts我们可以简单导出一个函数,如// export default function(...args) {//     console.log('init')// }import init from './lib/init/cli-init'; import list from './lib/list/cli-list';const command= {    init,    list};//map对应的type,从而执行function exec(type, ...args) {    config.debug = args[0].debug;    command[type](...args);}cmd  .usage('')  .version(config.version)  .description('欢迎使用ds-cli');cmd  .command('init')  .description('初始化组件模板')  .action((...args) => exec('init', ...args));cmd  .command('list')  .description('查看线上组件模板')  .action((...args) => exec('list', ...args));cmd.command('help')  .description('查看帮助')  .action(() => cmd.help());// 解析输入的参数cmd.parse(process.argv);if (!cmd.args.length) {  cmd.help();}复制代码

我们打包(执行npm run build)到bin文件夹下后,配置一下package.json的bin字段为bin/ds.js,然后发布npm(参考试一下命令。

如果失败,请重新审视上述之流程。

初始化模板

我们对模板的要求

  • 文件目录必须含有template文件夹,并且所需模板文件放在该目录下

 

 

 

  • 可用meta.js提高自定义程度(所谓动态化模板)
  • 模板文件名命名规范是ds-cli-‘name’-template,方便脚手架拉取
  • 可以不用线上的模板,如果本地有模板,可直接用之。如果是线上模板,那么应该先下载到本地用户目录的.ds-templates目录下(~/.ds-templates)

期望命令

ds init 
#模板名字和应用名字复制代码

流程与逻辑

if(当前目录下构建){    询问一下是不是当前目录,是的话进入run函数}else{    进入主流程run函数}//run函数function run(){    if(模板路径是本地文件路径){        //支持本地模板如ds init /usr/webpack test        if(路径存在){            //动态构造模板到你的目录如test            generate()        }else{            //报错日志        }    }else{        //1.检查当前process的node版本,大于6才可以用        //2.检查当前package.json的版本,跟远程仓库的版本比较一下。如果不一样,就提醒一下用户有新版本        //3.下载远程仓库到本地(本地一般存放用户目录里的.ds-template文件夹下),然后执行generate函数    }}复制代码

动态模板

大家也看到了,其实最重要的就是generate函数~

  • 而如果我们去掉这一步generate模板的话,其实就是相当于下载一个静态模板,如果我们对于用户自定义没要求的话,其实可以跳过这一步。
  • generate函数里面用到了,这个就相当于我们之前用的gulp,通过不断地编写中间件来优化打包后的结果。
function 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);    });}复制代码
  • 我们在模板(如 )目录下需要构造meta.js,自定义我们所需的字段
module.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"    }}复制代码

至此,这里我们已经说明了基本原理,你应该已经可以搭出适合你团队的脚手架和模板了。

列举模板仓库

一般情况下都会有list命令

感谢github给我们提供了api,有以下比较好玩的接口

  • 个人主要信息:
  • 个人所有repo:
  • repo详细信息:
  • issues列表。
  • 某条issue详情: issues都是以1,2,3这样的序列排号的。
const logSymbols = require('log-symbols');const chalk = require('chalk');export default async function(...args) {    // 获取仓库列表    const res=await request({        url: 'https://api.github.com/users/yokiyokiyoki/repos',        method: 'GET'        }    );    let list;    if(res.status===200) {        console.log(logSymbols.info,chalk.green('共有下列模板'));        list=res.data.filter((item)=> {            return item.name.includes('ds-cli')&&item.name.includes('template');        }).forEach(item=> {            console.log();            console.log(chalk.green(item.name));        });    } else {        console.log(logSymbols.error,`获取仓库列表失败${res.data}`);    }}复制代码

 

 

 

需要注意的是,有时候github抽风,请求的会比较慢,这样对用户体验不友好(尝试了几次ds list如果很慢,开发者会失去耐心)。

如果对用户体验有追求的同学,这里提供一个思路,就是缓存。可以把该结果写入文件,缓存到本地存模板的文件夹里(~/.ds-templates),同时把该请求时间(主要是用来判断是否是今天)也进去,然后我们在代码里判断一下:下次请求的时候,是否本本地有该文件,有的话就读一下,读了之后判断里面的时间和现在的时候是否是相隔一天,相隔一天就重新请求(时间间隔自己把握),覆盖原有缓存文件。

参考

  • :目前暂时命令有init和list(获取所有模板列表),含有详细注释,不懂你来!!!
  • :基于rollup和typescript构建类库
  • :基于vuepress的文档模板

作者:Yoki
链接:https://juejin.im/post/5c98dc71e51d4501806d0a98
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的文章
二叉树深度优先遍历和广度优先遍历
查看>>
生产者消费者模型,循环队列实现
查看>>
IA32时钟周期的一些内容
查看>>
获得github工程中的一个文件夹的方法
查看>>
《PostgreSQL技术内幕:查询优化深度探索》养成记
查看>>
PostgreSQL查询优化器详解之逻辑优化篇
查看>>
STM32中assert_param的使用
查看>>
IO口的作用
查看>>
归档与解归档
查看>>
为什么button在设置标题时要用一个方法,而不像lable一样直接用一个属性
查看>>
字符串的截取
查看>>
139. Word Break (DP)
查看>>
Tensorflow入门资料
查看>>
剑指_用两个栈实现队列
查看>>
剑指_栈的压入弹出序列
查看>>
剑指_复杂链表的复制
查看>>
FTP的命令
查看>>
CentOS操作系统下安装yum的方法
查看>>
FTP 常见问题
查看>>
zookeeper单机集群安装
查看>>