小程序01

目录

  • 页面目录介绍
  • app.json全局配置
  • 创建的文件夹中.json文件的配置
  • 数据绑定
  • 双线程模型
  • 小程序的宿主环境
    • 双线程模型
    • 通过双线程界面的渲染过程是怎样的?
  • 小程序的启动流程
  • 事件
    • 常见的事件
    • 给事件传参数
    • 参数currentTarget和Target的区别
    • touches和changedTouches的区别
  • 事件捕获和事件冒泡
  • 自定义组件
    • 组件的创建
    • 自定义组件文件内容
    • 如何在页面中使用自定义组件
    • 将显示在页面中数字进行动态修改
    • 页面向自定义组件传递数据
    • 组件将事件传给页面

页面目录介绍

|--pages  # 所有页面的存放位置
    |--index
    |   |--{}index.js  # 每个页面的JS文件
    |   |--index.json  # 每个页面的配置
    |   |--index.wxml  # 每个相当于html文件
    |   |--idnex.wxss  # 每个相当于css文件
    |--logs 
|--utils
    |--util.js  # 公共方法存放位置
|--app.js  # 全局的app对象文件,启动app的入口
|--app.json  # 全局的app配置文件
|--app.wxss  # 全局的类似样式文件
|--project.config.json  # 多人开发统一的配置文件
|--sitemap.json  # 关于本app小程序的对外描述信息

app.json全局配置

# 新增页面  在全局的app.json中的pages数组中注册,注意如果是首页的话,必须将注册的文件夹写在最前面的位置才起作用,其后的其他文件夹就会被覆盖掉,不能作为首页使用;
{
"pages": [
    "pages/tests/test",
    "pages/index/index",
    "pages/logs/logs"  # 末尾的文件夹注册不能加逗号,不然报错  
  ],
    
 # 小程序页面的头部配置
  "window": {
    "backgroundTextStyle": "light",  # 修改小程序名称字体背景颜色 只能是dark和light
    "navigationBarBackgroundColor": "#fff",  # 修改头部的背景颜色
    "navigationBarTitleText": "WeChat",  # 修改小程序的名称
    "navigationBarTextStyle": "black",  # 修改头部出现的所有字体的颜色,只能是black和white
      "enablePullDownRefresh": true  # 首页是否具有下拉刷新的功能
  },
  
 # 底部导航栏  list元组中最多不超过5项
 "tabBar": {
     "color": "#660066",  # 没被激活时的颜色
    "selectedColor": "#6666FF",  # 激活后的颜色
    "backgroundColor": "#FFFF99",  # 导航栏的背景颜色
    "borderStyle": "white",  # 导航栏上边框的颜色,只有black和white
     "list": [{
      "pagePath": "pages/tests/test",  # 显示的页面,对应我们在pages中创建的文件夹
      "text": "首页", # 创建的导航栏对应页面文字信息
      "iconPath": "images/icon1.png",  # 文字信息上面的小图标路径  我们需要自己创建一个与pages同级别的文件夹images,内部放我们需要展示的小图标
      "selectedIconPath": "images/icon1-active.png"  # 点击图标(或说文字),使图标高亮(激活状态),代表跳转到对应的页面
    },{
      "pagePath": "pages/tests1/test1",
      "text": "商品分类",
      "iconPath": "images/icon2.png",
      "selectedIconPath": "images/icon2-active.png"
    }
    ]
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}

创建的文件夹中.json文件的配置

{
  "usingComponents": {},
  "navigationBarTitleText": "八折活动"  # page中新建的文件夹中.json文件有配置头部标题,就优先使用,没配置就使用全局app.json文件中“window”对象配置的“微信小程序”
}

注意:新增一个页面,直接在app.json中的page数组中添加具体路径到文件名的绝对路径即可。

数据绑定

以我们创建的tests文件夹为例:
1.在 test.js中的“data对象”注册变量
2.在test.wxml中的view标签中渲染数据,将属性或变量写在{{}}中
{{ name }}

test.js

// pages/tests/test.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
      name:'sb',
      a:1,
      b:2,
      list:[{name:"egon",like:"sao"},
      {name:"echo",like:"xuexi"}]
  },
})

