# 微信小程序常见开发技巧

最近从零开发一个微信小程序项目,将一些常见的技巧与坑记录在此,文章以问题为导向,提供思路与源代码,希望能帮助到大家。

# Q1: 如何实现一个 rate 评分?

# 版本一

首先来一个简单版本,效果如下图所示:

star

  • 首先需要准备两张图片,一张是高亮的星星图片,一张是暗色的星星图片。

  • 主要的思路是建立一个数组[1,2,3,4,5],并在 data 中保存一个starNum,用于表示有几个星星。

  • 然后通过遍历数组,比 startNum 小时则渲染高亮的星星,否则渲染暗色的星星。

  • 通过绑定tap事件,修改starNum具体代码见下。

wxml如下所示,下文中的 for 内容为[1,2,3,4,5],不知道为啥无法显示:

<view class="star">
  录取指数:
  <block wx:for="{{[1,2,3,4,5]}}" wx:key="*this">
    <image
      src="{{starNum>=item?'/images/Star.png':'/images/Star (gray).png'}}"
      data-num="{{item}}"
      bindtap="handleChange"
      style="width:40rpx;height:40rpx"
    />
  </block>
</view>
1
2
3
4
5
6
7
8
9
10
11

JS如下图所示:

handleChange(e) {
    const { num } = e.currentTarget.dataset

    this.setData({
      starNum: num
    })
  }
1
2
3
4
5
6
7

如果说需要半个星星的,那么可以通过如下方法:

<view style="display: flex;">
  录取指数:
  <block wx:for="{{[1,2,3,4,5]}}" wx:key="*this">
    <image
      src="{{starNum>index?(starNum>index+0.5?fullStarUrl:halfStarUrl):nullStarUrl}}"
      style="width:40rpx;height:40rpx"
    />
  </block>
</view>
1
2
3
4
5
6
7
8
9

data中数据如下:

Page({
  data: {
    // 满星图片
    fullStarUrl: "/images/full@2.png",
    // 半星图片
    halfStarUrl: "/images/half-star.png",
    // 空星图片
    nullStarUrl: "/images/null@2.png",
  },
});
1
2
3
4
5
6
7
8
9
10

看到了吧,几行代码就可以搞定,这个版本可以和后端进行交互,比如发送Post请求,修改星星数。

# 版本二

  • 接着我们用一个纯 CSS 的方法实现一个相对来说比较复杂的(以下版本作者@子瑜说 IT)。

  • 去找个好看的 iconfont,[Iconfont-阿里巴巴矢量图标库];

  • 借用 5 个 radio 单选框,把默认样式都去掉,显示默认的星星;

  • 用 checked 伪类监听用户选中 ✅,由默认的星星变成高亮的星星;

  • 然后配合~兄弟操作符把当前选中的所有兄弟元素都一起高亮;

把 5 个 radio 单选框反向排列 ❗;

<link rel="stylesheet" href="//at.alicdn.com/t/font_1356455_c5d3d3ohlbq.css" />
1

一个很简洁的布局:

<div class="rate-content">
 <input type="radio" name="rate">
 <input type="radio" name="rate">
 <input type="radio" name="rate">
 <input type="radio" name="rate">
 <input type="radio" name="rate">
</div>
1
2
3
4
5
6
7

# 1、先把默认的星星显示出来

/* 去掉默认样式 */
input {
  -webkit-appearance: none;
  border: none;
  outline: none;
  cursor: pointer;
}

:root {
  /*高亮颜色*/
  --main: #ffa822;
  /*默认颜色*/
  --basic: #999;
}

.rate-content input[name="rate"] {
  font-family: "iconfont";
  /*之前引入的iconfont字体*/
  font-size: 30px;
  padding-right: 10px;
}

