蜗牛读书上能方便快捷

最近有需求我们的产品在PC端做一个面向用户的书评编辑器,让用户和编辑可以方便快捷的在蜗牛阅读上编辑和制作一些高质量的文章。主要困难在于富文本编辑器部分。

虽然这是业务需求,但是做业务的时候需要考虑到技术,所以在和需求商量是否支持IE8之后,我们决定使用Vue作为前端部分的技术架构。

前端架构

webpack 配置

Vue 是一个非常好的前端 MVVM 框架,轻量级、快速、文档友好且详细,而且代码组织也非常优雅,是我首选的 MVVM 架构。 Vue官方提供了一个非常方便快速上手的脚手架Vue-cli,但是因为不适合我们这里使用的Java Web架构,所以我没有使用,不过我也做了一个关于Vue-cli的工作。经过详细研究,我构建了自己的 webpack 配置。

以下是我生产环境的部分webpack配置,不复杂,因为我的业务场景不复杂,各种插件的功能也足够强大。

webpack.prod.config.js

devtool: ‘source-map’, 插件: [new CleanWebpackPlugin([‘dist’]),new ExtractTextPlugin(‘[name].css’),new webpack.DefinePlugin({‘process.env’: {NODE_ENV : ‘”production”‘}}),new webpack.optimize.CommonsChunkPlugin({name: ‘vendor’,minChunks: function(module, count) {return (module.resource &&/.js$/.test(module.resource ) &&module.resource.indexOf(‘node_modules’) >= 0)}}),new webpack.optimize.CommonsChunkPlugin({name: ‘manifest’,filename: ‘manifest.js’,chunks: [‘vendor’ ]}),new webpack.optimize.UglifyJsPlugin({sourceMap: true,compress: {warnings: false}}), ]

主要借鉴了Vue-cli中代码拆分的思路,开发环境的webpack配置差别不大,只是将sourcmap设置改为devtool: ‘#cheap-module-eval-source-map ‘,去掉代码压缩等

需要注意的一点是,我在构建环境的webpack配置中使用了vue-loader自带的postcss预处理器中的cssnano插件来压缩css部分代码,但是这个插件在打包的时候会要将 z-index: 10 压缩成 z-index: 1,需要添加设置 zindex: false 来避免这个问题,而 cssnano 插件的默认功能是删除未使用的 css 部分,例如我们需要 CSS3 动画构建的关键帧也被cssnano认为是未使用的CSS,在压缩过程中也会被删除。这有点混乱,所以为了避免这种情况,我们需要增加discar dUnused: false的设置:

webpack.prod.config.js

rules: [{test: /.vue$/,loader: ‘vue-loader’,options: {loaders: {css: ExtractTextPlugin.extract({use: ‘css-loader’,fallback: ‘vue-style -loader’}),scss: ExtractTextPlugin.extract({use: [‘css-loader’,’sass-loader’] ,fallback: ‘vue-style-loader’})},postcss: [require(‘autoprefixer’ )({browsers: [‘> 1%’]}),require(‘cssnano’)({zindex: false,discardUnused : false})],} }]

与 Java Web 结合

为了提取css文件,我在开发环境中没有使用Hot Module Reload机制(使用After ExtractTextPlugin提取css文件,修改后的css样式不能通过HMR自动更新,需要刷新手动)。

除了我们部门Java Web上的一些简单的静态活动页面外,主页面的托管页面会配置在另一个存放freeMarker的ftl文件的文件夹中,与静态文件的存放不同位置,这是部门Java Web一直使用的文件结构,不用改。

这使得Vue-cli或者一些常见的webpack配置根据文件hash生成打包文件然后使用html-webpack-plugin自动注入承载页面的功能不容易实现,需要结合部门自己情况的定制更符合自己项目的打包流程。

我们有一个网站应用自动部署平台。它的功能不仅是解析和编译后端工程代码,还会自动分析页面引用的静态资源,然后将资源的URL替换为对应的CDN域名。并添加与资源MD5值相关的查询值后缀。比如/static/js/app.js自动部署后会变成//yuedust.yuedu.126.net/snail_st/static/js /app.js?a63ed8a8。

所以由于目前的项目已经具备了CDN域名替换和文件hash计算的功能,所以我在webpack打包上就不需要多做,也可以利用这个特性,固定设置携带的URL页面引用的静态资源,部分代码如下:

index.ftl

这样设置后,无论是本地开发还是在线部署,都不需要修改ftl文件的内容,不仅有效利用了Code Split的优势,加快打包速度,缓存利用率高,同时也简化了开发和部署。一旦添加了页面引用的静态资源,就不需要改变路径了。

当然,这只是结合自己项目的Java Web项目结构和特点的一套webpack使用方法,仅供参考

开发富文本编辑器的经验教训

由于项目时间比较紧,在页面应用Vue框架的背景下,我自然想将Vue应用到富文本编辑器的开发中,结果发现这并不可行。

富文本中的数据呈现

Vue是数据和展示的双向绑定,可以很方便的将特定格式的数据渲染成对应的html。

但是网页上的富文本编辑器一般都使用元素的contenteditable属性。该属性不能实现双向绑定。如果要实时保存富文本数据,只能监控元素的输入。事件,然后读取元素的innerText然后修改数据,但是一旦数据被修改,就会触发Vue的视图更新,导致你编辑的元素的innerText被重新渲染。一旦元素重新渲染,用户输入会得到光标焦点消失,windows和mac os下输入法的实现有些不同。 mac下输入法输入中文会先将用户输入的拼写填充到输入元素中,导致得到的innerText不准确,所以想用vue的双向数据绑定机制开发富文本部分,并实现实时数据存储,问题多多。

富文本中的不可编辑区域

我们的书评内容的数据结构是一个包含各种项目类型的数组。项目的类型有:文本、图片、书籍和笔记,富文本编辑器需要显示和编辑这些数据。书籍和笔记的数据结构只能添加或删除,不能修改。这不同于传统的富文本编辑器,即富文本编辑器。区域需要插入或删除无法修改的元素。这一要求使普通的富文本编辑器变得特别。一开始我的想法是在contenteditable=”true”的编辑器主体中插入一个contenteditable=”false”的dom结构,导致插入部分的文本不兼容编辑器有很好的交互,包括删除、撤消、选择等,终于找到了另一个理想的解决方案。

开发富文本编辑器的一些经验

以下是针对该业务场景开发富文本编辑器的一些经验:

在开源富文本编辑器的基础上开发

知乎上有一个问题,为什么说富文本编辑器是天坑? ,里面提到了开发富文本编辑器遇到的很多困难,我的第一个版本也是想着从头开始,但是确实遇到了很多意想不到的问题,最后修修补补的结果还是不尽如人意。 .

所以如果你需要一个功能正常的富文本编辑器,尽量选择成熟稳定的开源项目,保证稳定性和可靠性。如果像我这样需要开发满足特定业务场景的富文本编辑器,也可以尝试在开源项目的基础上进行二次开发。虽然会有一些代码冗余,但是可以帮你避免很多前人踩过的坑,阅读这些项目的源码也能学到很多东西。被忽视的知识和特征。

我选择了一个名为wangEditor的项目,由中国的个人开发者维护。比较轻量级,源码比较清晰,便于二次开发。

基于DOM的数据渲染

为了实现WEB端的富文本编辑,在我踩过一些坑之后富文本编辑器 base64富文本编辑器 base64,我觉得最终还是会回归到DOM、Vue或者其他MVVM框架,确实给开发和维护带来了很多遍历,但是在富文本编辑领域,仍然没有 DOM API 可以控制。我的解决方案是根据服务器提供的书评的条目,组织相应的HTML,然后交给富文本编辑器进行初始化。

基于浏览器的document.execCommand API开发

当一个 HTML 文档处于设计模式(designMode)或者一个 HTML 元素设置了 contentEditable=”true” 时,我们可以使用 execCommand 方法运行一些命令来操作可编辑区域的内容。该 API 可以快速可靠地对富文本区域中选择的内容进行一系列操作。最重要的是它支持撤消和重做功能,并且在撤消和重做过程中能够完美地保持选择的状态是非常重要的。我们可以通过保存html来撤消和重做内容,但是选择或光标的撤消和重做很难用Javascript完美控制。仅保存上一个选择的范围对象并不能恢复选择或光标。

具体支持的 API,请参考 MDN 文档。

