前些天一直在学习入门Webpack,后来尝试了自己搭建一下一个简单的React开发环境,后来就在想可不可以自己写一个简单的脚手架,以免每次搭建一个简单的开发环境都需要自己一个个的配置,这样很麻烦,使用create-react-app的话,配置一大堆可能不会用到的功能,比较冗余,所以自己写一个超级简化的脚手架,只处理ES6代码、JSX语法和css模块,这样就满足了基本的使用。

后来在开发的过程中又遇到了新的麻烦,比如使用Node的child_process.spawn方法调用npm命令时,会出现错误,因为在Windows环境下,实际上要调用npm.cmd,而非npm,在这里出现了问题,还有一些其他问题,后来正好看到了@Jsonz大神写的两篇文章:探索 create-react-app 源码和create-react-app 源码解析之react-scripts,于是也照着学习了一下create-react-app脚手架的源码,基本解决了一些问题,最终写出来了一个简(can)单(fei)的React脚手架,当然还有许许多多的不足,但是这个学习的过程值得我记录下来。

这篇文章记录了以下知识:

  • 如何使用Node开发一个简单的脚手架。
  • 如何发布你的npm模块并定制命令。

一、开发React脚手架

create-react-app是一个很成功的、功能完善的脚手架,考虑到了许多方面,比如使用npm或者yarn,比如npmNode版本、日志的记录和打印等等诸多方面,开发环境搭建的也十分完善,除了基本的React开发之外,还考虑了图片、postcss、sass、graphQL等等模块的处理。由于能力有限,本文开发的脚手架只涵盖了基本模块的处理,不包含图片、sass……等等。

脚手架的作用主要是建立一个React开发的标准目录、并且配置好webpack打包工具,使得开发过程中可以直接在标准的目录上修改,然后通过配置好的命令启动本地服务器或者打包app。所以脚手架中应该包括一个模板文件夹,里面放入应该拷贝到用户工程文件夹的所有文件或目录。在使用脚手架时,先把模板文件夹中的内容拷贝到用户工程文件夹下,然后修改package.json配置文件,最后安装所有模块。这就是我开发的脚手架所完成的基本工作。

脚手架工程目录结构如下:

ROOT
│  .gitignore
│  .npmignore
│  LICENSE
│  package-lock.json
│  package.json
│  README.md
│
├─dist
├─package
│      create-react.js
│
└─templates│  .babelrc│  .gitignore│  README.md│  webpack.base.conf.js│  webpack.dev.conf.js│  webpack.prod.conf.js│├─dist└─src│  index.css│  index.html│  index.js│└─componentsApp.js

  

根据我的前一篇文章,搭建React开发环境,最小化的标准目录结构应该如下:

ROOT
│  .babelrc
│  .gitignore
│  README.md
│  webpack.base.conf.js
│  webpack.dev.conf.js
│  webpack.prod.conf.js
│
├─dist
└─src│  index.css│  index.html│  index.js│└─componentsApp.js

  

所以在脚手架根目录下的templates文件夹中应该包含以上文件,文件内的内容可以自由定制。

同样根据上一篇文章,需要安装的模块主要有:

'webpack',
'webpack-cli',
'html-webpack-plugin',
'clean-webpack-plugin',
'webpack-dev-server',
'css-loader',
'webpack-merge',
'style-loader',
'babel-preset-env',
'babel-loader',
'babel-polyfill',
'babel-preset-react'

  

'react',
'react-dom'

  

第一部分只需要安装在开发环境(npm i -D ...),第二部分生产环境也要安装(npm i --save ...)。

那么接下来可以通过Node实现脚手架的开发了。

首先介绍一些有用的并且会用到的模块:

  • cross-spawn:解决跨平台使用npm命令的问题的模块。
  • chalk:实现控制台彩色文字输出的模块。
  • fs-extra:实现了一些fs模块不包含的文件操(比如递归复制、删除等等)的模块。
  • commander: 实现命令行传入参数预处理的模块。
  • validate-npm-package-name:对于用户输入的工程名的可用性进行验证的模块。

首先,在代码中引入这些基本的模块:

