Rx[처음부터 시작]를 사용하여 단일 페이지 어플리케이션 구축

How ?
Basic, add your Html
Implement RxJS from Scratch
Fetch html files, Generate components
Router and Pages
Exemple
Repo github

어때요?


그래서 Angular, Vue, React 등 현재 프레임워크에서 일한 적이 있습니다. 한 페이지의 프로그램을 구축하고 싶습니다.
하지만 이번에는 자신에게 도전하고 스트라이크부터 구축하고 싶고, 그게 모자라면 자신의 RXJ를 실현하고 싶다.
여기서 우리는 어떻게 간단하고 효과적으로 이 점을 할 수 있는지 보게 될 것이다.
따라서 자바스크립트, Html, Css, nodejs를 사용하여 실시간 리셋을 진행할 것입니다
원하는 경우
  • HTML Custom Elements

  • 기본, 당신의 Html 추가


    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1, shrink-to-fit=no"
        />
        <title>Exemple</title>
      </head>
      <body>
        <my-header></my-header>
        <div id="app"></div>
        <my-footer></my-footer>
      </body>
      <script type="module" src="./index.js"></script>
      <link rel="stylesheet" href="./styles/index.css" />
    </html>
    
    <script type="module" src="./index.js"></script>
    
    스크립트를 가져오는 모듈처럼 가져오기 때문에, js 파일에서 'import {}from./any. js' 가 있는 js 파일을 가져옵니다.
    <my-header></my-header>
    
    <my-footer></my-footer>
    
    <div id="app"></div>
    
    이것은 우리가 단일 응용 프로그램 보기에 사용할 용기입니다.
    이 사용자 정의 요소들은 아직 정의되지 않았습니다. 나중에 'my -' 를 자신의 이름으로 바꿀 수 있습니다.

    RxJS를 처음부터 구현


    이 부분에 대해서 우리는 감사하다고 말할 수 있다. 우리는 더욱 가벼운 버전을 사용할 것이다
    그래서

    만약 당신이 우리 자신의 RxJS를 실현하고 싶다면, 우리는Subscriptions 클래스부터 시작해야 한다. 이것은 함수를 포함하는 용기이며, 낡은 함수를 포함한다.unsubscribe 방법을 호출할 때, 그 중의 모든 함수를 호출합니다.
    // a container for functions
    class Subscription {
      constructor() {
        this.teardowns = [];
      }
      add(teardown) {
        this.teardowns.push(teardown);
      }
      unsubscribe() {
        this.teardowns.forEach((teardown) => teardown());
        this.teardowns = [];
      }
    }
    
    파이프를 띤 산자 합성pipe의 개념이 간단하면 n 함수를 결합시켜 마지막 함수의 출력으로 모든 함수를 호출한다.
    이것이 바로 우리가 관측 가능한 데이터를 연결해야 하는 논리이다pipe 방법은 또 다른 관측 가능한 데이터를 되돌려줄 것이다.
    const pipe = (...fns) => (val) => fns.reduce((acc, f) => f(acc), val);
    
    pipe에 대해서도 우리는 하나의 유형으로 관찰할 수 있는 추상을 실현해야 한다.
    우리가 그것을 초기화할 때, 이 클래스는 매개 변수initFunc가 필요하기 때문에subscribe 함수를 사용하여 관찰자를 매개 변수로 호출할 것이다initFunc.
    const pipe = (...fns) => (val) => fns.reduce((acc, f) => f(acc), val);
    
    class Observable {
      constructor(initFunc) {
        this.initFunc = initFunc;
      }
      subscribe(observer) {
        const subscription = new Subscription();
        const subscriber = new Subscriber(observer, subscription);
        const teardown = this.initFunc(subscriber);
        subscription.add(teardown);
        return subscription;
      }
      pipe(...fns) {
        // provide source Obx to each function returned from pipeable operators,
        // to start the chaining operation provide the current source Obx (this)
        return pipe(...fns)(this);
      }
    }
    
    마지막으로 우리는 유사한 대리 대상을 실현해야 한다. Subscriber류의 역할은 complete를 호출할 때 사건이 포장된 관찰자에게 전파되는 것을 멈추는 것이다.
    // a safe wrapper around observers
    export default class Subscriber {
      constructor(_observer, _subscription) {
        this.observer = _observer;
        this.closed = false;
        this.subscription = _subscription;
        // 1. add an Observer completion logic to the Subscription container
        this.subscription.add(() => (this.closed = true)); // <- first function inside the subscription
      }
      next(value) {
        if (!this.closed) {
          this.observer.next(value);
        }
      }
      error(err) {
        if (!this.closed) {
          this.closed = true;
          this.observer.error(err);
          // 2. enable the Subscriber to call `unsubscribe` on completion
          this.subscription.unsubscribe(); // <- unsubscribe on error
        }
      }
      complete() {
        if (!this.closed) {
          this.closed = true;
          this.observer.complete();
          this.subscription.unsubscribe(); // <- unsubscribe on completion
        }
      }
    }
    

    html 파일 가져오기|구성 요소 생성


    리콜 파트에 오신 걸 환영합니다!우리는 로컬에서 사용하기 때문에 어떠한 의존 관계도 없기 때문에 html 파일을 스스로 가져와야 한다.아니오, import * as htmlTemplate from './template.html처럼 가져올 수 없습니다.
    근데 그거 알아요?이제 우리는 자신의 RXJ를 실현했고 약속이 아니라 관찰할 수 있는 것을 사용할 수 있다.
    우리는 _fetchLocal 파일에 api.js라는 방법을 만들어서 새로운 Observable와 요청 값을 되돌릴 수 있습니다._fetchLocal는 가져올 html 파일의 경로와 이름을 사용합니다.따라서 동일한 기능을 가진 페이지와 구성 요소를 가져올 수 있습니다.
    import Observable from "./../reactive/Observable.js";
    
    export const _fetchLocal = (path, file) => {
      return new Observable((observer) => {
        const req = new XMLHttpRequest();
        const url = `${path}/${file}`;
        req.responseType = "text/html";
        req.open("GET", url);
        req.send();
        req.onloadend = (e) => {
          observer.next(req);
        };
      });
    };
    
    현재 dom.js 파일을 만들고 registerComponent 함수를 넣을 수 있습니다. 이 함수는 구성 요소 클래스를 매개 변수로 합니다.
    이 함수에서 구성 요소나 페이지를 위한 이름 약정을 설정할 수 있습니다. (논리를 이 함수에 마음대로 추가할 수 있습니다.)
    export const registerComponent = (componentClass) => {
      const componentName = `my-${componentClass.name.toLowerCase()}`;
      customElements.define(componentName, componentClass);
    };
    
    이제 우리는 당신의 기반이 생겨서 더욱 쉽게 일을 시작할 수 있습니다. 우리는 Components 폴더를 만들고 우리의 첫 번째 Components를 만들 수 있습니다.
    꼬리와 꼬리를 위한 파일 ((Footer.html, Footer.js, Header.html, Header.js 을 만듭니다. Custom Elements 을 사용하는 것을 잊지 마십시오. 따라서 이 예에서 클래스를 HTMLElement 로 확장합니다.
    구조 함수에서 우리는 _fetchLocal 방법을 사용하여 관련 템플릿을 얻는다.
    <!-- footer -->
    <div class="footer">
      Hey footer
    </div>
    
    import { _fetchLocal } from "./../scripts/api/index.js";
    
    // Footer
    export default class Footer extends HTMLElement {
      constructor() {
        super();
        _fetchLocal("/components", "Footer.html").subscribe({
          next: (data) => {
            this.innerHTML = data.response;
          },
        });
      }
    }
    
    <!-- header -->
    <div class="flex-row">
      <nav class="nav">
        <a href="/" class="nav__link" data-link>Home</a>
        <a href="/dashboard" class="nav__link" data-link>Dashboard</a>
        <a href="/thispagedontexist" class="nav__link" data-link
          >This page don't exist</a
        >
      </nav>
    </div>
    
    // Header
    export default class Header extends HTMLElement {
      constructor() {
        super();
      }
      connectedCallback() {
        _fetchLocal("/components", "Header.html").subscribe({
          next: (data) => {
            this.innerHTML = data.response;
          },
        });
      }
    }
    
    그래서 지금 우리는 우리의 구성 요소를 등록해야 한다.
    주로js에서 define 방법을 만듭니다. 모든 구성 요소를 등록하기 위해서 registerComponent 사용합니다.define 방법을 init 함수에 넣다.
    import Header from "./../components/Header.js";
    import Footer from "./../components/Footer.js";
    
    import { _fetchLocal } from "./api/index.js";
    
    import { registerComponent } from "./dom/index.js";
    
    export function init() {
      define();
    }
    
    function define() {
      registerComponent(Header);
      registerComponent(Footer);
    }
    

    및 라우터 페이지


    우리는 반응 논리가 생겨서, 우리의 구성 요소가 응용 프로그램에 의해 다시 식별되었고, 현재 우리는 공격RouterPages를 할 수 있다.
    우리는 페이지를 위한 파일을 만들 수 있는 구성 요소처럼 구성 요소 논리를 존중합니다.
    그래서 우리는 404, Dashboard, Home에 pages 폴더의 파일을 만들 수 있다.( 404.html , 404.js , Dashboard.html , Dashboard.js , Home.html , Home.js )
    <!-- 404 -->
    <div class="404">
      <div>Are you lost ?</div>
    </div>
    
    import { _fetchLocal } from "./../scripts/api/index.js";
    
    export default class NotFound extends HTMLElement {
      constructor() {
        super();
        _fetchLocal("/components", "404.html").subscribe({
          next: (data) => {
            this.innerHTML = data.response;
          },
        });
      }
    }
    
    <!-- Dashboard -->
    <div class="dashboard">
      <div>this is dashboard</div>
    </div>
    
    import { _fetchLocal } from "./../scripts/api/index.js";
    
    export default class Dashboard extends HTMLElement {
      constructor() {
        super();
        _fetchLocal("/components", "Dashboard.html").subscribe({
          next: (data) => {
            this.innerHTML = data.response;
          },
        });
      }
    }
    
    <!-- Home -->
    <div class="home">
      <div>this is home</div>
    </div>
    
    import { _fetchLocal } from "./../scripts/api/index.js";
    
    export default class Home extends HTMLElement {
      constructor() {
        super();
        _fetchLocal("/components", "Home.html").subscribe({
          next: (data) => {
            this.innerHTML = data.response;
          },
        });
      }
    }
    
    따라서 router 폴더와 index.js 파일을 만듭니다.
    저희 index.js 파일에서 루트 논리를 androutes 대상에 넣고 pathcomponent를 키로 사용할 수 있습니다.
    이렇게:
    export const routes = [
      { path: "/", component: "Home" },
      { path: "/dashboard", component: "Dashboard" },
      { path: "/home", component: "Home" },
      { path: "/404", component: "404" },
    ];
    
    현재 우리는 Views 클래스가 필요합니다. 그러면 보기에서 제공하는 가져오기 HTML#app 용기에 설정할 수 있습니다.
    구조 함수에서 사용자 위치 경로 이름을 가져와 저희routes의 경로와 비교합니다. 일치하지 않으면 404페이지를 표시합니다.getHtml 방법은 _fetchLocal의 결과를 되돌려주고setView 방법은 획득한 html을 #app 용기에 넣는다.
    import { routes } from "./../router/index.js";
    import { _fetchLocal } from "./../api/index.js";
    
    export default class Views {
      layout;
      constructor() {
        this.layout = routes.filter((route) => {
          return route.path === location.pathname;
        })[0] || { component: "404" };
        this.getHtml().subscribe({ next: this.setView });
      }
    
      getHtml() {
        return _fetchLocal("/pages", `${this.layout.component}.html`);
      }
    
      setView(data) {
        document.querySelector("#app").innerHTML = data.response;
      }
    }
    
    main.js 파일을 되돌려줍니다. Views 함수에서 define 클래스를 호출하는 실례입니다.
    import Header from "./../components/Header.js";
    import Footer from "./../components/Footer.js";
    
    import { _fetchLocal } from "./api/index.js";
    
    import { registerComponent } from "./dom/index.js";
    import Views from "./dom/views.js";
    
    export function init() {
      define();
    }
    
    function define() {
      registerComponent(Header);
      registerComponent(Footer);
    
      new Views();
    }
    
    프레젠테이션에 대해 css를 추가할 수 있습니다. 자동 크기와 가운데 배치에 다음과 같은 내용을 추가할 수 있습니다.
    html,
    body {
      height: 100%;
      width: auto;
      margin: 0;
      padding: 0;
      display: flex;
      flex-direction: column;
    }
    
    /* reset all */
    *::before,
    *::after {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    
    a {
      padding: 0.25em;
    }
    
    #app {
      display: flex;
      flex-grow: 1;
      align-items: center;
      align-self: center;
    }
    

    예제



    현재 우리는 이 온라인 예시를 통해 우리의 결과를 볼 수 있다.보시다시피 저희 Header, 저희 Footer, 그리고 #app 용기의 레이아웃은 저희 공유기 논리를 사용하고 요청한 page 를 표시합니다.우리의 단일 페이지 응용 프로그램은 만들어진 것이고 수동적인 것이다. 모든 것이 매우 좋다.

    모든 독자들에게 감사 드립니다. 만약 당신이 끝까지 용기를 가지고 서 있다면.

    Repo github


    NOPR9D / htmljs 단일 응용 프로그램 rx


    0부터 의존 관계가 없다.공유기가 생겼어요...


    RxJS가 포함된 단일 어플리케이션|의존성 없음


    다음 네트워크 프로젝트에서 경반응 시작 프로그램을 사용하십시오.🚀
    View on GitHub

    좋은 웹페이지 즐겨찾기