使用 Oats~i 构建 Web 应用 - 设置
了解如何用两个命令设置 Oats~i Web 应用项目。

在上篇文章中,我写了一篇关于 Oats~i(一个开放的 Web 框架)的介绍性文章。我谈到了它的核心功能、一些工作原理以及可以期待的内容。如果你还没有阅读那篇文章,请快速浏览一下。
在过去的几天里,我围绕 Oats~i 创建了一些工具,以确保我们可以快速上手安装框架、运行入门项目,并无缝地进行教程学习。
本文是《使用 Oats~i 构建 Web 应用》系列的第一篇。我们将从两种方法开始设置 Oats~i 项目——使用 CLI 或手动方式。
视频链接:如果你想通过视频学习本教程,请点击此处:https://www.youtube.com/watch?v=V0Z6nxgTM2g
使用 Oats~i CLI(推荐)
这是设置 Oats~i 项目最推荐的方法。它可以节省你编写样板代码和安装 Oats~i 项目启动所需的依赖项的时间。
但是,你应该仅在创建全新项目时使用此方法,以避免 Oats~i 与你当前项目结构之间可能发生的文件冲突。
CLI 附带一个入门项目,其中包含一个主页和一个关于页面。你可以导航这两个页面,看到 Oats~i 已经在运行,处理路由和片段。
如何使用 Oats~i CLI
· 导航到你想要创建 Oats~i 项目的文件夹
· 打开终端并运行
npx oats-i-cli
· 按照提示操作,等待设置完成
· 运行
npm run dev
· 导航到构建开始时终端显示的地址(通常是 localhost:8080)。这将在你的浏览器中打开入门项目。(你可以使用局域网地址在连接到同一网络的其他设备上查看它)
手动安装
如果你有一个现有项目想要安装 Oats~i,或者喜欢硬核方式,你可以手动设置 Oats~i。这个过程要长得多,并且需要更多关注以确保一切正常工作。
安装依赖项
现在,首先导航到你的项目目录并打开终端。
首先,我们安装构建和运行 Oats~i 所需的依赖项。如果你是开始一个新项目,请运行以下命令。
npm init -y
然后,按照下面概述的步骤操作。
注意:除了安装 Oats~i 之外,如果你当前项目中已经安装了所需的库/依赖项,则可以跳过之后的任何步骤。
1. 安装 Oats~i(核心)
安装核心 Oats~i 库。
Run
npm install oats-i
2. 安装 Webpack
将 Webpack 安装为开发依赖项。Webpack 将使我们拥有更好的项目结构和其他功能,该库负责模块打包和资源管理。
Run
npm install - save-dev webpack webpack-cli
3. 安装 Webpack Dev Server
将 webpack dev server 安装为开发依赖项。这将允许我们运行一个开发服务器,在我们构建和测试 Oats~i Web 应用时,它会在新更改时自动更新。
Run
npm install - save-dev webpack-dev-server
4. 安装 Handlebars-Loader
强烈建议使用模板引擎来渲染 Oats~i 中的视图。我首选 handlebars。(了解更多关于 handlebars)
为了与 webpack 一起工作,我们需要将 handlebars-loader 安装为开发依赖项。这将允许我们使用 handlebars 模板在应用内生成和渲染我们的视图。
Run
npm install - save-dev handlebars-loader
5. 安装 HTML-Loader
为了创建服务器端视图,Oats~i 的基本配置使用 html-loader 和 html-webpack-plugin 的组合。让我们先将 html-loader 库安装为开发依赖项。
Run
npm install - save-dev html-loader
6. 安装 HTML-Webpack-Plugin
html-webpack-plugin 库允许我们使用 webpack 为我们的应用程序输出服务器端视图。它与 html-loader 协同工作。将其安装为开发依赖项。
Run
npm install - save-dev html-webpack-plugin
7. 安装 Babel-Loader
Babel-loader 将使用 webpack 加载和转换我们的 JavaScript 文件。将其安装为开发依赖项。
Run
npm install - save-dev babel-loader
8. 安装 Style-Loader 和 CSS-Loader
Style-loader 和 css-loader 将把我们的 css 导入注入到 html-loader 和 html-webpack-plugin 生成的 html 文件中作为样式表。将这些加载器安装为开发依赖项。
Run
npm install - save-dev style-loader npm install - save-dev css-loader
9. 安装 Webpack-Merge
Webpack-merge 允许我们合并多个 webpack 配置文件,从而使我们能够以最佳方式组织我们的配置文件以适应项目设置。将此库安装为开发依赖项。
Run
npm install - save-dev webpack-merge
10. 安装 Express-Handlebars
Express-handlebars 将允许我们使用 webpack 配置输出的 handlebars 视图文件在开发中模拟服务器端渲染,使用 html-loader 和 html-webpack-plugin。将此库安装为开发依赖项。
Run
npm install - save-dev express-handlebars
创建 Webpack 配置文件
在你的项目目录的根目录下,创建一个新文件夹并命名为“webpack-configs”。

