标签 uni-app 下的文章

引言

相信大家平常在开发过程中,经常会遇到此类需求。危险操作(如删除,退出登录)往往需要弹框提示给用户二次确认一下。本文介绍的是,如何使用js方法装饰器,封装二次确认弹框操作,增加小伙伴的开发效率。

装饰器介绍

2020-08-03T08:01:10.png

装饰器是对类、函数、属性之类的一种装饰,可以针对其添加一些额外的行为。详细使用,自行百度!

常规手段

view

<div class="bg-white flex justify-center">
    <div class="btn-primary" @click="clickMe">点我</div>
</div>

js

clickMe() {
    uni.showModal({
      title: "点击提示",
      content: "你确定要点我?",
      success: (res) => {
        if (res.confirm) {
          console.log("点我干啥");
        }
      },
    });
 }

效果展示

装饰器点击效果

装饰器写法

confirm.js

function confirm(message: string, title: string, cancelFn = function() {}) {
  return function(target: any, name: string, descriptor: any) {
    const originFn = descriptor.value;
    descriptor.value = function(rest: any) {
      uni.showModal({
        title: title,
        content: message,
        success: (res) => {
          if (res.confirm) {
            originFn.apply(this, rest);
          }
        },
      });
    };
  };
}

main.js

@confirm("你确定要点我?","点击提示")
  clickMe() {
    // uni.showModal({
    //   title: "点击提示",
    //   content: "你确定要点我?",
    //   success: (res) => {
    //     if (res.confirm) {
    //       console.log("点我干啥");
    //     }
    //   },
    // });
    console.log("点我干啥");
  }

效果展示

装饰器点击效果

u=3276836620,1954497454&fm=26&gp=0.jpg

remove-files-webpack-plugin

原理就是在项目中添加一个webpack插件,然后配置插件
项目根目录新增vue.config.js

const path = require('path')
const RemovePlugin = require('remove-files-webpack-plugin')

module.exports = {
    configureWebpack: {
        plugins: [
            new RemovePlugin({
                after: {
                    root: path.join(__dirname, './unpackage'),
                    include: [
                        path.join(__dirname, 'unpackage/dist', process.env.NODE_ENV === 'production' ?
                            'build' : 'dev', process.env
                            .UNI_PLATFORM, './mp-weixin/static/APPPIC')
                    ],
                    trash: false
                }
            })
        ]
    }
}

部分webpack类似的webpack插件

  1. copy-webpack-plugin
  2. clean-webpack-plugin

为什么需要?

微信小程序里面页面与自定义组件的区别较大,而页面的可复用程度太低了。要么就是跳转页面,要么就是copy代码。如果我们要增加代码的可复用性,就可以借用Vue组件的思想,将页面改写为自定义组件。

结构对比

两者的视图文件,样式文件没有什么区别。主要区别最大的就是js文件,我们首先来看一下两者的结构。

page.js

page({
  // 数据内容
  data:{},
  // 生命周期函数
  onLoad(){},
  // 一些自定义函数
  someMethods(){}
})

componet.js

componet({
  // 预设属性
  properties: {},
  // 数据内容
  data: {},
  //组件所在页面生命周期
  pageLifetimes: {},
  // 生命周期函数-挂载
  attached: function() {
    // 在组件实例进入页面节点树时执行
  },
  // 生命周期函数-卸载
  detached: function() {
    // 在组件实例被从页面节点树移除时执行
  },
  // 自定义函数写结构里面
  methods:{
    someMethods(){}
  }
})

我们可以看到,两者的基本相似,大同小异。所以改写起来就特别方便啦~

  1. 将page改为componet
  2. 适配生命周期函数,例如将页面onLoad()函数改写为组件的attached()函数
  3. 将自定义函数someMethods()写到Componet的methods里面

引言

