你的网站或许不需要前端构建(二)

news2024/11/17 13:45:53

前一阵,有朋友问我,能否在不进行前端编译构建的情况下,用现代语法开发网站界面。

于是,就有了这篇文章中提到的方案。

写在前面

这篇文章,依旧不想讨论构建或不构建,哪一种方案对开发更友好,更适合某个团队,只是在大环境都在构建,似乎不构建就无法写项目的环境下,分享一个相对轻的方案。

本篇文章中的代码,开源在 soulteary/You-Dont-Need-Build-JavaScript,有需要可以自取,欢迎“一键三连”。

2019 年,我写过一篇文章《你的网站或许不需要前端构建》,文章中方案开源在 GitHub:soulteary/You-Dont-Need-Webpack。当时,用这个技巧实现了美团内部的一个轻量的展示型的应用,得益于浏览器的资源加载机制,和脚本运行机制,这种取巧的方案赢得了非常好的性能表现。

关于这个方案背后的故事,如果你感兴趣,可以阅读文章尾部“机缘巧合出现的想法”。

不过,时过境迁,在 2024 年,或许方案中的技术栈应该有更稳定和有趣的替代品,我的选择是:百度 EFE 团队出品的 “SAN” 框架和周边生态。

大概只需要百十来行代码,就能够折腾出一个简略的“ MIS 后台模样”:

百十来行 SAN 代码的折腾效果

并且不同于各种慢吞吞的后台,这个用 SAN 搭建的免编译构建的方案,页面展示速度非常的快:

非常快速的渲染

技术选型

在聊实现之前,我们先来聊聊技术选型。

基础框架:Baidu 的 San

baidu/san 是一个轻量的前端开源框架,官方有一篇写的很好的介绍:《San介绍以及在百度APP的实践》,感兴趣可以自行翻阅,我这里就不多做赘述了。

如果用关键词来描述它,我直接能够想到的是:良好的前端兼容性、稳定更新接近十年、有稳定的生态周边、有大厂大流量应用踩坑背书、没有商业化诉求、相对纯粹的技术项目。

当然,之所以选择它作为本文的基础选型,还有一些客观和主观原因。文末的“主观原因和客观原因中有提”,这里就不展开了。

如果你想要深入跟随本文的方案,折腾你的应用,有两篇扩展阅读内容:最简单的AMD 模块规范的 Todos App 的代码 和 San 在线文档的基础语法部分。

之所以使用 AMD 作为模块规范,是因为相比其他的流行规范,AMD 拥有更好的浏览器兼容性,以及 EFE 团队恰好有一个很棒的加载器选型可用:ESL。

前端加载器:ESL (Enterprise Standard Loader)

ecomfe/esl 是百度 EFE 团队另外一个产品,可以看作是 requirejs 的强化版本,拥有比 requirejs 更小的尺寸、更高的性能和更健壮的程序。

程序的设计和用法都非常简单,一篇简单的文档足够你了解它该怎么使用:ESL 配置文档。如果你对 AMD 模块不熟悉,可以参考这篇模块定义的文档。

我们想不折腾构建,其中一个条件就是前端程序是能够按照我们的需求进行可靠的顺序加载,以及解析执行的,靠这个不到 5KB 的小工具就行啦。

前端路由器:San Router

baidu/san-router是 San 配套的项目,用来支持动态路由,嵌套路由,路由懒加载以及导航守卫等功能。如果你不想实现多页路由,或者想在当前页面折腾一些有趣的功能,这个简单有用的组件就派上用场了。

它的文档同样比较简单,不到十页的文档。

前端组件库:Santd

ecomfe/santd是 EFE 团队提供的适配 San 语法的 Ant Design 组件库实现。想要快速折腾出样式还过得去的界面,又不想太折腾 CSS 样式,用这类现成的样式库能够节约非常多的时间。样式库的文档在这里,需要什么组件的时候,翻出来直接复制粘贴用就行,非常方便。

当然,这个样式库的实现中,还有一些子依赖:包括日期组件库(dayjs)、响应式兼容垫片(enquire),在折腾的时候,我们需要做一些额外处理。不过,我们不需要直接和它们进行交互,所以也不需要查看它们的文档。

实践:搭起基础架子

