image.png

Kbone 是什么?

Kbone 是一个致力于微信小程序和 Web 端同构的解决方案,在适配层里模拟出浏览器环境,让 Web 端的代码可以不做什么改动便可运行在小程序里。

开发准备

  • 安装脚手架/初始化项目
    npm install -g kbone-cli
    kbone init to-do-list
  • 代码构建
    npm run build
    (具体的页面介绍后面会讲到)
  • 项目运行
    小程序端:npm run mp
    Web端: npm run web

通过两个命令把项目运行起来你就会发现 Kbone 的神奇之处,通过一份代码(这里我是基于 Vue)你就可以拥有两端的效果,再也不用担心同时维护两份代码了。

预览

正式开始之前我们先看看效果图,感受一下 Kbone 框架一份代码跑两端的神奇
image.pngimage.png

Kbone 目录了解

├─ build
│ ├─ miniprogram.config.js // mp-webpack-plugin 配置
│ ├─ webpack.base.config.js // Web 端构建基础配置
│ ├─ webpack.dev.config.js // Web 端构建开发环境配置
│ ├─ webpack.mp.config.js // 小程序端构建配置
│ └─ webpack.prod.config.js // Web 端构建生产环境配置
├─ dist
│ ├─ mp // 小程序端目标代码目录,使用微信开发者工具打开,用于生产环境
│ └─ web // web 端编译出的文件,用于生产环境
├─ src
│ ├─ common // 通用组件
│ ├─ mp // 小程序端入口目录
│ │ ├─ home // 小程序端 home 页面
│ │ │ └─ main.mp.js // 小程序端入口文件
│ │ └─ other // 小程序端 other 页面
│ │ └─ main.mp.js // 小程序端入口文件
│ ├─ detail // detail 页面
│ ├─ home // home 页面
│ ├─ list // list 页面
│ ├─ router // vue-router 路由定义
│ ├─ store // vuex 相关目录
│ ├─ App.vue // Web 端入口主视图
│ └─ main.js // Web 端入口文件
└─ index.html // Web 端入口模板
通过官方给我们的这个目录结构,我们可以很清晰的看到每个目录下各个文件的作用。这里我就对其中的一些文件进行解释一下。

miniprogram.config.js

这个文件是关于小程序端的一些配置,类似于原生的 json 配置

webpack.mp.config.js

小程序端构建配置,也就是构建小程序端代码的 webpack 配置,多页开发中会用到其中的一部分配置。

src/mp & main.mp.js

mp 用来存放小程序端的入口文件,这里设置小程序的一些页面,main.mp.js 相当于一个挂载操作,把它看成 mpvue 里面的 main.js 比较好理解,设置页面路由和挂载映射 Vue 里面的页面。
(其他的比较好理解,我就不一一赘述了)

Kbone 进阶 —- 多页开发

Vue 路由配置

Vue 的路由配置比较简单,直接在 src/router/index.js 下配置就好了,比较简单,不多说。

  1. import Vue from 'vue';
  2. import Router from 'vue-router';
  3. const Home = () => import( /* webpackChunkName: 'Home' */ '@/pages/home/Index.vue');
  4. const About = () => import( /* webpackChunkName: 'About' */ '@/pages/home/about.vue');
  5. const Cart = () => import( /* webpackChunkName: 'Cart' */ '@/pages/cart/index.vue');
  6. Vue.use(Router);
  7. export default new Router({
  8. mode: 'history',
  9. routes: [{
  10. path: '/(home|index)?',
  11. name: 'Home',
  12. component: Home,
  13. },
  14. {
  15. path: '/list',
  16. name: 'About',
  17. component: About,
  18. },
  19. {
  20. path: '/cart',
  21. name: 'Cart',
  22. component: Cart
  23. }
  24. ],
  25. });

小程序端页面建立/挂载

之前已经介绍过 src/mp 下存放的是小程序端的入口文件,也就是相当于小程序端页面的对于 Vue 页面的映射,每个文件夹下很简单,就一个 main.mp.js

  1. import Vue from 'vue'
  2. import Router from 'vue-router'
  3. import {
  4. sync
  5. } from 'vuex-router-sync'
  6. import App from '../App.vue'
  7. import store from '../store'
  8. import Home from '../pages/home.vue'
  9. import KboneUI from 'kbone-ui'
  10. import 'kbone-ui/lib/weui/weui.css'
  11. Vue.use(Router)
  12. const router = new Router({
  13. mode: 'history',
  14. routes: [{
  15. path: '/(home|index)?',
  16. name: 'Home',
  17. component: Home,
  18. }],
  19. })
  20. export default function createApp() {
  21. const container = document.createElement('div')
  22. container.id = 'app'
  23. document.body.appendChild(container)
  24. Vue.config.productionTip = false
  25. sync(store, router)
  26. Vue.use(KboneUI)
  27. return new Vue({
  28. el: '#app',
  29. router,
  30. store,
  31. render: h => h(App)
  32. })
  33. }

小程序入口

配置到了上一步,你可能觉得已经差不多了,因为在 Web 端已经可以通过路由看到效果了,然而在小程序端还看不到具体的效果甚至还在报错,这是因为少了关键的一步 —- 小程序页面入口文件的设置。

举个例子来说,上一步我们是给小程序的页面配好了钥匙,但是还没有把它拿过来去开相应的锁,现在我们就要拿它来开相应的的锁(小程序入口配置) —- webpack.mp.config.js

  1. // webpack.mp.config.js
  2. entry: {
  3. // js 入口
  4. home: path.resolve(__dirname, '../src/mp/home/main.mp.js'),
  5. list: path.resolve(__dirname, '../src/mp/list/main.mp.js'),
  6. cart: path.resolve(__dirname, '../src/mp/cart/main.mp.js'),
  7. },

