我们通常在开发页面的时候,希望能够将页面中每个部分都单独分离成小切片,每个切片都有自己的视图结构、样式和逻辑。而每个切片形成的视图结构、样式和逻辑的整体我们就称之为组件。

    组件有什么好处呢?
    1、复用性,每个组件都是独立的块,可以在任意的地方进行多次使用。
    2、独立使用决定了组件的维护性高。
    3、配置度高,虽然是独立但提供了接口,让开发者可以传入一些属性使其配置度高。

    例如在 Vue 中想要使用组件进行相互嵌套:

    1. const MyHeader = {
    2. template: `
    3. <header style="border: 1px solid #333;">
    4. <my-logo />
    5. <my-nav :nav-data="navData" />
    6. <my-user />
    7. </header>`,
    8. // 注册数据属性
    9. props: ["navData"],
    10. components: {
    11. MyLogo,
    12. MyNav,
    13. MyUser
    14. }
    15. };

    以上代码,我们定义了一个MyHeader的父组件,MyHeader下面又引入包含了myLogomyNavmyUser这些子组件,所以父子组件是一种包含关系。

    Vue 中如果你要使用组件就必须要通过components进行局部注册(当然也有全局注册,这个我们后面在讨论):

    1. const myHeader = {
    2. // ...
    3. components: {
    4. MyLogo,
    5. MyNav,
    6. MyUser
    7. }
    8. }

    myHeader组件通过props属性来注入父组件传递来的数据,然后又把navData数据传递给了myNav组件,像这样一层层的数据传递我们就称之为「单向数据流」!

    1. const App = {
    2. template: `
    3. <div>
    4. <!-- 使用组件 -->
    5. <!-- 父组件给子组件传递数据 -->
    6. <my-header :nav-data="navData" />
    7. <!-- 复用组件,相互独立 -->
    8. <my-header :nav-data="navData" />
    9. </div>`,
    10. components: {
    11. // 注册组件,这样的方式叫做局部注册
    12. // 局部注册:在组件内部的局部注册另外一个组件,只供当前组件使用
    13. MyHeader
    14. },
    15. data() {
    16. return {
    17. navData: [
    18. {
    19. id: 1,
    20. title: "百度",
    21. link: "https:www.baidu.com"
    22. },
    23. {
    24. id: 2,
    25. title: "谷歌",
    26. link: "https:www.google.com"
    27. },
    28. {
    29. id: 3,
    30. title: "必应",
    31. link: "https:www.bing.com"
    32. }
    33. ]
    34. };
    35. }
    36. };
    37. createApp(App).mount("#app");
    1. const MyHeader = {
    2. // ...
    3. // 注册数据属性
    4. props: ["navData"],
    5. };
    1. const MyNav = {
    2. // ...
    3. // 注册数据属性
    4. props: ["navData"]
    5. };

    子组件不能直接更改父组件传递过来的props数据,只能通过emit去向父组件发送事件,然后让父组件去更改props的数据:

    1. const MyNav = {
    2. template: `<button @click="onClickBtn">更改数据</button>`,
    3. // 注册数据属性
    4. props: ["navData"],
    5. methods:{
    6. onClickBtn(){
    7. // 不要这样做!!!
    8. this.navData.reverse()
    9. }
    10. }
    11. };
    1. const MyNav = {
    2. template: `<button @click="onClickBtn">更改数据</button>`,
    3. // 注册数据属性
    4. props: ["navData"],
    5. methods:{
    6. onClickBtn(){
    7. // 应该这样做!!!
    8. this.$emit("updateData")
    9. }
    10. }
    11. };
    12. const MyHeader = {
    13. template: `
    14. <header style="border: 1px solid #333;">
    15. <my-logo />
    16. <my-nav :nav-data="myNavData" @update-data="updateData" />
    17. <my-user />
    18. </header>`,
    19. // 注册数据属性
    20. props: ["navData"],
    21. components: {
    22. MyLogo,
    23. MyNav,
    24. MyUser
    25. },
    26. data(){
    27. return{
    28. // 初始化的时候进行赋值
    29. myNavData: this.navData
    30. }
    31. },
    32. methods:{
    33. updateData(){
    34. this.myNavData.reverse()
    35. }
    36. }
    37. };

    回到组件的概念,我们发现上面的组件形成了这样的一种结构:

    1. App (传递了 navData 数据)
    2. MyHeader (传递了 navData 数据)
    3. MyLogo
    4. MyNav (传递了 navData 数据)
    5. NavItem (传递了 navDataitem 数据)
    6. MyUser

    这样的结构我们就称之为组件树,这样的设计就是「组件化」设计。 :::info 💡 提示
    组件在封装的时候要想尽量的扁平化,没有必要把所有的内容全部拆分为组件,这样就会导致层层嵌套!不好进行维护,导致组件相互的依赖度太高。 :::