2024年Web前端最全前端CLI脚手架思路解析(2),面试题及参考答案

最后

中年危机是真实存在的,即便有技术傍身,还是难免对自己的生存能力产生质疑和焦虑,这些年职业发展,一直在寻求消除焦虑的依靠。

  • 技术要深入到什么程度?

  • 做久了技术总要转型管理?

  • 我能做什么,我想做什么?

  • 一技之长,就是深耕你的专业技能,你的专业技术。(重点)

  • 独立做事,当你的一技之长达到一定深度的时候,需要开始思考如何独立做事。(创业)

  • 拥有事业,选择一份使命,带领团队实现它。(创业)

一技之长分五个层次

  • 栈内技术 - 是指你的前端专业领域技术

  • 栈外技术 - 是指栈内技术的上下游,领域外的相关专业知识

  • 工程经验 - 是建设专业技术体系的“解决方案”

  • 带人做事 - 是对团队协作能力的要求

  • 业界发声 - 工作经验总结对外分享,与他人交流

永远不要放弃一技之长,它值得你长期信仰持有

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

主要内容包括html,css,html5,css3,JavaScript,正则表达式,函数,BOM,DOM,jQuery,AJAX,vue 等等。

功能策划


我们先用思维导图来策划一下我们的脚手架需要有哪些主要命令:init(初始化模板)、template(下载模板)、mirror(切换镜像)、upgrade(检查更新),相关导图如下:

78aad6607ab1e106072b5683bd60d781.png

开始动手


新建一个名为 js-plugin-cli 的文件夹后打开,执行 npm init -y 快速初始化一个 package.json,然后根据下面创建对应的文件结构:

js-plugin-cli

├─ .gitignore

├─ .npmignore

├─ .prettierrc

├─ LICENSE

├─ README.md

├─ bin

│  └─ index.js

├─ lib

│  ├─ init.js

│  ├─ config.js

│  ├─ download.js

│  ├─ mirror.js

│  └─ update.js

└─ package.json

其中 .gitignore、.npmignore、.prettierrc、LICENSE、README.md 是额外附属文件(非必须),但这里推荐创建好它们,相关内容根据自己习惯设定就行。

在项目里打开终端,先把需要的依赖装上,后续可以直接调用。

yarn add -D chalk commander download fs-extra handlebars inquirer log-symbols ora update-notifier

注册指令


当我们要运行调试脚手架时,通常执行 node ./bin/index.js 命令,但我还是习惯使用注册对应的指令,像 vue init webpack demovue 就是脚手架指令,其他命令行也要由它开头。打开 package.json 文件,先注册下指令:

“main”: “./bin/index.js”,

“bin”: {

“js-plugin-cli”: “./bin/index.js”

},

main 中指向入口文件 bin/index.js,而 bin 下的 js-plugin-cli 就是我们注册的指令,你可以设置你自己想要的名称(尽量简洁)。

万物皆-v


我们先编写基础代码,让 js-plugin-cli -v 这个命令能够在终端打印出来。

打开 bin/index.js 文件,编写以下代码 :

#!/usr/bin/env node

// 请求 commander 库

const program = require(‘commander’)

// 从 package.json 文件中请求 version 字段的值,-v和–version是参数

program.version(require(‘…/package.json’).version, ‘-v, --version’)

// 解析命令行参数

program.parse(process.argv)

其中 #!/usr/bin/env node (固定第一行)必加,主要是让系统看到这一行的时候,会沿着对应路径查找 node 并执行。

调试阶段时,为了保证 js-plugin-cli 指令可用,我们需要在项目下执行 npm link(不需要指令时用 npm unlink 断开),然后打开终端,输入以下命令并回车:

js-plugin-cli -v

此时,应该返回版本号 1.0.0,如图:

3091c0c063b22116144474fddd4221cd.png

接下来我们将开始写逻辑代码,为了维护方便,我们将在 lib 文件夹下分模块编写,然后在 bin/index.js 引用。

upgrade 检查更新


打开 lib/update.js 文件,编写以下代码 :

// 引用 update-notifier 库,用于检查更新

const updateNotifier = require(‘update-notifier’)

// 引用 chalk 库,用于控制台字符样式

