【山大智云项目日志】(五)源码分析之seahub-frontend-App.js

本文深入分析了一个React应用的App.js文件,解释了React.Fragment的使用,它是如何避免在DOM中添加额外节点的。同时,讨论了组件的生命周期方法,如componentWillMount和componentDidMount,以及如何根据URL导航和处理组件状态。此外,还介绍了导入的各个组件及其功能,展示了如何组织和渲染不同的页面内容。
摘要由CSDN通过智能技术生成

2021SC@SDUSC
继上次分析了主要根组件MarkDownEditor之后,我发现seafile首页还是与App.js里的内容极为相似只是App.js最终是以id="wrapper"被渲染到页面,让人误以为App.js并没有用。这里展开对App.js文件下源码的分析。
在这里第一次遇到了以下的代码形式:

return (
      <React.Fragment>
      ………………
        </React.Fragment>
    );

这里先了解以下React Fragment的用法。

React.Fragment 组件能够在不额外创建 DOM 元素的情况下,让 render()
方法中返回多个元素。一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。理解起来就是在我们定义组件的时候return里最外层包裹的div往往不想渲染到页面,那么就要用到我们的Fragment组件了。

因为我之前接触过Vue框架,这意思应该就和vue的<template ></ template >一样不会被渲染到页面。会被框架自动剔除。
详细用法可以参考博客:

https://blog.csdn.net/weixin_43720095/article/details/104943812

接下来看代码,先从以下引入的依赖看起,主要是src文件夹下componentspages子文件夹下的组件,因此猜测App.js是集合了各个组件功能的主要面板。首先先从组件的命名来进行初步猜测各个组件对应的功能。

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Router, navigate } from '@reach/router';
import MediaQuery from 'react-responsive';
import { Modal } from 'reactstrap';
import { siteRoot, canAddRepo, isDocs } from './utils/constants';
import { Utils } from './utils/utils';
import SystemNotification from './components/system-notification';
import SidePanel from './components/side-panel';  //引入侧边栏组件
import MainPanel from './components/main-panel';  //引入主面板组件
import DraftsView from './pages/drafts/drafts-view'; //引入草稿查看组件
import DraftContent from './pages/drafts/draft-content';
import FilesActivities from './pages/dashboard/files-activities';
import Starred from './pages/starred/starred';
import LinkedDevices from './pages/linked-devices/linked-devices';
import editUtilities from './utils/editor-utilities';   //引入文本编辑工具
import ShareAdminLibraries from './pages/share-admin/libraries'; //引入共享库页面
import ShareAdminFolders from './pages/share-admin/folders';   //引入共享文件夹
import ShareAdminShareLinks from './pages/share-admin/share-links';  //引入共享链接
import ShareAdminUploadLinks from './pages/share-admin/upload-links'; //引入共享上传链接
import SharedLibraries from './pages/shared-libs/shared-libs';     //引入公共库
import ShareWithOCM from './pages/share-with-ocm/shared-with-ocm';
import OCMRepoDir from './pages/share-with-ocm/remote-dir-view';
import MyLibraries from './pages/my-libs/my-libs';   //引入私人库
import MyLibDeleted from './pages/my-libs/my-libs-deleted'; //引入已删除的库
import PublicSharedView from './pages/shared-with-all/public-shared-view';  //猜测应该是公共视图
import LibContentView from './pages/lib-content-view/lib-content-view';
import Group from './pages/groups/group-view';   //引入组
import Groups from './pages/groups/groups-view';
import InvitationsView from './pages/invitations/invitations-view';
import Wikis from './pages/wikis/wikis';
import MainContentWrapper from './components/main-content-wrapper';

再来看App.js中与React组件生命周期有关的函数:

 componentWillMount() { //组件将要挂载时先设置侧边栏关闭。避免组件覆盖。
    if (!Utils.isDesktop()) {
      this.setState({
        isSidePanelClosed: true
      });
    }
  }
 componentDidMount() { //组件挂载后,将客户端URL导航到库 主要是初始化页面内容
    // url from client  e.g. http://127.0.0.1:8000/#common/lib/34e7fb92-e91d-499d-bcde-c30ea8af9828/
    // navigate to library page http://127.0.0.1:8000/library/34e7fb92-e91d-499d-bcde-c30ea8af9828/
    this.navigateClientUrlToLib();
    // e.g.  from http://127.0.0.1:8000/drafts/reviews/
    // get reviews
    // TODO: need refactor later
    let href = window.location.href.split('/');
    if (isDocs) {
      this.getDrafts();
    }
    this.setState({currentTab: href[href.length - 2]});
  }

navigateClientUrlToLib = () =>{  //导航的实现
    if(window.location.hash && window.location.hash.indexOf('common/lib') != -1){ //进行位置判定
      let splitUrlArray = window.location.hash.split('/');   //将位置拆分成数组,分解符是‘/’
      let repoID = splitUrlArray[splitUrlArray.length - 2];//取出数组最后一个元素作为repoID
      let url = siteRoot + 'library/' + repoID + '/';  //拼接url
      navigate(url, {repalce: true});    
    }
  }