熟悉uni-app的人应该都知道,uni-app并未对微信小程序云函数(本文统称云函数)进行相应的适配。但是,如果我们在某些业务场景的下需要使用云函数呢?我们知道,云函数可以复制到微信开发者工具,这样的话我们不得不每次编译一次就手动复制一次,不得不说麻烦至极。本文就问题做出以下解决方案。

本文环境

  1. Hbuilder X

Hbuilder X版本

  1. 微信开发者工具

微信开发者工具版本

创建云函数目录

首先,我们需要在uni-app项目文件夹下,创建一个云函数目录,路径随意,我这里是functions。然后先随便在里面放一些文件,这里以new_file.css为例。

修改manifest.json

在uni-app根目录下,修改manifest.json中的微信小程序项,结构如下

"mp-weixin" : {
        /* 小程序特有相关 */
        "appid" : "wxd7de467f6e6cf741",
        "cloudfunctionRoot": "./functions/", // 这一行就是标记云函数目录的字段
        "setting" : {
            "urlCheck" : false
        },
        "usingComponents" : true
    }

编写vue.config.js

  1. 我们在项目根目录创建vue.config.js文件
  2. 写入以下内容(如路径不一样请做相应适配)
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {
    configureWebpack: {
        plugins: [
            new CopyWebpackPlugin([{
                from: path.join(__dirname, 'cloudFunctions'),
                to: path.join(__dirname, 'unpackage/dist', process.env.NODE_ENV === 'production' ? 'build' : 'dev', process.env
                    .UNI_PLATFORM, 'cloudFunctions')
            }])
        ]
    }
}
  1. 编译运行

发现提示如下内容

提示内容

说明未安装copy-webpack-plugin插件,我们手动安装一下。

npm install -save copy-webpack-plugin
TIPS: 截至2020.6.4, uni-app暂不支持copy-webpack-plugin 6.0版,请安装5.0版

安装copy-webpack-plugin

然后编译运行,发现微信开发者工具里面出现以下内容。

微信开发者工具里面的内容


截止目前,已打通Hbuilder X到微信开发者工具的自动复制,即已解决本文的核心内容。以下为进一步测试。

创建云函数

我们在云函数根目录上右键,在右键菜单中,可以选择创建一个新的 Node.js 云函数,我们将该云函数命名为check。开发者工具在本地创建出云函数目录和入口 index.js 文件,同时在线上环境中创建出对应的云函数。创建成功后,工具会提示是否立即本地安装依赖,确定后工具会自动安装 wx-server-sdk。我们会看到以下内容。

创建好后将其同步复制到uni-app项目,即可为以后自动同步行方便,又可避免在输出文件夹中云函数的意外丢失。至此,相关文件编写工作转至Hbuilder X,云函数上传部署依旧在微信开发者工具。

云函数模板内容

编写云函数

默认的云函数只是一个返回用户基本数据的内容,我们将其修改至满足我们的业务需求,以内容安全云调用为例。

在云函数文件中写入以下内容

// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init()

// 云函数入口函数
exports.main = async(event, context) => {
  try {
    console.log('待检测文本:' + event.content);
    let result = await cloud.openapi.security.msgSecCheck({
      content: event.content
    })
    console.log('result:' + JSON.stringify(result));

    if (result && result.errCode.toString() === '87014') {
      return {
        code: 300,
        msg: '内容含有违法违规内容',
        data: result
      }
    } else {
      return {
        code: 200,
        msg: 'ok',
        data: result
      }
    }

  } catch (err) {
    if (err.errCode.toString() === '87014') {
      return {
        code: 300,
        msg: '内容含有违法违规内容',
        data: err
      }
    }
    return {
      code: 400,
      msg: '调用security接口异常',
      data: err
    }
  }
}

权限申明

云函数config权限声明

如上图,在函数目录下,创建一个config.json,文档说会自动创建,但是我实际操作时未自动创建。config.json内容如下。

{
    "permissions": {
        "openapi": [
            "security.msgSecCheck"
        ]
    }
}