.rate-content input[name="rate"]::after {
  content: "\e645";
  color: var(--basic);
  /*加点颜色过渡效果*/
  transition: color 0.4s ease;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

效果如下:

plain-star

# 2、实现选中单个星星

/* 实现选中单个星星 */
/* 高亮的星星 */
input[name="rate"]:checked::after {
  content: "\e73c";
  color: var(--main);
}
1
2
3
4
5
6

效果如下:

single

# 3、实现连同兄弟元素一起高亮

/* 实现选中单个星星 */
/* 高亮的星星 */
input[name="rate"]:checked::after,
input[name="rate"]:checked ~ input[name="rate"]::after {
  /*实现连同兄弟元素一起高亮*/
  content: "\e73c";
  color: var(--main);
}
1
2
3
4
5
6
7
8

效果如下:

multi

# 4、然后把 input 反向排列

.rate-content {
  display: flex;
  flex-flow: row-reverse;
}
1
2
3
4

效果如下:

revert

# 5、鼠标移入预览选中效果

input[name="rate"]:checked,
input[name="rate"]:hover::after {
  content: "\e73c";
  color: var(--main);
}

/* 兄弟元素一起高亮 */
input[name="rate"]:hover ~ input[name="rate"]::after {
  content: "\e73c";
  color: var(--main);
}
1
2
3
4
5
6
7
8
9
10
11

效果如下:

预览

# 6、加入放大动画

input[name="rate"] {
  transition: transform 0.2s ease;
}
input[name="rate"]:checked,
input[name="rate"]:hover {
  transform: scale(1.2);
}
1
2
3
4
5
6
7

效果如下:

放大

# Q2: 如何实现联想搜索并将联想结果的关键字高亮显示?

想要实现的效果是在input输入框中输入查询关键字的同时,向后端发起请求,获取联想结果,并展示。同时在展示的时候,将关键字高亮显示。

# 联想搜索

<view class="seachBar">
  <input
    value="{{inputValue}}"
    focus="true"
    confirm-type="search"
    placeholder="搜索感兴趣的城市、大学、专业"
    bindinput="onInput"
  />
</view>
<scroll-view scroll-y="true" class="search-res" hidden="{{hideScroll}}">
  <block wx:for="{{searchTip}}" wx:key="id">
    <view class="tip-item" bindtap="itemtap" data-info="{{item}}">
      {{item.name}}
    </view>
  </block>
</scroll-view>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

如上代码所示首先在input输入框中绑定onInput,onInput函数的实现如下所示。通过设置定时器,在规定时间内如果不再继续输入,则向后端发起请求,如果有数据,则通过scroll-view展示请求回来的联系结果,如果没有则隐藏scroll-view

// pages/search/search.js
Page({
  data: {
    // 模糊查询时长
    timer: 0,
    // 点击结果项之后替换到文本框的值
    inputValue: "",
    // 是否隐藏模糊查询的面板
    hideScroll: true,
    // 模糊查询结果
    searchTip: [],
  },

  onInput(e) {
    const inputValue = e.detail.value;

    clearTimeout(this.data.timer);
    // 设置一个定时器,在600秒内不输入新的内容则发起联想搜索,否则清空定时器
    let timer = setTimeout(() => {
      if (inputValue) {
        // 如果输入的关键字不为空,则发起请求获取联想值,这里以tips作为请求获取结果
        const tips = [
          { search_type: "city", name: "北京", id: 11 },
          { search_type: "school", name: "北京大学", id: 22 },
          { search_type: "major", name: "北京测绘工程", id: 33 },
        ];
        this.setData({
          inputValue: inputValue,
          searchTip: tips,
          hideScroll: false,
        });
        return;
      }
      // 如果为空,则收起
      this.setData({
        searchTip: [],
        hideScroll: true,
        inputValue: "",
      });
    }, 600);

    this.data.timer = timer;
  },
  itemtap(e) {
    const { info } = e.currentTarget.dataset;

    this.setData({
      // 将点击选择的值展示在input框中
      inputValue: info.name,
      // 当用户选择某个联想词,隐藏下拉列表
      hideScroll: true,
    });
    // 发起请求,获取查询结果
    this.searchByKeyWord(info);
  },
  searchByKeyWord(info) {
    // 发起请求,获取面板数据
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

# 关键字高亮显示

联系搜索相对来说比较简单,主要是通过计时器做到防抖。接下来需要介绍一下关键字高亮显示。实现效果如下图所示。

高亮

ReactVue可以通过正则表达式把匹配到的关键字用标签如<b class="你的高亮样式">关键字</b>替换掉原来的关键字,再将内容通过 dom 操作塞回原来的容器,但是小程序,我并没有发现可以这样实现的方法,如果有实现的可以私聊教教我哦。但是我换了一个思路,我用正则表达式将原来的关键字替换成%%${key}%%,再根据%%将整个字符串拆成数组。然后通过遍历的这个数组,如果值与输入的inputValue相同则切换高亮样式,否则原来的样式。代码如下所示:

<view>
  <view class="seachBar">
    <input
      value="{{inputValue}}"
      focus="true"
      confirm-type="search"
      placeholder="搜索感兴趣的城市、大学、专业"
      bindinput="onInput"
    />
  </view>
  <scroll-view scroll-y="true" class="search-res" hidden="{{hideScroll}}">
    <block wx:for="{{searchTip}}" wx:key="id">
      <view class="tip-item" bindtap="itemtap" data-info="{{item}}">
        <view class="left">
          <view class="content">
            <!-- 关键字高亮关键 -->
            <view
              wx:for="{{item.name}}"
              wx:for-item="textItem"
              wx:key="index"
              class="{{textItem == inputValue ? 'searchHigh' : '' }}"
            >
              {{textItem}}
            </view>
          </view>
        </view>
      </view>
    </block>
  </scroll-view>
</view>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Page({
  data: {
    // 模糊查询定时器id
    timer: 0,
    // 点击结果项之后替换到文本框的值
    inputValue: "",
    // 是否隐藏模糊查询的面板
    hideScroll: true,
    // 模糊查询结果
    searchTip: [],
  },

  // 将关键字左、关键字、右拆成数组
  getInf(str, key) {
    return str
      .replace(new RegExp(`${key}`, "g"), `%%${key}%%`)
      .split("%%")
      .filter((item) => {
        if (item) {
          return true;
        }
        return false;
      });
  },

  onInput(e) {
    const inputValue = e.detail.value;

    clearTimeout(this.data.timer);
    let timer = setTimeout(() => {
      if (inputValue) {
        // 如果输入的关键字不为空,则发起请求获取联想值,以tips假设为请求结果
        const tips = [
          { search_type: "city", name: "北京", id: 11 },
          { search_type: "school", name: "北京大学", id: 22 },
          { search_type: "major", name: "北京测绘工程", id: 33 },
        ];

        const newTips = tips.map((item) => {
          const { search_type, name, id } = item;

          const newContent = this.getInf(name, inputValue);

          return { search_type, name: newContent, id };
        });

        this.setData({
          inputValue: inputValue,
          searchTip: newTips,
          hideScroll: false,
        });
        return;
      }
      // 如果为空,则收起
      this.setData({
        searchTip: [],
        hideScroll: true,
        inputValue: "",
      });
    }, 600);

    this.data.timer = timer;
  },
  itemtap(e) {
    const { info } = e.currentTarget.dataset;

    this.setData({
      // 将点击选择的值展示在input框中
      inputValue: info.name.join(""),
      // 当用户选择某个联想词,隐藏下拉列表
      hideScroll: true,
    });
    // 发起请求,获取查询结果
    this.searchByKeyWord(info);
  },
  searchByKeyWord(info) {
    // 发起请求,获取面板数据
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

❤️ 爱心三连击

1.看到这里了就点个在看支持下吧,你的「在看」是我创作的动力。

2.关注公众号前端梦想家,「一起学前端」!

3.添加微信【qdw1370336125】,拉你进技术交流群一起学习。

Last Updated: 2/10/2021, 6:31:18 PM