挑战21天手写前端框架 day20 如何优雅的实现微生成器

本文涉及的产品
函数计算FC,每月15万CU 3个月
简介: 挑战21天手写前端框架 day20 如何优雅的实现微生成器

image.png

首先开篇依旧是解决之前的遗留问题,这是网友在下月亮有何贵干指出来的一个写框架会遇到的典型的 winPath 问题,也是我第一次维护 umi 所修改的问题,本来在 day12 编写的时候,应该写上去的,但是我给忘记了。


经典的 winPath 问题

在使用 path join 做路径拼接之后,会导致在 window 上的路径错误

image.png

image.png

随手用 winPath 处理一下。就是如图中这个朋友写的这样子。

packages/malita/src/entry.ts

import { winPath } from '@umijs/utils';
importStr += `import A${count} from '${winPath(route.element)}';\n`;
复制代码


接下里进入今天的主题,我们将多个项目的公共功能提取到框架层,形成开发脚手架。为了提效我们还可以将页面级别的内容整理成模版,用于快速创建相对应的资产,比如快速新建页面、快速新建服务、快速新建Mock等。


生成器的实现是我上手开发的第一个 node 服务,我觉得它可以作为一个新手入门的最佳上手项目。这么适合新手,为什么放到最后实现呢?这里留一个悬念,文末说明。


在没有生成器的时候,你如何在一个项目中新建一个新的路由页面,一般操作就是从另一个页面中复制,然后删除一些这个页面用不到的代码,然后再修改一些里面的内容。我们还是将这个过程分解出来,首先有一个地方存放了我们的模版文件,然后我们传入要修改的几个参数,替换模版文件中的数据,然后将它们写到我们指定的目录下。


其实昨天我们在编写依赖预编译的时候,用到了 copyFileSync 也可以理解为一个最基本的生成器,就是不改任何参数(文件名也是参数)。

fs.copyFileSync(path.join(pkgRoot, 'package.json'), path.join(target, 'package.json'))
复制代码


配置 generate 命令

program.command('generate').alias('g').description('微生成器').action(function(_, options) {
    const {
        generate
    } = require('../lib/generate');
    generate(options.args);
});
复制代码


我们就可以使用 malita g 类型 参数 这样的命令,来调用我们的微生成器了。 实现就是从 options.args 取到“类型”和“参数”,然后用 fs 提供的 api 一步一步的实现我们的需求,这种原始的实现方式,我们之前的19天中,已经写了不少了,我这里就不用这种方式实现了。


这里介绍一种更加优雅的方式,是在 umi@4 中实现好的基础生成器,也是现在 umi@4 插件中推荐使用的微生成器最佳实践。


当时的设计灵感其实还是聚焦在“约定式”,因为 umi@4 中会提供很多的微生成器支撑,因此就想着直接约定了模版的地址,然后提供统一的方法进行构建和生产,一些需要用户提供的数据,使用问答式的交互来获取。


使用 @umijs/utils 的 generateFile 来快速完成生成器

import { lodash, generateFile } from '@umijs/utils';
import path from 'path';
import fs from 'fs';
export const generate = async (args: string[]) => {
    const [type, name] = args;
    const absSrcPath = path.resolve(process.cwd(), 'src');
    const absPagesPath = path.resolve(absSrcPath, 'pages');
    if (fs.existsSync(absPagesPath) && type && name) {
        generateFile({
            path: path.join(__dirname, `../templates/${type}`),
            target: path.join(absPagesPath, name),
            data: {
                name: lodash.upperFirst(name),
            },
        });
    }
}
复制代码


然后我们只要编写模版文件 packages/malita/templates/page/index.tsx.tpl 就好了。 只要关注需要动态替换的参数。

import React from 'react';
import type { FC } from 'react';
interface {{{ name }}}PageProps {}
const {{{ name }}}Page: FC<{{{ name }}}PageProps> = () => {
  return <div>Hello {{{ name }}}</div>;
};
export default {{{ name }}}Page;
复制代码


这样我们的生成器编写就只需要聚焦在模版文件的编写上。

如果我们需要一些用户输入或者选择的数据,那么我们可以给 generateFile 传递一个 questions 对象。

const questions = [
    {
      name: 'hi',
      type: 'text',
      message: `What's your name?`,
    }
  ] as prompts.PromptObject[];
复制代码


然后修改模版文件

return <div>Hello {{{ hi }}}</div>;
复制代码


执行验证

pnpm g page abc
> @examples/app@1.0.0 g /Users/congxiaochen/Documents/malita/examples/app
> malita g "page" "abc"
✔ What's your name? … malita
Write: index.tsx
复制代码


最终生成 examples/app/src/pages/abc/index.tsx