然后在函数目录右键,上传并部署。

小程序调用云函数

App.vue

<script>
  export default {
    onLaunch() {
      wx.cloud.init();
    }
  }
</script>

index.vue

let res = await wx.cloud.callFunction({
          name: 'checkText',
          data: {
            "content": this.displayString
          }
        })
        if (res.result.code != 200) {
          uni.showModal({
            title: "温馨提示",
            content: "你所输入的内容可能含有违法违规内容,不支持进行下一步操作"
          })
          return
        }

效果展示

微信图片_20191022110949.png

引言:此方法可用作大部分微信小程序支持,但uni-app文档中却找不到相关说明的API

需求

需要在微信小程序中,实现一个中间图标突出显示的异形导航栏。

如下图

2019-08-14T09:17:36.png

实现方法设计

要做这种异形的导航栏,用直接在配置文件里面写list的方法肯定做不到。那么,就有以下两种可替代方法。

  1. 在每一个页面都加载一个tabBar组件,与页面同时渲染。
  2. 设置自定义tabBar,修改tabBar的样式。

优缺点分析:方法1实现起来略为简单,但是会出现代码可重用率低,降低性能,已经界面跳动等问题。方法2则是微信官方提供的,自定义方式,相信在性能方面也会有很大的优势。故选择方法2。

1. 查看文档及官方Demo

官方文档

简要描述一下就是需要在根目录中加入一个custom-tab-bar目录,里面的文件结构与自定义组件的结构一致。然后再在小程序配置文件中修改tabbar为custom模式。

官方代码

主要重点为三个部分

  • 配置文件

配置文件

  • custom-tab-bar目录

2019-08-14T09:25:15.png

  • 页面生命周期中的设置索引方法

2019-08-14T09:26:43.png

这段代码其实很容易理解,pageLifetimes就是监听组件所在页面的生命周期。上述代码就是监听页面显示。当页面显示后,获取到tabBar的对象,然后再设置tabBar中的index索引。

2. 迁移到uni-app框架

上面的方法是使用微信小程序的开发方式,而我使用的是uni-app框架开发微信小程序的。所以我们需要把它们移植到uni-app框架内。

  • 配置文件的修改

uni-app中,page.json被编译为微信小程序的app.json。所以,我们直接修改page.json

page.json所需要的修改

  • custom-tab-bar目录的适配

我们知道,uni-app使用的是类Vue开发,将一个Vue文件编译为四个微信页面文件(wxml,wxss,json,js)。那么,是否可以直接写一个custom-tab-bar.vue的文件呢?刚开始我也是这么想的,后来发现uni-app只会编译page目录和component目录下的vue文件。而微信小程序要求custom-tab-bar必须在项目的根目录下。那么就只能在uni-app下创建一个custom-tab-bar目录,并老老实实写微信四件套了。

custom-tab-bar目录的适配

写完后,uni-app会将该目录完美的复制至微信小程序项目的根目录。
  • tab页面内的适配方法

这个在我实际开发中,是最令我头痛的了。因为微信小程序的this引用与uni-app的this引用并不相同。所以如果直接复制代码是会编译出错的。而另一个问题则是,uni-app并未提供pageLifetimes的事件监听。

在我经过一番摸索之后,发现将设置索引方法写在onShow事件里面,效果是等效的。接下来便只剩下this的问题了。

如果直接复制的话,会出现无任何效果的情况

直接复制设置方法

因为uni-app的this引用不一样,所以它在判断getTabBar的时候,获取的是“undefined”所以不会执行下面的操作。如果你将判断去掉,则会直接报“undefined”错误。

难道实现不了?其实不然,万变不离其宗。uni-app也是编译到小程序的,所以绝对有迹可循。

我们首先看看uni-app里面this的内容。

this的指向内容

我们可以很明显的看到里面有个$mp的对象,说明这应该是微信小程序专用的对象。接下来我们继续分析$mp

