写点什么

「推荐收藏」提高组件库 Level 必做好这六件事

作者:GFE
  • 2022-11-16
    北京
  • 本文字数:4761 字

    阅读完需:约 16 分钟

「推荐收藏」提高组件库Level必做好这六件事

基础概念

随着前端工程化的概念越来越深入人心,脚手架应运而生。简单来说,「前端脚手架」就是指通过选择几个选项快速搭建项目基础代码的工具。前端脚手架可以有效避免我们 ctrl + C 和 ctrl + V 相同的代码框架和基础配置。

脚手架思路

在动手开始开发脚手架 CLI 之前我们先捋一下思路,纵览业界比较流行的几个脚手架,会发现虽然它们功能丰富度和复杂程度不一样,但是总体来说都会包含以下基本功能

CLI 搭建项目

  • 根据用户输入生成配置文件

  • 下载指定项目模板

  • 在目标目录生成新项目

CLI 运行项目

  • 本地启动预览

  • 热更新

  • 语法、代码规范检测

脚手架架构图

通过架构图了解下脚手架的大致工作流程



插件依赖

开发脚手架常用插件



创建脚手架


初始化项目

下载 node,创建一个新的文件夹,cmd 进入到文件夹目录运行:npm init,生成 package.json 文件


{  "name": "@gfe/cli",  "version": "1.0.0",  "description": "",  "main": "index.js",  "scripts": {    "test": "echo \"Error: no test specified\" && exit 1"  },  "author": "xcc",  "license": "ISC"}
复制代码


进入项目

修改 package.json 中的 bin 参数,专门放置用户的自定义命令,指定可执行文件的位置,bin 里的命令是可执行命令,模块安装的时候如果是全局安装,则 npm 会为 bin 中配置的文件创建一个全局软连接,在命令行工具里可以直接执行


{  "name": "@gfe/cli",  "version": "1.0.0",  "description": "",  "main": "index.js",  "bin":{      "gfe-cli": "src/gfe-cli.js"  },  "scripts": {    "test": "echo \"Error: no test specified\" && exit 1"  },  "author": "xcc",  "license": "ISC"}
复制代码


项目中新建 src 文件夹,新增 gfe-cli.js,文件为命令入口文件,文件第一行必须是 #!/usr/bin/env node,代表执行这个脚本的时候,调用/usr/bin 下的 node 解释器。

处理命令行

我们通过 commander 来设置不同的命令。


command 方法设置命令的名字、description 方法是设置命令的描述、alias 方法设置命令简称【懒人必备】、options 设置命令需要的参数。commander 更详细的文档可以去 commander 官网查看。


我们脚手架先加入三个命令:项目创建、项目初始化、项目启动。源代码在 src/gfe-cli.js 中。

项目模板

脚手架可以帮助我们快速生成一套指定的项目结构和配置,最常用的方式就是我们提前准备好一套通用的、易用的、规范的项目模板存放在指定位置,在脚手架执行创建项目命令的时候,直接将 **准备好的模板 **拷贝到目标目录下。


PS:这里有两个点需要我们关注一下:

项目模板存放位置方式

第一种是和脚手架打包在一起,在安装脚手架的时候就会将项目模板存放在全局目录下了,这种方式每次创建项目的时候都是从本地拷贝的速度很快,但是项目模板自身升级比较困难


第二种是将项目模板存在远端仓库(比如 gitlab 仓库),这种方式每次创建项目的时候都是通过某个地址 动态下载 的,项目模板更新方便。选择第二种,解耦了模板和脚手架,方便更新维护。


代码源码:


#!/usr/bin/env nodeconst program = require("commander"); // 用于捕获命令const chalk = require("chalk"); // 用于字体加色const download = require("download-git-repo"); // 用于下载git的包const inquirer = require("inquirer"); // 用于与用户输入做交互const ora = require("ora"); // 进度显示const symbols = require("log-symbols"); // 信息前面加✔或✖
program .version(require("../package.json").version, "-v,--version") .command("init <name>") .description("初始化项目中...") .action((name) => { inquirer .prompt([{ type: "list", name: "templates", message: "templates:", choices: [{ name: "vue", value: { gitUrl: "http://***/vue-demo.git", } }, { name: "react", value: { gitUrl: "http://***/react-demo.git", } }] }]) .then((answers) => { const { gitUrl } = answers.templates download( `${gitUrl}`, `./${name}`, { clone: true }, function (err) { if (err) { console.log(err); } else { console.log(symbols.success, chalk.green("创建项目成功")); } } ); }); });
program.parse(process.argv);
复制代码


