博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【vuejs深入二】vue源码解析之一,基础源码结构和htmlParse解析器
阅读量:4319 次
发布时间:2019-06-06

本文共 30722 字,大约阅读时间需要 102 分钟。

写在前面

  一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残。

  vuejs是一个优秀的前端mvvm框架,它的易用性和渐进式的理念可以使每一个前端开发人员感到舒服,感到easy。它内部的实现机制值得让我们深究,比如obServer的实现原理,为什么vue能够实现组件化等等,我们需要理解它内部的运行机制,代码结构,这样才能更深入的理解vue的优秀之处,能更好的贴合业务实际写出更恰当的代码。

  说明

    在展开本章之前,博主需要对自己看的源码文件进行一个简短的说明:

      博主最终选择首先阅读 Vue.js v2.1.3 这个版本的文件,什么文件呢? 不是基于es6的。 单独导入的。

      

    为什么大费周章要看这样的前端版本呢? 我给出的理由是,首先阅读目前浏览器中运行的js更符合渐进阅读的原则,有些es6的代码其实是js的语法糖,个人觉得语法糖吃的太多会长蛀虫,失了真正语言的精髓。当然完全使用es6博主完全没有意见,事实上博主工作中首选es6。但是在目前看来,先理解浏览器中的代码工作机制,再看es6的代码效果会更好,更贴切实际应用。并且博主会基于这个版本临摹一个简单点的vue,当理解充分后引入es6进行深入学习。

Vue.js的架构一

  

 

  一张图可以简略的了解vue的架构,当我们new Vue的时候,实际new的是Vue$3这个构造函数,紧接着,经过一系列判断处理,调用_init函数。 _init哪儿来的呢?

  通过initMinxin的调用,给当前Vue$3的prototype混入一个_init方法:

  

  在这个_init中经过六个步骤的操作,最终完成nw Vue()的操作:

  

  1.mergeOptions 合并策略对象,作用是合并父子组件options,它的作用是控制输出。即宽松输入,严格输出,通过一系列合并改装策略,将选项属性最终挂载到vm.$options上完成输出操作。

  2.initProxy初始化代理Proxy,它是为了后期的_render,使render时this指向proxy对象

  3.initLifecycle 初始化生命周期  这里很有意思

  4.initEvent 初始化事件,初始化生命周期之后,紧接着初始化事件处理,再紧接着代码里就可以看出来了,callHook调用事件。我们日常使用的beforeCreate、created分别在这调用

   5.initState 初始化data和props,我们的observer 发布订阅系统在这里实现。

  6.initRender 开始render

vue源码解析之一 htmlParse

  Virtual dom

    用过vue和react的人对虚拟dom这个概念应该大都不会太陌生了,它将真实的dom转换为AST节点,也就是转化成对象树的形式,当我们通过api操作dom时实际上是操作虚拟对象树,再由框架通过算法完成真实dom的转换,spa页面也是因为Virtual dom的出现渐渐出现在我们前端开发的选择中,今天博主暂时不过多介绍,这里我们来看看最基础的几个问题,html是怎么被解析成object的?指令的操作,变量的转换,运算符的操作。其实就是模板引擎的实现方式是怎样的?

  htmlparser

    在vue的源码中我们可以看到,vue中有一个parse函数:

    

    注释上的意思,这里将html格式的字符串转换为AST。

    整个vuejs文件八千行,这个parse函数功能占用了一千两百多行,可见这套转换逻辑在vue中的关键性,重要性,博主把它源代码抽了出来,可以直接调用:

    