tabBar 配合

配置好了入口仅仅只能看到首页(/index)的效果,这就需要使用 tabBar 了。

  tabBar: {
      color: "#000000",
      selectedColor: "#ffbf37",
      backgroundColor: "#ffffff",
      list: [{
          pageName: 'home',
          text: '英雄',
          iconPath: path.resolve(__dirname, '../src/assets/image/1.png'),
          selectedIconPath: path.resolve(__dirname, '../src/assets/image/1.png'),
      }, {
          pageName: 'list',
          text: '地图',
          iconPath: path.resolve(__dirname, '../src/assets/image/2.png'),
          selectedIconPath: path.resolve(__dirname, '../src/assets/image/2.png'),
      }],
     // 使用自定义 tabBar
      custom: path.join(__dirname, '../src/custom-tab-bar'),
    },

小结

总的来说使用 Kbone 进行多页开发的步骤就是:

  1. 设置 Vue 路由
  2. 建立对应页面并进行小程序页面挂载注册
  3. 修改小程序入口并配置对应的路由(如果有需要可以继续配置 tabBar)

    采坑总结

    关于样式

    scss配置

    小程序默认是less语言,如果要使用scss语言需要配置scss。

    命令行:npm install sass node-sass sass-loader —save

// 文件 webpack.dev.config.js
{
  test: /\.(scss|sass)$/,
    use: [{
      loader: 'vue-style-loader',
    }, {
      loader: 'css-loader',
    }, {
      loader: 'postcss-loader',
      options: {
        plugins: [
          autoprefixer,
        ],
      }
    }, {
      loader: 'sass-loader',
    }],
}
// 文件 webpack.mp.config.js
 {
   test: /\.(scss|sass)$/,
     use: [{
       loader: MiniCssExtractPlugin.loader,
       options: {
         modules: true,
       },
     },
           {
             loader: 'css-loader'
           },
           {
             loader: 'postcss-loader',
             options: {
               ident: 'postcss',
               plugins: () => {
                 return [
                   autoprefixer,
                   stylehacks(), // 剔除 ie hack 代码
                 ]
               }
             }
           },
           {
             loader: 'sass-loader'
           }
          ],
 },
// 文件 webpack.prod.config.js

   {
     test: /\.(scss|sass)$/,
       use: [{
         loader: MiniCssExtractPlugin.loader,
       }, {
         loader: 'css-loader',
       }, {
         loader: 'postcss-loader',
         options: {
           plugins: [
             autoprefixer,
           ],
         }
       }, {
         loader: 'sass-loader',
       }],
   }

图片资源

在写 demo 的时候发现一个问题,页面需要的图片资源构建的时候始终带不过去,查了一下官方提供的文档,目前暂不支持相对路径,静态资源可以考虑转成 base64 或者使用网络地址,我这里的处理办法是将图片上传到重构服务器,然后在webpack配置公共url,页面写相对路径,就能正常显示啦。(注意:本地的图片名要与服务器的图片名保持一致)

// webpack.base.config.js
{
  test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    loader: 'url-loader',
      options: {
        name: '[name].[ext]',
          limit: 1024,
            publicPath: 'https://test.mwegame.qq.com/ui/images', // 对于资源文件直接使用线上的 cdn 地址
      },
}
 // webpack.mp.config.js
   {
     test: /\.(png|jpg|jpeg|gif|svg|eot|woff|woff2|ttf)$/,
       use: [{
         loader: 'url-loader',
         options: {
           limit: 1024,
           name: '[name].[ext]',
           publicPath: 'https://test.mwegame.qq.com/ui/images', // 对于资源文件直接使用线上的 cdn 地址
           emitFile: false,
         }
       }],
   },

小程序适配

rpx 在 kbone 中好像不支持,尝试过 vue+kbone 对 web 端采用px适配,在构建小程序时希望能转成rpx,但可惜的是不会这样,去微信开放社区看了一下说要用 rem 做适配(要在 mp-webpack-plugin 这个插件的配置中的 global 字段内补充 rem 配置)

// miniprogram.config.js
 global: {
   rem: true
 },
// 结合重构适配规则,需要在App.vue做适配
created() {
  window.onload = function () {
    if (process.env.isMiniprogram) {
      document.documentElement.style.fontSize = 100 / 1080 * wx.getSystemInfoSync().screenWidth + "px"
    }
  }
}

关于分包

由于小程序最大限制是2048KB,我这边小程序构建出的代码超出这个限制就预览不了,后面我配置了小程序分包,查看分包成功了但是小程序还是报超出限制。查了资料,原来是kbone打包的mp文件不会自动更新,需要手动删除重新打包,小程序工具也需要重新打开。

    subpackages: {
      // all:['all'], // 默认首次进入的页面不需要配置分包
      login: ["login"],
      home: ["home"]
    },
  },

image.png

构建npm样式

开发者工具报错 Uncaught Error: module "pages/ home/miniprogram-render" is not defined
解决方案:开发者工具重新构建 npm
如果还是无法解决,删除打包出来的小程序文件,重新打包

总结

kbone解决了长久以来微信小程序和 Web 两个端的代码问题,在实际中可以少写一份代码,极大的减轻了开发和维护的工作量,虽然目前还存在一些 bug,但是我相信开发团队一定会努力的完善它。如果你觉得有用的话也用起来吧~ (Kbone 官方技术文档👉文档)