IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 关于为了少搬砖,而用node手写了一个React脚手架这件事 -> 正文阅读

[JavaScript知识库]关于为了少搬砖,而用node手写了一个React脚手架这件事

前言

hellow,大家好。最近刚写完一个react项目,又想写一个练练手,可是我突然发现一个问题。那就是又要从零构建一个完整的项目环境,避免不了要重复搬砖搬砖还是搬砖,确实挺麻烦,可能得花费20多分钟。啥?我项目都还没写就花了我半小时?气愤之下,我立刻想了个好主意,我能不能用node写个脚手架呢?就像vue react它们的脚手架一样,直接运行vue create xxx 就直接把它那套模板搬过来了,可是我的明显是基于他们之上再结合自己日常项目需求所需的额外的包来搭建我们的项目。现在很多公司都有自己的脚手架,无非是根据自己公司的项目来定制的一套模板而已,并且在此基础之上添加了一些脚本命令,再生成特定的文件夹和文件一系列操作。就比如创建一个store,我们可能需要下面这样的结构

启程

脚本是如何执行的?

大家在用脚手架创建项目时有没有这样的困惑,为啥敲个vue create xxx 它就知道帮我创建项目,它是如何运行的呢?我们要怎么实现呢?让我们带着问题一步一步实现吧。乍看觉得很复杂,其实当我们深入了解的时候就会发现,其实也没有这么难。

初始化

新建一个acr-cli 文件夹 寓意:a auto react cli 自动构建react项目执行脚本

npm init -y

初始化生成 package.json 文件新建index.js 入口文件先下载 commander 模块 辅助我们执行终端命令先实现一个最简单的命令 acr -V 或者 acr --version 查看我们脚手架版本

index.js

开头这个注释很重要!!这是标记运行脚本,不能省略

#!/usr/bin/env node
const program = require('commander')

// 查看版本号
program.version(require('./package.json').version)

// 解析终端指令
program.parse(process.argv); 

package.json

**注意添加 bin对象 这段指令!**指定脚本执行的入口文件即后续操作

{"name": "acr-cli","version": "1.0.0","description": "a auto create react cli","main": "index.js","bin": {"acr": "index.js"},"scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"keywords": ["react","vue","acr"],"author": "kzj","license": "MIT","homepage": "https://github.com/kzj0916","repository": {"type": "git","url": "https://github.com/kzj0916"},"dependencies": {"commander": "^6.1.0","download-git-repo": "^3.0.2","ejs": "^3.1.5"}
} 

修改完成后,执行脚本

npm link

使我们的配置生效此时运行 acr -V 或者 acr --version 就可以查看我们脚手架版本

实现 --help指令

index.js

#!/usr/bin/env node
const program = require('commander')

const helpOptions = require('./lib/core/help')

// 查看版本号
program.version(require('./package.json').version)

// 配置help指令
helpOptions()

// 解析终端指令
program.parse(process.argv); 

lib/core/help.js

注意,这里配置的 -d --dest 后面有用到哦

// 配置--help指令执行后的输出

const program = require('commander')

const helpOptions = () => {// 增加自己的optionsprogram.option('-a --acr', 'a auto create React cli');program.option('-d --dest <dest>', '配置目标路径,例如: -d /src/components')program.option('-f --framework <framework>', 'your frameword')//配置其他信息program.on('--help', function () {console.log("");console.log("其它配置:")console.log("other options~");})
}

module.exports = helpOptions 

现在我们 acr --help 指令也完成了,下一步主要是实现自动创建项目

实现自动创建项目

自动创建项目分三步

1.下载指定的react模板 --> git clone …
2.安装依赖 —> yarn install
3.运行项目 —> yarn start

index.js

#!/usr/bin/env node
const program = require('commander')

const helpOptions = require('./lib/core/help')
const createCommands = require('./lib/core/create')

// 查看版本号
program.version(require('./package.json').version)

// 配置help指令
helpOptions()

// 创建其他指令
createCommands()

// 解析终端指令
program.parse(process.argv); 

lib/core/create.js

监听终端执行的脚本,做出相应行动由于action执行函数体较复杂,为了代码的可读性进行了抽离,在actions封装

const program = require('commander');

const {createAction,
} = require('./action')

const createCommands = () => {program.command('create <project> [others...]').description('自动创建项目').action(createAction)
}

module.exports = createCommands 

action.js

根据我们上面的三步规划来运行的commandSpawn 就是对终端执行做了一些额外的配置,看下面的文件就知道了需要下载 download-git-repo 帮助我们远程下载git上的代码我个人的react模板地址reactTmp大家可以参考下

 const { promisify } = require('util');
const download = promisify(require('download-git-repo'));
const path = require('path');

// 模板地址
const { reacttmp } = require('../config/temp-git-path')
// 执行cmd指令
const { commandSpawn } = require('../utils/terminal')

