- 多次挂载的自定义组件之间如何通讯?
- 需求的场景描述(希望解决的问题) 我实现一个类似微信消息列表的场景,我通过遍历一个自定义组件得到列表(每个自定义组件就是一个消息提示),左滑显示删除操作按钮。 问题是我左滑出来第二个时没办法隐藏上一个 - 希望提供的能力 希望实现单一选择 [代码]<swipe-[代码][代码]delete[代码][代码] [代码][代码]wx:[代码][代码]for[代码][代码]=[代码][代码]"{{list}}"[代码][代码] [代码][代码]wx:key=[代码][代码]"{{index}}"[代码][代码] [代码][代码]index=[代码][代码]"{{index}}"[代码][代码] [代码][代码]active=[代码][代码]"{{swipeDeleteActive}}"[代码][代码] [代码][代码]bind:deleteItem=[代码][代码]"deleteItem"[代码][代码] [代码][代码]bind:touchStart=[代码][代码]"touchStart"[代码][代码]>[代码][代码] [代码][代码]<navigator url=[代码][代码]"{{item.url}}"[代码][代码]>[代码][代码] [代码][代码]{...}[代码][代码] [代码][代码]</navigator>[代码][代码]</swipe-[代码][代码]delete[代码][代码]>[代码] 我能想到的解决办法:组件内部获取key后去比对传入的索引,符合的按照active正常处理,不符合的判定不是当前项,这个我还没试 ( 因为不知道如何获取key ;- (
2019-06-30 - Comi - 小程序 markdown 渲染和代码高亮解决方案
写在前面 Comi 读 ['kəʊmɪ],类似中文 科米,是腾讯 Omi 团队开发的小程序代码高亮和 markdown 渲染组件。有了这个组件加持,小程序技术社区可以开始搞起来了。 体验 [图片] 感谢【小程序•云开发】提供技术支持。 预览 [图片] Comi 基于下面的 5 个组件进行开发: prismjs wxParse remarkable html2json htmlparser 先看 Comi 使用,再分析原理。 使用 先拷贝 此目录 到你的项目。 js: [代码]const comi = require('../../comi/comi.js'); Page({ onLoad: function () { comi(`你要渲染的 md!`, this) } }) [代码] wxml: [代码]<include src="../../comi/comi.wxml" /> [代码] wxss: [代码]@import "../../comi/comi.wxss"; [代码] 简单把! 在 omip 中使用 先拷贝 此目录 到你的项目。 js: [代码]import { WeElement, define } from 'omi' import './index.css' import comi from '../../components/comi/comi' define('page-index', class extends WeElement { install() { comi(`你要渲染的 md`, this.$scope) } render() { return ( <view> <include src="../../components/comi/comi.wxml" /> </view> ) } }) [代码] WeElement 里的 this 并不是小程序里的 this,需要使用 [代码]this.$scope[代码] 访问小程序 Page或 Component 的 this。 css: [代码]@import '../../components/comi/comi.wxss'; [代码] 原理 在开发 Comi 之前,我们进行了预研,是否有必要造这个轮子。 代码高亮预研 wxParse 只是用标签包括代码,并未处理代码转成 WXML,所以渲染出的代码是没有颜色 老牌的 highlightjs 没有 WXML 对应的方案 老牌的 highlightjs 对 JSX 高亮支持太差 prismjs 是 react 官方使用的高亮插件,对 JSX 支持高亮很好 prismjs 支持几乎所有的语言,并且支持自定义扩展语言 prismjs 拥有 Line Highlight 插件(目前还未移植到 Comi) 综合上面信息,决定基于 prismjs 二次开发。 markdown 渲染预研 wxParse 老牌的渲染组件,支持 markdown wxParse 内置的 showdownjs 不满足代码高亮的格式需求(比如语言种类也会生成一个标签,当然可以通过 wxss 隐藏) 小程序基础库 1.4.0 开始支持 [代码]rich-text[代码] 组件展示富文本,但是格式需要转成 json 高性能 remarkable,Facebook 和 Docusaurus 都在使用,支持 md 语法修改和扩展 [代码]<rich-text nodes="{{nodes}}" bindtap="tap"></rich-text> [代码] [代码]Page({ data: { nodes: [{ name: 'div', attrs: { class: 'div_class', style: 'line-height: 60px; color: red;' }, children: [{ type: 'text', text: 'Hello World!' }] }] }, tap() { console.log('tap') } }) [代码] 综合上面信息,放弃 rich-text,决定基于 wxParse + remarkable 二次开发,移除 showdownjs。Comi 需要 remarkable 的高性能和灵活性。markdown 会持久化存在 db, 在小程序内运行时转换成 wxml,所以对性能还是有一定要求。 劫持 prismjs tokens [代码]tokens: function(text, grammar, language) { var env = { code: text, grammar: grammar, language: language }; _.hooks.run('before-tokenize', env); env.tokens = _.tokenize(env.code, env.grammar); _.hooks.run('after-tokenize', env); for (var i = 0, len = env.tokens.length; i < len; i++) { var v = env.tokens[i] if (Object.prototype.toString.call(v.content) === '[object Array]') { v.deep = true this._walkContent(v.content) } } return env.tokens }, [代码] [图片] 这段代码增加 tokens 方法到 prismjs 中,原库自带的 prism.highlight 的会把 tokens 转成 html,因为我们的目标的 wxml,所以这里提前把 tokens 作为方法返回值。当然还做了一件事,就是扩展了 token item 的 deep 属性来决定是否需要继续向下遍历生成 wxml。 原始的 jsx: [代码]render() { const { tks } = this.data return ( <view class='pre language-jsx'> <view class='code'> {tks.map(tk => { return tk.deep ? <text class={'token ' + tk.type}>{ tk.content.map(stk => { return stk.deep ? stk.content.map(sstk => { return <text class={'token ' + sstk.type}>{sstk.content || sstk}</text> }) : <text class={'token ' + stk.type}>{stk.content || stk}</text> })}</text> : <text class={'token ' + tk.type}>{tk.content || tk}</text> })} </view> </view> ) } [代码] jsx 编译出生成的 wxml,把这段 wxml 嵌入到 wxparse 里: [代码]<!-- 千万 不要格式化下面的 wxml,不然 text 嵌套 text 导致换行全部出来了 --> <template name="wxParseCode"> <view class="pre language-jsx"> <view class="code"> <block wx:for="{{item.tks}}" wx:for-item="tk"> <block wx:if="{{tk.deep}}"><text class="{{'token ' + tk.type}}"><block wx:for="{{tk.content}}" wx:for-item="stk"><block wx:if="{{stk.deep}}"><text class="{{'token ' + sstk.type}}" wx:for="{{stk.content}}" wx:for-item="sstk">{{sstk.content || sstk}}</text> </block> <block wx:else><text class="{{'token ' + stk.type}}">{{stk.content || stk}}</text> </block> </block> </text> </block> <block wx:else><text class="{{'token ' + tk.type}}">{{tk.content || tk}}</text> </block> </block> </view> </view> </template> [代码] 这段 wxml 不能进行格式化美化,不然多出许多换行符,因为 text 嵌套 text 会保留换行符!! 修改 wxparse 里的分支逻辑: [代码]<block wx:elif="{{item.tagType == 'block'}}"> <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}"> <block wx:if="{{item.tag == 'pre'}}"> <template is="wxParseCode" data="{{item}}" /> </block> <block wx:elif="{{item.tag != 'pre'}}" > <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key=""> <template is="wxParse1" data="{{item}}" /> </block> </block> </view> </block> [代码] 当 [代码]item.tag[代码] 为 [代码]pre[代码] 的时候使用 wxParseCode 模板,数据传入 item。item 的数据从哪里来? 先修改 md 渲染器为 Remarkable: [代码]} else if (type == 'md' || type == 'markdown') { var converter = new Remarkable() var html = converter.render(data) transData = HtmlToJson.html2json(html, bindName); } [代码] 使用上面的 prism.tokens 计算出代码片段的 tokens,用于 wxparse 的模板渲染: [代码]function transPre(transData) { transData.nodes.forEach((node, index) => { if (node.tag == 'pre') { var lan = 'markup' if (node.nodes[0].classStr) { lan = node.nodes[0].classStr.split(' ')[0].replace('language-', '') } var tks = prism.tokens(node.nodes[0].nodes[0].text, prism.languages[lan], lan) transData.nodes[index].tks = tks } }) } [代码] language- 支持多少种呢?目前 comi 默认支持: markup css clike javascript bash json typescript jsx tsx 默认使用的主题 css 是 okaidia。如果 comi 默认的配置不支持你的需求,你可以: 进 https://prismjs.com/download.html 这里自行下载 劫持 prismjs tokens 拷贝进你下载的 prismjs 里 把 prismjs 拷贝替换掉 comi 自带的 prismjs 精简 comi 使用流程 WXML 提供两种文件引用方式 import 和 include。和 import 不同,include 可以将目标文件除了 template 和 wxs 外的整个代码引入,相当于是拷贝到 include 位置,如: [代码]<!-- index.wxml --> <include src="header.wxml" /> <view>body</view> <include src="footer.wxml" /> [代码] [代码]<!-- header.wxml --> <view>header</view> [代码] [代码]<!-- footer.wxml --> <view>footer</view> [代码] comi 利用了 import 和 include 特性简化使用流程: comi.wxml [代码]<import src="./wxParse.wxml"/> <template is="wxParse" data="{{wxParseData:article.nodes}}"/> [代码] comi.js [代码]var WxParse = require('./wxParse.js'); module.exports = function comi(md, scope) { WxParse.wxParse('article', 'md', md, scope, 5); } [代码] comi.wxss [代码]@import './wxParse.wxss'; @import './prism.wxss'; [代码] 使用时,只需要 : import [代码]comi.js[代码] include [代码]comi.wxml[代码] import [代码]comi.wxss[代码] 另外,在 omip 使用 comi 时候发现不会拷贝 include 的文件到 dist,发现 taro/omip 的正则没有去匹配 include 文件,所以,把: [代码]exports.REG_WXML_IMPORT = /<[import](.*)?src=(?:(?:'([^']*)')|(?:"([^"]*)"))/gi [代码] 改成: [代码]exports.REG_WXML_IMPORT = /<[import|inculde](.*)?src=(?:(?:'([^']*)')|(?:"([^"]*)"))/gi [代码] 搞定。 开始使用吧 Github Powered by Omi Team
2019-04-09