跳到主要内容

Markdown

WdMarkdown

适用场景

基于markdown-it实现的 markdown 展示组件。

基础能力说明

  1. 支持 markdown 语法
  2. 带有预设和选项的初始化,通过 options 传递预设选项。
({
html: false, // 在源码中启用 HTML 标签
xhtmlOut: false, // 使用 / 来闭合单标签 (比如 <br />)。
// 这个选项只对完全的 CommonMark 模式兼容。
breaks: false, // 转换段落里的 换行符 到 <br>。
langPrefix: 'language-', // 给围栏代码块的 CSS 语言前缀。对于额外的高亮代码非常有用。
linkify: false, // 将类似 URL 的文本自动转换为链接。

// 启用一些语言中立的替换 + 引号美化
typographer: false,

// 双 + 单引号替换对,当 typographer 启用时。
// 或者智能引号等,可以是 String 或 Array。
//
// 比方说,你可以支持 '«»„“' 给俄罗斯人使用, '„“‚‘' 给德国人使用。
quotes: '“”‘’',
});

扩展场景说明

1.如何在 Markdown 中使用 Mermaid

Mermaid是一个基于 JavaScript 的开源库,它允许用户通过简洁的文本和代码语法快速生成各种图表,包括流程图、序列图、甘特图和思维导图等。

    1. 加载 Mermaid

image

<script src="https://cdn.jsdelivr.net/npm/mermaid@10.9.1/dist/mermaid.min.js"></script>

国内 cdn 地址:

<script src="https://cdn.staticfile.net/mermaid/10.7.0/mermaid.min.js"></script>
注意
  • 当前加载外部资源仅支持在 Web 端使用,暂不支持小程序。
  • 在一些情况下,由于托管脚本依赖的 jsdeliver 的 CDN 在某些情况下国内无法连接,导致脚本不能正常启用。可考虑使用其他 CDN。
    1. 创建方法 mermaidRun
export default function ({ event, data }) {
setTimeout(() => {
mermaid.run({
querySelector: '.language-mermaid',
});
}, 0);
}
    1. 在 markdownReady 事件中调用 mermaidRun 方法,渲染图表
    1. 编写 markdown

```mermaid graph TD A[Christmas] -->|Get money| B(Go shopping) B --> C{Let me think} C -->|One| D[Laptop] C -->|Two| E[iPhone] C -->|Three| F[Car] ```

image

2.如何在 Markdown 中使用 katex

katex 是一个快速,易于使用的 JavaScript 库,用于在 Web 上进行 TeX 数学渲染。通过加载 katex 插件,可以支持 katex 渲染。

    1. 加载 katex

image

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
<!-- The loading of KaTeX is deferred to speed up page rendering -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
注意
  • 当前加载外部资源仅支持在 Web 端使用,暂不支持小程序。
  • 在一些情况下,由于托管脚本依赖的 jsdeliver 的 CDN 在某些情况下国内无法连接,导致脚本不能正常启用。
    1. 创建插件方法 katex
var katex = window.katex;

// Test if potential opening or closing delimieter
// Assumes that there is a "$" at state.src[pos]
function isValidDelim(state, pos) {
var prevChar,
nextChar,
max = state.posMax,
can_open = true,
can_close = true;

prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1;
nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1;

// Check non-whitespace conditions for opening and closing, and
// check that closing delimeter isn't followed by a number
if (
prevChar === 0x20 /* " " */ ||
prevChar === 0x09 /* \t */ ||
(nextChar >= 0x30 /* "0" */ && nextChar <= 0x39) /* "9" */
) {
can_close = false;
}
if (nextChar === 0x20 /* " " */ || nextChar === 0x09 /* \t */) {
can_open = false;
}

return {
can_open: can_open,
can_close: can_close,
};
}

function math_inline(state, silent) {
var start, match, token, res, pos, esc_count;

if (state.src[state.pos] !== '$') {
return false;
}

res = isValidDelim(state, state.pos);
if (!res.can_open) {
if (!silent) {
state.pending += '$';
}
state.pos += 1;
return true;
}

// First check for and bypass all properly escaped delimieters
// This loop will assume that the first leading backtick can not
// be the first character in state.src, which is known since
// we have found an opening delimieter already.
start = state.pos + 1;
match = start;
while ((match = state.src.indexOf('$', match)) !== -1) {
// Found potential $, look for escapes, pos will point to
// first non escape when complete
pos = match - 1;
while (state.src[pos] === '\\') {
pos -= 1;
}

// Even number of escapes, potential closing delimiter found
if ((match - pos) % 2 == 1) {
break;
}
match += 1;
}

// No closing delimter found. Consume $ and continue.
if (match === -1) {
if (!silent) {
state.pending += '$';
}
state.pos = start;
return true;
}

// Check if we have empty content, ie: $$. Do not parse.
if (match - start === 0) {
if (!silent) {
state.pending += '$$';
}
state.pos = start + 1;
return true;
}

// Check for valid closing delimiter
res = isValidDelim(state, match);
if (!res.can_close) {
if (!silent) {
state.pending += '$';
}
state.pos = start;
return true;
}

if (!silent) {
token = state.push('math_inline', 'math', 0);
token.markup = '$';
token.content = state.src.slice(start, match);
}

state.pos = match + 1;
return true;
}

