一.逻辑层与界面层分离
  小程序开发框架将我们需要完成的编码,划分成了两种类型的编码:逻辑编码(由JavaScript完成,业务数据供给界面事件处理),界面编码(页面结构WXML,页面样式WXSS,展示逻辑层的数据).
二.逻辑层的JavaScript
  小程序开发中使用到的JavaScript同常规网页开发所使用的JavaScript有所差异,在小程序的开发之中,有很多的.js文件,他们都属于页面的逻辑层,我们知道程序一旦启动就会去执行app.js文件中的代码,我们可以使用console.log()方法来验证.
  小程序不是运行在浏览器中,所以没有BOM和DOM对象,但是在小程序只用存在一些特有的全局成员:

 //app.js

 console.log("==============================");
//自定义逻辑代码
//1.小程序不是运行在浏览器中,所以没有BOM和DOM对象
console.log(window); //在小程序中无法获取到BOM中定义的成员
console.log(document); //无法获取到DOM中定义的成员
//2.小程序的JS有一些额外的成员(全局)
//App 方法:用于定义应用程序实例对象
//Page方法:用于定义页面对象
//getApp方法:用于获取全局应用程序对象,每个小程序都由唯一的应用程序实例
//getCurrentPages:用于获取当前页面的调用栈
//wx对象: 用来提供核心api
console.log(wx);
//3.小程序的JS是支持CommonJS规范(用于约定这些文件与文件之间是如何组织,相互之间协同的)的
//通过module 和 require来传递通用方法
const foo = require("./utils/foo.js");
foo.say("world");
console.log("==============================");
var obj = {
onLaunch: function() {
// 展示本地存储能力
var logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs) // 登录
wx.login({
success: res => {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
}
})
// 获取用户信息
wx.getSetting({
success: res => {
if (res.authSetting['scope.userInfo']) {
// 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
wx.getUserInfo({
success: res => {
// 可以将 res 发送给后台解码出 unionId
this.globalData.userInfo = res.userInfo // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
if (this.userInfoReadyCallback) {
this.userInfoReadyCallback(res)
}
}
})
}
}
})
},
globalData: {
userInfo: null
},
//自定义成员方法
foo: function() {
console.log("自定义成员方法");
}
};
//调用了一个APP方法(全局方法)
App(obj);
//调用App方法的作用是用来创建应用程序实例对象
//定义应用程序的生命周期事件
 function say(msg){
console.log("hello " + msg);
} // 导出say()方法
module.exports = {
say: say
}
//在标准的COmmonJS规范中还支持exports.say 来导出,但是在小程序当中是不支持的,只支持module.exports
 //index.js
//获取应用实例
//完成页面的逻辑,包括功能的实现
const app = getApp() Page({
data: {
motto: 'Hello World',
userInfo: {},
hasUserInfo: false,
canIUse: wx.canIUse('button.open-type.getUserInfo')
},
//事件处理函数
bindViewTap: function() {
wx.navigateTo({
url: '../logs/logs'
})
},
onLoad: function () {
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,
hasUserInfo: true
})
} else if (this.data.canIUse){
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
app.userInfoReadyCallback = res => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
} else {
// 在没有 open-type=getUserInfo 版本的兼容处理
wx.getUserInfo({
success: res => {
app.globalData.userInfo = res.userInfo
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
}
},
getUserInfo: function(e) {
console.log(e)
app.globalData.userInfo = e.detail.userInfo
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
})
},
onShow: function () {
//在每次该界面唤醒时,获取当前页面的调用栈:
console.log(getCurrentPages());
console.log("当前调用栈中的页面数量: " + getCurrentPages());
} })
 // pages/demo/demo.js

 //获取当前小程序对象实例
