P18:重构前台博客详细页面的文章导航

重构后前台详细页的文章导航部分出现了错误提示,这个主要是我们选择的markdown-navbar模块的坑,这里为大家推荐一个这方面的插件叫tocify.tsx,比较好用,所以我们直接拿来使用。

tocify.tsx文件简介

tocify.tsx文件,用这个扩展名的含义是他使用了typeScript语法来编写jsx的部分,为了更好的和普通的是jsx文件区分,所以这里使用了tsx的扩展名。

使用这个文件的两个必要条件

  1. 你的程序员中使用了Ant DesignUI库,因为它里边的导航部分,使用了antdAnchor组件
  2. 安装lodash模块,这个可以直接使用yarn add lodash来安装或者使用npm install --save lodash

上面两个条件满足后,你可以把文件tocify.tsx拷贝到你的项目里了,我这里放到了/blog/components文件夹下了,把它视为一种自定义组件。

tocify.tsx文件

import React from 'react';
import { Anchor } from 'antd';
import { last } from 'lodash';

const { Link } = Anchor;

export interface TocItem {
  anchor: string;
  level: number;
  text: string;
  children?: TocItem[];
}

export type TocItems = TocItem[]; // TOC目录树结构

export default class Tocify {
  tocItems: TocItems = [];

  index: number = 0;

  constructor() {
    this.tocItems = [];
    this.index = 0;
  }

  add(text: string, level: number) {
    const anchor = `toc${level}${++this.index}`;
    const item = { anchor, level, text };
    const items = this.tocItems;

    if (items.length === 0) { // 第一个 item 直接 push
      items.push(item);
    } else {
      let lastItem = last(items) as TocItem; // 最后一个 item

      if (item.level > lastItem.level) { // item 是 lastItem 的 children
        for (let i = lastItem.level + 1; i <= 2; i++) {
          const { children } = lastItem;
          if (!children) { // 如果 children 不存在
            lastItem.children = [item];
            break;
          }

          lastItem = last(children) as TocItem; // 重置 lastItem 为 children 的最后一个 item

          if (item.level <= lastItem.level) { // item level 小于或等于 lastItem level 都视为与 children 同级
            children.push(item);
            break;
          }
        }
      } else { // 置于最顶级
        items.push(item);
      }
    }

    return anchor;
  }

  reset = () => {
    this.tocItems = [];
    this.index = 0;
  };

  renderToc(items: TocItem[]) { // 递归 render
    return items.map(item => (
      <Link key={item.anchor} href={`#${item.anchor}`} title={item.text}>
        {item.children && this.renderToc(item.children)}
      </Link>
    ));
  }

  render() {
    return (
      <Anchor affix showInkInFixed>
         {this.renderToc(this.tocItems)}
      </Anchor>
    );
  }
}

其实这个文件也很简单,如果JavaScript基础没问题是完全可以看明白的。

使用tocify.tsx生成文章目录

接下来就可以利用tocify.tsx文件生成目录了,在/blog/pages/detailed.js引入。

import Tocify from '../components/tocify.tsx'

引入后,需要对marked的渲染进行自定义,这时候需要设置renderer.heading,就是写一个方法们重新定义对#这种标签的解析。代码如下:

renderer.heading = function(text, level, raw) {
  const anchor = tocify.add(text, level);
  return `<a id="${anchor}" href="#${anchor}" class="anchor-fix"><h${level}>${text}</h${level}></a>\n`;
};

最后在需要显示文章导航的地方,写下面的代码:

<div className="toc-list">
  {tocify && tocify.render()}
</div>

为了方便大家学习,给出detailed.js全部代码。

import React,{useState} from 'react'
import Head from 'next/head'
import {Row, Col, Space, Breadcrumb, Affix} from 'antd'
import {ClockCircleOutlined,VideoCameraOutlined,FireOutlined} from '@ant-design/icons'

