>  기사  >  웹 프론트엔드  >  Oats~i   개방형 웹 프레임워크 소개

Oats~i   개방형 웹 프레임워크 소개

王林
王林원래의
2024-08-14 10:34:32985검색

Introducing Oats~i - The Open Web Framework
저는 약 5년 동안 활발한 웹 개발자로 활동해 왔습니다. 2019년 말, 제가 공식적으로 웹 개발을 시작했을 때, 저는 클라이언트와 개인 프로젝트를 위한 웹사이트를 만드는 데 활용할 수 있는 방대한 웹 개발 도구 생태계에 둘러싸여 있었습니다.

그런데 나는 DIY의 길을 걷고 있는 나 자신을 발견했습니다. 고통스럽고 머리가 아픈 순간에 익숙해서가 아니라 웹 개발의 기본부터 배우고 싶었고 프레임워크로 바로 뛰어들어 거기에서 지식을 쌓고 싶었기 때문입니다.

게다가 대부분의 경험 많은 웹 개발자들이 조언하는 사항이기도 합니다. HTML, CSS, Vanilla JavaScript를 배우고 그 외에 무엇이든 쉽게 배울 수 있습니다.

5년이 지난 지금, 저는 어떻게든 제 자신만의 웹 프레임워크를 만들게 되었습니다. 웹 및 웹 API의 작동 방식에 대한 간단한 학습 연습으로 시작된 것은 결국 수많은 머리 긁는 순간, 실망, 유레카를 포함하는 본격적인 프로젝트가 되었습니다.

기본으로 돌아가게 해주는 개방형 웹 프레임워크인 Oats~i를 소개합니다. Oats~i는 강력한 확장성, 서버 측 렌더링, 동의 기반 라우팅, 데이터 관리자를 통한 반응성, 보기 관리자 및 후크를 갖춘 HTML, CSS 및 Vanilla JavaScript를 사용하여 웹 앱을 만들 수 있는 구조를 제공합니다. 기반 보기 시스템, 프래그먼트 위에 추가 레이아웃, 팝업 및 사용자 정의 보기를 지원하는 보기 패널, 매개변수, 쿼리 및 대상과 같은 "네이티브" 웹 탐색 기능 지원, 페이지 매김, 코드 분할 및 JavaScript에 대한 지연 로딩 및 번들을 확인하세요.

이 모든 기능은 기본적으로 프레임워크와 함께 제공되며 기본 모듈 번들러로 Webpack 위에서 실행됩니다.

Oats~i는 순전히 클라이언트 기반 시스템이기 때문에 서버 환경에 신경 쓰지 않습니다. 서버에서 실행 중인 JS가 없으므로 Oats~i 앱을 배포하는 데 특별한 서버 설정이 필요하지 않습니다.

자세히 알아보기 전에 현재 어디에서 실행되고 있나요?

여기: https://www.vertesolutions.com

해당 사이트는 에코컨설팅 및 에코비즈니스를 다루는 클라이언트의 생산 사이트입니다. 회사 이름은 Verte Environmental Solutions입니다. 따라서 "Verte 웹사이트"를 언급했다면 해당 사이트가 언급된 것입니다.

오츠~아이는 현재 다른 곳에서는 운영되지 않습니다.

편집: 소스 코드도 공개되어 있습니다. https://github.com/Oats-i/Oats-i

이 시점에 도달하기 전에 저는 사이트의 관리 패널(맞춤형)에서 프레임워크를 개발하고, 테스트하고, 업데이트하고, 수년에 걸쳐 새로운 기능을 추가해 왔습니다. 따라서 여기 클라이언트 사이트에서 제가 설명할 내용 중 일부를 놓치셨다면 해당 내용이 관리자에서 실행되고 있는지 확인하세요.

또한 제가 소개할 것은 몇 가지 기능을 설명할 수 있는 좀 더 구체화된 프레임워크입니다. 그러니 주의하세요. 이 글은 긴 글이 될 것입니다. 그리고 최대한 짧게 편집하려고 노력했습니다. 그러나 나는 이 서문을 통해 그 모든 것의 핵심을 다루었습니다. 나머지 부분은 향후 게시물에서 더 자세히 알아보기 전에 표면의 일부일 뿐입니다.

이제 소개는 이쯤 하겠습니다.

지금까지 구축한 Oats~i에 대해 좀 더 자세히 알아보고, 기본적으로 제공되는 기능과 향후 계획에 대해 살펴보겠습니다.

