微信小程序之ES6与事项助手

【微信小程序】微信小程序 manifest.json 配置 ES6 转 ES5 开关:3步让你的代码兼容性更强 ES6(ECMAScript 6)是 JavaScript 的最新标准,引入了许多新的语法和特性,如箭头函数、模板字符串、解构赋值等。然而,不是所有的环境都支持 ES6,特别是旧版本的浏览器和一些特定的运行环境。为了确保代码的兼容性,我们需要将 ES6 代码转换为 ES5 代码。通过本文的详细讲解,相信你已经学会了如何在文件中配置微信小程序的 ES6 转 ES5 开关。这个小小的配置步骤能够显著提高代码的兼容性和稳定性。如果你有任何问题或需要进一步的帮助,欢迎随时留言交流。 阅读详情

好久没有写关于微信小程序的随笔了,其实是不知道写点什么好,之前的豆瓣图书和知乎日报已经把小程序的基础部分写的很详细了,高级部分的API有些还得不到IDE的调试支持。之前发表了知乎日报小例,有网友问我小程序有没有关于日历显示的组件,可以显示所有天数的,自己看了一遍,好像没有这个组件,所以打算那这个功能来练手,在准备期间,微信开发者工具已经升级了两三次,添加了部分功能和修改了部分功能,导致之前的例子的写法不兼容更新后的IDE,还得修改代码。随着小程序的不断更新,功能越来越完善,我想我也应该紧跟官方的升级步伐,这次的案例使用了IDE支持的ES6和新的API。


这次介绍的是一个比较简单的小应用事项助手,其实跟事项也不沾多少边,只是作为辅助功能,只有数据的添加和删除,主要内容是日历这块内容。日历组件在web应用中应用非常广泛,插件也非常丰富,但是小程序不支持传统的插件写法,而是以数据驱动内容。

大部分的日历选择器都是差不多的,能显示当前的年份、月份和天数,可以选择某天、某月或者某年,我们可以打开操作系统中自带的日历观察一番。

日历的布局大同小异,本次案例的布局也是中规中矩,比较传统,头部显示当前年份月份,头部的左右个显示一个翻页按钮,跳转到上一月和下一月,下半部分显示当月的天数列表,由于每月的天数可能不一样,列表的格数是固定的,所以当月的天数显示使用高亮,其余的使用偏灰色彩。

事项助手首页事项助手首页

预备

本次案例用到了ES6,先来了解一下案列中用到的几个写法。本人也是顺带学习顺带编写,可能代码中还存在部分老的写法。

变量

ES6中声明变量可以用let声明变量,用const声明常量,即不可改变的量。

      
1
2
3
4
5
      
let version = '1.0.0';
const weekday = 7;
version = '2.0.0';
weekday = 8; //错误,用const声明的常量,不能修改值

本习惯用大写字母和下划线的组合方式来声明全局的常量

      
1
      
const CONFIG_COLOR = '#FAFAFA';

对象方法属性

小程序的每一个页面都有一个相对应的js文件,里面必不可少的就是Page函数,Page函数接受的参数是一个对象,我们经常使用的写法就是:

      
1
2
3
4
5
6
7
8
9
10
11
12
      
Page({
data: {
userAvatar: './images/avatar.png',
userName: 'Oopsguy'
},
onLoad: function() {
//....
},
onReady: function() {
//....
}
});

现在换做ES6的写法,我们可以这样:

      
1
2
3
4
5
6
7
8
9
10
11
12
      
Page({
data: {
userAvatar: './images/avatar.png',
userName: 'Oopsguy'
},
onLoad() {
//....
},
onReady() {
//....
}
});

我们可以把以前的键值写法省略掉,而且function声明也不需要了。

类

ES6中拥有了类这一概念,声明类的方式很简单,跟其他语言一样,差别不大:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
      
class Animal {
constructor() {
}
eat() {
}
static doSomething(param) {
//...
}
}
module.exports = Animal;

class关键字用于声明类,constructor是构造函数,static修饰静态方法。不能理解?我们看一下以前的js的简单写法:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
      
var Animal = function() {
};
Animal.prototype.eat = function() {
};
Animal.doSomething = function(param) {
};
module.exports = Animal;

简单的调用示例

      
1
2
3
4
      
let animal = new Animal();
animal.eat();
//静态方法
Animal.doSomething( 'param');

这里只是简单的展示了一下不同点,更多的只是还是需要读者自己翻阅更多的资料来学习。

解构

其实本人对结构也不太懂怎样解释,简单的来说就是可以把一个数组的元素或者对象的属性分解出来,直接获取,哈哈,解释的比较勉强,还是看看示例吧。

      
1
2
3
4
5
6
7
8
9
10
11
12
      
let obj = {
fullName: 'Xiao Ming',
gender: 'male',
role: 'admin'
};
let arr = [ 'elem1', 1, 30, 'arratElem3'];
let {fullName, role} = obj;
let [elem1, elem2] = arr;
console.log(fullName, role, elem1, elem2);

大家可能猜出了什么,看看输出结果:

      
1
      
> Xiao Ming admin elem1 1

我们只要把需要获取的属性或者元素别名指定解构体中,js会自动获取对应的属性或者下标对应的元素。这个新特性非常有用,比如我们需要在一个Pages data对象中一个属性获取对了属性值:

      
1
2
3
      
let year = this.data.year,
month = this.data.month,
day = this.data.day;

但是用解构的写法就很简洁:

      
1
      
let {year, month, day} = this.data;

再比如引入一个文件:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
      
function getDate(dateStr) {
if (dateStr) {
return new Date( Date.parse(dateStr));
}
return new Date();
}
function log(msg) {
if (!msg) return;
if (getApp().settings[ 'debug'])
console.log(msg);
let logs = wx.getStorageSync( 'logs') || [];
logs.unshift(msg)
wx.setStorageSync( 'logs', logs)
}
module.exports = {
getDate: getDate,
log: log
};

现在引入并调用外部文件的方法:

      
1
2
3
      
import {log} from '../../utils/util';
log( 'Application initialized !!');

import...from...是ES6的引入模块方式,等同于小程序总的require,但import可以选择导入哪些子模块。

箭头函数(Arrow Function)

刚开始我也不知道js的箭头函数到底是什么东西,用了才发现,这特么就是lambda表达式么。箭头函数简化了函数的写法,但是还是跟普通的function有区别,主要是在作用域上。

比如我们需要请求网络:

      
1
2
3
4
5
6
7
8
9
      
wx.request({
url: 'url',
header: {
'Content-Type': 'application/json'
},
success: function(res) {
console.log(res.data)
}
});

