javascript京喜小程序的高性能打造之路凹凸实验室

其中,“是否有用?”这个问题是非常主观的,对于不同场景的系统可能会有完全不一样的回答,所以FMP是一个比较模糊的概念指标,不存在规范化的数值衡量。

小程序作为一个新的内容载体,衡量指标跟Web应用是非常类似的。对于大多数小程序而言,上述指标对应的含义为:

综上,我们已基本确定了高性能的概念指标,接下来就是如何利用数值指标来描绘性能表现。

小程序官方针对小程序性能表现制订了权威的数值指标,主要围绕渲染表现、setData数据量、元素节点数和网络请求延时这几个维度来给予定义(下面只列出部分关键指标):

我们应该把这一系列的官方指标作为小程序的性能及格线,不断地打磨和提升小程序的整体体验,降低用户流失率。另外,这些指标会直接作为小程序体验评分工具的性能评分规则(体验评分工具会根据这些规则的权重和求和公式计算出体验得分)。

我们团队内部在官方性能指标的基础上,进一步浓缩优化指标系数,旨在对产品体验更高要求:

体验评分工具是目前检测小程序性能问题最直接有效的途径,我们团队已经把体验评分作为页面/组件是否能达到精品门槛的重要考量手段之一。

很多时候,宏观的耗时统计对于性能瓶颈点分析往往是杯水车薪,作用甚少,我们需要更细致地针对某个页面某些关键节点作测速统计,排查出暴露性能问题的代码区块,才能更有效地针对性优化。京喜小程序使用的是内部自研的测速系统,支持对地区、运营商、网络、客户端系统等多条件筛选,同时也支持数据可视化、同比分析数据等能力。京喜首页主要围绕页面onLoad、onReady、数据加载完成、首屏渲染完成、各业务组件首次渲染完成等几个关键节点统计测速上报,旨在全链路监控性能表现。

为了更好地为小程序制订性能优化措施,我们有必要先了解小程序的底层架构,以及与web浏览器的差异性。

作为小程序开发者,我们常常会被下面几个问题所困扰:

接下来,我们会结合小程序的底层架构分析出这些问题的根本原因,并针对性地给出解决方案。

小程序启动阶段,也就是如下图所示的展示加载界面的阶段。

1.准备运行环境:

2.下载小程序代码包:

在小程序初次启动时,需要下载编译后的代码包到本地。如果启动了小程序分包,则只有主包的内容会被下载。另外,代码包会保留在缓存中,后续启动会优先读取缓存。

3.加载小程序代码包:

小程序代码包下载好之后,会被加载到适当的线程中执行,基础库会完成所有页面的注册。

在页面注册过程中,基础库会调用页面JS文件的Page构造器方法,来记录页面的基础信息(包括初始数据、方法等)。

4.初始化小程序首页:

在小程序代码包加载完之后,基础库会根据启动路径找到首页,根据首页的基础信息初始化一个页面实例,并把信息传递给视图层,视图层会结合WXML结构、WXSS样式和初始数据来渲染界面。

经过多次业务迭代,无可避免的会存在一些弃用的组件/页面,以及不被调用的函数、样式规则,这些冗余代码会白白占据宝贵的代码包空间。而且,目前小程序的打包会将工程下所有文件都打入代码包内,并没有做依赖分析。

因此,我们需要及时地剔除不再使用的模块,以保证代码包空间利用率保持在较高水平。通过一些工具化手段可以有效地辅助完成这一工作。

WXML中的import和include:

{{text}}ABWXSS中的@import:

@import'./A.wxss'JS中的require/import:

constA=require('./A')所以,可以说小程序里的所有依赖模块都是有迹可循的,我们只需要利用这些关键字信息递归查找,遍历出文件依赖树,然后把没用的模块剔除掉。

小程序代码包最终会经过GZIP压缩放在CDN上,但GZIP压缩对于图片资源来说效果非常低。如JPG、PNG等格式文件,本身已经被压缩过了,再使用GZIP压缩有可能体积更大,得不偿失。所以,除了部分用于容错的图片必须放在代码包(譬如网络异常提示)之外,建议开发者把图片、视频等静态资源都放在CDN上。

这是一个“痛并快乐着”的优化措施。“痛”是因为需要给后台同学提改造需求,分分钟被打;“快乐”则是因为享受删代码的过程,而且万一出Bug也不用背锅了...(开个玩笑)

