We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
本篇是从零实现 vue2 系列第七篇,在 YourVue 中实现 v-if 和 v-for。
文章会最先更新在公众号:BUPPT。代码仓库:https://github.com/buppt/YourVue
在 main.js 的模版中加入这两个指令
main.js
<div v-if="count===1">count === 1</div> <div v-else-if="count===2">count === 2</div> <div v-else>count != 1 && count != 2</div> <div v-for="(item, key, index) in items"> <p>{{item}}</p> </div>
vue 解析模版时,遇到 v-if 会执行下面的函数,
function processIf (el) { var exp = getAndRemoveAttr(el, 'v-if'); //获取 if 表达式 if (exp) { el.if = exp; //增加 if 属性 addIfCondition(el, { // 增加 ifConditions 属性,属性值是一个对象 exp: exp, // exp 表示当前 v-if 的值 block: el // block 是当前 ast 对象的引用 }); } else { if (getAndRemoveAttr(el, 'v-else') != null) { //增加 el.else 属性 el.else = true; } var elseif = getAndRemoveAttr(el, 'v-else-if'); //添加 elseif 属性 if (elseif) { el.elseif = elseif; } } } function addIfCondition (el, condition) { //增加 ifConditions 属性 if (!el.ifConditions) { el.ifConditions = []; } el.ifConditions.push(condition); }
添加 ifConditions 参数,其中是 exp 是判断条件,block 就是当前 ast 的引用。
{ type: 1, if: "count===1", ifConditions: [ {exp: "count===1", block: {…}} {exp: "count===2", block: {…}} {exp: undefined, block: {…}} ] }
对于 v-else 和 v-else-if,并没有加到自己的 ast 树中,而是把自己对应的 ast 对象添加到最近的 v-if 的 ifConditions 里,代码如下:
if (element.elseif || element.else) { var prev = findPrevElement(parent.children); if (prev && prev.if) { addIfCondition(prev, { exp: el.elseif, block: el }); } } function findPrevElement (children) { var i = children.length; while (i--) { if (children[i].type === 1) { return children[i] } else { children.pop(); } } }
在 gencode 时添加判断,如果存在 if 判断,执行 genIf()。
function genElement(el){ if (el.for && !el.forProcessed) { return genFor(el) } else if (el.if && !el.ifProcessed) { return genIf(el) } else { ... } } export function genIf (el){ el.ifProcessed = true // avoid recursion return genIfConditions(el.ifConditions.slice()) } function genIfConditions (conditions) { if (!conditions.length) { return '_e()' } const condition = conditions.shift() if (condition.exp) { return `(${condition.exp})?${ genElement(condition.block) }:${ genIfConditions(conditions) }` } else { return `${genElement(condition.block)}` } }
其实 genIf 没有使用新的 render 函数,只是在 render 函数中添加了判断
(count===1)? _c('div',{},[_v("count === 1")]): (count===2)? _c('div',{},[_v("count === 2")]): _c('div',{},[_v("count != 1 && count != 2")] )
同样 v-for 在 ast 中添加了 for、alias、iterator1 和 iterator2 四个参数。
function parseFor (exp) { var inMatch = exp.match(forAliasRE); if (!inMatch) { return } var res = {}; res.for = inMatch[2].trim(); var alias = inMatch[1].trim().replace(stripParensRE, ''); var iteratorMatch = alias.match(forIteratorRE); if (iteratorMatch) { // v-for="(item, key, index) in items" res.alias = alias.replace(forIteratorRE, ''); //获取别名 (item) res.iterator1 = iteratorMatch[1].trim(); //获取索引1 (key) if (iteratorMatch[2]) { res.iterator2 = iteratorMatch[2].trim(); //获取索引2 (index) } } else { res.alias = alias; } return res; }
ast 如下
{ tag: "div" type: 1 for: "items" alias: "item" iterator1: "key" iterator2: "index" }
在 gencode 阶段,执行 genFor 时引入了新的 render 函数 _l()
_l()
export function genFor (el) { const exp = el.for const alias = el.alias const iterator1 = el.iterator1 ? `,${el.iterator1}` : '' const iterator2 = el.iterator2 ? `,${el.iterator2}` : '' el.forProcessed = true // avoid recursion return `_l((${exp}),` + `function(${alias}${iterator1}${iterator2}){` + `return ${genElement(el)}` + '})' }
生成的 code 函数如下
_l( (items), function(item,key,index){ return _c('div',{},[_c('p',{},[_v(_s(item))])]) } )
render 阶段执行 renderList 函数
export function initRender(vm){ vm._l = renderList } function renderList (val, render){ let ret, i, l, keys, key if (Array.isArray(val) || typeof val === 'string') { ret = new Array(val.length) for (i = 0, l = val.length; i < l; i++) { ret[i] = render(val[i], i) } } else if (typeof val === 'number') { ret = new Array(val) for (i = 0; i < val; i++) { ret[i] = render(i + 1, i) } } else { keys = Object.keys(val) ret = new Array(keys.length) for (i = 0, l = keys.length; i < l; i++) { key = keys[i] ret[i] = render(val[key], key, i) } } if (!ret) { ret = [] } ret._isVList = true return ret }
从 renderList 函数的实现可以看出来,v-for 可以遍历数组,还可以遍历字符串、number、object,最后会返回一个 VNode 数组。
这时候在父元素 createElement 的时候,就会通过第四篇文章中说过的 simpleNormalizeChildren 将 VNode 数组展开,然后 v-for 就和其他同层元素相同,一起创建和更新。
simpleNormalizeChildren
本篇代码:https://github.com/buppt/YourVue/tree/master/oldSrc/6.if%26for
The text was updated successfully, but these errors were encountered:
No branches or pull requests
写在前面
本篇是从零实现 vue2 系列第七篇,在 YourVue 中实现 v-if 和 v-for。
文章会最先更新在公众号:BUPPT。代码仓库:https://github.com/buppt/YourVue
正文
在
main.js
的模版中加入这两个指令v-if
vue 解析模版时,遇到 v-if 会执行下面的函数,
添加 ifConditions 参数,其中是 exp 是判断条件,block 就是当前 ast 的引用。
对于 v-else 和 v-else-if,并没有加到自己的 ast 树中,而是把自己对应的 ast 对象添加到最近的 v-if 的 ifConditions 里,代码如下:
在 gencode 时添加判断,如果存在 if 判断,执行 genIf()。
其实 genIf 没有使用新的 render 函数,只是在 render 函数中添加了判断
v-for
同样 v-for 在 ast 中添加了 for、alias、iterator1 和 iterator2 四个参数。
ast 如下
在 gencode 阶段,执行 genFor 时引入了新的 render 函数
_l()
生成的 code 函数如下
render 阶段执行 renderList 函数
从 renderList 函数的实现可以看出来,v-for 可以遍历数组,还可以遍历字符串、number、object,最后会返回一个 VNode 数组。
这时候在父元素 createElement 的时候,就会通过第四篇文章中说过的
simpleNormalizeChildren
将 VNode 数组展开,然后 v-for 就和其他同层元素相同,一起创建和更新。本篇代码:https://github.com/buppt/YourVue/tree/master/oldSrc/6.if%26for
The text was updated successfully, but these errors were encountered: