小程序组件实现原理(Web Components)

一、小程序组件系统

1-1.小程序结构

小程序结构分为逻辑层和视图层,视图层中的每个页面由组件组合而成。

小程序框架的目的就是为开发者提供了一系列基础组件及功能复杂的组件(地图、视频、直播等)供开发者使用,便于开发者快速搭建出任何页面。同时也提供了自定义组件的能力,开发者可以自行扩展更多的组件,以实现代码复用。

1-2.组件介绍
组件化:

一句话形容就是:“高内聚,低耦合”,对内各个元素彼此相互依赖,对外和其他组件的接口简单。最初的目的是代码重用,功能相对单一或者独立。在整个系统的代码层次上位于最底层,被其他代码所依赖,所以说组件化是纵向分层。

小程序组件实现:

小程序的视图是在WebView里渲染的,搭建视图的方式就是常用的HTML语言。但是如果直接提供HTML的能力,会存在一些不足之处:

  • 没法解决管控与安全问题:

    • 开发者可以利用<a>标签实现跳转到其它在线网页

    • 可以动态执行JavaScript创建dom等

  • 不利于快速开发

    • 标签太多,增加理解成本

    • web能力有限,复杂功能不好实现

小程序组件标准:

目前支付宝、字节、百度都会对齐微信小程序能力,减少开发者负担

1-3. 组件分类

小程序是结合了web和native能力的技术方案,在小程序组件层面既有纯web组件,也有基于端能力的native组件。小程序中大部分组件直接渲染在webview上,系统自带的原生组件位于更高的层级。

小程序提供的内置组件分为web组件和native组件两大类:

  • web组件:botton、view、text等大部分组件

  • native组件:input、textarea、picker、video、web-view、live-player、map等复杂功能组件

二、 WEB组件

web components很适合小程序组件的开发,Custom elements可以自定义组件,Shadow DOM 作用域内部的元素始终不会影响到它外部的元素,这为封装提供了便利

2-1. 原生组件Web Components

谷歌公司一直在推动浏览器的原生组件,即 Web Components API。相比第三方框架,原生组件简单直接,符合直觉,不用加载任何外部模块,代码量小。目前,它还在不断发展,但已经可用于生产环境。

Web Components 由三项主要技术组成,它们可以一起使用来创建封装功能的定制元素,可以在任何地方重用,不必担心代码冲突。

  • Custom elements(自定义元素):一组 JavaScript API,它使开发者能够将 HTML 页面的功能封装为 custom elements(自定义标签)。

  • Shadow DOM(影子 DOM):一组 JavaScript API,可以将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不同的部分不会混在一起,可使代码更加干净、整洁。它可以将一个隐藏的、独立的 DOM 附加到一个元素上。

  • HTML templates(HTML 模板): <template> 和 <slot> 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。

2-2. web component实现自定义组件

1)通过web components可以快捷创建自定义组件与使用

以上就可以直接使用一个自定义的元素,demo如下:

<popup-info 
    img="https://i-1-lanrentuku.52tup.com/2020/11/6/9232f107-4dac-4006-9c7e-e825df5e52e1.png?imageView2/2/w/500" 
    text="测试demo,text测试文案" 
/>

2)class内部代码实现:

class PopUpInfo extends HTMLElement {
  constructor() {
    // 必须首先调用 super 方法
    super();
    
    // 创建一个 shadow dom
    // mode: open表示可以js方法来获取Shadow DOM,close获取到null
    var shadow = this.attachShadow({mode: 'open'});
    
    // 创建一个盒子
    var wrapper = document.createElement('div');
    wrapper.setAttribute('class','wrapper');
    
    // 创建一个icon标签,存放图片链接
    var icon = document.createElement('div');
    icon.setAttribute('class','icon');
    // 获取自定义标签上img属性的内容,插入到src;
    var imgUrl = this.getAttribute('img');
    var img = document.createElement('img');
    img.src = imgUrl;
    icon.appendChild(img);
    
    // 创建一个info标签,存放text文本
    var info = document.createElement('div');
    info.setAttribute('class','info');
    // 获取自定义标签上text属性的内容,并添加到一个 span 标签内
    var text = this.getAttribute('text');
    info.textContent = text;
    
    
    // 创建一些 CSS,并应用到 shadow dom 上
    var style = document.createElement('style');
    // 简洁起见,省略了具体的 CSS
    style.textContent = `.wrapper{***}` 
    
    // 将创建的元素附加到 shadow dom
    shadow.appendChild(style);
    shadow.appendChild(wrapper);
    wrapper.appendChild(icon);
    wrapper.appendChild(info);
  }
}

