草庐IT

微信小程序(文件组成 、目录结构、生命周期方法、AppId、组件(标签)、语法、事件、Api、开发工具)

YF-SOD 2023-04-19 原文

目录

AppID

文件组成

目录结构

app.json

其它全局配置链接 

app.js

app.wxss

 App 参考文档

生命周期方法

App.js中周期方法

onload(opt)

onReady

onShow

onHide

onUnload

onError

onPageNotFound

onUnhandleRejection

onThemeChange

页面周期方法

onPullDownRefresh

onReachBottom

onShareAppMessage

onPageScroll

onAddToFavorites

小程序标签(组件)

view

scroll-view

text

block

template

import

include

input

swiper

swiper-item

audio

video

movable-area

movable-view

cover-view

cover-image

rich-text

progress

form

button

label

checkbox

checkbox-group

radio

radio-group

picker

slider

switch

textarea

icon

camera

其它标签

原生组件

使用限制

语法

数据绑定

指令

wx:for

wx:if|wx:elif|wx:else

hidden

require(path,callback,error)

示例

事件 

点击事件 

bindtap

catchtap

bind:|capture-bind:|capture-catch:

事件对象

其它事件

小程序API

showModel

其它api

微信小程序官网

微信小程序开发者工具


AppID

对应网上注册的小程序id。一个id对应一个小程序项目。

åå

文件组成

小程序一共由四种文件组成。

文件类型必需作用
js页面逻辑(没有window对象,即没有document对象,alert等)
wxml页面结构(相当于.html文件
json页面配置
wxss页面样式表(相当于.css文件

目录结构

 app.js为全局js(用于定义全局变量,全局生命周期等),app.json为全局的页面配置(包括导航栏、页面信息、网络请求超时时间等页面配置),app.wxss为全局的样式,util.js为全局的api。page下的每个文件夹代表一个页面,1个页面由上面4个文件组成。project.config.json项目配置文件

app.json

下面是常用初始配置。

pages数组中每一个对应一个页面(一般对应page文件夹下的一个文件),下面小程序只有2个页面index和logs,且初始显示的为index页面(写在第一位的页面会初始化时显示)。

navigationBarTitleText对应小程序顶部显示的名称。

tabBar的list对应底部导航栏内容,pagePath对应点击时跳转的页面。注意只有pagePath中有的页面才能看到tabBar。

networkTimeout为请求时长设置超时。

{
  "pages": [
    "pages/index/index",
    "pages/logs/index"
  ],
  "window": {
    "navigationBarTitleText": "Demo"
  },
  "tabBar": {
    "list": [{
      "pagePath": "pages/index/index",
      "text": "首页"
    }, {
      "pagePath": "pages/logs/index",
      "text": "日志"
    }]
  },
  "networkTimeout": {
    "request": 10000,
    "downloadFile": 10000
  },
  "debug": true
}

其它全局配置链接 

全局配置 | 微信开放文档

app.js

注意没有window对象,即没有document对象,alert等。

App() 必须在 app.js 中调用,必须调用且只能调用一次,用于生成注册小程序实例,绑定生命周期回调函数、错误监听和页面不存在监听函数等(注意:参数中的data属性和vue一样,在这里定义的数据,可以在wxml中用双大括号{{}}直接使用)。

// app.js
App({
  onLaunch (options) {
    // Do something initial when launch.
  },
  onShow (options) {
    // Do something when show.
  },
  onHide () {
    // Do something when hide.
  },
  onError (msg) {
    console.log(msg)
  },
  data:{
      //和vue一样,在这里定义的数据,可以在wxml中用双大括号{{}}直接使用。
  },
  globalData: 'I am global data'
})

整个小程序只有一个 App 实例,是全部页面共享的。开发者可以通过 getApp 方法获取到全局唯一的 App 实例,获取App上的数据或调用开发者注册在 App 上的函数。

// xxx.js
const appInstance = getApp()
console.log(appInstance.globalData) // I am global data

app.wxss

全局样式,生成的项目中自动设置了.container样式为display:flex;alignflex-direction:column等样式。

 App 参考文档

App(Object object) | 微信开放文档

生命周期方法

App.js中周期方法

执行顺序onload->onShow->onReady。

onload(opt)

监听页面加载,第一次完整打开小程序页面时打开小程序页面退出后再打开不算,除非清理了后台进程)。当navigate跳转到当前页面上,在url上带的参数会挂载在opt对象上。

onReady

监听页面初次渲染,全部加载和显示完成,可以提供给用户进行操作。

onShow

监听页面显示,每次从未激活状态变成激活状态(打开小程序后,从别的页面切换回小程序页面)。

onHide

监听页面隐藏,每次从激活状态变成未激活状态(打开小程序后,从小程序页面切到后台或别的页面)。

onUnload

监听页面卸载,正常关闭时执行(点击小程序中的退出按钮时触发,注意直接关闭进程不会触发)。

onError

监听发生错误事件。

onPageNotFound

监听页面不存在事件。

onUnhandleRejection

监听未处理的Promise拒绝事件。

onThemeChange

监听系统主题变化事件。

页面周期方法

包括app.js中的onload、onShow、onHide、onReady、onUnload,以及以下常用的。

onPullDownRefresh

监听用户下拉动作(向下滑动)。

onReachBottom

页面上拉触底事件。

onShareAppMessage

用户点击右上角分享按钮。

onPageScroll

页面滚动触发事件的处理事件。

onAddToFavorites

用户点击右上角收藏事件。

小程序标签(组件)

小程序会自动生成一个page根节点将所有内容包裹。

view

相当于div标签。

scroll-view

定义一块区域可滚动。scroll-x、scroll-y定义可以滚动的方向,注意纵向滚动时需要设定容器高度,想要全屏的滚动时设定100%高度无效,因为最外层有一个page根节点,需要同时给page设定100%高度才会生效。enable-back-to-top属性使点击标题回到顶部。bindscrolltoupper滚动到顶部/左边触发事件,bindscrolltolower滚动到底部/右边触发事件。

text

理解为span标签。selectable属性定义文本是否可选,默认为false。

block

没有显示意义标签,相当于一个虚拟的父节点标签。

template

可以在模板中定义代码片段,然后在不同的地方调用.,is属性为使用模板的名字。注意data属性要传一个解构的对象,该对象中的属性作为数据使用。

//定义模板
<template name="msgItem">
  <view>
    <text> {{index}}: {{msg}} </text>
    <text> Time: {{time}} </text>
  </view>
</template>
//使用模板
<template is="msgItem" data="{{...item}}"/>
Page({
  data: {
    item: {
      index: 0,
      msg: 'this is a template',
      time: '2016-09-15'
    }
  }
})
//显示
<view>
    <text> 0: this is a template </text>
    <text> Time:2016-09-15} </text>
  </view>

