1.1、文本插值

2022-11-12 11:30:33 浏览数 (1)

一、模板语法

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

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

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

1.1、文本插值

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

代码语言:javascript复制
<span>Message: {{ msg }}</span>

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

1.2、原始 HTML

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

代码语言:javascript复制
<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.

代码语言:javascript复制
<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)指利用网站漏洞从用户那里恶意盗取信息。

代码语言:javascript复制
<template>
  <h2>
    {{ msg }}
  </h2>
  <h2 v-text="msg"></h2>
  <div v-html="msg"></div>
  <textarea v-model="msg" style="width: 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 指令:

代码语言:javascript复制
<div v-bind:id="dynamicId"></div>

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

1.3.1、简写

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

代码语言:javascript复制
<div :id="dynamicId"></div>

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

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

1.3.2、布尔型属性

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

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

代码语言:javascript复制
<button :disabled="isButtonDisabled">Button</button>

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

1.3.3、动态绑定多个值

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

代码语言:javascript复制
const objectOfAttrs = {
  id: 'container',
  class: 'wrapper'
}

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

代码语言:javascript复制
<div v-bind="objectOfAttrs"></div>
代码语言:javascript复制
<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 表达式:

代码语言: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 后面。

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

代码语言:javascript复制
<!-- 这是一个语句,而非表达式 -->
{{ var a = 1 }}

<!-- 条件控制也不支持,请使用三元表达式 -->
{{ if (ok) { return message } }}

1.4.2、调用函数

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

代码语言:javascript复制
<span :title="toTitleDate(date)">
  {{ formatDate(date) }}
</span>

TIP

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

代码语言:javascript复制
<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>
代码语言:javascript复制
<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 扩展:

代码语言:javascript复制
// 之前(Vue 2.x)
Vue.prototype.$http = () => {}

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

代码语言:javascript复制
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');

取值:

代码语言:javascript复制
<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

  • v-text
  • v-html
  • v-show
  • v-if
  • v-else
  • v-else-if
  • v-for
  • v-on
  • v-bind
  • v-model
  • v-slot
  • v-pre
  • v-once
  • v-memo
  • v-cloak

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

代码语言:javascript复制
<p v-if="seen">Now you see me</p>

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

1.5.1、参数 Arguments

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

代码语言:javascript复制
<a v-bind:href="url"> ... </a>

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

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

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

代码语言:javascript复制
<a v-on:click="doSomething"> ... </a>

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

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

1.5.2、动态参数

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

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

<!-- 简写 -->
<a :[attributeName]="url"> ... </a>

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

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

代码语言:javascript复制
<a v-on:[eventName]="doSomething"> ... </a>

<!-- 简写 -->
<a @[eventName]="doSomething">

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

动态参数值的限制

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

动态参数语法的限制

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

代码语言:javascript复制
<!-- 这会触发一个编译器警告 -->
<a :['foo'   bar]="value"> ... </a>

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

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

代码语言:javascript复制
<a :[someAttr]="value"> ... </a>

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

代码语言:javascript复制
<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()

代码语言:javascript复制
<form @submit.prevent="onSubmit">...</form>

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

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

1.5.4、v-text

更新元素的文本内容。

  • 期望的绑定值类型:string
  • 详细信息 v-text 通过设置元素的 textContent 属性来工作,因此它将覆盖元素中所有现有的内容。如果你需要更新 textContent 的部分,应该使用 mustache interpolations 代替。
  • 示例
代码语言:javascript复制
<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 的作用域策略。

  • 示例:
代码语言:javascript复制
<div v-html="html"></div>
  • 参考:模板语法 - 原始 HTML
代码语言:javascript复制
<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> 元素。 v-else 和 v-else-if 也可以在 <template> 上使用。
代码语言:javascript复制
<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>
代码语言:javascript复制
<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> 表示仅包含文本或多个元素的条件块。
  • 示例
代码语言:javascript复制
<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> 表示仅包含文本或多个元素的条件块。
  • 示例
代码语言:javascript复制
<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

示例:

代码语言:javascript复制
<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 为正在迭代的元素提供一个别名:
代码语言:javascript复制
<div v-for="item in items">
  {{ item.text }}
</div>
  • 或者,你也可以为索引指定别名 (如果用在对象,则是键值):
代码语言:javascript复制
<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 来提供一个排序提示:
代码语言:javascript复制
<div v-for="item in items" :key="item.id">
  {{ item.text }}
