原生微信小程序全流程(基础知识+项目全流程)

小程序的基本使用

小程序文件类型

小程序主要提供了 4 种文件类型:

类型名称作用是否必须存在
.wxml用于页面的布局结构,相当于网页中 .html 文件
.wxss用于页面的样式,相当于网页中的 .css 文件
.js用于页面的逻辑
.json用于页面的配置

文件作用

文件名作用是否必须存在
app.js小程序入口(首先执行的文件)
app.json小程序的全局配置
app.wxss小程序的全局样式
project.config.json小程序开发者工具配置是(会自动创建)
sitemap.json小程序搜索优化

要点

  • data 初始化页面中的数据
  • setData 更新数据
  • {{}} 插值语法可以实现数据的渲染
  • bind:事件类型=事件回调
Page({  //index.js
  // 约定格式,使用data定义数据
  data: {
    message: 'nihao!'
  },
  changeMessage() {
    // this.setData修改数据
    this.setData({
      message: 'new!'
    })
  }
})

index.wxml

<view>{{message}}</view>
<button bind:tap="changeMessage" type="primary" size="mini">点我试试</button>

如何注册小程序事件监听?

Page({
处理函数() {}

})

<button bind:事件名=“处理函数”>

配置文件

在这里插入图片描述

分类:

全局 app.json

页面 page.json

全局配置

app.json 是当前小程序的全局配置,包括了:

  • 小程序首页
  • 界面表现
  • 网络超时时间
  • 底部 tab
  • …等配置
全局配置pages

用于指定小程序由哪些页面组成。

  1. 每一项都对应一个页面的 路径(含文件名) 信息。
  2. 小程序中的每一个页面都必须在pages下登记一下。
  3. 文件名不需要写文件后缀,框架会自动去寻找对应位置的 .json, .js, .wxml, .wxss 四个文件进行处理。

是一个数组,每一项表示一个页面

{
  pages:[
    "pages/index/index",
    "pages/logs/logs"
  ]
}

数组中的第一个元素表示小程序启动时默认打开的页面-主页

页面跳转
<navigator url="/pages/logs/logs">跳转到log页面</navigator>
新建页面(自动优先)
新建页面-手动

共三步:

  1. 新建空目录
  2. 新建页面(4个文件)
  3. 在pages下手动补充页面地址

要点:

  1. 页面都放在pages下,一个页面一个文件夹。
  2. 新建page时,不需要写文件后缀名。
新建页面-自动

在pages下补充一项 pages/page1/page1 ,然后保存,则会自动添加一个页面。

app.json

{
  pages:[
    "pages/index/index",
    "pages/logs/logs",
    "pages/page1/page1"
  ]
}
小程序配置-全局配置—window

用于设置小程序的状态栏、导航条、标题、窗口背景色。

https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#window

常见配置:

在这里插入图片描述

小程序配置-全局配置—tabBar

在这里插入图片描述

tabBar

tabBar 定义小程序 tab 栏的表现,如下图即所谓的 tab 栏:

在这里插入图片描述

常见配置属性

属性类型默认值是否必须说明
listarraytab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab
color16 进制颜色tab 上的文字默认颜色,仅支持十六进制颜色
selectedColor16 进制颜色tab 上的文字选中时的颜色,仅支持十六进制颜色
backgroundColor16 进制颜色tab 的背景色,仅只持 16 进制颜色
borderStylestringblacktabbar 上边框的颜色, 仅支持 black / white
positionstringbottomtabBar 的位置,仅支持 bottom / top

上述配置中 list 具体又包含以下内容:

属性类型默认值是否必须说明
pagePathstring页面路径,必须在 pages 中先定义
textstringtab 上按钮文字
iconPathstring图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片,当 position 为 top 时,不显示 icon
selectedIconPathstring选中时的图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片,当 position 为 top 时,不显示 icon

参考代码(app.josn)

{
  ......
  "tabBar": {
    "color": "#999",
    "selectedColor": "#e93b3d",
    "backgroundColor": "#fff",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        "iconPath": "static/tabbar/home-default.png",
        "selectedIconPath": "static/tabbar/home-active.png"
      },
      {
        "pagePath": "pages/logs/logs",
        "text": "日志",
        "iconPath": "static/tabbar/video-default.png",
        "selectedIconPath": "static/tabbar/video-active.png"
      },
      {
        "pagePath": "pages/index/demo",
        "text": "示例",
        "iconPath": "static/tabbar/face-default.png",
        "selectedIconPath": "static/tabbar/face-active.png"
      }
    ]
  }
}
小程序配置-页面配置
  • 页面配置只针对某个页面生效
    • 如 index.json 是针对 index 页面生效,demo.json 只针对页面 demo 生效
  • 不用写window字段
  • 优先级比全局配置高

常用配置:

属性类型默认值是否必须说明
navigationBarTitleTextstring空白导航栏标题文字内容
navigationBarTextStylestringblack导航栏标题颜色,仅支持 black / white
navigationBarBackgroundColor16 进制颜色#00000导航栏背景颜色,如 #000000
navigationStylestringdefault导航栏样式,仅支持 default / custom
enablePullDownRefreshbooleanfalse是否开启全局的下拉刷新
小程序适配-响应式单位rpx

rpx ,responsive pixel:

在小程序中的单位rpx,它的特点是能够自动地适配置不同尺寸的手机屏幕。

原理:不管手机屏幕具体多宽,100%的屏幕宽度就是750rpx

100%屏幕的宽度 = 750rpx

  1. 所有的设备宽度都是750rpx
  2. 在实际使用中只需要将设计稿调整为 750px 宽,然后 1:1 的比例来写长度(单位使用 rpx),如:设计稿中某个区域(盒子)的大小为 18090px ,写成小程序的尺寸为 18090rpx。

注:上述的规则仅适用于设计稿宽度为 750px

rpx (responsive pixel):规定不管屏幕为多少px,100%的屏幕宽度就是750rpx
100%屏幕的宽度 = 750rpx

内置组件—navigator(跳转)

navigator 是小程序中的导航标签,类似以前web中的a标签。通过 url 来指定跳转的页面

  • url: 页面路径
    • 支持相对和绝对路径
    • 路径为空会报错
    • 还可以跳到其他小程序
  • hover-class:点击态的样式
    • none 禁用点击效果
  • open-type:跳转方式
    • navigate。默认值
    • switchTab。跳转到tabbar页
属性名类型默认值说明
urlstring当前小程序内的跳转链接
open-typestringnavigate跳转方式
targetStringself在哪个目标上发生跳转,默认当前小程序
内置组件-image(图片)

在这里插入图片描述

<image 
src="图片资源地址" 
mode="图片裁剪,缩放方式"></image> 

image组件是一个有默认大小(320*240)的盒子。

  • src: 图片资源地址。相对地址,绝对地址(外网地址)。
  • mode: 默认值为scaleToFill,用来设置图片裁剪、缩放的模式。
    • scaleToFill。不保证缩放比,图片拉伸填满容器
    • aspectFit。保证缩放比,使图片的长边显示出来
    • aspectFill。保证缩放比,使图片的短边显示出啦
  • lazy-load:默认为false,是否开启懒加载模式。(3屏)
内置组件—swiper(轮播图)

swiper可以理解为小程序内置的轮播图标签,可以让方便快速地实现轮播功能。

<swiper>
  <swiper-item>1屏的内容 </swiper-item>
  <swiper-item>2屏的内容 </swiper-item>
  <swiper-item>3屏的内容 </swiper-item>
  <swiper-item>4屏的内容 </swiper-item>
</swiper>
  1. swiper:滑块容器。内只能写swiper-item。它的默认高度是150px;
  2. swiper-item:滑块单元。它的大小是: 宽度 和 高度 为 100% * 100%;
表单相关
  1. 输入框:input
    1. password密码类型,placeholder占位文字
  2. 单选框:radio-group和radio
    1. value指定表单数据,checked选中状态
  3. 复选框:checked-group和checkbox
    1. value指定表单数据,checked选中状态
  4. 选择框:picker
    1. mode:指定不同类型的选择框
scroll-view(滚动)

