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 })文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/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 管理軟件公司泄漏數百萬用戶數據
閱讀 2826·2021-11-25 09:43
自動化測試工具
閱讀 978·2021-10-11 10:57
Serverless容器實例Cube全面升級 快杰版計算性能提升16%!
閱讀 2477·2020-12-03 17:20
解決swiper.js中無縫輪播的問題。
閱讀 3716·2019-08-30 14:05
前端面試總結
閱讀 2422·2019-08-29 14:00
ie百分比的圓
閱讀 1991·2019-08-29 12:37
Vue學習日記(三)——Vue路由管理vue-router
閱讀 1661·2019-08-26 11:34
EasyUI編輯單元格擴展方法快捷鍵觸發,上下鍵選擇行。單擊單元格啟動編輯,鍵盤上下鍵tab鍵shi
閱讀 3201·2019-08-26 10:27