개방형 웹 프레임워크

Oats~i를 개방형 웹 프레임워크라고 부른다는 것은 Oats~i가 간단한 HTML 및 바닐라 JavaScript로 코드를 작성할 수 있고 CSS가 사실상 스타일링 도구인 간단하면서도 확장 가능한 프레임워크라는 의미입니다. 이 간단한 설정을 통해 Webpack에서 허용하고 구성할 수 있는 한 자체, 타사 또는 사용자 정의 템플릿 엔진, CSS 라이브러리 및 기타 도구를 추가할 수 있습니다.

조각 기반 시스템

Oats~i는 조각을 "구성 요소" 또는 웹 앱의 핵심 부분으로 생성하는 빌드 시스템을 통해 작동합니다. 다음과 같은 간단한 보기를 예로 들어보세요.

Introducing Oats~i - The Open Web Framework

Introducing Oats~i - The Open Web Framework

두 이미지 모두 사용자에게 항상 표시되는 앱의 기본 보기인 앱의 '루트 보기'를 갖습니다. 그런 다음 다른 보기(조각)가 동적으로 렌더링됩니다.

Introducing Oats~i - The Open Web Framework

Introducing Oats~i - The Open Web Framework

루트 보기에는 기본 탐색 링크나 버튼, 그리고 사용자가 앱에서 항상 볼 수 있고 일반적으로 변경되지 않는 기타 보기가 포함될 수 있습니다.

루트 보기 내부의 나머지 보기는 변경되며 이는 사용자의 라우팅에 따라 앱에서 로드 및 오프로드되는 프래그먼트를 기반으로 합니다. 프래그먼트는 주로 렌더링할 뷰를 가져오고 이를 대상 상위 노드에 배치한 다음 나머지 앱 및 비즈니스 로직을 연결할 수 있도록 하는 빌드 프로세스를 거칩니다.

Oats~i 빌드 프로세스는 일반적으로 프래그먼트에서 다음 핵심 메서드를 트리거합니다.

//gets the view for your fragment
async initializeView(cb){
}

//triggers after the fragment view has been attached to the DOM
onUIBind(serverSideRendered){
}

//triggers after the fragment has completed building and anytime its rebuilt for the same route
async onQueryParamsDataUpdate(changedParams, data, savedState, routeParams, isServerSide){
}

기본적으로 그게 전부입니다.

이러한 골격 구조에는 다음과 같은 몇 가지 유연성이 있습니다.

템플릿 엔진을 사용하여 간단한 HTML 렌더링 또는 복잡한 보기 로드(선택)

재정의한 첫 번째 메서드(initializeView())는 다음과 같이 완료할 수 있습니다.

async initializeView(cb){

  const viewAsString = `<p class="text">My view</p>`;
  this.onViewInitSuccess(viewAsString, cb);
}

뷰를 HTML 문자열로 가져와 이를 내부 메소드(onViewInitSuccess())에 전달합니다. 이 메소드는 원래 메소드에 전달된 콜백도 사용합니다.

onViewInitSuccess()를 호출하면 빌드 프로세스가 트리거되어 다음 단계를 계속 진행합니다.

JS 내에서 HTML을 문자열로 작성하는 것은 간단하고 Oats~i에서 허용하지만 종종 문제가 발생할 수 있습니다. 그러나 Oats~i에 대한 뷰 작성을 위한 새로운 구문이나 시스템을 구축하는 대신 Oats~i를 사용하면 사용 사례에 가장 적합한 템플릿 엔진을 연결하고 이를 웹팩 구성에 연결하여 마술처럼 작동하게 할 수 있습니다. .

Verte의 경우 핸들바 로더와 결합된 핸들바를 사용하여 별도의 뷰 파일을 hbs 형식으로 작성하고 코드에서 요구합니다.

그래서 대신

const viewAsString = `<p class="text">My view</p>`;

내 견해는 이제 다음과 같이 제공됩니다.

const viewAsString = require("./relative/path/to/view.hbs")(myTemplatingData);

예를 들어 ejs를 대신 사용하려면 웹팩 구성을 업데이트하고 해당 사용 사례에 맞는 가져오기 구문을 사용하면 됩니다.

Oats~i는 전달된 뷰가 HTML 문자열이라는 점에만 관심이 있습니다.

네트워크 소싱 보기

