微信小程序入门问题整理

页面的基本结构

入口文件初始化

app.json

{
  "pages": [
    "pages/welcome/welcome"    // 引导指向文件 
    // 这个地方也具备新建页面功能: 起个名字保存一下,即可新建页面

    //注:这里的注释仅做博客记录,真正的json文件中不可以使用注释
  ],
  "window":{  //配置窗口样式
    "navigationBarBackgroundColor":"#405f80"
  },
  "tabBar": {  //tabar
    "borderStyle": "white",
    "color": "#949494",
    "selectedColor": "#1F4BA5",
    "list": [
      {
        "pagePath": "pages/posts/post",
        "text": "文章",
        "iconPath":"images/tab/yuedu.png",
        "selectedIconPath":"images/tab/yuedu_hl.png"
      },{
      "pagePath": "pages/movies/movies",
      "text": "电影",
      "iconPath": "images/tab/dianying.png",
      "selectedIconPath": "images/tab/dianying_hl.png"
    }]
  }
}

js脚本文件

Page({

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

  /*生命周期函数--监听页面初次渲染完成*/
  onReady: function () {},

  /*生命周期函数--监听页面显示*/
  onShow: function () {},

  /*生命周期函数--监听页面隐藏*/
  onHide: function () {},

  /*生命周期函数--监听页面关闭*/
  onUnload: function () {},

  /*页面相关事件处理函数--监听用户下拉动作*/
  onPullDownRefresh: function () {},

  /*页面上拉触底事件的处理函数*/
  onReachBottom: function () {},

  /*用户点击右上角分享*/
  onShareAppMessage: function () {}


  //还可以自定义方法/属性
  //例子:
  imgPath:"/images/...",
  process:function(){
      
  }

})

执行顺序:onload onShow onready  
首先执行页面初始化    再执行页面显示函数  再执行页面渲染完成

wx.redirectTo 时会执行 onUnload
wx.navigateTo 时会执行 onHide
轮播的实现

最简单版本
<view>
    <swiper>
        <swiper-item>content1</swiper-item>
        <swiper-item>content2</swiper-item>
        <swiper-item>content3</swiper-item>
    </swiper>
</view>
这样文字就可以滑动了。swiper-item代表滑动的容器


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
换成图片
<view>
    <swiper>
        <swiper-item><image src="xxx.png" /></swiper-item>
        <swiper-item><image src="xxx.png" /></swiper-item>
        <swiper-item><image src="xxx.png" /></swiper-item>
    </swiper>
</view>
注:对<swiper-item>设置样式是没有用的。
对swiper整个组件设置高宽,必须设置到<swiper>根节点上。
<swiper-item>默认的高宽取的是<swiper>的高宽。
但是同时也需要对图片进行宽度100%设置。

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
可以在swiper根节点上设置轮播效果。
如:indicator-dots="{{true}}" 设置显示分页器
<swiper indicator-dots="{{true}}">  
    <swiper-item><image src="xxx.png" /></swiper-item>
    <swiper-item><image src="xxx.png" /></swiper-item>
    <swiper-item><image src="xxx.png" /></swiper-item>
</swiper>

更多参数参考下图 

swiper参数列表

轮播图绑定跳转页面:

//post.wxml:
<swiper indicator-dots="{{true}}">  
    <swiper-item>
        <image catchtap="onSwiperItemTap" src="xxx.png" data-postId="3"/>
        //查看数据相应页面的id
    </swiper-item>
    <swiper-item>
        <image catchtap="onSwiperItemTap" src="xxx.png" data-postId="4"/>
    </swiper-item>
    <swiper-item>
        <image catchtap="onSwiperItemTap" src="xxx.png" data-postId="5"/>
    </swiper-item>
</swiper>

//post.js:
onSwiperItemTap:function(event){
    var postId = event.currentTarget.dataset.postid;
    wx.navigateTo({
        url: "post-detail/post-detail?id=" + postId;
    })
}

>>>>>>>>>>>>>
优化catchtap="onSwiperItemTap"过多问题
使用事件冒泡的机制
<swiper catchtap="onSwiperTap" indicator-dots="{{true}}">  
    <swiper-item>
        <image src="xxx.png" data-postId="3"/>
        //查看数据相应页面的id
    </swiper-item>
    <swiper-item>
        <image src="xxx.png" data-postId="4"/>
    </swiper-item>
    <swiper-item>
        <image src="xxx.png" data-postId="5"/>
    </swiper-item>
</swiper>

事件冒泡机制:
点击图片,事件会由image传递到swiper-item上,再由swiper-item传递到swiper上。
swiper上使用的是catchtap,事件不会再往上传递了。
js:
onSwiperTap:function(event){
    var postId = event.target.dataset.postid; //注:target
    //target这里指的是image,而currentTarget指的是swiper
    wx.navigateTo({
        url:"post-detail/post-detail?id="+postId
    })
}

单独页面中的配置

小程序的样式以就近原则
比如:
pages中的welcome页面
app.json和welcome.json设置了相同配置,welcome.json优先级高。

全局json和页面json的区别:
//全局:对整个应用程序大的配置
可以配置:
pages、window、tabBar、networkTimeout、debug、
functionalPages、subpackages、workers、
requiredBackgroundModes、plugins、preloadRule、resizable
{
  "pages": [
    "pages/welcome/welcome"
  ],
  "window":{
    "navigationBarBackgroundColor":"#405f80"
  },
}

//单独页面
在单独页面下,只能配置window,既然只能配置window,所以小程序就规定,"window"不要了
{ 
  "navigationBarBackgroundColor":"#405f80"
},
绑定数据

简单例子:
//js
Page({
    data:{
        date:"Oct 22 2018"
    }
})
//wxml
<text>date</text>    //会显示Oct 22 2018

假如说文档数据是从服务器请求来的。
一般数据放到onLoad函数中,在页面初始化的同时向服务器请求数据。

//傻瓜版数据平铺渲染,下面会介绍for循环方式绑定。

