# Grace
> 一个精巧、易用的微信小程序开发辅助库
## 特点
1. 轻量、小巧、上手简单
2. 支持和Vue一样优雅的数据响应式
3. 支持数据自动更新、更改缓存、批量更新
4. 强大的网络功能
5. 支持全局事件总线
6. 支持跨页面传值
7. 支持mixins
## Demo
示例工程在 “quickstart-grace-demo”, 用微信小程序开发工具打开即可。
## 使用
1. 将 https://github.com/wendux/grace/blob/master/dist/grace.js 拷贝到小程序根目录下的grace目录,并命名为index.js
2. 创建页面时用`grace.page` 替换 `Page` 即可。
```javascript
import grace from "../../grace/index.js"
grace.page({
data: {
userInfo: {},
canIUse: true
},
onLoad() {
//直接通过$data赋值更新数据
this.$data.canIUse = false
//通过$http发起网络请求
this.$http.post("http://www.dtworkroom.com/doris/1/2.0.0/test", {xx: 7}).then((d) => {
console.log(d)
}).catch(err => {
console.log(err.status, err.message)
})
//全局事件总线-监听事件
this.$bus.$on("enventName", (data) => {
console.log(data)
})
//返回上一页,并传递数据
this.$goBack({retValue: "8"})
},
//跨页面传值
$onBackData(data) {
//接收页面返回的数据,
}
...
})
```
如果是注册组件(component)的话, 只需用 `grace.component` 替换 `Component` 构造器即可:
```javascript
// grace.component 替换 Component
grace.component({
properties: {
},
data: {
text:"我是自定义组件",
times:1
},
methods: {
onTap(){
//赋值更新
this.$data.text="自定义组件点击 +"+this.$data.times++
}
}
}
```
**注意:Grace 注入到实例中的所有方法和属性名都以“$”开始。**
## 数据响应式
微信小程序中数据发生变化后都要通过setData显式更新如:
```javascript
//更新单个字段
this.setData({
userInfo: res.userInfo
})
//更新多个字段
this.setData({
userInfo: res.userInfo
canIUse: false
})
```
这很明显是受了React的影响,好的不学🤦,如果你用过Vue, 你应该会觉得这看起来很不优雅,尤其是代码中零零散散要更新的值多的时候,代码看起来会很冗余,还有,有时为了改变一个变量,也得调一次`setData`.
现在,有了Grace, 它会让你的代码变的优雅,你可以像使用Vue一样更新数据:
```javascript
this.$data.userInfo=res.userInfo;
//更新多个字段,并非重新赋值
this.$data={
userInfo: res.userInfo
canIUse: false
}
```
现在,你可以直接通过赋值就能更新界面了。当然,您依旧可以使用`this.setData`来更新数据,grace会自动同步 `this.$data`.
### 数组更新检测
grace的数据响应式原理和Vue是一样的,(如果你熟悉Vue,可以跳过)对于数组:
#### 变异方法
grace包含一组观察数组的变异方法,所以它们也将会触发视图更新。这些方法如下:
- `push()`
- `pop()`
- `shift()`
- `unshift()`
- `splice()`
- `sort()`
- `reverse()`
#### 替换数组
变异方法 (mutation method),顾名思义,会改变被这些方法调用的原始数组。相比之下,也有非变异 (non-mutating method) 方法,例如:`filter()`, `concat()` 和 `slice()` 。这些不会改变原始数组,但**总是返回一个新数组**。当使用非变异方法时,可以用新数组替换旧数组:
```javascript
this.$data.items = this.$data.items.filter(function (item) {
return item.message.match(/Foo/)
})
```
#### 注意事项
由于 JavaScript 的限制,grace不能检测以下变动的数组:
1. 当你利用索引直接设置一个项时,例如:`this.$data.items[indexOfItem] = newValue`
2. 当你修改数组的长度时,例如:`this.$data.items.length = newLength`
为了解决第一类问题,以下两种方式都可以实现和 `this.$data.items[indexOfItem] = newValue` 相同的效果,同时也将触发状态更新:
```javascript
this.$data.$set(example1.items, indexOfItem, newValue)
```
```javascript
// Array.prototype.splice
this.$data.items.splice(indexOfItem, 1, newValue)
```
为了解决第二类问题,你可以使用 `splice`:
```javascript
this.$data.items.splice(newLength)
```
### 对象属性的添加
还是由于 JavaScript 的限制,**grace 不能检测对象属性的添加或删除**:
```javascript
grace.page({
data: {
a: 1
}
onLoad(){
//a现在是响应式的
this.$data.a=2;
//b不是响应式的
this.$data.b = 2
}
})
```
如果需要动态添加响应式属性,可以使用 `$data.$set(object, key, value)` ,例如:
```javascript
this.$data.$set(this.$data, 'b', 2)
```
### 数据变更缓存
根据微信[小程序官方优化建议](https://mp.weixin.qq.com/debug/wxadoc/dev/framework/performance/tips.html),grace可以避免如下问题:
1. **频繁的去 setData**
为了解决这个问题,grace引入了数据变更缓存机制,下面看一个例子:
```javascript
//开始缓存数据变更
this.$data.$cache();
//接下来是n次密集的数据更新
this.$data.name="doris"
this.$data.userCard.no="610xxx889"
this.$data.balance=66666
....
//统一提交变更
this.$data.$commit();
```
在调用`$cache()`之后,所有数据的变化将会缓存起来(不会触发`setData`), 直到调用 `$commit`后,才会统一刷新,这样可以避免了频繁调用`setData`带来的性能消耗。
2. **后台态页面进行 setData**
当页面进入后台态(用户不可见),不应该继续去进行`setData`,后台态页面的渲染用户是无法感受的,另外后台态页面去`setData`也会抢占前台页面的执行。当页面进入后台时,grace会自动停止数据更新,当页面再次转到前台时会自动开启渲染,而无需您手动去切换。
## Http
Grace通过Promise封装了wx.request, 并支持拦截器、请求配置等:
1. Restful API
```javascript
$http.get(url, [data], [options])
$http.post(url, data, [options])
$http.put(url, data, [options])
$http.delete(url,[data],[options])
$http.patch(url,[data],[options])
```
2. 多个并发请求
```javascript
var getUserRecords=()=>{
return this.$http.get('/user/133/records');
}
var getUserProjects=()=>{
return this.$http.get('/user/133/projects');
}
this.$http.all([getUserRecords(), getUserProjects()])
.then(this.$http.spread(function (records, projects) {
// Both requests are now complete
}))
.catch(function(error){
console.log(error)
})
```
3. 拦截器
```javascript
// Add a request interceptor
this.$http.interceptors.request.use((request)=>{
// Do something before request is sent
request.headers["X-Tag"]="grace";
// Complete the request with custom data
// return Promise.resolve("fake data")
})
// Add a response interceptor
this.$http.interceptors.response.use(
(response) => {
// Do something with response data .
// Just return the data field of response
return response.data
},
(err) => {
// Do something with response error
// return Promise.resolve("ssss")
}
)
```
Grace使用的http请求库是 [FLY](https://github.com/wendux/fly) , `$http`是 [FLY](https://github.com/wendux/fly)的一个实例,详情可以参照其官网,如果您想创建新的 [FLY](https://github.com/wendux/fly) 实例:
```javascript
var newHttp=grace.creatHttpClient();
```
**注意:grace创建页面时,所有页面的`$http`都是同一个[FLY](https://github.com/wendux/fly) 实例,你可以使用 `grace.http` 获取该实例 ,所以对 `grace.http`的配置,会在全局生效。**
所以如果你想要配置全局的拦截器、请求基地址、超