我用vue-cli 构建了项目,但是无奈自己以前没有 node 的基础,构建的项目文件参考我的前几篇文章,但没搞懂基础知识就弄这些难免有点虎头蛇尾了,而且网上的视频教程都是引用的CDN,所以自己也决定新建个html文件引用CDN先学习基础知识
一、案例展示
1. 初始模块
<body>
<div id="app">{{msg}}</div>
</body>
<script>
const app = new Vue({
//需要挂载的元素
el : "#app",
//定义数据
data : {
msg : "hello Vue!"
}
})
</script>
解析:我们声明了一个Vue对象实例并将其赋值给常量app,那么就可以使用vue的方法对象了嘛。我们挂载了body中id为app的div,用vue实例来管理它。
这样做的好处:真正实现了将数据从html中分离出来。将我们的编程范式由命令式编程转移到声明式编程,使编程更加方便易懂了。
2. Vue的列表展示
<body>
<div id="app">
<ul>
<!-- 循环,遍历每个li-->
<li v-for="item in movies">{{item}}</li>
</ul>
</div>
</body>
<script>
const app = new Vue({
el : '#app',
data : {
movies: ['盗梦空间','少年班','我和我的祖国']
}
})
</script>
分析:我们要写个列表,那么就要在data里面定义一个数组(这里的movies),用来展示一个列表各项所需的数据。既然是列表,body里面肯定是要由ul,li的。而且想到用循环遍历某个数组,循环里定义一个item,来表示每一项的内容。
3. 简单的计数器
<body>
<div id="app">
<h2>当前计数:{{counter}}</h2>
<button v-on:click="add">+</button>
<button @click="sub">-</button>
</div>
</body>
<script>
const app = new Vue({
el : '#app',
data : {
counter: 0
},
methods: {
add: function(){
this.counter++;
console.log("数字+1");
},
sub: function(){
this.counter--;
console.log("数字-1")
}
}
})
</script>
分析:
v-on 是对事件的监听,click 是事件,事件后面的 “ ”里面的内容是事件发生时要执行的东西。
v-on:click="“也可以简写为@click=”"(因为 @click 是 v-on:click="" 的语法糖,语法糖的意思就是一个语法的简写,简写后给你点甜头。。。)
Vue这个实例对象里面呢,又多了个methods属性,这个是方法,比如执行事件要运行的函数啊什么的。
为什么要用this.counter++;直接counter++呢?因为counter是实例对象Vue里面的一个变量,假如写counter那就会在全局作用域里面找counter,而这样是找不到的,所以要用this.counter。又为什么不用Vue.counter呢?这样是为了代码解耦。。。
二、理解vue中的MVVM
三、初探vue的生命周期
我们新建一个Vue实例时,其实在其内部(Vue源码)中做了很多工作,这些工作从开始到结束就相当于其生命周期。我们可在新建的Vue实例中写回调函数,回调函数会在源码中被调用,这样我们就可知道Vue进行到哪一步了。
新建一个Vue实例,Vue源码内部在完成一系列操作时,它在不同阶段会有不同的生命周期函数。例如第一步时有什么函数,第二步时有什么函数,那我们就可以在我们新建的实例中用这个函数完成我们所需要的操作。
(后面会深入了解,这里只是简单了解一下)
四、模板语法
1. 插值操作
- Mustache语法(双大括号语法)
不光可引用变量还可使用js表达式
<body>
<div id="app">
<h2>{{msg}}</h2>
<h2>{{firstname + " " + lastname}}</h2>
<h2>{{firstname}} {{lastname}}</h2>
<h2>{{counter * 2}}</h2>
</div>
</body>
<script>
const app = new Vue({
el : '#app',
data : {
msg: "你好",
firstname: "yuan",
lastname: "fajun",
counter: 10
}
})
</script>
- v-once 指令
元素和组件只渲染一次,不会随着数据的改变而改变(这里的数据不是指你代码给的数据,而是你在浏览器上调试页面时改的数据)
该指令后面无需跟任何得到表达式(比如之前的v-for是跟了表达式的)
<h2 v-once>{{msg}}<h2>
- v-html 指令
若获取的数据是一个标签,那如果直接用双括号法就会将这个标签按字符串显示出来。当我们想解析这个html标签直接用它的功能时那就需要用到html指令。
该指令后要接表达式,表达式里面是需要用html解析的变量
<body>
<div id="app">
<h2 v-html="url"></h2>
</div>
</body>
<script>
const app = new Vue({
el : '#app',
data : {
url: '<a href="http://www.baidu.com">百度一下</a>'
}
})
</script>
- v-text(一般不用)
它和Mustache(双括号语法)差不多,都是将文本显示不来。但是它有缺点,它不够灵活,因为当我们想添加一些直接显示的文本,用Mustache的话直接在它后面添加就好了;但是这个指令要添加的话添加到h2里面会被msg覆盖,添加不了,所以一般不用它。
<h2 v-text="msg"></h2>
- v-pre指令(用的少)
跳过Vue实例对象对Mustache语法的解析,直接显示内容。一般用于我们不想解析,想直接显示{{…}}
<h2 v-pre>{{msg}}</h2>
- v-cloak指令(后来一般不用)
假如js部分卡住了没有解析,那我们用Mustache语法时页面就会显示{{ }}一段时间,等js部分代码解析后,才会显示正常文本。这样会影响用户体验。那我们就会给元素添加一个v-cloak指令,js没解析就有这个指令,js解析后,这个指令自动消失。再我们添加个css样式,就可以实现js未加载前不显示东西了。
<h2 v-cloak>{{..}}</h2>
/*样式选择器*/
[v-cloak]{
display: none;
}
后面我们会用组件渲染的方式,所以这个指令一般不用了。
2. v-bind指令
前面的指令都是和Mustache语法有关,都是动态绑定元素的内容进行响应式显示。在现实开发中,我们需要常常需要对一个元素的属性进行动态绑定。因为一个元素的属性往往不会是写死的,需要从后端获取数据,再响应式的为属性赋值,这样的开发比写死属性有效率的多。动态绑定属性是需要用v-bind属性的。
v-bind的语法糖是 :(用语法糖用的非常多)
<body>
<div id="app">
<img v-bind:src="imgURL" alt="">
<!--v-bind的语法糖:-->
<a :href="aHref">百度一下</a>
</div>
</body>
<script>
const app = new Vue({
el : '#app',
data : {
imgURL: "https://f10.baidu.com/it/u=1559464017,2057851200&fm=76",
aHref: "http:www.baidu.com"
}
})
</script>
- v-bind动态绑定class(对象语法)
假如一个需求是要点击按钮,改变h2内容文字的颜色。首先想到的就是通过改变class属性值来改变样式吧。所以可以使用到v-bind动态绑定class来做,不过这里可以使用更简易的对象语法。
<h2 class="{类名1: 布尔值, 类名2: 布尔值}">你好啊</h2>
这就是对象语法,可传入好几个类名,布尔值为true时,那class属性的值就是该类名。
不过既然是要动态绑定,那就是下面这种写法。
<style>
.active{
color: red;
}
</style>
<body>
<div id="app">
<h2 :class="{active: isActive, negative: isNegative}">{{msg}}</h2>
<!--v-on:click的语法糖-->
<button @click="btnclick">点击我</button>
</div>
</body>
<script>
const app = new Vue({
el : '#app',
data : {
msg: "你好啊",
isActive: true,
isNegative: true
},
methods: {
//原本想用箭头函数写,但是箭头函数用不了this
btnclick: function(){
//使isActive值改变
this.isActive = !this.isActive;
}
}
})
</script>
实现原理就是使用对象语法动态绑定h2的class属性,并用isActive,isNegative代替属性类名的值,初始值为true,点击事件发生时改变isActive的值即可改变样式
并且动态绑定和以前的写死class属性是可以一起使用的,写死属性可保证属性一定会有个定值,动态绑定的可随着数据变化的值
<h2 class="title" :class="{active: isActive, negative: isNegative}">{{msg}}</h2>
若是觉得对象语法里面太多太麻烦,也可将其写入Vue实例中的methods中
<h2 class="title" :class="getClasses()">{{msg}}</h2>
const app = new Vue({
el : '#app',
data : {
msg: "你好啊",
isActive: true,
isNegative: true
},
methods: {
//原本想用箭头函数写,但是箭头函数用不了this
btnclick: function(){
//使isActive值改变
this.isActive = !this.isActive;
},
getClasses: function(){
return {active: this.isActive, negative: this.isNegative}
}
})
- v-bind 动态绑定class(数组语法)[用的少]
- 结合v-bind动态绑定class,v-for和v-on的知识完成一个简单的需求
需求:一队无序序列,初始条件下第一个序列文字颜色为红色,其他序列文字为黑色,点击某一序列时,其文字颜色变为红色,其他序列颜色均为黑色。
<style>
.active{
color: red;
}
</style>
</head>
<body>
<div id="app">
<ul>
<!--绑定key属性提高性能,响应式绑定class属性-->
<li v-for="(item,index) in movies"
:key="item"
:class="{active: isclickindex === index}"
@click="getclickindex(index)">{{item}}</li>
</ul>
</div>
</body>
<script>
const app = new Vue({
el: '#app',
data: {
movies: ["大闹天宫","哪吒","姜牙子"],
isclickindex: 0
},
methods: {
//将点击的li的index赋值给isclickindex
getclickindex(index) {
this.isclickindex = index;
}
}
})
</script>
- v-bind 动态绑定 style(对象语法)
跟上属性以及属性名即可
<!-- 必须要加单引号,不加的话20px会被当成一个变量来解析 -->
<h2 :style="{font-size: '20px'}">{{m}}</h2>
- v-bind 动态绑定 style(数组语法)
数组里面是data里的变量也可以是methods里得到方法
<h2 :style="[textStyle,backStyle]">{{m}}</h2>
五、计算属性
1. 基本使用
计算属性基本概念
计算属性是要写在实例 Vue 的 computed 属性中的,computed 和 methods 属性里都是写的函数,但是 computed 属性中写的是属性函数,methods 属性中写的是方法函数。
<body>
<div id="app">
{{fullName}}
</div>
</body>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: "yuan",
lastName: "fajun"
},
computed: {
fullName: function(){
return this.firstName + " " + this.lastName;
}
},
methods: {
}
})
</script>
fullName 是不用加小括号的,因为它虽然在 computed 里是用定义函数的方式定义的,但是其本质上还是一个属性,又因为它有像函数那样的计算性质,所以才称为计算属性。
2. 计算属性的复杂操作
前面的例子只是简单的字符串合并,体现不了计算属性得到便利之处,但是遇到复杂操作时就可以体现处计算属性的好处了。
现在有一个简单需求,三本书(包含其id,name,price),计算并显示它的总价格。
<body>
<div id="app">
<h2>总价格{{totalPrice}}</h2>
</div>
</body>
<script>
const app = new Vue({
el: '#app',
data: {
books: [
{id: "001", name: "深入理解计算机操作原理", price: 92},
{id: "002", name: "深入浅出的javascript", price: 88},
{id: "003", name: "javascript高级程序与设计", price: 103}
]
},
computed: {
totalPrice: function(){
let result = 0;
for(let i in this.books){
result += this.books[i].price;
}
return result;
}
},
})
</script>
若是不用计算属性,那写在html中的代码肯定是又长又臭的
3. 计算属性的setter 和 getter
其实上面的计算属性的写法只是一个简写,计算属性是一个属性,其内置了 setter 和 getter 方法
computed: {
set: function(){
}
get: function(){
}
}
不过我们基本上只用get方法,所以计算属性一般是只可读的,一般不修改其值,所以一般都写上面的简写方式。
4. 计算属性(computed)和 methods的对比
- 代码语义的区别
例如上面的例子,最后想展示的是书的总价格。那相比之下,肯定是一个不带小括号的属性比带小括号的方法要更合适一些,代码可读性更强 - 性能的区别
若是多次显示书的总价格,computed 里的属性函数一般只调用一次(若属性函数里的变量改变,则会重复调用),methods 里的方法方法函数调用次数和显示次数相同。那么显而易见计算属性(computed)的性能会比 methods 的性能好很多。
六、事件监听
1. v-on 的基本使用
在上面的 第三个案例 已经提到过。
2. v-on 的参数传递问题
2.1 事件调用的方法不需要传入参数
此时可以省略方法的小括号()
<button @click="btnclick">+</button>
2.2 事件调用的方法是需要传入参数的
此时按理来说不应该省略小括号()并且要传入所需的参数
<button @click="btnclick(event)">+</button>
但如果没有传入参数,只是写了个小括号()
<button @click="btnclick()">+</button>
那方法中的参数所对应的值会成为undefined
甚至如果连小括号()都省略了,那Vue会默认将浏览器生成的event对象(event对象是浏览器在完成例如点击,拖拽这样的事件时自身产生的一个对象,记录了一些数据)作为参数传入方法当中
<body>
<div id="app">
<button @click="btnclick">+</button>
</div>
</body>
<script>
const app = new Vue({
el: '#app',
data: {
},
methods: {
btnclick(event){
console.log(event);
}
}
})
</script>
2.3 事件调用的方法需要传入event对象,又需要传入其他参数
(只粘贴了改动部分的代码)
想使用浏览器定义的event对象,那就要用$event引用
<button @click="btnclick($event,123)">+</button>
methods: {
btnclick(event,abc){
console.log(event,abc);
}
}
3. v-on的常用修饰符使用
- .stop修饰符
当我们点击一个div里的button,那么会发生事件冒泡,相当于点击button后又点击了div,.stop就是阻止事件冒泡
<body>
<div id="app" @click="divclick">
aaaaaaa<button @click="btnclick">+</button>
</div>
</body>
<script>
const app = new Vue({
el: '#app',
data: {
},
methods: {
btnclick(){
console.log("button被点击");
},
divclick(){
console.log("div被点击");
}
}
})
</script>
点击button后发生事件冒泡
<div id="app" @click="divclick">
aaaaaaa<button @click.stop="btnclick">+</button>
</div>
阻止了事件冒泡
- prevent
阻止默认行为。
<form action="baidu">
<input type="submit" value="提交">
</form>
上面是个表单,点击提交按钮会提交到baidu(当然没有baidu,会提交失败,但还是会跳转页面)。如果我们不想点击后跳转页面,想让它执行自己的操作呢,那就要用.prevent操作符了。
<form action="baidu">
<input type="submit" value="提交" @click.prevent="submitclick">
</form>
七、条件渲染
1. v-if的使用
v-if后跟一个布尔值,true正常渲染,false不渲染即一片空白
<body>
<div id="app">
<h2 v-if="isshow">{{msg}}</h2>
</div>
</body>
<script>
const app = new Vue({
el: '#app',
data: {
msg: "isshow为true时显示我!",
isshow: true
},
})
2.v-if 和 v-else 的混合使用
v-if 后跟的值为 false 时,显示 v-else 所在的元素,跟原生js里的if else语句差不多
<div id="app">
<h2 v-if="isshow">{{msg}}</h2>
<h2 v-else>isshow为false时显示我!</h2>
</div>
3.v-if, v-else-if, v-else的混合使用
score>=90时表达式,得出来的结果也是个布尔值
不过当条件判断很多时不建议以上代码的操作,更建议创建一个计算属性(computed),在计算属性里面完成相关的if else条件判断。
4. v-show
决定一个元素是否显示,
v-show 和 v-if 都决定一个元素是否会在页面上显示,但是它们还是有很大的区别的。
v-if 和 v-show 的区别:
- v-if
其后跟的值为 false 时,包含这个指令的元素会在DOM中被删除,DOM中就没有这个元素了。由 false 变为 true 时,会重新在 DOM 中增加这个元素。 - v-show
其后跟的值为 false 时,包含这个指令的元素会增加一个内联样式 style=“display:none”,但是这个元素还是存在于 DOM 中。
v-if 和 v-show 的选择
- v-if
当元素需要在显示和隐藏中只切换一次时就选择 v-if 。在实际开发中,大多数情况下是从服务器获取数据从而决定是否显示渲染部分元素,所以 v-if 用的很多。 - v-show
当元素需要在显示和隐藏中切换频繁时选择 v-show,因为频繁切换的条件下其性能高一些。
八、循环遍历
1. 遍历数组
<!-- item只是一个变量而已,可随意更改 -->
<ul>
<!--没有使用索引值(下标)-->
<li v-for="item in movies">{{item}}</li>
<!--使用索引值(下标)-->
<li v-for="(item,index) in movies">{{index}}-{{item}}</li>
</ul>
2. 遍历对象
<!-- item只是一个变量而已,可随意更改 -->
<ul>
<!-- 只是获取属性值(value)-->
<li v-for="value in man">{{value}}</li>
<!-- 获取属性值和属性名(name)
注意,属性值因为比属性名更重要,所以一定放在前面!
-->
<li v-for="(value,name) in man">{{name}}-{{value}}</li>
<!-- 获取属性值,属性名和索引值(index)
索引值一定要放在最后,不过对象获取索引值的很少
-->
<li v-for="(value,name,index) in man">{{name}}-{{value}}-{{index}}</li>
</ul>
3. v-for 绑定和非绑定 key 的区别
<ul>
<li v-for="item in movies" :key="item">{{item}}</li>
</ul>
- v-for 绑定key的值一定要和显示的值一样,且这个值是一一对应的,不会轻易改变
- 在用 v-for 遍历时尽量绑定一个 key ,这样是为了实例 Vue 内部的性能更高
- 具体解释可见 这个视频
4. 数组中能做到响应式的刷新页面的方法
响应式指改变数据时,页面同时能够刷新。
九、图书购物车小案例
需求:实现下面的这种图书购物车,点击移除按钮可将其对应的书移除,所有的书移除完以后,整个界面不再显示,显示一段话:“购物车为空”。
- 过滤器
下面实例要用到的一个新知识点
(不完善)大致语法:
{{要过滤的元素 | 过滤器的名字}},过滤器是写在Vue实例的filters属性里面的
<td>{{item.price | showPrise}}</td>
filters: {
showPrise(price) {
//保留小数点后两位
return "¥" + price.toFixed(2);
}
}
完整代码
- html
<div id="app">
<!--添加一个div用于书本全部移除后页面的刷新-->
<div v-if="books.length > 0">
<table>
<thead>
<th> </th><th>书籍名称</th><th>出版日期</th><th>价格</th><th>购买数量</th><th>操作</th>
</thead>
<tbody>
<tr v-for="(item, index) in books">
<td>{{item.num}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<!--过滤器过滤-->
<td>{{item.price | showPrice}}</td>
<td>
<!--考虑到实际,买书数量必须大于等于1,所以要用button的disabled属性-->
<button @click="decrement(index)" :disabled="books[index].buycount <= 1">-</button>
{{item.buycount}}
<button @click="increment(index)">+</button>
</td>
<td><button @click="removeItem(index)">移除</button></td>
</tr>
</tbody>
</table>
<!--过滤器过滤-->
<h2>总价格:{{totalprice | showPrice}}</h2>
</div>
<!--所有书都被移除后运行此代码-->
<h2 v-else>购物车已被清空!!!</h2>
</div>
- css
table{
border: 1px solid #e9e9e9;
/*将每个td/th的间隔设为0*/
border-spacing: 0;
/*选择边框的模型*/
border-collapse: collapse;
}
td,th{
border: 1px solid #e9e9e9;
text-align: left;
padding: 15px;
}
th{
background-color: #f7f7f7;
font-weight: 600px;
}
- js
const app = new Vue({
el: "#app",
data: {
books: [{
num: 1,
name: "《算法导论》",
date: "2006-9",
price: 85,
buycount: 1
},{
num: 2,
name: "《UNIX编程技术》",
date: "2006-2",
price: 59,
buycount: 1
},{
num: 3,
name: "《编程珠玑》",
date: "2008-10",
price: 39,
buycount: 1
},{
num: 4,
name: "《代码大全》",
date: "2006-3",
price: 128,
buycount: 1
}]
},
methods: {
//按钮-调用的方法
decrement(index) {
this.books[index].buycount --;
},
//按钮+调用的方法
increment(index) {
this.books[index].buycount ++;
},
//移除按钮调用的方法
removeItem(index) {
//用splice方法删除数组中的一项从而达到删除一本书的目的
this.books.splice(index,1);
}
},
computed: {
//总价格
totalprice() {
let totalprice = 0;
for(let i = 0;i < this.books.length;i++){
totalprice += this.books[i].price * this.books[i].buycount;
}
return totalprice;
}
},
filters: {
showPrice(price) {
return "¥" + price.toFixed(2);
}
}
})
反思:
1. 在给一个元素添加一个新属性时,这个属性往往会用到Vue实例中的一些数据或方法,所以是要绑定这个属性的,可总是忘记绑定这个属性,这一点要谨记。
2. 计算属性中totalprice()属性中的for循环还有更简便的写法。
for循环里其实就是想拿到数组每一项的所需的属性而已,那就可以用for in / for of
原代码
for(let i = 0;i < this.books.length;i++){
totalprice += this.books[i].price * this.books[i].buycount;
}
for in (与一般的for循环相比,其写法更简单,没有多大改变,也是拿到索引值)
for(let i in books){
totalprice += this.books[i].price * this.books[i].buycount;
}
for of(它拿到的直接是数组的一项)
for(let book of books){
//这里的book是循环里的变量,所以不需要this
totalprice += book.price * book.buycount;
}
以后可以多用for in / for of
十、表单绑定
1. v-model 基本用法
<div id="app">
<input type="text" v-model="msg">
<h2>{{msg}}</h2>
</div>
const app = new Vue({
el: "#app",
data: {
msg: "你好啊"
},
)}
input 是和 msg 双向绑定的,具体来说应该是 input 的 value 和 msg 是双向绑定的,因为这两个数据一个改变另一个才会改变。
2. v-model 实现原理
v-model 实现了双向绑定。即表单输入元素和data内数据的实时更新,改变其中一个另一个都会改变。
(input事件是用户在input输入框内输入数据所发生的事件)
($event.target.value 是调用 event 对象下 target 属性的 value 属性,event 对象是发生一些事件时浏览器自动生成的一个对象,和该事件有关的数据都会保存在这个对象里面。)
3. v-model 和 radio 单选框的结合使用
name属性保证了两个单选框得到互斥,即选了一个就不能选另一个
name属性的值必须要一样,这两个单选框才能互斥
不过这里的v-model绑定得到数据一样的话也可以使两个单选框互斥,所以可省略name
<div id="app">
<label for="man">
<!--name属性保证了两个单选框得到互斥,即选了一个就不能选另一个
name属性的值必须要一样,这两个单选框才能互斥
不过这里的v-model绑定得到数据一样的话也可以使两个单选框互斥,所以可省略name
-->
<input type="radio" id="man" name="sex" v-model="sex" value="男"> 男
</label>
<label for="woman">
<input type="radio" id="woman" name="sex" v-model="sex" value="女"> 女
</label>
<h2>您选择的性别是:{{sex}}</h2>
</div>
const app = new Vue({
el: "#app",
data: {
sex: "男"
},
methods: {
},
computed: {
}
})
4. v-model 和 checkbox(复选框) 的结合使用
- checkbox中的单选框
场景:用户注册时需要同意协议,这个就是checkbox的单选框,用户不同意不能点击下一步,同意后才能点击下一步
<div id="app">
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议<br>
<button :disabled="!isAgree">下一步</button>
</label>
</div>
const app = new Vue({
el: "#app",
data: {
isAgree: false
},
methods: {
},
computed: {
}
})
- checkbox中的多选框
场景:叫你勾选你的爱好什么的。。。
<div id="app">
<h2>请选择您喜欢的小说:</h2>
<label for="zhetian">
<input type="checkbox" name="zhetian" value="遮天" v-model="hobbies">遮天
</label>
<label for="xiaohua">
<input type="checkbox" name="xiaohua" value="校花的贴身高手" v-model="hobbies">校花的贴身高手
</label>
<label for="doupo">
<input type="checkbox" name="doupo" value="斗破苍穹" v-model="hobbies">斗破苍穹
</label>
<label for="futianshi">
<input type="checkbox" name="futianshi" value="伏天氏" v-model="hobbies">伏天氏
</label>
<h2>您选择的小说有:{{hobbies}}</h2>
</div>
const app = new Vue({
el: "#app",
data: {
hobbies: []
},
methods: {
},
computed: {
}
})
5. 值绑定
就是使用 v-for, v-bind, {{mustache}} 这些语法给 value 动态赋值而已。
6. v-model 修饰符的使用
- lazy修饰符
默认情况下,随着用户输入数据的变化,其绑定的在data里的数据也会实时变化。
当我们不想实时变化,只想用户敲击回车或是输入框失去焦点时数据再变化,就可使用lazy修饰符。
<input type="text" v-model.lazy="msg">
- number修饰符
- trim修饰符