3)最后,使用define()方法将 custom element 注册到CustomElementRegistry上,在方法的参数里,我们指定了元素的名称,以及定义了元素功能的类。

customElements.define('popup-info', PopUpInfo);

4)以上,可以在页面上使用我们定义的 custom element了,浏览器中解析如下

5)在线code:

https://codepen.io/yuwenbinjie/pen/oNqaxegicon-default.png?t=N5K3https://codepen.io/yuwenbinjie/pen/oNqaxeg

2.3. 组件框架介绍

1)字节小程序:基于polymer UI框架实现

polymer是由谷歌的Palm webOS团队打造,并在2013 Google I/O大会上推出,旨在用最少的代码实现Web Components,解除框架间的限制的UI框架。polymer的核心思想是"Everything is an element"。

优点:

  1. 代码轻量化,支持模块开发,会自动生成shadow dom

  2. 支持数据绑定、事件监听,通过更改属性变量,控制UI状态

  3. 提供易用的生命周期回调:创建、节点首次挂载完毕,每次挂载,从文档树移除,属性变化时触发函数

2) 微信小程序:基于Exparser框架实现

Exparser是微信小程序的组件组织框架,内置在小程序基础库中,Exparser的组件模型与WebComponents标准中的ShadowDOM高度相似。Exparser会维护整个页面的节点树相关信息,包括节点的属性、事件绑定等,相当于一个简化版的Shadow DOM实现,性能表现更好同时代码尺寸也较小。

三、原生组件(native组件)

小程序踩过的坑:

为什么官方建议不要在scroll-view中使用input?

为什么页面滚动会出现输入框漂移?

为什么视频组件中的样式无法覆盖,新增功能按钮?

为什么在IDE中调试没问题,到真机上组件展示异常?

......

3-1. native组件优点
  1. 扩展Web的能力。比如像输入框组件(input, textarea)有更好地控制键盘的能力。

  2. 体验更好,同时也减轻WebView的渲染工作。比如像地图组件(map)这类较复杂的组件,其渲染工作不占用WebView线程,而交给更高效的客户端原生处理。

3-2. 运行机制
  1. 在webview创建容器,并将将web端的位置信息和大小信息传给客户端

  2. 客户端创建跟web容器位置大小相同的视图view视图,native组件覆盖webview空白区域

  3. 当webview容器位置或宽高发生变化时,组件会通知客户端做相应的调整,同理客户端位置发生变化也会通知webview

  4. 当用户直接操作原生组件时,webview接收不到对应的事件响应,由native组件监听到事件直接发送至逻辑层,触发webview层渲染。

IOS\安卓原生组件表现相同:native组件覆盖在webview上层

3-3. 原生组件的使用限制:
  • native组件层级最高,所有页面设置z-index无效,无法覆盖在native组件

  • native组件无法在scroll-view中使用,客户端监听不到web页面元素滚动,无法处理滚动更新位置

  • CSS 样式无法应用于native组件

  • 在IDE开发者工具上,原生组件是用 web 组件模拟的,因此很多情况并不能很好的还原真机的表现,建议在使用到原生组件时尽量在真机上进行调试

四、同层渲染

4-1. 同层渲染