Oats~i는 네트워크를 통해 조회수를 얻을 수 있도록 지원합니다. 이것이 부분적으로 초기화 보기() 메서드에 비동기가 존재하는 이유입니다.

Oats~i는 또한 이 단계에서 사용자 유형이나 기타 요인에 따른 전체 보기를 위해 네트워크 호출을 하거나 보기 및 비즈니스 로직에 따른 템플릿 데이터를 얻을 것으로 기대합니다.

여기서 수행하는 작업은 전적으로 비즈니스 및 기술적인 이유에 따라 다릅니다.

**참고: **빌드 시스템이 빌드 단계에서 Await 또는 then()을 사용하여 Promise가 해결될 때까지 기다리지 않고 대신 관련 메서드에 전달된 콜백을 사용하는 데는 그럴 만한 이유가 있습니다. 나중에 Oats~i가 어떻게 작동하는지 자세히 알아볼 때 그 점은 분명해질 것입니다.

바닐라 자바스크립트 또는 앱 또는 비즈니스 로직용 호환 JS 라이브러리

Oats~i 코드는 웹 브라우저가 이해하는 "기본" 언어인 바닐라 JavaScript로 되어 있습니다. 그러나 비즈니스 로직을 작성할 때 몇 가지 유연성을 가질 수 있습니다.

예를 들어 어떤 이유로든 프로젝트에 jQuery를 포팅하고 이를 사용하여 로직의 일부를 작성할 수 있습니다. 나는 실제로 오래전부터 Oats~i가 현재 상태로 구축되기 전에도 Verte 웹사이트에서 부드러운 스크롤 효과를 위해 약 5줄의 코드를 작성하기 위해 이 작업을 수행했습니다. (TLDR, 스택 오버플로를 넘어서 생각하기에는 게을렀습니다. ㅋㅋㅋ).

이론적으로 Oats~i를 TypeScript 환경에서 사용할 수 있지만 아직 테스트하지는 않았습니다. 제가 TypeScript를 사용한 유일한 용도는 JSDocs와 결합하여 프레임워크 내에서 유형을 문서화하는 타이핑 시스템이었습니다. 이 방법은 제가 얼마 전에 문서화한 방법입니다.

여기에서 빌드 프로세스 없이 입력 목적으로 JSDocs와 TypeScript를 통합하는 방법을 읽을 수 있습니다.

코드 분할 및 지연 로드

Webpack은 매우 복잡한 프로젝트 구성을 허용하는 강력한 웹 개발 도구로, 개발 팀이 고유한 사양에 맞게 프로젝트를 구축하는 데 필요한 유연성을 제공합니다.

Oats~i는 Webpack 위에서 실행되며 프레임워크는 주로 Webpack의 코드 분할 및 지연 로딩 기능을 사용하여 비동기 조각 청크 및 번들을 지원합니다.

즉, 조각을 하나의 번들로 로드하거나 webpack을 사용하여 여러 청크로 분할하여 Oats~i 웹 앱의 초기 로드 속도를 최적화할 수 있습니다. 앱에 필요한 경우 이를 네트워크 소스 뷰와 결합하고 Oats~i에서 앱을 최적화하여 로딩 시간에 관한 최고의 사용자 경험을 보장할 수 있는 여러 가지 방법이 있습니다.

Webpack을 사용한 고급 프로젝트 구성

아마도 Oats~i의 기반으로 webpack을 사용하는 것의 가장 큰 장점은 원하는 대로 앱을 제작할 수 있는 대규모 구성이 가능하다는 것입니다.

That's why you can set up templating engines that suit your view rendering process, configure babel and other loaders/plugins for your app, and simply build something that is fully-specced to your project's specifics.

Oats~i runs a simple base webpack configuration that sets up handlebars-loader, html-loader, css loader, asset loader, and HTMLWebpackPlugin to create your server-side views or templates. Using webpack-merge, you can extend these configurations and architect your web app as you want it.

This makes Oats~i works a lot like a plug-and-play system. It gives you a skeleton, and you can wrap and configure your app around it as you like.

Routing

Routing is a default feature in Oats~i. In fact, to run the app, you must provide routing information that the app will use to initialize itself and manage user navigation and fragment rendering.

A simple routing information looks like this:

Const MyRoutingInfos = [
  {
    route: "/my-route",
    target: myMainFragmentBuilder,
    nestedChildFragments: [
      myNestedChildFragmentBuilder
    ]
  }
]

