美文网首页
kbone + vue 问题集合

kbone + vue 问题集合

作者: 苏基 | 来源:发表于2020-03-30 00:15 被阅读0次

    使用kbone开发的微信小程序,只需要做稍微的修改就可以用到 web 端和 qq 小程序,很方便。

    因为我之前的项目工程化程度很高,所以在刚开始用的时候因为惯性思维遇到了一些问题。

    1. 多页面的配置

    在 kbone + vue 的项目中可以使用 vue-router,但是web用一个router文件,小程序的 router 要另写。

    我们在官方给出的 vue 项目模版的基础上做了一些目录调整:

    |-- build
    |   |-- miniprogram.config.js // 小程序配置文件
    |   |-- webpack.base.config.js // webpack 基础配置文件
    |   |-- webpack.dev.config.js // 开发环境 webpack 配置文件
    |   |-- webpack.mp.config.js // 小程序环境 webpack 配置文件
    |   |-- webpack.prod.config.js // 线上环境 webpack 配置文件
    |-- src
    |   |-- components // 原来是common,放公用组件
    |   |-- views // 放页面
    |   |   |-- home
    |   |   |   |-- Index.vue
    |   |   |   |-- mp
    |   |   |       |-- main.mp.js // home 模块的入口文件
    |   |   |-- article
    |   |       |-- Index.vue
    |   |       |-- mp
    |   |           |-- main.mp.js // article 模块的入口文件
    |   |-- router // web 端路由
    |   |-- store
    |   |-- App.vue
    |   |-- main.js
    

    重要改动:

    1. 把页面文件统一放到 views 文件夹下;
    2. 把 mp 目录中的内容拆分到每个模块下;

    现在我们要新增一个页面,文章详情页面,/article/:id,首先我们需要在 src/article/mp/main.mp.js 中添加路由:

    // src/article/mp/main.mp.js
    import ArticleDetail from '@/views/article/Index.vue'; // 注意对应的页面文件要用 import 引入,不要在 routes 里用 () => import('路径') 的方式引入
    
    const router = new Router({
      mode: 'history',
      routes: [
        {
          path: '/articles',
          name: 'ArticleList',
          component: ArticleList
        }
      ]
    });
    
    // miniprogram.config.js
    module.exports = {
      ...
      router: {
        home: ['/(home|index)?'],
        ariticle: ['/ariticles'] // 在此处添加新增的路由
      }
      ...
    }
    
    //  webpack.mp.config.js
    module.exports = {
      ...
      entry: {
        home: path.resolve(__dirname, '../src/views/home/mp/main.mp.js'),
        article: path.resolve(__dirname, '../src/views/article/mp/main.mp.js') // 在此处添加新增的模块入口
      }
      ...
    }
    

    这里要注意:

    1. 页面文件的引入方式,如果采用动态引入的写法,则小程序将无法正常显示页面。
    2. 每添加一个页面路由,要注意在对应模块的mp文件入口、miniprogram.config.jswebpack.mp.config.js 文件中做对应的配置修改。

    2. tabbar 的配置

    tabbar 的配置主要写在 miniprogram.config.js 中的 generate 属性里:

    // miniprogram.config.js
    module.exports = {
      ...
      generate: {
        tabBar: {
          color: '#8E8E93',
          selectedColor: '#0076FF',
          backgroundColor: '#FAFAFA',
          list: [
            {
               pageName: 'home',
               text: '首页',
               iconPath: path.resolve(__dirname, '../src/img/home.png'),
               selectedIconPath: path.resolve(__dirname, '../src/img/home-sel.png')
             },
            {
               pageName: 'article',
               text: '文章',
               iconPath: path.resolve(__dirname, '../src/img/article.png'),
               selectedIconPath: path.resolve(__dirname, '../src/img/article-sel.png')
             }
          ]
        }
      }
      ...
    }
    

    这里要注意:
    所有 tabbar 的页面在注册路由时,需要有一个 / 路径,比如,我们把 article 页面作为一个 tab 页,那么我们需要改写一下注册的路由写法:

    const router = new Router({
      mode: 'history',
      routes: [
        // 新增 / 指向和 /articles 一致
        {
          path: '/',
          name: 'ArticleList',
          component: ArticleList
        },
        {
          path: '/articles',
          name: 'ArticleList',
          component: ArticleList
        }
      ]
    });
    

    3. 网络请求

    因为在微信小程序中不能直接使用 axios,需要用 wx.request,而 web 端不能用 wx.request,所以我们需要找一个在 web 端和小程序上都能进行正常网络请求的方案。这里我们采用了 axios + adapter,即 axios-miniprogram-adapter

    使用的时候注意做一下环境判断:

    import axios from 'axios'
    import mpAdapter from 'axios-miniprogram-adapter'
    if (process.env.isMiniprogram) {
      axios.defaults.adapter = mpAdapter
    }
    

    4. 尺寸适配

    微信小程序和qq小程序都可以用官方推荐的 rpx 做适配,我们要做的是对 web 端做一下兼容。

    我们可以在 webpack 中的 sass-loader 里写一个全局变量 $isMini 做环境的区分,然后我们就可以写一个转换设计稿尺寸的方法,在这个方法里借助前面注册的变量进行尺寸转换(假设设计稿是 750px 宽的):

    // 各个 webpack 配置文件
    {
      loader: 'sass-loader',
      options: {
        prependData: '$isMini: false;'
      }
    }
    
    // 样式文件
    @function px($px) {
      @if $isMini {
        @return #{$px}rpx; // 小程序端尺寸处理
      } @else {
        @return $px; // web 端的处理,按自己的需求写
      }
    }
    

    这里要注意的是 rpx 和 px 的转换关系,以 iphone6 为例: 750rpx = 375px。

    微信小程序也支持 rem,设置的时候需要在 miniprogram.config.js 的 global 字段中配置:

    
    // miniprogram.config.js
    module.exports = {
      ...
      global: {
        rem: true
      }
      ...
    }
    

    因为我们用的是 sass,而官方模版用的是 less,这里就顺便提一下,如果要用 sass,记得要在 build 目录下的各个 webpack 文件中进行对应的 loader 配置哦。

    5. UI 库 Kbone-ui 的使用

    这个库可以在小程序和 web 端都能正常使用。

    要注意的是使用的时候,不要忘记引入样式文件哦:

    import { KScrollView, KToast } from 'kbone-ui';
    import 'kbone-ui/lib/weui/weui.css'; // 样式文件
    

    刚接触小程序的朋友还可以看一下微信原生组件的使用,因为 kbone-ui 的文档写的不是很详细,有些还是结合原生组件的文档好理解一点。

    包括内置组件的使用,也可以结合原生组件的文档使用。

    6. 自定义导航

    微信小程序现在支持单页面自定义导航,但是 kbone 如果要自定义导航,所有页面的导航都需要自己写。

    导航条分为两部分,statusbar(显示时间的部分)和 titlebar (标题栏)。

    image.png

    statusbar 的高度:

    const systemInfo = wx.getSystemInfoSync()
    const statusbarHeight = systemInfo.statusBarHeight; // statusbar 的高度
    

    titlebar 的高度:

    const rect = wx.getMenuButtonBoundingClientRect();
    const titlebarHeight = rect.bottom + rect.top - statusBarHeight * 2; // titlebar 的高度
    

    wx.getMenuButtonBoundingClientRect 这个 api 会返回胶囊的信息,官方文档

    导航条的高度:

    const totalHeight = statusbarHeight + titlebarHeight;
    

    如果是用在微信小程序上,那么现在这两个 api 的使用时机不需要特别注意,但如果是同时要用在qq小程序上,就需要注意使用的时机啦,如果直接在计算属性或者 created 中使用会出现报错,取不到内容的情况。

    我是在created 中用了 settimeout 延迟使用这两个 api来解决这个问题的。

    // NavigationBar.vue
    // 请求导航条的信息
    import getMenuBarRect from './getMenuBarRect.js'
    data() {
      return {
        systemInfo: null
      }
    }
    created() {
      setTimeout(() => {
        this.systemInfo = wx.getSystemInfoSync() || null;
        const rect = getMenuBarRect(this.systemInfo)
        // 高度计算和上面的一样
      }
    }
    
    // getMenuBarRect.js
    // setStorage 是自己封装的方法,小程序环境用 wx.setStorageSync,web端用 window.localStorage
    import { setStorage, getStorage, clearStorage } from '@/utils/storage';
    
    export default function getMenuBarRect(systemInfo) {
      let systemInfoLocal = null;
      try {
        const value = getStorage('key');
        if (value) {
          systemInfoLocal = JSON.parse(value);
        }
      } catch (e) {
        console.log('error', e);
      }
    
      if (!systemInfoLocal && systemInfo) {
        systemInfoLocal = systemInfo;
        setStorage('systemInfo', JSON.stringify(systemInfo));
      }
    
      let rect = null;
      try {
        const rectApi = process.env.isMiniprogram
          ? wx.getMenuButtonBoundingClientRect
          : process.env.isQQprogram
          ? qq.getMenuButtonBoundingClientRect
          : null;
        rect = rectApi ? rectApi() : null;
        if (rect === null) {
          throw 'getMenuButtonBoundingClientRect error rect = null';
        }
        //取值为0的情况
        if (!rect.statusBarHeight) {
          throw 'getMenuButtonBoundingClientRect error rect.value = 0';
        }
      } catch (error) {
        let gap = ''; //胶囊按钮上下间距 使导航内容居中
        let width = 96; //胶囊的宽度,android大部分96,ios为88
    
        if (!systemInfoLocal) {
          rect = {
            //获取不到胶囊信息就自定义重置一个
            bottom: 60,
            height: 32,
            left: 365 - width,
            right: 365,
            top: 28,
            width: width
          };
        } else {
          if (systemInfoLocal.platform === 'android') {
            gap = 8;
            width = 96;
          } else if (systemInfoLocal.platform === 'devtools') {
            if (systemInfoLocal.system.toLowerCase().indexOf('ios')) {
              gap = 5.5; //开发工具中ios手机
            } else {
              gap = 7.5; //开发工具中android和其他手机
            }
          } else {
            gap = 4;
            width = 88;
          }
          if (!systemInfoLocal.statusBarHeight) {
            //开启wifi的情况下修复statusBarHeight值获取不到
            systemInfoLocal.statusBarHeight =
              systemInfoLocal.screenHeight - systemInfoLocal.windowHeight - 20;
          }
          rect = {
            //获取不到胶囊信息就自定义重置一个
            bottom: systemInfoLocal.statusBarHeight + gap + 32,
            height: 32,
            left: systemInfoLocal.windowWidth - width - 10,
            right: systemInfoLocal.windowWidth - 10,
            top: systemInfoLocal.statusBarHeight + gap,
            width: width
          };
        }
        console.log('error', error);
        // console.log('rect', rect);
      }
      return rect;
    }
    

    getMenuBarRect 这个方法是参考了网上找到的一个作者的方法,做了点修改,修改后可以正常使用,但是有个问题,因为 systeminfo 一开始取不到值,会用默认值,后来取到正确值时自定义头部的高度会有个突变,就是会跳一下。这个还没有优化。

    7. 其他问题

    比如,自定义分享内容的path要写全;
    appid在微信小程序里叫appid,在qq小程序配置里叫qqappid;
    微信小程序图片可以只设置宽度不设置高度,这样不会变形;
    qq小程序图片只设置宽度不设置高度,图片会显示不出来,需要加属性mode="widthFix"
    页面的下拉刷新、加载中提示、上拉加载更多等配置需要在miniprogram.config.js 里配置;
    全局滚动事件也需要先配置,才能用 window.addEventListener('scroll', () => {...})
    而且要取到 scrolltop 要用 document.body.$$getBoundingClientRect

    总而言之呢就是多看文档、仔细看文档啦。

    页面跳转

    window.open相当于 navigateTo,页面打开方式为 open;
    window.location.href相当于redirectTo,页面打开方式为 jump;

    不可否认它确实有很多方便的地方,但是在样式方面和组件支持方面简直令人抓狂。另外,编译速度和开发者工具都很慢。真的是一言难尽。今天又是修bug的一天,修到吐。

    相关文章

      网友评论

          本文标题:kbone + vue 问题集合

          本文链接:https://www.haomeiwen.com/subject/wylpuhtx.html