test.wxml

pages/test/test.wxml
{{name}}
{{num}}
{{a+b}}
{{name}}
{{a}}+{{b}}
{{name+like}}
 # 页面会出现一个默认选中的单选框

  # 如果页面出现的选框默认没被选中,需要写成变量的形式{{ }}

123


  
{{key}}:{{value.name}}:{{value.like}}




  hello!

双线程模型

小程序01_第1张图片

小程序的宿主环境

微信客户端微信客户端提供双线程去执行wxml,wxss,js文件。

双线程模型

1.上述的渲染层上面运行wxml文件和wxss文件,渲染层使用是的webview线程进行渲染(一个程序会有多个页面,也就会有多个view线程进行运作)

2.js文件是运行在逻辑层,逻辑层的js是通过jscore进行运行的。

通过双线程界面的渲染过程是怎样的?

wxml与DOM树

其实我们wxml文件与我们html中的DOM树是一样的,这样我们就可以有js来模拟一个虚拟的DOM树:

小程序01_第2张图片

初始化渲染

小程序01_第3张图片

如果我们的wxml文件中如果有变量:要与js逻辑层共同渲染页面成为一个真正的DOM树:

界面数据发生变化

小程序01_第4张图片

1.如果通过setData把hello改成dsb,则js对象的的节点会发生改变.

  1. 这时会用diff算法对比两个对象的变化,

3 .然后将变化的部分应用到DOM树上

4.从而达到更新页面的目的,这也就是数据驱动的原理

总结

界面渲染的整体流程

1在渲染层将wxml文件与wxss文件转化成js对象也就是虚拟DOM

2 在逻辑成将虚拟的DOM对象配合生成,真实的DOM树,在交给渲染层渲染

3 当数据变化是,逻辑层提供更新数据,js对象发生改变,用diff算法进行比较

4 将更新的内容,反馈到真实的DOM树中,更新页面

小程序的启动流程

小程序01_第5张图片

在app生命周期中执行了什么?

小程序01_第6张图片

执行App()函数也就是注册一个App

1.在注册app的时候,可以判断小程序的进入场景,options就是捕获进入场景的参数

​ options对象:{scene: 1001, path: "pages/tests/test", query: {…}}

2.我们可以在执行通过生命周期函数,做一些数据请求

3.可以在app中设置一个全局对象,让所有页面都能使用

在页面的什么周期中执行了什么?

