js提取指定网站内容(用React和Next.js做一个简单的博客网站(上))

优采云 发布时间: 2022-01-19 07:20

  js提取指定网站内容(用React和Next.js做一个简单的博客网站(上))

  原文:使用 React 和 Next.js 构建博客(站点点)

  字数:4272 字(非直译,有补充)

  阅读:10 分钟

  

  大家好,在一个简单的React和Next.js的博客网站(第1部分)文章,我们了解了Next.js是什么,并手动创建了一个简单的Next.js项目,学习了如何基于模板创建一个简单的页面,这篇文章文章,我们继续改进这个案例。

  一、根据MD文档生成动态路由

  创建博客自然需要文章内容。如果我们每次写一个文章都创建一个JSX单页,这太不现实了,太费时间了,也很难维护。我们的开发者更喜欢使用 Markdown 文档来编写文档。

  幸运的是,Next.js 允许我们使用 Markdown 作为 文章 的数据源,根据文件名生成动态路由,并使文件内容的 HTML 静态化。

  1、在编写这个函数时,最好停止Next.js服务(Ctrl | Cmd + C)。

  2、接下来,在项目根目录下创建一个articles文件夹,将你的Markdown文件放在这里,例如:articles/article-01.md,MD文件格式如下显示:

  ---

title: The first article

description: This is the first article.

date: 2020-10-01

---

This is an article post.

## Subheading

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

  我们将文档的标题名称、文档描述和创建日期放在 - 之间。基于这种格式,npm 插件 Front-matter 可以读取上述相关信息来提取文档的标题、描述和创建日期。要将MD文档格式化成网页,我们还需要安装两个npm插件remark和remark-html。安装命令如下:

  npm i front-matter remark remark-html

  3、安装完成后,我们需要实现读取和格式化MD文件的功能,然后创建lib/posts-md.js工具函数文件。 getFileIds(dir)函数返回一个MD文件名数组(文件名不带.md扩展名),示例代码如下:

  import { promises as fsp } from 'fs';

import path from 'path';

import fm from 'front-matter';

import remark from 'remark';

import remarkhtml from 'remark-html';

import * as dateformat from './dateformat';

const fileExt = 'md';

// return absolute path to folder

function absPath(dir) {

  return (

    path.isAbsolute(dir) ? dir : path.resolve(process.cwd(), dir)

  );

}

// return array of files by type in a directory and remove extensions

export async function getFileIds(dir = './') {

  const loc = absPath(dir);

  const files = await fsp.readdir(loc);

  return files

    .filter((fn) => path.extname(fn) === `.${fileExt}`)

    .map((fn) => path.basename(fn, path.extname(fn)));

}

  获取文件名数组后,我们需要解析MD的具体内容,如标题、描述、创建日期、具体内容的HTML格式等。示例代码如下:

  export async function getFileData(dir = './', id) {

  const

    file = path.join(absPath(dir), `${id}.${fileExt}`),

    stat = await fsp.stat(file),

    data = await fsp.readFile(file, 'utf8'),

    matter = fm(data),

    html = (await remark().use(remarkhtml).process(matter.body)).toString();

  // date formatting

  const date = matter.attributes.date || stat.ctime;

  matter.attributes.date = date.toUTCString();

  matter.attributes.dateYMD = dateformat.ymd(date);

  matter.attributes.dateFriendly = dateformat.friendly(date);

  // word count

  const

    roundTo     = 10,

    readPerMin  = 200,

    numFormat   = new Intl.NumberFormat('en'),

    count       = matter.body.replace(/\W/g, ' ').replace(/\s+/g, ' ').split(' ').length,

    words       = Math.ceil(count / roundTo) * roundTo,

    mins        = Math.ceil(count / readPerMin);

  matter.attributes.wordcount = `${ numFormat.format(words) } words, ${ numFormat.format(mins) }-minute read`;

  return {

    id,

    html,

    ...matter.attributes

  };

}

  你可能注意到我使用了日期格式函数,它定义在 lib/dateformat.js 文件中,示例代码如下:

  // date formatting functions