var app = getApp();
Page({ /**
* 页面的初始数据
*/
data: { }, /**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
app.foo(); }, /**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { }, /**
* 生命周期函数--监听页面显示
*/
onShow: function () {
//在每次该界面唤醒时,获取当前页面的调用栈:
console.log(getCurrentPages());
console.log("当前调用栈中的页面数量: " + getCurrentPages());
console.log(getCurrentPages()[getCurrentPages().length - 1] == this);
}, /**
* 生命周期函数--监听页面隐藏
*/
onHide: function () { }, /**
* 生命周期函数--监听页面卸载
*/
onUnload: function () { }, /**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () { }, /**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () { }, /**
* 用户点击右上角分享
*/
onShareAppMessage: function () { }
})

三.界面层--数据绑定
  在程序的开发过程中,界面层提供一种模板机制,可以借助一种特殊的语法来完成数据的绑定,将动态的数据绑定到模板当中,最近将嵌入过后的数据显示到界面中,小程序中使用的是Mustache 语法(双大括号)语法:

 <!--pages/demo/demo.wxml-->
<!-- 框架最大的特点就是让我们开发者必须按照特定的方法来编写代码 -->
<!-- 数据绑定
1.数据在哪 (在当前页面对象Page的data属性中)
2.绑定到哪里 (想绑定到哪就在哪里用Mustache 语法进行输出)
在小程序中规定所有的数据绑定对在相应js文件的Page对象中的data属性中 -->
<view class="container">
<text>{{ message }}</text>
<text>{{ person.name }} : {{ person.age }}</text>
</view>
 // pages/demo/demo.js

 Page({
//为页面提供数据,界面和逻辑之间的桥梁
data:{
message:"hello world",
person:{
name: "张三",
age:25
}
}
})

--数据绑定语法补充,除了在页面标签的内部我们会使用Mustache语法进行输出,在标签的内部,我们也可以使用Mustache语法:

 <!--pages/demo/demo.wxml-->
<!-- 框架最大的特点就是让我们开发者必须按照特定的方法来编写代码 -->
<!-- 数据绑定
1.数据在哪 (在当前页面对象Page的data属性中)
2.绑定到哪里 (想绑定到哪就在哪里用Mustache 语法进行输出)
在小程序中规定所有的数据绑定对在相应js文件的Page对象中的data属性中 -->
<view class="container">
<text>{{ message }}</text>
<text>{{ person.name }} : {{ person.age }}</text>
<!-- Mustache语法可以用在:
1. innerHTML 元素内存
2. 元素的属性上
3. 不能用在标签名和属性名上面(只能用在vlaue上而无法使用在key上) -->
<view class="world {{ viewClassName }}"></view>
<!-- 可以直接使用字面量和简单的逻辑运算符 -->
<text>{{ 'hello' }}</text>
<text>{{ 111 }}</text>
<text>{{ 111 + 222 }}</text>
<text>{{ 111 < 222 ? 'yes' : 'no' }}</text>
<text>{{ true }}</text>
<!-- 如我我们默认不添加checked属性,那么默认为false,但是如我们添加了checked属性,则不论我们指定的是什么,都将显示被勾选;
在JS中将所有有值的字符串都认为是true ,因此我们想要表达一个true 我们可以使用 Mustache 语法来解析字符串
-->
<checkbox checked="false">复选框1</checkbox>
<checkbox checked="{{ false }}">复选框2</checkbox>
</view>

四.列表渲染
  在逻辑层中,很多的情况下都会是一个数组类型的数据(列表数据),那么此时在我们的界面层中就需要使用到列表渲染:

   <!-- 如我我们默认不添加checked属性,那么默认为false,但是如我们添加了checked属性,则不论我们指定的是什么,都将显示被勾选;