其实做一个不需要编译构建的前端网站的基础的架子很简单,一个 HTML5 标准的页面结构,搭配上一些基础的样式和脚本依赖,然后将其他的资源用加载器加载就好了:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Show case</title>

    <link rel="stylesheet" href="...">

    <script src="..."></script>

    <script>
      require.config({baseUrl: "./app"});
    </script>
  </head>
  <body>
    <div id="app"></div>
    <script>
      require(["main"], function (app) {
        app.init();
      });
    </script>
  </body>
</html>

虽然,我们可以将除了加载器之外的代码都用加载器来进行加载,来用“JS管理一切”,这个做法在早些年的淘宝很流行,但是在这个场景下没有必要。适当的将页面的基础依赖直接在页面中引入,有至少三个好处:

  1. 让页面能够更早的加载需要的资源,相比较 JS 程序抢占式的资源加载,页面渲染速度更快,还能够最大化利用浏览器对于资源的加载和执行优化。
  2. 减少了 JS 程序中的复杂的依赖管理,减少了闭包作用域绑定(加载器),降低了程序“副本数量”,节约了运行资源的同时,也提升了程序运行时的性能。
  3. 加载器加载的程序文件,也可以写的更简单,因为这些基础依赖都全局共享了,不需要声明和定义在模块内部。写的更少,出错更少。

页面程序执行性能分析

在浏览器性能分析页面,我们能够看到因为相对合理的程序拆分和直接加载,程序的加载和解析速度是非常快的。当然,也离不开 SAN 本身就很快的原因。

我们以实际情况为例,比如本文使用的前端资源,如果全部列举将会是下面这样(soulteary/You-Dont-Need-Build-JavaScript/src/dev.html页面示例):

<!-- 组件库样式文件 -->
<link rel="stylesheet" href="lib/santd@1.1.3/santd.min.css">
<!-- 组件库依赖的脚本程序 -->
<script src="lib/dayjs@1.11.10/dayjs.min.js"></script>
<script src="lib/dayjs@1.11.10/locale/zh-cn.min.js"></script>
<script src="lib/dayjs@1.11.10/plugin/utc.min.js"></script>
<script src="lib/dayjs@1.11.10/plugin/localeData.min.js"></script>
<script src="lib/dayjs@1.11.10/plugin/customParseFormat.min.js"></script>
<script src="lib/dayjs@1.11.10/plugin/weekOfYear.min.js"></script>
<script src="lib/dayjs@1.11.10/plugin/weekYear.min.js"></script>
<script src="lib/dayjs@1.11.10/plugin/advancedFormat.min.js"></script>
<script src="lib/enquire.js@2.1.6/enquire.min.js"></script>
<!-- SAN 框架 -->
<script src="lib/san@3.13.3/san.min.js"></script>
<!-- SAN 路由 -->
<script src="lib/san-router@2.0.2/san-router.min.js"></script>
<!-- SAN 组件库 -->
<script src="lib/santd@1.1.3/santd.js"></script>
<!-- 加载器 -->
<script src="lib/esljs@2.2.2/esl.min.js"></script>

尽管我不推荐任何程序的早期优化,以及现代浏览器对于这样的资源的加载已经有很好的优化了(并发数和多种缓存机制),但是摆在面上的、不费劲的低成本可优化点,我们可以顺手优化掉:

<link rel="stylesheet" href="lib/santd@1.1.3/santd.min.css">
<script src="lib/core@2023.12.04/core.min.js"></script>
<script src="lib/santd@1.1.3/santd.min.js"></script>
<script src="lib/esljs@2.2.2/esl.min.js"></script>

我们根据程序的更新频率和基础依赖状况,可以将不同组件进行合并,比如将组件库和加载器之外的程序都合并成核心依赖 core.min.js,这样可以减少十个请求,让程序整体加载速度在尤其是非 HTTP2 环境下更快一些。

上面的架子在实际运行过程中,会遇到一些小的问题,问题基本都在组件依赖库 Santd 和它的依赖 Dayjs 中。

解决不完全适配的模块问题

在 JavaScript 程序中,有很多种不同的模块化方案,而不同的方案导出的程序文件也是不同的,如果不依赖编程显示声明引入依赖的方式,以及搭配构建,那么有可能不同的组件在“装配连接”的时候,可能会有一些小问题,比如“名字对不上”(模块声明名称不匹配)。

