最近在学习vue.js,新版本是2.3.0了。在学习过滤器知识时,网上的一些例子是基于vue1.X官方提供的默认的过滤器,但是在vue2.x中全部取消了,都需要自己实现了。但是在vue.js的1.x版本中可以看到默认的过滤器的实现源码。我们可以把想过过滤器的源码复制出来,结合vue2.x过滤器的要求,做一些简单处理就可以使用了。在vue2.x中仍然可以在文本中使用过滤器,比如{{message | capitalize }} ,但是在v-for中就不能直接使用orderby 等过滤器了。

filter.html做测试,vue.js是2.3.0版本的

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title></title>
    <link rel="stylesheet" href="bootstrap-3.3.7/dist/css/bootstrap.min.css">
    <script src="vue.js"></script>
    <script src="filter.js"></script>
</head>

<body>
    <div id="app">
        <table class='table table-bordered'>
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Age</th>
                    <th>Sex</th>
                    <th>Salary</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="person in peopleOderBy ">
                    <td>{{ person.name | capitalize }}</td>
                    <td>{{ person.age }}</td>
                    <td>{{ person.sex }}</td>
                    <td>{{ person.salary | currency('¥','0') }}</td>
                </tr>
            </tbody>
        </table>

    </div>
</body>

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            people: [{
                name: 'jack',
                age: 30,
                sex: 'male',
                salary: '8000'
            }, {
                name: 'bill',
                age: 26,
                sex: 'male',
                salary: '7000'
            }, {
                name: 'Tracy',
                age: 22,
                sex: 'female',
                salary: '4500'
            }, {
                name: 'chris',
                age: 36,
                sex: 'male',
                salary: '9000'
            }]
        },

        computed: {
            peopleLimitBy: function() {
                return limitBy(this.people, 2, 1);
            },
            peopleFilterBy: function() {
                return filterBy(this.people, 'r', 'in', 'name', 'sex');
            },
            peopleOderBy: function() {
                return orderBy(this.people, 'age', 1);
            }

        }
    })
</script>
<style>
    body {
        margin: 0 auto;
        margin-left: 30%;
        margin-right: 30%;
        margin-top: 10px;
    }
</style>

</html>


其中filter.js就是从vue.js 1.x版本中复制出来的一些过滤器的代码

/**
 * 字符串的第一个字母大写
 */
Vue.filter('capitalize', function(value) {
    if (!value && value !== 0) return '';
    value = value.toString();
    return value.charAt(0).toUpperCase() + value.slice(1);
});

/**
 * 将字符串全部转为大写字母
 */
Vue.filter('uppercase', function(value) {
    return value || value === 0 ? value.toString().toUpperCase() : '';
});

/**
 * 将字符串全部转为小写字母
 */
Vue.filter('uppercase', function(value) {
    return value || value === 0 ? value.toString().toLowerCase() : '';
});

/**
 * 12345 => $12,345.00
 *
 * @param {String} sign
 * @param {Number} decimals Decimal places
 */

Vue.filter('currency', function(value, _currency, decimals) {
    value = parseFloat(value);
    if (!isFinite(value) || !value && value !== 0) return '';
    _currency = _currency != null ? _currency : '$';
    decimals = decimals != null ? decimals : 2;
    var stringified = Math.abs(value).toFixed(decimals);
    var _int = decimals ? stringified.slice(0, -1 - decimals) : stringified;
    var i = _int.length % 3;
    var head = i > 0 ? _int.slice(0, i) + (_int.length > 3 ? ',' : '') : '';
    var _float = decimals ? stringified.slice(-1 - decimals) : '';
    var sign = value < 0 ? '-' : '';
    var digitsRE = /(\d{3})(?=\d)/g;
    return sign + _currency + head + _int.slice(i).replace(digitsRE, '$1,') + _float;
});

/**
 * 'item' => 'items'
 *
 * @params
 *  an array of strings corresponding to
 *  the single, double, triple ... forms of the word to
 *  be pluralized. When the number to be pluralized
 *  exceeds the length of the args, it will use the last
 *  entry in the array.
 *
 *  e.g. ['single', 'double', 'triple', 'multiple']
 */

Vue.filter('pluralize', function(value) {
    var args = toArray(arguments, 1);
    return args.length > 1 ? args[value % 10 - 1] || args[args.length - 1] : args[0] + (value === 1 ? '' : 's');
});


/**
 * Limit filter for arrays
 *
 * @param {Number} n
 * @param {Number} offset (Decimal expected)
 */

