Vue - 后端解析md文件并在前端显示 (代码高亮+md中图片显示)
之前在做的个人博客,前端直接引入markdown文件并显示,当时的做法如下:这里如果用import()动态引入,必须写入静态路径,十分麻烦,难道项目部署上线时我还需要到这里来修改这个静态路径吗?肯定不会,所以我思来想去,还是采用后端解析md文件前端显示的流程,这样一系列文件路径的配置可以放在后端。1. 后端解析md文件1.1 配置md文件存储路径以及相关映射server:#端口port: 4000
之前在做的个人博客,前端直接引入markdown文件并显示,当时的做法如下:
这里如果用import()动态引入,必须写入静态路径,十分麻烦,难道项目部署上线时我还需要到这里来修改这个静态路径吗?肯定不会,所以我思来想去,还是采用后端解析md文件前端显示的流程,这样一系列文件路径的配置可以放在后端。
1. 后端解析md文件
1.1 Springboot中配置md文件存储路径以及相关映射
server:
#端口
port: 4000
#本地路径 为了映射md中的图片文件,必须填
address: localhost
#文件上传路径
file:
upload:
#注意md文件存放位置,注意最后结尾是'/'
abpath: F:/note/
urlpath: /note/**
#存放图像的路径映射,与md文件中的/assets对应
mdImageDir: /assets/**
1.2 根据配置建立url映射
这里需要在springboot中定义MyWebAppConfig进行映射配置:
@Configuration
public class MyWebAppConfig implements WebMvcConfigurer {
//文件夹绝对路径
@Value("${file.upload.abpath}")
private String abpath;
//文件夹url
@Value("${file.upload.urlpath}")
private String ulrpath;
//图片url
@Value("${file.upload.mdImageDir}")
private String mdImageDir;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//md文件位置建立映射
registry.addResourceHandler(ulrpath).addResourceLocations("file:"+abpath);
//图片位置建立映射
registry.addResourceHandler(mdImageDir).addResourceLocations("file:"+abpath+"assets/");
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
}
}
1.3 Controller层
@Value("${file.upload.abpath}")
private String abpath;
//文件夹url
@Value("${file.upload.mdImageDir}")
private String mdImageDir;
//端口
@Value("${server.port}")
private String port;
//地址
@Value("${server.address}")
private String address;
@Autowired
private ArticleService articleService;
//根据id返回md的str
@GetMapping("/api/getStrById/{id}")
public String getStrById(@PathVariable("id") long id) throws FileNotFoundException {
//通过id从数据库获取article对象
Article article = articleService.queryById(id);
//如果article不存在则抛出异常
if(StringUtils.isEmpty(article)){
throw new FileNotFoundException("文件不存在");
}
//组装成路径,比如F:\note\123.txt
String path = abpath + article.getName();
//通过路径获取str
String str = null;
try {
//自定义md2Html函数转化md文件为html
str = FileUtil.md2Html(path,"http://"+address+":"+port+"/assets/");
} catch (IOException e) {
e.printStackTrace();
//抛出自已定义的异常
throw new FileNotFoundException("文件所在路径找不到文件");
}
return str;
}
articleService的定义就不再赘述,之前博客说过,这里不是重点。FileNotFoundException的定义和拦截,见我之前的博客,这里也不说了。这里重点是md2Html()函数,即将md文件转化为string,我定义在FileUtil类中,这里用到了PegDownProcessor这个类,需要首先导入依赖:
<!--md转html工具-->
<dependency>
<groupId>org.pegdown</groupId>
<artifactId>pegdown</artifactId>
<version>1.6.0</version>
</dependency>
md2Html()函数定义:
public static String md2Html(String path,@Nullable String imgaddr) throws IOException {
String html = null;
FileReader r = new FileReader(path);
char[] cbuf = new char[1024];
while( r.read(cbuf) != -1){
html += new String(cbuf);
}
PegDownProcessor pdp = new PegDownProcessor(Integer.MAX_VALUE);
html = pdp.markdownToHtml(html);
if(!StringUtils.isEmpty(imgaddr)){
//将html中路径"assets/***" 变为 "http://localhost:4000/assets/"
String newHtml = StringUtils.replace(html, "assets/", imgaddr);
return newHtml;
}
return html;
}
由于md文件保存图片默认路径都是"assets/"之下(这里需要在Typroa中设置),如图:
这样在前端是无法访问到的,所以在md2Html()函数中对"assets/"路径进行替换:
//将html中路径"assets/***" 变为 "http://localhost:4000/assets/"
String newHtml = StringUtils.replace(html, "assets/", imgaddr);
访问测试看看:
2. 前端显示html
我前端是用vue写的,相关流程可以见我之前的博客。
2.1 axios定义交互函数
//根据地址获取md的html
export function getStrById(id){
return axios({
method: 'get',
url: `/getStrById/${id}`,
});
}
2.2 代码高亮配置
这里主要参考了https://blog.csdn.net/qq_39826207/article/details/109265099里的做法。
- 安装依赖
// 用于代码高亮显示
cnpm install highlight.js -S
// 代码行号显示插件
cnpm install highlightjs-line-numbers.js
- 在main.js文件中引入highlight.js
//引入代码高亮
import highlight from 'highlight.js';
Vue.use(highlight);
- 创建一个全局指令,用于代码高亮显示
在main.js文件中,创建一个自定义全局指令,如下所示:
//将代码高亮封装成一个指令
Vue.directive('highlight', (el) => {
let blocks = el.querySelectorAll('pre code')
blocks.forEach((block) => {
highlight.highlightBlock(block)
})
})
- 在具体Article.vue中进行显示
<template>
<div>
<!--这里只需要加上v-highlight即可高亮-->
<div v-highlight v-html="msg" class="styleClass"></div>
</div>
</template>
<script>
import axios from '@/request'
import {getStrById} from '@/api/article'
// 引入高亮样式,其他样式见https://blog.csdn.net/qq_39826207/article/details/109265099
import "highlight.js/styles/atelier-forest-dark.css";
export default {
data(){
return{
arId: '',
msg:'',
key: 0,
arName:''
}
},
methods:{
//获取md文件并解析为html
async getMd(){
// 通过id获取md文件的str
//this.$route.params.id是指$route传过来的id,即后台数据库id
await getStrById(this.$route.params.id).then(res=>{
console.log(res)
this.msg= res;
})
//强制重新渲染
this.key += 1
},
},
mounted(){
this.getMd();
}
}
</script>
最终测试效果如图:
3. 小结
关于代码高亮显示后的行号显示,一直没有搞定!还在继续寻找解决方案,后续会放上来。希望能够对正在用vue+springboot开发项目的朋友有所帮助,有问题请一起讨论,大家共同进步。
更多推荐
所有评论(0)