浅析SFX脚手架源码

栏目: 编程工具 · 发布时间: 6年前

内容简介:这段时间看了一些1、

一、前言

这段时间看了一些 sfx 的源码,收获颇深。本想找个时间更新一篇文章,但是最近事情比较多,没有时间去整理这些东西。趁这两天闲了下来,便整理了一下,然后跟大家分享一下。

二、SFX介绍

sfx 是一款优秀的用于迅速构建Web的应用工具,开发者只需要关注项目逻辑的代码,而不需要关心webpack打包、搭建本地Node服务等等诸如此类的这些问题。是一款基于模板化的开发工具,也就是把已经搭建好的项目结构给照搬过来,所有的配置都是暴露出来的,并且可以根据实际情况去做一些自定义配置,更加灵活自由,并且可以直接接入已有项目,并带来本地开发环境和生产环境的收益。

目前sfx2.x版本主要支持以下几个功能:

  • sfx2 init <projectName> 初始化项目模板文件
  • sfx2 dev [address] 使用sfx内置的node服务搭建本地开发环境,且可传入自定义webpack配置
  • sfx2 build [tasks...] 使用sfx内置的webpack4进行打包构建,且可传入自定义webpack配置
  • sfx2 eslint [files...] 使用sfx内置的eslint进行代码规范校验,且可传入指定目录文件

  • sfx2 unit  [files...] 使用sfx内置的 karma 进行单元测试 ,且可传入指定目录文件

三、为什么我们要使用统一脚手架

1、 可以减少开发人员配置脚手架带来的时间损耗;

2、 统一项目结构,方便管理,降低项目交接时带来熟悉项目的时间;

3、 方便统一技术栈,预先引入固定的组件库;

4、 提高开发人员在多个项目之间的快速切换能力,提高项目可维护性,统一公司技术栈,避免因为环境不同导致奇怪的问题。

四、SFX项目结构

浅析SFX脚手架源码

整个项目的目录结构如上图所示,下面我大概介绍每个文件夹的东西大致都是干嘛的。

1、bin (存放 sfx入口文件 ,这里放的 sfx 的一些命令文件,比如 sfx  init 这样的命令都是从由这里控制的)

2、 docs (一些 sfx的 项目介绍和使用注意事项啥的)

3、eslint (存放 sfx 进行 eslint 代码校验的自定义方法)

3、 lib (这里存放着一些需要的一些自定义方法)

4、 node_modules (第三方node模块 )

5、template (存放sfx进行 init 模板初始化的自定义方法与项目结构的模板)

6、util (存放封装的 工具 函数)

7、webpack (存放 sfx 进行 dev 开启本地node开发环境, build 打包 构建生产环境, unit 进行单元测试的自定义方法和相关webpack配置文件)

8、 一些杂七杂八的东西 (比如eslint配置、.gitignore、LICENSE等等诸如此类这些东西,不影响我们阅读源码,可以直接忽略掉。)

9、 package.json/README.md  

五、分析源码

1、package.json

按常规来说,了解一个项目,首先看 package.json

{    
    "name": "@sxf/sfx2",   
    "version": "2.1.5",   
    "description": "sfx cli",    
    "main": "index.js",    
    "license": "MIT",    
    "files": [        
        "bin",        
        "eslint",        
        "lib",        
        "template",        
        "util",        
        "webpack"    
    ],    
    "keywords": [
        "sfx",        
        "sfx2"    
    ],    
    "bin": {        
        "sfx2": "bin/sfx2"    
    },    
    "homepage": "http://code.sangfor.org/UED/FE-COMMON/sfx",    
    "bugs": {        
        "url": "http://code.sangfor.org/UED/FE-COMMON/sfx/issues"    
    },    
    "author": {        
        "email": "43115@sangfor.com",        
        "name": "zhangyuantao"    
    },    
    "scripts": {        
        "lint": "eslint"    
    },    
    "dependencies": {        
        "@babel/core": "^7.1.2",        
        "@babel/plugin-transform-reserved-words": "^7.0.0",        
        "@babel/preset-env": "^7.1.0",        
        "@cgroup/eslint-plugin-sfchecklist": "http://code.sangfor.org/UED/FE-COMMON/eslint-plugin-sfchecklist/-/archive/v2.0.0/eslint-plugin-sfchecklist-v2.0.0.tar.gz",        
        "@sxf/eslint-plugin-sfchecklist": "^3.1.3",        
        "babel-eslint": "^10.0.1",        
        "babel-loader": "^8.0.4",        
        "babel-plugin-transform-es3-member-expression-literals": "^6.22.0",        
        "babel-plugin-transform-es3-property-literals": "^6.22.0",        
        "babel-plugin-transform-member-expression-literals": "^6.9.4",        
        "babel-plugin-transform-property-literals": "^6.9.4",       
        "babel-polyfill": "^6.26.0",        
        "cache-loader": "^1.2.2",        
        "chai": "^4.2.0",        
        "commander": "^2.19.0",        
        "copy-webpack-plugin": "^4.5.4",        
        "css-loader": "^1.0.0",        
        "eslint": "^5.7.0",        
        "eslint-friendly-formatter": "^4.0.1",        
        "eslint-loader": "^2.1.1",        
        "eslint-plugin-html": "^4.0.6",        
        "eslint-plugin-vue": "^5.0.0-beta.3",        
        "eventsource-polyfill": "^0.9.6",        
        "express": "^4.16.4",        
        "file-loader": "^2.0.0",        
        "happypack": "^5.0.0",        
        "html-webpack-plugin": "^4.0.0-beta.5",        
        "http-proxy": "^1.17.0",        
        "is-glob": "^4.0.0",        
        "istanbul": "^0.4.5",        
        "istanbul-instrumenter-loader": "^3.0.1",        
        "karma": "^3.1.1",        
        "karma-chai": "^0.1.0",        
        "karma-chrome-launcher": "^2.2.0",        
        "karma-coverage": "^1.1.2",        
        "karma-coverage-istanbul-reporter": "^2.0.4",        
        "karma-mocha": "^1.3.0",        
        "karma-mocha-reporter": "^2.2.5",        
        "karma-sourcemap-loader": "^0.3.7",        
        "karma-webpack": "^3.0.5",        
        "less": "^3.8.1",        
        "less-loader": "^4.1.0",        
        "lodash": "^4.17.11",        
        "log4js": "^3.0.6",        
        "micromatch": "^3.1.10",        
        "mini-css-extract-plugin": "^0.4.4",        
        "mocha": "^5.2.0",        
        "opn": "^5.4.0",        
        "ora": "^3.0.0",        
        "phantomjs-polyfill": "^0.0.2",        
        "postcss-initial": "^3.0.0",        
        "postcss-loader": "^3.0.0",        
        "sass-loader": "^7.1.0",        
        "scss-loader": "^0.0.1",        
        "shelljs": "^0.8.2",        
        "style-loader": "^0.23.1",        
        "stylus": "^0.54.5",        
        "stylus-loader": "^3.0.2",        
        "ts-loader": "^5.2.2",        
        "typescript": "^3.1.4",        
        "uglifyjs-webpack-plugin": "^2.0.1",        
        "underscore.template": "^0.1.7",        
        "url-loader": "^1.1.2",        
        "vue-hot-reload-api": "^2.3.1",        
        "vue-loader": "^15.4.2",        
        "vue-style-loader": "^4.1.2",        
        "vue-template-compiler": "^2.5.17",        
        "webpack": "^4.22.0",        
        "webpack-dev-middleware": "^3.4.0",        
        "webpack-hot-middleware": "^2.24.3",        
        "webpack-merge": "^4.1.4"    },    
    "devDependencies": {        
        "docsify": "3.7.2",        
        "karma-phantomjs-launcher": "^1.0.4"    
    }}复制代码

从上述代码中,我们可以看到在 package.json 提供一个映射到本地本地文件名的 bin 字段,一旦被引入后, npm 将软链接这个文件到prefix/bin里面,以便于全局引入,或者在./node_modules/.bin/目录里,也就是说在npm link执行之后,就能在全局使用sfx2这个指令来执行bin/sfx2文件了。

2、bin/sfx2

#!/usr/bin/env node"use strict";

let commander = require('commander');
let sfx2 = require('../index');
const PACKAGE_JSON = require('../package.json');

console.log(`Welcome to use sfx cli tool (version: ${PACKAGE_JSON.version}), use "sfx2 --help" show more options.`);


commander    
    .version(PACKAGE_JSON.version)    
    .usage('<command> [options]')    
    .option('-n, --nolint', 'remove eslint')    
    .option('-c, --config [file]', 'sfx.config.js file')    
    .description(`        
        For Example:            
        sfx init vue            
        sfx build --nolint            
        sfx dev --config=custom.sfx.config.js            
        sfx eslint    
    `);

commander    
    .command('init <template>')    
    .option('-i, --install', 'auto run yarn install')       
    .option('-b, --build', 'auto run sfx build')    
    .description('init folder like yeoman')    
    .action((template, options) => {        
        sfx2.init(template, {            
            install: options.install,            
            build: options.build        
        });    
    });

commander    
    .command('build [tasks...]')    
    .description('build for production. [thirdParts, project]')    
    .action((tasks) => {        
        parseOption(commander);        
        sfx2.build(tasks).catch(err => {            
            console.error(err);            
            process.exit(-1);        
        });    
    });

commander    
    .command('dev [address]')    
    .description('run a local server for debug')    
    .action(address => {        
        parseOption(commander);        
        sfx2.dev(address);    
    });