function limitBy(arr, n, offset) {
    offset = offset ? parseInt(offset, 10) : 0;
    n = toNumber(n);
    return typeof n === 'number' ? arr.slice(offset, offset + n) : arr;
}

/**
 * Filter filter for arrays
 *
 * @param {String} search
 * @param {String} [delimiter]
 * @param {String} ...dataKeys
 */

function filterBy(arr, search, delimiter) {
    arr = convertArray(arr);
    if (search == null) {
        return arr;
    }
    if (typeof search === 'function') {
        return arr.filter(search);
    }
    // cast to lowercase string
    search = ('' + search).toLowerCase();
    // allow optional `in` delimiter
    // because why not
    var n = delimiter === 'in' ? 3 : 2;
    // extract and flatten keys
    var keys = Array.prototype.concat.apply([], toArray(arguments, n));
    var res = [];
    var item, key, val, j;
    for (var i = 0, l = arr.length; i < l; i++) {
        item = arr[i];
        val = item && item.$value || item;
        j = keys.length;
        if (j) {
            while (j--) {
                key = keys[j];
                if (key === '$key' && contains(item.$key, search) || contains(getPath(val, key), search)) {
                    res.push(item);
                    break;
                }
            }
        } else if (contains(item, search)) {
            res.push(item);
        }
    }
    return res;
};

/**
 * 排序
 */
function orderBy(arr) {
    var comparator = null;
    var sortKeys = undefined;
    arr = convertArray(arr);

    // determine order (last argument)
    var args = toArray(arguments, 1);
    var order = args[args.length - 1];
    if (typeof order === 'number') {
        order = order < 0 ? -1 : 1;
        args = args.length > 1 ? args.slice(0, -1) : args;
    } else {
        order = 1;
    }

    // determine sortKeys & comparator
    var firstArg = args[0];
    if (!firstArg) {
        return arr;
    } else if (typeof firstArg === 'function') {
        // custom comparator
        comparator = function(a, b) {
            return firstArg(a, b) * order;
        };
    } else {
        // string keys. flatten first
        sortKeys = Array.prototype.concat.apply([], args);
        comparator = function(a, b, i) {
            i = i || 0;
            return i >= sortKeys.length - 1 ? baseCompare(a, b, i) : baseCompare(a, b, i) || comparator(a, b, i + 1);
        };
    }

    function baseCompare(a, b, sortKeyIndex) {
        var sortKey = sortKeys[sortKeyIndex];
        if (sortKey) {
            if (sortKey !== '$key') {
                if (isObject(a) && '$value' in a) a = a.$value;
                if (isObject(b) && '$value' in b) b = b.$value;
            }
            a = isObject(a) ? getPath(a, sortKey) : a;
            b = isObject(b) ? getPath(b, sortKey) : b;
        }
        return a === b ? 0 : a > b ? order : -order;
    }

    // sort on a copy to avoid mutating original array
    return arr.slice().sort(comparator);
};


/**
 * Convert an Array-like object to a real Array.
 */
function toArray(list, start) {
    start = start || 0;
    var i = list.length - start;
    var ret = new Array(i);
    while (i--) {
        ret[i] = list[i + start];
    }
    return ret
}

function convertArray(value) {
    if (Array.isArray(value)) {
        return value;
    } else if (isPlainObject(value)) {
        // convert plain object to array.
        var keys = Object.keys(value);
        var i = keys.length;
        var res = new Array(i);
        var key;
        while (i--) {
            key = keys[i];
            res[i] = {
                $key: key,
                $value: value[key]
            };
        }
        return res;
    } else {
        if (typeof value === 'number' && !isNaN(value)) {
            value = range(value);
        }
        return value || [];
    }
}

/**
 * String contain helper
 *
 * @param {*} val
 * @param {String} search
 */

function contains(val, search) {
    var i;
    if (isPlainObject(val)) {
        var keys = Object.keys(val);
        i = keys.length;
        while (i--) {
            if (contains(val[keys[i]], search)) {
                return true;
            }
        }
    } else if (Array.isArray(val)) {
        i = val.length;
        while (i--) {
            if (contains(val[i], search)) {
                return true;
            }
        }
    } else if (val != null) {
        return val.toString().toLowerCase().indexOf(search) > -1;
    }
}



/**
 * Quick object check - this is primarily used to tell
 * Objects from primitive values when we know the value
 * is a JSON-compliant type.
 */
