打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
岁寒之松柏:小程序skyline渲染引擎初尝试 | 微信开放社区

小程序架构介绍

我们都知道小程序本质上是运行在安卓端,苹果端的混合APP,只是微信提供了一套JSBridge,方便用户对一些原生功能和微信相关的功能的进行调用。而微信为了安全和性能的需要,一改以往网络架构中的单线程架构,改为小程序的双线程架构。分别是AppServie 和 Webview 两个线程,我们在小程序中编写的JS代码就是运行在AppService线程的JSCore引擎(类似V8 引擎,一个Js解释器)中,而我们的Wxml和Wxss则会依赖WebView线程进行渲染。

目前架构存在的问题

这样的架构虽然已经极大了提高了webview的渲染性能,但是依然会存在一些问题比如:

  1. 当页面节点数目过多,很容易发生卡顿

  2. 当我们新建一个页面,就要新建一个Webview进行渲染

  3. 页面之间共享资源,需要使用Native进行通信,就会消耗更多性能

  4. 当AppService(逻辑层)与Webview(视图层)通信也需要依赖Native

所以为了解决这些问题小程序推出Skyline渲染引擎

Skyline引擎介绍

在Skyline环境中,Skyline 会创建了一条渲染线程来负责 Layout, Composite 和 Paint 等渲染任务,并在 AppService 中划出一个独立的上下文,来运行之前 WebView 承担的 JS 逻辑、DOM 树创建等逻辑。说白了就是之前的样式计算是放到渲染线程来处理,现在把和样式相关的逻辑也放到AppService线程中处理,个人猜测这个渲染线程很有可能很有可能就是flutter,这样的架构就极大减少内存的消耗,和线程上通信时间的消耗。原本wxs中的逻辑,也可以移到Appservice线程中运行

使用Skyline引擎的使用步骤

  1. 在app.json 文件添加 'lazyCodeLoading': 'requiredComponents'属性,这是因为Skyline 依赖按需注入的特性。