import React from 'react';
import type { FC } from 'react';
interface AbcPageProps {}
const AbcPage: FC<AbcPageProps> = () => {
  return <div>Hello malita</div>;
};
export default AbcPage;
复制代码

你可以用相同的方式实现各种各样的微生成器,比如 umi 中的微生成器列表

感谢阅读,本来微生成器是要放在前面实现的,但是我看掘金上不少读者都是刚刚进入软件这个行业的,说来也巧,今天跟红尘炼心大佬聊天还说到这个。我觉得程序开发最重要的还是编程思维的培养,这一点不管是早期的快速入门还是后期的高P晋升都是非常重要的。


很多时候我们在拿到需求的时候,立马想到的是程序上的实现,要写怎样的代码,用怎样的语法之类的。但其实我们更应该去思考的是这个需求的场景,和未来可以复用的情况,涉及的边界等等。这也是评判新手和高手的一个标准。像我们这个系列的解题思路,就是完全新手向的思维,看到问题解决问题。我在里面预留了很多的问题和边界的缺陷,我期待的是你能够发现它们,我觉得这是一件很有趣的事情,希望这个系列带给你的更多的是思考,而不像我的其他文章一样,仅仅是“告诉你有个什么东西,它怎么用”。


源码归档

相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
1天前
|
编解码 前端开发 JavaScript
深入探讨 PostCSS 的特点、优势以及在实际开发中的应用
PostCSS是一款用JavaScript实现的CSS处理工具,通过丰富的插件生态,支持代码优化、格式化、兼容性处理及性能提升,极大提升了前端开发效率和代码质量。它高度可定制,易于集成现有工作流,适用于大型项目和复杂设计需求。
8 3
|
3月前
|
前端开发 API 开发者
【前端数据革命】React与GraphQL协同工作:从理论到实践全面解析现代前端数据获取的新范式,开启高效开发之旅!
【8月更文挑战第31天】本文通过具体代码示例,介绍了如何利用 GraphQL 和 React 搭建高效的前端数据获取系统。GraphQL 作为一种新型数据查询语言,能精准获取所需数据、提供强大的类型系统、统一的 API 入口及实时数据订阅功能,有效解决了 RESTful API 在复杂前端应用中遇到的问题。通过集成 Apollo Client,React 应用能轻松实现数据查询与实时更新,大幅提升性能与用户体验。文章详细讲解了从安装配置到查询订阅的全过程,并分享了实践心得,适合各层次前端开发者学习参考。
37 0
|
6月前
|
SQL Java 数据库连接
深度解析MyBatis核心:探寻其核心对象的精妙设计
深度解析MyBatis核心:探寻其核心对象的精妙设计
103 1
深度解析MyBatis核心:探寻其核心对象的精妙设计
|
6月前
|
前端开发 JavaScript 容器
第九章(应用场景篇)Qiankun微前端深度解析与实践教程
第九章(应用场景篇)Qiankun微前端深度解析与实践教程
246 0
|
6月前
|
前端开发 JavaScript 测试技术
第八章(应用场景篇) 中大型项目的解构:从单体应用到微前端
第八章(应用场景篇) 中大型项目的解构:从单体应用到微前端
101 0
|
6月前
|
前端开发 JavaScript 搜索推荐
深入探讨单页面应用程序(SPA)的优势与实践
深入探讨单页面应用程序(SPA)的优势与实践
|
前端开发
前端学习案例4-promise的特性&微任务1
前端学习案例4-promise的特性&微任务1
39 0
前端学习案例4-promise的特性&微任务1
|
前端开发
前端学习案例5-promise的特性&微任务2
前端学习案例5-promise的特性&微任务2
63 0
前端学习案例5-promise的特性&微任务2
|
供应链 前端开发 应用服务中间件
几种微前端方案探究
随着技术的发展,前端应用承载的内容也日益复杂,基于此而产生的各种问题也应运而生,从MPA(Multi-Page Application,多页应用)到SPA(Single-Page Application,单页应用),虽然解决了切换体验的延迟问题,但也带来了首次加载时间长,以及工程爆炸增长后带来的巨石应用(Monolithic)问题;对于MPA来说,其部署简单,各应用之间天然硬隔离,并且具备技术栈无关、独立开发、独立部署等特点。要是能够将这两方的特点结合起来,会不会给用户和开发带来更好的用户体验?至此,在借鉴了微服务理念下,微前端便应运而生。
257 0
|
前端开发 JavaScript 测试技术
挑战21天手写前端框架 day4 框架的本质是命令行工具?
挑战21天手写前端框架 day4 框架的本质是命令行工具?
194 0
挑战21天手写前端框架 day4 框架的本质是命令行工具?