简介:面向前端开发者与文档撰写者的 Mermaid v10.6.1 稳定版渲染库压缩文件,解决在网页、应用或离线环境中快速绘制流程图、序列图、甘特图、类图等图表的痛点。zip 压缩包内仅 1 个 js 文件,即 mermaid.min.js,整体大小 851KB,体积轻量,可离线部署,无需外部服务即可在前端项目中即时渲染。已有 510 人学习下载。该版本在既有基础上修复了部分问题并增强稳定性,适合快速原型、功能演示、技术文档与数据分析。引入后即可将文本语法一键转换为专业图表,免去复杂绘图技能;同时支持二次扩展,兼容 VS Code、Jira、GitLab 等生态,直接提升了图表制作与开发协作效率。
1. 把 Mermaid v10.6.1 当本地渲染器:这份 JS 资源到底能救什么场
做前端页面开发或者写内部工具时,最让人头疼的不是图表本身,而是「用啥画图」这个问题。Canvas 手写太慢,ECharts 又太重,大部分时候我们只是想把一段文本描述变成一张能看的流程图——这时候 Mermaid 就是最合适的选择。这份 mermaid.min.js v10.6.1 是一个压缩后的完整版渲染库,拿到手直接在 HTML 里引入就能用,不需要 Node.js 环境,不需要构建工具,浏览器打开就能把graph TD之类的文本语法渲染成 SVG 图形。适合的场景很具体:写技术文档想嵌流程图但不想截图、做一个配置面板需要实时预览节点关系、或者在内网环境里不能访问 CDN 但还想用 Mermaid 的全部能力。它解决的痛点是——把图表从「设计稿」变成「可维护的文本」,改文字就能改图,版本还锁得死,不会今天能渲染明天突然报错。
2. Mermaid 的解析链路与语法规则:为什么文本能变成图
2.1 解析器的三段式流程:从字符串到 SVG 的完整路径
Mermaid 的本质是一个纯前端的 DSL(领域特定语言)解析器。它把用户写的类 Markdown 文本拆成词法单元,再根据不同类型的图表语法构建抽象语法树,最后走一个渲染器输出 SVG。这个流程在 10.6.1 版本里分成了三个解耦阶段:
第一阶段是parse,负责把原始字符串变成 AST。你在graph TD里写的每个节点 ID 和连线关系,会被拆成一个对象数组,每个对象携带id、type、text、shape这些字段。第二阶段是layout,它接收 AST 之后用内部实现的 Dagre 布局算法计算每个节点的坐标位置——注意 Mermaid 没有依赖外部布局库,而是在源码里内嵌了一个简化版的 Dagre,这也是它体积偏大(压缩后约 2.9MB)的原因之一。第三阶段是render,把节点坐标、连线路径、箭头标记拼装成 SVG 元素,插入到你指定的容器div里。
// 典型调用链:parse -> layout -> render 全自动,你只接触两个 API await mermaid.parse('graph TD; A-->B'); // 只校验语法,不渲染 const { svg } = await mermaid.render('preview', 'graph TD; A-->B'); // 渲染并返回 SVG 代码 document.getElementById('container').innerHTML = svg; // 手动插入容器这段代码展示了 Mermaid 最核心的两个 API 的协作关系。mermaid.parse()是纯校验接口,如果语法错误会抛异常,适合在用户输入时做实时提醒;mermaid.render()接收两个参数——第一个是 DOM 元素的id,第二个是图表文本,它返回一个 Promise,resolve 之后拿到svg字符串。我一般会在render之后手动把svg赋给目标容器,而不是让 Mermaid 自己找容器,因为这样能完全控制插入时机,避免渲染未完成时容器里闪现原始文本。
参数说明:render的第一个参数id不要求容器真实存在,Mermaid 内部会用这个 id 创建临时节点来测量尺寸,渲染完毕后就销毁了。第二个参数是图表定义,支持多行字符串,用反引号或\n拼接都可以。还有第三个可选参数importStyles,默认true,决定是否把 Mermaid 的默认 CSS 样式注入到 SVG 里,如果你在自己的样式表里已经定义了一套主题,可以传false关闭它。
2.2 v10.6.1 的版本边界:ESM 模块化之后的变化
10.6.1 这个版本号隐藏着一次架构变更的理解门槛。从 v10.0 开始,Mermaid 的源码从单体 IIFE 结构切换成了 ES Module 结构,mermaid.min.js这个文件其实是一个打包产物,内部用 Rollup 把几十个模块捆在了一起。这意味着它默认不在全局挂载mermaid对象,而是走export导出。你在<script>标签里直接引用的方式依然能用——因为打包时做了兼容处理,会往window上挂全局变量——但在type="module"的脚本里,你需要显式用import导入才能拿到引用。
// 错误示范:在 module 脚本里使用全局变量会得到 undefined // import './mermaid.min.js'; // console.log(window.mermaid); // undefined // 正确示范:显式导入命名导出 import mermaid from './mermaid.min.js'; mermaid.initialize({ startOnLoad: true });这个坑具体是这样的:如果你在 HTML 里用了<script type="module" src="app.js"></script>,那么即使你在另一个script标签里加载了mermaid.min.js,模块作用域里也看不到window.mermaid。解决方案有两个——要么在app.js里用import mermaid from './mermaid.min.js'直接引入,要么把 Mermaid 的加载标签放在模块脚本之前,并且在使用时用window.mermaid显式访问。我倾向于第一种做法,因为import的路径更清晰,而且还能触发浏览器的模块缓存,多个文件引用同一个 Mermaid 实例不会重复初始化。
另外 10.6.1 对浏览器版本有隐性要求:它内部使用了MutationObserver、ResizeObserver和Promise.allSettled,如果你要兼容 IE11 之类的古董浏览器,这个版本基本不用想了。我在一个内部管理系统里试过,Windows 7 自带的旧 Edge 打开会直接白屏报ResizeObserver is not defined,最后我只能退回 v9.x 的 IIFE 版本才解决问题——这就是版本边界,使用前必须先确认运行环境。
2.3 语法选型:流程图只用两三种节点写法就够了
很多人一上来就学 Mermaid 的全部节点语法,实战中真正常用的只有几种。流程图用graph TD(Top Down,从上到下)或graph LR(Left Right,从左到右),节点形状用A[文本]是矩形、A(文本)是圆角矩形、A{文本}是菱形判断。连线则是-->带箭头、---无箭头、-.->虚线带箭头、==>粗线带箭头。这套语法覆盖了 80% 的日常需求,剩下的子图subgraph和样式类classDef属于进阶用法,按需学就好。
graph TD A[收到需求] --> B{需求是否明确} B -->|是| C[进入开发] B -->|否| D[补充细节] C --> E[测试] --> F[上线] D --> B这段是流程图最典型的结构。B{需求是否明确}是菱形判断,B -->|是| C里的|是|是连线的文字标签,加上之后连线路径上会显示这个文本。注意C --> E[测试] --> F[上线]这种链式写法,Mermaid 支持把多条语句用分号或换行分隔,也可以像这样连写,中间节点自动生成。但有个细节要记住:D --> B这种回环边在布局时可能会导致节点位置重叠,如果发现渲染结果乱了,优先检查是不是有回环没走subgraph包裹。
3. 从零搭建流程图渲染环境:HTML 引入到参数配置全步骤
3.1 最小可运行模板:本地文件直接打开就能用
这一节直接给你一个可以复制到本地就跑起来的最小 HTML 页面。它做的事有:引入mermaid.min.js、初始化配置、把页面里带有language-mermaid类的代码块渲染成图。这套逻辑是官方推荐的标准用法,也是最不容易出错的起步方式。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Mermaid 本地渲染测试</title> <script src="./mermaid.min.js"></script> <style> #chart { width: 100%; min-height: 300px; background: #f8f9fa; } </style> </head> <body> <div id="chart"></div> <script> // 初始化并立即渲染一个流程图 mermaid.initialize({ startOnLoad: false, // 不走自动扫描,手动控制 theme: 'base', // 基础主题,颜色偏中性 securityLevel: 'strict', // 阻止 script 标签执行 fontFamily: 'Microsoft YaHei, sans-serif' }); const text = ` graph TD A[开始] --> B{已登录?} B -->|否| C[跳转登录页] B -->|是| D[加载用户数据] D --> E[渲染首页] `; mermaid.render('preview-svg', text).then(({ svg }) => { document.getElementById('chart').innerHTML = svg; }).catch(err => { console.error('渲染失败:', err.message); document.getElementById('chart').innerHTML = '<p style="color:red">语法错误或渲染超时</p>'; }); </script> </body> </html>这段代码的运行逻辑是:先通过mermaid.initialize()设置全局参数,然后手动调用mermaid.render()把文本变成 SVG 并插入#chart。之所以设置startOnLoad: false,是因为我们要精确控制渲染时机——真实项目里图表数据往往来自接口或用户输入,自动扫描机制在数据未就绪时会渲染出空图。
参数说明在这几个地方:
startOnLoad: false:关闭页面加载时的自动扫描。如果设为true,Mermaid 会查找class="mermaid"的元素并自动渲染,适合纯静态文档。手动模式更适合应用集成。theme: 'base':可选项有default、dark、forest、neutral、base。base是所有主题的基座,颜色最素,适合后期用 CSS 做二次定制。securityLevel: 'strict':这个参数是安全底线,strict模式会过滤掉图表文本里的<script>标签和javascript:伪协议,防止 XSS。如果你渲染的内容来自第三方输入,务必保持strict。fontFamily:控制 SVG 里所有文字的字体栈。默认是无衬线西文字体,渲染中文时样式不统一,我第一件事就是把它改成系统中文优先的列表。
3.2 动态数据场景:把接口 JSON 拼成 Mermaid 文本
静态写死在页面里的图很好做,但真实业务往往是后端给接口数据,前端动态拼接图表文本。这一节我以一个典型的「审批链路可视化」为例——后端返回节点列表和连线关系,前端把它拼成graph LR语句渲染出来。这个场景在工单系统、OA 系统里几乎天天见。
// 模拟接口返回数据 const approvalData = { nodes: [ { id: 'apply', label: '提交申请', type: 'start' }, { id: 'manager', label: '经理审批', type: 'normal' }, { id: 'finance', label: '财务审核', type: 'normal' }, { id: 'end', label: '流程结束', type: 'end' } ], edges: [ { from: 'apply', to: 'manager', label: '提交' }, { from: 'manager', to: 'finance', label: '通过' }, { from: 'manager', to: 'apply', label: '驳回' }, { from: 'finance', to: 'end', label: '通过' } ] }; // 动态拼接 Mermaid 文本 let chartText = 'graph TD\n'; approvalData.nodes.forEach(n => { // 根据节点类型选择不同形状 if (n.type === 'start') chartText += ` ${n.id}[${n.label}]\n`; else if (n.type === 'end') chartText += ` ${n.id}([${n.label}])\n`; else chartText += ` ${n.id}[${n.label}]\n`; }); approvalData.edges.forEach(e => { chartText += ` ${e.from} -->|${e.label}| ${e.to}\n`; }); // 渲染到容器 mermaid.render('flow-svg', chartText) .then(({ svg }) => { document.getElementById('approvalChart').innerHTML = svg; }) .catch(err => { alert('图表生成失败,请检查节点数据'); });这段拼接逻辑的关键点在于:节点 ID 必须是合法的变量名——不能含空格、连字符、中文。我见过很多新手直接用中文做 ID,比如提交申请[提交申请],Mermaid 解析会报Syntax error in text。正确做法是 ID 用英文或拼音,中文只放在[]标签里。另外type字段如果是end,我用了([文本])这种双括号语法,渲染出来是体育场形状,视觉上更醒目。
在这种动态场景里还有个常见需求——高亮当前审批节点。我一般用classDef配合指定节点 ID 实现,比如在拼文本时给当前节点加一行class currentNode highlight,然后在初始化配置里用themeCSS定义.highlight { fill: #ffc107; }。这样做的好处是样式与逻辑分离,改高亮色不需要动 JS 代码。
3.3 初始化参数的完整拆解:哪些能改、哪些是雷
mermaid.initialize({ ... })是配置的总入口,参数囊括了从主题到渲染行为的所有开关。我不打算每个参数都列一遍,只挑实战里真正影响产出效果的讲,并说明哪些参数改了之后会踩坑。
mermaid.initialize({ startOnLoad: false, themeVariables: { fontFamily: 'arial, "Microsoft YaHei", sans-serif', primaryColor: '#1890ff', primaryTextColor: '#fff', primaryBorderColor: '#1890ff', lineColor: '#333', textColor: '#333' }, flowchart: { diagramPadding: 8, htmlLabels: true, curve: 'basis' }, maxTextSize: 50000, suppressErrorRendering: false });themeVariables是一个高优先级的主题变量对象。深入看的话,primaryColor控制节点背景,lineColor控制连线颜色,textColor统一所有文字颜色。这些变量能覆盖theme里同名设置,做法是在不切换主题的前提下微调风格。但要小心:themeVariables里的变量名和 CSS 里的类名是两套体系,你改了primaryColor不会影响已经渲染出来的 SVG——它只对新渲染生效。所以每次改完配置后必须重新调用render。
flowchart.htmlLabels: true是另一个关键开关——它决定流程图的连线标签是用 HTML 渲染还是纯 SVG 文本。true时支持<br>换行和更丰富的样式,但渲染性能稍差;false时所有标签都是纯文本,性能好但样式受限。如果你的图节点超过 200 个,我建议把htmlLabels关掉,性能差距能拉开一倍。
再说flowchart.curve: 'basis'——这个参数控制连线是直线还是平滑曲线。默认值是basis(贝塞尔曲线),会让连线看起来柔和。想改成直角折线的话设为linear。我踩过的坑是:在securityLevel: 'strict'下改htmlLabels为true,某些边界情况下标签里的<br>会被过滤掉导致换行失效。排查了半天才发现是安全策略的过滤规则优先级更高——要换行只能换成\n配合纯文本模式。
4. 渲染阻塞与样式翻车的避坑实录:五条高频故障
4.1 节点文字里的特殊字符导致解析失败
现象:节点文本包含括号或引号,比如A[用户(id)],Mermaid 直接报Syntax error,整张图渲染不出来。
原因:Mermaid 的文本解析器把[、]、(、)当作语法边界,文本里出现这些字符会提前终结当前 token 的解析。这不是版本 bug,而是 DSL 语法本身的限制——所有用文本符号定义的图都有这个问题。
解决:文本里的特殊字符一律用实体编码或双引号包裹。常见做法是在拼文本时替换(为(、)为)、#为#。如果文本里需要显示-->这种箭头符号,则必须用-->的 HTML 实体-->,否则也会被当成连线语法。从那以后我凡是处理用户输入的文本,都先过一次转义函数再拼 Mermaid 文本。
4.2 中文乱码问题
现象:SVG 渲染出来了,但所有中文都变成了方框或乱码,在 Windows 服务器上部署后特别明显。
原因:SVG 里的字体由font-family决定,Mermaid 默认使用西文字体栈,没有显式指定中文字体。服务器端渲染时系统字体列表里没有对应中文字体,浏览器回退到默认字体,而某些 Linux 服务器镜像只装了点阵西文字体,中文直接不渲染。
解决:初始化时把fontFamily显式设置为"Microsoft YaHei", "PingFang SC", "Noto Sans CJK SC", sans-serif,并且确保 SVG 嵌入了正确的<style>标签。更彻底的做法是在mermaid.initialize里加themeVariables: { fontFamily: '...' },因为themeVariables的优先级高于顶层fontFamily——这个细节差点让我以为初始化参数失效了。
4.3 渲染超时或页面卡死
现象:图表有几百个节点,点击渲染之后页面直接白屏,控制台报Maximum call stack size exceeded或卡住不动。
原因:Mermaid 的布局计算是同步的,节点数量增长时耗时呈指数级上升。10.6.1 虽然内置了maxTextSize限制(默认 50000 字符),但这个限制只管文本总长度,不管节点数量。三百个节点的图,Dagre 布局计算就可能占用主线程超过 10 秒。
解决:给render调用包一层Promise.race超时控制,比如 5 秒后强制返回错误提示。另一个治本方案是把大图拆分成多个subgraph,让布局器按区块计算而不是全局计算。我在一个网络拓扑图项目里把五百多节点的图拆成五个子图后,渲染时间从 8 秒降到了 1 秒左右——不是平行计算,但每个子图的内部布局复杂度大大降低了,整体体验是质的飞跃。
4.4 异步加载后组件拿不到 Mermaid 实例
现象:页面里动态加载了mermaid.min.js之后,紧接着调用mermaid.render报mermaid is not defined。
原因:<script src="...">是默认的同步加载,但它只保证脚本执行完毕,不保证脚本里的模块初始化完成。Mermaid 的 ESM 版本从加载到挂载全局需要经过模块实例化的过程,这个过程在异步场景是竞态条件——加载完立刻调用,全局变量还没来得及挂上。
解决:使用onload回调或await等待加载完成。我一般会封装一个loadScript(url)函数,返回 Promise,resolve之后再调用initialize和render。如果用了 ES Module 的import,就不会遇到这个问题,因为import天然是异步完成后再执行后续代码。
4.5 主题切换后图表颜色不更新
现象:初始化时用theme: 'dark',后来改成theme: 'forest'再initialize,但页面上已经渲染出来的图颜色没变化。
原因:initialize只影响之后调用的render,对已插入 DOM 的 SVG 没有任何修改能力。SVG 里的颜色是内联属性,外部 CSS 无法覆盖(非!important情况下)。
解决:切换主题后必须重新调用render并替换容器内容。我通常把「当前主题名」存在全局变量里,每次render时读取它传给initialize,并且在切换主题的方法里先清空目标容器再渲染。如果你不想重新渲染,另一个方案是遍历 SVG 里的fill属性做批量替换,但那样性能和优雅程度都差很多。
5. 从流程图扩展到六种图表:时序图、甘特图、类图、状态图与饼图的实战写法
5.1 时序图:sequenceDiagram的参与者与消息语法
流程图解决的是节点关系,时序图解决的是时间顺序——谁先给谁发消息、返回什么结果。Mermaid 的时序图语法非常贴近自然语言,学习成本比流程图还低。
sequenceDiagram participant U as 用户 participant A as 前端 participant B as 后端 U->>A: 提交表单 A->>B: POST /api/submit B-->>A: 200 { code: 0 } A-->>U: 显示成功提示这个示例里participant U as 用户定义参与者并设置显示别名,避免在消息里写冗长的中文名。消息方向有三种写法:->>是实线箭头带横线(同步请求),-->>是虚线箭头(异步回调),->是无箭头实线(内部过程)。前端画接口调用时序图时,我最常用的是->>表示 HTTP 请求,-->>表示返回——因为浏览器 DevTools 里看到的请求/响应就是这么个时间关系。要注意的消息文本里不要带冒号和分号,这两个是时序图的语句分隔符,带了会被截断解析。
5.2 甘特图:gantt的任务依赖与日期格式
项目排期图用甘特图再合适不过。Mermaid 的gantt语法里,section是任务分组的标题,任务名 : 属性列表是具体任务行。
gantt title 产品迭代计划 dateFormat YYYY-MM-DD axisFormat %m/%d section 需求阶段 需求评审 :done, a1, 2025-03-01, 3d 原型设计 :active, a2, after a1, 5d section 开发阶段 前端开发 :a3, after a2, 10d 后端开发 :a4, after a2, 12d 联调测试 :a5, after a3 a4, 5ddateFormat YYYY-MM-DD声明日期格式,axisFormat %m/%d是横轴显示格式,相当于日期格式化函数。每个任务的属性是状态, id, 开始时间, 持续时长——其中done表示已完成(深色填充)、active表示进行中(高亮)、crit表示关键路径(红色描边)。after a1表示依赖 a1 完成后开始,a5那行依赖a3 a4两个任务都完成才能开始。这里最容易踩的坑是:任务 ID 不能重复,不然后定义的会覆盖先定义的,导致依赖关系错乱。甘特图用的日期解析库是 dayjs,它对YYYY-MM-DD和YYYY/MM/DD都兼容,但建议全代码库统一用连字符格式。
5.3 类图与状态图:classDiagram的可见性与继承
类图用在技术文档里描述对象模型很直观。classDiagram支持定义属性和方法,也支持继承、实现、关联等关系。
classDiagram class User { +String name +int age +login() bool -password } class Admin { +deleteUser(id) bool } class Permission { +check(user) bool } User <|-- Admin Admin --> Permission : uses+表示 public,-表示 private,#表示 protected,这跟 UML 标准的可见性符号保持一致,不用额外记忆。User <|-- Admin表示 Admin 继承 User,箭头指向父类;Admin --> Permission是依赖关系,带: uses标签说明用途。类图的布局是 Mermaid 里最复杂的之一,类多了之后容易重叠,我的经验是把每个类的方法控制在 5 个以内,否则图会变得非常高,横向阅读体验很差。
状态图stateDiagram-v2适合描述流程的状态流转,核心语法是state 状态名 : 描述和状态1 --> 状态2 : 事件,它还支持[*]表示初始和终止状态。这个图类型和流程图的区别在于它突出了「事件驱动」——每条连线上标的是触发事件而非动作。画状态图时我习惯把每个状态都加注释,最关键的状态用classDef高亮,这样阅读者一眼扫过去就能把握全流程。
5.4 饼图与仪表盘轻量化:pie语法的数据格式
如果你只是想快速展示占比数据,pie是最好的选择——不用引入图表库,一段文本就能变成 SVG 饼图,数据直接从后端接口拼接进来。
pie title 系统运行环境分布 "Linux" : 45 "Windows" : 35 "macOS" : 20标题用title声明,下面每行是"标签" : 数值。数值可以是整数或小数,Mermaid 会自动计算百分比并渲染在扇区上。这个语法唯一的坑是:标签必须用双引号包裹,否则含空格的标签会被拆成多个 token,渲染直接报错。饼图在移动端适配性一般,扇形标签在窄屏上会互相挤,我一般用在 PC 端内部看板上。
6. 把渲染结果导出成 PNG:SVG 转图片的两条成熟路径
Mermaid 渲染出来的 SVG 在页面上展示没问题,但业务方往往需要把图贴到 PPT 或文档里,这就涉及导出图片的功能——给你两条我都实测过的路径。
第一条路径是纯前端:利用XMLSerializer把 SVG 节点序列化成字符串,再用Blob生成下载链接。
function exportSVGToPNG(svgElement, scale = 2) { // 克隆 SVG 节点,避免污染原始 DOM const clone = svgElement.cloneNode(true); // 把克隆节点放到临时容器中,确保样式完整 const tempContainer = document.createElement('div'); tempContainer.appendChild(clone); document.body.appendChild(tempContainer); // 将 SVG 转为 XML 字符串,并生成 Blob const xml = new XMLSerializer().serializeToString(clone); const blob = new Blob([xml], { type: 'image/svg+xml;charset=utf-8' }); const url = URL.createObjectURL(blob); // 用 Image 加载 SVG,再绘制到 Canvas const img = new Image(); img.onload = () => { // scale 控制输出分辨率,2 倍可以保证清晰度 const canvas = document.createElement('canvas'); canvas.width = clone.clientWidth * scale; canvas.height = clone.clientHeight * scale; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, canvas.width, canvas.height); URL.revokeObjectURL(url); document.body.removeChild(tempContainer); // 触发下载 const a = document.createElement('a'); a.download = 'diagram.png'; a.href = canvas.toDataURL('image/png'); a.click(); }; img.src = url; }这条路径的关键在于clone.clientWidth——SVG 的width和height有时是百分比或 auto,直接读拿不到准确尺寸。所以我先把它挂到 DOM 上让浏览器计算布局,拿到实际像素值后传给 Canvas,而不是去猜 SVG 内部坐标。scale参数是用来提高导出分辨率的——Canvas 按scale倍放大,PNG 在 PPT 里放大两倍都不会糊。唯一要注意的是:如果 SVG 里用了外联字体(比如font-family指定了系统字体),Canvas 绘制时可能因为字体没加载完成而导致文字偏移,解决办法是在导出前先document.fonts.ready.then()等字体就绪。
第二条路径是用浏览器原生的右键另存为——但有个细节:Mermaid 渲染出的 SVG 默认没有xmlns命名空间,直接复制代码到独立 SVG 文件粘贴,图片会显示空白。我在导出时都会用XMLSerializer重新序列化,它会自动补上xmlns属性,这样生成的文件在任何图片查看器里都能正常打开。
自己实际操作下来,我养成的习惯是:在初始化配置里就把securityLevel设成strict,把所有动态输入的文本强制过一遍转义函数,然后每次改主题后都强制重新渲染一遍整页的图表。这些动作多花不了几分钟,但能让你避开前面那一整章提到的坑。希望这一篇的细节拆解能帮你把 Mermaid 落地得更顺。
本文还有配套的精品资源,点击获取