企业做网站公司怎么做,长春高端网站制作,室内设计师培训网课,用哪个软件制作网页前一阵#xff0c;有朋友问我#xff0c;能否在不进行前端编译构建的情况下#xff0c;用现代语法开发网站界面。
于是#xff0c;就有了这篇文章中提到的方案。
写在前面
这篇文章#xff0c;依旧不想讨论构建或不构建#xff0c;哪一种方案对开发更友好#xff0c;…前一阵有朋友问我能否在不进行前端编译构建的情况下用现代语法开发网站界面。
于是就有了这篇文章中提到的方案。
写在前面
这篇文章依旧不想讨论构建或不构建哪一种方案对开发更友好更适合某个团队只是在大环境都在构建似乎不构建就无法写项目的环境下分享一个相对轻的方案。
本篇文章中的代码开源在 soulteary/You-Dont-Need-Build-JavaScript有需要可以自取欢迎“一键三连”。
2019 年我写过一篇文章《你的网站或许不需要前端构建》文章中方案开源在 GitHubsoulteary/You-Dont-Need-Webpack。当时用这个技巧实现了美团内部的一个轻量的展示型的应用得益于浏览器的资源加载机制和脚本运行机制这种取巧的方案赢得了非常好的性能表现。
关于这个方案背后的故事如果你感兴趣可以阅读文章尾部“机缘巧合出现的想法”。
不过时过境迁在 2024 年或许方案中的技术栈应该有更稳定和有趣的替代品我的选择是百度 EFE 团队出品的 “SAN” 框架和周边生态。
大概只需要百十来行代码就能够折腾出一个简略的“ MIS 后台模样” 并且不同于各种慢吞吞的后台这个用 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 langenheadmeta charsetUTF-8 /meta nameviewport contentwidthdevice-width, initial-scale1.0 /titleShow case/titlelink relstylesheet href...script src.../scriptscriptrequire.config({baseUrl: ./app});/script/headbodydiv idapp/divscriptrequire([main], function (app) {app.init();});/script/body
/html虽然我们可以将除了加载器之外的代码都用加载器来进行加载来用“JS管理一切”这个做法在早些年的淘宝很流行但是在这个场景下没有必要。适当的将页面的基础依赖直接在页面中引入有至少三个好处
让页面能够更早的加载需要的资源相比较 JS 程序抢占式的资源加载页面渲染速度更快还能够最大化利用浏览器对于资源的加载和执行优化。减少了 JS 程序中的复杂的依赖管理减少了闭包作用域绑定加载器降低了程序“副本数量”节约了运行资源的同时也提升了程序运行时的性能。加载器加载的程序文件也可以写的更简单因为这些基础依赖都全局共享了不需要声明和定义在模块内部。写的更少出错更少。 在浏览器性能分析页面我们能够看到因为相对合理的程序拆分和直接加载程序的加载和解析速度是非常快的。当然也离不开 SAN 本身就很快的原因。
我们以实际情况为例比如本文使用的前端资源如果全部列举将会是下面这样soulteary/You-Dont-Need-Build-JavaScript/src/dev.html页面示例
!-- 组件库样式文件 --
link relstylesheet hreflib/santd1.1.3/santd.min.css
!-- 组件库依赖的脚本程序 --
script srclib/dayjs1.11.10/dayjs.min.js/script
script srclib/dayjs1.11.10/locale/zh-cn.min.js/script
script srclib/dayjs1.11.10/plugin/utc.min.js/script
script srclib/dayjs1.11.10/plugin/localeData.min.js/script
script srclib/dayjs1.11.10/plugin/customParseFormat.min.js/script
script srclib/dayjs1.11.10/plugin/weekOfYear.min.js/script
script srclib/dayjs1.11.10/plugin/weekYear.min.js/script
script srclib/dayjs1.11.10/plugin/advancedFormat.min.js/script
script srclib/enquire.js2.1.6/enquire.min.js/script
!-- SAN 框架 --
script srclib/san3.13.3/san.min.js/script
!-- SAN 路由 --
script srclib/san-router2.0.2/san-router.min.js/script
!-- SAN 组件库 --
script srclib/santd1.1.3/santd.js/script
!-- 加载器 --
script srclib/esljs2.2.2/esl.min.js/script尽管我不推荐任何程序的早期优化以及现代浏览器对于这样的资源的加载已经有很好的优化了并发数和多种缓存机制但是摆在面上的、不费劲的低成本可优化点我们可以顺手优化掉
link relstylesheet hreflib/santd1.1.3/santd.min.css
script srclib/core2023.12.04/core.min.js/script
script srclib/santd1.1.3/santd.min.js/script
script srclib/esljs2.2.2/esl.min.js/script我们根据程序的更新频率和基础依赖状况可以将不同组件进行合并比如将组件库和加载器之外的程序都合并成核心依赖 core.min.js这样可以减少十个请求让程序整体加载速度在尤其是非 HTTP2 环境下更快一些。
上面的架子在实际运行过程中会遇到一些小的问题问题基本都在组件依赖库 Santd 和它的依赖 Dayjs 中。
解决不完全适配的模块问题
在 JavaScript 程序中有很多种不同的模块化方案而不同的方案导出的程序文件也是不同的如果不依赖编程显示声明引入依赖的方式以及搭配构建那么有可能不同的组件在“装配连接”的时候可能会有一些小问题比如“名字对不上”模块声明名称不匹配。
如果我们将 esl 放置在 santd 前面那么组件库在加载的时候将完全遵守 AMD 模块加载方案来执行。
script srclib/esljs2.2.2/esl.min.js/script
script srclib/santd1.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 srclib/esljs2.2.2/esl.min.js/script
scriptrequire.config({ baseUrl: ./app });
/script
scriptrequire([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 等容器标签中声明 typetext/html避免程序执行的情况下来完成模版的保存。
实践编程页面模版
下面的页面模版同样来自 Santd 的示例虽然行数不多但是实现了一个完整的有顶部导航、侧边栏的经典布局
divs-layouts-header classheaderdiv classlogo/divs-menu themedark modehorizontal defaultSelectedKeys{{[1]}} styleline-height: 64pxs-menu-item key1Nav 1/s-menu-items-menu-item key2Nav 2/s-menu-items-menu-item key3Nav 3/s-menu-item/s-menu/s-headers-layouts-sider width{{200}} style{{{background: #fff}}}s-menu modeinline defaultSelectedKeys{{[3]}} defaultOpenKeys{{[sub1]}}s-sub-menu keysub1template slottitles-icon typeform /spanNavigation One/span/templates-menu-item key1 spanoption1/span/s-menu-items-menu-item key2 spanoption2/span/s-menu-items-menu-item key3 spanoption3/span/s-menu-items-menu-item key4 spanoption4/span/s-menu-item/s-sub-menus-sub-menu keysub2template slottitles-icon typecopy /spanNavigation Two/span/templates-menu-item key5 spanoption5/span/s-menu-items-menu-item key6 spanoption6/span/s-menu-items-menu-item key7 spanoption7/span/s-menu-item/s-sub-menu/s-menu/s-siders-layout style{{{padding: 0 24px 24px}}}s-breadcrumb style{{{margin: 16px 0}}}s-brcrumbitem href/Home/s-brcrumbitems-brcrumbitem href#List/s-brcrumbitems-brcrumbitemApp/s-brcrumbitem/s-breadcrumbs-content style{{{padding: 24px, background: #fff, minHeight: 280px}}}Content/s-content/s-layout/s-layout/s-layout
/divSantd 的文档中有非常多的例子你可以根据自己的需求进行组合做一个网站大概的操作就是参考官方示例 “复制粘贴”、刷新、“所见即所得”。
好了写到这里这个方案里所有的细节就都介绍完啦如果你感兴趣不妨把代码下载下来自己玩玩看。
其他
上面我们聊完了选型和组合将这些技术组件正确的装配在一起以及如何权衡和解决组件不适配的问题都偏技术实践细节。
下面分享一些和这个方案有关的事情以及这个选型偏好原因。
机缘巧合出现的想法
在 2019 年我在美团技术学院担任美团技术布道师作为团队里唯一熟悉 Coding 的同学当时需要折腾一个偏资讯展示的内部的技术门户的任务就落在了我的头上。作为一个在淘宝 UED、美团平台都干过前端岗的工程师考虑到前端构建工具和依赖每年推陈出新考虑到构建效率再高累计也要花不少时间、项目后续维护成本也还是蛮高的不由的开始想折腾一个简单一些 “所见即所得”不用维护构建、不用维护依赖的方案。
当然也有一些内部的原因比如哪怕是有一群朋友帮忙各种审批一路绿灯但是在集团内部想把非技术部门的完整的研发资源的系统流程跑通并不是一件容易的事情。系统流程上也有非常多的挑战甚至需要挑战非常多固化好的逻辑需要从代码仓库折腾到服务中心、数据和文件存储等等折腾包括 EE、SRE、安全、各种服务相关的维护方等等不亚于在公司内部系统折腾一个新研发部门上线。
当时有几个朋友善意提醒何必整这么复杂不如把项目挂靠在某个稳定服务下然后解决掉域名指向和程序托管就行了。经过一番查找还真找到了两个合适挂靠的服务都是公司级的应用可靠性非常高。
不过这两个朋友的服务综合分析下来前者最好只借用域名后者只有存取静态页面、静态资源存取的能力。当然我也不太好意思往朋友成体系的服务里塞一些不大相关技术站的前后端代码。也因为前面的原因我不得不思考资源依赖最少的方案包括不进行前端构建省得借朋友的机器给人找麻烦。
于是在这样的环境下我折腾出了一套方案其中前端的方案在短暂折腾之后写成了一篇《你的网站或许不需要前端构建》文章中的代码示例也开源在了 GitHubsoulteary/You-Dont-Need-Webpack。
San 选型的主观和客观原因
先来聊聊客观原因。
在 2024 年SAN 是为数不多保证现代语法开发的情况下还在努力保持向前兼容的框架没有一言不合的 break change。在接近十年的更新周期内一直有稳定的更新值得信赖。大厂有许多产品基于它构建有大量有流量验证的应用案例背书该踩的坑别人都替你踩完了不需要太过担心。团队相对稳定项目没有营收压力如果你翻阅提交记录和社区跟进记录不难分析出是 Geek Leader 带着一群技术专家为爱发电的纯粹的项目。
当然也有两个主观原因
折腾了各种前端项目后越来越厌倦构建尤其是时隔一两年再拿出项目如果需要重新初始化环境刷屏出现的各种依赖废弃提醒。而且也比较浪费笔记本性能即使我的设备性能都不差内存也都挺大24G64G。我也好和我一起用这套方案的同学也罢大家不需要靠前端项目复杂性来玩爬格子晋升的游戏也不需要这类项目技术栈找工作写代码可以纯粹一些。什么简单有效就用什么。
最后
如果你觉得文章、方案、或者文章中使用的开源软件 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