前端学习总结(一)

第一部分 工作中遇到的问题和解决思路

微信小程序相关

scroll-view 组件如何自动滚动到底部

需求分析:实现聊天功能的时候,每次进入聊天界面,都要自动滑动到底部,展示最近的消息,收到新消息的时候,继续滑动到底部,发送新消息也要这样展示。

解决思路:借助 scroll-view 组件的一些属性可以方便的实现此功能

scroll-into-view (参考微信小程序开发文档)

prop type default required desc
scroll-into-view string false 值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素

所以只要在 .js 文件的 data 中设置一个变量,用来指定当前最底部某子元素的 id,在循环渲染聊天列表数据的时候给每个聊天内容展示子项绑定一个 id 值,手动设置前面的变量为最后一项聊天内容的子项的 id 值即可,这个 id 值不能直接设置为 index,因此可以通过拼接的方式实现,上代码:

chatPanel.wxml

<!-- 
scroll-into-view="{{bottomId}}" 这个 bottomId 即为动态设置的值
因为不能直接用 index ,所以在前面拼接一个 item,当然你可以把这个换成任何你想要的字符
-->
  <scroll-view
    scroll-y="{{true}}"
    class="chat-content"
    scroll-top='0'
    scroll-into-view="{{bottomId}}"
    refresher-enabled="{{true}}"
    bindscrolltoupper="getMoreChatRecord"
    style="max-height: {{maxHeight}}rpx">
    <view
      wx:for="{{chatRecordList}}"
      wx:key="index"
      scroll-with-animation="{{true}}"
      id="item{{index+1}}"
      class="content-item {{item.msg_source == 1 ? 'right' : 'left'}}">
    </view>
  </scroll-view>

我们要个 scroll-view 设置 scroll-y="{{true}}",允许纵向滚动,默认值为false 即不可滚动,
这里有两点要注意一下:
1、scroll-view 需要设置 scroll-y="{{true}}",允许纵向滚动,因为它的默认值为 false;
2、scroll-view 需要指定高度,不然它会随子元素撑高,不会出现滚动的效果。

这里关于高度的设置又会有些问题,不能设置一个固定的高度,因为每个手机的屏幕高度其实是不一样的,现在的主流手机屏幕都比较修长,在开发适配的时候,我们针对某一型号的手机设置一个高度,距离底部的输入框距离刚好合适,达到互不干扰的目的。
但是这样的高度到了比当前设备更长的屏幕时,可能就会出现聊天内容距离底部输入框有一大段距离,导致内容的缺失,带来不好的用户体验;同理,遇到更短的手手机屏幕,聊天内容区又会与输入框距离过小或者直接跟输入框重叠了,导致用户永远也看不到底部的消息内容,这个解决思路也很简单,后面会详细介绍。

chatPanel.js

// 进入聊天界面,或许获取消息的处理
data: {
   chatRecordList: [],
   bottomId: '',
},
// 获取与某一特定用户之间的聊天记录
  async getChatRecord() {
    const {brandId, storeCode, openUserId} = this.data
    console.log(brandId, storeCode, openUserId)
    const res = await Message.getChatOfUser({brandId, storeCode, openUserId})
    if (res.resultCode == '8003') {
      this.setData({
        chatRecordList: res.result
      })
      this.setData({
        /* 获取到最后一条消息的拼接 id,设置 bottomId 即可 */
        bottomId: `item${this.data.chatRecordList.length}`
      })
    }
    console.log(this.data.chatRecordList)
  },

了解了基本的实现原理,在接收消息和发送消息的时候,也可以故技重施,在接收或发送消息成功之后插入消息列表,再把 bottomId 设置为最后一条消息的 id 值即可。

第二个问题是上面提到的如何正确设置 scorll-view 的高度

使用微信提供的接口 wx.getSystemInfo() 获取当前设备的信息,根据屏幕高度设置 scroll-view 的高度

/* 设置 scroll-view 高度 */
  setScrollViewHeight() {
    const height = wx.getSystemInfoSync().windowHeight
    /* 根据其他元素的高度及配合可使用窗口高度,合理设置高度值即可 */
    let maxHeight = 1220
    if (height < 500) {
      maxHeight = 1180
    } else if (height < 700) {
      maxHeight = 1220
    } else {
      maxHeight = 1250
    }
    this.setData({
      maxHeight
    })
  },

3、微信小程序 image 组件,如何做到宽度固定,高度自适应,比例不变

web 中 img 标签与微信小程序中的 image 标签对比:
html 中 img 标签的宽度是可以自适应的,不设置宽高,会取图片的原始宽高,设置了宽度,高度会自适应;设置了高度,宽度会自适应。
小程序中 image 标签会有一个默认的宽高 320*240px,不会自适应,需要通过设置其 mode 属性实现宽或高固定,高或宽等比例缩放

