登录官网 https://cn.vuejs.org/v2/guide/installation.html,下载开发版本或生产版本vue.js。然后将vue.js放在代码目录中,通过scrirt标签引入。
或者使用cdn方式:
生产版本,最好指明版本号:
在引入vue.js后,在script标签代码块中可以通过new Vue()的方式创建一个新的应用:
<body>
<div id="app">
{{message}}
</div>
{{message}}
<script type="text/javascript">
var data = {message: "Hello vue"};
# 创建新的视图应用(viewModel)
var vm = new Vue({
el: "#app",
data: data
});
</script>
</body>
在浏览器中查看效果,可以看到div标签内的message正常显示了数据,div之外的只显示了文本,即div的id=“app”定义了视图对应哪个应用,同时所在的div标签的范围即应用的作用域。
增加两行代码:
<body>
<div id="app1">
{{message}}
{{test}} // 展示 test
</div>
{{message}}
<script type="text/javascript">
var data = {message: "Hello vue"};
var app = new Vue({
el: "#app1",
data: data
});
// 修改message属性的值
data.message = "Modify message value!";
// 修改test属性的值
data.test = "test";
</script>
</body>
查看效果,发现data.message属性修改成功,但是data.test属性由于没有在初始化app的时候定义,修改失败。
即当一个 Vue 实例被创建时,它将 data 对象中的所有的属性加入到 Vue 的响应式系统中。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
只有当实例被创建时就已经存在于 data 中的属性才是响应式的。
<body>
<div id="app1">
{{message}}
</div>
<script type="text/javascript">
var data = {message: "Hello vue"};
var vm = new Vue({
el: "#app1",
data: data
});
data.message = "abc";
// 使用vm.message修改属性和修改原始数据具有同等效果
vm.message = "bcd";
console.log(vm.message === data.message);
</script>
</body>
<script type="text/javascript">
var data = {message: "Hello vue"};
var app = new Vue({
el: "#app1",
data: data
});
// 使用$watch监听message属性,当值发生变化时触发钩子函数
app.$watch("message", function (newVle, oldVal) {
console.log(newVle, oldVal)
});
data.message = "abc";
</script>
查看效果,当message被修改为“”abc“时出发了该方法,能获取new和old值。
其他方法参考API文档:https://cn.vuejs.org/v2/api/#%E5%AE%9E%E4%BE%8B%E5%B1%9E%E6%80%A7
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
官方文档:https://cn.vuejs.org/v2/api/#%E9%80%89%E9%A1%B9-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90
<body>
<div id="app1">
{{message}}
</div>
<script type="text/javascript">
var data = {message: "Hello vue"};
var vm = new Vue({
el: "#app1",
data: data,
// 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用
beforeCreate: function () {
console.log("beforeCreate");
},
// 在实例创建完成后被立即调用。
// 在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。
// 然而,挂载阶段还没开始,$el 属性目前尚不可用。
created: function () {
console.log("created");
},
// 在挂载开始之前被调用:相关的 render 函数首次被调用。该钩子在服务器端渲染期间不被调用。
beforeMount: function () {
console.log("beforeMount");
},
// 实例被挂载后调用,这时el被新创建的vm.$el替换了。
// 如果根实例挂载到了一个文档内的元素上,当mounted被调用时vm.$el也在文档内。
// 注意: mounted 不会保证所有的子组件也都一起被挂载。
// 如果你希望等到整个视图都渲染完毕,可以在 mounted 内部使用 vm.$nextTick:
// 该钩子在服务器端渲染期间不被调用。
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
});
console.log("mounted");
},
// 数据更新时调用,发生在虚拟 DOM 打补丁之前。
// 这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
// 该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
beforeUpdate: function () {
console.log("beforeUpdate");
},
// 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
// 当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。
// 然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。
// 注意 updated 不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在 updated 里使用 vm.$nextTick:
// 该钩子在服务器端渲染期间不被调用。
updated : function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been re-rendered
});
console.log("updated");
},
// 被 keep-alive 缓存的组件激活时调用。
// 该钩子在服务器端渲染期间不被调用。
activated: function () {
console.log("activated");
},
// 被 keep-alive 缓存的组件停用时调用。
// 该钩子在服务器端渲染期间不被调用。
deactivated: function () {
console.log("deactivated");
},
// 实例销毁之前调用。在这一步,实例仍然完全可用。
// 该钩子在服务器端渲染期间不被调用。
beforeDestroy: function () {
console.log("beforeDestroy");
},
// 实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
// 该钩子在服务器端渲染期间不被调用。
destroyed: function () {
console.log("destroyed");
}
});
vm.message = "test111";
</script>
</body>
最后一行修改vm.message属性时出发了beforeUpdate和updated钩子。
数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:
msg需要在vm的data中定义,当vm中的msg值发生变化时,视图中也会刷新:
<span>Message: {{ msg }}</span>
对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。
如下:计算、三目表达式、js的方法
<body>
<div id="app1">
<p>计算: {{num + 10}}</p>
<p>三目表达式: {{ok ? "YES": "NO"}}</p>
<p>js方法: {{string.split("").reverse().join("")}}</p>
</div>
<script type="text/javascript">
var data = {num: 1, string: "abc", ok: true};
var vm = new Vue({
el: "#app1",
data: data
});
</script>
</body>
指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
// 当ok属性为true时,p标签内容会展示出来。反之,会隐藏。
<p v-if="ok">现在你看到我了</p>
** v-cloak 指令**: 这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。
<body>
<div id="app">
<p>{{message}}</p>
</div>
<script type="text/javascript">
// 构造异常场景,1秒后再实例化vue;这会导致用户进入页面时会看到{{message}},等到vue实例化后才会去渲染message。
setTimeout(function () {
var vm = new Vue({
el: "#app",
data: {
message: "abc"
}
})
}, 1000)
</script>
解决办法,html中添加v-cloak属性,然后样式中添加[v-cloak] { display: none } ,初始时会隐藏掉div标签。等vue实例化后,会去删除v-cloak属性,这样div就可以显示出来,数据正常渲染。
<div id="app" v-cloak>
<p>{{message}}</p>
</div>
<style>
[v-cloak] {
display: none;
}
</style>
** v-text 指令** : v-text指令与mustache语法作用类似,但是v-text不实用,不能在前后拼接一些文本。
<span>Message: {{ msg }}</span>
<span v-text="msg"></span>
** v-pre 指令**: v-pre指令用于显示原始文本,即不经过vue编译转换。
<p>{{message}}</p>
<p v-pre>{{message}}</p>
v-once 指令:通过使用 v-once 指令,可以执行一次性地插值,即元素或组件只会渲染一次,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:
<body>
<div id="app1">
<span v-once>这个将不会改变: {{message}}</span>
<p></p>
{{message}}
</div>
<script type="text/javascript">
var data = {message: "Hello vue"};
var vm = new Vue({
el: "#app1",
data: data
});
vm.message = "test111";
</script>
</body>
** v-html 指令**:输出原始HTML。
如果后台返回的数据是一个html标签,前台为了输出真正的 HTML,需要使用 v-html 指令:
<body>
<div id="app1">
// 通过v-html输出原始html
<p v-html="message"></p>
{{message}}
</div>
<script type="text/javascript">
var data = {message: "Hello vue"};
var vm = new Vue({
el: "#app1",
data: data
});
vm.message = "Original html";
</script>
</body>
** v-bind 指令**: 动态绑定属性Attribute。
使用 v-bind 指令修改 HTML attribute:
例如有一种场景,color属性会变化,需要页面对应的字体随之变化。
<body>
<div id="app1">
// 通过v-bind 绑定class属性
<p v-bind:class="color">use v-bind.</p>
{{message}}
</div>
<script type="text/javascript">
var data = {message: "Hello vue",
color: "red"};
var vm = new Vue({
el: "#app1",
data: data
});
vm.message = "";
</script>
</body>
<style type="text/css">
.red {color: red;}
</style>
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind 指令可以用于响应式地更新 HTML attribute:
<a v-bind:href="url">...</a>
在这里 href 是参数,告知 v-bind 指令将该元素的 href attribute 与表达式 url 的值绑定。
另一个例子是 v-on 指令,它用于监听 DOM 事件:
<a v-on:click="doSomething">...</a>
在这里参数是监听的事件名,需要在vm的methods中定义doSomething。
<body>
<div id="app1">
<a v-bind:href="href">链接</a>
<p v-on:click="doSomething">click me</p>
</div>
<script type="text/javascript">
var data = {href: "https://cn.vuejs.org/v2/guide/syntax.html"};
var vm = new Vue({
el: "#app1",
data: data,
methods: {
doSomething: function () {
console.log("v-on click method.")
}
}
});
</script>
</body>
从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
<a v-bind:[attributeName]="url"> ... </a>
这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。
例如,如果 Vue 实例有一个 data 属性 attributeName,其值为 “href”,那么这个绑定将等价于 v-bind:href。
同样地,可以使用动态参数为一个动态的事件名绑定处理函数:
<a v-on:[eventName]="doSomething"> ... </a>
<!-- 这会触发一个编译警告 -->
<a v-bind:['h' + bar]="value"> ... </a>
因此,要使用没有空格或引号的表达式,或用计算属性替代这种复杂表达式。
<body>
<div id="app1">
<a v-bind:[someAttr]="value">链接</a>
</div>
<script type="text/javascript">
var data = {value: "https://cn.vuejs.org/v2/guide/syntax.html", someAttr: "href"};
var vm = new Vue({
el: "#app1",
data: data
});
</script>
</body
因此,需要在vue中定义小写的someattr,或视图中也使用小写的字符:
var data = {value: "https://cn.vuejs.org/v2/guide/syntax.html", someattr: "href"};
Vue 为 v-bind 和 v-on 这两个最常用的指令,提供了特定简写:
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>
计算属性: 如果要定义一个属性,但是需要通过计算才能确定值,比如拼接、计算,那么可以通过computed定义计算属性。
// 使用
<p>{{fullName}}</p>
var vm = new Vue({
el: "#app",
data: {
firstName: "Kobe",
lastName: "Byrant",
},
computed: {
// 定义计算属性,在html中调用时,实际上是调用了get方法。由于get方法返回值不是一个方法,所以在html中使用时不需要加()。
fullName: {
get: function () {
return this.firstName + " " + this.lastName
},
set: function (newName) {
console.log("newName: " + newName);
const name = newName.split(" ");
this.firstName = name[0];
this.lastName = name[1];
}
}
}
})
<div id="app">
<p>总价格:{{totalPrice}}</p>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
books: [
{id: 1, name: "a", price: 20},
{id: 2, name: "b", price: 30},
{id: 3, name: "c", price: 40},
{id: 4, name: "d", price: 50},
{id: 5, name: "e", price: 60}
]
},
computed: {
// 定义计算属性
totalPrice: function () {
let price = 0;
for (let i=0; i<this.books.length; i++) {
price += this.books[i].price;
}
return price
}
}
})
</script>
<body>
<div id="app" v-cloak>
<p>{{getFullName()}}</p>
<p>{{getFullName()}}</p>
<p>{{getFullName()}}</p>
<p>{{fullName}}</p>
<p>{{fullName}}</p>
<p>{{fullName}}</p>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
firstName: "Kobe",
lastName: "Byrant",
},
methods: {
getFullName: function () {
console.log("method---getFullName");
return this.firstName + " " + this.lastName
}
},
computed: {
fullName: function () {
console.log("computed---fullName");
return this.firstName + " " + this.lastName
}
}
})
</script>
修改firstName值,计算属性fullName会重新计算full:
操作元素的 class 列表和内联样式style是数据绑定的一个常见需求。因为它们都是属性,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。vue还提供对象或数组的方式。
// class 对象形式绑定
<p v-bind:class="{类1: boolean, 类2: boolean....}"> 链接 </p>
// style 对象形式绑定
<p class="borderClass" v-bind:style="{fontSize: '50px', color: 'red'}">{{message}}</p>
<body>
<div id="app1">
<p class="size" v-bind:class="{active: isActive, bg: showBg}"> 链接 </p>
</div>
<script type="text/javascript">
var data = {isActive: true, showBg: true};
var vm = new Vue({
el: "#app1",
data: data
});
</script>
</body>
<style type="text/css">
.size {font-size: 20px;}
.active {color: red;}
.bg {background-color: blue}
</style>
<body>
<div id="app1">
<p class="size" v-bind:class="classObject"> 链接 </p>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app1",
data: {
classObject: {
active: true,
bg: true
}
}
});
</script>
</body>
<p class="borderClass" v-bind:class="classed()">{{message}}</p>
// 定义方法
methods: {
classed: function () {
return {active: this.isActive, bg: this.showBg}
}
}
<body>
<div id="app1">
<p class="size" v-bind:class="classObject"> 链接 </p>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app1",
data: {
isActive: true,
bgShow: false
},
// 计算
computed: {
classObject: function () {
return {
active: this.isActive && this.bgShow,
bg: this.bgShow
}
}
}
});
</script>
</body>
可以把一个数组传给 v-bind:class,以应用一个 class 列表:
列表中还可以使用对象或者三目表达式的方式。
<body>
<div id="app1">
<p class="size" v-bind:class="[activeClass, borderShow ? 'borderClass' : '', {bg: bgShow}]"> 链接 </p>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app1",
data: {
activeClass: "active",
borderShow: true,
bgShow: true
},
});
</script>
</body>
<style type="text/css">
.size {font-size: 20px;}
.active {color: red;}
.borderClass {border: 3px solid black}
.bg {background-color: blue;}
</style>
<div id="app" v-cloak>
<p class="borderClass" v-bind:style="[style1, style2]">{{message}}</p>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
message: "abc",
style1: {fontSize: '50px'},
style2: {color: 'red'}
}
})
</script>
<body>
<div id="app1">
<span v-if="type==='A'">A</span>
<span v-else-if="type==='B'">B</span>
<span v-else-if="type==='C'">C</span>
<span v-else>Not A/B/C</span>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app1",
data: {
type: "B"
},
});
</script>
</body>
<div id="app1">
<template v-if="type==='A'">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p></p>
</template>
<template v-else-if="type==='B'">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
<template v-else>Not A/B/C</template>
</div>
<div id="app1">
<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>
<p></p>
<button v-on:click="modifyLogin()">modifyLogin</button>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app1",
data: {
loginType: "username"
},
methods: {
modifyLogin: function () {
// 切换loginType值
this.loginType = this.loginType === "username" ? "" : "username";
}
}
});
</script>
切换 loginType 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素, 不会被替换掉——仅仅是替换了它的 placeholder。
另外一种场景,如果是“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key 属性即可:
<div id="app1">
<template v-if="loginType === 'username'">
<label>Username</label>
<!-- input标签给定唯一的key属性,每次切换时vue都会重新刷新dom -->
<input placeholder="Enter your username" key="username">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="Email">
</template>
<p></p>
<button v-on:click="modifyLogin()">modifyLogin</button>
</div>
v-show 指令用法大致一样,不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。
注: v-show 不支持 元素,也不支持 v-else。
<div id="app1">
<h1 v-show="ok">Hello!</h1>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app1",
data: {
ok: false
}
});
</script>
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if 有更高的切换开销,而** v-show 有更高的初始渲染开销**。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
永远不要把 v-if 和 v-for 同时用在同一个元素上。
当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。
例如:
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
将会经过如下运算过滤出要显示的users:
this.users.map(function (user) {
if (user.isActive) {
return user.name
}
})
因此,哪怕只渲染出一小部分用户的元素,也得在每次重渲染的时候遍历整个列表,不论活跃用户是否发生了变化。
修改为:
computed: {
activeUsers: function () {
return this.users.filter(function (user) {
return user.isActive
})
}
}
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
我们将会获得如下好处:
(1)过滤后的列表只会在 users 数组发生相关变化时才被重新运算,过滤更高效。
(2)使用 v-for=“user in activeUsers” 之后,我们在渲染的时候只遍历活跃用户,渲染更高效。
(3)解耦渲染层的逻辑,可维护性 (对逻辑的更改和扩展) 更强。
用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。
v-for 还支持一个可选的第二个参数,即当前项的索引。
<div id="app1">
<!--遍历数组---第二个参数index可以不要 -->
<ul>
<li v-for="value, index in listStr">
{{ index }}---{{ value }}
</li>
</ul>
<ul>
<li v-for="value in listObj">
{{ value.a }}
</li>
</ul>
<!--遍历对象---第二个参数key可以不要 ,如果需要下标还可以写第三个参数-->
<ul>
<li v-for="value, key in Obj">
{{ key }} : {{ value }}
</li>
</ul>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app1",
data: {
listStr: ["a", "b", "c"],
listObj: [{a: 1}, {a: 2}, {a: 3}],
Obj: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2020-04-05'
}
}
});
</script>
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。这个类似 Vue 1.x 的 track-by="$index"。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
参考顺序表插入元素的操作,复杂度为O(n)。如果给定唯一的key索引,就可以想链表一样插入操作变为O(1);
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,需要为每项提供一个唯一 key 属性:
最好不要用下标作为key,比如: [“a”, “b”, “c”],以下标作为key,则对应关系就是0-a、1-b、2-c,如果在下标1插入d,,则原来下标对应的元素就会发生变化,vue就没办法跟踪到原有的节点(参考:https://segmentfault.com/q/1010000021434057/)。可以采用index+value的方式,提供唯一值(单纯用value可能会冲突)。
<ul>
<li v-for="value, index in Obj" v-bind:key="index+value">
{{ index }} : {{ value }}
</li>
</ul>
变异方法 (mutation method):
vue 侦听的数组的变异方法,它们触发列表数据更新时也将会触发视图更新。这些被包裹过的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
<body>
<div id="app1">
<ul>
<li v-for="value, index in listStr" v-bind:key="index">
{{ index }}---{{ value }}
</li>
</ul>
<button @click="pushList">pushList</button>
<button @click="popList">popList</button>
<button @click="shiftList">shiftList</button>
<button @click="unshiftList">unshiftList</button>
<button @click="spliceList">spliceList</button>
<button @click="sortList">sortList</button>
<button @click="reverseList">reverseList</button>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app1",
data: {
listStr: ["a", "b", "c"]
},
methods: {
// 列表尾部添加元素
pushList: function () {
this.listStr.push("d");
},
// 列表尾部删除元素
popList: function () {
this.listStr.pop();
},
// 列表头部添加元素
unshiftList: function () {
this.listStr.unshift("e");
},
// 列表头部删除元素
shiftList: function () {
this.listStr.shift();
},
// 指定下标删除n个/添加元素(index, n, item1, item2...)
// splice(start, num, x, xx, xxx...) 在下标start删除num个元素,添加x,xx,xxx...
spliceList: function () {
this.listStr.splice(0, 1);
},
// 列表排序
sortList: function () {
this.listStr.sort();
},
// 列表反转
reverseList: function () {
this.listStr.reverse();
}
}
});
</script>
</body>
app1.items = app1.items.filter(function (item) {
return item.message.match(/Foo/)
})
不会被vue 检测到数组变动的情况:
由于 JavaScript 的限制,Vue 不能检测以下数组的变动:
(1) 通过索引直接设置一个数组项时— items[1] = ‘x’。
(2)修改数组的长度时— items.length = 2。
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
以下两种方式可以解决列表添加元素的问题,Vue.set等同于vm.$set,也可以使用 vm.$set 方法是全局方法 Vue.set 的一个别名::
```javascript
// Vue.set添加元素
Vue.set(vm.items, indexOfItem, newValue)
vm.$set(vm.items, indexOfItem, newValue)
// splice方法添加元素
vm.items.splice(indexOfItem, 1, newValue)
为了解决第二类问题,可以使用 splice:
vm.items.splice(newLength)
由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:
var vm = new Vue({
data: {
a: 1
}
})
vm.a = 3 // `vm.a` 是响应式的
vm.b = 2 // `vm.b` 不是响应式的
对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性。例如,对于:
var vm = new Vue({
data: {
userProfile: {
name: 'Anika'
}
}
})
可以添加一个新的 age 属性到嵌套的 userProfile 对象:
Vue.set(vm.userProfile, 'age', 27)
vm.$set(vm.userProfile, 'age', 27)
当需要添加多个属性时:
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
类似于 v-if,你也可以利用带有 v-for 的 来循环渲染一段包含多个元素的内容。比如:
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
如果要动态更新标签的属性,需要用v-bind:属性或者缩写形式**:属性**来动态更新。比如:
<template v-for="data in checkboxData">
<input type="checkbox" :id="data" :value="data" v-model="checkedNames" v-on:change="checkboxEvent">
<label :for="data">{{data}}</label>
</template>
这部分内容需要先学习组件相关知识。
在自定义组件上,可以像在任何普通元素上一样使用 v-for。在2.2.0+ 版本里,当在组件上使用 v-for 时,key是必须的。
通过prop把迭代数据传递到组件里:
<div id="app">
<blog-post v-for="item in titles" :key="item.id" :title="item.name"></blog-post>
</div>
Vue.component('blog-post', {
props: ['title'],
template: 'Blog: {{ title }}
'
});
var vm = new Vue({
el: "#app",
data: {
temp: "abc",
titles: [
{id: 1, name: "ZZZZ"},
{id: 2, name: "XXXX"},
{id: 3, name: "CCCC"}
]
}
})
v-on 指令用于监听 DOM 事件,并在触发时运行一些 JavaScript 代码。
<body>
<div id="app">
<p>{{num}}</p>
<!-- 1. 简单的事件可以直接在html中添加 -->
<button v-on:click="num++">+</button>
<button v-on:click="num--">-</button>
<!-- 2. 复杂的事件可以通过添加方法 -->
<button v-on:click="increment">+</button>
<button v-on:click="decrement">-</button>
<!-- 3. v-on的语法糖:缩写形式 -->
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
num: 0
},
methods: {
// ES6中对象方法的新写法
increment () {
this.num ++;
},
decrement() {
this.num --;
}
}
})
</script>
v-on绑定方法的调用方式:
<div id="app">
<p>{{num}}</p>
<ul>
<li v-for="value, index in listStr" v-bind:key="index">
{{ index }}---{{ value }}
</li>
</ul>
<!-- 1. 方法不需要传入任何参数时可以省略括号 -->
<button v-on:click="increment">+</button>
<!-- 2. 方法需要传入参数时需要在括号中传入参数,否则vue会自动把事件event传入 -->
<button v-on:click="decrement('abc')">-</button>
<!-- 3. 方法需要获取普通参数和事件event时, 需要写括号并且事件通过$event传入 -->
<button v-on:click="pushList('test', $event)">pushList</button>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
num: 0,
listStr: ["a", "b", "c"]
},
methods: {
pushList: function (str, e) {
console.log("------pushList", str, e);
this.listStr.push(str);
},
increment () {
console.log("------increment");
this.num ++;
},
decrement(str) {
console.log("------decrement", str);
this.num --;
}
}
})
</script>
Vue.js 为 v-on 提供了事件修饰符。修饰符是由点开头的指令后缀来表示的。
.stop修饰符:阻止事件冒泡:
如下, 子级的点击事件会继续传播到父级:
<div id="app1">
<div v-on:click="parent">aaaaaaa
<button v-on:click="child">child</button>
</div>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app1",
methods: {
parent: function () {
console.log("parent");
},
child: function () {
console.log("child");
}
}
});
</script>
<div v-on:click="parent">aaaaaaa
<button v-on:click.stop="child">child</button>
</div>
其他修饰符:
.prevent
.capture
.self
.once
.passive
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 2.1.4新增 -->
<a v-on:click.once="doThis"></a>
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">
<input v-on:keyup.page-down="onPageDown">
你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。
在上述示例中,处理函数只会在 $event.key 等于 PageDown 时被调用。
注:keyCode 的事件用法已经被废弃了并可能不会被最新的浏览器支持。
<input v-on:keyup.13="submit">
为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
有一些按键 (.esc 以及所有的方向键) 在 IE9 中有不同的 key 值, 如果你想支持 IE9,这些内置的别名应该是首选。
你可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
.left
.right
.middle
这些修饰符会限制处理函数仅响应特定的鼠标按钮。
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
.ctrl
.alt
.shift
.meta
<!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。如果你想要这样的行为,请为 ctrl 换用 keyCode:keyup.17。
.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>
展示购物车书籍信息,涉及知识点: v-if、v-for、v-on、v-bind、计算属性、过滤器
<div id="app1">
<div v-if="books.length !== 0">
<table border="1">
<thead>
<tr>
<th></th>
<th>书籍名称</th>
<th>出生日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- 使用index+book.name作为key,保证唯一 -->
<tr v-for="book, index in books" :key="index+book.name">
<td>{{index + 1}}</td>
<td>{{book.name}}</td>
<td>{{book.date}}</td>
<td>{{book.price | showPrice}}</td>
<td>
<button @click="calculatePrice(index, -1)" :disabled="book.nums===1">-</button>
{{book.nums}}
<button @click="calculatePrice(index, 1)">+</button>
</td>
<td><button @click="removeBook(index)">移除</button></td>
</tr>
</tbody>
</table>
<!-- 过滤器 -->
总价: {{totalPrice | showPrice}}
</div>
<div v-else>当前购物车中没有书籍</div>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app1",
data: {
books: [
{id: 1, name: "《算法导论》", date: 2006-9, price: 85.00, nums: 1},
{id: 2, name: "《UNIX编程艺术》", date: 2006-2, price: 59.00, nums: 1},
{id: 3, name: "《编程珠玑》", date: 2008-10, price: 39.00, nums: 1},
{id: 4, name: "《代码大全》", date: 2006-3, price: 128.00, nums: 1},
]
},
methods: {
calculatePrice(index, num) {
num = parseFloat(num);
this.books[index].nums += num;
},
removeBook(index) {
// 删除列表元素用splice
this.books.splice(index, 1);
}
},
computed: {
// 总价格通过计算属性来展示,只有书籍信息发生变化时再重新计算
totalPrice() {
// 高阶函数reduce(func(前一次的值, 循环得到每一项){具体操作}, 初始值)
return this.books.reduce(function (preValue, book) {
return preValue + book.price * book.nums
}, 0)
}
},
// vue的过滤器
filters: {
// 过滤器一般都是输入值,通过对输入值做其他操作再返回
showPrice(price) {
return "¥" + price.toFixed(2)
}
}
});
</script>
v-model 指令在表单 、 及 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。用户需要通过 JavaScript 在组件的 data 选项中声明初始值。
v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
text 和 textarea 元素使用 value 属性和 input 事件;
checkbox 和 radio 使用 checked 属性和 change 事件;
select 字段将 value 作为 prop 并将 change 作为事件。
对于需要使用输入法 (如中文、日文、韩文等) 的语言, v-model 不会在输入法组合文字过程中得到更新。如果需要处理这个过程,使用 input 事件。例如:
<input v-model="message" placeholder="edit me" v-on:input="inputEvent()">
<textarea placeholder="add multiple lines" v-on:input="textareaEvent()">{{message1}</textarea>
修改为:
<textarea v-model="message1" placeholder="add multiple lines" v-on:input="textareaEvent()"></textarea>
<select v-model="selected" v-on:change="selectEvent">
<option disabled value="">请选择</option>
<option v-for="item in selectItems" :value="item">{{item}}</option>
</select>
<body>
<div id="app1">
<p>文本input</p>
<!-- input事件可以监听到在输入法中的变动,v-model不会处理输出法中的变动-->
<input v-model="message" placeholder="edit me" v-on:input="inputEvent()">
<label>Message is: {{ message }}</label>
<p>多行文本textarea</p>
<textarea v-model="message1" placeholder="add multiple lines" v-on:input="textareaEvent()"></textarea>
<label>Multiline message is: {{ message1 }}</label>
<br>
<!-- checkbox单选框,绑定到布尔值 -->
<p>单选框</p>
<input type="checkbox" id="data" v-model="checked">
<label for="data">Checked: {{checked}}</label>
<br>
<p>复选框</p>
<!-- checkbox复选框,绑定到同一个数组 -->
<template v-for="data in checkboxData">
<input type="checkbox" :id="data" :value="data" v-model="checkedNames" v-on:change="checkboxEvent">
<label :for="data">{{data}}</label>
</template>
<br>
<span>Checked names: {{ checkedNames }}</span>
<br>
<p>单选按钮</p>
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<span>Picked: {{ picked }}</span>
<br>
<p>select 下拉列表:单选</p>
<select v-model="selected" v-on:change="selectEvent">
<option disabled value="">请选择</option>
<option v-for="item in selectItems" :value="item">{{item}}</option>
</select>
<label>Selected: {{selected}}</label>
<p>select 下拉列表:多选</p>
<select v-model="selectedMultiple" multiple>
<option v-for="item in selectItems" :value="item">{{item}}</option>
</select>
<label>selectedMultiple: {{selectedMultiple}}</label>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app1",
data: {
message: "",
message1: "",
checked: false,
checkedNames: [],
checkboxData: ["Jack", "John", "Mike"],
picked: "",
selectItems: ["A", "B", "C"],
selected: "",
selectedMultiple: ["A"]
},
methods: {
inputEvent: function () {
console.log("inputEvent");
},
textareaEvent: function () {
console.log("textareaEvent");
},
checkboxEvent: function () {
console.log(this.checkedNames);
},
selectEvent: function (item) {
console.log("selectEvent");
}
}
});
</script>
v-model实际也是一个语法糖,用v-bind和v-on也可以实现:
<div id="app1">
<!-- <input type="text" v-model="message"> -->
<!-- 调用方法时不传参数,默认会把$event传给方法 -->
<!-- <input type="text" v-bind:value="message" v-on:input="inputValue"> -->
<input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
{{message}}
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app1",
data: {
message: "aaaaaaaaa"
},
methods: {
inputValue(event) {
// 通过event事件获取input框的实际输入值,刷新message属性
this.message = event.target.value;
}
}
});
</script>
对于单选按钮,复选框及选择框的选项,v-model 绑定的值通常是静态字符串 (对于复选框也可以是布尔值):
<!-- 当选中时,`picked` 为字符串 "a" -->
<input type="radio" v-model="picked" value="a">
<!-- `toggle` 为 true 或 false -->
<input type="checkbox" v-model="toggle">
<!-- 当选中第一个选项时,`selected` 为字符串 "abc" -->
<select v-model="selected">
<option value="abc">ABC</option>
</select>
<input type="checkbox" id="data" value="data" v-model="checked" true-value="yes" false-value="no">
// 当选中时
vm.checked=== ‘yes’
// 当没有选中时
vm.checked=== ‘no’
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="message" placeholder="edit me" v-on:input="inputEvent()">
oninput事件和onchange事件区别:oninput事件是当input的value值发生变化时就会触发,而onchang是当input失去焦点或敲回车并且它的value值发生变化时触发。
<input v-model.number="message" placeholder="edit me" v-on:input="inputEvent()">
<input v-model.trim="message" placeholder="edit me" v-on:input="inputEvent()">
待学习
组件是可复用的 Vue 实例,且带有一个名字,可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用。
因为组件是可复用的 Vue 实例,所以与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。
老版本的写法:
注:一个组件的 data 必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。否则,点击一个按钮就可能会影响到其它所有实例。
如下组件名称为button-counter:
// 1.创建组件构造器
const btnCom = Vue.extend({
data: function () {
return {
num: 0
}
},
methods: {
count: function () {
return this.num++
}
},
template: ""
});
// 2. 注册全局组件
Vue.component('button-counter', btnCom);
var vm = new Vue({
el: "#app1",
data: {
}
});
<div id="app">
// 3.使用组件
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
2.X版本之后很少用Vue.extend()去创建组件,可以直接用:
// 注册组件,传入一个选项对象 (自动调用 Vue.extend)
Vue.component('my-component', { /* ... */ })
组件注册有两种注册类型:全局注册和局部注册。
<script type="text/javascript">
// 注册全局组件
Vue.component('button-counter', {
data: function () {
return {
num: 0
}
},
methods: {
count: function () {
return this.num++
}
},
template: ""
});
var vm1 = new Vue({
el: "#app1",
data: {
}
});
var vm2 = new Vue({
el: "#app2",
data: {
}
});
</script>
<div id="app1">
// 在 app1中可以使用button-counter组件
<button-counter></button-counter>
</div>
<p>----------------------------</p>
<div id="app2">
// 在 app2中可以使用button-counter组件
<button-counter></button-counter>
</div>
<script type="text/javascript">
var vm1 = new Vue({
el: "#app1",
// 注册局部组件
components: {
buttonCounter: {
data: function () {
return {
num: 0
}
},
methods: {
count: function () {
return this.num++
}
},
template: ""
}
}
});
var vm2 = new Vue({
el: "#app2",
data: {
}
});
</script>
<div id="app1">
<button-counter></button-counter>
</div>
<p>----------------------------</p>
<div id="app2">
<button-counter></button-counter>
</div>
template中包含多个同级别标签时,需要用一个根元素包裹。
当组件模板包含多个同级别html标签时Vue 会显示一个错误,解决办法就是将模板的内容包裹在一个父元素内:
如下,使用div作为根元素包裹h3和div。
<div id="app">
<blog-post v-for="item in titles" :key="item.id" :item="item"></blog-post>
</div>
Vue.component('blog-post', {
props: ['item'],
template: 'Blog: {{ item.title }}
Content: {{item.content}}'
});
由于在js中写template,编辑器没有很好的支持,并且代码结构看着很乱,所以,有以下两种方式可以把template定义在html中,通过id进行关联:
<div id="app1">
<comp></comp>
</div>
<!--1. 通过script标签定义模板,type为text/x-template-->
<!--<script type="text/x-template" id="com1">-->
<!-- <div>-->
<!-- <h2>这是组件1</h2>-->
<!-- </div>-->
<!--</script>-->
<!--2. 通过template标签定义模板-->
<template id="com1">
<div>
<h2>这是组件1</h2>
</div>
</template>
<script type="text/javascript">
const com1 = Vue.extend({
template: "#com1"
});
// 注册全局组件
var vm = new Vue({
el: "#app1",
components: {
comp: com1
}
});
</script>
下面的例子中,自定义row组件,在table中使用:
<table border="1">
<tbody>
<row></row>
<row></row>
<row></row>
</tbody>
</table>
Vue.component("row",{
template:'this is a row '
});
实际效果与预想的不一致,组件被解析在了table外面:
产生这个问题的原因是:有些 HTML 元素,诸如 ul、ol、table 和 select,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 li、tr 和 option,只能出现在其它某些特定的元素内部。
自定义组件 row 会被作为无效的内容提升到外部,并导致最终渲染结果出错。
解决办法,使用is属性:
<table border="1">
<tbody>
<tr is="row"></tr>
<tr is="row"></tr>
<tr is="row"></tr>
</tbody>
</table>
需要注意的是如果从以下来源使用模板的话,这条限制是不存在的:
1. 字符串 (例如:template: ‘…’)
2. 单文件组件 (.vue)
3. script type=“text/x-template”
如下,
<div id="app1">
<comp></comp>
<com1></com1>
</div>
<script type="text/javascript">
// 构造组件1
const com1 = Vue.extend({
template: `
这是组件1
`
});
// 构造组件2
const com2 = Vue.extend({
template: `
这是组件2
`,
// 注册子组件
components: {
com1: com1
}
});
// 注册全局组件
var vm = new Vue({
el: "#app1",
components: {
comp: com2
}
});
</script>
<div id="app1">
<comp :cmovies="'abc'"></comp>
</div>
<template id="com1">
<div>
<ul>
<li v-for="movie in cmovies">{{movie}}</li>
</ul>
</div>
</template>
<script type="text/javascript">
const com1 = Vue.extend({
template: "#com1",
// 1. 数组形式:定义子组件的“属性名称列表”
// 父组件调用子组件时,通过v-bind绑定子组件的属性,传入值
// props: ["cmovies"],
// 2. 对象形式
// 可以定义参数类型
props: {
cmessage: {
type: String,
default: "子组件"
},
cmovies: {
type: Array,
// 数组和对象的default值 需要通过方法定义
default() {
return ["空"]
},
required: true
},
cprice: {
// 自定义校验器
validator(value) {
return value === "success"
}
}
}
});
// 注册全局组件
var vm = new Vue({
el: "#app1",
data: {
movies: ["海王", "海贼王", "海绵宝宝"]
},
components: {
comp: com1
}
});
</script>
② 父组件通过 “ v-on:子组件自定义的事件名称” 监听子组件事件,通过$event接收子组件事件传递的参数;
<div id="app1">
// 2. 父组件通过v-on监听子组件的自定义事件,
// 如果子组件有传递参数,则父组件通过$event接收子组件传递的参数
<comp @categoryclick="compClick($event)"></comp>
</div>
<template id="com1">
<div>
<button v-for="category in categories" @click="btnClick(category)">{{category.name}}</button>
</div>
</template>
<script type="text/javascript">
const com1 = Vue.extend({
template: "#com1",
data() {
return {
categories: [
{id: 1, name: "家电"},
{id: 2, name: "男装"},
{id: 3, name: "女装"}
]
}
},
methods: {
// 1. 子组件定义点击事件,通过$emit发送自定义事件(categoryclick)给父组件
// 第一个参数为自定义事件名称,第二个参数为需要传递的参数
btnClick(category) {
this.$emit("categoryclick", category)
}
}
});
// 注册全局组件
var vm = new Vue({
el: "#app1",
components: {
comp: com1
},
methods: {
// 入参为子组件传递的参数
compClick(category) {
console.log("compClick", category)
}
}
});
</script>
尽量避免使用驼峰命令定义属性名称,例如: 属性名称cMovies,在html中使用时就得写为中划线方式c-movies,否则会报错找不到cMovies。
props: {
// 子组件定义属性名称时,采用驼峰命名
cMovies: {
type: Array,
// 数组和对象的default值 需要通过方法定义
default() {
return ["空"]
},
required: true
}
}
<div id="app1">
// 父组件使用时,也使用驼峰命名
<comp :cMovies="movies"></comp>
</div>
如下,会报错找不到cMovies属性
在html中改为中划线方式可以正常使用:
<div id="app1">
<comp :c-movies="movies"></comp>
</div>
有两种方式 c h i l d r e n 和 children 和 children和refs。
$children:是一个列表,里面包含了所有的子组件。一般只有在要获取所有子组件时使用。
r e f s : 需 要 在 组 件 定 义 r e f 属 性 , 属 性 名 称 即 最 终 会 显 示 在 refs:需要在组件定义ref属性,属性名称即最终会显示在 refs:需要在组件定义ref属性,属性名称即最终会显示在refs对象中的key值。
<div id="app1">
<comp></comp>
<!-- 定义组件2在$refs中的key值 -->
<comp ref="component2"></comp>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn1">
<div>
<ul>
<li v-for="category in categories">{{category.name}}</li>
</ul>
</div>
</template>
<script type="text/javascript">
const cpn1 = Vue.extend({
template: "#cpn1",
data() {
return {
categories: [
{id: 1, name: "家电"},
{id: 2, name: "男装"}
]
}
},
methods: {
showMessage() {
console.log("cpn1");
}
}
});
var vm = new Vue({
el: "#app1",
components: {
comp: cpn1
},
methods: {
btnClick() {
// 通过$children获取所有的子组件列表
console.log(this.$children);
this.$children[0].showMessage();
// 通过$refs获取子组件对象
console.log(this.$refs);
this.$refs.component2.showMessage()
}
}
});
</script>
实际项目开发中,不建议在子组件中访问父组件,这样会降低组件的复用性。
$root: 获取根组件。
$parent:获取父组件。
<div id="app1">
<comp></comp>
</div>
<template id="cpn1">
<div>
<ul>
<li v-for="category in categories">{{category.name}}</li>
</ul>
<button @click="btn1Click">cpn1按钮</button>
<cpn2></cpn2>
</div>
</template>
<template id="cpn2">
<div>
<button @click="btn2Click">cpn2按钮</button>
</div>
</template>
<script type="text/javascript">
const cpn1 = Vue.extend({
template: "#cpn1",
data() {
return {
categories: [
{id: 1, name: "家电"},
{id: 2, name: "男装"}
]
}
},
methods: {
btn1Click() {
// 获取父组件
console.log("cpn1 $parent", this.$parent);
}
},
// 子组件cpn1中定义子组件cpn2
components: {
cpn2: {
template: "#cpn2",
methods: {
btn2Click() {
// 获取父组件
console.log("cpn2 $parent", this.$parent);
// 获取根组件
console.log("$root", this.$root);
console.log("$root", this.$root.test);
}
}
}
}
});
var vm = new Vue({
el: "#app1",
data: {
test: "123456"
},
components: {
comp: cpn1
}
});
</script>
如下:
cpn2的父组件是cpn1,是VueComponent实例。
cpn1的父组件是Vue实例,即根组件。
<div id="app1">
<cpn1>
<button>按钮</button>
</cpn1>
<h2>------------------------</h2>
<cpn1>
</cpn1>
</div>
<template id="cpn1">
<div>
<h3>这是子组件</h3>
<slot>
<!-- 定义插槽默认内容 -->
<h4>这是插槽中默认内容</h4>
</slot>
</div>
</template>
<script type="text/javascript">
const cpn1 = Vue.extend({
template: "#cpn1"
});
var vm = new Vue({
el: "#app1",
components: {
cpn1: cpn1
}
});
</script>
<div id="app1">
<cpn1>
<!-- 1. 直接使用要插入的元素,并定义slot属性 -->
<button slot="left"><</button>
<input type="text" slot="right">
<!-- 2. 使用template标签,并定义slot属性 -->
<template slot="left">
<button><</button>
</template>
<template slot="right">
<input type="text" slot="right">
</template>
<!-- 3. 官方推荐新写法 v-slot指令,可简写为 “#名称” -->
<template v-slot:left>
<button><</button>
</template>
<template #right>
<input type="text" slot="right">
</template>
</cpn1>
</div>
<template id="cpn1">
<div>
<h3>这是子组件</h3>
<slot name="left">
<h4>这是左插槽中默认内容</h4>
</slot>
<slot name="right">
<h4>这是右插槽中默认内容</h4>
</slot>
<slot><h3>非具名插槽的默认值</h3></slot>
</div>
</template>
<div id="app1">
<cpn1>
<h2>父组件使用的变量会去父级的data中找对应变量:{{test}}</h2>
</cpn1>
</div>
<template id="cpn1">
<div>
<h3>这是子组件</h3>
<h3>子组件中使用的变量会去自身的data中找对应变量:{{test}}</h3>
<slot></slot>
</div>
</template>
<script type="text/javascript">
const cpn1 = Vue.extend({
template: "#cpn1",
data() {
return {
test: "children scope data"
}
}
});
var vm = new Vue({
el: "#app1",
data: {
test: "parent scope data"
},
components: {
cpn1: cpn1
}
});
</script>
<div id="app1">
<!-- 官方推荐用法 -->
<cpn1>
<!-- 这个例子中没有定义具名插槽,默认的插槽名称为default,其中default也可以省略 -->
<!-- 但是有具名插槽时,不能省略“插槽名称” -->
<template v-slot:default="slotProps">
{{slotProps.user.lastName}}
</template>
</cpn1>
<!-- 使用slot-scope定义子组件插槽prop的名称是已废弃的用法 -->
<cpn1>
<template slot-scope="slotProps1">
{{slotProps1.user.lastName}}
</template>
</cpn1>
</div>
<template id="cpn1">
<div>
<h3>这是子组件</h3>
<slot :user="user">{{user.firstName}}</slot>
</div>
</template>
<script type="text/javascript">
const cpn1 = Vue.extend({
template: "#cpn1",
data() {
return {
user: {
lastName: "Byrant",
firstName : "Kobe"
}
}
}
});
var vm = new Vue({
el: "#app1",
components: {
cpn1: cpn1
}
});
</script>
在不同组件之间进行动态切换需要通过 Vue 的 component 元素加一个特殊的** is attribute ** 来实现:
<div id="app">
<component :is="currentView"></component>
<button @click="changeView('A')">切换到A</button>
<button @click="changeView('B')">切换到B</button>
<button @click="changeView('C')">切换到C</button>
</div>
var vm = new Vue({
el: "#app",
data: {
currentView: 'comA'
},
methods: {
changeView: function (name) {
this.currentView = "com" + name;
}
},
components: {
comA: {
template: '组件A'
},
comB: {
template: '组件B'
},
comC: {
template: '组件C'
}
},
})
一些功能可能要求我们和父级组件进行沟通。
// 在父组件通过 “v-on:子组件自定义组件名称” 来监听子组件的冒泡事件
<div id="app">
<div :style="{fontSize: postFontSize + 'px'}">
<blog-post v-for="item in titles" :key="item.id" :item="item" v-on:enlarge-text="postFontSize += 1">
</blog-post>
</div>
</div>
// 子组件通过调用内建的 $emit 方法传入事件名称来触发一个事件
// 如下子组件通过v-on:click绑定了点击事件,然后通过 $emit(“自定义事件名称”) 向父组件上报要触发的事件,类似于“冒泡”;
Vue.component('blog-post', {
props: ['item'],
template: 'Blog: {{ item.title }}
Content: {{item.content}}'
});
var vm = new Vue({
el: "#app",
data: {
temp: "abc",
postFontSize: 16,
titles: [
{id: 1, name: "ZZZZ", content: "11111111111111"},
{id: 2, name: "XXXX", content: "22222222222222"}
]
}
})
2. 使用事件抛出一个值:
可以使用 e m i t 的 第 二 个 参 数 来 提 供 要 抛 出 的 值 , 父 组 件 使 用 emit 的第二个参数来提供要抛出的值,父组件使用 emit的第二个参数来提供要抛出的值,父组件使用event接收值:
//子组件通过 $emit('enlarge-text', 2)抛出事件并传值
Vue.component('blog-post', {
props: ['item'],
template: 'Blog: {{ item.title }}
Content: {{item.content}}'
});
// 父组件通过$event接收值
<blog-post v-for="item in titles" :key="item.id" :item="item" v-on:enlarge-text="postFontSize += $event"> </blog-post>
或者用函数形式:
<blog-post v-for="item in titles" :key="item.id" :item="item" v-on:enlarge-text="modifyFontSize">
</blog-post>
// 父组件定义modifyFontSize方法,直接通过形参size接收值
methods: {
modifyFontSize: function (size) {
console.log("modifyFontSize!" + size);
this.postFontSize += size;
}
}
待学习
组件名强烈推荐遵循 W3C 规范中的自定义组件名 (字母全小写且必须包含一个连字符)。
使用 kebab-case (短横线分隔命名) 或使用 PascalCase (首字母大写命名) 定义一个组件都是允许的, 即 和 都是可接受的。但直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。
通过 Vue.component 来创建全局注册组件,它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中。比如:
Vue.component('my-component-name', {
// ... 选项 ...
})
所有子组件在各自内部也都可以相互使用。
全局注册往往是不够理想的。比如,使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便已经不再使用一个组件了,它仍然会被包含在最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。
可以通过在components 选项中定义想要使用的组件,来局部注册组件:
var ComponentA = { /* ... */ };
var ComponentB = { /* ... */ };
var vm = new Vue({
el: '#app',
components: {
// key就是自定义组件的名称
'component-a': ComponentA,
'component-b': ComponentB
}
})
注:局部注册的组件在其子组件中不可用。如果希望 ComponentA 在 ComponentB 中可用,则需要这样写:
var ComponentA = { /* ... */ }
var ComponentB = {
components: {
'component-a': ComponentA
},
// ...
}
待学习。
使用 Vue.component 来定义全局组件,紧接着用 new Vue({ el: '#container '}) 在每个页面内指定一个容器元素,这在很多中小规模的项目中运作的很好。但是更复杂的项目中,或者你的前端完全由 JavaScript 驱动的时候,下面这些缺点将变得非常明显:
文件扩展名为 .vue 的 single-file components(单文件组件) 为以上所有问题提供了解决方法,并且还可以使用 webpack 或 Browserify 等构建工具。
安装好环境后:
创建完成后的新项目目录结构,其中public是打包后用于部署到生成环境的目录;src是主要的开发目录,src/components目录就是存放自定义组件的目录;App.vue是项目的入口文件。
新建组件分为三部分:template即模板, style即样式, script即js代码。
如下创建自定义组件test:
<template>
<h3 class="red">test....{{msg}}</h3>
</template>
<script>
export default {
name: "test",
props: {
msg: {
type: String,
// 设置默认值,父级组件没有传参时使用默认值展示。
default: " default msg."
}
},
methods: {
},
data: function () {
return {}
}
}
</script>
<style scoped>
.red { color: red}
</style>