以下是通过修改类组件的状态进行面板操作。

  getDrafts = () => {  //直接通过设对象的方式进行状态的设置
    editUtilities.listDrafts().then(res => {
      this.setState({
        draftCounts: res.data.draft_counts,
        draftList: res.data.data,
        isLoadingDraft: false,
      });
    });
  }

  onCloseSidePanel = () => {  //关闭侧边面板
    this.setState({
      isSidePanelClosed: !this.state.isSidePanelClosed
    });
  }

  onShowSidePanel = () => { //开启侧边栏
    this.setState({
      isSidePanelClosed: !this.state.isSidePanelClosed
    });
  }

这一部分的函数通过获取url,拆分,拼接的过程值得学习,分析仍以注释的形式展现。

 onSearchedClick = (selectedItem) => {
    if (selectedItem.is_dir === true) {
      this.setState({currentTab: '', pathPrefix: []});
      let url = siteRoot + 'library/' + selectedItem.repo_id + '/' + selectedItem.repo_name + selectedItem.path; 
      //拼接站点根地址,选中项目的repo_id,repo_name以及选中项的path
      navigate(url, {repalce: true}); //拼接后实现地址跳转
    } else { 
      let url = siteRoot + 'lib/' + selectedItem.repo_id + '/file' + Utils.encodePath(selectedItem.path);
      let isWeChat = Utils.isWeChat();
      if (!isWeChat) {  //失败跳转到空页面
        let newWindow = window.open('about:blank');
        newWindow.location.href = url;
      } else {
        location.href = url;
      }
    }
  }

通过文章开头的方式进行组件的渲染: <React.Fragment>

render() {
    let { currentTab, isSidePanelClosed } = this.state;

    const home = canAddRepo ?
      <MyLibraries path={ siteRoot } onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} /> :
      <SharedLibrariesWrapper path={ siteRoot } onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />;

    return (
      <React.Fragment>
        <SystemNotification />
        <div id="main">
          <SidePanel isSidePanelClosed={this.state.isSidePanelClosed} onCloseSidePanel={this.onCloseSidePanel} currentTab={currentTab} tabItemClick={this.tabItemClick} draftCounts={this.state.draftCounts} />
          <MainPanel>
            <Router className="reach-router">
              {home}
              <FilesActivitiesWrapper path={siteRoot + 'dashboard'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
              <DraftsViewWrapper path={siteRoot + 'drafts'}
                onShowSidePanel={this.onShowSidePanel}
                onSearchedClick={this.onSearchedClick}
              >
                <DraftContent
                  path='/'
                  getDrafts={this.getDrafts}
                  isLoadingDraft={this.state.isLoadingDraft}
                  draftList={this.state.draftList}
                  updateDraftsList={this.updateDraftsList}
                />
              </DraftsViewWrapper>
              <StarredWrapper path={siteRoot + 'starred'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
              <LinkedDevicesWrapper path={siteRoot + 'linked-devices'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
              <ShareAdminLibrariesWrapper path={siteRoot + 'share-admin-libs'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
              <ShareAdminFoldersWrapper path={siteRoot + 'share-admin-folders'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
              <ShareAdminShareLinksWrapper path={siteRoot + 'share-admin-share-links'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
              <ShareAdminUploadLinksWrapper path={siteRoot + 'share-admin-upload-links'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
              <SharedLibrariesWrapper path={siteRoot + 'shared-libs'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
              <SharedWithOCMWrapper path={siteRoot + 'shared-with-ocm'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
              <OCMViaWebdavWrapper path={siteRoot + 'ocm-via-webdav'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
              <MyLibraries path={siteRoot + 'my-libs'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
              <MyLibDeleted path={siteRoot + 'my-libs/deleted/'} onSearchedClick={this.onSearchedClick} />
              <LibContentView path={siteRoot + 'library/:repoID/*'} pathPrefix={this.state.pathPrefix} onMenuClick={this.onShowSidePanel} onTabNavClick={this.tabItemClick}/>
              <OCMRepoDir path={siteRoot + 'remote-library/:providerID/:repoID/*'} pathPrefix={this.state.pathPrefix} onMenuClick={this.onShowSidePanel} onTabNavClick={this.tabItemClick}/>
              <Groups path={siteRoot + 'groups'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick}/>
              <Group
                path={siteRoot + 'group/:groupID'}
                onShowSidePanel={this.onShowSidePanel}
                onSearchedClick={this.onSearchedClick}
                onTabNavClick={this.tabItemClick}
                onGroupChanged={this.onGroupChanged}
              />
              <Wikis path={siteRoot + 'published'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick}/>
              <PublicSharedView path={siteRoot + 'org/'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} onTabNavClick={this.tabItemClick}/>
              <InvitationsView path={siteRoot + 'invitations/'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
            </Router>
          </MainPanel>
          <MediaQuery query="(max-width: 767.8px)">
            <Modal zIndex="1030" isOpen={!isSidePanelClosed} toggle={this.toggleSidePanel} contentClassName="d-none"></Modal>
          </MediaQuery>
        </div>
      </React.Fragment>
    );
  }
}

最后将App组件render到id为’wrapper’的元素上。

ReactDOM.render(
  <App />,
  document.getElementById('wrapper')
);

其余一些重要组件的实现也会同步更新到此篇博客。未完……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值