如果我们将 esl 放置在 santd 前面,那么组件库在加载的时候,将完全遵守 AMD 模块加载方案来执行。

<script src="lib/esljs@2.2.2/esl.min.js"></script>
<script src="lib/santd@1.1.3/santd.min.js"></script>

然后组件库会按照它程序声明中的顺序完成对 dayjs 和它的各种组件的加载:

typeof define === 'function' && define.amd ? define(['exports', 'san', 'dayjs', 'dayjs/plugin/utc', 'dayjs/plugin/localeData', 'dayjs/plugin/customParseFormat', 'dayjs/plugin/weekOfYear', 'dayjs/plugin/weekYear', 'dayjs/plugin/advancedFormat'], factory)

上面是 Santd 中对 dayjs 的依赖引用,不过 dayjs 默认没有像 San 生态一样,推出符合 AMD 模块的浏览器可直接使用的程序格式。虽然我们可以将 dayjs 进行适配和封装,但是这样不还得“编译构建”嘛。

我是真的一点都不想折腾和维护“编译构建”,那么有没有简单的点的做法呢?

dayjs 和它的组件在被浏览器执行后,会生成全局对象,santd 运行必要的要素其实是完备的,只是因为上面提到的原因,“它的对象名字和组件内引用对象对不上”。

仔细观察 santd 在 AMD 模块加载后,无非也就是执行了两个操作:

// 第一步:做模块的声明引入
var dayjs__default = 'default' in dayjs ? dayjs['default'] : dayjs;
// 问题:对齐导出组件的名称,dayjs 没啥问题,主要是它组件加载出问题了
utc = utc && Object.prototype.hasOwnProperty.call(utc, 'default') ? utc['default'] : utc;
localeData = localeData && Object.prototype.hasOwnProperty.call(localeData, 'default') ? localeData['default'] : localeData;
// ...

// 第二步:使用 dayjs
function getTodayTime(value) {
  var locale = value.locale();
  // 问题:浏览器引入的 dayjs 默认没有 amd 模块化,所以这样的模块加载方式会出错
  require("dayjs/locale/".concat(locale, ".js"));
  return dayjs__default().locale(locale).utcOffset(value.utcOffset());
}

一个是模块引入,另外一个是组件内部模块的加载 require,相关问题在程序注释中我都有提到,就不再展开。

想要让这个程序顺利的执行,我们只需要做一些字符串替换就够了:

var dayjs__default = dayjs;
dayjs.extend(window.dayjs_plugin_utc);
dayjs.extend(window.dayjs_plugin_localeData);
...

function getTodayTime(value) {
  var locale = value.locale();
  return dayjs__default().locale(locale).utcOffset(value.utcOffset());
}

而这个事情,为了避免遗漏,我们可以写个小的文本替换程序来处理。你可以用任意你喜欢的程序来解决类似上面的问题,我用 Go 写了一个一百来行的简单程序,包含了上面的处理和文件的字符串压缩:optimizer/optimizer.go。因为本文主要聊前端,我就不展开这部分了,感兴趣的同学可以自行翻阅。

架子部分搭起来后,我们就可以开始不涉及前端编译构建的方式来写代码了。先聊聊编写模块入口程序。

实践:编写入口程序

程序入口程序,我们在上面其实已经聊过。在 HTML 页面中,架子中有关加载器的例子是这样写的:

<script src="lib/esljs@2.2.2/esl.min.js"></script>
<script>
  require.config({ baseUrl: "./app" });
</script>
<script>
  require(["main"], function (app) {
    app.init();
  });
</script>

上面的程序执行后,会请求网站当前路径下的 ./app/main.js 文件,然后在文件加载完毕后,调用程序的 .init() 方法,完成应用的初始化。

页面实际资源加载情况

光看代码有点抽象,结合上面的浏览器资源请求详情和资源加载次序,是不是更直观啦。

如果你依赖多个文件,可以在 require( ... ) 中添加所有你需要的程序,以及在后面的(回调)函数中完成具体的逻辑,你无需考虑依赖是否下载完毕,加载器会确保你的所有依赖都下载完毕后,再执行你的具体程序逻辑。

