[vuejs]官方文档基础知识复习1

61 阅读5分钟

web component

要了解component,首先要知道component是什么。官方文档中是这样描述的:

Components allow us to split the UI into independent and reusable pieces, and think about each piece in isolation. 组件允许我们把UI分成独立、可重复使用的块,并且把这些块想象成是独立的。

CleanShot 2022-11-09 at 20.23.49@2x.png 从图示也可以看出,几乎每一个可以和其他元素独立开来的部分我们都可以把它想成是一个组件。

我觉得可以把组件暂且想象成游戏DLC,每个DLC都是相互独立的,但是它们都需要插入到本体中。

官方文档中还提到:

Vue also plays nicely with native Web Components. If you are curious about the relationship between Vue Components and native Web Components, read more here.

这里提到了一个概念是web component,那我们先来看看什么是web component。

(参考:Web Components Crash Course)

web component定义

Web compoents are set of web platform API's that allow us to create custom, reusable, encapsulated html tags to use in web pages and web apps.Web compoents do not require any special 3rd party libraries or frameworks but can certainly be used with them.

这里的重点是:web组件是一系列网络平台api,它让我们去创建封装的html标签。

web component主要builiding block

最主要的三个部分是:custom elements, shadow DOM 以及THML Templates,它们可以独立使用,但在构建web组件时,通常会三个都使用。

  1. 自定义元素指的就是我们可以自定义一个标签,比如我们可以定制一个标签叫做<game>
     <script>
        class Game extends HTMLElement {
            constructor(){ // 在元素实例被创建或更新时执行

            }
            
            connectedCallback(){ //  每次该元素被插入到dom中时执行

            }
            disconnectedCallback(){ //  每次该元素被从dom中移除时执行

            }
            attributeChangeCallback(){//  每次属性发生改变(增删改替)执行

            }

        }
        window.customElements.define('game', Game);
    </script>
  1. shadow dom会把一些样式和markup等封装到我们的自定义元素中,它是独立于dom树时,样式也是独立于网页的全局css的,比如我们的shadow dom里<h3>有样式,那么网页里的css再修改h3样式是不影响它的。 shadow dom的创建方式是:
element.attchShadow({mode: open})
// element指的是我们的自定义元素,mode:open指的是我们可以用浏览器的开发者工具去访问shadow dom里的内容。
// 这会创建一个shadowRoot
  1. html templates主要是定义我们web组件封装的markup,它会用slots来添加自定义文本。

一个web组件小demo

  1. 首先先创建一个自定义标签user-card,创建的过程很简单,只需要如下代码即可:
<body>
    <user-card></user-card>
    <script src="./user-card.js">
    </script>
</body>
class UserCard extends HTMLElement {
    constructor() {
        super();
        this.innerHTML = `Jhon Doe`;
    }
}

window.customElements.define('user-card', UserCard);

此时,我们的页面就可以出现Jhon Doe这几个字。

  1. 我们来添加一些自定义属性,因为每个user-card的内容都是不同的。 我们要拿到属性的值,需要通过getAttribute方法;

(1)我们可以直接用this.innerHtml


<body>
    <user-card name="Alex"></user-card>
    <script src="./user-card.js">
    </script>
</body>
----
class UserCard extends HTMLElement {
    constructor() {
        super();
        this.innerHTML = `<style>h3 { color: cora l}</style><h3>${this.getAttribute('name')}</h3>`;
    }
}

(2)更好和简洁的方法是用shadow dom把模版封装起来(下面还添加了一个avatar属性)

<body>
    <user-card name="Alex" avatar="https://randomuser.me/api/portraits/men/75.jpg"> </user-card>
    <script src="./user-card.js">
    </script>
</body>
------
const template = document.createElement('template');
template.innerHTML = `
 <style>
    h3 {
      color: coral;
    }
 </style>
 <div class="user-card">
    <img></img>
    <div>
    <h3> </h3>
 </div>
 </div>
`;

class UserCard extends HTMLElement {
    constructor() {
        super();

        this.attachShadow({ mode: 'open' }); // 创建shadow dom
        this.shadowRoot.appendChild(template.content.cloneNode(true)); //把template封装到shadow dom中,true代表会把所有后代子节点都克隆
        this.shadowRoot.querySelector('h3').innerText = this.getAttribute('name'); // 用attribute中的值动态更改h3中的内容
        this.shadowRoot.querySelector('img').src = this.getAttribute('avatar');
    }
}

window.customElements.define('user-card', UserCard);

(3)使用slot

    <user-card name="Alex" avatar="https://randomuser.me/api/portraits/men/75.jpg">
           // 如果只有一个slot 我们直接插入文字即可
        <div slot="email">alex@gmail.com</div>
        <div slot="phone">555-555--55 </div>
    </user-card>
    -------
    //template中
        <div class="info">
          // 如果只有一个slot 直接写<slot/>即可
           <p><slot name="email"/></p>
           <p><slot name="phone"/></p>
        </div>
    

(4)添加交互事件

