1.vue.js的快速入门使用
1.1 vue.js库的下载
vue.js是目前前端web开发最流行的工具库,由尤雨溪在2014年2月发布的。
另外几个常见的工具库:react.js /angular.js
官方网站:
英文:https://vuejs.org/
官方文档:https://cn.vuejs.org/v2/guide/
vue.js目前有1.x、2.x和3.x 版本,我们学习2.x版本的。
1.2 vue.js库的基本使用
在github下载:
在官网下载地址: https://cn.vuejs.org/v2/guide/installation.html
vue的引入类似于jQuery,开发中可以使用开发版本vue.js,产品上线要换成vue.min.js。
下图是github网站下载的vue.js目录
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
window.onload = function(){
// vue.js的代码开始于一个Vue对象。所以每次操作数据都要声明Vue对象开始。
var vm = new Vue({
el:'#app', // 设置当前vue对象要控制的标签范围。
data:{ // data是将要展示到HTML标签元素中的数据。
message: 'hello world!',
}
});
}
</script>
</head>
<body>
<div id="app">
<!-- {{ message }} 表示把vue对象里面data属性中的对应数据输出到页面中 -->
<!-- 在双标签中显示数据要通过{{ }}来完成 -->
<p>{{ message }}</p>
</div>
</body>
</html>
总结:
1. vue的使用要从创建Vue对象开始
var vm = new Vue();
2. 创建vue对象的时候,需要传递参数,是json对象,json对象对象必须至少有两个属性成员
var vm = new Vue({
el:"#app",
data: {
数据变量:"变量值",
数据变量:"变量值",
数据变量:"变量值",
},
});
el:设置vue可以操作的html内容范围,值一般就是css的id选择器。
data: 保存vue.js中要显示到html页面的数据。
3. vue.js要控制器的内容外围,必须先通过id来设置。
<div id="app">
<h1>{{message}}</h1>
<p>{{message}}</p>
</div>
1.3 vue.js的M-V-VM思想
MVVM 是Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式。
Model
指代的就是vue对象的data属性里面的数据。这里的数据要显示到页面中。
View
指代的就是vue中数据要显示的HTML页面,在vue中,也称之为“视图模板” 。
ViewModel
指代的是vue.js中我们编写代码时的vm对象了,它是vue.js的核心,负责连接 View 和 Model,保证视图和数据的一致性,所以前面代码中,data里面的数据被显示中p标签中就是vm对象自动完成的。
编写代码,让我们更加清晰的了解MVVM:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
window.onload = function(){
// vm视图控制对象,会实时监控,时刻保证data属性中的数据和html视图中的内容保持一致:
vm = new Vue({
el: "#app",
data:{
img: "logo.png",
num: 1,
content:"<h1>大标题</h1>",
url:"http://www.baidu.com"
},
// 事件触发时调用的方法,或者其他属性方法中调用的内部方法
methods:{
show(){
alert("hello")
}
}
});
}
// js中也有三元表达式,也叫三元运算符
// 格式:
// 条件?true:false;
</script>
</head>
<body>
<div id="app">
<input type="text" v-model="img">
{{"图片地址是:"+img}}
<span v-html="img.toLocaleUpperCase()"></span>
<img :src="img">
{{num%2==0?"偶数":"奇数"}}<br>
<p v-html="content"></p>
<p v-text="content"></p>
{{content}}<br>
<!-- <a v-bind:href="url">路飞</a>-->
<a :href="url">百度</a>
<p @click="show">内容</p>
<!-- <p v-on:click="show">内容</p>-->
</div>
</body>
</html>
在浏览器中可以在 console.log通过 vm对象可以直接访问el和data属性,甚至可以访问data里面的数据
console.log(vm.$el) # #box vm对象可以控制的范围
console.log(vm.$data); # vm对象要显示到页面中的数据
console.log(vm.$data.message); # 访问data里面的数据
console.log(vm.message);# 这个 message就是data里面声明的数据,也可以使用 vm.变量名显示其他数据,message只是举例.
总结:
1. 如果要输出data里面的数据作为普通标签的内容,需要使用{{ }}
用法:
vue对象的data属性:
data:{
name:"小明",
}
标签元素:
<h1>{{ name }}</h1>
2. 如果要输出data里面的数据作为表单元素的值,需要使用vue.js提供的元素属性v-model
用法:
vue对象的data属性:
data:{
name:"小明",
}
表单元素:
<input v-model="name">
使用v-model把data里面的数据显示到表单元素以后,一旦用户修改表单元素的值,则data里面对应数据的值也会随之发生改变,甚至,页面中凡是使用了这个数据都会发生变化。
3. 可以在普通标签中使用{{ }} 或者 v-html 来输出data里面的数据
<h1>{{message}}</h1>
4. 可以在表单标签中使用v-model属性来输出data里面的数据,同时还可以修改data里面的数据
<input type="text" v-model="username">
2. 常用指令
指令 (Directives) 是带有“v-”前缀的特殊属性。每一个指令在vue中都有固定的作用。
在vue中,提供了很多指令,常用的有:v-if、v-model、v-for等等。
指令会在vm对象的data属性的数据发生变化时,会同时改变元素中的其控制的内容或属性。
因为vue的历史版本原因,所以有一部分指令都有两种写法:
vue1.x写法 vue2.x的写法
v-html ----> {{ 普通文本 }} # vue2.x 也支持v-html,v-text,输出html代码的内容
v-bind:属性名 ----> :属性
v-on:事件名 ----> @事件名
2.1 操作属性
格式:
<标签名 :标签属性="data属性"></标签名>
<p :title="str1">{{ str1 }}</p> <!-- 也可以使用v-html显示双标签的内容,{{ }} 是简写 -->
<a :href="url2">淘宝</a>
显示wifi密码效果:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input :type="tp">
<input type="button" @mousedown="down" @mouseup="up" v-model="message">
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
message:"显示密码",
tp:"password",
},
methods:{
down(){
// 在methods中的子方法里面要操作data的属性,可以使用this.属性值
this.tp="text";
this.message="隐藏密码";
},
up(){
this.tp="password";
this.message="显示密码";
}
}
})
</script>
</body>
</html>
2.2 事件绑定
有两种事件操作的写法,@事件名 和 v-on:事件名
<button @click="num+=5">按钮2</button>
总结:
1. 使用@事件名来进行事件的绑定
语法:
<h1 @click="num++">{{num}}</h1>
2. 绑定的事件的事件名,全部都是js的事件名:
@submit ---> onsubmit
@focus ---> onfocus
....
完成商城购物车中的商品增加减少数量
步骤:
- 给vue对象添加操作数据的方法
- 在标签中使用指令调用操作数据的方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button @click="add">+</button>
<input type="text" v-model="num">
<button @click="sub">-</button>
单价:{{price}}
<!-- 保留两位小数-->
<p>总计:{{total.toFixed(2)}}</p>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
num: 1,
price: 39.8,
total: 39.8,
},
methods:{
add(){
// 增加数量 转化数字相加
this.num = parseInt(this.num) + 1;
this.calc();
},
sub(){
// 减少数量
if(this.num<=1){
return;
}
this.num -= 1;
this.calc();
},
calc(){
// 计算总价
this.total = this.price * this.num;
}
}
})
</script>
</body>
</html>
2.3 操作样式
2.3.1 控制标签class类名
格式:
<h1 :class="值">元素</h1> 值可以是字符串、对象、对象名、数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.box{
100px;
height: 100px;
background-color: #ff6666;
}
.box1{
border-radius: 22px;
}
.box2{
200px;
height: 200px;
background-color: #66f;
}
</style>
</head>
<body>
<div id="app">
<div :class="cls"></div>
<div :class="[cls,cls2]"></div>
<div :class="{box1:show_box1,box2:show_box2}"></div>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
cls: "box",
cls2: "box1",
// 布尔值变量如果是true,则不会添加对象的属性名作为样式
show_box1:false,
show_box2:true,
},
})
</script>
</body>
</html>
总结:
1. 给元素绑定class类名,最常用的就是第二种。
vue对象的data数据:
data:{
myObj:{
complete:true,
uncomplete:false,
}
}
html元素:
<div class="box" :class="myObj">2222</div>
最终浏览器效果:
<div class="box complete">2222</div>
控制白天黑夜
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.bg{
300px;
height: 1000px;
}
.baitian{
background-color: white;
}
.heiye{
background-color: #666;
}
</style>
</head>
<body>
<div id="app" class="bg" :class="{baitian:is_show,heiye:is_show_2}">
<button @click="change">关灯</button>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
is_show:true,
is_show_2:false
},
methods:{
change(){
if(this.is_show==true){
this.is_show=false;
this.is_show_2=true;
}else{
this.is_show=true;
this.is_show_2=false;
}
}
}
})
</script>
</body>
</html>
2.3.2 控制标签style样式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div style="100px;height:100px;" :style="{backgroundColor:`#6f6`}"></div>
<div style="" :style="box"></div>
<div style="" :style="[box,box2]"></div>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
box:{
backgroundColor:`#6f6`,
"100px",
height:"100px",
},
box2:{
borderRadius:"50px", // borderRadius 边框圆角
}
},
})
</script>
</body>
</html>
2.3.2 实例-vue版本选项卡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#card{
500px;
height: 350px;
}
.title{
height:50px;
}
.title span{
100px;
height: 50px;
background-color:#ccc;
display: inline-block;
line-height: 50px; /* 设置行和当前元素的高度相等,就可以让文本内容上下居中 */
text-align:center;
}
.content .list{
500px;
height: 300px;
background-color: yellow;
display: none;
}
.content .active{
display: block;
}
.title .current{
background-color:yellow;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="card">
<div class="title">
<span @mouseover="num=1" :class="num==1?'current':''">国内新闻</span>
<span @mouseover="num=2" :class="num==2?'current':''">国际新闻</span>
<span @mouseover="num=3" :class="num==3?'current':''">银河新闻</span>
</div>
<div class="content">
<div class="list" :class="num==1?'active':''">国内新闻列表</div>
<div class="list" :class="num==2?'active':''">国际新闻列表</div>
<div class="list" :class="num==3?'active':''">银河新闻列表</div>
</div>
</div>
<script>
// 实现一个js的特效时:关键是找出3个数据出来
// 1. 用户操作的元素
// 2. 用户触发的事件
// 3. 事件触发以后的效果是什么?
var vm = new Vue({
el:"#card",
data:{
num:1,
}
})
</script>
</body>
</html>
思路:
当用户点击标题栏的按钮[span]时,显示对应索引下标的内容块[.list]
代码实现:
2.4 条件渲染指令
vue中提供了两个指令可以用于判断是否要显示元素,分别是v-if和v-show。
2.4.1 v-if
标签元素:
<!-- vue对象最终会把条件的结果变成布尔值 -->
<h1 v-if="ok">Yes</h1>
data数据:
data:{
ok:false // true则是显示,false是隐藏
}
2.4.2 v-else
v-else指令来表示 v-if 的“else 块”,v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。
标签元素:
<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>
data数据:
data:{
ok:false // true则是显示,false是隐藏
}
2.4.3 v-else-if
可以出现多个v-else-if语句,但是v-else-if之前必须有一个v-if开头。后面可以跟着v-else,也可以没有。
标签元素:
<h1 v-if="num==1">num的值为1</h1>
<h1 v-else-if="num==2">num的值为2</h1>
<h1 v-else>num的值是{{num}}</h1>
data数据:
data:{
num:2
}
2.4.4 v-show
用法和v-if大致一样,区别在于2点:
- v-show后面不能v-else或者v-else-if
- v-show隐藏元素时,使用的是display:none来隐藏的,而v-if是直接从HTML文档中移除元素[ DOM操作中的remove ]
标签元素:
<h1 v-show="ok">Hello!</h1>
data数据:
data:{
ok:false // true则是显示,false是隐藏
}
2.5 列表渲染指令
在vue中,可以通过v-for指令可以将一组数据渲染到页面中,数据可以是数组或者对象。
数据是数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<table border="1" width="800px">
<tr>
<th>序号</th>
<th>id</th>
<th>姓名</th>
<th>年龄</th>
</tr>
<tr v-for="v,k in student_list">
<td>{{k+1}}</td>
<td>{{v.id}}</td>
<td>{{v.name}}</td>
<td>{{v.age}}</td>
</tr>
</table>
</div>
<script>
var vm1 = new Vue({
el:"#app",
data:{
student_list:[
{"id":1,"name":"zbb","age":18},
{"id":1,"name":"zbb","age":18},
{"id":1,"name":"zbb","age":18},
{"id":1,"name":"zbb","age":18},
{"id":1,"name":"zbb","age":18},
]
},
})
</script>
</body>
</html>
数据是对象:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<ul id="app">
<!-- v是每一个value值,j是每一个键名-->
<li v-for="v, j in book">{{j}}:{{v}}</li>
</ul>
<script>
var vm1 = new Vue({
el:"#app",
data:{
book: {
// "attr":"value"
"id":11,
"title":"图书名称1",
"price":200
},
},
})
</script>
</body>
</html>
练习:
goods:[
{"name":"python入门","price":150},
{"name":"python进阶","price":100},
{"name":"python高级","price":75},
{"name":"python研究","price":60},
{"name":"python放弃","price":110},
]
# 把上面的数据采用table表格输出到页面,价格大于60的数据需要添加背景色橙色[orange]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<style>
.orange{
background: orange ;
}
</style>
<body>
<div id="app">
<table border="1" width="800px">
<tr>
<th>序号</th>
<th>课程</th>
<th>价格</th>
</tr>
<tr v-for="v,k in goods" :class="v.price>60?'orange':''">
<td>{{k+1}}</td>
<td>{{v.name}}</td>
<td>{{v.price}}</td>
</tr>
</table>
</div>
<script>
var vm1 = new Vue({
el: "#app",
data: {
goods: [
{"name": "python入门", "price": 150},
{"name": "python进阶", "price": 100},
{"name": "python高级", "price": 75},
{"name": "python研究", "price": 60},
{"name": "python放弃", "price": 110},
]
},
})
</script>
</body>
</html>
2.6 在表中添加数据
第一步:
循环表格, 编写添加对话框, 更新列表,编写删除按钮
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<style>
.win {
background: orange;
300px;
height: 100px;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
</style>
<body>
<div id="app">
<table border="1" width="800px" align="center">
<td colspan="5">
<button @click="win_show = 1">add</button>
</td>
<tr>
<th>序号</th>
<th>课程</th>
<th>价格</th>
<th>按钮</th>
</tr>
<tr v-for="v,k in goods">
<td>{{k+1}}</td>
<td>{{v.name}}</td>
<td>{{v.price}}</td>
<td>
<button>edit</button>
<button @click="del(k)">del</button>
</td>
</tr>
</table>
<div class="win" v-show=win_show>
<lable>课程:<input type="text" v-model="name"></lable>
<br>
<lable>价格:<input type="text" v-model="price"></lable>
<br>
<button @click="sava">确定</button>
<button @click="close">取消</button>
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
win_show: false,
goods: [
{"name": "python入门", "price": 150},
{"name": "python进阶", "price": 100},
{"name": "python高级", "price": 75},
{"name": "python研究", "price": 60},
{"name": "python放弃", "price": 110},
],
name: "",
price: "",
},
methods: {
sava() {
// 追加成员
this.goods.push({
"name": this.name,
"price": this.price
});
this.close();
},
close() {
// 关闭窗口
this.win_show = false;
// 清空窗口中的数据
this.title = "";
this.number = "";
this.price = ""
},
del(k){
this.goods.splice(k,1)
}
}
})
</script>
</body>
</html>
第二步,编辑按钮
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<style>
.win {
background: orange;
300px;
height: 100px;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
</style>
<body>
<div id="app">
<table border="1" width="800px" align="center">
<td colspan="5">
<button @click="win_show = 1">add</button>
</td>
<tr>
<th>序号</th>
<th>课程</th>
<th>价格</th>
<th>按钮</th>
</tr>
<tr v-for="v,k in goods">
<td>{{k+1}}</td>
<td>{{v.name}}</td>
<td>{{v.price}}</td>
<td>
<button @click="edit(k)">edit</button>
<button @click="del(k)">del</button>
</td>
</tr>
</table>
<div class="win" v-show=win_show>
<lable>课程:<input type="text" v-model="name"></lable>
<br>
<lable>价格:<input type="text" v-model="price"></lable>
<br>
<button @click="sava">确定</button>
<button @click="close">取消</button>
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
win_show: false,
goods: [
{"name": "python入门", "price": 150},
{"name": "python进阶", "price": 100},
{"name": "python高级", "price": 75},
{"name": "python研究", "price": 60},
{"name": "python放弃", "price": 110},
],
name: "",
price: "",
current: -1, //没有编辑任何的内容
},
methods: {
sava() {
if (this.current == -1) {
// 追加成员
this.goods.push({
"name": this.name,
"price": this.price
});
}else{
//编辑
this.goods[this.current].name=this.name;
this.goods[this.current].price=this.price;
}
this.close();
},
close() {
// 关闭窗口
this.win_show = false;
//重置编辑操作
this.current=-1
// 清空窗口中的数据
this.title = "";
this.number = "";
this.price = ""
},
del(k) {
this.goods.splice(k, 1)
},
edit(k){
this.current=k;
this.name=this.goods[k].name;
this.price=this.goods[k].price;
this.win_show=1;
}
}
})
</script>
</body>
</html>
3. Vue对象提供的属性功能
3.1 过滤器
过滤器,就是vue允许开发者自定义的文本格式化函数,可以使用在两个地方:输出内容和操作数据中。
定义过滤器的方式有两种。
3.1.1 使用Vue.filter()进行全局定义
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="js/filters.js"></script>
</head>
<body>
<div id="box">
<p>{{price|RMB}}</p>
</div>
<script>
/*
* // 过滤器,有两种:
// 全局过滤器, 通过Vue.filter("过滤器名称",匿名函数)
Vue.filter("RMB", function(v){
return "¥"+v;
});
*/
var vm = new Vue({
el:"#box",
data:{
price:30.5
}
})
</script>
</body>
</html>
3.1.2 在vue对象中通过filters属性局部
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="box">
<p>{{price|RMB}}</p>
</div>
<script>
var vm = new Vue({
el:"#box",
data:{
price:30.5
},
filters:{
RMB(v){
return "¥"+v;
}
}
})
</script>
</body>
</html>
3.4 计算和侦听属性
3.4.1 计算属性
我们之前学习过字符串反转,如果直接把反转的代码写在元素中,则会使得其他同事在开发时时不易发现数据被调整了,所以vue提供了一个计算属性(computed),可以让我们把调整data数据的代码存在在该属性中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="box">
<p>{{price}}</p>
<p>{{price2}}</p>
</div>
<script>
// 当我们需要针对data的数据尽心调整成另一个变量,留作后面其他地方进行运算使用时,可以使用计算属性得出一个新的变量
var vm = new Vue({
el: "#box",
data: {
price: 30.5
},
// 计算属性
computed: {
price2: function () {
return this.price.toFixed(2);
}
}
})
</script>
</body>
</html>
3.4.2 监听属性
侦听属性,可以帮助我们侦听data某个数据的变化,从而做相应的自定义操作。
侦听属性是一个对象,它的键是要监听的对象或者变量,值一般是函数,当侦听的data数据发生变化时,会自定执行的对应函数,这个函数在被调用时,vue会传入两个形参,第一个是变化前的数据值,第二个是变化后的数据值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="box">
数量:<input type="text" v-model="num">
单价:<input type="text" v-model="price">
总价:{{total}}
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
num: 0,
price: 30.5,
total: 0
},
watch: {
num(newval, oldval) {
console.log("修改后num="+newval);
console.log("修改前num="+oldval);
this.total = this.price * this.num;
},
price() {
this.total = this.price * this.num;
}
}
})
</script>
</body>
</html>
3.5 vue对象的生命周期
每个Vue对象在创建时都要经过一系列的初始化过程。在这个过程中Vue.js会自动运行一些叫做生命周期的的钩子函数,我们可以使用这些函数,在对象创建的不同阶段加上我们需要的代码,实现特定的功能。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="box">
{{num}}
<input type="text" v-model="num">
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
num: 30,
},
beforeCreate() {
// 这里的代码执行时,vm对象尚未创建,所以data中的数据是无法操作
// console.log(this.num); // undefined
},
created() {
// 这里的代码执行时,vm对象已经创建完成,但是还没有把数据和视图模板进行绑定
// console.log(this.num); // data的数据已经可以操作了。
// console.log(this.$el); // 此时还没有绑定视图
// 这里可以用于编写从后端获取数据的代码
},
beforeMount() {
// 这里的代码执行时,已经绑定了视图,但是没有更新视图中的数据
console.log(this.$el.innerHTML);
},
mounted() {
// 这里的代码执行时,已经把data中的数据替换了模板视图中对应的内容了
console.log(this.$el);
// 这里可以用于编写一些需要操作视图额初始化代码
},
beforeUpdate() {
// 更新html模板的数据之前
console.log(this.num);
console.log(this.$el.innerHTML)
},
updated() {
// 更新html模板的数据之后
console.log(this.num);
console.log(this.$el.innerHTML)
}
})
</script>
</body>
</html>
总结:
在vue使用的过程中,如果要初始化操作,把初始化操作的代码放在 mounted 中执行。
mounted阶段就是在vm对象已经把data数据实现到页面以后。一般页面初始化使用。例如,用户访问页面加载成功以后,就要执行的ajax请求。
另一个就是created,这个阶段就是在 vue对象创建以后,把ajax请求后端数据的代码放进 created
3.2 阻止事件冒泡和刷新页面
什么是事件冒泡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body onclick="alert('body')">
<div onclick="alert('div')">
<button>按钮</button>
</div>
<script>
// 事件,event,在js中表示用户和浏览器之间进行的一次交互过程
// 事件在触发时,就会有一个事件对象来记录整个事件发生的过程和发生的位置
// 从事件发生位置由内及外,根据标签之间父子嵌套关系,逐层往外传播,让父级元素触发同类事件,这种事件的传递方式,就是 事件冒泡
// 事件冒泡有好,有坏。
// 好处就是可以利用这种机制,实现事件委托
// 坏处就是当前元素的父级元素有同类事件,会随着冒泡直接全部执行
</script>
</body>
</html>
使用.stop和.prevent
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="box" @click="show2">
<!-- @click.stop来阻止事件冒泡 -->
<button @click.stop="show">按钮</button>
<!-- @click.prevent来阻止标签刷新 -->
<a href="" @click.prevent="show3">a链接</a>
</div>
<script>
var vm = new Vue({
el: "#box",
data: {},
methods: {
show() {
alert("按钮");
},
show2() {
// alert("父元素");
},
show3() {
console.log("一句话");
}
}
})
</script>
</body>
</html>
3.3 综合案例-todolist
我的计划列表
html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>todolist</title>
<style type="text/css">
.list_con{
600px;
margin:50px auto 0;
}
.inputtxt{
550px;
height:30px;
border:1px solid #ccc;
padding:0px;
text-indent:10px;
}
.inputbtn{
40px;
height:32px;
padding:0px;
border:1px solid #ccc;
}
.list{
margin:0;
padding:0;
list-style:none;
margin-top:20px;
}
.list li{
height:40px;
line-height:40px;
border-bottom:1px solid #ccc;
}
.list li span{
float:left;
}
.list li a{
float:right;
text-decoration:none;
margin:0 10px;
}
</style>
</head>
<body>
<div class="list_con">
<h2>To do list</h2>
<input type="text" name="" id="txt1" class="inputtxt">
<input type="button" name="" value="增加" id="btn1" class="inputbtn">
<ul id="list" class="list">
<!-- javascript:; # 阻止a标签跳转 -->
<li>
<span>学习html</span>
<a href="javascript:;" class="up"> ↑ </a>
<a href="javascript:;" class="down"> ↓ </a>
<a href="javascript:;" class="del">删除</a>
</li>
<li><span>学习css</span><a href="javascript:;" class="up"> ↑ </a><a href="javascript:;" class="down"> ↓ </a><a href="javascript:;" class="del">删除</a></li>
<li><span>学习javascript</span><a href="javascript:;" class="up"> ↑ </a><a href="javascript:;" class="down"> ↓ </a><a href="javascript:;" class="del">删除</a></li>
</ul>
</div>
</body>
</html>
特效实现效果:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>todolist</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style type="text/css">
.list_con {
600px;
margin: 50px auto 0;
}
.inputtxt {
550px;
height: 30px;
border: 1px solid #ccc;
padding: 0px;
text-indent: 10px;
}
.inputbtn {
40px;
height: 32px;
padding: 0px;
border: 1px solid #ccc;
}
.list {
margin: 0;
padding: 0;
list-style: none;
margin-top: 20px;
}
.list li {
height: 40px;
line-height: 40px;
border-bottom: 1px solid #ccc;
}
.list li span {
float: left;
}
.list li a {
float: right;
text-decoration: none;
margin: 0 10px;
}
</style>
</head>
<body>
<div class="list_con" id="app">
<h2>To do list</h2>
<input type="text" v-model="text" id="txt1" class="inputtxt">
<input type="button" @click="add" value="增加" id="btn1" class="inputbtn">
<ul id="list" class="list">
<!-- javascript:; # 阻止a标签跳转 -->
<li v-for="item,key in todolist">
<span>{{item}}</span>
<a href="javascript:;" @click="up(key)" class="up"> ↑ </a>
<a href="javascript:;" @click="down(key)" class="down"> ↓ </a>
<a href="javascript:;" @click="del(key)" class="del">删除</a>
</li>
</ul>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
text: "",
todolist: [
"学习html",
"学习css",
"学习javascript",
]
},
methods: {
add() {
this.todolist.push(this.text);
this.text = "";
},
del(key) {
// splice 万能函数
// 删除和替换
// 参数1: 开始下表
// 参数2: 元素长度,如果不填默认删除到最后
// 参数3: 表示使用当前参数替换已经删除内容的位置
this.todolist.splice(key, 1);
},
up(key) {
// 向上移动
// splice(删除的开始位置,删除的成员个数, 替换的新数据)
if (key === 0) {
return;
}
//就是删掉然后插入
ret = this.todolist.splice(key, 1)[0];
this.todolist.splice(key - 1, 0, ret);
},
down(key) {
// 向下移动
ret = this.todolist.splice(key, 1)[0];
this.todolist.splice(key + 1, 0, ret);
}
}
});
</script>
</body>
</html>
4. 通过axios实现数据请求
vue.js默认没有提供ajax功能的。
所以使用vue的时候,一般都会使用axios的插件来实现ajax与后端服务器的数据交互。
注意,axios本质上就是javascript的ajax封装,所以会被同源策略限制。
下载地址:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
axios提供发送请求的常用方法有两个:axios.get() 和 axios.post() 。
增 post
删 delete
改 put/patch
查 get
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>axios</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<button @click="get_data">点击</button>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {},
methods: {
get_data() {
// 发送get请求
// axios.get('http://baidu.com', {
// params: {
// //查询字符串的键值对
// username: 'zbb'
// }
// }, {
// //请求头信息
// Compant: "oldboy"
// });
// 发送post请求
axios.post("http://www.baidu.com", {
username: "xiaoming",
password: "123456",
}, {
// 请求头信息
}).then((response) => {
// 请求成功以后,axios执行的代码
// response.data 响应体数据
}).catch(function (error) {
// 请求发生异常错误时,axios执行的代码
// error 错误对象(本地)
// error.response 来自服务端的响应错误信息
});
}
}
});
// 匿名函数
// var func = function(response){
// // 函数代码
// };
//
// // 箭头函数
// var func = (response)=>{
// // 函数代码
// }
</script>
</body>
</html>
总结
// 发送get请求
// 参数1: 必填,字符串,请求的数据接口的url地址,例如请求地址:http://www.baidu.com?id=200
// 参数2:可选,json对象,要提供给数据接口的参数
// 参数3:可选,json对象,请求头信息
// 发送post请求,参数和使用和axios.get()一样。
// 参数1: 必填,字符串,请求的数据接口的url地址
// 参数2:必填,json对象,要提供给数据接口的参数,如果没有参数,则必须使用{}
// 参数3:可选,json对象,请求头信息
4.1 json
json是 JavaScript Object Notation 的首字母缩写,单词的意思是javascript对象表示法,这里说的json指的是类似于javascript对象的一种数据格式。
json的作用:在不同的系统平台,或不同编程语言之间传递数据。
4.1.1 json数据的语法
json数据对象类似于JavaScript中的对象,但是它的键对应的值里面是没有函数方法的,值可以是普通变量,不支持undefined,值还可以是数组或者json对象。
// 原生的js的json对象
var obj = {
age:10,
sex: '女',
work:function(){
return "好好学习",
}
}
// json数据的对象格式,json数据格式,是没有方法的,只有属性:
{
"name":"tom",
"age":18
}
// json数据的数组格式:
["tom",18,"programmer"]
复杂的json格式数据可以包含对象和数组的写法。
{
"name":"小明",
"age":200,
"is_delete": false,
"fav":["code","eat","swim","read"],
"son":{
"name":"小小明",
"age":100,
"lve":["code","eat"]
}
}
// 数组结构也可以作为json传输数据。
json数据可以保存在.json文件中,一般里面就只有一个json对象。
总结:
1. json文件的后缀是.json
2. json文件一般保存一个单一的json数据
3. json数据的属性不能是方法或者undefined,属性值只能:数值[整数,小数,布尔值]、字符串、json和数组
4. json数据只使用双引号、每一个属性成员之间使用逗号隔开,并且最后一个成员没有逗号。
{
"name":"小明",
"age":200,
"fav":["code","eat","swim","read"],
"son":{
"name":"小小明",
"age":100
}
}
工具:postman可以用于测试开发的数据接口。
4.1.2 js中提供的json数据转换方法
javascript提供了一个JSON对象来操作json数据的数据转换.
方法 | 参数 | 返回值 | 描述 |
---|---|---|---|
stringify | json对象 | 字符串 | json对象转成字符串 |
parse | 字符串 | json对象 | 字符串格式的json数据转成json对象 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>axios</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<script>
var data = {
"name":"xiaopmiong",
"age":13,
"sex":true
};
// 把json对象转换成json字符串
json_str = JSON.stringify(data);
console.log()
// 把json字符串装换成json对象
data_str = '{"name":"xiaopmiong","age":13,"sex":true}';
json_obj = JSON.parse(data_str);
console.log(json_obj);
</script>
</body>
</html>
4.2 ajax
ajax,一般中文称之为:"阿贾克斯",是英文 “Async Javascript And Xml”的简写,译作:异步js和xml数据传输数据。
ajax的作用: ajax可以让js代替浏览器向后端程序发送http请求,与后端通信,在用户不知道的情况下操作数据和信息,从而实现页面局部刷新数据/无刷新更新数据。
所以开发中ajax是很常用的技术,主要用于操作后端提供的数据接口
,从而实现网站的前后端分离
。
ajax技术的原理是实例化js的XMLHttpRequest对象,使用此对象提供的内置方法就可以与后端进行数据通信。
4.2.1 数据接口
数据接口,也叫api接口,表示后端提供
操作数据/功能的url地址给客户端使用。
客户端通过发起请求向服务端提供的url地址申请操作数据【操作一般:增删查改】
同时在工作中,大部分数据接口都不是手写,而是通过函数库/框架来生成。
4.2.3 ajax的使用
ajax的使用必须与服务端程序配合使用,但是目前我们先学习ajax的使用,所以暂时先不涉及到服务端python代码的编写。因此,我们可以使用别人写好的数据接口进行调用。
jQuery将ajax封装成了一个函数$.ajax(),我们可以直接用这个函数来执行ajax请求。
编写代码获取接口提供的数据:
vue版本:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>axios</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<button @click="get_data">点击</button>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {},
methods: {
get_data() {
axios.get("http://wthrcdn.etouch.cn/weather_mini?city=北京", {
}).then(response => {
// 成功
console.log(response.data);
}).catch(error => {
// 报错
console.log(error.response);
});
}
}
})
</script>
</body>
</html>
4.2.4 同源策略
同源策略,是浏览器为了保护用户信息安全的一种安全机制。所谓的同源就是指代通信的两个地址(例如服务端接口地址与浏览器客户端页面地址)之间比较,是否协议、域名(IP)和端口相同。不同源的客户端脚本[javascript]在没有明确授权的情况下,没有权限读写对方信息。
ajax本质上还是javascript,是运行在浏览器中的脚本语言,所以会被受到浏览器的同源策略所限制。
前端地址:http://www.oldboy.cn/index.html |
是否同源 | 原因 |
---|---|---|
http://www.oldboy.cn/user/login.html |
是 | 协议、域名、端口相同 |
http://www.oldboy.cn/about.html |
是 | 协议、域名、端口相同 |
https://www.oldboy.cn/user/login.html |
否 | 协议不同 ( https和http ) |
http:/www.oldboy.cn:5000/user/login.html |
否 | 端口 不同( 5000和80) |
http://bbs.oldboy.cn/user/login.html |
否 | 域名不同 ( bbs和www ) |
同源策略针对ajax的拦截,代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
<script src="js/axios.js"></script>
</head>
<body>
<div id="app">
<button @click="get_music">点击获取天气</button>
</div>
<script>
let vm = new Vue({
el:"#app",
data:{},
methods:{
get_music(){
axios.get("http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.search.catalogSug&query=我的中国心")
.then(response=>{
console.log(response);
}).catch(error=>{
console.log(error.response)
});
}
}
})
</script>
</body>
</html>
上面代码运行错误如下:
Access to XMLHttpRequest at 'http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.search.catalogSug&query=%E6%88%91%E7%9A%84%E4%B8%AD%E5%9B%BD%E5%BF%83' from origin 'http://localhost:63342' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
上面错误,关键词:Access-Control-Allow-Origin
只要出现这个关键词,就是访问受限。出现同源策略的拦截问题。
4.2.5 ajax跨域(跨源)方案之CORS
ajax跨域(跨源)方案:后端授权[CORS],jsonp,服务端代理
CORS是一个W3C标准,全称是"跨域资源共享",它允许浏览器向跨源的后端服务器发出ajax请求,从而克服了AJAX只能同源使用的限制。
实现CORS主要依靠<mark>后端服务器中响应数据中设置响应头信息Access-Control-Allow-Origin返回</mark>的。
django的视图
def post(request):
response = new Response()
response .headers["Access-Control-Allow-Origin"] = "http://localhost:63342"
return response;
// 在响应行信息里面设置以下内容:
Access-Control-Allow-Origin: ajax所在的域名地址
Access-Control-Allow-Origin: www.oldboy.cn # 表示只允许www.oldboy.cn域名的客户端的ajax跨域访问
// * 表示任意源,表示允许任意源下的客户端的ajax都可以访问当前服务端信息
Access-Control-Allow-Origin: *
总结:
0. 同源策略:浏览器的一种保护用户数据的一种安全机制。
浏览器会限制ajax不能跨源访问其他源的数据地址。
同源:判断两个通信的地址之间,是否协议,域名[IP],端口一致。
ajax: http://127.0.0.1/index.html
api数据接口: http://localhost/index
这两个是同源么?不是同源的。是否同源的判断依据不会根据电脑来判断,而是通过协议、域名、端口的字符串是否来判断。
1. ajax默认情况下会受到同源策略的影响,一旦受到影响会报错误如下:
No 'Access-Control-Allow-Origin' header is present on the requested resource
2. 解决ajax只能同源访问数据接口的方式:
1. CORS,跨域资源共享,在服务端的响应行中设置:
Access-Control-Allow-Origin: 允许访问的域名地址
2. jsonp
3. 是否服务端代理
思路:通过python来请求对应的服务器接口,获取到数据以后,
jsonp方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jsonp</title>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head>
<body>
<button>点击</button>
<script>
// function get_data(){
// // 发送请求
// // 1. 创建一个支持跨域标签的标签[script]
// var script = document.createElement("script");
// // 2. 给标签加上src属性,指向请求的地址
// script.src="http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.search.catalogSug&query=我的中国心"
// console.log(script);
// // 3. 把设置好的script标签放到head标签里面
// document.head.appendChild(script);
// // 4. 调用服务器返回的数据,提前声明一个函数,返回的数据作为参数
//
// }
// // callback(`{"song":[{"bitrate_fee":"{\"0\":\"0|0\",\"1\":\"0|0\"}","weight":"13099","songname":"我的中国心","resource_type":"0","songid":"604603771","has_mv":"0","yyr_artist":"0","resource_type_ext":"0","artistname":"CBS","info":"","resource_provider":"1","control":"0000000000","encrypted_songid":"78082409857A085D08CADC"},{"bitrate_fee":"{\"0\":\"129|-1\",\"1\":\"-1|-1\"}","weight":"4199","songname":"月亮代表我的中国心","resource_type":"0","songid":"544880401","has_mv":"0","yyr_artist":"0","resource_type_ext":"2","artistname":"大庆小芳","info":"","resource_provider":"1","control":"0000000000","encrypted_songid":"3408207A37080859645F87"}],"order":"song,album","error_code":22000,"album":[{"albumname":"我的中国心","weight":"130","artistname":"CBS","resource_type_ext":"0","artistpic":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/3b22a5976d07dbbe38b60f92ab2b6afd\/660129785\/660129785.jpg@s_2,w_40,h_40","albumid":"604603768"},{"albumname":"我的中国心","weight":"0","artistname":"陈东东","resource_type_ext":"0","artistpic":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/default_album.jpg@s_2,w_40,h_40","albumid":"611655247"}]}`)
// function callback(data){
// console.log(data);
// }
$.ajax({
url:"http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.search.catalogSug&query=我的中国心",
type:"get",
dataType:"jsonp"
}).then(response=>{
console.log(response);
});
</script>
</body>
</html>
5. 组件化开发
5.1 组件[component]
组件(Component)是自定义封装的功能。在前端开发过程中,经常出现多个网页的功能是重复的,而且很多不同的页面之间,也存在同样的功能。
而在网页中实现一个功能,需要使用html定义功能的内容结构,使用css声明功能的外观样式,还要使用js来定义功能的特效,因此就产生了把一个功能相关的[HTML、css和javascript]代码封装在一起组成一个整体的代码块封装模式,我们称之为“组件”。
所以,组件就是一个html网页中的功能,一般就是一个标签,标签中有自己的html内容结构,css样式和js特效。
这样,前端人员就可以在组件化开发时,只需要书写一次代码,随处引入即可使用。
vue的组件有两种:默认组件[全局组件] 和 单文件组件
5.1.1 默认组件(很少用)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>zbb</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="page">
<!-- 组件名aa 写好之后直接运行组件名即可-->
<aa></aa>
<aa></aa>
</div>
<script>
Vue.component("aa", {
template: "<p>公共组件<span @click='show'>{{message}}</span></p>",
// data在组件中变成了一个函数,函数的返回值必须是一个json对象
data: function () {
return {
message: "这是一本书"
}
},
methods: {
show() {
alert(this.message)
}
}
})
var vm = new Vue({
el: '#page',
data: {},
})
</script>
</body>
</html>
6. Vue自动化工具(Vue-cli)
前面学习了普通组件以后,接下来我们继续学习单文件组件则需要提前先安装准备一些组件开发工具。否则无法使用和学习单文件组件。
一般情况下,单文件组件,我们运行在 自动化工具vue-CLI中,可以帮我们编译单文件组件。所以我们需要在系统中先搭建vue-CLI工具,
Vue CLI 需要 Node.js 8.9 或更高版本 (推荐 8.11.0+)。你可以使用 nvm 或 nvm-windows在同一台电脑中管理多个 Node 版本。
nvm工具的下载和安装: https://www.jianshu.com/p/d0e0935b150a
https://www.jianshu.com/p/622ad36ee020
安装记录:
打开:https://github.com/coreybutler/nvm-windows/releases 下载然后安装
常用的nvm命令
nvm list # 列出目前在nvm里面安装的所有node版本
nvm install node版本号 # 安装指定版本的node.js
nvm uninstall node版本号 # 卸载指定版本的node.js
nvm use node版本号 # 切换当前使用的node.js版本
如果使用nvm工具,则直接可以不用自己手动下载,如果使用nvm下载安装 node的npm比较慢的时候,可以修改nvm的配置文件(在安装根目录下)
#settings.txt
root: C: ool
vm [这里的目录地址是安装nvm时自己设置的地址,要根据实际修改]
path: C: ool
odejs
arch: 64
proxy: none
node_mirror: http://npm.taobao.org/mirrors/node/
npm_mirror: https://npm.taobao.org/mirrors/npm/
6.1 安装node.js
Node.js是一个新的后端(后台)语言,它的语法和JavaScript类似,所以可以说它是属于前端的后端语言,后端语言和前端语言的区别:
- 运行环境:后端语言一般运行在服务器端,前端语言运行在客户端的浏览器上
- 功能:后端语言可以操作文件,可以读写数据库,前端语言不能操作文件,不能读写数据库。
我们一般安装LTS(长线支持版本 Long-Time Support):
下载地址:https://nodejs.org/en/download/【上面已经安装了nvm,那么这里不用手动安装了】
node.js的版本有两大分支:
官方发布的node.js版本:0.xx.xx 这种版本号就是官方发布的版本
社区发布的node.js版本:xx.xx.x 就是社区开发的版本
Node.js如果安装成功,可以查看Node.js的版本,在终端输入如下命令:
node -v
6.2 npm
在安装node.js完成后,在node.js中会同时帮我们安装一个npm包管理器npm。我们可以借助npm命令来安装node.js的包。这个工具相当于python的pip管理器。
npm install -g 包名 # 安装模块 -g表示全局安装,如果没有-g,则表示在当前项目安装
npm list # 查看当前目录下已安装的node包
npm view 包名 engines # 查看包所依赖的Node的版本
npm outdated # 检查包是否已经过时,命令会列出所有已过时的包
npm update 包名 # 更新node包
npm uninstall 包名 # 卸载node包
npm 命令 -h # 查看指定命令的帮助文档
6.3 安装Vue-cli
npm install -g vue-cli
如果安装速度过慢,一直超时,可以考虑切换npm镜像源:http://npm.taobao.org/
6.4 使用Vue-CLI初始化创建前端项目
6.4.1 生成项目目录
使用vue自动化工具可以快速搭建单页应用项目目录。
该工具为现代化的前端开发工作流提供了开箱即用的构建配置。只需几分钟即可创建并启动一个带热重载、保存时静态检查以及可用于生产环境的构建配置的项目:
#生成一个基于 webpack 模板的新项目
vue init webpack 项目目录名
例如:
vue init webpack myproject
// 启动开发服务器 ctrl+c 停止服务
cd myproject
npm run dev # 运行这个命令就可以启动node提供的测试http服务器
运行了上面代码以后,终端下会出现以下效果提示:
6.4.2 项目目录结构
src 主开发目录,要开发的单文件组件全部在这个目录下的components目录下
static 静态资源目录,所有的css,js文件放在这个文件夹
dist项目打包发布文件夹,最后要上线单文件项目文件都在这个文件夹中[后面打包项目,让项目中的vue组件经过编译变成js 代码以后,dist就出现了]
node_modules目录是node的包目录,
config是配置目录,
build是项目打包时依赖的目录
src/router 路由,后面需要我们在使用Router路由的时候,自己声明.
6.4.3 项目执行流程图
整个项目是一个主文件index.html,index.html中会引入src文件夹中的main.js,
main.js中会导入顶级单文件组件App.vue,
App.vue中会通过组件嵌套或者路由来引用components文件夹中的其他单文件组件。
7. 单文件组件的使用
组件有两种:普通组件、单文件组件
普通组件的缺点:
- html代码是作为js的字符串进行编写,所以组装和开发的时候不易理解,而且没有高亮效果。
- 普通组件用在小项目中非常合适,但是复杂的大项目中,如果把更多的组件放在html文件中,那么维护成本就会变得非常昂贵。
- 普通组件只是整合了js和html,但是css代码被剥离出去了。使用的时候的时候不好处理。
将一个组件相关的html结构,css样式,以及交互的JavaScript代码从html文件中剥离出来,合成一个文件,这种文件就是单文件组件,相当于一个组件具有了结构、表现和行为的完整功能,方便组件之间随意组合以及组件的重用,这种文件的扩展名为“.vue”,比如:"Home.vue"。
在组件中编辑三个标签,编写视图、vm对象和css样式代码。
7.1 完成案例-点击加减数字
创建Home.vue
<template>
<div>
<button @click="add">+</button>
<input type="text" v-model="num">
<button @click="sub">-</button>
</div>
</template>
<script>
export default {
name: "Home",//组件名,提供给路由,进行页面渲染
data() {
return {
num: 0,
}
},
methods: {
add() {
this.num = parseInt(this.num) + 1;
},
sub() {
if (this.num <= 1) {
return 0;
} else {
this.num -= 1;
}
}
}
}
</script>
<style scoped>
/*空间*/
input[type=text] {
400px;
}
</style>
在App.vue组件中调用上面的组件
<template>
<div id="app">
<Home></Home>
</div>
</template>
<script>
import Home from "./components/Home";
export default {
name: 'App',
components: {
Home
}
}
</script>
<style>
</style>
在开发vue项目之前,需要手动把 App.vue的HelloWorld组件代码以及默认的css样式,清除。
7.2 组件的嵌套
有时候开发vue项目时,页面也可以算是一个大组件,同时页面也可以分成多个子组件.
因为,产生了父组件调用子组件的情况.
例如,我们可以声明一个组件,作为父组件
在components/创建一个子组件,例如,是Menu.vue
<template>
<div class="menu">
<ul>
<li v-for="i in list">{{i}}</li>
</ul>
</div>
</template>
<script>
export default {
name: "Menu",
data() {
return {
list: [
"首页",
"列表",
"详情"
],
}
}
}
</script>
<style scoped>
ul{
list-style: none;
margin: 0;
padding: 0;
}
</style>
然后,在父组件中调用上面声明的子组件。
Home.vue
<template>
<div>
<Menu></Menu>
<button @click="add">+</button>
<input type="text" v-model="num">
<button @click="sub">-</button>
</div>
</template>
<script>
import Menu from "./Menu";
export default {
name: "Home",
components: {Menu},
//组件名,提供给路由,进行页面渲染
data() {
return {
num: 0,
}
},
methods: {
add() {
this.num = parseInt(this.num) + 1;
},
sub() {
if (this.num <= 1) {
return 0;
} else {
this.num -= 1;
}
}
},
comments: {
Menu
}
}
</script>
<style scoped>
/*空间*/
input[type=text] {
40px;
}
</style>
最后,父组件被App.vue调用.就可以看到页面效果.
<template>
<div id="app">
<Home></Home>
</div>
</template>
<script>
import Home from "./components/Home";
export default {
name: 'App',
components: {
Home
}
}
</script>
<style>
</style>
7.3 传递数据
父组件的数据传递给子组件
例如,我们希望把父组件的数据传递给子组件.
可以通过props属性来进行数据传递.
传递数据三个步骤:
-
在父组件中,调用子组件的组件标签时,使用属性值的方式往下传递数据
<template> <div> <Menu msg="传递的数据" :mynum="num"></Menu> <button @click="add">+</button> <input type="text" v-model="num"> <button @click="sub">-</button> </div> </template>
.上面表示在父组件调用Menu子组件的时候传递了2个数据:
.如果要传递变量,属性名左边必须加上冒号:,同时,属性名是自定义的,会在子组件中使用。
.如果要传递普通字符串数据,则不需要加上冒号: -
在子组件中接受上面父组件传递的数据,需要在vm组件对象中,使用props属性类接受。
<script> export default { name: "Menu", props:["msg","mynum"], data() { return { list: [ "首页", "列表", "详情" ], } } } </script> // 上面 props属性中表示接受了两个数据。
-
在子组件中的template中使用父组件传递过来的数据.
<template> <div class="menu"> <h1>{{msg}}</h1> <h1>{{mynum}}</h1> <ul> <li v-for="i in list">{{i}}</li> </ul> </div> </template>
使用父组件传递数据给子组件时, 注意一下几点:
-
传递数据是变量,则需要在属性左边添加冒号.
传递数据是变量,这种数据称之为"动态数据传递"
传递数据不是变量,这种数据称之为"静态数据传递"
-
父组件中修改了数据,在子组件中会被同步修改,但是,子组件中的数据修改了,是不是影响到父组件中的数据.
这种情况,在开发时,也被称为"单向数据流"
子组件传递数据给父组件
-
在子组件中,通过
this.$emit()
来调用父组件中定义的事件.<template> <div class="menu"> <h1>{{msg}}</h1> <h1>{{mynum}}</h1> <input type="text" v-model="mynum"> <ul> <li v-for="i in list">{{i}}</li> </ul> </div> </template> <script> export default { name: "Menu", props: ["msg", "mynum"], data() { return { list: [ "首页", "列表", "详情" ], } }, //这里传值 watch: { mynum() { this.$emit("zz",this.mynum) } } } </script> <style scoped> ul { list-style: none; margin: 0; padding: 0; } </style>
-
父组件中声明一个和子组件中
this.$emit("自定义事件名称")
对应的事件属性。<template> <div> <Menu msg="传递的数据" :mynum="num" @zz="zz"></Menu> <button @click="add">+</button> <input type="text" v-model="num"> <button @click="sub">-</button> </div> </template>
-
父组件中,声明一个自定义方法,在事件被调用时,执行的。
<script> import Menu from "./Menu"; export default { name: "Home", components: {Menu}, //组件名,提供给路由,进行页面渲染 data() { return { num: 0, } }, methods: { add() { this.num = parseInt(this.num) + 1; }, sub() { if (this.num <= 1) { return 0; } else { this.num -= 1; } }, zz(data) { console.log("父组件绑定的方法"); this.num=data } }, comments: { Menu } } </script>
<style scoped> /*空间*/ input[type=text] { 40px; } </style>
8. 在组件中使用axios获取数据
默认情况下,我们的项目中并没有对axios包的支持,所以我们需要下载安装。
在项目根目录中使用 npm安装包
npm install axios
接着在main.js文件中,导入axios并把axios对象 挂载到vue属性中作为一个子对象,这样我们才能在组件中使用。
import Vue from 'vue' // 这里表示从别的目录下导入 单文件组件
import App from './App' // 从node_modules目录中导入包
import axios from 'axios' // 把对象挂载vue中
Vue.config.productionTip = false
Vue.prototype.$axios = axios; // 把对象挂载vue中
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})
8.1 在组建中使用axios获取数据
<script>
export default{
methods:{
// 使用axios请求数据
get_w() {
this.$axios.get("http://wthrcdn.etouch.cn/weather_mini?city=青岛").then((response) => {
console.log(response.data);
})
}
},
</script>
效果:
使用的时候,因为本质上来说,我们还是原来的axios,所以也会收到同源策略的影响。
9.查询未来5天的天气
单文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="city" placeholder="请填写查询的城市" id="">
<button @click="get_weather">查看天气</button>
<table border="1" width="600">
<tr>
<td colspan="4">今天天气情况: {{weather_info}}</td>
</tr>
<tr>
<th>日期</th>
<th>风力</th>
<th>温度</th>
<th>状况</th>
</tr>
<tr v-for="item in date_list">
<td>{{item.date}}</td>
<td>{{item.fengli|sub_cdata}}</td>
<td>{{item.low}}~{{item.high}}</td>
<td>{{item.type}}</td>
</tr>
</table>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
city: "",
weather_info:"",
date_list:[],
},
filters:{
sub_cdata(data){
data = data.replace("<![CDATA[","");
data = data.replace("]]>","");
return data;
}
},
methods:{
get_weather(){
axios.get("http://wthrcdn.etouch.cn/weather_mini",{
params:{
city:this.city,
}
}).then(response=>{
console.log(response.data);
this.weather_info = response.data.data.ganmao;
this.date_list = response.data.data.forecast;
}).catch(error=>{
console.log(error);
})
}
}
})
</script>
</body>
</html>
组件
App.vue
<template>
<div id="app">
<Tiqian></Tiqian>
</div>
</template>
<script>
import Tiqian from "./components/Tiqian";
export default {
name: 'App',
components: {
Tiqian
}
}
</script>
<style>
</style>
Tiqian
<template>
<div>
<input type="text" v-model="city" placeholder="请填写查询的城市" id="">
<button @click="get_weather">查看天气</button>
<table border="1" width="600">
<tr>
<td colspan="4">今天天气情况: {{weather_info}}</td>
</tr>
<tr>
<th>日期</th>
<th>风力</th>
<th>温度</th>
<th>状况</th>
</tr>
<tr v-for="item in date_list">
<td>{{item.date}}</td>
<td>{{item.fengli|sub_cdata}}</td>
<td>{{item.low}}~{{item.high}}</td>
<td>{{item.type}}</td>
</tr>
</table>
</div>
</template>
<script>
export default {
name: "tiqian",
data() {
return {
city: "",
weather_info: "",
date_list: [],
}
},
filters: {
sub_cdata(data) {
data = data.replace("<![CDATA[", "");
data = data.replace("]]>", "");
return data;
}
},
methods: {
get_weather() {
this.$axios.get("http://wthrcdn.etouch.cn/weather_mini", {
params: {
city: this.city,
}
}).then(response => {
console.log(response.data);
this.weather_info = response.data.data.ganmao;
this.date_list = response.data.data.forecast;
}).catch(error => {
console.log(error);
})
}
}
}
</script>;
<style scoped>
</style>
1. 项目分析
首页
导航、登录注册栏、轮播图、底部导航
2. 项目搭建
2.1 创建项目目录
cd 项目目录
vue init webpack luffycity
除了code test,test2其他都选择
接下来,我们根据终端上效果显示的对应地址来访问项目(如果有多个vue项目在运行,8080端口被占据了,服务器会自动改端口,所以根据自己实际在操作中看到的地址来访问。)
2.2 初始化项目
清除默认的HelloWorld.vue组件和APP.vue中的默认模板代码和默认样式
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
</style>
2.3.2 配置路由
2.3.2.1 初始化路由对象
在src目录下的router目录下修改index.js路由文件
index.js路由文件中,编写初始化路由对象的代码 .
import Vue from 'vue'
import Router from 'vue-router'
// import HelloWorld from '@/components/HelloWorld'
// 这里导入可以让让用户访问的组件
Vue.use(Router)
export default new Router({
// 设置路由模式为‘history’,去掉默认的#
mode: "history",
routes: [
{
// path: '/',
// name: 'HelloWorld' 路由别名
// component: HelloWorld 组件类名
}
]
})
接下来,我们可以查看效果了,一张白纸~
3. 引入ElementUI
对于前端页面布局,我们可以使用一些开源的UI框架来配合开发,Vue开发前端项目中,比较常用的就是ElementUI了。
ElementUI是饿了么团队开发的一个UI组件框架,这个框架提前帮我们提供了很多已经写好的通用模块,我们可以在Vue项目中引入来使用,这个框架的使用类似于我们前面学习的bootstrap框架,也就是说,我们完全可以把官方文档中的组件代码拿来就用,有定制性的内容,可以直接通过样式进行覆盖修改就可以了。
https://element.eleme.cn/
3.1 快速安装ElementUI
项目根目录执行以下命令:
npm i element-ui -S
上面的命令等同于 npm install element-ui --save
3.2 配置ElementUI到项目中
在main.js中导入ElementUI,并调用。代码:
// 导入 elementUI的组件库
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// 调用插件
Vue.use(ElementUI);
或者在index中添加
在index.html入口文件中,加载样式库,代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- i mport CSS -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>
<body>
<div id="app">
<el-button @click="visible = true">Button</el-button>
<el-dialog :visible.sync="visible" title="Hello world">
<p>Try Element</p>
</el-dialog>
</div>
</body>
<!-- import Vue before Element -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- import JavaScript -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script>
new Vue({
el: '#app',
data: function() {
return { visible: false }
}
})
</script>
</html>
成功引入了ElementUI以后,接下来我们就可以开始进入前端页面开发,首先是首页。
4. 首页
首页采用了上下页面布局,首页是导航栏、轮播图。。。脚部等几个小模块。所以我们可以把首页作为一个组件进行开发,然后把首页的这些小模块作为单独的组件来进行开发。
4.1 创建首页组件
在src/components目录下创建文件 Home.vue
代码:
<template>
<div class="home">首页</div>
</template>
<script>
export default {
name: "Home"
}
</script>
<style scoped>
</style>
4.1.1 创建首页对应的路由
在router/index.js中引入Home组件,并设置Home组件作为首页路由。
代码:
import Vue from 'vue'
import Router from 'vue-router'
import Home from "../components/Home";
// 这里导入可以让让用户访问的组件
Vue.use(Router)
export default new Router({
// 设置路由模式为‘history’,去掉默认的#
mode: "history",
routes: [
{
path: '/',
name: 'Home' ,
component: Home
}
]
})
4.2 开发头部子组件
经过前面的观察,可以发现导航不仅在首页出现,其他页面也有,所以对于这些不同页面中公共的内容,可以创建一个单独的组件目录存放。
创建Header.vue目录路径,编写代码:
<template>
<div>头部</div>
</template>
<script>
export default {
name: "Header"
}
</script>
<style scoped>
</style>
4.2.1 在首页引入导航组件
代码:home.vue
<template>
<div class="home">首页
<Header></Header>
</div>
</template>
<script>
import Header from "./common/Header";
export default {
name: "Home",
data() {
return {};
},
components: {
Header
}
}
</script>
<style scoped>
</style>
接下来,我们就可以在组件中参考ElementUI文档来进行样式开发了。
初始化样式
@charset "utf-8";
/* 声明全局样式和项目的初始化样式 */
body,h1,h2,h3,h4,p,table,tr,td,ul,li,a,form,input,select,option,textarea{
margin:0;
padding: 0;
font-size: 15px;
}
a{
text-decoration: none;
color: #333;
}
ul,li{
list-style: none;
}
table{
border-collapse: collapse; /* 合并边框 */
}
img{
max- 100%;
max-height: 100%;
}
/* 工具的全局样式 */
.full-left{
float: left!important;
}
.full-right{
float: right!important;
}
[class*=" el-icon-"], [class^=el-icon-]{
font-size: 50px;
}
.el-carousel__arrow{
80px;
height: 80px;
}
.el-checkbox__input.is-checked .el-checkbox__inner,
.el-checkbox__input.is-indeterminate .el-checkbox__inner{
background: #ffc210;
border-color: #ffc210;
border: none;
}
.el-checkbox__inner:hover{
border-color: #9b9b9b;
}
.el-checkbox__inner{
16px;
height: 16px;
border: 1px solid #9b9b9b;
border-radius: 0;
}
.el-checkbox__inner::after{
height: 9px;
5px;
}
在main.js中引入 初始化css
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from "./router/index"
Vue.config.productionTip = false;
// elementUI 导入
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// 调用插件
Vue.use(ElementUI);
// 加载初始化样式
import "../static/css/reset.css";
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
Header的子组件代码:
<template>
<div class="header-box">
<div class="header">
<el-row>
<el-col :span="4" class="logo">
<a href=""><img src="/static/images/logo.svg" alt=""></a>
</el-col>
<el-col :span="15">
<el-menu class="nav" mode="horizontal">
<el-menu-item>免费课</el-menu-item>
<el-menu-item>实战课</el-menu-item>
<el-menu-item>老男孩教育</el-menu-item>
</el-menu>
</el-col>
<el-col :span="5" class="header-right">
<a href="" class="cart">
<img src="/static/images/cart.svg" alt="">
购物车
</a>
<div class="loginbar">
<a href="" class="login">登录</a>
|
<a href="" class="register">注册</a>
</div>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
export default {
name:"Header",
data(){
return {
}
}
}
</script>
<style scoped>
.header-box{
height: 80px;
}
.header{
box-shadow: 0 0.5px 0.5px 0 #c9c9c9;
position: fixed;
top: 0;
left: 0;
right: 0;
margin: auto;
z-index: 99;
background: #fff;
}
.logo{
height: 80px;
line-height: 80px;
}
.logo a{
display: block;
overflow: hidden;
height: 80px;
line-height: 80px;
text-align: center;
}
.logo a img{
vertical-align: middle;
}
.nav{
height: 80px;
}
.el-menu--horizontal>.el-menu-item{
height: 80px;
line-height: 80px;
}
.header .nav li{
color: #4a4a4a;
}
.header .nav li:hover{
color: #000;
}
.header .header-right{
height: 80px;
line-height: 80px;
}
.header .header-right .cart{
float:left;
margin-right: 20px;
height: 28px;
88px;
margin-top: 26px;
line-height: 28px;
}
.header .header-right .cart:hover{
background: #f0f0f0;
border-radius: 17px;
}
.header .header-right .cart img{
15px;
margin-right: 4px;
margin-left: 6px;
}
.header .header-right .loginbar{
float: left;
}
.header .header-right .loginbar a:hover{
color: #000;
}
</style>
4.3 开发轮播图子组件
4.3.1 创建Banner.vue组件文件
代码:
<template>
<div class="banner">
</div>
</template>
<script>
export default {
name:"Banner",
data(){
return {};
}
}
</script>
<style scoped>
</style>
4.3.1 在Home组件中引入Banner子组件
<template>
<div class="home">
<Header/>
<Banner/>
</div>
</template>
<script>
import Header from "./common/Header"
import Banner from "./common/Banner"
export default{
name:"Home",
data(){
return {};
},
components:{
Header,
Banner,
}
}
</script>
<style scoped>
</style>
接下来,在ElementUI中有对应的轮播图[跑马灯]效果,可以直接提取过来使用。
注意,图片保存到static目录下。保存在assets目录下的图片等同于保存在static/images目录下。
对于图片的使用,如果是vue代码中直接要使用的图片,可以保存accets目录下,如果是第三方插件要使用到的图片,需要保存在static目录下。其实本质上来说,所有的图片都是保存在static目录下的,而assets目录下的内容,最终被vue解析成地址的时候,也是在static目录的.
Banner.vue组件,代码:
<template>
<div class="banner">
<el-carousel height="506px">
<el-carousel-item v-for="banner in banner_list" :key="banner">
<a :href="banner.link"><img width="100%" :src="banner.img" alt=""></a>
</el-carousel-item>
</el-carousel>
</div>
</template>
<script>
export default {
name:"Banner",
data(){
return {
banner_list:[
{link:"http://www.baidu.com",img:"/static/images/banner1.png"},
{link:"http://www.baidu.com",img:"/static/images/banner2.png"},
]
};
}
}
</script>
<style scoped>
</style>
4.5 页面脚部
4.5.1 创建脚部组件文件
代码:
<template>
<el-container>
</el-container>
</template>
<script>
export default {
name:"Footer",
data(){
return {}
}
}
</script>
<style scoped>
</style>
4.5.2 在Home组件中引入Footer组件
Home组件代码:
<template>
<div class="home">
<Header/>
<Banner/>
<Footer/>
</div>
</template>
<script>
import Header from "./common/Header"
import Banner from "./common/Banner"
import Footer from "./common/Footer"
export default{
name:"Home",
data(){
return {};
},
components:{
Header,
Banner,
Footer,
}
}
</script>
<style scoped>
</style>
4.5.3 编写脚部样式
<template>
<div class="footer">
<el-container>
<el-row>
<el-col :span="4">
<router-link to="">关于我们</router-link>
</el-col>
<el-col :span="4">
<router-link to="">联系我们</router-link>
</el-col>
<el-col :span="4">
<router-link to="">商务合作</router-link>
</el-col>
<el-col :span="4">
<router-link to="">帮助中心</router-link>
</el-col>
<el-col :span="4">
<router-link to="">意见反馈</router-link>
</el-col>
<el-col :span="4">
<router-link to="">新手指南</router-link>
</el-col>
<el-col :span="24"><p class="copyright">Copyright © sadasdsy.com版权所有 | 京ICP备17072161号-1</p></el-col>
</el-row>
</el-container>
</div>
</template>
<script>
export default {
name: "Footer",
data() {
return {}
}
}
</script>
<style scoped>
.footer {
100%;
height: 128px;
background: #25292e;
}
.footer .el-container {
1200px;
margin: auto;
}
.footer .el-row {
align-items: center;
padding: 0 200px;
padding-bottom: 15px;
100%;
margin-top: 38px;
}
.footer .el-row a {
color: #fff;
font-size: 14px;
}
.footer .el-row .copyright {
text-align: center;
color: #fff;
font-size: 14px;
}
</style>