如果下载完项目,需要执行 shell 命令

node 脚本中,执行指定的 shell 命令


const { spawn } =  require('child_process')// 执行终端命令的子线程const terminal = async (...args) => {  return new Promise((resolve) => {    // 子线程    const proc = spawn(...args)    // 子线程 proc --终端的输出转到--> 主线程 process    proc.stdout.pipe(process.stdout)    proc.stderr.pipe(process.stderr)    proc.on('close', () => {      resolve()    })  })}module.exports = {  terminal}
复制代码


const install = async(name) => {  const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'  await utils.terminal(npm, ['install'], {cwd: `./${name}`})}
// 初始化module.exports = install
复制代码


执行进程命令 npm 需要判断 const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'

配置全局 CLI 命令

我们的脚手架开发完成发布到 npm 后,可以通过 npm install -g gfe-cli **全局安装 **的方式安装就可以直接使用 CLI 命令了。


但是开发过程中为了 **方便调试和实时同步 **修改,需要另外的方式将 CLI 命令链接到全局。


可以在 gfe-cli 目录下执行 **npm link **,该命令可以将 gfe-cli 下的 bin 命令软链接到全局,直接使用。


Tips:npm link 的时候遇到过几个小坑跟大家分享一下


在开发的过程中可能会遇到 **执行命令失败 **的情况,比如 zsh: command not found: gfe-cli。


  • 尝试重新链接 npm link,再失败的话就尝试先删除掉全局命令 npm unlink gfe-cli 然后再链接,一般情况下这样就可以解决了。

  • 还是不行就去全局目录里删除 gfe-cli 文件夹

发布配置

{  "name": "@gfe/cli",  "version": "1.0.0",  "description": "",  "main": "index.js",  "bin":{    "gfe-cli": "src/gfe-cli.js"  },  "keywords": [    "tools",    "javascript",    "cli"  ],  "scripts": {    "test": "echo \"Error: no test specified\" && exit 1"  },  "author": "xcc",  "license": "ISC"}
复制代码


name 是包的名字,可以直接写包名,比如 cli,或者添加域,类似于 @gfe/cli 这种,@后面是你 npm 注册的用户名。key 为包的关键字。


.npmrc 文件,发布配置文件


registry=https://*******/email=****always-auth=true_auth=****
复制代码


.npmignore 文件,忽略上传文件


node_modules/.babelrctsconfig.json.eslintrc.js
复制代码


npm publish 发布

开发注意


简述 ESM 和 CJS 模块

早期 Javascript 这门语言是没有模块化的概念的,直到 nodejs 诞生,才把模块系统引入 js。nodejs 使用的是 CJS(Commonjs)规范,也就是我们平时所见的 require、module.exports。而 js 语言标准的模块规范是 ESM(Ecmascript Module),也就是我们在前端工程大量使用的 import、export 语法。nodejs 已经在逐步支持 ESM,目前很多主流浏览器也已经原生支持 ESM。

项目使用的是 ESM 还是 CJS?

Node.js 8.5.0 增加了 ESM 的实验性支持,使用**--experimental-modules **标识,加上以 **.mjs **为后缀的文件名可以让 nodejs 执行 ESM 规范导入导出的模块。例如:


node --experimental-modules index.mjs
复制代码


Node.js 12.17.0,移除了**--experimental-modules **标识。虽然 ESM 还是试验性的,但已经相对稳定了。


之后的版本,nodejs 按以下流程判断模块系统是用 ESM 还是 CJS:



如何让 require 和 import 在同一文件中使用

ES6 标准发布后,module 成为标准,标准的使用是以 export 指令导出接口,以 import 引入模块,但是在我们一贯的 node 模块中,我们采用的是 CommonJS 规范,使用 require 引入模块,使用 module.exports 导出接口


node 中通常引入模块是 require 语法,而非 import 语法。 可编写一个 js 文件 即支持 require 语法,又支持 import 语法


export const funA = () => {} ; // 函数funA export const funB = () => {} ; // 函数funB
复制代码


改为


const funA = () => {} ; // 函数funA const funB = () => {} ; // 函数funBmodule.exports = {  funA:funA,  funB:funB,}
复制代码


运行 serve.js


const Utils = require('./util')
复制代码


nodejs 原本支持 commonjs 的模块化规范,就是 require 这类型的


如果想要使用 es6 export import 的模块化规范,启动方式:


将文件修改为 mjs 后缀,或者修改 package.json 中的 type: module

Rollup 打包注意


为何使用 Rollup 打包工具

Rollup.js 是一个模块打包工具,可以帮助你从一个入口文件开始,将所有使用到的模块文件都打包到一个最终的发布文件中(极其适合构建一个工具库,这也是选择用 rollup 来打包的原因)


Rollup.js 有两个重要的特性,其中一个就是它使用 ES6 的模块标准,这意味着你可以直接使用 import 和 export 而不需要引入 babel(当然,在现在的项目中,babel 可以说是必用的工具了)


Rollup.js 一个重要特性叫做'tree-shaking',这个特性可以帮助你将无用代码(即没有使用到的代码)从最终的生成文件中删去。(这个特性是基于 ES6 模块的静态分析的,也就是说,只有 export 而没有 import 的变量是不会被打包到最终代码中的)

问题一 报错 SyntaxError: Unexpected character '!'

入口文件 首行必须添加 #!/usr/bin/env node 用 Rollup 打包报错 可在 rollup.config.js 配置这段代码


rollup 配置文件的两个属性:banner 和 footer,这两个属性会在生成文件的开头和结尾插入一段你自定义的字符串 可新增参数 banner:'#!/usr/bin/env node'

问题二 报错 [!] Error: 'default' is not exported by ****

rollup 默认是不支持 CommonJS 模块的,自己写的时候可以尽量避免使用 CommonJS 模块的语法,但有些外部库的是 cjs 或者 umd(由 webpack 打包的),所以使用这些外部库就需要支持 CommonJS 模块


可引入 rollup-plugin-commonjs 处理

问题三 报错 (!) Unresolved dependencies

  1. 帮助 Rollup 查找外部模块 引入 rollup-plugin-node-resolve

  2. external 属性:使用 rollup 打包,我们在自己的库中需要使用第三方库,例如 fs,path 等,又不想在最终生成的打包文件中出现 fs,path

配置 rollup.config.js

import json from 'rollup-plugin-json';import resolve from 'rollup-plugin-node-resolve';import commonjs from 'rollup-plugin-commonjs';import del from 'rollup-plugin-delete'import { dependencies } from './package.json'const external = Object.keys(dependencies || '')export default {  input: './src/gfe-cli.js',  output: {    file: './dist/gfe-cli.mjs',    banner: '#!/usr/bin/env node'  },  external:[...external, 'fs', 'path'],  plugins: [    del({      targets: 'dist/*'    }),    commonjs(),    resolve({      modulesOnly: true,      preferBuiltins: true    }),    json()  ],};
复制代码


npm 发布注意


npm 发布私库成功之后,下载找不到地址?

原因:下载未配置私库地址


例如: 脚手架是**@gfe/n-cli**,需要在全局安装 -g


在全局要配置:打开 cmd 自动定位到 C:\Users\55409>


在 55409 文件夹里配置 .npmrc 文件


@gfe:registry=https://****/registry=https://registry.npm.taobao.org/
复制代码


@gfe 需要对应私库地址 其余内部依赖走 npm 公共


发布于: 刚刚阅读数: 2
用户头像

GFE

关注

一起成长、一起分享、一起创造价值 2020-04-09 加入

💻 努力打造这块领土,构建完整良好的知识体系 🏠 保持对技术的热爱,保持初心,保持丰富的想象力 🏷️ 标签 | 热爱技术 钻研探索 有责任心 前端领域 📥 邮箱 | gfe@goldentec.com

评论

发布
暂无评论
「推荐收藏」提高组件库Level必做好这六件事_前端_GFE_InfoQ写作社区