低代码组件扩展方案在复杂业务场景下的设计与实践

news2025/7/8 13:57:52

组件是爱速搭的前端页面可视化模块的核心能力之一,它将前端研发人员从无休止的页面样式微调和分辨率兼容工作中解放了出来。

目前,爱速搭通过内置的上百种功能组件(120+),基本可以覆盖大部分中后台页面的可视化设计场景。组件的相关的设计理念和实现细节我们可以在前文面向复杂业务场景下的低代码平台组件设计与实践分享中看到。

实际开发的过程中,前端研发人员往往会面临大量定制 UI 或者有极为复杂交互的页面,使用单一的组件实现会比较困难。因此,爱速搭提供了自定义组件扩展机制,让开发人员可以定制化开发功能组件和业务组件,丰富可视化搭建物料,从而实现复杂业务场景的页面的设计与搭建。 

目前爱速搭已支持三种自定义组件扩展方式:Custom 自定义组件、线上版自定义组件和 NPM 组件扩展包对于交互和功能较为简单的自定义组件,可以使用 Custom 自定义组件和线上版自定义组件;而对于复杂的功能,我们建议使用 NPM 组件扩展包方式实现。

本文主要介绍 NPM 组件扩展包的设计原理和开发实践。

1        组件扩展包功能简介

NPM 组件扩展包是一种通过导入 NPM 包来扩展和补充爱速搭组件物料的机制,支持本地 IDE 纯 Coding 的开发模式,自由度较高,可用于开发任何复杂业务场景的自定义组件。

为了方便用户在本地开发自定义组件,百度智能云低代码平台爱速搭提供了一套自定义组件扩展包的开发工具(amis-widget-cli),支持各类自定义组件模板的下载、本地预览&调试、平台预览(linkDebug)和编译等一系列工具。

NPM组件扩展包主要包括如下四个功能点:

  • 支持同时扩展多个自定义组件

一个组件扩展包中可放置多个不同类型的自定义组件,比如 multiple-custom-widget-template 内包括三种技术栈的自定义组件。如果我们将该组件添加到爱速搭应用中,会同时导入 react-info-card、hello-jquery、vue-info-card 三种不同类型的自定义组件。

  • 支持多种技术栈

爱速搭的组件扩展支持 Vue 2.0、Vue 3.0、React、jQuery 和 UniApp 等多种技术栈,用户可以使用自己熟悉的一种技术栈来开发自定义组件。

  • 支持三种自定义组件本地预览&调试方式

  1. 组件预览模式(preview): 用于预览当前自定义组件内容。
  2. 本地开发模式(dev):在本地页面设计器中预览&调试自定义组件,用于确认页面设计器能否正常使用和展示当前自定义组件。

  3. 外链调试(linkDebug):在爱速搭中预览&调试本地的自定义组件,用于确认爱速搭平台中能否正常使用和展示当前自定义组件。

  • 支持多种类型的自定义组件扩展

  1. amis 自定义组件:用于扩展和补充 web 应用/普通页面的自定义组件物料。

  2. 小程序自定义组件:用于扩展和补充小程序应用页面的自定义组件物料。

  3. 快应用自定义组件:用于扩展和补充快应用和快应用卡片的自定义组件物料。

2        组件扩展包工作原理

NPM 组件扩展包的运行框架图如下所示

图片

2.1        NPM 自定义组件的两个核心模块

一个完整的自定义组件包含两个功能模块:amis 渲染器和 amis-editor 插件:

  • 「amis 渲染器」是页面渲染时需要调取的模块,如果没有它则意味着页面中不能正常显示自定义组件内容。

  • 「amis-editor 插件」是打通页面设计器的关键功能模块,可用于设置自定义组件在页面设计器中的组件物料面板中的显示位置(哪个分类下展示,展示顺序是什么,描述信息是什么等),也可用于设置首次插入页面时的初始数据是什么,所有和页面设计器关联的数据都在 amis-editor 插件中。

2.2        本地开发 NPM 组件扩展包

开发 NPM 组件扩展包时会用到两个工具: amis-widget、amis-widget-cli。