import Header from '../components/Header'
import Author from '../components/Author'
import Advert from '../components/Advert'
import Footer from '../components/Footer'

import MarkNav from 'markdown-navbar';
import 'markdown-navbar/dist/navbar.css';
import axios from 'axios'
import marked from 'marked'
import hljs from "highlight.js";
import 'highlight.js/styles/monokai-sublime.css';
import Tocify from '../components/tocify.tsx'

const Detailed = (props) => {
  const IconText = ({ icon, text }) => (
    <Space>
      {React.createElement(icon)}
      {text}
    </Space>
  );
  const tocify = new Tocify()
  renderer.heading = function(text, level, raw) {
    const anchor = tocify.add(text, level);
    return `<a id="${anchor}" href="#${anchor}" class="anchor-fix"><h${level}>${text}</h${level}></a>\n`;
  };
  const renderer = new marked.Renderer();
  marked.setOptions({
    renderer: renderer, 
    gfm: true,
    pedantic: false,
    sanitize: false,
    tables: true,
    breaks: false,
    smartLists: true,
    smartypants: false,
    highlight: function (code) {
      return hljs.highlightAuto(code).value;
    }
  }); 

  let html = marked(props.article_content);
  
  return (
    <>
      <Head>
        <title>博客详细页</title>
      </Head>
      <Header />
      <Row className="comm-main" type="flex" justify="center">
        <Col className="comm-left" xs={24} sm={24} md={16} lg={18} xl={14}  >
            <div>
              <div className="bread-div">
                <Breadcrumb>
                  <Breadcrumb.Item><a href="/">首页</a></Breadcrumb.Item>
                  <Breadcrumb.Item>教程列表</Breadcrumb.Item>
                  <Breadcrumb.Item>xxxx</Breadcrumb.Item>
                </Breadcrumb>
              </div>

             <div>
                <div className="detailed-title">
                React实战教程笔记-码云笔记开发
                </div>

                <div className="list-icon center">
                  <IconText icon={ClockCircleOutlined} text="2020-12-01" key="list-vertical-star-o" />
                  <IconText icon={VideoCameraOutlined} text="前端教程" key="list-vertical-like-o" />
                  <IconText icon={FireOutlined} text="5498人" key="list-vertical-message" />
                </div>

                <div className="detailed-content"
                 dangerouslySetInnerHTML={{__html:html}}
                 >
                </div>

             </div>

            </div>
        </Col>

        <Col className="comm-right" xs={0} sm={0} md={7} lg={5} xl={4}>
          <Author />
          <Advert />
          <Affix offsetTop={5}>
            <div className="detailed-nav comm-box">
              <div className="nav-title">文章目录</div>
              <div className="toc-list">
                {tocify && tocify.render()}
              </div>
            </div>
          </Affix>
        </Col>
      </Row>
      <Footer/>
   </>
  )
}

Detailed.getInitialProps = async(context) => {
  let id = context.query.id;

  const promise = new Promise((resolve)=>{
    axios('http://127.0.0.1:7001/default/getArticleById/' + id).then((res) => {
      console.log(res)
      resolve(res.data.data[0])
    })
  })

  return await promise
}

export default Detailed

这样就完成了前端详细文章页面的文章导航,可以预览一下效果。

1. 本站所有免费资源来源于用户上传和网络,因此不包含技术服务请大家谅解!如有侵权请邮件联系客服!
2. 本站不保证所提供下载的免费资源的准确性、安全性和完整性,免费资源仅供下载学习之用!如有链接无法下载、失效,请联系客服处理!
3. 您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容资源!如用于商业或者非法用途,与本站无关,一切后果请用户自负!
4. 如果您也有好的资源或技术教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
5. 加入前端开发QQ群:565733884,我们大家一起来交流技术!
码云笔记 » P18:重构前台博客详细页面的文章导航

发表评论

准备开启WordPress网站建设推广?

联系我们 定制开发