当前位置: 首页 > news >正文

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

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

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

写在前面

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

本篇文章中的代码,开源在 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

相关文章:

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

前一阵&#xff0c;有朋友问我&#xff0c;能否在不进行前端编译构建的情况下&#xff0c;用现代语法开发网站界面。 于是&#xff0c;就有了这篇文章中提到的方案。 写在前面 这篇文章&#xff0c;依旧不想讨论构建或不构建&#xff0c;哪一种方案对开发更友好&#xff0c;…...

flutter 使用adb 同时连接 多个模拟器

MUMU模拟器 MuMu模拟器官网_安卓12模拟器_网易手游模拟器 传统只需要 连接一个 默认命令是 默认端口是7555 adb connect 127.0.0.1:7555 但是需要同时连接调试多个模拟器的时候 就需要连接多个 这里可以使用自带的多开 多开后 使用 1 是对应多开的序号 这样就可以查看对…...

网络四元组

文章目录 网络四元组 今天我们来聊聊 网络四元组 网络四元组 四元组&#xff0c;简单理解就是在 TCP 协议中&#xff0c;去确定一个客户端连接的组成要素&#xff0c;它包括源 IP 地址、目标 IP 地址、源端口号、目标端口号。 正常情况下&#xff0c;我们对于网络通信的认识可…...

[实践总结] 限制正则表达式匹配次数/时间 防止DoS攻击

思路 1、优化正则表达式 2、正则表达式无法优化的话&#xff0c;可以考虑限制匹配次数&#xff0c;或者限制匹配时间 限制 匹配次数 public class CountedCharSequence implements CharSequence {private final CharSequence charSequence;private long count;public Counte…...

ffmpeg 5.0版本调试 ffmpeg 5.01 static版本

ffmpeg 5.0版本调试 写法&#xff1a;ffmpeg -rtsp_transport tcp -re -i rtsp://admin:BYTtest2019192.168.1.2:554/h264/ch1/main/av_stream -q 5 -f mpegts -fflags nobuffer -c:v mpeg1video -an -s 960x540 http://127.0.0.1:12345/demo本地写法 ffmpeg -timeout 5000000…...

应用在游戏机触摸屏中的触摸感应芯片

触屏游戏机的屏幕是由液晶屏和触控层组成的。触控层分为电容式触屏和电阻式触屏两种。电容式触屏是将悬空电极和屏幕玻璃上的电极组成静电场,当人体接近屏幕时,就会改变静电场分布,从而实现触摸的位置探测。而电阻式触屏则是利用玻璃上的两层电极之间通电形成一个电阻值,当手指…...

D-Link DES-108 交换机

D-Link DES-108 交换机 1. 百兆交换机 8 口References ​ D-Link Corporation is a Taiwanese multinational networking equipment manufacturing corporation headquartered in Taipei, Taiwan. Taiwanese&#xff1a;adj. 台湾的 n. 台湾人 headquarter [hedkwɔ:tə]&#…...

VIT用于图像分类 学习笔记(附代码)

论文地址&#xff1a;https://arxiv.org/abs/2010.11929 代码地址&#xff1a;https://github.com/bubbliiiing/classification-pytorch 1.是什么&#xff1f; Vision Transformer&#xff08;VIT&#xff09;是一种基于Transformer架构的图像分类模型。它将图像分割成一系列…...

MongoDB Certified Associate Developer 认证考试心得

介绍 前段时间通过了 MongoDB Associate Developer 考试&#xff0c;也记下了一些心得&#xff0c;结果忘记发出来了&#xff0c;现在重新整理下。通过考试后证书是这样的: MongoDB 目前有两个认证证书 1. MongoDB Associate Developer 认证掌握使用MongoDB 来构建现代应用…...

基于Java车间工时管理系统(源码+部署文档)

博主介绍&#xff1a; ✌至今服务客户已经1000、专注于Java技术领域、项目定制、技术答疑、开发工具、毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅 &#x1f447;&#x1f3fb; 不然下次找不到 Java项目精品实…...

2024.1.5

今天真是狂学了一天的C&#xff0c;什么期末考试&#xff0c;滚tmd&#xff08;就一门政治&#xff0c;不能影响我c的脚步&#xff09;&#xff0c;今天还是指针&#xff0c;主要是函数指针和函数指针数组&#xff0c;将简单的两位数计算器程序用此方式更加简单的实现了&#x…...

水库大坝安全监测设计与施工经验

