重构预览我们为了避免受页面全局样式影响,是在iframe中做的预览,而且是直接把html字符串传给iframe的“srcdoc”来控制内容。如果想要给它添加样式,要么在iframe中引入样式文件,要么动态添加style标签。虽然这都可以通过拼接html字符串的方式来实现,但这样太难维护,而且不易扩展。所以我想把iframe中的内容做成一个独立的页面,页面向外暴露一些方法,用于更新页面内容与添加样式。
首先新建文件src/previewer/index.ts,作为预览页面的入口文件,先简单的写个console.log用于等下验证是否生效
console.log(‘这是预览器的内部页面’)
然后修改webpack.config.js,在entry中添加previewer,并在plugin中为其添加个html模板
// ... entry: { app: ['./src/index.tsx'], // 预览器页面入口 previewer: ['./src/previewer/index.ts'], }, // ... plugins: [ // ... new HtmlWebPackPlugin({ title: 'Markdown Previewer', chunks: ['previewer'], filename: 'previewer.html', }), // ... ], // ...
然后修改预览组件src/components/previewer.tsx中iframe的src
return ( <iframe title="previewer" src="previewer.html" className="w-full h-full" /> )
重启工程,在控制台看到了预览器内部页面的输出内容
说明入口正常,可以进一步做功能了。
先来实现更新内容,即要能够插入Markdown转换成的html内容。我们先在页面中插入一个div元素,用作整个文档的容器,修改src/previewer/index.ts
// 创建文档容器 const docWrapper = document.createElement('div') docWrapper.setAttribute('id', 'doc-content') // 将容器插入body document.body.appendChild(docWrapper)
然后向外暴露一个更新内容的方法
// 避免ts检查错误 const wd = window as any // 对外暴露更新 wd.updateDoc = (html: string) => { docWrapper.innerHTML = html }
再次修改组件src/components/previewer.tsx,调用更新文档内容的方法
// .. // iframe引用 const iframeRef = useRef<HTMLIFrameElement>(null) // 文档内容变化时更新 useEffect(() => { if (iframeRef.current) { const wd = iframeRef.current.contentWindow as any if (wd && wd.updateDoc) { wd.updateDoc(marked(doc)) } } }, [doc]) // ...
功能正常,重构完成,下面来添加基础样式。
基础样式
我并不擅长文档排版,所以在github上找了个简单的Markdown样式,叫“Markdown-CSS”。我们先用它实现基本功能,不用考虑样式是否美观。从该项目的描述发现它并没有发布到npm,所以直接下载文件,将“Markdown.css”放到我们项目的src/previewer目录下。修改src/previewer/index.ts,引入Markdown.css
import './markdown.css'
效果
基础样式已经添加上了,但基础样式中的代码只是普通的文本,没有任何高亮。
代码高亮
marked不直接支持代码高亮,但提供了相关配置。我们使用highlight.js来格式化代码,先安装highlight.js
yarn add highlight.js
然后在组件previewer.tsx中配置marked
// ... import hljs from 'highlight.js' // ... // 设置marked选项 marked.setOptions({ langPrefix: 'hljs ', highlight: (code, lang) => { const hlCode = hljs.highlight(lang, code).value return hlCode }, })
然后在预览的页面中引入highlight.js所需的css
import 'highlight.js/styles/atom-one-dark.css'
highlight.js支持很多风格,我们先用atom-one-dark.css实现基本功能,后期改为通过设置动态加载。效果
今天时间有限,先做这些,下期再见