跳到主要内容

脚手架开发

具备脚手架开发的能力

1、可以减少开发人员配置脚手架带来的时间损耗(特殊功能可以fork脚手架后再自行定制);

2、统一项目结构,方便管理,也降低项目交接时带来的需要熟悉项目的时间;

3、方便统一技术栈,可以预先引入固定的组件库以及相关库;

意义:

提高开发人员在多个项目之间的快速切换能力,提高项目可维护性,统一公司技术栈,避免因为环境不同导致奇怪的问题。

常用库​

  • chalk:(控制台字符样式)
  • commander: (实现 NodeJS 命令行)
  • download:(实现文件远程下载)
  • fs-extra:(增强的基础文件操作库)
  • handlebars:实现模板字符替换)
  • inquirer:(实现命令行之间的交互)
  • log-symbols:(为各种日志级别提供着色符号)
  • ora:(优雅终端 Spinner 等待动画)
  • update-notifier:(npm 在线检查更新)

集成构建工具 【webpack/gulp】​

手写q-cli脚手架,集成webpack打包工具

特点:

  • webpack5现在打包速度也提升了不少
  • 基于 webpack 构建,并带有合理的默认配置;
  • 统一处理好webpack配置优化、打包优化等,用户无需重新配置
  • 开发者可以做到零配置,就可以快速开发项目
  • 暴露tool.config.js ,让用户可以自定义配置
  • 通过配置脚手架tool.config.js配置webpack

需要用到的webpack工具:【需要安装在生产环境,否则用户使用的话,是找不到这个包,因为在nom install是安装这个包的dependencies】

  • webpack-chain
  • webpack-bundle-analyzer
  • webpack-dev-server
  • webpack-merge

思路:​

  • 1、在根目录下生成q.config.js配置文件

  • 2、tool.config.js的配置与webpack配置合并

  • 3、可注册run命令,通过npx tool-cli run,命令运行,执行webpack配置

    • run.js 【webpack沙箱环境启动】
    js
    import webpackConfig from './lib/webpack/webpack.config' //webpack配置
    type OptionsType = {
    dev:string,
    build:string
    }
    // 可接受options参数 【如dev、build等】
    const run = (options:OptionsType)=>{
    const nowPath = process.cwd() //获取当前工作目录
    webpackConfig(options) //运行webpack配置

    }
    export default run

    q.config.ts声明,参考vue-cli配置

    js
    import HtmlWebpackPlugin from "html-webpack-plugin";
    import { Configuration as WebpackConfig } from "webpack";
    export declare type Configuration = {
    /**
    * 生成HTML页面配置:https://webpack.js.org/plugins/html-webpack-plugin/
    */
    pages?: HtmlWebpackPlugin.Options;
    /**
    * 开发环境配置: https://webpack.js.org/configuration/dev-server/
    */
    devServer?: {};
    /**
    * 如果这个值是一个对象,则会通过 webpack-merge 合并到最终的配置中。
    */
    configureWebpack?: (config: WebpackConfig) => WebpackConfig;
    };

集成eslint、perttier​

eslint​

  • Linter

它只是解析和报告代码。特别是,Linter 对象不处理配置对象或文件

Linter 是一个构造函数,您可以通过传入要使用的选项来创建一个新实例

支持typescript:

当分析 TypeScript 文件时, 需要使用@typescript-eslint类型检查器

  • @typescript-eslint

流行脚手架源码分析 【vue-cli,create-react-app】​

@vue/cli功能主要是三部分组成的:

  • @vue/cli 负责命令行参数收集
  • @vue/cli-service 这个包是启动vue的引擎及核心,承载webpack配置。
  • @vue/plugin-xxx vue-cli的插件,一个插件对应一个功能,与上边用户自定义feature选项一一对应。

upgrade 检查更新​

  • update-notifier:npm 在线检查更新
  • update-notifier 检测更新机制是通过 package.json 文件的 name 字段值和 version 字段值来进行校验:它通过 name 字段值从 npmjs 获取库的最新版本号,然后再跟本地库的 version 字段值进行比对,如果本地库的版本号低于 npmjs 上最新版本号,则会有相关的更新提示。
js
// 引用 update-notifier 库,用于检查更新
const updateNotifier = require('update-notifier')
// 引用 chalk 库,用于控制台字符样式
const chalk = require('chalk')
// 引入 package.json 文件,用于 update-notifier 库读取相关信息
const pkg = require('../package.json')

// updateNotifier 是 update-notifier 的方法,其他方法可到 npmjs 查看
const notifier = updateNotifier({
// 从 package.json 获取 name 和 version 进行查询
pkg,
// 设定检查更新周期,默认为 1000 * 60 * 60 * 24(1 天)
// 这里设定为 1000 毫秒(1秒)
updateCheckInterval: 1000,
})