即使对于文档中不支持的一些API,也建议使用以上API组合实现。比如要替换一段HTML内容,首先应该通过Javascript创建一个对应的选择区域,然后运行delete命令删除该内容。 ,然后通过insertHTML插入需要的HTML,从而充分利用浏览器的撤消和重做功能,并与其他操作串联。

富文本中的换行

富文本编辑器中的换行是一个值得注意的问题,我在开发书评编辑器时遇到了一些问题:

在富文本中显示换行符似乎很容易。有几种解决方案,比如设置CSS空白和匹配换行符,或者添加

元素到DOM,似乎都可以达到目的。但是书评编辑器的特殊之处在于,这是一个已经制定好的数据结构,并且客户端也有一个编辑器,这涉及到Web、iOS、Andorid三个端的一致性。

因为没有概念

在客户端,客户端编辑器换行位置需要插入的回车符都是n,而这些换行符在WEB中如果需要显示为换行符,则需要将空白设置为 pre 或 pre-line

如果设置为white-space:pre;,确实可以显示文字换行,但是如果是这样一段数据:

这是书评中的一段文本数据。其中有两个换行符,也就是说应该分三行显示,包括一个空行。显示效果如下:

如果要在DOM节点中显示这样的数据,设置为white-space:pre;, line break虽然保留,因为第一行数据是连续的,white-space:pre;保持数据行原样换行,导致第一行超过 DOM 的最大宽度。这种方法显然行不通。

如果设置为 white-space: pre-line,pre-line 可以正确显示换行符并自动将超过一行的文本换行到下一行,看起来很完美。但是,一旦在换行符之后输入文本(例如中间的空行),问题就会再次出现。在white-space:pre-line的元素中,如果在换行符后面输入文本,则换行符会被删除,文本会跳转到上一行继续显示,这显然是不能接受的。

最后的解决办法是插入

元素来实现换行。通过

实现的换行

不会导致输入文本换行失败的问题,也不会父元素需要设置white-space:pre;,所以我们需要将客户端插入文本中的n转换成

,最后当HTML结构重新解析成书评数据时,需要再次转换回来,保证客户端编辑和显示的一致性。当然,中间还有一系列的转换逻辑,包括兼容客户端老版本编辑器的一些bug。最后,实现一致性是浪费时间。

富文本中的不可编辑区域

如上两张图所示,我们书评的一部分是用户引用的一部分内容。一本书,或者用户阅读时记录的书的原文,这些数据结构是不能修改的,只能插入或删除。一开始我的想法是把这部分的DOM结构设置成contenteditable=”false”,但是这样的设置代码还不够好弥补体验。

后来我改变了主意,因为这是一个不能编辑只能查看的DOM,而插入富文本编辑器中的图片可以和文本一起很好地操作和维护。 ,那为什么不直接把不可编辑的显示区转成图片插入到富文本区呢,原来这个思路最终体验还是很不错的,除了一个小技术问题,下面会讲解.

将 DOM 转换为图像

将 DOM 转换为图像,社区中已经有很多成熟的开源库可以使用。比如我使用dom-to-image,有一个问题需要注意:DOM转换成图片,而canvas的toDataUrl()函数基本就是用来把图片转换成base64编码的URL。有一个安全策略,就是如果画布中绘制的DOM结构在有图片,并且图片与当前页面的域名不同(这在我们的开发场景中很常见)。由于安全策略的限制,此时浏览器是不允许调用canvas的toDataUrl()方法的,我们的书卡中肯定有书的封面。封面的域名是我们的CDN域名,所以转换成图片是有限的。

为了解决这个问题,涉及到一个前端的IMG标签属性:crossOrigin,如果这个属性设置为anonymous,浏览器会在请求这张图片的Request Headers中附上Origin是这行信息当前域名,告诉静态资源服务器图片所在的位置,我需要跨域访问和我的这个图片的域名,请在图片的Response Headers中附加Access-Control-Allow-Methods和Access-Control -Allow-Origin 两行信息,如下图:

请求的图片渲染成canvas,浏览器不会限制canvas转base64的URL。

此功能需要服务器的支持,有些服务器即使附加了Request Headers字段也不会返回想要的Response。

