内容简介:首先在入口文件处,使用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:变量、数组、字符串
- 动画学数据结构:轻松掌握数组和字符串
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Practical Django Projects, Second Edition
James Bennett / Apress / 2009 / 44.99
Build a django content management system, blog, and social networking site with James Bennett as he introduces version 1.1 of the popular Django framework. You’ll work through the development of ea......一起来看看 《Practical Django Projects, Second Edition》 这本书的介绍吧!