- 公众号和小程序都绑定了开发者平台,当用户先关注公众号再使用小程序时,公众号和小程序用户怎么关联?
[图片] 公众号和微信小程序都绑定了开发者平台,当用户先关注公众号再使用小程序时,公众号和小程序用户怎么才能通过unionID进行关联呢?
2020-08-12 - 【好文】小程序动态换肤解决方案 - 接口篇
需求说明 上一篇文章我是先通过在小程序本地预设几种主题样式,然后改变类名的方式来实现小程序的换肤功能的; 但是产品经理觉得每次改主题配置文件,都要发版,觉得太麻烦了,于是发话了:我想在管理后台有一个界面,可以让运营自行设置颜色,然后小程序这边根据运营在后台设置的色值来实现动态换肤,你们来帮我实现一下。 方案和问题 首先我们知道小程序是不能动态引入 [代码]wxss[代码] 文件的,这时候的色值字段是需要从后端接口获取之后,然后通过 [代码]style[代码] 内联的方式动态写入到需要改变色值的页面元素的标签上; 工作量之大,可想而知,因此,我们需要思考下面几个问题,然后尽可能写出可维护性,可扩展性的代码来: 页面元素组件化 —— 像[代码]按钮[代码] [代码]标签[代码] [代码]选项卡[代码] [代码]价格字体[代码] [代码]模态窗[代码]等组件抽离出来,认真考虑需要换肤的页面元素,避免二次编写; 避免内联样式直接编写,提高代码可阅读性 —— 内联编写样式会导致大量的 [代码]wxml[代码] 和 [代码]wxss[代码] 代码耦合一起,可考虑采用 [代码]wxs[代码] 编写模板字符串,动态引入,减少耦合; 避免色值字段频繁赋值 —— 页面或者组件引入 [代码]behaviors[代码] 混入色值字段,减少色值赋值代码编写; ps: 后续我会想办法尽可能通过别的手段,来通过js结合stylus预编译语言的方式,将本地篇和接口篇两种方案结合起来,即读取接口返回的色值之后,动态改变stylus文件的预设色值变量,而不是内联的方式实现小程序动态换肤 实现 接下来具体来详细详解一下我的思路和如何实现这一过程: model层: 接口会返回色值配置信息,我创建了一个 [代码]model[代码] 来存储这些信息,于是,我用单例的方式创建一个全局唯一的 [代码]model[代码] 对象 —— [代码]ViModel[代码] [代码]// viModel.js /** * 主题对象:是一个单例 * @param {*} mainColor 主色值 * @param {*} subColor 辅色值 */ function ViModel(mainColor, subColor) { if (typeof ViModel.instance == 'object') { return ViModel.instance } this.mainColor = mainColor this.subColor = subColor ViModel.instance = this return this } module.exports = { save: function(mainColor = '', subColor = '') { return new ViModel(mainColor, subColor) }, get: function() { return new ViModel() } } [代码] service层: 这是接口层,封装了读取主题样式的接口,比较简单,用 [代码]setTimeout[代码] 模拟了请求接口访问的延时,默认设置了 [代码]500[代码] ms,如果大家想要更清楚的观察 observer 监听器 的处理,可以将值调大若干倍 [代码]// service.js const getSkinSettings = () => { return new Promise((resolve, reject) => { // 模拟后端接口访问,暂时用500ms作为延时处理请求 setTimeout(() => { const resData = { code: 200, data: { mainColor: '#ff9e00', subColor: '#454545' } } // 判断状态码是否为200 if (resData.code == 200) { resolve(resData) } else { reject({ code: resData.code, message: '网络出错了' }) } }, 500) }) } module.exports = { getSkinSettings, } [代码] view层: 视图层,这只是一个内联css属性转化字符串的过程,我美其名曰视图层,正如我开篇所说的,[代码]内联[代码] 样式的编写会导致大量的 [代码]wxml[代码] 和 [代码]wxss[代码]代码冗余在一起,如果换肤的元素涉及到的 [代码]css[代码] 属性改动过多,再加上一堆的 [代码]js[代码] 的逻辑代码,后期维护代码必定是灾难性的,根本无法下手,大家可以看下我优化后的处理方式: [代码]// vi.wxs /** * css属性模板字符串构造 * * color => color属性字符串赋值构造 * background => background属性字符串赋值构造 */ var STYLE_TEMPLATE = { color: function(val) { return 'color: ' + val + '!important;' }, background: function(val) { return 'background: ' + val + '!important;' } } module.exports = { /** * 模板字符串方法 * * @param theme 主题样式对象 * @param key 需要构建内联css属性 * @param second 是否需要用到辅色 */ s: function(theme, key, second = false) { theme = theme || {} if (typeof theme === 'object') { var color = second ? theme.subColor : theme.mainColor return STYLE_TEMPLATE[key](color) } } } [代码] 注意:wxs文件的编写不能出现es6以后的语法,只能用es5及以下的语法进行编写 mixin: 上面解决完 [代码]wxml[代码] 和 [代码]wxss[代码] 代码混合的问题之后,接下来就是 [代码]js[代码] 的冗余问题了;我们获取到接口的色值信息之后,还需要将其赋值到[代码]Page[代码] 或者 [代码]Component[代码] 对象中去,也就是 [代码]this.setData({....})[代码]的方式, 才能使得页面重新 [代码]render[代码],进行换肤;</br> 微信小程序原生提供一种 [代码]Behavior[代码] 的属性,使我们避免反复 [代码]setData[代码] 操作,十分方便: [代码]// viBehaviors.js const observer = require('./observer'); const viModel = require('./viModel'); module.exports = Behavior({ data: { vi: null }, attached() { // 1. 如果接口响应过长,创建监听,回调函数中读取结果进行换肤 observer.addNotice('kNoticeVi', function(res) { this.setData({ vi: res }) }.bind(this)) // 2. 如果接口响应较快,modal有值,直接赋值,进行换肤 var modal = viModel.get() if (modal.mainColor || modal.subColor) { this.setData({ vi: modal }) } }, detached() { observer.removeNotice('kNoticeVi') } }) [代码] 到这里为止,基本的功能性代码就已经完成了,接下来我们来看一下具体的使用方法吧 具体使用 小程序启动,我们就需要去请求色值配置接口,获取主题样式,如果是需要从后台返回前台的时候也要考虑主题变动,可以在 [代码]onShow[代码] 方法处理 [代码]// app.js const { getSkinSettings } = require('./js/service'); const observer = require('./js/observer'); const viModel = require('./js/viModel'); App({ onLaunch: function () { // 页面启动,请求接口 getSkinSettings().then(res => { // 获取色值,保存到modal对象中 const { mainColor, subColor } = res.data viModel.save(mainColor, subColor) // 发送通知,变更色值 observer.postNotice('kNoticeVi', res.data) }).catch(err => { console.log(err) }) } }) [代码] 混入主题样式字段 [代码]Page[代码] 页面混入 [代码]// interface.js const viBehaviors = require('../../js/viBehaviors'); Page({ behaviors: [viBehaviors], onLoad() {} }) [代码] [代码]Component[代码] 组件混入 [代码]// wxButton.js const viBehaviors = require('../../js/viBehaviors'); Component({ behaviors: [viBehaviors], properties: { // 按钮文本 btnText: { type: String, value: '' }, // 是否为辅助按钮,更换辅色皮肤 secondary: { type: Boolean, value: false } } }) [代码] 内联样式动态换肤 [代码]Page[代码] 页面动态换肤 [代码]<view class="intro"> <view class="font mb10">正常字体</view> <view class="font font-vi mb10" style="{{_.s(vi, 'color')}}">vi色字体</view> <view class="btn main-btn mb10" style="{{_.s(vi, 'background')}}">主色按钮</view> <view class="btn sub-btn" style="{{_.s(vi, 'background', true)}}">辅色按钮</view> <!-- 按钮组件 --> <wxButton class="mb10" btnText="组件按钮(主色)" /> <wxButton class="mb10" btnText="组件按钮(辅色)" secondary /> </view> <!-- 引入模板函数 --> <wxs module="_" src="../../wxs/vi.wxs"></wxs> [代码] [代码]Component[代码] 组件动态换肤 [代码]<view class="btn" style="{{_.s(vi, 'background', secondary)}}">{{ btnText }}</view> <!-- 模板函数 --> <wxs module="_" src="../../wxs/vi.wxs" /> [代码] 再来对比一下传统的内联方式处理换肤功能的实现: [代码]<view style="color: {{ mainColor }}; background: {{ background }}">vi色字体</view> [代码] 如果后期再加入复杂的逻辑代码,开发人员后期再去阅读代码简直就是要抓狂的; 当然了,这篇文章的方案只是一定程度上简化了内联代码的编写,原理还是内联样式的注入; 我目前有一个想法,想通过某种手段在获取接口主题样式字段之后,借助 [代码]stylus[代码] 等预编译语言的变量机制,动态修改其变量,改变主题样式,方为上策; 总结:这两篇文章只是给大家提供一下小程序换肤的解决思路,文中如有不足之处,希望大家多多留言,或者去github上给我提issue,必定及时处理,谢谢大家。 效果预览 接口响应较快 —— [代码]ViModel[代码] 取值换肤 [图片] 接口响应过慢 —— [代码]observer[代码] 监听器回调取值换肤 [图片] 项目地址 项目地址:https://github.com/csonchen/wxSkin 这是本文案例的项目地址,为了方便大家浏览项目,我把编译后的wxss文件也一并上传了,大家打开就能预览,码字不易,大家如果觉得好,希望大家都去点下star哈,谢谢大家。。。
2020-04-24