# react_blog **Repository Path**: mus-z/react_blog ## Basic Information - **Project Name**: react_blog - **Description**: No description available - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-05-24 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 个人技术博客实战--react_blog #### [跟随技术胖老师的步伐](https://www.bilibili.com/video/BV1CJ411377B?p=2)--React、React hooks、Next.js、Antd、Koa2->egg.js、mysql、axios、marked+highlight.js(取代React-markdown)、react-router-dom、nginx ![image-20200525134333032](https://gitee.com/mus-z/blogImage/raw/master/img/20200525134339.png) [TOC] ### day01 初始化+前端布局结束 #### 1.1环境搭建 项目初始化,使用`npx create-next-app blog` 引入`@zeit/next-css`使用并且配置根目录下`next.config.js` 想使用Icon单独引入`yarn add @ant-design/icons --save` ```js const withCss = require('@zeit/next-css') if(typeof require !== 'undefined'){ require.extensions['.css']=file=>{} } module.exports = withCss({}) ``` 引入`babel-plugin-import`,根目录下`.babelrc` ```json { "presets":["next/babel"], //Next.js的总配置文件,相当于继承了它本身的所有配置 "plugins":[ //增加新的插件,这个插件就是让antd可以按需引入,包括CSS [ "import", { "libraryName":"antd" , "style":"css" ////取消按需加载才能build } ] ] } ``` ![image-20200524095248810](https://gitee.com/mus-z/blogImage/raw/master/img/20200613095440.png) #### 1.2博客头部header 栅格化布局 UI组件中已经都封装好了,原理也很简单是24网格式,可以配置响应式 ![image-20200524104218166](https://gitee.com/mus-z/blogImage/raw/master/img/20200613095233.png) ![image-20200524112348257](https://gitee.com/mus-z/blogImage/raw/master/img/20200613095250.png) 预览一下组件 ![image-20200524112419124](https://gitee.com/mus-z/blogImage/raw/master/img/20200613095302.png) #### 1.3两栏布局 同样运用栅格化布局 ```js import Head from 'next/head' import Header from '../component/Header' import { Row, Col, Menu, } from "antd"; export default function Home() { return (
博客首页
zuo
you
) } ``` ![](https://gitee.com/mus-z/blogImage/raw/master/img/20200613095320.png) ![image-20200524120129704](https://gitee.com/mus-z/blogImage/raw/master/img/20200613095323.png) ![image-20200524120155357](https://gitee.com/mus-z/blogImage/raw/master/img/20200613095451.png) #### 1.4 List博客列表 通过antd里的`List`组件和各种内置的icon可以很简单配置 ```js
最新日志
}//表头 dataSource={mylist}//数据源数组 // loading={true} itemLayout={"vertical"}//文字竖直方向 renderItem={(item) => (//渲染每一项
{item.title}
{"2020-5-24"} {"测试文章"} {"12"}
{item.context}
)} /> ``` ![image-20200524155420544](https://gitee.com/mus-z/blogImage/raw/master/img/20200524155425.png) #### 1.5 右侧作者栏 单独写一个Author组件 ```js const Author = () => { return (
头像
{"2020开始看前端知识的,想找工作的17级萌新。现在已经学习html、css、javascript、es6语法、react、redux等的比较入门的内容,现在在学习实战项目知识orzzz"} 平台
); }; ``` ![](https://gitee.com/mus-z/blogImage/raw/master/img/20200524170541.png) ![image-20200524170654220](https://gitee.com/mus-z/blogImage/raw/master/img/20200524170655.png) #### 1.6 右侧作者栏下面的功能栏(广告为例) ```js const Advert = () => { return (

我觉得这个栏目在没有广告就隐藏吧,或者用于其他功能

); }; export default Advert ``` ![image-20200524171926580](https://gitee.com/mus-z/blogImage/raw/master/img/20200524171927.png) ![image-20200524171938246](https://gitee.com/mus-z/blogImage/raw/master/img/20200524171939.png) #### 1.7 Footer ```js const Footer=()=>{ return (
React Node Ant-Design
©
) } export default Footer ``` ![image-20200524190314373](https://gitee.com/mus-z/blogImage/raw/master/img/20200524190319.png) #### 1.8 AList 目前基本上和index差不多 然后跟着实现了一个面包屑导航 ```js 首页 列表 2 3 ``` ![image-20200524190347882](https://gitee.com/mus-z/blogImage/raw/master/img/20200524190354.png) #### 1.9 详情页-基本页面构造 ```js import Head from 'next/head' import Header from '../component/Header' import { Row, Col,Breadcrumb } from "antd"; import {CalendarOutlined,FolderOpenFilled,EyeFilled} from "@ant-design/icons"; import "../public/css/pages/common.css" import "../public/css/pages/detail.css" import Author from "../component/Author"; import Advert from "../component/Advert"; import Footer from "../component/Footer"; function Detail() { return (
detail
首页 列表 xxx
xxxxxx项目
{"2020-05-24"} {"类型"} {"12"}人
markdown解析内容
) } export default Detail ``` ![image-20200524201220617](https://gitee.com/mus-z/blogImage/raw/master/img/20200524201227.png) ![](https://gitee.com/mus-z/blogImage/raw/master/img/20200524201256.png) #### 1.10 详情页-react-markdown解析 [https://github.com/rexxars/react-markdown](https://github.com/rexxars/react-markdown) ``` yarn add react-markdown ``` ```javascript import ReactMarkdown from "react-markdown" let markdown='#\n # P01:课程介绍和环境搭建\n' + '[ **M** ] arkdown + E [ **ditor** ] = **Mditor** \n' + '> Mditor 是一个简洁、易于集成、方便扩展、期望舒服的编写 markdown 的编辑器,仅此而已... \n\n' + '**这是加粗的文字**\n\n' + '*这是倾斜的文字*`\n\n' + '***这是斜体加粗的文字***\n\n' + '~~这是加删除线的文字~~ \n\n'+ '\`console.log(111)\` \n\n'+ '# p02:来个Hello World 初始Vue3.0\n' + '> aaaaaaaaa\n' + '>> bbbbbbbbb\n' + '>>> cccccccccc\n'+ '***\n\n\n' + '# p03:Vue3.0基础知识讲解\n' + '> aaaaaaaaa\n' + '>> bbbbbbbbb\n' + '>>> cccccccccc\n\n'+ '# p04:Vue3.0基础知识讲解\n' + '> aaaaaaaaa\n' + '>> bbbbbbbbb\n' + '>>> cccccccccc\n\n'+ '# p05:Vue3.0基础知识讲解\n' + '> aaaaaaaaa\n' + '>> bbbbbbbbb\n' + '>>> cccccccccc\n\n'+ '# p06:Vue3.0基础知识讲解\n' + '> aaaaaaaaa\n' + '>> bbbbbbbbb\n' + '>>> cccccccccc\n\n'+ '# p07:Vue3.0基础知识讲解\n' + '> aaaaaaaaa\n' + '>> bbbbbbbbb\n' + '>>> cccccccccc\n\n'+ '```var a=11; ```' ``` ```javascript
markdown解析内容
``` ![image-20200524215042850](https://gitee.com/mus-z/blogImage/raw/master/img/20200524215045.png) ![image-20200524215206501](https://gitee.com/mus-z/blogImage/raw/master/img/20200524215207.png) #### 1.11 详情页-markdown-navbar导航 ```js yarn add markdown-navbar ``` markdown-navbar有个小bug 会自己不显示第一个目录`ordered={false}`的时候 所以前面要加一个空换行 ```js ``` 加绑定位置用antd的`Affix` 给一个fix状态时的top10 ```js
文章目录
``` ![image-20200524230336267](https://gitee.com/mus-z/blogImage/raw/master/img/20200524230341.png) ### day02 中台搭建接口 #### 2.1安装egg.js [https://github.com/eggjs/egg](https://github.com/eggjs/egg) ``` cnpm install -g egg-init ``` ``` cd service ``` ``` egg-init --type=simple ``` ``` npm install ``` 缓慢的下载过程之后 ![image-20200525135807752](https://gitee.com/mus-z/blogImage/raw/master/img/20200525135809.png) ``` yarn dev ``` 或 ``` npm run dev ``` 运行一下 ![image-20200525135935115](https://gitee.com/mus-z/blogImage/raw/master/img/20200525135937.png) ![image-20200525135957160](https://gitee.com/mus-z/blogImage/raw/master/img/20200525135959.png) #### 2.2 egg.js 目录结构+规范 ![image-20200525140830658](https://gitee.com/mus-z/blogImage/raw/master/img/20200525140834.png) 主要app编写、config配置 ![image-20200525141637126](https://gitee.com/mus-z/blogImage/raw/master/img/20200525141640.png) ![image-20200525141701513](https://gitee.com/mus-z/blogImage/raw/master/img/20200525141718.png) 尝试配置路由地址 ![image-20200525142611005](https://gitee.com/mus-z/blogImage/raw/master/img/20200525142613.png) ![image-20200525142627312](https://gitee.com/mus-z/blogImage/raw/master/img/20200525142628.png) #### 2.3 RESTful风格+路由配置 ![image-20200525143446095](https://gitee.com/mus-z/blogImage/raw/master/img/20200525143447.png) > 前台会访问controller里面的default,然后配置这个home.js 中的 index方法 > > ![image-20200525144215157](https://gitee.com/mus-z/blogImage/raw/master/img/20200525144216.png) > 新建router文件夹分别对应前台的default和后台的admin > > ![image-20200525144201536](https://gitee.com/mus-z/blogImage/raw/master/img/20200525144202.png) > 在主router里面用require方法引入 ./router/default > > ![image-20200525144840518](https://gitee.com/mus-z/blogImage/raw/master/img/20200525144841.png) ![image-20200525144948714](https://gitee.com/mus-z/blogImage/raw/master/img/20200525144949.png) #### 2.4 egg-mysql 安装mysql插件 ``` yarn add egg-mysql ``` 配置插件就需要配置config 找到config/plugin.js ```js exports.mysql={ enable:true, packgae:'egg-mysql' } ``` 我跟视频着装了个phpstudy,虽然电脑里面本来就有mysqlworkbench ![image-20200525151557000](https://gitee.com/mus-z/blogImage/raw/master/img/20200525151559.png) 然后转入config.default.js 查看[https://www.npmjs.com/package/egg-mysql](https://www.npmjs.com/package/egg-mysql)配置 添加到config中,然后修改数据库配置 ```js config.mysql = { // database configuration client: { // host host: 'localhost', // port port: '3306', // username user: 'root', // password password: 'mysql57', // database database: 'react_blog', }, // load into app, default is open app: true, // load into agent, default is close agent: false, }; ``` 在home.js中修改 ![image-20200525154428517](https://gitee.com/mus-z/blogImage/raw/master/img/20200525154429.png) ```js let result=await this.app.mysql.get("blog_content",{}) console.log(result) ctx.body = result; ``` 可以查到我们连接 的数据库中的blog_content表中的默认数据 #### 2.5 配置数据库并查询 ![image-20200525162102144](https://gitee.com/mus-z/blogImage/raw/master/img/20200525162103.png) default/home.js ```js async getArticleList() { let sql=`SELECT article.id as id ,`+ `article.article_title as title ,`+ `article.article_content as content ,`+ `article.article_introduce as introduce ,`+ `article.article_addtime as addtime ,`+ `article.article_viewcount as viewcount ,`+ `type.typeName as typename ,`+ `type.orderNum as ordernum `+ `FROM article LEFT JOIN type ON article.type_id=type.Id` //console.log(sql) const { ctx } = this; const results= await this.app.mysql.query(sql);//查询语句 ctx.body = {data:results}; ``` router/default.js ```js router.get('/default/getArticleList',controller.default.home.getArticleList) ``` 尝试获取数据 ![image-20200525162324439](https://gitee.com/mus-z/blogImage/raw/master/img/20200525162326.png) ### day03 前中结合 #### 3.1 首页用axios获取中台数据 ```js Home.getInitialProps=async()=>{ let promise=new Promise((resolve,reject)=>{ let url='http://127.0.0.1:7001/default/getArticleList'//egg.js中配置好的RESTful接口 try{axios(url).then( (res)=>{ console.log(res.data) resolve(res.data) } ) }catch{ reject('err') } }) return await promise; //需要返回promise类型对象 } ``` ![image-20200526090258452](https://gitee.com/mus-z/blogImage/raw/master/img/20200526090306.png) ![image-20200526090350693](https://gitee.com/mus-z/blogImage/raw/master/img/20200526090352.png) #### 3.2 详细页拿数据+解决跨域(egg-cors 设置文本标题链接 ```js
{item.title}
``` 尝试获取 ![image-20200526093305336](https://gitee.com/mus-z/blogImage/raw/master/img/20200526093309.png) ![image-20200526093322378](https://gitee.com/mus-z/blogImage/raw/master/img/20200526093323.png) ![image-20200526093352689](https://gitee.com/mus-z/blogImage/raw/master/img/20200526093353.png) 但是不让跨域了2333 ![image-20200526093430282](https://gitee.com/mus-z/blogImage/raw/master/img/20200526093431.png) 所以要使用`egg-cors`,在service中安装 ``` yarn add egg-cors ``` 修改config.js ![image-20200526093803791](https://gitee.com/mus-z/blogImage/raw/master/img/20200526093805.png) 然后的config.default.js ![image-20200526095414028](https://gitee.com/mus-z/blogImage/raw/master/img/20200526095416.png) 对于主页的get请求,detail接收到上下文参数id ![image-20200526095456354](https://gitee.com/mus-z/blogImage/raw/master/img/20200526095457.png) 修改一下之前的动态路由让接口方法能拿到id ```js router.get('/default/getArticleById/:id',controller.default.home.getArticleById) ``` ![image-20200526095809697](https://gitee.com/mus-z/blogImage/raw/master/img/20200526095810.png) 请求到数据输出到控制台了,但是还没改到文章中 #### 3.3 重构markdown,高亮(marked+highlight) ``` yarn add highlight.js yarn add marked ``` ![image-20200526102608107](https://gitee.com/mus-z/blogImage/raw/master/img/20200526102613.png) ```js 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) ``` - renderer: 这个是必须填写的,你可以通过自定义的`Renderer`渲染出自定义的格式 - gfm:启动类似Github样式的Markdown,填写true或者false - pedatic:只解析符合Markdown定义的,不修正Markdown的错误。填写true或者false - sanitize: 原始输出,忽略HTML标签,这个作为一个开发人员,一定要写flase - tables: 支持Github形式的表格,必须打开gfm选项 - breaks: 支持Github换行符,必须打开gfm选项,填写true或者false - smartLists:优化列表输出,这个填写ture之后,你的样式会好看很多,所以建议设置成ture - highlight: 高亮显示规则 ,这里我们将使用highlight.js来完成 ** 增加Code的高亮显示 ** 在设置`setOptions`属性时,可以直接设置高亮显示,代码如下: ```js highlight: function (code) { return hljs.highlightAuto(code).value; } ``` 设置完成后,你在浏览器检查代码时就可以出现hljs的样式,说明你的效果加成功了,实现了高亮显示代码。 这个配置完成后,就可以连接到数据库了。 改一下数据库中的content ``` # 创建数据库 现在我们还没有数据库,所以需要先建立数据库,直接使用PhP Study里的SQL_Front5.3来管理数据,如果你没有安装需要安装一下,安装完成后点后面的管理按钮,就可以管理了。(这个过程看视频吧) 输入你数据库的用户名和密码,然后点击进入。 进入后新建一个数据库react_blog,这个名字你可以自己起\n新建一个表`blog_content,`字段就是`title`、`type`、`introduce`和`content`\n随便写条数据进去,这个自由发挥吧 这样数据库的准备就写好了,接下来需要验证一下,数据库是否已经连接上了。 ​``` highlight: function (code) { return hljs.highlightAuto(code).value; } ​``` ``` 这里技术胖老师更新了下本页的css样式,我也打算现在先复制粘贴,等之后有时间自己弄一下喜欢的风格 detail.js ```js import Head from "next/head"; import Header from "../component/Header"; import { Row, Col, Breadcrumb, Affix } from "antd"; import { CalendarOutlined, FolderOpenFilled, EyeFilled, } from "@ant-design/icons"; import "../public/css/pages/common.css"; import "../public/css/pages/detail.css"; import Author from "../component/Author"; import Advert from "../component/Advert"; import Footer from "../component/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"; function Detail(props) { console.log(props) const renderer= new marked.Renderer() marked.setOptions({ renderer:renderer, gfm:true, pedantic:false,//false容错 sanitize:false,//false不忽略html标签 tables:true,//gfm breaks:false,//gfm smartypants: false, smartLists:true, highlight:function(code){return hljs.highlightAuto(code).value} }) let html=marked (props.content) //console.log(html,props.content) return (
detail
首页 列表 xxx
{props.title}
{new Date(Number(props.addtime)).toLocaleString()} {props.typename} {props.viewcount}人
文章目录
); } Detail.getInitialProps=async(context)=>{ //console.log(context) let id=context.query.id; let promise=new Promise((resolve,reject)=>{ let url=`http://127.0.0.1:7001/default/getArticleById/${id}`//egg.js中配置好的RESTful接口 try{axios(url).then( (res)=>{ console.log(res.data.data[0]) resolve(res.data.data[0]) } ) }catch{ reject('err') } }) return await promise; //需要返回promise类型对象 } export default Detail; ``` ![image-20200526110914961](https://gitee.com/mus-z/blogImage/raw/master/img/20200526110918.png) 那个error是navbar有点问题 #### 3.4 重构导航栏 > 插件tocify.tsx > > ```js > 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 => ( > > {item.children && this.renderToc(item.children)} > > )); > } > > render() { > return ( > > {this.renderToc(this.tocItems)} > > ); > } > } > ``` 还未开源 ![image-20200526111404779](https://gitee.com/mus-z/blogImage/raw/master/img/20200526111409.png) ``` yarn add lodash ``` ![image-20200526113125067](https://gitee.com/mus-z/blogImage/raw/master/img/20200526113127.png) 引入之后 ```js renderer.heading = function (text, level, raw) { const anchor = tocify.add(text, level); //自定义bar的渲染方式 return `${text}\n`; }; ``` 为了方便观察对比 整个detail.js代码 ```js import Head from "next/head"; import Header from "../component/Header"; import { Row, Col, Breadcrumb, Affix } from "antd"; import { CalendarOutlined, FolderOpenFilled, EyeFilled, } from "@ant-design/icons"; import "../public/css/pages/common.css"; import "../public/css/pages/detail.css"; import Author from "../component/Author"; import Advert from "../component/Advert"; import Footer from "../component/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 "../component/tocify.tsx"; function Detail(props) { console.log(props); const tocify = new Tocify(); const renderer = new marked.Renderer(); renderer.heading = function (text, level, raw) { const anchor = tocify.add(text, level); //自定义bar的渲染方式 return `${text}\n`; }; marked.setOptions({ renderer: renderer, gfm: true, pedantic: false, //false容错 sanitize: false, //false不忽略html标签 tables: true, //gfm breaks: false, //gfm smartypants: false, smartLists: true, highlight: function (code) { return hljs.highlightAuto(code).value; }, }); let html = marked(props.content); //console.log(html,props.content) return (
detail
首页 列表 xxx
{props.title}
{new Date(Number(props.addtime)).toLocaleString()} {props.typename} {props.viewcount}人
文章目录
{tocify && tocify.render()}
); } Detail.getInitialProps = async (context) => { //console.log(context) let id = context.query.id; let promise = new Promise((resolve, reject) => { let url = `http://127.0.0.1:7001/default/getArticleById/${id}`; //egg.js中配置好的RESTful接口 try { axios(url).then((res) => { console.log(res.data.data[0]); resolve(res.data.data[0]); }); } catch { reject("err"); } }); return await promise; //需要返回promise类型对象 }; export default Detail; ``` ![image-20200526113657872](https://gitee.com/mus-z/blogImage/raw/master/img/20200526113659.png) 这效果是真心不错的orz,连分级和滚动动画都加进来了(但是这个滚动绑定是咋实现的) ### day04 继续前中 #### 4.1 配置url接口+Header链接 ![image-20200528152713820](https://gitee.com/mus-z/blogImage/raw/master/img/20200528152724.png) 把接口集中在apiUrl文件中 引入后调用,如下图 ![image-20200528152833058](https://gitee.com/mus-z/blogImage/raw/master/img/20200528152834.png) 配置新的接口流程(用来生成Header的link) ·方法`app/controller/default/home.js`实现 ![image-20200528170042318](https://gitee.com/mus-z/blogImage/raw/master/img/20200528170044.png) ·路由`app/router/default.js`导出(通过router.js出去的) ![image-20200528170312385](https://gitee.com/mus-z/blogImage/raw/master/img/20200528170313.png) ·配置前端的自定义路由接口 ![image-20200528170350998](https://gitee.com/mus-z/blogImage/raw/master/img/20200528170352.png) 之后在Header中使用,模拟生命周期的didmount ![image-20200528170447998](https://gitee.com/mus-z/blogImage/raw/master/img/20200528170449.png) 然后map循环生成组件 ![image-20200528165815462](https://gitee.com/mus-z/blogImage/raw/master/img/20200528165831.png) #### 4.2 list页面使用传递的数据 配置新的接口 ![image-20200528175314418](https://gitee.com/mus-z/blogImage/raw/master/img/20200528175321.png) 过程略 list中获取传参 ![image-20200528175401082](https://gitee.com/mus-z/blogImage/raw/master/img/20200528175414.png) ![image-20200528175828092](https://gitee.com/mus-z/blogImage/raw/master/img/20200528175829.png) 下面的内容也是根据之前的静态页面做修改即可 ![image-20200528181504722](https://gitee.com/mus-z/blogImage/raw/master/img/20200528181505.png) ![image-20200528181646786](https://gitee.com/mus-z/blogImage/raw/master/img/20200528181647.png) ![image-20200528181809341](https://gitee.com/mus-z/blogImage/raw/master/img/20200528181810.png) #### 4.3 让首页和列表页也都支持markdown解析预览 根据detail页面 引入 ```js import marked from "marked"; import hljs from "highlight.js"; import "highlight.js/styles/monokai-sublime.css"; ``` 然后复制marked初始化部分 ```js const renderer = new marked.Renderer(); marked.setOptions({ renderer: renderer, gfm: true, pedantic: false, //false容错 sanitize: false, //false不忽略html标签 tables: true, //gfm breaks: false, //gfm smartypants: false, smartLists: true, highlight: function (code) { return hljs.highlightAuto(code).value; }, }); //marked(data); ``` 在下面注入 ```js
``` ![image-20200528183355355](https://gitee.com/mus-z/blogImage/raw/master/img/20200528183356.png) 同理改到list页面中 不过这里还是通过封装函数的方式吧 ![image-20200528184745033](https://gitee.com/mus-z/blogImage/raw/master/img/20200528184746.png) ```js import marked from "marked"; import hljs from "highlight.js"; import "highlight.js/styles/monokai-sublime.css"; export default function (md) { const renderer = new marked.Renderer(); marked.setOptions({ renderer: renderer, gfm: true, pedantic: false, //false容错 sanitize: false, //false不忽略html标签 tables: true, //gfm breaks: false, //gfm smartypants: false, smartLists: true, highlight: function (code) { return hljs.highlightAuto(code).value; }, }); return marked(md); } ``` ![image-20200528184822388](https://gitee.com/mus-z/blogImage/raw/master/img/20200528184823.png) ![image-20200528184841242](https://gitee.com/mus-z/blogImage/raw/master/img/20200528184842.png) 直接引入即可,这时候发现css样式有点不对, 我把`detail.css`中关于code的样式放到`common.js`中就可以了 index中也可以用这个封装的函数得到解析之后的字符串 不过detail中不行,因为多了一个封装的bar导航,所以还是得单独写在detail中 ### day05 后台初始化+添加页面 #### 5.1 后台项目初始化 用 create-react-app和antd作为项目结构 #### 5.2 加路由 安装 react-router-dom ![](https://gitee.com/mus-z/blogImage/raw/master/img/20200609164056.png) ![image-20200609164116928](https://gitee.com/mus-z/blogImage/raw/master/img/20200609164118.png) #### 5.3登录card框![image-20200609172138083](https://gitee.com/mus-z/blogImage/raw/master/img/20200609172140.png) ```js import React, { useState } from "react"; import "antd/dist/antd.css"; import { Card, Input, Button, Spin } from "antd"; import { UserOutlined ,KeyOutlined} from "@ant-design/icons"; import "../css/Login.css" function Login() { const [userName, setUserName] = useState(""); const [password, setPassword] = useState(""); const [isLoading, setInLoading] = useState(false); const checkLogin=()=>{ setInLoading(true) setTimeout(()=>{setInLoading(false)},1000) } return (
} onChange={(e) => { setUserName(e.target.value); }} />

} onChange={(e) => { setPassword(e.target.value); }} />

); } ``` #### 5.4 使用antd的layout侧栏布局 [https://ant.design/components/layout-cn/](https://ant.design/components/layout-cn/) 改写成hooks形式 ```js import React, { useState } from "react"; import { Layout, Menu, Breadcrumb } from "antd"; import { DesktopOutlined, PieChartOutlined, FileOutlined, CommentOutlined, UserOutlined, } from "@ant-design/icons"; import "../css/AdminIndex.css"; const { Header, Content, Footer, Sider } = Layout; const { SubMenu } = Menu; function AdminIndex() { let [collapsed, setCollapsed] = useState(false); const onCollapse = (collapsed) => { console.log(collapsed); setCollapsed(collapsed); }; return ( <>
}> 工作台 } title='文章管理'> 添加文章 文章列表 修改文章 } >留言管理 } >用户管理
这是头部
博客管理系统 工作台
工作台的内容
@admin-blog
); } export default AdminIndex; ``` ![image-20200609174933385](https://gitee.com/mus-z/blogImage/raw/master/img/20200609174934.png) #### 5.5 添加文章页面 示意图 ![image-20200609184000350](https://gitee.com/mus-z/blogImage/raw/master/img/20200609184002.png) 配置router ![image-20200609183641672](https://gitee.com/mus-z/blogImage/raw/master/img/20200609183643.png) 然后使用栅格布局 ```js import React, { useState } from "react"; import marked from "marked"; import "../css/addArticle.css"; import { Row, Col, Input, Select, Button, DatePicker } from "antd"; const { Option } = Select; const { TextArea } = Input; function AddArticle() { return (



); } export default AddArticle; ``` ![image-20200609185941478](https://gitee.com/mus-z/blogImage/raw/master/img/20200609185942.png) 配置之前使用过的marked 然后定义些state并绑定 ![image-20200609210612510](https://gitee.com/mus-z/blogImage/raw/master/img/20200609210618.png) ![image-20200609210639441](https://gitee.com/mus-z/blogImage/raw/master/img/20200609210640.png) 大致效果有一些了,但是还没有解决换行和多行代码块的问题。。 发现之前的highlight.js没装上,另外注意多行代码块的第一行不要放文本 不然会渲染的时候布局有问题 ![image-20200609215345967](https://gitee.com/mus-z/blogImage/raw/master/img/20200609215352.png) 而且想要出现换行需要两次回车 #### 5.6 搞定导入解析 通过antd的Upload组件,我们这里用子组件Dragger ```js import React, { useState, useEffect } from "react"; import "../css/addArticle.css"; import { Row, Col, Input, Select, Button, DatePicker } from "antd"; import Marked from "../component/Marked"; import { Upload } from "antd"; import { InboxOutlined } from "@ant-design/icons"; const { Dragger } = Upload; const { Option } = Select; const { TextArea } = Input; function AddArticle() { const [articleId, setArticleId] = useState(0); // 文章的ID,如果是0说明是新增加,如果不是0,说明是修改 const [articleTitle, setArticleTitle] = useState(""); //文章标题 const [articleContent, setArticleContent] = useState(""); //markdown的编辑内容 const [markdownContent, setMarkdownContent] = useState("预览内容"); //html内容 const [introducemd, setIntroducemd] = useState(""); //简介的markdown内容 const [introducehtml, setIntroducehtml] = useState("等待编辑"); //简介的html内容 const [showDate, setShowDate] = useState(); //发布日期 const [updateDate, setUpdateDate] = useState(); //修改日志的日期 const [typeInfo, setTypeInfo] = useState([]); // 文章类别信息 const [selectedType, setSelectType] = useState(1); //选择的文章类别 let [filename, setFile] = useState(""); //存储导入文件名 let props = { name: "file", beforeUpload(file, list) { //注意:IE9 不支持该方法。 console.log(list); if (window.FileReader) { let filename = file.name; //文件名 var reader = new FileReader(); reader.onload = function () { //加载完毕之后 //console.log(this.result); setArticleContent(reader.result); //文件传给输入栏 setArticleTitle(filename.split(".") ? filename.split(".").slice(0,filename.split(".").length-1).join('.') : "");//文件标题处理,把后缀去掉 setFile(filename); }; reader.readAsText(file); } return false; }, showUploadList: false, //不显示文件list }; const changeTitle = (e) => { setArticleTitle(e.target.value); }; const changeContent = (e) => { //console.log(e.target.value); setArticleContent(e.target.value); //setMarkdownContent(Marked(e.target.value)); }; const changeIntroduce = (e) => { setIntroducemd(e.target.value); //setIntroducehtml(Marked(e.target.value)); }; useEffect(() => { setMarkdownContent(Marked(articleContent)); }, [articleContent]); //预览随articleContent更新 useEffect(() => { setIntroducehtml(Marked(introducemd)); }, [introducemd]); //预览随articleContent更新 return (
{/* 第一行 */}
{/* 第二行 */}

{filename.length == 0 ? ( <>

导入markdown

拖拽上传/点击上传

) : (

{filename}

)}
{ console.log(value); }} >

{/* 第三行 */}

{/* 第三行 */}