微信小程序03-页面交互

零、文章目录

微信小程序03-页面交互

1、案例:比较数字大小

(1)案例分析
  • 需求:本案例将实现“比较数字大小”微信小程序,它的功能是当用户输入两个数字后,点击“比较”按钮可以自动比较这两个数字的大小。

  • 两个输入框,可以输入数字,输入后点击“比较”按钮,按钮下方会显示比较结果。

  • 比较结果有3种情况

    • 如果第1个数字比第2个数字大,则比较结果为“第1个数大”;
    • 如果第2个数字比第1个数字大,则比较结果为“第2个数大”;
    • 如果第1个数字和第2个数字相等,则比较结果为“两数相等”。
(2)知识储备-Page()函数
  • 在微信小程序中,页面交互的代码写在页面的JS文件中,每个页面都需要通过Page()函数进行注册。需要注意的是,Page()函数只能写在微信小程序每个页面对应的JS文件中,并且每个页面只能注册一个。
  • Page()函数的参数是一个对象,通过该对象可以指定页面初始数据、页面生命周期回调函数和页面事件处理函数。
    • data:页面初始数据
    • onLoad:页面生命周期回调函数
    • onPullDownRefresh:页面事件处理函数
Page({// 页面初始数据data: {},// 页面生命周期回调函数,以onLoad()为例onLoad: function () {console.log('onLoad()函数执行了')},// 页面事件处理函数,以onPullDownRefresh()为例onPullDownRefresh: function () {console.log('onPullDownRefresh()函数执行了')}}) 
  • **页面初始数据:**页面初始数据是指页面第一次渲染时所用到的数据。
data: {msg1: 'Hello',msg2: 'World'
}, 
  • **页面生命周期回调函数:**在微信小程序中,页面的生命周期是指每个页面“加载→渲染→销毁”的过程,每个页面都有生命周期。如果想要在某个特定的时机进行特定的处理,则可以通过页面生命周期回调函数来完成。

image-20240909104604518

  • **页面事件处理函数:**在微信小程序中,用户可能会在页面上进行一些操作,例如上拉、下拉、滚动页面等,这些可以通过页面事件处理函数来完成。
    • 使用onPullDownRefresh()函数前,需要在app.json配置文件中将enablePullDownRefresh配置项设为true。

image-20240909110116637

(2)知识储备-数据绑定
  • 数据绑定概念

    • 在微信小程序开发过程中,一般会将页面中的数据从WXML文件中分离出来,通过JS文件操作页面中的数据。
    • 假如有一个电商类的微信小程序,每个商品的详情页面的结构是相同的,区别是页面展示的数据不同。在实际开发中,开发者只编写一个页面,通过更改页面中的数据来实现不同的商品详情页面。这种开发方式是将页面中的数据分离出来,放到页面的JS文件中,通过程序控制页面中数据的展示。
  • **数据绑定实现:**微信小程序提供了Mustache语法(又称为双大括号语法)用于实现数据绑定,可将data中的数据通过Mustache语法输出到页面上。

  • 数据绑定代码案例

    • 在pages/index/index.js文件中,在data中定义一个message数据

      • Page({data: {message: 'Hello World'}
        })
        
    • 在pages/index/index.wxml文件中编写页面结构

      • <view>{{ message }}</view>
        
    • **运行结果:**页面上显示了message变量对应的值,也就是把“HelloWorld”渲染到页面代码中{{ message }}所在的位置,实现了从逻辑层到视图层的数据显示。

(2)知识储备-事件绑定
  • **事件绑定:**事件是视图层到逻辑层的通信方式,通过给组件绑定事件,可以监听用户的操作行为,然后在对应的事件处理函数中进行相应的业务处理。例如,为页面中的按钮绑定事件,当用户点击按钮时,就触发了事件。
  • 常见的事件如下,分为两类
    • **冒泡事件:**当一个组件上的事件被触发后,该事件会向父组件传递,比如点击事件、长按事件、触摸事件。
    • **非冒泡事件:**当一个组件上的事件被触发后,该事件不会向父组件传递,比如其他事件。

image-20240910105924434

  • 事件绑定实现

    • 可以通过为组件添加“bind+事件名称”属性或“catch+事件名称”属性来完成,属性的值为事件处理函数,当组件的事件被触发时,会主动执行事件处理函数。
    • bind和catch的区别在于,bind不会阻止冒泡事件向上冒泡,而catch可以阻止冒泡事件向上冒泡。
    • 为组件绑定事件后,可以将事件处理函数定义在Page({})中。
  • 事件绑定代码案例

    • 在pages/index/index.wxml文件中为button组件绑定tap事件

      • <button bindtap="compare">比较</button>
        
    • 在pages/index/index.js文件的Page({})中定义compare()函数

      • compare: function () {console.log('比较按钮被单击了')
        }, //可以写成简写形式
        compare () {console.log('比较按钮被单击了')
        }, 
        
    • **运行结果:**单击“比较”按钮,在控制台输出“比较按钮被单击了”。

(2)知识储备-事件对象
  • 事件对象
    • 在微信小程序的开发过程中,有时需要获取事件发生时的一些信息,例如事件类型、事件发生的时间、触发事件的对象等,此时可以通过事件对象来获取。
    • 当事件处理函数被调用时,微信小程序会将事件对象以参数的形式传给事件处理函数。
  • 事件对象的属性如下
    • **target:**获取触发事件的组件的一些属性值集合。
    • **currentTarget:**获取当前组件的一些属性值集合。

image-20240910120150849

  • 事件对象代码案例

    • 修改pages/index/index.js文件中的compare()函数,通过参数接收事件对象,并将事件对象输出到控制台

      • compare: function (e) {console.log(e)
        }, 
        
    • **运行结果:**单击“比较”按钮,控制台输出参数e表示事件对象。

  • 事件对象代码案例-演示target和currentTarget区别

    • 在pages/index/index.wxml文件中编写页面结构

      • <view bindtap="viewtap" id="outer">outer<view id="inner">inner</view>
        </view>
        
    • 在pages/index/index.js文件中添加viewtap()事件处理函数

      • viewtap: function (e) {console.log(e.target.id + '-' + e.currentTarget.id)
        }, 
        
    • **运行结果:**当单击outer时,控制台中的输出结果为outer-outer,而单击inner时,控制台中的输出结果为inner-outer。由此可见,e.target获取的是子元素的属性值集合,而e.currentTarget获取的是父元素的属性值集合。

(2)知识储备-this关键字
  • **this关键字:**在微信小程序开发过程中,有时需要在函数中访问页面中定义的一些数据,或者调用页面中定义的一些函数,此时可以通过this关键字来实现。this关键字代表当前页面对象。
  • **代码演示:**在onLoad()函数中通过this关键字访问data中的num数据并调用test()函数。程序运行后,在控制台中可以看到程序输出了this.data.num的值“1”和“test()函数执行了”。
Page({data: { num: 1 },               // 定义data数据test: function () {              // 定义test()函数console.log('test()函数执行了')
},onLoad: function () {console.log(this.data.num)    // 通过this关键字访问data中的num数据this.test()                   // 通过this关键字调用test()函数}
}) 
(2)知识储备-setData()方法
  • setData()方法

    • 通过数据绑定可以将data中定义的数据渲染到页面,但是如果数据发生了变化,页面并不会同步更新数据。
    • 为了实现在数据变化时使页面同步更新,微信小程序提供了setData()方法,该方法可以立即改变data中的数据,并通过异步的方式将数据渲染到页面上。
  • setData()语法

this.setData(data[, callback])

image-20240910131113983

  • setData()代码案例

    • 在pages/index/index.js文件中编写页面中所需的数据message和事件处理函数changeText()

      • Page({data: {message: 'Hello World'},changeText: function () {this.setData({message: 'hello微信小程序'})}
        }) 
        
    • 在pages/index/index.wxml文件中编写页面结构

      • <view bindtap="changeText">{{ message }}</view>
        
    • **运行结果:**单击前页面中显示的文字为“Hello World”,单击后页面中显示的文字为“hello微信小程序”。

(2)知识储备-条件渲染
  • 条件渲染

    • 在微信小程序开发过程中,如果需要根据不同的判断结果显示不同的组件,可以使用条件渲染来实现。
    • 条件渲染通过标签的wx:if控制属性来完成。使用wx:if="{{ val }}"来判断是否需要渲染标签对应的组件,如果变量val的值为true,则渲染组件并输出;变量val的值为false,则不渲染组件。
  • **代码演示:**通过变量condition的值来控制是否渲染view组件。

<view wx:if="{{ condition }}">True</view>
  • 代码演示:
    • 给标签设置了wx:if控制属性后,可以为后面的标签设置wx:elif、wx:else控制属性。
    • wx:elif控制属性表示当前面标签的if条件不满足时,继续判断elif(else if)的条件;
    • wx:else控制属性表示当前面的if条件不满足时,渲染else对应的组件。
    • wx:else控制属性也可以直接出现在wx:if控制属性的后面。
<view wx:if="{{ count < 1 }}">0</view>
<view wx:elif="{{ count == 1 }}">1</view>
<view wx:else>2</view>
(2)知识储备-<block>标签
  • **<block>标签:**当使用一个判断条件决定是否显示或者隐藏多个组件时,通常会在其外部包裹一个<block>标签,这样可直接控制这个外部标签的显示或隐藏。该标签并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,比view能耗低。
<block wx:if="{{ true }}"><view>view1</view><view>view2</view>
</block> 
(2)知识储备-hidden属性
  • hidden属性
    • 除wx:if控制属性外,hidden属性也可以控制组件的显示与隐藏,条件为true时隐藏组件里面的内容,条件为false时显示组件里面的内容
    • hidden属性和wx:if控制属性不同之处
      • wx:if控制属性的初始渲染条件为false,只有条件第一次变为true的时候才开始渲染。
      • hidden属性所在的组件始终会被渲染,只是简单的控制显示与隐藏。
<text hidden="{{ hidden }}">hidden为true时不显示</text>
(3)案例实现
  • 准备工作

    • ①创建项目。在微信开发者工具中创建一个新的微信小程序项目,项目名称为“比较数字大小”,模板选择“不使用模板”。

    • ②配置导航栏。在pages/index/index.json文件中配置页面导航栏。

    • ③配置样式。在pages/index/index.wxss文件中配置本项目的页面样式。

    image-20240910133752969

  • **实现页面结构:**在pages/index/index.wxml文件中编写“比较数字大小”微信小程序的页面结构。

<!--index.wxml-->
<view><text>请输入第1个数字:</text><input type="number" bindinput="num1Input" />
</view>
<view><text>请输入第2个数字:</text><input type="number" bindinput="num2Input" />
</view>
<button bindtap="compare">比较</button>
<view><text wx:if="{{ result }}">比较结果:{{ result }}</text>
</view>
  • **实现事件处理函数:**在pages/index/index.js文件的Page({})中编写事件处理函数
// index.js
Page({data: {result: ''},num1: 0, // 保存第1个数字num2: 0, // 保存第2个数字num1Input: function (e) {this.num1 = Number(e.detail.value)},num2Input: function (e) {this.num2 = Number(e.detail.value)},compare: function () {var str = ''if (this.num1 > this.num2) {str = '第1个数大'} else if (this.num1 < this.num2) {str = '第2个数大'} else {str = '两数相等'}this.setData({result: str})}
})
  • 页面实现效果

image-20240910134136623

2、案例:计算器

(1)案例分析
  • 需求:在日常生活中,计算器是人们广泛使用的工具,可以帮助我们快速且方便地计算金额、成本、利润等。
  • 在计算器中可以进行整数和小数的加(+)、减(-)、乘(×)、除(÷)运算。
    • “C”按钮为清除按钮,表示将输入的数字全部清空;
    • “DEL”按钮为删除按钮,表示删除前面输入的一个数字;
    • “+/-”按钮为正负号切换按钮,用于实现正负数切换;
    • “.”按钮为小数点按钮,表示在计算过程中可以输入小数进行计算;
    • “=”按钮为等号按钮,表示对输入的数字进行计算。
(2)知识储备-data-*自定义属性
  • 自定义属性

    • 在组件中,有时需要为事件处理函数传递参数。在Vue.js中可以直接使用函数进行传参,但是这种写法在微信小程序中并不适用。微信小程序可以通过自定义属性来进行传参。
    • 微信小程序中的data-*是一个自定义属性,data-*自定义属性实际上是由data-前缀加上一个自定义的属性名组成的,属性名中如果有多个单词,用连字符“-”连接。
    • data-*自定义属性的属性值表示要传参的数据。在事件处理函数中通过target或currentTarget对象的dataset属性可以获取数据。dataset属性是一个对象,该对象的属性与data-*自定义属性相对应。需要注意的是,自定义属性名的连字符写法会被转换成驼峰写法,并且大写字母会自动转换成小写字母,例如,data-element-type会被转换为dataset对象的elementType属性,data-elementType会被转换为dataset对象的elementtype属性。
  • 自定义属性代码实现

    • 在pages/index/index.wxml文件中编写页面结构

      • <view bindtap="demo" data-name="xiaochengxu" data-age="6">
        获取姓名和年龄
        </view>
        <view>姓名:{{ name }}</view>
        <view>年龄:{{ age }}</view>
        
    • 在pages/index/index.js文件中编写页面逻辑

      •   Page({data: {name: '初始名字',age: 0},demo: function (e) {this.setData({name: e.target.dataset.name,age: e.target.dataset.age})}}) 
        
    • 运行结果:单击“获取姓名和年龄”,name编程xiaochengxu,age变成6

(2)知识储备-模块
  • **模块:**在微信小程序中,为了提高代码的可复用性,通常会将一些公共的代码抽离成单独的JS文件,作为模块使用,每个JS文件均为一个模块。
  • **模块语法:**微信小程序提供了模块化开发的语法,可以使用module.exports语法对外暴露接口,然后在需要使用模块的地方通过require()函数引入模块。
  • **创建模块:**在项目的根目录下创建一个utils目录,用于保存项目中的模块,然后在该目录下创建welcome.js文件
module.exports = {message: 'welcome'
} 
  • **引入模块:**在页面的JS文件中使用require()函数将模块引入
var welcome = require('../../utils/welcome.js')
Page({onLoad: function () {console.log(welcome.message)}
}) 
(3)案例实现
  • 准备工作

    • ①创建项目。在微信开发者工具中创建一个新的微信小程序项目,项目名称为“计算器”,模板选择“不使用模板”。

    • ②配置导航栏。在pages/index/index.json文件中配置页面导航栏。

    • ③配置样式。在pages/index/index.wxss文件中配置本项目的页面样式。

    • ④创建utils文件夹。utils文件夹保存了项目中用到的两个公共文件,分别是math.js和calc.js。

      • math.js文件实现了数字的精确计算,用于解决JavaScript浮点型数据计算精度不准确的问题;

      • // 精准计算功能,用于解决JavaScript浮点数运算精度不准确的问题
        module.exports = {add: function(arg1, arg2) {var r1, r2, mtry {r1 = arg1.toString().split('.')[1].length} catch (e) {r1 = 0}try {r2 = arg2.toString().split('.')[1].length} catch (e) {r2 = 0}m = Math.pow(10, Math.max(r1, r2))return (arg1 * m + arg2 * m) / m},sub: function(arg1, arg2) {var r1, r2, m, ntry {r1 = arg1.toString().split('.')[1].length} catch (e) {r1 = 0}try {r2 = arg2.toString().split('.')[1].length} catch (e) {r2 = 0}m = Math.pow(10, Math.max(r1, r2))// 动态控制精度长度n = (r1 >= r2) ? r1 : r2return ((arg1 * m - arg2 * m) / m).toFixed(n)},mul: function(arg1, arg2) {var m = 0,s1 = arg1.toString(),s2 = arg2.toString()try {m += s1.split('.')[1].length} catch (e) {}try {m += s2.split('.')[1].length} catch (e) {}return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m)},div: function(arg1, arg2) {var t1 = 0,t2 = 0,r1, r2try {t1 = arg1.toString().split('.')[1].length} catch (e) {}try {t2 = arg2.toString().split('.')[1].length} catch (e) {}r1 = Number(arg1.toString().replace('.', ''))r2 = Number(arg2.toString().replace('.', ''))return (r1 / r2) * Math.pow(10, t2 - t1)}}
        
      • calc.js文件提供了一个计算器对象,用于简化开发逻辑。

        • const math = require('./math.js')// 计算器中的数字处理
          module.exports = {target: 'num1',  // 表示当前正在输入哪个数字num1: '0',       // 保存第1个数字num2: '0',       // 保存第2个数字op: '',          // 运算符,值可以是 + - × ÷// 设置当前数字setNum (num) {this[this.target] = num},// 获取当前数字getNum () {return this[this.target]},// 切换到第2个数字changeNum2 () {this.target = 'num2'},// 重置reset () {this.num1 = this.num2 = '0'this.target = 'num1'this.op = ''},// 进行计算getResult () {if (this.op === '+') {return math.add(this.num1, this.num2) + ''} else if (this.op === '-') {return math.sub(this.num1, this.num2) + ''} else if (this.op === '×') {return math.mul(this.num1, this.num2) + ''} else if (this.op === '÷') {return math.div(this.num1, this.num2) + ''}}
          }
          
  • **实现页面结构:**在pages/index/index.wxml文件中编写“计算器”微信小程序的页面结构

<!--index.wxml-->
<view class="result"><!-- 结果区域 --><view class="result-sub">{{ sub }}</view><view class="result-num">{{ num }}</view>
</view>
<view class="btns"><!-- 按钮区域 --><!-- 按钮区域第1行按钮的结构 --><view><view hover-class="bg" hover-stay-time="50" bindtap="resetBtn">C</view><view hover-class="bg" hover-stay-time="50" bindtap="delBtn">DEL</view><view hover-class="bg" hover-stay-time="50" bindtap="negBtn">+/-</view><view hover-class="bg" hover-stay-time="50" bindtap="opBtn" data-val="÷">÷</view></view><!-- 按钮区域第2行按钮的结构 --><view><view hover-class="bg" hover-stay-time="50" bindtap="numBtn" data-val="7">7</view><view hover-class="bg" hover-stay-time="50" bindtap="numBtn" data-val="8">8</view><view hover-class="bg" hover-stay-time="50" bindtap="numBtn" data-val="9">9</view><view hover-class="bg" hover-stay-time="50" bindtap="opBtn" data-val="×">×</view></view><!-- 按钮区域第3行按钮的结构 --><view><view hover-class="bg" hover-stay-time="50" bindtap="numBtn" data-val="4">4</view><view hover-class="bg" hover-stay-time="50" bindtap="numBtn" data-val="5">5</view><view hover-class="bg" hover-stay-time="50" bindtap="numBtn" data-val="6">6</view><view hover-class="bg" hover-stay-time="50" bindtap="opBtn" data-val=""></view></view><!-- 按钮区域第4行按钮的结构 --><view><view hover-class="bg" hover-stay-time="50" bindtap="numBtn" data-val="1">1</view><view hover-class="bg" hover-stay-time="50" bindtap="numBtn" data-val="2">2</view><view hover-class="bg" hover-stay-time="50" bindtap="numBtn" data-val="3">3</view><view hover-class="bg" hover-stay-time="50" bindtap="opBtn" data-val=""></view></view><!-- 按钮区域第5行按钮的结构 --><view><view hover-class="bg" hover-stay-time="50" bindtap="numBtn" data-val="0">0</view><view hover-class="bg" hover-stay-time="50" bindtap="dotBtn">.</view><view hover-class="bg" hover-stay-time="50" bindtap="execBtn">=</view></view>
</view>
  • **实现页面逻辑:**在pages/index/index.js文件的Page({})中编写页面逻辑
// index.js
const calc = require('../../utils/calc.js')Page({data: {sub: '',num: '0'},// 设置3个变量标识numChangeFlag: false,execFlag: false,resultFlag: false,numBtn: function (e) {// 点击数字按钮,获取对应的数字,将其值赋给numvar num = e.target.dataset.valif (this.resultFlag) {this.resetBtn()}if (this.numChangeFlag) {this.numChangeFlag = falsethis.execFlag = true // 代表已输入第2个数字this.data.num = '0' // 将num设为0,避免数字进行拼接calc.changeNum2() // 将target切换到第2个数字}// 设置输入的数字calc.setNum(this.data.num === '0' ? num : this.data.num + num)// 在页面中显示输入的数字this.setData({num: calc.getNum()})},opBtn: function (e) {calc.op = e.target.dataset.valthis.numChangeFlag = true// 判断是否已经输入第2个数字if (this.execFlag) {this.execFlag = false// 已经输入第2个数字,再判断当前是否为计算结果状态if (this.resultFlag) {// 当前是计算结果状态,需要在计算结果的基础上计算this.resultFlag = false} else {// 连续计算,将计算结果作为第1个数字calc.num1 = calc.getResult()}}this.setData({sub: calc.num1 + ' ' + calc.op + ' ',num: calc.num1})},// “=”按钮的事件处理函数execBtn: function () {if (this.numChangeFlag) {this.numChangeFlag = falsethis.execFlag = truecalc.num2 = this.data.num}// 如果已经输入第2个数字,执行计算操作if (this.execFlag) {this.resultFlag = truevar result = calc.getResult()this.setData({sub: calc.num1 + ' ' + calc.op + ' ' + calc.num2 + ' = ',num: result})calc.num1 = result}},resetBtn: function () {calc.reset() // 调用reset()实现数字、运算符的重置this.execFlag = falsethis.numChangeFlag = falsethis.resultFlag = falsethis.setData({sub: '',num: '0'})},// “.”按钮(小数点按钮)的事件处理函数dotBtn: function () {// 如果当前是计算结果状态,则重置计算器if (this.resultFlag) {this.resetBtn()}// 如果等待输入第2个数字且还没有输入第2个数字,设为“0.”if (this.numChangeFlag) {this.numChangeFlag = falsecalc.setNum('0.')} else if (this.data.num.indexOf('.') < 0) {// 如果当前数字中没有“.”,需要加上“.”calc.setNum(this.data.num + '.')}this.setData({num: calc.getNum()})},// DEL按钮(删除按钮)的事件处理函数delBtn: function () {// 如果当前是计算结果状态,则重置计算器if (this.resultFlag) {return this.resetBtn()}// 非计算结果状态,删除当前数字中最右边的一个字符var num = this.data.num.substr(0, this.data.num.length - 1)calc.setNum(num === '' || num === '-' || num === '-0.' ? '0' : num)this.setData({num: calc.getNum()})},// “+/-”按钮(正负切换按钮)的事件处理函数negBtn: function () {// 如果是0,不加正负号if (this.data.num === '0' || this.data.num === '0.') {return}// 如果当前是计算结果状态,则重置计算器if (this.resultFlag) {this.resetBtn()} else if (this.data.num.indexOf('-') < 0) {// 当前没有负号,加负号calc.setNum('-' + this.data.num)} else {// 当前有负号,去掉负号calc.setNum(this.data.num.substr(1))}this.setData({num: calc.getNum()})}
})
  • 页面实现效果

image-20240910145157233

3、案例:美食列表

(1)案例分析
  • 需求:“美食列表”微信小程序是一个展示美食名称、美食图片及美食商家的电话、地址和营业时间等信息的微信小程序。
  • 美食列表包含多条美食信息
    • 每条美食信息左侧为美食图片
    • 右侧为美食详细信息,包括美食名称、电话、地址和营业时间。
  • 该页面具有上拉触底加载数据和下拉刷新两个功能
    • 用户上拉美食列表页时,如果页面即将到达底部,会自动加载更多数据;
    • 用户下拉页面时,如果到达顶部后进行下拉操作,可以刷新页面。
(2)知识储备-列表渲染
  • 列表渲染

    • 为了方便用户查找美食信息,微信小程序的页面通常以列表的形式展示美食信息。
    • 在实际开发中,通常将列表数据保存为数组或对象,然后在页面中通过列表渲染的方式输出数据。
  • 列表渲染语法

    • 列表渲染通过wx:for控制属性来实现。微信小程序进行列表渲染时,会根据列表中数据的数量渲染相应数量的内容。
    • 在wx:for控制属性所在标签的内部
      • 使用item变量获取当前项的值
      • 使用index变量获取当前项的数组索引或对象属性名。
      • 如果不想使用item和index这两个变量名,还可以通过wx:for-item控制属性更改item的变量名;通过wx:for-index控制属性更改index的变量名。
    • wx:for控制属性通常搭配wx:key控制属性使用
      • wx:key控制属性用于为每一项设置唯一标识,这样可以在数据改变后页面重新渲染时,使原有组件保持自身的状态。
      • 在设置wx:key的值时,如果item本身就是一个具有唯一性的字符串或数字,则可以将wx:key的值设置为*this*this表示item本身。
  • 代码案例演示

    • 在pages/index/index.js文件的Page({})中编写页面数据

      • data: {arr: [ 'a', 'b', 'c']
        }
        
    • 在pages/index/index.wxml文件中编写页面结构,通过列表渲染的方式将arr数组渲染到页面中

      • <view wx:for="{{ arr }}" wx:key="*this">{{ index }} {{ item }}
        </view>
        
  • 代码案例演示:对象数组

    • 在pages/index/index.js文件的Page({})中编写页面数据

      • data: {list: [{ message: '梅' , id: 1 }, { message: '兰' , id: 2 },{ message: '竹' , id: 3 }, { message: '菊' , id: 4 }]
        } 
        
    • 在pages/index/index.wxml文件中编写页面结构,将list数组中的数据在页面中显示出来

      • <view wx:for="{{ list }}" wx:key="id">
        {{ index }}-----{{ item.message }}======={{ item.id }}
        </view>
        
  • 代码案例演示:通过wx:for-item、wx:for-index更改item和index的变量名

    • <view wx:for="{{ list }}" wx:for-item="item2" wx:for-index="index2" wx:key="id">
      {{ index2 }}:{{ item2.message }}
      </view>
      
(2)知识储备-网络请求
  • 网络请求

    • 客户端与服务器进行交互时,客户端请求服务器的过程称为网络请求。
    • 例如,获取用户的头像信息,需要客户端向服务器发送请求,服务器查询到数据后把数据传递给客户端。
    • 在微信小程序中实现网络请求时,需要服务器给微信小程序提供服务器接口。
  • 微信对接口的安全限制

    • ①只能请求HTTPS协议的服务器接口。
    • ②必须登录微信小程序管理后台,将服务器接口的域名添加到信任列表中。
    • 当服务器接口不满足以上两个条件时,可以在微信开发者工具的本地设置中勾选“不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书”复选框,跳过对服务器接口的校验。但是此做法仅限在开发与调试阶段使用。
  • 网络请求语法

    • 在微信小程序中发起网络请求可以通过调用wx.request()方法来实现。
    • wx.request()方法的常见选项如下
      • method选项的合法值包括OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE和CONNECT

    image-20240910153417687

    image-20240910153433681

  • 网络请求代码演示

    • 每个wx.request()方法都是一个请求任务,可以通过abort()方法将其取消

      •    // 发起网络请求var requestTask = wx.request({url: 'URL地址',// wx.request()的常见参数……})// 取消请求任务requestTask.abort() 
        
    • 通过wx.request()方法发起一个GET方式的请求

      •   wx.request({url: 'URL地址',method: 'GET',data: {name: 'zs'},success: res => {console.log(res)}}) 
        
(2)知识储备-提示框
  • wx.showLoading()方法

    • wx.showLoading()方法用于弹出加载提示框,加载提示框弹出后,不会自动关闭,需要手动调用wx.hideLoading()方法才能关闭载提示框。
    • wx.showLoading()方法的常用选项如下

    image-20240910154355623

    image-20240910154417434

    • 代码演示如下
      wx.showLoading({title: '加载中',})setTimeout(function () {wx.hideLoading()}, 2000)
    
  • wx.showToast()方法

    • wx.showToast()方法用于显示消息提示框,该方法的常用选项如下
      • icon选项的合法值包括success(成功图标)、error(失败图标)、loading(加载图标)和none(无图标)。
        • 当icon的值为success、error、loading时,title选项中文本最多显示7个汉字长度;
        • 当icon的值为none时,title选项中文本最多可显示两行。

    image-20240910154623895

    • 代码演示如下
    wx.showToast({title: '成功',icon: 'success',duration: 2000
    }) 
    
(2)知识储备-WXS
  • WXS

    • WXS(WeiXin Script)是微信小程序独有的一套脚本语言,可以结合WXML构建出页面结构。
    • WXS的典型应用场景是“过滤器”,所谓的过滤器是指在渲染数据之前,对数据进行处理,处理结果最终会显示在页面上。
  • WXS的4个特点

    • WXS与JavaScript不同
      • ①WXS有8种数据类型,包括number(数值)、string(字符串)、boolean(布尔)、object(对象)、function(函数)、array(数组)、date(日期)、regexp(正则)。
      • ②WXS不支持let、const、解构赋值、展开运算符、箭头函数、对象属性简写等语法,WXS支持var定义变量、普通function函数等语法。
      • ③WXS遵循CommonJS规范。在每个模块内部,module变量代表当前模块,这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。在使用require()函数引用其他模块时,得到的是被引用模块中module.exports所指的对象。
    • WXS不能作为组件的事件回调:WXS经常与Mustache语法配合使用,但是在WXS中定义的函数不能作为组件的事件回调函数。
    • 具有隔离性:隔离性是指WXS代码的运行环境和其他JavaScript代码是隔离的,体现在以下两个方面。
      • ①在WXS代码中不能调用页面的JS文件定义的函数。
      • ②在WXS代码中不能调用微信小程序提供的API。
    • 在iOS设备上效率高:在iOS设备上,微信小程序内WXS代码的执行速度比JavaScript代码快2~20倍;在Android设备上,两者的运行效率无差异。
  • WXS语法

    • WXS代码可以写在页面的WXML文件的<wxs>标签内(内嵌WXS脚本)。
    • WXS代码可以写在以.wxs为后缀名的文件中(外联WXS脚本)。
    • 每一个.wxs文件和<wxs>标签均为一个单独的模块,有自己独立的作用域,即在一个模块内定义的变量和函数默认为私有的,对其他模块不可见。
    • 在页面的WXML文件中使用<wxs>双标签语法时,必须提供module属性,用于指定当前WXS的模块名称,以便于在WXML中访问模块中的成员;当<wxs>标签为单闭合标签或标签中内容为空时需提供src属性,src属性的属性值为引用的.wxs文件的相对路径。
  • **WXS代码演示-内嵌WXS脚本:**在pages/index/index.wxml文件中编写页面结构并内嵌WXS脚本

 <wxs module="m1">function toUpper(str) {return str.toUpperCase()}module.exports = {toUpper: toUpper}</wxs><view>{{ m1.toUpper('weixin') }}</view>
  • WXS代码演示-外联WXS脚本:

    • 通常情况下,将以.wxs为后缀名的文件存放在utils目录下,该目录用于存放工具类函数或公共模块。首先在utils/tool.wxs文件中编写外链WXS脚本。
      var msg = 'hello world'module.exports = {message: msg} 
    
    • 在pages/index/index.wxml文件中编写页面结构。
     <wxs module="m2" src="../../utils/tool.wxs"></wxs><view>{{ m2.message }}</view> 
    
(2)知识储备-上拉触底
  • 上拉触底

    • 在原生应用或者网页的交互中,经常会有上拉加载这个功能。用户在浏览列表页面时,手指在手机屏幕上进行上拉滑动操作,通过上拉加载请求数据,增加列表数据。
    • 微信小程序提供了onReachBottom()事件处理函数,即页面上拉触底事件处理函数,用于监听当前页面的上拉触底事件。
    • onReachBottom()事件处理函数的示例代码如下
    onReachBottom: function () {console.log('触发了上拉触底的事件')
    },
    
  • 上拉触底默认距离

    • 在默认情况下,触发上拉触底事件时,滚动条距离页面底部的距离为50px,即上拉触底距离为50px。

    • 在实际开发中,开发人员可以根据实际需求,在全局或页面的JSON配置文件中,通过onReachBottomDistance属性修改上拉触底的距离。

(2)知识储备-下拉刷新
  • 下拉刷新

    • 在原生应用的交互中,经常会有下拉刷新操作,即当用户下拉页面到达顶部时,再进行下拉可以将数据重新加载。

    • 在微信小程序中,也可以实现下拉刷新的效果。启用下拉刷新有2种方式。

      • 全局开启下拉刷新:在app.json文件的window节点中,将enablePullDownRefresh设置为true。
      • 局部开启下拉刷新:在页面的JSON文件中,将enablePullDownRefresh设置为true。
    • 开启下拉刷新后,当下拉操作执行时,就会触发onPullDownRefresh()事件处理函数。

      onPullDownRefresh: function () {console.log('触发了下拉刷新的事件')
      }
      
  • 加载提示弹回时机设定

    • 当执行了下拉刷新操作后,页面顶部会出现加载提示,并且页面需要延迟一段时间才会弹回去。
    • 为了优化用户体验,可以在完成下拉刷新的数据加载后,立即调用wx.stopPullDownRefresh()方法停止使用当前页面的下拉刷新加载效果。
    wx.stopPullDownRefresh()
    
(3)案例实现
  • 准备工作

    • ①创建项目。在微信开发者工具中创建一个新的微信小程序项目,项目名称为“美食列表”,模板选择“不使用模板”。
    • ②配置页面。项目创建完成后,在app.json文件中配置一个shoplist页。
    "pages": ["pages/shoplist/shoplist"
    ],
    
    • ③配置导航栏。在pages/shoplist/shoplist.json文件中配置页面导航栏。
    • ④配置页面样式。在pages/shoplist/shoplist.wxss文件中配置页面样式。
    • ⑤启动服务器。切换工作目录到nodejs服务程序目录,打开命令提示符,然后在命令提示符中执行如下命令,启动服务器。
    node index.js
    

    image-20240911095215275

  • **获取初始数据:**在pages/shoplist/shoplist.js文件的Page({})中编写页面逻辑。

// pages/shoplist/shoplist.js
Page({data: {shopList: [], // 保存美食列表信息},listData: {page: 1, // 默认请求第1页的数据pageSize: 10, // 默认每页请求10条数据total: 0 // 数据总数,默认为0},isLoading: false, // 当前是否正在加载数据getShopList: function (cb) {this.isLoading = true// 请求数据之前,展示加载效果,接口调用结束后,停止加载效果wx.showLoading({title: '数据加载中...'})wx.request({url: 'http://127.0.0.1:3000/data',method: 'GET',data: {page: this.listData.page,pageSize: this.listData.pageSize},success: res => {console.log(res)this.setData({shopList: [...this.data.shopList, ...res.data],})this.listData.total = res.header['X-Total-Count'] - 0},complete: () => {// 隐藏加载效果wx.hideLoading()this.isLoading = falsecb && cb()}})},onLoad() {this.getShopList()},onReachBottom: function () {if (this.listData.page * this.listData.pageSize >= this.listData.total) {// 没有下一页的数据了return wx.showToast({title: '数据加载完毕!',icon: 'none'})}if (this.isLoading) {return}// 页码自增++this.listData.page// 请求下一页数据this.getShopList()},onPullDownRefresh: function () {// 需要重置的数据this.setData({shopList: []})this.listData.page = 1this.listData.total = 0// 重新发起数据请求this.getShopList(() => {wx.stopPullDownRefresh()})}
})
  • **实现页面渲染:**在pages/shoplist/shoplist.wxml文件中进行页面渲染。
<!--pages/shoplist/shoplist.wxml-->
<wxs src="../../utils/tools.wxs" module="tools"></wxs>
<view class="shop-item" wx:for="{{ shopList }}" wx:key="id"><view class="thumb"><image src="{{ item.image }}"></image></view><view class="info"><text class="shop-title">{{ item.name }}</text><text>电话:{{ tools.splitPhone(item.phone) }}</text><text>地址:{{ item.address }}</text><text>营业时间:{{ item.businessHours }}</text></view>
</view>
  • **处理电话格式:**在项目根目录下创建utils文件夹,将处理电话函数封装到utils/tools.wxs文件中。
 function splitPhone(str) {if (str.length !== 11) {return str}var arr = str.split('')arr.splice(3, 0, '-')arr.splice(8, 0, '-')return arr.join('')}module.exports = {splitPhone: splitPhone} 
  • **设置上拉触底距离和下拉刷新及样式:**在pages/shoplist/shoplist.json文件中配置上拉触底的距离为200px。
{"navigationBarTitleText": "美食","onReachBottomDistance": 200,"enablePullDownRefresh": true,"backgroundColor": "#efefef","backgroundTextStyle": "dark"
}
  • 页面实现效果

image-20240911111928548

4、案例:问卷调查

(1)案例分析
  • 需求:调查问卷又称调查表或询问表,是以问题的形式系统地记载调查内容的一种印件。传统的调查问卷是纸质的,发布和收集都不太方便,而通过微信小程序制作调查问卷,可以在短时间内快速收集反馈信息,相比纸质调查问卷极大地提高了效率。假设有一位大学老师,想通过调查问卷来了解同学们的专业技能、对开设公开课的意见等信息,从而根据同学们的建议制订下一步的教学计划。
  • 调查问卷需要填写的信息包括姓名、性别、专业技能和您的意见。
    • “姓名”通过单行输入框填写。
    • “性别”通过单选框选择。
    • “专业技能”通过多选框选择。
    • “您的意见”通过多行输入框填写。
    • 页面底部的“提交”按钮用于将用户输入的信息提交。
(2)知识储备-双向数据绑定
  • 单向数据绑定

    • 普通属性的绑定都是单向的,如果使用this.setData({ value:‘leaf’ })来更新value,则this.data.value和输入框中显示的值都会被更新为leaf
    <input value="{{ value }}" />
    
    • 但是如果用户在页面中修改了输入框里的值,则this.data.value的值不会发生改变。
  • 双向数据绑定:

    • 双向数据绑定的实现方式是在对应属性之前添加model:前缀。
    <input model:value="{{ value }}" />
    
    • 如果输入框的值被更改了,this.data.value也会随之更改。同时,页面的WXML文件中所有绑定了value的位置也会被一同更新,数据监听器也会被正常触发。
(3)案例实现
  • 准备工作

    • ①创建项目。在微信开发者工具中创建一个新的微信小程序项目,项目名称为“调查问卷”,模板选择“不使用模板”。
    • ②配置页面。项目创建完成后,在app.json文件中配置1个form页面。
    "pages": ["pages/form/form"
    ],
    
    • ③配置导航栏。在pages/form/form.json文件中配置页面导航栏。
    • ④配置页面样式。在pages/form/form.wxss文件中配置页面样式。
    • ⑤启动服务器。切换工作目录到nodejs服务程序目录,打开命令提示符,然后在命令提示符中执行如下命令,启动服务器。
    node index.js
    

    image-20240911134812392

  • **获取初始数据:**在pages/form/form.js文件的onLoad()事件处理函数中实现页面加载完成时自动向服务器发送请求,获取表单中的初始数据。

// pages/form/form.js
Page({data: {},onLoad: function () {wx.showLoading({title: '数据加载中'})wx.request({url: 'http://127.0.0.1:3000/',success: res => {// statusCode为HTTP状态码,200表示网络请求成功,数据获取成功if (res.statusCode === 200) {this.setData(res.data)console.log(res.data)} else {wx.showModal({title: '服务器异常'})}setTimeout(() => {wx.hideLoading()}, 500)},fail: function () {wx.hideLoading()wx.showModal({title: '网络异常,无法请求服务器'})},})},radioChange: function (e) {var val = e.detail.valuethis.data.gender.forEach((v) => {v.value === val ? v.checked = true : v.checked = false})},checkboxChange: function (e) {var val = e.detail.valuethis.data.skills.forEach((v) => {val.includes(v.value) ? v.checked = true : v.checked = false})},submit: function () {wx.request({url: 'http://127.0.0.1:3000',method: 'POST',data: this.data,success: res => {wx.showModal({title: '提交完成',showCancel: false})}})}
})
  • **实现页面渲染:**在pages/form/form.wxml文件中编写内容区域的整体结构。
<!--pages/form/form.wxml-->
<view class="container"><!-- 姓名区域 --><view><text>姓名:</text><input type="text" model:value="{{ name }}" /></view><!-- 性别区域 --><view><text>性别:</text><radio-group bindchange="radioChange"><label wx:for="{{ gender }}" wx:key="value"><radio value="{{ item.value }}" checked="{{ item.checked }}" />{{ item.name }}</label></radio-group></view><!-- 专业技能区域 --><view><text>专业技能:</text><checkbox-group bindchange="checkboxChange"><label wx:for="{{ skills }}" wx:key="value"><checkbox value="{{ item.value }}" checked="{{ item.checked }}" />{{ item.name }}</label></checkbox-group></view><!-- 意见区域 --><view><text>您的意见:</text><textarea model:value="{{ opinion }}" /></view><button type="primary" bindtap="submit">提交</button>
</view>
  • 页面实现效果

image-20240911135134297

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/143506.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

图数据归一化

图数据归一化 文章目录 图数据归一化前言一、图结构归一化1.邻接矩阵归一化2.拉普拉斯矩阵归一化 二、图特征归一化1.输入特征归一化2.中间层特征归一化 前言 在图分析和图网络的研究领域中&#xff0c;图归一化扮演着至关重要的角色。它包括一系列技术和方法&#xff0c;旨在…

关于循环Socket创建超Linux文件句柄限制现象分析

项目场景&#xff1a; 在操作系统的世界中万物皆文件。之前拜读过一些作品&#xff1a;针对于socket的创建&#xff0c;Linux也相应创建文件&#xff08;专业术语中也称文件句柄&#xff09;&#xff0c;于是&#xff0c;我想做一些关于极限的操作&#xff0c;看看这些极限操作…

RTR_Chapter_5 上

第五章 着色基础 在渲染三维物体的图像时&#xff0c;场景中的模型不仅仅需要有正确的几何形状&#xff0c;还应当具备想要的材质外观。根据应用程序的不同&#xff0c;这些外观具有非常广泛的范围&#xff0c;从真实感渲染&#xff08;即物体外观几乎和真实世界中的一模一样&a…

指针和引用;内联函数和普通函数

1. 指针和引用 1.1 定义和性质区别 指针是一个变量&#xff0c;只不过这个变量存储的是一个地址&#xff0c;指向内存的一个存储单元&#xff1b;而引用跟原来的变量实质上是同一个东西&#xff0c;只不过是原变量的一个别名而已。可以有const指针&#xff0c;常量指针可以改…

社恐人群的社交新宠:实时人脸融合互动应用

目录 简介 实现思路 1. 引入所需库 2. 人脸检测器和特征点模型的初始化 3. 打开摄像头 4. 选择覆盖的图片 5. 获取图片的尺寸 6. FPS计算初始化 7. 主循环处理每一帧 8. 人脸检测和特征点识别 9. 处理每一张检测到的人脸 10. 调整图片大小并叠加到人脸上 11. FPS计…

PowerShell install 一键部署Oracle19c

Oracle19c前言 Oracle 19c 是甲骨文公司推出的一款企业级关系数据库管理系统,它带来了许多新的功能和改进,使得数据库管理更加高效、安全和可靠。以下是关于 Oracle 19c 的详细介绍: 主要新特性 多租户架构:支持多租户架构,允许多个独立的数据库实例在同一个物理服务器上…

JAVA开源项目 校园美食分享平台 计算机毕业设计

本文项目编号 T 033 &#xff0c;文末自助获取源码 \color{red}{T033&#xff0c;文末自助获取源码} T033&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

【开源大模型生态8】这么多开源大模型

这张图片列出了多个开源的大模型&#xff0c;按照应用层、平台层、基础层和算力层进行了分类。下面我会为您逐一介绍这些模型及其特点&#xff1a; 应用层 AquilaCode: 一种用于自然语言处理的应用程序&#xff0c;可能是用于文本编码或解码的任务。SQLCoder: 可能是一种用于…

FLUX屠榜了小红书,平台这会也真假难辨

最近&#xff0c;小红书被一种新潮的AI绘图技术“屠榜”&#xff0c;这种技术就是FLUX。通过FLUX生成的虚拟美女照片&#xff0c;不仅成功“骗过”了平台审核&#xff0c;还让无数普通用户和商家惊讶。 越来越多人开始讨论这项技术的潜力&#xff0c;甚至一些并非技术玩家的用…

《SmartX ELF 虚拟化核心功能集》发布,详解 80+ 功能特性和 6 例金融实践

《SmartX ELF 虚拟化核心功能集》电子书现已发布&#xff01;本书详细介绍了 SmartX ELF 虚拟化及云平台核心功能&#xff0c;包含虚机服务、容器服务、网络服务、存储服务、运维管理、工具服务、数据保护等各个方面。 即刻下载电子书&#xff0c;了解如何利用基于 SmartX ELF …

【FPGA XDMA AXI Bridge 模式】PCIe:BARs 和 AXI:BARs 含义解析

一. XDMA IP核两种模式 Xilinx的 DMA/Bridge Subsystem for PCI Express IP核中&#xff0c;支持普通的XDMA模式&#xff0c;但是这种模式只允许主机端发起PCIe 读写请求&#xff0c;FPGA内部无法主动发起读写请求&#xff0c;也即FPGA无法主动读写HOST的内存。 而该IP核的另…

c++编程(26)——智能指针

欢迎来到博主的专栏&#xff1a;c编程 博主ID&#xff1a;代码小豪 文章目录 智能指针什么是智能指针&#xff1f; auto_ptrunique_ptrshare_ptrshared_ptr缺陷 weak_ptr 智能指针 什么是智能指针&#xff1f; 智能指针是c中关于动态内存管理的重要一环&#xff0c;在智能指针…

力扣718-最长重复子数组(Java详细题解)

题目链接&#xff1a;718. 最长重复子数组 - 力扣&#xff08;LeetCode&#xff09; 前情提要&#xff1a; 因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。 dp五部曲。 1.确定dp数组和i下标的含义。 2.确定递推公式。 3.dp初始化。 4.确定dp的遍历顺序。 5…

Linux嵌入式相机 — 项目总结

main函数执行流程 1、初始化触摸屏 Touch_screen_Init();struct tsdev *ts NULL; ts ts_setup(NULL, 0); //以阻塞打开2、初始化 LCD LCD_Init(void); 通过 ioctl 函数获取 LCD 的固定参数、可变参数&#xff0c;得到分辨率、bpp、一行的长度&#xff08;以字节为单位&a…

【MATLAB源码-第225期】基于matlab的计算器GUI设计仿真,能够实现基础运算,三角函数以及幂运算

操作环境&#xff1a; MATLAB 2022a 1、算法描述 界面布局 计算器界面的主要元素分为几大部分&#xff1a;显示屏、功能按钮、数字按钮和操作符按钮。 显示屏 显示屏&#xff08;Edit Text&#xff09;&#xff1a;位于界面顶部中央&#xff0c;用于显示用户输入的表达式和…

【激励广告带来的广告收入与用户留存率的双重提升】

激励广告带来的广告收入与用户留存率的双重提升 ) 随着移动应用市场的竞争加剧&#xff0c;如何通过广告变现成为众多开发者关注的焦点。其中&#xff0c;激励广告&#xff08;Rewarded Ads&#xff09;凭借其用户友好、互动性强等特点&#xff0c;逐渐成为开发者的首选。那些…

Java——Static与final修饰的变量与方法(总结)

前言&#xff1a; Java语法学过一遍之后&#xff0c;我相信大多数和我一样脑瓜子嗡嗡的&#xff0c;甚至有点乱了&#xff0c;这时候应该自己把之前的能总结的&#xff0c;或者不熟悉的都要总结一遍&#xff0c;以便于后期的学习&#xff01;&#xff01; static修饰的成员变量…

[附源码]SpringBoot+VUE+Java实现人脸识别系统

今天带来一款优秀的项目&#xff1a;java人脸识别系统源码 。 系统采用的流行的前后端分离结构&#xff0c;内含功能包括 “人脸数数据录入”&#xff0c;“人脸管理”&#xff0c;“摄像头识别” 如果您有任何问题&#xff0c;也请联系小编&#xff0c;小编是经验丰富的程序员…

数码好物抢先看!2024有什么好用又实惠的好物推荐!

在数字科技日新月异的今天&#xff0c;各种数码好物层出不穷&#xff0c;它们以其先进的技术、创新的功能以及不断提升的性能&#xff0c;为我们的生活带来了极大的便利和乐趣。对于消费者来说&#xff0c;在众多的数码产品中挑选出好用又实惠的好物&#xff0c;无疑是一件既令…

Spring Controller

服务器控制 响应架构 Spring Boot 内集成了 Tomcat 服务器&#xff0c;也可以外接 Tomcat 服务器。通过控制层接收浏览器的 URL 请求进行操作并返回数据。 底层和浏览器的信息交互仍旧由 servlet 完成&#xff0c;服务器整体架构如下&#xff1a; Server&#xff1a; Tomcat…