前端工程化(1)—带你用node手写脚手架

2,031 阅读9分钟

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

前言

随着自己技能和工龄的增长,接手的项目业务逻辑越来越复杂,需求也在不断增加。自己对于技术的思考也会越来越深入。

创建vue、react项目时,我们可以使用cli工具快速构建,但是其原理是什么,如何实现呢?如果想在普通项目中也引入能提高开发效率的es6、sass等,上线前的编译却又觉得麻烦。重复性的手动操作该如何避免?多人协作时,有什么办法可以强制要求代码统一的风格,保证代码质量?公司开启类似的新项目时,能不能根据旧项目自动复用,而不是手动copy...... 这些大大小小的问题最终都指向了一个名词——前端工程化

因为涉及面广,只能一点一点查,而百度出来的资料太乱,不知道现在是否还适用(前端技术发展太快),公司没有现成的大佬请教,碰见了问题不知道怎么解决。这一切都在阻碍我深入了解。好在最近参加的训练营里,关于这个问题,有特别详细的课程设计,一星期的学习,彻底解决了我半年以来的困惑。对于前端技术,有了突飞猛进的理解

(这里表扬一下拉勾训练营的课程设计,太针对我的痛点了,半个月就记了一整本的笔记。 ^v^)。 一想到可能还有许多和我一样困惑的秃头少男少女,还在困惑中上下求索而不得,我决定将我学到的分享给大家,只愿和我有同样疑问的你,少掉几根头发。

关于前端工程化的认识

首先,你要认识到前端工程化不是一个工具,它是根据实际业务的特点,将前端开发的整个流程 规范化、标准化,从创建项目——编码——预览测试——提交——部署,都遵循一定的标准和规范,通过不同的步骤采用不同的工具提高开发效率,减低成本的一种手段。

比如说:

  • 创建项目时:利用脚手架来自动完成基础结构的搭建、创建特定类型的文件
  • 编码时,自动格式化代码、校验代码的风格、编译新特性、自动打包、mock数据等
  • 预览测试时,自动开启服务器、热更新、sourceMap等
  • 自动部署、提交等

目前一个成熟的工程化集成案例vue-cli,就是官方为我们提供的针对特定类型(vue单文件项目)的工程化方案,其不仅创建了项目,还约定了目录结构,提供了工具(热更新服务,自动编译单文件组件,lint规范等)。

由此可知,就像前面说过的,前端工程化涉及面很广,一篇文章是介绍不完的。本篇我们先重点了解脚手架。其他的请关注后续文章。

关于脚手架的认识

脚手架主要是在构建项目环节的工程化表现,可以给项目提高基础结构、规范和约定。

常用的脚手架工具有以下几种:

  • 项目型:vue/react/angular提供的只针对特定项目的脚手架。
  • 通用型:yeoman 根据模板生成自定义生成对应的项目结构。
  • 特定文件型:plop,开发过程中,生成常用的特定类型的文件。如views、components

因为针对项目型的脚手架已经有现成的了。所以这里主要了解yeoman和plop,来帮助我们在日常的开发中,灵活操作,提高效率。

yeoman+Generator 的使用

初步体验yeoman+Generator

yeoman更像是脚手架运行平台,通过yeoman搭配不同的generator,来生成对应类型的项目。

eg:要自动生成一个node_modules模块:查询generator官网,得知要搭配使用的generator是generator-node

  • 下载
npm i yo -g
npm i generator-node -g
  • 初始化
mkdir nodedemo
cd nodedemo
npm init -y
  • 运行:根据命令行提示回答问题
yo node    //模块命去掉generator-,所以运行generator-node,是yo node

自定义Generator

日常开发中,根据需求去 yeoman官网查找合适的generator并下载,通过yo generator名 运行,填写命令行选项后,即可生成项目结构,也可以开发自己的generator,自定义脚手架。

  • 新建文件目录,模块名字必须是generator-xx,(eg:generator-sample)在该目录下创建以下文件结构:(必须按这个结构)
generators
	app
    	templates  //模板
        	bar.html //eg:例子,你可以放自己的文件
        index.js  //入口文件
package.json         // "name": "generator-sample",
  • 安装工具依赖 yeoman-generator:generator的基类,提供了一些工具函数,让我们创建generator更便捷。
  • index.js 主流程编写:此文件作为 Generator 的核心入口,需要导出一个继承自 Yeoman Generator 的类型,Yeoman Generator 在工作时会自动调用我们在此类型中定义的一些生命周期方法,我们在这些方法中可以通过调用父类提供的一些工具方法实现一些功能,例如文件写入
const Generator = require('yeoman-generator')
module.exports = class extends Generator {
  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 () {
    // Yeoman 自动在生成文件阶段调用此方法
   // 我们这里尝试往项目目录中写入文件
    this.fs.write(
      this.destinationPath('temp.txt'),
      Math.random().toString()
    )
  }
}
  • 一般writing方法中,我们经常是根据模板创建新文件的。
 writing () {
    // 模板文件路径
    const tmpl = this.templatePath('bar.html')
    // 输出目标路径
    const output = this.destinationPath('bar.html')
    // 模板数据上下文
    const context = this.answers
    this.fs.copyTpl(tmpl, output, context)
}
  • 使用或者测试 :
    • 方式一:发布到npm上,然后全局安装npm install mini-cli -g,在新的目录中,执行mini-cli命令
    • 方式二:将该模块 npm link到全局。然后在新的目录中,执行mini-cli命令。

