一. 工程化
1. 主要解决的问题
- 传统语言或语法的弊端
- 无法使用模块化 / 组件化
- 重复的机械式工作
- 代码风格统一,质量保证
- 依赖后端服务接口支持
- 整体依赖后端项目
2. 工程化的表现
- 一切以提高效率,降低成本,质量保证为目的的手段都属于工程化
二. 脚手架工具
1. 概念
- 本质作用:创建项目的基础结构,提供项目规范和约定
2. 常用的脚手架工具
-
针对某个框架的脚手架
- create-react-app
- vue-cli
- angular-cli
-
通用型脚手架
- yeoman
-
基础使用
- 安装 yeoman,yarn global add yo
- 开发 node 项目
- 安装 generator-node 模块,yarn global add generator-node
- 创建目录,mkdir my-module
- 进入目录,cd my-module
- 创建 node 目录结构,yo node
- sub generator
- 生成 cli 应用
- yo node:cli
- package.json 中出现一个 bin: lib/cli.js 的配置
- 项目根目录生成 lib 目录
- 生成 cli 应用
-
yarn link 模块名 将模块链接到全局
-
yarn unlink 模块名 将模块从全局移除
-
使用步骤
- 明确需求
- 找到合适的 Generator
- 全局范围安装找到的 Generator
- 通过 yo 运行对应的 Generator
- 通过命令行交互填写选项
- 生成所需要的项目结构
-
自定义 Generator
-
创建 Generator 模块
-
基础结构
|- generators ---------------------- 生成器目录 |- app ------------------------- 默认生成器目录 |- index.js ---------------- 默认生成器实现 |- components ------------------ 其他生成器目录 |- index.js ---------------- 其他生成器实现 |- package.json -------------------- 模块包配置文件 -
步骤
-
创建模块目录,mkdir generator-sample,注意:模块必须是 generator- 的模式,否则 yeoman 找不到
-
进入模块目录,cd generator-sample
-
初始化 package.json 文件
-
安装 generator 的基类,yarn add yeoman-generator
-
创建基础目录
-
编写代码
/** * 此文件作为 Generator 的核心入口 * 需要导出一个继承自 Yeoman Generator 的类型 * Yeoman Generator 在工作时会自动调用在此类型中定义的一些生命周期方法 * 在这些方法中通过调用父类提供的一些工具方法,例如文件写入 */ const Generator = require('yeoman-generator') module.exports = class extends Generator { // yeoman 会在生成文件时自动调用 writing 方法 writing() { this.fs.write( // 通过 this.destinationPath() 获取绝对路径 this.destinationPath('temp.txt'), Math.random().toString() ) } }
-
-
根据模板创建文件
-
模板文件 foo.txt,模板文件创建在 app/templates 中
这是一个模板 内部可以使用 EJS 模板标记输出数据 例如 <%= title%> 其他的 EJS 语法也支持 <% if (success) { %> 我是判断逻辑 <% } %> -
模板生成逻辑
/** * 此文件作为 Generator 的核心入口 * 需要导出一个继承自 Yeoman Generator 的类型 * Yeoman Generator 再工作时会自动调用在此类型中定义的一些生命周期方法 * 在这些方法中通过调用父类提供的一些工具方法,例如文件写入 */ const Generator = require('yeoman-generator') module.exports = class extends Generator { // yeoman 会在生成文件时自动调用 writing 方法 writing() { // 模板文件路径 const temp = this.templatePath('foo.txt') // 输出路径 const dest = this.destinationPath('foo.txt') // 模板上下文 const context = { title: 'Hello kjy', success: false } // 自动 copy context 输出到输出文件上 this.fs.copyTpl(temp, dest, context) } } -
用户输入
-
模板文件 bar.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><%= name%></title> </head> <body> <h1><%= name%></h1> </body> </html> -
模板生成逻辑
/** * 此文件作为 Generator 的核心入口 * 需要导出一个继承自 Yeoman Generator 的类型 * Yeoman Generator 再工作时会自动调用在此类型中定义的一些生命周期方法 * 在这些方法中通过调用父类提供的一些工具方法,例如文件写入 */ const Generator = require('yeoman-generator') module.exports = class extends Generator { // 命令行交互 prompting () { // 通过 this.prompt 方法发出对用户命令行询问 // 这个方法返回一个 Promise // 加入 return,有更好的异步流程体验 return this.prompt([ { type: 'input', name: 'name', message: 'Your projsct name', default: this.appname // appname 为项目生成目录名称 } ]) .then(answers => { // 接受完用户输入的结果 this.answers = answers }) } // yeoman 会在生成文件时自动调用 writing 方法 writing() { // 模板文件路径 const temp = this.templatePath('bar.html') // 输出路径 const dest = this.destinationPath('bar.html') // 模板上下文 const context = this.answers // 自动 copy context 输出到输出文件上 this.fs.copyTpl(temp, dest, context) } }
-
-
发布 Generator
- 创建一个远端仓库
- echo node_modules > .gitignore
- git init
- git add .
- git commit -m '提交代码'
- github 上创建仓库
- git remote add origin 远端仓库地址
- git push -u origin master
- 通过 npm publish 或 yarn publish 发布模块
- 注:yarn 的是国外的,需要修改镜像配置,需要使用官方的镜像
- 创建一个远端仓库
-
-
-
- yeoman
-
脚手架工作原理
-
自定义脚手架
-
初始化 package.json 文件
-
在 package.json 文件中添加 bin 属性,"bin":"cli.js"
-
项目根目录新建 cli.js
#!/usr/bin.env node // 脚手架的工作过程 // 1. 通过命令行交互的方式询问用户问题 // 2. 根据用户回答的问题生成文件 const inquirer = require('inquirer') const path = require('path') const fs = require('fs') const ejs = require('ejs') inquirer.prompt([ { type: 'input', name: 'name', message: 'Project name', } ]) .then(answers => { // 模板路径 const temp = path.join(__dirname, 'templates') // 目标目录 const dest = process.cwd() // 将模板下的文件全部转换到目标目录 fs.readdir(temp, (err, files) => { if (err) throw err files.forEach(file => { // 通过模板引擎渲染文件 ejs.renderFile(path.join(temp, file), answers, (err, result) => { if(err) throw err fs.writeFileSync(path.join(dest, file), result) }) }) }) })
-
-