function isObject(obj) {
    return obj !== null && typeof obj === 'object'
}

function isPlainObject(obj) {
    return Object.prototype.toString.call(obj) === '[object Object]';
}

/**
 * Check and convert possible numeric strings to numbers
 * before setting back to data
 *
 * @param {*} value
 * @return {*|Number}
 */

function toNumber(value) {
    if (typeof value !== 'string') {
        return value;
    } else {
        var parsed = Number(value);
        return isNaN(parsed) ? value : parsed;
    }
}

function getPath(obj, path) {
    return parseExpression(path).get(obj);
}

/**
 * Parse an expression into re-written getter/setters.
 *
 * @param {String} exp
 * @param {Boolean} needSet
 * @return {Function}
 */

var expressionCache = new Cache(1000);
var pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/;
var booleanLiteralRE = /^(?:true|false)$/;

function parseExpression(exp, needSet) {
    exp = exp.trim();
    // try cache
    var hit = expressionCache.get(exp);
    if (hit) {
        if (needSet && !hit.set) {
            hit.set = compileSetter(hit.exp);
        }
        return hit;
    }
    var res = { exp: exp };
    res.get = isSimplePath(exp) && exp.indexOf('[') < 0
        // optimized super simple getter
        ?
        makeGetterFn('scope.' + exp)
        // dynamic getter
        :
        compileGetter(exp);
    if (needSet) {
        res.set = compileSetter(exp);
    }
    expressionCache.put(exp, res);
    return res;
}

function Cache(limit) {
    this.size = 0;
    this.limit = limit;
    this.head = this.tail = undefined;
    this._keymap = Object.create(null);
}

var p = Cache.prototype;

/**
 * Put <value> into the cache associated with <key>.
 * Returns the entry which was removed to make room for
 * the new entry. Otherwise undefined is returned.
 * (i.e. if there was enough room already).
 *
 * @param {String} key
 * @param {*} value
 * @return {Entry|undefined}
 */

p.put = function(key, value) {
    var removed;
    if (this.size === this.limit) {
        removed = this.shift();
    }

    var entry = this.get(key, true);
    if (!entry) {
        entry = {
            key: key
        };
        this._keymap[key] = entry;
        if (this.tail) {
            this.tail.newer = entry;
            entry.older = this.tail;
        } else {
            this.head = entry;
        }
        this.tail = entry;
        this.size++;
    }
    entry.value = value;

    return removed;
};

/**
 * Purge the least recently used (oldest) entry from the
 * cache. Returns the removed entry or undefined if the
 * cache was empty.
 */

p.shift = function() {
    var entry = this.head;
    if (entry) {
        this.head = this.head.newer;
        this.head.older = undefined;
        entry.newer = entry.older = undefined;
        this._keymap[entry.key] = undefined;
        this.size--;
    }
    return entry;
};

/**
 * Get and register recent use of <key>. Returns the value
 * associated with <key> or undefined if not in cache.
 *
 * @param {String} key
 * @param {Boolean} returnEntry
 * @return {Entry|*}
 */

p.get = function(key, returnEntry) {
    var entry = this._keymap[key];
    if (entry === undefined) return;
    if (entry === this.tail) {
        return returnEntry ? entry : entry.value;
    }
    // HEAD--------------TAIL
    //   <.older   .newer>
    //  <--- add direction --
    //   A  B  C  <D>  E
    if (entry.newer) {
        if (entry === this.head) {
            this.head = entry.newer;
        }
        entry.newer.older = entry.older; // C <-- E.
    }
    if (entry.older) {
        entry.older.newer = entry.newer; // C. --> E
    }
    entry.newer = undefined; // D --x
    entry.older = this.tail; // D. --> E
    if (this.tail) {
        this.tail.newer = entry; // E. <-- D
    }
    this.tail = entry;
    return returnEntry ? entry : entry.value;
};

function isSimplePath(exp) {
    return pathTestRE.test(exp) &&
        // don't treat true/false as paths
        !booleanLiteralRE.test(exp) &&
        // Math constants e.g. Math.PI, Math.E etc.
        exp.slice(0, 5) !== 'Math.';
}

function makeGetterFn(body) {
    try {
        /* eslint-disable no-new-func */
        return new Function('scope', 'return ' + body + ';');
        /* eslint-enable no-new-func */
    } catch (e) {
        'development' !== 'production' && warn('Invalid expression. ' + 'Generated function body: ' + body);
    }
}


Logo

前往低代码交流专区

更多推荐