Giter Club home page Giter Club logo

grace's Introduction

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 即可。
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 构造器即可:

// grace.component 替换 Component
grace.component({
  properties: {
  },
  data: {
     text:"我是自定义组件",
     times:1
  },
  methods: {
    onTap(){
      //赋值更新
      this.$data.text="自定义组件点击 +"+this.$data.times++
    }
  }
}

注意:Grace 注入到实例中的所有方法和属性名都以“$”开始。

数据响应式

微信小程序中数据发生变化后都要通过setData显式更新如:

//更新单个字段
this.setData({
    userInfo: res.userInfo
 })
//更新多个字段
this.setData({
    userInfo: res.userInfo
    canIUse: false
})

这很明显是受了React的影响,好的不学🤦‍,如果你用过Vue, 你应该会觉得这看起来很不优雅,尤其是代码中零零散散要更新的值多的时候,代码看起来会很冗余,还有,有时为了改变一个变量,也得调一次setData.

现在,有了Grace, 它会让你的代码变的优雅,你可以像使用Vue一样更新数据:

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() 。这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组:

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 相同的效果,同时也将触发状态更新:

this.$data.$set(example1.items, indexOfItem, newValue)
// Array.prototype.splice
this.$data.items.splice(indexOfItem, 1, newValue)

为了解决第二类问题,你可以使用 splice

this.$data.items.splice(newLength)

对象属性的添加

还是由于 JavaScript 的限制,grace 不能检测对象属性的添加或删除

grace.page({
  data: {
    a: 1
  }
  onLoad(){
   //a现在是响应式的
   this.$data.a=2;
   //b不是响应式的
   this.$data.b = 2
  }
})

如果需要动态添加响应式属性,可以使用 $data.$set(object, key, value) ,例如:

this.$data.$set(this.$data, 'b', 2)

数据变更缓存

根据微信小程序官方优化建议,grace可以避免如下问题:

  1. 频繁的去 setData

    为了解决这个问题,grace引入了数据变更缓存机制,下面看一个例子:

    //开始缓存数据变更
    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

    $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. 多个并发请求

    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. 拦截器

    // 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 , $httpFLY的一个实例,详情可以参照其官网,如果您想创建新的 FLY 实例:

var newHttp=grace.creatHttpClient();

注意:grace创建页面时,所有页面的$http都是同一个FLY 实例,你可以使用 grace.http 获取该实例 ,所以对 grace.http的配置,会在全局生效。

所以如果你想要配置全局的拦截器、请求基地址、超时时间等可以创建一个帮助文件,然后页面引入这个文件即可:

import grace from "../grace/index.js"
grace.http.config.baseURL = 'http://www.dtworkroom.com/doris/1/2.0.0/'
grace.http.config.timeout = 5000;
grace.http.interceptors.request.use((config, promise) => {
    //拦截器逻辑
    //console.log(config.body);
});
export default grace;

事件总线

全局事件总线可以在全局(跨页面)触发、监听事件。

$on(eventName,handler)

监听事件

this.$bus.$on("enventName",(arg1,arg2)=>{
      //事件处理器参数为$emit触发事件时传递的参数
  	  console.log(arg1)
})

$emit(eventName,[…arguments])

触发事件

this.$bus.$emit("enventName", 1,2) 

$off(eventName,[handler])

取消监听

this.$bus.$off("eventName",handler)

当提供hanlder时,只将该hanlder移出监听者队列,如果没有传handler,则清空该事件的监听者队列。

注意,全局只有一个$bus实例,您也可以使用 grace.bus 替代 this.$bus.

跨页面传值

在小程序中打开新页面时可以通过url的query向新页面传值,这很容易,如:

wx.navigateTo({
  //传递id,在新页面onLoad中获取
  url: 'test?id=1'
})

但是,新页面关闭时如何向前一个页面返回数据? 小程序中没有提供直接的方法,grace给所有页面添加了一个回调,用于接收页面回传的数据,如下:

grace.page({
  data:{}
  $onBackData(data){
   //接收页面返回的数据,
  }  
  ...         
})

上面的页面我们记为A, 假设你打开了一个新页面B, 你需要在B中选择一些信息后回传给A,那么你在B中应该:

grace.page({
  data: {},
  bindViewTap(){
    //返回上一个页面,并回传一些数据
    this.$goBack({xxx:5});
  }
  ...
}

$goBack([data],[delta])

关闭当前页面,返回上一页面或多级页面,如果存在data, 则会调用返回到的页面的$onBackData回调,若data不存在,则不会回调$onBackData.

delta 意义同 wx.navigateBack参数的delta, 表示回退的页面数,默认为1(上一页),如果如果 delta 大于现有页面数,则返回到首页。

mixin

混入 (mixins) 是一种分发页面(Page)可复用功能的非常灵活的方式。简而言之,他可以在小程序创建页面时,混合页面选项,可以实现给所有页面添加一些钩子的功能,如果还不理解,不要紧,下面来看一个例子:

实现:在任何页面调用onLoadonShow 时打印日志,并输出当前页面id.

  1. 创建一个help.js文件

    import grace from "../grace/index.js"
    var page=grace.page;
    grace.page=function(ob){
      mixin(ob,{
        onLoad(){
         //页面调用onShow时打印出当前页面id 
         console.log("onLoad, pageID:"+this.$id)
        },
        onShow(){
         //页面调用onShow时打印出当前页面id 
         console.log("onShow, pageID:"+this.$id)
      	}
      })
      //创建页面
      page.call(grace,ob)
    }
    export default grace;
  2. 在创建Page时引入help.js

    import grace from "../../utils/help.js"
    grace.page({
      data:{}
    })
    
    //控制台输出
    > onLoad, pageID:1
    > onShow, pageID:1

这样一来,相当于给所有的Page添加了onLoadonShow 预处理。

可以看到,mixin通过混入页面创建参数给页面添加统一的预处理功能,相当于添加了页面钩子。

选项合并

当页面构建对象和混入对象含有同名选项时,这些选项将以恰当的方式混合。

  1. 数据对象在内部会进行浅合并 (一层属性深度),在和页面构建数对象发生冲突时以页面构建数对象数据优先。

  2. 同名钩子函数将混合为一个数组,因此都将被调用。另外,混入对象的钩子将在页面自身钩子之前调用。

     
    grace.mixin(ob,{
        onShow(){
         console.log("mixin onShow")
        }
     });
    
    ...
    
    grace.page({
       onShow(){
         console.log("page onShow")
       }
    })
    
    //页面显示时会输出: 
    > mixin onShow
    > page onShow

和wepy比较

请参考:https://juejin.im/post/5aa0e45af265da23a404635a

grace's People

Contributors

wendux avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

grace's Issues

content-type设置不生效

this.$http.post(api.editBuyCommodity,{
      Price: 1.1,
      Deposit: 1.1,
      CategoryID: 1,
      Quantity: 1,
      Quality: 9,
      Picture: '11'
    },{
        headers:{
          'Content-Type': 'application/json'
        }
    })

image

http patch 错误

这个能用吗?$http.patch(url,[data],[options])
测试时一直得到 request failed: method invalid
据我所知小程序好像不支持 PATCH

组件中data数据不变化

1.page传入属性num,数值为1给组件
2.组件能拿到数值为1的num
3.page中num数值发生变化,+1变成2
4.组件中 {{num}} 也能显示成2,但是通过$this.data.num获取到值始终为1

提供小程序代码片段 wechatide://minicode/KKFRv3mP7e3r
@wendux 麻烦看下

对restful支持问题

按照文档所说的引入了grace,用$http发起http请求的时候,patch方法用不了,“request:fail method is invalid"(get/post/delete可以用)。求解

o is not defined in event-bus.js

$off(eventName, cb){
  var cbs = global._graceBus[eventName] || [];
  if (cb) {
    for (var index = 0; index < cbs.length; ++index) {
      if (cb == o) {
        cbs.splice(index, 1);
        break
      }
    }
  } else {
    global._graceBus[eventName] = [];
  }
}

this is my code↓

onLoad() {
  this.$bus.$on('refreshVehicleInfo', this.refreshVehicleInfo);    
},
onUnload() {
  this.$bus.$off('refreshVehicleInfo', this.refreshVehicleInfo);
},

cause

ReferenceError: o is not defined
at Object.$off (http://127.0.0.1:11386/appservice/libs/grace.js:112:24)
at r.onUnload (http://127.0.0.1:11386/appservice/subPackages/home/otherVehicleInfo/otherVehicleInfo.js:26:15)
at r.e.(anonymous function) (http://127.0.0.1:11386/appservice/libs/grace.js:382:78)
at r.t.(anonymous function) [as onUnload] (http://127.0.0.1:11386/appservice/libs/ald/ald-stat.js:29:30)
at r. (http://127.0.0.1:11386/appservice/__dev__/WAService.js:18:1832)
at N (http://127.0.0.1:11386/appservice/__dev__/WAService.js:17:12181)
at W (http://127.0.0.1:11386/appservice/__dev__/WAService.js:17:12977)
at G (http://127.0.0.1:11386/appservice/__dev__/WAService.js:17:14040)
at Function. (http://127.0.0.1:11386/appservice/__dev__/WAService.js:17:16550)
at http://127.0.0.1:11386/appservice/__dev__/WAService.js:17:22296

typescript 中导入失败

typescript 中导入失败:

VM1629:2 file: jslibs/grace.js
 unknown: Unexpected token (7:0)
    5 | 
   6 | 
>  7 | <!DOCTYPE html>
     | ^
   8 | <html lang="en">
   9 |   <head>
  10 |     <meta charset="utf-8">

setData 参数疑问

context.setData($data, null, null, true) 
function hookFun(ob, x, y, z) {
        if (x || y || z) {
            return realFun.call(context, ob, x, y);
        } else {
            context.$data = ob;
        }
    }

bind.js中为什么要这么设置x,y,z这几个参数,看起来好像没多大用呀,是为了以后扩展吗

如何在http拦截器中发起异步请求

拦截器里执行异步任务时

var log = console.log
log(grace)
var newHttp = grace.createHttpClient();
// 请求拦截器
grace.http.interceptors.request.use((request, con) => {
  request.headers["version"] = api.version
  log('发起请求:')
  var wait = grace.http.interceptors.request.await
  if (!grace.checkToken()) {
    log('没有access-token')
    return wait(  //这里出错
      newHttp.get('http://api.com/token').then((res)=>{
        //...
        return request
      })
    );    
  } else {
    request.headers["access-token"] = grace.checkToken()
  }
})

出错的地方报 "Cannot set property 'p' of undefined"

支持

仔细的分析了一下目前准备开始的项目情况,觉得还是轻量级的库方式比较更合适,
以扩展微信小程序原有框架功能的方式,尽量保持不破坏原有框架的结构。
肯定是踩了很多坑才写的这个库把:)。支持支持!

不过有一个担心,随着微信小程序框架更新,这个库的兼容性咋样?

http拦截器里promise不起作用

我的意思是如果访问接口a,a需要token,但是系统中没有token,需要访问接口b来获取,
我把检测token和接口b写在了 拦截器里,但是还没等到获取到token,a接口就已经开始响应了。

发现Mixin问题,不知道算不算BUG

首先:

Mixin 代码:

onLoad(options) {  
  console.log(options)
}
test(a, b) {
  console.log(a, b)
}

Page 代码:

onLoad(options) {
  console.log(options)
}
test(x, y) {
  console.log(x,y)
}

第一个问题:

原生onLoad的options参数是个对象,比较好用,如:

{isBack: 1}

经过Mixin之后 值会变成:

[{isBack: 1}]

第二个问题

如果是多个参数 Mixin之后直接变成一个参数了

test(1, 2) 

 // [1, 2] undefined

解决办法

翻了一下你的代码,mixin文件 第10行:

origin[key] = function() {
   var arg = [].slice.call(arguments)
   ob[key].call(this, arg)
   return v.call(this, arg)
 }

这里你把数组从参数中取出来之后变成一个数组了,然后直接call回去。
我的解决方案:

origin[key] = function() {
   var arg = [].slice.call(arguments)
   ob[key].call(this, ...arg)
   return v.call(this, ...arg)
 }

最后

感谢,我直接在公司项目上你的框架了。哈哈哈哈哈。

$onBackData 方法中修改页面数据无效

下面的代码 data.city_name 获取值 但输出日志 修改后:undefined

  //接收页面返回数据
  $onBackData: function (data) {
    this.setData({
      city2: data.city_name
    })
    console.log("修改后:"+this.data.city2);
  }

只支持import?

小程序的模块化是commonjs,用import好像没问题 不知道真机会不会有问题

小程序1991,开发者工具运行demo正常,真机提示错误

错误堆栈
[publib]:3 thirdScriptError
origin is not defined;at pages/index/index page lifeCycleMethod onLoad function
ReferenceError: origin is not defined
at hook (weapp:///grace/index.js:37:3)
at e.onLoad (weapp:///grace/index.js:66:7)
at e.origin.(anonymous function) (weapp:///grace/mixin.js:16:19)
at e. ([publib]:17:30038)
at B ([publib]:17:16312)
at H ([publib]:17:18240)
at G ([publib]:17:18506)
at Function. ([publib]:17:21242)
at [publib]:17:26966
at [publib]:5:21737
errorReport @ [publib]:3
[publib]:3 thirdScriptError
Cannot set property '$hide' of undefined;at pages/index/index page lifeCycleMethod onShow function
TypeError: Cannot set property '$hide' of undefined
at e.onShow (weapp:///grace/index.js:87:15)
at e.origin.(anonymous function) (weapp:///grace/mixin.js:16:19)
at e. ([publib]:17:30038)
at B ([publib]:17:16324)
at H ([publib]:17:18240)
at G ([publib]:17:18506)
at Function. ([publib]:17:21242)
at [publib]:17:26966
at [publib]:5:21737
at Array.forEach ()

这个是撒子原因呢

[publib]:3 thirdScriptError
Cannot set property '$hide' of undefined;at pages/field/index page lifeCycleMethod onShow function
TypeError: Cannot set property '$hide' of undefined
at e.onShow (weapp:///grace/index.js:34:15)
at e.origin.(anonymous function) (weapp:///grace/mixin.js:16:19)
at e. ([publib]:17:25368)
at B ([publib]:17:11913)
at H ([publib]:17:13820)
at G ([publib]:17:14086)
at Function. ([publib]:17:16550)
at [publib]:17:22296
at [publib]:5:21555
at Array.forEach ()

能否使用原生微信component

原生微信组件是指使用Component创建的组件,没有使用grace.component。能否直接使用?还是需要一一适配才能使用

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.