2

我们在向大家征集开发课程大纲后,便陆续收到了很多建议。大家的热情与支持,是我们持续更新的动力。

由于大家的课程建议比较散,需要结合实际案例进行讲解,因此我们先从最简单的开始。本文将介绍如何通过知晓云的内容库以及数据表,快速制作一个包含评论功能的个人博客。
适用人群:

  • 🙋刚接触编程的大学生(中小学生有多余精力也可,知晓云用户最小年龄 14 岁喔)
  • 🙋有兴趣学习前端开发的产品、测试和运营人员
  • 🙋业余爱好,有大量的学习时间,希望通过掌握基础开发知识,未来将开发应用作为副业

如果你有兴趣分享你的开发经验,请联系小晓云投稿(微信号:minsupport3),期待你的分享。

本文将使用到的技术:

  • 知晓云 Web SDK
  • React
  • Geist UI 库,用于快速构建样式
  • react-router-dom,用于页面跳转
  • Moment.js,用于解析日期

假设现在我们有一个完整样式的个人博客,里面包含两个页面,一个是首页,另一个是文章页。首页主要包含文章列表、标题和链接等内容,而文章页包含了文章内容、评论和登录功能。

我们先看一下截图,具体的代码可从这里下载:https://github.com/ifanrx/min...

首页:

文章页:

接下来,我们将直接进入具体页面和功能的开发实战(教程较长,可先收藏)。

一、两行代码接入知晓云

首先,在知晓云控制台创建「个人博客」应用,然后我们安装知晓云 Web SDK 依赖:

yarn add minapp-sdk

之后,在 src/index.js 中初始化 SDK。BaaS.init 方法里的 clientID 填入知晓云控制台「设置」中分配的 clientID 即可:

import BaaS from 'minapp-sdk'
BaaS.init('a4d2d62965ddb57fa4xx')

我们还需要在控制台中将本地项目的域名设置安全域名,具体请参考:https://doc.minapp.com/js-sdk...

二、免开发实现内容库管理文章

对文章的内容进行管理,我们可以利用知晓云控制台提供的内容库进行文章的编写,从而更高效地生产文章。

我们先在知晓云控制台左侧导航栏中点击「开发 - 内容」,进入内容库:

如果之前没有使用过内容库,那么可以先新建一个内容库,点击「马上开始」:

输入内容库名,将访问权限设置为所有人可见,并提交:

之后,我们创建一个分类,这里可自定义名称:

点击添加内容,新建一篇文章:

在新建内容页,我们输入文章标题、分类和内容等信息,并保存提交,这样我们第一篇文章就诞生啦:

我们还可以根据需要创建更多的文章和分类,至此,博客的内容已经完成了。

三、简单几步,实现首页动态获取文章分类和列表

在获取文章分类和列表之前,我们先初始化一下内容库:

const contentGroupId = 1630893238319930
const contentGroup = new BaaS.ContentGroup(contentGroupId)

其中,contentGroupId 可以在控制台,创建一条内容后的内容列表获取。也可以通过点击内容列表左上方标题旁边的「meta」, 查看 contentGroupId 等信息,按需选用。

获取文章分类非常简单,我们可以调用刚才定义的 contentGroup 中的 getCategoryList 方法获取:

/**
* 获取分类列表
*/
const getCategoryList = async () => {
 const res = await contentGroup.getCategoryList()
 return res.data.objects
}

而获取文章列表(内容库列表)也是十分类似,调用 find 方法即可。另外,我们还可以传入一个分类 id,精准获取该分类下的所有文章:

/**
* 获取内容库列表
*/
const getContentGroupList = async categoryId => {
 const query = new BaaS.Query()
 query.arrayContains('categories', [categoryId])
 
 const res = await contentGroup.setQuery(query).find()
 return res.data.objects
}