但是在支持该功能的服务器端,有时设置了crossOrigin=”anonymous”,仍然显示错误。不是这个属性不生效,而是我们的图片一般都存储在CDN上。为了更快的返回用户的请求,CDN会缓存图片的响应,而这些缓存的响应显然没有Access-Control-Allow-Methods和Access-Control-Allow-Origin这两行信息,所以在这一次,即使我们认为我们的请求中包含crossOrigin=”anonymous”,CDN服务器也不会认为这是一个不同的请求,所以返回给我们的响应是之前缓存的,从而导致了这个问题的发生。

在这种情况下,我们需要在我们请求的图片 URL 中添加一个时间戳,以避免被 CDN 服务器缓存。

避免使用CDN来提高渲染速度

在前端开发中,提到提高页面加载速度,一般都会提到最大化利用CDN缓存静态资源,提高静态资源的性能。访问速度,让网页内容可以更快地呈现给用户。

但是,在将包含跨域CDN图像的DOM节点渲染成图像的情况下,从CDN代理节点请求图像资源会比直接请求静态资源源站点更昂贵。其实这也很容易理解:

为了使用 HTML5“canvasAPI 将包含跨域 CDN 图像的 DOM 渲染成图像,我们需要为图像添加 crossOrigin=”anonymous” 属性。并在图片的请求 URL 中添加时间戳

如果我们访问CDN域名下的图片,并在URL中添加新的时间戳,那么对该图片资源的请求对于CDN代理节点是无效的。必须是全新的,也就是说它会认为这个节点上没有这个资源的缓存。

当CDN代理节点遇到未缓存的资源时,会从静态资源的源站请求。得到结果后再转发给用户,这相当于说我们请求一个带时间戳的图片URL不仅没有使用CDN的缓存加速,而是CDN代理节点充当了中介,显然会增加资源的回报。耗时

上面两张图分别是请求CDN域名图片和请求源站图片的耗时经过多次测试可以发现请求CDN域名图片的耗时基本都在200ms以上,而对源站的请求基本在100ms以内。因此,有时候,比如这种特殊情况,请求 CDN 域名下的资源可能会增加请求时间。

承诺很好

根据上面的流程,我需要将一个包含各种类型物品的数组从服务器解析成一个HTML字符串,其中包含由于书籍和笔记类型的物品需要转换成base64格式的图片,有时间问题:

text和picture类型的item可以直接得到对应的html字符串,而books和note-type的item,需要通过网络请求和canvas进行转换,但最后我需要得到整个初始HTML内容初始化富文本编辑器,然后让用户去这些HTML DOM节点进行编辑,这是需要用到的Promise.all API。代码示例如下:

App.vue

/* 将服务器返回的书评项目转换为html字符串,并传递给富文本编辑器 @param {json array} items 书评项目 @return {promise} 处理完所有项目后返回resolve(htmlStr),否则reject(错误) */ convertItemsToHtml(items){return new Promise ( (resolve, reject) => {let htmlStr = ”;let itemStr = ”;let itemPromises = items.map( item => {return new Promise ( (解决, 拒绝) => {switch(item.resourceType){case ‘Text’:itemStr =

“文本”>${item.text}

;resolve(itemStr);break;…case ‘BookNote’:let $BookNoteEle = $(

${item.bookNote.markText}

).appendTo($(‘body’));domtoimage.toPng($BookNoteEle[0], {style: {opacity: 1, zIndex: 1} }).then(function (dataUrl) {itemStr =

“读书笔记”>

“BookNote” >’${escape(JSON.stringify(item))}’ src=”${dataUrl}”>

;$BookNoteEle.remove();resolve(itemStr); }).catch(function (error) {console.error(‘图像生成失败’, error);reject(error);});break;}})})Promise.all(itemPromises).then( ([. ..itemStrs]) => {htmlStr = itemStrs.reduce( (acc, val) => {return acc + val}, ”);resolve(htmlStr);}).catch( (error) => {reject(错误);})}) },

使用 Promise.all 和其他一些 ES6 特性可以使我们的代码更加强大和简洁。

以上是我针对特定业务需求开发富文本编辑器时遇到的一些问题和经验。可能有一些错误,希望能帮忙改正。富文本编辑中遇到的其他一些常见问题,可以通过研究一些开源成熟的富文本编辑器项目来解答。

免费获取验证码、内容安全、短信发送、直播点播体验套餐和云服务器套餐

更多网易技术、产品和运营经验分享,请点击这里。

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论