实践:编写程序的 Main 函数

接下来我们来完成被入口程序引用的第一个程序 main.js

define(["./components/container"], function (Container, require) {
  var router = sanRouter.router;
  router.add({ rule: "/", Component: Container, target: "#app" });

  return {
    init: function () {
      router.start();
    },
  };
});

上面的程序使用了 San Router 来初始化一个单页应用,你可以参考上文提到的文档,在页面中添加更多路由。如果你选择制作多页应用,那么只注册一个 / 根路由也就足够了。

程序执行后,会将它依赖的 ./components/container 程序下载并挂载为页面的组件。如果你不喜欢在 define 中进行依赖声明,也可以用下面的方式,它们是等价的:

var Container = require("./components/container");

实践:编写第一个页面

下面的内容,主要来自 Santd 的示例,我们只需要将示例内容包裹在我们的模版代码中,就能够完成一个现代 SFC 写法,支持双向绑定,模版和逻辑分离的页面程序了:

define(function (require) {
  var template = require("tpl!./container.html");

  // --- santd 示例开始
  var Layout = santd.Layout;
  var Menu = santd.Menu;
  var Icon = santd.Icon;
  var Breadcrumb = santd.Breadcrumb;

  return san.defineComponent({
    components: {
      "s-layout": Layout,
      "s-header": Layout.Header,
      "s-content": Layout.Content,
      "s-sider": Layout.Sider,
      "s-menu": Menu,
      "s-sub-menu": Menu.Sub,
      "s-menu-item": Menu.Item,
      "s-icon": Icon,
      "s-breadcrumb": Breadcrumb,
      "s-brcrumbitem": Breadcrumb.Item,
    },
    initData() {
      return {
        inlineCollapsed: false,
      };
    },
    toggleCollapsed() {
      this.data.set("inlineCollapsed", !this.data.get("inlineCollapsed"));
    },
	// --- santd 示例结束

    template: template,
  });
});

ESL 插件:模版加载函数

下面的模版加载函数,来自 baidu/san/example/todos-amd/src/tpl.js:

/* global ActiveXObject */
define(function (require) {
  return {
    load: function (resourceId, req, load) {
      var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");

      xhr.open("GET", req.toUrl(resourceId), true);

      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
            var source = xhr.responseText;
            load(source);
          }

          /* jshint -W054 */
          xhr.onreadystatechange = new Function();
          /* jshint +W054 */
          xhr = null;
        }
      };

      xhr.send(null);
    },
  };
});

这个以 XHR 请求为核心的函数主要做一件事,就是将 HTML 以文本(xhr.responseText)的形式传递给我们的调用函数,更灵活以及无副作用的的请求模版。

当然,如果你不喜欢这样获取模版,我们还有其他的方案,比如预置模版在 <textarea> 这类不会转译内容的 HTML 块元素中,或在 script 等容器标签中声明 type="text/html",避免程序执行的情况下,来完成模版的保存。

实践:编程页面模版

下面的页面模版同样来自 Santd 的示例,虽然行数不多,但是实现了一个完整的有顶部导航、侧边栏的经典布局:

<div>
  <s-layout>
    <s-header class="header">
      <div class="logo"></div>
      <s-menu theme="dark" mode="horizontal" defaultSelectedKeys="{{['1']}}" style="line-height: 64px">
        <s-menu-item key="1">Nav 1</s-menu-item>
        <s-menu-item key="2">Nav 2</s-menu-item>
        <s-menu-item key="3">Nav 3</s-menu-item>
      </s-menu>
    </s-header>
    <s-layout>
      <s-sider width="{{200}}" style="{{{background: '#fff'}}}">
        <s-menu mode="inline" defaultSelectedKeys="{{['3']}}" defaultOpenKeys="{{['sub1']}}">
          <s-sub-menu key="sub1">
            <template slot="title">
              <s-icon type="form" />
              <span>Navigation One</span>
            </template>
            <s-menu-item key="1"> <span>option1</span></s-menu-item>
            <s-menu-item key="2"> <span>option2</span></s-menu-item>
            <s-menu-item key="3"> <span>option3</span></s-menu-item>
            <s-menu-item key="4"> <span>option4</span></s-menu-item>
          </s-sub-menu>
          <s-sub-menu key="sub2">
            <template slot="title">
              <s-icon type="copy" />
              <span>Navigation Two</span>
            </template>
            <s-menu-item key="5"> <span>option5</span></s-menu-item>
            <s-menu-item key="6"> <span>option6</span></s-menu-item>
            <s-menu-item key="7"> <span>option7</span></s-menu-item>
          </s-sub-menu>
        </s-menu>
      </s-sider>
      <s-layout style="{{{padding: '0 24px 24px'}}}">
        <s-breadcrumb style="{{{margin: '16px 0'}}}">
          <s-brcrumbitem href="/">Home</s-brcrumbitem>
          <s-brcrumbitem href="#">List</s-brcrumbitem>
          <s-brcrumbitem>App</s-brcrumbitem></s-breadcrumb
        >
        <s-content style="{{{padding: '24px', background: '#fff', minHeight: '280px'}}}">Content</s-content></s-layout
      >
    </s-layout>
  </s-layout>
</div>

Santd 的文档中有非常多的例子,你可以根据自己的需求进行组合,做一个网站,大概的操作就是参考官方示例 “复制粘贴”、刷新、“所见即所得”。

好了,写到这里,这个方案里所有的细节就都介绍完啦,如果你感兴趣,不妨把代码下载下来自己玩玩看。

其他

上面我们聊完了选型,和组合将这些技术组件正确的装配在一起,以及如何权衡和解决组件不适配的问题,都偏技术实践细节。

下面分享一些和这个方案有关的事情,以及这个选型偏好原因。

机缘巧合出现的想法

在 2019 年,我在美团技术学院,担任美团技术布道师,作为团队里唯一熟悉 Coding 的同学,当时需要折腾一个偏资讯展示的内部的技术门户的任务就落在了我的头上。作为一个在淘宝 UED、美团平台都干过前端岗的工程师,考虑到前端构建工具和依赖每年推陈出新,考虑到构建效率再高(累计)也要花不少时间、项目后续维护成本也还是蛮高的,不由的开始想折腾一个简单一些 “所见即所得”,不用维护构建、不用维护依赖的方案。

当然,也有一些内部的原因,比如哪怕是有一群朋友帮忙,各种审批一路绿灯,但是在集团内部,想把非技术部门的完整的研发资源的系统流程跑通,并不是一件容易的事情。系统流程上也有非常多的挑战,甚至需要挑战非常多固化好的逻辑,需要从代码仓库折腾到服务中心、数据和文件存储等等,折腾包括 EE、SRE、安全、各种服务相关的维护方等等,不亚于在公司内部系统折腾一个新研发部门上线。

当时有几个朋友善意提醒,何必整这么复杂,不如把项目挂靠在某个稳定服务下,然后解决掉域名指向和程序托管就行了。经过一番查找,还真找到了两个合适挂靠的服务,都是公司级的应用,可靠性非常高。

不过,这两个朋友的服务,综合分析下来,前者最好只借用域名,后者只有存取静态页面、静态资源存取的能力。当然,我也不太好意思往朋友成体系的服务里塞一些不大相关技术站的前后端代码。也因为前面的原因,我不得不思考资源依赖最少的方案,包括不进行前端构建(省得借朋友的机器,给人找麻烦)。

于是,在这样的环境下,我折腾出了一套方案,其中前端的方案,在短暂折腾之后,写成了一篇《你的网站或许不需要前端构建》,文章中的代码示例也开源在了 GitHub:soulteary/You-Dont-Need-Webpack。

San 选型的主观和客观原因

先来聊聊客观原因。

  1. 在 2024 年,SAN 是为数不多保证现代语法开发的情况下,还在努力保持向前兼容的框架,没有一言不合的 break change。在接近十年的更新周期内,一直有稳定的更新,值得信赖。
  2. 大厂有许多产品基于它构建,有大量有流量验证的应用案例背书,该踩的坑别人都替你踩完了,不需要太过担心。
  3. 团队相对稳定,项目没有营收压力,如果你翻阅提交记录和社区跟进记录,不难分析出是 Geek Leader 带着一群技术专家为爱发电的纯粹的项目。