在JS中将所有有值的字符串都认为是true ,因此我们想要表达一个true 我们可以使用 Mustache 语法来解析字符串
-->
<checkbox checked="false">复选框1</checkbox>
<checkbox checked="{{ false }}">复选框2</checkbox>
<!-- 列表数组渲染(列表数据绑定) -->
<view>
<!-- <view class="task_1">
<checkbox checked="{{ true }}"></checkbox>
<text>HTML</text>
</view>
<view class="task_2">
<checkbox checked="{{ false }}"></checkbox>
<text>JS</text>
</view>
<view class="task_3">
<checkbox checked="{{ false }}"></checkbox>
<text>CSS</text>
</view> -->
<!-- 循环遍历输出列表:
1.明确页面结构中的循环体(在这里就是 最外层的view),删除多余的内容,只留下一个循环体,在剩下的一个上加上wx:for的属性
属性值为我们需要进行遍历的数据源,数据源是一个数组
2.在循环体的内部(标签)使用item代表当前被遍历的元素
3.如果在我们的Page对象的data中存在一个全局的item属性,此时我们无法在内部获取到全局的item属性,如果想要获取到我们的
全局item属性,那么需要使用wx: for-item 属性在自定义循环体的指向
4.如果我们想要在循环体中拿到当前的index,那么默认我们可以直接使用index变量({{ index }})来获取我们的索引值,如果和
我们的全局变量中途,那么则可以使用wx:for-index来指定索引别名
5.在官方之中.默认是支持循环嵌套的
-->
<view wx:for="{{ todos }}" wx:for-item="aaa" wx:for-index="bbb">
<text>{{ bbb + 1 }}:</text>
<checkbox checked="{{ aaa.complete }}"></checkbox>
<text>{{ item.name }} {{ aaa.name }}</text>
</view>
<!-- 打印 9*9 乘法表 -->
<view>
<view wx:for="{{ [1,2,3,4,5,6,7,8,9] }}" wx:for-item="i">
<view wx:for="{{ [1,2,3,4,5,6,7,8,9] }}" wx:for-item="j">
<!-- <view wx:if="{{ i <= j }}"> -->
<text>{{ i }} * {{ j }} = {{ i * j }}</text>
<!-- </view> -->
</view>
</view>
</view>
</view>

五.事件处理

   <!-- 绑定事件
1.触碰事件(bindTap):相当于click点击事件,在当前控件下触碰和送开,移除则不会触发-->
<button bindtap="buttonTapHandle">点击</button>
   buttonTapHandle: function(e){
console.log("点击按钮打印此条.");
// console.dir()将一个对象以树状的形式打印到控制台,便于我们调试复杂对象
console.dir(e);
}

六.事件冒泡

   <!-- 事件冒泡
1.我们需要知道手机点击的工作原理是在手机屏幕点击的点,垂直以里发射一条射线,这条射线会先碰到蓝色的块,但是蓝色的块被包裹来红色的块里,所以会同时触发两个事件(先触发内部事件,再触发外部事件).
2.在小程序之中阻止冒泡:绑定事件除了使用bindtap外,还能够使用catchtap,catchtap具有阻止冒泡的功能,并且绑定事件-->
<view style="width:200px;height:200px;background-color:red" bindtap="outerHandler">
<view style="width:100px;height:100px;background-color:blue" catchtap="innerHandler"> </view>
</view>
   innerHandler: function(){
console.log("触发内部事件");
},
outerHandler: function(){
console.log("触发外部事件");
}

七.事件传参

   <!-- 在网页的开发之中,经常会有多个组件去调用相同方法的情况,此时如果我们需要知道当前触发这个方法的是哪一个组件,通常我们呢可以通过this来获取当前对象,当然也可以通过传递一个参数来完成对于当前对象的判断.
但是在小程序之中,我们在指定事件属性的时候,只能去指定事件的名字,而无法像网页那样去指定方法的参数,因此我们在方法中定义的参数e所拿到的就是当前的事件对象,我们可以发现在target属性下,存在一个dataset属性,其记录了data属性信息,例如data-id,data-name这些自定义的属性,都是根据Dataset这个属性来获取的.
在小程序中事件处理函数中的this,表示的还是这个Page页面对象,如果我们在定义data属性的时候,使用了多个中横线,那么将自动将该中横线忽略,将后方的变量名转变为驼峰式命名法来展现
总结:
1.事件的基本使用: bind[xxx] catch[xxx]
2.小程序中事件冒泡处理的机制: 使用catch[xxx]
3.如果需要给事件处理函数指定参数,只能通过dataset的这种方式-->
<button bindtap="handleWithParm" data-id="123" data-name="hello" data-hello-world="hello world">参数事件</button>
   handleWithParm(e){
console.log(e); //获取当前的点击元素
console.log(e.target.dataset); //获取元素上所有data-[xxx]属性的集合
}