// callback -> promisify(函数) -> Promise -> async await
// project 所创建的文件名
// 创建项目
const createAction = async (project) => {console.log("正在构建项目中~")// 1.远程克隆react的模板await download(reacttmp, project, { clone: true })// 2.初始化依赖下载 判断电脑配置环境 yarn installconst command = process.platform === 'win32' ? 'yarn.cmd' : 'yarn';await commandSpawn(command, ['install'], { cwd: `./${project}` })//3.运行项目 执行yarn startcommandSpawn(command, ['start'], { cwd: `./${project}` });
}module.exports = {createAction
} 

utils/terminal.js

 const { spawn } = require('child_process');

const commandSpawn = (...args) => {return new Promise((resolve, reject) => {const childProcess = spawn(...args);// 下载执行时的进程打印 成功下载或失败下载childProcess.stdout.pipe(process.stdout);childProcess.stderr.pipe(process.stderr);// 下载执行完毕childProcess.on("close", () => {resolve();})})
}

module.exports = {commandSpawn
} 

运行

acr create demothree

自动创建组件

每次创建组件时,既要创建文件夹又要创建俩文件,如下

create.js

这里以自动创建组件为例,下面的基本上是一样的思想就不过多阐述了,具体看看源码

const program = require('commander');

const {createAction,addComponentAction,addPageAndRouteAction,addStoreAction,addServiceAction
} = require('./action')

const createCommands = () => {program.command('create <project> [others...]').description('自动创建项目').action(createAction)program.command('addcpn <name>').description('自动创建组件').action((name) => {addComponentAction(name, program.dest || 'src/components');})program.command('addpage <page>').description('自动创建页面').action((page) => {addPageAndRouteAction(page, program.dest || 'src/views');})program.command('addstore <store>').description('自动创建store').action((store) => {addStoreAction(store, program.dest || 'src/store');})program.command('addserver <serve>').description('自动创建service').action((serve) => {addServiceAction(serve, program.dest || 'src/services');})
}

module.exports = createCommands 

action.js

compile 根据ejs编译生成模板 需要下载ejscreateDir 判断路径是否存在并创建文件夹 错误则返回falsewriteToFile 写入内容至对应文件

const { promisify } = require('util');
const download = promisify(require('download-git-repo'));
const path = require('path');

// 模板地址
const { reacttmp } = require('../config/temp-git-path')
// 执行cmd指令
const { commandSpawn } = require('../utils/terminal')
// 编译模板
const { compile, writeToFile, createDir } = require('../utils/utils')

// callback -> promisify(函数) -> Promise -> async await
// project 所创建的文件名
// 创建项目
const createAction = async (project) => {console.log("正在构建项目中~")// 远程克隆react的模板await download(reacttmp, project, { clone: true })// 初始化依赖下载 判断电脑配置环境const command = process.platform === 'win32' ? 'yarn.cmd' : 'yarn';await commandSpawn(command, ['install'], { cwd: `./${project}` })//运行项目 执行npm run startcommandSpawn(command, ['start'], { cwd: `./${project}` });
}

// 创建组件
const addComponentAction = async (name, dest) => {// 获取编译成功后的模板内容const component = await compile("react-component.ejs", { name, wrapperName: name + 'Wrapper' })const style = await compile("react-style.ejs", { wrapperName: name + 'Wrapper' })// 写入文件的操作const targetDest = path.resolve(dest, name.toLowerCase())if (createDir(targetDest)) {const componentPath = path.resolve(targetDest, `index.tsx`);const stylePath = path.resolve(targetDest, `style.ts`);writeToFile(componentPath, component);writeToFile(stylePath, style);}
}

module.exports = {createAction,addComponentAction,
} 

util.js

const path = require('path');
const fs = require('fs');
const ejs = require('ejs');

// 编译并生成对应模板 templateName模板名 data额外参数
const compile = (templateName, data) => {const templatePosition = `../templates/${templateName}`;// 获取模板完整路径const templatePath = path.resolve(__dirname, templatePosition);return new Promise((resolve, reject) => {ejs.renderFile(templatePath, { data }, {}, (err, result) => {if (err) {console.log(err);reject(err);return;}resolve(result);})})
}

const writeToFile = (path, content) => {// 判断path是否存在, 如果不存在, 创建对应的文件夹return fs.promises.writeFile(path, content);
}

// eg src/components/navbar/header
// 递归生成文件夹
const createDir = (dirPath) => {if (!fs.existsSync(dirPath)) {if (createDir(path.dirname(dirPath))) {fs.mkdirSync(dirPath);return true}} else {return true}

}

module.exports = {compile,writeToFile,createDir
} 

大功告成

写完了,让我们来看看效果吧默认组件文件夹是这样的

acr addcpn Header

acr addcpn Header -d src/views/home

执行前

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-21 00:19:29  更:2022-09-21 00:24:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:38:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码