Page({
  /**
   * 页面的初始数据
   */
  data: {
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    console.log("onload")
  },

  /**
 * 生命周期函数--监听页面显示
 */
  onShow: function () {
    console.log("onshow")
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {
    console.log("onReady")
  },
  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {
    console.log("onHide")
  },
  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {
    console.log("onUnload")
  },
  //监听用户下拉动作,
  onPullDownRefresh :function(){
    //如果要用到这个,必须在全局的app.json中的window对象加上"enablePullDownRefresh": true
    console.log("下拉刷下")
  }, 
  //页面上拉触底事件的处理函数
  onReachBottom:function(){
    console.log("上拉到底部")
},
//页面滚动触发事件的处理函数
onPageScroll:   function(e){
  console.log("滚轮在动",e)
} 

})

1 在生命周期函数中发送网络请求,从服务端获取数据

2 初始化一些数据,在data里面,以方便wxml引用

3 监听wxml的事件,绑定对应的事件函数

4 还有页面滚动,上拉,下拉等

页面的生命周期

小程序01_第7张图片

在虚拟DOM节点完成以后,wxml文件等待着.js文件传数据,此时触发onReady函数。

事件

常见的事件

类型 触发条件
touchstart 手指触摸动作开始
touchmove 手指触摸后移动
touchcancel 手指触摸动作被打断,如来电提醒,弹窗
touchend 手指触摸动作结束
tap 手指触摸后马上离开
longpress 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发
longtap 手指触摸后,超过350ms再离开(推荐使用longpress事件代替)
transitionend 会在 WXSS transition 或 wx.createAnimation 动画结束后触发
animationstart 会在一个 WXSS animation 动画开始时触发
animationiteration 会在一个 WXSS animation 一次迭代结束时触发
animationend 会在一个 WXSS animation 动画完成时触发
touchforcechange 在支持 3D Touch 的 iPhone 设备,重按时会触发

有两个注意点

  Touchcancle: 在某些特定场景下才会触发(比如来电打断等) 
  tap事件和longpress事件通常只会触发其中一个

给事件传参数

在test.wxml文件中写一个button标签,然后给该按钮绑定一个tap事件,函数名叫click。


在test.js中写click函数

page({
    
  data: {
      name:'sb',
      a:1,
      b:2,
      list:[{name:"egon",like:"sao"},
      {name:"echo",like:"xuexi"}]
  },
    
 click:function(e){
     console.log("你点我了!")
     console.log(e)
  }
})
'''
返回的e中都有哪些参数:
{type: "tap", timeStamp: 1607, target: {…}, currentTarget: {…}, detail: {…}, …}
changedTouches: [{…}]
currentTarget:
dataset: {age: "sb", name: 2}
id: ""
offsetLeft: 0
offsetTop: 214
__proto__: Object
detail: {x: 166, y: 240}   鼠标点击“button按钮的位置”
target:
dataset: {age: "sb", name: 2}
id: ""
offsetLeft: 0
offsetTop: 214
__proto__: Object
timeStamp: 1607   打开页面之后,在多长事件内点击的“button按钮”
touches: [{…}]
type: "tap"  通过什么样的事件类型,触发的当前函数
_requireActive: true
currentTarget.dataset
'''

参数currentTarget和Target的区别

test.wxml

# 嵌套的两个标签

外面

里面

test.js

click1: function (e) {
    console.log("你点我了1!")
    console.log(e)
  },
   click2: function (e) {
    console.log("你点我了2!")
    console.log(e)
  }

test.wxss

.outter{
  height: 300rpx;
  background-color: red;
}
.inner{
  height: 100rpx;
  background-color: blue;
}

在点击内部的属性为inner的标签时,控制台打印的结果是两个标签都被触发了,先触发inner,再触发outter;内部inner返回的参数currentTarget和target没区别;而外部的就有了区别

inner:

{type: "tap", timeStamp: 231738, target: {…}, currentTarget: {…}, detail: {…}, …}
changedTouches: [{…}]
currentTarget:
dataset: {name: "2"}
id: ""
offsetLeft: 0
offsetTop: 281
__proto__: Object
detail: {x: 96, y: 304}
target:
dataset:
name: "2"
__proto__: Object
id: ""
offsetLeft: 0
offsetTop: 281
__proto__: Object
timeStamp: 231738
touches: [{…}]
type: "tap"
_requireActive: true
__proto__: Object

outter:

{type: "tap", timeStamp: 231738, target: {…}, currentTarget: {…}, detail: {…}, …}
changedTouches: [{…}]
currentTarget:
dataset: {name: "1"}
id: ""
offsetLeft: 0
offsetTop: 260
__proto__: Object
detail:
x: 96
y: 304
__proto__: Object
target:
dataset: {name: "2"}
id: ""
offsetLeft: 0
offsetTop: 281
__proto__:
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
nv_constructor: "Object"
nv_toString: ƒ ()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
timeStamp: 231738
touches: [{…}]
type: "tap"
_requireActive: true
__proto__: Object

如果要将内部的参数传到外部,就需要使用currentTarget.dataset.name,不能使用Target,这就是这两个参数的区别;

touches和changedTouches的区别

小程序01_第8张图片

事件捕获和事件冒泡

事件捕获是从外到内的

test.wxml


外面的

中间的

里面的


test.wxss

.outter{
  height: 600rpx;
  background-color: red;
}
.middle{
  height: 400rpx;
  background-color: yellow;
}
.inner{
  height: 200rpx;
  background-color: blue;
}

test.js

page({
    click1: function () {
    console.log("外面的!")
  },
   click2: function () {
    console.log("中间的!")
  },
  click3: function () {
    console.log("里面的!")
  },
})

控制台打印结果:

小程序01_第9张图片

事件的冒泡是从内到外的

test.wxml


外面的

中间的

里面的


小程序01_第10张图片

事件的传递和冒泡的顺序是:先从外面向里面传递,然后事件再从内部向外冒泡

小程序01_第11张图片

阻止事件的传递

将捕获事件capture-bind改成capture-catch:tap="click2"

小程序01_第12张图片

阻止事件的冒泡

将事件bind:tap 修改成catch:tap

小程序01_第13张图片

自定义组件

组件的创建

点微信开发工具页面上的“+”、“目录”,创建一个普通的文件夹,右键文件夹选中“Component”,即可创建一个组件。

自定义的组件的文件格式与页面文件一样,都有.js .json .wxml .wxss文件

自定义组件文件内容

.wxml

此文件编写组件模板


components/tes/tes.wxml


  {{innerText}}
    

.js

放组件中的属性和方法

// components/tes/tes.js
Component({     # 有别于页面的Page
  /**
   * 组件的属性列表
   */
  properties: {  # 页面的属性在这里注册

  },

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

  },

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

  }
})

