• Vue3学习笔记(三)——模板语法、Class 与 Style 、ES6新增数组方法、UI库介绍


    一、模板语法

    Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。

    在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态变更时,Vue 能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的 DOM 操作。

    如果你对虚拟 DOM 的概念比较熟悉,并且偏好直接使用 JavaScript,你也可以结合可选的 JSX 支持直接手写渲染函数而不采用模板。但请注意,这将不会享受到和模板同等级别的编译时优化。

    1.1、文本插值

    最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号):

    <span>Message: {{ msg }}</span>

    双大括号标签会被替换为相应组件实例中 msg 属性的值。同时每次 msg 属性更改时它也会同步更新。

    1.2、原始 HTML

    双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html 指令

    <p>Using text interpolation: {{ rawHtml }}</p>
    <p>Using v-html directive: <span v-html="rawHtml"></span></p>

    Using text interpolation: <span style="color: red">This should be red.</span>

    Using v-html directive: This should be red.

    <template>
      <h2>
        {{ msg }}
      </h2>
      <h2 v-text="msg"></h2>
      <h2 v-html="msg"></h2>
    </template>
    
    <script lang="ts">
    import { ref, getCurrentInstance } from "vue";
    export default {
      setup() {
        let msg = "<p style='color:blue'>Hello, world!</p>";
        return { msg };
      },
    };
    </script>

    这里我们遇到了一个新的概念。这里看到的 v-html attribute 被称为一个指令。指令由 v- 作为前缀,表明它们是一些由 Vue 提供的特殊 attribute,你可能已经猜到了,它们将为渲染的 DOM 应用特殊的响应式行为。这里我们做的事情简单来说就是:在当前组件实例上,将此元素的 innerHTML 与 rawHtml 属性保持同步。

    span 的内容将会被替换为 rawHtml 属性的值,插值为纯 HTML——数据绑定将会被忽略。注意,你不能使用 v-html 来拼接组合模板,因为 Vue 不是一个基于字符串的模板引擎。在使用 Vue 时,应当使用组件作为 UI 重用和组合的基本单元。

    安全警告

    在网站上动态渲染任意 HTML 是非常危险的,因为这非常容易造成 XSS 漏洞。请仅在内容安全可信时再使用 v-html,并且永远不要使用用户提供的 HTML 内容。

    跨站脚本攻击(也称为XSS)指利用网站漏洞从用户那里恶意盗取信息。

    <template>
      <h2>
        {{ msg }}
      </h2>
      <h2 v-text="msg"></h2>
      <div v-html="msg"></div>
      <textarea v-model="msg" style=" 100%; height: 300px"></textarea>
    </template>
    
    <script lang="ts">
    import { ref, getCurrentInstance } from "vue";
    export default {
      setup() {
        let msg = ref("<p style='color:blue'>Hello, world!</p>");
        return { msg };
      },
    };
    </script>

    1.3、属性绑定

    双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind 指令

    <div v-bind:id="dynamicId"></div>

    v-bind 指令指示 Vue 将元素的 id attribute 与组件的 dynamicId 属性保持一致。如果绑定的值是 null 或者 undefined,那么该 attribute 将会从渲染的元素上移除。

    1.3.1、简写

    因为 v-bind 非常常用,我们提供了特定的简写语法:

    <div :id="dynamicId"></div>

    开头为 : 的 attribute 可能和一般的 HTML attribute 看起来不太一样,但它的确是合法的 attribute 名称字符,并且所有支持 Vue 的浏览器都能正确解析它。此外,他们不会出现在最终渲染的 DOM 中。简写语法是可选的,但相信在你了解了它更多的用处后,你应该会更喜欢它。

    接下来的指引中,我们都将在示例中使用简写语法,因为这是在实际开发中更常见的用法。

    1.3.2、布尔型属性

    布尔型 attribute 依据 true / false 值来决定 attribute 是否应该存在于该元素上。disabled 就是最常见的例子之一。

    v-bind 在这种场景下的行为略有不同:

    <button :disabled="isButtonDisabled">Button</button>

    当 isButtonDisabled 为真值或一个空字符串 (即 <button disabled="">) 时,元素会包含这个 disabled attribute。而当其为其他假值时 attribute 将被忽略。

    1.3.3、动态绑定多个值

    如果你有像这样的一个包含多个 attribute 的 JavaScript 对象:

    const objectOfAttrs = {
      id: 'container',
      class: 'wrapper'
    }

    通过不带参数的 v-bind,你可以将它们绑定到单个元素上:

    <div v-bind="objectOfAttrs"></div>
    <template>
      <button :title="msg + '!!!'">冒号绑定</button>
      <button v-bind:title="msg + '!!!'">v-bind绑定</button>
      <button :disabled="isDisalbed">被禁用的按钮</button>
      <button v-bind="attrs">登录按钮</button>
    </template>
    
    <script lang="ts">
    import { ref, getCurrentInstance } from "vue";
    export default {
      setup() {
        let msg = "这是一个按钮";
        let isDisalbed = ref(true);
        let attrs = {
          class: "cls1",
          style: "color:red",
          id: "btnLogin",
        };
        return { msg, isDisalbed, attrs };
      },
    };
    </script>

    1.4、使用 JavaScript 表达式

    至此,我们仅在模板中绑定了一些简单的属性名。但是 Vue 实际上在所有的数据绑定中都支持完整的 JavaScript 表达式:

    {{ number + 1 }}
    
    		{{ ok ? 'YES' : 'NO' }}
    
    		{{ message.split('').reverse().join('') }}
    
    		<div :id="`list-${id}`"></div>
    		

    这些表达式都会被作为 JavaScript ,以组件为作用域解析执行。

    在 Vue 模板内,JavaScript 表达式可以被使用在如下场景上:

    • 在文本插值中 (双大括号)
    • 在任何 Vue 指令 (以 v- 开头的特殊属性) 属性的值中

    1.4.1、仅支持表达式

    每个绑定仅支持单一表达式,也就是一段能够被求值的 JavaScript 代码。一个简单的判断方法是是否可以合法地写在 return 后面。

    因此,下面的例子都是无效的:

    <!-- 这是一个语句,而非表达式 -->
    {{ var a = 1 }}
    
    <!-- 条件控制也不支持,请使用三元表达式 -->
    {{ if (ok) { return message } }}

    1.4.2、调用函数

    可以在绑定的表达式中使用一个组件暴露的方法:

    <span :title="toTitleDate(date)">
      {{ formatDate(date) }}
    </span>

    TIP

    绑定在表达式中的方法在组件每次更新时都会被重新调用,因此不应该产生任何副作用,比如改变数据或触发异步操作。

    <template>
      {{ show() }}
      <hr />
      <input v-model="msg" />
    </template>
    <script lang="ts" setup>
    import { ref } from "vue";
    function show() {
      console.log("show被调用了");
    }
    let msg = ref("Hello");
    </script>

    <template>
      <button @click="msg = msg ? 'ok' : 'no'">
        {{ (msg + "!!!").split("").reverse().join("") }}
      </button>
    
      <h2>
        {{ `消息内容是:${msg}` }}
      </h2>
    
      <input v-model="msg" />
    
      <h2>
        {{ showInfo() }}
      </h2>
    
      <h2>
        {{ showInfoComputed }}
      </h2>
    </template>
    
    <script lang="ts">
    import { computed } from "@vue/reactivity";
    import { ref, getCurrentInstance } from "vue";
    export default {
      setup() {
        let msg = ref("这是一个按钮");
        function showInfo() {
          console.log("showInfo被调用了");
          return "showinfo的返回值";
        }
        let showInfoComputed = computed(() => {
          console.log("showInfoComputed被调用了");
          return "showInfoComputed的返回值";
        });
        return { msg, showInfo, showInfoComputed };
      },
    };
    </script>

    1.4.3、受限的全局访问

    模板中的表达式将被沙盒化,仅能够访问到有限的全局对象列表。该列表中会暴露常用的内置全局对象,比如 Math 和 Date

    没有显式包含在列表中的全局对象将不能在模板内表达式中访问,例如用户附加在 window 上的属性。然而,你也可以自行在 app.config.globalProperties 上显式地添加它们,供所有的 Vue 表达式使用。

    1.4.4、vue3.0之全局变量app.config.globalProperties的使用

    globalProperties

    类型:[key: string]: any
    默认:undefined
    用法
    添加一个可以在应用的任何组件实例中访问的全局 property。组件的 property 在命名冲突具有优先权。

    这可以代替 Vue 2.x Vue.prototype 扩展:

    // 之前(Vue 2.x)
    Vue.prototype.$http = () => {}

    Vue3挂载前添加全局对象,main.ts:

    import { createApp } from 'vue'
    import App from './App.vue'
    import HelloWorld from './components/HelloWorld.vue'
    import router from './router'
    
    let app=createApp(App);
    
    app.config.globalProperties.msg="abc";
    
    app.use(router).mount('#app');

    取值:

    <template>
      <input v-model="info" />
      <button @click="info += '1'">加1</button>
    </template>
    <script lang="ts">
    import { ref, getCurrentInstance } from "vue";
    export default {
      setup() {
        let msg = getCurrentInstance()?.appContext.config.globalProperties.msg;
        let info = ref(msg);
    
        return { info };
      },
      mounted(this: any) {
        console.log(this.msg);
      },
    };
    </script>

    注意:vue3推荐使用依赖注入:provide和inject。原因:vue/rfcs.

    1.5、指令 Directives

    指令是带有 v- 前缀的特殊 attribute。Vue 提供了许多内置指令,包括上面我们所介绍的 v-bind 和 v-html

    指令 attribute 的期望值为一个 JavaScript 表达式 (除了少数几个例外,即之后要讨论到的 v-forv-on 和 v-slot)。一个指令的任务是在其表达式的值变化时响应式地更新 DOM。以 v-if 为例:

    <p v-if="seen">Now you see me</p>

    这里,v-if 指令会基于表达式 seen 的值的真假来移除/插入该 <p> 元素。

    1.5.1、参数 Arguments

    某些指令会需要一个“参数”,在指令名后通过一个冒号隔开做标识。例如用 v-bind 指令来响应式地更新一个 HTML attribute:

    <a v-bind:href="url"> ... </a>
    
    <!-- 简写 -->
    <a :href="url"> ... </a>

    这里 href 就是一个参数,它告诉 v-bind 指令将表达式 url 的值绑定到元素的 href attribute 上。在简写中,参数前的一切 (例如 v-bind:) 都会被缩略为一个 : 字符。

    另一个例子是 v-on 指令,它将监听 DOM 事件:

    <a v-on:click="doSomething"> ... </a>
    
    <!-- 简写 -->
    <a @click="doSomething"> ... </a>

    这里的参数是要监听的事件名称:clickv-on 有一个相应的缩写,即 @ 字符。我们之后也会讨论关于事件处理的更多细节。

    1.5.2、动态参数

    同样在指令参数上也可以使用一个 JavaScript 表达式,需要包含在一对方括号内:

    <!--
    注意,参数表达式有一些约束,
    参见下面“动态参数值的限制”与“动态参数语法的限制”章节的解释
    -->
    <a v-bind:[attributeName]="url"> ... </a>
    
    <!-- 简写 -->
    <a :[attributeName]="url"> ... </a>

    这里的 attributeName 会作为一个 JavaScript 表达式被动态执行,计算得到的值会被用作最终的参数。举例来说,如果你的组件实例有一个数据属性 attributeName,其值为 "href",那么这个绑定就等价于 v-bind:href

    相似地,你还可以将一个函数绑定到动态的事件名称上:

    <a v-on:[eventName]="doSomething"> ... </a>
    
    <!-- 简写 -->
    <a @[eventName]="doSomething">

    在此示例中,当 eventName 的值是 "focus" 时,v-on:[eventName] 就等价于 v-on:focus

    动态参数值的限制

    动态参数中表达式的值应当是一个字符串,或者是 null。特殊值 null 意为显式移除该绑定。其他非字符串的值会触发警告。

    动态参数语法的限制

    动态参数表达式因为某些字符的缘故有一些语法限制,比如空格和引号,在 HTML attribute 名称中都是不合法的。例如下面的示例:

    <!-- 这会触发一个编译器警告 -->
    <a :['foo' + bar]="value"> ... </a>

    如果你需要传入一个复杂的动态参数,我们推荐使用计算属性替换复杂的表达式,也是 Vue 最基础的概念之一,我们很快就会讲到。

    当使用 DOM 内嵌模板 (直接写在 HTML 文件里的模板) 时,我们需要避免在名称中使用大写字母,因为浏览器会强制将其转换为小写:

    <a :[someAttr]="value"> ... </a>

    上面的例子将会在 DOM 内嵌模板中被转换为 :[someattr]。如果你的组件拥有 “someAttr” 属性而非 “someattr”,这段代码将不会工作。单文件组件内的模板不受此限制。

    <template>
      <button :[attrName]="'button'">这是一个按钮</button>
      要绑定的属性名称:<input v-model="attrName" />
    
      <hr />
    
      <button @[eventName]="show">这是一个按钮</button>
      要绑定的事件名称:<input v-model="eventName" />
    </template>
    
    <script lang="ts">
    import { ref } from "vue";
    export default {
      setup() {
        let attrName = ref("title");
        let eventName = ref("click");
        let show = (e) => {
          console.log(eventName.value);
        };
        return { attrName, eventName, show };
      },
    };
    </script>
    <style scoped></style>

    1.5.3、修饰符 Modifiers

    修饰符是以点开头的特殊后缀,表明指令需要以一些特殊的方式被绑定。例如 .prevent 修饰符会告知 v-on 指令对触发的事件调用 event.preventDefault()

    <form @submit.prevent="onSubmit">...</form>

    之后在讲到 v-on 和 v-model 的功能时,你将会看到其他修饰符的例子。

    最后,在这里你可以直观地看到完整的指令语法:

    指令语法图

    1.5.4、v-text

    更新元素的文本内容。

    • 期望的绑定值类型:string

    • 详细信息

      v-text 通过设置元素的 textContent 属性来工作,因此它将覆盖元素中所有现有的内容。如果你需要更新 textContent 的部分,应该使用 mustache interpolations 代替。

    • 示例

    • <span v-text="msg"></span>
      <!-- 等同于 -->
      <span>{{msg}}</span>
    • 参考:模板语法 - 文本插值

    1.5.5、v-html

    更新元素的 innerHTML

    • 期望的绑定值类型:string

    • 详细信息

    v-html 的内容直接作为普通 HTML 插入—— Vue 模板语法是不会被解析的。如果你发现自己正打算用 v-html 来编写模板,不如重新想想怎么使用组件来代替。

    安全说明

    在你的站点上动态渲染任意的 HTML 是非常危险的,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要将用户提供的内容作为插值

    单文件组件scoped 样式将不会作用于 v-html 里的内容,因为 HTML 内容不会被 Vue 的模板编译器解析。如果你想让 v-html 的内容也支持 scoped CSS,你可以使用 CSS modules 或使用一个额外的全局 <style> 元素,手动设置类似 BEM 的作用域策略。

    <template>
      <h2 v-html="msg"></h2>
      <p>这是一个P标签</p>
    </template>
    
    <script lang="ts">
    import { computed } from "@vue/reactivity";
    import { ref, getCurrentInstance } from "vue";
    export default {
      setup() {
        let msg = ref("<p>这是一个按钮</p>");
        return { msg };
      },
    };
    </script>
    <style scoped>
    p {
      color: red;
    }
    </style>

    1.5.6、v-show

    基于表达式值的真假性,来改变元素的可见性。

    • 期望的绑定值类型:any

    • 详细信息

      v-show 通过设置内联样式的 display CSS 属性来工作,当元素可见时将使用初始 display 值。当条件改变时,也会触发过渡效果。

    • 参考:条件渲染 - v-show

    1.5.7、v-if

    基于表达式值的真假性,来条件性地渲染元素或者模板片段。

    • 期望的绑定值类型:any

    • 详细信息

      当 v-if 元素被触发,元素及其所包含的指令/组件都会销毁和重构。如果初始条件是假,那么其内部的内容根本都不会被渲染。

      可用于 <template> 表示仅包含文本或多个元素的条件块。

      当条件改变时会触发过渡效果。

      当同时使用时,v-if 比 v-for 优先级更高。我们并不推荐在一元素上同时使用这两个指令 — 查看列表渲染指南详情。

    • 参考:条件渲染 - v-if

    • 因为 v-if 是一个指令,他必须依附于某个元素。但如果我们想要切换不止一个元素呢?在这种情况下我们可以在一个 <template> 元素上使用 v-if,这只是一个不可见的包装器元素,最后渲染的结果并不会包含这个 <template> 元素。

      <template v-if="ok">
        <h1>Title</h1>
        <p>Paragraph 1</p>
        <p>Paragraph 2</p>
      </template>

      v-else 和 v-else-if 也可以在 <template> 上使用。

    • <template>
        <button @click="isShow = !isShow">{{ isShow ? "隐藏" : "显示" }}</button>
      
        <div v-if="isShow">
          <h2>这段文字的显示与隐藏完全是由isShow控制的,DOM控制的</h2>
          <textarea>textarea</textarea>
          <textarea>textarea</textarea>
        </div>
      
        <template v-if="isShow">
          <h2>这段文字的显示与隐藏完全是由isShow控制的,DOM控制的</h2>
          <textarea>textarea</textarea>
          <textarea>textarea</textarea>
        </template>
      </template>
      
      <script lang="ts">
      import { ref } from "vue";
      export default {
        setup() {
          let isShow = ref(true);
          return { isShow };
        },
      };
      </script>
      <style scoped></style>

    1.5.8、v-if vs v-show

    • v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
    • v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
    • 相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display 属性会被切换。
    • 总的来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。

    1.5.9、v-else

    表示 v-if 或 v-if / v-else-if 链式调用的“else 块”。

    • 无需传入表达式

    • 详细信息

      • 限定:上一个兄弟元素必须有 v-if 或 v-else-if

      • 可用于 <template> 表示仅包含文本或多个元素的条件块。

    • 示例

    • <div v-if="Math.random() > 0.5">
        Now you see me
      </div>
      <div v-else>
        Now you don't
      </div>
    • 参考:条件渲染 - v-else

    1.5.10、v-else-if

    表示 v-if 的“else if 块”。可以进行链式调用。

    • 期望的绑定值类型:any

    • 详细信息

      • 限定:上一个兄弟元素必须有 v-if 或 v-else-if

      • 可用于 <template> 表示仅包含文本或多个元素的条件块。

    • 示例

    • <div v-if="type === 'A'">
        A
      </div>
      <div v-else-if="type === 'B'">
        B
      </div>
      <div v-else-if="type === 'C'">
        C
      </div>
      <div v-else>
        Not A/B/C
      </div>
    • 参考:条件渲染 - v-else-if

    示例:

    <template>
      <h2 v-html="msg"></h2>
      <template v-if="isShow">
        <p>这是一个P标签</p>
        <button>abc</button>
      </template>
      <button @click="isShow = !isShow">{{ `isShow=${isShow}` }}</button>
    
      <div v-if="Math.random() > 0.5">Math.random()&gt;0.5</div>
      <div v-else>Math.random()&lt;=0.5</div>
    
      <hr />
      <input v-model="type" />
      <p v-if="type === 'A'">优秀</p>
      <p v-else-if="type === 'B'">良好</p>
      <p v-else-if="type === 'C'">达标</p>
      <p v-else>不达标</p>
    </template>
    
    <script lang="ts">
    import { computed } from "@vue/reactivity";
    import { ref, getCurrentInstance } from "vue";
    export default {
      setup() {
        let msg = ref("<p>这是一个按钮</p>");
        let isShow = ref(true);
        let type = ref("A");
        return { msg, isShow, type };
      },
    };
    </script>
    <style scoped>
    p {
      color: red;
    }
    </style>

    结果:

    修改:

    1.5.11、v-for

    基于原始数据多次渲染元素或模板块。

    • 期望的绑定值类型:Array | Object | number | string | Iterable

    • 详细信息

      指令值必须使用特殊语法 alias in expression 为正在迭代的元素提供一个别名:

    • <div v-for="item in items">
        {{ item.text }}
      </div>
    • 或者,你也可以为索引指定别名 (如果用在对象,则是键值):

    • <div v-for="(item, index) in items"></div>
      <div v-for="(value, key) in object"></div>
      <div v-for="(value, name, index) in object"></div>
    • v-for 的默认方式是尝试就地更新元素而不移动它们。要强制其重新排序元素,你需要用特殊 attribute key 来提供一个排序提示:

    • <div v-for="item in items" :key="item.id">
        {{ item.text }}
      </div>
    • v-for 也可以用于 Iterable Protocol 的实现,包括原生 Map 和 Set

    • <template>
        <ul>
          <li v-for="(item, index) in users" :key="item.id">
            {{ index }} - {{ item.id }} - {{ item.name }}
            <hr />
            <ul>
              <li v-for="(value, key, index) in item">
                {{ index }} - {{ key }}:{{ value }}
              </li>
            </ul>
          </li>
        </ul>
        <hr />
        <ul>
          <li v-for="(value, key, index) in user">
            {{ index }} - {{ key }}:{{ value }}
          </li>
        </ul>
      </template>
      
      <script lang="ts">
      import { reactive, ref } from "vue";
      export default {
        setup() {
          let users = reactive([
            { id: 201, name: "mark" },
            { id: 202, name: "rose" },
            { id: 203, name: "jack" },
          ]);
      
          let user = { name: "张三", age: 19 };
      
          return { users, user };
        },
      };
      </script>
      <style scoped>
      .div1 {
        height: 200px;
        overflow: auto;
        width: 300px;
        background-color: lightgoldenrodyellow;
      }
      .div1 ul li {
        height: 300px;
        line-height: 300px;
      }
      </style>

    • 参考:

    1.5.12、v-on

    给元素绑定事件监听器。

    • 缩写:@

    • 期望的绑定值类型:Function | Inline Statement | Object (不带参数)

    • 参数:event (使用对象语法则为可选项)

    • 修饰符:

      • .stop ——调用 event.stopPropagation()
      • <template>
          <div id="div1" @click="showInfo">
            <button @click.stop="showInfo">stop示例</button>
          </div>
        </template>
        
        <script lang="ts">
        import { reactive, ref } from "vue";
        export default {
          setup() {
            function showInfo() {
              console.log("事件被触发了");
            }
            return { showInfo };
          },
        };
        </script>
        <style scoped>
        #div1 {
          height: 100px;
          line-height: 100px;
          text-align: center;
          width: 200px;
          background: yellow;
        }
        </style>

      • .prevent ——调用 event.preventDefault()
      • <template>
          <div id="div1" @click="showInfo">
            <button @click.stop="showInfo">stop示例</button>
            <a href="http://best.cnblogs.com" @click.stop.prevent="showInfo">博客2</a>
          </div>
        
          <hr />
        
          <a href="http://best.cnblogs.com" @click.prevent="showInfo">博客1</a>
        </template>
        
        <script lang="ts">
        import { reactive, ref } from "vue";
        export default {
          setup() {
            function showInfo() {
              console.log("事件被触发了");
            }
            return { showInfo };
          },
        };
        </script>
        <style scoped>
        #div1 {
          height: 100px;
          line-height: 100px;
          text-align: center;
          width: 200px;
          background: yellow;
        }
        </style>

      • .capture ——在捕获模式添加事件监听器。
      • <template>
          <div id="div1" @click.capture="showInfo('2、父元素', $event)">
            <button @click="showInfo('1、子元素', $event)">capture示例</button>
          </div>
        </template>
        
        <script lang="ts">
        import { reactive, ref } from "vue";
        export default {
          setup() {
            function showInfo(msg, event) {
              console.log(msg, event);
            }
            return { showInfo };
          },
        };
        </script>
        <style scoped>
        #div1 {
          height: 100px;
          line-height: 100px;
          text-align: center;
          width: 200px;
          background: yellow;
        }
        </style>

      • .self ——只有事件从元素本身发出才触发处理函数。
      • <template>
          <div id="div1" @click.self="showInfo('2、父元素', $event)">
            <button @click="showInfo('1、子元素', $event)">self示例</button>
          </div>
        </template>
        
        <script lang="ts">
        import { reactive, ref } from "vue";
        export default {
          setup() {
            function showInfo(msg, event) {
              console.log(msg, event.target);
            }
            return { showInfo };
          },
        };
        </script>
        <style scoped>
        #div1 {
          height: 100px;
          line-height: 100px;
          text-align: center;
          width: 200px;
          background: yellow;
        }
        </style>

      • .{keyAlias} ——只在某些按键下触发处理函数。
        • 常用别名如下:  

          Vue中常用的按键别名:

        • 回车 => enter

          删除 => delete (捕获"删除”和“退格”键)

          退出 => esc

          空格 => space

          换行 => tab (不适合用keyup事件 适合用于keydown事件)

          上 => up

          下 => down

          左 => left

          右 => right

          <template>
            <input @keyup.enter="keyEvent" />
          </template>
          
          <script lang="ts">
          import { reactive, ref } from "vue";
          export default {
            setup() {
              function keyEvent(e) {
                console.log("key:" + e.key, "code:" + e.code, "keyCode:" + e.keyCode);
              }
          
              return { keyEvent };
            },
          };
          </script>
          <style scoped>
          .div1 {
            height: 200px;
            overflow: auto;
            width: 300px;
            background-color: lightgoldenrodyellow;
          }
          .div1 ul li {
            height: 300px;
            line-height: 300px;
          }
          </style>

      • .once ——最多触发一次处理函数。
      • .left ——只在鼠标左键事件触发处理函数。
      • .right ——只在鼠标右键事件触发处理函数。
      • .middle ——只在鼠标中键事件触发处理函数。
      • <template>
          <input @keyup.ctrl.alt.s="keyEvent" />
          <button @click.right.prevent="rightClickEvent">只有按右键才触发事件</button>
          <button @mousedown.middle="rightClickEvent">只有按中键才触发事件</button>
          <button @mouseup.left="rightClickEvent">只有按左键才触发事件</button>
        </template>
        
        <script lang="ts">
        import { reactive, ref } from "vue";
        export default {
          setup() {
            function keyEvent(e) {
              console.log("key:" + e.key, "code:" + e.code, "keyCode:" + e.keyCode);
              return false;
            }
        
            function rightClickEvent(e) {
              console.log("您点击了", e);
              console.log(["", "", ""][e.button] + "键被点击");
            }
        
            return { keyEvent, rightClickEvent };
          },
        };
        </script>
        <style scoped></style>

      • 模拟同步延时
      • <!DOCTYPE html>
        <html lang="en">
          <head>
            <meta charset="UTF-8" />
            <meta http-equiv="X-UA-Compatible" content="IE=edge" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>Document</title>
          </head>
          <body>
            <button id="btnLogin">登录</button>
            <script>
              //异步的,异步IO
              // setTimeout(() => {
              //   console.log("A、2000毫秒后你可以看到控制台输出该消息");
              // }, 2000);
        
              // console.log("B、2000毫秒后你可以看到控制台输出该消息");
        
              function sleep(millSeconds) {
                let time = new Date().getTime();
                while (new Date().getTime() <= time + millSeconds);
              }
        
              for (var i = 0; i < 10; i++) {
                console.log(i);
                sleep(2000);
              }
            </script>
          </body>
        </html>

      • .passive ——通过 { passive: true } 附加一个 DOM 事件。 positive正面的
        •   
          <!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 -->
          <!-- 以防其中包含 `event.preventDefault()` -->
          <div @scroll.passive="onScroll">...</div>
    • <template>
        <!--wheel是中间的滚轮-->
        <!--scroll是滚动事件-->
        <div id="div1" @wheel.passive="eventMethod">
          <ul>
            <li>rose</li>
            <li>mark</li>
            <li>jack</li>
            <li>lucy</li>
            <li>lili</li>
          </ul>
        </div>
      </template>
      
      <script lang="ts">
      import { reactive, ref } from "vue";
      export default {
        setup() {
          //延时
          let sleep = (millSeconds) => {
            let time = new Date().getTime();
            while (new Date().getTime() <= time + millSeconds);
          };
      
          let eventMethod = (e) => {
            sleep(2000);
            console.log(e);
          };
          return { eventMethod };
        },
      };
      </script>
      <style scoped>
      #div1 {
        width: 300px;
        height: 100px;
        background: lightgoldenrodyellow;
        overflow: auto;
      }
      
      #div1 li {
        margin: 20px;
        background-color: aquamarine;
        height: 100px;
        width: 200px;
      }
      </style>

    • 详细信息

      事件类型由参数来指定。表达式可以是一个方法名,一个内联声明,如果有修饰符则可省略。

      当用于普通元素,只监听原生 DOM 事件。当用于自定义元素组件,则监听子组件触发的自定义事件。

      当监听原生 DOM 事件时,方法接收原生事件作为唯一参数。如果使用内联声明,声明可以访问一个特殊的 $event 变量:v-on:click="handle('ok', $event)"

      v-on 还支持绑定不带参数的事件/监听器对的对象。请注意,当使用对象语法时,不支持任何修饰符。

    • 示例:

    • <!-- 方法处理函数 -->
      <button v-on:click="doThis"></button>
      
      <!-- 动态事件 -->
      <button v-on:[event]="doThis"></button>
      
      <!-- 内联声明 -->
      <button v-on:click="doThat('hello', $event)"></button>
      
      <!-- 缩写 -->
      <button @click="doThis"></button>
      
      <!-- 使用缩写的动态事件 -->
      <button @[event]="doThis"></button>
      
      <!-- 停止传播 -->
      <button @click.stop="doThis"></button>
      
      <!-- 阻止默认事件 -->
      <button @click.prevent="doThis"></button>
      
      <!-- 不带表达式地阻止默认事件 -->
      <form @submit.prevent></form>
      
      <!-- 链式调用修饰符 -->
      <button @click.stop.prevent="doThis"></button>
      
      <!-- 按键用于 keyAlias 修饰符-->
      <input @keyup.enter="onEnter" />
      
      <!-- 点击事件将最多触发一次 -->
      <button v-on:click.once="doThis"></button>
      
      <!-- 对象语法 -->
      <button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
    • 监听子组件的自定义事件 (当子组件的“my-event”事件被触发,处理函数将被调用):

    • <MyComponent @my-event="handleThis" />
      
      <!-- 内联声明 -->
      <MyComponent @my-event="handleThis(123, $event)" />
    • 参考:

    1.5.13、v-bind

    动态的绑定一个或多个 attribute,也可以是组件的 prop。

    • 缩写:: 或者 . (当使用 .prop 修饰符)

    • 期望:any (带参数) | Object (不带参数)

    • 参数:attrOrProp (可选的)

    • 修饰符:

      • .camel ——将短横线命名的 attribute 转变为驼峰式命名。
      • .prop ——强制绑定为 DOM property。3.2+
      • .attr ——强制绑定为 DOM attribute。3.2+
    • 用途:

      当用于绑定 class 或 style attribute,v-bind 支持额外的值类型如数组或对象。详见下方的指南链接。

      在处理绑定时,Vue 默认会利用 in 操作符来检查该元素上是否定义了和绑定的 key 同名的 DOM property。如果存在同名的 property,则 Vue 会把作为 DOM property 赋值,而不是作为 attribute 设置。这个行为在大多数情况都符合期望的绑定值类型,但是你也可以显式用 .prop 和 .attr 修饰符来强制绑定方式。有时这是必要的,特别是在和自定义元素打交道时。

      当用于组件 props 绑定时,所绑定的 props 必须在子组件中已被正确声明。

      当不带参数使用时,可以用于绑定一个包含了多个 attribute 名称-绑定值对的对象。

    • 示例:

    • <!-- 绑定 attribute -->
      <img v-bind:src="imageSrc" />
      
      <!-- 动态 attribute 名 -->
      <button v-bind:[key]="value"></button>
      
      <!-- 缩写 -->
      <img :src="imageSrc" />
      
      <!-- 缩写形式的动态 attribute 名 -->
      <button :[key]="value"></button>
      
      <!-- 内联字符串拼接 -->
      <img :src="'/path/to/images/' + fileName" />
      
      <!-- class 绑定 -->
      <div :class="{ red: isRed }"></div>
      <div :class="[classA, classB]"></div>
      <div :class="[classA, { classB: isB, classC: isC }]"></div>
      
      <!-- style 绑定 -->
      <div :style="{ fontSize: size + 'px' }"></div>
      <div :style="[styleObjectA, styleObjectB]"></div>
      
      <!-- 绑定对象形式的 attribute -->
      <div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
      
      <!-- prop 绑定。“prop” 必须在子组件中已声明。 -->
      <MyComponent :prop="someThing" />
      
      <!-- 传递子父组件共有的 prop -->
      <MyComponent v-bind="$props" />
      
      <!-- XLink -->
      <svg><a :xlink:special="foo"></a></svg>
    • .prop 修饰符也有专门的缩写,.

    • <div :someProperty.prop="someObject"></div>
      
      <!-- 等同于 -->
      <div .someProperty="someObject"></div>
    • 当在 DOM 内模板使用 .camel 修饰符,可以驼峰化 v-bind attribute 的名称,例如 SVG viewBox attribute:

    • <svg :view-box.camel="viewBox"></svg>
    • 如果使用字符串模板或使用构建步骤预编译模板,则不需要 .camel

    • 参考:

    1.5.14、v-model

    在表单输入元素或组件上创建双向绑定。

    • 期望的绑定值类型:根据表单输入元素或组件输出的值而变化

    • 仅限:

      • <input>
      • <select>
      • <textarea>
      • components
    • 修饰符:

      • .lazy ——监听 change 事件而不是 input
      • .number ——将输入的合法符串转为数字
      • .trim ——移除输入内容两端空格
    • <template>
        <p>
          <input v-model.lazy="msg" />
        </p>
        <p>
          <select v-model="msg">
            <option value="apple">苹果</option>
            <option value="orange">橙子</option>
          </select>
        </p>
        <p>
          <textarea v-model="msg" />
        </p>
        <h2>
          {{ msg }}
        </h2>
        <p>
          <input v-model.number.trim="cardno" @keyup="show" />
        </p>
        <h2>卡号:{{ cardno }}</h2>
      </template>
      
      <script lang="ts">
      import { reactive, ref } from "vue";
      export default {
        setup() {
          let msg = ref("");
          let cardno = ref("");
          function show() {
            console.log(typeof cardno.value, cardno);
          }
          return { msg, cardno, show };
        },
      };
      </script>
      <style scoped>
      #div1 {
        width: 300px;
        height: 100px;
        background: lightgoldenrodyellow;
        overflow: auto;
      }
      
      #div1 li {
        margin: 20px;
        background-color: aquamarine;
        height: 100px;
        width: 200px;
      }
      </style>

    • 参考:

    1.5.15、v-slot

    用于声明具名插槽或是期望接收 props 的作用域插槽。

    • 缩写:#

    • 期望的绑定值类型:能够合法在函数参数位置使用的 JavaScript 表达式。支持解构语法。绑定值是可选的——只有在给作用域插槽传递 props 才需要。

    • 参数:插槽名 (可选,默认是 default)

    • 仅限:

      • <template>
      • components (用于带有 prop 的单个默认插槽)
    • 示例:

    • <!-- 具名插槽 -->
      <BaseLayout>
        <template v-slot:header>
          Header content
        </template>
      
        <template v-slot:default>
          Default slot content
        </template>
      
        <template v-slot:footer>
          Footer content
        </template>
      </BaseLayout>
      
      <!-- 接收 prop 的具名插槽 -->
      <InfiniteScroll>
        <template v-slot:item="slotProps">
          <div class="item">
            {{ slotProps.item.text }}
          </div>
        </template>
      </InfiniteScroll>
      
      <!-- 接收 prop 的默认插槽,并解构 -->
      <Mouse v-slot="{ x, y }">
        Mouse position: {{ x }}, {{ y }}
      </Mouse>
    • 参考:

    1.5.16、v-pre

    跳过该元素及其所有子元素的编译。

    • 无需传入

    • 详细信息

      元素内具有 v-pre,所有 Vue 模板语法都会被保留并按原样渲染。最常见的用例就是显示原始双大括号标签及内容。

    • <template>
        <div v-pre>
          <h2>
            {{ msg }}
          </h2>
        </div>
      </template>
      
      <script lang="ts">
      import { reactive, ref } from "vue";
      export default {
        setup() {
          let msg = ref("apple");
          return { msg };
        },
      };
      </script>
      <style scoped>
      </style>

    • 示例:

    • <span v-pre>{{ this will not be compiled }}</span>

    1.5.17、v-once

    仅渲染元素和组件一次,并跳过之后的更新。

    • 无需传入

    • 详细信息

      在随后的重新渲染,元素/组件及其所有子项将被当作静态内容并跳过渲染。这可以用来优化更新时的性能。

    • <!-- 单个元素 -->
      <span v-once>This will never change: {{msg}}</span>
      <!-- 带有子元素的元素 -->
      <div v-once>
        <h1>comment</h1>
        <p>{{msg}}</p>
      </div>
      <!-- 组件 -->
      <MyComponent v-once :comment="msg" />
      <!-- `v-for` 指令 -->
      <ul>
        <li v-for="i in list" v-once>{{i}}</li>
      </ul>
    • 从 3.2 起,你也可以搭配 v-memo 的无效条件来缓存部分模板。

    • 参考:

    1.5.18、v-memo

    • 期望的绑定值类型:any[]

    • 详细信息

      缓存一个模板的子树。在元素和组件上都可以使用。为了实现缓存,该指令需要传入一个固定长度的依赖值数组进行比较。如果数组里的每个值都与最后一次的渲染相同,那么整个子树的更新将被跳过。举例来说:

    • <div v-memo="[valueA, valueB]">
        ...
      </div>
    • <template>
        valueA:<input v-model="valueA" /> msg:<input v-model="msg" />
        <div v-memo="[valueA]">
          <h2>msg:{{ msg }} {{ show() }}</h2>
        </div>
      </template>
      
      <script lang="ts">
      import { reactive, ref } from "vue";
      export default {
        setup() {
          let msg = ref("apple");
          let valueA = ref("ok");
          function show() {
            console.log("msg被渲染了");
          }
          return { msg, valueA, show };
        },
      };
      </script>
      <style scoped></style>

      当组件重新渲染,如果 valueA 和 valueB 都保持不变,这个 <div> 及其子项的所有更新都将被跳过。实际上,甚至虚拟 DOM 的 vnode 创建也将被跳过,因为缓存的子树副本可以被重新使用。

      正确指定缓存数组很重要,否则应该生效的更新可能被跳过。v-memo 传入空依赖数组 (v-memo="[]") 将与 v-once 效果相同。

      与 v-for 一起使用

      v-memo 仅用于性能至上场景中的微小优化,应该很少需要。最常见的情况可能是有助于渲染海量 v-for 列表 (长度超过 1000 的情况):

    • <div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
        <p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
        <p>...more child nodes</p>
      </div>
    • 当组件的 selected 状态改变,默认会重新创建大量的 vnode,尽管绝大部分都跟之前是一模一样的。v-memo 用在这里本质上是在说“只有当该项的被选中状态改变时才需要更新”。这使得每个选中状态没有变的项能完全重用之前的 vnode 并跳过差异比较。注意这里 memo 依赖数组中并不需要包含 item.id,因为 Vue 也会根据 item 的 :key 进行判断。

      警告

      当搭配 v-for 使用 v-memo,确保两者都绑定在同一个元素上。v-memo 不能用在 v-for 内部。

      v-memo 也能被用于在一些默认优化失败的边际情况下,手动避免子组件出现不需要的更新。但是一样的,开发者需要负责指定正确的依赖数组以免跳过必要的更新。

    • <template>
        <ul
          v-for="(item, index) in users"
          :key="item.id"
          v-memo="[selected == item.id]"
        >
          <input
            name="user"
            type="radio"
            :value="item.id"
            v-on:change="selected = item.id"
            :checked="selected === item.id"
          />
          {{
            item.id
          }}
          -
          {{
            item.name
          }}
          -
          {{
            showMsg(item)
          }}
          - selected:
          {{
            item.id === selected
          }}
        </ul>
        选择编号:{{ selected }}
        <input v-model="selected" />
      
        <input v-model="key" />
      </template>
      
      <script lang="ts">
      import { reactive, ref } from "vue";
      export default {
        setup() {
          let users = reactive([
            { id: 201, name: "mark" },
            { id: 202, name: "rose" },
            { id: 203, name: "jack" },
            { id: 204, name: "lucy" },
          ]);
          let selected = ref(0);
          function showMsg(item) {
            console.log("渲染了", item);
          }
          let key = ref("100");
          return { users, selected, showMsg, key };
        },
      };
      </script>
      <style scoped>
      .div1 {
        height: 200px;
        overflow: auto;
        width: 300px;
        background-color: lightgoldenrodyellow;
      }
      .div1 ul li {
        height: 300px;
        line-height: 300px;
      }
      </style>

    • 参考:

    1.5.19、v-cloak

    用于隐藏尚未完成编译的 DOM 模板。

    • 无需传入

    • 详细信息

      该指令只在没有构建步骤的环境下需要使用。

      当使用直接在 DOM 中书写的模板时,可能会出现一种叫做“未编译模板闪现”的情况:用户可能先看到的是还没编译完成的双大括号标签,直到挂载的组件将它们替换为实际渲染的内容。

      v-cloak 会保留在所绑定的元素上,直到相关组件实例被挂载后才移除。配合像 [v-cloak] { display: none } 这样的 CSS 规则,它可以在组件编译完毕前隐藏原始模板。

    • 示例:

      [v-cloak] {
        display: none;
      }
      <div v-cloak>
        {{ message }}
      </div>

      直到编译完成前,<div> 将不可见。

    1.6、重要的v-for指令

    1.6.1、v-for

    我们可以使用 v-for 指令基于一个数组来渲染一个列表。v-for 指令的值需要使用 item in items 形式的特殊语法,其中 items 是源数据的数组,而 item 是迭代项的别名:

    const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
    <li v-for="item in items">
      {{ item.message }}
    </li>

    在 v-for 块中可以完整地访问父作用域内的属性和变量。v-for 也支持使用可选的第二个参数表示当前项的位置索引。

    const parentMessage = ref('Parent')
    const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
    <li v-for="(item, index) in items">
      {{ parentMessage }} - {{ index }} - {{ item.message }}
    </li>
    • Parent - 0 - Foo
    • Parent - 1 - Bar

    v-for 变量的作用域和下面的 JavaScript 代码很类似:

    const parentMessage = 'Parent'
    const items = [
      /* ... */
    ]
    
    items.forEach((item, index) => {
      // 可以访问外层的 `parentMessage`
      // 而 `item` 和 `index` 只在这个作用域可用
      console.log(parentMessage, item.message, index)
    })

    注意 v-for 是如何对应 forEach 回调的函数签名的。实际上,你也可以在定义 v-for 的变量别名时使用解构,和解构函数参数类似:

    <li v-for="{ message } in items">
      {{ message }}
    </li>
    
    <!-- 有 index 索引时 -->
    <li v-for="({ message }, index) in items">
      {{ message }} {{ index }}
    </li>

    对于多层嵌套的 v-for,作用域的工作方式和函数的作用域很类似。每个 v-for 作用域都可以访问到父级作用域:

    <li v-for="item in items">
      <span v-for="childItem in item.children">
        {{ item.message }} {{ childItem }}
      </span>
    </li>

    你也可以使用 of 作为分隔符来替代 in,这更接近 JavaScript 的迭代器语法:

    <div v-for="item of items"></div>

    1.6.2、v-for 与对象

    你也可以使用 v-for 来遍历一个对象的所有属性。遍历的顺序会基于对该对象调用 Object.keys() 的返回值来决定。

    const myObject = reactive({
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    })
    <ul>
      <li v-for="value in myObject">
        {{ value }}
      </li>
    </ul>

    可以通过提供第二个参数表示属性名 (例如 key):

    <li v-for="(value, key) in myObject">
      {{ key }}: {{ value }}
    </li>

    第三个参数表示位置索引:

    <li v-for="(value, key, index) in myObject">
      {{ index }}. {{ key }}: {{ value }}
    </li>

    1.6.3、在 v-for 里使用范围值

    v-for 可以直接接受一个整数值。在这种用例中,会将该模板基于 1...n 的取值范围重复多次。

    <span v-for="n in 10">{{ n }}</span>

    注意此处 n 的初值是从 1 开始而非 0

    1.6.4、template 上的v-for

    与模板上的 v-if 类似,你也可以在 <template> 标签上使用 v-for 来渲染一个包含多个元素的块。例如:

    <ul>
      <template v-for="item in items">
        <li>{{ item.msg }}</li>
        <li class="divider" role="presentation"></li>
      </template>
    </ul>

    1.6.3、v-for 与 v-if

    注意

    同时使用 v-if 和 v-for 是不推荐的,因为这样二者的优先级不明显。请转阅风格指南查看更多细节。

    当它们同时存在于一个节点上时,v-if 比 v-for 的优先级更高。这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名:

    <!--
     这会抛出一个错误,因为属性 todo 此时
     没有在该实例上定义
    -->
    <li v-for="todo in todos" v-if="!todo.isComplete">
      {{ todo.name }}
    </li>

    在外新包装一层 <template> 再在其上使用 v-for 可以解决这个问题 (这也更加明显易读):

    <template v-for="todo in todos">
      <li v-if="!todo.isComplete">
        {{ todo.name }}
      </li>
    </template>

    1.6.4、通过 key 管理状态

    Vue 默认按照“就地更新”的策略来更新通过 v-for 渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。

    默认模式是高效的,但只适用于列表渲染输出的结果不依赖子组件状态或者临时 DOM 状态 (例如表单输入值) 的情况。

    为了给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对应的块提供一个唯一的 key attribute:

    <div v-for="item in items" :key="item.id">
      <!-- 内容 -->
    </div>

    当你使用 <template v-for> 时,key 应该被放置在这个 <template> 容器上:

    <template v-for="todo in todos" :key="todo.name">
      <li>{{ todo.name }}</li>
    </template>

    注意

    key 在这里是一个通过 v-bind 绑定的特殊 attribute。请不要和在 v-for 中使用对象里所提到的对象属性名相混淆。

    推荐在任何可行的时候为 v-for 提供一个 key attribute,除非所迭代的 DOM 内容非常简单 (例如:不包含组件或有状态的 DOM 元素),或者你想有意采用默认行为来提高性能。

    key 绑定的值期望是一个基础类型的值,例如字符串或 number 类型。不要用对象作为 v-for 的 key。关于 key attribute 的更多用途细节,请参阅 key API 文档

    示例:

    <template>
      id:<input type="text" v-model="id" />,name:<input
        type="text"
        v-model="name"
      /><button @click="add1">在末尾添加新人物</button>
      <button @click="add2">在前部添加新人物</button>
      <div v-for="item in list">
        <input type="checkbox" />
        {{ item.id }}.{{ item.name }}
      </div>
    </template>
    
    <script lang="ts">
    import { reactive, ref } from "vue";
    export default {
      setup() {
        let list = reactive([
          {
            id: 1,
            name: "刘备",
          },
          {
            id: 2,
            name: "孙权",
          },
          {
            id: 3,
            name: "曹操",
          },
          {
            id: 4,
            name: "诸葛亮",
          },
        ]);
        let id = ref(0);
        let name = ref("");
    
        function add1() {
          list.push({
            id: id.value,
            name: name.value,
          });
        }
        function add2() {
          list.unshift({
            id: id.value,
            name: name.value,
          });
        }
    
        return { list, id, name, add1, add2 };
      },
    };
    </script>
    <style scoped></style>

    在这里插入图片描述
    事先勾选3.曹操,使用push方法王数组中添加对象并没有什么问题
    在这里插入图片描述
    但是,如果将push修改unshift方式添加对象,

    v就会发现执行同样的操作时,原先勾选曹操变成了勾选孙权
    在这里插入图片描述
    这是因为勾选是和索引绑定的,当使用unshift方法时,原先的数组对象索引都加1,所以勾选的位置就往回退了一个对象,但是索引仍然不变。因此,就需要将checkbox的勾选和对象本身绑定,而非其索引,这里就需要使用到key。

    前面说到需要将checkbox和对象数组中的对象绑定,但是当我们输入如下代码却发现页面报错

    <div v-for="item in list" :key="item">
    <input type="checkbox">
    {{item.id}}.{{item.name}}
    </div>

    所以可以得出v-for循环时,key属性只能使用number或者string,而不是整个对象,修改代码

    key的作用主要是为了高效的更新虚拟DOM

    1、在组件中使用v-for循环的时候,如果v-for出现了问题,就必须同时指定唯一的字符串/数字类型的:key值。

    2、v-for循环时,key属性只能用number或者string类型。

    3、key使用时,必须使用bind属性绑定的形式,指定key的值。

    6.5、组件上使用 v-for

    这一小节假设你已了解组件的相关知识,或者你也可以先跳过这里,之后再回来看。

    我们可以直接在组件上使用 v-for,和在一般的元素上使用没有区别 (别忘记提供一个 key):

    <MyComponent v-for="item in items" :key="item.id" />

    但是,这不会自动将任何数据传递给组件,因为组件有自己独立的作用域。为了将迭代后的数据传递到组件中,我们还需要传递 props:

    <MyComponent
      v-for="(item, index) in items"
      :item="item"
      :index="index"
      :key="item.id"
    />

    不自动将 item 注入组件的原因是,这会使组件与 v-for 的工作方式紧密耦合。明确其数据的来源可以使组件在其他情况下重用。

    这里是一个简单的 Todo List 的例子,展示了如何通过 v-for 来渲染一个组件列表,并向每个实例中传入不同的数据。

    1.6.6、组件 和 v-for

    了解组件相关知识,查看  组件 。Feel free to skip it and come back later.

    在自定义组件里,你可以像任何普通元素一样用 v-for 。

    <my-component v-for="item in items"></my-component>

    然而他不能自动传递数据到组件里,因为组件有自己独立的作用域。为了传递迭代数据到组件里,我们要用 props :

    <my-component
      v-for="(item, index) in items"
      v-bind:item="item"
      v-bind:index="index">
    </my-component>

    不自动注入 item 到组件里的原因是,因为这使得组件会紧密耦合到 v-for 如何运作。在一些情况下,明确数据的来源可以使组件可重用。

    下面是一个简单的 todo list 完整的例子:

    <div id="todo-list-example">
      <input
        v-model="newTodoText"
        v-on:keyup.enter="addNewTodo"
        placeholder="Add a todo"
      >
      <ul>
        <li
          is="todo-item"
          v-for="(todo, index) in todos"
          v-bind:title="todo"
          v-on:remove="todos.splice(index, 1)"
        ></li>
      </ul>
    </div>
    Vue.component('todo-item', {
      template: '
        <li>
          {{ title }}
          <button v-on:click="$emit(\'remove\')">X</button>
        </li>',
      props: ['title']
    })
    new Vue({
      el: '#todo-list-example',
      data: {
        newTodoText: '',
        todos: [
          'Do the dishes',
          'Take out the trash',
          'Mow the lawn'
        ]
      },
      methods: {
        addNewTodo: function () {
          this.todos.push(this.newTodoText)
          this.newTodoText = ''
        }
      }
    })

    示例:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title>列表渲染</title>
        </head>
        <body>
            <div id="app1">
                任务:<input v-model="newTask" @keyup.enter="addNew" placeholder="请输入您要完成的任务" />
                <ul>
                    <li is="todoitem" v-for="(task,index) in tasks" :title="task" @remove="removeItem(index)"></li>
                </ul>
            </div>
            <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
            <script type="text/javascript">
                Vue.component("todoitem", {
                    template: "<li>{{title}} <button @click='$emit(\"remove\")'>X</button></li>",
                    props: ['title']
                });
    
                var app1 = new Vue({
                    el: "#app1",
                    data: {
                        newTask: '',
                        tasks: ["买一本书", "给爸妈打电话", "整理自己的硬盘"]
                    },
                    methods: {
                        addNew: function() {
                            this.tasks.unshift(this.newTask);
                            this.newTask = '';
                        },
                        removeItem: function(index) {
                            if(confirm('确定要移除吗?')) {
                                this.tasks.splice(index, 1);
                            }
                        }
                    }
                });
            </script>
        </body>
    </html>

    结果:

    1.7、数组变化侦测

    1.7.1、变更方法

    Vue 能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。这些变更方法包括:

    push()
    pop()
    shift()
    unshift()
    splice()
    sort()
    reverse()
    1. push () 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
    
    2. pop () 方法数组最后一位元素删除并返回数组的最后一个元素。
    
    3. shift () 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
    
    4. unshift () 方法可向数组的开头添加一个或更多元素,并返回新的长度。
    
    5.splice (index,howmany,item1, …, itemX) 方法向 / 从数组中添加 / 删除项目,然后返回被删除的项目
    第一个参数:表示从哪个索引位置(index)添加 / 删除元素
    第二个参数:要删除的项目数量。如果设置为 0,则不会删除项目。
    第三个参数:可选。向数组添加的新项目。
    
    6. sort () 方法对原列表进行排序,如果指定参数,则使用比较函数指定的比较函数。
    arr.sort (sortby) 可选。规定排序顺序。必须是函数。
    
    7. 例:大小排序
    
    function sortNumber (a, b) {
      return a - b
    }
    let arr = [10,5,40,25,1000,1]
    arr.sort(sortNumber)
    console.log(arr) // [1, 5, 10, 25, 40, 1000]
    8. reverse () 方法颠倒数组中元素的顺序。
    
    9. 替换数组
    
    它们不会变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以用新数组替换旧数组:
    
    example1.items = example1.items.filter(function (item) {
      return item.message.match(/Foo/)
    })
    
    10.filter () 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
    
    11.concat () 方法用于连接两个或多个数组。
    
    12.slice () 方法可从已有的数组中返回选定的元素。

    1.7.2、替换一个数组

    变更方法,顾名思义,就是会对调用它们的原数组进行变更。相对地,也有一些不可变 (immutable) 方法,例如 filter()concat() 和 slice(),这些都不会更改原数组,而总是返回一个新数组。当遇到的是非变更方法时,我们需要将旧的数组替换为新的:

    // `items` 是一个数组的 ref
    items.value = items.value.filter((item) => item.message.match(/Foo/))

    你可能认为这将导致 Vue 丢弃现有的 DOM 并重新渲染整个列表——幸运的是,情况并非如此。Vue 实现了一些巧妙的方法来最大化对 DOM 元素的重用,因此用另一个包含部分重叠对象的数组来做替换,仍会是一种非常高效的操作。

    <template>
      {{ arr1 }}
    
      <button @click="changeArray">修改数组</button>
    </template>
    
    <script lang="ts">
    import { reactive, ref } from "vue";
    export default {
      setup() {
        let arr1 = ref([1, 2, 3, 4, 5, 6]);
    
        function changeArray() {
          arr1.value = arr1.value.filter((a) => a % 2 == 0);
        }
        return { arr1, changeArray };
      },
    };
    </script>
    <style scoped></style>

    1.7.3、展示过滤或排序后的结果

    有时,我们希望显示数组经过过滤或排序后的内容,而不实际变更或重置原始数据。在这种情况下,你可以创建返回已过滤或已排序数组的计算属性。

    举例来说:

    const numbers = ref([1, 2, 3, 4, 5])
    
    const evenNumbers = computed(() => {
      return numbers.value.filter((n) => n % 2 === 0)
    })
    <li v-for="n in evenNumbers">{{ n }}</li>

    在计算属性不可行的情况下 (例如在多层嵌套的 v-for 循环中),你可以使用以下方法:

    const sets = ref([
      [1, 2, 3, 4, 5],
      [6, 7, 8, 9, 10]
    ])
    
    function even(numbers) {
      return numbers.filter((number) => number % 2 === 0)
    }
    <ul v-for="numbers in sets">
      <li v-for="n in even(numbers)">{{ n }}</li>
    </ul>

    在计算属性中使用 reverse() 和 sort() 的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做。请在调用这些方法之前创建一个原数组的副本:

    - return numbers.reverse()
    + return [...numbers].reverse()

    二、Class 与 Style 绑定

    数据绑定一个常见需求是操作元素的 class 列表和它的内联样式。因为它们都是属性 ,我们可以用v-bind 处理它们:只需要计算出表达式最终的字符串。不过,字符串拼接麻烦又易错。因此,在 v-bind 用于 class 和 style 时, Vue.js 专门增强了它。表达式的结果类型除了字符串之外,还可以是对象或数组。

    2.1、绑定 HTML Class

    2.1.1、对象语法

    我们可以传给 v-bind:class 一个对象,以动态地切换 class 。

    <div v-bind:class="{ active: isActive }"></div>

    上面的语法表示 classactive 的更新将取决于数据属性 isActive 是否为 真值 。

    <template>
      <h2 :class="{ active: isActive }">{{ isActive ? "激活" : "未激活" }}</h2>
      <button @click="isActive = !isActive">isActive:{{ isActive }}</button>
    </template>
    
    <script lang="ts" setup>
    import { computed } from "@vue/reactivity";
    import { reactive, ref, toRefs } from "vue";
    
    let isActive = ref(false);
    </script>
    <style scoped>
    .active {
      background: #9f9;
    }
    </style>

    我们也可以在对象中传入更多属性用来动态切换多个 class 。此外, v-bind:class 指令可以与普通的 class 属性共存。如下模板:

    <div class="static"
         v-bind:class="{ active: isActive, 'text-danger': hasError }">
    </div>

    如下 data:

    data: {
      isActive: true,
      hasError: false
    }

    渲染为:

    <div class="static active"></div>

    当 isActive 或者 hasError 变化时,class 列表将相应地更新。例如,如果 hasError的值为 true , class列表将变为 "static active text-danger"

    你也可以直接绑定数据里的一个对象:

    <div v-bind:class="classObject"></div>
    data: {
      classObject: {
        active: true,
        'text-danger': false
      }
    }

    渲染的结果和上面一样。我们也可以在这里绑定返回对象的 计算属性。这是一个常用且强大的模式:

    <div v-bind:class="classObject"></div>
    data: {
      isActive: true,
      error: null
    },
    computed: {
      classObject: function () {
        return {
          active: this.isActive && !this.error,
          'text-danger': this.error && this.error.type === 'fatal',
        }
      }
    }

    示例:

    <template>
      <h2 :class="{ active: isActive, 'text-danger': true }">
        {{ isActive ? "激活" : "未激活" }}
      </h2>
      <button @click="isActive = !isActive">isActive:{{ isActive }}</button>
    
      <p>
        <label>价格:</label>
        <input v-model="price" />
        <span class="valid" :class="{ requiredPrice }">请输入价格</span>
        <span class="valid" :class="{ rangePrice }">价格只能是0-10000之间</span>
      </p>
    </template>
    
    <script lang="ts" setup>
    import { computed } from "@vue/reactivity";
    import { reactive, ref, toRefs } from "vue";
    
    let isActive = ref(false);
    let price = ref();
    let requiredPrice = computed(() => {
      return price + "" === "" || isNaN(price.value);
    });
    let rangePrice = computed(() => {
      return price.value < 0 || price.value > 10000;
    });
    </script>
    <style scoped>
    .active {
      background: #9f9;
    }
    .text-danger {
      color: orangered;
    }
    .valid {
      color: orangered;
      display: none;
    }
    .requiredPrice,
    .rangePrice {
      display: inline;
    }
    </style>

    结果:

     

    2.1.2、数组语法

    我们可以把一个数组传给 v-bind:class ,以应用一个 class 列表:

    <div v-bind:class="[activeClass, errorClass]">
    data: {
      activeClass: 'active',
      errorClass: 'text-danger'
    }

    渲染为:

    <div class="active text-danger"></div>

    如果你也想根据条件切换列表中的 class ,可以用三元表达式:

    <div v-bind:class="[isActive ? activeClass : '', errorClass]">

    此例始终添加 errorClass ,但是只有在 isActive 是 true 时添加 activeClass 。

    不过,当有多个条件 class 时这样写有些繁琐。可以在数组语法中使用对象语法:

    <div v-bind:class="[{ active: isActive }, errorClass]">

    示例:

    <!DOCTYPE html>
    <html>
    
        <head>
            <meta charset="UTF-8">
            <title>Class 与 Style 绑定</title>
        </head>
    
        <body>
            <div id="app1">
                <span class="bgGreen" v-bind:class="[isHide,isRight]">span3</span>
                <span class="bgGreen" v-bind:class="[(isShow?'bg3':''),isRight,bg4]">span4</span>
                <span class="bgGreen" v-bind:class="[{bg3:isShow},isRight,bg4]">span5</span>
            </div>
    
            <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
            <script type="text/javascript">
                var app1 = new Vue({
                    el: "#app1",
                    data: {
                        isShow: true,
                        isHide: 'bg1',
                        isRight: 'bg2',
                        price: 0
                    }
    
                });
            </script>
        </body>
    
    </html>

    结果:

    2.1.3、With Components

    This section assumes knowledge of  Vue Components. Feel free to skip it and come back later.

    When you use the class attribute on a custom component, those classes will be added to the component’s root element. Existing classes on this element will not be overwritten.

    For example, if you declare this component:

    Vue.component('my-component', {
      template: '<p class="foo bar">Hi</p>'
    })

    Then add some classes when using it:

    <my-component class="baz boo"></my-component>

    The rendered HTML will be:

    <p class="foo bar baz boo">Hi</p>

    The same is true for class bindings:

    <my-component v-bind:class="{ active: isActive }"></my-component>

    When isActive is truthy, the rendered HTML will be:

    <div class="foo bar active"></div>

    2.2、绑定内联样式

    2.2.1、对象语法

    v-bind:style 的对象语法十分直观——看着非常像 CSS ,其实它是一个 JavaScript 对象。 CSS 属性名可以用驼峰式(camelCase)或短横分隔命名(kebab-case):

    <template>
      <h2 :style="{ background: background, fontSize: fontSize + 'px' }">样式表</h2>
    </template>
    
    <script lang="ts" setup>
    import { computed } from "@vue/reactivity";
    import { reactive, ref, toRefs } from "vue";
    let background = ref("red");
    let fontSize = ref(30);
    </script>
    <style scoped>
    </style>

    直接绑定到一个样式对象通常更好,让模板更清晰:

    <template>
      <h2 :style="objStyle">样式表</h2>
    </template>
    
    <script lang="ts" setup>
    let objStyle = {
      color: "blue",
      textDecoration: "underline",
    };
    </script>
    <style scoped></style>

    同样的,对象语法常常结合返回对象的计算属性使用。

    2.2.2、数组语法

    v-bind:style 的数组语法可以将多个样式对象应用到一个元素上:

    <div v-bind:style="[baseStyles, overridingStyles]">

    <template>
      <h2 :style="[objStyle, borderStyle]">样式表</h2>
    </template>
    
    <script lang="ts" setup>
    let borderStyle = {
      border: "1px solid #666",
    };
    let objStyle = {
      color: "blue",
      textDecoration: "underline",
    };
    </script>
    <style scoped></style>

    2.2.3、自动添加前缀

    当 v-bind:style 使用需要特定前缀的 CSS 属性时,如 transform ,Vue.js 会自动侦测并添加相应的前缀。

    官方帮助: http://vuejs.org/guide/class-and-style.html

    三、ES6新增数组方法

    ECMAScript2015中新增了9个方法,分别是:

    1. Array.prototype.indexOf
    2. Array.prototype.lastIndexOf
    3. Array.prototype.every
    4. Array.prototype.some
    5. Array.prototype.forEach
    6. Array.prototype.map
    7. Array.prototype.filter
    8. Array.prototype.reduce
    9. Array.prototype.reduceRight

    3.1、indexOf()找到的元素位置

    indexOf()方法返回在该数组中第一个找到的元素位置,如果它不存在则返回-1。
    不使用indexOf时:

    var arr = ['apple','orange','pear'],
    found = false;
    for(var i= 0, l = arr.length; i< l; i++){
    if(arr[i] === 'orange'){
    found = true;
    }
    }
    console.log("found:",found);

    使用后:

    var arr = ['apple','orange','pear'];
    console.log("found:", arr.indexOf("orange") != -1);

    3.2、filter()过滤

    该filter()方法创建一个新的匹配过滤条件的数组。
    不用 filter() 时

    var arr = [
    {"name":"apple", "count": 2},
    {"name":"orange", "count": 5},
    {"name":"pear", "count": 3},
    {"name":"orange", "count": 16},
    ];
    var newArr = [];
    for(var i= 0, l = arr.length; i< l; i++){
    if(arr[i].name === "orange" ){
    newArr.push(arr[i]);
    }
    }
    console.log("Filter results:",newArr);

    用了 filter():

    var arr = [
    {"name":"apple", "count": 2},
    {"name":"orange", "count": 5},
    {"name":"pear", "count": 3},
    {"name":"orange", "count": 16},
    ];
    
    var newArr = arr.filter(function(item){
    return item.name === "orange";
    });
    console.log("Filter results:",newArr);

    3.3、forEach()迭代

    forEach为每个元素执行对应的方法

    var arr = [1,2,3,4,5,6,7,8];
    
    // Uses the usual "for" loop to iterate
    for(var i= 0, l = arr.length; i< l; i++){
    console.log(arr[i]);
    }
    
    console.log("========================");
    
    //Uses forEach to iterate
    arr.forEach(function(item,index){
    console.log(item);
    });

    forEach是用来替换for循环的

    3.4、map()映射

    map()对数组的每个元素进行一定操作(映射)后,会返回一个新的数组

    不使用map:

    var oldArr = [{first_name:"Colin",last_name:"Toh"},{first_name:"Addy",last_name:"Osmani"},{first_name:"Yehuda",last_name:"Katz"}];
    
    function getNewArr(){
    
    var newArr = [];
    
    for(var i= 0, l = oldArr.length; i< l; i++){
    var item = oldArr[i];
    item.full_name = [item.first_name,item.last_name].join(" ");
    newArr[i] = item;
    }
    
    return newArr;
    }
    
    console.log(getNewArr());

    使用map后:

    var oldArr = [{first_name:"Colin",last_name:"Toh"},{first_name:"Addy",last_name:"Osmani"},{first_name:"Yehuda",last_name:"Katz"}];
    
    function getNewArr(){
    
    return oldArr.map(function(item,index){
    item.full_name = [item.first_name,item.last_name].join(" ");
    return item;
    });
    
    }
    
    console.log(getNewArr());

    map()是处理服务器返回数据时是一个非常实用的函数。

    3.5、reduce()累加器

    reduce()可以实现一个累加器的功能,将数组的每个值(从左到右)将其降低到一个值。
    说实话刚开始理解这句话有点难度,它太抽象了。
    场景: 统计一个数组中有多少个不重复的单词
    不使用reduce时:

    var arr = ["apple","orange","apple","orange","pear","orange"];
    
    function getWordCnt(){
    var obj = {};
    
    for(var i= 0, l = arr.length; i< l; i++){
    var item = arr[i];
    obj[item] = (obj[item] +1 ) || 1;
    }
    
    return obj;
    }
    
    console.log(getWordCnt());

    使用reduce()后

    var arr = ["apple","orange","apple","orange","pear","orange"];
    
    function getWordCnt(){
    return arr.reduce(function(prev,next){
    prev[next] = (prev[next] + 1) || 1;
    return prev;
    },{});
    }
    
    console.log(getWordCnt());

    让我先解释一下我自己对reduce的理解。reduce(callback, initialValue)会传入两个变量。回调函数(callback)和初始值(initialValue)。假设函数它有个传入参数,prev和next,index和array。prev和next你是必须要了解的。
    一般来讲prev是从数组中第一个元素开始的,next是第二个元素。但是当你传入初始值(initialValue)后,第一个prev将是initivalValue,next将是数组中的第一个元素。
    比如:

    /*
    * 二者的区别,在console中运行一下即可知晓
    */
    
    var arr = ["apple","orange"];
    
    function noPassValue(){
    return arr.reduce(function(prev,next){
    console.log("prev:",prev);
    console.log("next:",next);
    
    return prev + " " +next;
    });
    }
    function passValue(){
    return arr.reduce(function(prev,next){
    console.log("prev:",prev);
    console.log("next:",next);
    
    prev[next] = 1;
    return prev;
    },{});
    }
    
    console.log("No Additional parameter:",noPassValue());
    console.log("----------------");
    console.log("With {} as an additional parameter:",passValue());

    示例:

    <!DOCTYPE html>
    <html>
    
        <head>
            <meta charset="UTF-8">
            <title>列表渲染</title>
        </head>
    
        <body>
            <div id="app1">
                <span v-for="n in items">
                        {{n}} 
                    </span>
                <button @click="indexOfMethod">indexOf()找到的元素位置</button>
                <button @click="filterMethod">filter()过滤</button>
                <button @click="forEachMethod">forEach()迭代</button>
                <button @click="mapMethod">map()映射</button>
                <button @click="reduceMethod">reduce()累加器</button>
            </div>
            <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
            <script type="text/javascript">
                var app1 = new Vue({
                    el: "#app1",
                    data: {
                        items: [1, 3, 7, 9, 2, 4, 6, 8, 3],
                        fruits: [{
                                "name": "apple",
                                "count": 2
                            },
                            {
                                "name": "orange",
                                "count": 5
                            },
                            {
                                "name": "pear",
                                "count": 3
                            },
                            {
                                "name": "orange",
                                "count": 16
                            }
                        ],
                        words: ["apple", "orange", "apple", "orange", "pear", "orange"]
                    },
                    methods: {
                        indexOfMethod: function() {
                            console.log("数字3第一次出现的位置是:" + this.items.indexOf(3));
                            console.log("数字5第一次出现的位置是:" + this.items.indexOf(5));
                        },
                        filterMethod: function() {
                            //获得数量不小于5的水果
                            var arr1 = this.fruits.filter(function(f) {
                                return f.count >= 5;
                            });
                            console.log(JSON.stringify(arr1));
                            //获得名称中含有r的水果
                            var arr2 = this.fruits.filter(function(f) {
                                return f.name.match(/r/igm);
                            });
                            console.log(JSON.stringify(arr2));
                        },
                        forEachMethod: function() {
                            this.fruits.forEach(function(obj, index) {
                                console.log(index + "-" + obj.name + "-" + obj.count);
                            });
                        },
                        mapMethod: function() {
                            var arr3 = this.fruits.map(function(obj, index) {
                                obj.showInfo = index + "->水果:" + obj.name + ",数量:" + obj.count;
                                return obj;
                            });
                            console.log(JSON.stringify(arr3));
                        },
                        reduceMethod: function() {
                            var objs = {};
                            for(var i = 0, l = this.words.length; i < l; i++) {
                                var item = this.words[i];
                                objs[item] = (objs[item] + 1) || 1;
                            }
                            console.log(JSON.stringify(objs));
    
                            var objs2 = this.words.reduce(function(prev, next) {
                                console.log("prev:", JSON.stringify(prev));
                                console.log("next:", JSON.stringify(next));
                                prev[next] = (prev[next] + 1) || 1;
                                return prev;
                            }, {});
                            console.log(JSON.stringify(objs2));
                        }
                    }
                });
            </script>
        </body>
    
    </html>

    结果:

    结果

    四、VUE UI框架

    4.1、PC端UI库

    4.1.1、 element-plus(PC端) 

    饿了么UI库

    文档地址:https://element-plus.org/zh-CN/

    npm install element-plus --save

    4.1.2、完整引入

    如果你对打包后的文件大小不是很在乎,那么使用完整导入会更方便。

    // main.ts
    import { createApp } from 'vue'
    import ElementPlus from 'element-plus'
    import 'element-plus/dist/index.css'
    import App from './App.vue'
    
    const app = createApp(App)
    
    app.use(ElementPlus)
    app.mount('#app')
    

    4.1.3、Volar 支持

    如果您使用 Volar,请在 tsconfig.json 中通过 compilerOptions.type 指定全局组件类型。

    // tsconfig.json
    {
      "compilerOptions": {
        // ...
        "types": ["element-plus/global"]
      }
    }
    

    4.1.4、按需导入

    您需要使用额外的插件来导入要使用的组件。

    自动导入推荐

    首先你需要安装unplugin-vue-components 和 unplugin-auto-import这两款插件

    npm install -D unplugin-vue-components unplugin-auto-import
    

    然后把下列代码插入到你的 Vite 或 Webpack 的配置文件中

    // webpack.config.js
    const { defineConfig } = require('@vue/cli-service')
    
    // webpack.config.js
    const AutoImport = require('unplugin-auto-import/webpack')
    const Components = require('unplugin-vue-components/webpack')
    const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
    
    module.exports = defineConfig({
      transpileDependencies: true,
      configureWebpack:{
        plugins: [
          AutoImport({
            resolvers: [ElementPlusResolver()],
          }),
          Components({
            resolvers: [ElementPlusResolver()],
          }),
        ],
      }
    })

    想了解更多打包 (RollupVue CLI) 和配置工具,请参考 unplugin-vue-components 和 unplugin-auto-import

    4.1.5、手动导入

    Element Plus 提供了基于 ES Module 开箱即用的 Tree Shaking 功能。

    但是你需要安装 unplugin-element-plus 来导入样式。 请参考 文档 了解如何配置它。

    App.vue

    <template>
      <el-button>I am ElButton</el-button>
    </template>
    <script>
      import { ElButton } from 'element-plus'
      export default {
        components: { ElButton },
      }
    </script>
    
    // vite.config.ts
    import { defineConfig } from 'vite'
    import ElementPlus from 'unplugin-element-plus/vite'
    
    export default defineConfig({
      // ...
      plugins: [ElementPlus()],
    })
    

    WARNING

    如果您使用 unplugin-element-plus 并且只使用组件 API,您需要手动导入样式。

    示例︰

    import 'element-plus/es/components/message/style/css'
    import { ElMessage } from 'element-plus'
    

    4.1.6、快捷搭建项目模板

    我们提供了 Vite 模板。 对于 Laravel 用户,我们也准备了相应的模板,同样可以直接下载使用。

    4.1.7、全局配置

    在引入 Element Plus 时,可以传入一个包含 size 和 zIndex 属性的全局配置对象。 size 用于设置表单组件的默认尺寸,zIndex 用于设置弹出组件的层级,zIndex 的默认值为 2000。

    完整引入:

    import { createApp } from 'vue'
    import ElementPlus from 'element-plus'
    import App from './App.vue'
    
    const app = createApp(App)
    app.use(ElementPlus, { size: 'small', zIndex: 3000 })
    

    按需引入:

    <template>
      <el-config-provider :size="size" :z-index="zIndex">
        <app />
      </el-config-provider>
    </template>
    
    <script>
    import { defineComponent } from 'vue'
    import { ElConfigProvider } from 'element-plus'
    
    export default defineComponent({
      components: {
        ElConfigProvider,
      },
      setup() {
        return {
          zIndex: 3000,
          size: 'small',
        }
      },
    })
    </script>

    4.1.18、CDN引入在HTML中直接使用

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <!-- Import style -->
        <link
          rel="stylesheet"
          href="//cdn.jsdelivr.net/npm/element-plus/dist/index.css"
        />
        <!-- Import Vue 3 -->
        <script src="//cdn.jsdelivr.net/npm/vue@3"></script>
        <!-- Import component library -->
        <script src="//cdn.jsdelivr.net/npm/element-plus"></script>
      </head>
      <body>
        <div id="app">
          <el-button @click="messageHandle">{{message}}</el-button>
        </div>
        <script>
          const { ElMessage } = ElementPlus;
          let app = Vue.createApp({
            data() {
              return {
                message: "Hello Element Plus!",
              };
            },
            setup() {
              function messageHandle() {
                ElMessage({
                  message: "操作成功了!",
                  type: "success",
                  "show-close": true,
                });
              }
              return { messageHandle };
            },
          })
            .use(ElementPlus)
            .mount("#app");
        </script>
      </body>
    </html>

    2. tdesign-vue-next(PC端)
    腾讯优质 UI 组件,配套工具完满,设计工整,文档清晰
    文档地址:https://tdesign.tencent.com/vue-next/overview

    3. arco-design-vue(PC端)
    字节跳动 UI 组件库开源,大厂逻辑,设计文档完美
    文档地址:https://arco.design/vue/docs/start

    4. ant-design-vue(PC端)
    蚂蚁前端 UI 库,面向企业级中后台
    文档地址:https://www.antdv.com/components/overview-cn

    5. naive-ui(PC端)
    宝藏 Vue UI 库,Vue UI 新星,从 Vue 3 起步
    文档地址:https://www.naiveui.com/zh-CN/os-theme/docs/introduction

    6. Buefy(PC端)
    为基于 Bulma 的 Vue.js 提供了轻量级的 UI 组件
    文档地址:https://buefy.org/documentation

    7. Vue Material(PC端)
    简单、轻巧,并且完全按照 Google Material Design 规范构建
    文档地址:https://www.creative-tim.com/vuematerial/components/app

    8. Vuesax(PC端)
    一个用Vuesax创建的UI组件框架,具有独特和愉快的风格
    文档地址:https://vuesax.com/docs/guide/

    9. Vuestic(PC端)
    完全兼容Vue.js 3,包含50多个功能丰富的组件,可用于任何设计解决方案,允许通过配置和CSS变量全局配置组件,完全响应和支持现代浏览器(除IE11),与i18n无缝集成
    文档地址:https://vuestic.dev/zh/introduction/overview

    10. View UI Plus(PC端)
    View UI Plus 是 View Design 设计体系中基于 Vue.js 3 的一套 UI 组件库,主要用于企业级中后台系统。
    文档地址:https://www.iviewui.com/view-ui-plus/guide/introduce

    https://github.com/iview/iview
    https://iviewui.com/

    4.2、移动端UI库

    4.2.1. vant-ui(移动端)

    有赞团队开源移动 UI 组件库,全面支持 Vue 3,针对移动端
    文档地址:https://vant-contrib.gitee.io/vant/#/zh-CN

    4.2.2、在HTML中直接使用vant,CDN依赖

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <!-- 引入样式文件 -->
        <link
          rel="stylesheet"
          href="https://fastly.jsdelivr.net/npm/vant@3/lib/index.css"
        />
      </head>
      <body>
        <div id="app">
          <van-button @click="showInfo">按钮</van-button>
        </div>
        <!-- 引入 Vue 和 Vant 的 JS 文件 -->
        <script src="https://fastly.jsdelivr.net/npm/vue@3"></script>
        <script src="https://fastly.jsdelivr.net/npm/vant@3/lib/vant.min.js"></script>
    
        <script>
          // 在 #app 标签下渲染一个按钮组件
          const app = Vue.createApp({
            setup() {
              function showInfo() {
                vant.Toast("提示");
              }
              return { showInfo };
            },
          });
          app.use(vant);
    
          // 通过 CDN 引入时不会自动注册 Lazyload 组件
          // 可以通过下面的方式手动注册
          app.use(vant.Lazyload);
    
          app.mount("#app");
        </script>
      </body>
    </html>

    2. nutui(移动端)
    京东出品,移动端友好,面向电商业务场景
    文档地址:https://nutui.jd.com/#/guide/intro

    3. vuetify(移动端)
    老牌 Vue UI ,基于谷歌的 Material Design 样式开发
    文档地址:https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides

    4. varlet(移动端)
    Varlet 是一个基于 Vue3 开发的 Material 风格移动端组件库,全面拥抱 Vue3 生态,由社区建立起来的组件库团队进行维护。
    文档地址:https://varlet.gitee.io/varlet-ui/#/zh-CN/home

    5. tdesign-mobile-vue(移动端)
    tdesign的移动端版本UI库,面向vue3
    文档地址:https://tdesign.tencent.com/mobile-vue/getting-started

    4.3、PC端后台管理

    4.3.1、打不开github的方法

    记住三个网站:

    github网址查询:https://ipaddress.com/website/github.com

    github域名查询:https://ipaddress.com/website/github.global.ssl.fastly.net

    github静态资源ip:https://ipaddress.com/website/assets-cdn.github.com

    1、打开hosts文件(C:\Windows\System32\drivers\etc)

    2、然末尾放入一下两个 IP 地址:

    GitHub Start
    140.82.114.4 github.com
    199.232.69.194 github.global.ssl.fastly.net
    # GitHub End

    保存退出

    3、在 CMD 命令行中执行下面语句 来刷新 DNS,重启浏览器之后就能进入Github 网址。

    vue-admin-better

    vue-pure-admin

    Geeker-Admin

     

     

     

    五、数制转换

    JS中,通过利用js方法,可以很方便的实现2,8,10,16进制之间的相互转换

    1、2,8,16进制格式的数据转换到10进制数据

    var num=parseInt(arg1,arg2);

    第一个参数就是需要转换为10进制的数,arg2就是被转换数据的进制值,可以是2,8,16等。

    如:将十六进制的数‘d9’转为十进制数:

    var num=parseInt(d9,16);//num=217

    2、将10进制格式的数据转为2,8,16进制格式数据

    var num=parseInt(“217”);//如果这个数是字符串格式的,需要执行这一步
    var oxNum=num.toString(16);//参数可以是2,8,16.设置转换10进制数据到对应进制格式,本例是将num转成16进制数据 oxNum=d9

    3、2,8,10,16任意进制之间转化

    通过10进制作为媒介,便可以实现任意进制之间相互转化了

    六、示例下载

    https://gitee.com/zhangguo5/vue3_-chapter1.git

    小红书项目要求:

    http://www.cnblogs.com/xsblog/p/8144290.html

    七、作业

    7.1、完成一个简单的南方打游戏,页面随机出现字符(1级出现5个,2级10个,一直增加;打完50个没有错则升级到下一级;第n关打错n个就结束),字符可以下落,可以有动画效果;

    7.2、可以滚动的“回顶端”

     不允许使用fixed布局,要求使用鼠标滚动事件,要有动画效果,延迟下移

    7.3、请完成一个只能输入数字与-的文本框

    7.4、完成一个不能输入空格的文本框

    7.5、完成上课的每一个示例

    7.6、请完成一个商品管理模块,要求如下:

    • 使用Element+vue技术实现
    • 添加与修改功能使用模式窗口
    • 支持全选与反选,隔行换色与光棒效果
    • 详细是点击时在弹出层中显示当前商品的所有信息
    • 尝试分页(选做)

    7.7、增强商品管理

    • 数据从后台加载,请注意跨域(axios)
    • 实现搜索功能(Lodash)
    • 有验证、有错误提示
    • 增加删除与添加时的动画效果(选作)
    • 了解UIKit(选作)

    7.8、请完成一个智能机器人,重点练习使用vue-cli+app界面(任意框架)

    接口:http://www.itpk.cn/robot.php、http://www.haoservice.com/docs/119

    打包后在手机端根据提问给出答案,app界面

    7.9、请随机生成100个字母,放入数组中,统计每个字母出现的次数,使用两种方法使用:a不使用reduce,b使用reduce

    八、视频

    【Vue3 + Vuex + Pinia + TypeScript + Router】 https://www.bilibili.com/video/BV1at4y1F75D?share_source=copy_web&vd_source=475a31f3c5d6353a782007cd4c638a8a

  • 相关阅读:
    Python中数据在内存中存储汇总
    python每日一题:爬取一些代理网站获取动态ip
    python每日一题:关于反爬虫措施
    python每日一题:爬虫一些需要登录账号密码的网站
    python每日一题:爬虫某网站图书信息
    【MyBatis】MyBatis CRUD
    【MyBatis】自定义 MyBatis
    【JavaWeb】i18n 国际化
    【JavaWeb】AJAX 请求
    【JavaWeb】JSON 文件
  • 原文地址:https://www.cnblogs.com/best/p/16824776.html
Copyright © 2020-2023  润新知