├── Vue流程图.xmind ├── 《实现 Virtual DOM 下的一个 VNode 节点》.js ├── README.md ├── 《Vuex状态管理的工作原理》.js ├── 《响应式系统的基本原理》.js ├── 《响应式系统的依赖收集追踪原理》.js ├── 《批量异步更新策略及 nextTick 原理》.js ├── 《数据状态更新时的差异 diff 及 patch 机制》.js └── 《template 模板是怎样通过 Compile 编译的》.js /Vue流程图.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/VueDemo/HEAD/Vue流程图.xmind -------------------------------------------------------------------------------- /《实现 Virtual DOM 下的一个 VNode 节点》.js: -------------------------------------------------------------------------------- 1 | class VNode { 2 | constructor (tag, data, children, text, elm) { 3 | this.tag = tag; 4 | this.data = data; 5 | this.children = children; 6 | this.text = text; 7 | this.elm = elm; 8 | } 9 | } 10 | 11 | function createEmptyVNode () { 12 | const node = new VNode(); 13 | node.text = ''; 14 | return node; 15 | } 16 | 17 | function createTextVNode (val) { 18 | return new VNode(undefined, undefined, undefined, String(val)); 19 | } 20 | 21 | function cloneVNode (node) { 22 | const cloneVnode = new VNode( 23 | node.tag, 24 | node.data, 25 | node.children, 26 | node.text, 27 | node.elm 28 | ); 29 | 30 | return cloneVnode; 31 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Vue Demo 2 | 3 | 掘金小册[《剖析 Vue.js 内部运行机制》](https://juejin.im/book/5a36661851882538e2259c0f)配套Demo。 4 | 5 |  6 | 7 | 如对内容有任何疑问,请提[issue](https://github.com/answershuto/VueDemo/issues)或者直接联系我[染陌](https://github.com/answershuto)。 8 | 9 | ## 关于作者 10 | 11 | 作者: 染陌 12 | 13 | 微信: answershuto 14 | 15 | Email:answershuto@gmail.com or answershuto@126.com 16 | 17 | Github: [https://github.com/answershuto](https://github.com/answershuto) 18 | 19 | 掘金:[https://juejin.im/user/58f87ae844d9040069ca7507](https://juejin.im/user/58f87ae844d9040069ca7507) 20 | 21 | 知乎:[https://www.zhihu.com/people/cao-yang-49/activities](https://www.zhihu.com/people/cao-yang-49/activities) 22 | 23 | 知乎专栏: [https://zhuanlan.zhihu.com/ranmo](https://zhuanlan.zhihu.com/ranmo) 24 | -------------------------------------------------------------------------------- /《Vuex状态管理的工作原理》.js: -------------------------------------------------------------------------------- 1 | let Vue; 2 | 3 | class Store { 4 | constructor () { 5 | this._vm = new Vue({ 6 | data: { 7 | $$state: this.state 8 | } 9 | }) 10 | } 11 | 12 | commit (type, payload, _options) { 13 | const entry = this._mutations[type]; 14 | entry.forEach(function commitIterator (handler) { 15 | handler(payload); 16 | }); 17 | } 18 | 19 | dispatch (type, payload) { 20 | const entry = this._actions[type]; 21 | 22 | return entry.length > 1 23 | ? Promise.all(entry.map(handler => handler(payload))) 24 | : entry[0](payload); 25 | } 26 | } 27 | 28 | function vuexInit () { 29 | const options = this.$options; 30 | if (options.store) { 31 | this.$store = options.store; 32 | } else { 33 | this.$store = options.parent.$store; 34 | } 35 | } 36 | 37 | export default install (_Vue) { 38 | Vue.mixin({ beforeCreate: vuexInit }); 39 | Vue = _Vue; 40 | } -------------------------------------------------------------------------------- /《响应式系统的基本原理》.js: -------------------------------------------------------------------------------- 1 | function observer (value) { 2 | if (!value || (typeof value !== 'object')) { 3 | return; 4 | } 5 | 6 | Object.keys(value).forEach((key) => { 7 | defineReactive(value, key, value[key]); 8 | }); 9 | } 10 | 11 | function cb (val) { 12 | console.log("视图更新啦~", val); 13 | } 14 | 15 | function defineReactive (obj, key, val) { 16 | Object.defineProperty(obj, key, { 17 | enumerable: true, 18 | configurable: true, 19 | get: function reactiveGetter () { 20 | return val; 21 | }, 22 | set: function reactiveSetter (newVal) { 23 | if (newVal === val) return; 24 | val = newVal; 25 | cb(newVal); 26 | } 27 | }); 28 | } 29 | 30 | class Vue { 31 | constructor(options) { 32 | this._data = options.data; 33 | observer(this._data); 34 | } 35 | } 36 | 37 | let o = new Vue({ 38 | data: { 39 | test: "I am test." 40 | } 41 | }); 42 | o._data.test = "hello,test."; -------------------------------------------------------------------------------- /《响应式系统的依赖收集追踪原理》.js: -------------------------------------------------------------------------------- 1 | class Dep { 2 | constructor () { 3 | this.subs = []; 4 | } 5 | 6 | addSub (sub) { 7 | this.subs.push(sub); 8 | } 9 | 10 | notify () { 11 | this.subs.forEach((sub) => { 12 | sub.update(); 13 | }) 14 | } 15 | } 16 | 17 | class Watcher { 18 | constructor () { 19 | Dep.target = this; 20 | } 21 | 22 | update () { 23 | console.log("视图更新啦~"); 24 | } 25 | } 26 | 27 | function observer (value) { 28 | if (!value || (typeof value !== 'object')) { 29 | return; 30 | } 31 | 32 | Object.keys(value).forEach((key) => { 33 | defineReactive(value, key, value[key]); 34 | }); 35 | } 36 | 37 | function defineReactive (obj, key, val) { 38 | const dep = new Dep(); 39 | 40 | Object.defineProperty(obj, key, { 41 | enumerable: true, 42 | configurable: true, 43 | get: function reactiveGetter () { 44 | dep.addSub(Dep.target); 45 | return val; 46 | }, 47 | set: function reactiveSetter (newVal) { 48 | if (newVal === val) return; 49 | val = newVal; 50 | dep.notify(); 51 | } 52 | }); 53 | } 54 | 55 | class Vue { 56 | constructor(options) { 57 | this._data = options.data; 58 | observer(this._data); 59 | new Watcher(); 60 | console.log('render~', this._data.test); 61 | } 62 | } 63 | 64 | let o = new Vue({ 65 | data: { 66 | test: "I am test." 67 | } 68 | }); 69 | o._data.test = "hello,test."; 70 | 71 | Dep.target = null; -------------------------------------------------------------------------------- /《批量异步更新策略及 nextTick 原理》.js: -------------------------------------------------------------------------------- 1 | let uid = 0; 2 | 3 | class Watcher { 4 | constructor () { 5 | this.id = ++uid; 6 | } 7 | 8 | update () { 9 | console.log('watch' + this.id + ' update'); 10 | queueWatcher(this); 11 | } 12 | 13 | run () { 14 | console.log('watch' + this.id + '视图更新啦~'); 15 | } 16 | } 17 | 18 | let callbacks = []; 19 | let pending = false; 20 | 21 | function nextTick (cb) { 22 | callbacks.push(cb); 23 | 24 | if (!pending) { 25 | pending = true; 26 | setTimeout(flushCallbacks, 0); 27 | } 28 | } 29 | 30 | function flushCallbacks () { 31 | pending = false; 32 | const copies = callbacks.slice(0); 33 | callbacks.length = 0; 34 | for (let i = 0; i < copies.length; i++) { 35 | copies[i](); 36 | } 37 | } 38 | 39 | let has = {}; 40 | let queue = []; 41 | let waiting = false; 42 | 43 | function flushSchedulerQueue () { 44 | let watcher, id; 45 | 46 | for (index = 0; index < queue.length; index++) { 47 | watcher = queue[index] 48 | id = watcher.id; 49 | has[id] = null; 50 | watcher.run(); 51 | } 52 | 53 | waiting = false; 54 | } 55 | 56 | function queueWatcher(watcher) { 57 | const id = watcher.id; 58 | if (has[id] == null) { 59 | has[id] = true; 60 | queue.push(watcher); 61 | 62 | if (!waiting) { 63 | waiting = true; 64 | nextTick(flushSchedulerQueue); 65 | } 66 | } 67 | } 68 | 69 | (function () { 70 | let watch1 = new Watcher(); 71 | let watch2 = new Watcher(); 72 | 73 | watch1.update(); 74 | watch1.update(); 75 | watch2.update(); 76 | })(); -------------------------------------------------------------------------------- /《数据状态更新时的差异 diff 及 patch 机制》.js: -------------------------------------------------------------------------------- 1 | function patch (oldVNode, vnode, parentElm) { 2 | if (!oldVnode) { 3 | addVnodes(parentElm, null, vnode, 0, vnode.length - 1); 4 | } else if (!vnode) { 5 | removeVnodes(parentElm, oldVnode, 0, oldVnode.length - 1); 6 | } else { 7 | if (sameVnode(oldVNode, vnode)) { 8 | patchVnode(oldVNode, vnode); 9 | } else { 10 | removeVnodes(parentElm, oldVnode, 0, oldVnode.length - 1); 11 | addVnodes(parentElm, null, vnode, 0, vnode.length - 1); 12 | } 13 | } 14 | } 15 | 16 | function patchVnode (oldVnode, vnode) { 17 | if (oldVnode === vnode) { 18 | return; 19 | } 20 | 21 | if (vnode.isStatic && oldVnode.isStatic && vnode.key === oldVnode.key) { 22 | vnode.elm = oldVnode.elm; 23 | vnode.componentInstance = oldVnode.componentInstance; 24 | return; 25 | } 26 | 27 | const elm = vnode.elm = oldVnode.elm; 28 | const oldCh = oldVnode.children; 29 | const ch = vnode.children; 30 | 31 | if (vnode.text) { 32 | nodeOps.setTextContent(elm, vnode.text); 33 | } else { 34 | if (oldCh && ch && (oldCh !== ch)) { 35 | updateChildren(elm, oldCh, ch); 36 | } else if (ch) { 37 | if (oldVnode.text) nodeOps.setTextContent(elm, ''); 38 | addVnodes(elm, null, ch, 0, ch.length - 1); 39 | } else if (oldCh) { 40 | removeVnodes(elm, oldCh, 0, oldCh.length - 1) 41 | } else if (oldVnode.text) { 42 | nodeOps.setTextContent(elm, '') 43 | } 44 | } 45 | } 46 | 47 | function sameInputType (a, b) { 48 | if (a.tag !== 'input') return true 49 | let i 50 | const typeA = (i = a.data) && (i = i.attrs) && i.type 51 | const typeB = (i = b.data) && (i = i.attrs) && i.type 52 | return typeA === typeB 53 | } 54 | 55 | function sameVnode () { 56 | return ( 57 | a.key === b.key && 58 | a.tag === b.tag && 59 | a.isComment === b.isComment && 60 | !!a.data === !!b.data && 61 | sameInputType(a, b) 62 | ) 63 | } 64 | 65 | function insert (parent, elm, ref) { 66 | if (parent) { 67 | if (ref) { 68 | if (ref.parentNode === parent) { 69 | nodeOps.insertBefore(parent, elm, ref); 70 | } 71 | } else { 72 | nodeOps.appendChild(parent, elm) 73 | } 74 | } 75 | } 76 | 77 | function createElm (vnode, parentElm, refElm) { 78 | if (vnode.tag) { 79 | insert(parentElm, nodeOps.createElement(vnode.tag), refElm); 80 | } else { 81 | insert(parentElm, nodeOps.createTextNode(vnode.text), refElm); 82 | } 83 | } 84 | 85 | function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx) { 86 | for (; startIdx <= endIdx; ++startIdx) { 87 | createElm(vnodes[startIdx], parentElm, refElm); 88 | } 89 | } 90 | 91 | function removeNode (el) { 92 | const parent = nodeOps.parentNode(el); 93 | if (parent) { 94 | nodeOps.removeChild(parent, el); 95 | } 96 | } 97 | 98 | function removeVnodes (parentElm, vnodes, startIdx, endIdx) { 99 | for (; startIdx <= endIdx; ++startIdx) { 100 | const ch = vnodes[startIdx] 101 | if (ch) { 102 | removeNode(ch.elm); 103 | } 104 | } 105 | } 106 | 107 | function createKeyToOldIdx (children, beginIdx, endIdx) { 108 | let i, key 109 | const map = {} 110 | for (i = beginIdx; i <= endIdx; ++i) { 111 | key = children[i].key 112 | if (isDef(key)) map[key] = i 113 | } 114 | return map 115 | } 116 | 117 | function updateChildren (parentElm, oldCh, newCh) { 118 | let oldStartIdx = 0; 119 | let newStartIdx = 0; 120 | let oldEndIdx = oldCh.length - 1; 121 | let oldStartVnode = oldCh[0]; 122 | let oldEndVnode = oldCh[oldEndIdx]; 123 | let newEndIdx = newCh.length - 1; 124 | let newStartVnode = newCh[0]; 125 | let newEndVnode = newCh[newEndIdx]; 126 | let oldKeyToIdx, idxInOld, elmToMove, refElm; 127 | 128 | while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { 129 | if (!oldStartVnode) { 130 | oldStartVnode = oldCh[++oldStartIdx]; 131 | } else if (!oldEndVnode) { 132 | oldEndVnode = oldCh[--oldEndIdx]; 133 | } else if (sameVnode(oldStartVnode, newStartVnode)) { 134 | patchVnode(oldStartVnode, newStartVnode); 135 | oldStartVnode = oldCh[++oldStartIdx]; 136 | newStartVnode = newCh[++newStartIdx]; 137 | } else if (sameVnode(oldEndVnode, newEndVnode)) { 138 | patchVnode(oldEndVnode, newEndVnode); 139 | oldEndVnode = oldCh[--oldEndIdx]; 140 | newEndVnode = newCh[--newEndIdx]; 141 | } else if (sameVnode(oldStartVnode, newEndVnode)) { 142 | patchVnode(oldStartVnode, newEndVnode); 143 | nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm)); 144 | oldStartVnode = oldCh[++oldStartIdx]; 145 | newEndVnode = newCh[--newEndIdx]; 146 | } else if (sameVnode(oldEndVnode, newStartVnode)) { 147 | patchVnode(oldEndVnode, newStartVnode); 148 | nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm); 149 | oldEndVnode = oldCh[--oldEndIdx]; 150 | newStartVnode = newCh[++newStartIdx]; 151 | } else { 152 | let elmToMove = oldCh[idxInOld]; 153 | if (!oldKeyToIdx) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); 154 | idxInOld = newStartVnode.key ? oldKeyToIdx[newStartVnode.key] : null; 155 | if (!idxInOld) { 156 | createElm(newStartVnode, parentElm); 157 | newStartVnode = newCh[++newStartIdx]; 158 | } else { 159 | elmToMove = oldCh[idxInOld]; 160 | if (sameVnode(elmToMove, newStartVnode)) { 161 | patchVnode(elmToMove, newStartVnode); 162 | oldCh[idxInOld] = undefined; 163 | nodeOps.insertBefore(parentElm, newStartVnode.elm, oldStartVnode.elm); 164 | newStartVnode = newCh[++newStartIdx]; 165 | } else { 166 | createElm(newStartVnode, parentElm); 167 | newStartVnode = newCh[++newStartIdx]; 168 | } 169 | } 170 | } 171 | } 172 | 173 | if (oldStartIdx > oldEndIdx) { 174 | refElm = (newCh[newEndIdx + 1]) ? newCh[newEndIdx + 1].elm : null; 175 | addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx); 176 | } else if (newStartIdx > newEndIdx) { 177 | removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx); 178 | } 179 | } 180 | 181 | const nodeOps = { 182 | setTextContent () { 183 | 184 | }, 185 | parentNode () { 186 | 187 | }, 188 | removeChild () { 189 | 190 | }, 191 | nextSibling () { 192 | 193 | }, 194 | insertBefore () { 195 | 196 | } 197 | } -------------------------------------------------------------------------------- /《template 模板是怎样通过 Compile 编译的》.js: -------------------------------------------------------------------------------- 1 | const ncname = '[a-zA-Z_][\\w\\-\\.]*'; 2 | const singleAttrIdentifier = /([^\s"'<>/=]+)/ 3 | const singleAttrAssign = /(?:=)/ 4 | const singleAttrValues = [ 5 | /"([^"]*)"+/.source, 6 | /'([^']*)'+/.source, 7 | /([^\s"'=<>`]+)/.source 8 | ] 9 | const attribute = new RegExp( 10 | '^\\s*' + singleAttrIdentifier.source + 11 | '(?:\\s*(' + singleAttrAssign.source + ')' + 12 | '\\s*(?:' + singleAttrValues.join('|') + '))?' 13 | ) 14 | 15 | const qnameCapture = '((?:' + ncname + '\\:)?' + ncname + ')' 16 | const startTagOpen = new RegExp('^<' + qnameCapture) 17 | const startTagClose = /^\s*(\/?)>/ 18 | 19 | const endTag = new RegExp('^<\\/' + qnameCapture + '[^>]*>') 20 | 21 | const defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g 22 | 23 | const forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/ 24 | 25 | const stack = []; 26 | let currentParent, root; 27 | 28 | let index = 0; 29 | 30 | function advance (n) { 31 | index += n 32 | html = html.substring(n) 33 | } 34 | 35 | function makeAttrsMap (attrs) { 36 | const map = {} 37 | for (let i = 0, l = attrs.length; i < l; i++) { 38 | map[attrs[i].name] = attrs[i].value; 39 | } 40 | return map 41 | } 42 | 43 | function parseStartTag () { 44 | const start = html.match(startTagOpen); 45 | if (start) { 46 | const match = { 47 | tagName: start[1], 48 | attrs: [], 49 | start: index 50 | } 51 | advance(start[0].length); 52 | 53 | let end, attr 54 | while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { 55 | advance(attr[0].length) 56 | match.attrs.push({ 57 | name: attr[1], 58 | value: attr[3] 59 | }); 60 | } 61 | if (end) { 62 | match.unarySlash = end[1]; 63 | advance(end[0].length); 64 | match.end = index; 65 | return match 66 | } 67 | } 68 | } 69 | 70 | function parseEndTag (tagName) { 71 | let pos; 72 | for (pos = stack.length - 1; pos >= 0; pos--) { 73 | if (stack[pos].lowerCasedTag === tagName.toLowerCase()) { 74 | break; 75 | } 76 | } 77 | 78 | if (pos >= 0) { 79 | if (pos > 0) { 80 | currentParent = stack[pos - 1]; 81 | } else { 82 | currentParent = null; 83 | } 84 | stack.length = pos; 85 | } 86 | } 87 | 88 | function parseText (text) { 89 | if (!defaultTagRE.test(text)) return; 90 | 91 | const tokens = []; 92 | let lastIndex = defaultTagRE.lastIndex = 0 93 | let match, index 94 | while ((match = defaultTagRE.exec(text))) { 95 | index = match.index 96 | 97 | if (index > lastIndex) { 98 | tokens.push(JSON.stringify(text.slice(lastIndex, index))) 99 | } 100 | 101 | const exp = match[1].trim() 102 | tokens.push(`_s(${exp})`) 103 | lastIndex = index + match[0].length 104 | } 105 | 106 | if (lastIndex < text.length) { 107 | tokens.push(JSON.stringify(text.slice(lastIndex))) 108 | } 109 | return tokens.join('+'); 110 | } 111 | 112 | function getAndRemoveAttr (el, name) { 113 | let val 114 | if ((val = el.attrsMap[name]) != null) { 115 | const list = el.attrsList 116 | for (let i = 0, l = list.length; i < l; i++) { 117 | if (list[i].name === name) { 118 | list.splice(i, 1) 119 | break 120 | } 121 | } 122 | } 123 | return val 124 | } 125 | 126 | function processFor (el) { 127 | let exp; 128 | if ((exp = getAndRemoveAttr(el, 'v-for'))) { 129 | const inMatch = exp.match(forAliasRE); 130 | el.for = inMatch[2].trim(); 131 | el.alias = inMatch[1].trim(); 132 | } 133 | } 134 | 135 | function processIf (el) { 136 | const exp = getAndRemoveAttr(el, 'v-if'); 137 | if (exp) { 138 | el.if = exp; 139 | if (!el.ifConditions) { 140 | el.ifConditions = []; 141 | } 142 | el.ifConditions.push({ 143 | exp: exp, 144 | block: el 145 | }); 146 | } 147 | } 148 | 149 | function parseHTML () { 150 | while(html) { 151 | let textEnd = html.indexOf('<'); 152 | if (textEnd === 0) { 153 | const endTagMatch = html.match(endTag) 154 | if (endTagMatch) { 155 | advance(endTagMatch[0].length); 156 | parseEndTag(endTagMatch[1]); 157 | continue; 158 | } 159 | if (html.match(startTagOpen)) { 160 | const startTagMatch = parseStartTag(); 161 | const element = { 162 | type: 1, 163 | tag: startTagMatch.tagName, 164 | lowerCasedTag: startTagMatch.tagName.toLowerCase(), 165 | attrsList: startTagMatch.attrs, 166 | attrsMap: makeAttrsMap(startTagMatch.attrs), 167 | parent: currentParent, 168 | children: [] 169 | } 170 | 171 | processIf(element); 172 | processFor(element); 173 | 174 | if(!root){ 175 | root = element 176 | } 177 | 178 | if(currentParent){ 179 | currentParent.children.push(element); 180 | } 181 | 182 | if(!startTagMatch.unarySlash) { 183 | stack.push(element); 184 | currentParent = element; 185 | } 186 | continue; 187 | } 188 | } else { 189 | text = html.substring(0, textEnd) 190 | advance(textEnd) 191 | let expression; 192 | if (expression = parseText(text)) { 193 | currentParent.children.push({ 194 | type: 2, 195 | text, 196 | expression 197 | }); 198 | } else { 199 | currentParent.children.push({ 200 | type: 3, 201 | text, 202 | }); 203 | } 204 | continue; 205 | } 206 | } 207 | return root; 208 | } 209 | 210 | function parse () { 211 | return parseHTML(); 212 | } 213 | 214 | function optimize (rootAst) { 215 | function isStatic (node) { 216 | if (node.type === 2) { 217 | return false 218 | } 219 | if (node.type === 3) { 220 | return true 221 | } 222 | return (!node.if && !node.for); 223 | } 224 | function markStatic (node) { 225 | node.static = isStatic(node); 226 | if (node.type === 1) { 227 | for (let i = 0, l = node.children.length; i < l; i++) { 228 | const child = node.children[i]; 229 | markStatic(child); 230 | if (!child.static) { 231 | node.static = false; 232 | } 233 | } 234 | } 235 | } 236 | 237 | function markStaticRoots (node) { 238 | if (node.type === 1) { 239 | if (node.static && node.children.length && !( 240 | node.children.length === 1 && 241 | node.children[0].type === 3 242 | )) { 243 | node.staticRoot = true; 244 | return; 245 | } else { 246 | node.staticRoot = false; 247 | } 248 | } 249 | } 250 | 251 | markStatic(rootAst); 252 | markStaticRoots(rootAst); 253 | } 254 | 255 | function generate (rootAst) { 256 | 257 | function genIf (el) { 258 | el.ifProcessed = true; 259 | if (!el.ifConditions.length) { 260 | return '_e()'; 261 | } 262 | return `(${el.ifConditions[0].exp})?${genElement(el.ifConditions[0].block)}: _e()` 263 | } 264 | 265 | function genFor (el) { 266 | el.forProcessed = true; 267 | 268 | const exp = el.for; 269 | const alias = el.alias; 270 | const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''; 271 | const iterator2 = el.iterator2 ? `,${el.iterator2}` : ''; 272 | 273 | return `_l((${exp}),` + 274 | `function(${alias}${iterator1}${iterator2}){` + 275 | `return ${genElement(el)}` + 276 | '})'; 277 | } 278 | 279 | function genText (el) { 280 | return `_v(${el.expression})`; 281 | } 282 | 283 | function genNode (el) { 284 | if (el.type === 1) { 285 | return genElement(el); 286 | } else { 287 | return genText(el); 288 | } 289 | } 290 | 291 | function genChildren (el) { 292 | const children = el.children; 293 | 294 | if (children && children.length > 0) { 295 | return `${children.map(genNode).join(',')}`; 296 | } 297 | } 298 | 299 | function genElement (el) { 300 | if (el.if && !el.ifProcessed) { 301 | return genIf(el); 302 | } else if (el.for && !el.forProcessed) { 303 | return genFor(el); 304 | } else { 305 | const children = genChildren(el); 306 | let code; 307 | code = `_c('${el.tag},'{ 308 | staticClass: ${el.attrsMap && el.attrsMap[':class']}, 309 | class: ${el.attrsMap && el.attrsMap['class']}, 310 | }${ 311 | children ? `,${children}` : '' 312 | })` 313 | return code; 314 | } 315 | } 316 | 317 | const code = rootAst ? genElement(rootAst) : '_c("div")' 318 | return { 319 | render: `with(this){return ${code}}`, 320 | } 321 | } 322 | 323 | // 324 | var html = '