工程化概述
1、工程化的定义和主要解决的问题
-
前端工程化,全副武装:通过工程化提升【战斗力】
-
面临的问题:
- 想要使用ES6+ 新特性,但是兼容有问题
- 想要使用Less/Sass/PostCSS增强CSS的编程性,但是运行环境不能直接支持
- 想要使用模块化的方式提高项目的可维护性,但运行环境不能直接支持
- 部署上线前需要手动压缩代码及资源文件,部署过程需要手动上传代码到服务器
- 多人协作开发,无法硬性统一大家的代码风格,从仓库中pull回来的代码质量无法保证
- 部分功能开发时需要等待后端服务器接口提前完成
-
主要解决的问题
- 传统语言或语法的弊端
- 无法使用模块化/组件化
- 重复的机械式工作
- 代码风格统一、质量保证
- 依赖后端服务接口支持
- 整体依赖后端项目
2、一个项目过程中工程化的表现
-
工程化表现:一切以提高效率、降低成本、质量保证为目的的手段都属于【工程化】
-
一切重复的工作都应该被自动化: 创建项目 -> 编码 -> 预览/测试 -> 提交 -> 部署
-
工程化 =/= 工具
脚手架工具
1、脚手架工具概要:前端工程化的发起者
-
脚手架的本质作用:创建项目基础结构、提供项目规范和约定
- 相同的组织结构
- 相同的开发范式
- 相同的模块依赖
- 相同的工具配置
- 相同的基础代码
-
举个例子:IDE 创建项目的过程就是一个脚手架的工作流程
-
前端脚手架
-
内容概要
- 脚手架的作用
- 常用的脚手架工具
- 通用脚手架工具剖析
- 开发一款脚手架
2、常用的脚手架工具
-
React项目 --> creat-react-app
-
Vue.js项目 --> vue-cli
-
Angular项目 --> angular-cli
-
根据信息创建对应的项目基础结构
-
Yeoman(通用脚手架工具,根据模板生成对应的项目结构,灵活,扩展)(最老牌、最强大、最通用)(用于创造现代化应用的脚手架工具)(脚手架运行平台)
-
Plop(开发过程中用于创建特定类型的文件,例如创建一个组件/模块所需要的文件)
-
重点关注几个有代表性的工具
3、Yeoman:The web`s scaffolding tool for modern webapps
-
$ yo webapp --> webapp generator --> web app structure
-
$ yo chrome-extension --> chrome-extension generator --> chrome extension structure
-
$ yo node --> node genertor --> node module structure
-
优点和缺点过于通用,不够专注
4、Yeoman 基础使用
-
步骤
- node -v //v10.15.0
- npm -v //6.4.1
- yarn -v //1.22.4,视频中使用它和npm 类似
- yarn global add yo //yarn global v1.22.4
- //使用generator-node
- yarn global add generator-node //yarn global v1.22.4
- d: //选择d盘
- cd zce //视频中切换的目录
- cd Desktop\ //视频中切换的目录
- mkdir my-module //创建一个新的文件夹
- cd my-module\ //选择这个目录下
- yo node //运行刚刚安装的generator-node生成器
- //提出问题了,[Module Name]你的模块叫什么名字? 输入my-module
- //提出问题了,[The name above already exists on npm, choose another?]告诉你你的模块已经在npm上已经存在了,是否要选择另外一个,你的生成器自动检查了你的包名是否可用? 选择Yes
- //选择新的包名 提出问题了,[Module Name]你的模块叫什么名字? 输入my-module
- //提出问题了,[The name above already exists on npm, choose another?]告诉你你的模块已经在npm上已经存在了,是否要选择另外一个,你的生成器自动检查了你的包名是否可用? 选择No 用于演示
- //项目主页github地址 github.com/tjh/my-modu…
- //项目作者的名字 tjh
- //邮箱(w@zce.me)
- //主页(zce.me)
- //模块的一些关键词 module, node
- //是否发送代码覆盖率的报告到一个平台上 这里先不使用 No
- //node版本 默认是全部
- //github的username(zce)
- //license MIT
- //生成中。。。
- //回车结束脚手架流程
-
总结
- 在全局范围安装yo
$ npm install yo --global # or yarn global add yo
- 安装对应的generator
$ npm install generator-node --global # or yarn global add generator-node
- 通过yo运行generator
mkdir my-module $ yo node
5、Sub Generator
- 项目终端启用
- yo node:cli
- //是否重写package.json overwrite
- yarn link //到全局范围
- my-module --help //
- yarn install v1.17.3 //yarn安装依赖
- my-module --help //generator子集的generator它的特性,去官方文档看看确认下有没有子集生成器
6、Yeoman 使用步骤总结
1.明确你的需求; 2.找到合适的Generator; 3.全局范围安装找到的Generator; 4.通过Yo运行对应的Generator; 5.通过命令行交互填写选项; 6.生成你所需要的项目结构;
- yeoman.io/generators/
- 例如:网站,webapp
- 项目终端:yarn global add generator-webapp
- yo webapp
- //配置其他
- 例如:网站,webapp
6、自定义Generator(基于Yeoman搭建自己的脚手架)
- 创建Generator模块(Generator本质上就是一个NPM模块)
.
├── generator/ ········································· 生成器目录
│ └── app/ ·········································· 默认生成器目录
│ └── index.js ··································· 默认生成器实现
└── package.json........................................ 模块包配置文件
.
├── generator/ ········································· 生成器目录
│ ├── app/ ·········································· 默认生成器目录
│ | └── index.js ··································· 默认生成器实现
| └── component/ ····································· 其他生成器实现
| └── index.js ··································· 其他生成器实现
└── package.json........................................ 模块包配置文件
-
generator-
-
mkdir generator-sample //创建文件夹目录
-
yarn init //package.json
-
yarn add yeoman-generator //提供生成器的一个基类,提供工具函数,创建生成器的时候更加便捷
-
//打开vscode 创建文件夹generators/app/index.js //此文件作为Generator 的核心入口,需要导出一个继承自 Yeoman Generator 的类型,Yeoman Generator 在工作时会自动调用我们在此类型中定义的一些生命周期方法,我们在这些方法中可以通过调用父类提供的一些工具方法实现一些功能,例如:文件写入
const Generator = require('yeoman-generator')
module.exports = class extends Gerator { writing () { // Yeoman 自动在生成文件阶段调用此方法 // 我们这里尝试往项目目录中写入文件 this.fs.write( this.dextinationPath('temp.txt'), Math.random().toString() ) } }
-
yarn link //全局模块包
-
cd ..
-
mkdir my-proj\
-
yo sample //创建了一个桌面的my-proj文件夹下面temp的txt文件
-
//打开temp.txt这个文件就是刚刚写入的一个随机数
7、根据模板创建文件
//app下面创建一个templates文件夹,templates文件夹下面创建一个foo.txt文件
//foo.txt这是一个模板文件,内部可以使用EJS模板标记输出数据
//例如:<%= title %>
//其它的 EJS 语法也支持
<% if (success) { %>
哈哈哈
<% } %>
const Generator = require('yeoman-generator')
module.exports = class extends Gerator {
writing () {
//通过模板方式写入文件到目录
//模板文件路径
const tmpl = this.templatePath('foo.txt')
//输出目标路径
const output = this.desinationPath('foo.txt')
//模板数据上下文
const context = {title: 'Hello tjh~', success: false}
this.fs.copyTpl(tmpl, output, context) //自动的把我们的模板文件映射到生成到输出文件上
}
}
//打开项目终端D:\zce\Desktop\my-proj
- yo sample
相对于手动创建每一个文件,模板的方式大大提高了效率(特别是在文件比较多,比较复杂的情况下)
8、接收用户输入
const Generator = require('yeoman-generator')
module.exports = class extends Gerator {
prompting () {
//Yeoman 在询问用户环节会自动调用此方法
//在此方法中可以调用父类的 prompt() 方法发出对用户的命令行询问
return this.prompt({
type: 'input',
name: 'name',
message: 'Your project name',
default: this.appname //appname 为项目生成目录名称
})
.then(answers => {
//answers => {name: 'user input value'}
this.answers = answers
})
}
writing () {
//模板文件路径
const tmpl = this.templatePath('foo.txt')
//输出目标路径
const output = this.desinationPath('foo.txt')
//模板数据上下文
const context = {title: 'Hello tjh~', success: false}
this.fs.copyTpl(tmpl, output, context) //自动的把我们的模板文件映射到生成到输出文件上
}
}
- yo sample
//提示Your project name my-project
//在Yeoman中如何动态的去接收用户输入数据的一种显示方式
9、Vue Generator 案例
-
mkdir generator-zce-vue
-
cd generator-zce-vue\
-
yarn init //package.json
-
yarn add yeoman-generator //依赖 //项目中建立generator/app/index.js const Generator = require('yeoman-generator') //通过require载入yeoman这样的一个基类
module.exports = class extends Gerator { //导出一个类型继承generator prompting () { return this.prompt([ { type: 'input', name: 'name', message: 'Your project name', default: this.appname //appname 为项目生成目录名称 } ]) .then(answers => { this.answers = answers }) } //以命令行交互的方式
//批量结构批量生产 //app 文件夹下创建templates文件夹 //把my-vue-project 下面的所有拷贝到templates中,作为模板 //README.md package.json index.js 中关于名称的,全部替换为EJS <%= name %> writing () { //把每一个文件都通过模板转换到目标路径,用循环的方式批量生产每一个文件 const templates = [ //templates文件夹下的每一个文件的路径 '.browserslistrc', '.editorconfig', '.env.development', '.env.production', ... 'src/store/state.js', 'src/utils.request.js', 'src/views/About.vue', 'src/view/Home.vue', ] templates.forEach(item => { //item => 每个文件路径 this.fs.copyTpl( this.templatePath(item), this.destinationPath(item), this.answers ) }) } - yarn link //link到全局 - mkdir my-proj //定位到全新的目录通过yorman去运行刚刚所创建的这样一个generator - cd my-proj\ - dir - yo zce-vue //输入项目名称如果报错看数组中是否有yarn.lock project-name //EJS的模板标记在index.js文件中<%%(多加一个%转义)= BASE_URL %> - cls - yo zce-vue //重新生成对应新的项目 //输入项目名称 proj-vue - dir - code . //需要被替换的数据已经被替换掉了 //原始的项目结构就可以被复用起来了 //后续可以自己加上更多选项使脚手架更加灵活,更加通用}
10、发布 Generator (相当于发布npm的模块)
将项目源代码托管到一个公开的源代码仓库
先创建一个本地的仓库
- echo node_modules > .gitignore 忽略node_modules这样一个目录
- git init 初始化本地空仓库
- git status 本地仓库的一个状态
- git add . 当前目录下所有的文件
- git commit -m "feat: initial commit" 创建完提交
本地的日志提交到github
在github上创建新的仓库,库名 generator-zce-vue
把远端仓库的地址
- git remote add origin https://github.com/zce/generator-zce-vue.git 为本地仓库添加一个远端仓库的别名
- git push -u origin master push的时候就可以使用这个别名,本地的分子master推送到远端的master
已经创建完仓库
开始发布
npm或者yarn
- yarn publish
提示是否要给package.json的版本做一个修改,直接回车不做任何的修改
第一会让你输入用户名和密码,以后直接输入密码
提示错,国内用淘宝的镜像取代官方的镜像,修改镜像配置或者publish跟上一个registry的一个参数
- yarn publish --registry-https://registry.yarnpkg.com
去网址看看 npmjs.com/package/generator-zce-vue 这一模块就可以通过npm全局yarn按钮,再通过Yorman去使用它
注意:要使你的generator-zce-vue在官方列表也会出现的话,给你的项目添加Yorman-generator关键词,这样Yorman的官方就会发现它了
11、Plop(一个小而美的脚手架工具)
- 一般plop集成到项目当中,用来自动化的去创建同类型的项目文件
- 平常面临的问题,开发过程中,经常重复创建同类型的文件
- 原始的方法:创建一个新的文件夹(组件),文件下面创建N个文件(.css .js .test.js)
- plop的方法:终端输入命令行,yarn plop component 这个例子中components是父级文件夹
- 优点:大大提高项目中重复创建文件夹及文件的效率
12、Plop的具体使用
//首先,将plop作为一个npm的模块安装到我们的开发依赖中
-
yarn add plop --dev
-
//项目根目录中创建一个文件plopfile.js,这个文件是plop 入口文件,需要导出一个函数;此函数接收一个plop 对象,这个对象提供了一系列的工具函数,用于创建生成器任务;
module.exports = plpo => { //导出怎样一个函数 plop.setGenerator('component',{ //函数中接收一个plop形式参数; setGenerator接收两个参数,一个参数是生成器的名字,第二个是生成器的一些配置选项
description: 'create a component', //配置选项中指定一下这个生成器的描述 prompts:[ //generator指定generator工作的时候会发出的命令行问题 { type: 'input', //type去指定输入方式 name: 'name', //name去指定返回值的一个键 message: 'component name', //message屏幕中给定的一个提示 default: 'MyComponent' //default命令的默认答案 } ], actions: [ //生成器在完成交互命令之后,需要执行的一些动作;他可以是一个数组,每个对象就是一个动作对象 { type: 'add', //type指定动作的类型;add代表添加一个全新的文件 path: 'src/components.{{name}}/{{name.js}}', //path属性指定需要添加的文件会被添加到哪个具体的路径;双花括号这种插值表达式的方式去插入我们刚刚在命令行交互中得到的数据;这里的name就是上面prompts中的name的取值 templateFile: 'plop-templates/component.hbs' //指定本次添加文件的模板文件是什么 //在根目录中创建plop-templates/component.hbs //component.hbs在这个文件中 - import React form 'react'; //可以通过hbs模板引擎的方式去创建一些模板文件 - export default () => ( //这些模板都是遵循hbs的模板语法 <div className="{{}}"></div> ) } ] })}
-
yarn plop component //(生成器的名字)
-
Footer //新文件的创建
-
总结:
- 将plop模块作为项目开发依赖安装
- 在项目根目录下创建一个plopfile.js文件
- 在plopfile.js文件中定义脚手架任务
- 编写用于生成特定类型文件的模板
- 通过plop提供的CLI运行脚手架任务
13、脚手架的工作原理
-
mkdir sample-scaffolding //通过mkdir创建一个项目目录
-
cd sample-scaffolding\
-
yarn init //初始化package.json的文件
-
code . //通过vscode打开这个目录 //在package.json中添加bin字段,用于cri的入口文件 //"bin": "cli.js", //项目中创建cli.js #!/usr/bin/env node //Node CLI 应用入口文件必须要有这样的文件头 //如果是 Linux 或者 macOS 系统下还需要修改此文件的读写权限为 755 //具体就是通过 chmod 755 cli.js 实现修改
- console.log("cli wworking!")
-
yarn link //把这一模块link到全局
-
sample-scaffolding //使用命令执行console.log //脚手架的工作过程: //1、通过命令行交互询问用户问题 //2、根据用户回答的结果生成文件
-
yarn add inquirer //安装inquirer这个模块 //在代码中载入它
-
const fs = require('fs')
-
const path = require('path')
-
const inquirer = require('inquirer')
-
const ejs = require('ejs')
-
inquirer.prompt([ //用于发起命令行的询问,接收一个数组参数 { type: 'input', //指定问题的输入方式 name: 'name', //指定返回值的那个键 message: 'Project name?' //指定屏幕上给用户的一个提示
} ]) .then(anwsers => { //console.log(anwsers)
//模板目录 const tmplDir = path.join(_dirname, 'templates') //目标目录 const destDir = process.cwd()
//将木半夏的文件全部转换到目标目录 fs.readdir(tmplDir, (err, files) => { if (err) throw err files.forEach(file => { // console.log(file) //模板引擎渲染文件 ejs.renderFile(path.join(tmplDir, file), anwsers, (err, result) => { if (err) throw err
console.log(result) //将结果写入的方式写入到目标目录 fs.writeFileSync(path.join(destDir, file), result) }) })})
}) //接收到的用户的答案
-
-
sample-scaffolding //使用命令执行console.log //输入名称 hello
//根目录新建templates文件夹,下面新建index.html文件 //html:5 在<%= name %>
//templates文件夹下面新建style.css文件
- yarn add ejs //安装ejs 的模板引擎
//清空命令行
- cls
- cd .. //定位到全新的目录
- mkdir demo
- cd demo
- sample-scaffolding //名称输入bar