用函数还是可以简化一定的代码量,哈哈哈。

      
1
2
3
4
5
6
7
8
9
      
wx.request({
url: 'url',
header: {
'Content-Type': 'application/json'
},
success: (res) => {
console.log(res.data)
}
});

注意到那个success指向的回调函数了么,function关键字没了,被醒目的=>符号取代了。看到这里大家是不是认为以后我们写function就用箭头函数代替呢?答案是不一定,而且要非常小心!

function和箭头函数虽然看似一样,只是写法简化了,其实是不一样的,function声明的函数和箭头函数的作用域不同,这是一个不小心就变坑的地方。

      
1
2
3
4
5
6
7
8
9
10
11
12
13
      
Page({
data: {
windowHeight: 0
},
onLoad() {
let _this = this;
wx.getSystemInfo({
success: function(res) {
_this.setData({ windowHeight: res.windowHeight});
}
});
}
});

一般我们获取设备的屏幕高度差不多是这样的步骤,在页面刚加载的onLoad方法中通过wx.getSystemInfoAPI来获取设备的屏幕高度,由于success指向的回调函数作用域跟onLoad不一样,所以我们无法像onLoad函数体中直接写this.setData来设置值。我们可以定义一个临时变量指向this,然后再回调函数中调用。

哪箭头函数的写法有什么不一样呢?

      
1
2
3
4
5
6
7
8
9
10
11
12
13
      
Page({
data: {
windowHeight: 0
},
onLoad() {
let _this = this;
wx.getSystemInfo({
success: (res) => {
_this.setData({ windowHeight: res.windowHeight});
}
});
}
});

运行之后好像感觉没什么区别呀,都能正常执行,结果也一样。确实没什么区别,你甚至这样写都可以:

      
1
2
3
4
5
6
7
8
9
10
11
12
      
Page({
data: {
windowHeight: 0
},
onLoad() {
wx.getSystemInfo({
success: (res) => {
this.setData({ windowHeight: res.windowHeight});
}
});
}
});

咦?这样写,this的指向的作用域不是不一样么?其实这就是要说明的,箭头函数是不绑定作用域的,不会改变当前this的作用域,既然这样,在箭头函数中的this就会根据作用域链来指向上一层的作用域,也就是onLoad的作用域,所以他们得到的结果都是一样的。

其实我个人的习惯是无论用普通的函数写法还是箭头函数的写法,都习惯声明临时的_this来指向需要的作用域,因为箭头函数没有绑定作用域,写的层次深了,感觉就会很乱,理解起来比较困难,在后面的案例中,我也会延续这个习惯。

Promise

写js经常写的东西除了数组对象就是回调函数,记不记得用jQuery的ajax用得特别爽,如果是多层嵌套调用的话,那些回调函数简直像盖楼梯一样壮观。现在Promise来了,我们再也不用为这些回调地狱发愁,用Promise来解决回调问题非常优雅,链式调用也非常的方便。

Promise是ES6内置的类,其使用简单,简化了异步编程的繁琐层次问题,比较简单的用法是:

      
1
2
3
4
5
6
7
      
new Promise( (resolve, reject) => {
//success
//resolve();
//error
//reject();
});

实例化一个Promise对象,它接受一个函数参数,此函数有两个回调参数,resolve和reject,如果正常执行使用resolve执行传递,如果是失败或者错误可以用reject来执行传递,其实他们就是一个状态的转换。可以暂时理解为success和fail。

来看一下简单的示例:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
      
let ret = true;
let pro = new Promise( (resolve, reject) => {
ret ? resolve( 'true') : reject( 'false');
}).then( (res) => {
console.log(res);
return 'SUCCESS';
}, (rej) => {
console.log(rej);
return 'ERROR';
}).then( (success) => {
console.log(success);
let value = 0 / 1;
}, (error) => {
console.log(error);
}).catch( (ex) => {
console.log(ex);
});

或许我们已经看出些什么了,实例化出一个Promise,根据ret的布尔值决定是否resolve执行正常回调流程还是执行reject回调走意外的流程,显然ret是true,当执行resolve时,传递了一个字符串参数true,可以看到实例化出来的Promise对象后面链式调用了很多then方法,其实then方法同样也是有resolve和reject两个回调参数,上层的Promise执行的回调传递到then函数中,Promise的resolve传递到then的resolve,同理reject也一样,之后我们发现最后一个catch函数,这是一个捕抓异常的函数,当流程发生异常,我们可以在catch方法中获取异常并处理。

可能解释的比较羞涩,看看下面例子,发出一个网络请求,获取用户头像,再把用户头像插入DOM中,再睡眠2000ms,再打印出SUCCESS,再睡眠3000ms,在alert出ERROR,再休眠1000ms,最后打印出ERROR。这…看起来有点丧心病狂,但只是举个例子:

      
1
2
3
4
5
6
7
8
9
10
11
12
      
$.get( '/user/1/avatar', (data) => {
$( '#avatar img').attr( 'src', data[ 'avatar']);
setTimeout( () => {
console.log( 'SUCCESS');
setTimeout( () => {
alert( 'ERROR');
setTimeout( () => {
console.log( 'ERROR');
}, 1000);
}, 3000)
}, 2000);
});

一共有四个回调函数,也不算多,如果有十几个回调呢?直至是噩梦呀。一层一层的嵌套,看起来已经眼花了。那么Promise能做些什么改变呢?

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
      
function sleep(time) {
return new Promise( (resolve) => {
setTimeout(resolve, time);
});
}
new Promise( (resolve) => {
$.get( '/user/1/avatar', resolve);
}).then( (avatar) => {
$( '#avatar img').attr( 'src', avatar);
}).then( () => {
return sleep( 2000);
}).then( () => {
console.log( 'SUCCESS');
return sleep( 3000);
}).then( () => {
alert( 'ERROR');
return sleep( 1000);
}).then( () => {
console.log( 'ERROR');
});

额…看起来怎么使用Promise代码量比不使用的还多呀。不要介意,嘿嘿,可能是我个人封装不精,但是使用Promise的代码可读性确实比上面的要好很多,而且我们不必写一堆的嵌套回调函数,在享受使用同步写法的待遇,又可以得到异步的功能,两全其美,这样的写法还是比较符合日常的思维方式,哈哈。

看看小程序中怎么应用,在小程序项目的app.js中,我们经常看见这段代码:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
      
App({
getUserInfo: function(cb){
var that = this
if( this.globalData.userInfo){
typeof cb == "function" && cb( this.globalData.userInfo)
} else{
wx.login({
success: function () {
wx.getUserInfo({
success: function (res) {
that.globalData.userInfo = res.userInfo
typeof cb == "function" && cb(that.globalData.userInfo)
}
})
}
})
}
}
});

