标签 微信小程序 下的文章

引言

相信大家平常在开发过程中,经常会遇到此类需求。危险操作(如删除,退出登录)往往需要弹框提示给用户二次确认一下。本文介绍的是,如何使用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("点我干啥");
  }

效果展示

装饰器点击效果

为什么需要?

微信小程序里面页面与自定义组件的区别较大,而页面的可复用程度太低了。要么就是跳转页面,要么就是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书写过程冗余的情况。

为啥需要这个?

有时候我们写微信小程序,写好了。在手机上运行感觉效果还不错,正想分享给别人。突然发现没有分享选项,纳尼?原来是忘记写onShareAppMessage()了。有得加上,再编译。不得不说,麻烦的一匹。

应运而生

然后我就想,要是可以让默认支持分享就好了。

wepy实现方法

大致思路

import wepy from 'wepy';
export default class Page extends wepy.page {
}
分析代码,发现所有的page都继承于wepy.page, 而wepy.page又继承于wepy.component. 这感情好。也就意味着我只需要构建一个超类继承wepy.page就可以了

实践

新建page.js文件,内容如下
import wepy from 'wepy';
export default class Page extends wepy.page {
    onShareAppMessage() {
        
    }
}
然后再调整一下页面继承
import Page from '../page';
export default class Index extends Page {
}
编译运行,果然如此!

如果要自定义分享内容怎么办?

在子page里面重构onShareAppMessage就可以啦。

tips: 现使用uni-app后发现已经默认支持分享了

后续: uni-app说这是个BUG,已经修复了。。

后备方案——使用mixin全局混入

  1. 在main.js中添加以下内容。
import qs from 'query-string'
Vue.mixin({
    onShareAppMessage() {
        console.log('分享路径', '/pages/index?route=' + this.$mp.page.route + '&' + qs.stringify(this.$mp.query))
        return {
            path: '/pages/index?route=' + this.$mp.page.route + '&' + qs.stringify(this.$mp.query)
        };
    }
})

以上代码实现了所有页面都支持转发,并将转发路径设为主页。

  1. 在主页的onLoad函数中加入以下内容
if (options.route) {
                uni.navigateTo({
                    url: '/' + options.route + '?' + qs.stringify(options)
                })
            }

这样就实现了,进入后的页面跳转。

  1. 为什么要这样做?

因为大家都知道,当我们直接分享小程序页面给别人的时候。别人点击进入非tab页面,是没有返回操作的。增加了上面的功能后,所有页面的分享,都会先进入主页,再跳转至分享的页面。用户就可以无缝对接啦!

更新,现小程序已支持页面返回

想达到本文效果,需使用wepy框架。不了解wepy?转https://tencent.github.io/wepy/index.html

什么是async/await?

在最新的ES7(ES2017)中提出的前端异步特性:async、await。
async顾名思义是“异步”的意思,async用于声明一个函数是异步的。而await从字面意思上是“等待”的意思,就是用于等待异步完成。也就是我们平常所说的异步等待。不过需注意await只能在async函数中使用

为什么需要async/await?

在async/await之前,我们有三种方式写异步代码

1. 嵌套回调

其中思想就是,a函数执行完了得到的结果后在执行b。
形如

wx.getSetting({
      success(res) {
        console.log(res.authSetting['scope.userLocation']);
        if (!res.authSetting['scope.userLocation']) {
          wx.authorize({
            scope: 'scope.userLocation',
            fail(res) {
              Toast('无法获取位置,采用默认排序');
            }
          });
        } else {
          wx.getLocation({
            type: 'wgs84',
            success(res) {
              _this.setData({ location: res });
              console.log('您的位置信息:', res);
            },
            fail() {
              Toast('无法获取位置,采用默认排序');
            }
          });
        }
      }
    });
上面的代码你不用看,就会感觉。这啥东西?乱七八糟的。这就是嵌套回调。很不巧,原生微信小程序开发就是这样的。

2. 以Promise为主的链式回调

所谓Promise,简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。形如
var p1 = new Promise((resolve, reject) => {
 setTimeout(resolve, 1000, 'done');
 })
p1.then(data=>{
 console.log(data); // done
})

如果你的函数够多的话,那么就会一直then()下去。

为了优雅,Promise提供了一个方法Promise.all([p1,p2,p3]) ,用于将多个Promise实例,包装成一个新的Promise实例。接收的参数是一个数组,p1、p2、p3都是Promise对象
分两种情况:
  1. p1、p2、p3的状态都是resolve的时候,Promise.all的状态才会变成resolve;
  2. 只要p1、p2、p3中有一个的状态为reject,那么Promise.all的状态就会变成reject;