When Oats~i loads from the server, it checks the current url and finds a match for it in the provided routing info. In Verte's case, when you load "/", Oats~i searches for the routing info with that that route as a match and then inflates the fragments in order from "target" to each nested child fragment.

You can also provide a default route that Oats~i will try to start the app from, unless the client had sourced the page from a valid route given in your routing info.

Params in Routing

Oats~i also supports the use of params in routes, using the colon syntax commonly used in express.

Therefore, a route defined like /:myParams is valid, and will map for routes such as /user-1, /user-2, /user-3.

Oats~i goes a step farther and parses these params for you.
When setting up your fragment, you have the option of setting up params it should watch out for. The name of the param should be an EXACT match to the name used in your routing info.

When building the fragment, Oats~i will parse the value, note any changes, and pass two arguments to your onQueryParamsDataUpdate() method. These are an object of all watched params that have changed, and the current value of all watched params.

Therefore, if you have a fragment that shows user information, defined under the route /:userID, and the client first navigates to /user-xyz, you'll be able to read the value of userID as user-xyz. If the client routes again and this time the route is /user-abc, you'll immediately know that the value of userID has changed to user-abc and you can respond appropriately.

Queries Support

Queries are also a core part of web browsing and urls. Oats~i also parses queries for you, as long as you tell the fragment to watch them, using their keys.

For instance, if your route /:userID maps to /user-3?promptUpgrade=true, and you specify in your fragment that you want to watch updates for the query with the key "promptUpgrade", these will be parsed and sent to the method onQueryParamsDataUpdate() as well.

However:

You cannot write routes in your routing info using queries. Only params are supported. Oats~i looks for the valid routing info for a given url after truncating any queries and targets. The parsing will be done afterwards.

Verte's website already uses this mechanism when rendering views for blog articles at the blog article page. The route for each article is parameterized and we only respond to a change in the watched param.

Consent-Based Routing

This is perhaps a very unique feature from Oats~i. Consent-based routing gives you power over the user experience, allowing you to warn users about navigating away from a crucial page in case there are any pending processes, all controlled in-app.

Instead of using the provided standard browser API that pops up a dialog box, Oats~i uses a mix of History API and state management to detect a pop or navigation, ask the current rendered fragments for consent, halt subsequent navigation attempts, and proceed only if the user grants it permission.

If the user chooses to remain in their current view, Oats~i restores the browser's navigation path to the original state.

Of course, having users click on "ok" every time they want to navigate around your app is a bad idea. So, by default, Oats~i fragments and view panels (more on these later) consent to a navigation attempt by default.

Verte internally uses this to safeguard the admin when curating blog content, in case the current draft has not yet been picked up by the autosave script within its time delta. In case the admin wants to navigate away from the blog editor and there are unsaved drafts, they'll get a warning through a dialog and choose to either continue navigating away or stay on the page and manually save their work.

Pop-Ups, Dialogs, and More Layouts Using View Panels

In Oats~i, the framework will primarily render a route through fragments. However, there's an extra utility called view panels that allows you to render other views that your fragment may need on the fly. These include dialog boxes, hamburger panels, or even loading screens with bespoke information that the user may need.

To spawn a view panel, you have to request for it through the view panels manager. Oats~i self manages views for fragments and view panels, meaning you never have to write logic to bind your primary fragment views to the DOM or remove them once a view panel or its associated fragment is being destroyed due to a change in navigation.

A view panel, spawned by a view panels manager is also automatically wired into the consent-routing process of the fragment, allowing you to extend fragment functionality.

View panels can also watch params and queries.

Route-Triggered and Direct-Triggered View Panels

View panels can be triggered either by route changes or directly via a call to the fragment's view panels manager. For the former, this is where having queries in your route and linking them to a view panel within the fragment can come in handy.

If you have a route "/:post-id" which is currently represented in the browser as "/nice-post?showComments=true", you can use a route-triggered view panel within the fragment to automatically pop a side panel that loads the post comments and allows the user to read through them.

