专栏首页极乐技术社区kbone,十分钟让 Vue 项目同时支持小程序

kbone,十分钟让 Vue 项目同时支持小程序

微信小程序开发过程中,许多开发者会遇到 小程序 与 Web 端一起的需求,由于 小程序 与 Web 端的运行环境不同,开发者往往需要维护两套类似的代码,这对开发者来说比较耗费力气,并且会出现不同步的情况。

小程序作为web的配套必需品,已经成为开发者不可避免的工作。多终端、多形态的开发无疑徒增工作量,费力不讨好。Kbone 就是一个致力于微信小程序和 Web 端同构的解决方案。一套代码同时开发完微信小程序以及web端。

基本结构

首先,我们来看下一个基本的 kbone 项目的目录结构(这里的 todo 是基于 Vue 的示例, kbone 也有 React , Preact , Omi 等版本,详情可移步 kbone github )。

因为 kbone 是为了解决 小程序 与 Web 端的问题,所以每个目录下的配置都会有两份(小程序 与 Web 端各一份)

入口

不管是 小程序 端还是 Web 端,都需要入口文件。在 src/index 目录下, main.js 为 Web 端用主入口, main.mp.js 则为 小程序 端用主入口。

当然,Web 端会比 小程序 多一个入口页面,即 index.html (位于根目录下)。

下面两段代码分别是 小程序端 入口与 Web 端入口的代码,可以看到 小程序端的入口代码封装在 createApp 函数里面(这里固定即可),内部会比 Web 端多一个创建 app 节点的操作,其他的基本就是一致的。

todo.vue

在上面的入口图可以看到,源码目录中,除了入口文件分开之前,页面文件就是共用的了,这里直接使用 Vue 的写法即可,不用做特殊的适应。

配置

写完代码之后,我们要怎么跑项目呢?这时,配置就派上用场啦。

Web 端配置为正常的 Vue 配置,小程序端配置与 Web 端配置的唯一不同就是需要引入 mp-webpack-plugin 插件来将 Vue 组件转化为小程序代码。

构建代码

接着,我们需要构建代码,让代码可以运行到各自的运行环境中去。构建完成后,生产代码会位于 dist 目录中。

小程序端 的构建会比 Web 端的构建多一个步骤,就是 npm 构建。

进入 dist/mp 目录,执行 npm install 安装依赖,用开发者工具将 dist/mp 目录作为小程序项目导入之后,点击工具栏下的 构建 npm ,即可预览效果。

效果

最后,我们来看一下 todo 的效果。kbone 初体验,done~

todo 代码可到 kbone/demo13 自提。

具体方案实现

接下来就来探讨下具体方案的实现。

社区 Web 端是基于 Vue 实现的,使用了 Vue-router、Vuex 等插件。Vue 想必大家挺熟悉的了,它是市面上一款非常流行的 Web 框架,提供组件化等特性,其原理大致如下:

Vue 模板可以认为是一种附加了一些特殊语法的 HTML 片段,一般来说一份 Vue 模板对应一个组件,在构建阶段编译成调用 Dom 接口的 JS 函数,调用此 JS 函数就会创建出组件对应的 Dom 树片段进而渲染到浏览器上。小程序里是支持运行 JS 的,但是这里用到的 Dom 接口和渲染到浏览器上的功能小程序不具备,所以无法直接将 Web 端社区代码移植到小程序中。原因就在于小程序为了安全和性能而采用了双线程的架构,运行用户 JS 代码的逻辑层是一个纯粹的 JSCore,没有任何浏览器相关的实现,这里得想办法将 Web 端代码转成小程序代码。

业界常见做法:将 Vue 模板直接转成小程序的 WXML 模板

那么问题来了,如何将 Vue 代码转成小程序代码?这里先看下业界常见的做法:将 Vue 模板直接转成小程序的 WXML 模板。

使用做法相当于抛弃了浏览器中建 Dom 树的过程,而是直接交由小程序来对模板进行编译创建出小程序的模板树,进而渲染到小程序页面中。

一般来说这个做法对于普通场景是够用的,但是对于一些更复杂的场景就很不好处理了,比如社区中的一个简单例子:社区帖子详情展示富文本内容,点击内容中的图片可预览。

这主要是因为 Vue 模板和 WXML 模板的语法并不是直接对等的,Vue 的特性设计也和小程序的设计无法划等号,这自然就导致了部分 Vue 特性的丢失。比如像 Vue 中的 v-html 指令、ref 获取 Dom 节点、过滤器等就通通用不了。当然不止是 Vue 自身的特性,一些原本依赖 Dom/Bom 接口的 Vue 插件也无法使用,比如 Vue-router 等,而这些正是社区高度依赖的,在不对社区代码做大范围改造的话是无法使用此方案的。

此路不通,那还有其他的方法么?

换个思路:做一个适配层

答案是有的,这里我们就得换一种思路来解决这个问题。回到最初的点上,我们无法将 Web 端代码移植到小程序中是因为小程序没有 Dom 接口,那么我们想办法做出一个适配层,将这个差异给抹掉不就行了么?

有了想法就要实施,仿造出 Dom 接口并不难,事实上在 Nodejs 端就有人做过类似的事,比如 jsDom 这个库的实现,让我们可以在没有真实浏览器环境下可以对一些依赖 Dom 接口的 Web 端代码进行测试。

仿造了 Dom 接口给 Vue 调用,进而创建出了仿造 Dom 树。根据前面提到的小程序架构,用户的 JS 代码是执行在逻辑层的,也就是说我们创建出的 Dom 树也是存在与逻辑层的内存之中,接下来要解决的难题是如何将这棵 Dom 树渲染到小程序页面中。

