Skip to content
New issue

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

[vue] vue为什么要求组件模板只能有一个根元素? #457

Open
haizhilin2013 opened this issue Jun 22, 2019 · 10 comments
Open

[vue] vue为什么要求组件模板只能有一个根元素? #457

haizhilin2013 opened this issue Jun 22, 2019 · 10 comments
Labels
vue

Comments

@haizhilin2013
Copy link
Collaborator

@haizhilin2013 haizhilin2013 commented Jun 22, 2019

[vue] vue为什么要求组件模板只能有一个根元素?

@haizhilin2013 haizhilin2013 added the vue label Jun 22, 2019
@wangjinA

This comment has been minimized.

Copy link

@wangjinA wangjinA commented Jun 29, 2019

因为"树"状数据结构,肯定要有个"根",一个遍历起始点吧,我是这么理解的。

@imRz1015

This comment has been minimized.

Copy link

@imRz1015 imRz1015 commented Jun 29, 2019

我是在知乎上看到的这个问题,转念一想,用了大半年的vue,好像真的没有了解过:

‘为什么只能有且只有一个根元素’

于是我花了二十多分钟去找了一下答案......竟然没有找到答案....

好的现在我来说说我的理解,如果有不对的地方欢迎指出。

我觉得这个问题需要从两个方面来说起:

1.new Vue({el:'#app'})

2.单文件组件中,template下的元素div

一、

当我们实例化Vue的时候,填写一个el选项,来指定我们的SPA入口:

let vm = new Vue({
    el:'#app'
})

同时我们也会在body里面新增一个id为app的div

<body>
    <div id='app'></div>
</body>

这很好理解,就是为vue开启一个入口,那我们不妨来想想,如果我在body下这样

<body>
    <div id='app1'></div>
    <div id='app2'></div>
</body>

Vue其实并不知道哪一个才是我们的入口,因为对于一个入口来讲,这个入口就是一个‘Vue类’,Vue需要把这个入口里面的所有东西拿来渲染,处理,最后再重新插入到dom中。
如果同时设置了多个入口,那么vue就不知道哪一个才是这个‘类’。

二、

当我们在webpack搭建的vue开发环境下,使用单文件组件时,你可能会这样:

<template>
    <div class='component'></div>
</template>

那这里为什么template下也必须有且只能有一个div呢?

这里我们要先看一看template这个标签,这个标签是HTML5出来的新标签,它有三个特性:

1.隐藏性:该标签不会显示在页面的任何地方,即便里面有多少内容,它永远都是隐藏的状态;

2.任意性:该标签可以写在页面的任何地方,甚至是head、body、sciprt标签内;

3.无效性:该标签里的任何HTML内容都是无效的,不会起任何作用;

但是呢,你可以通过innerHTML来获取到里面的内容。

知道了这个,我们再来看.vue的单文件组件。其实本质上,一个单文件组件,本质上(我认为)会被各种各样的loader处理成为.js文件(因为当你import一个单文件组件并打印出来的时候,是一个vue实例),通过template的任意性我们知道,template包裹的HTML可以写在任何地方,那么对于一个.vue来讲,这个template里面的内容就是会被vue处理为虚拟dom并渲染的内容,导致结果又回到了开始 :既然一个.vue单文件组件是一个vue实例,那么这个实例的入口在哪里?

如果在template下有多个div,那么该如何指定这个vue实例的根入口?
为了让组件能够正常的生成一个vue实例,那么这个div会被自然的处理成程序的入口。

通过这个‘根节点’,来递归遍历整个vue‘树’下的所有节点,并处理为vdom,最后再渲染成真正的HTML,插入在正确的位置

那么这个入口,就是这个树的‘根’,各个子元素,子组件,就是这个树的‘枝叶’,而自然而然地,这棵‘树’,就是指一个vue实例了。

如有错误,欢迎讨论~

@PanJiaChen

This comment has been minimized.

Copy link

@PanJiaChen PanJiaChen commented Jul 1, 2019

官方其实有解释。

vuejs/vue#7088 (comment)

@happyzhanls

This comment has been minimized.

Copy link

@happyzhanls happyzhanls commented Jul 4, 2019

谢谢楼上两位兄弟的答案,都很有参考价值

@abcdGJJ

This comment has been minimized.

Copy link

@abcdGJJ abcdGJJ commented Jul 9, 2019

有点疑惑,Vue 1.x的时候是允许有多个根元素的,难道那时候没有vdom么?

@Yanhua67

This comment has been minimized.

Copy link

@Yanhua67 Yanhua67 commented Aug 1, 2019

@PanJiaChen 活捉大佬!

@zejunking

This comment has been minimized.

Copy link

@zejunking zejunking commented Aug 15, 2019

有点疑惑,Vue 1.x的时候是允许有多个根元素的,难道那时候没有vdom么?

VDOM是vue2.0新增的

@bilibiliou

This comment has been minimized.

Copy link

@bilibiliou bilibiliou commented Nov 18, 2019

我觉得这个问题可以抽象为:逻辑抽象树为什么只能有一个根

从效率上,如果多个根,那么就会产生多个入口(遍历、查找)从效率上来说都不方便

其次,如果一颗树有多个根,其实是可以优化的。肯定存在一个子节点。通过这个该子节点访问到所有的节点。那么,优化后,这个子节点就成为了新的树的根节点

此外从vue 的角度来说,如果一个组件有多个根,说明你可以把这个组件拆开成两个组件,这样既进行了解耦,也会为后续的维护和迭代提供方便

@liuxiaoyang1

This comment has been minimized.

Copy link

@liuxiaoyang1 liuxiaoyang1 commented Nov 20, 2019

Technical, due to how the diff algorithm is written. It's obviously possible to update it, but it takes significant changes to the current algorithm (React did that during a complete rewrite).
技术上,取决于diff算法的编写方式。显然可以对其进行更新,但是需要对当前算法进行重大更改(React在完全重写期间进行了更改)。

@sunyxq

This comment has been minimized.

Copy link

@sunyxq sunyxq commented Dec 4, 2019

组件的template最终会转换成VNode对象,一个组件对应一个根元素对应一个VNode对象

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.