• 小程序01


    页面目录介绍

    |--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标签中渲染数据,将属性或变量写在{{}}中
    <view>{{ name }}</view>
    

    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

    <text>pages/test/test.wxml</test>
    <view>{{name}}</view>
    <view>{{num}}</view>
    <view>{{a+b}}</view>
    <view>{{name}}</view>
    <view>{{a}}+{{b}}</view>
    <view>{{name+like}}</view>
    <checkbox checked="true"</checkbox> # 页面会出现一个默认选中的单选框
    <checkbox checked="true"</checkbox>
    <checkbox checked="{{false}}"></checkbox>  # 如果页面出现的选框默认没被选中,需要写成变量的形式{{ }}
    
    <view class="name{{b}}">123</view>
    
    <!--for循环  wx:key="index"不写能正常运行,写了可以加快遍历速度-->
    <view wx:for="{{list}}" wx:for-index='key' wx:for-item='value' 
    wx:key="index">  
    {{key}}:{{value.name}}:{{value.like}}
    
    </view>
    <!--if判断-->
    <view wx:if="{{b<5}}">
      hello!
    </view>
    

    双线程模型

    小程序的宿主环境

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

    双线程模型

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

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

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

    wxml与DOM树

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

    初始化渲染

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

    界面数据发生变化

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

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

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

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

    总结

    界面渲染的整体流程

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

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

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

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

    小程序的启动流程

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

    执行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 还有页面滚动,上拉,下拉等

    页面的生命周期

    在虚拟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。

    <!-- 给页面按钮绑定一个tap事件,绑定事件的函数名叫click -->
    <button bind:tap='click' data-name="{{b}}" data-age="sb">按钮</button>
    

    在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

    # 嵌套的两个标签
    <view class="outter" bind:tap="click1" data-name="1">
    外面
    <view class="inner" bind:tap='click2' data-name="2">
    里面
    </view>
    </view>
    

    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的区别

    事件捕获和事件冒泡

    事件捕获是从外到内的

    test.wxml

    <view class="outter" capture-bind:tap="click1" >
    外面的
    <view class="middle" capture-bind:tap='click2' >
    中间的
    <view class="inner" capture-bind:tap='click3' >
    里面的
    </view>
    </view>
    </view>
    

    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("里面的!")
      },
    })
    

    控制台打印结果:

    事件的冒泡是从内到外的

    test.wxml

    <view class="outter" bind:tap="click1" >
    外面的
    <view class="middle" bind:tap='click2' >
    中间的
    <view class="inner" bind:tap='click3' >
    里面的
    </view>
    </view>
    </view>
    

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

    阻止事件的传递

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

    阻止事件的冒泡

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

    自定义组件

    组件的创建

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

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

    自定义组件文件内容

    .wxml

    此文件编写组件模板

    <!--components/tes/tes.wxml-->
    <text>components/tes/tes.wxml</text>
    <!-- 这是自定义组件的内部WXML结构 -->
    <view class="inner">
      {{innerText}}
        <slot></slot>
    </view>
    

    .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中注册该组件

    <tes></tes>
    

    将组件引用到页面**

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

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

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

    # .wxml文件
    <view>{{a}}</view>
    <button bind:tap='click4'>加数按钮</button>
    
    # .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
    <tes name="是的"></tes>
    

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

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

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

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

    组件将事件传给页面

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

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

    # components/tes/tes.wxml
    <button bind:tap='click4' data-ss="123">组件中的加数按钮</button>
    
    # 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
    <view>{{a}}</view>
    <!-- 页面中捕获(监听)来自组件.js文件中的icre事件,这个事件可以随便取名,但是这里在绑定的时候,必须是组建中传过来的那个事件(icre) -->
    <tes name="是的" bind:icre="click66"></tes>
    
    # 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
    

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

  • 相关阅读:
    省市区选择器
    查找算法 分享1:顺序查找
    查找算法 分享2:折半(二分)查找
    编程实现对键盘输入的英文名句子进行加密。用加密方法为,当内容为英文字母时其在26字母中的其后三个字母代替该字母,若为其它字符时不变。
    查找算法 分享3:分块查找
    NSMutableDicitionary 的setValue和setObject的区别
    查找算法 分享4:哈希查找
    玩转博客园的5个小技巧
    WCF读书笔记安全:基础知识(身份验证、授权、传输安全)
    北京的房价
  • 原文地址:https://www.cnblogs.com/zhangchaocoming/p/12203819.html
Copyright © 2020-2023  润新知