This feature is typically accessible through the onQueryParamsDataUpdate() method. Calling super (in case you've overridden it) will invoke the fragment's view panels manager to attempt to render any route-triggered view panels.

The biggest advantage of this kind of setup is that your view panel's rendering and behavior is now tied to the navigation, making the user experience more natural.

So, given our example, if the user navigated to "/nice-post?showComments=true", read the comments, and pressed back, the route will change back to "/nice-post", the view panels manager will note this change, and automatically trigger the destruction process for the view panel as long as consent has been granted.

Just like fragments, view panels also grant consent by default. Therefore, you should override the consent method ONLY when necessary.

Reactivity and Data Management

A modern web framework is not complete without a good touch of reactivity and data management. And here's where perhaps the most crucial difference between Oats~i and other web frameworks comes in.

Oats~i doesn't automatically couple views to a piece of data or state.

Instead, this is left entirely to the developer to do it based on their app or business logic.

As is, you can use Oats~i to build a web app with multiple static pages rendered under fragments and view panels and end it at that. The app will just work. If you want to add data, network calls, and reactivity, the data manager utility covers everything, and only to the scope that you determine, without affecting any surrounding views or data.

Let's look at the data manager and its supporting utilities: the network interface and view managers.

The Data Manager

The data manager is an Oats~i utility that allows you to tie data, server-resources, and client views together. The data manager holds an array of models, a model being the core piece or type of data associated with a section of your app and its selected views.

Currently, I've designed it to take a model as an object with arrays nested within, as it's the most common format for passing data around client and server resources (as Json).

Therefore, a simple model can look something like this:

{
  my: string,
  simple: number,
  obj: {
    ofArrays: number[],
    objArrays: { objKey: string }[]
  }
}

The data manager works by scoping its model. This means that every bit of the model can be treated as a unit, creating a set of dot-separated keys that define a specific value or type in your data.

For instance, in the example above, the data manager will break down the model into the following scopes: "MODEL_ROOT | my | simple | obj | obj.ofArrays | obj.objArrays | obj.objArrays.array.objKey "

These scopes represent:

MODEL_ROOT -> {
  my: string,
  simple: number,
  obj: {
    ofArrays: number[],
    objArrays: { objKey: string }[]
  }
}

my -> string,

simple -> number

obj -> {
  ofArrays: number[],
  objArrays: { objKey: string }[]
}

obj.ofArrays -> number[]

obj.objArrays -> { objKey: string }[]

obj.objArrays.array.objKey -> string

You can treat these scopes as dot-separated paths to a distinct piece of data.

With these scopes, the data manager then gives you, the developer, fine-grained control of your data, allowing to assign a network interface or view manager(s) to any of these data.

Let's shallowly dive into what these two are.

Introducing Oats~i - The Open Web Framework

Network Interface

In most apps (native or web), the data shown to the user is sourced from an outside resource, a server. Therefore, the internal model often needs an API interface that sits between itself and the external resource.

In Oats~i's case, the network interface will perform the CRUD operation you need in relation to the data held by the data manager and ensure both ends are in sync.

The network interface is defined as an object with three methods:

getReqBody()

This method gets the body of the request and other data such as method, address, headers, etc.

onDataLoadPostProcess()

Because the type of response data and the type of your internal model may vary, the network interface allows you to post-process the response and provide the final data in the data manager's model type.

onDataLoadError()

This method allows you to format the error response in case the network call fails.

Network Interface Scoping

API designs are varied, meaning, the addresses or routes used to make CRUD operations for a given piece of data can be different.

For instance, a social media app can have a different API for loading all posts, and each post running unique APIs to repost, like, or report the post.

Assuming such an architecture, using scoping within the data manager allows you to specify unique network interfaces for each scope.

For instance, you can have a network interface for the MODEL_ROOT network call (which will load the posts), "repost" network call, and any other call that can be scoped out of the structure of the model the data manager holds.

This gives you a whole unique way of viewing your data, breaking it down from one large resource with a common end point, to a collection of multiple data units that can be handled independently through the data manager.

A key thing to note here is that you can only have one network interface per scope, creating a single "endpoint" for every scoped piece of data in your model.

View Manager

Through the network interface, the data manager can now keep data in sync between its model and the server. Now what about displaying it to the user and, more importantly, showing them when it's changing?

That's where the view manager comes in.

View managers respond to mutations or changes happening to data held by the data manager, through a network operation or a direct in-app change.

Oats~i currently supports two types of view managers - a standard view manager and a list view manager.

A standard view manager is ideal for simple views with components that are not duplicated over a list. On the other hand, a list view manager is best for "complex" views with view components duplicated over a list.

Regardless of the type, a view manager will tell you of the following changes within a model or its scoped types:

onMutate()

This method fires when a data type of the scope is changing

onCommit()

This method fires when a mutation of the data type of the scope has been completed, thus committed

onCancel()

This method fires when a mutation of the data type of the scope has been cancelled

onError()

This method fires when a mutation of the data type of the scope has encountered an error, allowing you to retry

There's also the builder set of methods, which allow you to pass in a view (as a HTML string) inflated with your data. These methods also inform you of when the view has been attached or about to be detached, depending on the mutation.

These three methods are:

inflateRoot()

Gets the templated view as a string for the data provided

onViewAttach()

Calls when the view has been attached to the DOM

onViewDetach()

Calls when the view is about to be detached from the DOM
You can see the results of these interactions in the blog pages of Verte's website.

Using the combination of builder methods, root hooks, and component hooks, the data-driven dynamic views of the blog and blog article fragments can show loading animations when we're sourcing data from the network, show error messages in case of a failure, and populate the views once the new data from the network interface has been committed.

A view manager will also have component hooks which allow for even finer grained reactivity, with the elements of each view node.

For instance, using the model:

{
  my: string,
  simple: number,
  obj: {
    ofArrays: number[],
    objArrays: { objKey: string }[]
  }
}

And a view manager of the scope "MODEL_ROOT" (therefore the whole model), we can assume that the main view component showing the data of the MODEL_ROOT scope, has components within it that my show the specific data held in "my", "simple", "obj", or generally the child scopes of MODEL_ROOT.

Therefore, you can set up a component or element of your view to react to changes in these "child" values.

All these hook methods get a viewNode parameter passed to them by the view manager, so you always have a reference of which view node these data changes are associated with and query its components as you need.

However, you should not bother with removing these core view elements once they're no longer needed. The view manager handles that for you.

No Virtual DOM

Oats~i doesn't operate through a virtual DOM. Instead, the fragments, view panels, and view managers directly use the DOM APIs to insert or remove DOM elements.

After inserting your view component into the DOM, the view manager will provide you with its direct reference in the builder, root, and component hooks. Therefore, you can just directly add listeners, change attributes, or simply manipulate the DOM element using the direct DOM apis.

Lifecycle Management

A core bit of a complex web app is lifecycle management. Oats~i has its own lifecycle management process for fragments and view panels, whose functions are extended to other utilities such as the data manager, view managers, and remote request util (the actual utility the data manager uses in conjunction with the network interface to make network calls).

Therefore, straight off the bat, using Oats~i and its core utilities will have lifecycle automatically managed for you.

For instance, if you're using the data manager within a fragment to make a CRUD operation, and the user navigates away from the fragment, the data manager and remote request util will be able to cancel all network operations, skip updating view managers, and unregister them, because your fragment or view panel no longer exists.

Listening to Lifecycle Events

As an Oats~i developer, you can make use of a fragment or view panel's lifecycle management to create robust lifecycle-aware libraries that will work well in an Oats~i environment.

You just have to grab the lifecycle object using the internal method,

getLifeCycleObject()

and attach listeners to it. These listeners typically include four methods for:

onFragmentRunning()

Called when the fragment has been created and is running

onFragmentCancelled()

Called when the fragment's build has been cancelled

onFragmentDestroyed()

Called when the fragment has been destroyed

onViewReady()

Called when the fragment's view has been attached to DOM

*Note: *"Fragment" here also applies to view panels.

The main calls you need to watch out for are onFragmentRunning(), onViewReady(), and onFragmentDestroyed(). If your library adds functionality that is not UI-related, you can enable the library after getting the onFragmentRunning() call.

If the library manipulates views (such as an animation library), you can enable its functionality after receiving the onViewReady() call.
Once you get the onFragmentDestroyed() call, pack up, and stop everything.

OOP-Based Core

We have talk about a lot about some core features of Oats~i but we haven't talked about paradigm. How will you write core Oats~i code?

Well, Oats~i is an OOP-based web framework. That means most utilities are provided as classes. A fragment is created from an AppMainFragment or AppChildFragment class. The data manager is a class. View managers are classes and so on.

I chose OOP because of its reusability, garbage collection, and a much cleaner way of managing functions and processes within Oats~i.

For instance, no pun intended, having the fragment as a class allows Oats~i to do something clever. It never reconstructs the fragment class if it determines that its being reused. Instead, the build process just goes directly to firing onQueryParamsDataUpdate(), and doesn't re-render the fragment's main view or update that section of the DOM, since it's unnecessary.

Another advantage of doing this is that your fragment can retain part of its state amidst related route calls.

For instance, in Verte's case, when you're in the fragment that renders a blog article, clicking on another article under the "Other stories" list doesn't reconstruct the fragment. Instead, the original view is untouched, and only the dynamic, data-driven views, ran by the data manager in conjunction with the view manager, update based on the new param value obtained from onQueryParamsDataUpdate().

Exploiting Functional Programming

Just because the Oats~i core uses OOP, doesn't mean you're fully restricted to creating libraries that follow the OOP paradigm. Simply making them lifecycle aware is enough.

This will allow them to capture and free resources from the fragment as Oats~i renders and destroys them.

When porting Verte's client to Oats~i, I've used this exact strategy to reuse some functional scripts I'd written for the original webpages.

Therefore, I expect very few bottlenecks and paradigm strictness for developers seeking to use their functional scripts in an Oats~i project, as long as they're lifecycle aware.

Server-Side Rendering (Views and Data)

Finally, a big part of modern web frameworks - server-side rendering.
Oats~i natively supports server-side rendering, with no need for running JavaScript on the server.

Using HTMLWebpackPlugin, you can extract the views you use for each fragment in your Oats~i app into their own .html/.hbs files that you can send to the client when they request for a page on a fresh load.

The only requirement is your view structure from the server is the same as the app would have rendered it.

Introducing Oats~i - The Open Web Framework

But we're not done yet.

Data Manager Hydration

The views you'll render from your server most likely will represent some state of data. How does Oats~i handle this and proceed from the server-side rendered state?

You'll ideally be using the data manager to manage dynamic or data-driven views in your Oats~i app. Now, using it, you can can leverage server-side hydration that uses a script rendered in the head tag from the server to help the data manager understand the data state from the server, save it, and have attached view managers also update their states based on it, and continue running the app from there.

Here's how it works.

In your markup's head, at the server, you can add a script of the following format:

<script id="hydrator_id">
  const DataManagerHydrationInfo = {
    "info_key": {
      info: model[]
      extras: *
    }
  }
  window.DataManagerHydrationInfo = DataManagerHydrationInfo;
</script>

This script provides important information for the data manager from the server, that gives it the full picture or an idea of the data state.

Each data manager will have an "info_key" that it will read its data state from. Once you set the data manager to hydrate from server side, it will find the script with the id you provide, get the exposed variable DataManagerHydrationInfo, and read the value of "info_key".

This value should be an array, ideally of the same type as the data manager's model. However, it can be different.

That's because the data manager runs a multi-step hydration process.

Validation

Reading from a script can have its own issues and vulnerabilities. You can run a validation check on the data stored in the hydration script before the data manager commits it.

Preprocessing

Depending on your business logic and web app design, the data format sourced from your server can be different from the model you run in your data manager. Oats~i's data manager runs an optional preprocessing step during hydration, that allows you to convert the data from the hydrator to your model's format.

Network Step

This step permits you to be cautious with the data you let free in your hydration script, open to web scrapers, robots, and search engines.

You can run an optional network step where you can get private or hidden data that your data manager's model needs, but should never be privy to web scrapers or robots scouring the web.

For instance, if you're hydrating a shopping cart, you can have the hydration script from the server contain only general information about the products, with public ids that when passed to your secure backend, will return more secret information that you'll use to check-out the user.

So, your hydration script can hold information as basic as what is already rendered in the html, have the data manager commit that immediately internally, then source everything else from the network cycle.

What Next for Oats~i?

If you've managed to read up to this point, kudos, you're a champ! That's the best I could do to try and squeeze roughly four years of work into a small "introductory" blog post.

Oats~i has been a massive learning project for me and am both anxious and excited to let the tech community know about it. There's a lot to unpack, teach, learn, and debug.

My plan at the moment is to open source Oats~i. I'm working on the specifics and hopefully the whole codebase will drop in the next few days and we can all dig in, build actual web apps through the framework, and take it up its paces.

For now, I'll appreciate your feedback, comments, and questions concerning Oats~i, if you have any.
Check out Verte Environmental Solution's website and see it in action.

I'm available on LinkedIn, so drop by and say hi.

See you soon, when we'll, hopefully, start building with Oats~i.

EDIT: The source code is now public. https://github.com/Oats-i/Oats-i

위 내용은 Oats~i   개방형 웹 프레임워크 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
이전 기사:CSS 프로젝트 1다음 기사:CSS 프로젝트 1