这是个方法是获取当前用户的信息,首先先检查globalData对象中有没有缓存有userInfo对象(存储用户的信息),如果有就返回给用户传进来的回掉函数,否则就请求接口获取用用户信息,获取用户信息之前,微信小程序要求先调用wx.login认证,才能调用wx.getUserInfo接口。

看的出代码的层次已经有点深了,我们可以用Promise来简化一下(-_-|| 说的有点夸张,实际上这点嵌套还是可以的)

wx.getUserInfo和wx.login这两个接口都用共同的属性success和fail,我们可以封装起来:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
      
/**
* @param {Function} func 接口
* @param {Object} options 接口参数
* @returns {Promise} Promise对象
*/
function promiseHandle(func, options) {
options = options || {};
return new Promise( (resolve, reject) => {
if ( typeof func !== 'function')
reject();
options.success = resolve;
options.fail = reject;
func(options);
});
}
App({
getUserInfo(cb) {
if ( typeof cb !== "function") return;
let that = this;
if (that.globalData.userInfo) {
cb(that.globalData.userInfo);
} else {
promiseHandle(wx.login)
.then( () => promiseHandle(wx.getUserInfo))
.then( (res) => {
that.globalData.userInfo = res.userInfo;
cb(that.globalData.userInfo);
})
.catch( (err) => {
log(err);
});
}
}
});

可以看出,使用了Promise之后,代码简洁了不少,层次深度也降低了不少,好家伙,很管用!

其实本次代码中的回调嵌套很少的,为了尽量使用到ES6的新特性,少量的回调嵌套也使用了Promise处理。

介绍了那么多,主要了为了还不了解ES6的读者能够预热一下知识,为后面的案例做好准备,当然,肯定有同学已经对ES6了如指掌,本人也是刚刚学习,欢迎指正错误。

思路

在开工之前,我们先理一下思路,一个普通的日历显示功能应该怎么做,该怎样入手。

日期

获取日期相关的信息,肯定用到Date对象。

      
1
2
3
4
      
let date = new Date();
let day = date.getDate(); //当月的天
let month = date.getMonth() + 1; //月份,从0开始
let year = date.getFullYear(); //年份

我们需要知道当前展示月份的天数。

      
1
      
let dayCount = new Date(currentYear, currentMonth, 0).getDate();

得到可当月月份的天数,可以展示出所有的天数列表,但是我们一样要或者上一个页的天数和下一个页的天数,如果当前月份是1月或者12月,我们还需要额外判断上一页是上一年的12月,下一页是下一年的一月份。

我们可能需要获取足够多的日期信息来展示(不仅仅是当前月份,还有上一月或者上一年和下一月或者下一年)

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
      
data = {
currentDate: currentDateObj.getDate(), //当天日期第几天
currentYear: currentDateObj.getFullYear(), //当天年份
currentDay: currentDateObj.getDay(), //当天星期
currentMonth: currentDateObj.getMonth() + 1, //当天月份
showMonth: showMonth, //当前显示月份
showDate: showDate, //当前显示月份的第几天
showYear: showYear, //当前显示月份的年份
beforeYear: beforeYear, //当前页上一页的年份
beforMonth: beforMonth, //当前页上一页的月份
afterYear: afterYear, //当前页下一页的年份
afterMonth: afterMonth, //当前页下一页的月份
selected: selected //当前被选择的日期信息
};

能显示日期之后,当然还没有完,我们需要一个选择日期的功能,即用户可以点击指定那一天,也可以选择哪一年或者哪一个月,选择年份和月份我们可以用Picker组件来展示,选择具体的哪天这就需要在日期列表上的每一天都要绑定一个点击事件来响应用户的点击动作,用户选择具体的日期后,可能会随意翻页,所以必须要保存好当前选择的日期。

存储

示例程序中用到了数据存储,关系到小程序中的数据缓存API,官方提供的API比较多,我只是用了两个异步的数据缓存API。

wx.setStorage({key: KEY, data: DATA});
      
1
2
      
let allData =[{ id: 1, title: 'title1'}, { id: 2, title: 'title2'}];
wx.setStorageSync({ key: Config.ITEMS_SAVE_KEY, data: allData});
参数 说明
KEY 存储数据的键名
DATA 存储的数据
wx.getStorage({key: KEY});
      
1
2
3
4
5
6
7
      
let allData = wx.getStorage({
key: Config.ITEMS_SAVE_KEY
success: allData => {
let obj1 = allData[ 0];
console.log(obj1.title);
}
});
参数 说明
KEY 存储数据的键名

编码

建立工程的步骤就不讲了,直接进入主题,应用只有两个页面,一个首页,一个详情页,结构清晰,功能简单。

日历

先来看看首页,日历的wxml结构;

结构分为上中下三部分,header为头部,用于展示翻页按钮和当前日期信息。在.week.row和.body.row元素中展示星期和天数列表,这里的布局采用了比较low的百分比分栏,总共有7栏,100/7哈哈,想高逼格的可以采用css的分栏布局和flex布局。

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
      
<view class="og-calendar">
<view class="header">
<view class="btn month-pre" bindtap="changeDateEvent" data-year="{{data.beforeYear}}" data-month="{{data.beforMonth}}">
<image src="../../images/prepage.png"> </image>
</view>
<view class="date-info">
<picker mode="date" fields="month" value="{{pickerDateValue}}" bindchange="datePickerChangeEvent">
<text>{{data.showYear}}年{{data.showMonth > 9 ? data.showMonth : ('0' + data.showMonth)}}月 </text>
</picker>
</view>
<view class="btn month-next" bindtap="changeDateEvent" data-year="{{data.afterYear}}" data-month="{{data.afterMonth}}">
<image src="../../images/nextpage.png"> </image>
</view>
</view>
<view class="week row">
<view class="col">
<text>一 </text>
</view>
<view class="col">
<text>二 </text>
</view>
<view class="col">
<text>三 </text>
</view>
<view class="col">
<text>四 </text>
</view>
<view class="col">
<text>五 </text>
</view>
<view class="col">
<text>六 </text>
</view>
<view class="col">
<text>日 </text>
</view>
</view>
<view class="body row">
<block wx:for="{{data.dates}}" wx:key="_id">
<view bindtap="dateClickEvent" data-year="{{item.year}}" data-month="{{item.month}}" data-date="{{item.date}}" class="col {{data.showMonth == item.month ? '' : 'old'}} {{data.currentDate == item.date && data.currentYear==item.year && data.currentMonth == item.month ? 'current' : ''}} { {item.active ? 'active' : ''}}">
<text>{{item.date}} </text>
</view>
</block>
</view>
</view>