commander    
    .command('eslint [files...]')    
    .description('run eslint')    
    .action((files) => {        
        let ret = sfx2.eslint(files);        
        if (ret === false) {            
            process.exit(-1);        
        }    
    });

commander    
    .command('unit [files...]')    
    .option('-s, --single', 'single run')    
    .option('-e, --auto-exit', 'auto exit when karma completed')    
    .description('run unit test task')    
    .action((files, options) => {        
        parseOption(commander);        
        sfx2.unit(files, {            
            single: options.single,            
            autoExit: options.autoExit        
        });    
    });

commander.parse(process.argv);

function parseOption (commander) {
    if (commander.nolint) {        
        process.env.COMMANDER_OPTION_NOLINT = !!commander.nolint;    
    }    
    if (commander.config) {        
        process.env.COMMANDER_OPTION_SFX_FILE = commander.config;    
    }
}复制代码

文件第一行代码 #!/usr/bin/env node"use strict" 是为了告诉系统,指定使用node执行该脚本文件。

在开始阅读上述源码之前,首先我要了解一个工具( commander ),它是node.js命令行界面的完整解决方案,可以在通过解析sfx2后面的命令执行不同操作(函数),并且可以给对应命令设置参数、参数的简写和命令描述等等。

在了解commander的使用方法之后,就可以知道脚手架到底是如何执行的了。

3、template/init

  • 流程图

浅析SFX脚手架源码

  • 依赖模块

浅析SFX脚手架源码

  • run函数(每个命令都有一个对应的run函数作为该命令的入口函数)

浅析SFX脚手架源码

  • 首先执行的是 checkEnv 方法:

浅析SFX脚手架源码

浅析SFX脚手架源码

  • 从注释可以看出该方法是用来检测当前目录是否为空,使用node的fs.readdirSync同步获取一个包含“指定目录下所有文件名称”的数组对象,再通过process.cwd获取Node.js 进程的当前工作目录。如果当前目录下不为空,则给出提示并退出进程process.exit(1)。
  • 执行完checkEnv方法之后,进入异步控制流程,执行 stdIn 函数

浅析SFX脚手架源码

  • 上述代码中的 prompt 方法是通过 process.stdout process. stdin 实现询问框的输出和输入
  • 通过用户的输入项目的基本信息之后,返回项目基本配置信息,根据配置信息中的模板名称,开始拷贝 template 目录下的模板目录文件

浅析SFX脚手架源码

  • src为sfx项目中初始化的模板项目文件路径
  • dist为当前sfx运行在哪个工作目录,也就是我们要将模板文件copy到的指定路径
  • option为之前用户填入的项目信息

执行 _copy 方法

浅析SFX脚手架源码

  • existsSync:以同步的方法检测目录是否存在
  • statSync(src).isDirectory : 以同步的方式判断当前路径是否是一个目录
  • mkdirSync:以同步的方式创建目录
  • readdirSync:  返回一个包含“指定目录下所有文件名称”的数组对象
  • writeFileSync:  以同步的方式将数据写入文件,文件已存在的情况下,原内容将被替换

这个方法先后做以下几件事情:

  • 判断模板源目录是否存在,不存在则给对应信息,并退出当前进程,此处应将return修改为process.exit(1),否则后续的run主函数中的代码依旧会执行。
  • 使用递归来遍历源目录来实现整个源目录拷贝到目标路径

4、webpack/dev/index (后续更新)

5、webpack/third_part/index  (后续更新)

6、webpack/prod/index (后续更新)

7、eslint/lint (后续更新)

8、webpack/test/index (后续更新)

六、对比其他脚手架

对比omi-cli,sfx可以直接接入已有的项目来给项目带来开发环境和生产环境的收益,而omi-cli则是注重的是为开发者更快的进入一个新项目的开发。

七、可优化的部分

1、在 webpack.config.base.js 中的多进程压缩配置 UglifyJsPlugin中的 parallel选项设置为true默认开启 require('os').cpus().length - 1 个并发数来进行压缩,可将其设置为 require('os').cpus().length 来增加一个并发数来提高压缩的效率。

2、 sfx init 的流程可以做优化

浅析SFX脚手架源码

  • 这个流程减少了用户自己去创建文件夹并进入文件夹的操作
  • 在指定模板的时候应该根据已有的模板去给用户选择,如下图: 浅析SFX脚手架源码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Data Structures and Algorithms

Data Structures and Algorithms

Alfred V. Aho、Jeffrey D. Ullman、John E. Hopcroft / Addison Wesley / 1983-1-11 / USD 74.20

The authors' treatment of data structures in Data Structures and Algorithms is unified by an informal notion of "abstract data types," allowing readers to compare different implementations of the same......一起来看看 《Data Structures and Algorithms》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

html转js在线工具
html转js在线工具

html转js在线工具