var hasProto = '__proto__' in {};// Browser environment sniffingvar inBrowser =  typeof window !== 'undefined' &&  Object.prototype.toString.call(window) !== '[object Object]';var UA = inBrowser && window.navigator.userAgent.toLowerCase();var isIE = UA && /msie|trident/.test(UA);var isIE9 = UA && UA.indexOf('msie 9.0') > 0;var isEdge = UA && UA.indexOf('edge/') > 0;var isAndroid = UA && UA.indexOf('android') > 0;var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA);function makeMap(keys,expectsLowerCase){  var _map = {};  for(var i=0;i
/=]+)/;var singleAttrAssign = /(?:=)/;var singleAttrValues = [ // attr value double quotes /"([^"]*)"+/.source, // attr value, single quotes /'([^']*)'+/.source, // attr value, no quotes /([^\s"'=<>`]+)/.source];var attribute = new RegExp( '^\\s*' + singleAttrIdentifier.source + '(?:\\s*(' + singleAttrAssign.source + ')' + '\\s*(?:' + singleAttrValues.join('|') + '))?');// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName// but for Vue templates we can enforce a simple charsetvar ncname = '[a-zA-Z_][\\w\\-\\.]*';var qnameCapture = '((?:' + ncname + '\\:)?' + ncname + ')';var startTagOpen = new RegExp('^<' + qnameCapture);var startTagClose = /^\s*(\/?)>/;var endTag = new RegExp('^<\\/' + qnameCapture + '[^>]*>');var doctype = /^]+>/i;var comment = /^
'); if (commentEnd >= 0) { advance(commentEnd + 3); continue } } // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment if (conditionalComment.test(html)) { var conditionalEnd = html.indexOf(']>'); if (conditionalEnd >= 0) { advance(conditionalEnd + 2); continue } } // Doctype: var doctypeMatch = html.match(doctype); if (doctypeMatch) { advance(doctypeMatch[0].length); continue } // End tag: var endTagMatch = html.match(endTag); if (endTagMatch) { var curIndex = index; advance(endTagMatch[0].length); parseEndTag(endTagMatch[0], endTagMatch[1], curIndex, index); continue } // Start tag: var startTagMatch = parseStartTag(); if (startTagMatch) { handleStartTag(startTagMatch); continue } } var text = (void 0), rest$1 = (void 0), next = (void 0); if (textEnd > 0) { rest$1 = html.slice(textEnd); while ( !endTag.test(rest$1) && !startTagOpen.test(rest$1) && !comment.test(rest$1) && !conditionalComment.test(rest$1) ) { // < in plain text, be forgiving and treat it as text next = rest$1.indexOf('<', 1); if (next < 0) { break } textEnd += next; rest$1 = html.slice(textEnd); } text = html.substring(0, textEnd); advance(textEnd); } if (textEnd < 0) { text = html; html = ''; } if (options.chars && text) { options.chars(text); } } else { var stackedTag = lastTag.toLowerCase(); var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(
]*>)', 'i')); var endTagLength = 0; var rest = html.replace(reStackedTag, function (all, text, endTag) { endTagLength = endTag.length; if (stackedTag !== 'script' && stackedTag !== 'style' && stackedTag !== 'noscript') { text = text .replace(/
/g, '$1') .replace(/
/g, '$1'); } if (options.chars) { options.chars(text); } return '' }); index += html.length - rest.length; html = rest; parseEndTag('
', stackedTag, index - endTagLength, index); } if (html === last && options.chars) { options.chars(html); break } } // Clean up any remaining tags parseEndTag(); function advance (n) { index += n; html = html.substring(n); } function parseStartTag () { var start = html.match(startTagOpen); if (start) { var match = { tagName: start[1], attrs: [], start: index }; advance(start[0].length); var end, attr; while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { advance(attr[0].length); match.attrs.push(attr); } if (end) { match.unarySlash = end[1]; advance(end[0].length); match.end = index; return match } } } function handleStartTag (match) { var tagName = match.tagName; var unarySlash = match.unarySlash; if (expectHTML) { if (lastTag === 'p' && isNonPhrasingTag(tagName)) { parseEndTag('', lastTag); } if (canBeLeftOpenTag(tagName) && lastTag === tagName) { parseEndTag('', tagName); } } var unary = isUnaryTag$$1(tagName) || tagName === 'html' && lastTag === 'head' || !!unarySlash; var l = match.attrs.length; var attrs = new Array(l); for (var i = 0; i < l; i++) { var args = match.attrs[i]; // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778 if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) { if (args[3] === '') { delete args[3]; } if (args[4] === '') { delete args[4]; } if (args[5] === '') { delete args[5]; } } var value = args[3] || args[4] || args[5] || ''; attrs[i] = { name: args[1], value: decodeAttr( value, options.shouldDecodeNewlines ) }; } if (!unary) { stack.push({ tag: tagName, attrs: attrs }); lastTag = tagName; unarySlash = ''; } if (options.start) { options.start(tagName, attrs, unary, match.start, match.end); } } function parseEndTag (tag, tagName, start, end) { var pos; if (start == null) { start = index; } if (end == null) { end = index; } // Find the closest opened tag of the same type if (tagName) { var needle = tagName.toLowerCase(); for (pos = stack.length - 1; pos >= 0; pos--) { if (stack[pos].tag.toLowerCase() === needle) { break } } } else { // If no tag name is provided, clean shop pos = 0; } if (pos >= 0) { // Close all the open elements, up the stack for (var i = stack.length - 1; i >= pos; i--) { if (options.end) { options.end(stack[i].tag, start, end); } } // Remove the open elements from the stack stack.length = pos; lastTag = pos && stack[pos - 1].tag; } else if (tagName.toLowerCase() === 'br') { if (options.start) { options.start(tagName, [], true, start, end); } } else if (tagName.toLowerCase() === 'p') { if (options.start) { options.start(tagName, [], false, start, end); } if (options.end) { options.end(tagName, start, end); } } }}/* */function parseFilters (exp) { var inSingle = false; var inDouble = false; var inTemplateString = false; var inRegex = false; var curly = 0; var square = 0; var paren = 0; var lastFilterIndex = 0; var 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) { if (c === 0x2f && prev !== 0x5C) { inRegex = false; } } else if ( c === 0x7C && // pipe 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 0x2f: inRegex = 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 (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, filter) { var i = filter.indexOf('('); if (i < 0) { // _f: resolveFilter return ("_f(\"" + filter + "\")(" + exp + ")") } else { var name = filter.slice(0, i); var args = filter.slice(i + 1); return ("_f(\"" + name + "\")(" + exp + "," + args) }}/* */var defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g;var regexEscapeRE = /[-.*+?^${}()|[\]/\\]/g;var buildRegex = cached(function (delimiters) { var open = delimiters[0].replace(regexEscapeRE, '\\$&'); var close = delimiters[1].replace(regexEscapeRE, '\\$&'); return new RegExp(open + '((?:.|\\n)+?)' + close, 'g')});function parseText ( text, delimiters) { var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE; if (!tagRE.test(text)) { return } var tokens = []; var lastIndex = tagRE.lastIndex = 0; var match, index; while ((match = tagRE.exec(text))) { index = match.index; // push text token if (index > lastIndex) { tokens.push(JSON.stringify(text.slice(lastIndex, index))); } // tag token var exp = parseFilters(match[1].trim()); tokens.push(("_s(" + exp + ")")); lastIndex = index + match[0].length; } if (lastIndex < text.length) { tokens.push(JSON.stringify(text.slice(lastIndex))); } return tokens.join('+')}/* */function baseWarn (msg) { console.error(("[Vue parser]: " + msg));}function pluckModuleFunction ( modules, key) { return modules ? modules.map(function (m) { return m[key]; }).filter(function (_) { return _; }) : []}function addProp (el, name, value) { (el.props || (el.props = [])).push({ name: name, value: value });}function addAttr (el, name, value) { (el.attrs || (el.attrs = [])).push({ name: name, value: value });}function addDirective ( el, name, rawName, value, arg, modifiers) { (el.directives || (el.directives = [])).push({ name: name, rawName: rawName, value: value, arg: arg, modifiers: modifiers });}function addHandler ( el, name, value, modifiers, important) { // check capture modifier if (modifiers && modifiers.capture) { delete modifiers.capture; name = '!' + name; // mark the event as captured } var events; if (modifiers && modifiers.native) { delete modifiers.native; events = el.nativeEvents || (el.nativeEvents = {}); } else { events = el.events || (el.events = {}); } var newHandler = { value: value, modifiers: modifiers }; var 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; }}function getBindingAttr ( el, name, getStatic) { var dynamicValue = getAndRemoveAttr(el, ':' + name) || getAndRemoveAttr(el, 'v-bind:' + name); if (dynamicValue != null) { return parseFilters(dynamicValue) } else if (getStatic !== false) { var staticValue = getAndRemoveAttr(el, name); if (staticValue != null) { return JSON.stringify(staticValue) } }}function getAndRemoveAttr (el, name) { var val; if ((val = el.attrsMap[name]) != null) { var list = el.attrsList; for (var i = 0, l = list.length; i < l; i++) { if (list[i].name === name) { list.splice(i, 1); break } } } return val}var len;var str;var chr;var index$1;var expressionPos;var expressionEndPos;/** * parse directive model to do the array update transform. a[idx] = val => $$a.splice($$idx, 1, val) * * for loop possible cases: * * - test * - test[idx] * - test[test1[idx]] * - test["a"][idx] * - xxx.test[a[a].test1[idx]] * - test.xxx.a["asa"][test1[idx]] * */function parseModel (val) { str = val; len = str.length; index$1 = expressionPos = expressionEndPos = 0; if (val.indexOf('[') < 0 || val.lastIndexOf(']') < len - 1) { return { exp: val, idx: null } } while (!eof()) { chr = next(); /* istanbul ignore if */ if (isStringStart(chr)) { parseString(chr); } else if (chr === 0x5B) { parseBracket(chr); } } return { exp: val.substring(0, expressionPos), idx: val.substring(expressionPos + 1, expressionEndPos) }}function next () { return str.charCodeAt(++index$1)}function eof () { return index$1 >= len}function isStringStart (chr) { return chr === 0x22 || chr === 0x27}function parseBracket (chr) { var inBracket = 1; expressionPos = index$1; while (!eof()) { chr = next(); if (isStringStart(chr)) { parseString(chr); continue } if (chr === 0x5B) { inBracket++; } if (chr === 0x5D) { inBracket--; } if (inBracket === 0) { expressionEndPos = index$1; break } }}function parseString (chr) { var stringQuote = chr; while (!eof()) { chr = next(); if (chr === stringQuote) { break } }}/* */var dirRE = /^v-|^@|^:/;var forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/;var forIteratorRE = /\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/;var bindRE = /^:|^v-bind:/;var onRE = /^@|^v-on:/;var argRE = /:(.*)$/;var modifierRE = /\.[^.]+/g;var decodeHTMLCached = cached(decode);// configurable statevar warn$1;var platformGetTagNamespace;var platformMustUseProp;var platformIsPreTag;var preTransforms;var transforms;var postTransforms;var delimiters;var no = function(){ return false;};/** * Convert HTML string to AST. */function parse ( template, options) { warn$1 = options.warn || baseWarn; platformGetTagNamespace = options.getTagNamespace || no; platformMustUseProp = options.mustUseProp || no; platformIsPreTag = options.isPreTag || no; preTransforms = pluckModuleFunction(options.modules, 'preTransformNode'); transforms = pluckModuleFunction(options.modules, 'transformNode'); postTransforms = pluckModuleFunction(options.modules, 'postTransformNode'); delimiters = options.delimiters; var stack = []; var preserveWhitespace = options.preserveWhitespace !== false; var root; var currentParent; var inVPre = false; var inPre = false; var warned = false; parseHTML(template, { expectHTML: options.expectHTML, isUnaryTag: options.isUnaryTag, shouldDecodeNewlines: options.shouldDecodeNewlines, start: function start (tag, attrs, unary) { // check namespace. // inherit parent ns if there is one var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag); // handle IE svg bug /* istanbul ignore if */ if (isIE && ns === 'svg') { attrs = guardIESVGBug(attrs); } var element = { type: 1, tag: tag, attrsList: attrs, attrsMap: makeAttrsMap(attrs), parent: currentParent, children: [] }; if (ns) { element.ns = ns; } if (isForbiddenTag(element) && !isServerRendering()) { element.forbidden = true; "development" !== 'production' && warn$1( 'Templates should only be responsible for mapping the state to the ' + 'UI. Avoid placing tags with side-effects in your templates, such as ' + "<" + tag + ">." ); } // apply pre-transforms for (var i = 0; i < preTransforms.length; i++) { preTransforms[i](element, options); } if (!inVPre) { processPre(element); if (element.pre) { inVPre = true; } } if (platformIsPreTag(element.tag)) { inPre = true; } if (inVPre) { processRawAttrs(element); } else { processFor(element); processIf(element); processOnce(element); processKey(element); // determine whether this is a plain element after // removing structural attributes element.plain = !element.key && !attrs.length; processRef(element); processSlot(element); processComponent(element); for (var i$1 = 0; i$1 < transforms.length; i$1++) { transforms[i$1](element, options); } processAttrs(element); } function checkRootConstraints (el) { if ("development" !== 'production' && !warned) { if (el.tag === 'slot' || el.tag === 'template') { warned = true; warn$1( "Cannot use <" + (el.tag) + "> as component root element because it may " + 'contain multiple nodes:\n' + template ); } if (el.attrsMap.hasOwnProperty('v-for')) { warned = true; warn$1( 'Cannot use v-for on stateful component root element because ' + 'it renders multiple elements:\n' + template ); } } } // tree management if (!root) { root = element; checkRootConstraints(root); } else if (!stack.length) { // allow root elements with v-if, v-else-if and v-else if (root.if && (element.elseif || element.else)) { checkRootConstraints(element); addIfCondition(root, { exp: element.elseif, block: element }); } else if ("development" !== 'production' && !warned) { warned = true; warn$1( "Component template should contain exactly one root element:" + "\n\n" + template + "\n\n" + "If you are using v-if on multiple elements, " + "use v-else-if to chain them instead." ); } } if (currentParent && !element.forbidden) { if (element.elseif || element.else) { processIfConditions(element, currentParent); } else if (element.slotScope) { // scoped slot currentParent.plain = false; var name = element.slotTarget || 'default';(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element; } else { currentParent.children.push(element); element.parent = currentParent; } } if (!unary) { currentParent = element; stack.push(element); } // apply post-transforms for (var i$2 = 0; i$2 < postTransforms.length; i$2++) { postTransforms[i$2](element, options); } }, end: function end () { // remove trailing whitespace var element = stack[stack.length - 1]; var lastNode = element.children[element.children.length - 1]; if (lastNode && lastNode.type === 3 && lastNode.text === ' ') { element.children.pop(); } // pop stack stack.length -= 1; currentParent = stack[stack.length - 1]; // check pre state if (element.pre) { inVPre = false; } if (platformIsPreTag(element.tag)) { inPre = false; } }, chars: function chars (text) { if (!currentParent) { if ("development" !== 'production' && !warned && text === template) { warned = true; warn$1( 'Component template requires a root element, rather than just text:\n\n' + template ); } return } // IE textarea placeholder bug /* istanbul ignore if */ if (isIE && currentParent.tag === 'textarea' && currentParent.attrsMap.placeholder === text) { return } text = inPre || text.trim() ? decodeHTMLCached(text) // only preserve whitespace if its not right after a starting tag : preserveWhitespace && currentParent.children.length ? ' ' : ''; if (text) { var expression; if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) { currentParent.children.push({ type: 2, expression: expression, text: text }); } else { currentParent.children.push({ type: 3, text: text }); } } } }); return root}function processRawAttrs (el) { var l = el.attrsList.length; if (l) { var attrs = el.attrs = new Array(l); for (var i = 0; i < l; i++) { attrs[i] = { name: el.attrsList[i].name, value: JSON.stringify(el.attrsList[i].value) }; } } else if (!el.pre) { // non root node in pre blocks with no attributes el.plain = true; }}function processKey (el) { var exp = getBindingAttr(el, 'key'); if (exp) { if ("development" !== 'production' && el.tag === 'template') { warn$1("
View Code

大家可以自行测试,当我们运行

parse("
",{})

这段代码时,返回的结果是:

一个带有attrs和children等各种属性的ast对象。这个应该很好理解,比如我们现在可以将一个div表示成:

{    tag:"div",    attrs:[{name:"id",value:"div1"}],   children:[]      }

对应的dom就应该是:

parse函数实现了这个转换的步骤,通过各种正则适配将html解析成ast对象;vue中有很多定制需求,比如代码:

function processFor (el) {  var exp;  if ((exp = getAndRemoveAttr(el, 'v-for'))) {    var inMatch = exp.match(forAliasRE);    if (!inMatch) {      "development" !== 'production' && warn$1(        ("Invalid v-for expression: " + exp)      );      return    }    el.for = inMatch[2].trim();    var alias = inMatch[1].trim();    var iteratorMatch = alias.match(forIteratorRE);    if (iteratorMatch) {      el.alias = iteratorMatch[1].trim();      el.iterator1 = iteratorMatch[2].trim();      if (iteratorMatch[3]) {        el.iterator2 = iteratorMatch[3].trim();      }    } else {      el.alias = alias;    }  }}

这段代码就是我们所用的v-for指令的基础解析了:让我们看看执行parse("<div v-for='a in b'></div>",{}) 会发生什么呢?

报错提示我们div是根节点不能添加v-for指令,恩,平时vue-for写到根节点的同学应该也有吧,报错是这里发出的。

生成的ast属性上多了两个个key, alias 和 for ,alias是别名的意思,for当然是for的对象了,最后生成的vnode节点对象会有一个context,当前作用域引用,应该会从context中调用这个对象进行for循环。

看到这里其实你可以看出,还有很多指令的实现方式都在这里完成,大家可以复制上面我分离出来的源码细细把玩,甚至你可以基于这个parse实现一个小vue,指令什么的一应具全。 例如v-if:

function processIf (el) {  var 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;    }    var elseif = getAndRemoveAttr(el, 'v-else-if');    if (elseif) {      el.elseif = elseif;    }  }}function processIfConditions (el, parent) {  var prev = findPrevElement(parent.children);  if (prev && prev.if) {    addIfCondition(prev, {      exp: el.elseif,      block: el    });  } else {    warn$1(      "v-" + (el.elseif ? ('else-if="' + el.elseif + '"') : 'else') + " " +      "used on element <" + (el.tag) + "> without corresponding v-if."    );  }}

等等。

稍后,下一章节博主将要实现一个自己的parse,并记录我的实现逻辑,感兴趣的可以持续关注。我觉得可以通过这个出发点去统筹全局,理解vue的设计模型,在理解了observer绑定机制之后,再从parse开始解析,一直到生命周期,事件,等等。

写在后面

  mvvm框架和webpack的出现确实改变了前端的开发方式,使得学习前端变成了一门有着深入学问的课题。在我们日常开发中应该不断地学习,归纳,总结,寻找新的思想,对原有的代码有好的补充和好的改进。

       写的不好,谢谢大家观看。 后续有空会新增更多关于开发的知识分享。  

       如果你有什么疑问,你可以联系我,或者在下方评论。

    

转载于:https://www.cnblogs.com/ztfjs/p/vue2.html

你可能感兴趣的文章
OpenJudge计算概论-与7无关的数
查看>>
proxy-target-class="true" 与proxy-target-class="false"的区别
查看>>
npm安装包很慢
查看>>
jquery是如何清除ajax缓存的
查看>>
Android核心分析(转载)
查看>>
自学_HTML<一>
查看>>
IOS 发布 升级新版本
查看>>
上传绕过补充
查看>>
sql-leetcode Consecutive Numbers
查看>>
C# winform DataGridView操作 (转)
查看>>
一致性Hash算法及使用场景
查看>>
JS - Lexical Structure
查看>>
【2】oracle创建表空间
查看>>
剑指offer-二叉树中和为某一值的路径
查看>>
Java反射机制
查看>>
Python 正则表达式
查看>>
C++ AppendMenu
查看>>
在所选中的物体中取消选中一些物体.txt
查看>>
grid - 网格项目跨行或跨列
查看>>
Shell 基本运算符
查看>>