八.单向数据流

   <!-- 单向数据流
在小程序开发之中,数据一定是从逻辑层获取而来的,而后将数据绑定到界面层,界面上则可以通过一些输入将数据同步回来.从而形成一个数据扭转的回环.
1.bindinput事件:用户输入时触发-->
<view>
<input style="border:1px solid #000;" bindinput="inputHandler"></input>
<text>{{ inputMessage }}</text>
</view>
   inputHandler: function(e) {
//e.detail.value 获取到当前输入的值
console.log(e.detail.value);
//小程序中的数据绑定,本质上是一次绑定,在绑定完成之后,并不会去监视数据的变化.如果想要得到此次的变化,那么就需要使用setData的方法进行改变
//setData是用来改变data数据的,与直接赋值区别在于setData可以通知界面作出变化,而直接赋值的话没有办法实现这一点(早期的JS无法实现)
this.setData({
inputMessage: "您输入的内容: " + e.detail.value
})
console.log(this.data.message);
}

九.登录页面案例

 <!--pages/login/login.wxml-->
<view class="container">
<view>
<input class="username" placeholder="请输入用户名" style="border:1px solid #000"
value="{{ username }}" bindinput="usernameChangeHandler"/>
<input class="password" type="password" placeholder="请输入密码" style="border:1px solid #000"
value="{{ password }}" bindinput="passwordChangeHandler"/>
</view>
<view>
<button type="primary" bindtap="buttonLogin">登录</button>
<button type="default" bindtap="buttonRegister">注册 </button>
</view>
</view>
 // pages/login/login.js
/* 实现登录界面
1. 设计数据的结构(data属性)
2. 将数据绑定到指定的元素上
*/
Page({
data: {
username: "",
password: ""
},
//用于处理登录按钮点击的事件
buttonLogin: function() {
console.log("执行登录事件");
},
//用于处理注册按钮点击的事件
buttonRegister: function() {
//1. 首先需要知道用户输入了什么
console.log(this.data.username + " " + this.data.password);
//2. 根据用户输入的值去判断
//3. 根据判断的结果做出响应
console.log("执行注册事件");
},
//用户输入用户名
usernameChangeHandler: function(e) {
this.setData({
username: e.detail.value
})
},
//用户输入密码
passwordChangeHandler: function(e) {
this.setData({
password: e.detail.value
})
}
})

十.抽象共同的事件处理函数
  如我在我们的代码之中为每一个input标签都增加一个事件绑定函数,那么在我们的代码之中,将会存在很多的重复代码,为了避免这样的的情况,我们可以抽象共同的事件处理函数:

 <!--pages/login/login.wxml-->
<view class="container">
<view>
<input class="username" placeholder="请输入用户名" style="border:1px solid #000"
value="{{ username }}" bindinput="inputChangeHandler" data-name="username"/>
<input class="password" type="password" placeholder="请输入密码" style="border:1px solid #000"
value="{{ password }}" bindinput="inputChangeHandler" data-name="password"/>
</view>
<view>
<button type="primary" bindtap="buttonLogin">登录</button>
<button type="default" bindtap="buttonRegister">注册 </button>
</view>
</view>
 // pages/login/login.js
/* 实现登录界面
1. 设计数据的结构(data属性)
2. 将数据绑定到指定的元素上
*/
Page({
data: {
username: "",
password: ""
},
//用于处理登录按钮点击的事件
buttonLogin: function() {
console.log(this.data.username + " " + this.data.password);
console.log("执行登录事件");
},
//用于处理注册按钮点击的事件
buttonRegister: function() {
//1. 首先需要知道用户输入了什么
console.log(this.data.username + " " + this.data.password);
//2. 根据用户输入的值去判断
//3. 根据判断的结果做出响应
console.log("执行注册事件");
},
//用户输入用户名
// usernameChangeHandler: function(e) {
// this.setData({
// username: e.detail.value
// })
// },
//用户输入密码
// passwordChangeHandler: function(e) {
// this.setData({
// password: e.detail.value
// }),
// console.log(e);
// }
//抽象上述两个方法,完成事件
inputChangeHandler: function(e) {
var key = e.target.dataset.name;
var value = e.detail.value;
var changed = {};
changed[key] = value;
this.setData(changed);
}
})