所以我们可以用Promise.all()来解决多个异步依赖调用。

3. 使用Generators

function *main() {
    var x = yield 1;
    var y = yield x;
    var z = yield (y * 2);
}
上面代码中的每一条语句都会按顺序一个一个地执行。Yield关键字标明了代码中被阻塞的点(只能被generator函数自己阻塞,外部代码不能阻塞generator函数的执行),但是不会改变*main()函数中代码的执行顺序。这段代码很简单!

但是,这三种写起来都还是不够优雅,ES7做了优化改进,async/await应运而生,async/await相比较Promise 对象then 函数的嵌套,与 Generator 执行的繁琐(需要借助co才能自动执行,否则得手动调用next()), Async/Await 可以让你轻松写出同步风格的代码同时又拥有异步机制,更加简洁,逻辑更加清晰。

示例
async a(){};
const b = await a();

这样做的好处?

唔,你不觉得一个优雅的代码就该是这样吗?好吧,其实这样更容易符合我们平常的思维逻辑

回到本文的题目

在wepy1.4.1以后的版本(之前的版本都是默认开启的),默认不支持async/await,需要用户手动加入,方法如下:

进入项目根目录,安装runtime包

npm install wepy-async-function --save

修改wepy.config.js加入runtime配置

        babel: {
            "presets": [
                "env"
            ],
            "plugins": [
                "transform-export-extensions",
                "syntax-export-extensions"
            ]
        }

在app.wpy中引入引入runtime包

import 'wepy-async-function'; 

在app.wpy中使API promise化

重写构造函数,使其支持async/await。
export default class extends wepy.app {
    constructor () {
        super();
        this.use('promisify');
    }
}

重启编译

wepy build --no-cache

使用示例

在wepy框架官方文档中已说明,对所有的微信小程序都支持async/await操作。只需将形如wx.getuserInfo改写为wepy.getuserInfo即可
async userInfoAsync() {
    const _this = this;
    const data = await wepy.getSetting(); //获取设置数据
    if (data.authSetting['scope.userInfo']) { //判断是否有获取用户信息的权限
      await wepy.login(); //登录
      let data = await wepy.getUserInfo();//获取用户信息
      _this.userInfo = data.userInfo;//采用wepy框架修改过后的功能,支持直接赋值数据绑定
      _this.$apply(); //在async的函数中,必须主动执行`$apply()`来进行脏数据检查
    }
  }
async onShow() {
    this.userInfoAsync(); //调用async函数
  }
以上代码实现了异步同步用户userInfo的功能

参考文献:

  1. 使用Promise链式调用解决多个异步回调的问题
  2. 关于js的callback回调函数以及嵌套回调函数的执行过程理解
  3. ES6 Generators并发
  4. ES7前端异步玩法:async/await理解
  5. 理解 JavaScript 的 async/await
  6. Async/await学习
  7. 浅谈async/await
  8. wepy项目中使用async await

rpx单位

rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。要实现4:3的展示则可以
image{
    width:750rpx;
    height:563rpx;
}

使用组件库,写的css样式优先级太低

不管三七二十一,加!important
.findButton {
  width:300px !important;
  height:35px !important;
}

样式各种飘,不居中怎么办?

<view class="nickName">
  <view>{{userInfo.nickName}}</view>
</view>
.nickName {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 44px;
}

selectedColor无效

selectedColor 和 list 是同级别的。写在list数组外面就可以了。

背景虚化

blur中的参数是虚化比例
filter: blur(1px);

scroll-view设置`scroll-x"也不会横向滚动

要给scroll-view加上white-space: nowrap; ,给scroll-view的子元素box加上display:inline-block;

介绍

TmWeBlog--微信小程序版WeTypecho魔改版

原项目介绍

众所周知,现在由于移动互联网的普及,网站访问量下降,导致个人站长非常难混。

WeTypecho则能帮您快速搭建微信小程序,将Typecho博客的内容映射到微信小程序。
帮助您在一定程度上获取更多来自微信的流量。WeTypecho的安非常简单,只需三分钟,就能搭建。


原项目自定义的修改版

项目地址

原项目地址
当前项目地址

功能

目前支持的基本功能如下:

  • 首页预览置顶文章
  • 评论,回复,点赞
  • 转发,分享到朋友圈
  • 图片视频显示
  • 支持markdown,html解析

扫码预览

gh_3d207c9d6dd4_258.jpg

ToDoList

  • [ ] 留言回复通知