浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop

文章内容输出来源:拉勾教育 大前端高薪训练营

前端工程化

基本介绍

定义

前端工程化是指遵循一定的标准和规范,通过工具去提高效率,降低成本的一种手段。

面临的问题

1、想要使用 ES6 + 新特性,但是兼容有问题

2、想要使用 Less / Sass / PostCSS 增强 CSS 的编程性,但是运行环境不能直接支持

3、想要使用模块化的方式提高项目的可维护性,但运行环境不能直接支持

4、部署上线前需要手动压缩代码及资源文件,部署过程需要手动上传代码到服务器

5、多人协作开发,无法硬性统一大家的代码风格,从仓库中 pull 回来的代码质量无法保证

6、部分功能开发时,需要等待后端服务接口提前完成

主要解决的问题

1、传统语言或语法的弊端

2、无法使用模块化/组件化

3、重复的机械式工作

4、代码风格统一、质量保证

5、依赖后端服务接口支持

6、整体依赖后端项目

表现形式

一切以提高效率、降低成本、质量保证为目的的手段都属于工程化

  • 1,工程化的具体表现
    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第1张图片

    一切重复的工作都应该被自动化。实现前端工程化可以从 模块化、组件化、规范化、自动化等方面出发。

  • 2,工程化不等于工具
    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第2张图片
    一些成熟的工程化集成:create-react-app、vue-cli、angular-cli、gatsby-cli。

脚手架工具

基本介绍

简单来说,脚手架就是用来自动的去帮我们创建项目基础文件的一个工具。脚手架工具,可以认为是前端工程化的发起者。

作用

创建项目基础结构、提供项目规范和约定。

  • 一般来说,一个项目中包含一些相同的约定,即:

    1)相同的组织结构

    2)相同的开发范式

    3)相同的模块依赖

    4)相同的工具配置

    5)相同的基础代码

我们可以通过脚手架工具搭建一个特定的项目骨架,然后基于这个骨架去进行相关的开发工作。但是,前端脚手架是以独立的工具存在,不会集成在IDE中。

常用工具

Yeoman

基本介绍

准备工作

  • 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,生成你所需要的项目结构;

Generator

  • 使用 Yeoman 提供的 yo 命令,去运行 generator-node 生成器。运行特定的生成器,就是将 generator- 前缀去掉,直接使用 yo 运行后面的部分即可。

      $ mkdir project-dir-name
      $ cd path/to/project-dir
      $ yo node  
    

    运行结果,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第3张图片
    项目结构,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第4张图片

Sub Generator

在已有的项目结构中,创建一些特定类型的文件,可以使用 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 应用程序的本地数据文件夹中。

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第5张图片
    并且,会将新的模块的cmd脚本文件,注册到 yarn global bin 的路径下。
    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第6张图片

  • 3,最后,需要安装项目依赖。

      $ yarn
    

    注意

    每个用户的bin目录不同,cmd命令存放不同,因此,配置的环境变量路径也不同。
    查看生成的 cmd 脚本文件,如下图所示,无法找到 link 后的模块。

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第7张图片
    解决方案,详见【https://blog.csdn.net/zimeng303/article/details/109736592】

  • 4,全局访问生成的模块

      $ module-name --help
    

    运行结果,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第8张图片
    注意

    并不是每一个 generator 都存在子集的生成器,需要以官方文档为准。

自定义 Generator

基于 Yeoman 搭建自己的脚手架,Generator 本质上就是一个 NPM 模块。

命名规范

generator- name

注意

​ 如果命名不规范,那么后期 Yeoman 就无法找到所定义的 生成器模块。

基本步骤
创建 Generator 模块
  • 1,创建的 Generator 目录结构,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第9张图片

  • 2,创建的 Sub Generator 目录结构,component 文件夹存放子集生成器,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第10张图片

创建 Generator 步骤
  • 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 所在目录结构,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第11张图片

  • 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盘,如下图:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第12张图片

  • 7,新建项目目录,进入目录,执行 yeoman 操作

      $ cd path/to/project-dir
      $ yo sample
    

    注意

    由于我的 yarn 本地链接到的是在 C盘,直接使用上面的操作会报出如下错误:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第13张图片
    此时,有三种解决方案

    一, 将 link的 generator-sample拷贝到yarn的全局安装目录中,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第14张图片
    **二,**在执行上述操作之前,先将 generator-sample 链接到当前文件夹下,命令如下:

      $ cd path/to/project-dir
      $ yarn link generator-sample
      $ yo sample
    

    **三,**使用命令,将其在全局范围内安装

      $ npm install --force -g generator-sample
    

    通过上面的操作,便可以使 Yeoman 找到这个 generator-sample 依赖包了。
    个人推荐第二种或第三种

    运行结果,如下图所示:

    在这里插入图片描述
    执行后,会在项目目录下写入 temp.txt 文件,并在文件内写入 随机数内容,如图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第15张图片

根据模板创建文件

根据模板创建文件,相对于手动创建每一个文件,模板的方式大大提高了效率。

  • 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 模板引擎的写法。

    代码示例如下:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第16张图片
    其中的 “name” 为在index.js中 prompt() 方法中传入的数组参数元素中 name 的值。

Vue Generator 案例
创建 Generator
  • 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 中,使其作为模板文件存在,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第17张图片

  • 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 使用方法,执行结果,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第18张图片

发布 Generator
  • 1,在发布之前,首先将项目的源代码托管到公开的代码仓库里面,这里我们使用GitHub。

    具体操作,详见【GIT关联本地仓库与远端仓库】

  • 2,在 命令行界面输入发布命令。

      $ yarn publish # or npm publish
    

    运行结果,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第19张图片
    如果使用的镜像资源超时,会报出上面的错误,此时需要修改请求地址。

    解决方案详见:【yarn publish 报错】

    注意

    在第一次执行时,会要求输入npm 的用户名、邮箱和密码,后期输入,只需要输入密码即可。

    登录、登出命令如下:

      $ yarn login # or npm login # 登录命令
      $ yarn logout # or npm logout # 登出命令
    
  • 3,若使用淘宝的镜像资源,则会报错下面的错误,这是由于淘宝镜像是只读的,不能将模块进行发布。

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第20张图片
    解决方案

    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 默认镜像源
    

    成功运行结果,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第21张图片
    若还有其他报错,请查看【yarn publish 报错】

  • 4,可以去 npm官网,查看刚刚发布的模块,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第22张图片

  • 5,使用 npm / yarn 进行全局范围的安装,并使用 yo 命令运行这个 Generator。

Plop

基本介绍

一个小而美的脚手架工具,创建项目中特定类型的文件的小工具。类似于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名称
    

    执行结果,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第23张图片
    上面的结果即表示执行成功,我们可以去对应的文件夹下,找到生成的文件,如下图所示:

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第24张图片

脚手架工作原理

脚手架工具就是一个 node cli 的应用。

  • 1,通过 在package.json 中添加属性 bin ,设置 入口文件,用于指定CLI应用的入口文件

    浅谈前端工程化 之 前端脚手架工具 -- Yeoman、Plop_第25张图片

  • 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)
                  })
              })
          })
      })
    

你可能感兴趣的:(前端笔记,前端)