.btn.month-pre和.btn.month-next翻页按钮,都绑定了changeDateEvent的tap事件,各自都用自己的data-year和data-mont属性,这两个属性是临时存值,当点击按钮翻页的时候,我们需要知道当前的年份和日期,以便可以更加方便地翻到上一页或者下一页。

changeDateEvent事件比较简单:

      
1
2
3
4
      
changeDateEvent(e) {
const {year, month} = e.currentTarget.dataset;
changeDate.call( this, new Date(year, parseInt(month) - 1, 1));
}

点击翻页按钮,根据回调进来的event对象来获取元素上的data-*属性,然后调用changeDate这个方法来更新日历数据,这个方法接收一个Date对象,代表要翻页后的日期。

暂且不关心changeDate具体干了些什么,看看.body.row里有一个循环,每一个元素都绑定了dateClickEvent事件,而且每一个元素都附带了自己所属的年份、月份和天数信息,这些信息是非常有用的,当点击了具体的某一天,可以通过获取元素上的data-*信息来知道我们具体选择的日期。除此之外,元素上的class属性包裹了一长串的判断表达式。这些语句最终的目的是为了给元素动态变更,.old代表当前的日期不是本月日期,因为每一版的日期除了当前月份的日期还可能包含上一月和下一月的部分日期,我们给予它灰色的样式显示,.current代表今天的日期,用实心填充颜色的背景样式修饰,.active即代表着当前选中的日期。

dateClickEvent事件其实也是调用了changeDate事件,本质上也是也是改变日期,额外的工作就是保存选中的日期到selected对象中。

      
1
2
3
4
5
6
7
8
9
10
11
12
13
      
dateClickEvent(e) {
const {year, month, date} = e.currentTarget.dataset;
const {data} = this.data;
let selectDateText = '';
data[ 'selected'][ 'year'] = year;
data[ 'selected'][ 'month'] = month;
data[ 'selected'][ 'date'] = date;
this.setData({ data: data });
changeDate.call( this, new Date(year, parseInt(month) - 1, date));
}

来看看重中之重的changeDate函数,这个函数的代码比较多,虽然堆砌大量在一个函数中是个不好的习惯,不过里面声明变量和赋值比较多,业务代码比较少:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
      
/**
* 变更日期数据
* @param {Date} targetDate 当前日期对象
*/
function changeDate(targetDate) {
let date = targetDate || new Date();
let currentDateObj = new Date();
let showMonth, //当天显示月份
showYear, //当前显示年份
showDay, //当前显示星期
showDate, //当前显示第几天
showMonthFirstDateDay, //当前显示月份第一天的星期
showMonthLastDateDay, //当前显示月份最后一天的星期
showMonthDateCount; //当前月份的总天数
let data = [];
showDate = date.getDate();
showMonth = date.getMonth() + 1;
showYear = date.getFullYear();
showDay = date.getDay();
showMonthDateCount = new Date(showYear, showMonth, 0).getDate();
date.setDate( 1);
showMonthFirstDateDay = date.getDay(); //当前显示月份第一天的星期
date.setDate(showMonthDateCount);
showMonthLastDateDay = date.getDay(); //当前显示月份最后一天的星期
let beforeDayCount = 0,
beforeYear, //上页月年份
beforMonth, //上页月份
afterYear, //下页年份
afterMonth, //下页月份
afterDayCount = 0, //上页显示天数
beforeMonthDayCount = 0; //上页月份总天数
//上一个月月份
beforMonth = showMonth === 1 ? 12 : showMonth - 1;
//上一个月年份
beforeYear = showMonth === 1 ? showYear - 1 : showYear;
//下个月月份
afterMonth = showMonth === 12 ? 1 : showMonth + 1;
//下个月年份
afterYear = showMonth === 12 ? showYear + 1 : showYear;
//获取上一页的显示天数
if (showMonthFirstDateDay != 0)
beforeDayCount = showMonthFirstDateDay - 1;
else
beforeDayCount = 6;
//获取下页的显示天数
if (showMonthLastDateDay != 0)
afterDayCount = 7 - showMonthLastDateDay;
else
showMonthLastDateDay = 0;
//如果天数不够6行,则补充完整
let tDay = showMonthDateCount + beforeDayCount + afterDayCount;
if (tDay <= 35)
afterDayCount += ( 42 - tDay); //6行7列 = 42
//虽然翻页了,但是保存用户选中的日期信息是非常有必要的
let selected = this.data.data[ 'selected'] || { year: showYear, month: showMonth, date: showDate };
let selectDateText = selected.year + '年' + formatNumber(selected.month) + '月' + formatNumber(selected.date) + '日';
data = {
currentDate: currentDateObj.getDate(), //当天日期第几天
currentYear: currentDateObj.getFullYear(), //当天年份
currentDay: currentDateObj.getDay(), //当天星期
currentMonth: currentDateObj.getMonth() + 1, //当天月份
showMonth: showMonth, //当前显示月份
showDate: showDate, //当前显示月份的第几天
showYear: showYear, //当前显示月份的年份
beforeYear: beforeYear, //当前页上一页的年份
beforMonth: beforMonth, //当前页上一页的月份
afterYear: afterYear, //当前页下一页的年份
afterMonth: afterMonth, //当前页下一页的月份
selected: selected,
selectDateText: selectDateText
};
let dates = [];
let _id = 0; //为wx:key指定
//上一月的日期
if (beforeDayCount > 0) {
beforeMonthDayCount = new Date(beforeYear, beforMonth, 0).getDate();
for ( let fIdx = 0; fIdx < beforeDayCount; fIdx++) {
dates.unshift({
_id: _id,
year: beforeYear,
month: beforMonth,
date: beforeMonthDayCount - fIdx
});
_id++;
}
}
//当前月份的日期
for ( let cIdx = 1; cIdx <= showMonthDateCount; cIdx++) {
dates.push({
_id: _id,
active: (selected[ 'year'] == showYear && selected[ 'month'] == showMonth && selected[ 'date'] == cIdx), //选中状态判断
year: showYear,
month: showMonth,
date: cIdx
});
_id++;
}
//下一月的日期
if (afterDayCount > 0) {
for ( let lIdx = 1; lIdx <= afterDayCount; lIdx++) {
dates.push({
_id: _id,
year: afterYear,
month: afterMonth,
date: lIdx
});
_id++;
}
}
data.dates = dates;
this.setData({ data: data, pickerDateValue: showYear + '-' + showMonth });
loadItemListData.call( this);
}

