文章内容输出来源:拉勾教育 大前端高薪训练营
定义
前端工程化是指遵循一定的标准和规范,通过工具去提高效率,降低成本的一种手段。
面临的问题
1、想要使用 ES6 + 新特性,但是兼容有问题
2、想要使用 Less / Sass / PostCSS 增强 CSS 的编程性,但是运行环境不能直接支持
3、想要使用模块化的方式提高项目的可维护性,但运行环境不能直接支持
4、部署上线前需要手动压缩代码及资源文件,部署过程需要手动上传代码到服务器
5、多人协作开发,无法硬性统一大家的代码风格,从仓库中 pull 回来的代码质量无法保证
6、部分功能开发时,需要等待后端服务接口提前完成
主要解决的问题
1、传统语言或语法的弊端
2、无法使用模块化/组件化
3、重复的机械式工作
4、代码风格统一、质量保证
5、依赖后端服务接口支持
6、整体依赖后端项目
一切以提高效率、降低成本、质量保证为目的的手段都属于工程化。
一切重复的工作都应该被自动化。实现前端工程化可以从 模块化、组件化、规范化、自动化等方面出发。
2,工程化不等于工具
一些成熟的工程化集成:create-react-app、vue-cli、angular-cli、gatsby-cli。
简单来说,脚手架就是用来自动的去帮我们创建项目基础文件的一个工具。脚手架工具,可以认为是前端工程化的发起者。
作用
创建项目基础结构、提供项目规范和约定。
一般来说,一个项目中包含一些相同的约定,即:
1)相同的组织结构
2)相同的开发范式
3)相同的模块依赖
4)相同的工具配置
5)相同的基础代码
我们可以通过脚手架工具搭建一个特定的项目骨架,然后基于这个骨架去进行相关的开发工作。但是,前端脚手架是以独立的工具存在,不会集成在IDE中。
1,全局安装,可以使用 yarn / npm / cnpm 命令
$ yarn global add yo # or npm install yo --global
2,Yeoman 需要搭配特定的 Generator 使用,在这里使用的node_module,因此还需要全局安装 generator-node
$ yarn global add generator-node # or npm install generator-node --global
注意
如果出现无法识别 yo 命令的问题,请查看yarn的配置,里面详细讲述了解决方案。
1,明确你的需求;
2,找到合适的 Generator;
3,全局范围安装找到的 Generator;
4,通过 Yo 运行对应的 Generator;
5,通过命令行交互填写选项;
6,生成你所需要的项目结构;
使用 Yeoman 提供的 yo 命令,去运行 generator-node 生成器。运行特定的生成器,就是将 generator- 前缀去掉,直接使用 yo 运行后面的部分即可。
$ mkdir project-dir-name
$ cd path/to/project-dir
$ yo node
运行结果,如下图所示:
在已有的项目结构中,创建一些特定类型的文件,可以使用 Yeoman 提供的 Sub Generator 进行实现。
1,使用 generator-node中的子集的生成器,即cli生成器,它可以生成cli所需要的一些文件。
命令代码如下:
$ cd path/to/project-dir
$ yo node:cli # or yo + generator的名字 + : + Sub Generator的名字
运行结果,如下图所示:
2,通过以下命令,将这个模块连接到全局范围,使其可以作为全局的命令行模块进行使用。
$ yarn link # or npm link
可以看到,此时会将新的模块生成的依赖包,存放到 Yarn 应用程序的本地数据文件夹中。
3,最后,需要安装项目依赖。
$ yarn
注意
每个用户的bin目录不同,cmd命令存放不同,因此,配置的环境变量路径也不同。
查看生成的 cmd 脚本文件,如下图所示,无法找到 link 后的模块。
解决方案,详见【https://blog.csdn.net/zimeng303/article/details/109736592】
4,全局访问生成的模块
$ module-name --help
运行结果,如下图所示:
并不是每一个 generator 都存在子集的生成器,需要以官方文档为准。
基于 Yeoman 搭建自己的脚手架,Generator 本质上就是一个 NPM 模块。
generator- name
注意
如果命名不规范,那么后期 Yeoman 就无法找到所定义的 生成器模块。
1,创建 Generator 的文件夹,命名要符合命名规范,如 generator-sample
$ mkdir generator-sample
2,使用 yarn 或者 npm ,创建并初始化 package.json 文件
$ cd generator-sample
$ yarn init # or npm init
3,安装 yeoman-generator 的模块,此模块提供生成器的基类,此基类又提供了工具函数,可以使在创建 Generator 时更加便捷。
$ yarn add yeoman-generator # or npm install yeoman-generator
4,生成器的入口文件 index.js 所在目录结构,如下图所示:
5,入口文件 index.js ,需要导出一个继承自 Yeoman Generator 的类型。
代码示例如下:
// index.js : 此文件作为 Generator 的核心入口
// 引入 yeoman-generator,以便可以使用其内部的基类,从而使用其工具函数...
const Generator = require('yeoman-generator')
// 导出一个继承自 Yeoman Generator 的类型
module.exports = class extends Generator {
writing () {
// Yeoman 自动在生成文件阶段调用此方法
// 我们这里尝试往项目目录中写入文件
this.fs.write( // 通过父类中的fs模块进行写入,与 node中的不一样
// 写入文件的绝对路径
this.destinationPath('temp.txt'), // destinationPath 自动获取,生成项目目录下对应的文件路径
Math.random().toString() // 写入文件内容
)
}
}
可以看到,Yeoman Generator 在工作时,会自动调用我们在此类型中定义的一些生命周期方法,我们在这些方法中可以通过调用父类提供的一些工具方法,去实现一些功能,例如文件导入等。
6,通过 yarn link 或者 npm link 的方式,将自定义的 Generator 模块,链接到全局范围,使之成为一个全局模块包。
$ yarn link # or npm link
我的 yarn 本地链接到的是在 C盘,如下图:
7,新建项目目录,进入目录,执行 yeoman 操作
$ cd path/to/project-dir
$ yo sample
注意
由于我的 yarn 本地链接到的是在 C盘,直接使用上面的操作会报出如下错误:
一, 将 link的 generator-sample拷贝到yarn的全局安装目录中,如下图所示:
**二,**在执行上述操作之前,先将 generator-sample 链接到当前文件夹下,命令如下:
$ cd path/to/project-dir
$ yarn link generator-sample
$ yo sample
**三,**使用命令,将其在全局范围内安装
$ npm install --force -g generator-sample
通过上面的操作,便可以使 Yeoman 找到这个 generator-sample 依赖包了。
个人推荐第二种或第三种
运行结果,如下图所示:
根据模板创建文件,相对于手动创建每一个文件,模板的方式大大提高了效率。
1,模板文件内部可以使用 EJS 模板标记输出数据,如:
<%= title %>
其他的 EJS 语法也支持,如:
<% if (success) {
%>
哈哈哈
<% } %>
2,使用模板文件代替直接写入的方式
代码示例如下(index.js):
module.exports = class extends Generator {
writing () {
// Yeoman 自动在生成文件阶段调用此方法
// 通过模板方式写入文件到目标目录
// 模板文件路径
const tmpl = this.templatePath('foo.txt')
// 输出目标文件
const output = this.destinationPath('foo.txt')
// 输出数据上下文
const context = {
title: 'Hello zce~', success: false }
this.fs.copyTpl(tmpl, output, context)
}
}
3,动态接收用户输入数据
代码示例如下(index.js):
const Generator = require('yeoman-generator')
module.exports = class extends Generator {
prompting () {
// Yeoman 在询问用户环节会自动调用此方法
// 在此方法中可以调用父类的 prompt() 方法发出对用户的命令行询问
// 接收一个数组参数,数组的每一项都是一个问题对象
return this.prompt([ // 返回一个 Promise对象
{
type: 'input', // 使用用户输入的方式,让用户提交信息
name: 'name', // 最终得到结果的一个键
message: 'Your project name', // 在界面中给用户的提示,即问题
default: this.appname // appname 为项目生成目录名称
}
]).then(answers => {
//用户输入之后的结果
// answers => { name:'user input value' }
this.answers = answers // 便于writing时使用
})
}
writing () {
// Yeoman 自动在生成文件阶段调用此方法
// 通过模板方式写入文件到目标目录
// 模板文件路径
const tmpl = this.templatePath('bar.html')
// 输出目标文件
const output = this.destinationPath('bar.html')
// 输出数据上下文
// 此时使用用户输入数据,作为模板文件的上下文
const context = this.answers
this.fs.copyTpl(tmpl, output, context)
}
}
在 templates 目录下面新建 bar.html,并使用 ESJ 模板引擎的写法。
代码示例如下:
1,创建 generator-vue文件夹,并创建及初始化 package.json 包管理文件,以及安装 yeoman-generator 模块
$ mkdir generator-vue
$ cd generator-vue
$ yarn init # or npm init
$ yarn add yeoman-generator # or npm install yeoman-generator -D
2,将 vue中生成的目录结构,拷贝到 Generator 中,使其作为模板文件存在,如下图所示:
3,根据自定义 Generator 步骤,在 index.js 入口文件中,添加 prompting() 方法和writing()方法,prompting() 方法代码如上,此处只书写 writing() 方法。
代码示例如下:
const Generator = require('yeoman-generator')
module.exports = class extends Generator {
prompting() {
// ... , 代码同上
}
writing() {
// 把每一个文件都通过模板转换到目标路径
const templates = [
'.browserslistrc',
'.editorconfig',
'.env.development',
'.env.production',
'.eslintrc.js',
'.gitignore',
'babel.config.js',
'package.json',
'postcss.config.js',
'README.md',
'public/favicon.ico',
'public/index.html',
'src/App.vue',
'src/main.js',
'src/router.js',
'src/assets/logo.png',
'src/components/HelloWorld.vue',
'src/store/actions.js',
'src/store/getters.js',
'src/store/index.js',
'src/store/mutations.js',
'src/store/state.js',
'src/utils/request.js',
'src/views/About.vue',
'src/views/Home.vue'
]
templates.forEach(item => {
// item => 每个文件路径
// 使用fs的copyTpl() 方法,将每一个对应的模板生成到对应的文件
this.fs.copyTpl(
this.templatePath(item),
this.destinationPath(item),
this.answers
)
})
}
}
4,使用 generator-vue 方法,同自定义Generator 使用方法,执行结果,如下图所示:
1,在发布之前,首先将项目的源代码托管到公开的代码仓库里面,这里我们使用GitHub。
具体操作,详见【GIT关联本地仓库与远端仓库】
2,在 命令行界面输入发布命令。
$ yarn publish # or npm publish
运行结果,如下图所示:
如果使用的镜像资源超时,会报出上面的错误,此时需要修改请求地址。
解决方案详见:【yarn publish 报错】
注意
在第一次执行时,会要求输入npm 的用户名、邮箱和密码,后期输入,只需要输入密码即可。
登录、登出命令如下:
$ yarn login # or npm login # 登录命令
$ yarn logout # or npm logout # 登出命令
3,若使用淘宝的镜像资源,则会报错下面的错误,这是由于淘宝镜像是只读的,不能将模块进行发布。
1,可以改变镜像资源
$ npm config set registry https://registry.yarnpkg.org # yarn 镜像源
$ npm config set registry https://registry.npmjs.org # node 默认镜像源
2,在命令后面,直接跟上相关的镜像资源
$ yarn publish --registry-https://registry.yarnpkg.com # yarn 镜像源
$ yarn publish --registry-https://registry.npmjs.org # node 默认镜像源
成功运行结果,如下图所示:
4,可以去 npm官网,查看刚刚发布的模块,如下图所示:
5,使用 npm / yarn 进行全局范围的安装,并使用 yo 命令运行这个 Generator。
一个小而美的脚手架工具,创建项目中特定类型的文件的小工具。类似于Yeoman中的Sub Generator,但是他一般不会独立使用,而是集成在项目中,用来自动化的创建同类型的项目文件,例如 创建一个组件 / 模块所需要的文件。
Plop 可以提高创建重复文件的效率。
1,将 plop 模块作为项目开发依赖安装;
2,在项目根目录下创建一个 plopfile.js 文件;
3,在 plopfile.js 文件中定义脚手架任务;
4,编写用于生成特定类型文件的模板;
5,通过 Plop 提供的 CLI 运行脚手架任务。
这里以react为例,集成Plop。
1,全局安装 create-react-app 脚手架
$ yarn global add create-react-app # or npm install create-react-app -g
2,使用 create-react-app 脚手架,创建项目
$ create-react-app my-react-app
$ cd my-react-app
3,在项目中集成plop,安装 plop 模块
$ yarn add plop --dev # or npm install plop -D
4,在项目根目录下,新建一个 plopfile.js 文件,定义 Plop 脚手架任务
代码示例如下:
// Plop 入口文件,需要导出一个函数
// 此函数接收一个 plop对象,并且提供了一系列的工具函数,用于创建生成器任务
module.exports = plop => {
/**
* setGenerator() 方法接收两个参数,
* 一,生成器的名字
* 二,生成器的配置选项
*/
plop.setGenerator('component', {
description: 'create a component',
prompts: [ // 指定Generator在执行时,发出的命令行问题
{
type: 'input',
name: 'name',
message: 'component name',
default: 'MyComponent'
}
], // 完成命令行交互过后,需要执行的动作
actions: [ // 数组中的每一个对象,都表示一个动作对象, 可以创建多个模板
{
// type指定动作的类型,add代表添加一个全新的文件
type: 'add',
// path指定新添加的文件会被添加到哪一个具体的路径下
// 可以通过{
{}}插值表达式的形式,去动态插入刚才用户输入的数据
path: 'src/components/{
{name}}/{
{name}}.js',
// 指定模板文件
templateFile: 'plop-templates/components.hbs'
},
{
type: 'add',
path: 'src/components/{
{name}}/{
{name}}.css',
templateFile: 'plop-templates/components.css.hbs'
},
{
type: 'add',
path: 'src/components/{
{name}}/{
{name}}.test.js',
templateFile: 'plop-templates/components.test.hbs'
}
]
})
}
5,创建对应的模板文件,,模板文件采用Handlebars模板引擎进行书写,一般放置在根目录下的plop-templates下
代码示例如下:(components.hbs)
import React from 'React'
export default () => {
<div className="{
{name}}">
<h1>{
{
name}} Component</h1>
</div>
}
代码示例如下:(components.css.hbs)
.{
{
name}} {
}
代码示例如下:(components.test.hbs)
import {
render, screen } from '@testing-library/react';
import {
{
name}} from './{
{name}}';
test('renders learn react link', () => {
render(<<name>> />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
6,在安装 plop 时,Plop 提供了一个 CLI 的应用程序,由于yarn会自动找到node_modules下的bin目录下的命令行工具,所以可以通过 yarn 去启动这个程序
$ yarn plop component # yarn plop 定义的Generator名称
执行结果,如下图所示:
脚手架工具就是一个 node cli 的应用。
1,通过 在package.json 中添加属性 bin ,设置 入口文件,用于指定CLI应用的入口文件
2,安装 inquirer 模块,通过使用 inquirer 这一模块,实现用户交互
$ yarn add inquirer # or npm install inquirer
3,安装 ejs 模块,进行文件渲染
$ yarn add ejs # or npm install ejs
4,编写 cli.js 文件
代码示例如下:
#!/user/bin/env node
// Node CLI 应用入口文件必须要有这样的文件头
// 如果是 Linux 或者 macOS 系统下还需要修改此文件的读写权限为 755
// 具体就是通过 chmod 755 cli.js 实现修改
// 脚手架的工作过程:
// 1. 通过命令行交互询问用户问题
// 2. 根据用户回答的结果生成文件
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(answers => {
// 根据用户回答的结果生成文件
// 模板目录
const tmplDir = path.join(__dirname, 'templates')
// 目标目录
const destDir = process.cwd()
// 将模板下的文件全部转化到目标目录
fs.readdir(tmplDir, (err, files) => {
if (err) throw err
files.forEach(file => {
// 每一个file都是相对于templates下的相对路径
// 通过模板引擎渲染文件,这里采用 ejs
/**
* renderFile() 方法 接收三个参数
* 一,文件的绝对路径
* 二,渲染的数据
* 三,回调函数
*/
ejs.renderFile(path.join(tmplDir, file), answers, (err, result) => {
if (err) throw err
// 将结果写入目标文件路径
fs.writeFileSync(path.join(destDir, file), result)
})
})
})
})