当然,也有两个主观原因:

  1. 折腾了各种前端项目后,越来越厌倦构建,尤其是时隔一两年再拿出项目,如果需要重新初始化环境,刷屏出现的各种依赖废弃提醒。而且,也比较浪费笔记本性能,即使我的设备性能都不差,内存也都挺大(24G~64G)。
  2. 我也好,和我一起用这套方案的同学也罢,大家不需要靠前端项目复杂性来玩爬格子晋升的游戏,也不需要这类项目技术栈找工作,写代码可以纯粹一些。什么简单有效,就用什么。

最后

如果你觉得文章、方案、或者文章中使用的开源软件 SAN 不错,欢迎不吝一键三连。当然,如果是针对项目的 Pull Request 就更好啦。

这篇文章只是一个开始,接下来,“有关界面”的折腾文章里,我会不断的更新和完善这个“不构建”的方案。

–EOF


本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)

本文作者: 苏洋

创建时间: 2024年01月04日
统计字数: 10797字
阅读时间: 22分钟阅读
本文链接: https://soulteary.com/2024/01/04/your-website-may-not-need-front-end-builds-chapter-2.html

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

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

相关文章

go-cqhttp作者停止维护——替代品OpenShamrock的使用方法

目录 前言 解决办法 配置要求 实操 刷入面具 安装lsp框架 安装OpenShamrock和QQ 注意 大功告成 前言 由于QQ官方针对协议库的围追堵截&#xff0c;go-cqhttp已经无力维护下去了 原文连接 QQ Bot的未来以及迁移建议 Issue #2471 Mrs4s/go-cqhttp (github.com)https…

基于WS2812的圣诞树

项目说明 通过使用1010封装的WS2812灯珠&#xff0c;实现整体观感和谐、可视角度更佳的迷你圣诞树采用电池供电触摸开关机手机遥控方案&#xff0c;主打一个优雅。 主要特色 1、灯板部分&#xff08;圣诞树主体&#xff09; 使用1010封装的WS2812灯珠&#xff0c;体积小更和…

Jetson Orin Nano_安装jtop指令(遇到循环提示重启服务的问题)、查看系统运行情况及基本信息

1、安装jtop 1.1、如果一切顺利的话&#xff0c;流程如下 安装jetson-stats&#xff08;前提&#xff1a;安装pip3&#xff09; sudo pip3 install jetson-stats 执行jtop&#xff0c;根据提示需要重启服务 sudo systemctl restart jtop.service sudo jtop 1.2、循环提示…

深入理解Java中资源加载的方法及Spring的ResourceLoader应用

在Java开发中&#xff0c;资源加载是一个基础而重要的操作。本文将深入探讨Java中两种常见的资源加载方式&#xff1a;ClassLoader的getResource方法和Class的getResource方法&#xff0c;并介绍Spring框架中的ResourceLoader的应用。 1. 资源加载的两种方式 1.1 ClassLoader…

Spring见解4 基于注解的AOP配置

5.基于注解的AOP配置 5.1.创建工程 5.1.1.pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation…

基于群居蜘蛛算法优化的Elman神经网络数据预测 - 附代码

基于群居蜘蛛算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于群居蜘蛛算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于群居蜘蛛优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&…

Python如何实现微信支付功能代码示例

微信支付是一种基于互联网的移动支付服务&#xff0c;由中国的即时通讯工具微信提供。用户可以通过微信支付在微信平台上进行在线支付、转账和收款。微信支付支持多种支付方式&#xff0c;包括银行卡支付、微信钱包余额支付、扫码支付等。用户可以用微信支付购买商品、支付账单…

【Java】LockSupport原理与使用

LockSupport&#xff1a; 关键字段&#xff1a; private static final sun.misc.Unsafe UNSAFE;private static final long parkBlockerOffset; Unsafe&#xff1a;"魔法类"&#xff0c;较为底层&#xff0c;在LockSupport类中用于线程调度(线程阻塞、线程恢复等)。…

【递归】C++算法:124 二叉树中的最大路径和

作者推荐 【动态规划】【字符串】扰乱字符串 本文涉及的基础知识点 递归 124. 二叉树中的最大路径和 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#x…

【响应式编程-03】Lambda表达式底层实现原理