进入此文件夹,并在其中创建两个名为“main”和“oats~i”的新文件夹。
你的文件夹结构现在应该如下所示

现在,导航到“oats~i”并创建另外两个名为“default”和“main”的文件夹。
你的文件夹结构现在应该如下所示

— — —
“default”文件夹将包含 Oats~i 所需的默认 webpack 配置,以使其依赖于 webpack 的功能正常工作。目前,那就是代码分割和片段的懒加载。
“main”文件夹将包含 Oats~i 使用和推荐的加载器的 webpack 配置。这些是我们先前在“安装依赖项”阶段安装的加载器。如果你想更改加载器,请随时稍后编辑此配置。
— — —
导航到“default”文件夹并创建一个名为“webpack.config.js”的新文件

打开文件并将以下代码粘贴进去。
//@ts-check const DefaultOats_iConfig = { optimization: { splitChunks: { minSize: 0, //Minimum size, in bytes, for a chunk to be generated. minSizeReduction: 1, //Minimum size reduction to the main chunk (bundle), in bytes, needed for a chunk to be generated. minChunks: 2, cacheGroups: { commons: { chunks: "async", //Allow chunks to be shared between sync and async } } } } } module.exports = DefaultOats_iConfig;
现在,导航回“oats~i”文件夹并进入“main”。
创建一个新文件并命名为“webpack.config.js”。

打开文件并将以下代码粘贴进去。
//@ts-check /** * Contains loaders */ const DefaultOats_iLoadersConfig = { module: { rules: [ { test: /\.(html|sv.hbs|p.hbs)$/, use: [ { loader: "html-loader", options: { minimize: false } } ] }, { test: /\.(hbs)$/, exclude: /\.(sv.hbs|p.hbs)/, use: [ { loader: "handlebars-loader", options: { inlineRequires: "./assets" } } ] }, { test: /\.(js)$/, exclude: /node_modules/, use: [ { loader: "babel-loader" } ] }, { test: /\.(png|svg|jpg|gif)$/, type: 'asset/resource', }, { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] } ] } } module.exports = DefaultOats_iLoadersConfig;
我们已经完成了 Oats~i 的核心 webpack 配置。现在,我们需要将它们合并到一个我们将用于整个项目的通用配置文件中。
现在,导航回“oats~i”文件夹,然后返回“webpack-configurations”文件夹。现在进入“main”。
创建一个新文件并命名为“webpack.config.js”。