主要思路:在connectedCallback 中为button添加click事件监听。


    contructor(){
         ...
       this.showInfo = true;  //添加showInfo属性值
       ...
     }
    connectedCallback() { // 添加事件监听
        this.shadowRoot.querySelector('#toggle-info').addEventListener('click', () => {
            this.toggleInfo();
        })
    }

    disconnectedCallback() { // 移除事件监听
        this.shadowRoot.querySelector('#toggle-info').removeEventListener();
    }

    toggleInfo() { // 切换info信息的显示与否
        this.showInfo = !this.showInfo;

        const info = this.shadowRoot.querySelector('.info');
        const toggleBtn = this.shadowRoot.querySelector('#toggle-info');

        if (this.showInfo) {
            info.style.display = 'block';
            toggleBtn.innerText = 'Hide Info'
        } else {
            info.style.display = 'none';
            toggleBtn.innerText = 'Show Info'
        }
    }

完整代码

总结一下上面的内容,简单来说,web组件的三个主要构成是1:自定义元素;2.shadow dom 3. html模版。 其中1是用constrcutor来构建实例,然后给它attach shadow dom, 而2又封装了3。

vue组件基本使用

其实根据上面web组件,我们就可以想到,vue组件的学习也可以从上面的几个步骤来进行: (1)构建使用自定义标签(vue组件) (2)传递并使用属性; (3)利用slot (4)事件交互

可以看出官方文档的构成基本上也是这个思路。

构建使用自定义标签(定义一个vue组件)

  1. 创建并使用vue组件

(1)在components创建一个vue拓展名的文件,这里我们还是以user-card为例,所以建立一个UserCard.vue文件,在里面加上最基本的模版。

<template>
   <h3>Alex</h3>
</template>

(2) App.vue中引入UserCard.vue

<template>
<user-card></user-card>
</template>

<script>
import UserCard from "./components/UserCard.vue";
export default {
   name: "App",
   components: {
       UserCard, //把usercard注册到当前模版中
  },
};
</script>

此时页面中可以出现Alex这几个字。

上面出现了“组件注册”这个概念。组件注册分为“全局注册”和“局部(local)注册”。上面我们使用的是局部注册。

全局注册是把组件注册到整个应用中,使用app.component()方法来注册。全局注册之后,再别的组件中使用usercard的话,就无需再导入和注册。

import UserCard from './components/UserCard.vue'

const app = createApp(App)
app.component('user-card', UserCard)
app.mount('#app') 

(2)传递并使用属性 在上面的web组件中,我们并没有一个地方把该组件都有哪些属性整齐地列出来,而在vue组件中,我们可以很清楚地把所有属性都列出来。

UserCard.vue

<template>
    <div class="container">
        <div class="card-left">
            <h3>{{ name }}</h3>
            <!-- 这里图片需要用v-bind -->
            <img v-bind:src=avatar alt="">
        </div>

        <div class="info card-right">
            <div>{{ email }}</div>
            <div>{{ phone }}</div>
        </div>
    </div>
</template>

<script>
export default {
    props: ['name', 'avatar', 'email', 'phone']
}

App.vue

<template>
<user-card class="user-card" 
   v-for="user in users"
   :key="user.id"
   :name="user.name"  
   :avatar ="user.avatar"
   :email = "user.email"
   :phone = "user.phone">
   </user-card>
</template>

<script>
import UserCard from './components/UserCard.vue'
export default {
  name: 'App',
  components: {
       UserCard
  },
  data(){
    return {
     users:[
        { id: 1, name: 'Alex', avatar:'https://randomuser.me/api/portraits/men/32.jpg', email: 'alex@gmail.com', phone: '55-55--55'},
        { id: 2, name: 'Louie',avatar:'https://randomuser.me/api/portraits/women/76.jpg', email: 'louie@gmail.com', phone: '22-44-44'},
     ]
    }
  }
}

(3)利用slot 这里我们新建一个button组件,然后把button的文字作为slot传进去

<template>
    <button class="button-34" role="button">
        <slot></slot>
    </button>
</template>
...css

在user card中再引入注册button组件,并加入到模版中

   <fancy-button class="btn">hide info</fancy-button>

(4)事件交互

目前为止页面是这个样子 CleanShot 2022-11-09 at 23.32.40@2x.png

我们需要在usercard组件中实现:点击button后,一来card上的电话和邮箱消失,二来button上的文字也要改变。

      <div class="info card-right">
      <!-- 当infoVisible为true时,显示这部分元素,否则隐藏这部分元素 -->
            <div class="hidable" v-show="infoVisible">
                <div>{{ email }}</div>
                <div>{{ phone }}</div>
            </div>
            <fancy-button class="btn" @click=toggleInfo>
             <!-- 根据infoVisible的值来决定button文字 -->
                <span v-show="infoVisible">hide info</span>
                <span v-show="!infoVisible">show info</span>
            </fancy-button>
        </div>

script中添加:

   data() {
        return {
            infoVisible: true
        }
    },
    methods: {
        toggleInfo() {
            this.infoVisible = !this.infoVisible
        }
    }