通过让后台承担更多的业务逻辑,可以节省小程序前端代码量,同时线上问题还支持紧急修复,不需要经历小程序的提审、发布上线等繁琐过程。

总结得出,一般不涉及前端计算的展示类逻辑,都可以适当做后移。譬如京喜首页中的幕帘弹窗(如下图)逻辑,这里共有10+种弹窗类型,以前的做法是前端从接口拉取10+个不同字段,根据优先级和“是否已展示”(该状态存储在本地缓存)来决定展示哪一种,最后代码大概是这样的:

//检查每种弹窗类型是否已展示Promise.all([check(popup_1),check(popup_2),//...check(popup_n)]).then(result=>{//优先级排序constqueue=[{show:result.popup_1data:data.popup_1},{show:result.popup_2data:data.popup_2},//...{show:result.popup_ndata:data.popup_n}]})逻辑后移之后,前端只需负责拿幕帘字段做展示就可以了,代码变成这样:

this.setData({popup:data.popup})

京喜首页作为电商系统的门户,需要应对各类频繁的营销活动、升级改版等,同时也要满足不同用户属性的界面个性化需求(俗称“千人千面”)。如何既能减少为应对多样化场景而产生的代码量,又可以提升研发效率,成为燃眉之急。

类似于组件复用的理念,我们需要提供更丰富的可配置能力,实现更高的代码复用度。参考小时候很喜欢玩的“乐高”积木玩具,我们把首页模块的模板元素作颗粒度更细的划分,根据样式和功能抽象出一块块“积木”原料(称为插件元素)。当首页模块在处理接口数据时,会启动插件引擎逐个装载插件,最终输出个性化的模板样式,整个流程就好比堆积木。当后续产品/运营需要新增模板时,只要在插件库中挑选插件排列组合即可,不需要额外新增/修改组件内容,也更不会产生难以维护的if/else逻辑,soeasy~

当然,要完成这样的插件化改造免不了几个先决条件:

下面为大家提供部分例程来辅助理解。其中,use方法会接受各类处理钩子最终拼接出一个Function,在对应模块处理数据时会被调用。

白屏阶段,是指小程序代码包下载完(也就是启动界面结束)之后,页面完成首屏渲染的这一阶段,也就是FMP(首次有效绘制)。

小程序提供了读写本地缓存的接口,数据存储在设备硬盘上。由于本地I/O读写(毫秒级)会比网络请求(秒级)要快很多,所以在用户访问页面时,可以优先从缓存中取上一次接口调用成功的数据来渲染视图,待网络请求成功后再覆盖最新数据重新渲染。除此之外,缓存数据还可以作为兜底数据,避免出现接口请求失败时页面空窗,一石二鸟。

但并非所有场景都适合缓存策略,譬如对数据即时性要求非常高的场景(如抢购入口)来说,展示老数据可能会引发一些问题。

小程序内部接口的响应体类型都是application/octet-stream,即数据格式未知,使本地代理无法正确解析。

如果这几个问题点都不会影响到你的场景,那么可以尝试开启预拉取能力,这对于小程序首屏渲染速度是质的提升。

为了尽快获取到服务端数据,比较常见的做法是在页面onLoad钩子被触发时发起网络请求,但其实这并不是最快的方式。从发起页面跳转,到下一个页面onLoad的过程中,小程序需要完成一些环境初始化及页面实例化的工作,耗时大概为300~400毫秒。

实际上,我们可以在发起跳转前(如wx.navigateTo调用前),提前请求下一个页面的主接口并存储在全局Promise对象中,待下个页面加载完成后从Promise对象中读取数据即可。

这也是双线程模型所带来的优势之一,不同于多页面web应用在页面跳转/刷新时就销毁掉window对象。

如果开启了分包加载能力,在用户访问到分包内某个页面时,小程序才会开始下载对应的分包。当处于分包下载阶段时,页面会维持在“白屏”的启动态,这用户体验是比较糟糕的。

以京喜小程序如此庞大的小程序项目为例,每个模块背后都可能有着海量的后台服务作支撑,而这些后台服务间的通信和数据交互都会存在一定的时延。我们根据京喜首页的页面结构,把所有模块划分成两类:主体模块(导航、商品轮播、商品豆腐块等)和非主体模块(幕帘弹窗、右侧挂件等)。

这也是关键渲染路径优化思路之一,通过延迟非关键元素的渲染时机,为关键渲染路径腾出资源。