.json

{
  "component": true,  # 如果想把这个组件添加到页面,这里就必须设置成true。这里是进行自定义组件声明
  "usingComponents": {}  # 想要调用哪个组件,就需要将哪一个组件导进来;组件也可以嵌套组件
}

.wxss

在此文件中加入组件样式,注意:在组件wxss中不应使用ID选择器,属性选择器和标签选择器。

/* components/tes/tes.wxss */  # 写组件样式的文件
/* 这里的样式只应用于这个自定义组件 */
.inner {
  color: red;
}

如何在页面中使用自定义组件

首先要在页面的.json文件中进行引用声明,还要提供对应的组件名和组件路径

{
    // 引用声明
  "usingComponents": {
      // 要使用的组件的名称 和组件的路径
    "tes":"/components/tes/tes"
  },
  "navigationBarTitleText": "八折活动"
}

之后,在对应页面的.wxml中注册该组件

将组件引用到页面**

1.在页面的.json文件中注册组件
{
  "usingComponents": {
    "tes":"/components/tes/tes"
  },
2.将组件.json文件中的component修改成true
{
  "component": true,
  "usingComponents": {}
}
3.将组件到页面文件的.wxml中,进行渲染。

将显示在页面中数字进行动态修改

渲染的页面上添加一个按钮(button按钮),并将要进行修改的数字的初始值也渲染在页面上({{a}})将该按钮绑定一个点击事件,当点击该按钮时,触发事件,通过this.setData将修改的值渲染到页面中。

# .wxml文件
{{a}}
# .js
  click4: function (e) {
    this.setData({
      a:this.data.a+1
    }),
    console.log("this.setData:",this.setData)
    console.log("this.data:", this.data)
  },
'''
this.setData: ƒ (e,t){var n=this;return"function"==typeof t&&setTimeout(function(){t.call(n)},0),H(this).setData(e)}

this.data: {name: "sb", a: 2, b: 2, list: Array(2), __webviewId__: 19}
a: 2
b: 2
list: (2) [{…}, {…}]
name: "sb"
__webviewId__: 19
__proto__: Object
'''

页面向自定义组件传递数据

1.将要传到组件中的数据写在要传的组件中,这里name="是的"就是即将传入组件的数据

# pages/tests/test.wxml

2.在组件的.js文件中的properties属性中注册该属性,

  properties: {
    name: {
      type: String,
      value: "你好!"  # 这个value是默认值,当页面没有传值过来,又要渲染这个name属性,就渲染这个默认值;如果页面传了name,就渲染传过来的,把这个默认值覆盖掉;
    }
  },
  /**
   #  组件的初始数据  也可以通过组件的.wxml文件直接渲染到前端页面上去
   */
  data: {
      title:"data中的"
  },

3.将组件properties中的数据通过变量的形式写在组件的.wxml文件中,进行渲染

{{title}}components/tes/tes.wxml 我添加到页面上了!{{name}}

组件将事件传给页面

还以上述动态的修改页面数字为例:

将渲染在页面上的button按钮写在组件中,在组件中给该按钮绑定一个事件(triggerEvent),当点击事件的时候,组件将事件传递到页面,然后在页面中再进行tap事件绑定函数,在页面的js文件中再去动态修改页面数字。页面的.js文件接收组件传来的参数和事件类型,可以通过e.detail获取参数,通过e.type获取定义的事件。

# components/tes/tes.wxml
# components/tes/tes.js
Component({
      methods: {
      click4:function(e){
      console.log("我是组件中的e:",e)
      console.log("我是组件中的this:",this)
      this.triggerEvent("icre",{"INDEX:":"我来自组件,这里用triggerEvent将数据带到页面"},{})
    }
  }
})

'''
/* 我是组件中的e: 

{ type: "tap", timeStamp: 3380, target: { … }, currentTarget: { … }, detail: { … }, … }
changedTouches: [{ … }]
currentTarget:
dataset: { ss: "123" }
id: ""
offsetLeft: 0
offsetTop: 625
__proto__: Object
detail: { x: 193, y: 651 }
target:
dataset: { ss: "123" }
id: ""
offsetLeft: 0
offsetTop: 625
__proto__: Object
timeStamp: 3380
touches: [{ … }]
type: "tap"
_requireActive: true
__proto__: Object 
===========================================
我是组件中的this: 
r { __wxWebviewId__: 17, __wxExparserNodeId__: "5e379cf3" }
__wxExparserNodeId__: "5e379cf3"
__wxWebviewId__: 17
data: Object
name: "是的"   页面的name数据传到组件
__proto__: Object
dataset: (...)
id: (...)
is: (...)
properties: (...)
__proto__:
click4: ƒ click4(e)
constructor: ƒ r()
data: (...)
dataset: (...)
id: (...)
is: (...)
properties: (...)
__proto__: Object  */
'''
# pages/tests/test.wxml
{{a}}

# pages/tests/test.js
  click66:function(e){
    console.log(e)
    this.setData({
     a:this.data.a+1
    })
  }
'''
组件中携带的参数和事件的类型(type)都在页面的.js文件中接收了,可以在组件的.js文件的methods方法中写入参数和事件类型。

这是e接收的数据:type有组件传来的自定义事件,detail接收的是传来的参数  
{ type: "icre", timeStamp: 6178, target: { … }, currentTarget: { … }, detail: { … }, … }
changedTouches: undefined
currentTarget: { id: "", dataset: { … } }
detail:
INDEX:: "我来自组件,这里用triggerEvent将数据带到页面"
__proto__: Object
target:
dataset: { }
id: ""
__proto__: Object
timeStamp: 6178
touches: undefined
type: "icre"
_requireActive: undefined
__proto__: Object 
'''

一般在.js文件中的this指的都是当前页面,以下是console.log(this)的内容

我是this: r {__wxWebviewId__: 13, __wxExparserNodeId__: "47bdd23b", __route__: "pages/index/index", route: "pages/index/index", bindViewTap: ƒ, …}
bindViewTap: ƒ ()
getUserInfo: ƒ ()
arguments: (...)
caller: (...)
length: 1
name: "bound getUserInfo"
nv_length: (...)
__proto__: ƒ ()
[[TargetFunction]]: ƒ getUserInfo(e)
[[BoundThis]]: r
[[BoundArgs]]: Array(0)
onHide: ƒ ()
onLoad: ƒ ()
onReady: ƒ ()
onRouteEnd: ƒ ()
onShow: ƒ ()
onUnload: ƒ ()
options: {}
route: "pages/index/index"
__route__: "pages/index/index"
__wxExparserNodeId__: "47bdd23b"
__wxWebviewId__: 13
data: (...)
dataset: (...)
id: (...)
is: (...)
properties: (...)
__proto__:
bindViewTap: ƒ bindViewTap()
getUserInfo: ƒ getUserInfo(e)
onLoad: ƒ onLoad()
__freeData__: {}
constructor: ƒ r()
data: (...)
dataset: undefined
id: (...)
is: (...)
properties: (...)
__proto__: Object

详细总结看这里:小程序总结

你可能感兴趣的:(小程序01)