虽然这段这段代码有点啰嗦,不过总结下来无非就是获取当前月的信息,上一页的信息和下一页的信息,这些信息包括具体的年月日和星期。

年月选择Picker

既然是日历,必不可少的功能就是让用户可以选择显示指定的年份和月份,用pciker组件来实现最合适不过了,官方更新的api,目前未知,picker组件已经支持mode = date模式的风格,即原生的日期选择。触发选择的区域关联在了日历的header上。

      
1
2
3
4
5
      
<view class="date-info">
<picker mode="date" fields="month" value="{{pickerDateValue}}" bindchange="datePickerChangeEvent">
<text>{{data.showYear}}年{{data.showMonth > 9 ? data.showMonth : ('0' + data.showMonth)}}月 </text>
</picker>
</view>

mode=date指定pciker是日期选择风格,fields=month则显示组件显示日期的精度显示当月份即可,组件初始化的值为pickerDateValue,绑定了datePickerChangeEvent事件,当选择的日期发生变化时,就会触发此事件。

      
1
2
3
4
      
datePickerChangeEvent(e) {
const date = new Date( Date.parse(e.detail.value));
changeDate.call( this, new Date(date.getFullYear(), date.getMonth(), 1));
}

PickerPicker

事项存储

此应用还有小小的事项功能,可以添加事项条目,事项包括了标题、内容和等级,说白了其实就是一个功能不全的TODO应用…

既然涉及到存储,肯定需要操作缓存的方法,自己也是刚搞前端那不久,不太明白javascript的封装约定,借鉴之前在java所用的模式,分为了两个文件,一个是仓库类(数据的CURD操作),另一个是业务类(附带处理部分业务),缓存的配置放置于Config文件中,类中用到了异步的缓存操作API,所以使用Promise模式封装。

首先是把Promise封装成通用的方法,顺便封装部分经常用到的函数:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
      
/**
* 生成GUID序列号
* @returns {string} GUID
*/
function guid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( /[xy]/g, function (c) {
let r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString( 16);
});
}
/**
* 记录日志
* @param {Mixed} 记录的信息
* @returns {Void}
*/
function log(msg) {
if (!msg) return;
if (getApp().settings[ 'debug'])
console.log(msg);
let logs = wx.getStorageSync( 'logs') || [];
logs.unshift(msg)
wx.setStorageSync( 'logs', logs)
}
/**
* @param {Function} func 接口
* @param {Object} options 接口参数
* @returns {Promise} Promise对象
*/
function promiseHandle(func, options) {
options = options || {};
return new Promise( (resolve, reject) => {
if ( typeof func !== 'function')
reject();
options.success = resolve;
options.fail = reject;
func(options);
});
}
module.exports = {
guid: guid,
log: log,
promiseHandle: promiseHandle
}

guid方法用于生成每一个事项的id,方便查询,log方法用于日志记录,promiseHandle把小程序的大部分异步API封装到了Promise对象中。

具体的Config配置文件:

      
1
2
3
4
5
6
7
8
9
      
module.exports = {
ITEMS_SAVE_KEY: 'todo_item_save_Key',
//事项等级
LEVEL: {
normal: 1,
warning: 2,
danger: 3
}
};

数据操作仓库类 DataRepository:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
      
import Config from 'Config';
import {guid, log, promiseHandle} from '../utils/util';
class DataRepository {
/**
* 添加数据
* @param {Object} 添加的数据
* @returns {Promise}
*/
static addData(data) {
if (!data) return false;
data[ '_id'] = guid();
return DataRepository.findAllData().then( allData => {
allData = allData || [];
allData.unshift(data);
wx.setStorage({ key:Config.ITEMS_SAVE_KEY, data: allData});
});
}
/**
* 删除数据
* @param {string} id 数据项idid
* @returns {Promise}
*/
static removeData(id) {
return DataRepository.findAllData().then( data => {
if (!data) return;
for ( let idx = 0, len = data.length; idx < len; idx++) {
if (data[idx] && data[idx][ '_id'] == id) {
data.splice(idx, 1);
break;
}
}
wx.setStorage({ key: Config.ITEMS_SAVE_KEY, data: data});
});
}
/**
* 批量删除数据
* @param {Array} range id集合
* @returns {Promise}
*/
static removeRange(range) {
if (!range) return;
return DataRepository.findAllData().then( data => {
if (!data) return;
let indexs = [];
for ( let rIdx = 0, rLen = range.length; rIdx < rLen; rIdx++) {
for ( let idx = 0, len = data.length; idx < len; idx++) {
if (data[idx] && data[idx][ '_id'] == range[rIdx]) {
indexs.push(idx);
break;
}
}
}
let tmpIdx = 0;
indexs.forEach( item => {
data.splice(item - tmpIdx, 1);
tmpIdx++;
});
wx.setStorage({ key: Config.ITEMS_SAVE_KEY, data: data});
});
}
/**
* 更新数据
* @param {Object} data 数据
* @returns {Promise}
*/
static saveData(data) {
if (!data || !data[ '_id']) return false;
return DataRepository.findAllData().then( allData => {
if (!allData) return false;
for ( let idx = 0, len = allData.length; i < len; i++) {
if (allData[i] && allData[i][ '_id'] == data[ '_id']) {
allData[i] = data;
break;
}
}
wx.setStorage({ key: Config.ITEMS_SAVE_KEY, data: data});
});
}
/**
* 获取所有数据
* @returns {Promise} Promise实例
*/
static findAllData() {
return promiseHandle(wx.getStorage, { key: Config.ITEMS_SAVE_KEY}).then( res => res.data ? res.data : []).catch( ex => {
log(ex);
});
}
/**
* 查找数据
* @param {Function} 回调
* @returns {Promise} Promise实例
*/
static findBy(predicate) {
return DataRepository.findAllData().then( data => {
if (data) {
data = data.filter( item => predicate(item));
}
return data;
});
}
}
module.exports = DataRepository;

数据业务类 DataService:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
      