--除此之外,我们还可以使用表单的方式来进行数据的接收,这样可以免除这一些列复杂的操作:

     <view>
<input class="username" placeholder="请输入用户名" style="border:1px solid #000"
value="{{ username }}" name="username" />
<input class="password" type="password" placeholder="请输入密码" style="border:1px solid #000"
value="{{ password }}" name="password" />
</view>
<view>
<button type="primary" form-type="submit">登录</button>
<button type="default" form-type="submit" >注册 </button>
</view>
 //使用表单提交的方式来完成数据接收
loginSubmit: function(e){
console.log(e.detail.value);
}

十一.条件渲染

   <!-- 条件渲染 -->
<view>
<button bindtap="showButtonHandler">点击切换面板展示</button>
<text wx:if="{{ !show }}">
面板中显示的内容
面板中显示的内容
面板中显示的内容
</text>
<!-- 如果想要将多个模块同时进行判断,可以使用block标签,block标签没有其他任何的作用,只是作为一个判断属性的载体,
在页面渲染过程中并没有实际的意义 -->
<block wx:if="{{ !show }}">
<text style="display:block;">面板中显示的内容1</text>
<text style="display:block;">面板中显示的内容2</text>
<text style="display:block;">面板中显示的内容3</text>
</block>
<!-- hidden只是默认需显示,但是依旧会被渲染,使用if的则不会被渲染,因此wx:if是惰性的,当条件为假的时候,是无渲染的
1.hidden:标签还是会工作,只是不显示在界面上
2.wx:if: 惰性,只有显示的时候才会进行局部的渲染 -->
<view hidden="{{ !show }}">
<text style="display:block;">面板中显示的内容4</text>
<text style="display:block;">面板中显示的内容5</text>
<text style="display:block;">面板中显示的内容6</text>
</view>
</view>

十二.界面层(WXSS vs CSS)
  rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。即将所有设备都使用rpx为750设置为撑满屏幕宽度.

 .demo {
width: 750rpx;
height: 200px;
background-color: red;
}

最新文章

  1. 解决PHP-问题:Class &#39;SimpleXMLElement&#39; not found in
  2. css雪碧图生成工具4.1更新
  3. 小菜学习设计模式(五)—控制反转(Ioc)
  4. 16、C++获取磁盘空间的方法
  5. Careercup - Facebook面试题 - 5412018236424192
  6. 跨站脚本攻击(XSS)
  7. 简洁JS 日历控件 支持日期和月份选择
  8. Linux Shell编程(14)——内部变量
  9. C++ —— 构建开源的开发环境
  10. 浮点与整形在GUI下的相关思考
  11. Image的Stride
  12. Python之路:迭代器和yield生成器
  13. xml类型转换列表显示 SQL查询
  14. hdu 5015(矩阵快速幂z )
  15. php扩展之Yar
  16. logging日志——Basic Logging Tutorial
  17. Spring Cloud Gateway入门
  18. SQL Server 常用函数使用方法(持续更新)
  19. struts2_maven_learning
  20. mybatis异常invalid comparison: java.util.Date and java.lang.String

热门文章

  1. spark复习笔记(7):sparkstreaming
  2. 前端之CSS基础
  3. ABBYY FineReader 14.0.107.232 Enterprise 下载和安装使用
  4. js中跳出forEach循环
  5. Linux架构之NFS共享存储1
  6. eclipse 代码提示快捷键 alt+/
  7. 2019ICPC网赛南京站B题 super_log(欧拉降幂
  8. gitignore 忽略文件
  9. Task9.Attention
  10. 日志管理工具logrotate