{ 'pages': [ 'pages/index/index', 'pages/logs/logs', 'pages/test/test' ], 'window': { 'backgroundTextStyle': 'light', 'navigationBarBackgroundColor': '#fff', 'navigationBarTitleText': 'Weixin', 'navigationBarTextStyle': 'black' }, 'sitemapLocation': 'sitemap.json', // 在 app.json 文件添加 'lazyCodeLoading': 'requiredComponents'}
  1. 在全局或页面配置中声明为 Skyline 渲染,即 app.json 或 page.json 配上'renderer': 'skyline'

  2. Skyline 不支持页面全局滚动,需在页面配置项加上 'disableScroll': true,在需要滚动的区域使用scroll-view 实现

  3. Skyline 不支持原生导航栏,需在页面配置项加上 'navigationStyle': 'custom',并自行实现自定义导航栏

{  'usingComponents': {},   // 在 app.json 文件添加或者page页面的json中添加
  'disableScroll': true,  'navigationStyle': 'custom'
  //也可以放在App.json文件中
  'renderer': 'skyline'

 }
  1. 组件适配,参考 Skyline 基础组件支持与差异

  2. WXSS 适配,参考 Skyline WXSS 样式支持与差异

  3. 在本地设置中勾选Skyline渲染调试,如果看不到这个选项框,看一下是否在app.json中配置了'renderer': 'skyline'

Skyline的 worklet 动画介绍

小程序采用双线程架构,渲染线程(UI 线程)和逻辑线程(JS 线程)分离。JS线程不会影响 UI线程的动画表现,如滚动效果。但引入的问题是,UI线程的事件发生后,需跨线程传递到 JS线程,进而触发开发者回调,当做交互动画(如拖动元素)时,这种异步性会带来较大的延迟和不稳定,worklet动画正是为解决这类问题而诞生的,使得小程序可以做到类原生动画般的体验

worklet函数定义

function helloWorklet() { 'worklet'; //'worklet'声明该函数为work函数,可以在js线程和UI线程中调用 console.log('hello worklet'); } Page({ onLoad(options) { helloWorklet('hello') // print: hello wx.worklet.runOnUI(helloWorklet)() }, })

在小程序控制台可以看到如下输出


如果看见SkylineGlobal is not defined错误看看是否开启了Skyline渲染调试

worklet函数间的相互调用

function slave() {  'worklet';  return 'I am slave'}function master() {  'worklet'; 
  const value = slave()  console.log(value);
}

从 UI 线程调回到 JS 线程

const {runOnUI ,runOnJS} = wx.workletfunction jsFun(message) { // 普通函数不需要声明为worklet console.log(message) }function uiFun() { 'worklet'; runOnJS(jsFun)('I am from UI') }

使用shared共享数据

由worklet函数捕获的静态变量,会在编译期间序列化后生成在UI线程的拷贝环境之中,这就导致我们在JS线程中后续更新了变量,但是在UI线程中时得不到最新的数值的。

const obj = { name: 'skyline'}function someWorklet() {  'worklet'
  console.log(obj.name) // 输出的仍旧是 skyline}
obj.name = 'change name'wx.worklet.runOnUI(someWorklet)()

因此shyline使用shared来实现线程之间数据的共享

const { shared, runOnUI } = wx.workletconst offset = shared(0)function someWorklet() { 'worklet' console.log(offset.value) // 输出的是新值 1} offset.value = 1runOnUI(someWorklet)()

简单案例–实现探探的卡片功能

注意:编辑器版本:1.06.2303162基础库版本:2.30.2

先看效果

代码如下 <br>
wxml 代码

<navigation-bar title='探探' /><view class='page'>
  <block wx:for='{{containers}}' wx:key='*this'>
    <pan-gesture-handler data-id='container-{{index}}'  onGestureEvent='handlePan'>
      <view id='container-{{index}}' class='container' style='z-index: {{zIdnexes[index]}};background-image: url({{partContentList[index]}});'>
      </view>
    </pan-gesture-handler>
  </block></view>

scss代码

.page{ display: flex; justify-content: center; align-items: center; height: 100vh; width: 100vw; position: relative; .container{ height: 80vh; width: 95vw; background-color: burlywood; position: absolute; border-radius: 16rpx; display: flex; justify-content: center; align-items: center; background-size: cover; .image{ display: block; height: 1067rpx; width: 712rpx; margin: 0 0; } } }

核心逻辑

import { useAnimation, setAni, Animation, GestureState } from './method'Page<{ pos: Animation }, any>({  /**
   * 页面的初始数据
   */
  data: {
    containers: [      'burlywood',      'blue',      'cyan',      'black'
    ],
    zIdnexes:[],
    current:0,
    partContentList:[]
  },  /**
   * 生命周期函数--监听页面加载
   */
  onLoad() {    this.initNode()    // 当前node的下标
    this.active = wx.worklet.shared(0)    // 当前contentList的下标
    this.current = wx.worklet.shared(0)    this.zIndex = 100000

  },
  initNode() {    // 用与保存shared值
    this.Nodes = {}    // 图片文件
    this.contentList = [      'https://i.hexuexiao.cn/up/ca/63/4a/a32912fc26b8445797c8095ab74a63ca.jpg',      'https://th.bing.com/th/id/OIP.kSrrRGx6nqOgWzbaEvVD9AHaNK?pid=ImgDet&rs=1',      'https://img.zmtc.com/2019/0806/20190806061552744.jpg',      'https://img.zmtc.com/2019/0806/20190806061000600.jpg',      'https://img.ratoo.net/uploads/allimg/190523/7-1Z5231J058.jpg',      'https://th.bing.com/th/id/R.47de9dfcc25d579d84850d4575d24a6a?rik=%2fGkmrewzIEY4Iw&riu=http%3a%2f%2fimg3.redocn.com%2ftupian%2f20150930%2fqizhimeinvlisheyingtu_5034226.jpg&ehk=rG9Ks2QRzj81mZl38gVGmWVAgCHVLWppoDezpfwdxjo%3d&risl=&pid=ImgRaw&r=0',      'https://th.bing.com/th/id/R.95f8e6f6bd5b660ae3ad4f3e0d712276?rik=ELKcha%2bE5ryuiw&riu=http%3a%2f%2f222.186.12.239%3a10010%2fwlp_180123%2f003.jpg&ehk=mVN7AzIRR%2fmVPJYWrWOFbEiher3QWtwSdH%2f%2fe4lE7n8%3d&risl=&pid=ImgRaw&r=0'
    ]    this.data.containers.forEach((_: string, index: number) => {      if (index == 0) {        this.Nodes[`#container-${index}`] = useAnimation(`#container-${index}`, { x: 0, y: 0 }, this)        this.setData({
          [`zIdnexes[${index}]`]:100000-index,
          [`partContentList[${index}]`]:this.contentList[index]
        })
      } else {        console.log('10123')        this.Nodes[`#container-${index}`] = useAnimation(`#container-${index}`, { x: 0, y: 20, scale: 0.95 }, this)        this.setData({
          [`zIdnexes[${index}]`]:100000-index,
          [`partContentList[${index}]`]:this.contentList[index]
        })
      }

    });
  },
  handlePan(evt: any) {    'worklet';    console.log(evt)    const now = this.Nodes[`#container-${this.active.value}`] as Animation    const next = this.Nodes[`#container-${(this.active.value+1)%4}`] as Animation    if (evt.state == GestureState.ACTIVE) {      // 滑动激活状态
      // 设置当前的滑动块
      now.x.value += evt.deltaX
      now.y.value += evt.deltaY
      now.rotate.value = now.x.value * 10 / 360
      // 设置下一个滑动块
      let rate = Math.abs(now.x.value) / 150
      rate = rate > 1 ? 1 : rate
      next.y.value = (20 - rate * 20) < 0 ? 0 : (20 - rate * 20)
      next.scale.value = 0.95 + rate * 0.05
    }    if (evt.state == GestureState.END) {      // 滑动结束
      if (Math.abs(now.x.value) < 150) {        // 判断是否超过界限值
        setAni(now.x, 0)
        setAni(now.y, 0)
        setAni(now.rotate, 0)
      } else if (now.x.value < 0) {        // 判断判断左划还是右划
        setAni(now.x, -2000)
        setAni(now.y, -2000)
        setAni(now.rotate, 0)        // 通知js线程进行数据的更新
        wx.worklet.runOnJS(this.toNext.bind(this))()
      } else if (now.x.value > 0) {
        setAni(now.x, 2000)
        setAni(now.y, -2000)
        setAni(now.rotate, 0)
        wx.worklet.runOnJS(this.toNext.bind(this))()
      }
    }
  },  // 将当前序号的跳转到下一个
  toNext(){    const current = this.current.value+1
    this.active.value = current%4
    this.current.value = current    this.setData({
      current
    })    if(current-2>=0){
      wx.worklet.runOnUI(this.toReset)((current-2)%4) 
      this.setData({
        [`zIdnexes[${(current-2)%4}]`]:99998-current,
        [`partContentList[${(current-2)%4}]`]:this.contentList[current+2]
      })
    }

  },  // 将动画归位
  toReset(index:number){    'worklet';    const reset = this.Nodes[`#container-${index}`] as Animation
    setAni(reset.x, 0,0)
    setAni(reset.y, 20,0)
    setAni(reset.rotate, 0,0)
    setAni(reset.scale, 0.95,0)
  }
})

参考

skyline worklet 动画

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
SAP UI5和微信小程序对比之我见
微信小程序https请求wx.request详细教程
小程序对接php接口接收数据
Python Flask 搭建微信小程序后台详解
【微信小程序开发•系列文章二】视图层
小程序云开发全套实战教程(最全)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服