//js:
Page({
  data:{

  },
  onLoad:function(){
    //假如说这样一段数据是从服务器获取来的
    var local_database = [
      {
        date: "Sep 18 2016",
        title: "文章标题",
        imgSrc: "/images/post/crab.png",
        avatar: "/images/avatar/1.png",
        content: "文章简短文字介绍",
        reading: "112",
        collection: "96",
        headImgSrc: "/images/post/crab.png",
        author: "吴彦祖",
        dateTime: "24小时前",
        detail: "文章详细内容介绍",
        postId: 0,
        music: {
        url: "https://xxx/c957/ddf46174d597d368111db3ff9fbdaa7a.mp3",
        title: "当你老了 (Live)-李健",
        coverImg: "http://p1.music.126.net/93vWfsA7gvpBK20Ify82bw==/2908208256687521.jpg"
      }
    },
    要把数据全都绑定到wxml文件中
    所以要把local_database放到上面的data:{}结构体中,作为data的一个属性
    使用小程序提供的setData语法:
    this.setData(local_database)  //相当于把local_database 放到data:{}
})

//wxml:
<view class='post-container'>
    <view class='post-author-date'>
      <image class='post-author' src='{{avatar}}'></image>
      <text class='post-date'>{{date}}</text>
    </view>
    <text class='post-title'>{{title}}</text>
    <image class='post-image' src='{{imgSrc}}'></image>
    <text class='post-content'>{{content}}</text>
    <view class='post-like'>
      <image class='post-like-image' src='/images/icon/chat.png'></image>
      <text class='post-like-font'>{{collection}}</text>
      <image class='post-like-image' src='/images/icon/view.png'></image>
      <text class='post-like-font'>{{reading}}</text>
    </view>
  </view>

判断&&渲染

<text wx:if="{{true}}"></text>
wx:for 渲染

//js:
Page({
  data:{

  },
  onLoad:function(options){
    var posts_content = [
      {
        date: "Oct 23 2018",
        title: "战争与松脂",
        imgSrc: "/images/post/crab.png",
        avatar: "/images/avatar/1.png",
        content: "这是一部关于会议、艺术、爱与战争的杰出著作。",
        reading: "112",
        collection: "96",
      },
      {
        date: "Oct 23 2018",
        title: "第二十二条军规",
        imgSrc: "/images/post/xx.png",
        avatar: "/images/avatar/2.png",
        content: "《第二十二条军规》是一部严肃的、讽刺性极强的小说。",
        reading: "162",
        collection: "126",
      },
    ]
    //错误示范 
    this.setData(posts_content)  //传递到data中  
    //在wxml中绑定posts_content时,他找不到这个。
    //因为它是把posts_content里面的数据拿出来平铺到data中,自然没有posts_content
    
    正确思路,给他起个名字
     this.setData({
        posts_key: posts_content
    }) 
    //相当于在data:{
    //    posts_key:[  ]  //数据绑定到这里
    //},
})

<block wx:for="{{posts_key}}" wx:for-item="item">  
    // wx:for="{{posts_content}}" 绑定上方变量
    // wx:for-item="item" 定义子元素代称,子元素叫做item
    // wx:for-item="item" 其实也可以不用指定,不写也可以使用,for循环默认子元素就是item

    //wx:for-index="idx" 想知道一个子元素序号是多少;{{idx}}输出
    //wx:for-index="idx" 其实也可以不用指定,默认是 {{index}}
    <view class="container">
        <view>
              <image src="{{item.avatar}}" />  // 绑定头像
              <text>{{item.date}}</text>  // 绑定日期
        </view>
        <text>{{item.title}}</text>  //绑定标题
        <image src="{{item.imgSrc}}" />  //绑定封面
        <text>{{item.content}}</text>  //绑定文本描述
        <view>
             <image src="xxx/chat.png" />
             <text>{{item.collection}}</text>  //绑定收藏人数
             <image src="xxx/view.png" />
             <text>{{item.reading}}</text> //绑定预览人数
        </view>
    </view>
</block>
页面跳转

用到的两个知识点

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

小程序有个默认的规定
比如使用tap事件,做一个tap事件绑定,在前面加bind
上面的事件,当做事件绑定的时候,都需要在前面加上bind

<view bindtap="onTap"></view> 
点击的时候立刻执行这个函数

js:
onTap:function(){
    wx.navigateTo({
        url:"../posts/post"   //跳转到post页面
    })
}


>>>>>>>>>>>>>>>>>>>

模板跳转:
<template catchtap="onTap"></template >   
// 把跳转绑定到template上是没有效果的
// 原因:template就是一个占位符,当页面文件编译之后,template就消失了。
// 代替的是一些template里面的一些元素

解决办法:
在template上面绑定一个view
<block wx:for="{{postList}}" wx:for-item="item" wx:for-index="idx">
  <view catchtap="onPostTap" >     // ☜
     <template is="postItem" data="{{...item}}" />  
  </view>                          // ☜
</block>
冒泡与非冒泡

<view bindtap="onTap">
    <text bindtap="onTextTap"></text>
</view>

//JS:
Page({
    onTap:function(event){
        console.log("execute onTap")
    },
    onTextTap:function(event){
        console.log("execute onTextTap")
    },
})


//输出:
点击<text bindtap="onTextTap"></text>时:
execute onTextTap  //先输出文字
execute onTap  //再输出外面边框

点击父级<view bindtap="onTap"></view>时:
execute onTap  //只输出外面边框

点击子元素,如果父级有监听函数,父级函数也会执行,这就是冒泡。
阻止事件冒泡:
<view bindtap="onTap">
    <text catchtap="onTextTap">文本</text>  //注意:catchtap
</view>

//JS:
Page({
    onTap:function(event){
        console.log("execute onTap")
    },
    onTextTap:function(event){
        console.log("execute onTextTap")
    },
})

点击<text catchtap="onTextTap">文本</text>时:
execute onTextTap  //只输出文字

常见的touch、tap等大部分都是属于冒泡事件。
只有一些为数极少的如<form/>的submit事件<input />的input事件<scroll-view/>的scroll事件等是非冒泡事件。

将业务数据分离到单独的数据文件夹

// posts-data.js

var local_database = [
  {
    date: "Oct 23 2018",
    title: "红鞋子",
    imgSrc: "/images/post/1.png",
    avatar: "/images/avatar/1.png",
    content: "一只红鞋子总是想念着另一只红鞋子,想啊,想啊,想有一个家,一个暖暖的、亮亮的家。",
    reading: "112",
    collection: "96",
    headImgSrc: "/images/post/crab.png",
    author: "明天吧",
    dateTime: "1小时前",
    detail: "红鞋子,我想做一片绿叶,桥那边,月亮是块大烙饼,开满蒲公英的地方...",
    postId: 0,
    music: {
      url: "https://xxx/ddf46174d5f9fbdaa7a.mp3",
      title: "当你老了 (Live)-李健",
      coverImg: "http://xx/2908208256687521.jpg"
    }
  }, {
    date: "Oct 22 2018",
    title: "月亮和六便士",
    imgSrc: "/images/post/2.png",
    avatar: "/images/avatar/2.png",
    content: "《月亮和六便士》中,“我”是伦敦怀才不遇的作家",
    reading: "112",
    collection: "96",
    headImgSrc: "/images/post/crab.png",
    author: "特暖暖的",
    dateTime: "2小时前",
    detail: "《月亮和六便士》中,“我”是伦敦怀才不遇的作家,偶然间认识了一位证券经纪人,
              对方在人届中年后突然响应内心的呼唤,离经叛道舍弃一切,先是奔赴巴黎,后又
              到南太平洋的塔希提岛与土著人一起生活,全身心投入绘画,并在死后声名大噪。",
    postId: 1,
    music: {
      url: "https://xxx/ddf46174d5f9fbdaa7a.mp3",
      title: "水流众生 - 李健",
      coverImg: "http://xx/2908208256687521.jpg"
    }
  },
]

》》》》》》该位置定义出口

如何引入数据

// 使用require方法加载js模块儿文件
首先要在 posts-data.js 上面的位置定义一个出口
module.exports = {
  postList: local_database  //给对象定义一个名字
}
一定要定义,实际上和node.js中的模块输出是一样的


注意:上面不是只能指定一个postList属性变量,
如果还有其他的,可以在后面继续添加,如下:
var a = "2"
module.exports = {
  postList: local_database,
  a_key: a
}



>>>>>>>>>>>> 
在其他页面引入
比如在pages/posts/post.js引入变量

//post.js 页头加入
var postsData = require('../../data/posts-data.js')  
//必须使用相对路径
Page({
    data:{
    },
    onLoad:function(options){
        this.setData({
            posts_key:postsData.postList
        });
        /小程序总是会读取data对象来做数据绑定
        /而读取数据是在onLoad事件执行之后发生的。
    }
})
template模板的使用

新建一个模板

模板文件只需要wxmlwxss页面

编写模板
<template name="postItem">
   <view>
      <image src="{{item.avatar}}" />  // 绑定头像
      <text>{{item.date}}</text>  // 绑定日期
   </view>
   <text>{{item.title}}</text>  //绑定标题
   <image src="{{item.imgSrc}}" />  //绑定封面
   <text>{{item.content}}</text>  //绑定文本描述
   <view>
     <image src="xxx/chat.png" />
     <text>{{item.collection}}</text>  //绑定收藏人数
     <image src="xxx/view.png" />
     <text>{{item.reading}}</text> //绑定预览人数
  </view>
</template>


使用template模板:

引入结构:
-----------------------------------------------------
在pages/posts/post.wxml下引入template模板
<import src="post-item/post-item-template.wxml" />

<template is="postItem" />

如果template在for循环中,需要传递data数据:
<block wx:for="{{postList}}" wx:for-item="item" wx:for-index="idx">
    <template is="postItem" data="{{item}}" />  
    //data="{{item}}"  使用数据子元素填充
</block>


引入样式:
-----------------------------------------------------
还需要在pages/posts/post.wxss下引入template模板的样式
@import "post-item/post-item-template.wxss";

模板引用数据不使用item做前缀的方法:

注意:上面的template 模板中的数据添加了item作为前缀。{{item.date}}
如果不添加item前缀直接{{date}}绑定数据:
<template name="postItem">
   <view>
      <image src="{{avatar}}" />  // 绑定头像
      <text>{{date}}</text>  // 绑定日期
   </view>
   <text>{{title}}</text>  //绑定标题
   <image src="{{imgSrc}}" />  //绑定封面
   <text>{{content}}</text>  //绑定文本描述
   <view>
     <image src="xxx/chat.png" />
     <text>{{collection}}</text>  //绑定收藏人数
     <image src="xxx/view.png" />
     <text>{{reading}}</text> //绑定预览人数
  </view>
</template>

在引用template在for循环中,需要传递data数据:
<block wx:for="{{postList}}" wx:for-item="item" wx:for-index="idx">
    <template is="postItem" data="{{...item}}" />    注意data="{{...item}}"
</block>

{{item}}与{{...item}}的区别:
{{item}} 指的是☞:数据数组上面的一个子元素,这个子元素是一个JavaScript对象。
{{...item}} 指的是☞:把对象展开平铺了,都是直接的属性名,所以绑定的时候不需要指定item了。
根据ID跳转不同页面

绑定页面ID:

// posts-data.js

var local_database = [
  {
    date: "Oct 23 2018",
    title: "红鞋子",
    imgSrc: "/images/post/1.png",
    avatar: "/images/avatar/1.png",
    content: "一只红鞋子总是想念着另一只红鞋子,想啊,想啊,想有一个家,一个暖暖的、亮亮的家。",
    reading: "112",
    collection: "96",
    headImgSrc: "/images/post/crab.png",
    author: "明天吧",
    dateTime: "1小时前",
    detail: "红鞋子,我想做一片绿叶,桥那边,月亮是块大烙饼,开满蒲公英的地方...",
    postId: 0,
    music: {
      url: "https://xxx/ddf46174d5f9fbdaa7a.mp3",
      title: "当你老了 (Live)-李健",
      coverImg: "http://xx/2908208256687521.jpg"
    }
  }, {
    date: "Oct 22 2018",
    title: "月亮和六便士",
    imgSrc: "/images/post/2.png",
    avatar: "/images/avatar/2.png",
    content: "《月亮和六便士》中,“我”是伦敦怀才不遇的作家",
    reading: "112",
    collection: "96",
    headImgSrc: "/images/post/crab.png",
    author: "特暖暖的",
    dateTime: "2小时前",
    detail: "《月亮和六便士》中,“我”是伦敦怀才不遇的作家,偶然间认识了一位证券经纪人,
              对方在人届中年后突然响应内心的呼唤,离经叛道舍弃一切,先是奔赴巴黎,后又
              到南太平洋的塔希提岛与土著人一起生活,全身心投入绘画,并在死后声名大噪。",
    postId: 1,
    music: {
      url: "https://xxx/ddf46174d5f9fbdaa7a.mp3",
      title: "水流众生 - 李健",
      coverImg: "http://xx/2908208256687521.jpg"
    }
  },
]

module.exports = {
  postList: local_database
}

以上是获取的数据,注意数组每个子对象都有一个`postId`来记录id,以示区分
页面如何取到该ID?
<block wx:for="{{postList}}" wx:for-item="item" wx:for-index="idx">
  <view catchtap="onPostTap"  data-postId="{{item.postId}}">    
  // ☝ data-postId="{{item.postId}}">  
  // data-xxx  data-前缀是必须 ,后面的可以自己起名
  //{{item}} 就是数据单个对象,可以通过item.postId取到postId值
  //当然不一定非要叫data-postId,随意起名data-postName也可以
     <template is="postItem" data="{{...item}}" />  
  </view>
</block>

绑定后的id

获取页面ID:

绑定ID之后,在当前脚本文件获取点击元素的postId
js:
Page({
    data:{
    
    },
    onLoad:function(){
        
    },
    onPostTap:function(event){
          var postId = event.currentTarget.dataset.postid;

          event 事件给的框架对象
          currentTarget 当前鼠标点击的事件,对应上面的绑定onPostTap的view

          dataset  所有自定义数据的集合
          上面只定义了一个<view catchtap="onPostTap"  data-postId="{{item.postId}}">   
          所有他只有一个postId
    }
})
  举例多个自定义数据
  <view catchtap="onPostTap"  data-postId="{{item.postId}}" data-post-name="name">    
      <template is="postItem" data="{{...item}}" />  
  </view>

上面自定义了两个数据 data-postId="{{item.postId}}" data-post-name="name"
输出时,只有`-`后面的字母大写。其他全部转为小写。


根据页面ID跳转:

<view catchtap="onPostTap"  data-postId="{{item.postId}}">    
  <template is="postItem" data="{{...item}}" />  
</view>

post.js:(传递)
var postsData = require('../../data/posts-data.js');

Page({
    data:{
    
    },
    onLoad:function(){
        
    },
    onPostTap:function(event){
          var postId = event.currentTarget.dataset.postid;  //取到ID
          wx.navigateTo({  //使用它的原因,是详情页需要返回按钮
              url:"post-detail/post-detail?id="+postId 
          })
    }
})

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
post-detail.js:(接收)
Page({
    onLoad:function(option){
        var postId = option.id  //这里的id和上面?id对应
    }
})
post-detail.js可以拿到id后,需要从posts-data.js找到对应的数据,绑定到post-detail.wxml中

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
post-detail.js:
先引入数据
var postsData = require('../../../data/posts-data.js')

Page({
    onLoad:function(option){
        var postId = option.id
        var postData = postsData.postList[postId]  // ☜ 新增该行
        // 获取对应数据  从引入数据文件postsData.postList获取对应数据
        this.setData({
            postData: postData
        })
    }
})

post-detail.wxml:
<view>
    <image src="{{postData.headImgSrc}}" />
    <view>
        <image src="{{postData.avatar}}" />
        <text>{{postData.author}}</text>
        <text>{{postData.dateTime}}</text>
    </view>
    <text>{{postData.title}}</text>
    <text>{{postData.detail}}</text>
</view>
绑定数据时:需要加前缀postData  因为数据导出时,绑定到postData上
postsData
缓存

收藏功能:
当用户点击收藏按钮时,小程序应该向服务器发送一个收藏或取消收藏的状态指令,
然后由服务器把用户的收藏状态指令记录下来,在下次初始化页面的时候由服务器
把数据传递到客户端这里来,并且表现出收藏的按钮是收藏的还是未收藏的状态。

但是目前没有服务器,所以需要一个服务器替代品,可以永久记录文章收藏状态的一个地方。
小程序缓存就具备这样的功能。

如何设置一个缓存:

Page({
    onLoad:function(){

    },
    // wx.setStorage('key','盖伦')     //☜新建一个异步缓存
    wx.setStorageSync('key','盖伦')      //☜新建一个同步缓存
    可以使用开发者工具的Storage查看缓存。(第一次需要进入页面创建缓存)
})

如何修改一个缓存:

Page({
    onLoad:function(){

    },
    wx.setStorageSync('key','盖伦')      //☜新建一个同步缓存
    wx.setStorageSync('key',{    //☜修改一个缓存  key需要指定哪个缓存
        game:"英雄联盟",
        developer:"拳头"
    })
})

如何获取一个缓存:

<image catchtap="onCollectionTap" src="xx/xxx.png" />
onCollectionTap:function(event){
    var game = wx.getStorageSync('key')  //☜获取键值为key的同步缓存 
    console.log(game)
}
//点击输出 {game:"英雄联盟",developer:"拳头"}

如何删除一个指定key缓存:

<image catchtap="onCollectionTap" src="xx/xxx.png" />
<image catchtap="onRemoveTap" src="xx/xxy.png" />
onRemoveTap:function(event){
    wx.removeStorageSync('key')   //☜删除键值为key的同步缓存 
    wx.clearStorageSync();   //☜删除所有缓存 
}

图片两种状态的切换

jQ: 拿到image的dom,通过脚本文件动态控制图片的src,实现动态的轮转。

小程序:通过数据绑定的方法或者使用小程序提供的组件if else,实现图片的显示隐藏和动态切换。

小程序提倡数据优先的思想,必须通过动态绑定来实现。这种思想比较接近angular.js

使用if else的方法:

//post-detail.wxml:
<image wx:if="{{collected}}" src="xxx/collection.png" />  /收藏的图片
<image wx:else src="xxx/collection-anti.png" />  /未收藏的图片

在脚本中控制{{collected}}的真假,从而决定到底显示哪一张图片。
变量{{collected}}为真的话
显示collection.png图片,否则显示collection-anti.png

使用数据绑定三元运算方法:

<image catchtap = "onMusicTap" class="audio" 
src ="{{isPlayingMusic? '/images/music-stop.png' : 'images/music-start.png'}}"

使用缓存控制图片显隐实现收藏效果:

//post-detail.wxml:
<image wx:if="{{collected}}" catchtap="onColletionTap" src="xxx/collection.png" /> 
<image wx:else catchtap="onColletionTap" src="xxx/collection-anti.png" />
//两张图片绑定catchtap="onColletionTap"

//post-detail.js
Page({
    data:{
    
    },
    onLoad:function(option){
        var postId = option.id;   //当前文章的id

        为了供下面的函数onColletionTap可以使用postId
        需要把值传递给data
        this.setData({
            currentPostId: postId 
        })

        //思路,存放记录文章收藏状态
        /*var postsCollected={
            1: "true",
            2: "false",
            3: "true",
            ...
        }*/
        var postsCollected = wx.getStorageSync('posts_collected')  
        //读取所有文章缓存状态
        if(postsCollected){  //缓存是存在的,才去读取。
            var postcollected = postsCollected[postId]
            //获取当前页面的收藏状态
            if(postsCollected){  //缓存是存在的
              this.setData({
                  collected: postcollected; 
                  // 把detail文章是不是收藏,绑定到了collected上
                  //从而在页面可以读取到它,控制显隐状态
              })
            }
        }else{   //如果缓存不存在,需要一个创建缓存的流程
            var postsCollected = {};   //创建空对象
            postsCollected[postId] = false;  //把当前的状态设置为未收藏
            wx.setStorageSync('posts_collected',postsCollected)    //设置该缓存
        }
    },
    onColletionTap:function(event){  //当用户点击收藏和取消收藏
        //首先获取缓存
        var postsCollected = wx.getStorageSync('posts_collected')
        //查看当前页面收藏状态
        var postCollected = postsCollected[this.data.currentPostId]
        //currentPostId可以使用因为上面函数把值传递到了data中
        postCollected = !postCollected;  //获取到状态后取反。
        // 收藏的变成未收藏的,未收藏的变为收藏
        postsCollected[this.data.currentPostId] = postCollected;
        //更新当前的状态值
        wx.setStorageSync('posts_collected',postsCollected);
        //对当前的缓存进行更新(更新文章是否的缓存值)
        this.setData({
            collected: postCollected  //更新数据绑定
        })
    }
})
小程序弹框(交互反馈)

为了使用时候更方便知道编写位置就在上面代码基础上编写
Page({
    data:{
    },
    onColletionTap:function(event){  //当用户点击收藏和取消收藏
       这段代码可忽视
       >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        //首先获取缓存
        var postsCollected = wx.getStorageSync('posts_collected')
        //查看当前页面收藏状态
        var postCollected = postsCollected[this.data.currentPostId]
        //currentPostId可以使用因为上面函数把值传递到了data中
        postCollected = !postCollected;  //获取到状态后取反。
        // 收藏的变成未收藏的,未收藏的变为收藏
        postsCollected[this.data.currentPostId] = postCollected;
        //更新当前的状态值
        wx.setStorageSync('posts_collected',postsCollected);
        //对当前的缓存进行更新(更新文章是否的缓存值)
        this.setData({
            collected: postCollected  //更新数据绑定
        })
        >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        已上代码可忽视
  
        //交互
        wx.showToast({  //不需要用户操作,会自动消失
            //title:"收藏成功"   这种写法不是动态的
            title:postCollected?"收藏成功":"取消成功",
            duration:"1000",  //消失时间 1s 默认1500
             icon:"success"  //success loading
        })

        相反的办法  隐藏
        wx.hideToast()
        setTimeout(function(){
            wx.hideToast()
        },2000)
    }
})
小程序弹框(操作反馈)

模态弹框,不主动关闭就会一直存在。
wx.showModal({
    title:"收藏",
    content:"是否收藏该文章",  
    showCancel:"true",  //显示取消按钮
    cancelText:"不收藏",  //取消按钮文字
    cancelColor:"#333",  //取消按钮文字颜色
    confirmText:"收藏",  //确定按钮
    confirmColor:"#405f80"  //确定按钮文字颜色
})


>>>>>>>>>>>>>>>>>>>>
符合项目的弹框
小程序弹框(交互反馈[底部])

出现在底部的弹框
onShareTap:function(event){
    var itemList = [
            "分享给微信好友",
             "分享到朋友圈",
             "分享到QQ",
             "分享到微博"
    ]
    wx.showActionSheet({
        itemList:itemList,
        itemColor: "#405f80",  //设置颜色
        success:function(res){
            / res.cancel  用户是不是点击了取消按钮 
            / res.tapIndex  数组元素的序号,从0开始
            wx.showModal({
                title: "用户分享到了"+itemList[res.tapIndex],
                content: "现在无法实现分享功能,小程序不支持"
            })
        }
    })
}
音乐播放

歌曲和封面图片等,都必须使用在线链接,因为小程序有大小限制。
小程序有两种实现音乐播放的方法:一种是组件,一种API

API:

  • wx.playBackgroundAudio() 播放
  • wx.pauseBackgroundAudio() 暂停
  • wx.stopBackgroundAudio() 停止
  • wx.onBackgroundAudioPlay(callback) 监听音乐播放
  • wx.onBackgroundAudioPause(callback) 监听音乐暂停
  • wx.onBackgroundAudioStop(callback) 监听音乐停止
<image catchtap = "onMusicTap" class="audio" 
src ="{{isPlayingMusic? '/images/music-stop.png' : 'images/music-start.png'}}"


var postsData = require('../../data/posts-data.js')  //引入数据
Page({
  data:{
    isPlayingMusic:false  //记录音乐播放状态
    定不定义都行,就是强迫症
  }
  onMusicTap:function(event){
    var currentPostId = this.data.currentPostId;  //获取当前页面ID

    记录播放状态和文章收藏一样
    var isPlayingMusic = this.data.isPlayingMusic; //拿到播放状态
    
    要根据当前的音乐播放状态来考虑是不是暂停,正在播放才能考虑是不是暂停
    if(isPlayingMusic){  //如果是真
        wx.pauseBackgroundAudio();  音乐暂停
        this.setData({  //暂停音乐之后
            isPlayingMusic: false;
        })
    }else{
      wx.playBackgroundAudio({
          //常用的 地址 标题 封面图片
          dataUrl:postsData.postList[currentPostId].music.url,
          title:postsData.postList[currentPostId].music.title,
          coverImgUrl:postsData.postList[currentPostId].music.coverImg
      })
      this.setData({  //播放音乐之后
          isPlayingMusic: true;
      })
  }
})

解决音乐总控开关和音乐不同步问题

最好写在onload里面:
//上面的js文件:
Page({
    data:{
    
    },
    onLoad:function(){
        ...
        var that = this;
        wx.onBackgroundAudioPlay(function(){  //监听到音乐播放
            that.setData({
                isPlayingMusic: true;  //设置状态
            })
        }),
        wx.onBackgroundAudioPause(function(){  //监听到音乐暂停
            that.setData({
                isPlayingMusic: false;  //设置状态
            })
        })
    }

})

解决BUG:播放音乐之后,再进入还是未播放状态

把变量存到全局
页面的js实现页面生命周期控制
全局app.js设置的是关于应用程序生命周期的一些方法和属性
app.js
App({
    globalData:{  //专门保存全局的一些数据
        g_isPlayingMusic: false,
    }

    >>>>>>>>
    APP的生命周期函数只有三个
    onLaunch:function(){  //应用程序启动
        console.log("onLaunch")
    },
    onShow:function(){  //应用程序显示
        console.log("onShow")
    },
    onHide:function(){  //隐藏到后台,页面不会销毁
        console.log("onHide")
    }
    >>>>>>>>
    执行顺序: onLaunch  onShow

    onHide没有执行的原因
    在ios上可以按Home键,把应用程序放到后台去,这个时候会执行onHide方法

    当隐藏之后onLaunch是不会执行的,只会执行onShow
    关闭了应用程序之后再启动才会执行onLaunch
})

在post-detail.js中尝试拿到变量 `g_isPlayingMusic`
//post-detail.js
var app = getApp();

Page({
    data:{
        isPlayingMusic:false,
    },
    onLoad:function(option){
       // var globalData = app.globalData;  //拿到全局globalData
      ...
      if(app.globalData.g_isPlayingMusic){
          this.setData({
                isPlayingMusic: true;  
                //如果全局记录播放,当前改成播放状态
            })
      }
      ...  
      wx.onBackgroundAudioPlay(function(){  //监听到音乐播放
          that.setData({
              isPlayingMusic: true;  //设置状态
          })
          app.globalData.g_isPlayingMusic = true;
          //改变全局播放状态
      });
      wx.onBackgroundAudioPause(function(){  //监听到音乐暂停
          that.setData({
              isPlayingMusic: false;  //设置状态
          })
          app.globalData.g_isPlayingMusic = false;
          //同步全局播放状态
      })
    }
})

解决BUG:播放音乐之后,切换另外页面歌曲是正在播放

造成这个问题的原因:
在页面中播放歌曲,把全局歌曲播放状态设置了true
换另外页面的时候,又执行onload函数
获取的是全局播放状态

if(app.globalData.g_isPlayingMusic){
    this.setData({
        isPlayingMusic: true;  
        //如果全局记录播放,当前改成播放状态
    })
}

全局状态为true,从而导致了图标状态为true
实际上,全局的g_isPlayingMusic指的不是当前页面的播放状态
而是上一篇播放页面的状态,g_isPlayingMusic指代不对。

解决办法:
现在需要另外的一个变量,来记录当前的音乐播放指代是哪个页面;
而且这样的属性也应该是全局的。
在APP中再设置一个变量:
// app.js:
App({
    globalData:{
        g_isPlayingMusic: false,
        g_currentMusicPostId:null  //新增
    }
})

//g_isPlayingMusic 音乐是不是被播放
//g_currentMusicPostId 哪一个音乐正在被播放

//post-detail.js:
wx.onBackgroundAudioPlay(function(){ 
    that.setData({
        isPlayingMusic: true;
    })
    app.globalData.g_isPlayingMusic = true;
    app.globalData.g_currentMusicPostId = this.data.currentPostId
    //记录ID
});
wx.onBackgroundAudioPause(function(){
    that.setData({
        isPlayingMusic: false;
    })
    app.globalData.g_isPlayingMusic = false;
    app.globalData.g_currentMusicPostId = null;
    //音乐暂停的时候把当前播放的音乐给清空
})

还要判断一下:
if(app.globalData.g_isPlayingMusic 
    && app.globalData.g_currentMusicPostId === postId){
    //判断是不是当前ID
    this.setData({
        isPlayingMusic: true;  
    })
}
Tab选项卡

//app.json:
{
    "page":[],
    "window":{},
    "tabBar":{
       "list":[
            {
                "pagePath":"pages/index/index",
                "text":"首页"
            },{
                "pagePath":"pages/logs/logs",
                "text":"日志"
            }
        ]   
     }
}

没有写的必要,在app.json中输入tabBar回车,会自动补全。
图标、字体、颜色等参考API就搞定
要跳转的页面(想要出现tabbar的页面),必须放到list下面。
template的使用

**组件编写 
//demo-template.wxml:
<template name="demoTemplate">
  ...  
</template>

**引入template
<import src="../demo-template/demo-template.wxml" />
<template is="demoTemplate" />

**引入template样式
@import "../demo-template/demo-template.wxss";
获取在线接口数据

movies.js:
Page({
    onLoad:function(){
        wx.request({
          url:'http://t.yushu.im/v2/movie/top250',
          data:{},  //用来向服务器提交数据
          method:'GET'  //OPTIONS, GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT
          // header:{}  设置请求的header
          success:function(res){
              //success
              console.log(res)
          },
          fail:function(){
              //fail  //请求超时,网断了,走这个(网络建立没有成功)
              console.log("failed")
          },
          complete:function(){
              //complete
          }
        })
    }
})

输出数据

优化获取数据:

// app.js
App({
    globalData:{
      ...
      doubanBase: "http://t.yushu.im"
    }
})

// movie.js:
var app = getApp();
Page({
    onLoad:function(event){
        var inTheatersUrl = app.globalData.doubanBase + "/v2/movie/in_theaters";
        var comingSoonUrl = app.globalData.doubanBase + "/v2/movie/coming_soon";
        var top250Url = app.globalData.doubanBase + "/v2/movie/top250";

        //发送请求,获取数据
        this.getMovieListData(inTheatersUrl);  
        this.getMovieListData(comingSoonUrl);
        this.getMovieListData(top250Url);
    },
    getMovieListData:function(url){
        wx.request({
          url: url,
          method:'GET'
          header:{  //新版本更新后可以忽略
              "Content-Type":""
          },
          success:function(res){
              //success
              console.log(res)
          },
          fail:function(){
              console.log(error)
          }
    }
})


//设置只获取三条数据
var inTheatersUrl = app.globalData.doubanBase 
   +"/v2/movie/in_theaters"+"?start=0&count=3";
//从第0个开始,只取三条数据(根据文档请求属性来的)
wx.request是异步方法
var data = this.getMovieListData(inTheatersUrl)是获取不到数据的 

数据绑定

// movie.js:
Page({
    data:{
        inTheaters:{},
        comingSoon:{},
        top250:{}
        //如果不先定义,下面的方法都是异步的
        //初始化的时候,找不到变量,绑定的就会出错
        //如果是对象的话,一定要给他空值
    },
    onLoad:function(event){
        var inTheatersUrl = app.globalData.doubanBase + "/v2/movie/in_theaters"+"?start=0&count=3";
        var comingSoonUrl = app.globalData.doubanBase + "/v2/movie/coming_soon"+"?start=0&count=3";
        var top250Url = app.globalData.doubanBase + "/v2/movie/top250"+"?start=0&count=3";

        this.getMovieListData(inTheatersUrl,"inTheaters","正在热映");   //传递一个key识别 
        this.getMovieListData(comingSoonUrl,"comingSoon","即将上映");
        this.getMovieListData(top250Url,"top250","豆瓣Top250");
    },
     getMovieListData:function(url,settedKey,categoryTitle){
        var that = this;
        wx.request({
          url: url,
          method:'GET'
          header:{  //新版本更新后可以忽略
              "Content-Type":""
          },
          success:function(res){
              //success
              console.log(res)
              that.processDoubanData(res.data,settedKey,categoryTitle)  //处理数据
          },
          fail:function(){
              console.log(error)
          }
    },
    processDoubanData:function(moviesDouban,settedKey,categoryTitle){
          var movies = [];
          for(var idx in moviesDouban.subjects){
                var subject = movieDouban.subjects[idx];
                var title = subject.title;
                if(title.length>=6){  //如果字数超过6个 截取一下
                    title = title.substring(0,6)+"...";
                }
                var temp = {
                    stars:util.convertToStarsArray(subject.rating.stars),
                    title:title;
                    average:subject.rating.average, //综合评分
                    coverageUrl: subject.images.large, //大图
                    movieId:subject.id  //方便跳转电影详情
                }
                movies.push(temp)
          }

          //生成key的对象
          var readyData = {};
          readyData[settedKey] = {
              categoryTitle:categoryTitle,  //标题
              movies:movies
          }
          this.setData(readyData)
    }
})

// 绑定数据
<view class="container">
    <view>
        <template is="movieListTemplate" data="{{...inTheaters}}"/>
    </view>
    <view>
        <template is="movieListTemplate" data="{{...comingSoon}}"/>
    </view>
    <view>
        <template is="movieListTemplate" data="{{...top250}}"/>
    </view>
</view>

movieListTemplate:
<template name="movieListTemplate">
  <view>
    <text>{{categoryTitle}}</text>  //标题数据
    <view>
        <text>更多</text>
         <image src="/images/icon/arrow-right.png" />
    </view>
  </view>
  <view class="movies-container">
    <block wx:for="{{movies}}" wx:for-item="movie">
        <template is="movieTemplate" data="{{...movie}}" />
    </block>
  </view>
</template>

movieTemplate:
<template name="movieTemplate">
    <view class="movie-container">
        <image class="movie-img" src="{{coverageUrl}}" />
        <text class="movie-title">{{title}}</text>
        <template is="starsTemplate" data="{{stars:stars,score:average}}"/>
    </view>
</template>

starsTemplate:
<template name="starsTemplate">
    <view class="stars-container">
        <view class="stars">
          //循环生成星星
          <block wx:for="{{stars}}" wx:for-item="i">
            <image wx:if="{{i}}" src="/images/icon/star.png" />
            <image wx:else src="/images/icon/none-star.png" />
          </block>
            <!--<image src="/images/icon/star.png" />
            <image src="/images/icon/star.png" />
            <image src="/images/icon/star.png" />
            <image src="/images/icon/star.png" />
            <image src="/images/icon/star.png" />-->
        </view>
        <text class="star-score">{{average}}</text>
    </view>
</template>

评分功能实现


豆瓣的评分 50=5颗星  35=3星半  45=4星半
简化版,不考虑0.5星的
把评分转换成数组:
5星: [1,1,1,1,1]
3星: [1,1,1,0,0]

utils/util.js 存放公共方法:
//util.js:
function convertToStarsArray(stars){
    var num = stars.toString().substring(0,1);
    var array = [];
    for(var i =1;i<=5;i++){
        if(i<=num){
            array.push(1);
        }else{
            array.push(0);
        }
    }
    return array;
}
module.exports = {
    convertToStarsArray:convertToStarsArray  //输出
}

//movie.js
var util = require('../../utils/util.js')  //引用


如果要写半星:
3.5星 = [1,1,1,2,0]  用2代替半星
<image wx:if="{{i==1}}" />
<image wx:elif="{{i==2}}" />
<image wx:else />
更多电影列表

跳转页:
// movie-list-template: 
<view class="movie-head">
  <text class="slogan">{{categoryTitle}}</text>
  <view catchtap="onMoreTap" class="more" data-category="{{categoryTitle}}">
  //绑定,在onMoreTap就能获取到
    <text class="more-text">更多</text>
    <image class="more-img" src="/images/icon/arrow-right.png">
  </view>
</view>

onMoreTap:function(event){
    var category = event.currentTarget.dataset.category;
    wx.navigateTo({
        url:"more-movie/more-movie?category="+category
        //跳转url
    })
}

//more-movie.js:  获取category
Page({
    data:{},
    onLoad:function(options){
        var category = options.category;
    }
})

动态设置导航栏标题方法

Page({
    data:{
        navigateTitle:"",
    },
    onLoad:function(options){
        var category = options.category;
        this.setData({
            navigateTitle:category 
        })
    },
    onReady:function(event){
        wx.setNavigationBarTitle({
            title:this.data.navigateTitle,
            success:function(res){
                //success
            }
        })
    }
})

//设置导航标题不能写在onload
onload指的是页面初始化,页面初始化过程中是不应该操作和UI相关的东西的,因为页面还没有正式生成

onReady当页面已经准备完毕了,才执行onReady函数

onShow 也不可以使用,他们之间的执行顺序onloady onShow onReady,
onReady表示页面已经渲染完成了,写在onShow里面会闪一下,然后被覆盖。

Q/A整理


Q:1. 小程序单位rpx是什么,如何使用?
A:小程序使用rpx当做单位。他的功能类似于动态rem
rpx指的不是物理像素点,指的是逻辑像素点。
比如iPhone6的宽度是750,但在浏览器预览中宽度是375。
你根据设计稿上面的尺寸写400px;就会超过屏幕,因为它指的是物理像素。
如果使用400rpx。当屏幕375时,他会自动按照750的宽度改变比例。功能类似于移动端中的动态REM。会适应所有手机屏幕尺寸。

Q:2.小程序有哪些文件类型?

  • wxml 是编写小程序骨架的文件。
  • json 是编写小程序配置的文件。
  • js 是编写小程序行为的文件。
  • wxss 是编写小程序样式的文件。

Q:3.<text>标签有啥特质

  • 支持嵌套text
  • 支持 \n
  • 文字可以在手机上长按选中

Q:4.为什么做for循环渲染推荐使用<block>标签把内容包括起来?
答:<block>标签没有一个具体功能性作用,不像swiperimage等标签有实际作用,可以把他理解成一个括号,凡是在<block>标签里面代码都可以识别成一个整体。

Q:5.for循环有哪些指定参数?

  • wx:for="{{data}}"
    必须 指定循环数据
  • wx:for-item="item"
    非必须 不写默认为{{item}}
  • wx:for-index="idx"
    非必须 不写默认为{{index}}

Q:6.bindtap和catchtap的区别?

  • bindtap(事件冒泡)
    点击子元素,如果父级有监听函数,父级函数也会执行
  • catchtap(非冒泡)
    点击子元素,不管父级有没有监听函数,父级函数都不会执行

Q:7.微信小程序提供了哪些常用跳转API,各有什么功能?
A:区别:

  • wx.navigateTo 页面父子级跳转:
    当前页面没有关闭,后台运行,跳转的页面返回按钮
    注:小程序限制,层级最多五层;保证了小程序的简洁性。
  • wx.redirectTo 页面平行跳转:
    当前页面关闭,跳转的页面没有返回按钮
  • wx.switchTab
    只能跳转含有tabbar的页面,上面两种不能跳转有tabbar的页面。

A:相同点,参数相同:

  • url 存放跳转地址
  • success 当跳转成功之后会执行这个函数
  • fail 当跳转失败之后会执行这个函数
  • complete 无论成功还是失败都会执行这个函数

Q:8.数据绑定时{{item}}与{{...item}}的区别?
{{item}}指数组的单个对象数据,绑定数据时需要指定{{item.xxx}}
{{...item}}是把该单个对象数据平铺,不需要指定就可以直接使用对象属性,调用时{{xxx}}
Q:9.自定义属性是什么?
在组件的属性列表里面,凡是data-+"自定义单词"构成的属性,我们都称之为自定义属性。

自定义属性有一些规则:

  • 必须以data固定形式开头
  • 可以有若干个-连接若干个单词, 例:data-postId-name-dd

Q:10.缓存有哪些特性?
在小程序里,如果设置了一些缓存之后,不向用户主动提供wx.removeStorageSync('key')(单一删除)、wx.clearStorageSync()(全部删除)的方法,这个缓存是永久存在的。并没有失效期这个概念。

缓存上限不是以缓存个数来计算的,是以体积来计算的,缓存的上限最大不能超过10MB

小程序的缓存总共有
四类操作八种方法
每个操作同时具有同步和异步的方法

Q:11.wx.showToast和wx.showModal在逻辑上的区别?
wx.showToast 不需要用户确认,会自动消失
wx.showModal必须需要用户确认

Q:12.全局app.js和页面xx.js区别?

  • 全局app.js: App({})开头,控制的是关于应用程序生命周期的一些方法和属性
  • 页面.js: Page({})开头,实现页面生命周期控制

Q:13.如何引入template模板

  • 引入template:
    <import src="../demo-template/demo-template.wxml" />
    <template is="demoTemplate" />
  • 引入template样式:
    @import "../demo-template/demo-template.wxss";

Q:14.如何引入js文件

  • 输出
    module.exports = { xxx:xxx }
  • 引用
    var util = require('../../utils/util.js')

Q:15.如何引入全局App.js文件
var app = getApp();
Q:16.template模板中推荐使用什么路径?
推荐使用绝对路径,引入时可能嵌套层级不同,使用绝对路径可以解决路径错误问题。
Q:17.event的target和currentTarget有什么区别?
target指的是当前点击的组件;
currentTarget指的是事件捕获的组件;
Q:18.在线数据如何获得?

Q:19.多个数据绑定?
{{stars:stars,score:average}}
含义:stars和average在这里又重新组装成JavaScript对象,传递给内部。
Q:20.如何设置自定义属性,并且拿到它?
设置自定义属性:data-category="{{categoryTitle}}"
拿到当前自定义属性值:event.currentTarget.dataset.category;

推荐阅读更多精彩内容

  • 《微信小程序开发 入门与实践》 简介 最近有看完这本书,闲来无事整理个文档。 整理完成日期:2019/02/27。...
    webmrxu阅读 1,499评论 0 3
  • 因新工作主要负责微信小程序这一块,最近的重心就移到这一块,该博客是对微信小程序整体的整理归纳以及标明一些细节点,初...
    majun00阅读 4,325评论 0 7
  • 整理于网络 http://blog.poetries.top/2018/08/09/xiaochengxu-not...
    poetries阅读 6,747评论 1 33
  • 妹妹终于不再发烧了,早上喝了点粥,可以自己开心的玩会了。悬着的心总算能放下点了。 窗外已是零下,这一年中最冷的时候...
    悠然一夏阅读 18评论 0 0
  • 夜色 微微澜 灯光 黯黯然 我爱的他 是否会出现 和我一起漫步在这星星月光下 星泪溅漫 散出无尽的思念 徘徊于云月...
    酒浓情伤阅读 68评论 0 1