const toMonth = new Intl.DateTimeFormat('en', { month: 'long' });

// format a date to YYYY-MM-DD

export function ymd(date) {

  return date instanceof Date

    ? `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, '0')}-${String(date.getUTCDate()).padStart(2, '0')}` : '';

}

// format a date to DD MMMM, YYYY

export function friendly(date) {

  return date instanceof Date

    ? `${date.getUTCDate()} ${toMonth.format(date)}, ${date.getUTCFullYear()}` : '';

}

  4、Next.js 使用带有 [ ] 符号的特殊文件名生成动态路由。接下来,我们在 Pages 目录中创建这个特殊的文件 pages/articles/[id].js。 Next.js 使用 id 作为路由的参数,生成 /articles/article-01 的页面路由。

  pages/articles/[id].js 该文件实现了Next.js独有的GetStaticPaths()函数(静态生成),在项目构建时生成指定的路由路径。比如本例中,articles目录下的MD为 文档返回如下数组格式,id会匹配pages/articles/[id].js对应的[id]参数生成动态路由:

  [

  { params: { id: "article-01" } },

  { params: { id: "article-02" } },

  { params: { id: "article-03" } },

  ...

]

  该方法调用读取lib/posts-md.js文件中getFileIds文件路径列表的方法。示例代码如下:

  import { getFileIds, getFileData } from '../../lib/posts-md';

// post directory

const postsDir = 'articles';

// dynamic route IDs

export async function getStaticPaths() {

  const

    paths = (await getFileIds(postsDir))

      .map((id) => ({ params: { id } }));

  return {

    paths,

    fallback: false,

  };

}

  5、动态路由生成后,我们需要实现MD内容格式化和渲染。我们实现了 Next.js 特有的异步方法 getStaticProps({ params }),并在项目构建时调用这个函数(静态生成)。通过id参数调用lib/posts-md.js文件中getFileData()定义的方法,将MD文档的内容异步返回给收录postData属性的组件(第六点的代码部分)。示例代码如下:

  // dynamic route content

export async function getStaticProps({ params }) {

  return {

    props: {

      postData: await getFileData(postsDir, params.id),

    },

  };

}

  6、获取到数据后,我们需要将其填充到组件的模板中,并以更友好的形式显示出来。我们在pages/articles/[id].js中编写JSX相关代码,并将文章内容嵌套在上一节的组件模板中,示例代码如下:

  import Layout from '../../components/layout';

import Head from 'next/head';

...

export default function Article({ postData }) {

  // generate HTML from markdown content

  const html = `

    ${ postData.title }

    ${ postData.dateFriendly }

    ${ postData.wordcount }</p>

    ${ postData.html }

  `;

  return (

    

      

        { postData.title }

        

      

      

    

  );

}

</p>

  最后,我们需要重启 Next.js 服务。如果一切正常,你会发现在浏览器上可以通过/articles/文件名的路径查看所有MD文档,例如:3000/articles/article-01对应/articles/article-01. md 这个MD文件,效果如下图:

  

  二、创建博客列表页面

  对于博客相关的内容页面,我们需要按照文档创建时间的倒序构建博客列表页面

  1、首先我们在lib/posts-md.js文件中定义一个getAllFiles()方法来获取指定目录下的文件列表:

  // return sorted array of all posts for indexes

export async function getAllFiles(dir) {

  const

    now = dateformat.ymd(new Date()),

    files = await getFileIds(dir),

    data = await Promise.allSettled( files.map(id => getFileData(dir, id)) );

  return data

    .filter(md => md.value && md.value.dateYMD  md.value)

    .sort((a, b) => (a.dateYMD  (

          

        ))}

      

    

  );

}

  4、你可能注意到我在上面引用了一段代码

  组件,在components/pagelink.js文件中定义,该组件实现了显示文章的标题、链接、描述、日期等,示例代码如下:

  import Link from &#39;next/link&#39;;

