裸奔向前 發表於 2019-7-25 11:15:00

从微信小程序开发者工具源码看实现原理(四)- - 自适应布局

<p>从前面从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计可以知道,小程序大部分是通过web技术进行渲染的,也就是最终通过浏览器的dom tree + cssom来生成渲染树;既然最终是通过css来绘制ui布局,我们知道小程序提供的自适应css单位<code>rpx</code>在浏览器环境根本不被识别,所以小程序最终还是将<code>rpx</code>单位转化为浏览器识别的css长度单位,到底是怎么转化的呢,本节就来探讨一下转化机制。</p>
<h2 id="小程序样式转换">小程序样式转换</h2>
<p>在从微信小程序开发者工具源码看实现原理(二)- - 小程序技术实现中可以知道,小程序中的wxss样式文件进行的主要转换转换rpx单位,视图层模板注入转换后的wxss代码如下图:</p>
<p><img src="https://img2018.cnblogs.com/blog/408483/201907/408483-20190721163830976-1238156340.png"></p>
<p>上面的内容就是注入到视图层pageframe模板中的css代码,其内容包括:</p>
<ul>
<li>提供rpx单位到px单位的转换</li>
<li>提供动态插入转换后样式内容到dom中的js方法</li>
<li>每个页面引入公共样式,即app.wxss转换后的css内容</li>
</ul>
<p>上面提到的这些转换操作都是内置到小程序的<code>wcsc</code>可执行程序中,通过调用可执行程序来完成具体转换工作。最终注入到页面中的css内容如下图所示:<br>
<img src="https://img2018.cnblogs.com/blog/408483/201907/408483-20190721165947307-2009913248.png"></p>
<h2 id="小程序自适应单位rpx转换">小程序自适应单位rpx转换</h2>
<p>小程序的自适应布局采用的内部实现的<code>rpx</code>来完成,但是其不被web识别,所以<code>rpx</code>单位转换是指:</p>
<blockquote>
<p>是将小程序的css单位rpx转换为web识别的css单位px</p>
</blockquote>
<p>那么小程序怎么来进行rpx与px之间的转换呢?先来看一下官网有关rpx的描述:</p>
<blockquote>
<p>rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。</p>
</blockquote>
<p>由此可以看出,小程序在实现rpx转换时,不论是什么屏幕的手机,都是将屏幕宽度固定设为750rpx,然后根据实际屏幕的设备像素比<code>dpr</code>(dpr = 设备像素 / css像素)来进行转换的。具体对应关系如下:</p>
<blockquote>
<p>1rpx = (number/ 750) * 设备宽度 px</p>
</blockquote>
<p>下面通过小程序开发者工具简单分析小程序<code>wcsc</code>可执行命令程序生成的有关rpx转换的js代码</p>
<h3 id="首先获取小程序的设备宽度">首先获取小程序的设备宽度</h3>
<p><img src="https://img2018.cnblogs.com/blog/408483/201907/408483-20190721173507550-953208515.png"></p>
<p>小程序开发者工具在初始渲染一个页面时会首先获取设备宽度deviceWidth和dpr,然后会通过<code>checkDeviceWidth</code>方法(wcsc可执行命令注入的代码)检查修正二者的值,因为屏幕<strong>orientation</strong>方向可能变化,在上面代码有这么一段:</p>
<pre><code>if (window.screen.orientation &amp;&amp; /^landscape/.test(window.screen.orientation.type || "")) {
   newDeviceWidth = newDeviceHeight;
}
</code></pre>
<p>该代码利用<code>window.screen.orientation</code>来判断手机的横竖方向,若处于横屏的时候,webview的宽度与高度值会互换,即高度值就是屏幕的真实宽度;需要注意的是小程序开发者工具的webview这一点与移动端手机表现不太一致。</p>
<p>另外,需要补充两点:</p>
<ul>
<li>利用window.screen.orientation这个判断手机方向的特性大部分浏览器支持情况比较差,具体可以看这里。但是小程序开发者工具使用基于chrome的webview,这个是支持的。</li>
<li>代码的<code>window.__checkDeviceWidth__</code>在小程序的一些基础库(如2.3.2)中是没有定义的;但是新的版本(2.7.7)是有该方法定义的,但是从什么版本开始支持的不得而知。</li>
</ul>
<h3 id="rpx单位转换">rpx单位转换</h3>
<p><img src="https://img2018.cnblogs.com/blog/408483/201907/408483-20190723142512188-1693591602.png"></p>
<p>正如官网所描述的,小程序将屏幕固定750rpx,然后根据当前屏幕宽度以及设置的rpx值,最终推算出rpx对应的px值。<br>
补充一点,在设置的rpx值转换为px值大于0小于1时,不论设置的rpx值是多少,最终在dpr不是1的ios情况下会始终返回0.5px,其他情况始终返回1px;例如下面代码:</p>
<pre><code class="language-css">.text {
height: 1rpx;
background: #333;
}
</code></pre>
<p>最终在开发者工具中转换的px值为0.5,如下图:<br>
<img src="https://img2018.cnblogs.com/blog/408483/201907/408483-20190723223807465-1113930820.png"></p>
<h2 id="小程序屏幕旋转自适应转换过程">小程序屏幕旋转自适应转换过程</h2>
<p>通过上面转换rpx值,一旦转换完成后转换值就固定了;但是对于支持屏幕旋转的情况,这显然不是我们希望的结果,期望根据屏幕旋转的方向来重新转换对应的rpx值。<br>
小程序从2.4.0基础版本开始通过配置<code>"pageOrientation": "auto"</code>开始支持屏幕旋转,这就需要知道屏幕发生变化的时机来做对应的处理。具体分两个方面转换:<strong>wxss样式文件转换</strong>和<strong>style内联样式转换</strong>。</p>
<h3 id="wxss样式文件自适应转换">wxss样式文件自适应转换</h3>
<p><strong>首先</strong>,在视图层,wxss样式文件经rpx初始转换后并将样式注入到页面过程中,会向<code>window.__rpxRecalculatingFuncs__</code>数组中收集窗口变化时的回调;先看<code>wcsc</code>可执行程序输出的处理rpx转换相关的<strong>setCssToHead</strong>函数实现,其最终返回<strong>rewritor</strong>函数,对应代码如下图:<br>
<img src="https://img2018.cnblogs.com/blog/408483/201907/408483-20190724202307401-1701632969.png"></p>
<p>可以看出在转换后的样式嵌入到<code>document.head</code>中后,依然保存有创建的style元素的句柄,在页面窗口变更时执行对应的回调来修正rpx转换后的px值。</p>
<p><strong>然后</strong>,在小程序基础库WAWebview内部初始时会使用<code>wx.onWindowResize(fn)</code>来注册窗口变更的事件回调,注册事件内部会执行<code>window.__rpxRecalculatingFuncs__</code>中的回调,具体代码如下图:<br>
<img src="https://img2018.cnblogs.com/blog/408483/201907/408483-20190725110509548-613612374.png"></p>
<p>这样,视图窗口变更时就会通知样式文件进行重新rpx转换,最后将最新转换的样式内容更新到页面中。</p>
<p>那么,<strong>小程序如何把握屏幕切换的触发时机呢?</strong></p>
<p>这个触发时机在微信环境是由native提供感知能力,开发环境则是小程序开发工具本身提供支持。拿开微信开发者工具来说明具体的整个过程:</p>
<ul>
<li>视图层与业务逻辑层分别注册<code>onViewDidResize</code>事件回调</li>
<li>开发者工具感知到窗口变化会通过websocket方式向视图层和业务逻辑层同时发送执行<code>onViewDidResize</code>回调的消息</li>
<li><code>onViewDidResize</code>会分别执行通过<code>wx.onWindowResize(fn)</code>注册的回调</li>
</ul>
<h3 id="内联样式自适应转换">内联样式自适应转换</h3>
<p>内联样式转换在底层基础库是采用<code>transformRpx</code>方法来转换rpx值的,思路与上面介绍的一样,唯一不同点就是是否对0进行修正,具体代码如下:</p>
<pre><code class="language-js">var $ = function(e) { // e为要转换的rpx值,V为设备宽度
   return 0 === e &amp;&amp; function(e) {
      var t = window.__wcc_version_info__;
      if (t) return t;
    }("fixZeroRpx") ? 0 : (e = e / 750 * V, 0 === (e = Math.floor(e + 1e-4)) ? 1 !== dpr &amp;&amp; isIPhone ? .5 : 1 : e)
}
</code></pre>
<p>通过获取<code>window.__wcc_version_info__.fixZeroRpx</code>的值来判断rpx为0时如何转换;而<code>window.__wcc_version_info__</code>的定义赋值是在<code>wcc</code>可执行命令转换wxml文件生成的js脚本中完成的,下面是wcc生成有关赋值代码:<br>
<img src="https://img2018.cnblogs.com/blog/408483/201907/408483-20190725104009110-964495457.png"></p>
<p>具体样式文件自适应转换过程如下:</p>
<ul>
<li>视图层在生成virtual dom过程中会收集每个元素的属性,其中包括style属性</li>
<li>在生成dom过程中,针对元素的style属性使用<code>transformRpx</code>进行转换,转换后内容应用到具体dom元素</li>
<li>为含有rpx单位内联样式dom元素绑定窗口变化回调,窗口变化时style中的rpx进行重新转换并应用到dom元素上</li>
</ul><br><br>
来源:https://www.cnblogs.com/wonyun/p/11195157.html
頁: [1]
查看完整版本: 从微信小程序开发者工具源码看实现原理(四)- - 自适应布局