类似上一条措施,继续以京喜小程序首页为例,我们在主体模块的基础上再度划分出首屏模块(商品豆腐块以上部分)和非首屏模块(商品豆腐块及以下部分)。当小程序获取到主体模块的数据后,会优先渲染首屏模块,在所有首屏模块都渲染完成后才会渲染非首屏模块和非主体模块,以此确保首屏内容以最快速度呈现。

鉴于移动端设备的分辨率是有上限的,很多图片的尺寸常常远大于页面元素尺寸,这非常浪费网络资源(一般图片尺寸2倍于页面元素真实尺寸比较合适)。得益于京东内部强大的图片处理服务,我们可以通过资源的命名规则和请求参数来获取服务端优化后的图片:

这两者都是比较老生常谈的图片优化技术,这里就不打算细讲了。

在不得不使用大图资源的场景下,我们可以适当使用“体验换速度”的措施来提升渲染性能。

下面为大家提供部分例程:

“白屏”的加载体验对于首次访问的用户来说是难以接受的,我们可以使用尺寸稳定的骨架屏,来辅助实现真实模块占位和瞬间加载。

有趣的是,京喜首页的骨架屏方案经历了“统一管理”和“(组件)独立管理”两个阶段。出于避免对组件的侵入性考虑,最初的骨架屏是由一个完整的骨架屏组件统一管理的:

页面主体但这种做法的维护成本比较高,每次页面主体模块更新迭代,都需要在骨架屏组件中的对应节点同步更新(譬如某个模块的尺寸被调整)。除此之外,感官上从骨架屏到真实模块的切换是跳跃式的,这是因为骨架屏组件和页面主体节点之间的关系是整体条件互斥的,只有当页面主体数据Ready(或渲染完毕)时才会把骨架屏组件销毁,渲染(或展示)主体内容。

//banner.scss.banner--skeleton{@includeskeleton;.banner_wrapper{@includeskeleton__block;}}

当调用wx.navigateTo打开一个新的小程序页面时,小程序框架会完成这几步工作:

1.准备新的webview线程环境,包括基础库的初始化;

2.从逻辑层到视图层的初始数据通信;

3.视图层根据逻辑层的数据,结合WXML片段构建出节点树(包括节点属性、事件绑定等信息),最终与WXSS结合完成页面渲染;

尽可能地把多次setData调用合并成一次。

在Taro框架下,调用setState时提供的对象会被加入到一个数组中,当下一次事件循环执行的时候再把这些对象合并一起,通过setData传递给原生小程序。

所以,与视图层渲染无关的数据尽量不要放在data中,可以放在页面(组件)类的其他字段下。

每当调用setData更新数据时,会引起视图层的重新渲染,小程序会结合新的data数据和WXML片段构建出新的节点树,并与当前节点树进行比较得出最终需要更新的节点(属性)。

即使小程序在底层框架层面已经对节点树更新进行了diff,但我们依旧可以优化这次diff的性能。譬如,在调用setData时,提前确保传递的所有新数据都是有变化的,也就是针对data提前做一次diff。

Taro框架内部做了这一层优化。在每次调用原生小程序的setData之前,Taro会把最新的state和当前页面实例的data做一次diff,筛选出有必要更新的数据再执行setData。

当用户事件(如Click、Touch事件等)被触发时,视图层会把事件信息反馈给逻辑层,这也是一个线程间通信的过程。但,如果没有在逻辑层中绑定事件的回调函数,通信将不会被触发。

所以,尽量减少不必要的事件绑定,尤其是像onPageScroll这种会被频繁触发的用户事件,会使通信过程频繁发生。

组件节点支持附加自定义数据dataset(见下面例子),当用户事件被触发时,视图层会把事件target和dataset数据传输给逻辑层。那么,当自定义数据量越大,事件通信的耗时就会越长,所以应该避免在自定义数据中设置太多数据。

不难得出,如果自定义组件的颗粒度太粗,组件逻辑过重,会影响节点树构建和新/旧节点树diff的效率,从而影响到组件内setData的性能。另外,如果组件内使用了createSelectorQuery来查找节点,过于庞大的节点树结构也会影响查找效率。

WXML数据绑定是小程序中父组件向子组件传递动态数据的较为常见的方式,如下面例程所示:ComponentA组件中的变量a、b通过组件属性传递给ComponentB组件。在此过程中,不可避免地需要经历一次ComponentA组件的setData调用方可完成任务,这就会产生线程间的通信。“合情合理”,但,如果传递给子组件的数据只有一部分是与视图渲染有关呢?