<!-- 需要指定 image 组件的宽度 -->
<image mode="widthFix" src="{{item.msg_content}}"></image>

4、微信小程序输入框,弹出键盘

问题描述:输入框通过 fixed 定位与窗口底部,手机输入时弹出键盘,期望的效果是输入框能够相当与键盘底部定位,实际的情况是键盘上去了,输入框还是停留在页面最底部,且被键盘挡住。

解决方法:动态调整输入框所在容器相对于底部定位的 bottom 值,当弹出键盘时,bottom 值就等于键盘高度的值;键盘消失,bottom 值在回归为 0

chatPanel.wxml

<view
    class="input-panel"
    style="transform: translateY({{-inputBottom}}px)">
    <input
      adjust-position="{{true}}"
      bindfocus="focus"
      bindblur="blur"
      class="input-value"
      bindinput="inputContent"
      value="{{chatContent}}"
      placeholder="请输入内容"
      type="text"/>
    <view
      bind:tap="chooseImage"
      class="add center">
      +
    </view>
    <view bind:tap="sendMessageToUser" class="send center">发送</view>
  </view>

chatPanel.js

/**
   * 输入键盘弹出处理
   * 聚焦
   * 失焦
   */
  focus(e) {
    this.setData({
      // 通过 e.detail.height 可获取键盘高度值
      inputBottom: e.detail.height
    })
  },
  blur() {
    this.setData({
      inputBottom: 0
    })
  },

5、点击某张图片实现全屏展示

描述:如果时多张照片,可以实现左右滑动
可通过微信小程序提供的方法实现

wx.previewImage({
  current: url,  // 当前展示图片 url
  urls: userContractUrl,  // 需要全屏展示的 url 列表
})

健康证正反两面的查看:
.wxml

<view class="common-img">
    <view 
      wx:for="{{userHealthUrl}}"
      wx:key="item"
      class="img-item">
      <image
        data-url="{{item}}"
        bind:tap="previewHealthImage"
        src="{{item}}">
      </image>
   </view>
</view>

.js

previewHealthImage(e) {
    const {url} = e.currentTarget.dataset
    // urlHelathUrl 为正反两面图片集合
    // [urlString, urlString]
    const {userHealthUrl} = this.data
    this.coverImage(url, userHealthUrl)
  },
  coverImage(current, urls) {
    wx.previewImage({current, urls})
  },

6、如何方便的实现一个单选功能

之前一致的解决思路:给列表的每一项设一个标识,通常是 isActive,通过这个字段的 true/false 的切换来表示选中的项。
这样做比较麻烦,在切换选项的时候需要判断当前选择的是不是已经选中的,如果是已经选中的,只要把当前选中的 isActive 设置为 false 即可,如果当前选择的不是已经选中的,需要对整个列表遍历做一次判断,先把所有的都设置为false,再设置选中项为 true,比较麻烦。
所以只需要再 data 中额外定义一个变量,可以是 index(不建议,没有语义化,当有多个列表时也不能每个都用index),建议用一个能表示当前唯一值的标识,通常是id。

.js

data: {
  activeTabId: '10001',
  tabList: [{
      id: '10001',
      text: '首页'
    }, {
      id: '10002',
      text: '购物车'
    }, {
      id: '10003',
      text: '个人中心,'
    }]
}

changeActiveTab

changeActiveTab(e) {
    const {id} = e.currentTarget.dataset
    this.setData({activeTabId: id})
  }

接下来便可以通过选中的 id 值从列表中获取到,相关的信息,在有需要的时候。

7、小程序自定义组件 wxss 警告

Some selectors are not allowed in component wxss, including tag name selectors, ID selectors, and attribute selectors.

通过查阅文档可知,在组件样式书写中有以下需要注意的地方:

  • 不能使用 id 选择器
  • 不能使用后代选择器 .a .b
  • 不能使用 app.wxss 中的样式,可通过 @import 的方式引入或者在组件 wxss 中重写一遍

8、微信小程序在 Page({}) 外部书写的代码

Page 外面书写的代码,会在小程序打开之后就会调用,不管当前是不是在代码书写的 page 页面,所以尽量不要在外部书写过多的逻辑代码,可能会影响性能,因为不管是否切换到了那个页面,都会加载。

一般会在顶部引入一些第三方的库文件,或者一些通用的方法,通常是请求后端接口的业务逻辑。

import {Message} from '../../model/message';
import {Common} from '../../model/common';
const common = new Common()

第二部分、一些需要注意的点

1、对一个数组进行操作的时候,不要总是用循环,要优先考虑一些数组已经实现的内置方法

比如,通过 id 值从一个列表中找到该项的完整数据

使用 for 循环

const item
for (let i = 0; i < arr.length; i ++) {
  if (arr[i].id === id) {
    item = arr[i]
    break;
  }
} 