function math_block(state, start, end, silent) {
var firstLine,
lastLine,
next,
lastPos,
found = false,
token,
pos = state.bMarks[start] + state.tShift[start],
max = state.eMarks[start];

if (pos + 2 > max) {
return false;
}
if (state.src.slice(pos, pos + 2) !== '$$') {
return false;
}

pos += 2;
firstLine = state.src.slice(pos, max);

if (silent) {
return true;
}
if (firstLine.trim().slice(-2) === '$$') {
// Single line expression
firstLine = firstLine.trim().slice(0, -2);
found = true;
}

for (next = start; !found; ) {
next++;

if (next >= end) {
break;
}

pos = state.bMarks[next] + state.tShift[next];
max = state.eMarks[next];

if (pos < max && state.tShift[next] < state.blkIndent) {
// non-empty line with negative indent should stop the list:
break;
}

if (state.src.slice(pos, max).trim().slice(-2) === '$$') {
lastPos = state.src.slice(0, max).lastIndexOf('$$');
lastLine = state.src.slice(pos, lastPos);
found = true;
}
}

state.line = next + 1;

token = state.push('math_block', 'math', 0);
token.block = true;
token.content =
(firstLine && firstLine.trim() ? firstLine + '\n' : '') +
state.getLines(start + 1, next, state.tShift[start], true) +
(lastLine && lastLine.trim() ? lastLine : '');
token.map = [start, state.line];
token.markup = '$$';
return true;
}

export default function math_plugin(md, options) {
// Default options

options = options || {};

// set KaTeX as the renderer for markdown-it-simplemath
var katexInline = function (latex) {
options.displayMode = false;
try {
return katex.renderToString(latex, options);
} catch (error) {
if (options.throwOnError) {
console.log(error);
}
return latex;
}
};

var inlineRenderer = function (tokens, idx) {
return katexInline(tokens[idx].content);
};

var katexBlock = function (latex) {
options.displayMode = true;
try {
return '<p>' + katex.renderToString(latex, options) + '</p>';
} catch (error) {
if (options.throwOnError) {
console.log(error);
}
return latex;
}
};

var blockRenderer = function (tokens, idx) {
return katexBlock(tokens[idx].content) + '\n';
};

md.inline.ruler.after('escape', 'math_inline', math_inline);
md.block.ruler.after('blockquote', 'math_block', math_block, {
alt: ['paragraph', 'reference', 'blockquote', 'list'],
});
md.renderer.rules.math_inline = inlineRenderer;
md.renderer.rules.math_block = blockRenderer;
}
    1. 使用插件,在 markdownReady 事件中调用更新 markdown 实例的方法
$w.markdown2.markdownInstance?.use($w.page.handler.katex);
    1. 编写 markdown

# Maxwell's Equations


equation | description
----------|------------
$\nabla \cdot \vec{\mathbf{B}} = 0$ | divergence of $\vec{\mathbf{B}}$ is zero
$\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} = \vec{\mathbf{0}}$ | curl of $\vec{\mathbf{E}}$ is proportional to the rate of change of $\vec{\mathbf{B}}$
$\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} = \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} = 4 \pi \rho$ | _wha?_

image

属性介绍

组件接收的外部传入的属性

属性名
属性标识
类型
说明
内容valuestring

markdown的展示内容