``scroll-view 在页面中指定一个**可以滚动**的区域,并且这个可滚动的区域能够实现一些高级的交互,比如:下拉刷新`等。

scroll-view 中嵌套任意需要滚动的内容,要求内容必须有溢出(scroll-view有固定的尺寸),垂直滚动时 scroll-view 必须要指定高度。

属性

  • scroll-x 属性是否允许水平方面滚动
  • scroll-y 属性是否允许垂直方向滚动
  • refresher-enable 属性是否开启下拉刷新的交互
小程序样式-全局样式

app.wxss 定义全局样式,该文件中的样式会在所有的页面生效。

注:page 在每个页面中都有,它是由小程序自动添加上的,相当于网页中的 body 标签。

小程序样式-静态资源

小程序中 .wxss 文件中不支持使用本地路径的资源,比如背景图片是不允许使用本地图片路径的,必须使用网络路径(https:// 或 http:// 开头)或者转换成 base64 编码。

小程序样式-字体图标(iconfont)

小程序中字体图片的使用与网页中基本上是一致的,唯一的区别是小程序的 .wxss 文件中不支持使用本地字体文件,我们使用 iconfont 平台提供的服务生成字体文件后,直接使用其线上的字体文件地址。

资源参考:https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4061819

小程序基本使用-请求数据并渲染

小程序模板语法-数据绑定

在js中定义数据

Page({
  data: {
    isOpen: true,
    message: 'hello world!'
  }
})

小程序的data是一个对象,不同于vue的data是一个函数

在模块中获取使用数据

小程序中使用 {{}} 实现数据与模板的绑定

  1. 内容绑定:<view>{{ 属性名 }}</view>
  2. 属性绑定: <input value="{{属性名}}" />

{{}} 内写的是表达式

简易双向绑定
  1. 小程序中提供了 model:value="{{数据名}}" 语法来实现双向的数据绑定

  2. 目前只能用在 inputtextarea 组件中。

    只能是一个单一字段的绑定,不能嵌套对象.否则出现以下报错:

    [pages/index/index] Two-way binding does not support complex data paths currently. This two-way binding is ignored.

    双向绑定目前不支持复杂的数据路径。这种双向绑定将被忽略。

小程序修改数据

格式:

<元素 bind:事件名="处理函数">
this.setData({
  属性名1: 新值1,
  属性名2: 新值2
})


this.setData({
  "属性1.属性2":})
模板语法—条件渲染

小程序中的条件渲染的方式有两种

1.wx:if

    • 在小程序中,使用wx:if="{{条件}}"来判断是否需要渲染该代码块
    • 也可以用wx:elif 和 wx:else来添加else判断

2.hidden

    • 在小程序中,使用hidden="{{条件}}"也能控制元素的显示与隐藏
    • 条件为true则隐藏,否则则显示
wx:if 与 hidden区别

1.区别

    1. wx:if是通过动态创建或移除元素来控制元素是否可见
    2. hidden 是通过样式(none/block)来控制元素是否可见

2.要点

  • 如果一个标签频繁切换显示,建议使用 hidden。例如:折叠面板,抽屉面板等等
  • 如果不频繁切换,建议使用wx:if,它有更好的初始化性能。
模板语法—列表渲染—基础

在这里插入图片描述

格式

<元素 wx:for="{{列表数据}}" >
   <!--  wx:for 结构内可以使用两个变量(1)item:循环项(2)index:循环索引 -->
   {{item}}, {{index}}
</元素>

手动指定索引名和当前项的变量名

<view wx:for="{{list}}" wx:for-item="value" wx:for-index="key">
	{{key}}-{{value}}
</view>
模板语法-列表渲染-wx:key

wx:key 针对不同的数组类型有不同的写法

  • 普通数组 wx:key=“*this”
  • 数组对象 wx:key=“具有唯一性的某个属性名”
小程序内置API-网络请求

网络请求

调用 wx.request 能够在小程序中发起网络请求与后端接口进行数据的交互,其语法格式如下:

wx.request({
  url: '这里是接口的地址',
  method: '这里是请求的方法',
  data: '请求时提交的数据',
  header: {/* 请求头信息 */},
  success: () => {/* 成功的回调 */},
  fail: () => {/* 失败的回调 */},
  complete: () => {/* 成功或失败的回调 */}
})
配置网络请求合法域名

域名必须是https

在这里插入图片描述
在这里插入图片描述

小程序内置api-界面交互
showLoading效果

配合网络请求来使用

wx.showLoading 显示 loading 提示框

  • title 文字提示内容
  • mask 是否显示透明蒙层,防止触摸穿透
hideLoading
  • wx.hideLoading 隐藏 loading 提示框
showToast

wx.showToast 消息提示框(轻提示)

  • title 提示的标题
  • mask 是否显示透明蒙层,防止触摸穿透
  • duration 延迟时间(提示框显示多久,单位是毫秒)
  • icon 指定图标,none 不使用图标
操作注意:
  1. 发请求之前,showLoading
  2. 请求结束之后(无论成败),hideLoading
  3. 数据渲染成功之后,showToast
微信小程序本地存储
  • wx.setStorageSync(key, value) 存入一个数据,复杂类型数据不需要 JSON.stringify 处理
  • wx.getStorageSync(key) 读取本地key数据,复杂类型数据不需要 JSON.parse 处理
  • wx.removeStorageSync(key) 删除本地key数据
  • wx.clearStorageSync()清空本地全部数据
微信小程序API的特征

API的用法分类三类:

  1. 异步的api
  2. 同步的api
  3. 支持promise的api
异步 API

绝大部分的 API 都是异步方式,通过回调函数获取 API 执行的结果

  • success API 调用成功时执行的回调
  • fail API 调用失败时执行的回调
  • complete API 调用结束时执行的回调(无论成功或失败)

基本格式:

wx.api名称({success(res){ console.log(成功执行api的结果) }})
支持promise的api

部分异步的 API 也支持以 Promise 方式返回结果,此时可以配合 asyc/await 来使用。

例如:支持Promise格式的异步api有:

  • wx.getSystemInfoSync()
  • wx.getStorage

支持Promise格式的异步api有:

  • wx.request()
同步 API

部分 API 支持以同步方式获取结果,这些 API 的名称都 **Sync** 结尾。如

基本格式:

const result = wx.api名称()
事件处理-事件对象&传参

事件对象

bind:事件类型=事件回调 //回调函数第1个参数即为事件对象

事件回调传参

小程序的事件回调不支持传参数

因此要将模板中的数据传递到事件回调中就必须要通过事件对象来实现。

方式1:

  • 补充参数:

     <button bind:tap="eventHandler" **mark:属性名="值"**>点击我看看</button>
    
    
  • 获取值:

    eventHandler(ev){ console.log(**ev.mark.属性名**) 
    
    

方式2:

  • 补充参数 :

    <button bind:tap="eventHandler" **data-属性名="值"** >点击我看看</button>
    
    
  • 获取值:

    eventHandler(ev){ console.log(**ev.target.dataset.属性名**) }
    
    
事件处理-组件事件

事件类型只属于某个组件,我们将其称为组件事件

前面介绍的 tap 事件可以在绝大部分组件是监听,我们可以将其理解为通用事件类型,然而也有事件类型只属于某个组件,我们将其称为组件事件。

组件不同,支持的事件也不同

scroll-view组件中的事件

  1. bind:scrolltolower 当滚动内容到达底部或最右侧时触发
  2. bind:refresherrefresh 执行下拉操作时触发

另外,还有注意一个特别的属性

refresher-triggered 用它来控制下拉刷新状态

事件处理-表单组件中的事件

如何获取表单中,用户选择的值?

  1. input: 简易双向绑定
  2. radioGroup: 绑定change事件,在事件对象中detail.value拿到值
  3. checkboxGroup: 绑定change事件,在事件对象中detail.value拿到值
  4. picker: 绑定change事件,在事件对象中detail.value拿到值
  • change 表单数据发生改变时触发(input 不支持)

​ 5.整体表单提交

  • form: submit事件 表单提交时触发,button 按钮必须指定 form-type 属性
生命周期-页面生命周期
  • 分类
    • 应用生命周期
    • 页面生命周期
    • 组件生命周期

生命周期是一些名称固定,会自动执行的函数。

页面生命周期-基本使用

  • onLoad 在页面加载完成时执行,只会执行 1 次,常用于获取地址参数和网络请求

  • onReady页面初次渲染完成

  • onShow 在页面处于可见状态时执行,常用于动态更新数据或状态

  • onHide 在页面处于不见状态时执行,常用于销毁长时间运行的任务,如定时器

    页面生命周期-应用场景

onLoad(){ // 发起请求 }

onShow(){ // 动态更新数据或状态 }

onHide 在页面处于不见状态时执行,常用于销毁长时间运行的任务,如定时器

onReady 在页面初次渲染完成时执行,只会执行 1 次,常用于节点操作或动画交互等场景

生命周期-应用生命周期

app.js

  • onLaunch 小程序启动时执行1次,常用于获取场景值或者启动时的一些参数(如自定义分享)
  • onShow 小程序前台运行时执行,常用于更新数据或状态
  • onHide 小程序后台运行时执地,常用于销毁长时间运行的任务,如定时器。
// pages/lifetimes/index.js
Page({
  
  // 小程序转发/分享
  onShareAppMessage() {
    return {
      title: '小程序学习',
      path: '/pages/index/index?test=测试数据',
      imageUrl: '/static/images/cover.png'
    }
  }
})

小程序基础-分包加载&&自定义组件&&&项目全流程

小程序分包加载

小程序分包加载-为什么要分包加载

  • 微信平台对小程序单个包的代码体积限制为 2M,超过 2M 的情况下可以采用分包来解决
  • 即使小程序代码体积没有超过 2M 时也可以拆分成多个包来实现按需加载
  • 配置文件能忽略的只有静态资源,代码无法被忽略

在这里插入图片描述

配置忽略文件

project.config.json

{
  "description": "项目配置文件",
  "packOptions": {
    "ignore": [
      {
        "value": "static/uploads",
        "type": "folder"
      }
    ],
    "include": []
  },

type: 表示要忽略的资源类型

value: 表示具体要忽略的

小程序分包加载-使用分包配置

分类:

  1. 主包:

app.json

{
  // 省略其他的...
  
  "subPackages": [
      {
        "root": "subpkg_user", // 分包代码的目录,其实就是一个独立的文件夹
        "pages": [
          "pages/profile/profile"
        ]
      },
      {
        "root": "subpkg_order", // 文件夹
        "pages": [
          "pages/order_list/index", 
          "pages/order_list/index"
        ]
      }
  ]
}

注意: 写完分包之后,如果对应的文件夹和页面不存在,它会自动创建文件夹和页面

小程序分包—预加载

https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/preload.html

在打开小程序启动的时候只下载主包代码,分包并不会下载,因此能够提升小程序启动时的打开速度,但是分包的代码只有在访问到分包的页面时才去下载,这样用户就需要有一定时间的等待(一般不太影响),通过分包预加载技术可以实现提前去下载分包的代码,这样分包页面的访问速度也会得到提升。
小程序通过 preloadRule 配置需要预加载的分包。

app.json

{   ......
	"preloadRule": {
    "页面地址,进入这个页面就需要预加载分包": {       // pages/index/index 
      "network": "网络环境",                     // "wifi"
      "packages": ["要预加载的包名"]              //["goods_pkg"]
    }
  },   ......
}      //当用户访问到 pages/index/index 时,在 wifi 网络前提下预先下载 goods_pkg 分包的代码。
  • 指定某个页面路径做为 key,含义是当访问这个页面时会去预加载一个分包
  • network 预加载分包的网络条件,可选值为 all、wifi,默认为 wifi
  • packages 指定要预下载的分包名或根路径

配置完成之后,访问指定页面时,就会在控制台输出提示。

在这里插入图片描述

自定义组件—基本使用

创建组件

通常将项目中的组件都放在一个独立的目录下,并且一般就给这个文件夹取名为:components 。这个目录需要我们手动进行创建。

  1. 新建一个目录:components

  2. 在components上点击鼠标右键,选择「新建Component」

  3. 填入组件的名字。它会自动创建4个同名的文件。(这一点和创建页面是一样的)

在这里插入图片描述

组件和页面的结构区别:

  1. 组件的配置文件(.json文件)中,有一个配置项:component: true
  2. 组件的 .js 文件中调用 Component 函数,页面的.js文件中调用Page函数

注册组件

  • 页面注册是在使用组件的(xxxx.json)中通过 usingComponents 进行注册,只能在当前页面中组件
  • 全局注册是在 app.json 文件中通过 usingComponents 对自定义组件进行注册,可以在任意页面中使用
"usingComponents": {
    "my-test": "/components/MyTest/index"
}

使用组件

在wxml中,直接通过标签的方式使用即可。

自定义组件—组件样式
  1. 组件中的样式不要使用标签选择器
  2. 组件中,样式默认是隔离的: 自定义组件的样式只受到自定义组件 wxss 的影响
  3. 通过对组件的配置,可以取消这个隔离的状态

样式隔离注意点

  • app.wxss中的全局样式对组件无效
  • 只有class选择器具有样式隔离效果,id选择器、属性选择器、标签选择器不受样式隔离的影响

建议:在组件和引用组件的页面中建议使用class选择器,不要使用id、属性、标签选择器

修改组件样式的隔离选项

默认情况下,自定义组件的样式隔离特性能够防止组件内外样式互相干扰的问题。但有时,我们希望外界能够控制组件内部的样式,此时,可以通过在组件的.js文件中设置: options → addGlobalClass 为true

XX.js

Component({
  options: {
    addGlobalClass: true
  }
})

在页面中设置的同类名的选择器就能作用于子组件内部的元素。但是,组件内的class选择器,不能影响页面的元素。

自定义组件—组件样式-外部样式类

组件希望接受外部传入的样式类。此时可以在 Component 中用 externalClasses 定义若干个外部样式类。

在开发组件时,主动暴露给组件使用者,修改组件内部样式

组件 custom-component.js

/* 组件 custom-component.js */
Component({
  externalClasses: ['my-class']
});

组件 custom-component.wxml

<!-- 组件的wxml -->
<!-- 这里的my-class相当于一个占位符 -->
<view class="my-class">components/MyTest/index.wxml</view>

页面的 WXML

<!-- 页面的 WXML -->
<custom-component my-class="red-text" />
<custom-component my-class="large-text" />

页面的wxss

.red-text{ color: red; }
.large-text {font-size: 50px; }

外部样式类相当于用一个类名去当占位符,以便于在后期使用时替换成真实的类名,方便添加额外的样式。

参考:https://vant-contrib.gitee.io/vant-weapp/#/button#wai-bu-yang-shi-lei
在这里插入图片描述

自定义组件—数据方法

组件的典型结构

// borderImage.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    
  },

  /**
   * 组件的初始数据
   */
  data: {
    
  },

  /**
   * 组件的方法列表
   */
  methods: {
    
  }
})

定义数据

在小程序中,用于组件模板渲染的私有数据,需要定义到data

methods方法

在小程序的组件中,事件处理函数和自定义方法需要定义到methods

自定义组件—组件插槽
单个插槽

在小程序中,默认情况下每个自定义组件中只允许使用一个插槽进行占位。

<!--components/MyTest2/index.wxml-->
<view>
  <text>components/MyTest2/index.wxml</text>
  <!-- 对于不确定的内容,可以使用slot进行占位,具体内容交给使用者确定 -->
  <slot></slot>
</view>

使用组件

<my-test2>
  <!-- 这里的内容将被放到组件中<slot>的位置 -->
  <view>
    这里是slot里的内容
  </view>
</my-test2>

多插槽(具名插槽)

组价.js

Component({
  options: {
    multipleSlots: true // 在组件定义时的选项中启用多 slot 支持
  },
  // ... 省略其他
})

此时,可以在这个组件的 wxml 中使用多个 slot ,以不同的 name 来区分。

定义插槽

<view>
  <text>components/MyTest2/index.wxml</text>
  <!-- 对于不确定的内容,可以使用slot进行占位,具体内容交给使用者确定 -->
  <!-- <slot></slot> -->
  <slot name="before"></slot>
  <view>
    ---------这里是分割线--------
  </view>
  <slot name="after"></slot>
</view>

使用组件

<my-test2>
  <!-- 这里的内容将被放到组件中<slot>的位置 -->
  <!-- <view>
    这里是slot里的内容
  </view> -->
  <view slot="before">
    这里是before slot里的内容
  </view>
  <view slot="after">
    这里是after slot里的内容
  </view>
</my-test2>

自定义组件—生命周期

组件生命周期-lifetimes

生命周期参数描述
created在组件实例刚刚被创建时执行,此时还不能调用 setData,一般用于给组件的this添加一些自定义的属性字段
attached在组件实例进入页面节点树时执行,绝大多数初始化工作可以在这个时机进行,例如发请求获取初始数据
ready在组件在视图层布局完成后执行
moved在组件实例被移动到节点树另一个位置时执行
detached在组件实例被从页面节点树移除时执行,适合做一些清理工作
errorObject Error每当组件方法抛出错误时执行

生命周期函数要写在lifetimes里边

lifetimes: {
    created() {
      console.log('组件被created') // 这里使用setData不会引起视图的更新
      this.setData({ msg: 'abc!' })
    },
    attached() {
      this.setData({ msg: 'abcd' })
    }
  }

自定义组件-属性(父传子)

在小程序中,properties是组件的对外属性,用于接收外界传递到组件中的数据

父组件传入属性值

<my-test isOpen max="9" min="1" />

子组件.js中接收

Component({
	properties: {
    isOpen: Boolean,
  	min: Number, // 直接写类型
    max: {       // 写类型 + 初始值
    	type: Number,
      value: 10 // value用于指定默认值
    }
  }
})

自定义组件-组件通讯-自定义事件triggerEvent(子传父)

在这里插入图片描述

Vant组件库

官方文档:https://vant-contrib.gitee.io/vant-weapp/#/quickstart

步骤一 通过 npm 安装

npm i @vant/weapp -S --production

步骤二 修改 app.json

将 app.json 中的 "style": "v2" 去除

步骤三 修改 project.config.json

开发者工具创建的项目,miniprogramRoot 默认为 miniprogrampackage.json 在其外部,npm 构建无法正常工作。

需要手动在 project.config.json 内添加如下配置,使开发者工具可以正确索引到 npm 依赖的位置。

{
  ...
  "setting": {
    ...
    "packNpmManually": true,
    "packNpmRelationList": [
      {
        "packageJsonPath": "./package.json",
        "miniprogramNpmDistDir": "./miniprogram/"
      }
    ]
  }
}

步骤四 构建 npm 包(重点)

开发者工具上 > “工具” > “构建npm”

在这里插入图片描述

使用

去app.json(全局注册)或页面.json(局部注册)中注册

"usingComponents": {
  "van-button": "@vant/weapp/button/index"
}

在页面中使用

<van-button type="primary">按钮</van-button>

小程序开发环境-优化目录结构

项目的根目录
├── miniprogram  // 项目相关的代码夹
├── node_modules // npm包目录
├── package.json
├── project.config.json
├── package-lock.json
└── sitemap.json

在这里插入图片描述

修改 project.config.json 中的配置项

{
  // 省略其他......
  "setting": {
    // 省略其他......
    "packNpmManually": true,
    "packNpmRelationList": [
      {
        "miniprogramNpmDistDir": "./miniprogram",
        "packageJsonPath": "package.json"
      }
    ]
  },
  "miniprogramRoot": "miniprogram/"
}

启用 less/sass

通过 less/sass 可以更好的管理 css 样式,通过 project.config.json 可以启用对 less/sass 的支持。

{
  "setting": {
    "useCompilerPlugins": ["sass"]
  }
}

然后将 .wxss 文件后缀改换成 .scss 即可。

启动项目

  1. 拉取代码
  2. 导入项目

使用小程序开发者工具导入【项目】的代码

3. 使用小程序开发者工具构建 npm

  1. 安装包:npm install
  2. 手动构建: 【工具】→【构建npm】

project.config.json的几个配置

{
  "miniprogramRoot": "miniprogram/",
  "setting": {
    "useCompilerPlugins": ["sass"],
    "packNpmManually": true,
    "packNpmRelationList": [
      {
        "packageJsonPath": "./package.json",
        "miniprogramNpmDistDir": "./miniprogram"
      }
    ],
  }
}
  • miniprogramRoot 项目的根目录为 miniprogram
  • setting.useCompilerPlugins 启用了 sass 支持
  • packNpmRelationList 指定了 npm 构建时所需的 package.json 的位置以及构建后代码的生成位置

4. 改成自己的appid

这个项目中的appid是别人的,如果我们需要改成自己的。

基础封装-消息反馈

将所有通用的工具方法封装到 utils/utils.js 中

/**
 * 用户消息反馈
 * @param {string} title 文字提示的内容
 */
export const toast = (title = '数据加载失败...') => {
  wx.showToast({
    title,
    mask: true,
    icon: 'none',
  })
}
// 挂载到全局对象 wx
wx.$toast = toast

app.js

// 在入口中执行 utils.js
import './utils/utils.js'
App({
  // ...
})

使用

 wx.$toast('//提示文字', "icon图标")
基础封装-网络请求

安装第三方的包-构建

  1. npm install wechat-http
  2. 安装完成后还必须要构建 npm后才可以使用

wechat-http用法与 axios 类似:

  • http.baseURL 配置接口基础路径
  • http.getGET 方法发起请求
  • http.postPOST 方法发起请求
  • http.putPUT 方法发起请求
  • http.deleteDELETE 方法发起请求
  • http.intercept 配置请求和响应拦截器
  • http 本身做为函数调用也能用于发起网络请求

二次封装

新建 utils/http.js 文件

// 导入 http 模块
import http from 'wechat-http'
// 基础路径
http.baseURL = 'https://live-api.itheima.net'
// 挂载到全局对象
wx.http = http
// 普通的模块导出
export default http

以全局对象方式调用时需要在入口中执行 utils/http.js

// 执行 uitls/http.js
import './utils/http.js'
App({
  // ...
})

配置响应拦截器

// 配置响应拦截器
http.intercept.response = function ({ data, config }) {

  // 检测接口是否正常返回结果
  if (data.code !== 10000) {
    wx.$toast()
    return Promise.reject(data)
  }

  // 只保留data数据,其它的都过滤掉
  return data.data
}
跳转传参

点击公告列表后将公告的ID通过地址参数传递到公告详情页面,在公告详情页 onLoad 生命周期中读取到公告 ID,然后调用接口获取公告详情的数据。

在这里插入图片描述
在这里插入图片描述

van-count-down 组件(倒计时)的应用
<van-count-down use-slot time="{{6000}}" bind:change="countDownChange">
  <text>{{timeData.seconds}}秒后重新获取</text>
</van-count-down>

time: 指定了倒计时多少毫秒

bind:change每隔一秒的回调,它会传出来当前的倒计时信息

 countDownChange(ev) {
    console.log(ev.detail)
    this.setData({
      timeData: ev.detail,
      getCodeBtnVisible: ev.detail.minutes === 0 && ev.detail.seconds === 0,
    })
  },
表单验证插件使用

先在data中设置mobile,再在模板中进行双向绑定

model:value=“{{mobile}}”

  1. 安装构建 表单验证码插件 wechat-validate
npm install wechat-validate
  1. 将插件导入到项目中
  • behaviors 将插件注入到页面中
  • rules 由插件提供的属性,用来定义数据验证的规则(类似于 Element UI)
  • validate 由插件提供的方法,根据 rules 的规则来对数据进行验证
// 导入表单验证插件
import validate from 'wechat-validate'
Page({
  data: {
    mobile: '' // 省略其他
  },
  behaviors: [validate], // 将插件注入到页面实例中
  rules: {
    mobile: [
      {required: true, message: '请填写手机号码!'},
      {pattern: /^1[3-8]\d{9}$/, message: '请填写正确的手机号码!'}
    ]
  },
  getSMSCode() {
    // 获取验证结果
    const { valid, message } = this.validate('mobile')
    // 如果验证不合法则不再执行后面的逻辑
    if (!valid) return wx.$toast(message)
    console.log('getCode')
    this.setData({ getCodeBtnVisible: false })
  },
})
保存token-跳转
  1. 在app.js中设置setToken方法,保存到本地存储
App({
  // ...
  setToken(key, token) {
 		// 将 token 记录在应用实例中   
    this[key] = token
    // 将 token 存入本地
    wx.setStorageSync(key, token)
  }
})

2.点击登录 | 注册发送请求成功之后

  const app = getApp()    //小程序中获取全局的实例对象
  app.setToken('token', res.token)
  app.setToken('refreshToken', res.refreshToken)
 //  跳转
  const url = '/pages/profile/index'
  wx.redirectTo({ url })
登录检测-鉴权组件

在这里插入图片描述

1.在根目录中创建 components 文件夹用来存放全局的组件,然后通过小程序开发者工具创建一个名为 authorization 的组件

2.接下来全局来注册这个组件,保证任何页面中都可以直接应用 authorization 组件

{
  "usingComponents": {
    "authorization": "/components/authorization/index"
  },
}

3.到用户信息页面中应用 authorization 使用做为页面根节点

<authorization>
  	...
</authorization>

4.在authorization中补充插槽和状态

<!--components/authorization/index.wxml-->
<slot wx:if="{{isLogin}}"></slot> 
  data: {
    isLogin: false
  },

读取本地存储token

读取本地存储的 token 数据,用于判断是否曾登录过

// app.js
App({
  ......
    getToken() {
    // 将 token 数据记到应用实例中
    // return this.token = wx.getStorageSync('token')
    return this.token
  }
})

在组件内读token并处理

data: {
  isLogin: false
},
lifetimes: {
  attached() {
    const isLogin = !!getApp().getToken()    
    //const app = getApp()  const isLogin = !!app.getToken()
    this.setData({ isLogin })
    if (!isLogin) {
      wx.redirectTo({ url: '/pages/login/index' })
    }
  }
},

地址重定向,登录成功后跳回到原来的页面

authoirzation 组件检测登录时获取当前页面栈实例,并在跳转到登录页面时在 URL 地址上拼凑参数:

// /components/authorization/index.js
Component({
  // ...
  lifetimes: {
    attached() {
      // 获取登录状态
      const isLogin = !!getApp().token
      // 变更登录状态
      this.setData({ isLogin })
      // 获取页面栈
      const pageStack = getCurrentPages()
      // 获取页面路径
      const currentPage = pageStack.pop()
      // 未登录的情况下跳转到登录页面
      if (!isLogin) {
        wx.redirectTo({
          url: '/pages/login/index?redirectURL=/' + currentPage.route,
        })
      }
    },
  },
})

在这里插入图片描述

用户管理-显示默认值

app.js中添加初始值

App({
  globalData: {},
  userInfo: { avatar: '', nickName: '微信用户1' }
}

在onLoad中加载值

data: {
  avatar: '',
  nickName: ''
},
onLoad() {
  const app = getApp()
  console.log(app.userInfo)
  const { avatar, nickName } = app.userInfo
  this.setData({ avatar, nickName })
  // 用户未登录时不必请求
  app.token && this.getUserProfile()
},
配置请求拦截器

将用户的登录状态通过自定义的头信息 Authorization 随接口调用时一起发送到服务端。

// 导入 wechat-http 模块
import http from 'wechat-http'
// 配置接口基础路径
http.baseURL = 'https://live-api.itheima.net'
// 配置请求拦截器

http.intercept.request = function (options) {
  console.log('请求拦截器', options.header)
  // 扩展头信息
  const defaultHeader = {}
  // 身份认证
  const token = getApp().getToken()
  if (token) {
    defaultHeader.Authorization = 'Bearer ' + getApp().getToken()
  }
  // 与默认头信息合并
  options.header = Object.assign({}, defaultHeader, options.header)
  // 处理后的请求参数
  return options
}

注:传递 token 时需要拼凑字符串前缀 "Bearer "

文件上传(例:更新用户头像)

获取用户选择的头像地址,通过 wx.uploadFile 将图片上传到服务端。

wx.uploadFile 的基本语法:

  • url 上传接口地址
  • filePath 待上传文件的临时路径(该路径只能用于小程序内部)
  • name 接口接收上传文件的数据名称(由后端指定)
  • formData 除上传文件外的其它数据
  • header 自定义头信息
  • success 上传成功的回调函数
  • fail 上传失败后的回调函数
  • complete 上传完成时的回调(无论成功或失败)

注:该 API 不支持返回 Promise,调用该 API 时,需要提前在小程序管理后台添加服务器域名。

<!-- pages/profile/index.wxml -->
<authorization>
  <view class="profile">
    <van-cell center title="头像">
      <van-icon slot="right-icon" name="arrow" size="16" color="#c3c3c5" />
      <button
        class="button"
        size="mini"
        hover-class="none"
        bind:chooseavatar="updateUserAvatar" //事件,事件名全部小写,A千万不要大写,不会触发
        open-type="chooseAvatar">    //与上边事件对应
        <image class="avatar" src="{{avatar}}"></image>
      </button>
    </van-cell>
    ...
  </view>
</authorization>
// pages/profile/index.js
const pageStack = getCurrentPages()
Page({
    ...
  // 更新用户头像
  updateUserAvatar(ev.detail.avatarUrl) {
    // 调用 API 上传文件
    wx.uploadFile({
      // 接口地址
      url: wx.$http.baseURL + '/upload',
      // 待上传的文件路径
      filePath: avatar,
      name: 'file',// wx.uploadFile 要求必传。
      header: {
        Authorization: 'Bearer ' + getApp().getToken()   // 用户登录状态
      },
      formData: { // 是我们自己的接口文档的要求。可以不传,默认就是avatar
        type: 'avatar'
      },
      success: (result) => {
        console.log(JSON.parse(result.data))
        const res = JSON.parse(result.data)
        // console.log(res.data.url)
        const avatar = res.data.url
        // 1. 在页面上显示
        this.setData({ avatar })
        // 2. 更新全局数据
        const app = getApp()
        app.userInfo.avatar = avatar
        // 3. 通过页面栈找到my/index页面,更新它的avatar信息
        const pages = getCurrentPages()
        // pages[0].data.nickName = nickName 直接修改数据不会让视图更新
        // 调用setData更新
         pages[0].setData({ avatar })
      }
    })
  }
})

上述代码中通过 wx.http.baseURL 获取接口服务器地址,通过应用实例获取 token

refresh_token使用

在这里插入图片描述

  • token:
    • 作用:在访问一些接口时,需要传入token,就是它。
    • 有效期:2小时(安全)。
  • refresh_token
    • 作用: 当token的有效期过了之后,可以使用它去请求一个特殊接口(这个接口也是后端指定的,明确需要传入refresh_token),并返回一个新的token回来(有效期还是2小时),以替换过期的那个token。
    • 有效期:14天。(最理想的情况下,一次登陆可以持续14天。)

在这里插入图片描述

1.用户在首次完成登录时会分别得到 token 和 refresh_token

2.当 token 失效后(例如2小时之后),调用接口A会返回 401 状态码(这是与后端约定好的规则)

3.检测状态码是否为 401**,如果是,则携带refreshToken去调用刷新token的接口

4.刷新 token 的接口后会返回新的 token 和 refreshToken

5.把401的接口A重新发送一遍

注意:
refresh_token也是有过期时间的,只不过一般会比token过期时间更长一些。这就是为啥如果某个应用我们天天打开,则不会提示我们登录,如果是有几周或更长时间去打开时,会再次要求我们登录。
refresh_token一个更常见的名字叫token无感刷新。

refreshToken功能-基本实现
// 响应拦截器
http.intercept.response = async ({ statusCode, data, config }) => {
  console.log(statusCode, data, config)
  // console.log(statusCode) // http 响应状态码
  // console.log(config) // 发起请求时的参数
  if (data.code === 401) {
    const app = getApp()
    // 调用接口获取新的 token
    const res = await http({
      url: '/refreshToken',
      method: 'POST',
      header: {
        Authorization: 'Bearer ' + app.getToken('refreshToken'),
      }
    })

    app.setToken('token', res.token)
    app.setToken('refreshToken', res.refreshToken)
     // 获得新的token后需要重新发送刚刚未完成的请求
    config = Object.assign(config, {
      header: {
        // 更新后的 token
        Authorization: 'Bearer ' + res.token,
      },
    })
    // 重新发请求
    return http(config)
  }
  // 拦截器处理后的响应结果
  if (data.code === 10000) {
    return data.data
  } else {
    wx.$toast(data.message || '请求失败')
    return Promise.reject(data.message)
  }
}
refreshToken也过期的特殊处理

在这里插入图片描述

完整版响应拦截器

// 响应拦截器
http.intercept.response = async ({ statusCode, data, config }) => {
  console.log(statusCode, data, config)
  // console.log(statusCode) // http 响应状态码
  // console.log(config) // 发起请求时的参数
  if (data.code === 401) {
++    if (config.url.includes('/refreshToken')) {
++      console.log('/refreshToken过期了')
++      // 获取当前页面的路径,保证登录成功后能跳回到原来页面
++      const pageStack = getCurrentPages()
++      const currentPage = pageStack.pop()
++      const redirectURL = currentPage.route
++      // 跳由跳转(登录页面)
++      wx.redirectTo({
++        url: '/pages/login/index?redirectURL=/' + redirectURL,
++      })
++      return Promise.reject('refreshToken也过期了,就只能重新登录了')
++    }
    const app = getApp()
    // 调用接口获取新的 token
    const res = await http({
      url: '/refreshToken',
      method: 'POST',
      header: {
        Authorization: 'Bearer ' + app.getToken('refreshToken'),
      }
    })

    app.setToken('token', res.token)
    app.setToken('refreshToken', res.refreshToken)
    config = Object.assign(config, {
      header: {
        // 更新后的 token
        Authorization: 'Bearer ' + res.token,
      },
    })
    // 重新发请求
    return http(config)
  }
  // 拦截器处理后的响应结果
  else if (data.code === 10000) {
    return data.data
  } else {
    wx.$toast(data.message || '请求失败')
    return Promise.reject(data.message)
  }
}
腾讯位置服务-需要提前注册

文档地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/jsSdkOverview

使用步骤(共4步)

  1. 申请开发者密钥(key):申请密钥(地址:https://lbs.qq.com/dev/console/application/mine)
  2. 开通webserviceAPI服务:控制台 ->应用管理 -> 我的应用->添加key-> 勾选WebServiceAPI -> 保存(小程序SDK需要用到webserviceAPI的部分服务,所以使用该功能的KEY需要具备相应的权限)

在这里插入图片描述

3.下载微信小程序JavaScriptSDK,微信小程序JavaScriptSDK v1.1(https://mapapi.qq.com/web/miniprogram/JSSDK/qqmap-wx-jssdk1.1.zip) JavaScriptSDK v1.2(https://mapapi.qq.com/web/miniprogram/JSSDK/qqmap-wx-jssdk1.2.zip) js文件

4.安全域名设置,在小程序管理后台-> 开发 -> 开发管理 -> 开发设置 -> “服务器域名” 中设置request合法域名,添加https://apis.map.qq.com

地理定位-wx.getLocation

在这里插入图片描述

获取用户所在位置的经纬度。在小程序中调用这个接口时必须先在 app.json 中申请调用权限(开发环境可以省略)。

//app.json
{
  "requiredPrivateInfos": [
++    "getLocation"
  ],
  "permission": {
    "scope.userLocation": {
      "desc": "你的位置信息将用于小程序位置接口的效果展示"
    }
  },
}

在这里插入图片描述

Page({
  onLoad() {
    this.getLocation()
  },
  async getLocation() {
    const res = await wx.getLocation() // 要提前申请权限
    console.log(res)
  },
})

wx.getLocation返回的结果格式大致如下:

accuracy: 65
errMsg: "getLocation:ok"
horizontalAccuracy: 65
latitude: 30.88131
longitude: 114.37509
speed: -1
verticalAccuracy: 65

wx.getLocation 只能得到经纬度信息

逆地址解析-reverseGeocoder

由坐标 → 坐标所在位置的文字描述的转换,输入坐标返回地理位置信息和附近poi列表

文档地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodReverseGeocoder

1. 导入 QQMapWX 并设置好 key

在这里插入图片描述

2.在代码中补充getPoint方法:

  1. 调用接口把经纬度转换成对应位置的文字
  2. 保存文字到address
// 导入位置服务实例
import QQMap from '../../../utils/qqmap'

Page({   ......
  onLoad() {
    this.getLocation()
  },
  async getLocation() {
    // 调用小程序API获取经纬度等信息
    const { latitude, longitude } = await wx.getLocation()  //获取用户经纬度
    this.getPoint(latitude, longitude)  
  },
  getPoint(latitude, longitude) {
    // 逆地址解析(根据经纬度来获取地址)
    QQMap.reverseGeocoder({
      location: [latitude, longitude].join(','),
      success: (result) => {
        const address = res.address
        this.setData({ address })
      },
    })
  }
})

3.渲染页面

 <van-cell-group border="{{false}}" title="当前地点">
  <van-cell title="{{address}}" border="{{false}}">  //border="{{false}}"设置无边框样式
    <text bind:tap="chooseLocation" class="enjoy-icon icon-locate">重新定位</text>
  </van-cell>
</van-cell-group>
QQMap地点搜索—search

根据当前的定位,调用 QQMap.search() 找到周边的信息。

搜索周边poi(Point of Interest),比如:“酒店” “餐饮” “娱乐” “学校” 等等
文档地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodSearch

在这里插入图片描述

在小程序中调用这个接口时必须要在 app.json 中申请调用权限

//app.json
{
  "requiredPrivateInfos": [
++    "chooseLocation"
  ]
}
// 选择新的位置
async chooseLocation() {
  // 调用小程序 API 获取新的位置
  const { latitude, longitude } = await wx.chooseLocation()
  this.getPoint(latitude, longitude)   // 获取新的位置经纬度
},

getPoint(latitude, longitude) {
  wx.showLoading({
    title: '正在加载...',      // 显示loading提示
  })

  // 逆地址解析(根据经纬度来获取地址)
  QQMap.reverseGeocoder({
    location: [latitude, longitude].join(','),
    success: ({ result: { address } }) => {
      this.setData({ address })
    },
  })

  QQMap.search({
    keyword: '住宅小区', //搜索关键词
    location: [latitude, longitude].join(','), //设置周边搜索中心点
    page_size: 5,   //只显示5条信息,不设置此项默认为10
    success: (result) => {     //success 是一个回调函数,表示搜索成功后的处理逻辑。
      const points = result.data
      this.setData({ points }) // 渲染数据
    },
    fail: (err) => {    //fail 是一个回调函数,表示搜索失败后的处理逻辑。
      console.log(err.message)
    },
    complete: () => {//complete 是一个回调函数,表示搜索结束后的处理逻辑(无论搜索成功还是失败)
      wx.hideLoading()   // 隐藏loading提示
    },
  })
},
重新定位-wx.chooseLocation

在这里插入图片描述

申请权限

获取用户指定位置的经纬度。在小程序中调用这个接口时必须要在 app.json 中申请调用权限

{
  "requiredPrivateInfos": [
    "chooseLocation"
  ]
}

示例代码:

// 选择新的位置
async chooseLocation() {
  // 调用小程序 API 获取新的位置
  const { latitude, longitude } = await wx.chooseLocation()
  // 获取新的位置附近的小区
  this.getPoint(latitude, longitude)

  console.log('起点位置:', latitude, longitude)
},

getPoint(latitude, longitude) {
  // 显示loading提示
  wx.showLoading({
    title: '正在加载...',
  })

  // 逆地址解析(根据经纬度来获取地址)
  QQMap.reverseGeocoder({
    location: [latitude, longitude].join(','),
    success: ({ result: { address } }) => {
      // console.log(address)
      // 数据数据
      this.setData({ address })
    },
  })

  QQMap.search({
    keyword: '住宅小区', //搜索关键词
    location: [latitude, longitude].join(','), //设置周边搜索中心点
    page_size: 5,
    success: (result) => {
      // console.log(result)
      // 过滤掉多余的数据
      const points = result.data.map(({ id, title, _distance }) => {
        return { id, title, _distance }
      })

      // console.log(points)
      // 渲染数据
      this.setData({ points })
    },
    fail: (err) => {
      console.log(err.message)
    },
    complete: () => {
      // 隐藏loading提示
      wx.hideLoading()
    },
  })
},
图片收集(收集身份证信息)

小程序没有input type="file"用于选择文件,要实现类似功能,用以下api:
wx.chooseMedia**:**拍摄或从手机相册中选择图片或视频。
低版本请用wx.chooseImage

1.绑定事件选择身份证图片上传。

2.发送请求上传图片,拿到上传后的图片地址。

Page({
 ...
  async uploadPicture(ev) {
    // 获取图片临时地址
    const res = await wx.chooseMedia({
      count: 1,
      mediaType: ['image'],
      sizeType: ['compressed'],
    })
    const tempPath = res.tempFiles[0].tempFilePath
    const type = ev.mark.type
    // 上传图片到服务器
    wx.uploadFile({
      url: wx.$http.baseURL + '/upload',
      filePath: tempPath,
      name: 'file',
      header: {
        Authorization:  'Bearer ' +  getApp().getToken(),
      },
      success: (res) => {
        const res = JSON.parse(result.data)
        console.log(res.data.url) 							// 上传成功的回调
        this.setData({ [type]: res.data.url })
      },
    })
  },
})
校验表单信息

获取了全部的表单数据后再对数据进行验证,说明如下:

  • 房屋的信息是通过url地址获取的不需要验证
  • 性别可以指定默认值也不需要验证
  • 剩下的数据通过 wechat-validate 插件进行验证:
// house_pkg/pages/form/index.js
// 导入表单验证插件
import wxValidate from 'wechat-validate'
Page({
  behaviors: [wxValidate],
  data: {
    point: '',
    building: '',
    room: '',
    name: '',
    gender: 1,
    mobile: '',
    idcardFrontUrl: '',
    idcardBackUrl: '',
  },
  rules: {
    name: [
      { required: true, message: '业主姓名不能为空!' },
      { pattern: /^[\u4e00-\u9fa5]{2,5}$/, message: '业主姓名只能为中文!' },
    ],
    mobile: [
      { required: true, message: '业主手机号不能为空!' },
      { pattern: /^1[3-8]\d{9}$/, message: '请填写正确的手机号!' },
    ],
    idcardFrontUrl: [
      { required: true, message: '请上传身份证国徽面!' }
    ],
    idcardBackUrl: [
      { required: true, message: '请上传身份证照片面!' }
    ],
  },
})
表单收集—收集预约日期

1.时间选择控件:van-datetime-pickerhttps://vant-contrib.gitee.io/vant-weapp/#/datetime-picker

2.弹出层控件:van-popuphttps://vant-contrib.gitee.io/vant-weapp/#/popup

  1. 给选择日期单元绑定事件——显示选择日期弹层(van-popup)

2.给日期控件绑定确认事件confirm

3.在确认回调中获取时间戳

4.将时间戳格式化并显示

selectDate(ev) {
  // console.log(ev)
  this.setData({
    currentDate: ev.detail,
    appointment: wx.$utils.formatDate(ev.detail), // 格式化日期
  })
  this.closeDateLayer()
},

补充格式化日期的函数

formatDate(time) {
  const d = new Date(time)
  const year = d.getFullYear()
  let month = d.getMonth() + 1 // 获取月份,月份从0开始,所以加1
  let day = d.getDate()
  month = month < 10 ? '0' + month : month
  day = day < 10 ? '0' + day : day
  return `${year}-${month}-${day}`
},

wxml

<van-cell title-width="100" title="预约日期" value-class="{{appointment && 'active-cell'}}" bind:click="openDateLayer"
        is-link value="{{appointment || '请选择上门维修日期'}}" />
...省略
<van-popup bind:close="closeDateLayer" round show="{{ dateLayerVisible }}" position="bottom">
  <van-datetime-picker bind:cancel="closeDateLayer" bind:confirm="selectDate" type="date" value="{{ currentDate }}"
    min-date="{{ 1664582400000 }}" />
</van-popup>
加载更多

在scroll-view上添加 bindscrolltolower=“loadMore”

  <scroll-view 
  bindscrolltolower="loadMore"
  show-scrollbar="{{false}}" enhanced scroll-y>
    <view class="repairs">
      <view class="repairs-title">我的报修</view>
loadMore() {
    // if(是否有更多的数据)
    if (this.data.total <= this.data.list.length)
      return
    console.log('更多的数据')
    // 把页码+1,发请求,请求回来的数据要 追加 到原数组
    // [5] → [10]
    this.data.page++
    this.getList()

  },
  async getList() {
    // 发请求
    const { pageTotal, total, rows: list } = await wx.$http.get('/repair', { current: this.data.page, pageSize: this.data.pageSize })
    console.log(list, pageTotal, total)
    // 渲染数据
    // 在原来的基础上添加数据
    this.setData({ total, list: [...this.data.list, ...list] })
  },

路线规划

路线规划是常见的一个功能,它用来在地图上展示两点间路线,要使用这个功能需要用到两部分的知识:

  1. 小程序提供的 map 组件用来在页面渲染地图

map | 微信开放文档https://developers.weixin.qq.com/miniprogram/dev/component/map.html)

  1. 腾讯位置服务计算两点路线的所有坐标点(经纬度)

首先来看小程序提供的地图组件 map

  • latitude 指定地图中心点的纬度
  • longitude 指定地图中心点的经功
  • scale 指定地图初始的缩放比例,取值范围 3 - 20
  • markers 地图上的标记点
  • polyline 地图上的路线

latitude、longitude、scale 相对容易理解,重点来看一下 markers 的使用:

repqir_pkg/pages/detail/index.js

Page({
  data: {
    markers: [
      {
        id: 1,
        latitude: 40.22077,
        longitude: 116.23128,
        width: 24,
        height: 30,
      },
      {
        id: 2,
        latitude: 40.225857999999995,
        longitude: 116.23246699999999,
        iconPath: '/static/images/marker.png',
        width: 40,
        height: 40,
      },
    ],
  }
})

在定义标记点时每个标记点必须要指定 ID 属性,否则会有错误产生,通过 iconPath 可以自定义标记点的图片,width/height 定义标记点的大小尺寸。

polyline

polyline 用来在在图上展示两点间的行进路线,需要传递给它路线对应的坐标点(很多个点组成的线),获取这些坐标点需要通过位置服务计算得到。

计算两点间路线的坐标点需要用到位置服务的[路线规划]https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodDirection方法

repqir_pkg/pages/detail/index.js

// repqir_pkg/pages/detail/index.js
Page({
  data: {},
  onLoad() {
    // 生成路线
    this.getPolyline()
  },
  // 调用位置服务(路线规划)
  getPolyline() {
    qqMap.direction({
      mode: 'bicycling',
      from: '40.227978,116.22998',
      to: '40.22077,116.23128',
      success: ({ result }) => {
        const coors = result.routes[0].polyline
        const points = []
        //坐标解压(返回的点串坐标,通过前向差分进行压缩)
        for (let i = 2; i < coors.length; i++) {
          coors[i] = Number(coors[i - 2]) + Number(coors[i]) / 1000000
        }
        // 获取经纬度
        for (let i = 0; i < coors.length; i += 2) {
          points.push({ latitude: coors[i], longitude: coors[i + 1] })
        }
        // 渲染数据
        this.setData({
          latitude: points[30].latitude,
          longitude: points[30].longitude,
          polyline: [
            {points, color: '#5591af', width: 4},
          ],
        })
      },
    })
  },
})

计算出来的坐标点是经过压缩的,需要按着官方指定的方式对数据进行解压才可以获取真正的坐标点,并且为了适应小程序地图组件的用法,还需要对数据进行二次的加工。

关于数据的处理大家只需要参考文档来实现就可以,可以说之所这么操作是腾讯位置服务规订好的,做为开发者按着官方提从的方法来应用即可。

自定义分享

[onShareAppMessage]https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareAppMessage-Object-object

监听用户点击页面内转发按钮(button 组件 open-type=“share”)或右上角菜单“转发”按钮的行为,并自定义转发内容。

注意:只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮

Page({
  onLoad({ id, encryptedData }) {
    this.getPassport(id)
    this.getPassportShare(encryptedData)
  },
  // 获取访客详情(通行证)
  async getPassport(id) {
    // 检测是否存在 id
    if (!id) return
    // 调用接口
    const passport = await wx.$http.get('/visitor/' + id)
    // 渲染数据
    this.setData({ ...passport })
  }
  async getPassportShare(encryptedData) {
    // 检测是否存在 id
    if (!encryptedData) return
    // 调用接口
    const passport = await wx.$http.get('/visitor/share/' + this.data.encryptedData)
    // 渲染数据
    this.setData({ passport })
  },
  onShareAppMessage() {
    return {
      title: '查看通行证',
      path: '/visitor_pkg/pages/passport/index?encryptedData=' + encryptedData,
      imageUrl: 'https://enjoy-plus.oss-cn-beijing.aliyuncs.com/images/share_poster.png',
    }
  },
})
保存到本地api介绍

[saveImageToPhotosAlbum]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html

保存图片到系统相册。

[getImageInfo]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.getImageInfo.html

获取图片信息。网络图片需先配置 download 域名才能生效。

在这里插入图片描述

保存到本地实现

1.给按钮绑定事件,调用getImageInfo获取图片临时路径

2.调用saveImageToPhotosAlbum传入临时路径完成保存功能

// 保存图片
  async saveQRCode() {
    try {
      // 读取图片信息
      const { path } = await wx.getImageInfo({
        src: this.data.url,
      })
      // 保存图片到相册
      wx.saveImageToPhotosAlbum({ filePath: path })
    } catch (err) {
      wx.$toast('保存图片失败,稍后重试!')
    }
  },

ush({ latitude: coors[i], longitude: coors[i + 1] })
}
// 渲染数据
this.setData({
latitude: points[30].latitude,
longitude: points[30].longitude,
polyline: [
{points, color: ‘#5591af’, width: 4},
],
})
},
})
},
})


计算出来的坐标点是经过压缩的,需要按着官方指定的方式对数据进行解压才可以获取真正的坐标点,并且为了适应小程序地图组件的用法,还需要对数据进行二次的加工。

关于数据的处理大家只需要参考文档来实现就可以,可以说之所这么操作是腾讯位置服务规订好的,做为开发者按着官方提从的方法来应用即可。

### 自定义分享

**[onShareAppMessage]https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareAppMessage-Object-object**

监听用户点击页面内转发按钮([button](https://developers.weixin.qq.com/miniprogram/dev/component/button.html) 组件 open-type="share")或右上角菜单“转发”按钮的行为,并自定义转发内容。

**注意:只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮**

```js
Page({
  onLoad({ id, encryptedData }) {
    this.getPassport(id)
    this.getPassportShare(encryptedData)
  },
  // 获取访客详情(通行证)
  async getPassport(id) {
    // 检测是否存在 id
    if (!id) return
    // 调用接口
    const passport = await wx.$http.get('/visitor/' + id)
    // 渲染数据
    this.setData({ ...passport })
  }
  async getPassportShare(encryptedData) {
    // 检测是否存在 id
    if (!encryptedData) return
    // 调用接口
    const passport = await wx.$http.get('/visitor/share/' + this.data.encryptedData)
    // 渲染数据
    this.setData({ passport })
  },
  onShareAppMessage() {
    return {
      title: '查看通行证',
      path: '/visitor_pkg/pages/passport/index?encryptedData=' + encryptedData,
      imageUrl: 'https://enjoy-plus.oss-cn-beijing.aliyuncs.com/images/share_poster.png',
    }
  },
})
保存到本地api介绍

