学习笔记(七)——前端工程化+脚手架工具

前端工程化

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

前端工程化主要解决的问题

  • 传统语言或语法的弊端
  • 无法使用模块化/组件化
  • 重复的机械工作
  • 代码风格统一、质量保证
  • 依赖后端服务接口支持
  • 整体依赖后端项目

脚手架工具

常用脚手架工具

  • create-react-app - React项目
  • vue-cli - Vue.js项目
  • angular-cli - Angular项目
  • Yeoman
  • Plop

Yeoman

可用于创建任何类型的通用前端工程脚手架

  • 使用步骤总结

    • 明确需求
    • 找到合适的generator,并全局安装
    • 通过yo运行generator,进行命令行交互填写相应的信息,最终生成所需的项目工程结构
  • 基础使用

    • 安装 yarn global add yo
    • 安装指定类型的generator
      • yarn global add generator-node node工程
    • 通过yo运行generator
      • yo node
  • Sub Generator

    子集生成器,用于生成特定的文件

    • 例如生成cli相关的配置文件 yo node:cli
  • 自定义generator

    基于yeoman搭建自己的脚手架

    创建generator本质上是创建一个npm模块

    • generator基本结构

      image-20200902113746677
    • generator命名约定

      • generator-<name>
    • 新建generator目录

    • 使用 yarn init -y 初始化

    • yarn add yeoman-generator 添加依赖

      • 提供了一些创建generator的工具,使创建generator更加便捷
    • 创建generators/app/index.js相应的目录结构及文件

      • index.js文件作为generator的核心入口

      • 需要导出一个继承自yeoman generator的类型

      • yeoman generator在执行过程中会自动调用我们在此类型中定义的一些生命周期方法

      • 可以在这些方法中通过调用父类的一些工具方法来实现一些功能,例如:文件写入

        // index.js
        // generator-sample
        const Generator = require('yeoman-generator')
        
        module.exports = class extends Generator {
            writing () {
                this.fs.write(
                    this.destinationPath('tmp.txt'),
                    Math.random().toString()
                )
            }
        }
        
    • 使用 yarn link 命令将本地模块链接到全局

    • 使用yo执行相应的generator,例如 yo sample

    • 使用ejs模板创建文件

      • 在app目录下创建templates目录,并放置相应的模板文件

        这是一个模板文件
        模板文件的名字是 <%= title %>
        <% if (success) { %>
            world
        <% } %>
        
      • 使用 this.fs.copyTpl(from, to, context) 命令根据模板创建文件

        const Generator = require('yeoman-generator')
        
        module.exports = class extends Generator {
            writing () {
                // this.fs.write(
                //     this.destinationPath('tmp.txt'),
                //     Math.random().toString()
                // )
                const tmpl = this.templatePath('foo.txt') // 获取模板文件路径
                const out = this.destinationPath('foo.txt') // 输出路径
                const context = { title: 'hello', success: true } //模板数据上下文
                this.fs.copyTpl(tmpl, out, context);
            }
        }
        
      • 执行generator创建文件,得到foo.txt

        // foo.txt
        这是一个模板文件
        模板文件的名字是 hello
        
            world
        
    • 接收用户输入

      • 通过实现prompting() 方法来实现,在此方法中,调用父类的prompt(),返回一个promise对象

      • prompt()方法接收一个问题对象数组

      • 问题对象question包含type、name、message、default等属性

        • type: input 用户输入方式
        • name: answers对象的key
        • message: 交互提示信息
        • default: 交互问题的默认值
      • prompt().then(answers => {}) 获得问答结果对象 { name: '......' }

        const Generator = require('yeoman-generator')
        
        module.exports = class extends Generator {
            prompting () {
                return this.prompt([
                    {
                         type: 'input',
                         name: 'name',
                         message: 'project name ?',
                         default: this.appname
                    }
                ]).then(answers => {
                    this.answers = answers
                })
            }
            writing () {
                // this.fs.write(
                //     this.destinationPath('tmp.txt'),
                //     Math.random().toString()
                // )
                const tmpl = this.templatePath('bar.html') // 获取模板文件路径
                const out = this.destinationPath('bar.html') // 输出路径
                const context = this.answers //模板数据上下文
                this.fs.copyTpl(tmpl, out, context);
            }
        }
        
      • 执行generator

        image-20200902151638321
  • Vue Generator案例

    • 创建generator-vue目录,并 yarn init -y 初始化

    • 创建generators\app\index.js 目录结构及文件

    • 创建generators\templates目录,并将所有模板文件放入

    • 修改index.js文件

      // generators\app\index.js
      
      const Generator = require('yeoman-generator')
      
      module.exports = class extends Generator {
          prompting () {
              return this.prompt([
                  {
                      type: 'input',
                      name: 'name',
                      message: 'project name ?',
                      default: this.appname
                  }
              ]).then(answers => {
                  this.answers = answers
              })
          }
          writing () {
              const templates = [
                  // ... 所有模板文件的路径
              ]
              templates.forEach(t => {
                  this.fs.copyTpl(
                      this.templatePath(t),
                      this.destinationPath(t),
                      this.answers
                  )
              })
          }
      }
      
    • 关于模板冲突

      • 需要自动添加的模板文件中,可能存在也是用了ejs模板语法的文件,此时需要对其对应的模板语法进行转译,即<%%
  • 发布generator

    • 将模块托管到一个仓库(gitee/github/gitlab等)
    • 通过 npm publishyarn publish 命令发布成一个公开的模块
    • 如果修改过npm或yarn的源,例如淘宝的镜像,那么在发布时会提示错误,需要在publish命令后面使用 --registry= 参数指定原镜像地址
      • npm publish --regishtry=https://registry.npmjs.org
      • yarn publish --registry=https://registry.yarnpkg.com