import

用于在其他文件中引入模板使用。注意import没有继承性

例如下面C import B,B import A,在 C 中可以使用 B 定义的template,在 B 中可以使用 A 定义的template,但是 C 不能使用 A 定义的template。 

<!-- A.wxml -->
<template name="A">
  <text> A template </text>
</template>
<!-- B.wxml -->
<import src="a.wxml"/>
<template name="B">
  <text> B template </text>
</template>
<!-- C.wxml -->
<import src="b.wxml"/>
<template is="A"/>  <!-- Error! Can not use tempalte when not import A. -->
<template is="B"/>

include

include 可以将目标文件除了 <template/> <wxs/> 外的整个代码引入,相当于是拷贝到 include 位置 (单标签)。

input

输入框,placeholder属性为占位提示内容,value属性为默认输入框中的值,password属性定义为密码框;type属性定义输入内容,有text、number、digit(文本、数字、小数);bindinput绑定键盘输入时触发事件,event.detail = {value, cursor, keyCode},keyCode 为键值,2.1.0 起支持,处理函数可以直接 return 一个字符串,将替换输入框的内容;confirm-type属性设置输入时弹出键盘的右下角按钮的文字,仅在type='text'时生效,有send、search、next、go、done(发送、搜索、下一个、前往、完成);

跳转标签,相当于a标签,用于跳转页面不能跳转网页,url属性定义小程序内的页面路径或者其它小程序的路径;target属性定义在哪个目标上跳转,默认self为当前小程序,miniProgram为其它小程序;open-type属性为打开方式,默认为navigate新页面入栈,redirect为替换栈顶的页面(此时没有回退功能),switchTab可以跳转到app.json中tabBar中的用到的页面(默认是不能跳转的) ;

swiper

轮播图容器,包裹swpier-item标签,swpier-item为每个轮播的项目。autopaly属性设置自动播放,interval属性设置自动轮播的时间间隔(单位为ms),indicator-dots属性设置轮播的点。duration为自动轮播时滑动到下一张的时间。current为当前显示图片序号。current-itiem-id属性定义初始显示的图片的序号(该序号为swiper-item中item-id属性值)。