接下来,我们开始构建一个根据分类定义的文章列表。我们通过 useState 定义一个 articles 的变量,然后在页面初次渲染完毕的时候,先获取分类列表,然后通过遍历获取分类 ID。之后根据 ID 获取分类下的文章即可:

  const [articles, setArticles] = useState()
 
  useEffect(() => {
   const getArticleList = async () => {
     const categoryList = await getCategoryList()
 
     const articleList = {}
     for (const category of categoryList) {
       const { name, id } = category
       const articles = await getContentGroupList(id)
       articleList[name] = articles
     }
 
     setArticles(articleList)
   }
 
   getArticleList()
 }, [])

别忘了在 html 结构也要相应地做修改,且把标题的 href 链接对应修改为文章的 id:

<section style={{ width: 1000, margin: '0 auto' }}>
 <Grid.Container direction="column">
   {Object.keys(articles).map(category => {
     const articleList = articles[category]
     return (
       <Grid xs="24" direction="column" key={category}>
         <Spacer h={5} />
         <Text h4>{category}</Text>
         {articleList.map(article => {
           return (
             <Link
               href={`/article/${article.id}`}
               underline
               key={article.id}
             >
               <Text h2 font="42px">
                 {article.title}
               </Text>
             </Link>
           )
         })}
       </Grid>
     )
   })}
 </Grid.Container>
 <Spacer h={5} />
</section>

至此,刷新一下页面,我们的首页就完成了。

四、省时省力,快速获取文章内容、结构和样式

相比首页文章列表,文章页获取内容会更简单,只需要传入一个 ID 即可。在上一节我们了解到,如何获取内容库 ID,并实例化一个内容库来获取相应的分类(getCategoryList)和文章列表(find)。

而文章内容,我们可以通过 getContent(id) 获取:

/**
* 获取文章内容
* @param {*} id
*/
const getArticle = async id => {
 const res = await contentGroup.getContent(id)
 return res.data
}

由于在构建首页文章链接的时候,我们已经传入对应的文章 ID,因此,在进入文章页的时候,我们可以通过 useParams 这个方法来获取:

 const { id } = useParams()

有了 ID,我们接下来就可以获取相应的内容了:

const [article, setArticle] = useState()
 
useEffect(() => {
 const getArticleContent = async () => {
   const res = await getArticle(id)
   setArticle(res)
 }
 
 getArticleContent()
}, [id])

在内容库使用富文本编辑的文章,知晓云已经贴心地帮我们处理好 html 的结构、样式和分行,无需特地为其操心。我们只需要把动态内容填入文章页即可,实在是省时省力。

<Layout>
 <BlogHeader
   title={article.title}
   subTitle={article.description}
   date={moment.unix(article.created_at).format('YYYY年M月D日')}
 />
 
 <article style={{ width: 1000, margin: '0 auto' }}>
   <Spacer h={5} />
   <Grid.Container direction="column">
     <Grid xs="24" direction="column">
       {article?.content && (
         <div dangerouslySetInnerHTML={{ __html: article?.content }}></div>
       )}
       <Spacer h={2} />
     </Grid>
   </Grid.Container>
   <Spacer h={5} />
 </article>
 
 <Comment />
</Layout>

五、免接口开发,快速实现登录/注册功能

一个完善的个人博客,少不了评论功能。但每一条评论我们需要知道是哪个人创建的,因此我们要求每个人在评论前必须登录,这样做不但可以更好地跟对方互动,还可以有效减少垃圾评论的产生。

接下来我们来实现登录功能。在我们提供的文章页拉到最下方,点击登录,页面会弹窗让用户输入账号和密码,同时也有一个按钮切换到注册账户。

而实现登录注册的交互也十分简单。知晓云提供了一个非常方便易用的登录方法 BaaS.auth.login,我们只需要传入账户密码即可。

BaaS.auth.login({username: 'ifanrx', password: 'ifanrx123'}).then(user => {
 console.log(user)
})

