前端工程化实战-开发企业级CLI

这篇具有很好参考价值的文章主要介绍了前端工程化实战-开发企业级CLI。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前端工程化实战-开发企业级CLI

1. 前言

脚手架大家一定都不陌生,比如我们经常使用的 vue-cli、create-react-app,它可以帮助我们快速的初始化一个项目,无需从零配置,极大的方便我们的开发。到这里你可能会疑惑,既然市面上有成熟的脚手架,为什么需要写一个属于自己的脚手架呢。因为公共脚手架虽然强大,但并不能满足我们的实际开发需求。

例如项目中已有的沉淀,项目架构、接口请求的统一处理、换肤、业务组件、eslint配置等,这些想要用到新项目中,只能通过复制粘贴,会存在以下弊端:

  • 重复性劳动,繁琐且浪费时间
  • 已有项目沉淀分散在各处,很容易有所遗漏
  • 项目间的配置差异很可能会被忽略
  • 人工操作永远都有可能犯错,建新项目时,总要花时间去排错

如果我们自己开发一套脚手架,定制自己的模板,复制粘贴的人工流程就会转换为 cli 的自动化流程, 还可以通过维护不同的模板以适应不同业务需求。既然要开发一套脚手架,站在巨人肩膀上显然省事多了,我们先来看看业界知名脚手架Vue CLI是如何实现的。

 

 2.Vue CLI 原理分析 

Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供:

  • 通过 @vue/cli 实现的交互式的项目脚手架。
  • 通过 @vue/cli + @vue/cli-service-global 实现的零配置原型开发。
  • 一个运行时依赖 (@vue/cli-service),该依赖:
    • 可升级;
    • 基于 webpack 构建,并带有合理的默认配置;
    • 可以通过项目内的配置文件进行配置;
    • 可以通过插件进行扩展。
  • 一个丰富的官方插件集合,集成了前端生态中最好的工具。

  • 一套完全图形化的创建和管理 Vue.js 项目的用户界面。

     

2.1

全局 vue 执行命令存放在哪里

以mac为例,使用命令 where vue,就可以查到 vue 命令所在位置,找到所在位置后,查看目录即可分析源码。

 

  2.2

vue命令是从哪里注册的

找到源码目录中的package.json,我们会看到如下代码:

前端工程化实战-开发企业级CLI

可以看到bin字段指定了可执行文件的命令名以及可执行文件的路径,npm安装一个依赖时,如果该依赖的package.json中指定了bin的信息,那么同时会创建一个全局的软连接指向该命令所对应的可执行文件。详细可查看npm的官方文档:package.json中bin的使用说明。

  2.3

依赖包分析

包名 用途
commander 完整的 node.js 命令行解决方案, Commander 负责将参数解析为选项和命令参数
shelljs 用来执行shell命令
inquirer 通用交互式命令行用户界面的集合
semver 语义化版本控制
chalk 设置终端字符串样式

  2.4

脚手架都做了哪些事情

2.4.1 HTML 和静态资源

html文件是一个会被 html-webpack-plugin 处理的模板。在构建过程中,资源链接会被自动注入。另外,Vue CLI 也会自动注入 resource hint (preload/prefetch、manifest 和图标链接 (当用到 PWA 插件时) 以及构建过程中处理的 JavaScript 和 CSS 文件的资源链接。

2.4.2  CSS 相关

Vue CLI生成项目支持 PostCSS、CSS Modules 和包含 Sass、Less、Stylus 在内的预处理器,你可以在创建项目的时候选择预处理器。

2.4.3  webpack 相关

Vue CLI基于 webpack 构建,并带有合理的默认配置,可以通过项目内的配置文件进行配置,还可以通过插件进行扩展。

2.4.4  模式与环境变量

模式是 Vue CLI 项目中一个重要的概念。默认情况下,一个 Vue CLI 项目有三个模式:

development 模式用于 vue-cli-service servetest 模式用于 vue-cli-service test:unitproduction 模式用于 vue-cli-service build 和 vue-cli-service test:e2e你可以通过传递 --mode 选项参数为命令行覆写默认的模式。

 2.4.5  构建目标

当你运行 vue-cli-service build 时,你可以通过 --target 选项指定不同的构建目标。它允许你将相同的源代码根据不同的用例生成不同的构建。

通过以上对Vue CLI 的分析,我们就对脚手架工具提供的构建集成能力有了一个大概的了解。这有助于我们在使用具体工具时快速定位问题的边界,当我们自己设计脚手架的时候,我们也可以参照和借鉴,可以适用于我们业务的有:

  • 通过命令行与用户交互
  • 根据用户的选择生成对应的文件,实现的零配置原型开发
需要做出修改的部分有:
  • 基于 vite 构建,并带有合理的默认配置;
  • 预定义业务模板,根据用户选择生成
  • 业务模板基础支持:
    • HTML 和静态资源处理
    • 内置css预处理器
    • 内置vite配置,可以直接修改vite配置文件
    • 内置test、pre、pro三种模式,并生成对应的配置文件

按照上述总结,让我们一步一步编写自己的脚手架吧,首先是通过命令行与用户交互,那么我们需要有一个可执行命令的名字,也是脚手架的名字,这里我们就叫做dt-fe-cli

 3.脚手架实现 

  3.1

命令行工具编写

  3.1.1  初始化项目

我们的脚手架叫做dt-fe-cli,创建dt-fe-cli文件夹,执行npm init -y初始化仓库,生成package.json文件。

在dt-fe-cli文件夹下创建bin文件夹,并在里面创建cli.mjs文件,此文件作为我们脚手架的入口,需要将其配置到package.json的bin字段。

{
    "name": "@auto/dt-fe-cli",
    "version": "0.0.1",
    "bin": {
        "dt-fe-cli": "bin/cli.mjs"
    }
}

这样我们脚手架的入口就有了,继续编写脚手架的功能吧

  3.1.2  指令

dt-fe-cli 作为全局命令,同时提供了很多指令。

  • dt-fe-cli --version可以查看 dt-fe-cli 版本
  • dt-fe-cli --help可以查看帮助文档
  • dt-fe-cli create xxx可以创建一个项目 ...

前端工程化实战-开发企业级CLI

  3.1.3  create命令

create接受一个项目名作为参数,这里还提供了额外选项-f, --force,此选项代表如果本地已经存在同名文件夹,是否覆写。命令行解决方案需要依赖第三方库commander

import create from '../lib/create.mjs'

program
  .command('create <app-name>')
  .description('create a new project powered by dt-fe-cli')
  .option("-f, --force", "overwrite target directory if it exists")
  .action((projectName, options) => {
    create(projectName, options)
  })

  3.1.4  create方法设计与实现

执行create命令后,如何创建项目呢,我们公司的项目都是托管在内部gitlab上面的,所以直接使用git clone去拉取模板项目,这里需要依赖第三方库shelljs,那么这里就需要首先判断git是否存在,不存在提示并退出。之前我们还写了一个额外选项,用来表示如果本地已经存在同名文件夹,是否覆写。若没有此选项,还需要交互式的询问,这里需要依赖第三方库inquirer。

create.mjs:

import chalk from 'chalk'
import fse from 'fs-extra'
import shelljs from 'shelljs'
import path from 'path'
import inquirer from 'inquirer'

async function create(projectName, options) {
  const targetDirectory = path.join(process.cwd(), projectName)
  try {
    // 判断是否存在git,不存在则提示并退出
    if (!shelljs.which('git')) {
      console(chalk.red('Sorry, dt-fe-cli requires git'));
      return
    }
    const isExist = await fse.pathExists(targetDirectory)
    // 判断目录下是否存在同名文件夹
    if (isExist) {
      if (options.force) {
        await fse.remove(targetDirectory);
      } else {
        const { isOverwrite } = await new inquirer.prompt([
          {
            name: "isOverwrite", // 与返回值对应
            type: "list",
            message: "Target directory already exists. Pick an action:",
            choices: [
              { name: "Overwrite", value: true },
              { name: "Cancel", value: false },
            ],
          },
        ]);
        // 移除同名文件夹
        if (isOverwrite) {
          console.log('remove existing directory...')
          await fse.remove(targetDirectory);
        } else {
          return;
        }
      }
    }
    // 项目类型
    const { projectType } = await new inquirer.prompt([
      {
        name: "projectType", // 与返回值对应
        type: "list",
        message: "Please select project type:",
        choices: [
          { name: "pc", value: 'pc' },
          { name: "h5", value: 'h5' },
        ],
      },
    ]);
    const PROJECT_MAP = {
      pc: 'pc.git',
      h5: 'h5.git'
    }
    // 安装依赖项目
    shelljs.exec(`git clone ${PROJECT_MAP[projectType]} ${projectName}`, async (code, stdout, stderr) => {
      if (code === 0) {
        progress.start()
        try {
          // 删除原有.git
          await fse.remove(path.join(process.cwd(), projectName, '.git'))
        } catch (error) {
          console.log(error)
        }
        progress.succeed()
        console.log(`\r\nSuccessfully created project ${chalk.cyan(projectName)}`);
        console.log(`\r\n  cd ${chalk.cyan(projectName)}`);
        console.log("  git init");
        console.log("  pnpm install");
        console.log("  pnpm dev");
      }
    })
  } catch (error) {
    console.log(error);
  }
}

  3.1.5  node版本检查

执行 create 命令后,创建项目会去 gitlab 拉取代码下载我们自定义的模版,目前我使用的模版均由 Vite3 创建,Vite3 需要 Node.js 版本 14.18+,16+。所以在使用脚手架时,可以先检查一下当前 Node.js 版本是否符合,不符合则抛出异常。当前依赖的 Node.js 版本需要将其配置到package.json的engines字段,判断当前 Node.js 版本是否符合需要依赖第三方库semver

package.json:

{
    "engines": {
        "node": ">= 14.18.0"
    },
}

cli.mjs:

import { readFile } from 'fs/promises'
import semverSatisfies from 'semver/functions/satisfies.js'

const { engines: { node: requiredVersion }, version } = JSON.parse(
  await readFile(
    new URL('../package.json', import.meta.url)
  )
)

function checkNodeVersion (wanted, id) {
  if (!semverSatisfies(process.version, wanted, { includePrerelease: true })) {
    console.log(chalk.red(
      'You are using Node ' + process.version + ', but this version of ' + id +
      ' requires Node ' + wanted + '.\nPlease upgrade your Node version.'
    ))
    process.exit(1)
  }
}

checkNodeVersion(requiredVersion, 'dt-fe-cli')

到这里,脚手架的基本功能就已经开发完毕了,剩下的就是我们的项目模板了

  3.2

模版设计支持功能

前端工程化实战-开发企业级CLI

3.2.1  TypeScript

使用 Vite3 构建,Vite3 天然支持引入 .ts 文件

  3.2.2  打包自动上传CDN

const { execSync } = require('child_process');
const { loadEnv } = require('vite')

const env = process.argv[2]
const { VITE_BASE_URL } = loadEnv(env, process.cwd(), '')
const prefix = `${env}${VITE_BASE_URL}`

execSync(`vite build --mode ${env} --base=https://cdn.com/${prefix}`);
uploadCDN({
  Dir: `dist/assets`,
  Prefix: prefix
})

  3.2.3  commit 校验

npm install husky --save-dev
npm pkg set scripts.prepare="husky install"
npm run prepare
npx husky add .husky/pre-commit "npm run lint"
git add .husky/pre-commit

  3.2.4  eslint校验

ESLint通用配置的部分这里就不再赘述了,这里介绍一下我们业务里面自定义的ESLint插件。eslint校验大家都很熟悉,市面上也有很多eslint插件,但随着项目不断迭代发展,我们团队的编码规范使用现有的eslint插件已经无法满足了,需要自己创建插件,并融入到cli的模板当中。

 ► 创建插件

开始创建插件的最简单方法是使用 Yeoman 生成器。生成器将指导您设置插件的骨架

npm i -g yo generator-eslint
yo eslint:plugin

以上命令会生成如下目录

.
├── README.md
├── lib
│   ├── index.js
│   └── rules
├── package.json
└── tests
    └── lib
        └── rules

插件可以在 ESLint 中使用的额外规则。为此,插件必须导出一个包含规则 ID 到规则的键值映射的规则对象,举个简单的例子,我们想创建一条不允许使用console.log的规则

  ►  创建规则
yo eslint:rule

此命令会在lib/rules文件夹下创建一个新的js文件,一个规则对应一个可导出的 node 模块

"use strict";

//-------------------------------------------------------
// Rule Definition
//-------------------------------------------------------

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
  meta: {
    type: "suggestion",
    docs: {
      description: "disallow unnecessary semicolons",
      recommended: true,
      url: "https://eslint.org/docs/rules/no-extra-semi"
    },
    fixable: "code",
    schema: [] // no options
  },
  create(context) {
    return {
      // callback functions
    };
  }
};

上面这段代码是一个规则的源码文件的基本格式,一个规则的源文件输出一个对象,它由 meta 和 create 两部分组成。

⚫meta(对象)包含规则的元数据,如规则类型、文档、可接受参数的schema等等

⚫create (function) 返回一个对象,其中包含了 ESLint 在遍历 JavaScript 代码的抽象语法树 AST (ESTree 定义的 AST) 时,用来访问节点的方法。

核心其实在于create方法,我们若想知道如何编写create方法,首先要明白其原理,那就是 ESLint 是如何分析我们所编写的代码呢?相信大家对此也都有所了解,没错,就是AST (Abstract Syntax Tree(抽象语法树))

  ►  插件原理

前端工程化实战-开发企业级CLI

ESLint 解析器将代码转换为 ESLint 可以评估的抽象语法树。默认情况下,ESLint 使用内置的 Espree 解析器,它与标准的 JavaScript 运行时和版本兼容,然后去拦截检测是否符合我们规定的书写方式,最后让其展示报错、警告或正常通过。ESLint 的核心就是规则(rules),而定义规则的核心就是利用 AST 来做校验,那就让我们看一下代码 AST 中会表现为什么样子。

前端工程化实战-开发企业级CLI

上图可以看出,console.log对应 AST 中type为ExpressionStatement(表达式语句),表达式类型为CallExpression(调用表达式),被调用者类型为MemberExpression(成员表达式),被调用对象名为console,属性名为log,根据上述信息,我们就可以来完善create方法了
  ►  编写规则
