内容简介:首先在入口文件处,使用template属性或者el属性获取到需要解析的html字符串1.html字符串,如2.如果值以 # 开始,则它将被用作选择符,并使用匹配元素的 innerHTML 作为模板
获取html字符串
首先在入口文件处,使用template属性或者el属性获取到需要解析的html字符串
template
1.html字符串,如
Vue.component('alert-box', { template: ` <div class="demo-alert-box"> <strong>Error!</strong> <slot></slot> </div> ` })
2.如果值以 # 开始,则它将被用作选择符,并使用匹配元素的 innerHTML 作为模板
el
类型:string | Element
通过el获取需要解析的模版
export function query (el: string | Element): Element { if (typeof el === 'string') { const selected = document.querySelector(el) if (!selected) { process.env.NODE_ENV !== 'production' && warn( 'Cannot find element: ' + el ) return document.createElement('div') } return selected } else { return el } }
解析html字符串
通过while循环结合正则表达式逐步匹配拆解html字符串
匹配注释与html声明文件
// Comment: // 添加在root元素下面的comment会被忽略 if (comment.test(html)) { const commentEnd = html.indexOf('-->') if (commentEnd >= 0) { if (options.shouldKeepComment) { options.comment(html.substring(4, commentEnd), index, index + commentEnd + 3) } advance(commentEnd + 3) continue } } // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment // ie comment if (conditionalComment.test(html)) { const conditionalEnd = html.indexOf(']>') if (conditionalEnd >= 0) { advance(conditionalEnd + 2) continue } } // Doctype: // match const doctypeMatch = html.match(doctype) if (doctypeMatch) { advance(doctypeMatch[0].length) continue }
匹配标签起始位置
匹配标签起始位置,startTagOpen匹配到,但是startTagClose匹配失败,那么失败前的html片段就会被抛弃。
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z${unicodeLetters}]*` const qnameCapture = `((?:${ncname}\\:)?${ncname})` const startTagOpen = new RegExp(`^<${qnameCapture}`)
/** 解析起始标签,使用正则匹配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 } } }
const startTagClose = /^\s*(\/?)>/
进一步处理macth对象
/** 解析上一步获取的正则attrs,保存为{name, value}格式, * 并且将被浏览器转译的换行或特殊字符或者href里面的换行反转为相应符号, * 最后将tagname,attrs等传递给调用函数的start函数 */ function handleStartTag (match) { const tagName = match.tagName const unarySlash = match.unarySlash if (expectHTML) { // 如标题标签,不应该被p标签包裹,如果父级标签是p,则提前闭合这个p标签 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 } } // 非自闭合标签,存入stack数组 if (!unary) { stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs, start: match.start, end: match.end }) // 1.修改lastTag,保存堆中的最上层数组项 lastTag = tagName } // 将【匹配到的元素返回给上一级解析 if (options.start) { options.start(tagName, attrs, unary, match.start, match.end) } }
start函数解析标签起始对象
createASTElement
export function createASTElement ( tag: string, attrs: Array<ASTAttr>, parent: ASTElement | void ): ASTElement { return { type: 1, tag, attrsList: attrs, attrsMap: makeAttrsMap(attrs), rawAttrsMap: {}, parent, children: [] } }
start (tag, attrs, unary, start) { // check namespace. // inherit parent ns if there is one const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag) // handle IE svg bug /* istanbul ignore if */ if (isIE && ns === 'svg') { attrs = guardIESVGBug(attrs) } // 将传入的数据转化成ast对象 type=1 let element: ASTElement = createASTElement(tag, attrs, currentParent) if (ns) { element.ns = ns } if (process.env.NODE_ENV !== 'production') { if (options.outputSourceRange) { element.start = start element.rawAttrsMap = element.attrsList.reduce((cumulated, attr) => { cumulated[attr.name] = attr return cumulated }, {}) } attrs.forEach(attr => { if (invalidAttributeRE.test(attr.name)) { warn( `Invalid dynamic argument expression: attribute names cannot contain ` + `spaces, quotes, <, >, / or =.`, { start: attr.start + attr.name.indexOf(`[`), end: attr.start + attr.name.length } ) } }) } if (isForbiddenTag(element) && !isServerRendering()) { element.forbidden = true process.env.NODE_ENV !== 'production' && warn( 'Templates should only be responsible for mapping the state to the ' + 'UI. Avoid placing tags with side-effects in your templates, such as ' + `<${tag}>` + ', as they will not be parsed.', { start: element.start } ) } // apply pre-transforms // 提前解析 <input :type='type' v-model='input' /> for (let i = 0; i < preTransforms.length; i++) { element = preTransforms[i](element, options) || element } // v-pre check if (!inVPre) { processPre(element) if (element.pre) { inVPre = true } } // pre tag if (platformIsPreTag(element.tag)) { inPre = true } // 如果是带有pre属性,跳过解析 if (inVPre) { // el.attrslist => el.attrs processRawAttrs(element) } else if (!element.processed) { // structural directives // 解析v-for= “item in items”,生成element.for,element.alias,element.ite processFor(element) // 解析v-if,v-else-if,v-else;v-if processIf(element) // element.once v-once用于渲染一次组件 processOnce(element) } // 第一个start tag 为root if (!root) { root = element if (process.env.NODE_ENV !== 'production') { // 不能使用slot,template,v-for在root上 checkRootConstraints(root) } } // 非自闭合 if (!unary) { // last <=> currentParent = element [] currentParent = element stack.push(element) } else { closeElement(element) } }
processIf
/** 如果解析到v-if,给element增加if对象,如果解析到else或者v-else-if, * 标记,等到标签闭合的时候做处理。 */ function processIf (el) { const exp = getAndRemoveAttr(el, 'v-if') if (exp) { el.if = exp addIfCondition(el, { exp: exp, block: el }) } else { if (getAndRemoveAttr(el, 'v-else') != null) { el.else = true } const elseif = getAndRemoveAttr(el, 'v-else-if') if (elseif) { el.elseif = elseif } } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 字符串(与数组相进行比较)
- C/C++——字符串数组
- go语言-数组、字符串和切片
- 谈 Golang 中的字符串和字节数组
- 1.11 JavaScript2:变量、数组、字符串
- 动画学数据结构:轻松掌握数组和字符串
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。