其中 amis-widget 提供用于注册 amis 渲染器 和 amis-editor 插件的方法,而 amis-widget-cli 是用于开发 amis 自定义组件的脚手架,其核心是基于 AKFun (https://github.com/wibetter/akfun)现有的前端工程能力,为用户提供自定义组件模板下载、预览&调试、编译和多技术栈支持等功能。

图片

用户在本地完成自定义组件的编码后,可通过构建工具(使用 amis-widget-cli 或者 webpack、rollup 等构建工具)进行编译,将自定义 amis 渲染器和自定义 amis-editor 插件分别打包成静态 js 脚本,并发布成一个 NPM 包。

关于开发 NPM 组件扩展包的一些建议:

  • NPM 扩展包编译时默认会剔除第三方 npm 依赖,使用多个组件扩展包时,第三方依赖只会打包一次,避免平台运行时重复加载第三方依赖功能的代码。

  • 尽可能将同一类型的自定义组件放在一个组件扩展包中,多个自定义组件或者多个组件扩展包用到的公共模块建议封装成单独的NPM模块,最后通过 NPM 依赖的形式进行引用。

2.3        在应用中添加 NPM 组件扩展包

爱速搭支持组织级和应用级的 NPM 组件扩展包,组织级添加的组件扩展包,在组织下所有应用中可见可用,但不可编辑(不能设置显隐、排序和分类等操作)。

图片

在组织或者应用中添加 NPM 组件扩展包后会自动触发构建工作,将当前组织或应用中的所有组件扩展包分别打包成来两个 js 静态脚本(渲染器和插件),存放到平台默认存储对象中,并将静态脚本路径分别记录在应用中的 npmCustomComponentSrc 和 npmCustomPluginSrc 上。之所以打包成两个 js 静态脚本,是为了在应用运行时环境仅加载渲染器脚本,避免加载多余的设计态脚本代码。

3        多技术栈支持

爱速搭和 amis 属于 React 技术栈体系,对于其他技术栈的自定义组件是不能直接在平台运行的。因此我们在注册自定义组件时,支持将非 React 技术栈的自定义组件先包裹成 React 组件对象后再进行注册。

下面我们以 Vue 2.0 和 Vue 3.0 为例,展示转换为 React 组件的关键方法。

图片

将 Vue 2.0 组件对象转换成 React 组件关键方法

import React from 'react';
import ReactDOM from 'react-dom';
import Vue from 'vue';
import { ScopedContext, IScopedContext, RendererProps } from 'amis-core';
import { extendObject } from '../utils';

export function createVue2Component(vueObj: any) {
if (!vueObj || (typeof vueObj !== 'function' && typeof vueObj !== 'object')) {
return;
  }
class VueFactory extends React.Component<RendererProps> {
    domRef: any;
    vm: any;
    isUnmount: boolean;
    static contextType = ScopedContext;

constructor(props: RendererProps, context: IScopedContext) {
super(props);
this.domRef = React.createRef();

const scoped = context;
      scoped.registerComponent(this);

this.resolveAmisProps = this.resolveAmisProps.bind(this);
this.renderChild = this.renderChild.bind(this);
    }

    componentDidMount() {
const { amisData, amisFunc } = this.resolveAmisProps();
const { data, ...rest } = (vueObj =
        typeof vueObj === 'function' ? new vueObj() : vueObj);
const vueData = typeof data === 'function' ? data() : data;
const curVueData = extendObject(vueData, amisData);
this.vm = new Vue({
        ...rest,
data: () => curVueData,
        props: extendObject(amisFunc, rest.props || {}),
      });

      Object.keys(amisFunc).forEach((key) => {
this.vm.$props[key] = amisFunc[key];
if (key === 'render') {
this.vm.$props['renderChild'] = (
            schemaPosition: string,
            childSchema: any,
            insertElemId: string,
          ) => {
this.renderChild(schemaPosition, childSchema, insertElemId);
          };
        }
      });
this.domRef.current.appendChild(this.vm.$mount().$el);
    }

    renderChild(
      schemaPosition: string,
      childSchema: any,
      insertElemId: string,
    ) {
      let childElemCont = null;
if (this.props['render'] && childSchema && insertElemId) {
const childElem = this.props['render'](schemaPosition, childSchema);
        childElemCont = ReactDOM.render(
          childElem,
          document.getElementById(insertElemId),
        );
      }
return childElemCont;
    }

    componentDidUpdate() {
if (!this.isUnmount) {
const { amisData } = this.resolveAmisProps();
if (this.vm) {
          Object.keys(amisData).forEach((key) => {
this.vm[key] = amisData[key];
          });
this.vm.$forceUpdate();
        }
      }
    }

    componentWillUnmount() {
this.isUnmount = true;
const scoped = this.context as IScopedContext;
      scoped.unRegisterComponent(this);
this.vm.$destroy();
    }

    resolveAmisProps() {
      let amisFunc: any = {};
      let amisData: any = {};

      Object.keys(this.props).forEach((key) => {
const value = this.props[key];
if (typeof value === 'function') {
          amisFunc[key] = value;
        } else {
          amisData[key] = value;
        }
      });
return { amisData, amisFunc };
    }

    render() {
return <div ref={this.domRef}></div>;
    }
  }

return VueFactory;
}
  • 将 Vue 3.0 组件对象转换成 React 组件关键方
import React from 'react';
import { ScopedContext, IScopedContext, RendererProps } from 'amis-core';
import { createApp, getCurrentInstance, ref, isProxy, shallowRef } from 'vue';
import { isObject, extendObject } from '../utils';

export function createVue3Component(vueObj: any) {
  if (!vueObj || (typeof vueObj !== 'function' && typeof vueObj !== 'object')) {
    return;
  }

  class VueFactory extends React.Component<RendererProps> {
    domRef: any;
    app: any;
    vm: any;
    isUnmount: boolean;
    static contextType = ScopedContext;

    constructor(props: RendererProps, context: IScopedContext) {
      super(props);
      this.domRef = React.createRef();

      const scoped = context;
      scoped.registerComponent(this);

      this.resolveAmisProps = this.resolveAmisProps.bind(this);
    }

    componentDidMount() {
      const { amisData, amisFunc } = this.resolveAmisProps();
      const { data, ...rest } = (vueObj =
        typeof vueObj === 'function' ? new vueObj() : vueObj);
      const vueData = typeof data === 'function' ? data() : data;

      const curVueData = extendObject(vueData, amisData);

      this.app = createApp({
        data: () => curVueData,
        ...rest,
        props: extendObject(amisFunc, rest.props || {}),
      });
      this.vm = this.app.mount(this.domRef.current);
    }

    componentDidUpdate() {
      if (!this.isUnmount) {
        const { amisData } = this.resolveAmisProps();
        if (this.vm) {
          Object.keys(amisData).forEach((key) => {
            this.vm[key] = amisData[key];
          });
          this.vm.$forceUpdate();
        }
      }
    }

    componentWillUnmount() {
      this.isUnmount = true;
      const scoped = this.context as IScopedContext;
      scoped.unRegisterComponent(this);
      this.app.unmount();
    }

    resolveAmisProps() {
      let amisFunc: any = {};
      let amisData: any = {};

      Object.keys(this.props).forEach((key) => {
        const value = this.props[key];
        if (typeof value === 'function') {
          amisFunc[key] = value;
        } else {
          if (isProxy(value)) {
            amisData[key] = shallowRef(value);
          } else if (isObject(value)) {
            amisData[key] = ref(value);
          } else {
            amisData[key] = value;
          }
        }
      });
      return { amisData, amisFunc };
    }

    render() {
      return <div ref={this.domRef}></div>;
    }
  }

  return VueFactory;
}

4        小程序和快应用自定义组件扩展机制

在实际的开发过程中,往往会存在 H5、小程序、APP、大屏、快应用等多种展示终端。在传统的开发模式下,开发人员往往需要为多端重新编写对应的代码。

爱速搭通过「同一个 DSL 可视化编辑器」+「多套运行时」实现一次搭建构建多端应用,让用户可以通过爱速搭快速搭建 H5、APP、快应用和各类小程序应用。

当前,爱速搭移动应用和快应用通过 aipage-editor 实现可视化页面设计,可在线设计 H5、小程序、快应用和 APP 等页面。其中 APP 和各类小程序运行时使用 UniApp 运行时承载页面渲染和展示,快应用页面使用快应用运行时承载页面渲染和展示。

扩展小程序自定义组件时,开发 UniApp 版自定义组件的同时还需要额外「开发」一份对应的 H5 自定义组件。其中 H5 自定义组件是小程序自定义组件的「替身组件」,也叫预览组件(仅在平台端预览展示使用)。这个预览组件支持通过 UniApp 版的自定义组件编译生成,无需额外开发。当发布或导出小程序应用时,应用中用到的预览组件会替换成对应的 UniApp 版自定义组件。(一个小程序抽奖自定义组件示例:https://github.com/wibetter/lottery-custom-widget)

快应用自定义组件和小程序自定义组件的扩展机制是一样的,但对应的预览组件需要额外开发,用于充当快应用自定义组件的“替身组件”。

图片
小程序自定义组件扩展机制原理图

5        NPM 组件扩展包开发实战

下面我们将通过一个简单的示例(使用 Vue 2.0)来开发并发布一个 amis 组件扩展包。

图片

需要准备的环境:

  • node(推荐 v17.4.0,或更新版本)

  • npm(推荐 8.3.1,或更新版本)

需要使用到的 NPM 工具:

  • amis-widget-cli(自定义组件开发脚手架)

  • amis-widget(自定义组件注册器,支持 React 和 Vue2.0 技术栈,用于注册自定义渲染器和插件)

开发 amis 组件扩展包关键步骤:

步骤 1:全局安装 amis-widget-cli

yarn global add amis-widget-cli 或者 npm i -g amis-widget-cli

步骤 2:初始化 NPM 组件扩展包

amis init -e=amis -m=copy 或 amis init --editor=amis --mode=copy

目前已提供多种自定义组件类型:

图片

比如使用 Vue 2.0 开发一个 amis 组件扩展包(amis 自定义组件),可选择 Vue 自定义组件,输入组件扩展包名称后,即可得到如下目录结构的自定义组件项目:

图片

备注:自定义组件目录说明请见 vue-custom-widget-template# 目录说明

步骤 3:开发一个自定义组件

使用 Vue 2.0开发 amis 自定义组件(Vue 2.0 使用文档请见 Vue 官方文档)。

图片

步骤 4:注册为一个爱速搭可用的 amis 自定义组件

开发完成的自定义组件,使用 amis-widget 中的 registerRendererByType 方法注册为爱速搭可识别的自定义组件(amis 渲染器):

import InfoCard from './widget/info-card';
import { registerRendererByType } from 'amis-widget';

registerRendererByType(InfoCard, {
  type: 'vue-info-card',
  usage: 'renderer',
  weight: 99,
  framework: 'vue',
});

步骤 5:为 amis 自定义组件设置基本属性和可配置项

使用 amis-widget 中的 registerAmisEditorPlugin 方法为自定义组件制定基本属性和配置项,后续在编辑器端使用时,左侧组件物料面板会按照基本属性中的分类和排序展示当前自定义组件。

其中 scaffold 中的数据会作为自定义组件初次添加到页面中的默认数据,panelControls 中的数据则会用于生成自定义组件的配置项(编辑器端的右侧属性配置面板)。

import { registerAmisEditorPlugin } from 'amis-widget';

export class InfoCardPlugin {
  rendererName = 'vue-info-card';
  $schema = '/schemas/UnkownSchema.json';
  name = 'vue组件';
  description = '信息展示卡片';
  tags = ['自定义'];
  icon = 'fa fa-file-code-o';
  scaffolds = [
    {
      type: 'vue-info-card',
      label: 'vue组件1',
      name: 'info-card1',
      scaffold: {
        type: 'vue-info-card',
        label: 'vue组件1',
        title:
          'amis 是一个低代码前端框架,它使用 JSON 配置来生成页面,可以减少页面开发工作量,极大提升效率。',
        backgroundImage:
          'https://suda.cdn.bcebos.com/widget-tpl/%E6%99%BA%E8%83%BD%E7%94%9F%E6%80%81.png',
        img_count: 5,
        comment_count: 2021,
      },
    }
  ];
  previewSchema = {
    type: 'vue-info-card',
    label: 'vue-info-card',
  };

  panelTitle = '配置';

  panelControls = [
    {
      type: 'textarea',
      name: 'title',
      label: '卡片title',
      value:
        'amis 是一个低代码前端框架,它使用 JSON 配置来生成页面,可以减少页面开发工作量,极大提升效率。',
    },
    {
      type: 'text',
      name: 'backgroundImage',
      label: '展示图片',
      value:
        'https://search-operate.cdn.bcebos.com/64c279f23794a831f9a8e7a4e0b722dd.jpg',
    },
    {
      type: 'input-number',
      name: 'img_count',
      label: '图片数量',
      value: 3,
    },
    {
      type: 'input-number',
      name: 'comment_count',
      label: '评论数',
      value: 2021,
    },
  ];
}

registerAmisEditorPlugin(InfoCardPlugin);

export default InfoCardPlugin;

步骤 6:本地预览&调试自定义组件内容

预览方式 1:在控制台输入 npm run preview ,打开浏览器预览自定义组件内容。

预览方式 2:在控制台输入 npm run dev 后,在本地打开一个页面编辑器(amis-editor),预览和调试自定义组件内容。

图片

步骤 7:在爱速搭中调试自定义组件

当自定义组件开发完成,并可在编辑器端正常使用和展示时,我们还可以使用 linkDebug 调试工具验证当前自定义组件在爱速搭平台中能否正常使用。

在控制台输入:npm run linkDebug 后,复制控制台输出的脚本地址,添加到爱速搭/临时外链中并保存。

图片

打开应用中一个普通页面,进入页面编辑器查看能否正常使用当前自定义组件:

图片

步骤 8:发布 NPM 组件扩展包

当自定义组件开发完成,并能正常在爱搭平台中正常使用和展示,就可以将其发布成一个 NPM 组件扩展包。

  • 构建自定义组件静态脚本

发布前需要先构建自定义组件,执行:npm run build2lib。构建完成的自定义组件静态脚本默认存放在dist目录下。

  • 在 package.json 中声明自定义组件信息

package.json / amis-widgets 中添加自定义组件信息,导入爱速搭时会读取这里的信息:

图片

  • 发布到 npm 仓库

执行:npm publish,需确保当前NPM组件扩展包名称和版本号是唯一的。

图片

至此我们已经成功发布了一个 NPM 组件扩展包。

6        常见使用疑问

Q:为什么使用 NPM 方式托管自定义组件?

A:爱速搭的主要客户是私有化部署使用,使用 NPM 托管方式可以更好的与平台解耦,用户在开发自定义组件无需过多关注平台功能,比如编译后的 js 静态资源不放对象存储也能正常使用。

Q:现有业务组件能否快速发布成 NPM 组件扩展包?

A:现有业务组件无需进行结构性改造,只需将其注册成 amis 渲染器,同时按需补充 amis-editor 插件,即可快速发布成 NPM 组件扩展包。

Q:爱速搭支持单独使用 amis-widget 开发自定义组件吗?

A:对于有一定前端工程能力的用户或者团队,可单独使用 amis-widget 开发自定义组件,无需额外引入 amis-widget-cli,可以完整保留业务现有的前端工程代码。

Q:爱速搭支持使用私有 NPM 仓库发布组件扩展包吗?

A:对于涉密业务组件,爱速搭私有化部署时支持配置私有 NPM 托管仓库(比如百度的私有 NPM 仓库、或者淘宝的私有仓库)。

爱速搭平台默认使用的官方 NPM 仓库的搜索接口(NPM_SEARCH)实现组件扩展包搜索导入功能,但如果私有化部署时配置成私有仓库(私有仓库自身没有NPM的搜索接口),会导致搜索导入功能失效。添加组件扩展包时,请使用「手动添加NPM组件扩展包」方式添加 NPM 组件扩展包。

图片

7        小结

百度智能云低代码平台爱速搭的目标是让开发者少写或者不写代码也可以可快速搭建企业信息化管理系统。

爱通过通过内置的 120+ 功能组件和可视化页面设计器,可实现大部分中后台页面的可视化设计;通过 API 管理和 API 编排,实现了业务接口的可视化设计;通过数据模型,实现了内外部数据表的可视化设计;通过 JSONQL,实现了 SQL 可视化设计并支持多种数据库类型,最终实现整个应用的一站式的全栈可视化搭建能力。


企业借助爱速搭平台完成信息化系统的建设的同时,往往还会面对老旧系统的维护工作,此时企业也可借助本文介绍的组件扩展包能力,将老旧业务系统的业务模块封装成NPM自定义组件封装为业务组件物料,完成老旧系统到低代码的快速迁移,实现企业内部新老系统的有机融合。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1822186.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是数字人大?一分钟带你了解!

在数字化浪潮席卷全球的今天&#xff0c;中国作为数字经济的领跑者&#xff0c;正积极推动数字技术与国家治理体系的深度融合。其中&#xff0c;“数字人大”作为新时代国家治理体系和治理能力现代化的重要一环&#xff0c;正逐步成为推动民主法治建设、提升人大工作效能的新引…

ThinkPHP5.0 apache服务器配置URL重写,index.php去除

本地环境wamp .htaccess文件代码 <IfModule mod_rewrite.c>Options FollowSymlinks -MultiviewsRewriteEngine onRewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-fRewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L] </IfModule> 踩过这个坑&a…

不用下软件,51建模网上传模型就能直接在网页预览!

在数字化时代&#xff0c;3D建模和渲染技术日益成为各行各业不可或缺的工具。然而&#xff0c;传统的建模和预览流程往往需要用户安装复杂的软件&#xff0c;这不仅增加了技术门槛&#xff0c;也限制了模型在不同设备间的共享和查看。 为了解决这一痛点&#xff0c;51建模网凭…

【在线OJ】发帖功能前后段代码实现

一、页面布局 二、前端代码 <template><div id"app"><div style"height: 100vh"><div style"display: flex" ><el-input style"width: 95%" v-model"title" placeholder"输入标题"&g…

光储充一体化,开启绿色出行新篇章

一、追光逐梦&#xff0c;绿色能源点亮未来 在蔚蓝的天空下&#xff0c;光伏发电板如同一片片金色的叶子&#xff0c;静静地捕捉着太阳的光芒。它们不仅为大地带来光明&#xff0c;更是绿色出行的强大后盾。光储充一体化充电站&#xff0c;以光伏为源&#xff0c;储能为桥&…

蓝牙模块的不同版本迭代发展与技术趋势

蓝牙技术自1999年首次亮相以来&#xff0c;已经历了从1.0到5.0的多个版本迭代&#xff0c;每一次的更新都带来了显著的性能提升和广泛的应用前景。本文将综述蓝牙模块的版本迭代&#xff0c;分析其主要改进点&#xff0c;并探讨蓝牙模块在物联网、医疗、穿戴式设备等领域的应用…

Luma AI 推出梦幻机:据说吊打Sora和快手可灵(KLING)|TodayAI

近日&#xff0c;美国初创公司 Luma AI 宣布推出其最新的文本生成视频工具——梦幻机&#xff08;Dream Machine&#xff09;。这一消息发布的时间正好在中国科技公司快手推出其文本生成视频模型可灵&#xff08;KLING&#xff09;几天之后&#xff0c;标志着视频生成领域的又一…

560亿美元薪酬获批!马斯克:特斯拉未来市值将不止5万亿美元

KlipC报道&#xff1a;6月13日&#xff0c;美国电动汽车制造商特斯拉公司举办年度股东大会&#xff0c;其CEO马斯克对特斯拉生产销售、未来车型计划和在无人驾驶能等领域的发展进行了报告。此外&#xff0c;特斯拉股东批准了马斯克的560亿美元薪酬方案以及特斯拉总部迁至得克萨…

qt如何在linux平台上设置编译生成windows程序文件,跨平台?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「qt的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;QT本来目标就是跨平台&#xf…

【PL理论】(23) 函数式语言:let-in 示例的分解 | 谁在使用动态作用域?

&#x1f4ad; 写在前面&#xff1a;本章我们将对函数式语言的讲解进行收尾&#xff0c;分解一下之前讲的 let-in 示例。然后讨论一下谁在使用动态作用域。 目录 0x00 let-in 示例的分解 0x01 谁使用动态作用域&#xff1f; 0x00 let-in 示例的分解 让我们详细检查这个示例…

Kafka流计算培训:打造Kafka技术专家,引领大数据未来

Kafka流计算培训课程是一门旨在帮助大数据从业人员和欲从事Kafka技术的人员快速掌握Kafka核心技术的专业培训项目。 在这个3天的课程中&#xff0c;我们将全面细致地讲解Kafka流计算软件的配置、Kafka流计算开发和流计算管道设计等内容&#xff0c;让学员能够在实际工作中灵活…

【MySQL】日志详解

本文使用的MySQL版本是8 日志概览 它们记录了数据库系统中的不同操作和事件&#xff0c;以便于故障排除、性能优化和数据恢复。本文将介绍MySQL中常见的几种日志&#xff0c;同时也会介绍一点常用的选项。 官方文档&#xff1a;MySQL :: MySQL 8.0 Reference Manual :: 7.4 M…

QT:QT中的默认代码 QT 创建控件的两种方式

目录 QT中的默认代码 新项目的结构 主函数 wiget类的声明文件.h wiget类的定义文件.cpp form file界面文件 .pro文件 QT 创建控件的两种方式 通过ui界面创建控件 通过代码方式创建控件 QT中的默认代码 新项目的结构 主函数 基本概念&#xff1a;Qt 在创建的一个 Wi…

Allegro X PCB设计小诀窍--如何在Allegro X中为PCB标注尺寸

背景介绍&#xff1a;在PCB设计时&#xff0c;标注尺寸是非常重要的一步&#xff0c;PCB上标注的板框、孔径、倒角等尺寸信息可以辅助生产、组装和维护。Allegro X PCB设计工具提供了多种PCB标注方式&#xff0c;可以满足设计人员个性化的标注需求&#xff0c;提升PCB设计效率。…

SSM小区疫情防控系统-计算机毕业设计源码03748

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 小区疫情防控系统&#xff0c;主要的模块包括查看首页、轮播图&#xff08;轮播图管理&#xff09;、社区公告管理&#xff08;社区公告&#…

制造业几大系统(MES/WMS/QMS/ERP)的集成

制造业的几大系统包括MES&#xff08;制造执行系统&#xff09;、WMS&#xff08;仓库管理系统&#xff09;、QMS&#xff08;质量管理系统&#xff09;和ERP&#xff08;企业资源计划&#xff09;系统。这些系统在制造业中扮演着不同的角色&#xff0c;可以通过集成实现更高效…

【PLG洞察】|向Figma学习如何打造标杆客户和实施分销策略

Figma是一款功能强大的在线协同设计工具&#xff0c;它主要被用于界面设计、原型设计和用户体验设计。作为国外知名的saas企业&#xff0c;对标国内的saas蓝海&#xff0c;它的增长实在惊人&#xff01;据称&#xff0c;Figma2020年的收入已达$75M, 2021年6月&#xff0c;美国的…

深入解析 Java 标准库:构建高效应用的基石

Java 标准库&#xff0c;也称为 Java API&#xff0c;是一组预先编写的类和接口&#xff0c;为 Java 应用程序提供了一系列丰富的功能。这些库被组织成包&#xff08;packages&#xff09;&#xff0c;每个包都包含了一组相关的类和接口&#xff0c;用于处理特定的任务&#xf…

红黑树【C++实现】

文章目录 红黑树的概念红黑树的性质红黑树的操作红黑树结点的定义红黑树的插入情况一&#xff1a;插入结点的叔叔存在&#xff0c;且叔叔的颜色是红色情况二: 插入结点的叔叔存在&#xff0c;且叔叔的颜色是黑色情况三: 插入结点的叔叔不存在 红黑树的验证红黑树的查找 红黑树的…

香港裸机云多IP服务器与普通独享IP服务器的区别

在当前的云计算和服务器托管领域&#xff0c;香港裸机云多IP服务器和普通独享IP服务器是两种常见的选择。它们各自具有独特的特点和优势&#xff0c;适用于不同的应用场景。以下是对这两种服务器类型的详细比较&#xff1a; 一、概念定义 香港裸机云多IP服务器&#xff1a;这是…