手写一个合格的前端脚手架

为什么我们需要一套脚手架

为什么我们需要一套脚手架,它能帮助我们解决哪些痛点问题。

•前端项目配置越来越繁琐、耗时,重复无意义的工作•项目结构不统一、不规范•前端项目类型繁多,不同项目不同配置,管理成本高•脚手架也可以是一套命令集,不只用来创建项目

那么为什么不用一些开源框架自身的 CLI 工具,需要自己开发呢,这里仁者见仁智者见智,我个人建议就是对于中型团队以上需要自己维护一套脚手架,因为可控性高,能满足团队特定需求的研发。

如何按照开源要求开发一个前端脚手架?

下面是我们常见的前端开源目录结构

脚手架的设计

思路

•解耦:脚手架与模板分离•脚手架负责构建流程,通过命令行与用户交互,获取项目信息•模板负责统一项目结构、工作流程、依赖项管理•脚手架需要检测模板的版本是否有更新,支持模板的删除与新建•……

流程图

代码讲解

目录结构

配置 Git hook

首先进行开发前的准备工作,来保证你代码的质量。

Husky + Lint-staged

通过 Git hook 完成 commitlint、ESLint、prettiter 等,具体配置我后面会给源码,有兴趣的可以自己搜索下。

// package.json
"husky": {"hooks": {"pre-commit": "lint-staged","commit-msg": "commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged": {"**/*.js": ["eslint --fix","prettier --write","git add"]}

package.json 下的 bin 字段

bin:配置内部命令对应的可执行文件位置,配置命令后,npm 会寻找到对应的可执行文件,然后在 node_modules/.bin 目录下建立对应的符号链接。

由于 node_modules/.bin 会在运行时候加入到系统的环境变量,因此我们可以通过 npm 调用命令来执行脚本。

所有 node_modules/.bin 目录下的命令都可以通过 npm run [命令] 执行。

所以我们需要在 package.json 配置入口

"bin": {"easy": "bin/easy.js"}

npm link 本地调试

这里介绍下开发脚手架的调试方法。npm link 官网使用介绍。使用方法:

// cd 到你项目的bin目录(脚本)下
$ npm link

去掉 link 也非常方便:

npm unlink linkname

bin 目录下的入口文件

#!/usr/bin/env nodeconst program = require('commander'); // 命令行工具
const chalk = require('chalk'); // 命令行输出美化
const didYouMean = require('didyoumean'); // 简易的智能匹配引擎
const semver = require('semver'); // npm的语义版本包
const enhanceErrorMessages = require('../lib/util/enhanceErrorMessages.js');
const requiredNodeVersion = require('../package.json').engines.node;didYouMean.threshold = 0.6;function checkNodeVersion(wanted, cliName) {// 检测node版本是否符合要求范围if (!semver.satisfies(process.version, wanted)) {console.log(chalk.red('You are using Node ' +process.version +', but this version of ' +cliName +' requires Node ' +wanted +'.\nPlease upgrade your Node version.'));// 退出进程process.exit(1);}
}// 检测node版本
checkNodeVersion(requiredNodeVersion, '@easy/cli');program.version(require('../package').version, '-v, --version') // 版本.usage('<command> [options]'); // 使用信息// 初始化项目模板
program.command('create <template-name> <project-name>').description('create a new project from a template').action((templateName, projectName, cmd) => {// 输入参数校验validateArgsLen(process.argv.length, 5);require('../lib/easy-create')(lowercase(templateName), projectName);});// 添加一个项目模板
program.command('add <template-name> <git-repo-address>').description('add a project template').action((templateName, gitRepoAddress, cmd) => {validateArgsLen(process.argv.length, 5);require('../lib/add-template')(lowercase(templateName), gitRepoAddress);});// 列出支持的项目模板
program.command('list').description('list all available project template').action(cmd => {validateArgsLen(process.argv.length, 3);require('../lib/list-template')();});// 删除一个项目模板
program.command('delete <template-name>').description('delete a project template').action((templateName, cmd) => {validateArgsLen(process.argv.length, 4);require('../lib/delete-template')(templateName);});// 处理非法命令
program.arguments('<command>').action(cmd => {// 不退出输出帮助信息program.outputHelp();console.log(`  ` + chalk.red(`Unknown command ${chalk.yellow(cmd)}.`));console.log();suggestCommands(cmd);
});// 重写commander某些事件
enhanceErrorMessages('missingArgument', argsName => {return `Missing required argument ${chalk.yellow(`<${argsName}>`)}`;
});program.parse(process.argv); // 把命令行参数传给commander解析// 输入easy显示帮助信息
if (!process.argv.slice(2).length) {program.outputHelp();
}// easy支持的命令
function suggestCommands(cmd) {const avaliableCommands = program.commands.map(cmd => {return cmd._name;});// 简易智能匹配用户命令const suggestion = didYouMean(cmd, avaliableCommands);if (suggestion) {console.log(`  ` + chalk.red(`Did you mean ${chalk.yellow(suggestion)}?`));}
}function lowercase(str) {return str.toLocaleLowerCase();
}function validateArgsLen(argvLen, maxArgvLens) {if (argvLen > maxArgvLens) {console.log(chalk.yellow('\n Info: You provided more than argument. the rest are ignored.'));}
}

其他代码就不贴了我会给出源码链接,下面分享一下几个有意思的点。建议大家有兴趣的跟着敲一遍,有很多小细节需要注意。

发布脚本

// script/release.jsconst { execSync } = require('child_process');
const semver = require('semver');
const inquirer = require('inquirer');const currentVerison = require('../package.json').version;const release = async () => {console.log(`Current easy cli version is ${currentVerison}`);const releaseActions = ['patch', 'minor', 'major'];const versions = {};// 生成预发布版本标示releaseActions.map(r => (versions[r] = semver.inc(currentVerison, r)));const releaseChoices = releaseActions.map(r => ({name: `${r} (${versions[r]})`,value: r}));// 选择发布方式const { release } = await inquirer.prompt([{name: 'release',message: 'Select a release type',type: 'list',choices: [...releaseChoices]}]);// 优先自定义版本const version = versions[release];// 二次确认发布const { yes } = await inquirer.prompt([{name: 'yes',message: `Confirm releasing ${version}`,type: 'confirm'}]);if (yes) {execSync(`standard-version -r ${release}`, {stdio: 'inherit'});}
};release().catch(err => {console.error(err);process.exit(1);
});

npm version 与 tag

官网关于 npm version 的介绍:

https://docs.npmjs.com/cli/version.html

如果不熟悉 Node 语义化版本可以阅读:

https://semver.org/lang/zh-CN/
npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease [--preid=<prerelease-id>] | from-git]'npm [-v | --version]' to print npm version
'npm view <pkg> version' to view a package's published version
'npm ls' to inspect current package/dependency versions

其实我们自己使用 npn publish,最终执行的还是 npm version 下命令。

官网关于 npm-dist-tag 的介绍:

npm-dist-tag[1]

npm install <name>@<tag>
npm install --tag <tag>

npm 也有 tag 的概念,一般情况下我们不会指定 tag,这个时候默认使用的就是 latest 这个 tag,所有的发布与安装都是最新的正式版本,如果指定 tag 之后,我们可以在这个 tag 上发布一个新的版本,用户安装时候也可以指定这个 tag 来进行安装,你可以简单理解 tag 类型 git 中的 branch。

常用的一些关于 tag 的命令:

# 查看当前的tag和对应的version。
npm dist-tag ls# 查看my-package发布过的所有版本号。
npm view my-package versions# 给my-package设置tag,对应到版本version。
npm dist-tag add my-package@version tag

如果一不小心把测试版发布成了正式版?发布之前我们是这样的:

latest: 1.0.0
next: 1.0.0-alpha.0

错误的把 1.0.0-alpha.1 直接 npm publish:

latest: 1.0.0-alpha.1
next: 1.0.0-alpha.0

解决方法:

# 把原来的1.0.0设置成最新的正式版
$ npm dist-tag add my-package@1.0.0 latest# 把1.0.0-alpha.1更新到最新的测试版
$ npm dist-tag add my-package@1.0.0-alpha.1 next

npm publish 一个包

1.创建一个 npm 账户2.cd 到你需要发布的 repo 仓库下, 记得切换到 npm 源(或者公司内网自建源)3.npm login,需要输入用户名、密码、邮箱4.npm publish

集成 CI(Travis CI)自动发布

每次手动发布太 low 了,要是可以自动发布就好了。

Travis CI 提供的是持续集成服务(Continuous Integration,简称 CI)。它绑定 GitHub/GitLab 等上面的项目,只要有新的代码,就会自动抓取。然后,提供一个运行环境,执行测试,完成构建,还能部署到服务器。

持续集成指的是只要代码有变更,就自动运行构建和测试,反馈运行结果。确保符合预期以后,再将新代码“集成”到主干。

简单理解就是:它的作用是自动帮你做好从代码测试到发布的一系列流程,配合版本控制使用的话可以设置成每一次 push 都自动进行一次集成,保证代码的正确性。

注意现在 GitHub 也出了集成工具,感兴趣的可以去体验下。

如果你的项目是在 GitHub 并且是开源的,推荐使用这个 org[2]

使用 GitHub 进行登录 Travis CI,完成一些授权工作,Travis CI 才能监听到你的 GitHub 项目代码的变化。

Travis CI 要求你项目的根目录必须有一个配置文件 .travis.yml 文件,这是一个配置文件,指定 travis 的行为,该文件还必须保存在 GitHub 的仓库。一旦有新的 push,travis 就会找到这个文件进行执行。

关于 travis 更多使用推荐阅读官网[3],这里主要讲下利用 travis 自动发布包到 npm[4],Continuous Integration environments[5]

下面是一个 .travis.yml 配置文件:

language: node_js
node_js:- '8'
cache:directories:- node_modules
install:- npm install
script:- npm run lint
deploy:provider: npmemail: "$NPM_EMAIL"api_key: "$AUTH_TOKEN"skip_cleanup: trueon:branch: master
# after_success:

然后在你的 travis 上选择需要开启 CI 的项目。

配置对应环境变量到该仓库下如:

环境变量名格式必须为“大写字母_大写字母”格式。

token 生成也非常简单,官网[6]介绍,可以直接在你的 npm 账户下的 tokens 页面手动生成或者通过 npm 命令行生成。

# 切换到npm源下, 登陆npm
npm login# 生成token, npm可以指定生成token的权限(只读或者可读可写)
npm token create

然后配置一些脚本来执行 npm version,这样当你包版本有更新后 push 到 GitHub repo,就会触发 travis 自动发包到 npm。

DEMO

源码链接[7]

推荐阅读

若川知乎高赞:有哪些必看的 JS库?
我在阿里招前端,我该怎么帮你?(现在还可以加模拟面试群)
如何拿下阿里巴巴 P6 的前端 Offer
如何准备阿里P6/P7前端面试--项目经历准备篇
大厂面试官常问的亮点,该如何做出?
如何从初级到专家(P4-P7)打破成长瓶颈和有效突破
若川知乎问答:2年前端经验,做的项目没什么技术含量,怎么办?

末尾

你好,我是若川,江湖人称菜如若川,历时一年只写了一个学习源码整体架构系列~(点击蓝字了解我)

  1. 关注若川视野,回复"pdf" 领取优质前端书籍pdf,回复"1",可加群长期交流学习

  2. 我的博客地址:https://lxchuan12.gitee.io 欢迎收藏

  3. 觉得文章不错,可以点个在看呀^_^另外欢迎留言交流~

精选前端好文,伴你不断成长

若川原创文章精选!可点击

小提醒:若川视野公众号面试、源码等文章合集在菜单栏中间【源码精选】按钮,欢迎点击阅读,也可以星标我的公众号,便于查找

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/276233.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第一篇cnblog!

本人才疏学浅&#xff0c;终于通过了cnblog的审核&#xff0c;兴奋之余&#xff0c;发表感言——不容易啊&#xff01;在我的博闻里面&#xff0c;随笔类当然就是技术类的比较多的&#xff0c;特别是实例类的。理论类的大部分放在文章板块&#xff0c;本人e文特别好(哈哈&#…

利用JMeter进行压力测试(1)(转)

转自&#xff1a;http://www.cnblogs.com/game-over/archive/2010/01/08/1642685.html压力测试以软件响应速度为测试目标&#xff0c;尤其是在较短时间内大量并发用户的同时访问时&#xff0c;软件的性能和抗压能力。 JMeter是一款开源的压力测试工具&#xff0c;目前最新Rele…

Git 内部原理图解——对象、分支以及如何从零开始建仓库

我们中的许多人每天都在使用 git&#xff0c;但是有多少人知道它的内部是怎么运作的呢&#xff1f;例如我们使用 git commit 时发生了什么&#xff1f;提交&#xff08;commit&#xff09;与提交之间保存的是什么&#xff1f;两次提交之间难道只是文件的差异&#xff08;dif…

Google, 请不要离开我们!

虽然我是.net阵营, 力挺Silverlight, 但是我真心希望谷歌留在中国, 如果她能够靠谈判求的言论自由的权利, 那将对中国的拥有自由信仰的一族产生重大的影响. 谷歌离开了中国, 不是她想抛弃中国市场, 而是中国决策者背叛了人性. 在此留下 Google 2010年1月14日的logo, 智慧的幽默…

28岁自学3年前端成功转行的励志故事

为什么转行因为混得不好。在成为程序员之前&#xff0c;我干过很多工作。由于学历的问题&#xff08;高中&#xff09;&#xff0c;我的工作基本上都是体力活。包括但不限于&#xff1a;工厂普工、销售&#xff08;没有干销售的才能&#xff09;、搬运工、摆地摊等&#xff0c;…

usb 驱动

usb 驱动学习总结&#xff1a; usb 采用分层的拓扑结构&#xff0c;金字塔型&#xff0c;最多是7层。usb 是主从结构&#xff0c;主和主或者从和从之间不能交换数据。理论上一个usb主控制器最多可接127个设备&#xff0c;协议规定每个usb设备具有一个7bit的地址&#xff0c;范围…

面试字节跳动后的2点总结,建议收藏!

首先我来辟个谣&#xff1a;随便打开一个招聘网站&#xff0c;你会发现前端工程师的岗位需求依旧庞大&#xff0c;大厂人才奇缺&#xff0c;就业薪资起点高&#xff0c;无行业限制。&#xff08;数据来源&#xff1a;职友集&#xff09;前端开发的行业大环境行业升级&#xff0…

phpexcel中文教程-设置表格字体颜色背景样式、数据格式、对齐方式、添加图片、批注、文字块、合并拆分单元格、单元格密码保护

转载连接&#xff1a;http://www.cnblogs.com/huangcong/p/3687665.html phpexcel中文教程-设置表格字体颜色背景样式、数据格式、对齐方式、添加图片、批注、文字块、合并拆分单元格、单元格密码保护 首先到phpexcel官网上下载最新的phpexcel类&#xff0c;下周解压缩一个cla…

2021年的今天,如何成为一名专业的前端工程师?

大家好&#xff0c;我是若川。今天给分享一篇来自阿里克军大佬的好文。以下是正文~如果你想成为一名专业的前端工程师&#xff0c;那么你需要了解要学什么&#xff0c;学到什么程度&#xff0c;以及如何有效地学习。大学里没有正规的前端技术课程&#xff0c;普遍缺少比较权威的…

nc65右键生成菜单_DbSchema生成表单和报表,原来如此简单

DbSchema 8 for Mac是mac上一款非常实用的商业数据库ER图绘制软件&#xff0c;可以轻松的对文档进行注释或标注&#xff0c;而且Dbschema集成了SQL和数据工具&#xff0c;能生成直观的图表、PDF文件或HTML 5文档等&#xff0c;非常的实用。现在就来给大家分享DbSchema如何生成表…

若川的2020年度总结,水波不兴

前言从2014年开始&#xff0c;每一年都会写年度总结&#xff0c;坚持了6个年头。回顾2014&#xff0c;约定2015&#xff08;QQ空间日志&#xff09;2015年总结&#xff0c;淡化旧标签&#xff0c;无惧未来&#xff08;QQ空间日志&#xff09;2016年度总结&#xff0c;毕业工作2…

年度总结文章的抽奖结果公布

大家好&#xff0c;我是若川。2月4日&#xff0c;发表了我的2020年度总结文章《若川的2020年度总结&#xff0c;水波不兴》&#xff0c;本以为阅读量应该突破一千会比较快&#xff0c;实际上比较艰难&#xff0c;而且还掉粉10来人。2020年运营公众号以来&#xff0c;不知不觉发…

Elon Musk

人物事件 成长学习 1971年6月28日&#xff0c;埃隆马斯克在南非的比勒陀利亚出生&#xff0c;他的 埃隆马斯克 父亲是一名南非机电工程师&#xff0c;母亲是加拿大人&#xff0c;从事营养师兼模特。[8] 1981年&#xff0c;10岁的马斯克就拥有了自己的第一台电脑&#xff0c;并…

真诚推荐这7个大佬的公众号,碎片化学习

逆水行舟&#xff0c;不进则退。我们的工作已经占用了大块的时间了&#xff0c;剩下的只有各种碎片&#xff0c;最适合碎片时间学习的&#xff0c;莫过于优质的技术干货公众号啦~以下这些是小编精选&#xff0c;里面有很多资讯和资源&#xff0c;内含干货&#xff0c;希望能给大…

[转]Windows 7 产品密钥是否安全

提到Windows 7&#xff08;或Windows Server 2008&#xff09;有些人认为自己的产品密钥&#xff08;Product Key&#xff09;很安全&#xff0c;甚至在公司内部有些网管也认为公司部署的Windows 7 系统的密钥不会泄露。但其实并非如此&#xff0c;众所周知我们的密钥都是写在注…

HttpWatch的Result中出现Aborted的原因分析[配图]

转载链接&#xff1a;http://www.cnblogs.com/yutiansanshou/archive/2013/02/01/2889486.html 我们在使用HttpWatch进行Web调试的过程中有时候会看到非HTTP Status Code&#xff08;状态码&#xff09;的值&#xff0c; 例如&#xff1a;(Aborted)。 (Aborted)是HttpWatch中定…

《大规模分布式系统架构与设计实战》

《大规模分布式系统架构与设计实战》 基本信息 作者&#xff1a; 彭渊 丛书名&#xff1a; 大数据技术丛书 出版社&#xff1a;机械工业出版社 ISBN&#xff1a;9787111455035 上架时间&#xff1a;2014-2-21 出版日期&#xff1a;2014 年2月 开本&#xff1a;16开 页码&…

WINDOWS下的squid

今天写这篇教程目的在于分享自己在WINDOWS主机下配置squid的方法。哪些地方写的不完善或是不完整或是需要修改的地方&#xff0c;大家可以提出。我会第一时间纠正。下面看正文部分。先提条件&#xff0c;您预安装配置squid的这台计算机必须是联入网络的&#xff0c;系统版本是w…

Spring Batch 批量处理策略

为了帮助设计和实现批量处理系统&#xff0c;基本的批量应用是通过块和模式来构建的&#xff0c;同时也应该能够为程序开发人员和设计人员提供结构的样例和基础的批量处理程序。当你开始设计一个批量作业任务的时候&#xff0c;商业逻辑应该被拆分一系列的步骤&#xff0c;而这…

如何从零开始开发一个 Chrome 插件?

什么是浏览器插件&#xff1f;简单来说浏览器插件&#xff0c;是浏览器上的一种工具&#xff0c;可以提供一些浏览器没有的功能&#xff0c;帮你做一些有趣的事情。开发者可以根据自己的喜欢&#xff0c;去实现一些功能。插件基于Web技术&#xff08;html、css、js&#xff09;…