关于this.$options._component的值。

来源:17-1 组件通信provide和inject

慕工程3718384

2019-12-03

Test 中的 _component的值 为什么首字母被小些了?而Test3组件中没有?

 Vue.component('Test',{
                template:"<div>{{message}}<Test2 /></div>",
                provide(){
                    return {
                        elTest:this
                    }
                },
                data(){
                    return {
                        message:'message from ' + this.$options._componentTag
                        //_componentTag = test
                    }
                },
                methods:{
                    change:function(component) {
                        this.message = 'message from ' + component
                    }
                }
            });
             Vue.component('Test2',{
                template:"<Test3 />"
            });
            
            Vue.component('Test3',{
               template:"<button type='button' @click='changeMessage'> change </button>",
               inject:['elTest'],
               methods:{
                   changeMessage:function () {
                       this.elTest.change(this.$options._componentTag);
                       //_componentTag = Test3
                   }
               }
            });
            
写回答

3回答

Sam

2019-12-04

你好,这是一个非常好的问题,要搞清楚这个问题需要对 Vue 的源码比较熟悉,问题出在 template 获取的部分,大致原理如下:

在 $mount 方法中,会使用如下代码获取 template 的 html 字符串:

template = getOuterHTML(el);

对于本案例中返回的结果为:

"<div id="root">
      <test></test>
    </div>"

注意,这里的 Test 已经被转为了 test,此后在 baseCompile 的 parse 会将 template 解析为 AST,在解析 AST 过程中会将 id 为 root 的 div 的 children 解析出来,children 中包含了 test 标签,最后会生成 render 函数如下:

(function anonymous(
) {
with(this){return _c('div',{attrs:{"id":"root"}},[_c('test')],1)}
})

可以看到 _c('test') 中已经将 test 变成小写,而 Test 组件中的 template 为:

<div>{{message}}<Test2 /></div>

Test 组件的渲染方法如下:

child.$mount(hydrating ? vnode.elm : undefined, hydrating);

该方法中会获取组件的 template,这里的获取方法与 Vue 实例中不同:

if (!options.render) {
      var template = options.template;
      if (template) {
        if (typeof template === 'string') {
          if (template.charAt(0) === '#') {
            template = idToTemplate(template);
            /* istanbul ignore if */
            if (!template) {
              warn(
                ("Template element not found or is empty: " + (options.template)),
                this
              );
            }
          }
        } else if (template.nodeType) {
          template = template.innerHTML;
        } else {
          {
            warn('invalid template option:' + template, this);
          }
          return this
        }
      } else if (el) {
        template = getOuterHTML(el);
      }

最终会执行 Test 组件的 render 方法:

(function anonymous(
) {
with(this){return _c('div',[_v(_s(message)),_c('Test2')],1)}
})


2
1
Hemingway_AT
老师源码功力深厚啊,什么时候能看懂这编译渲染过程...
2019-12-10
共1条回复

Sam

2019-12-04

你看源码非常细心,不过想搞清楚这个问题较为复杂,最后一句话概况,造成这种差异的原因是:template 获取方式不同。

写在 root 下的 Test 通过:

template = getOuterHTML(el);

获取 template,getOuterHTML 通过 el.outerHTML 来实现,而组件的 template 是直接返回的,没有做任何处理,所以造成了这种差异

1
0

Hemingway_AT

2019-12-10

现象是:除了第一层级的组件,_componentTag 小写,其子组件和孙子组件...都是大写

0
0

Vue Element+Node.js开发企业通用管理后台系统

基于Element的中后台课程,一套中小型企业通用的后台管理系统

2829 学习 · 1714 问题

查看课程