export default function Pagelink(props) {

  const link = `/${ props.postsdir }/${ props.id }`;

  return (

    

      <a>{ props.title }</a>

      { props.datefriendly }

      

  { props.description }

    

  );

}

</p>

  博客列表页面的功能都在这里完成。在浏览器中输入:3000/articles,预览效果如下图:

  

  所有 MD 文件都将在此页面上列出。随着内容的增加,需要添加相关的逻辑进行分页。这里需要用到getStaticPaths()方法,并且这个页面需要改成pages/articles/[index].js(注意:index可以换成你想要的参数,但是需要和里面的参数对应getStaticPaths方法),在页面构建的时候生成对应的页面路由,可以参考第一部分根据MD文档生成动态路由,具体逻辑如何实现可以考虑,这里不再介绍;

  三、创建网站导航

  为了方便用户浏览我们的博客网站,我们需要新建一个components/navmenu.js导航组件来实现网站导航的功能。由于函数的简单性,这里就不做解释了。 ,示例代码如下:

  import Link from &#39;next/link&#39;;

import Link from &#39;next/link&#39;;

// menu name and link

const menu = [

  { text: &#39;home&#39;, link: &#39;/&#39; },

  { text: &#39;about&#39;, link: &#39;/about&#39; },

  { text: &#39;articles&#39;, link: &#39;/articles&#39; }

];

// render menu

export default function Navmenu() {

  // get current page route

  const

    router = useRouter(),

    currentPage = router.pathname;

  return (

    

      

        { menu.map(item => (

          

        ))}

      

    

  )

}

// render individual menu link

function Navlink({ text, link, currentpage }) {

  if (link === currentpage) {

    return (

      { text }

    );

  }

  else {

    return (

      <a>{ text }</a>

    );

  }

}

  导航组件完成后,我们将其引入到 components/header.js 组件中。示例代码如下:

  import Navmenu from &#39;./navmenu&#39;;

  更新后的JSX代码如下:

  ...

  

...

  完成后博客导航效果如下图:

  

  四、使用 Sass 为您的博客添加全局样式

  到此,一个基于MD文档的简单博客网站到此就完成了,最后还要给网站添加样式,不然网站真的太丑了。

  Next.js 可以使用 Sass、Less、PostCSS、Styled JSX、CSS 模块、plain old CSS 等方式给网站添加样式,这里我们使用 Sass 给网站添加样式,这里我们手动安装 Sass 为项目:

  npm i sass

  接下来,我们可以为每个组件定义相关的样式,然后将它们组合到一个styles/global.scss文件中。由于本文文章重点介绍Next.JS的使用,这里就不做介绍了。 Sass怎么写,有兴趣的同学可以点击阅读文末原文下载本文的Sass风格:

  // settings

@import &#39;01-settings/_variables&#39;;

@import &#39;01-settings/_mixins&#39;;

// reset

@import &#39;02-generic/_reset&#39;;

// elements

@import &#39;03-elements/_primary&#39;;

// layout

@import &#39;04-layout/_site&#39;;

// components

@import &#39;05-components/_header&#39;;

@import &#39;05-components/_footer&#39;;

@import &#39;05-components/_article&#39;;

  最后我们需要将styles/global.scss导入到特殊文件pages/_app.js中,这样网站所有页面都可以使用这个样式,示例代码如下:

  import &#39;../styles/global.scss&#39;;

export default function App({ Component, pageProps }) {

  return 

};

  最后我们重启 Next.js 服务,你会看到一个漂亮的博客主页,如下图:

  

  待续

  由于篇幅原因,今天的文章就到这里,一个基于MD文档的简单博客网站就完成了,通过这个文章我们学习了如何基于MD文档生成动态路由,完成 文章 内容页面、列表页面、导航,并为 网站 添加漂亮的样式。在下一篇文章中,我们为blog网站添加dark mode,根据界面数据渲染内容(服务端渲染),以及如何编译项目并将blog网站部署到Node.js 服务器 按需或纯静态部署,最后会提供完整的项目源码,敬请期待...

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线