一、简要描述 Lambda的底层实现原理Lambda表达式编译和运行过程 二、Lambda的底层实现原理 Lambda表达式的本质 函数式接口的匿名子类的匿名对象 反编译&#xff1a;cfr-0.145.jar 反编译&#xff1a;LambdaMetafactory.metafactory() 跟踪调试&#xff0c;转储Lambda类&#x…

HarmonyOS 应用开发学习笔记 ets自定义组件及其引用 @Component自定义组件

Component注解的作用是用来构建自定义组件 Component组件官方文档 自定义组件具有以下特点&#xff1a; 可组合&#xff1a;允许开发者组合使用系统组件、及其属性和方法。 可重用&#xff1a;自定义组件可以被其他组件重用&#xff0c;并作为不同的实例在不同的父组件或容器…

防止源代码泄露的10大措施

当下&#xff0c;防止源代码泄露成为了企业和开发团队关注的焦点。 我在网上就看到过这样的案例&#xff1a; 2017年&#xff0c;一位前员工将包含公司商业机密的代码上传到了GitHub的公有仓库中&#xff0c;造成大疆源代码泄露。攻击者能够通过这些源代码访问客户敏感信息&am…

嵌入式系统复习--基于ARM的嵌入式程序设计

文章目录 上一篇编译环境ADS编译环境下的伪操作GNU编译环境下的伪操作ARM汇编语言的伪指令 汇编语言程序设计相关运算操作符汇编语言格式汇编语言程序重点C语言的一些技巧 下一篇 上一篇 嵌入式系统复习–Thumb指令集 编译环境 ADS/SDT IDE开发环境&#xff1a;它由ARM公司开…

毛虫目标检测数据集VOC格式550张

毛虫&#xff0c;一种令人惊叹的生物&#xff0c;以其独特的外貌和习性&#xff0c;成为了自然界中的一道亮丽风景。 毛虫的外观非常特别&#xff0c;身体呈圆柱形&#xff0c;表面覆盖着许多细小的毛发&#xff0c;这使得它们在叶子上伪装得非常好。它们的头部有一对坚硬的颚…

vmware安装centos 7.6 操作系统

vmware安装centos 7.6 操作系统 1、下载centos 7.6 操作系统镜像文件2、安装centos 7.6操作系统3、配置centos 7.6 操作系统3.1、配置静态IP地址 和 dns3.2、查看磁盘分区3.3、查看系统版本 1、下载centos 7.6 操作系统镜像文件 这里选择 2018年10月发布的 7.6 版本 官方下载链…

flutter学习-day23-使用extended_image处理图片的加载和操作

文章目录 1. 介绍2. 属性介绍3. 使用 1. 介绍 在 Flutter 的开发过程中&#xff0c;经常会遇到图片的显示和加载处理&#xff0c;通常显示一个图片&#xff0c;都有很多细节需要处理&#xff0c;比如图片的加载、缓存、错误处理、图片的压缩、图片的格式转换等&#xff0c;如果…

代码随想录 718. 最长重复子数组

题目 给两个整数数组 nums1 和 nums2 &#xff0c;返回 两个数组中 公共的 、长度最长的子数组的长度 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,3,2,1], nums2 [3,2,1,4,7] 输出&#xff1a;3 解释&#xff1a;长度最长的公共子数组是 [3,2,1] 。 示例 2&#xff1…

创新性文生视频模型,南洋理工开源FreeInit

文本领域的ChatGPT&#xff0c;画图领域的Midjourney都展现出了大模型强大的一面&#xff0c;虽然视频领域有Gen-2这样的领导者&#xff0c;但现有的视频扩散模型在生成的效果中仍然存在时间一致性不足和不自然的动态效果。 南洋理工大学S实验室的研究人员发现&#xff0c;扩散…

前端 -- 基础 路径 -- 相对路径 详解

目录 导语引入 &#xff1a; 相对路径 &#xff1a; 相对路径 包含哪些 &#xff1a; 同一级路径 &#xff1a; 下一级路径 &#xff1a; 上一级路径 &#xff1a; 导语引入 &#xff1a; # 大家都清楚&#xff0c;在我们日常所见到的网页里&#xff0c;要涉及好多…