实战教程|使用知晓云快速制作一个简单的个人博客 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ifanrcloud
V2EX    前端开发

实战教程|使用知晓云快速制作一个简单的个人博客

  •  
  •   ifanrcloud
    ifanrx 2021-09-10 10:23:59 +08:00 1359 次点击
    这是一个创建于 1501 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本文将介绍如何通过知晓云的内容库以及数据表,快速制作一个包含评论功能的个人博客。

    适用人群:

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

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

    本文将使用到的技术:

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

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

    我们先看一下截图,具体的代码可从这里下载: https://github.com/ifanrx/minapp-blog-example/tree/blog-structure-and-styles

    首页:

    文章页:

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

    一、两行代码接入知晓云

    首先,在知晓云控制台创建「个人博客」应用,然后我们安装知晓云 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/web/how-to.html

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

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

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

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

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

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

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

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

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

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

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

    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/minapp-blog-example

    3 条回复    2021-09-10 15:26:13 +08:00
    shpkng
        1
    shpkng  
       2021-09-10 10:40:10 +08:00
    功能看着还不错, 但是还是建议发推广区
    dilu
        2
    dilu  
       2021-09-10 10:55:41 +08:00
    长文直接拉倒最后,果然有个二维码
    ifanrcloud
        3
    ifanrcloud  
    OP
       2021-09-10 15:26:13 +08:00
    @shpkng 好的,感谢建议
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1351 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 23:55 PVG 07:55 LAX 16:55 JFK 19:55
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86