0%

vuepress中设置图片居中

  1. Markdown-it 的数据结构及解析过程
  2. 自定义图片处理规则(inline)
  3. 设置图片居中

API: https://markdown-it.github.io/markdown-it

Markdown-it 的数据结构及解析过程

markdown-it 中是以 Token 来标识整个文档及里面的元素,可以认为它就是 html 中标签的抽象

解析时,将首先把整个md文件的内容抽象成一个大的 Token,接着渲染这个 Token,得到 html。抽象的过程分为不断迭代的两小步,直至文档尾。第一小步先解析块级元素(block),比如代码段、段落、引用等;第二小步解析行内元素(inline),比如图片、文字。每一步都对应相应的提取规则(Rule)。

得到 Token 后,使用 render() 函数进行渲染,渲染的时候也有相应的规则,不过这些规则只针对行内元素。

具体过程描述如上,详细的处理规则可以通过接下来的实例进一步理解。

源码仓库地址: https://github.com/markdown-it/markdown-it/tree/master/lib

下面两部分的代码均可写在 vuepress 的配置文件中:.vuepress/config.js - markdown.extendMarkdown(md => {...})

自定义图片处理规则(inline)

这部分其实没什么用,因为一开始的目的是把图片居中,想着在包裹图片的<p>加一个text-align:center样式,所以首先想着把图片搞出来处理,结果发现应该是从段落入手。然后这部分是实验的代码,边对照着源码敲出来的,不得不佩服 markdown-it 的开发者,把js写成了c语言,各种指针的调用,阅读起来难度有点大,极具挑战性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 先将原先的图片处理规则禁用
md.inline.ruler.disable('image');

// 接着写入自定义的图片处理规则
md.inline.ruler.before('image', 'imageCenter', (state) => {
let parsedImage = state.src.match(/!\[([\w\W]*)\]\(([\w\W]*)\)/);
if (parsedImage) {
let imageName = parsedImage[1]
let imageLink = parsedImage[2];

if (!imageName || !imageLink) { return; }

// 加入到 token 中,才可以在 renderer 中找到对应的 rule
let token = state.push('imageCenter', 'imageCenter', 0);
// 这个返回值是个指针
token.attrs = {
imageName,
imageLink
}

// 更改指针位置,表示解析完毕这串字符串
state.pos = state.posMax;

// 返回 true,表示字符串处理完毕
return true;
}
});



const imageCenterRender = (tokens, idx) => {
let content = tokens[idx];
console.log('\n\n渲染的内容: \n', content);

return '<img />';
}

md.renderer.rules.imageCenter = imageCenterRender;

设置图片居中

这部分代码是把 markdown-it 源码中的 lib/rules_block/paragraph.js paragraph.js 处理部分直接抄过来,然后改了一行,往 <p> 的属性中加入了图片居中的样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 自定义 block 段落中的图片显示
md.block.ruler.disable('paragraph');
md.block.ruler.after('paragraph', 'm_paragraph', (state, startLine) => {
var content, terminate, i, l, token, oldParentType,
nextLine = startLine + 1,
terminatorRules = state.md.block.ruler.getRules('paragraph'),
endLine = state.lineMax;

oldParentType = state.parentType;
state.parentType = 'paragraph';

// jump line-by-line until empty one or EOF
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
// this would be a code block normally, but after paragraph
// it's considered a lazy continuation regardless of what's there
if (state.sCount[nextLine] - state.blkIndent > 3) { continue; }

// quirk for blockquotes, this line should already be checked by that rule
if (state.sCount[nextLine] < 0) { continue; }

// Some tags can terminate paragraph without empty line.
terminate = false;
for (i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true;
break;
}
}
if (terminate) { break; }
}

content = state.getLines(startLine, nextLine, state.blkIndent, false).trim();

state.line = nextLine;

token = state.push('paragraph_open', 'p', 1);
// 如果有图片,则居中
if (/^\!\[[\w\W]*\]\([\w\W]*\)$/.test(content)) {
token.attrSet('style', "text-align:center;");
}
token.map = [startLine, state.line];

token = state.push('inline', '', 0);
token.content = content;
token.map = [startLine, state.line];
token.children = [];

token = state.push('paragraph_close', 'p', -1);

state.parentType = oldParentType;

return true;
});