打开文件并将以下代码粘贴进去。
//@ts-check const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const DevServerMiddlewareConfig = require("../../proxy-server/proxy_server"); //The folder we'll have our assets emitted after build const DIST_PATH_PUBLIC_ASSETS = "../../dist/public"; const { merge } = require("webpack-merge"); const DefaultOats_iConfig = require("../oats~i/default/webpack.config"); const DefaultOats_iLoadersConfig = require("../oats~i/main/webpack.config"); //@ts-expect-error module.exports = merge(DefaultOats_iConfig, DefaultOats_iLoadersConfig, { mode: "development", devtool: "eval-source-map", output: { //Where we'll output public assets path: path.resolve(__dirname, `${DIST_PATH_PUBLIC_ASSETS}`), publicPath: "/", assetModuleFilename: 'assets/[name][ext]', filename: "js/[name].dev_bundle.js", clean: true }, entry: { //The main entry (app) index: "./src/app/index/scripts/index.js", }, plugins: [ new HtmlWebpackPlugin({ template: "./src/server/home/home.sv.hbs", filename: "../views/home.hbs", chunks: ["index"], minify: false }) ], devServer: { devMiddleware: { writeToDisk: true, //Because of our configured server }, setupMiddlewares: DevServerMiddlewareConfig, } });
现在,我们已经完成了 webpack 配置的设置,足以运行 Oats~i 项目。
更新 package.json
导航回你的项目根文件夹。打开 package.json,找到“scripts”行,然后在“test”之后添加以下行(记住用逗号分隔)。
"dev": "webpack-dev-server - config ./webpack-configs/main/webpack.config.js"

设置开发服务器中间件
在我们的最终 webpack 配置文件中,我们在以下位置指定了一个用于 webpack 开发服务器的中间件文件
setupMiddlewares: DevServerMiddlewareConfig
在正常情况下,你不需要这种设置。你可以简单地以 html 格式编写你的服务器视图文件,使用 html-loader 和 html-webpack-plugin 来生成它们,并在开发过程中直接由 webpack-dev-server 提供服务。
然而,你将在稍后了解到,这对于构建一个已经为服务器端渲染做好了准备的 Oats~i 项目来说,并不是最佳设置。服务器端文件已经是 html 格式,这意味着在初始请求时,在渲染给客户端之前,它们不容易用数据进行模板化。
为了适应这一点,默认的 Oats~i 设置确保你为你的服务器视图创建模板文件,这些文件可以轻松地在每次客户端请求新页面时从服务器进行渲染。
我们的开发服务器中间件设置将允许我们在实际服务器上模拟这种设置,以用于我们的开发环境。
通过其默认设置,只要你不关心为新片段进行服务器端渲染,你就不需要为添加到项目中的新片段进行更新。然而,一旦你达到想要进行服务器端渲染并在开发中进行测试的阶段,设置起来就会容易得多、快捷得多,而无需更改你在项目中已经使用的文件格式。
让我们设置这个配置
在你的项目根目录下,创建一个新文件夹并命名为“proxy-server”。在此新文件夹内,创建一个文件并命名为“proxy_server.js”

打开文件并将以下代码粘贴进去
//@ts-check const express = require("express"); const path = require("path"); const hbs = require("express-handlebars"); const DevServerMiddlewareConfig = (middlewares, devServer) => { /** * @type {import("express").Application} */ const app = devServer.app; //Configure the view engine app.set("views", path.resolve(__dirname, "../dist/views")); app.set("view engine", "hbs"); app.engine("hbs", hbs.engine({ extname: "hbs", layoutsDir: path.resolve(__dirname, "../dist/views"), partialsDir: path.resolve(__dirname, "../dist/views/partials") })); //for json app.use(express.json()); //I think params and queries app.use(express.urlencoded({ extended: false })); //static app.use(express.static(path.resolve(__dirname, "../dist/public"))); //My middlewares //Capture all app.get("/*", (req, res, next) => { res.render("home", { layout: "home" }); }); return middlewares; } module.exports = DevServerMiddlewareConfig;
此配置将捕获所有到开发服务器的请求,并返回 home.hbs 布局文件。一旦你开始创建自己的 Oats~i 项目,你就可以稍后将其重命名为你的文件的实际名称,并且只要你不要求任何片段进行服务器端渲染,就可以保持原样。
创建 jsconfig.json
Oats~i 使用 TypeScript 声明文件和 JSDoc 的组合进行类型标注。存在一个轻微的问题,即使用框架时类型可能无法始终正确反映,轻微损害了开发体验。
与其重构 100 多个文件和数千行代码,我找到了一个方法,让 TypeScript 和 IntelliSense(至少在 VSCode 中)能够理解 Oats~i 中使用的 JSDoc 类型。
为此,请导航到你的项目根文件夹。创建一个名为“jsconfig.json”的文件。

