The filter

Function:

Used for some common text formatting

Usage:

Filters can be used in two places: double curly brace interpolation and v-bind expressions (the latter supported as of 2.1.0+). Filters should be added to the end of JavaScript expressions, indicated by the “pipe” symbol:

<! -- In double braces -->
{{ message | capitalize }}

<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
Copy the code

How to create a filter

  1. Vue.filter(‘id’,function(){}) Global filter definition
  2. Filters: {‘id’ : function(){}} filters inside the component

Source code analysis

First, compilation stage

The parse phase

We found that there are two ways to use filters:

  • In the properties of v – bind: id = “XXX | filterA”
  • In the text double braces interpolation {{XXX | filterA | filterB (arg1, arg2)}}
1. The properties of v – bind: id = “XXX | filterA”

In processAttrs(), which parse handles the start node, the responsive attributes are matched by bindre.test (name), and the filters in the values are parseFilters(value).

if (bindRE.test(name)) { // v-bind
    / / get the name of the attribute Removed: | v - bind:
    name = name.replace(bindRE, ' ')
        // Parse the value into the correct value
    value = parseFilters(value)
    isProp = false
    
}

Copy the code
compiler\parser\filter-parser.js
* @param {*} exp */
export function parseFilters(exp: string) :string {
    // If it is in ''
    let inSingle = false
    // Is it in ""
    let inDouble = false
    // Whether in ' '
    let inTemplateString = false
    // Is it in re \\
    let inRegex = false
    // Find a culy + 1 in {{then find a} culy + 1 until culy = 0 {.. } closure
    let curly = 0
    // As with {{there is one [plus one] minus one
    let square = 0
    // Same as {{there is one (plus one) minus one
    let paren = 0
    //
    let lastFilterIndex = 0
    let c, prev, i, expression, filters

    for (i = 0; i < exp.length; i++) {
        prev = c
        c = exp.charCodeAt(i)
        if (inSingle) {
            / / '\
            if (c === 0x27&& prev ! = =0x5C) inSingle = false
        } else if (inDouble) {
            // " \
            if (c === 0x22&& prev ! = =0x5C) inDouble = false
        } else if (inTemplateString) {
            //  `
            if (c === 0x60&& prev ! = =0x5C) inTemplateString = false
        } else if (inRegex) {
            // Currently in regular expression/start
            / / / /
            if (c === 0x2f&& prev ! = =0x5C) inRegex = false
        } else if (
            // If not previously in '"'/that is, string or re
            / / then judgment If the current character is |
            / / if the current for | characters
            / / and the next (a) not | characters
            // And not in the {} object
            // Not in the [] array
            // And not in ()
            // Then this is a filter cut-off point
            c === 0x7C && // pipe
            exp.charCodeAt(i + 1)! = =0x7C &&
            exp.charCodeAt(i - 1)! = =0x7C&&! curly && ! square && ! paren ) {/ * if no expression so that this is the first line in front of the symbol "|" meet again | because the previous expression = 'message' execution pushFilter () * /
           
            if (expression === undefined) {
                // first filter, end of expression
                // The filter expression starts after the pipe symbol
                lastFilterIndex = i + 1
                // Store the filter expression
                expression = exp.slice(0, i).trim()
            } else {
                pushFilter()
            }
        } else {
            switch (c) {
                case 0x22:    
                    inDouble = true;
                    break // "
                case 0x27:
                    inSingle = true;
                    break / / '
                case 0x60:
                    inTemplateString = true;
                    break / / `
                case 0x28:
                    paren++;
                    break / / (
                case 0x29:
                    paren--;
                    break // )
                case 0x5B:
                    square++;
                    break / / /
                case 0x5D:
                    square--;
                    break // ]
                case 0x7B:
                    curly++;
                    break / / {
                case 0x7D:
                    curly--;
                    break // }
            }
            if (c === 0x2f) { // /
                let j = i - 1
                let p
                    // find first non-whitespace prev char
                for (; j >= 0; j--) {
                    p = exp.charAt(j)
                    if(p ! = =' ') break
                }
                if(! p || ! validDivisionCharRE.test(p)) { inRegex =true}}}}if (expression === undefined) {
        expression = exp.slice(0, i).trim()
    } else if(lastFilterIndex ! = =0) {
        pushFilter()
    }

    // Get the current filter and store it in an array of filters
    // filters = [ 'filterA' , 'filterB']
    function pushFilter() {
        (filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim())
        lastFilterIndex = i + 1
    }

    if (filters) {
        for (i = 0; i < filters.length; i++) {
            expression = wrapFilter(expression, filters[i])
        }
    }

    return expression
}
Copy the code

Parsing filters is simple:

  1. Will the value of the attribute once upon a time in the future to start a a match, the key symbol: “|” and eliminate “”,” ‘, ` `, / /, | | (string, regular) in the pipe ‘|’ symbols.

Such as:

<div v-bind:id="message + 'xxx|bbb' + (a||b) + `cccc` | filterA | filterB(arg1,arg2)"></div>
Copy the code

Match characters one by one in the future If found ` “shows that in the string, so didn’t end until you find the next match, / At the same time matching () {} [] these needs both sides equal closure So | is valid and the last condition rule out | |

  1. So until you hit the first correct | above, then the previous expression and storage in expression, continue to match back again meet |, so the expression has a value, that this is not the first filter pushFilter () to deal with on a filter

Exp = message filters = ['filterA','filterB(arg1,arg2)'] the first step is to generate the filter expression string for filterA using exp as an argument _f("filterA")(message) (_f("filterA")(arg1,arg2) => _f("filterB")(_f("filterA")(message),arg1,arg2) * @param {string} exp The value of a filter is the value of the expression * @param {string} filter * @returns {string} */
function wrapFilter(exp: string, filter: string) :string {
    FilterB (arg1,arg2)'
    const i = filter.indexOf('(')
    if (i < 0) {
        _f("filterA")(message)
        // _f: resolveFilter
        return `_f("${filter}"),${exp}) `
    } else {
        // Filter name
        const name = filter.slice(0, i)
        // Filter custom input parameter
        const args = filter.slice(i + 1)
        / / generated "_f (" filterB") (message, arg1, arg2)"
        return `_f("${name}"),${exp}${args ! = =') ' ? ', ' + args : args}`}}Copy the code

The exp = message + ‘XXX | BBB’ + (a | | b) + CCCC, filter = filterA

  1. Continue to determine whether the filter is (), at this time does not exist, then the filter’s name First into the parameter is in front of exp.

generate

"_f("filterA")(message + 'xxx|bbb' + (a||b) + `cccc`)"
Copy the code
  1. The previous result is exp, found to be present (, and then generated
"_f("filterB")(_f("filterA")(message + 'xxx|bbb' + (a||b) + `cccc`),arg1,arg2)"
Copy the code
2. The text double braces interpolation {{message | capitalize}}

Text is processed in the chars() method within parse and there is a parseText() method that parses {{}}


export function parseText(text: string, delimiters ? : [string, string]) :TextParseResult | void {
    // Process text content such as:
    // {{obj.name}} is {{obj.job}}
    while ((match = tagRE.exec(text))) {
        // ' {{obj.name}} is {{obj.job}} ' => [ 0: '{{obj.name}}' , 1: 'obj.name' ,index : 1, input: ' {{obj.name}} is {{obj.job}} ']
        // mate. index Gets the starting index of the current match
        index = match.index
        // push text token
        // If {{}} is preceded by static text such as (space.. {{xx}} XXX {{}}) then you need to save the static text
        if (index > lastIndex) {
            
            rawTokens.push(tokenValue = text.slice(lastIndex, index))
            // Save static text in tokens
            tokens.push(JSON.stringify(tokenValue))
        }
        // tag token
        // Parse the filter
        const exp = parseFilters(match[1].trim())
        _s('obj.name') => this['obj.name']
        tokens.push(`_s(${exp}) `)}}Copy the code

See const exp = parseFilters(match[1].trim()) for {{}}.

Render phase

We find that the compiler node will compile the _f(){} expression if it encounters a filter

"_f("filterB")(_f("filterA")(message + 'xxx|bbb' + (a||b) + `cccc`),arg1,arg2)"
Copy the code
core\instance\render-helpers\resolve-filter.js
/** * Runtime helper for resolving filters */
export function resolveFilter (id: string) :Function {
  return resolveAsset(this.$options, 'filters', id, true) || identity
}

Copy the code

The same filters in the vm.$options filters are retrieved by resolveAsset

Then _f (” filterA “) (message + ‘XXX | BBB’ + (a | | b) + CCCC), arg1, arg2 as the parameters.

Key points:

  1. ParseFilters ()

The last

For more blogs, see github.com/imisou/vue….

Author: HardStone

Copyright Notice: Freely reproduced – Non-commercial – Non-derivative – Remain signed (Creative Commons 3.0 License)