//ComponentBComponent({properties:{propA:String,propB:String,},methods:{onLoad:function(){this.data.propAthis.data.propB}}})推荐一种特定场景下非常便捷的做法:通过事件总线(EventBus),也就是发布/订阅模式,来完成由父向子的数据传递。其构成非常简单(例程只提供关键代码...):

一个全局的事件调度中心

classEventBus{constructor(){this.events={}}on(key,cb){this.events[key].push(cb)}trigger(key,args){this.events[key].forEach(function(cb){cb.call(this,...args)})}remove(){}}constevent=newEventBus()事件订阅者

//子组件Component({created(){event.on('data-ready',(data)=>{this.setData({data})})}})事件发布者

//ParentComponent({ready(){event.trigger('data-ready',data)}})子组件被创建时事先监听数据下发事件,当父组件获取到数据后触发事件把数据传递给子组件,这整个过程都是在小程序的逻辑层里同步执行,比数据绑定的方式速度更快。

但并非所有场景都适合这种做法。像京喜首页这种具有“数据单向传递”、“展示型交互”特性、且一级子组件数量庞大的场景,使用事件总线的效益将会非常高;但若是频繁“双向数据流“的场景,用这种方式会导致事件交错难以维护。

我们可能会遇到这样的需求,多个组件之间位置不固定,支持随时随地灵活配置,京喜首页也存在类似的诉求。

京喜首页主体可被划分为若干个业务组件(如搜索框、导航栏、商品轮播等),这些业务组件的顺序是不固定的,今天是搜索框在最顶部,明天有可能变成导航栏在顶部了(夸张了...)。我们不可能针对多种顺序可能性提供多套实现,这就需要用到小程序的自定义模板//search-bar.jsComponent({properties:{floorId:Number,},created(){event.on('data-ready',(comps)=>{constdata=comps[this.data.floorId]//根据楼层位置取数据})}})貌似非常轻松地完成需求,但值得思考的是:如果组件顺序调整了,所有组件的生命周期会发生什么变化?

假设,上一次渲染的组件顺序是['search-bar','nav-bar','banner','icon-nav'],现在需要把nav-bar组件去掉,调整为['search-bar','banner','icon-nav']。经实验得出,当某个组件节点发生变化时,其前面的组件不受影响,其后面的组件都会被销毁重新挂载。

原理很简单,每个组件都有各自隔离的节点树(ShadowTree),页面body也是一个节点树。在调整组件顺序时,小程序框架会遍历比较新/旧节点树的差异,于是发现新节点树的nav-bar组件节点不见了,就认为该(树)分支下从nav-bar节点起发生了变化,往后节点都需要重渲染。

但实际上,这里的组件顺序是没有变化的,丢失的组件按道理不应该影响到其他组件的正常渲染。所以,我们在setData前先进行了新旧组件列表diff:如果newList里面的组件是oldList的子集,且相对顺序没有发生变化,则所有组件不重新挂载。除此之外,我们还要在接口数据的相应位置填充上空数据,把该组件隐藏掉,done。

通过组件diff的手段,可以有效降低视图层的渲染压力,如果有类似场景的朋友,也可以参考这种方案。

想必没有什么会比小程序Crash更影响用户体验了。

不过内存告警的信息收集倒是有意义的,我们可以把内存告警信息(包括页面路径、客户端版本、终端手机型号等)上报到日志系统,分析出哪些页面Crash率比较高,从而针对性地做优化,降低页面复杂度等等。

根据双线程模型,小程序每一个页面都会独立一个webview线程,但逻辑层是单线程的,也就是所有的webview线程共享一个JS线程。以至于当页面切换到后台态时,仍然有可能抢占到逻辑层的资源,譬如没有销毁的setInterval、setTimeout定时器:

//PageAPage({onLoad(){leti=0setInterval(()=>{i++},100)}})即使如小程序的组件,在页面进入后台态时依然是会持续轮播的。正确的做法是,在页面onHide的时候手动把定时器清理掉,有必要时再在onShow阶段恢复定时器。坦白讲,区区一个定时器回调函数的执行,对于系统的影响应该是微不足道的,但不容忽视的是回调函数里的代码逻辑,譬如在定时器回调里持续setData大量数据,这就非常难受了...