const chalk = require(‘chalk’)

// 引入 package.json 文件,用于 update-notifier 库读取相关信息

const pkg = require(‘…/package.json’)

// updateNotifier 是 update-notifier 的方法,其他方法可到 npmjs 查看

const notifier = updateNotifier({

// 从 package.json 获取 name 和 version 进行查询

pkg,

// 设定检查更新周期,默认为 1000 * 60 * 60 * 24(1 天)

// 这里设定为 1000 毫秒(1秒)

updateCheckInterval: 1000,

})

function updateChk() {

// 当检测到版本时,notifier.update 会返回 Object

// 此时可以用 notifier.update.latest 获取最新版本号

if (notifier.update) {

console.log(New version available: ${chalk.cyan(notifier.update.latest)}, it's recommended that you update before using.)

notifier.notify()

} else {

console.log(‘No new version is available.’)

}

}

// 将上面的 updateChk() 方法导出

module.exports = updateChk

这里需要说明两点:updateCheckInterval 默认是 1 天,也就意味着今天检测更新了一次,下一次能进行检测更新的时间点应该为明天同这个时间点之后,否则周期内检测更新都会转到 No new version is available.

举个栗子:我今天10点的时候检查更新了一次,提示有新版本可用,然后我下午4点再检查一次,此时将不会再提示有新版本可用,只能等到明天10点过后再检测更新才会重新提示新版本可用。因此,将 updateCheckInterval 设置为 1000 毫秒,就能使每次检测更新保持最新状态。

另外,update-notifier 检测更新机制是通过 package.json 文件的 name 字段值和 version 字段值来进行校验:它通过 name 字段值从 npmjs 获取库的最新版本号,然后再跟本地库的 version 字段值进行比对,如果本地库的版本号低于 npmjs 上最新版本号,则会有相关的更新提示。

当然,此时我们还需要把 upgrade 命令声明一下,打开 bin/index.js 文件,在合适的位置添加以下代码:

// 请求 lib/update.js

const updateChk = require(‘…/lib/update’)

// upgrade 检测更新

program

// 声明的命令

.command(‘upgrade’)

// 描述信息,在帮助信息时显示

.description(“Check the js-plugin-cli version.”)

.action(() => {

// 执行 lib/update.js 里面的操作

updateChk()

})

添加后的代码应该如图所示:

4780849b17d026536bfcb996a05cbd89.png

记得把 program.parse(process.argv) 放到最后就行。

添加好代码后,打开控制台,输入命令 js-plugin-cli upgrade 查看效果:

a17790cd65fe3ef420c61aad1c481163.png

为了测试效果,我将本地库 js-plugin-clipackage.jsonname 改为 vuepress-creatorversion 默认为 1.0.0,而 npmjs 上 vuepress-creator 脚手架最新版本为 2.x,因此会有更新的提示。

mirror 切换镜像链接


我们通常会把模板放 Github 上,但是在国内从 Github 下载模板不是一般的慢,所以我考虑将模板放 Vercel 上,但是为了避免一些地区的用户因网络问题不能正常下载模板的问题,我们需要将模板链接变成可定义的,然后用户就可以自定义模板链接,更改为他们自己觉得稳定的镜像托管平台上,甚至还可以把模板下载下来,放到他们自己服务器上维护。

为了能够记录切换后的镜像链接,我们需要在本地创建 config.json 文件来保存相关信息,当然不是由我们手动创建,而是让脚手架来创建,整个逻辑过程如下:

a62b111301d37689827263ed5fe53158.png

所以我们还需要在 lib 文件夹下创建 config.js 文件,用于生成默认配置文件。

打开 lib/config.js 文件,添加以下代码:

// 请求 fs-extra 库

const fse = require(‘fs-extra’)

const path = require(‘path’)

// 声明配置文件内容

const jsonConfig = {

“name”: “js-plugin-cli”,

“mirror”: “https://zpfz.vercel.app/download/files/frontend/tpl/js-plugin-cli/”

}

// 拼接 config.json 完整路径

const configPath = path.resolve(__dirname,‘…/config.json’)

async function defConfig() {

try {

// 利用 fs-extra 封装的方法,将 jsonConfig 内容保存成 json 文件

await fse.outputJson(configPath, jsonConfig)

} catch (err) {

console.error(err)

process.exit()

}

}

// 将上面的 defConfig() 方法导出

module.exports = defConfig

这里需要注意的是,我们不要再直接去用内置的 fs 库,推荐使用增强库 fs-extrafs-extra 除了封装原有基础文件操作方法外,还有方便的 json 文件读写方法。

打开 lib/mirror.js 文件,添加以下代码:

// 请求 log-symbols 库

const symbols = require(‘log-symbols’)

// 请求 fs-extra 库

const fse = require(‘fs-extra’)

const path = require(‘path’)

// 请求 config.js 文件

const defConfig = require(‘./config’)

// 拼接 config.json 完整路径

const cfgPath = path.resolve(__dirname,‘…/config.json’)

async function setMirror(link) {

// 判断 config.json 文件是否存在

const exists = await fse.pathExists(cfgPath)

if (exists){

// 存在时直接写入配置

mirrorAction(link)

}else{

// 不存在时先初始化配置,然后再写入配置

await defConfig()

mirrorAction(link)

}

}

async function mirrorAction(link){

try {

// 读取 config.json 文件

const jsonConfig = await fse.readJson(cfgPath)

// 将传进来的参数 link 写入 config.json 文件

jsonConfig.mirror = link

// 再写入 config.json 文件

await fse.writeJson(cfgPath, jsonConfig)

// 等待写入后再提示配置成功

console.log(symbols.success, ‘Set the mirror successful.’)

} catch (err) {

// 如果出错,提示报错信息

console.log(symbols.error, chalk.red(Set the mirror failed. ${err}))

process.exit()

}

}

// 将上面的 setMirror(link) 方法导出

module.exports = setMirror

需要注意的是 asyncawait,这里用的是 Async/Await 的写法,其他相关写法可参照 fs-extra[10] 。async 一般默认放函数前面,而 await 看情况添加,举个例子:

const jsonConfig = await fse.readJson(cfgPath)

jsonConfig.mirror = link

await fse.writeJson(cfgPath, jsonConfig)

console.log(symbols.success, ‘Set the mirror successful.’)

我们需要等待 fs-extra 读取完,才可以进行下一步,如果不等待,就会继续执行 jsonConfig.mirror = link 语句,就会导致传入的 json 结构发生变化。再比如 await fse.writeJson(cfgPath, jsonConfig) 这句,如果去掉 await,将意味着还在写入 json 数据(假设写入数据需要花 1 分钟)时,就已经继续执行下一个语句,也就是提示 Set the mirror successful.,但实际上写入文件不会那么久,就算去掉 await,也不能明显看出先后执行关系。

老规矩,我们还需要把 mirror 命令声明一下,打开 bin/index.js 文件,在合适的位置添加以下代码:

// 请求 lib/mirror.js

const setMirror = require(‘…/lib/mirror’)

// mirror 切换镜像链接

program

.command(‘mirror <template_mirror>’)

.description(“Set the template mirror.”)

.action((tplMirror) => {

setMirror(tplMirror)

})

打开控制台,输入命令 js-plugin-cli mirror 你的镜像链接 查看效果:

6da4e37d884714ad774d1e63f1feeba1.png

此时,在项目下应该已经生成 config.json 文件,里面相关内容应该为:

{

“name”: “js-plugin-cli”,

“mirror”: “https://zpfz.vercel.app/download/files/frontend/tpl/js-plugin-cli/”

}

download 下载/更新模板


网络上很多教程在谈及脚手架下载模板时都会选择 download-git-repo 库,但是这里我选择 download 库,因为利用它可以实现更自由的下载方式,毕竟 download-git-repo 库主要还是针对 Github 等平台的下载,而 download 库可以下载任何链接的资源,甚至还有强大的解压功能(无需再安装其他解压库)。

在此之前,我们得先明白 lib/download.js 需要执行哪些逻辑:下载/更新模板应属于强制机制,也就是说,不管用户本地是否有模板存在,lib/download.js 都会下载并覆盖原有文件,以保持模板的最新状态,相关逻辑图示如下:

4eeda50f2f4dcf5a567c5c3e12cd7a9a.png

打开 lib/download.js 文件,添加以下代码:

// 请求 download 库,用于下载模板

const download = require(‘download’)

// 请求 ora 库,用于实现等待动画

const ora = require(‘ora’)

// 请求 chalk 库,用于实现控制台字符样式

const chalk = require(‘chalk’)

// 请求 fs-extra 库,用于文件操作

const fse = require(‘fs-extra’)

const path = require(‘path’)

// 请求 config.js 文件

const defConfig = require(‘./config’)

// 拼接 config.json 完整路径

const cfgPath = path.resolve(__dirname,‘…/config.json’)

// 拼接 template 模板文件夹完整路径

const tplPath = path.resolve(__dirname,‘…/template’)

async function dlTemplate() {

// 参考上方 mirror.js 主代码注释

const exists = await fse.pathExists(cfgPath)

if (exists){

// 这里记得加 await,在 init.js 调用时使用 async/await 生效

await dlAction()

}else{

await defConfig()

// 同上

await dlAction()

}

}

async function dlAction(){

// 清空模板文件夹的相关内容,用法见 fs-extra 的 README.md

try {

await fse.remove(tplPath)

} catch (err) {

console.error(err)

process.exit()

}

// 读取配置,用于获取镜像链接

const jsonConfig = await fse.readJson(cfgPath)

// Spinner 初始设置

const dlSpinner = ora(chalk.cyan(‘Downloading template…’))

// 开始执行等待动画

dlSpinner.start()

try {

// 下载模板后解压

await download(jsonConfig.mirror + ‘template.zip’, path.resolve(__dirname,‘…/template/’),{extract:true});

} catch (err) {

// 下载失败时提示

dlSpinner.text = chalk.red(Download template failed. ${err})

// 终止等待动画并显示 X 标志

dlSpinner.fail()

process.exit()

}

// 下载成功时提示

dlSpinner.text = ‘Download template successful.’

// 终止等待动画并显示 ✔ 标志

dlSpinner.succeed()

}

// 将上面的 dlTemplate() 方法导出

module.exports = dlTemplate

我们先用 fse.remove() 清空模板文件夹的内容(不考虑模板文件夹存在与否,因为文件夹不存在不会报错),然后执行等待动画并请求下载,模板文件名固定为 template.zipdownload 语句里的 extract:true 表示开启解压。

上述代码有两处加了 process.exit(),意味着将强制进程尽快退出(有点类似 return 的作用,只不过 process.exit() 结束的是整个进程),哪怕还有未完全完成的异步操作。

就比如说第二个 process.exit() 吧,当你镜像链接处于 404 或者其他状态,它会返回你相应的报错信息并退出进程,就不会继续执行下面 dlSpinner.text 语句了。

我们还需要把 template 命令声明一下,打开 bin/index.js 文件,在合适的位置添加以下代码:

// 请求 lib/download.js

const dlTemplate = require(‘…/lib/download’)

// template 下载/更新模板

program

.command(‘template’)

.description(“Download template from mirror.”)

.action(() => {

dlTemplate()

})

打开控制台,输入命令 js-plugin-cli template 查看效果:

自学几个月前端,为什么感觉什么都没学到??


这种现象在很多的初学者和自学前端的同学中是比较的常见的。

因为自学走的弯路是比较的多的,会踩很多的坑,学习的过程中是比较的迷茫的。

最重要的是,在学习的过程中,不知道每个部分该学哪些知识点,学到什么程度才算好,学了能做什么。

很多自学的朋友往往都是自己去找资料学习的,资料上有的或许就学到了,资料上没有的或许就没有学到。

这就会给人一个错误的信息就是,我把资料上的学完了,估计也-就差不多的了。

但是真的是这样的吗?非也,因为很多人找的资料就是很基础的。学完了也就是掌握一点基础的东西。分享给你一份前端分析路线,你可以参考。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

还有很多的同学在学习的过程中一味的追求学的速度,很快速的刷视频,写了后面忘了前面,最后什么都没有学到,什么都知道,但是什么都不懂,要具体说,也说不出个所以然。

所以学习编程一定要注重实践操作,练习敲代码的时间一定要多余看视频的时间。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值