注册也是通过简单的 BaaS.auth.register 方法调用即可。由于登录和注册我们是共用一个弹窗,在弹窗提交的方法里我们简单地将注册和登录方法通过判断来调用:

 /**
  * 登录/注册弹窗提交
  */
 const onAccountSubmit = async () => {
   if (!username) {
     setToast({ text: '请输入用户名', type: 'error' })
     return
   }
 
   if (!password) {
     setToast({ text: '请输入密码', type: 'error' })
     return
   }
 
   const request = isRegister ? BaaS.auth.register : BaaS.auth.login
 
   try {
     const user = await request({ username, password })
     console.log('登录用户 - ', user)
     setCurrentUser(user)
     closeModal()
   } catch (error) {
     console.log('登录/注册错误', error.toString())
     setToast({ text: error.toString(), type: 'error' })
   }
 }

六、实现高实时性、高互动性的评论功能

接下来我们本文最后一个功能:评论。评论功能主要用到数据表,涉及到简单的表设计、数据存储和权限控制等。

我们先设想一下数据表的结构。评论涉及到的字段主要包含以下几个:

  • 用户(用户名)
  • 评论
  • 文章 id(对应某篇文章)
  • 创建日期

根据以上的构思,我们可以大概列出如下信息:

得到以上信息,我们开始准备工作。现在控制台新建一张名为 comment 的数据表:

数据表的权限需要限制登录用户才可以录入,但所有人可读。

之后我们往 comment 表添加两个字段,分别是 commentarticle。由于知晓云已经贴心地默认帮我们加上了 created_by 和 created_at 字段,这里便可略过。

接着我们在代码里调用知晓云提供的方法新增一条评论。注意这个 id 是文章 id,需要在文章页传入:

  /**
  * 提交评论
  */
 const onCommentSubmit = async () => {
   if (!comment) {
     setToast({ text: '请输入评论', type: 'error' })
     return
   }
 
   const commentRecord = CommentTable.create()
 
   try {
     await commentRecord.set({ comment, article: id }).save()
     getCommentList(id) // 新增后,我们需要刷新一下评论列表
     setComment('')
   } catch (error) {
     console.log('创建评论失败', error.toString())
     setToast({ text: '创建评论失败', type: 'error' })
     return
   }
 }

注意在新增后,我们需要刷新一下评论列表。

由于 created_by 是系统默认字段,而且可以展开,我们可以通过 expand(‘created_by’) 来展开用户的账户信息。

同时我们也可以通过 query 查询,指定只显示该文章下的评论。

最后,我们可以通过 orderBy([‘-created_at’]) 按创建时间将最新的评论显示在最前面:

  /**
  * 获取评论列表
  */
 const getCommentList = async id => {
   const query = new BaaS.Query()
   query.compare('article', '=', id)
   const res = await CommentTable.expand('created_by')
     .orderBy(['-created_at'])
     .setQuery(query)
     .find()
   setCommentList(res.data.objects)
 }

最后别忘记修改一下页面结构,改为动态显示:

<Grid.Container direction="column">
 {commentList.map(comment => {
   return (
     <Grid xs={24} direction="column" key={comment.id}>
       <Card width="100%">
         <div
           style={{
             display: 'flex',
             alignItems: 'center',
             marginTop: -20,
           }}
         >
           <Text p b font="20px">
             {comment.created_by._username}
           </Text>
           <Spacer w={1} />
           <Text p style={{ color: 'gray' }}>
             {moment
               .unix(comment.created_at)
               .format('YYYY年M月D日 HH:mm:ss')}
           </Text>
         </div>
 
         <Text p style={{ marginTop: -5 }}>
           {comment.comment}
         </Text>
       </Card>
       <Spacer h={1} />
     </Grid>
   )
 })}
</Grid.Container>

至此,本文涉及的所有博客功能均已完成。

总结

本文使用知晓云实现了个人博客数据列表、登录和评论功能的构建。使用知晓云能大大提高开发效率,更多功能等你来发掘。

本项目完整代码可到我们的 GitHub 仓库获取:https://github.com/ifanrx/min...


知晓云
705 声望1.6k 粉丝