对于大图片资源(譬如满屏的gif图)来说,我们只能尽可能对图片进行降质或裁剪,当然不使用是最好的。

然而无可避免地,当用户快速滚动长列表时,被销毁的组件可能来不及加载完,视觉上就会出现短暂的白屏。我们可以适当地调整销毁阈值,或者优化骨架图的样式来尽可能提升体验感。

结合上述的种种方法论,京喜小程序首页进行全方位升级改造之后给出了答卷:

1.Audits审计工具的性能得分86;

3.优化前后的测速数据对比:

凹凸实验室(Aotu.io,英文简称O2)始建于2015年10月,是一个年轻基情的技术团队。

THE END
1.三模板与配置(下)8、WXSS模板样式-全局样式和局部样式 9、全局配置-全局配置文件及常用的配置项 小程序根目录下的app.json文件是全局配置文件。其中常用配置项有: 10、全局配置-小程序窗口的组成部分 11、全局配置-window 11-1、了解 window 节点常用的配置项 11-2、设置导航栏的标题 ...http://www.mynw.cn/network/17574.html
2.小程序开发:必须掌握的HTMLCSS和JavaScript技术css/* 这个div元素将有一个背景颜色 */ #myDiv { background-color: #f0f0f0; } /* 这个p元素将有一个字体大小 */ p { font-size:16px; } ### CSS 小程序特性###1.样式绑定小程序可以使用v-bind样式绑定数据到元素的属性中。 css<!-- 将myData绑定到div元素的class属性 --> ...http://www.shili8.cn/article/detail_20002674969.html
3.在HTML文档中,应用CSS外部样式表的正确位置是Ahead部分B...在HTML文档中,应用CSS外部样式表的正确位置是 A、head部分 B、body部分 C、文档的末尾 D、文档的顶部 点击查看答案进入小程序搜题 你可能喜欢 CAD的圆命令绘制方法包含以下几种( )。 A. 半径 B. 两点 C. 三点 D. 相切、相切、相切 点击查看答案进入小程序搜题 A.2 B.-2 C.-1 D.1 点击查...https://m.ppkao.com/wangke/daan/ec1691bdca144f3db69401eee4276e0b
4.小程序组件使用的时候没有样式是怎么回事?直接粘贴的小程序组件form表单,acss也复制了 ? 请登录后发表内容 3个回答按发布时间降序 lucazhou 2023-02-20 看样式那里加了scoped没有,可能不是没样式,而是有其他样式影响到它了 ? 赞同0 回复1 打赏 lucazhou回复lucazhou 2023-02-20 在开发者工具里查看一下,加载的样式是哪里的 ? 1 回复 打赏 ...http://forum.alipay.com/mini-app/post/114801011
5.weuiwxss首页文档和下载为微信小程序设计的WeUI软件文档 官方下载 极速下载 62.0 安全指数 概览 资讯 博客 问答 安全信息 软件简介 概述 WeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一。包含 button、cell、dialog、 progress、 toast、article、actionsheet、icon 等各...https://www.oschina.net/p/weui-wxss
6.微信小程序复习全套试题题库(52页)(错 ) 在微信小程序中,AppID又称为小程序ID,是每个小程序的唯一标识。(对 ) 使用微信小程序必须先安装微信。( 对) 微信小程序开发模式类似与vue,同时支持组件化开发。( 对) 微信小程序开发类似于传统的网页开发,微信内部对语言进行了定制。( 对) 微信小程序不支持ES6语法,但支持CSS动画。( 错) 在微信小...https://max.book118.com/html/2021/0725/8027140124003124.shtm
1.微信开发者工具小程序设置字体居中样式微信小程序css篇---字体(Font) 一.字体:font。属性在一个声明中设置所有字体属性。 可设置的属性是(按顺序): "font-style font-variant font-weight font-size/line-height font-family". font-size和font-family的值是必需的。如果缺少了其他值,默认值将被插入,如果有默认值的话. 1...https://blog.51cto.com/u_16213674/12538572
2.css笔记1 css引入方式 <!DOCTYPE html> Title <!--内嵌式引入--> <!----> <!--a{--> <!--color:rebeccapurple;--> <!--font-size:30px;--> <!--font-weight:900;--> <!--}--> <!--p{--> <!--ackground-color:gold;--> <!--}--> ...https://www.u72.net/daima/nx1d6.html
3.微信小程序5步玩转CSS,微信小程序样式设计不再是难题?即刻启航,编程之旅更有趣 标题:5步玩转CSS,微信小程序样式设计不再是难题? 引言 嗨,小伙伴们!今天我们要聊聊CSS在微信小程序中的应用。是不是觉得CSS听起来就很高大上,其实它就是让你的小程序看起来美美的那个小秘密武器。不用担心,即使你是CSS新手,跟着我们的步伐一步步来,保证让你轻松上手,让小程...https://blog.csdn.net/z_344791576/article/details/143481346
4.微信小程序QQ小程序前端200道面试题及答案(更新中)总结: link和@import都没有放置顺序的要求,但是不同的放置位置可能会造成效果显示的差异。对于link,无论放到哪个位置,都是一边加载数据,一边进行优化,视觉感受很好;而对于@import,放置到哪里,才从哪里开始加载CSS样式,即先加载数据,然后加载样式,如果网速不佳,可能会造成只有数据出来,而样式一点点加载的效果。并且在...https://leheavengame.com/article/64d7982931f3516f1405dd03
5.简单编写小程序的CSS样式教程腾讯云开发者社区我们在完成了小程序的内容编辑后,就需要对样式骨架进行调节与调优,才能写出最符合用户体验的样式内容。 今天,来自FInClip的工程师给大家带来编写css样式的干货教程,一起来看看吧。 一、总体样式 小程序允许在顶层放置一个 app.fxss 文件,里面采用 CSS 语法设置页面样式。这个文件的设置,对所有页面都有效。 https://cloud.tencent.com/developer/article/1928930
6.微信小程序笔记开发模式不同:网页通过浏览器+代码编辑器开发,小程序有自己的开发者账号、开发者工具 体验 通过微信扫描右侧二维码,可体验微信小程序的具体样式和功能介绍; 使用手机微信(6.7.2 及以上版本) 扫码后,弹出的应用就是小程序。 注册开发者 使用浏览器打开https://mp.weixin.qq.com/网址,点击右上角立即注册即可进入到...https://smmcat.cn/?page_id=7595
7.小程序官方文档小程序版作者本人是从事iOS 开发的,但从小程序出来到开放公测,一直都想去研究研究,奈何各种"因素"没有去试(上班有公司项目,下班就去夜跑,哈哈,反正就是借口)到 2016.12.11日,公司也有意做小程序开发,终于定下心来研究一番,在此分享一下。 学习首先肯定去看官方文档,当然只看不行,起码敲一个Demo出来嘛,看着文档还比较...https://www.jianshu.com/p/5a781c989299
8.Taro问题描述 Taro-ui 升级到2.0.2以上,微信小程序编译后css样式全部失效,退回到1.5.4正常 复现步骤 sudo cnpm install --save taro-ui@2.0.2 npx taro build --type weapp --watch 期望行为 升级到2.1.0能正常使用 报错信息 没有报错 系统信息 Taro v1.2.26 Taro CLI 1.https://github.com/NervJS/taro-ui/issues/536
9.微信小程序WXSS样式文件教程JavaScript大厂稀缺内推资格,内招信息,35岁后程序员返聘机会……【 脚本之家合作内推渠道,注册就能看!】WXSS官方文档https://developers.weixin.qq.com/miniprogram/dev/framework/1. WXSSWXSS(WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式。与CSS 相比,WXSS 扩展的特性有:尺寸单位样式...https://www.jb51.net/article/244953.htm
10.TailwindCSS在小程序中的应用LaravelChina社区pigzzz 搬砖 @ 工地 https://learnku.com/articles/60035
11.云开发WeUI框架这样weui的css样式就被引入到我们的小程序中啦,那我们该如何使用WeUI已经写好的样式呢? Flex布局 前面我们已经了解了如何给wxml文件添加文字、链接、图片等元素和组件,我们希望给这些元素和组件的排版更加结构化,不再是单纯的上下关系,还有左右关系,以及左右上下嵌套的关系,这个时候就需要了解布局方面的知识啦。 https://www.w3cschool.cn/cloudbasehandbook/cloudbasehandbook-xdms387n.html
12.微信小程序WXSS样式文件教程WXSS官方文档 https://developers.weixin.qq.com/miniprogram/dev/framework/ 1. WXSS WXSS(WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式。 与CSS 相比,WXSS 扩展的特性有: 尺寸单位 样式导入 1.1. 尺寸单位 rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone...https://www.finclip.com/news/f/68946.html