随着我国的科技水平不断上升&#xff0c;带动了我国的水电建设向更高层次发展。目前&#xff0c;我国的水电站大坝已有上百座&#xff0c;并且大坝安全检测仪器质量与先进技术不断更新发展&#xff0c;如今水电站大坝数据信息采集与观测资料分析&#xff0c;能够有效提高水库大…...

媒体捕捉-拍照

引言 在项目开发中&#xff0c;从媒体库中选择图片或使用相机拍摄图片是一个极为普遍的需求。通常&#xff0c;我们使用UIImagePickerController来实现单张图片选择或启动相机拍照。整个拍照过程由UIImagePickerController内部实现&#xff0c;无需我们关心细节&#xff0c;只…...

Typora+PicGo+Gitee构建云存储图片

创建Gitee仓库 首先&#xff0c;打开工作台 - Gitee.com&#xff0c;自行注册一个账户 注册完后&#xff0c;新建一个仓库&#xff08;记得仓库要开源&#xff09; 然后创建完仓库后&#xff0c;鼠标移动到右上角头像位置&#xff0c;选择设置&#xff0c;并点击&#xff…...

【话题】ChatGPT等大语言模型为什么没有智能2

我们接着上一次的讨论&#xff0c;继续探索大模型的存在的问题。正巧CSDN最近在搞文章活动&#xff0c;我们来看看大模型“幻觉”。当然&#xff0c;本文可能有很多我自己的“幻觉”&#xff0c;欢迎批评指正。如果这么说的话&#xff0c;其实很容易得出一个小结论——大模型如…...

通过大量生物、地球、农业、气象、生态、环境科学领域中案例,一起探索如何优雅地使用大模型吧!

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…...

slf4j+logback源码加载流程解析

slf4j绑定logback源码解析 Logger log LoggerFactory.getLogger(LogbackDemo.class);如上述代码所示&#xff0c;在项目中通常会这样创建一个Logger对象去打印日志。 然后点进去&#xff0c;会走到LoggerFactory的getILoggerFactory()方法&#xff0c;如下代码所示。 public …...

KVM虚拟机部署K8S重启后/etc/hosts内容丢失

前言 使用KVM开了虚拟机部署K8S&#xff0c;部署完成后重启&#xff0c;节点的pod等信息无法获取到&#xff0c;查看报错初步推测为域名解析失效&#xff0c;查看/etc/hosts后发现安装k8s时添加的内容全部消失 网上搜索一番之后发现了 如果直接修改 /etc/hosts 文件&#xff0…...

Redis使用场景(五)

Redis实战精讲-13小时彻底学会Redis 1.计数器 可以对 String 进行自增自减运算&#xff0c;从而实现计数器功能。 Redis 这种内存型数据库的读写性能非常高&#xff0c;很适合存储频繁读写的计数量。 2.缓存 将热点数据放到内存中&#xff0c;设置内存的最大使用量以及淘汰策略…...

【UnityShader入门精要学习笔记】(2)GPU流水线

本系列为作者学习UnityShader入门精要而作的笔记&#xff0c;内容将包括&#xff1a; 书本中句子照抄 个人批注项目源码一堆新手会犯的错误潜在的太监断更&#xff0c;有始无终 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 文章目录 上节复习GPU流水线顶点着色…...

CSS免费在线字体格式转换器 CSS @font-face 生成器

今天竟意外发现的一款免费的“网页字体生成器”&#xff0c;功能强大又好用~ 工具地址&#xff1a;https://transfonter.org/ 根据你设置生成后的文件预览&#xff1a; 支持TTF、OTF、WOFF、WOFF2 或 SVG字体格式转换生成&#xff0c;每个文件最大15MB。转换完成以后还会生成一…...

Codeium在IDEA里的3个坑

转载自Codeium在IDEA里的3个坑&#xff1a;无法log in&#xff0c;downloading language server和中文乱码_downloading codeium language server...-CSDN博客文章浏览阅读1.7w次&#xff0c;点赞26次&#xff0c;收藏47次。Codeium安装IDEA插件的3个常见坑_downloading codeiu…...

C-C++ 项目构建指南:如何使用 Makefile 提高开发效率

Makefile是一个常用的自动化构建工具&#xff0c;它可以为开发人员提供方便的项目构建方式。在C/C项目中&#xff0c;Makefile可以用来编译、链接和生成可执行文件。使用Makefile的好处是可以自动执行一系列命令&#xff0c;从而减少手动操作的复杂性和出错的可能性。此外&…...

基于SpringBoot的图书管理系统

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 🚀🚀🚀SpringBoot 阿博图书管理系…...