import DataRepository from 'DataRepository';
import {promiseHandle} from '../utils/util';
/**
* 数据业务类
*/
class DataSerivce {
constructor(props) {
props = props || {};
this.id = props[ '_id'] || 0;
this.content = props[ 'content'] || '';
this.date = props[ 'date'] || '';
this.month = props[ 'month'] || '';
this.year = props[ 'year'] || '';
this.level = props[ 'level'] || '';
this.title = props[ 'title'] || '';
}
/**
* 保存当前对象数据
*/
save() {
if ( this._checkProps()) {
return DataRepository.addData({
title: this.title,
content: this.content,
year: this.year,
month: this.month,
date: this.date,
level: this.level,
addDate: new Date().getTime()
});
}
}
/**
* 获取所有事项数据
*/
static findAll() {
return DataRepository.findAllData()
.then( data => data.data ? data.data : []);
}
/**
* 通过id获取事项
*/
static findById(id) {
return DataRepository.findBy( item => item[ '_id'] == id)
.then( items => (items && items.length > 0) ? items[ 0] : null);
}
/**
* 根据id删除事项数据
*/
delete() {
return DataRepository.removeData( this.id);
}
/**
* 批量删除数据
* @param {Array} ids 事项Id集合
*/
static deleteRange(...ids) {
return DataRepository.removeRange(ids);
}
/**
* 根据日期查找所有符合条件的事项记录
* @param {Date} date 日期对象
* @returns {Array} 事项集合
*/
static findByDate(date) {
if (!date) return [];
return DataRepository.findBy( item => {
return item && item[ 'date'] == date.getDate() &&
item[ 'month'] == date.getMonth() &&
item[ 'year'] == date.getFullYear();
}).then( data => data);
}
_checkProps() {
return this.title && this.level && this.date && this.year && this.month;
}
}
module.exports = DataSerivce;

本人的对数组的操作不是很熟悉,代码看起来有点臃肿,仅供参考。

好了,进入正题,每天的事项可以用一个列表来展示,列表方在日历下边,具体结构:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
      
<view class="common-list">
<view class="header" wx:if="{{itemList.length > 0}}">
<text>事项信息 </text>
</view>
<block wx:for="{{itemList}}" wx:key="id">
<view class="item" bindtap="listItemClickEvent" data-id="{{item._id}}" bindlongtap="listItemLongTapEvent">
<view class="inner {{isEditMode ? 'with-check' : ''}}">
<view class="checker" wx:if="{{isEditMode}}">
<icon type="circle" wx:if="{{!item.checked}}" color="#FFF" size="20" />
<icon type="success" wx:else color="#E14848" size="20" />
</view>
<image wx:if="{{item.level == 1}}" class="icon" src="../../images/success.png" />
<image wx:if="{{item.level == 2}}" class="icon" src="../../images/notice.png" />
<image wx:if="{{item.level == 3}}" class="icon" src="../../images/fav-round.png" />
<view class="content">
<text class="title">{{item.title}} </text>
</view>
</view>
</view>
</block>
<view class="header text-center" wx:if="{{!itemList || itemList.length <= 0}}">
<text>当前日期没有事项记录 </text>
</view>
</view>

列表的数据加载全靠这个方法loadItemListData:

      
1
2
3
4
5
6
7
8
9
10
      
/**
* 加载事项列表数据
*/
function loadItemListData() {
const {year, month, date} = this.data.data.selected;
let _this = this;
DataService.findByDate( new Date( Date.parse([year, month, date].join( '-')))).then( (data) => {
_this.setData({ itemList: data });
});
}

DataService.findByDate这个方法通过传入一个日期来获取指定日期的事项。成功获取数据之后,在模板中遍历数据,根据level属性来显示不同颜色的图标,让事项等级一目了然。

既然有数据列表,数据从哪来?当然是需要一个数据的添加面板。

首页的有下表有FloatAction操作工具按钮,在这里添加一个添加数据按钮,添加的事项的日期属于用户选中的日期,添加面板默认是隐藏起来的,当点击添加按钮,面板就会向上滑动出现,可以用animationAPI实现动画效果,其实本质也是CSS3动画。

      
1
2
3
4
5
6
7
8
9
10
11
12
13
      
<view class="updatePanel" style="top: {{updatePanelTop}}px;height:{{updatePanelTop}}px" animation="{{updatePanelAnimationData}}">
<input placeholder="请输入事项标题" value="{{todoInputValue}}" bindchange="todoInputChangeEvent" />
<textarea placeholder="请输入事项内容" value="{{todoTextAreaValue}}" bindblur="todoTextAreaChangeEvent"> </textarea>
<view class="level">
<block wx:for="{{levelSelectData}}" wx:key="*this">
<view bindtap="levelClickEvent" data-level="{{item}}" class="item {{item == 1 ? 'border-normal' : ''}} {{item == 2 ? 'border-warning' : '' }} {{item == 3 ? 'border-danger' : ''}} {{item == levelSelectedValue && item == 1 ? 'bg-normal' : ''}} {{item == levelSelectedValue && item == 2 ? 'bg-warning' : ''}} {{item == levelSelectedValue && item == 3 ? 'bg-danger' : ''}}"> </view>
</block>
</view>
<view class="footer">
<view class="btn" bindtap="closeUpdatePanelEvent">取消 </view>
<view class="btn primary" bindtap="saveDataEvent">保存 </view>
</view>
</view>

在我写到这个内容之前,官方还没有textarea组件,现在新增了,完美解决遗憾。

添加面板的动画控制:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
      
/**
* 显示事项数据添加更新面板
*/
function showUpdatePanel() {
let animation = wx.createAnimation({
duration: 600
});
animation.translateY( '-100%').step();
this.setData({
updatePanelAnimationData: animation.export()
});
}
/**
* 显示模态窗口
* @param {String} msg 显示消息
*/
function showModal(msg) {
this.setData({
isModalShow: true,
isMaskShow: true,
modalMsg: msg
});
}
/**
* 关闭模态窗口
*/
function closeModal() {
this.setData({
isModalShow: false,
isMaskShow: false,
modalMsg: ''
});
}
/**
* 关闭事项数据添加更新面板
*/
function closeUpdatePanel() {
let animation = wx.createAnimation({
duration: 600
});
animation.translateY( '100%').step();
this.setData({
updatePanelAnimationData: animation.export()
});
}

主要靠translateY来控制垂直方向的移动动画,刚进入页面的时候获取屏幕的高度,把面板的高度设置与屏幕高度一致,上滑的时候100%就刚好覆盖整个屏幕。

主要的添加事项逻辑:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
      
// 保存事项数据
saveDataEvent() {
const {todoInputValue, todoTextAreaValue, levelSelectedValue} = this.data;
const {year, month, date} = this.data.data.selected;
console.log(todoInputValue, todoTextAreaValue);
if (todoInputValue !== '') {
let promise = new DataService({
title: todoInputValue,
content: todoTextAreaValue,
level: levelSelectedValue,
year: year,
month: parseInt(month) - 1,
date: date
}).save();
promise && promise.then( () => {
//清空表单
this.setData({
todoTextAreaValue: '',
levelSelectedValue: '',
todoInputValue: ''
});
loadItemListData.call( this);
})
closeUpdatePanel.call( this);
} else {
showModal.call( this, '请填写事项内容');
}
}