同层渲染是为了解决原生组件的层级问题,在支持同层渲染后,原生组件与其它组件可以随意叠加,有关层级的限制将不再存在。但需要注意的是,组件内部仍由原生渲染,样式一般还是对原生组件内部无效。

4-2. IOS方案

当DOM设置overflow为scroll时,IOS系统自动生成WKChildScrollview(WKChildScrollview也为原生组件,webkit内核已经处理了与其他DOM元素间的层级关系),可以监听滚动,原生组件会直接挂载在预先创建好的WKChildScrollview容器下,通过给DOM元素添加backgroungColor属性匹配对应的WKChildScrollview容器(backgroungColor作为id,匹配rgb值),IOS10以上通过className匹配。

4-3. 安卓方案

使用头条自研TTwebview内核,当dom元素设置tt-render-in-browser属性时,可在页面挖一个透明洞,将native组件放在webview下面,解决覆盖问题,通过设置特定属性用于TTwebview滚动监听。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这里是使用 Kbone 实现 web小程序同构的示例: 1. 首先,在你的项目中安装 Kbone: ``` npm install kbone --save ``` 2. 然后在你的项目中创建一个 Kbone 的入口文件,例如 `index.kbone.js`: ```js import Vue from 'vue' import App from './App.vue' // 导出小程序配置 export default { // 小程序配置 config: { pages: ['pages/index/index'], window: { backgroundTextStyle: 'light', navigationBarBackgroundColor: '#fff', navigationBarTitleText: 'Kbone Demo', navigationBarTextStyle: 'black' }, tabBar: { color: '#666', selectedColor: '#b4282d', backgroundColor: '#fafafa', borderStyle: 'black', list: [ { pagePath: 'pages/index/index', text: '首页' }, { pagePath: 'pages/logs/logs', text: '日志' } ] } }, // Vue 实例配置 vue: { render: h => h(App), }, } ``` 3. 在你的项目中创建一个小程序页面文件,例如 `index.ux`: ```html <template> <div> <h1>{{ title }}</h1> <p>{{ content }}</p> </div> </template> <script> export default { data: { title: 'Kbone Demo', content: '这是一个 Kbone 示例页面' }, onLoad() { console.log('页面加载完成') } } </script> <style> h1 { font-size: 32px; color: #b4282d; margin: 40px 0 20px; } p { font-size: 28px; color: #666; line-height: 1.5; } </style> ``` 4. 在你的项目中创建一个 Vue 组件文件,例如 `App.vue`: ```vue <template> <div class="app"> <h1>{{ title }}</h1> <p>{{ content }}</p> </div> </template> <script> export default { data() { return { title: 'Kbone Demo', content: '这是一个 Kbone 示例页面' } }, mounted() { console.log('页面加载完成') } } </script> <style> .app { text-align: center; padding: 40px 0; } h1 { font-size: 32px; color: #b4282d; margin: 40px 0 20px; } p { font-size: 28px; color: #666; line-height: 1.5; } </style> ``` 5. 最后,在你的项目中创建一个启动文件,例如 `server.js`,使用 Kbone 的 `start` 方法启动应用程序: ```js const Kbone = require('kbone') // 创建 Kbone 实例 const app = new Kbone({ // 小程序配置文件路径 mpConfig: { appid: 'your appid', secret: 'your appsecret', token: 'your token', encodingAESKey: 'your encodingAESKey' }, // Kbone 配置 router: { mode: 'history', base: '/' }, webpackConfig: { entry: { 'app': './src/index.kbone.js' } }, // Vue 实例配置 vue: { root: { components: { App: () => import('./src/App.vue') }, template: '<App />' } } }) // 启动应用程序 app.start({ port: 8900, success() { console.log('应用程序启动成功') } }) ``` 这就是一个简单的 Kbone 示例,它实现了 web小程序的同构。当你在浏览器中访问 `http://localhost:8900/` 时,它将显示一个 web 页面;当你在微信小程序中搜索并打开该应用时,它将显示一个小程序页面,两者共享相同的页面内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值