Existing problems
As a front-end cut figure son, writing style is also our daily base operation, but inevitably there will be a need to dynamically change the element style, in the small program dynamic control style may be the following code, to a basic button component as an example:
<view class="l-btn {{ 'l-btn-' + size }} {{ 'l-btn-' + type }} {{ 'l-btn-' + shape }} {{plain? 'l-btn-plain':''}} {{ disabled ? 'l-btn-disabled' : ''}} l-class" />
Copy the code
In order to control the style of a button using props such as size, Type, shape, etc., the code used a lot of double curly braces to splicing the class name. If the class name was too many, the style of an element would be confused and the class name to be modified could not be found.
How to solve
React applet is a relatively new technology. Some of its problems can be solved by other technologies. Let’s take a look at how react solves this problem.
React uses the NPM package classnames– a utility function that concatenates all styles.
var classNames = require('classnames');
class Button extends React.Component {
// ...
render () {
var btnClass = classNames({
btn: true.'btn-pressed': this.state.isPressed,
'btn-over':!this.state.isPressed && this.state.isHovered
});
return <button className={btnClass}>{this.props.label}</button>; }}Copy the code
We can use the same idea to implement a similar tool function in a small program to help us solve this problem.
The solution
Because this tool function does not involve some interaction on the interface, but only does a process to the input variables, it can be considered to use WXS to implement it, which can also improve the performance of the page. First, we need to clarify the parameter types accepted by this function, including numbers, strings, arrays and objects.
First we create an array called classname to store the classname.
- Number, string: directly add push to
classname
In the. - Array:
classname
Splice with array. - Object: traverses pairs of objects
value
Is truekey
Added to theclassname
In the.
Given the above rules, we can write the following WXS and extract the corresponding class name.
function classNames() {
var classes = []
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i]
if(! arg)continue
var argType = typeof arg
if (argType === "string" || argType === "number") {
classes.push(arg)
} else if (Array.isArray(arg)) {
if (arg.length) {
classes = classes.concat(arg)
}
} else if (argType === "object") {
Object.keys(arg).forEach(function (key) {
if (arg[key]) {
classes.push(key)
}
})
}
}
return classes.join("")}module.exports = classNames
Copy the code
You thought you were done? The console threw two errors: Array and Object, two built-in JS objects, cannot be used in WXS at all.
Since it doesn’t exist, let’s build one, of course we can’t build built-in objects, but we’re going to use a method that we can emulate.
We can check whether the current argument’s constructor is an Array by checking whether its constructor equals Array.
function isArray(array) {
return array && array.constructor === "Array"
}
Copy the code
The keys method is a bit more difficult to implement. We can first convert the object to a string, then go to the re match replacement, and finally get an array of keys.
function keys(obj) {
return JSON.stringify(obj)
.replace(REGEXP, "")
.split(",")
.map(function (item) {
return item.split(":") [0]})}Copy the code
After completing these two alternative methods, we can replace the methods in the previous code, and the final code is as follows:
function isArray(array) {
return array && array.constructor === "Array"
}
var REGEXP = getRegExp('{|} | ""'."g")
function keys(obj) {
return JSON.stringify(obj)
.replace(REGEXP, "")
.split(",")
.map(function (item) {
return item.split(":") [0]})}function classNames() {
var classes = []
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i]
if(! arg)continue
var argType = typeof arg
Object.keys({})
if (argType === "string" || argType === "number") {
classes.push(arg)
} else if (isArray(arg)) {
if (arg.length) {
classes = classes.concat(arg)
}
} else if (argType === "object") {
keys(arg).forEach(function (key) {
if (arg[key]) {
classes.push(key)
}
})
}
}
return classes.join("")}module.exports = classNames
Copy the code
With this utility function, we can have fun writing styles in WXML.
<wxs module="classname" src="./index.wxs" />
<view class="container {{classname(1, ['c'],'less', {a: true, b: 1})}}">
111
</view>
Copy the code
conclusion
Although we solved the above problem with WXS, the following notation is not supported in objects.
{
'a-b': true} {[`l-${a}`] :true
}
Copy the code
If you can try to implement a similar approach in JS, it is also possible to dynamically generate style bindings through the component’s Observer function.