</div>
  • v-for 也可以用于 Iterable Protocol 的实现,包括原生 Map 和 Set
代码语言:javascript复制
<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()
    • .prevent ——调用 event.preventDefault()
    代码语言:javascript复制
    <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 ——在捕获模式添加事件监听器。
    代码语言:javascript复制
    <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 ——只有事件从元素本身发出才触发处理函数。
    代码语言:javascript复制
    <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
    • .once ——最多触发一次处理函数。
    • .left ——只在鼠标左键事件触发处理函数。
    • .right ——只在鼠标右键事件触发处理函数。
    • .middle ——只在鼠标中键事件触发处理函数。
    • .passive ——通过 { passive: true } 附加一个 DOM 事件。
      代码语言:javascript复制
      <template>
        <div
          @click.self="submit('1父元素div的事件', $event)"
          style="height: 200px; background: lightblue"
        >
          <button @click.right.prevent="submit('2子元素button的事件', $event)">
            提交
          </button>
      
          <input @keyup.enter="submit('3子元素input的事件', $event)" />
        </div>
      
        <a
          @click.prevent="submit('4元素a的事件', $event)"
          href="http://best.cnblogs.com"
          >我的博客</a
        >
      
        <div class="div1" @wheel.passive="divEvent">
          <ul>
            <li>
              <h3>1</h3>
            </li>
            <li>
              <h3>2</h3>
            </li>
            <li>
              <h3>3</h3>
            </li>
            <li>
              <h3>4</h3>
            </li>
          </ul>
        </div>
      </template>
      
      <script lang="ts">
      import { ref } from "vue";
      export default {
        setup() {
          function submit(info, event) {
            console.log("提交事件", info);
            console.log(event.target);
          }
          function divEvent(e) {
            let time = new Date().getTime()   2 * 1000;
            while (new Date().getTime() <= time) {}
            console.log("滚动了....");
          }
          return { submit, divEvent };
        },
      };
      </script>
      <style scoped>
      .div1 {
        height: 200px;
        overflow: auto;
        width: 300px;
        background-color: lightgoldenrodyellow;
      }
      .div1 ul li {
        height: 300px;
        line-height: 300px;
      }
      </style>
    代码语言:javascript复制
    <!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 -->
    <!-- 以防其中包含 `event.preventDefault()` -->
    <div @scroll.passive="onScroll">...</div>
代码语言:javascript复制
<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>
  • 详细信息 事件类型由参数来指定。表达式可以是一个方法名,一个内联声明,如果有修饰符则可省略。 当用于普通元素,只监听原生 DOM 事件。当用于自定义元素组件,则监听子组件触发的自定义事件。 当监听原生 DOM 事件时,方法接收原生事件作为唯一参数。如果使用内联声明,声明可以访问一个特殊的 event 变量:v-on:click="handle('ok',
  • 示例:
代码语言:javascript复制
<!-- 方法处理函数 -->
<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”事件被触发,处理函数将被调用):
代码语言:javascript复制
<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 名称-绑定值对的对象。
  • 示例:
代码语言:javascript复制
<!-- 绑定 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 修饰符也有专门的缩写,.
代码语言:javascript复制
<div :someProperty.prop="someObject"></div>

<!-- 等同于 -->
<div .someProperty="someObject"></div>
  • 当在 DOM 内模板使用 .camel 修饰符,可以驼峰化 v-bind attribute 的名称,例如 SVG viewBox attribute:
代码语言:javascript复制
<svg :view-box.camel="viewBox"></svg>
  • 如果使用字符串模板或使用构建步骤预编译模板,则不需要 .camel
  • 参考:
    • Class 与 Style 绑定
    • 组件 - Prop 传递细节

1.5.14、v-model

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

  • 期望的绑定值类型:根据表单输入元素或组件输出的值而变化
  • 仅限:
    • <input>
    • <select>
    • <textarea>
    • components
  • 修饰符:
    • .lazy ——监听 change 事件而不是 input
    • .number ——将输入的合法符串转为数字
    • .trim ——移除输入内容两端空格
  • 参考:
    • 表单输入绑定
    • 组件事件 - 配合 v-model 使用

1.5.15、v-slot

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

  • 缩写:#
  • 期望的绑定值类型:能够合法在函数参数位置使用的 JavaScript 表达式。支持解构语法。绑定值是可选的——只有在给作用域插槽传递 props 才需要。
  • 参数:插槽名 (可选,默认是 default)
  • 仅限:
    • <template>
    • components (用于带有 prop 的单个默认插槽)
  • 示例:
代码语言:javascript复制
<!-- 具名插槽 -->
<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 模板语法都会被保留并按原样渲染。最常见的用例就是显示原始双大括号标签及内容。
  • 示例:
代码语言:javascript复制
<span v-pre>{{ this will not be compiled }}</span>

1.5.17、v-once

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

  • 无需传入
  • 详细信息 在随后的重新渲染,元素/组件及其所有子项将被当作静态内容并跳过渲染。这可以用来优化更新时的性能。
代码语言:javascript复制
<!-- 单个元素 -->
<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 的无效条件来缓存部分模板。
  • 参考:
    • 数据绑定语法 - 插值
    • v-memo

1.5.18、v-memo

  • 期望的绑定值类型:any[]
  • 详细信息 缓存一个模板的子树。在元素和组件上都可以使用。为了实现缓存,该指令需要传入一个固定长度的依赖值数组进行比较。如果数组里的每个值都与最后一次的渲染相同,那么整个子树的更新将被跳过。举例来说:
代码语言:javascript复制
<div v-memo="[valueA, valueB]">
  ...
</div>
  • 当组件重新渲染,如果 valueA 和 valueB 都保持不变,这个 <div> 及其子项的所有更新都将被跳过。实际上,甚至虚拟 DOM 的 vnode 创建也将被跳过,因为缓存的子树副本可以被重新使用。 正确指定缓存数组很重要,否则应该生效的更新可能被跳过。v-memo 传入空依赖数组 (v-memo="[]") 将与 v-once 效果相同。 与 v-for 一起使用 v-memo 仅用于性能至上场景中的微小优化,应该很少需要。最常见的情况可能是有助于渲染海量 v-for 列表 (长度超过 1000 的情况):
代码语言:javascript复制
<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 也能被用于在一些默认优化失败的边际情况下,手动避免子组件出现不需要的更新。但是一样的,开发者需要负责指定正确的依赖数组以免跳过必要的更新。
代码语言:javascript复制
<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>
  • 参考:
    • v-once

1.5.19、v-cloak

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

无需传入

详细信息

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

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

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

示例:

代码语言:javascript复制
[v-cloak] {
  display: none;
}
代码语言:javascript复制
<div v-cloak>
  {{ message }}
</div>

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

1.6、重要的v-for指令

1.6.1、v-for

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

代码语言:javascript复制
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
<li v-for="item in items">
  {{ item.message }}
</li>

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

代码语言:javascript复制
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 代码很类似:

代码语言:javascript复制
const parentMessage = 'Parent'
const items = [
  /* ... */
]

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

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

代码语言:javascript复制
<li v-for="{ message } in items">
  {{ message }}
</li>

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

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

代码语言:javascript复制
<li v-for="item in items">
  <span v-for="childItem in item.children">
    {{ item.message }} {{ childItem }}
  </span>
</li>

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

代码语言:javascript复制
<div v-for="item of items"></div>

1.6.2、v-for 与对象

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

代码语言:javascript复制
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):

代码语言:javascript复制
<li v-for="(value, key) in myObject">
  {{ key }}: {{ value }}
</li>

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

代码语言:javascript复制
<li v-for="(value, key, index) in myObject">
  {{ index }}. {{ key }}: {{ value }}
</li>

在演练场中尝试一下

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

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

代码语言:javascript复制
<span v-for="n in 10">{{ n }}</span>

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

1.6.4、template 上的v-for

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

代码语言:javascript复制
<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 作用域内定义的变量别名:

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

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

代码语言:javascript复制
<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:

代码语言:javascript复制
<div v-for="item in items" :key="item.id">
  <!-- 内容 -->
</div>

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

代码语言:javascript复制
<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 文档。

1.6.5、组件上使用 v-for

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

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

代码语言:javascript复制
<MyComponent v-for="item in items" :key="item.id" />

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

代码语言:javascript复制
<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 。

代码语言:javascript复制
<my-component v-for="item in items"></my-component>

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

代码语言:javascript复制
<my-component
  v-for="(item, index) in items"
  v-bind:item="item"
  v-bind:index="index">
</my-component>

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

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

代码语言:javascript复制
<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>
代码语言:javascript复制
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 = ''
    }
  }
})

示例:

代码语言:javascript复制
<!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.6.7、key