swiper-item

轮播项目。

audio

音频播放标签(单标签),src为播放音频的资源地址,loop属性设置是否循环播放,controls属性设置是否看见暂停控件,poster属性为播放器初始显示的图片,name属性为歌名,author属性为作者名,binderror属性为播放错误触发事件,bindplay为播放触发事件 ,id属性绑定的值可以通过wx.createAudioContext(id值)拿到音频节点,上面有play、pause方法控制播放与暂停,seek(number)控制播放进度,单位为秒。

最新采用wx.createInnerAudioContext()获取音频空间对象,该对象上通过src设置播放音频地址,autoplay设置自动播放,onPlay(callback)开始播放,要接收一个回调函数作为参数,onError(callback)为播放发送错误时执行传入的回调函数。

video

视频播放标签,src属性指定视频播放地址;initial-time指定初始播放位置;controls为进度条;loop为是循环播放;autoplay为自动播放;muted为静音;danmu-btn为显示弹幕按钮;danmu-list为一个数组,数组内每个对象为一条弹幕(例如[{text:'这是弹幕',color:'red',time:1}]),表示在1s的时候弹幕按钮打开的话显示一条红色的弹幕),enable-danmu为初始化时打开弹幕按钮;id属性用于通过wx.createVideoContext(id)获取video节点。video节点上有sendDanmu(obj)方法用于发送弹幕,obj为弹幕对象例如{text:'这是弹幕',color:'red'}。

movable-area

搭配movable-view标签(可移动内容),定义可移动区域。scale-area属性定义可以双指放大可移动区域。

movable-view

可移动内容,在movable-view区域移动。direction属性定义可移动方向有all、vertical、horizontal、none;out-of-bounds属性定义可以拖动超出可移动区域;scale定义可以双指放大可移动内容;inertia输定定义惯性效果。

cover-view

覆盖在原生组件之上的视图,常用于覆盖在video、canvas、camera等媒体组件上。

cover-image

覆盖在原生组件之上的图片视图,常用于覆盖在video、canvas、camera等媒体组件上。

rich-text

富文本,用于显示html5中的标签div、a、span等。注意是通过属性确定显示内容

node属性为html内容的字符串htmlstring或者数组nodes(虚拟节点的对象形式),会将node 属性中的内容以html形式渲染。

progress

进度条标签。percent属性确定进度条百分比;show-info属性在进度条右侧显示百分比;stroke-width属性为进度条线的宽度;color为进度条颜色;active属性为有加载过程。

form

表单标签,bindsubmit绑定提交事件,点击嵌套在form标签带有属性form-type=‘submit’的button标签触发;bindreset绑定重置事件(不需要手动写重置也会重置form表单中的内容),点击嵌套在form标签带有属性form-type=‘reset’的button标签触发。

button

按钮,type定义样式,size大小,color定义颜色;form-type属性对应form中的提交或重置事件。

label

用来改进表单组件的可用性。

使用 for 属性找到对应的id,或者将控件放在该标签下,当点击时,就会触发对应的控件。 for优先级高于内部控件,内部有多个控件的时候默认触发第一个控件。 目前可以绑定的控件有:buttoncheckboxradioswitchinput

checkbox

单选框,value对应绑定的实际值。

checkbox-group

多项选择器,内部由多个checkbox组成。bindchange绑定事件为checkbox-group中选中项发生改变时触发 change 事件,detail = {value:[选中的 checkbox 的value的数组]}。

radio

圆形单选按钮。checked为是否被选中;disabled为是否禁用;value为实际的值。

radio-group

单项选择器,内部由多个 radio 组成。bindchang为radio-group中选中项发生改变时触发事件,detail = {value:[选中的 radio 的value的数组]}。

picker

从底部弹起的滚动选择器。mode为选择器的模式,有selector、multiSelector、time、date、region(分别为普通、多列、事件、日期、省市区选择器,其中默认为普通);range是选择器选择的内容为一个数组(多列选择时该数组中包含多个数组,每一列对应一个数组);bindchange为选择后触发事件。

slider

滑动选择器。min为最小值;max为最大值;step为每次滑动最小的步长(取值必须大于 0,且(max - min)/step为整数);value为实际的值。

switch

开关,type为checkbox时为单选框;bindchange为开关点击时触发事件。

textarea

多行输入框。

icon

