[Project]JavaScriptを使用したルーティング


開始します。🌈


ふーん!昨日と今日で10時間近くウロウロして、やっと実現!🖐🏻🖐🏻🖐🏻
実は原理はとっくに知っていましたが、私のすべてのコードを覆す必要があるので、
結果はもう一つの小細工(?)探している間に変わった
しかし、結局私は貴重なものを手に入れた.
ためらってもためらわないうちに直接叫んでください.👍👍
では、どうやって実現したのか見てみましょう?!

本題📖


私はまずAppに以下のページを置きました!
ここで注意したいのは、SPAの特徴です.
これにより、ページをルーティングとは別に、上記でインスタンスとして呼び出すことができ、後でルーティングの移動に応じてページを容易に貼り付けることができる.
そのため、初期ロードには時間がかかりますが、最終的には速度が速いという利点があります!👍👍
実際、私はこれを放棄したいと思っています.趣旨から、一貫性のために、私はもっと多くの時間を費やしました.😅
コードは次のとおりです.
import router from './apis/router.js';
import MainPage from './pages/MainPage.js';
import PostEditPage from './pages/PostEditPage.js';
import { READ_POST_ROUTE } from '../src/utils/constants.js';
import removeAllChildNodes from './utils/removeAllChildNodes.js';

export default function App({ $target }) {
  const postEditPage = new PostEditPage({
    $target,
    initialState: {
      postId: 'new',
    },
  });

  const mainPage = new MainPage({
    $target,
    initialState: {
      username: 'jengyoung',
      documents: [],
    },
    onClick: id => {
      history.pushState(null, null, READ_POST_ROUTE + `/${id}`);
      this.route();
    },
  });

  this.route = () => {
    removeAllChildNodes($target); // App 초기화
    const { pathname } = window.location;
    const splitedPath = pathname.split('/');

    if (pathname === undefined || pathname === '/') {
      mainPage.setState();
    } else if (pathname.indexOf(READ_POST_ROUTE + '/') === 0) {
      const postId = splitedPath[2];
      postEditPage.setState({ postId });
    }
  };

  this.route();

  router(() => this.route());
}
もしここに何か特別な場所があったら.うーん、removeAllChildNodesなので、すべてのノードを削除する関数を作成しました!特にありません.while文を書いて削除しました
export default function removeAllChildNodes(node) {
  while (node.hasChildNodes()) {
    node.removeChild(node.lastChild);
  }
}
特に、ページに設定されたstateは、更新されるたびにレンダリングされます(正確には、同じページコンポーネント(posts/1->posts/2)に移動するとします.
私は次のsetStateに従います.
ページを見てみましょうか?!

MainPage.js


昨日とずいぶん変わった
熟考の末、ノーソンのようにSideBarに名前をつけることにしたが、個性的にタイトルに掲げた.これからはもう一つ作ります!
import request from '../apis/request.js';
import Header from '../components/common/Header.js';
import SideBar from '../components/SideBar.js';

/*
  {
    username: string
    documents: [<object>]
  }
*/
export default function MainPage({
  $target,
  initialState = { username: '', documents: [] },
  onClick,
}) {
  this.state = initialState;

  const $page = new DocumentFragment();
  // const $page = document.createElement('div');
  const sideBar = new SideBar({
    $target: $page,
    initialState,
    onClick,
  });

  const header = new Header({
    $target: $page,
    headerSize: 'h5',
    initialState: {
      content: this.state.username,
    },
  });

  this.setState = () => {
    const posts = request();
    this.state = {
      ...this.state,
      documents: posts,
    };

    const { username, documents } = this.state;
    header.setState({ content: username });
    sideBar.setState({
      username,
      documents,
    });

    this.render();
  };

  this.render = () => {
    $target.appendChild($page);
  };
}

PostEditPage.js


ここでもずいぶん変わっていますが、考えてみると、レンダリングは次のように考えられます.
レンダーれんだりんぐ:画面にペイントします.
誰がこのレンダリングの主導権を持つべきですか?私があなたに聞いたとき、
結局、ページが持っているものがもっと確実だと思ったので、ページに描くことにしました.
したがって、setStateでレンダリングを再描画する場合、最終的にpostFormによって独立して素子内部でレンダリングされると、renderは呼び出されなくなる.
import PostForm from '../components/PostForm.js';
import debounce from '../utils/debounce.js';
import { getItem, setItem } from '../utils/storage.js';
/*
 * this.state = {
 *   postId: string,
 * }
 */
export default function PostEditPage({
  $target,
  initialState = { postId: 'new' },
}) {
  const $page = document.createDocumentFragment();
  this.state = initialState;
  const { postId } = this.state;

  const defaultValue = { title: '', content: '' };
  const post = getItem(getLocalPostKey(postId), defaultValue);

  const postForm = new PostForm({
    $target: $page,
    initialState: {
      ...post,
    },
    onEdit: post => {
      debounce(setItem, 2000)(getLocalPostKey(this.state.postId), { ...post });
    },
  });

  // postId가 바뀔 때 페이지의 상태가 변화합니다!
  this.setState = nextState => {
    this.state = nextState;
    const post = getItem(getLocalPostKey(this.state.postId), defaultValue);
    postForm.setState(post);
    this.render();
  };

  this.render = () => {
    if ($target.querySelector('form') === null) {
      postForm.render(); // 에디터의 경우 여기서 렌더링을 해줘야, setState할 때 다시 렌더링되지 않습니다.
    }
    $target.appendChild($page);
  };
}

const getLocalPostKey = postId => {
  return `temp-save-${postId}`;
};

PostForm.js


逆に、PostForm構成部品については、既存のvalueがレンダリング中に変更されたが、構成部品レンダリングの観点からは不適切であるように見えるため、setStateで行うことにした.
逆に、素子レンダリングの依存性がより明確に理解でき、満足しています.😄
import Input from './common/Input.js';

export default function PostForm({
  $target,
  initialState = {
    title: '',
    content: '',
  },
  onEdit,
}) {
  // 초기 컴포넌트를 DOM에 추가하고, 상태를 초기화합니다.
  const $editor = document.createElement('form');
  /*
   * this.state = {
   *   title: string
   *   content: string
   * }
   */
  this.state = initialState;

  /*************************************
   *            component              *
   *************************************/
  const postTitle = new Input({
    $target: $editor,
    initialState: this.state.title,
    onChange: title => {
      const nextState = {
        ...this.state,
        title,
      };
      this.setState(nextState);
      postTitle.setState(title);
      onEdit(this.state);
    },
  });
  const $postContent = document.createElement('textarea');

  this.setState = nextState => {
    this.state = {
      ...this.state,
      ...nextState,
    };
    const { content } = this.state;
    $postContent.value = content;
    postTitle.setState(this.state.title);
  };

  this.render = () => {
    $editor.appendChild($postContent);
    $target.appendChild($editor);
  };

  $postContent.addEventListener('keyup', e => {
    this.setState({
      ...this.state,
      content: e.target.value,
    });
    onEdit({ ...this.state });
  });
}

router


ああ、結果的に素子がこのように設定されています.どうやってルーティングしたのですか?!historyAPIを使っています
一緒に勉強していたチョンヒョンの助けで、後ろを歩いても曖昧に表現された.
この時代最高の開発者であるジョンヒョンに大きな拍手を送った.👏👏👏
簡単に言えば、appに設定されたthis.routeをパラメータとして受け取り、イベントに基づいて処理します.
// route change라는 이벤트를 발생시킵니다.
const DISPATCH_ROUTE_CHANGE = 'route-change';

export default function router(onRoute) {
  window.addEventListener(DISPATCH_ROUTE_CHANGE, e => {
    const { nextUrl } = e.detail;

    if (nextUrl) {
      history.pushState(null, null, nextUrl);
      console.log('nextUrl', nextUrl);
      onRoute();
    }
  });

  window.addEventListener('popstate', () => {
    onRoute();
  });
}

export const push = nextUrl => {
  window.dispatchEvent(
    new CustomEvent(DISPATCH_ROUTE_CHANGE, {
      detail: {
        nextUrl,
      },
    }),
  );
};
結果を見てみましょう.

HTML開発者として、あなたのデザインはかなりきれいです!😅😅

の最後の部分👏


今はRouteまでやった!では、Defcosが提供するapiと組み合わせて、本格的なタスクを作成しましょう.
皆さん、コードが楽しいことを祈っています.😃😄😝