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
某天需要完成一个从0~4的v-for循环:
// 🌰 <template> <div> <p>0</p> . . . <p>4</p> </div> </template>
本着一行代码搞定的想法,遂写入如下代码:
一行代码搞定
<template> <div> <p v-for="item in [...Array(5)].map((t,i) => i)">{{item}}</p> </div> </template>
却只得到了一个div标签,一个p标签都没有渲染出来!事实上把这段代码放到chrome中打印可以得到如下:
> [...Array(5)].map((t,i) => i) > [0, 1, 2, 3, 4]
那么哪一步出问题了呢?
[...Array(5)]这个代码的含义是扩展一个长度为5的空数组,得到一个
[...Array(5)]
var arr = [undefined, undefined, undefined, undefined, undefined]。
var arr = [undefined, undefined, undefined, undefined, undefined]
换成es5代码则为:
Array.apply(null, Array(5)); [].concat.apply([], Array(5));
再将arr执行map循环,得到[0, 1, 2, 3, 4]。
arr
[0, 1, 2, 3, 4]
既然语法没有错误,我决定深入vue源码,找到罪魁祸首的源代码,看看它对我的[...Array(5)]做了什么。
我出错的项目是使用webpack打包的vue项目:
// main.js import Vue from 'vue';
首先我们需要知道vue的编译器和运行时是分开的(vue构建版本)。那么我们导入的是什么包呢?
因为vue包的package.json设置了main属性为dist/vue.runtime.common.js ,故webpack默认使用的
dist/vue.runtime.common.js
是vue.runtime.common.js运行时版本,所以需要借助vue-loader来编译我们*.vue文件内的template。
vue.runtime.common.js
vue-loader
*.vue
在追责vue-loader之前,我们先简单介绍下vue的模板是怎么变成真实DOM的。
template => AST => optimize => code => render => vnode => dom
简单概况下一共有6个步骤:
第一步将对模板做解析,使用一个parse函数,生成AST。
parse
第二步将AST代码进行优化optimize,深度遍历AST树,为其中的静态节点进行标记static和staticRoot属性,为以后模板的更新起到优化作用。
static
staticRoot
第三步将优化后的AST树转换成运行时执行的code字符串,const code = generate(ast, options)。
const code = generate(ast, options)
第四步利用compileToFunctions将code字符串转换为render函数。
compileToFunctions
第五步在$mount挂载的时候会新建一个watcher实例,初始化或监听到数据变化时就会执行_update,它内部会执行_render,从而调用我们的render函数生成vnode。
$mount
_update
_render
最后一步,_update拿到vnode之后执行patch,利用patch进行比较,再借助createElm创建真实dom。
patch
createElm
打开vue-loader,在example/source.vue里输入我们的🌰,运行它的dev进行调试,。
example/source.vue
按照编译步骤概况思路走,我们很容易的就能找到各阶段的编译结果并打印。
(为了不浪费时间,我们用二分的思路来查找问题发生在哪一阶段!)
.
emmm找不到!
可以看到vue-loader并没有把模板编译器放到自己代码里,而是写在了一个插件vue-template-compiler里
vue-template-compiler
// allow using custom compiler via options const compiler = options.compiler || require('vue-template-compiler')
然后将compiler传入一个@vue/component-compiler-utils工具中,主要的步骤都在这里面执行。
@vue/component-compiler-utils
const { compileTemplate } = require('@vue/component-compiler-utils') . const finalOptions = { compiler, ... } . const compiled = compileTemplate(finalOptions)
打开@vue/component-compiler-utils:
很容易的就能找到lib/compileTemplate.ts内的actuallyCompile,编译操作都在这里面。
lib/compileTemplate.ts
actuallyCompile
很容易的找到:
const { render, staticRenderFns, tips, errors } = compile( source, finalCompilerOptions )
这里的render就是我们要找的code,让我们把它打印出来看看:
render
// code with(this){return _c('div',_l(([...Array(5)].map((t,i) => i)),function(item){return _c('p',[_v(_s(item))])}),0)}
可以看到code中[...Array(5)]还没有执行,那么问题就不在code代码串,让我们继续找下一个~
继续看actuallyCompile,得到code以后,通过定义一个toFunction将它转换成render函数,然后又使用了一个插件:
toFunction
const transpile = require('vue-template-es2015-compiler') let code = transpile( `var __render__ = ${toFunction(render)}\n` + `var __staticRenderFns__ = [${staticRenderFns.map(toFunction)}]`, finalTranspileOptions ) + `\n`
此时的这个code就是我们可以交给挂载去执行的render函数,让我们打印它看一看:
// render var render = function() { var _vm = this var _h = _vm.$createElement var _c = _vm._self._c || _h return _c( "div", _vm._l( [].concat(Array(5)).map(function(t, i) { return i }), function(item) { return _c("p", [_vm._v(_vm._s(item))]) } ), 0 ) } var staticRenderFns = [] render._withStripped = true
注意!!!我们的[...Array(5)]被转换成[].concat(Array(5))这个东西了!!
[].concat(Array(5))
> [].concat(Array(5)) > []
因为是空数组,所以map出来也是空数组,所以渲染不出p标签。
那么transpile也就是vue-template-es2015-compiler为什么会这么做呢?github由readme可以看出这个包的功能是:
transpile
vue-template-es2015-compiler
1.使用Buble库来支持模板中的ES2015语法;
Buble
2.删除code代码块的with;
好!凶手已经呼之欲出,就是Buble!这是一个受babel启发的ES6语法编译器,introduction。
我去找了它的issues81作者完全没有想改的意图😡。
结案!解决方案的话暂时把[...Array(5)]放到js里利用babel把~
The text was updated successfully, but these errors were encountered:
No branches or pull requests
导火索
某天需要完成一个从0~4的v-for循环:
本着
一行代码搞定
的想法,遂写入如下代码:却只得到了一个div标签,一个p标签都没有渲染出来!事实上把这段代码放到chrome中打印可以得到如下:
那么哪一步出问题了呢?
排查
1.理解语法
[...Array(5)]
这个代码的含义是扩展一个长度为5的空数组,得到一个var arr = [undefined, undefined, undefined, undefined, undefined]
。换成es5代码则为:
再将
arr
执行map循环,得到[0, 1, 2, 3, 4]
。2.找到罪魁祸首
既然语法没有错误,我决定深入vue源码,找到罪魁祸首的源代码,看看它对我的
[...Array(5)]
做了什么。a.谁在编译
我出错的项目是使用webpack打包的vue项目:
首先我们需要知道vue的编译器和运行时是分开的(vue构建版本)。那么我们导入的是什么包呢?
因为vue包的package.json设置了main属性为
dist/vue.runtime.common.js
,故webpack默认使用的是
vue.runtime.common.js
运行时版本,所以需要借助vue-loader
来编译我们*.vue
文件内的template。b.template编译概括
在追责vue-loader之前,我们先简单介绍下vue的模板是怎么变成真实DOM的。
简单概况下一共有6个步骤:
1. Template => AST
第一步将对模板做解析,使用一个
parse
函数,生成AST。2.AST => optimize
第二步将AST代码进行优化optimize,深度遍历AST树,为其中的静态节点进行标记
static
和staticRoot
属性,为以后模板的更新起到优化作用。3.optimize => code
第三步将优化后的AST树转换成运行时执行的code字符串,
const code = generate(ast, options)
。4.code => render
第四步利用
compileToFunctions
将code字符串转换为render函数。5.render => vnode
第五步在
$mount
挂载的时候会新建一个watcher实例,初始化或监听到数据变化时就会执行_update
,它内部会执行_render
,从而调用我们的render函数生成vnode。6.vnode => dom
最后一步,
_update
拿到vnode之后执行patch
,利用patch
进行比较,再借助createElm
创建真实dom。c.vue-loader 哪错了
打开vue-loader,在
example/source.vue
里输入我们的🌰,运行它的dev进行调试,。按照编译步骤概况思路走,我们很容易的就能找到各阶段的编译结果并打印。
(为了不浪费时间,我们用二分的思路来查找问题发生在哪一阶段!)
1.code代码串(lib/loaders/templateLoader.js)
.
.
emmm找不到!
可以看到vue-loader并没有把模板编译器放到自己代码里,而是写在了一个插件
vue-template-compiler
里然后将compiler传入一个
@vue/component-compiler-utils
工具中,主要的步骤都在这里面执行。打开
@vue/component-compiler-utils
:很容易的就能找到
lib/compileTemplate.ts
内的actuallyCompile
,编译操作都在这里面。很容易的找到:
这里的
render
就是我们要找的code,让我们把它打印出来看看:可以看到code中
[...Array(5)]
还没有执行,那么问题就不在code代码串,让我们继续找下一个~2.render函数
继续看
actuallyCompile
,得到code以后,通过定义一个toFunction
将它转换成render函数,然后又使用了一个插件:此时的这个code就是我们可以交给挂载去执行的render函数,让我们打印它看一看:
注意!!!我们的
[...Array(5)]
被转换成[].concat(Array(5))
这个东西了!!因为是空数组,所以map出来也是空数组,所以渲染不出p标签。
那么
transpile
也就是vue-template-es2015-compiler
为什么会这么做呢?github由readme可以看出这个包的功能是:1.使用
Buble
库来支持模板中的ES2015语法;2.删除code代码块的with;
好!凶手已经呼之欲出,就是
Buble
!这是一个受babel启发的ES6语法编译器,introduction。我去找了它的issues81作者完全没有想改的意图😡。
结案!解决方案的话暂时把
[...Array(5)]
放到js里利用babel把~d.template编译详细解读
黄轶vue源码分享
言川源码分析
The text was updated successfully, but these errors were encountered: