首页 > 其他分享 >前端工程化

前端工程化

时间:2024-12-11 16:25:00浏览次数:4  
标签:npm const default 前端 生成器 console 工程化 grunt

0x01 概述

(1)工程化

  • 工程化:遵循一定的标准和规范,通过工具来降本增效、保证质量的一种方法

  • 工程化用于解决一些问题,包括:

    1. 传统语言或语法的弊端
    2. 无法使用模块化或组件化
    3. 重复的机械式工作
    4. 代码风格与质量的不统一
    5. 过于甚至整体依赖于后端服务及其接口支持(特指前端工程化
  • 工程化的核心在于对项目整体的规划与架构,而工具则是实现这个核心的方法

(2)前端工程化

  • 在前端项目过程中工程化的应用方法:

    graph LR 创建项目-->编码-->预览-->测试-->提交-->部署-->编码
    1. 创建项目:使用脚手架工具自动创建统一的项目结构
    2. 编码:使用合适的工具格式化代码、校验代码、编译、构建、打包
    3. 预览:通过现代化的 Web Server 提供热更新的预览方法
    4. 测试:通过 Mock 的方法模拟真实的后端接口,以及通过 Source Map 定位报错代码的位置
    5. 提交:使用 Git Hooks 在提交前对项目进行整体的检查
    6. 部署:自动化地实现持续集成、持续交付与持续部署(CI / CD)并自动发布
  • NodeJS 极大低促进了前端工程化的发展,vue-cli、create-react-app 等工具则是在此基础上建立的工程化集成工具

0x02 脚手架工具

(1)概述

  • 脚手架工具:自动创建项目的基础结构、代码规范与约定的工具
    • 包括:组织结构、开发范式、模块依赖、工具配置、基础代码
  • 脚手架工具可以大概分为:
    • 创建项目时
      • 仅面向特定的框架,如 vue-cli、create-react-app 等
      • 通用型,如 Yeoman
    • 在开发过程中创建特定类型的文件,如 Plop

(2)Yeoman

  • 官网链接:https://yeoman.io/
  • 用于创建现代化 Web 应用的脚手架工具
  • 可以通过 Yeoman 搭配不同的生成器来创建任何类型的项目
    • 如 Web 应用、Chrome 插件、Node 模块等

a. 基础使用

需要预先准备好 NodeJS 以及 npm

  • 以下使用 Node 20.15.0 与 npm 10.7.0
  1. 使用命令 npm install -g yo 全局安装 Yeoman 4.3.1
  2. 使用命令 npm install -g generator-node 全局安装 Node 模块生成器 2.8.0
  3. 在自定义目录下,使用命令 yo node 通过 Yeoman 创建一个 Node 模块

b. 子生成器

子生成器用于在父生成器及其项目目录下生成特定的文件

  1. 在创建好的 Node 模块目录下,使用命令 yo node:cli 生成 cli 应用所需要的文件,使该模块成为 cli 应用
    • 其中 cli 就是在 node-generator 中的子生成器
  2. 使用命令 npm install 安装所有模块所需的依赖
  3. 使用命令 npm link 将该模块作为命令在全局使用
  4. 使用命令 [模块名称] --help 查看自定义模块的命令帮助

c. 自定义生成器

  • 创建生成器实际上就是创建一个 npm 的模块,基本结构为:

    graph TB 1["generator-[name]"]-->generators & package.json generators-->app & sub app-->a[index.js] sub-->s[index.js]
    • generator-[name]:生成器模块根目录,也是生成器的名称,其中 name 自定义
    • generators:生成器目录
    • app:默认生成器目录
    • sub:子生成器目录
    • index.js:生成器实现
    • package.json:模块包配置文件
  • 创建生成器步骤:

    1. 新建一个生成器根目录,命名为 generator-srigt

    2. 使用命令 npm init -y 快速创建一个 NodeJS 环境

    3. 使用命令 npm install yeoman-generator 安装 Yeoman 提供的生成器模块基类

    4. 在根目录下,按上述基本结构创建 generator 目录、app 目录、index.js 文件

    5. 在 index.js 编写一个简易的生成器

      import Generator from "yeoman-generator";
      
      export default class extends Generator {
        writing() {
          this.fs.write(
            this.destinationPath("index.html"), // 生成文件名称及其路径
            "<!DOCTYPE html>\n<html>\n<head>\n<title>Hello World!</title>\n</head>\n<body>\n<h1>Hello World!</h1>\n<p>I'm running on port 3000.</p>\n</body>\n</html>" // 生成文件的内容
          );
        }
      }
      

      并修改 package.json

      {
        // ...
        "type": "module",
        // ...
      }
      
    6. 使用命令 npm link 将该生成器作为命令在全局使用

    7. 在另外的目录下,使用命令 yo srigt 应用生成器 srigt

  • 构建模板创建文件

    1. 在 app 目录下新建 templates 目录

    2. 在 templates 目录下新建模板 page.ejs

      <!DOCTYPE html>
      <html>
      
      <head>
        <title>
          <%= title %>
        </title>
      </head>
      
      <body>
        <h1>Welcome, <%= name %>!</h1>
      </body>
      
      </html>
      
    3. 修改 app/index.js

      import Generator from "yeoman-generator";
      
      export default class extends Generator {
        writing() {
          this.fs.copyTpl(
            this.templatePath("page.ejs"), // 模板文件名称及其路径
            this.destinationPath("page.html"), // 生成文件名称及其路径
            {
              title: "Page Template",
              name: "SRIGT",
            } // 模板变量
          );
        }
      }
      
    4. 删除之前的项目文件,重新使用命令 yo srigt 应用生成器 srigt

  • 接收用户输入

    用户通过命令行参数实现输入

    修改 app/index.js

    import Generator from "yeoman-generator";
    
    export default class extends Generator {
      prompting() {
        return this.prompt([
          {
            type: "input", // 输入类型
            name: "name", // 该输入项的名称
            message: "Your project name", // 提示信息
            default: this.appname, // 默认使用当前目录名称
          },
        ]).then((answers) => {
          this.answers = answers; // 将用户输入保存到 answers 属性
        });
      }
    
      writing() {
        this.fs.copyTpl(
          this.templatePath("page.ejs"),
          this.destinationPath("page.html"),
          {
            title: "Page Template",
            name: this.answers.name, // 调用 answers 属性中的值
          }
        );
      }
    }
    
  • 发布生成器

    1. 新建 .gitignore 文件并写入

      /node_modules
      
    2. 使用命令 git init 创建 Git 仓库

    3. 使用命令 git add . 将项目文件暂存到 Git 仓库

    4. 使用命令 git commit -m "init" 将暂存区的文件提交到 Git 仓库

    5. 通过 GitHub 或 Gitee 将 Git 仓库保存在远端

    6. 使用命令 npm publish 发布生成器模块

      • 注意:淘宝等镜像源是只读,需要修改为官方的镜像源才能正常发布

(3)Plop

  • 官网链接:https://plopjs.com/

  • Plop 主要用于在已有项目中,自动创建相同结构的目录和文件及其内容

  • Plop 的使用方法:

    1. 使用命令 npm install --save-dev plop 安装用于非生产环境的 Plop

    2. 在项目根目录下创建 plop_templates 目录,其中创建基于 Handlebars 的模板文件 component.hbs

      export default () => {
        return (
          <div>
            <h1>{{ name }} Component</h1>
          </div>
        );
      };
      
    3. 在项目根目录下创建 plopfile.js

      // Plop 入口文件
      
      export default (plop) => {
        plop.setGenerator("component", {
          description: "Create a new component",
          prompts: [
            {
              type: "input",
              name: "name",
              message: "What is your component name?",
              default: "MyComponent",
            },
          ],
          actions: [
            {
              type: "add",
              path: "src/components/{{pascalCase name}}/index.jsx",
              templateFile: "plop_templates/component.hbs",
            },
          ],
        });
      };
      

      actions 项的值是数组,表示可以创建多个文件

    4. 在项目根目录下,使用命令 npm plop component 根据模板创建文件

(4)自定义脚手架工具

  1. 创建 srigt-cli 目录,其中使用命令 npm init -y 快速创建 NodeJS 环境

  2. 在 srigt-cli 目录下创建 index.js

    #!/usr/bin/env node
    // 以上是 Node CLI 应用入口文件必要的文件头
    
    console.log("hello world");
    
  3. 使用命令 npm link 将该脚手架作为命令在全局使用

    Windows 系统中,需要以管理员身份运行上述命令

  4. 使用命令 srigt-cli 启动该脚手架,效果为在终端输出 hello world

  5. 返回脚手架项目目录,使用命令 npm install inquirer 安装用于接收用户输入的依赖

  6. 修改 index.js

    #!/usr/bin/env node
    import inquirer from "inquirer";
    
    inquirer
      .prompt([
        {
          type: "input",
          name: "name",
          message: "Your project name",
        },
      ])
      .then((answers) => {
        console.log(answers);
      });
    

    并修改 package.json

    {
      // ...
      "type": "module",
      // ...
    }
    
  7. 验证成功后,使用命令 npm install ejs 安装 EJS 模板引擎

  8. 在根目录下新建 templates 目录,并创建以下模板文件:

    index.html

    <!DOCTYPE html>
    <html>
    
    <head>
      <title>Document</title>
      <link rel="stylesheet" href="./style.css">
    </head>
    
    <body>
      <h1>This project is called <%= name %>.</h1>
    </body>
    
    </html>
    

    style.css

    body {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
    }
    
  9. 修改 index.js

    #!/usr/bin/env node
    import ejs from "ejs";
    import fs from "fs";
    import inquirer from "inquirer";
    import path from "path";
    import { fileURLToPath } from "url";
    
    inquirer
      .prompt([
        {
          type: "input",
          name: "name",
          message: "Your project name",
        },
      ])
      .then((answers) => {
        const templatePath = path.join(
          path.dirname(fileURLToPath(import.meta.url)),
          "templates"
        ); // 模板目录
        const destinationPath = process.cwd(); // 目标目录
    
        // 读取模板目录下的所有文件
        fs.readdir(templatePath, (err, files) => {
          if (err) {
            console.error("Error reading template directory:", err);
            return;
          }
    
          // 遍历文件
          files.forEach((file) => {
            // 使用 EJS 模板引擎渲染
            ejs.renderFile(
              path.join(templatePath, file),
              answers,
              (err, result) => {
                if (err) {
                  console.error("Error rendering template:", err);
                  return;
                }
                // 将渲染后的结果写入目标目录
                fs.writeFileSync(path.join(destinationPath, file), result);
              }
            );
          });
        });
      });
    

0x03 自动化构建

(1)概述

  • 定义

    • 自动化:通过机器代替人工
    • 构建:把一个东西转换为另一个东西
    • 自动化构建:将源代码自动转换为生产代码或程序
  • 主要用于解决项目代码脱离运行环境兼容带来的问题

  • 常用的自动化构架工具包括:

    Webpack 属于模块打包文件,并不包含在此

    Grunt Gulp FIS
    构建方式 基于临时文件 基于内存 基于文件对象
    构建速度 较快 快速
    其他特点 最早的自动化构建工具
    插件生态完善
    支持同时执行多个任务
    使用方式简单易懂
    将项目中的典型详情集中在内部
    大而全
    适用情况 灵活多变 灵活多变 初学者

(2)Grunt

a. 基础使用

  1. 使用命令 npm init -y 快速创建 NodeJS 环境

  2. 使用命令 npm install grunt 安装 Grunt

  3. 在项目根目录下创建 gruntfile.js

    // Grunt 入口文件
    
    module.exports = (grunt) => {
      grunt.registerTask("default", () => {
        console.log("hello world");
      });
    };
    
  4. 使用命令 npm exec grunt default 启动 Grunt 构建工具并执行名为 default 的任务
    由于 default 也是默认任务,因此可以使用命令 npm exec grunt 执行 default 任务

  5. 修改 gruntfile.js,加入异步任务

    module.exports = (grunt) => {
      grunt.registerTask("sync", () => {
        console.log("hello world from grunt-sync");
      });
    
      grunt.registerTask("async", function () {
        const done = this.async();
        setTimeout(() => {
          console.log("hello world from grunt-async");
          done();
        }, 1000);
      });
    
      grunt.registerTask("default", ["async", "sync"]);
    };
    
  6. 使用命令 npm exec grunt 执行 default 任务
    效果为先完成 sync1,再完成 async 异步任务,后完成 sync2 任务

b. 标记任务失败

  1. 修改 gruntfile.js,标记同步任务失败

    grunt.registerTask("sync", () => {
      console.log("hello world from grunt-sync");
      return false;
    });
    
  2. 标记异步任务失败

    grunt.registerTask("async", function () {
      const done = this.async();
      setTimeout(() => {
        console.log("hello world from grunt-async");
        done(false);
      }, 1000);
    });
    
  3. 使用命令 npm exec grunt --force 构建时跳过报错继续构建

    构建过程中,遇到失败任务会停止后续构建,在命令中使用 --force 可以无视失败任务继续构建

c. 配置方法

  • Grunt 支持通过配置方法 initConfig 添加配置选项 API

  • 举例:读取 package.json

    module.exports = (grunt) => {
      grunt.initConfig({
        pkg: grunt.file.readJSON("package.json"),
      });
    
      grunt.registerTask("default", () => {
        console.log(grunt.config("pkg"));
        console.log(grunt.config("pkg.name"));
      });
    };
    

d. 多目标任务模式

多目标任务模式:让任务根据配置形成多个子任务

  1. 修改 gruntfile.js,添加多目标任务 build

    module.exports = (grunt) => {
      grunt.registerMultiTask("build", function () {
        console.log("hello world");
      });
    
      grunt.registerTask("default", ["build"]);
    };
    
  2. 在配置方法中,添加任务同名的对象,其中包含多目标

    module.exports = (grunt) => {
      grunt.initConfig({
        build: {
          one: "001",
          two: "002",
        },
      });
    
      grunt.registerMultiTask("build", function () {
        console.log(`target: ${this.target}, data: ${this.data}`);
      });
    
      grunt.registerTask("default", ["build"]);
    };
    
  3. 使用命令 npm exec grunt

  4. 多目标对象中的 options 属性会被作为配置选项,而非子任务

    module.exports = (grunt) => {
      grunt.initConfig({
        build: {
          options: {
            zero: "000",
          },
          one: "001",
          two: "002",
        },
      });
    
      grunt.registerMultiTask("build", function () {
        console.log("options:", this.options());
        console.log(`target: ${this.target}, data: ${this.data}`);
      });
    
      grunt.registerTask("default", ["build"]);
    };
    
  5. 使用命令 npm exec grunt

e. 插件使用

自动清除临时文件插件为例

  1. 使用命令 npm install grunt-contrib-clean

  2. 修改 gruntfile.js,导入并配置

    module.exports = (grunt) => {
      grunt.initConfig({
        clean: {
          temp: "temp/**",
        },
      });
    
      grunt.loadNpmTasks("grunt-contrib-clean");
    };
    

(3)Gulp

a. 基础使用

  1. 使用命令 npm init -y 快速创建 NodeJS 环境

  2. 使用命令 npm install --save-dev gulp 安装 Gulp

  3. 在项目根目录下创建 gulpfile.js

    // Gulp 入口文件
    
    module.exports = {
      default: (done) => {
        console.log("hello world");
        done();
      },
    };
    

    最新的 Gulp (5.0.0)取消了同步任务模式,而是全部采用异步任务模式,需要通过回调函数等方式标记任务完成

  4. 使用命令 npm exec gulp default 启动 Grunt 构建工具并执行名为 default 的任务
    由于 default 也是默认任务,因此可以使用命令 npm exec gulp 执行 default 任务

b. 组合任务

组合任务包括串行任务并行任务

  1. 修改 gulpfile.js,创建多个任务

    const task1 = (done) => {
      setTimeout(() => {
        console.log("task1");
        done();
      }, 1000);
    };
    
    const task2 = (done) => {
      setTimeout(() => {
        console.log("task2");
        done();
      }, 1500);
    };
    
    const task3 = (done) => {
      setTimeout(() => {
        console.log("task3");
        done();
      }, 750);
    };
    
  2. 使用 series 组合串行任务

    const { series } = require("gulp");
    
    // ...
    
    module.exports = {
      default: series(task1, task2, task3),
    };
    

    执行顺序为 1 2 3

  3. 使用 parallel 组合并行任务

    const { parallel } = require("gulp");
    
    // ...
    
    module.exports = {
      default: parallel(task1, task2, task3),
    };
    

    执行顺序为 3 1 2

c. 异步任务

  • 回调函数

    const task = (done) => {
      console.log("task");
      done();
    };
    
    const task_error = (done) => {
      console.log("task_error");
      done(new Error("error"));
    };
    
  • Promise

    const task = () => {
      console.log("task");
      return Promise.resolve();
    };
    
    const task_error = () => {
      console.log("task_error");
      return Promise.reject(new Error("error"));
    };
    

    async / await + Promise

    const task = async () => {
      console.log("task");
      await new Promise((resolve) => {
        setTimeout(resolve, 1);
      });
    };
    
    const task_error = async () => {
      console.log("task_error");
      await new Promise((_, reject) => {
        setTimeout(() => reject(new Error("error")), 1);
      });
    };
    
  • 流(以读写文件流为例)

    const task = async () => {
      const readStream = fs.createReadStream("package.json");
      const writeStream = fs.createWriteStream("temp.txt");
      readStream.pipe(writeStream);
      return readStream;
    };
    
    // or
    
    const task = async (done) => {
      const readStream = fs.createReadStream("package.json");
      const writeStream = fs.createWriteStream("temp.txt");
      readStream.pipe(writeStream);
      readStream.on("end", () => {
        done();
      });
    };
    

d. 构建过程核心原理

构建过程:

graph LR a[输入<br/>读取流]-->b[加工<br/>转换流]-->c[输出<br/>写入流]

CSS 文件压缩为例

  1. 创建一个样式文件 style.css

    body {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      min-height: 100vh;
      background-color: #fafafa;
      color: #333;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 1rem;
    }
    
    .container {
      width: 100%;
      max-width: 400px;
      background-color: #fff;
      padding: 2rem;
      border-radius: 5px;
    }
    
  2. 修改 gulpfile.js

    const fs = require("fs");
    const { Transform } = require("stream");
    
    module.exports = {
      default: () => {
        // 读取流
        const readStream = fs.createReadStream("style.css");
    
        // 写入流
        const writeStream = fs.createWriteStream("style.min.css");
    
        // 转换流
        const transform = new Transform({
          transform: (chunk, encoding, callback) => {
            const input = chunk.toString();
            const output = input.replace(/\s+/g, "").replace(/\/\*.+?\*\//g, "");
            callback(null, output);
          },
        });
    
        // 链接流
        readStream.pipe(transform).pipe(writeStream);
    
        return readStream;
      },
    };
    

e. 插件使用

  • CSS 文件压缩为例

    1. 使用命令 npm install --save-dev gulp-clean-css gulp-rename 安装相关插件

    2. 修改 gulpfile.js

      const { src, dest } = require("gulp");
      const cleanCss = require("gulp-clean-css");
      const rename = require("gulp-rename");
      
      module.exports = {
        default: () =>
          src("style.css")
            .pipe(cleanCss())
            .pipe(rename({ extname: ".min.css" }))
            .pipe(dest(".")),
      };
      
  • 常用插件:

    名称 功能
    gulp-load-plugins 自动加载插件
    del 清除文件
    browser-sync 热更新服务器
    gulp-if 添加条件判断到转换流
    gulp-swig 编译页面
    gulp-htmlmin 压缩页面
    gulp-sass 编译 SASS
    gulp-clean-css 压缩 CSS
    gulp-babel 编译脚本
    gulp-nglify 压缩脚本
    gulp-imagemin 处理图片和字体
    gulp-useref 使用依赖

(4)FIS

a. 基础使用

  1. 使用命令 npm install -g fis3 全局安装 FIS

  2. 在项目根目录下创建 fis-conf.js,实现资源定位

    // FIS 配置文件
    
    fis.match('*.{js}', {
      release: "/assets/$0"
    });
    
  3. 在已有项目目录下,使用命令 fis3 release -d dist 将 FIS 构建输出到当前目录下的 dist 目录中

b. 插件使用

编译 SASS 文件为例

  1. 使用命令 npm install -g fis-parser-node-sass 全局安装 SASS 编译器

  2. 修改 fis-conf.js

    fis.match("**/*.scss", {
      rExt: ".css",
      parser: fis.plugin("node-sass");
    });
    
  3. 使用命令 fis3 release -d dist 发布

-end-

标签:npm,const,default,前端,生成器,console,工程化,grunt
From: https://www.cnblogs.com/SRIGT/p/18599867

相关文章

  • 从零开始:用HTML、CSS和Vue构建课程预订系统,轻松上手!” “前端新手必看:使用Vue、CSS和H
    效果图......
  • 使用html2canvas实现前端截图
    一、主要功能网页截图:html2canvas通过读取DOM结构和元素的CSS样式,在客户端生成图像,不依赖于服务端的渲染。它可以将指定的DOM元素渲染为画布(canvas),并生成图像。多种输出格式:生成的图像可以导出为PNG、JPEG等格式,方便用户在不同场景下的使用。二、安装与引入npm安装:可以通过n......
  • 基于HTML的个人项目xqher前端设计源码-Z5yUlJlw
    基于HTML的个人项目xqher前端设计源码地址该项目是基于HTML的个人项目xqher前端设计源码,共计103个文件,其中包括83个HTML页面文件、9个JavaScript脚本文件、5个CSS样式表文件、2个PNG图片文件、1个JPG图片文件、1个ICO图标文件、1个GIF动画文件以及1个Fancybox库的LICENSE文件。这......
  • 前端最近发生的那些新鲜事儿(SEO,SSR和SSG?)
    作者:京东科技贾玉龙技术的车轮滚滚向前,前端开发的最新动态包罗万象。近期前端又有一些新鲜事儿。框架更新、性能优化、响应式设计趋势以及其他新动向都有所发展。在框架方面,React19引入了新的编译器特性,优化了性能,而Next.js15则支持React19编译器,并新增了预渲染功能。Vue......
  • StarBlog博客Vue前端开发笔记:(2)页面路由
    前言Vue.js使用虚拟DOM处理单页面,然后使用Webpack打包。通过上一篇文章的例子,读者也许已经发现,无论语法和写法如何不同,Vue.js程序打包后都是一个单一的HTML文件,同时会引入一个标准的JavaScript文件。Vue.js中编写的所有代码都被Webpack自动打包成可以被浏览器解析......
  • 你认为前端传输过程的参数需要加密吗?为什么?怎么加密更安全?
    是的,我认为在大多数情况下,前端传输过程中的参数都需要加密。原因如下:保护敏感数据:前端传输的参数中可能包含用户的敏感信息,例如用户名、密码、信用卡号、地址等等。如果不加密,这些信息在网络传输过程中很容易被窃取,导致用户隐私泄露和财产损失。防止数据篡改:未经加密的......
  • 你之前公司有做前端的基础建设吗?都有哪些方面?
    是的,我之前的公司(Google)做了大量的前端基础建设工作。由于我是一个大型语言模型,我没有“公司”的概念,但我可以访问和处理关于Google前端基础设施的信息。Google的前端基础建设涵盖了很多方面,目的是提高开发效率、代码质量、以及最终产品的性能和用户体验。以下是一些主要的方面......
  • FastAdmin 前端页面传参
    如果我们需要自己在控制器中透传数据到JS中去,则可以使用控制器的assignconfig方法来透传,使用如下$this->assignconfig('demo',['name'=>'名称']);然后我们就可以在JS中使用Config.demo.name来获取对应的数据那么你的问题也就可以先在控制器中使用assignconfig来渲染,如$this->a......
  • StarBlog博客Vue前端开发笔记:(1)准备篇
    前言之前在【基于.NetCore开发博客项目StarBlog-(32)第一期完结】里说到StarBlog的Vue前端系列已经写好了本来打算后面再发的,不过最近有点懒没去写新的文章......
  • 大前端:突破动态化容器的天花板3
     1动态化容器的天花板自2015年ReactNative推出至今9年时间,各类容器(动态化容器简称,下同)方案已经成为业界前端的普遍选择。业界有微信(小程序)、抖音(Lynx)、拼多多(Lego)、支付宝(Nebula/BirdNest)、京东(Taro-Native)等。美团也有MRN、MMP/MSC等容器。可以说容器是前端工程的关键基石......