[saveImageToPhotosAlbum]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html

保存图片到系统相册。

[getImageInfo]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.getImageInfo.html

获取图片信息。网络图片需先配置 download 域名才能生效。

[外链图片转存中…(img-M8SkNwoO-1687267859406)]

保存到本地实现

1.给按钮绑定事件,调用getImageInfo获取图片临时路径

2.调用saveImageToPhotosAlbum传入临时路径完成保存功能

// 保存图片
  async saveQRCode() {
    try {
      // 读取图片信息
      const { path } = await wx.getImageInfo({
        src: this.data.url,
      })
      // 保存图片到相册
      wx.saveImageToPhotosAlbum({ filePath: path })
    } catch (err) {
      wx.$toast('保存图片失败,稍后重试!')
    }
  },
  • 4
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
**微信小程序案例:SSM研知识题库小程序** 此资源是一套完整的小程序项目源码及说明文档,旨在帮助计算机专业的毕业生快速实现一个基于SSM框架(Spring、SpringMVC、MyBatis)的微信小程序。该项目不仅可作为毕业设计的参考,还可作为实际项目开发基础,具有很高的实用价值。 **一、项目简介** SSM研知识题库小程序是一个专为研究生打造的在线学习平台,提供丰富的题库资源和便捷的在线答题功能。用户可以通过该小程序进行知识点学习和自测,提高学习效率。同时,管理员可以对题库进行管理,包括题目的添加、修改和删除等操作。 **二、技术架构** 1. 前端:采用微信小程序原生开发,界面美观,交互流畅。 2. 后端:基于SSM框架,使用Spring负责业务逻辑处理,SpringMVC负责请求分发和视图渲染,MyBatis负责数据库操作。 3. 数据库:采用MySQL,存储题库数据及相关信息。 **三、核心功能** 1. 用户登录注册:支持用户通过微信一键授权登录或手动注册账号。 2. 题库浏览:用户可以查看所有题库列表,按科目分类浏览题目。 3. 在线答题:用户选择题目进行在线答题,系统自动判分并显示答案解析。 4. 个人中心:用户可以查看自己的答题记录和学习进度。 5. 管理员后台:管理员可以对题库进行增删改查操作,管理用户信息等。 **四、项目优势** 1. 开源:项目源码完开源,方便二次开发和定制。 2. 易用性:基于成熟的SSM框架,降低了开发难度和维护成本。 3. 可扩展性:项目结构清晰,功能模块划分合理,便于后期扩展新功能。 4. 实用性:针对研究生学习需求设计,具有较高的实用价值和市场潜力。 **五、获取方式** 请访问以下链接下载资源包: 链接:https://pan.baidu.com/s/1i759_0X8h9jU4r8-Q6gSw 提取码:xxxx 内含项目源码、数据库脚本及详细说明文档,祝您毕业设计顺利!
《Java 基础核心总结》 Java 概述 什么是 Java2 Java 的特点Java 开发环境 JDK JRE Java 开发环境配置 Java 基本语法 数据类型基础语法运算符 Java 执行控制流程条件语句 if 条件语句 if...else 条件语句if...else if 多分支语句switch 多分支语句 循环语句 while 循环语句do...while 循环for 循环语句 跳转语句 break 语句 continue 语句面向对象 类也是-种对象对象的创建 属性和方法 构造方法 方法重载 方法的重写 初始化 类的初始化 成员初始化 构造器初始化初始化顺序 数组初始化 对象的销毁 对象作用域 this 和 super 访问控制权限继承 多态组合代理 向上转型static final 接口和抽象类接口 抽象类异常 认 识 Exception 什么是 Throwable 常见的 Exception 与 Exception 有关的 Java 关键字 throws 和 throw try 、finally 、catch 什么是 Error 内部类 创建内部类集合 Iterable 接口顶层接口 ArrayList Vector LinkedList 类Stack HashSet TreeSet LinkedHashSet 类 PriorityQueue HashMap TreeMap 类 LinkedHashMap 类 Hashtable 类IdentityHashMap 类WeakHashMap 类 Collections 类集合实现类特征图 泛形 泛型的使用 用泛型表示类 用泛型表示接口泛型方法 泛型通配符 反射 Class 类Field 类Method 类ClassLoader 类 枚举 枚举特性 枚举和普通类-样枚举神秘之处 枚举类 I/O File 类 基础 IO 类和相关方法InputStream OutputStream Reader 类Writer 类 InputStream 及其子类 OutputStream 及其子类Reader 及其子类Writer 及其子类 注解 关于 null 的几种处理方式大小写敏感 null 是任何引用类型的初始值 null 只是-种特殊的值使用 Null-Safe 方法null 判断 关于思维导图 Java.IO Java.lang Java.math Java.net Java 基础核心总结 V2.0 IO 传统的 BIO BIO NIO 和 AIO 的区别什么是流 流的分类 节点流和处理流 Java IO 的核心类 File Java IO 流对象 字节流对象InputStream OutputStream 字符流对象Reader Writer 字节流与字符流的转换新潮的 NIO 缓冲区(Buffer)通道(Channel) 示例:文件拷贝案例 BIO 和 NIO 拷贝文件的区别操作系统的零拷贝 选择器(Selectors) 选择键(SelectionKey) 示例:简易的客户端服务器通信 集合 集合框架总览 -、Iterator Iterable ListIterator 二、Map 和 Collection 接口Map 集合体系详解 HashMap LinkedHashMap TreeMap WeakHashMap Hashtable Collection 集合体系详解 Set 接口 AbstractSet 抽象类SortedSet 接口HashSet LinkedHashSet TreeSet List 接口 AbstractList 和 AbstractSequentialList Vector Stack ArrayList LinkedList Queue接口Deque 接口 AbstractQueue 抽象类LinkedList ArrayDeque PriorityQueue 反射的思想及作用 反射的基本使用 获取类的 Class 对象构造类的实例化对象获取-个类的所有信息 获取类中的变量(Field) 获取类中的方法(Method) 获取类的构造器(Constructor) 获取注解 通过反射调用方法反射的应用场景 Spring 的 IOC 容器反射 + 抽象工厂模式 JDBC 加载数据库驱动类反射的优势及缺陷 增加程序的灵活性破坏类的封装性 性能损耗 代理模式 静态代理与动态代理常见的动态代理实现JDK Proxy CGLIB JDK Proxy 和 CGLIB 的对比动态代理的实际应用 Spring AOP 变量 变量汇总实例变量 实例变量的特点局变量 静态变量 静态变量的特点类变量 局部变量
**微信小程序案例:健康管理** 此资源是一个完整的计算机专业毕业设计项目,致力于实现一个功能丰富、易于使用的健康管理微信小程序。该小程序旨在帮助用户更好地管理自己的健康数据,并提供个性化的健康建议。 **一、项目概述** 该项目基于微信小程序平台开发,充分利用了微信强大的社交属性和广泛的用户基础。小程序包含了一个简洁直观的界面,以及多个核心功能模块,如健康数据记录、健康风险评估、运动计划制定等。 **二、核心功能** 1. **健康数据记录**:用户可以方便地记录自己的体重、血压、血糖等健康数据,并查看历史记录。 2. **健康风险评估**:基于用户输入的健康数据,小程序能够评估用户患病的风险,并提供相应的预防建议。 3. **运动计划制定**:根据用户的身体状况和运动目标,小程序能够智能推荐合适的运动计划。 4. **健康资讯推送**:定期为用户推送最新的健康资讯和养生知识。 5. **社交互动**:用户之间可以分享健康经验、相互鼓励和支持。 **三、技术实现** 该项目采用了前后端分离的技术架构,前端使用微信小程序原生开发,后端采用Node.js + MySQL数据库实现。为了提高系统的可扩展性和可维护性,项目还采用了模块化设计和RESTful API接口规范。 **四、二次开发定制** 该项目的源代码和详细的设计文档已包含在RAR压缩包中。开发者可以根据自己的需求进行二次开发和定制,例如增加新的功能模块、优化用户体验等。同时,我们也提供了完善的技术支持和售后服务,确保您的开发过程顺利进行。 总之,这个健康管理微信小程序案例是一个极具实用价值和商业潜力的毕业设计项目。它不仅可以作为您学术研究的成果展示,还可以直接应用于实际的商业场景中,为人们的健康生活方式提供便捷的支持。
一、Java 基础 1 1. JDK 和 JRE 有什么区别? 1 2. == 和 equals 的区别是什么? 1 3. 两个对象的 hashCode()相同,则 equals()也一定为 true,对吗? 3 4. final 在 java 中有什么作用? 4 5. java 中的 Math.round(-1.5) 等于多少? 4 6. String 属于基础的数据类型吗? 4 7. java 中操作字符串都有哪些类?它们之间有什么区别? 4 8. String str="i"与 String str=new String("i")一样吗? 5 9. 如何将字符串反转? 5 10. String 类的常用方法都有那些? 5 11. 抽象类必须要有抽象方法吗? 6 12. 普通类和抽象类有哪些区别? 6 13. 抽象类能使用 final 修饰吗? 6 14. 接口和抽象类有什么区别? 7 15. java 中 IO 流分为几种? 7 16. BIO、NIO、AIO 有什么区别? 7 17. Files的常用方法都有哪些? 8 二、容器 8 18. java 容器都有哪些? 8 19. Collection 和 Collections 有什么区别? 9 20. List、Set、Map 之间的区别是什么? 9 21. HashMap 和 Hashtable 有什么区别? 10 22. 如何决定使用 HashMap 还是 TreeMap? 10 23. 说一下 HashMap 的实现原理? 10 24. 说一下 HashSet 的实现原理? 11 25. ArrayList 和 LinkedList 的区别是什么? 11 26. 如何实现数组和 List 之间的转换? 11 27. ArrayList 和 Vector 的区别是什么? 11 28. Array 和 ArrayList 有何区别? 12 29. 在 Queue 中 poll()和 remove()有什么区别? 12 30. 哪些集合类是线程安的? 12 31. 迭代器 Iterator 是什么? 12 32. Iterator 怎么使用?有什么特点? 12 33. Iterator 和 ListIterator 有什么区别? 13 三、多线程 13 35. 并行和并发有什么区别? 13 36. 线程和进程的区别? 14 37. 守护线程是什么? 14 38. 创建线程有哪几种方式? 14 39. 说一下 runnable 和 callable 有什么区别? 15 40. 线程有哪些状态? 15 41. sleep() 和 wait() 有什么区别? 16 42. notify()和 notifyAll()有什么区别? 16 43. 线程的 run()和 start()有什么区别? 16 44. 创建线程池有哪几种方式? 17 45. 线程池都有哪些状态? 18 46. 线程池中 submit()和 execute()方法有什么区别? 18 49. 什么是死锁? 19 50. 怎么防止死锁? 19 51. ThreadLocal 是什么?有哪些使用场景? 20 52.说一下 synchronized 底层实现原理? 20 53. synchronized 和 volatile 的区别是什么? 21 54. synchronized 和 Lock 有什么区别? 21 55. synchronized 和 ReentrantLock 区别是什么? 22 56. 说一下 atomic 的原理? 22 四、反射 23 57. 什么是反射? 23 58. 什么是 java 序列化?什么情况下需要序列化? 23 59. 动态代理是什么?有哪些应用? 23 60. 怎么实现动态代理? 24 五、对象拷贝 24 61. 为什么要使用克隆? 24 62. 如何实现对象克隆? 24 63. 深拷贝和浅拷贝区别是什么? 28 六、Java Web 28 64. jsp 和 servlet 有什么区别? 28 65. jsp 有哪些内置对象?作用分别是什么? 29 66. 说一下 jsp 的 4 种作用域? 29 67. session 和 cookie 有什么区别? 30 68. 说一下 session 的工作原理? 31 69. 如果客户端禁止 cookie 能实现 session 还能用吗? 31 70. spring mvc 和 struts 的区别是什么? 31 71. 如何避免 sql 注入? 33 72. 什么是 XSS 攻击,如何避免? 33 73. 什么是 CSRF 攻击,如何避免? 33 七、异常 35 74. throw 和 throws 的区别? 35 75. final、finally、finalize 有什么区别? 35 76. try-catch-finally 中哪个部分可以省略? 35 77. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗? 36 78. 常见的异常类有哪些? 38 八、网络 39 79. http 响应码 301 和 302 代表的是什么?有什么区别? 39 80. forward 和 redirect 的区别? 39 81. 简述 tcp 和 udp的区别? 40 82. tcp 为什么要三次握手,两次不行吗?为什么? 40 84. OSI 的七层模型都有哪些? 42 85. get 和 post 请求有哪些区别? 42 86. 如何实现跨域? 43 87.说一下 JSONP 实现原理? 49 九、设计模式 49 88. 说一下你熟悉的设计模式? 49 89. 简单工厂和抽象工厂有什么区别? 49 十、Spring / Spring MVC 52 90. 为什么要使用 spring? 52 91. 解释一下什么是 aop? 53 92. 解释一下什么是 ioc? 54 93. spring 有哪些主要模块? 56 94. spring 常用的注入方式有哪些? 57 95. spring 中的 bean 是线程安的吗? 57 96. spring 支持几种 bean 的作用域? 58 97. spring 自动装配 bean 有哪些方式? 59 98. spring 事务实现方式有哪些? 59 99. 说一下 spring 的事务隔离? 59 100. 说一下 spring mvc 运行流程? 60 101. spring mvc 有哪些组件? 61 102. @RequestMapping 的作用是什么? 62 103. @Autowired 的作用是什么? 62
HashMap、Hashtable和HashSet是Java中常用的集合类。 HashMap和Hashtable都实现了Map接口,用于存储键值对。它们的主要区别在于线程安性和同步性。HashMap是非线程安的,而Hashtable是线程安的。这意味着多个线程可以同时访问Hashtable,但不能同时访问HashMap。此外,HashMap允许存储null键值对,而Hashtable不允许。Java 5引入的ConcurrentHashMap可以作为Hashtable的替代,它在扩展性方面更好。 HashSet实现了Set接口,用于存储无序、不重复的元素。它底层使用HashMap来存储数据,HashSet存储对象而不存储键值对。 总结一下,HashMap和Hashtable都是用于存储键值对的,区别在于线程安性和同步性。HashSet是用于存储不重复元素的集合。 引用: HashMap和Hashtable都实现了Map接口,主要的区别有:线程安性,同步(synchronization),以及速度。HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。 HashMap是非synchronized,而Hashtable是synchronized,意味着Hashtable是线程安的,多个线程可以共享一个Hashtable;而多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器。 HashMap可以通过下面的语句进行同步:Map m = Collections.synchronizeMap(hashMap); 引用:HashMap和Hashtable两个类都实现了Map接口,二者保存K-V对(key-value对);HashSet则实现了Set接口,性质类似于集合。 引用:HashSet:散列表,无序的,不会记录插入的顺序,实现了 Set 接口,以对象作为元素,拒绝接受重复的对象,底层依靠 HashMap来存储数据, 底层使用HashTable来保证元素的不重复性,实际上使用的是HashMap的一个实例。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [java集合HashMap、HashTable、HashSet详解](https://blog.csdn.net/weixin_38166557/article/details/99230114)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [浅析Java中Map与HashMap,Hashtable,HashSet的区别](https://download.csdn.net/download/weixin_38570296/12813847)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java:HashMap、HashSet、HashTable](https://blog.csdn.net/weixin_48493408/article/details/123465326)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值