$mp的指向内容

这里面有一个隐藏很深的getTabBar方法,我们直接调用它,和在微信小程序里面调用this.getTabBar是等效的。

所以我们就可以把onShow里面的内容写成这样。

设置索引方法

一些优雅点的封装

设置索引方法独立出来

在methods对象中,添加

setTabBarIndex(index){
            if (typeof this.$mp.page.getTabBar === 'function' &&
                this.$mp.page.getTabBar()) {
                this.$mp.page.getTabBar().setData({
                    selected:index
                })
            }
        }

使用mixin避免重复书写复制

main.js中,添加

Vue.mixin({
    methods:{
        setTabBarIndex(index){
            if (typeof this.$mp.page.getTabBar === 'function' &&
                this.$mp.page.getTabBar()) {
                this.$mp.page.getTabBar().setData({
                    selected:index
                })
            }
        }
    }
})

混入后的使用

在页面文件中

onShow() {
            this.setTabBarIndex(0) //index为当前tab的索引
        }
over!

v-html介绍

如果不使用v-html而是直接将html标签加入视图层会出现html标签不解析的情况,如果我们想实现解析的效果。vue中提供了v-html指令。使用 v-html 指令,你可以将html标签解析渲染到视图层。

微信小程序如何使用?

由于微信小程序只是类vue的,与vue存在着很多不同点。但是为了秉承将vue进行到底的宗旨,很多微信小程序开发框架如雨后春笋。在我使用的过程中,感觉uni-app与vue最为贴近,而且适配了很多vue的特性,如:v-model,filters,v-html等。官网是这样介绍的。

uni-app支持的vue特性

其中,他们是通过微信小程序rich-text的属性来实现v-html的效果的。所以,我们想知道支持什么标签,就得移步至rich-text | 微信开发文档

开始实现Demo

demo 中的需求是一个赛事报名list,其中赛事状态有,预报名,报名成功,正在进行,已结束等。

预实现方法:由于微信小程序的rich-text组件会禁用所有的节点事件,所以想通过@click绑定v-html节点事件,filter渲染界面。但是在实现过程中发现不支持在v-html中使用filter, 故后来采用methods的返回值来实现视图层的渲染。

methods实现v-html渲染

效果如下:至此效果,v-html解析成功

v-html 渲染效果

添加状态判断

最终效果

v-model 介绍

首先明确一点,v-model仅仅是语法糖。

<input type="text" v-model="something">

等价于

  <input
                type="text"
                v-bind:value="something"
                v-on:input="something = $event.target.value">

它将一个较复杂的input双向数据绑定简化了他的书写方式。

微信小程序绑定input

最开始我是使用微信推荐的写法

<input bindtap="input" />

然后在js里面解析event,然后再setData

uni-app下v-model的写法

本文采用uni-app自定义组件模式

wxml

...
<form @submit="formSubmit" @reset="formReset" :model="formItem" class="tm-every-center padding bg-white">
                    <view class="section flex align-center">
                        <view class="section__title">姓名:</view>
                        <input name="name" placeholder="请填写您的姓名" class="bg-gray" v-model="formItem.name" />
                    </view>
                    <view class="section flex align-center">
                        <view class="section__title">手机:</view>
                        <input name="name" placeholder="请填写您的姓名" class="bg-gray" v-model="formItem.phone" />
                    </view>
                    <view class="section flex align-center">
                        <view class="section__title">赛点:</view>
                        <input name="name" placeholder="请填写您的姓名" class="bg-gray" v-model="formItem.competition" />
                    </view>
                    <view class="btn-area margin-top"><button formType="submit" class="cu-btn">提交</button></view>
                </form>
                ...

然后在data里面设置

formItem: {
                name:'',
                phone:'',
                competition: ''
            }

在submit事件回调里面就可以这样写

submit(){
    this.$api.post(formItem).then(res => {
    })
}

完美解决微信小程序input书写过程冗余的情况。