SegmentFault 前端最新的文章
2021-05-08T16:10:34+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
css - overflow-wrap、word-wrap、word-break、white-space 使用总结
https://segmentfault.com/a/1190000039966613
2021-05-08T16:10:34+08:00
2021-05-08T16:10:34+08:00
技术四毛喵
https://segmentfault.com/u/only1zhuo
0
<h2>示例</h2><p>除了特别说明外,文章展示效果均以以下示例作为基底说明</p><pre><code><style>
div {
width: 80px;
background: yellowgreen;
}
</style>
<div>
nice ojbkkkkkkkk aa
</div></code></pre><p>效果:<br><img src="/img/bVcRQPe" alt="" title=""><br>可以看到在默认情况下,有两个比较需要注意的地方<br>1、在一行内,当最后一个单词无法完整放下,这个单词会放到下一行去<br>2、在一行内,如果只有一个单词,且该单词的长度已经超过容器的长度,单词末尾部分的字母只会溢出容器,而不会放到下一行</p><p>这两种现象有时候并不是我们想要的,特别是第二种现象</p><h2>overflow-wrap、word-wrap</h2><p>两者效果等同,只不过 overflow-wrap 是 CSS3 里的属性,用来取代 word-wrap,但在开发时,为了兼容性,应该都写上两者。文章均以 overflow-wrap 为代表说明其作用。</p><table><thead><tr><th>值</th><th>描述</th></tr></thead><tbody><tr><td>normal</td><td>只允许在断字点换行,默认值</td></tr><tr><td>break-word</td><td>允许在长单词或 URL 地址内部进行换行</td></tr></tbody></table><p>添加样式 overflow-wrap: break-word</p><pre><code><style>
div {
width: 80px;
background: yellowgreen;
overflow-wrap: break-word;
}
</style></code></pre><p>效果:<br><img src="/img/bVcRQ3j" alt="" title=""><br>1、由于 nice 后面搭配了 ojbk(后面的k省略说明)会让 ojbk 难以完整与 nice 同行,所以 nice 后面直接换行,让 ojbk 显示在下一行。<br>2、ojbk 的宽度本来就大于容器的宽度,后面的 k 只能显示到下一行<br>3、aa 可以完整放到第三行</p><p>得到结论:<br>overflow-wrap: break-word 的作用<br>1、若行内的最后的一个单词如果无法完整放下,则直接将整个单词放到下一行<br>2、若这个单词的宽度大于容器的宽度,则将多余的字母放到下一行</p><h2>word-break</h2><table><thead><tr><th>值</th><th>描述</th></tr></thead><tbody><tr><td>normal</td><td>使用浏览器默认的换行规则。</td></tr><tr><td>break-all</td><td>允许在单词内换行。</td></tr><tr><td>keep-all</td><td>只能在半角空格或连字符处换行。</td></tr></tbody></table><p>添加样式 word-break: break-all</p><pre><code><style>
div {
width: 80px;
background: yellowgreen;
overflow-wrap: break-word; // 这个可不写
word-break: break-all;
}
</style></code></pre><p>效果:<br><img src="/img/bVcRQ5W" alt="" title=""><br>可以看到 nice 和 ojbk 是同行,而 ojbk 多出的字母部分放到第二行<br>得到结论:<br>word-break: break-all 的作用<br>1、若行内的最后一个单词无法完整放下,则多余的字母放到下一行</p><p>当 word-break 值为 keep-all<br>不允许CJK(Chinese/Japanese/Korean)文本中的单词换行,只能在半角空格或连字符处换行。<br>由于这里要更换 CJK 语言,因此用新例子展示效果</p><pre><code><style>
div {
width: 80px;
background: yellowgreen;
}
</style>
<div>
我你他,我你他
</div></code></pre><p><img src="/img/bVcRQ7j" alt="" title=""><br>可以看到在默认情况下,就有 word-break: break-all 的类似效果,实际上 word-break: break-all 和 overflow-wrap: break-word 是针对非 CJK 语言,word-break: keep-all 是针对 CJK 语言。</p><p>添加样式 word-break: keep-all</p><pre><code><style>
div {
width: 80px;
background: yellowgreen;
word-break: keep-all;
}
</style></code></pre><p><img src="/img/bVcRQ7H" alt="" title=""><br>逗号后面直接换行,第二个的“我你他”被放到了第二行<br>得到结论:<br>break-word: keep-all 的作用<br>1、断句的标准是根据标点符号来进行,效果与 overflow-wrap: break-word 类似,只是针对的语言不同</p><h2>overflow-wrap 和 word-break 的优先级</h2><p>虽然在标准上的定义,overflow-wrap 是控制是否允许断句,而 word-break 是控制怎样断句,但上述例子很明显,overflow-wrap 也有断句作用。</p><p>那么两者的优先级是怎样的?我们先看下以下实践情况。<br>1、在 word-break 非 break-all 的情况下,overflow-wrap: break-word 才会起到自身的断句作用。<br>2、在 word-break 为 break-all 的情况下,overflow-wrap: break-word 不会起到自身的断句作用。<br>3、word-break: keep-all 只针对 CJK 语言,不在讨论范围内<br>也即是说针对非 CJK 语言, word-break 的断句作用优先级大于 overflow-wrap</p><h2>white-space</h2><p>white-space 用于控制设置如何处理元素内的空白</p><table><thead><tr><th>值</th><th>描述</th></tr></thead><tbody><tr><td>normal</td><td>默认。空白会被浏览器忽略。</td></tr><tr><td>pre</td><td>空白会被浏览器保留。其行为方式类似 HTML 中的 <pre> 标签。</td></tr><tr><td>nowrap</td><td>文本不会换行,文本会在在同一行上继续,直到遇到 <br> 标签为止。</td></tr><tr><td>pre-wrap</td><td>保留空白符序列,但是正常地进行换行。</td></tr><tr><td>pre-line</td><td>合并空白符序列,但是保留换行符。</td></tr><tr><td>inherit</td><td>规定应该从父元素继承 white-space 属性的值。</td></tr></tbody></table><p>当 white-space 设置了 nowrap,overflow-wrap 和 word-break 就没有任何用处了,因为文本被强制显示为一行了。</p><h2>总结</h2><p>overflow-wrap(word-wrap) 和 word-break 都可以起到断句作用,对于非 CJK 语言的断句,word-break 的优先级更高。</p><p>是不是有人和我一样疑惑,为什么不直接在 word-break 定义一个可以起到 overflow-wrap: break-word 效果的新属性值?overflow-wrap 属性是不是多此一举?相信很多人都被这两个属性搞得头晕眼花。<br>很奇怪的是,明明在标准上,word-break 只有三个属性值,在 chrome 里输入时发现有 break-word 值,而且该效果和 overflow: break-word 效果一样。</p><p><img src="/img/bVcRReq" alt="image.png" title="image.png"></p><p>而且为什么 CJK 语言的断句作用和非 CJK 语言的断句作用放到同一个属性(word-break)里面,若设置了word-break: keep-all,那不就无法同时设置 word-break: break-all ?CJK 语言和非 CJK 语言不应该产生冲突。</p><p>没有深究这两个问题,也许上述的问题都是历史遗留下来。</p><h2>参考</h2><p><a href="https://link.segmentfault.com/?enc=G1mSIi4KkWieTaxHTESSRw%3D%3D.2Xx1ASfD3Vq42jeCRPK6ouCHpmZjaK%2F4naR70Hdv4bJS%2FZ4mYjpJSai6Va3wlSubdTm3TP4edbO4xFJELdYkvg%3D%3D" rel="nofollow">https://www.cnblogs.com/yingz...</a><br><a href="https://link.segmentfault.com/?enc=aPl%2BjHx5fYBt6xZ9L64Qsw%3D%3D.u7C1IM%2FOGb39QC7K%2F0vBwTzwscaWxEA%2FG3Jn2twBbkg%3D" rel="nofollow">https://www.jianshu.com/p/53d</a></p><h2>共同进步</h2><p>欢迎大家点赞、关注、收藏、分享<br>若文章有错漏,请各位在评论区指正<br>ฅ(`・ω・´)ฅ</p>
网页播放器埋坑,看这一篇就搞定!!!
https://segmentfault.com/a/1190000038688060
2020-12-28T10:52:39+08:00
2020-12-28T10:52:39+08:00
技术四毛喵
https://segmentfault.com/u/only1zhuo
2
<p>在座的傻 x</p><p>本喵来了</p><p>今天写的文章一(亿)级棒!</p><p><img src="/img/remote/1460000038688066" alt="" title=""></p><p>没错,今天要说的对象就是“网页播放器”这个逼,这玩意看起来没什么难度,但实际上有几个坑,下面由本帅气无敌喵逐一解说如何解决在开发中可能会遇到的坑。</p><ol><li>无法自动播放</li><li>全屏问题</li><li>手机端播放器自定义控制条无法显示</li></ol><h2>01无法自动播放</h2><p>不就是 video 标签添加一个 autoplay 吗?很遗憾...事情并没有这么简单。</p><p>桌面版的 Safari 在 2017 年的 11 版开始禁止了带有声音的视频自动播放,2018 年 4 月份发布的 Chrome 66 也禁止了,随后各个浏览器都跟随着苹果和谷歌两位大佬的脚本逐一禁止了。</p><p>请注意,是禁止带有声音的网页视频自动播放,因此,自动播放和声音是有奸情!!!</p><p>想要自动播放可以通过以下几种方式解决。</p><ul><li>video 标签添加一个 muted 属性(静音)</li></ul><p>静音的视频是允许自动播放。</p><ul><li>用户在网站上进行了互动</li></ul><p> 这个办法在字面上可能很难理解,举个栗子吧。</p><p>杰瑞鼠打开了浏览器,访问了黄网“TOM and JERRY”的首页(带有视频),在首页点击某个黄涩视频链接,进入到了这个视频详情。</p><p>那么假设首页和这个视频详情的 video 都有 autoplay 这个属性,没有 muted 属性,请问这两个视频是否在网页加载的那一刻起能够自动播放吗?</p><p><img src="/img/remote/1460000038688065" alt="" title=""></p><p>答案是:首页的 video 不能自动播放,但是视频详情里的 video 可以播放(且有声音),因为在同一个会话里,在进入视频详情前,用户已经打开了同站点下的首页,也就是说,用户已经与网站进行了交互,而用户刚进入首页前,是没有访问过同站点下的其他网页,也即是没有任何交互,因此首页的 video 不能自动播放。</p><ul><li>浏览器设置“允许自动播放”</li></ul><p>这种方法就是全面开放自动播放功能,没有“静音”和“交互”的限制了,需要用户自行设置。以 chome 浏览器为例,在地址栏访问 chrome://flags/,搜索“Autoplay”,即可搜索到该设置(不过,高版本的 chrome 已经不是在这里设置了,本喵也找不到设置的地方)。</p><p><img src="/img/remote/1460000038688070" alt="" title=""></p><p> 再以火狐浏览器为例,在选项里找“自动播放”,即可找到。</p><p><img src="/img/remote/1460000038688071" alt="" title=""></p><ul><li>浏览器域名白名单(未证实)</li></ul><p>听说浏览器有个域名白名单的东西,只要将域名填入该白名单,访问相关网站就可以自动播放,但目前为止本喵手上没有足够证据证实各个大厂的 py 交易。</p><p>上述几种自动播放解决方法各有缺点,为了让用户体验好一点,有时候,我们需要检测网页打开时是否支持自动播放,若不支持,则在视频中间显示播放按钮或者出现其他的样式,下面贴一下检测自动播放代码。</p><pre><code>var promise = document.querySelector('video').play()
if (promise !== undefined) {
promise.then(() => {
// 支持自动播放且有声音
}).catch(error => {
// 不支持自动播放且有声音,这个时候可以在视频上面显示一个播放图标,提示用户自己点击播放
})
}</code></pre><p> 但不是所有的浏览器的 video.play() 都支持返回 promise。想了解更多,可以到这个网站看看。</p><p><a href="https://link.segmentfault.com/?enc=bdM%2FvzlHusqPf1wNYqS9fw%3D%3D.HdIppytSKyfHLfHUuxvZq4TExcGqAhI38Ch6atB3x8ZcwO7U2l4VtlUufd94fxGMUlM9Mf4Mc%2BHH%2FKM8ywHC%2BaYRjZDZGsWsX4hyzxzpF%2BM%3D" rel="nofollow">https://developer.mozilla.org...</a></p><h2>02 全屏问题</h2><p>废话不说,先贴代码。</p><pre><code><!-- 这里专门用一个 div 包含 video,这样做是有原因的,下面有说明 -->
<div class="video-box">
<video src="xxx" poster=""></video>
</div>
<script>
// 检测是否全屏(IOS 端无效)
function checkFullScreen() {
var isFull = document.webkitIsFullScreen || document.mozFullScreen ||
document.msFullscreenElement || document.fullscreenElement
if (isFull == null || isFull == undefined) {
isFull = false
}
return isFull
}
// 全屏
function toggleFullScreen() {
var videoBox = document.querySelector('.video-box')
// video 是 video-player 里面的 video 标签,这个用于 ios 端全屏
var video = document.querySelector('.video-box video')
var isFull = checkFullScreen()
//W3C
if (videoBox.requestFullscreen) {
isFull ? document.exitFullscreen() : videoBox.requestFullscreen()
}
//FireFox
else if (videoBox.mozRequestFullScreen) {
isFull ? document.mozCancelFullScreen() : videoBox.mozRequestFullScreen()
}
//Chrome
else if (videoBox.webkitRequestFullScreen) {
isFull ? document.webkitCancelFullScreen() : videoBox.webkitRequestFullScreen()
}
//IE11
else if (videoBox.msRequestFullscreen) {
isFull ? document.msExitFullscreen() : videoBox.msRequestFullscreen()
}
// IOS(特例)
else if (video.webkitEnterFullscreen) {
video.webkitEnterFullscreen()
}
// 这里可以做一些判断,更换缩放图标(对 IOS 端没效果)
if (isFull) {
} else {
}
}
</script></code></pre><p> 上面的全屏代码适用于 pc(Windows和Mac)、移动端(安卓和IOS),主要使用到了 requestFullscreen API,该 API 适用于 PC 和安卓,但很遗憾,IOS 不适用,IOS 端就是个另类,只能用 webkitEnterFullscreen API 进入全屏,而且没有退出全屏的 API,进入全屏后,只能用 iphone 的手势或自带控件进行退出全屏。</p><p>如果你发现 requestFullscreen API 失效,以下网站有说明 requestFullscreen API 失效的原因。</p><p><a href="https://link.segmentfault.com/?enc=pRMPUWMBv68HsABQrfZfsg%3D%3D.J3QKj2zbT9Sgn2qXgc87EHifncerdfTsc%2FHwHNl%2F9lTvuM8sx%2FzlRLT3Yo54%2Fx%2BVnzDFttyymcFa1bAHVqhGjfyDE91RAc27WFh81xnK3LbPcZ6xWGEFnvMfw8s4QKru" rel="nofollow">https://stackoverflow.com/que...</a></p><p>But 事情没有这么简单,在开发过程中依旧会遇到很多问题,下面逐一说明。</p><ul><li>PC 端全屏后无法监听 ESC 键</li></ul><p>在 PC 端里,有时候需要退出全屏后做一些操作,点击退出全屏按钮跑逻辑是没问题的,但是不要忘记,按 ESC 键也是退出全屏操作,也需要进行跑逻辑。而当元素使用了 requestFullscreen API 进行全屏时,不需要写任何代码,ESC 键就会默认用来退出全屏,我们自然会想到用 onkeydown 事件来监听 ESC,虽然 onkeydown 事件是可监听,但是唯独 ESC 键是无法监听。</p><pre><code>document.onkeydown = function () {
if (event.keyCode == 27) {
// 退出全屏
}
}</code></pre><p> 也即是说,上面的代码,按 ESC 是不走的。那咋办?别担心,我们可以利用 onresize 曲线救国,当用户按下 ESC 时,全屏就会失效,windows 的宽高会变小,即会触发 onresize,这个时候再检测是不是非全屏情况,再跑逻辑。</p><pre><code>window.onresize = function () {
if (!checkFullScreen()) {
// 按 esc 退出全屏需要执行的代码
}
}</code></pre><ul><li>IOS端全屏无法自定义控制条</li></ul><p>由于 IOS 端的全屏只能调用 webkitEnterFullscreen 将 video 标签进行全屏(其实就是 IOS 端内置的全屏,注意:是只能针对 video 标签全屏),全屏时是不能显示自定义的控制条。</p><p>But 我们可以用样式写一个“假全屏”的代码。</p><pre><code><script>
// 为了方便,下面用了 jq
// 判断是否是 IOS 端
var isIOS = !(navigator.userAgent.indexOf("Android") > -1) && (/Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent))
function toggleFullscreen() {
if ($('.video-box').hasClass('fullscreen')) {
// 这里设死高度 210 了,可以自己更改
$('.video-box').removeClass('fullscreen').removeClass('transform').width('100%').height(210).css('margin', '0')
} else {
$('.video-box').addClass('fullscreen')
if ($(window).width() < $(window).height() && isIOS) {
$('.video-box').width($(window).height()).height($(window).width()).css('margin', '-' + $(window).width() / 2 + 'px 0 0 -' + $(window).height() / 2 + 'px').addClass('transform')
} else {
$('.video-box').width($(window).width()).height($(window).height()).css('margin', '-' + $(window).height() / 2 + 'px 0 0 -' + $(window).width() / 2 + 'px')
}
}
}
// 重力感应,改变屏幕方向,如果是全屏时,也进行相应的改变
window.onresize = function () {
if ($('.video-box').hasClass('fullscreen')) {
if ($(window).width() < $(window).height() && isIOS) {
$('.video-box').width($(window).height()).height($(window).width()).css('margin', '-' + $(window).width() / 2 + 'px 0 0 -' + $(window).height() / 2 + 'px').addClass('transform')
} else {
$('.video-box').width($(window).width()).height($(window).height()).css('margin', '-' + $(window).height() / 2 + 'px 0 0 -' + $(window).width() / 2 + 'px').removeClass('transform')
}
}
}
</script>
<style>
/* 屏幕旋转的样式 */
.video-box.fullScreen.transform {
transform: rotate(90deg);
-ms-transform: rotate(90deg);
-webkit-transform: rotate(90deg);
-o-transform: rotate(90deg);
-moz-transform: rotate(90deg);
transform-origin: 50%;
-ms-transform-origin: 50%;
-webkit-transform-origin: 50%;
-moz-transform-origin: 50%;
-o-transform-origin: 50%;
}
/* 屏幕改变方向时,播放器控制条,需要固定 */
.video-box.fullScreen.transform .control {
position: fixed;
}
/* 全屏需要更改位置 */
.video-box.fullScreen {
position: fixed;
top: 50%;
left: 50%;
z-index: 10000;
}
</style></code></pre><p>上面的代码也兼容安卓端</p><p> 不过这种方式存在一些体验问题,比如:使用重力感应横屏时,浏览器的地址栏会浮在上面,这个地址栏是无法去掉的。</p><p><img src="/img/remote/1460000038688067" alt="" title=""></p><ul><li>视频全屏时也需要将控制条进行全屏</li></ul><p>估计没有傻 x 产品会用播放器自带的丑陋控制条吧......既然我们要自定义控制条的样式,那就不会在 video 标签添加 controls 属性,而是在 video-box 的 div 下添加一个控制条相关的 div。</p><pre><code><div class="video-box">
<video src="xxx" poster=""></video>
<!-- 控制条 -->
<div class="ctrl-box"></div>
</div></code></pre><p> 那么全屏时,我们不应该只考虑将 video 进行全屏,同时也要将控制条进行全屏,实现很简单,只要将 video-box 这个父类标签进行全屏即可。这也是为什么全屏代码里是将 video-box 进行全屏,而不是单独将 video 全屏,以及为什么要有个父类 div 包住 video。</p><ul><li>iframe 里面的元素全屏失败</li></ul><p>如果 video 标签是在 iframe 里,iframe 必须声明允许全屏。</p><pre><code><iframe src="" allowfullscreen="allowfullscreen" mozallowfullscreen="mozallowfullscreen" msallowfullscreen="msallowfullscreen" oallowfullscreen="oallowfullscreen" webkitallowfullscreen="webkitallowfullscreen"></iframe></code></pre><h2>03 手机端播放器自定义控制条无法显示</h2><p>如今,手机端主要分为两大阵容:安卓和 IOS,在这两个端里,我们自定义的播放器控制条都会出现各种问题,下面一一列举。</p><ul><li>安卓</li></ul><p>在安卓端里,因为比较开放,浏览器可以说是百花齐放,有各种各样的功能,其中,有个对开发者不友好的情况就是某些浏览器(例如:QQ 浏览器)会去掉我们自定义的播放器控制条,改用浏览器内置的默认控制条。</p><p>解决方法如下:</p><p>...</p><p>...</p><p>...</p><p><img src="/img/remote/1460000038688069" alt="" title=""></p><p>无解!!!</p><p>先等等,别急着抓本喵猫尾。这个确实是无解的,有些浏览器厂商就是不愿意开发者可以自定义控制条,你可能会说为什么像虎牙直播这种大厂在手机上的 QQ 浏览器可以使用自定义控制条,那是因为......嗯......他们有 py 交易,没错,就是 py 交易!</p><p>例如:QQ 浏览器就有一个域名白名单的东西,而虎牙直播的域名就在这个白名单里,所以我们访问虎牙直播网站时,可以发现虎牙直播是可以显示自定义控制条。</p><p>不信?你试下在电脑使用 Fiddler 软件在本地做下代理,让手机和电脑连接同一个局域网,配置这个代理,然后再在电脑上做一下 hosts,将虎牙的域名映射到本地 IP,你就可以在手机里的 QQ 浏览器通过虎牙的域名访问到自己的网站,看看是不是自定义控制条显示出来了。</p><p>有关这个问题,本喵还特地在思否提问了。</p><p><a href="https://segmentfault.com/q/1010000037446428">https://segmentfault.com/q/10...</a></p><p>没错,就是无解,所以哪个傻 x 产品问你为什么这个功能无法实现,你就回答他,“你在教我做事?”</p><ul><li>IOS</li></ul><p>大家都清楚 IOS 系统是封闭的,封闭到规定 IOS 端里的所有厂商的浏览器都不能支持 requestFullscreen API,只能支持独有的 webkitEnterFullscreen API,全屏时只能显示苹果的那一套控件,导致全屏是没任何办法显示自定义控制条的,不过在非全屏情况下,是可以显示自定义进度条,当然你也可以用上文那里的“假全屏”代码。</p><p>好了好了</p><p>这期干货就到这里了</p><p>本喵要捉杰瑞鼠了</p><p>溜了溜了</p><p><img src="/img/remote/1460000038688068" alt="" title=""></p>
前端的三种数据绑定技术
https://segmentfault.com/a/1190000038675419
2020-12-27T11:49:02+08:00
2020-12-27T11:49:02+08:00
技术四毛喵
https://segmentfault.com/u/only1zhuo
0
<p>本喵写了以下三种不同方式的数据绑定(只实现了单向绑定):</p><ul><li>第一种,使用了“脏值”检测,该方法是 angular 的数据绑定原理。</li><li>第二种,使用了 es5 的 Object.defineProperty(),vue2 的数据绑定就是基于该方法。</li><li>第三种,使用了 es6 的 Proxy ,vue3 的数据绑定开始全盘换为这种方式。</li></ul><p>废话不多说,直接撸起代码~</p><p><img src="/img/remote/1460000039901037" alt="image.png" title="image.png"></p><h2>01 脏值检测</h2><p>如果绑定的数据过多,脏值检测可能会造成性能问题,因为每次改变值,都需要进行轮询改变对应的值。</p><pre><code><!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>脏值检测</title>
</head>
<body>
<h3>脏值检测</h3>
<button a-click="add" style="width: 40%; height: 50px;">增加</button>
<button a-click="reset" style="width: 40%; height: 50px;">重置</button>
<div>
<span>第一个绑定数据:</span>
<span id="aa" style="color:#CC6600" a-bind="counter"></span>
</div>
<div>
<span>第二个绑定数据:</span>
<span style="color:#CCCC33" a-bind="counter"></span>
</div>
<div>
<span>第三个绑定数据:</span>
<span style="color:#336699" a-bind="counter"></span>
</div>
<script type="text/javascript">
window.onload = function () {
// 首次加载需要初始化数据
apply()
bind()
}
// data
let counter = 0
// methods
function add() {
counter++
}
function reset() {
counter = 0
}
// bind event
function bind() {
let list = document.querySelectorAll("[a-click]")
list.forEach(item => {
item.onclick = function () {
window[item.getAttribute("a-click")]()
apply()
}
})
}
// bind data
function apply() {
let list = document.querySelectorAll("[a-bind='counter']")
list.forEach(item => {
if (item.innerHTML !== counter + '') {
item.innerHTML = counter
}
})
}
</script>
</body>
</html></code></pre><h2>02 Object.defineProperty(ES5)</h2><p> 该方法是目前比较主流的方法,兼容性也不错,支持 ie8(注意:下面并没实现 vue2 的发布订阅者模式,有空再撸一个出来)。</p><pre><code><!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Object.defineProperty</title>
</head>
<body>
<h3>Object.defineProperty(ES5语法)</h3>
<button a-click="add" style="width: 40%; height: 50px;">增加</button>
<button a-click="reset" style="width: 40%; height: 50px;">重置</button>
<div>
<span>第一个绑定数据:</span>
<span style="color:#CC6600" a-bind="counter"></span>
</div>
<div>
<span>第二个绑定数据:</span>
<span style="color:#CCCC33" a-bind="counter"></span>
</div>
<div>
<span>第三个绑定数据:</span>
<span style="color:#336699" a-bind="counter"></span>
</div>
<script type="text/javascript">
window.onload = function () {
// 首次加载需要初始化数据
apply('counter', obj.counter)
bind()
}
// data
let obj = {
_counter: 0
}
// counter 只是一个载体,真正的值存储在 _counter
Object.defineProperty(obj, 'counter', {
get: function () {
//console.log('get:', counter)
return this._counter
},
set: function (val) {
this._counter = val
//console.log('set:', counter)
apply('counter', this._counter)
}
})
// methods
function add() {
obj.counter++
}
function reset() {
obj.counter = 0
}
// bind event
function bind() {
let list = document.querySelectorAll('[a-click]')
list.forEach(item => {
item.onclick = function () {
window[item.getAttribute('a-click')]()
}
})
}
// bind data
function apply(str, val) {
let list = document.querySelectorAll(`[a-bind=${str}]`)
list.forEach(item => {
if (item.innerHTML !== val + '') {
item.innerHTML = val
}
})
}
</script>
</body>
</html></code></pre><h2>03 Proxy(ES6)</h2><p>—</p><p> 相比上面两种方法,用 es6 Proxy 来写数据绑定,代码会直观很多,而且很易用,不过遗憾的是 Proxy 兼容性很差,IE 是全面不支持它,而且 babel 没法完全将它转为 es5 语法,虽然有 google 大佬写的 Polyfill,但那个也是有残缺的(不知道尤大在 vue3 里怎么解决它)。</p><pre><code><!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>proxy</title>
</head>
<body>
<h3>proxy(ES6语法)</h3>
<button a-click="add" style="width: 40%; height: 50px;">增加</button>
<button a-click="reset" style="width: 40%; height: 50px;">重置</button>
<div>
<span>第一个绑定数据:</span>
<span style="color:#CC6600" a-bind="counter"></span>
</div>
<div>
<span>第二个绑定数据:</span>
<span style="color:#CCCC33" a-bind="counter"></span>
</div>
<div>
<span>第三个绑定数据:</span>
<span style="color:#336699" a-bind="counter"></span>
</div>
<script type="text/javascript">
window.onload = function () {
// 首次加载需要初始化数据
apply('counter', obj.counter)
bind()
}
// data
let obj = new Proxy({
counter: 0
}, {
set: function (obj, prop, value) {
obj[prop] = value
if (prop == 'counter') {
apply('counter', value)
}
return true
}
})
// methods
function add() {
obj.counter++
}
function reset() {
obj.counter = 0
}
// bind event
function bind() {
let list = document.querySelectorAll('[a-click]')
list.forEach(item => {
item.onclick = function () {
window[item.getAttribute('a-click')]()
}
})
}
// bind data
function apply(str, val) {
let list = document.querySelectorAll(`[a-bind=${str}]`)
list.forEach(item => {
if (item.innerHTML !== val + '') {
item.innerHTML = val
}
})
}
</script>
</body>
</html></code></pre><h2>04 总结</h2><p> 除了上面三种方式外,其实原本还有一种 Object.observe 方法,该方法是在 es7 的草案中,不过经过各位大佬的讨论,还是废弃了这种方法,只有 chrome 曾经支持过(没错,是曾经,现在不支持了),这里就不鞭尸了(懒)。上面三种方式,无疑 proxy 是一个趋势,vue3 也改用它了,相信未来几年,proxy 会得到各个技术淫的热捧。<br> <br> 那么问题来了,react 的数据绑定技术是基于什么?有哪位技术淫可以告知一下吗?</p>