getAndRemoveAttr
从ast模板对象中取出相应的属性。
检测属性是否存在,通过对象attrsMap来检测,提升效率
如果存在,则从attrsList中中移除
如果第三个传参为true,删除attrsMap中对应的属性
返回取到的结果,或者undefined
// note: this only removes the attr from the Array (attrsList) so that it // doesn"t get processed by processAttrs. // By default it does NOT remove it from the map (attrsMap) because the map is // needed during codegen. export function getAndRemoveAttr ( el: ASTElement, name: string, removeFromMap?: boolean ): ?string { let val if ((val = el.attrsMap[name]) != null) { const list = el.attrsList for (let i = 0, l = list.length; i < l; i++) { if (list[i].name === name) { list.splice(i, 1) break } } } if (removeFromMap) { delete el.attrsMap[name] } return val }getBindingAttr
获取动态属性值
拼接属性名:|v-bind,调用getAndRemoveAttr读取相应的属性值
如果获取到相应的属性值,则调用parseFilters解析返回值中可能存在的过滤器,并返回
如果第三个传参不为false,返回相应的静态属性,并将静态属性格式化为字符串""demo1"",返回
否则,无返回
/** 第三个参数传true会在获取不到动态属性的时候取静态属性 */ export function getBindingAttr ( el: ASTElement, name: string, getStatic?: boolean ): ?string { const dynamicValue = getAndRemoveAttr(el, ":" + name) || getAndRemoveAttr(el, "v-bind:" + name) if (dynamicValue != null) { // 如果存在过滤器,将匹配到的字符串使用过滤器包裹。 return parseFilters(dynamicValue) } else if (getStatic !== false) { const staticValue = getAndRemoveAttr(el, name) if (staticValue != null) { return JSON.stringify(staticValue) } } }parseFilters
{{input | filter1 | filter2 }} 解析为:_f("filter2")(_f("filter1")(input))
/* @flow */ /** 匹配任意非空字符,),.,+,-,_,$,] * 排除一些其他用法产生的/,诸如a++ / b, a-- / b, a/b, (a + a1) / b, ../path */ const validDivisionCharRE = /[w).+-_$]]/ /** 解析出filter的条件是:匹配到|, * 并且|不在单引号,双引号,模板引用符,正则,括号,中括号,大括号中, * 并且不是|| */ export function parseFilters (exp: string): string { let inSingle = false let inDouble = false let inTemplateString = false let inRegex = false let curly = 0 let square = 0 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) // 0x5C => 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) { if (c === 0x2f && prev !== 0x5C) inRegex = false } else if ( c === 0x7C && // | exp.charCodeAt(i + 1) !== 0x7C && exp.charCodeAt(i - 1) !== 0x7C && !curly && !square && !paren ) { if (expression === undefined) { // first filter, end of expression lastFilterIndex = i + 1 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() } 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 } function wrapFilter (exp: string, filter: string): string { const i = filter.indexOf("(") if (i < 0) { // _f: resolveFilter return `_f("${filter}")(${exp})` } else { const name = filter.slice(0, i) const args = filter.slice(i + 1) return `_f("${name}")(${exp}${args !== ")" ? "," + args : args}` } }preTransformNode
/* @flow */ /** * Expand input[v-model] with dyanmic type bindings into v-if-else chains * Turn this: * * into this: * * * */ import { addRawAttr, getBindingAttr, getAndRemoveAttr } from "compiler/helpers" import { processFor, processElement, addIfCondition, createASTElement } from "compiler/parser/index" /** 处理input标签的v-model */ function preTransformNode (el: ASTElement, options: CompilerOptions) { if (el.tag === "input") { const map = el.attrsMap if (!map["v-model"]) { return } let typeBinding if (map[":type"] || map["v-bind:type"]) { typeBinding = getBindingAttr(el, "type") } if (!map.type && !typeBinding && map["v-bind"]) { typeBinding = `(${map["v-bind"]}).type` } if (typeBinding) { const ifCondition = getAndRemoveAttr(el, "v-if", true) const ifConditionExtra = ifCondition ? `&&(${ifCondition})` : `` const hasElse = getAndRemoveAttr(el, "v-else", true) != null const elseIfCondition = getAndRemoveAttr(el, "v-else-if", true) // 1. checkbox const branch0 = cloneASTElement(el) // process for on the main node,如果有v-for,将v-for解析为for,alias等属性,并添加到branch0上 processFor(branch0) // ast直接添加type属性 addRawAttr(branch0, "type", "checkbox") processElement(branch0, options) branch0.processed = true // prevent it from double-processed branch0.if = `(${typeBinding})==="checkbox"` + ifConditionExtra addIfCondition(branch0, { exp: branch0.if, block: branch0 }) // 2. add radio else-if condition const branch1 = cloneASTElement(el) getAndRemoveAttr(branch1, "v-for", true) addRawAttr(branch1, "type", "radio") processElement(branch1, options) addIfCondition(branch0, { exp: `(${typeBinding})==="radio"` + ifConditionExtra, block: branch1 }) // 3. other const branch2 = cloneASTElement(el) getAndRemoveAttr(branch2, "v-for", true) addRawAttr(branch2, ":type", typeBinding) processElement(branch2, options) addIfCondition(branch0, { exp: ifCondition, block: branch2 }) if (hasElse) { branch0.else = true } else if (elseIfCondition) { branch0.elseif = elseIfCondition } return branch0 } } } function cloneASTElement (el) { return createASTElement(el.tag, el.attrsList.slice(), el.parent) } export default { preTransformNode }processFor
extend(el, res),el.for, el.alias, el.iterator1, el.iterator2
export function processFor (el: ASTElement) { let exp if ((exp = getAndRemoveAttr(el, "v-for"))) { const res = parseFor(exp) if (res) { extend(el, res) } else if (process.env.NODE_ENV !== "production") { warn( `Invalid v-for expression: ${exp}` ) } } }parseFor
这个函数解析v-for字符串,并返回,比如
item in items返回{ for: items, alias: item };
(item, index) in items返回{ for: items, alias: item, iterator1: index };
(item, key, index) in items返回{ for: items, alias: item, iterator1: key, iterator2: index };
/** * item in items *?最小贪婪匹配,如 a in b in c 则匹配 * a 而不是 a in b, in item 则匹配 "" */ export const forAliasRE = /([^]*?)s+(?:in|of)s+([^]*)/ /** * 匹配多个参数。 */ export const forIteratorRE = /,([^,}]]*)(?:,([^,}]]*))?$/ const stripParensRE = /^(|)$/g /** 解析v-for表达式,并返回 */ export function parseFor (exp: string): ?ForParseResult { const inMatch = exp.match(forAliasRE) if (!inMatch) return const res = {} res.for = inMatch[2].trim() const alias = inMatch[1].trim().replace(stripParensRE, "") const iteratorMatch = alias.match(forIteratorRE) if (iteratorMatch) { res.alias = alias.replace(forIteratorRE, "") res.iterator1 = iteratorMatch[1].trim() if (iteratorMatch[2]) { res.iterator2 = iteratorMatch[2].trim() } } else { res.alias = alias } return res }processElement
export function processElement (element: ASTElement, options: CompilerOptions) { processKey(element) // determine whether this is a plain element after // removing structural attributes element.plain = !element.key && !element.attrsList.length processRef(element) processSlot(element) processComponent(element) /** * 赋值 el.staticClass, classBinding, staticStyle, styleBinding */ for (let i = 0; i < transforms.length; i++) { element = transforms[i](element, options) || element } /** 处理attrsList 剩余属性 */ processAttrs(element) }processSlot
如果el.tag === slot,获取el的name并赋值给slotname属性
不是
获取元素的动态slot,如果不是template且slotScope属性不存在,则给el的attrs数组属性增加{name: "slot", value: slotTarget}
function processSlot (el) { if (el.tag === "slot") { el.slotName = getBindingAttr(el, "name") if (process.env.NODE_ENV !== "production" && el.key) { warn( ``key` does not work onprocessComponentbecause slots are abstract outlets ` + `and can possibly expand into multiple elements. ` + `Use the key on a wrapping element instead.` ) } } else { let slotScope if (el.tag === "template") { slotScope = getAndRemoveAttr(el, "scope") /* istanbul ignore if */ if (process.env.NODE_ENV !== "production" && slotScope) { warn( `the "scope" attribute for scoped slots have been deprecated and ` + `replaced by "slot-scope" since 2.5. The new "slot-scope" attribute ` + `can also be used on plain elements in addition to to ` + `denote scoped slots.`, true ) } el.slotScope = slotScope || getAndRemoveAttr(el, "slot-scope") } else if ((slotScope = getAndRemoveAttr(el, "slot-scope"))) { /* istanbul ignore if */ if (process.env.NODE_ENV !== "production" && el.attrsMap["v-for"]) { warn( `Ambiguous combined usage of slot-scope and v-for on <${el.tag}> ` + `(v-for takes higher priority). Use a wrapper for the ` + `scoped slot to make it clearer.`, true ) } el.slotScope = slotScope } const slotTarget = getBindingAttr(el, "slot") if (slotTarget) { el.slotTarget = slotTarget === """" ? ""default"" : slotTarget // preserve slot as an attribute for native shadow DOM compat // only for non-scoped slots. if (el.tag !== "template" && !el.slotScope) { addAttr(el, "slot", slotTarget) } } } }
function processComponent (el) { let binding if ((binding = getBindingAttr(el, "is"))) { el.component = binding } if (getAndRemoveAttr(el, "inline-template") != null) { el.inlineTemplate = true } }processAttrs
对于动态绑定属性,首先截取修饰符,并根据修饰符修饰name,增加事件等,再根据属性名来判断是绑定props还是attrs,需要动态更新的绑定props。
对于普通属性,直接增加attr,muted除外,因为这个属性如果使用attr无法触发更新
function processAttrs (el) { const list = el.attrsList let i, l, name, rawName, value, modifiers, isProp for (i = 0, l = list.length; i < l; i++) { name = rawName = list[i].name value = list[i].value if (dirRE.test(name)) { // mark element as dynamic el.hasBindings = true // modifiers :input.number modifiers = parseModifiers(name) if (modifiers) { name = name.replace(modifierRE, "") // :input } if (bindRE.test(name)) { // v-bind name = name.replace(bindRE, "") // input value = parseFilters(value) // _f("filter1")(value) isProp = false if (modifiers) { if (modifiers.prop) { isProp = true name = camelize(name) if (name === "innerHtml") name = "innerHTML" } if (modifiers.camel) { name = camelize(name) } // 双向绑定,通过emit事件来触发 if (modifiers.sync) { /** 在el的event或者nativeevent中添加事件 * el.event.value = {value: value=$event} || [...] */ addHandler( el, `update:${camelize(name)}`, genAssignmentCode(value, `$event`) // "value=$event" ) } } if (isProp || ( !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name) )) { addProp(el, name, value) } else { addAttr(el, name, value) } } else if (onRE.test(name)) { // v-on name = name.replace(onRE, "") addHandler(el, name, value, modifiers, false, warn) } else { // normal directives name = name.replace(dirRE, "") // parse arg const argMatch = name.match(argRE) const arg = argMatch && argMatch[1] if (arg) { name = name.slice(0, -(arg.length + 1)) } addDirective(el, name, rawName, value, arg, modifiers) if (process.env.NODE_ENV !== "production" && name === "model") { checkForAliasModel(el, value) } } } else { // literal attribute if (process.env.NODE_ENV !== "production") { const res = parseText(value, delimiters) if (res) { warn( `${name}="${value}": ` + "Interpolation inside attributes has been removed. " + "Use v-bind or the colon shorthand instead. For example, " + "instead of, use." ) } } addAttr(el, name, JSON.stringify(value)) // #6887 firefox doesn"t update muted state if set via attribute // even immediately after element creation if (!el.component && name === "muted" && platformMustUseProp(el.tag, el.attrsMap.type, name)) { addProp(el, name, "true") } } } } parseModel/** * Parse a v-model expression into a base path and a final key segment. * Handles both dot-path and possible square brackets. * * Possible cases: * * - test {exp: "test", key: null} * - test[key] {exp: "test", key: "key"} * - test[test1[key]] {exp: "test", key: "test1[key]"} * - test["a"][key] {exp: "test["a"]", key: "key"} * - xxx.test[a[a].test1[key]] {exp: "xxx.test", key: "a[a].test1[key]"} * - test.xxx.a["asa"][test1[key]] {exp: "test.xxx.a["asa"]", key: "test1[key]"} * */ let len, str, chr, index, expressionPos, expressionEndPos type ModelParseResult = { exp: string, key: string | null } /** 获取最靠后的一个完整项为key */ export function parseModel (val: string): ModelParseResult { // Fix https://github.com/vuejs/vue/pull/7730 // allow v-model="obj.val " (trailing whitespace) val = val.trim() len = val.length if (val.indexOf("[") < 0 || val.lastIndexOf("]") < len - 1) { index = val.lastIndexOf(".") if (index > -1) { return { exp: val.slice(0, index), key: """ + val.slice(index + 1) + """ } } else { return { exp: val, key: null } } } str = val index = expressionPos = expressionEndPos = 0 while (!eof()) { chr = next() /* istanbul ignore if */ if (isStringStart(chr)) { parseString(chr) } else if (chr === 0x5B) { // [ parseBracket(chr) } } return { exp: val.slice(0, expressionPos), key: val.slice(expressionPos + 1, expressionEndPos) } } function next (): number { return str.charCodeAt(++index) } function eof (): boolean { return index >= len } function isStringStart (chr: number): boolean { return chr === 0x22 || chr === 0x27 } function parseBracket (chr: number): void { let inBracket = 1 expressionPos = index while (!eof()) { chr = next() if (isStringStart(chr)) { parseString(chr) continue } if (chr === 0x5B) inBracket++ if (chr === 0x5D) inBracket-- if (inBracket === 0) { expressionEndPos = index break } } } function parseString (chr: number): void { const stringQuote = chr while (!eof()) { chr = next() if (chr === stringQuote) { break } } }model根据不同的tag类型,将v-model转化成不同的事件绑定onchange、oninput等
export default function model ( el: ASTElement, dir: ASTDirective, _warn: Function ): ?boolean { warn = _warn const value = dir.value const modifiers = dir.modifiers const tag = el.tag const type = el.attrsMap.type if (process.env.NODE_ENV !== "production") { // inputs with type="file" are read only and setting the input"s // value will throw an error. if (tag === "input" && type === "file") { warn( `<${el.tag} v-model="${value}" type="file">: ` + `File inputs are read only. Use a v-on:change listener instead.` ) } } if (el.component) { genComponentModel(el, value, modifiers) // component v-model doesn"t need extra runtime return false } else if (tag === "select") { genSelect(el, value, modifiers) } else if (tag === "input" && type === "checkbox") { genCheckboxModel(el, value, modifiers) } else if (tag === "input" && type === "radio") { genRadioModel(el, value, modifiers) } else if (tag === "input" || tag === "textarea") { genDefaultModel(el, value, modifiers) } else if (!config.isReservedTag(tag)) { genComponentModel(el, value, modifiers) // component v-model doesn"t need extra runtime return false } else if (process.env.NODE_ENV !== "production") { warn( `<${el.tag} v-model="${value}">: ` + `v-model is not supported on this element type. ` + "If you are working with contenteditable, it"s recommended to " + "wrap a library dedicated for that purpose inside a custom component." ) } // ensure runtime directive metadata return true }genComponentModel返回设置value值的一个对象集合
export function genComponentModel ( el: ASTElement, value: string, modifiers: ?ASTModifiers ): ?boolean { const { number, trim } = modifiers || {} const baseValueExpression = "$$v" let valueExpression = baseValueExpression if (trim) { valueExpression = `(typeof ${baseValueExpression} === "string"` + `? ${baseValueExpression}.trim()` + `: ${baseValueExpression})` } if (number) { valueExpression = `_n(${valueExpression})` } const assignment = genAssignmentCode(value, valueExpression) el.model = { value: `(${value})`, expression: `"${value}"`, callback: `function (${baseValueExpression}) {${assignment}}` // 类似 function ($$v) { data=_n($$v.tirm()) } } }genAssignmentCode/** * Cross-platform codegen helper for generating v-model value assignment code. * 返回类似 data=_n($$v.tirm()); * $set(data, key, _n($$v.tirm())); */ export function genAssignmentCode ( value: string, assignment: string ): string { const res = parseModel(value) if (res.key === null) { return `${value}=${assignment}` } else { return `$set(${res.exp}, ${res.key}, ${assignment})` } }addHandler首先检测modifiers参数是否存在,并根据参数中特定属性去重写name参数
再根据是否有native修饰符来决定向el.nativeEvents还是el.events添加对应名称的属性值{value:
value.trim()}export function addHandler ( el: ASTElement, name: string, value: string, modifiers: ?ASTModifiers, important?: boolean, warn?: Function ) { modifiers = modifiers || emptyObject // warn prevent and passive modifier /* istanbul ignore if */ if ( process.env.NODE_ENV !== "production" && warn && modifiers.prevent && modifiers.passive ) { warn( "passive and prevent can"t be used together. " + "Passive handler can"t prevent default event." ) } // check capture modifier if (modifiers.capture) { delete modifiers.capture name = "!" + name // mark the event as captured } if (modifiers.once) { delete modifiers.once name = "~" + name // mark the event as once } /* istanbul ignore if */ if (modifiers.passive) { delete modifiers.passive name = "&" + name // mark the event as passive } // normalize click.right and click.middle since they don"t actually fire // this is technically browser-specific, but at least for now browsers are // the only target envs that have right/middle clicks. if (name === "click") { if (modifiers.right) { name = "contextmenu" delete modifiers.right } else if (modifiers.middle) { name = "mouseup" } } let events if (modifiers.native) { delete modifiers.native events = el.nativeEvents || (el.nativeEvents = {}) } else { events = el.events || (el.events = {}) } const newHandler: any = { value: value.trim() } if (modifiers !== emptyObject) { newHandler.modifiers = modifiers } const handlers = events[name] /* istanbul ignore if */ if (Array.isArray(handlers)) { important ? handlers.unshift(newHandler) : handlers.push(newHandler) } else if (handlers) { events[name] = important ? [newHandler, handlers] : [handlers, newHandler] } else { events[name] = newHandler } el.plain = false }Array.apply(null, { length: 20 })创建可遍历的空数组
canBeLeftOpenTag以下标签如果只写了左侧的tag,浏览器会自动补全
export const canBeLeftOpenTag = makeMap( "colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source" )isUnaryTag自闭合标签
export const isUnaryTag = makeMap( "area,base,br,col,embed,frame,hr,img,input,isindex,keygen," + "link,meta,param,source,track,wbr" )isNonPhrasingTagexport const isNonPhrasingTag = makeMap( "address,article,aside,base,blockquote,body,caption,col,colgroup,dd," + "details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form," + "h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta," + "optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead," + "title,tr,track" )parseStartTag/** 解析起始标签,使用正则匹配attrs,并将匹配到的正则数组放到attrs数组里面 */ function parseStartTag () { // 标签名 const start = html.match(startTagOpen) if (start) { const match = { tagName: start[1], attrs: [], start: index } advance(start[0].length) let end, attr // 解析attr while (!(end = html.match(startTagClose)) && (attr = html.match(dynamicArgAttribute) || html.match(attribute))) { attr.start = index advance(attr[0].length) attr.end = index match.attrs.push(attr) } if (end) { // 是否匹配到自闭合符号/,匹配到则设置标志属性unarySlash="/" match.unarySlash = end[1] advance(end[0].length) match.end = index return match } } }handleStartTag/** 解析上一步获取的正则attrs,保存为{name, value}格式, * 并且将被浏览器转译的换行或特殊字符或者href里面的换行反转为相应符号, * 最后将tagname,attrs等传递给调用函数的start函数 */ function handleStartTag (match) { const tagName = match.tagName const unarySlash = match.unarySlash if (expectHTML) { if (lastTag === "p" && isNonPhrasingTag(tagName)) { parseEndTag(lastTag) } if (canBeLeftOpenTag(tagName) && lastTag === tagName) { parseEndTag(tagName) } } const unary = isUnaryTag(tagName) || !!unarySlash const l = match.attrs.length const attrs = new Array(l) for (let i = 0; i < l; i++) { const args = match.attrs[i] // 优先获取匹配到的第三个正则捕获 const value = args[3] || args[4] || args[5] || "" const shouldDecodeNewlines = tagName === "a" && args[1] === "href" ? options.shouldDecodeNewlinesForHref : options.shouldDecodeNewlines attrs[i] = { name: args[1], value: decodeAttr(value, shouldDecodeNewlines) } if (process.env.NODE_ENV !== "production" && options.outputSourceRange) { attrs[i].start = args.start + args[0].match(/^s*/).length attrs[i].end = args.end } } if (!unary) { stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs, start: match.start, end: match.end }) lastTag = tagName } if (options.start) { options.start(tagName, attrs, unary, match.start, match.end) } }addRawAttr向ast对象里面添加attr{name, value, ...}
// add a raw attr (use this in preTransforms) export function addRawAttr (el: ASTElement, name: string, value: any, range?: Range) { el.attrsMap[name] = value el.attrsList.push(rangeSetItem({ name, value }, range)) }processSlotContent2.6版本取消scope,slot,slot-scope属性,采用v-slot
v-slot仅适用于template或者组件
针对tempalte,给ast赋值:el.slotTarget = name el.slotTargetDynamic = dynamic el.slotScope = slotBinding.value || emptySlotScopeToke针对组件,给ast赋值:
el.scopedSlots = {slotTarget, slotTargetDynamic, slotScope}并且将el不含v-slot属性的child赋值给scopedslots.children,el.children = []
// handle content being passed to a component as slot, // e.g. ,function processSlotContent (el) { let slotScope if (el.tag === "template") { slotScope = getAndRemoveAttr(el, "scope") /* istanbul ignore if */ if (process.env.NODE_ENV !== "production" && slotScope) { warn( `the "scope" attribute for scoped slots have been deprecated and ` + `replaced by "slot-scope" since 2.5. The new "slot-scope" attribute ` + `can also be used on plain elements in addition to to ` + `denote scoped slots.`, el.rawAttrsMap["scope"], true ) } el.slotScope = slotScope || getAndRemoveAttr(el, "slot-scope") } else if ((slotScope = getAndRemoveAttr(el, "slot-scope"))) { /* istanbul ignore if */ if (process.env.NODE_ENV !== "production" && el.attrsMap["v-for"]) { warn( `Ambiguous combined usage of slot-scope and v-for on <${el.tag}> ` + `(v-for takes higher priority). Use a wrapper for the ` + `scoped slot to make it clearer.`, el.rawAttrsMap["slot-scope"], true ) } el.slotScope = slotScope } // slot="xxx" const slotTarget = getBindingAttr(el, "slot") if (slotTarget) { el.slotTarget = slotTarget === """" ? ""default"" : slotTarget el.slotTargetDynamic = !!(el.attrsMap[":slot"] || el.attrsMap["v-bind:slot"]) // preserve slot as an attribute for native shadow DOM compat // only for non-scoped slots. if (el.tag !== "template" && !el.slotScope) { addAttr(el, "slot", slotTarget, getRawBindingAttr(el, "slot")) } } // 2.6 v-slot syntax if (process.env.NEW_SLOT_SYNTAX) { if (el.tag === "template") { // v-slot on const slotBinding = getAndRemoveAttrByRegex(el, slotRE) if (slotBinding) { if (process.env.NODE_ENV !== "production") { if (el.slotTarget || el.slotScope) { warn( `Unexpected mixed usage of different slot syntaxes.`, el ) } if (el.parent && !maybeComponent(el.parent)) { warn( ` can only appear at the root level inside ` + `the receiving the component`, el ) } } const { name, dynamic } = getSlotName(slotBinding) el.slotTarget = name el.slotTargetDynamic = dynamic el.slotScope = slotBinding.value || emptySlotScopeToken // force it into a scoped slot for perf } } else { // v-slot on component, denotes default slot const slotBinding = getAndRemoveAttrByRegex(el, slotRE) if (slotBinding) { if (process.env.NODE_ENV !== "production") { if (!maybeComponent(el)) { warn( `v-slot can only be used on components or .`, slotBinding ) } if (el.slotScope || el.slotTarget) { warn( `Unexpected mixed usage of different slot syntaxes.`, el ) } if (el.scopedSlots) { warn( `To avoid scope ambiguity, the default slot should also use ` + ` syntax when there are other named slots.`, slotBinding ) } } // add the component"s children to its default slot const slots = el.scopedSlots || (el.scopedSlots = {}) const { name, dynamic } = getSlotName(slotBinding) const slotContainer = slots[name] = createASTElement("template", [], el) slotContainer.slotTarget = name slotContainer.slotTargetDynamic = dynamic slotContainer.children = el.children.filter((c: any) => { if (!c.slotScope) { // 内部的slotScope全部无效 c.parent = slotContainer return true } }) slotContainer.slotScope = slotBinding.value || emptySlotScopeToken // remove children as they are returned from scopedSlots now el.children = [] // mark el non-plain so data gets generated el.plain = false } } } } parseStyleText返回style对象
export const parseStyleText = cached(function (cssText) { const res = {} // 负向捕获,匹配后面没有闭括号的分号 const listDelimiter = /;(?![^(]*))/g // split传入正则作为分隔符,正则里面的捕获也会成为数组的数组项 const propertyDelimiter = /:(.+)/ cssText.split(listDelimiter).forEach(function (item) { if (item) { const tmp = item.split(propertyDelimiter) tmp.length > 1 && (res[tmp[0].trim()] = tmp[1].trim()) } }) return res })文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/100746.html
相关文章
第7期 Datawhale 组队学习计划
马上就要开始啦这次共组织15个组队学习 涵盖了AI领域从理论知识到动手实践的内容 按照下面给出的最完备学习路线分类 难度系数分为低、中、高三档 可以按照需要参加 - 学习路线 - showImg(https://segmentfault.com/img/remote/1460000019082128); showImg(https://segmentfault.com/img/remote/...
慕课网js面试题学习笔记(ES6 标准) ——实时更新
摘要:而第一种方法只能判断引用类型,不能判断值类型,因为值类型没有对应的构造函数描述一个对象的过程生成一个新的空对象指向这个新对象执行构造函数中的代码,即对赋值将新对象的属性指向构造函数的属性返回,即得到新对象。 最近在在看前端面试教程,这篇文章里大部分是看视频的过程中自己遇到的不清楚的知识点,内容很简单,只是起到一个梳理作用。有些地方也根据自己的理解在作者的基础上加了点东西,如有错误,欢迎...
前端补集 - 收藏集 - 掘金
摘要:原文地址一个非常适合入门学习的博客项目前端掘金一个非常适合入门学习的项目,代码清晰结构合理新闻前端掘金介绍一个由编写的新闻。深入浅出读书笔记知乎专栏前端专栏前端掘金去年的一篇老文章,恰好今天专栏开通,迁移过来。 破解前端面试(80% 应聘者不及格系列):从闭包说起 - 掘金修订说明:发布《80% 应聘者都不及格的 JS 面试题》之后,全网阅读量超过 6W,在知乎、掘金、cnodejs ...
前端补集 - 收藏集 - 掘金
摘要:原文地址一个非常适合入门学习的博客项目前端掘金一个非常适合入门学习的项目,代码清晰结构合理新闻前端掘金介绍一个由编写的新闻。深入浅出读书笔记知乎专栏前端专栏前端掘金去年的一篇老文章,恰好今天专栏开通,迁移过来。 破解前端面试(80% 应聘者不及格系列):从闭包说起 - 掘金修订说明:发布《80% 应聘者都不及格的 JS 面试题》之后,全网阅读量超过 6W,在知乎、掘金、cnodejs ...
发表评论
0条评论
tuniutech
男|高级讲师
TA的文章
阅读更多
因S3配置错误,Wi-Fi 管理软件公司泄漏数百万用户数据
阅读 2839·2021-11-25 09:43
自动化测试工具
阅读 998·2021-10-11 10:57
Serverless容器实例Cube全面升级 快杰版计算性能提升16%!
阅读 2492·2020-12-03 17:20
解决swiper.js中无缝轮播的问题。
阅读 3738·2019-08-30 14:05
前端面试总结
阅读 2432·2019-08-29 14:00
ie百分比的圆
阅读 2003·2019-08-29 12:37
Vue学习日记(三)——Vue路由管理vue-router
阅读 1675·2019-08-26 11:34
EasyUI编辑单元格扩展方法快捷键触发,上下键选择行。单击单元格启动编辑,键盘上下键tab键shi
阅读 3220·2019-08-26 10:27