const spawn = require('cross-spawn');
const chalk = require('chalk');
const os = require('os');
const fs = require('fs-extra');
const path = require('path');
const commander = require('commander');
const validateProjectName = require('validate-npm-package-name');
const packageJson = require('../package.json');

  

然后定义我们的模板复制函数:

function copyTemplates() {try {if(!fs.existsSync(path.resolve(__dirname, '../templates'))) {console.log(chalk.red('Cannot find the template files !'));process.exit(1);}fs.copySync(path.resolve(__dirname, '../templates'), process.cwd());console.log(chalk.green('Template files copied successfully!'));return true;}catch(e) {console.log(chalk.red(`Error occured: ${e}`))}
}

  

fs模块首先检测模板文件是否存在(防止被用户删除),如果存在则通过fs的同步拷贝方法(copySync)拷贝到脚手架的当前工作目录(即process.cwd()),如果不存在则弹出错误信息,随后使用退出码1退出进程。

随后定义package.json的处理函数;

function generatePackageJson() {let packageJson = {name: projectName,version: '1.0.0',description: '',scripts: {start: 'webpack-dev-server --open --config webpack.dev.conf.js',build: 'webpack --config webpack.prod.conf.js'},author: '',license: ''};try {fs.writeFileSync(path.resolve(process.cwd(), 'package.json'), JSON.stringify(packageJson));console.log(chalk.green('Package.json generated successfully!'));}catch(e) {console.log(chalk.red(e))}
}

  

可以看出先是定义了一个JavaScript Object,然后修改属性之后通过fs模块将其JSON字符串写入到了package.json文件中,实现了package.json的生成。

最后安装所有的依赖,分为devDependencies和dependencies:

function installAll() {console.log(chalk.green('Start installing ...'));let devDependencies = ['webpack', 'webpack-cli', 'html-webpack-plugin', 'clean-webpack-plugin', 'webpack-dev-server', 'css-loader', 'webpack-merge', 'style-loader', 'babel-preset-env', 'babel-loader', 'babel-polyfill', 'babel-preset-react'];let dependencies = ['react', 'react-dom'];const child = spawn('cnpm', ['install', '-D'].concat(devDependencies), { stdio: 'inherit' });child.on('close', function(code) {if(code !== 0) {console.log(chalk.red('Error occured while installing dependencies!'));process.exit(1);}else {const child = spawn('cnpm', ['install', '--save'].concat(dependencies), {stdio: 'inherit'})child.on('close', function(code) {if(code !== 0) {console.log(chalk.red('Error occured while installing dependencies!'));process.exit(1);}else {console.log(chalk.green('Installation completed successfully!'));console.log();console.log(chalk.green('Start the local server with : '))console.log();console.log(chalk.cyan('    npm run start'))console.log();console.log(chalk.green('or build your app via :'));console.log();console.log(chalk.cyan('    npm run build'));}})}});
}

  

函数中,通过cross-spawn执行了cnpm的安装命令,值得注意的是其配置项:

{ stdio: 'inherit'
}

  

代表将子进程的输出管道连接到父进程上,及父进程可以自动接受子进程的输出结果,详情见options.stdio。

通过commander模块实现命令行参数的预处理;

const program = commander
.version(packageJson.version)
.usage(' [options]')
.arguments('<project-name>')
.action(name => {projectName = name;
})
.allowUnknownOption()
.parse(process.argv);

  

其中,version方法定义了create-react-application -V的输出结果,usage定义了命令行里的用法,arguments定义了程序所接受的默认参数,然后在action函数回调中处理了这个默认参数,allowUnknownOption表示接受多余参数,parse表示把多余未解析的参数解析到process.argv中去。

最后是调用三个方法实现React开发环境的搭建:

if(projectName == undefined) {console.log(chalk.red('Please pass the project name while using create-react!'));console.log(chalk.green('for example:'))console.log();console.log('   create-react-application ' + chalk.yellow('<react-app>'));
}
else {const validateResult = validateProjectName(projectName);if(validateResult.validForNewPackages) {copyTemplates();generatePackageJson();installAll();//console.log(chalk.green(`Congratulations! React app has been created successfully in ${process.cwd()}`));}else {console.log(chalk.red('The project name given is invalid!'));process.exit(1);}
}

  

如果接受的工程名为空,那么弹出警告。如果不为空,就验证工程名的可用性,如果不可用,就弹出警告并且退出进程,否则调用之前定义的三个主要函数,完成环境的搭建。

截止到此,使用该程序的方式仍然是node xxx.js --parameters的方式,我们需要自定义一个命令,并且最好将程序上传到npm,便于使用。

二、定义你的命令并且发布npm包

实现自定义命令并发布npm模块只需要以下几步:

  • 修改入口文件,头部添加以下两句:

    #!/usr/bin/env node'use strict'
    

      

    第二行也一定不能少!

  • 修改package.json,添加bin属性:

    // package.json{"bin": {"create-react-application": "package/create-react.js"}}
    

      

  • 执行以下命令:

    npm link
    

      

  • 注册npm账户(如已经注册则可以忽略)。

  • 执行以下命令:

    npm adduser
    

      

    并输入账户密码。

  • 执行以下命令:

    npm publish
    

      

    接下来就可以收到发布成功的邮件啦!

如果要更新你的npm模块,执行以下步骤:

  • 使用一下命令更新你的版本号:

    npm version x.x.x
    

      

  • 再使用以下命令发布;

    npm publish
    

      

执行完以上步骤之后,就可以在npm下载你的模块啦!

三、FQA

(1)关于#!/usr/bin/env node

这是Unix系操作系统中的一种写法,名字叫做Shebang或者Hashbang等等。在Wikipedia的解释中,把这一行代码写在脚本中,使得操作系统把脚本当做可执行文件执行时,会找到对应的程序执行(比如此文中的node),而这段代码本身会被解释器所忽略。

(2)关于npm link

在npm官方文档的解释中,npm link的执行,是一个两步的过程。当你在你的包中使用npm link时,会将全局文件夹:{prefix}/lib/node_modules/<package>链接到执行npm link的文件夹,同样也会将执行npm link命令的包中的所有可执行文件链接到全局文件夹{prefix}/bin/{name}中。

此外,npm link project-name会将全局安装的project-name模块链接到执行npm link命令的当前文件夹的node_modules中。

根据npm官方文档,prefix的值可为:

  • /usr/local (大部分系统中)
  • %AppData%\npm (Windows中)

具体参考:prefix configuration和npm link

四、简单尝试

本文所开发的脚手架已经上传到了npm,可以通过以下步骤查看实际效果:

  • 安装create-react-application

    npm i -D create-react-application
    

      

    或者

    npm i -g create-react-application
    

      

  • 使用create-react-application

    create-react-application <project-name>
    

      

源码已经上传到了GitHub,欢迎大家一起哈啤(#手动滑稽)。

此外文中还有许多不足,比如关于npm link的解释我也还不是很清楚,欢迎大家补充指教!

五、参考文章

  • npm link 命令的作用浅析
  • prefix configuration
  • npm link
  • 基于Webpack搭建React开发环境
  • 探索 create-react-app 源码
  • npm 发布 packages

转载于:https://www.cnblogs.com/DM428/p/9068133.html

Webpack笔记(三)——一款破产版脚手架的开发相关推荐

  1. 阿里云与MongoDB达成战略合作,成“唯一”;苹果将推出三款5G版iPhone;谷歌正式推出 TensorFlow 企业版……...

      戳蓝字"CSDN云计算"关注我们哦! 嗨,大家好,重磅君带来的[云重磅]特别栏目,如期而至,每周五第一时间为大家带来重磅新闻.把握技术风向标,了解行业应用与实践,就交给我重磅君 ...

  2. C语言程序设计笔记(浙大翁恺版) 第三周:判断

    按照中国大学MOOC上浙江大学翁恺老师主讲的版本所作,B站上也有资源.原课程链接如下: https://www.icourse163.org/course/ZJU-9001 由于是大三抽空回头整理的, ...

  3. 三款html版女朋友表白告白代码,动态爱心表白代码,总有一款适合你,可定制表白内容

    三款html版女朋友表白告白代码,动态爱心表白代码,总有一款适合你,可定制表白内容 完整代码下载地址:三款html版女朋友表白告白代码,动态爱心表白代码 index.html <!DOCTYPE ...

  4. 四款 5G 版 iPhone 12 齐发,支持北斗系统,你准备好了吗?

    整理 | 郑丽媛.屠敏 头图 | CSDN下载自东方IC 真快,又见面了.北京时间 10 月 14 日凌晨 1 点,Apple 举办的新品发布会如约而至.今年有关 iPhone 新品的到来有些迟,好在 ...

  5. 深入理解Webpack核心模块Tapable钩子[异步版]

    接上一篇文章 深入理解Webpack核心模块WTApable钩子(同步版) tapable中三个注册方法 1 tap(同步) 2 tapAsync(cb) 3 tapPromise(注册的是Promi ...

  6. 华为光伏usb适配器_华为系列原装充电器拆解第三弹:比亚迪版华为10W充电器

    在对华为18W充电器的比亚迪版和赛尔康版进行拆解之后,充电头网今天继续为大家带来华为10W充电器的比亚迪版和达宏版的拆解.这两种10W规格的华为充电器外观延续了华为原装充电器的风格,而且型号也是一样的 ...

  7. webpack笔记一:webpack的介绍,安装,加载css、图片、字体等

    写在前面的话: 在当前grunt.gulp.webpack成为日常工具的情况下,如果你还只是熟练的使用html.css和激块瑞的话,已经远远不能满足项目的需求,所以你得变强,你需要懂得更多.现在前端不 ...

  8. C语言程序设计笔记(浙大翁恺版) 第九周:指针

    按照中国大学MOOC上浙江大学翁恺老师主讲的版本所作,B站上也有资源.原课程链接如下: https://www.icourse163.org/course/ZJU-9001 由于是大三抽空回头整理的, ...

  9. C语言程序设计笔记(浙大翁恺版) 第二周:计算

    按照中国大学MOOC上浙江大学翁恺老师主讲的版本所作,B站上也有资源.原课程链接如下: https://www.icourse163.org/course/ZJU-9001 由于是大三抽空回头整理的, ...

  10. C语言程序设计笔记(浙大翁恺版) 第八周:数组

    按照中国大学MOOC上浙江大学翁恺老师主讲的版本所作,B站上也有资源.原课程链接如下: https://www.icourse163.org/course/ZJU-9001 由于是大三抽空回头整理的, ...

最新文章

  1. flask jinja2 mysql_flask/jinja2 SSTI注入学习
  2. 熊猫烧香演神话——在Windows mobile手机中运行
  3. c++中友元函数详解
  4. 论文阅读(3)--SPDA-CNN: Unifying Semantic Part Detection and Abstraction for Fine-grained Recognition
  5. Android Input 子系统初探
  6. 04.卷积神经网络 W1.卷积神经网络
  7. Vue封装下拉框组件时,为document绑定原生事件addEventlistener(click“),切换页面之后事件还未被摧毁...
  8. 品质标签分几种颜色_常见标签种类分析
  9. ip 地址 192.168.1.255 代表( )。_如何批量ping大量ip地址?一个软件搞定
  10. mysql+mmm+主动模式_MySQL集群搭建(4)-MMM+LVS+Keepalived
  11. Elasticsearch6.3.0环境安装
  12. Eclipse安装svn插件的几种方式
  13. 【原文件】辞海(第六版彩图本) pdf
  14. 项目部 vs 项目组
  15. 一些不错的酷站欣赏的网站
  16. 小学英语之拯救小学生 V1.0 名词单复数变化规则
  17. 【系统相关】远程连接,凭据不工作
  18. AndroidStudio 高频使用快捷键及使用技巧(gif图)
  19. HTML动画XYZ轴的用法详解
  20. LogMeIn软件介绍

热门文章

  1. Java--ArrayList的遍历
  2. W3school中js学习
  3. mysql MyISAM和InnoDB
  4. 【收藏】基于Dockerfile搭建JAVA Tomcat运行环境
  5. 字符串转换为小数(如String a=“123“),转换之后为123.0
  6. 选择JSF不选Struts的十大理由
  7. 【bzoj1699/USACO2007】Balanced Lineup排队——RMQ问题
  8. 深入理解事件(event)与委托(delegate)
  9. Python 之图片对比
  10. 30个免费网页设计模板