当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用 “就地复用” 策略。如果数据项的顺序被改变,而不是移动 DOM 元素来匹配数据项的顺序, Vue 将简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x 的track-by="$index" 。

这个默认的模式是有效的,但是只适用于不依赖子组件状态或临时 DOM 状态(例如:表单输入值)的列表渲染输出。

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。理想的 key 值是每项都有唯一 id。这个特殊的属性相当于 Vue 1.x 的 track-by ,但它的工作方式类似于一个属性,所以你需要用v-bind 来绑定动态值(在这里使用简写):

代码语言:javascript复制
<div v-for="item in items" :key="item.id">
  <!-- 内容 -->
</div>

建议尽可能使用 v-for 来提供 key ,除非迭代 DOM 内容足够简单,或者你是故意要依赖于默认行为来获得性能提升。

因为它是 Vue 识别节点的一个通用机制, key 并不特别与 v-for 关联,key 还具有其他用途,我们将在后面的指南中看到其他用途。

1.6.8、用 key 管理可复用的元素

Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。例如,如果你允许用户在不同的登录方式之间切换:

代码语言:javascript复制
<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address">
</template>

自己动手试一试,在输入框中输入一些文本,然后按下切换按钮:那么在上面的代码中切换 loginType 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素,<input> 不会被替换掉——仅仅是替换了它的 placeholder

示例:

代码语言:javascript复制
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>条件渲染</title>
    </head>
    <body>
        <div id="app1">
            <template v-if="type==='username'">
                <label>帐号:</label>
                <input placeholder="请输入您的帐号" />
            </template>
            <template v-else>
                <label>邮箱:</label>
                <input placeholder="请输入您的电子邮箱" />
            </template>
            <p>
                <a href="" @click.prevent="type='username'">用户名登录</a> | <a href="" @click.prevent="type='email'">邮箱登录</a> 
            </p>
        </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,
                    type: "username"
                }
            });
        </script>
    </body>
</html>

结果:

点击邮箱登录

这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key 属性即可:

代码语言:javascript复制
<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

现在,每次切换时,输入框都将被重新渲染。

代码语言:javascript复制
        <div id="app1">
            <template v-if="type==='username'">
                <label>帐号:</label>
                <input placeholder="请输入您的帐号" key="username"/>
            </template>
            <template v-else>
                <label>邮箱:</label>
                <input placeholder="请输入您的电子邮箱" key="email"/>
            </template>
            <p>
                <a href="" @click.prevent="type='username'">用户名登录</a> | <a href="" @click.prevent="type='email'">邮箱登录</a> 
            </p>
        </div>

注意,<label> 元素仍然会被高效地复用,因为它们没有添加 key 属性。

1.7、数组变化侦测

1.7.1、变异方法

Vue 包含一组观察数组的变异方法,所以它们也将会触发视图更新。这些方法如下:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

你打开控制台,然后用前面例子的 items 数组调用突变方法:example1.items.push({ message: 'Baz' }) 。

1.7.2、重塑数组

变异方法(mutation method),顾名思义,会改变被这些方法调用的原始数组。相比之下,也有非变异(non-mutating method)方法,例如: filter()concat()slice() 。这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组:

代码语言:javascript复制
example1.items = example1.items.filter(function (item) {
  return item.message.match(/Foo/)
})

你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。幸运的是,事实并非如此。 Vue 实现了一些智能启发式方法来最大化 DOM 元素重用,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。

示例:

代码语言:javascript复制
<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>列表渲染</title>
    </head>

    <body>
        <div id="app1">
            <ul>
                <li v-for="n in items">
                    <h2>{{n}}</h2>
                </li>
            </ul>
            <button @click="items.splice(0,3)">修改数组(变异)</button>
            <button @click="items.slice(0,3)">修改数组(不变异)</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,5,7,9,2,4,6,8,0]
                }
            });
        </script>
    </body>

</html>

结果:

如果原数组发生了变化则View也会重新渲染,如果原数组未发生变化只是读取后返回了新的数组对象则不会渲染。

1.7.3、触发数组状态更新

由于 JavaScript 的限制, Vue 不能检测以下变动的数组:

  1. 当你直接设置一个项的索引时,例如: vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如: vm.items.length = newLength

为了避免第一种情况,以下两种方式将达到像 vm.items[indexOfItem] = newValue 的效果, 同时也将触发状态更新:

代码语言:javascript复制
// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
代码语言:javascript复制
// Array.prototype.splice`
example1.items.splice(indexOfItem, 1, newValue)

避免第二种情况,使用 splice

代码语言:javascript复制
example1.items.splice(newLength)

splice数组详解:

代码语言:javascript复制
View Code

示例:

代码语言:javascript复制
<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>列表渲染</title>
    </head>

    <body>
        <div id="app1">
            <ul>
                <li v-for="n in items">
                    <h2>{{n}}</h2>
                </li>
            </ul>
            <button @click="items[2]=55">修改第3个元素的值为55(无效)</button>
            <button @click="setValue">Vue.set修改第3个元素的值为55</button>
            <button @click="items.splice(2,1,55)">Vue.set修改第3个元素的值为55</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, 5, 7, 9, 2, 4, 6, 8, 0]
                },
                methods: {
                    setValue: function() {
                        Vue.set(this.items, 2, 55);
                    }
                }
            });
        </script>
    </body>

</html>

结果:

1.7.4、对象更改检测注意事项

还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:

代码语言:javascript复制
var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` 现在是响应式的

vm.b = 2
// `vm.b` 不是响应式的

对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于:

代码语言:javascript复制
var vm = new Vue({
  data: {
    userProfile: {
      name: 'Anika'
    }
  }
})

你可以添加一个新的 age 属性到嵌套的 userProfile 对象:

代码语言:javascript复制
Vue.set(vm.userProfile, 'age', 27)

你还可以使用 vm.$set 实例方法,它只是全局 Vue.set 的别名:

代码语言:javascript复制
vm.$set(this.userProfile, 'age', 27)

有时你可能需要为已有对象赋予多个新属性,比如使用 Object.assign() 或 _.extend()。在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性,不要像这样:

代码语言:javascript复制
Object.assign(this.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})

你应该这样做:

代码语言:javascript复制
this.userProfile = Object.assign({}, this.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})

示例:

代码语言:javascript复制
<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>对象更改检测注意事项</title>
    </head>

    <body>
        <div id="app1">
            <h2>对象更改检测注意事项</h2>
            {{stu.name}} - {{stu.age}}
            <p>
                <button @click="setAge">在stu对象中添加age属性并设置值为100</button>
            </p>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            var app1 = new Vue({
                el: "#app1",
                data: {
                    stu:{
                        name:"tom"
                    }
                },
                methods:{
                    setAge:function(){
                        Vue.set(this.stu,'age',100);
                    }
                },
                beforeUpdate:function(){
                    console.log("更新前" this.name);
                },
                updated:function(){
                    console.log("更新后" this.name);
                }
            });
        </script>
    </body>

</html>

结果:

注意:

如果data中数据没有被绑定到DOM中,则修改后DOM不会更新,updated与beforeUpdate事件也不会执行(Hook function)

data 的根元素后加入无效,可以使用Vue.set添加元素中的属性,这样会变成响应式的成员

1.7.5、显示过滤/排序结果

有时,我们想要显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。在这种情况下,可以创建返回过滤或排序数组的计算属性。

例如:

代码语言:javascript复制
<li v-for="n in evenNumbers">{{ n }}</li>
代码语言:javascript复制
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
  evenNumbers: function () {
    return this.numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

或者,您也可以使用在计算属性是不可行的 method 方法 (例如,在嵌套 v-for 循环中):

代码语言:javascript复制
<li v-for="n in even(numbers)">{{ n }}</li>
代码语言:javascript复制
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
  even: function (numbers) {
    return numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

示例:

代码语言:javascript复制
<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>列表渲染</title>
    </head>

    <body>
        <div id="app1">
            <p>
                <span v-for="n in items">
                    {{n}}
                </span>
            </p>
            <p>
                <span v-for="n in even">
                    {{n}}
                </span></p>
            <p>
                <span v-for="n in odd()">
                    {{n}}
                </span></p>
        </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, 5, 7, 9, 2, 4, 6, 8, 0]
                },
                computed: {
                    even: function() {
                        return this.items.filter(function(n) {
                            return n % 2 === 0;
                        });
                    }
                },
                methods: {
                    odd: function() {
                        return this.items.filter(function(n) {
                            return n % 2 === 1;
                        });
                    }
                }
            });
        </script>
    </body>

</html>

结果:

官方原文:  http://vuejs.org/guide/list.html

二、Class 与 Style 绑定

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

2.1、绑定 HTML Class

2.1.1、对象语法

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

代码语言:javascript复制
<div v-bind:class="{ active: isActive }"></div>

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

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

代码语言:javascript复制
<div class="static"
     v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>

如下 data:

代码语言:javascript复制
data: {
  isActive: true,
  hasError: false
}

渲染为:

代码语言:javascript复制
<div class="static active"></div>

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

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

代码语言:javascript复制
<div v-bind:class="classObject"></div>
代码语言:javascript复制
data: {
  classObject: {
    active: true,
    'text-danger': false
  }
}

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

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

示例:

代码语言:javascript复制
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Class 与 Style 绑定</title>
        <style>
            .valide{
                display: none;
            }
            .priceRequired{
                display: inline;
            }
            .priceRange{
                display: inline;
            }
        </style>
    </head>
    <body>
        <div id="app1">
             <span class="bgGreen" v-bind:class="{bgRed:isShow,bgBlue:isActive}">span1</span>
             
             <span class="bgGreen" v-bind:class="classObj1">span2</span>
             
             <p>
                 <label>价格:</label>
                 <input v-model="price" /><span class="valide"  v-bind:class="priceValide1">必填</span><span class="valide"  v-bind:class="priceValide2">介于0.1-99999之间</span>
             </p>
        </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,
                    isActive:false,
                    classObj1:{
                        isHide:true,
                        isRight:false
                    },
                    price:0
                },
                computed:
                {
                    priceValide1:function(){
                        return {
                            priceRequired:this.price===''
                        }
                    },
                    priceValide2:function(){
                        return {
                            priceRange:this.price!=''&&(this.price<0.1||this.price>99999)
                        }
                    }
                }
            });
            
        
        </script>
    </body>
</html>

结果:

2.1.2、数组语法

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

代码语言:javascript复制
<div v-bind:class="[activeClass, errorClass]">
代码语言:javascript复制
data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}

渲染为:

代码语言:javascript复制
<div class="active text-danger"></div>

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

代码语言:javascript复制
<div v-bind:class="[isActive ? activeClass : '', errorClass]">

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

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

代码语言:javascript复制
<div v-bind:class="[{ active: isActive }, errorClass]">

示例:

代码语言:javascript复制
<!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:

代码语言:javascript复制
Vue.component('my-component', {
  template: '<p class="foo bar">Hi</p>'
})

Then add some classes when using it:

代码语言:javascript复制
<my-component class="baz boo"></my-component>

The rendered HTML will be:

代码语言:javascript复制
<p class="foo bar baz boo">Hi</p>

The same is true for class bindings:

代码语言:javascript复制
<my-component v-bind:class="{ active: isActive }"></my-component>

When isActive is truthy, the rendered HTML will be:

代码语言:javascript复制
<div class="foo bar active"></div>

2.2、绑定内联样式

2.2.1、对象语法

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

代码语言:javascript复制
<div v-bind:style="{ color: activeColor, fontSize: fontSize   'px' }"></div>
代码语言:javascript复制
data: {
  activeColor: 'red',
  fontSize: 30
}

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

代码语言:javascript复制
<div v-bind:style="styleObject"></div>
代码语言:javascript复制
data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

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

2.2.2、数组语法

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

代码语言:javascript复制
<div v-bind:style="[baseStyles, overridingStyles]">

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时:

代码语言:javascript复制
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);

使用后:

代码语言:javascript复制
var arr = ['apple','orange','pear'];
console.log("found:", arr.indexOf("orange") != -1);

3.2、filter()过滤

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

代码语言:javascript复制
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():

代码语言:javascript复制
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为每个元素执行对应的方法

代码语言:javascript复制
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:

代码语言:javascript复制
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后:

代码语言:javascript复制
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时:

代码语言:javascript复制
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()后

代码语言:javascript复制
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将是数组中的第一个元素。 比如:

代码语言:javascript复制
/*
* 二者的区别,在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());

示例:

代码语言:javascript复制
<!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、移动端

1. vonic 一个基于 vue.js 和 ionic 样式的 UI 框架,用于快速构建移动端单页应用,很简约,是我喜欢的风格 star 2.3k

中文文档 在线预览

2.vux 基于WeUIVue(2.x)开发的移动端UI组件库 star 10k

基于webpack vue-loader vux可以快速开发移动端页面,配合vux-loader方便你在WeUI的基础上定制需要的样式。

中文文档 在线预览

3.Mint UI 由饿了么前端团队推出的 Mint UI 是一个基于 Vue.js 的移动端组件库 star 8.3k

中文文档 github地址 在线预览

4.MUI  最接近原生APP体验的高性能前端框架 star 7.5k 

中文文档 github地址

5.Muse-ui   基于 Vue 2.0 和 Material Design 的 UI 组件库 star 4.9k

中文文档 github地址

6.Vant是有赞前端团队基于有赞统一的规范实现的 Vue 组件库,提供了一整套 UI 基础组件和业务组件。star 1k

中文文档 github地址

7.Cube UI star 3k

滴滴 WebApp 团队 实现的 基于 Vue.js 实现的精致移动端组件库

github地址 中文文档

特性

  • 质量可靠 由滴滴内部组件库精简提炼而来,经历了业务一年多的考验,并且每个组件都有充分单元测试,为后续集成提供保障。
  • 体验极致 以迅速响应、动画流畅、接近原生为目标,在交互体验方面追求极致。
  • 标准规范 遵循统一的设计交互标准,高度还原设计效果;接口标准化,统一规范使用方式,开发更加简单高效。
  • 扩展性强 支持按需引入和后编译,轻量灵活;扩展性强,可以方便地基于现有组件实现二次开发

4.2、PC端

1)Element 饿了么 vue 2.0后台UI框架 (Star:18382)

https://github.com/ElemeFE/element

(2)iview组件库 (Star:10186)

 iView 主要服务于 PC 界面的中后台业务,很优秀的组件库,可惜不适合移动端 https://github.com/iview/iview https://iviewui.com/

(3)vux 基于Vue和WeUI的移动UI组件 (Star:9762) Vux是基于WeUI和Vue(2.x)开发的移动端UI组件库,主要服务于微信页面。 https://github.com/airyland/vux https://vux.li/

(4)Mint-UI 饿了么移动端组件库 (Star:8062) 由饿了么前端团队推出的 Mint UI 是一个基于 Vue.js 的移动端组件库 https://github.com/ElemeFE/mint-ui

(5)vue-admin 管理面板UI框架 (Star:6289) https://github.com/vue-bulma/vue-admin

(6)vue-material为 Vue.js 打造的 Material 风格的组件 (Star:4550) https://github.com/vuematerial/vue-material https://vuematerial.github.io/#/

(7)vue-strap基于 Vue.js 的 Bootstrap 组件 (Star:4400) https://github.com/yuche/vue-strap http://yuche.github.io/vue-strap/

(8)KeenUI 基于Material Design的UI (Star:3041) https://josephuspaye.github.io/Keen-UI/

(9)vonic (Star:2276) https://github.com/wangdahoo/vonic/ https://wangdahoo.github.io/vonic/docs/#/

(10)Radon-ui awe大神最近写的一套UI (Star:791) https://github.com/luojilab/radon-ui

(11)N3-components 基于N3ui (Star:781) https://github.com/N3-components/N3-components

(12)vue-carbon (Star:739) https://github.com/myronliu347/vue-carbon

五、数制转换

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

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

代码语言:javascript复制
var num=parseInt(arg1,arg2);

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

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

代码语言:javascript复制
var num=parseInt(d9,16);//num=217

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

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

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

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

六、示例下载

https://git.coding.net/zhangguo5/vue2.git

小红书项目要求:

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

七、作业

5.1、请把v-on指令的每一种“修饰符”与调用方式都应用一遍。

代码语言:javascript复制
<!-- 方法处理器 -->
<button v-on:click="doThis"></button>
<!-- 对象语法 (2.4.0 ) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
<!-- 内联语句 -->
<button v-on:click="doThat('hello', $event)"></button>
<!-- 缩写 -->
<button @click="doThis"></button>
<!-- 停止冒泡 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认行为 -->
<button @click.prevent="doThis"></button>
<!-- 阻止默认行为,没有表达式 -->
<form @submit.prevent></form>
<!--  串联修饰符 -->
<button @click.stop.prevent="doThis"></button>
<!-- 键修饰符,键别名 -->
<input @keyup.enter="onEnter">
<!-- 键修饰符,键代码 -->
<input @keyup.13="onEnter">
<!-- 点击回调只会触发一次 -->
<button v-on:click.once="doThis"></button>

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

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

5.3、增强商品管理

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

5.4、复现所有上课示例

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

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

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

八、视频

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

0 人点赞