使用 Array.prototype.find()

const item = arr.find((ele) => ele.id === id)

使用 Array.prototype.filter()

// filter() 返回的是一个数组
const list = arr.filter((ele) => ele.id === id)
const item = list[0]

这样做的好处之一是语义更加清晰,通过 find filter 这样的关键词就能很大成程度上知道这段代码的目的;好处之二自然也是更好的熟悉更“高级”的语法,活学活用。

2、避免使用无意义的标识 00 01 类似这种状态标识,建议使用常量保存起来,语义化更加清晰(加上必要的注释)

场景分析:页面中有两个 tab 标签可以点击切换,
一般情况下,我们都是通过给每一个tab栏指定一个标识 00 01,然后再切换的时候,更新当前选中的状态值。其实这样做不好,我们自己知道当时写代码的时候 00 01 代表啥,可是到将来他人维护的时候,看到这个标识肯定会一脸费解
所以我们可以考虑定义一个常量,用来存储每个 tab 栏的标识,能够提高代码的可读性。

第三部分、日常思考

1、闭包的初中高级应用

初级:定义
闭包是一种现象,一种内部函数可以访问外部函数作用的现象,常见的形式就是在一个函数体内部定义一个内部函数,这个内部函数可以访问函数体内的任何变量,即使通过 return 的方式被传递到其他执行环境中,依然可以访问此前外部函数的变量,且函数体得不到内存回收,会造成内存泄漏

function foo() {
  var count = 1
  function bar() {
    count ++
    console.log(count)
  }
  return bar
}

var outer = foo()
outer()  // 2
outer()  // 3
outer()  // 4

中级:常见形式
高级:常见应用

模拟私有方法
闭包 + 立即执行函数

// 一个简单的计数器
let counter = (function () {
  let privateCount = 0
  function changePrivateCount(count) {
    privateCount += count
  }
  return {
    addCount: function (val) {
      changePrivateCount(1)
    },
    reduceCount: function (val) {
      changePrivateCount(-1)
    },
    countValue: function () {
      return privateCount
    }
  }
})()

let counterOne = counter
counterOne.addCount()
let counterTwo = counter
counterTwo.addCount()

counterOne.countValue()  // 2
counterTwo.countValue()  // 2
// 两个 counter 操作的是同一个词法作用域
// 生成多个 counter,操作独立的词法作用域(不使用立即执行函数)

Creating closures in loops: A common mistake(在循环中创建闭包:一个常见的错误)

正常情况下

for (var i = 0; i < 10; i ++) {
  console.log(i)
}

按照顺序打印出 0 ~ 9

接下来我们期望每过一秒钟,打印一个递增的数值

for(var i = 0; i < 10; i ++) {
  setTimeout(() => {
    console.log(i)
  }, 1000 * i)
}

实际上,隔了十秒后,打印出十个 10

for(var i = 0; i < 10; i ++) {
  setTimeout(() => {
    console.log(i)
  }, 1000 * i)
  //
  setTimeout(() => {
    console.log(i)
  }, 1000 * 0)
  setTimeout(() => {
    console.log(i)
  }, 1000 * 1)
  setTimeout(() => {
    console.log(i)
  }, 1000 * 2)
  setTimeout(() => {
    console.log(i)
  }, 1000 * 3)
}

for 循环是同步的,但是 setTimeout 内部执行的函数时异步的,也就是说在执行 setTimeout 内部的代码时,循环已经结束了,最终值时 10
共用了同一个作用域下的变量

如何改动,使其的得到我们期望的效果?
立即执行函数
块级作用域

2、防抖节流的实现

场景的引入:在某些用户的提交操作,需要加上防抖的判断,防止用户多次点击触发多次提交请求。当然这只是第一层,只能控制再一定的时常内只触发一次提交操作。
如果用户的网路过慢,在用户触发请求过后,一段时间内还没得到响应的情况下,又一次触发了提交操作,这时候防抖和节流都是控制不了的。
此时,我们需要一个锁,这个锁默认是开着的,一旦用户发起了请求,立马关闭这个锁,这个时候无论用户出发了多少次提交操作都走不到发送请求的那步。
然后在得到成功响应的时候,把锁打开,这个时候用户可以再次发送请求。当然,有成功的响应,自然也有失败的响应。失败的响应又分为以下几种情况:

  • 请求参数等异常,导致请求失败了
  • 网络在那一瞬间出问题了
  • 服务器在响应那次请求的时候发生了异常(500)

以上几种情况,我们都需要手动把锁打开,不然用户无法执行下一次的操作。
这种做法在处理用户登录的情况,感觉可以取代防抖这类的函数,直接用一个锁来控制即可。毕竟,防抖节流在这种的应用场景下就显得很鸡肋了。

3、网络异常导致数据渲染不出来白屏怎么处理,怎么全局统一处理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容