获取添加面板上的数据和当前选择的日期直接用DataSerivce对象保存即可。

由于篇幅有限,剩下的数据删除和数据查看逻辑也比较简单,不再细说,本文主要是介绍小程序的ES6开发。

批量编辑批量编辑

写完这篇文章的时候,小程序已经公测了好久。本人是个人用户,没有资格参与公测,热情也减半了不少,接触小程序也有一个多月了,写了三个例子,感觉还好,至少能够写出点东西来,不枉这番努力。

效果图

最终效果图最终效果图

源代码仓库

https://github.com/oopsguy/WechatSmallApps/tree/master/MatterAssistant

【微信小程序】manifest.json配置-ES6转ES5开关 文件是 uni-app 项目的配置文件,用于指定应用的基本信息,如名称、图标、启动页面等。对于微信小程序,它还包含了一些特定的配置项,如 ES6 转 ES5 开关。通过以上步骤,你可以在文件中成功配置 ES6 转 ES5 开关。正确配置 ES6 转 ES5 开关是确保小程序代码兼容性的关键步骤。希望本文对你有所帮助,如果有任何问题或需要进一步的帮助,请随时留言交流。😊。 阅读详情

相关推荐

微信小程序-事项助手开发.zip

微信小程序-事项助手开发.zip,小程序模板代码,可以直接从源码里粘贴复制过来,虽然这样做不利于自己独立编写代码。

功能实现(零散)