plop 的使用

一般集成到项目中,用于生成同类型的项目文件,如在vue或者react项目中,自动生成view/component....(想象一下,如果项目有50多个个view,每次新建view,写基本结构,重复性的操作,无聊还浪费时间。如果还有固定的文件命名要求,比如首字母大写,你就知道统一使用plop创建view有多好了)

具体使用

  • 下载安装 npm i plop --dev
  • 设置模板文件:在根目录创建plop-templates目录,在该目录使用Handlebars模板引擎写法,创建文件。eg:这里我们创建react基本文件
//plop-templates/component.hbs
import React from 'react';

export default () => (
  <div className="{{name}}">
    <h1>{{name}} Component</h1>
  </div>
)
  • 设置生成器:在根目录创建plopfile.js文件,是Plop 入口文件,需要导出一个函数,此函数接收一个 plop 对象,用于创建生成器任务
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'
      },
     ...
    ]
  })
}
  • 使用 npx plop component ,回答命令行问题,自动生成文件

总结

  • 下载plop模块
  • 创建plopffile.js 文件并定义脚手架任务
  • cli 运行脚手架任务
  • 编写模板templates目录

使用 NodeJS 完成一个自定义的小型脚手架工具

脚手架工作原理:通过cli启动(cli.js) —— 通过命令行(inquirer.prompt)回答问题 —— 将回答的结果+template(ejs) ,生成文件结构

脚手架工具就是一个node cli应用,创建脚手架就是创建一个cli应用。

  • 首先创建目录并进入,初始化创建出 package.json
mkdir mini-cli
cd mini-cli
npm init -y
code .
  • 在package.json中 输入 bin入口
  • 在根目录创建 cli.js文件 添加bin 入口标识
{
  //...
  "bin": "cli.js",
  //...
}
#!/usr/bin/env node

  • 在根目录创建 templates 目录,里面放入要自动化生成的模板文件。eg:这里随便放了一个html文件和css文件。文件中动态部分可使用ejs模板语法。
//index.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>project name: <%= name %></h1>
    <p>project description: <%= desc %></p>
</body>
</html>
//style.css
body{
    background-color: red;
}
  • 在cli.js中 写主要应用逻辑
#!/usr/bin/env node

const fs = require('fs')
const path = require('path')
const ejs = require('ejs')  //引入ejs模块 结合所需功能问题变量 改写 templates 下项目文件 达到所需功能
const inquirer = require('inquirer') //引入inquirer 模块 创建用户与命令行交互的工具 编写所需问题及字段

inquirer.prompt([
//设置要交互的问题,一个对象是一个要交互的问题
    {
        type: 'input',
        name: 'name',
        message: 'project name?',
        default: 'projectdemo'
    }
    ...
])
	.then(answers => { 
    //在inquirer回调中 结合nodejs 读写功能 和ejs 模块将answers变量 重写到项目中
    // answers  {name:'',desc:''}
        const tmplDir = path.join(__dirname, 'templates')  //当前路径\templates
        const destDir = process.cwd()  //命令行根目录,当前路径

        fs.readdir(tmplDir, (err, files) => {  
            if (err) throw err
            files.forEach(file => {
                // 把模板替换
                ejs.renderFile(path.join(tmplDir, file), answers, (err, result) => {
                    if (err) throw err
                    // 替换成功后,写入在当前文件夹
                    fs.writeFileSync(path.join(destDir, file), result)
                })
            })
        })
    })
  • 测试:
    • 方式一:发布到npm上,然后全局安装npm install mini-cli -g,在新的目录中,执行mini-cli命令,回答命令行问题,即可自动生成新的文件结构。
    • 方式二:将该模块 npm link到全局。然后在新的目录中,执行mini-cli命令,回答命令行问题,即可自动生成新的文件结构。

总结

学会了自定义构建脚手架,能帮助我们开发时,减少不必要的重复性操作,项目构建初期,可以快速生成相应的架构,基本配置文件等。了解这些过程,也能帮助我们加深对node.js、vue-cli的理解。作为一个想提高的前端er,一点要get该技能。

前端工程化的另一个非常重要的组成部分是自动化构建,通过自动化构建工作流,可以将源代码转换为更适合生产环境的压缩代码。针对自动化构建工作流中,自动编译es6语法、sass语法、模板文件、启动开发服务器预览,监测源代码变化自动编译、浏览器自动刷新、打包压缩等一系列处理,我将会在下一篇文章前端工程化(2)—自动化构建 中分享。

再次感觉拉勾训练营的老师和班主任,因为你们,让我能再最短的时间,以最高效率的学会这些难懂的模块。

感谢观看,如果有讲解不到位的地方或者疑问,请在下方评论区和我互动。