function updateChk() {
// 当检测到版本时,notifier.update 会返回 Object
// 此时可以用 notifier.update.latest 获取最新版本号
if (notifier.update) {
console.log(`New version available: ${chalk.cyan(notifier.update.latest)}, it's recommended that you update before using.`)
notifier.notify()
} else {
console.log('No new version is available.')
}
}

// 将上面的 updateChk() 方法导出
module.exports = updateChk

utils工具方法​

js
import path from 'path';
import fs from 'fs-extra';
// 获取工程目录
export const appDirectory = fs.realpathSync(process.cwd());
// 解析路径
export const resolveApp = function (relativePath: string) {
return path.resolve(appDirectory, relativePath);
};
//异步等待
function sleep(timeout) {
return new Promise((resolve => {
setTimeout(resolve, timeout);
}));
}

优雅生成配置文件​

  • 生成q.config.ts 配置文件,可合并webpack配置
  • 模板文件生成package.json
    • 生成特定依赖
    • react模板,添加react、react-dom依赖
    • vue模板,添加vue依赖
  • 针对模板项目【vue、react】生成特定的配置文件q.config.js
    • vue模板,需要使用vue-loader
  • 集成eslint配置, 生成.eslint.json
  • 集成perttier,生成perttier.json
  • 生成tsconfig.json

Generator.ts

  • 关键代码
js
async render(source, additionalData = {}, ejsOptions = {}) {
const baseDir = extractCallDir(); // 获取调用者的文件目录路径
source = path.resolve(baseDir, source);
const _files = await globby(["**/*"], {
cwd: source, // 当前工作目录
dot: true // 允许匹配以.开头的
});
const data = this._resolveData(additionalData);
for (const rawPath of _files) {
const sourcePath = path.resolve(source, rawPath);
// 解析文件内容
const content = await this.renderFile(sourcePath, data, ejsOptions);
Object.assign(this.files, { [rawPath]: content });
}
}

// 利用 ejs 渲染模板文件,解析文件内容
async renderFile(name, data, ejsOptions) {
const template = fs.readFileSync(name, "utf-8");
const yamlFront = await import("yaml-front-matter");
// 解析为 YAML 文档
const parsed = yamlFront.loadFront(template);
const content = parsed.__content;
let finalTemplate = content.trim() + `\n`;
// 返回渲染结果的字符串
return ejs.render(finalTemplate, data, ejsOptions);
}

模板渲染​

  • 将命令行的参数传入到模版里面,渲染出来,然后写入到文件中就可以了

ejs渲染引擎​

  • 用来将 EJS模版结合着 JSON数据转换为 HTML

文档:https://ejs.bootcss.com/

基本语法:

  • 模板语法:
    • <%= %> : 输出到html页面
    • <% %> : 一般用于控制语句和声明语句,比如:if…else,switch,for,var声明等
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= projectname %></title>
</head>
<body>
<div>
<%= projectname %>
</div>
</body>
</html>
  • render
    • 将字符串作为模板,是一种同步方法
js
ejs.render(str,{
projectname:'xxx'
}, options)
  • renderFile
    • filename 参数是目标文件路径
    • data 是你要传进模板的数据对象
    • option 是配置选项, 可以省略, 具体内容查看文档
    • function(err, str){} 是回调函数.
    • err 是错误信息, 没有错误时为 null.
    • str 是模板生成的 HTML 内容.
js
ejs.renderFile(filename, {
projectname:'xxx'
}, options, function(err, str){});

发布脚手架​

1、npm公开包的名称需要是唯一的。

2、指定官方源,确保能发包成功

  • package.json的 publishConfig字段指定官方源
    • registry 源地址
    • tag 发布对应的 dist-tag标签
    • access 如果是 scoped包,一定需要设置为 public(付费账号除外)

因为淘宝源只能下载包,不能上传包。但是不用淘宝源,安装其他依赖又慢的不行。 解决方法: 安装依赖,统一从淘宝源拉,保证依赖安装速度。发包时借助 npm包的package.json的 publishConfig字段指定官方源,确保能发包成功

js
"publishConfig": {
"registry": "https://registry.npmjs.org/", //发布到npm官网上,不是淘宝源
"access": "public"
},

遇到问题总结​

ora模块问题​

  • 安装版本v6+, import ora from 'ora'出现报错的情况的话, 可安装v5+版本
    • v5版本 是用commonjs模块的
    • v6版本 是ES模块
text
npm i -S ora@5

使用ESM模块问题​

如何将我的 CommonJS 项目移动到 ESM / 如何让我的 TypeScript 项目输出 ESM?

优雅读取package.json​

js
const pkg = fs.readFileSync('./package.json')
const json = JSON.parse(pkg)

webpack-dev-server问题​

V3版本和V4版本区别:

js
const compiler = Webpack(devConfig);
const devServer = new WebpackDevServer(
{ hot: true, open: true, host: "localhost", port: 8000 },
compiler
);
devServer.startCallback(() => {
console.log("启动中...");
});