示例:"---\n__Advertisement :)__\n\n- __[pica](https://nodeca.github.io/pica/demo/)__ - high quality and fast image\n resize in browser.\n- __[babelfish](https://github.com/nodeca/babelfish/)__ - developer friendly\n i18n with plurals support and easy syntax.\n\nYou will like those projects!\n\n---\n\n# h1 Heading 8-)\n## h2 Heading\n### h3 Heading\n#### h4 Heading\n##### h5 Heading\n###### h6 Heading\n\n\n## Horizontal Rules\n\n___\n\n---\n\n***\n\n\n## Typographic replacements\n\nEnable typographer option to see result.\n\n(c) (C) (r) (R) (tm) (TM) (p) (P) +-\n\ntest.. test... test..... test?..... test!....\n\n!!!!!! ???? ,, -- ---\n\n\"Smartypants, double quotes\" and 'single quotes'\n\n\n## Emphasis\n\n**This is bold text**\n\n__This is bold text__\n\n*This is italic text*\n\n_This is italic text_\n\n~~Strikethrough~~\n\n\n## Blockquotes\n\n\n> Blockquotes can also be nested...\n>> ...by using additional greater-than signs right next to each other...\n> > > ...or with spaces between arrows.\n\n\n## Lists\n\nUnordered\n\n+ Create a list by starting a line with `+`, `-`, or `*`\n+ Sub-lists are made by indenting 2 spaces:\n - Marker character change forces new list start:\n * Ac tristique libero volutpat at\n + Facilisis in pretium nisl aliquet\n - Nulla volutpat aliquam velit\n+ Very easy!\n\nOrdered\n\n1. Lorem ipsum dolor sit amet\n2. Consectetur adipiscing elit\n3. Integer molestie lorem at massa\n\n\n1. You can use sequential numbers...\n1. ...or keep all the numbers as `1.`\n\nStart numbering with offset:\n\n57. foo\n1. bar\n\n\n## Code\n\nInline `code`\n\nIndented code\n\n // Some comments\n line 1 of code\n line 2 of code\n line 3 of code\n\n\nBlock code \"fences\"\n\n```\nSample text here...\n```\n\nSyntax highlighting\n\n``` js\nvar foo = function (bar) {\n return bar++;\n};\n\nconsole.log(foo(5));\n```\n\n## Tables\n\n| Option | Description |\n| ------ | ----------- |\n| data | path to data files to supply the data that will be passed into templates. |\n| engine | engine to be used for processing templates. Handlebars is the default. |\n| ext | extension to be used for dest files. |\n\nRight aligned columns\n\n| Option | Description |\n| ------:| -----------:|\n| data | path to data files to supply the data that will be passed into templates. |\n| engine | engine to be used for processing templates. Handlebars is the default. |\n| ext | extension to be used for dest files. |\n\n\n## Links\n\n[link text](http://dev.nodeca.com)\n\n[link with title](http://nodeca.github.io/pica/demo/ \"title text!\")\n\nAutoconverted link https://github.com/nodeca/pica (enable linkify to see)\n\n\n## Images\n\n![image](https://octodex.github.com/images/minion.png)\n"

选项optionsobject

markdown的初始化配置

事件

组件暴露的事件,可以监听组件的事件来触发一些外部的动作

事件名
事件code
事件出参 event.detail
适用情况
说明
markdown ReadyonReadyobject
  • markdownInstance: object markdown实例
    兼容三端

    当前markdown已完成实例初始化,可以调用markdown实例方法

    属性 API

    通过属性 API,可以获取组件内部的状态和属性值,可以通过$w.componentId.propertyName 来访问组件内部的值,如 $w.input1.value ,详请请参考 属性 API

    只读属性名
    属性标识
    类型
    说明
    内容valuestring

    markdown的展示内容

    markdown实例markdownInstanceobject

    可以通过markdown实例调用markdown实例方法,如:markdownInstance.render("# markdown-it rulezz!")具体内容见markdown-it官方文档

    方法 API

    通过方法 API,可以通过程序触发组件内部的方法,比如提交表单,显示弹窗等, 可以通过$w.componentId.methodName来调用组件方法,如 $w.form1.submit()

    方法名
    方法标识
    参数
    方法说明
    更新markdown实例updateMarkdownInstanceobject
    • markdownInstance: object markdown实例

      可以通过markdown实例调用markdown实例方法,如:markdownInstance.render("# markdown-it rulezz!")具体内容见markdown-it官方文档

      更新markdown实例

      样式 API

      通过样式 API,可以覆盖组件中内部元素的样式来实现自定义,例如在低代码编辑器中中通过 #wd-page-root .wd-btn 即可为所有的按钮组件编写样式,通过 :scope 可以控制单个组件样式, 详细说明请参考样式 API

      名称
      类名
      说明和示例
      根元素.wd-markdown链接组件根元素
      /* :scope 指的是当前组件元素 */
      /* 具体可参考样式 API 文档 */
      :scope.wd-markdown {
        /* 在这里编写CSS 样式 */
      }
      PC 端链接根元素.wd-pc-markdown可以为 PC 端的链接编写样式
      /* :scope 指的是当前组件元素 */
      /* 具体可参考样式 API 文档 */
      :scope.wd-pc-markdown {
        /* 在这里编写CSS 样式 */
      }
      H5 端链接根元素.wd-h5-markdown可以为 H5 端的链接编写样式
      /* :scope 指的是当前组件元素 */
      /* 具体可参考样式 API 文档 */
      :scope.wd-h5-markdown {
        /* 在这里编写CSS 样式 */
      }
      小程序端链接根元素.wd-mp-markdown可以为小程序端的链接编写样式
      /* :scope 指的是当前组件元素 */
      /* 具体可参考样式 API 文档 */
      :scope.wd-mp-markdown {
        /* 在这里编写CSS 样式 */
      }

      了解样式 API

      限制说明

      小程序的 markdown 展示,是通过 markdown-it 解析成 html,再通过小程序的富文本组件进行展示。因此,小程序的 markdown 展示能力是有限的,一些样式细节与 web 端略有差别,请以小程序实际运行效果为准。

      常见问题

      为什么外链图片显示异常?

      很多网站的图片都会判断页面来源,可能是图片设置了防盗链,在直接输入的时候没有访问来源,但在你的页面中使用时,会判断页面来源来禁止访问。可以尝试使用其他图片资源,或检查 F12 开发者工具中的加载失败信息。