有type、size、color属性,type定义图标的图片,size单位为像素(传入数字),color为图标颜色。dispiay:inline-block。

camera

相机标签。device-position属性front定义相机前置,back为后置;flash属性定义闪光灯,默认auto,off为关;ctx=wx.createCameraContext()获得相机上下文;ctx.takephoto({quality:'high',success:(res)=>{}})进行拍照,传入一个对象进行设置拍照精度quality,成功后的回调success接收一个参数res.tempImagePath上为拍照的图片存储的路径。

其它标签

视图容器 | 微信开放文档

原生组件

教程 | 《小程序开发指南》

在内置组件中,有一些组件较为特殊,它们并不完全在Exparser的渲染体系下,而是由客户端原生参与组件的渲染,这类组件我们称为“原生组件”,这也是小程序Hybrid技术的一个应用。

原生组件有:camera、canvas、input、live-player、live-pusher、map、textarea、video。

使用限制

  1. 原生组件的层级是最高层,其他组件无法通过z-indx来覆盖。
  2. 原生组件可以覆盖原生组件。
  3. 部分样式无法支持原生组件CSS动画、position:fixed。
  4. 在IOS下原生组件不支持触摸事件。

教程 | 《小程序开发指南》

语法

数据绑定

和vue中的数据绑定使用相同,通过data属性并在wxml中通过双大括号{{}}使用。注意boolean类型的一定要放在双大括号中,不然默认为字符串。

指令

wx:for

列表渲染,和vue中的v-for相同,其中默认循环变量为index(数组当前元素的下标值)和item(数组当前元素的变量名)。可以通过wx:for-index和wx:for-item来重新命名循环变量。例如下面:

<view wx:for="{{[1,2,3]}}" wx:for-index="idx" wx:for-item="itemName" key=>
  {{idx}}: {{itemName}}
</view>
//生成
<view>0:1</view>
<view>1:2</view>
<view>2:3</view>

注意当列表中项目的位置会动态改变或者有新的项目添加到列表中时需要设置wx:key

wx:key 的值以两种形式提供

  1. 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
  2. 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。

wx:if|wx:elif|wx:else

条件渲染,条件为false时节点不会被创建。和vue中v-if、v-else-if、v-else相同。注意绑定时使用双大括号,不然绑定的是字符串。

hidden

相当于vue中的v-show,节点会被渲染出来,为false时设置了 display:none。注意绑定时使用双大括号,不然绑定的是字符串。

require(path,callback,error)

引入模块。返回模块通过 module.exports 或 exports 暴露的接口。

path为需要引入模块文件相对于当前文件的相对路径,或 npm 模块名,或 npm 模块路径(默认不支持绝对路径)。callback为异步加载成功回调函数,该回调函数参数为成功加载的模块。error

可以支持链式调用,如下:

require
    .async('path/to/mod')
    .then((mod) => {
        console.log(mod)
    })
    .catch(({ errMsg, mod }) => {
        console.error(`path: ${mod}, ${errMsg}`)
    })

示例

// common.js
function sayHello(name) {
  console.log(`Hello ${name} !`)
}
function sayGoodbye(name) {
  console.log(`Goodbye ${name} !`)
}

module.exports.sayHello = sayHello
exports.sayGoodbye = sayGoodbye

//使用
var common = require('路径/common.js')
Page({
  helloMINA: function() {
    common.sayHello('MINA')
  },
  goodbyeMINA: function() {
    common.sayGoodbye('MINA')
  }
})

事件 

触发顺序先捕获后冒泡,捕获触发顺序为先外层后内层,冒泡触发顺序为先内层后外层。

点击事件 

bindtap

绑定点击事件,和vue相同,不需要绑定this就可以在函数中通过this访问data中的数据。注意该事件为冒泡事件。

<view id="tapTest" data-hi="Weixin" bindtap="tapName"> Click me! </view>
Page({
  tapName: function(event) {
    console.log(event)
  }
})

catchtap

绑定点击事件,和vue相同,不需要绑定this就可以在函数中通过this访问data中的数据。注意该事件为捕获事件,不会冒泡。

bind:|capture-bind:|capture-catch:

用于触摸类事件的捕获阶段。bind:为允许冒泡,capture-bind:为事件捕获(不许冒泡),capture-cath:为中断捕获(不许冒泡+中断)。

下面:点击 inner view 会先后调用handleTap2handleTap4handleTap3handleTap1