Plop

一款小而美的脚手架工具

用于创建项目中特定类型的文件

类似于Yeoman的sub generator

  • 基本使用

    • 将plop作为开发依赖进行安装 yarn add plop --dev

    • 创建plopfile.js文件

      • plop工作的入口文件

      • 需要导出一个函数,该函数接收一个plop对象,提供创建生成器任务的工具

        // plopfile.js
        // 用于创建component
        module.exports = plop => {
            plop.setGenerator('component', {
                description: 'create a component',
                prompts: [
                    {
                        type: 'input',
                        name: 'name',
                        message: 'component name',
                        default: 'MyComponent'
                    }
                ],
                actions: [
                    {
                        type: 'add',
                        path: 'src/components/{{name}}/{{name}}.js',
                        templateFile: 'plop-templates/component.hbs'
                    }
                ]
            })
        }
        
    • 创建plop-templates目录,用于存放plop模板文件 (handlebars模板文件)

    • 通过plop提供的CLI运行脚手架任务

      • yarn plop component 执行指定生成器任务

脚手架工作原理

  • 创建一个node cli模块

    • 创建模块目录
    • 使用 yarn init -y 初始化
    • 修改 package.json 文件,添加 bin: cli.js
    • 创建cli.js
      • Node CLI 应用入口文件
      • 必须包含文件头 #!/usr/bin/env node 使之在node环境下执行
      • 如果是linux或macOS系统还需修改文件读写权限 chmod 755 cli.js
    • 执行 yarn link
  • 脚手架工作过程

    • 通过命令行交互询问用户问题
    • 根据用户回答的结果生成文件
  • cli.js示例

    #!/usr/bin/env node
    
    // Node CLI 应用入口文件必须包含文件头
    // 如果是linux或macOS系统还需修改文件读写权限 chmod 755 cli.js
    
    // 脚手架工作过程
    // 1、通过命令行交互询问用户问题
    // 2、根据用户回答的结果生成文件
    
    const path = require('path')
    const inquirer = require('inquirer')
    const { readdir, writeFileSync } = require('fs')
    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()
        // 读取模板并生成文件
        readdir(tmplDir, (err, files) => {
            if (err) throw err
            files.forEach(file => {
                // ejs模板引擎渲染文件
                ejs.renderFile(path.join(tmplDir, file), answers, (err, result) => {
                    if (err) throw err
                    writeFileSync(path.join(destDir, file), result)
                })
            })
        })
    })
    
    • 辅助工具
      • inquirer 用于命令行交互
      • ejs 模板
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260