打开它并粘贴下面的代码
{ "include": [ "*/**/*.js", "**/*", "/**/*", "node_modules/oats-i" //to get type definitions for oats-i in your project ], }
注意:这个部分是 CLI 自动生成的,所以不要对使用 CLI 设置的 Oats~i 项目执行此操作。
创建入门项目文件
现在,让我们将所有内容整合起来,创建我们的入门项目文件,以首次运行 Oats~i 应用。
服务器端基础布局
导航到你的项目根文件夹并创建一个名为“src”的新文件夹。此文件夹将包含我们项目的所有源文件。
在“src”文件夹内,创建两个名为“app”和“server”的文件夹。

导航到“server”文件夹并创建一个名为“home”的新文件夹。在“home”文件夹内,创建一个名为“home.sv.hbs”的新文件

打开文件并将以下代码粘贴进去
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Home - My Oats~i App</title> </head> <body> <app-root id="my-app"> <div id="nav"> <a href="/" class="home-link">Home</a> </div> <main-fragment> </main-fragment> </app-root> </body> </html>
应用文件
现在导航回“src”。进入“app”文件夹,创建两个名为“fragments”和“index”的文件夹。

导航到“index”文件夹并创建两个名为“scripts”和“styles”的文件夹。

在“scripts”文件夹内,创建一个名为“routing-info”的新文件夹。在“routing-info”内创建两个文件,名为“app_main_nav_info.js”和“app_routing_info.js”

主导航信息
打开“app_main_nav_info.js”并粘贴以下代码
//@ts-check import MainNavigationInfoBuilder from "oats-i/router/utils/nav-info/main_nav_info_builder"; const AppMainNavInfo = MainNavigationInfoBuilder.buildMainNavigationInfo([ { selector: "home-link", defaultRoute: "/", baseActiveRoute: "/", } ]); export default AppMainNavInfo;
主路由信息
现在打开“app_routing_info.js”并粘贴以下代码
//@ts-check import RoutingInfoUtils from "oats-i/router/utils/routing-info/routing_info_utils"; import AppMainNavInfo from "./app_main_nav_info"; import homeMainFragmentBuilder from "../../../fragments/home/scripts/home_main_fragment"; const AppRoutingInfo = RoutingInfoUtils.buildMainRoutingInfo([ { route: "/", target: homeMainFragmentBuilder, nestedChildFragments: null } ], AppMainNavInfo); export default AppRoutingInfo;
Index.css
我们将创建一个 index.css 文件,这是有特殊原因的,如果你想要一致的行为,该原因必须在所有 Oats~i 项目中复制。
导航回“index”文件夹,并创建一个名为“styles”的新文件夹。在该文件夹内,创建一个名为“index.css”的新文件

打开文件并将以下代码粘贴进去
/* Crucial styling to allow specially structured A links to still have clicks intercepted by router. */ /* Carry over to your project */ a *:not([click-override=true]){ pointer-events: none }
这段 CSS 代码的作用是移除 A 标签内嵌套元素的指针事件,以确保浏览器在 Oats~i 之前不会拦截它。它还让开发者可以自由地通过在 A 标签内嵌套的任何元素上设置 attribute click-override=true 来覆盖此行为。
但是,请注意,在 Oats~i 目前的状态下,它不会拦截带有具有该属性的子元素的 A 标签的链接。
这意味着你可以安全地编写 A 标签,而无需进行任何修改或特殊属性,Oats~i 将自动拦截它们并在本地导航你的应用程序。只有当你想要停止这种行为并让浏览器手动路由网站时,才需要添加特殊属性。
在所有你创建的 Oats~i 项目中都应用此 CSS 指令。如果你使用 CLI,你会在 index.css 中找到它。
Index.js
导航回“scripts”(在 index 内部)并创建一个名为“index.js”的新文件。

打开文件并粘贴以下代码。
//@ts-check //import styles import "../styles/index.css"; import AppStateManager from "oats-i/base-history/app_state_manager"; import appRoot from "oats-i/bin/app_root" import AppRoutingInfo from "./routing-info/app_routing_info"; import MainRouter from "oats-i/router/main_router"; import AppMainNavInfo from "./routing-info/app_main_nav_info"; function initApp(){ const appStateManager = new AppStateManager(AppRoutingInfo); appRoot.initApp(appStateManager, new MainRouter(AppRoutingInfo, appStateManager, (args) => {}, "", async (url) => { return { canAccess: true, fallbackRoute: "/" } }), { template: null, mainNavInfos: AppMainNavInfo }, ""); } initApp();
Fragments
导航回“app”文件夹。导航到“fragments”并创建一个名为“home”的新文件夹。
在“home”内部,创建一个名为“scripts”的新文件夹。在“scripts”内部,创建一个名为“home_main_fragment.js”的新文件。

打开文件并将以下代码粘贴进去。
//@ts-check import AppFragmentBuilder from "oats-i/fragments/builder/AppFragmentBuilder"; import AppMainFragment from "oats-i/fragments/AppMainFragment" class HomeMainFragment extends AppMainFragment{ async initializeView(cb){ //@ts-expect-error cannot find module (for view) const uiTemplate = require("../views/home_fragment.hbs")(); this.onViewInitSuccess(uiTemplate, cb); } } const homeMainFragmentBuilder = new AppFragmentBuilder(HomeMainFragment, { localRoutingInfos: null, viewID: "home-main-fragment", }); export default homeMainFragmentBuilder;
现在导航回“home”并创建一个名为“views”的新文件夹。在“views”内部,创建一个名为“home_fragment.hbs”的新文件

打开文件并将以下代码粘贴进去
<h1>Home Fragment<h1/>
测试配置
导航到你的项目根目录。打开终端并运行
npm run dev
这将启动 webpack-dev-server,它将打包文件并运行 Oats~i。如果你在终端显示的 URL(通常是 localhost:8080)上打开浏览器,看到一个显示“Home Fragment”的页面,则表示你的项目已成功设置,Oats~i 运行正常。
配置可扩展性
无论你是手动设置 Oats~i 项目还是使用 CLI,都可以享受到配置灵活性,这得益于 Oats~i 运行在 Webpack 之上。
基本上,除了默认的 Oats~i webpack 配置之外,只要你理解 webpack、插件和加载器,以及它们将如何影响你的项目,你就可以根据自己的喜好更改任何其他内容。
例如,你可以有一个生产配置,它将使用 MiniCssExtractPlugin 将你的 CSS 提取到文件中,这些文件将被添加到最终的 html-webpack-plugin 输出中。你可以使用高级的 babel 配置,甚至可以将 handlebars-loader 切换为你喜欢的模板引擎的加载器。
然而,Oats~i 提供的默认设置对于大多数项目来说已经足够好了。在后续的教程中,我们将添加一个新的配置来创建最终的生产构建,其中包含诸如最小化等关键功能。
延伸阅读
我鼓励你学习 Webpack,了解它为何是必需的,以及如何配置它,以便充分利用 Oats~i 和其他使用 Webpack 作为打包器的项目。
注册并关注以获取下一个教程
这就是 Oats~i 项目的设置。如果你正在处理一个新项目,请直接使用 CLI。它更简单、更快,并且会直接加载到一个漂亮的入门项目中,你可以检查它,并在我们一起开始设置完整的项目(包含视图、样式和脚本文件)之前,获得如何设置这样一个项目的想法。
在下一个教程中,我们将创建我们在 Oats~i 中的第一个简单项目,届时我们将开始学习 Oats~i 中的路由信息、导航信息和片段。
点赞并关注,以便在下一个教程发布时收到通知。
我们下次见。
***
我可以在LinkedIn上找到你,所以过来打个招呼吧。
Oats~i 源代码:https://github.com/Oats-i/Oats-i
Oats~i X: https://x.com/Oatsi_js
Oats~i YouTube: https://www.youtube.com/@Oatsi