"use strict";

//-------------------------------------------------------
// Rule Definition
//-------------------------------------------------------

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
  meta: {
    type: "suggestion",
    fixable: "code",
    schema: [], // no options
  },
  create(context) {
    return {
      //  key 是 selector
      'CallExpression MemberExpression': (node) => {
        const { property, object } = node;
        // 如果在 AST 中匹配到了console.log,就用 context.report() 来发布警告或错误
        if (object.name === 'console' && property.name === 'log') {
          context.report({
            node,
            message: 'console.log is forbidden.'
          });
        }
      }
    };
  }
};

至此,包含一条规则(禁止使用console.log)的 ESLint 插件就编写完成了,接下来将此项目发布到npm平台就可以在项目模板中下载使用了

 最后 

 

本文介绍了如何从零编写一个我们自己的脚手架,并且可以根据不同业务场景区分模版,把业务已有的积累沉淀进去,以上便是本次分享的全部内容,希望对你有所帮助 ^_^

 

作者| 马春键 文章来源地址https://www.toymoban.com/news/detail-517604.html

到了这里,关于前端工程化实战-开发企业级CLI的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端Vue入门-day03-用Vue实现工程化、组件化开发

    (创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 生命周期 Vue 生命周期 和 生命周期的四个阶段  Vue 生命周期函数(钩子函数) 案例-create的应用 案例-mounted的应用 工程化开发 脚手架 Vue CLI 开发 Vue 的两种方式 基本介

    2024年02月15日
    浏览(38)
  • 前端工程化 搭建私有组件库 组件从开发到发布私有npm仓库的全过程

    前言 基于Vue3.0 + TS的组件从开发组件库到发布私有npm仓库的全过程 环境 这里列出本文所使用的环境版本 vue 3.0 vue/cli 4.5.9 nodeJs 14.15.1 npm 6.14.8 vue --version @vue/cli 4.5.9 npm -v 6.14.8 node -v v14.15.1 步骤 创建项目 使用 vue-cli 创建一个 vue3 项目,假设项目名为 avatar-ui-vue vue create avatar-u

    2024年02月02日
    浏览(36)
  • 前端工程化详解——理解与实践前端工程化

    前言: 前端工程化一直是一个老生常谈的问题,不管是面试还是我们在公司做基建都会经常提到前端工程化,那么为什么经常会说到前端工程化,并没有听过后端工程化、Java工程化或者Python工程化呢?我们理解的前端工程化是不是一直都是Webpack的性能调优,或者是一个cli工

    2024年02月02日
    浏览(48)
  • 为react项目添加开发/提交规范(前端工程化、eslint、prettier、husky、commitlint、stylelint)

    因历史遗留原因,接手的项目没有代码提醒/格式化,包括 eslint、pretttier,也没有 commit 提交校验,如 husky、commitlint、stylelint,与其期待自己或者同事的代码写得完美无缺,不如通过一些工具来进行规范和约束。 eslint 是一个代码校验工具,用来规范项目代码风格。 初始化 通

    2024年02月14日
    浏览(79)
  • 什么是前端工程化?

    前端工程化是一种思想,而不是某种技术。主要目的是为了提高效率和降低成本,也就是说在开发的过程中可以提高开发效率,减少不必要的重复性工作等。 tip 现实生活举例 建房子谁不会呢?请几个工人一上来就开始弄,在建筑的过程中缺了材料就叫个工人去买,发现工期

    2024年02月15日
    浏览(46)
  • 前端工程化

    手把手带你走进Babel的编译世界 - 掘金 (juejin.cn) 我们所写的代码转换为机器能识别的一种 树形结构, 本身是由一堆节点(Node)组成,每个节点都表示源代码中的一种结构。 不同结构用类型(Type)来区分,常见的节点类型有Identifier(标识符),Expression(表达式),VariableDeclarat

    2023年04月11日
    浏览(36)
  • 前端工程化-VUE

    高效的开发离不开基础工程的搭建。本章主要介绍如何使用Vue进行实际SPA项目的开发,这里使用的是目前热门的JavaScript应用程序模块打包工具Webpack,进行模块化开发、代码编译和打包。 Vue脚手架指的是Vue-cli,它是一个专门为单页面应用快速搭建繁杂程序的脚手架,它可以轻

    2024年02月08日
    浏览(39)
  • 前端工程化-NPM

    NPM代表npmjs.org这个网站,这个站点存储了很多Node.js的第三方功能包。 NPM的全称是Node Package Manager,它是一个Node.js包管理和分发工具,已经成为非官方的发布Node模块(包)的标准。它可以让JavaScript开发者能够更加轻松地共享代码和共用代码片段,并且通过NPM管理需要分享的代

    2024年02月09日
    浏览(41)
  • 前端工程化相关

    知道软件包名,拿到源码或者路径的方法 在浏览器输入以下内容,就可以找到你想要的。。。 unpkg.com/输入包名 ESM特性清单: 自动采取严格模式,忽略“use strict” 每个ESM模块都是单独的私有作用域; ESM是通过CORS去请求外部JS模块的 ESM的script标签会延迟执行脚本,就要有

    2024年01月17日
    浏览(47)
  • 前端工程化与webpack

    能够说出什么是前端工程化 能够说出webpack的作用 能够掌握webpack的基本使用 了解常用plugin的基本使用 了解常用loader的基本使用 能够说出Source Map的作用 前端工程化 webpack的基本使用 webpack中的插件 webpack中的loader 打包发布 Source Map 1.1小白眼中的前端开发vs实际的前端开发 实际

    2024年02月13日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包