这里需要先简单介绍一下小程序的渲染原理:小程序的双线程架构,逻辑层会执行用户的 JS 代码进而产生一组数据,这组数据会发往视图层;视图层接收到数据后,结合用户的 WXML 模板创建出组件树,之后小程序再将组件树渲染出来。这里的组件树和 Dom 树很类似,只是它是由官方内置组件或自定义组件拼接而成而不是 Dom 节点。这里我们能不能将仿造出来的 Dom 树映射到小程序的组件树上?

小程序组件树是根据 WXML 模板创建出来的,而仿造 Dom 树结构是不稳定的,我们无法提前预知它会生成什么样的结构,也就无法提前准备后可以描述任意 Dom 树的 WXML 模板,除非直接将 Vue 模板转换成 WXML 模板,但这样又绕回前面的问题上了。

小程序组件树中的组件有两种:内置组件和自定义组件,内置组件是由官方提供的如 video、map 这样的组件,而自定义组件是一种支持由用户利用现有组件自行组装的组件,能否利用它来做些什么?

使用 Web 端概念来做个简单解释,内置组件就像是 div、span 这些 HTML 标签,而自定义组件就像是 Web 中的 Vue 组件。Vue 组件可以将 HTML 标签以及其他的 Vue 组件进行组装,自定义组件同理,主要用于功能模块的抽象、封装和复用。不过自定义组件有个很奇妙的特性,它支持自引用,也就是说它可以自己引用自己来进行组装。

自定义组件可以自己引用自己,那么我们就可以利用这个特性来进行递归创建组件,进而创建出一棵组件树:

比如上图的例子,我们封装了一个 custom-dom 组件,这个组件里面也使用了 custom-dom 组件用于渲染子组件。那么只要我们执行一下 setData,把 children 数据传递过去就可以创建出子组件,子组件本身也是 custom-dom 组件,它同样可以执行这个逻辑把各自的子组件创建出来,这样就实现了组件的递归创建,只要我们拥有完整的 Dom 树结构,就可以创建出相对应的一棵组件树。

这里递归的终止条件是遇到特定节点、文本节点或者孩子节点为空。然后在创建出组件树后,将 Dom 节点和自定义组件实例进行绑定以便后续的 Dom 更新和操作即可。

我们将其归纳为两个模块:仿造接口和自定义组件。正因为这个方案是通过提供适配器的方式来仿造出 Web 环境,所以用户代码不需要做任何魔改,大部分特性都可以继续使用不需要被删减,比如 vue-router、window.location 操作等。

原本 Web 端代码是基于 Vue 来搭建的,其中还用到了诸多插件/库,如 Vue-router、Vuex、Markdown-it 等,同时还支持了服务端渲染。但是不管 Web 端是怎么实现的,底层终究是调浏览器的那些接口,所以对于用户层面的代码我们不做任何调整,只是将浏览器那一层替换掉即可。

整个构建流程是基于 Webpack 来实现的,使用 Kbone 构建出小程序代码也是基于 Webpack 来实现,只需要在原本 Web 端构建流程上实现一个 Webpack 插件,在构建原本 Web 端代码到小程序端时追加 Kbone 和一些小程序相关的代码即可。

本文分享自微信公众号 - 极乐技术社区(wxapp-union)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-03-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 基于 Vue.js 的小程序开发框架

    基于 Vue.js 的小程序开发框架,从底层支持 Vue.js 语法和构建工具体系。

    极乐君
  • 详解微信原生小程序架构及同构方案

    最近实习中参与了H5项目向小程序迁移的工作,在微信官方文档和一些帖子上学习了小程序运行机制和底层原理,以及与Web页面的区别,在此基础上又看了一些关于小程序同构...

    极乐君
  • 『组件』大转盘、刮刮乐、老虎机……

    组件 框架为开发者提供了一系列基础组件,开发者可以通过组合这些基础组件进行快速开发。 什么是组件: 组件是视图层的基本组成单元。 组件自带一些功能与微信风格的样...

    极乐君
  • day24 03 多继承

     A,B,C,D四个类,其中D类继承A,B,C三个父类,因此也叫多继承,子类方法调用的时候先找自己里面的,没有再根据就近原则逐个找父类里面的,最后没有还是会报错

    py3study
  • iOS开发之WidgetKit

    iOS 14 Apple 推出了 WidgetKit,Widget 就像一个迷你版的 App,可以快速访问它所提供的信息—比如天气、日历事件、笔记等。Widge...

    YungFan
  • win10 uwp url encode

    开发中,经常遇到使用中文无法作为 URL 传输的情况,如果想把 中文作为 URL 传输,那么需要对中文进行转换。 UWP 提供一些方法让我们很容易把 中文转为 ...

    林德熙
  • 『2018年1月知识点合集』

    谢伟
  • 自然语言处理-搜索中常用的bm25

    BM25算法是一种常见用来做相关度打分的公式,思路比较简单,主要就是计算一个query里面所有词和文档的相关度,然后在把分数做累加操作,而每个词的相关度分数主要...

    学到老
  • Python Web聊天室--首页

    在layout里创建base.html(基础模板,我们可以在其他的模板里调用这个,可以减少代码重复率)

    py3study
  • 猿实战14——前台类目之广告牌设置

    上一个章节,猿人君教会了你如何去实现前台类目,虽然已经讲得很详细了,还是有朋友认为比较困难。可能是每个人的基础真的不一样吧。

    山旮旯的胖子

扫码关注云+社区

领取腾讯云代金券