<view id="outer" bind:touchstart="handleTap1" capture-bind:touchstart="handleTap2">
  outer view
  <view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
    inner view
  </view>
</view>

将上面代码中的第一个capture-bind改为capture-catch,将只触发handleTap2

事件对象

绑定的函数上会默认接收一个事件对象为参数,该对象具有以下属性:

  • type:事件类型
  • timestamp:时间戳
  • target:属性集合(事件源标签|组件)。
    • id:事件源组件id。
    • dataset:data-事件源组件上的自定义属性集合(触发事件的标签上自定义属性data-name=‘yf’,则dataset中有属性name的值为‘yf’)。
  • currentTarget:当前组件的一些属性值集合(用于区分事件冒泡)。
    • dataset data-事件源组件上的自定义属性集合。

其它事件

事件 | 微信开放文档

小程序API

wx.env | 微信开放文档

通过wx.调用api。

showModel

弹出提示框

wx.showModal({
  title: '提示',
  content: '这是一个模态弹窗',
  success (res) {
    if (res.confirm) {
      console.log('用户点击确定')
    } else if (res.cancel) {
      console.log('用户点击取消')
    }
  }
})

其它api

基础 | 微信开放文档

微信小程序官网

微信开放文档

微信小程序开发者工具

稳定版 Stable Build | 微信开放文档

有关微信小程序(文件组成 、目录结构、生命周期方法、AppId、组件(标签)、语法、事件、Api、开发工具)的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby - 树顶语法无限循环 - 2

    我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

  3. ruby - 在院子里用@param 标签警告 - 2

    我试图使用yard记录一些Ruby代码,尽管我所做的正是所描述的here或here#@param[Integer]thenumberoftrials(>=0)#@param[Float]successprobabilityineachtrialdefinitialize(n,p)#initialize...end虽然我仍然得到这个奇怪的错误@paramtaghasunknownparametername:the@paramtaghasunknownparametername:success然后生成的html看起来很奇怪。我称yard为:$yarddoc-mmarkdown我做错了什么?

  4. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

    所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

  5. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

    我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

  6. ruby - 覆盖相似的方法,更短的语法 - 2

    在Ruby类中,我重写了三个方法,并且在每个方法中,我基本上做同样的事情:classExampleClassdefconfirmation_required?is_allowed&&superenddefpostpone_email_change?is_allowed&&superenddefreconfirmation_required?is_allowed&&superendend有更简洁的语法吗?如何缩短代码? 最佳答案 如何使用别名?classExampleClassdefconfirmation_required?is_a

  7. ruby 语法糖 : dealing with nils - 2

    可能已经问过了,但我找不到它。这里有2个常见的情况(对我来说,在编程Rails时......)用ruby​​编写是令人沮丧的:"astring".match(/abc(.+)abc/)[1]在这种情况下,我得到一个错误,因为字符串不匹配,因此在nil上调用[]运算符。我想找到的是比以下内容更好的替代方法:temp="astring".match(/abc(.+)abc/);temp.nil??nil:temp[1]简而言之,如果不匹配,则简单地返回nil而不会出错第二种情况是这样的:var=something.very.long.and.tedious.to.writevar=some

  8. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  9. ruby - Ruby 语法糖有 "rules"吗? - 2

    我正在学习Ruby的基础知识(刚刚开始),我遇到了Hash.[]method.它被引入a=["foo",1,"bar",2]=>["foo",1,"bar",2]Hash[*a]=>{"foo"=>1,"bar"=>2}稍加思索,我发现Hash[*a]等同于Hash.[](*a)或Hash.[]*一个。我的问题是为什么会这样。是什么让您将*a放在方括号内,是否有某种规则可以在何时何地使用“it”?编辑:我的措辞似乎造成了一些困惑。我不是在问数组扩展。我明白了。我的问题基本上是:如果[]是方法名称,为什么可以将参数放在括号内?这看起来几乎——但不完全是——就像说如果你有一个方法Foo.d

  10. css - 用 watir 检查标签类? - 2

    我有一个div,它根据表单是否正确提交而改变。我想知道是否可以检查类的特定元素?开始元素看起来像这样。如果输入不正确,添加错误类。 最佳答案 试试这个:browser.div(:id=>"myerrortest").class_name更多信息:http://watir.github.com/watir-webdriver/doc/Watir/HTMLElement.html#class_name-instance_method另一种选择是只查看具有您期望的类的div是否存在browser.div((:id=>"myerrortes

随机推荐