矩阵对角线遍历

Diagonal 2614. 对角线上的质数 class Solution {public int diagonalPrime(int[][] nums) {int n = nums....

【教程】Typecho Joe主题开启并修复壁纸相册不显示问题

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 背景说明 Joe主题本身支持“壁纸”功能&#xff0c;其实就是相册。当时还在网上找了好久相册部署的开源项目&#xff0c;太傻了。 但是网上教程很少&#xff0c;一没说如何开启壁纸功能&#xff0c;二没说开启后为…...

MR混合现实情景实训教学系统在法律专业课堂上的应用

MR混合现实情景实训教学系统是一种将虚拟现实&#xff08;VR&#xff09;、增强现实&#xff08;AR&#xff09;相结合的先进技术。在法律教学课堂上&#xff0c;MR教学系统为学生模拟模拟法庭、案例分析等多种形式&#xff0c;让学生在实践中掌握法律知识&#xff0c;提高法律…...

车载 Android之 核心服务 - CarPropertyService 的VehicleHAL

前言: 本文是车载Android之核心服务-CarPropertyService的第二篇&#xff0c;了解一下CarPropertyService的VehicleHAL, 第一篇在车载 Android之 核心服务 - CarPropertyService 解析-CSDN博客&#xff0c;有兴趣的 朋友可以去看下。 本节介绍 AndroidAutomotiveOS中对于 Veh…...

年底了,准备跳槽的可以看看...

前两天跟朋友感慨&#xff0c;今年的铜九铁十、裁员、疫情导致好多人都没拿到offer!现在已经1月了&#xff0c;具体明年的金三银四只剩下两个月。 对于想跳槽的职场人来说&#xff0c;绝对要从现在开始做准备了。这时候&#xff0c;很多高薪技术岗、管理岗的缺口和市场需求也出…...

Bagging算法_随机森林Random_Forest

Bagging B a g g i n g Bagging Bagging是并行式集成学习方法最著名的代表&#xff0c;这个名字是由 B o o t s t r a p A G G r e g a t I N G Bootstrap AGGregatING BootstrapAGGregatING而来&#xff0c;顾名思义&#xff0c;该算法由 B o o s t s t r a p Booststrap Boos…...

wordpress集成文库插件/seo优化包括哪些

一、学生背景学生姓名 &#xff1a; 韦同学成绩概况 &#xff1a; 绩点 2.7所学专业 &#xff1a; Computer science就读学校 &#xff1a; 明尼苏达录取专业 &#xff1a; informationtechnology留学层级 &#xff1a; 硕士录取结果 &#xff1a; 悉尼大学*二、申请诊断分析优…...

网站开发工程师项目经验怎么写/百度推广电话客服24小时

最近在研究 TP受益匪浅... http://今天就从 基本函数 M和D开始高手莫见笑..有不对的地方 望一起讨论两个有共同点..都是实例化对象用的..但是 在使用时 两者却不一样...为什么??先看看代码函数M http://blog.csdn.net/siren0203/article/details/5974415...

东莞网页制作招聘信息/怎样优化网站

一、 QT内置的ICON资源保存在QStyle类里。 可以通过成员函数 QStyle::standardIcon 来获取。 保存的icon有&#xff1a; enum QStyle::StandardPixmap This enum describes the available standard pixmaps. A standard pixmap is a pixmap that can follow some existing…...

沈阳网站建设公众号/网站建设的数字化和互联网化

源码 资源在qq群:2076966127 这个是我更新这个系列的第二期&#xff0c;现在看看几个月之前画的UI我真想吐了。好丑啊~~~~~ 现在我再一些大型的 图标网站找到了很多好看简介免费的图标~~ https://www.flaticon.com 这个就很不错~ 还是先准备好你的QtDesigner这个原本是为了C准备…...

网站版权 备案icp/注册网址在哪里注册

消息队列适合解决的问题参考文章&#xff1a; &#xff08;1&#xff09;消息队列适合解决的问题 &#xff08;2&#xff09;https://www.cnblogs.com/chjxbt/p/11377402.html &#xff08;3&#xff09;https://www.codeprj.com/blog/ad9afa1.html 备忘一下。...

文化建设基金管理有限公司网站/北京seo排名公司

41、42知识点1:委托构造函数:一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程。 class OH{ OH(string s, int a, int b):book(s),price(a),sale(b){} //三参数构造函数的参数列表和函数体首先被执行 OH():OH("",0,0);//默认构造函数又委托给了…...