*****绝对定位的页面居中***** postion:abouslat     left:50%;margin-left=负的div宽度的一半 ******在页面底部跑上来一个弹框****** 把弹出框fixd定位top100%定位在最下面,并 $(".gamedown").click(function(){  var download = $("#download")

chose_DoIt的博客 543

微信小程序-微信小程序-事项助手

微信小程序-事项助手 说明: 实现了待办事项管理完整功能,包括新增任务,修改任务,完成任务等。 数据接口: 使用本地数据接口 目录结构: images — 存放项目图片文件 calendar— 存放项目日历样式文件 datas — 存放项目数据文件和数据处理库文件 pages — 存放项目页面渲染相关文件 utils — 存放promise polyfill和日期格式化处理文件 开发环境: 微信web开发者工具 v0.11.112301 项目截图: https://www.getweapp.com/project?projectId=58368fb8e8ff074c22472f15 感谢: 本项目原始版本由oopsguy提供:https://github.com/oopsguy/WechatSmallApps/tree/master/MatterAssistant

增删改查方案

js部分: data: { user:'', pass:'' }, add:function(){ //初始化 const db = wx.cloud.database() // collection集合 db.collection('student').add({ // data 字段表示需增加的 JSON 数据 data:{ //获取值 user:this.data.user, ...

javfen的博客 1876

事项助手小程序

这次介绍的是一个比较简单的小应用事项助手,其实跟事项也不沾多少边,只是作为辅助功能,只有数据的添加和删除,主要内容是日历这块内容。日历组件在web应用中应用非常广泛,插件也非常丰富,但是小程序不支持传统的插件写法,而是以数据驱动内容。

微信小程序Demo:事项助手(在日历上添加事件备注)

微信小程序Demo:事项助手(在日历上添加事件备注) 项目地址:http://www.wxapp-union.com/thread-9210-1-1.html

微信小程序——ES6语法简介

ECMAScript 6(简称ES6)是于2015年6月正式发布的JavaScript语言的标准,正式名为ECMAScript 2015(ES2015)。 小程序在很久之前就支持了ES6了,因此在小程序的开发中,我们可以适当的使用ES6中的一些新特性,来简化代码,高效开发。 在使用微信开发者工具调试之前,记得先勾选上es6转es5选项,这样工具才会将es6语法进行转换。

wct040923的博客 2846

微信小程序-事项助手

由于官方IDE更新到了0.11.112301版本,移除了对Promise的支持,造成事项助手不能正常运行,解决此问题,在项目中引入第三方兼容库Bluebird支持Promise,代码已经整合到项目代码中。 免责声明:本站所有文章和图片均来自用户分享和网络收集,文章和图片版权归原作者及原出处所有,仅供学习与参考,请勿用于商业用途,如果损害了您的权利,请联系网站客服处理。

微信小程序学习笔记

nodejs和微信小程序的学习比较

benben044的专栏 1347

[终于解决]Ubuntu14.04 32位安装移动网卡(0bda:b711)驱动8188gu tplink-WN726n

大致梳理一下问题解决的流程 首先,在台式机做了双系统,台式机只有一个网线接口,由于项目要求,需要占用网口传输数据,所以用之前买的tplink-WN726n免驱版2.0的移动网卡 【note】免驱版只能在Windows下安装使用,在Ubuntu系统中无法使用,所以,需要另辟蹊径。 找了很多资料,尝试很多方法,有的复杂有的简单,终于解决。 【思路】首先在Ubuntu系统中输入lsusb,查看是否检测到USB网卡设备。然后从Github中复制代码,构建,安装,然后modprobe一下,到前面那步还不行,就再来一步

瑞o 1万+

工业视觉光源频闪控制的三种核心模式与选型实战

本文深入解析工业视觉系统中光源频闪控制的三种核心模式:光源主控、相机主控与硬同步。针对高速飞拍、缺陷检测等场景,详细对比了各模式的时序原理、硬件要求与适用场景,并提供了基于产线速度与检测需求的实战选型指南,帮助工程师有效消除运动模糊、提升成像质量与系统稳定性。

weixin_29299807的博客 430

es6 获取本月本年_微信小程序之ES6与事项助手

本文发表至今已有一段时间,错别字多、文笔混乱、内容过于陈旧。本人建议读者不必细究,大概浏览即可,最新的开发指南还是以官方文档为准,该博文的示例代码经过了重构,已经与官方文档同步,可能与文中的代码片段有较大差异,请以 Github 仓库上的代码为准。好久没有写关于微信小程序的随笔了,其实是不知道写点什么好,之前的豆瓣图书和知乎日报已经把小程序的基础部分写的很详细了,高级部分的API有些还得不到IDE...

weixin_39615643的博客 304

【从零搭建到实战运行】基于PX4-1.13与XTDrone的FAST-LIVO2 Gazebo仿真全流程解析

本文详细解析了从零开始搭建基于PX4-1.13飞控、XTDrone仿真框架与Gazebo的FAST-LIVO2无人机仿真平台的全流程。内容涵盖环境配置、Livox雷达插件定制、算法部署与参数调试,旨在帮助开发者快速构建完整的激光-惯性-视觉SLAM仿真环境,并解决集成过程中的常见问题。

weixin_29218509的博客 520

wechat-miniprogram-examples学习路线图:从3个Demo到独立开发小程序的10个关键步骤

wechat-miniprogram-examples 是一套非常适合新手入门的微信小程序示例合集,内含豆瓣图书、事项助手、知乎日报三个由浅入深的完整 Demo。这份微信小程序学习路线图将带你从零开始,通过阅读和改造这三个小程序示例,一步步掌握页面搭建、网络请求、ES6 语法、组件化开发等核心技能,最终具备独立开发小程序的能力。 [![微信小程序示例项目概览](https://raw.gitco

gitblog_00767的博客 1066

schrodinger 薛定谔

链接: https://pan.baidu.com/s/1Duh4GsoytIklvZp7TdHpWg 密码: wmr1 Linux系统版 进入 Schrodinger_Suites_2021-2_Linux-x86_64 sudo ./INSTALL 中间按默认的来即可,也可以自己调整安装目录。安装目录一定要记住。 安装完,把patcher 拷贝到安装目录 sudo ./patcher 点击Patch即可。 启动程序,进入安装目录 ./maestro ...

周迪新的博客 1万+

Unity 热更 之 【YooAsset 热更】几分钟快速了解 YooAsset [功能面板]、以及 [基础代码] 说明

Unity 的资源管理系统不断发展,以适应不同项目的需求和解决特定的问题。AssetBundle 和 Addressable Asset System 是 Unity 官方提供的资源管理解决方案,而 YooAsset 是第三方提供的资源管理系统。简化的API和易用性YooAsset 可能提供了更简单直观的 API,使得资源加载和管理更加容易。对于不熟悉 Addressable 或 AssetBundle 复杂配置的开发者来说,YooAsset 可能是一个更易于上手的选择。性能优化。

仙魁XAN 2955

3个必学的微信小程序示例项目:从入门到实战完整指南 [特殊字符]

想要快速掌握微信小程序开发?这个开源项目为你提供了3个完整的实战案例,覆盖了从简单应用到复杂交互的各种开发场景。微信小程序示例项目是学习小程序开发的绝佳资源,无论你是初学者还是有经验的开发者,都能从中获得实用价值。 ## 📚 为什么选择这个微信小程序示例项目? 这个项目包含了3个精心设计的微信小程序示例,每个都代表了一种典型的应用类型: ### 豆瓣图书小程序 - 内容展示类应用 [![豆

gitblog_00873的博客 544

巧妙解决网页弹窗重复出现问题

本文探讨了网页弹窗关闭后刷新重现的问题及解决方案。通过分析原生JavaScript实现的弹窗代码存在的缺陷,提出了使用jQuery改进的方法。关键点包括:利用event.preventDefault()阻止默认行为避免刷新,采用jQuery的hide("slow")方法实现优雅的关闭动画,确保关闭状态持久化。改进后的方案既解决了弹窗重复出现的问题,又提升了用户体验,为网页弹窗交互提供了更优的实现方式。文中提供了完整的HTML、CSS和JavaScript代码示例,便于开发者直接应用。

2501_90323865的博客 179

微信小程序+ES6实例

当然,ES6还提供了许多其他特性,如Promise、async/await、Set和Map等,这些都可以在微信小程序中得到应用。箭头函数是ES6中引入的一种更简洁的函数写法,它自动绑定了定义时所在上下文的this值,这在小程序开发中特别有用,因为小程序的页面脚本中经常需要处理this指向问题。模板字符串是ES6中引入的一种新的字符串表示方法,它使用反引号(``)来标识,并且可以在其中嵌入变量或表达式。在ES6中,你可以在函数参数列表中为参数指定默认值,这样当调用函数时如果没有提供该参数,就会使用默认值。

赵玉的专栏 801

ARM功耗管理之功耗状态及功耗模式

思考:功耗状态?功耗模式?Core&Cluster&Device&SoC如何映射?

AI-安全-功耗 780

Python爬虫:实时交通流量数据爬取与路况分析

随着城市化进程的不断推进,交通流量的管理与预测已成为智能交通系统的重要研究方向。交通流量数据不仅为城市交通管理提供了科学依据,还为驾驶员提供了实时路况信息,帮助其做出更加合理的出行决策。通过爬虫技术,我们可以实时获取城市的交通流量数据,从而分析路况,优化交通管理。在这篇博客中,我们将介绍如何使用Python编写一个爬虫来爬取实时交通流量数据,分析道路的交通状况。我们将使用最新的技术工具来构建爬虫,包括。

2201_76125261的博客 1267

【系】微信小程序云开发实战坚果商城-ES6 简单入门

第 1-2 课:ES6 简单入门 1 var 、 let 、 const 3 箭头函数 4 数组的扩展 5 class类 6 Promise 对象

havendream的博客 4093

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

微信小程序中wxs不能用es6语法 比如includes不能用 只能用 es5中的indexOf代替

根据微信官方文档,wxs 中只能使用 ECMAScript 5 的语法和 API,不支持 ECMAScript 6 及以上的特性。indexOf 方法是 ECMAScript 5 就支持的,它的作用是查找一个字符串在另一个字符串中第一次出现的位置,如果找不到则返回 -1。记住,在 wxs 中,你只能使用 ECMAScript 5 的特性。如果你需要使用更高版本的 JavaScript 特性,你可能需要在 JavaScript 文件中处理这些逻辑,然后将结果传递给 Wxml,而不是在 wxs 中处理。

qq_35915504的博客 1276

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

JavaScript ES6

ES6是EcmaScript第六版本,全称:ECMAScript 6.0。2015年出版新增了:模块化,面向对象语法,Promise、箭头函数、let、const、数组解构赋值等等。ECMAScript 6 目前基本成为业界标准,它的普及速度比 ES5 要快很多,主要原因是现代浏览器对 ES6 的支持相当迅速,尤其是 Chrome 和 Firefox 浏览器,已经支持 ES6 中绝大多数的特性。...

zxcve的博客 612

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

微信小程序自定义组件中的注意事项(包含ES6的箭头函数,解构赋值)

创建自定义组件: 1.在微信开发者工具中新建文件夹 2.点击文件右键点击"新建components" 3.命名文件,将会创建四个文件(.js文件中显示Component而不是Page) 命名组件: 1.在Pages文件夹中新建一个页面(create) 2.在.json文件中的 "usingComponents" 中加入"标签名":"自定义组件路径" { "usingComponents": { "Tabs":"../../components/Tabs/Tabs" } } 调用

yangaoyuan1999的博客 589
上一篇: jQuery Ajax 实例 全解析
下一篇: 8个让程序员追悔莫及的职业建议
drl1210
博客等级 码龄10年 26粉丝 7原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值