Vue快速掌握
文章目录
Vue
1. vue简介
什么是vue?
Vue是一套用于构建用户界面的渐进式JavaScript框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,方便与第三方库或既有项目整合。
Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件
特点
-
易用
- 在有HTML CSS JavaScript的基础上,快速上手。
-
灵活
- 简单小巧的核心,渐进式技术栈,足以应付任何规模的应用。
-
性能
- 20kb min+gzip 运行大小、超快虚拟 DOM 、最省心的优化。
2. MVC和MVVM
MVC是后端分层开发概念
MVVM是前端视图层的概念,主要关注于视图层分离,也就是说,MVVM把前端的视图层,分为了三部分:Model,View,ViewModel
3.Vue基本指令
3.1 Vue基本代码
-
创建一个html
-
导入vue
vue cdn:
<script type="text/javascript" src="https://unpkg.com/vue"></script>
-
创建一个Vue实例
当我们导入包后,在浏览器内存中,就多了一个vue构造函数
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="https://unpkg.com/vue"></script> </head> <body> <!--vue中通过el绑定了该标签--> <div id="app"> <h1>{{msg}}</h1> </div> <script> var vm = new Vue({ el : '#app', // 表示,当前我们new的这个Vue实例,要控制页面上的哪个区域 #app 绑定了id为app的标签 data: { // data 属性中,存放的是el中要用到的数据 msg: 'Hello,World!' // 通过Vue提供的指令,很方便的就能把数据渲染到页面上,不再手动操作DOM元素了 } }); </script> </body> </html>
3.2 v-cloak
解决插值表达式闪烁问题,闪烁问题:在打开页面时,会显示出{{msg}}
表达式,为了提升用户体验,我们在数据还没有加载出来时,让其显示空白
首先我们要给使用了插值表达式的标签,加上v-cloak
属性
然后通过css设置具有v-cloak
属性的标签的样式[v-cloak] {display: none;}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<h1 v-cloak>{{msg}}</h1>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
msg: 'Hello,World!'
}
});
</script>
</body>
</html>
3.3 v-text
和插值表达式没有太大区别,但是还是存在一些区别
- 默认
v-text
不会具有闪烁问题,因为v-text
是属性 v-text
会覆盖标签中原本的内容,但是插值表达式只会替换自己的占位符,不会清空其他内容
<h1 v-text="msg"></h1></h2>
3.4 v-html
和v-text相似,但是v-html可以解析html标签,v-html和v-text一样,同样会覆盖掉标签中原来的内容
<div id="app">
<div v-html="msg"></div>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
msg: '<h3>i am a good boy!</h3>'
}
});
</script>
3.5 v-bind
v-bind:
是vue中提供用来绑定属性的指令
<div id="app">
<input type="button" v-bind:title="mytitle" value="按钮">
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
mytitle:"这是一个自己定义的title"
}
});
</script>
v-bind:
支持拼接,可以写合法的js表达式
<input type="button" v-bind:title="mytitle + '123'" value="按钮">
v-bind:
可以简写成一个冒号:
<input type="button" v-bind:title="mytitle" value="按钮">
3.6 v-on
Vue中提供了v-on:
事件绑定机制
<div id="app">
<input type="button" value="按钮" v-on:click="sayHi">
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
},
methods: { // 这个methods属性中定义了当前Vue实例所有可用的方法
sayHi : function () {
alert("Hi~~~");
}
}
});
</script>
v-on:
可以简写成@
<input type="button" value="按钮" @click="sayHi">
3.7 简单跑马灯效果制作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="app">
<button @click="start">开始</button>
<button @click="end" >暂停</button>
<div v-text="msg"></div>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
msg : '大风起兮云飞扬。威加海内兮归故乡。安得猛士兮守四方!',
flag : false, // 存放是否已经开启定时器的标志
intervalName:"" // 存放定时器名称
},
methods:{
start() {
var _this = this; // 解决在定时器内部this的指向问题
if (!this.flag){
this.flag=true;
this.intervalName = setInterval(function () {
var s = _this.msg.substring(0,1);
var e = _this.msg.substring(1);
_this.msg = e + s;
},100);
}
},
end(){
if (this.flag){
this.flag=false;
clearInterval(this.intervalName);
}
}
}
});
</script>
</body>
</html>
3.8 事件修饰符
事件默认会有冒泡机制
.stop 阻止冒泡
.prevent 阻止默认事件
.capture 添加事件侦听器时使用事件捕获模式
.self 只当事件在该元素本身(比如不是子元素)触发时触发回调,相当于阻止自己被冒泡
.once 事件只触发一次
<div id="app">
<!--捕获机制,从外到里执行事件,相反方向的冒泡-->
<div class="inner" @click.capture="divhandler">
<!--阻止冒泡-->
<input type="button" value="点我一下" @click.stop="btnhandler">
<!--阻止默认事件-->
<a href="https://www.baidu.com" @click.prevent="linkClick">百度</a>
<!--事件只触发一次 阻止默认行为也只会触发一次-->
<a href="https://www.baidu.com" @click.prevent.once="linkClick">百度</a>
</div>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
},
methods : {
divhandler(){
alert("点击了div");
},
btnhandler(){
alert("点击了btn");
},
linkClick(){
alert("出发了linkClick事件");
}
}
});
</script>
3.9 v-model表单元素数据双向绑定
v-model指令,可以实现表单元素和Model中的数据的双向绑定
注意:v-model只能用用在表单元素中!!
当页面中的数据修改时,data中的数据也会修改
当data中的数据修改时,页面中的数据也会修改
<div id="app">
<input type="text" v-model="msg" >
<p v-text="msg"></p>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
msg : ''
}
});
</script>
通过v-model实现一个简单的小计算器
<div id="app">
<input type="text" v-model="num1">
<select v-model="opt">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
<input type="text" v-model="num2">
<input type="button" value="=" @click="test">
<input type="text" v-model="result">
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
num1 : '',
num2 : '',
result:'',
opt:'+'
},
methods: {
test(){
// 方法一:
// switch (this.opt) {
// case '+':
// this.result = parseInt(this.num1)+parseInt(this.num2);
// break;
// case '-':
// this.result = parseInt(this.num1)-parseInt(this.num2);
// break;
// case '*':
// this.result = parseInt(this.num1)*parseInt(this.num2);
// break;
// case '/':
// this.result = parseInt(this.num1)/parseInt(this.num2);
// break;
// }
// 方法二:
var codeStr = 'parseInt(this.num1)'+this.opt+'parseInt(this.num2)';
this.result = eval(codeStr); // eval函数解析执行 尽量少用这种方式
}
}
});
</script>
3.10 通过属性绑定为元素设置class类样式
-
数组
直接传递一个数组,注意:这里的class需要使用v-bind做数据绑定
<div id="app"> <h1 :class="['red','thin']">这是一个很大的h1,大到你无法想象~</h1> </div>
-
数组中使用三元表达式
<div id="app"> <h1 :class="['red','thin',flag==true?'active':'']">这是一个很大的h1,大到你无法想象</h1> </div> <script> var vm = new Vue({ el : '#app', data : { flag: true } }); </script>
-
数组中嵌套对象
对象代替三元表达式,提高代码的可读性
<div id="app"> <h1 :class="['red','thin',{'active':flag}]">这是一个很大的h1,大到你无法想象</h1> </div> <script> var vm = new Vue({ el : '#app', data : { flag: true } }); </script>
-
直接使用对象
在为class使用v-bind绑定对象的时候,对象是属性是类名,属性值是一个标识符;对象的属性可以加引号也可以不加引号
<h1 :class="{thin:true,red:true,italic:false,active:true}">这是一个很大的h1,大到你无法想象</h1>
3.11 通过属性绑定为元素设置style行内样式
-
直接在元素上通过
:style
的形式,书写样式对象<div id="app"> <h1 :style="{color:'red','font-weight':'200'}">这是一个h1</h1> </div>
-
将样式对象,定义到data中,并直接引用到
:style
中<div id="app"> <h1 :style=styleobj>这是一个h1</h1> </div> <script> var vm = new Vue({ el:'#app', data : { styleobj : {color:'red','font-weight':'200'} } }); </script>
-
在
:style
中通过数组,引用多个data
上的样式对象<div id="app"> <h1 :style=[styleobj,styleobj2]>这是一个h1</h1> </div> <script> var vm = new Vue({ el:'#app', data : { styleobj : {color:'red','font-weight':'200'}, styleobj2: {'font-style':'italic','letter-spacing': '0.5em'} } }); </script>
3.12 v-for
-
遍历普通数组
<div id="app"> <!--item 是每一次遍历到的对象/数据 index 索引--> <p v-for="(item,index) in list" v-text="'索引值:'+index+' 每一项:'+item" ></p> </div> <script> var vm = new Vue({ el : '#app', data : { list : [1,2,3,4,5,6] } }); </script>
-
遍历数组对象
<div id="app"> <!--item 是每一次遍历到的对象/数据 index 索引--> <p v-for="(item,index) in list" v-text="'索引值:'+index+' id:'+item.id+' name:'+item.name" ></p> </div> <script> var vm = new Vue({ el : '#app', data : { list : [ {id : 1,name : 'ls'}, {id : 2,name : 'zs'}, {id : 3,name : 'ww'}, {id : 4,name : 'cj'}, ] } }); </script>
-
遍历对象
在遍历对象身上的键值对的时候,除了有val:值,key:属性名,在第三个位置还有index:索引
<div id="app"> <!--val 是属性值, key 是属性名, index 是索引--> <p v-for="(val,key,index) in user " v-text="'索引:'+index+' key: '+key+' value: '+val"></p> </div> <script> var vm = new Vue({ el : '#app', data : { user : { id:1, name:'杨幂', gender:'woman', age : '12' } } }); </script>
-
迭代数字
v-for迭代数字,i 从1开始
<div id="app"> <p v-for="i in 10" v-text="'这是第'+i+'次循环~'"></p> </div>
v-for中key的使用注意事项
在2.20+的版本里,当在组件中使用v-for时,key是必须的
在循环时,通过key来标识现在这一项的唯一身份
<div id="app">
<input type="text" placeholder="id" v-model="id">
<input type="text" placeholder="name" v-model="name">
<input type="button" value="添加" @click="add">
<!--注意:v-for 循环的时候,key属性只能使用number或string-->
<!--注意:key 在使用的时候,必须使用v-bind: 属性绑定的形式指定key 的值-->
<!--在组件中,使用v-for循环的时候,或者在一些特殊情况中,如果v-for有问题,必须在使用v-for的同时指定唯一的字符串或数字类型的 key值-->
<p v-for="(item,index) in list" :key="item.id">
<input type="checkbox">
{{item.id}}-------------{{item.name}}
</p>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
id:'',
name:'',
list : [
{id :1,name:'刘备'},
{id :2,name:'关羽'},
{id :3,name:'张飞'},
{id :4,name:'赵云'},
]
},
methods : {
add(){
var user = {id:this.id,name:this.name};
this.list.unshift(user);
this.id="";
this.name="";
}
}
});
3.13 v-for和v-show
一般来说,v-if更高的切换消耗而v-show有更高的初始渲染消耗,因此,如果需要频繁切换,v-show较好,如果在运行时条件不大可能改变v-if较好
<div id="app">
<input type="button" value="切换" @click="change">
<h2 v-if="flag">这是用v-if控制的元素</h2>
<h2 v-show="flag">这是用v-show控制的元素</h2>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
flag:true
},
methods : {
change(){
this.flag = !this.flag;
}
}
});
</script>
v-if 每次都会重新删除或创建元素
v-show 每次不会重新进行DOM的创建或删除操作,只是相当于是给标签加上了display:none
样式
4. 增删查案例
<div id="app">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">添加品牌</h3>
</div>
<div class="panel-body form-inline">
<label>
Id:
<input type="text" class="form-control" v-model="id">
</label>
<label>
Name:
<input type="text" class="form-control" v-model="name">
</label>
<input type="button" class="btn btn-primary" value="添加" @click="add">
<label>
搜索名称关键字:
<input type="text" class="form-control" @input="search" v-model="keywords">
</label>
</div>
</div>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Ctime</th>
<th>Operation</th>
</tr>
</thead>
<tbody>
<!--之前,v-for中的数据,都是直接data上的list中直接渲染过来的-->
<!--现在,我们自定义了一个search方法,同时,把搜索的关键字通过传参的形式,传递给search方法-->
<!--search方法内部,通过执行for循环,把所有符合搜索关键字的数据保存到一个新数组中,返回-->
<tr v-for="(item,index) in search(keywords)" :key="item.id">
<th v-text="item.id"></th>
<th v-text="item.name"></th>
<th v-text="item.ctime"></th>
<th><input type="button" value="删除" @click="del(item.id)" class="btn btn-primary"></th>
</tr>
</tbody>
</table>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
id:'',
name:'',
keywords:'',
list : [
{id:1,name:'奔驰',ctime:new Date()},
{id:2,name:'宝马',ctime:new Date()},
{id:3,name:'福特',ctime:new Date()},
{id:4,name:'兰博基尼',ctime:new Date()},
{id:5,name:'玛莎拉蒂',ctime:new Date()}
]
},
methods : {
add(){
this.list.push({id:this.id,name: this.name,ctime: new Date()});
this.id="";
this.name="";
},
del(id){
that = this;
this.list.forEach(function (item,index) {
if (item.id==id){
that.list.splice(index,1);
}
});
},
search(){
var keywords = this.keywords;
// 方法一:
// var nlist = [];
// this.list.forEach(function (item,index) {
// if (item.name.indexOf(keywords)!=-1){
// nlist.push(item);
// }
// })
// return nlist;
// 方法二:
return this.list.filter(function (item) {
if (item.name.includes(keywords)){
return item;
}
})
}
}
});
</script>
5. 过滤器
Vue中允许自己定义过滤器,可被用作一些常见的文本格式化,过滤器可以用在两个地方:mustachc
插值和v-bind
表达式,过滤器应该被添加在JavaScript表达式尾部,有“管道”符指示
过滤器在调用时:
<p>{{name | nameope}}</p>
{{属性 | 过滤器名称}}
5.1 全局过滤器
所谓的全局过滤器,就是所有Vue实例共享的
全局过滤器的定义:
Vue.filter('过滤器名称',function (data) {
// 对传过来的值做处理
})
过滤器中的function第一个参数已经被规定死了,永远都是过滤器管道符前面传递过来的数据,如{{name | nameope}}
,就是向nameope过滤器中传递name参数
过滤器要定义在Vue实例之前
调用过滤器时,也可以传递参数,要在定义过滤器时进行参数接收,可以传递多个参数
<div id="app">
<h1>{{msg | msgFormat('阳光') }}</h1>
</div>
<script>
// 定义一个vue的全局过滤器
Vue.filter('msgFormat',function (data,arg) {
return data.replace(/单纯/g,arg);
});
var vm = new Vue({
el : '#app',
data : {
msg : '曾经,我也是一个单纯的少年,单纯的我傻傻的问,谁是世界上最单纯的男人'
}
});
</script>
可以调用多个过滤器,多个过滤器之间用管道符 | 隔开
<div id="app">
<h1>{{msg | msgFormat('疯狂') | test }}</h1>
</div>
<script>
// 定义一个vue的全局过滤器
Vue.filter('msgFormat',function (data,arg) {
return data.replace(/单纯/g,arg);
});
Vue.filter('test',function (data) {
return data + "-------这是第二个过滤器"
})
var vm = new Vue({
el : '#app',
data : {
msg : '曾经,我也是一个单纯的少年,单纯的我傻傻的问,谁是世界上最单纯的男人'
}
});
</script>
调用多个过滤器时,第一个过滤器处理完成后,交给第二个过滤器,最后一个过滤器处理完成后,输出结果~
时间格式化:
<th>{{item.ctime | timeFormat}}</th>
// 进行时间的格式化
<script>
Vue.filter('timeFormat',function (data) {
var dt = new Date(data);
var y = dt.getFullYear();
var m = dt.getMonth()+1;
var d = dt.getDate();
var h = dt.getHours();
var mi = dt.getMinutes();
var s = dt.getSeconds();
return `${y}-${m}-${d} ${h}:${mi}:${s}`; // 模板字符串
})
</script>
5.2 定义私有(局部)过滤器
<div id="app2">
<h2>{{dt|dtFormat}}</h2>
</div>
<script>
var vm2 = new Vue({
el : '#app2',
data : {
dt : new Date()
},
methods: {
},
filters: {
dtFormat: function (data) {
var dt = new Date(data);
var y = dt.getFullYear();
var m = dt.getMonth();
var d = dt.getDate();
var h = dt.getHours();
var min = dt.getMinutes();
var s = dt.getSeconds();
return `${y}-${m}-${d} ${h}:${min}:${s}`;
}
}
});
</script>
过滤器调用的时候采取就近原则,如果私有过滤器和全局过滤器名称一致,这时候优先调用私有过滤器
补充:ES6中字符串新方法padStart,padEnd
string.prototype.padStart(maxLength,fillString="");//参数1:填充长度后总长度 参数2:用什么填充
string.prototype.padEnd(2,'0'); // 参数1:填充长度后总长度 参数2:用什么填充
6. 自定义按键修饰符
<!--当回车键抬起时执行add方法-->
<input type="text" @keyup.enter="add">
vue中提供的
.enter .up .tab
.down .delete .left
.esc(捕获删除和退格键) .right
.space
我们可以查询js里面的键盘时间对应的键盘码
<!--键盘吗113对应的键是F2 当F2键抬起时执行add方法-->
<input type="text" @keyup.113="add">
我们可以自定义全局按键修饰符
<input type="text" @keyup.f2="add">
<script>
Vue.config.keyCodes.f2=113;
</script>
这样,按键被定义后就可以直接使用了,相当于对键盘码取了一个别名,如果未定义不可以直接使用f2
7. 指令
在Vue中,所有的指令,在调用的时候都以v-
开头
7.1 自定义全局指令
在原生js中,文本框自动获取焦点
document.getElementById("search").focus();
但是在Vue中,不提倡这么做,我们可以通过自定义指令来实现
使用Vue.directive
定义全局指令,其中参数1:指令的名称,在定义的时候,指令名不与要加前缀v-
,在调用时,必须在指令名称前加v-
前缀。参数2:是一个对象,这个对象上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作。
- bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作
- inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)
- update:所有组件的VNode更新时调用,但是可能发生在其孩子的VNode更新之前,指令的值可能发生了改变,也可能没改变
- componentUpdated:所有组件的VNode及其孩子的VNode全部更新时调用
- unbind:只调用一次,指令与元素解绑时调用
在每个函数中,第一个参数永远是el,表示被绑定的那个元素,这个el参数,是一个原生的js对象
定义全局指令
<script>
// 参数1:指令的名称,在定义的时候,指令的名称前面,不需要加 v- 前缀,在调用的时候必须加上v-前缀
// 参数2:是一个对象,这个对象上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作
Vue.directive('focus',{
bind:function (el) {
// 每当指令绑定到元素上的时候,会立即执行这个bind函数,只执行一次
// 在元素刚绑定了指令的时候,还没有插入到DOM中去,这时候调用focus方法没有作用
},
inserted:function (el) {
// 元素插入到DOM中的时候会执行inserted函数 ,只触发一次
el.focus();
},
updated:function (el) {
// 当组件VNode更新的时候会执行updated 可能会触发多次
}
})
</script>
和JS行为有关的操作,最好在inserted函数中执行,防止JS行为不生效,和样式相关的操作,一般都在bind函数中执行
Vue.directive('color',{
// 样式,只要通过指令绑定给了元素,不管这个元素有没有被插入到页面中去,这个元素肯定有了一个内联的样式
//将来元素肯定会显示到页面中,这时候,浏览器的渲染引擎必然会解析样式,应用给这个元素
bind:function (el) {
el.style.color="red";
}
})
钩子函数的参数
- el:指令所绑定的元素,可以用来直接操作DOM
- binding:一个对象包含以下属性:
name
:指令名,不包括v-
前缀value
:指令的绑定值,例如:v-my-directive="1 + 1"
中,绑定值为2
oldValue
:指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用。无论值是否改变都可用expression
:字符串形式的指令表达式。例如v-my-directive="1 + 1"
中,表达式为"1 + 1"
。arg
:传给指令的参数,可选。例如v-my-directive:foo
中,参数为"foo"
modifiers
:一个包含修饰符的对象。例如:v-my-directive.foo.bar
中,修饰符对象为{ foo: true, bar: true }
vnode
:Vue 编译生成的虚拟节点oldVnode
:上一个虚拟节点,仅在update
和componentUpdated
钩子中可用
<input type="text" v-color="'green'">
Vue.directive('color',{
// 样式,只要通过指令绑定给了元素,不管这个元素有没有被插入到页面中去,这个元素肯定有了一个内联的样式
//将来元素肯定会显示到页面中,这时候,浏览器的渲染引擎必然会解析样式,应用给这个元素
bind:function (el,bindding) {
el.style.color=bindding.value; // 通过bindding参数来获取传递的参数
}
})
7.2 定义私有指令
<div id="app2">
<p v-size="'50px'" v-text="msg"></p>
</div>
<script>
var vm2 = new Vue({
el : '#app2',
data : {
msg: 'hello,world!'
},
methods: {
},
filters: {
},
directives: {
'size' : {
bind : function (el,binding) {
el.style.fontSize=binding.value;
}
}
}
});
</script>
8. Vue实例生命周期函数
什么是生命周期?
从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期!
生命周期钩子:就是生命周期事件的别名!
生命周期钩子=生命周期函数=生命周期事件
主要生命周期函数分类:
- 创建期间的生命周期函数:
- beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好data和methods属性
- created:实例已经在内存中创建OK,此时data和methods已经创建OK,此时还没有开始编译模板
- beforeMount:此时已经完成了模板编译,但还没有挂载到页面中
- mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示
- 运行期间的生命周期函数:
- beforeUpdate:状态更新之前执行此函数,此时data中的状态值是最新的,但是界面上显示的数据还是旧的,因为此时还没有开始重新渲染DOM节点
- updated:实例更新完毕之后调用此函数,此时data中的状态值和界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了
- 销毁期间的生命周期函数:
- beforeDestory:实例销毁之前调用,在这一步,实例仍然完全可用
- destroyed:Vue实例销毁后调用,调用后,Vue实例指示的所有东西都会解绑定,所有的时间监听器会被移除,所有的子实例也会被销毁
<div id="app">
<input type="button" value="改变msg" @click="msg='No'">
<h3 id="h3">{{msg}}</h3>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
msg : 'ok'
},
methods: {
show : function () {
console.log("执行了show方法~");
}
},
beforeCreate:function () {
// 这是我们遇到的第一个生命周期函数,实例被创建出来之前,执行它
// 在beforeCreate生命周期函数执行的时候,data和methods中的数据都还没有初始化
console.log(this.msg); // undefined
// this.show(); // this.show() is not a function
},
created: function () {
// 在created中,data和methods中的数据都已经被初始化好了
// 如果要调用methods中的方法,或者操作data中的数据,最早只能在该方法中
console.log(this.msg); // ok
this.show(); // 执行了show方法~
},
beforeMount :function () {
// 模板已经编译完成了,但是尚未把模板渲染到页面中
// 在beforeMount执行的时候,页面中的元素还没有真正被替换过来,只是之前写的一些模板字符串
console.log(document.getElementById("h3").innerText); // {{msg}}
},
mounted :function () {
// 内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了
// mounted 是实例创建期间的最后一个生命周期函数,当执行完mounted就表示,实例已经被完全创建好了
// 此时,如果没有其它操作的话,这个实例就静静的存在内存中
// 如果要通过某些插件操作页面上的dom节点,最早要在该方法中
console.log(document.getElementById("h3").innerText); // ok
},
beforeUpdate : function () {
// 这时候表示,我们的界面还没有被更新,但是data数据已经被更新了
// 当执行该函数的时候,页面中显示的数据还是旧的,此时data中的数据是最新的,页面尚未和最新的数据保存同步
console.log("界面上元素的内容:"+document.getElementById("h3").innerText); // 界面上元素的内容:ok
console.log("data中的msg数据:"+this.msg); // data中的msg数据:No
},
updated:function () {
// 页面和data数据已经保持同步了,都是最新的
console.log("界面上元素的内容:"+document.getElementById("h3").innerText); // 界面上元素的内容:No
console.log("data中的msg数据:"+this.msg); // data中的msg数据:No
},
beforeDestroy : function () {
// 当执行该函数时,就已经从运行阶段进入了销毁阶段
// 当执行该函数时,实例身上所有的data和所有的methods以及过滤器,指令等都处于可用状态,还没有真正执行销毁过程
},
destroyed : function () {
// 当执行到该函数时,实例已经被完全销毁了,此时实例身上所有的data和所有的methods以及过滤器,指令等都不可用了
}
});
</script>
9. vue-resource
实现get,post,jsonp请求
首先要导入vue-resource.js cdn:
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue-resource/1.5.1/vue-resource.min.js"></script>
注意:vue-resource依赖于vue,所以要先导入vue在导入vue-resource
// global Vue object
Vue.http.get('/someUrl', [config]).then(successCallback, errorCallback);
Vue.http.post('/someUrl', [body], [config]).then(successCallback, errorCallback);
// in a Vue instance
this.$http.get('/someUrl', [config]).then(successCallback, errorCallback);
this.$http.post('/someUrl', [body], [config]).then(successCallback, errorCallback);
get(url, [config])
head(url, [config])
delete(url, [config])
jsonp(url, [config])
post(url, [body], [config])
put(url, [body], [config])
patch(url, [body], [config])
Config
Parameter | Type | Description |
---|---|---|
url | string | URL to which the request is sent |
body | Object , FormData , string | Data to be sent as the request body |
headers | Object | Headers object to be sent as HTTP request headers |
params | Object | Parameters object to be sent as URL parameters |
method | string | HTTP method (e.g. GET, POST, …) |
responseType | string | Type of the response body (e.g. text, blob, json, …) |
timeout | number | Request timeout in milliseconds (0 means no timeout) |
credentials | boolean | Indicates whether or not cross-site Access-Control requests should be made using credentials |
emulateHTTP | boolean | Send PUT, PATCH and DELETE requests with a HTTP POST and set the X-HTTP-Method-Override header |
emulateJSON | boolean | Send request body as application/x-www-form-urlencoded content type |
before | function(request) | Callback function to modify the request object before it is sent |
uploadProgress | function(event) | Callback function to handle the ProgressEvent of uploads |
downloadProgress | function(event) | Callback function to handle the ProgressEvent of downloads |
Response
A request resolves to a response object with the following properties and methods:
Property | Type | Description |
---|---|---|
url | string | Response URL origin |
body | Object , Blob , string | Response body |
headers | Header | Response Headers object |
ok | boolean | HTTP status code between 200 and 299 |
status | number | HTTP status code of the response |
statusText | string | HTTP status text of the response |
Method | Type | Description |
text() | Promise | Resolves the body as string |
json() | Promise | Resolves the body as parsed JSON object |
blob() | Promise | Resolves the body as Blob object |
<script>
var vm = new Vue({
el : '#app',
data : {
},
methods : {
getInfo : function () { // 发起get请求
// 当发起get请求后,通过.then来设置成功的回调函数
this.$http.get('localhost:8080/getTest',{params:{"arg":3}})
.then(function (result) {
// 请求成功执行回调函数
console.log(result);
},function () {
// 请求失败执行回调函数
console.log("请求失败!");
})
},
postInfo:function () {
// 发起post请求
// 手动发起的post请求,默认没有表单格式,有的服务器处理不了
// 通过post方法的第三个参数, {emulateJSON:true} 设置提交的内容类型为普通表单数据
this.$http.post('localhsot:8080/postTest',{params: {"id":1}},{})
.then(function (result) {
// 请求成功
console.log(result)
},function () {
// 请求失败
console.log("请求失败!")
})
},
jsonpInfo : function () { //发起jsonp请求
this.$http.jsonp('localhost:8080/jsonpTest',{params:{"id":2}}).then(function (result) {
// 请求成功
// 返回的为字符串,不是json对象
console.log(result);
}, function () {
console.log("请求失败");
})
}
}
});
</script>
vue-rosource可以进行全局配置来设置根路径:
Vue.http.options.root="http://localhost:8080";
// 在发起请求时
// 路径会自动拼接为 http://localhost:8080/test
this.$http.get("/test").then(function(result){
...
});
全局配置emulateJSON选项
Vue.http.options.emulateJSON = true;
这样在发起POST请求时,就不用再单独设置提交的内容类型为普通表单数据了
jsonp的实现原理:
- 由于浏览器的安全性限制,不允许AJAX跨域请求(访问协议不同,域名不同,端口号不同)的数据接口,浏览器认为这种访问不安全
- 可以通过动态创建
script
标签的形式,把script
标签的src属性,指向数据接口的地址,因为script
标签不存在跨域限制,这种数据获取方式,乘坐jsonp(注意:jsonp只支持get请求)
所以,在实际的开发中,采用前后端分离,前后端部署在不同的服务器上,为了解决跨域问题,一般都会使用jsonp请求
10. axios
Axios是一个开源的可以用在浏览器和Node.js的异步通信框架,主要作用的实现AJAX异步通信
// get请求
方法里可以只写路径。如果请求失败捕获一下异常。
axios
.get('http://rap2api.taobao.org/app/mock/23080/resources/search',{
params: {
id: 5
}
})
.then(res => {
console.log('数据是:', res);
})
.catch((e) => { // 处理异常
console.log('获取数据失败');
});
// post请求
this.$axios.post('http://rap2api.taobao.org/app/mock/121145/post',{
name: '小月'
})
.then(function(res){
console.log(res);
})
.catch(function(err){
console.log(err);
});
// 一次合并发送多个请求
function getUserAccount(){
return axios.get('/user/12345');
}
function getUserPermissions(){
return axios.get('/user/12345/permissions');
}
this.$axios.all([getUserAccount(),getUserPermissions()])
.then(axios.spread(function(res1,res2){
//当这两个请求都完成的时候会触发这个函数,两个参数分别代表返回的结果
}))
首先要导入axios,通过cdn
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
var vm = new Vue({
el : '#app',
methods : {
},
data() {
return {
msg:"helloworld!",
// 请求返回参数合适,必须和json字符串一样
info : {
user : {
id :null,
name:null
}
}
}
},
mounted:function() {
that = this; // 要注意this的指向问题
axios.get('data.json').then(function (result) {
that.info = result.data.user;
})
}
});
</script>
<!--下面这种写法也是可以的 只是data中的数据的全局可见的 会造成变量污染 所以我们一般会使用上面那种方法,使用return包裹,数据中的变量只在当前组件中生效,不会影响其他组件-->
<script>
var vm = new Vue({
el : '#app',
data : {
msg: "hello",
info : {
user : {
id : null,
name : null
}
}
},
methods : {
},
mounted:function() {
that = this;
axios.get('data.json').then(function (result) {
that.info = result.data.user;
})
}
});
</script>
为什么要写renturn返回
因为不使用return包裹的数据会在项目的全局可见,会造成变量污染
使用return包裹后数据中变量只在当前组件中生效,不会影响其他组件。
参数
1、url(必写)
请求地址
2、method
请求方法,默认是get
3、baseURL(常用)
baseURL会添加到url前(url是绝对地址除外)。
4、transformRequest
transformRequest
选项允许我们在请求发送到服务器之前对请求的数据做出一些改动
该选项只适用于以下请求方式:put/post/patch
5、transformResponse
transformResponse
选项允许我们在数据传送到then/catch
方法之前对数据进行改动
6、headers(常用,如设置请求头json类型)
自定义请求头信息
7、params(常用,只有get请求设置params,其他请求需设置params,即只有get的请求参数位于url后,其他请求参数都在请求体中)
params
选项是要随请求一起发送的请求参数----一般链接在URL后面
8、data(常用)
data
选项是作为一个请求体而需要被发送的数据,该选项只适用于方法:put/post/patch
在浏览器上data只能是FormData, File, Blob格式
9、timeout(常用)
超时时间
10、withCredentials
withCredentails
选项表明了是否是跨域请求、默认是default
11、onUploadProgress
onUploadProgress
上传进度事件
12、onDownloadProgress
下载进度的事件
13、maxContentLength
相应内容的最大值
注意:axios也要跨域问题!!axios只有两种请求方式:GET和POST
11. 计算属性
可以把计算属性想象成缓存!
当其中的某个数据发生改变时,其数据才会更新!!
在computed中可以定义一些属性,这些属性叫做【计算属性】,计算属性的本质就是一个方法,只不过,我们在使用这些计算属性的时候,是把他们的名称直接当作属性来使用,并不会把计算属性当作方法来使用
注意:计算属性,在引用的时候,一定不要加()去调用,直接把他当作普通属性
注意:计算属性内部,所依赖的任何data中的数据,只要发生变化,必然会重新计算 这个 计算属性的值
注意:计算属性的求值结果,会被缓存起来,方便下次直接使用,如果,计算属性方法中,所依赖的任何数据,都没有发生过变化,则不会重新对计算属性求值
<div id="app">
<h3>currentTime1 : {{currentTime1()}}</h3>
<h3>currentTime2 : {{currentTime2}}</h3>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
msg : "hello,world!"
},
methods: {
currentTime1 : function () {
return new Date();
}
},
computed:{
// 计算属性,methods和computed方法名重名后,只会调用methods中的方法
// 计算属性中的方法通过属性来调用,不用加()
currentTime2 : function () {
this.msg; // 当msg的数据改变时,返回结果才会重新刷新
return new Date();
}
}
})
</script>
将那些不经常变化的数据,我们可以将其放入缓存中!
12. 动画
12.1 使用过度类名实现动画
Vue 提供了 transition
的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡
- 条件渲染 (使用
v-if
) - 条件展示 (使用
v-show
) - 动态组件
- 组件根节点
在进入/离开的过渡中,会有 6 个 class 切换。
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。v-enter-to
:2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter
被移除),在过渡/动画完成之后移除。v-leave
:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。v-leave-to
:2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时v-leave
被删除),在过渡/动画完成之后移除。
<!-- 自定义两组样式,来控制transition内部的元素实现动画 -->
<style>
/* v-enter【这是一个时间点】 进入之前,元素的起始状态,此时还没有开始进入 */
/* v-leave-to 【这是一个时间点】 是动画离开之后,离开的终止状态,此时,元素动画已经结束了 */
.v-enter,
.v-leave-to {
/* 透明度为0 */
opacity: 0;
transform: translate(80px);
}
/* v-enter-active 【入场动画的时间段】 */
/* v-leave-active 【离场动画的时间段】 */
.v-enter-active,
.v-leave-active {
transition: all 0.4s ease;
}
</style>
</head>
<body>
<div id="app">
<!-- 需求:点击按钮,让h3显示,再点击按钮,让h3隐藏 -->
<input type="button" value="切换" @click="flag=!flag">
<!-- 使用transition元素,把需要被动画控制的元素包裹起来 -->
<!-- transition元素是Vue官方提供的元素 -->
<transition><h3 v-if="flag">这是一个h3</h3></transition>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
flag: false
}
});
</script>
</body>
对于这些在过渡中切换的类名来说,如果你使用一个没有名字的<transition>
,则 v-
是这些类名的默认前缀。如果你使用了<transition name=me>
,那么v-enter
会替换为me-enter
12.2 使用第三方animate.css类库实现动画
Animate.css官网:https://animate.style/
animate.css cdn:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css">
<div id="app">
<input type="button" value="切换" @click="flag=!flag">
<transition enter-active-class="animate__animated animate__bounce" leave-active-class="animate__animated animate__wobble" :duration="{enter:200,leave:400}">
<h3 v-show="flag">这是一个H3</h3>
</transition>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
flag : false
}
});
</script>
12.3 钩子函数实现半场动画
半场动画:只有入场,没有离场的动画
<!--相当于动画的生命周期-->
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
>
<!-- ... -->
</transition>
// ...
methods: {
// --------
// 进入中
// --------
// 动画钩子函数的第一个参数,el 表示要执行动画的那个DOM元素
beforeEnter: function (el) {
// ...
},
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
enter: function (el, done) {
// ...
done()
},
afterEnter: function (el) {
// ...
},
enterCancelled: function (el) {
// ...
},
// --------
// 离开时
// --------
beforeLeave: function (el) {
// ...
},
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
leave: function (el, done) {
// ...
done()
},
afterLeave: function (el) {
// ...
},
// leaveCancelled 只用于 v-show 中
leaveCancelled: function (el) {
// ...
}
}
如果要实现半场动画,只需要写入场或者离场的就可以了!!
12.4 小球半场动画
<div id="app">
<input type="button" value="执行" @click="flag=!flag">
<transition v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter">
<div class="ball" v-show="flag"></div>
</transition>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
flag : false
},
methods : {
beforeEnter : function(el){
// 入场之前 , 此时动画尚未开始
// 可以在beforeEnter中设置元素开始动画之前的起始样式
// 设置小球开始动画之前的起始位置
el.style.transform = "translate(0,0)";
},
enter : function(el,done){
// 这句话没有实际的作用,但是不写出不来动画效果
// 可以任务el.offsetWidth会强制动画刷新
el.offsetWidth;
// enter表示动画开始之后的样式,这里,可以设置小球完成动画之后的结束状态
el.style.transform = "translate(150px,450px)";
el.style.transition = "all 1s ease";
// 这里的done起始就是afterEnter函数,也就是说:done是函数afterEnter的引用
// 当只用 JavaScript 过渡的时候,在 enter 和 leave 中必须使用 done 进行回调。否则,它们将被同步调用,过渡会立即完成。
done();
},
afterEnter : function(el){
// 动画完成之后,会调用该函数
this.flag = !this.flag;
}
}
})
</script>
12.5 列表动画
**在实现列表过渡的时候,如果需要过渡的元素是通过v-for循环渲染出来的,不能使用标签包裹,需要使用标签包裹 **
如果要为 v-for 循环创建的元素设置动画,必须为每一个元素设置 :key 属性
给transition-group添加appear属性,实现当页面刚展示出来的时候,入场时候的效果
通过为transiont-group设置tag属性,指定transition-group渲染为指定的元素,如果不指定tag属性,则默认渲染为span
<style>
li {
border: 1px dashed #999;
margin: 5px;
line-height: 35px;
padding-left: 5px;
font-size: 12px;
width: 100%;
}
li:hover {
background-color: #999;
transition: all 0.4s ease;
}
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateY(50px);
}
.v-enter-active,
.v-leave-active {
transition: all 1s ease;
}
/* v-move 和 v-leave-acvice 配合使用能够实现后续的元素渐渐的补齐 */
.v-move {
transition: all 0.4s ease;
}
.v-leave-active {
position: absolute;
}
</style>
</head>
<body>
<div id="app">
<div>
<label>id: <input type="text" placeholder="id" v-model="id"> </label>
<label>name: <input type="text" placeholder="name" v-model="name" @keyup.enter="add"></label>
<input type="button" value="添加" @click="add">
</div>
<!-- 在实现列表过渡的时候,如果需要过渡的元素是通过v-for循环渲染出来的,不能使用<transition>标签包裹,需要使用<transition-group>标签包裹 -->
<!-- 如果要为 v-for 循环创建的元素设置动画,必须为每一个元素设置 :key 属性 -->
<!--给transition-group添加appear属性,实现入场时候的效果-->
<!--通过为transiont-group设置tag属性,指定transition-group渲染为指定的元素,如果不指定tag属性,则默认渲染为span-->
<transition-group appear tag="ul">
<li v-for="(user,index) in list" :key="user.id" @click="del(index)">{{user.id}}------------{{user.name}}</li>
</transition-group>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
id: "",
name:"",
list : [
{id:1,name:'唐嫣'},
{id:2,name:'陈乔恩'},
{id:3,name:'刘诗诗'},
{id:4,name:'刘亦菲'},
{id:5,name:'白百合'}
]
},
methods : {
add: function(){
this.list.push({id:this.id,name:this.name});
this.id=this.name="";
},
del:function(index){
this.list.splice(index,1);
}
}
})
</script>
</body>
13. 组件
什么是组件:组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件!
组件化和模块化的不同:
- 模块化是从代码逻辑的角度进行划分,方便代码分层开发,保证每个功能模块的只能单一
- 组件化是从UI界面的角度进行划分的,前端的组件化,方便UI组件的重用
13.1 创建组件的方式
方式一: 使用Vue.extend 来创建全局的Vue组件
- 使用Vue.extend来创建全局的Vue组件 (通过template属性指定了组件要展示的HTML结构)
- 使用Vue.component(‘组件名称’,创建出来的组件模板对象)
- 如果要使用组件 直接,把组件名称以Html的形式引入到页面中即可
注意:!!在创建全局组件时,组件名称可以用驼峰命名,但是在引用组件时,要使用 - 的形式连接,大写变小写
<div id="app">
<!-- 如果要使用组件 直接,把组件名称以Html的形式引入到页面中即可 -->
<!-- 注意:!!在创建全局组件时,组件名称可以用驼峰命名,但是在引用组件时,要使用 - 的形式连接,大写变小写 -->
<my-com1></my-com1>
</div>
<script>
// 1 使用Vue.extend来创建全局的Vue组件
var com1 = Vue.extend({
template : '<h3>hello,world</h3>', // 通过template属性指定了组件要展示的HTML结构
});
// 2 使用Vue.component('组件名称',创建出来的组件模板对象)
Vue.extend('myCom1',com1);
var vm = new Vue({
el : '#app',
data : {
}
});
</script>
Vue.extend 和 Vue.extend 合并为一步
Vue.component 第一个参数:组件名称 第二个参数:Vue.extend 创建的组件,其中template属性就是组件将来要展示的html
<div id="app">
<!-- 如果要使用组件 直接,把组件名称以Html的形式引入到页面中即可 -->
<!-- 注意:!!在创建全局组件时,组件名称可以用驼峰命名,但是在引用组件时,要使用 - 的形式连接,大写变小写 -->
<my-com1></my-com1>
</div>
<script>
// Vue.component 第一个参数:组件名称 第二个参数:Vue.extend 创建的组件,其中template属性就是组件将来要展示的html
Vue.component('myCom1',Vue.extend({
template : '<h3>hello world!</h3>'
}));
var vm = new Vue({
el : '#app',
data : {
}
});
</script>
方式二:相当于对方式一的简化
<script>
Vue.component('mycom2',{
// 注意,无论是哪种方式创建出来的组件,组件的template属性指向的模板内容必须有且只能有唯一的一个根元素
template : '<h3>hello,world!</h3>'
})
var vm = new Vue({
el : '#app',
data : {
}
});
</script>
方式三:使用标签定义组件模板
在被控制的 #app 外面,使用template元素定义组件的模板结构
<!-- 在被控制的 #app 外面,使用template元素定义组件的模板结构 -->
<template id="temp">
<h2>hello,world!1</h2>
</template>
<div id="app">
<mycom></mycom>
</div>
<script>
Vue.component('mycom',{
template : '#temp'
})
var vm = new Vue({
el : '#app',
data : {
}
});
</script>
13.2 使用components定义私有组件
<template id="mycom">
<h2>hello,world!</h2>
</template>
<div id="app">
<mycom></mycom>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
},
components : {
mycom : {
template : '#mycom'
}
}
})
</script>
13.3 组件中的data
data
组件可以有自己的data数据
组件中的data和实例的data有点不一样,实例中的data可以为一个对象,但是组件中的data必须是一个方法
组件中的data除了必须为一个方法外,这个方法内部还必须返回一个对象
组件中的data数据,使用方式,和实例中的data使用方式完全一样!!
<template id="temp">
<h3 v-text="msg"></h3>
</template>
<div id="app">
<mycom></mycom>
</div>
<script>
Vue.component('mycom',{
template : '#temp',
data(){
return {
msg : 'hello,world!'
}
}
})
var vm = new Vue({
el : '#app',
data : {
}
});
</script>
组件中的data必须是一个方法,这样组件中data数据就是组件私有的了,不会影响到其他组件!
<template id="temp">
<div>
<input type="button" value="+1" @click = "increment">
<h3>{{num}}</h3>
</div>
</template>
<div id="app">
<mycom></mycom>
<hr>
<mycom></mycom>
<hr>
<mycom></mycom>
<hr>
</div>
<script>
var dataObj = {num : 0}
Vue.component('mycom',{
template : '#temp',
data(){
return dataObj
},
methods : {
increment : function(){
console.log(this.num)
this.num++;
}
}
})
var vm = new Vue({
el : '#app'
});
</script>
如果像这样,组件中的data数据就不是私有的了,多次引用同一个组件mycom,其中一个变化,其他组件中的data数据也会随之变化,所以组件中的data必须是一个方法
<template id="temp">
<div>
<input type="button" value="+1" @click = "increment">
<h3>{{num}}</h3>
</div>
</template>
<div id="app">
<mycom></mycom>
<hr>
<mycom></mycom>
<hr>
<mycom></mycom>
<hr>
</div>
<script>
Vue.component('mycom',{
template : '#temp',
data(){
return {
num : 0
}
},
methods : {
increment : function(){
console.log(this.num)
this.num++;
}
}
})
var vm = new Vue({
el : '#app'
});
</script>
13.4 组件切换
方式一:使用v-if和v-else结合flag进行切换
<template id="login">
<h1>登录</h1>
</template>
<template id="register">
<h1>注册</h1>
</template>
<div id="app">
<input type="button" value="登录" @click = "flag = true" >
<input type="button" value="注册" @click = "flag = false">
<login v-if="flag"></login>
<register v-else="flag"></register>
</div>
<script>
Vue.component('login',{
template : '#login'
})
Vue.component('register',{
template : '#register'
})
var vm = new Vue({
el : '#app',
data : {
flag : true
}
})
</script>
方式二:使用Vue提供的component元素实现组件切换
vue提供了component元素来展示对应名称的组件
<template id="login">
<h1>登录</h1>
</template>
<template id="register">
<h1>注册</h1>
</template>
<div id="app">
<!-- vue提供了component元素来展示对应名称的组件 -->
<!-- component 是一个占位符 :is 属性,可以用来指定要展示的组件的名称 -->
<input type="button" value="登录" @click = "comName = 'login'">
<input type="button" value="注册" @click = "comName = 'register'">
<component :is="comName"></component>
</div>
<script>
Vue.component('login',{
template : '#login'
})
Vue.component('register',{
template : '#register'
})
var vm = new Vue({
el : '#app',
data : {
comName : 'login'
}
})
</script>
方式三:应用切换动画和model方式
直接用<transition>
包裹<component>
组件标签,<transition>
标签有一个属性mode
,通过mode属性设置组件切换时候的方式,如out-in
是先离场,后进场
<style>
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateY(150px);
}
.v-enter-active,
.v-leave-active{
transition: all 1s ease;
}
</style>
<body>
<template id="login">
<h1>登录</h1>
</template>
<template id="register">
<h1>注册</h1>
</template>
<div id="app">
<!-- vue提供了component元素来展示对应名称的组件 -->
<!-- component 是一个占位符 :is 属性,可以用来指定要展示的组件的名称 -->
<input type="button" value="登录" @click = "comName = 'login'">
<input type="button" value="注册" @click = "comName = 'register'">
<!-- 通过mode属性设置组件切换时候的方式 out-in 先离场后进场 -->
<transition mode="out-in">
<component :is="comName"></component>
</transition>
</div>
<script>
Vue.component('login',{
template : '#login'
})
Vue.component('register',{
template : '#register'
})
var vm = new Vue({
el : '#app',
data : {
comName : 'login'
}
})
</script>
</body>
13.5 组件传值
父向子组件传值
-
子组件,默认不能够直接访问到父组件data中的数据和methods中的方法
-
父组件,可以在引用子组件的时候,通过属性绑定(v-bind)的形式,把需要传递给子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用
-
注意:组件中的所有props中的数据,都是通过父组件传递给子组件的
-
props 中的数据都是只读的,无法重新赋值,data中的数据都是可读可写的
<div id="app">
<!-- 父组件,可以在引用子组件的时候,通过属性绑定(v-bind)的形式,把需要传递给子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用 -->
<com1 :parentmsg="msg"></com1>
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
msg : '123 父组件中的数据'
},
components : {
// 子组件,默认不能够直接访问到父组件data中的数据和methods中的方法
com1 : {
template : '<h1>这是子组件---{{parentmsg}}</h1>',
// 注意:组件中的所有props中的数据,都是通过父组件传递给子组件的
// props 中的数据都是只读的,无法重新赋值,data中的数据都是可读可写的
// 把父组件传递过来的 parentmsg 属性,现在props 数组中,定义一下,这样,才能使用这个数据
props:['parentmsg'],
data(){
return {
parentmsg : 'hello'
}
}
}
}
})
</script>
父组件把方法传递给子组件,子组件通过事件调用向父组件传值
- 父组件 向子组件传递方法,使用的是事件绑定机制
v-on:
当我们自定义了一个事件属性之后,那么子组件就能通过某些方式来调用传递进去的这个方法了 - 通过
this.$emit('绑定的事件属性名')
来拿到并执行父组件的方法 - 相当于子组件调用父组件的方法,方法还是由父组件来执行
<template id="temp">
<div>
<h1>这是子组件</h1>
<input type="button" @click="myclick" value="点击一下">
</div>
</template>
<div id="app">
<!-- 父组件 向子组件传递方法,使用的是事件绑定机制 v-on: 当我们自定义了一个事件属性之后,那么子组件就能通过某些方式来调用传递进去的这个方法了 -->
<com1 @func="show"></com1>
</div>
<script>
// 定义了一个字面量类型的组件模板对象
var com1 = {
template : "#temp",
methods : {
myclick : function(){
// 当点击子组件按钮的时候 拿到父组件传递过来的方法并调用这个方法
// emit 触发
this.$emit('func',this.sonmsg,789)
}
},
data(){
return {
sonmsg : {id : 1 ,name : '唐嫣'}
}
}
}
var vm = new Vue({
el : '#app',
data : {
dataFromSon : ''
},
methods : {
show : function(arg1,arg2){
this.dataFromSon = arg1; // 把从子组件传递的数据存放到data中
console.log("调用了父组件的show方法~---------"+arg1.id+"----"+arg1.name+"--"+arg2);
}
},
components : {
com1 : com1
}
})
</script>
14. 综合小案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css">
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<style>
li {
border: 1px dashed #999;
margin: 5px;
line-height: 35px;
padding-left: 5px;
font-size: 12px;
width: 100%;
}
li:hover {
background-color: #999;
transition: all 0.4s ease;
}
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateY(50px);
}
.v-enter-active,
.v-leave-active {
transition: all 1s ease;
}
.v-move {
transition: all 1s ease;
}
.v-leave-active {
position: absolute;
}
</style>
</head>
<body>
<template id="temp">
<div>
<div class="form-group">
<label>评论人:</label>
<input type="text" class="form-control" v-model="user" v-focus>
</div>
<div class="form-group">
<label>内容:</label>
<textarea class="form-control" v-model="content"></textarea>
</div>
<div class="form-group">
<input type="button" value="发表评论" class="btn btn-primary" @click="add">
</div>
</div>
</template>
<div id="app">
<comment-box @func="add"></comment-box>
<transition-group appear tag="ul" class="list-group">
<li class="list-group-item" v-for="(item,index) in list" :key="item.id" @click="del(index)">
<span class="badge">评论人:{{item.user}}</span>
{{item.content}}
</li>
</transition-group>
</div>
<script>
var commentBox = {
template : '#temp',
data(){
return {
user : '',
content : ''
}
},
methods : {
add : function(){
var comment = {id:Date.now(),user:this.user,content:this.content};
var list = JSON.parse(localStorage.getItem('cmts') || '[]'); // 读取本地缓存 如果读去不到 则返回一个空数组
list.unshift(comment); // 向从本地缓存中读取到的数组添加
localStorage.setItem('cmts',JSON.stringify(list)); // 将添加后的新数组重新存储到本地缓存中
this.user=this.content="";
this.$emit('func');
}
},
props : []
}
Vue.directive('focus',{
inserted : function(el) {
el.focus();
}
})
var vm = new Vue({
el : '#app',
data : {
list : [
// {id:Date.now()+"1",user:'唐嫣',content:'天生我材必有用'},
// {id:Date.now()+"2",user:'陈乔恩',content:'千金散尽还复来'},
// {id:Date.now()+"3",user:'刘诗诗',content:'大漠孤烟直'},
// {id:Date.now()+"4",user:'关晓彤',content:'长河落日圆'},
// {id:Date.now()+"5",user:'刘亦菲',content:'劝君更进一杯酒,西出阳关无故人'}
]
},
methods : {
add : function(){
var list = JSON.parse(localStorage.getItem('cmts') || '[]');
this.list = list;
},
del : function(index){
var list = JSON.parse(localStorage.getItem('cmts') || '[]'); // 获取到本地缓存中的数组
list.splice(index,1); // 根据index 删除一个数据
localStorage.setItem('cmts',JSON.stringify(list)); // 把删除后的数据重新存储到本地缓存中
this.list=list;
// this.list.splice(index,1);
}
},
components : {
commentBox : commentBox
},
beforeCreate() {
},
created(){
this.add();
}
})
</script>
</body>
</html>
15. 使用ref获取DOM元素和组件引用
子组件向父组件传值和父组件调用子组件方法,通过$ref
<div id="app">
<input type="button" value="获取元素" @click="getElement">
<h3 ref="myh3">哈哈哈,今天天气太好了!</h3>
<hr>
<!--通过ref属性 指定ref的值,这样通过$ref中就可以获取到组件了-->
<login ref="login"></login>
</div>
<script>
var login ={
template : '<h1>登录</h1>',
data(){
return {
msg : 'son msg'
}
},
methods: {
show() {
console.log("调用了子组件的方法!")
}
}
}
var vm = new Vue({
el : '#app',
data : {
},
methods : {
getElement: function(){
// ref 是英文单词 reference
console.log(this.$refs)
this.$refs.login.show(); // 执行了子组件的show()方法
}
},
components : {
login : 'login'
}
})
</script>
16. 路由
什么是路由?
**后端路由:**对于普通网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源
**前端路由:**对于单页面应用程序员来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不包含hash相关的内容,所以,单页面程序中的页面跳转主要用hash实现
在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由)
16.1 vue-router
vue-router cdn:
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
在引入vue-router之前,一定要先引入vue,因为vue-router依赖于vue
如果使用npm安装,在一个模块化工程中使用它,必须要通过Vue.use()
明确地安装路由功能
如果使用全局的script
标签,则无需手动安装
步骤:
-
导入vue-router.js
-
创建一个路由对象,当导入 vue-router包之后,在 window全局对象中,就有了一个路由的构造函数,叫做VueRouter,在 new 路由对象的时候,可以为构造函数,传递一个配置对象route,route 表示【路由匹配规则】的意思,每个路由规则都是一个对象,这个规则对象,有两个必须的属性:属性1:path,表示监听哪个路由链接地址;属性2:component,表示如果路由是前面匹配到的path,则暂时component属性对应的组件。component的属性值必须是一个组件的模板对象,不能是组件的引用名称(字符串)
-
将路由规则对象,注册到vm实例上,用来监听URL地址变化,然后展示对应的组件
-
router-link
用来实现路由跳转,是有vue-router提供,它有一个属性to
,用来指定组件的路径,router-link
渲染出来后也是<a>
标签,如果直接使用<a>
标签,其href属性指定的路径前要加上#
-
router-view
是vue-router提供的元素,专门用来当作占位符的,将来,路由规则匹配到的组件就会展示到这个router-view中
<div id="app">
<a href="#/login">登录</a>
<a href="#/register">注册</a>
<router-link to="/login">登录</router-link>
<router-link to="/register">注册</router-link>
<!-- 这是vue-router提供的元素,专门用来当作占位符的,将来,路由规则匹配到的组件就会展示到这个router-view中 -->
<router-view></router-view>
</div>
<script>
var login = {
template : '<h1>登录</h1>'
}
var register = {
template : '<h1>注册</h1>'
}
// 创建一个路由对象,当导入 vue-router包之后,在 window全局对象中,就有了一个路由的构造函数,叫做VueRouter
// 在 new 路由对象的时候,可以为构造函数,传递一个配置对象
var routerObj = new VueRouter({
//route // 这个配置对象中的route 表示【路由匹配规则】的意思
routes : [ // 路由匹配规则
mode: 'history', //去掉url中的#
// 每个路由规则都是一个对象,这个规则对象,有两个必须的属性:
// 属性1:path,表示监听哪个路由链接地址
// 属性2:component,表示如果路由是前面匹配到的path,则暂时component属性对应的组件
// component的属性值必须是一个组件的模板对象,不能是组件的引用名称(字符串)
{path:'/login',component:login},
{path:'/register',component:register}
]
})
var vm = new Vue({
el : '#app',
components : {
login : 'login',
register : 'register'
},
router:routerObj // 将路由规则对象,注册到vm实例上,用来监听URL地址变化,然后展示对应的组件
})
</script>
16.2 路由redirect重定向
-
可以使用这中方式来实现,跳转,在访问
/
根路径的时候,会自动跳转到login
组件,但此时浏览器地址栏还是会显示/
根路径,所以不推荐这种方式{path:'/',component:login}
-
我们可以通过,
redirect
属性来实现重定向{path:'/',redirect:'/login'}
16.3 设置选中路由高亮的两种方式
方式一
在<router-link>
标签被选中时,会自动加上两个类,router-link-active
和router-link-exact-active
所以我们可以给router-link-active
类添加样式,来实现选中时的高亮效果
<style>
.router-link-active {
color: red;
}
</style>
这样,在被选中的<router-link>
标签就会有高亮效果了
方式二
如果要修改链接激活时使用的css类名,默认值可以通过路由的构造选项linkActiveClass
来全局配置,如
var routerObj = new VueRouter({
routes : [
{path:'/login',component:login},
{path:'/register',component:register},
{path:'/',redirect:'/login'},
],
// 修改链接激活时的css类名,在链接选中时,就会自动为标签添加active类
linkActiveClass : 'active'
})
.active {
font-size: 80px;
}
16.4 路由切换动画
直接给<router-view>
标签包裹<transition>
标签就可以了!
<style>
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(150px);
}
.v-enter-active,
.v-leave-active {
transition: all 0.5s ease;
}
</style>
<transition mode="out-in">
<router-view></router-view>
</transition>
16.5 路由传参
使用query方式传递参数
如果在路由中,使用?问号拼接参数,给路由传递参数,则不需要修改路由规则的path属性
this.$route.query
可以获取到路由传递的参数,一般在组件的生命周期函数 created
函数中获取路由传递的参数
- 传递参数
<router-link to="/login?id=10&name=zs">登录</router-link>
- 获取参数
var login = {
template : '<h1>登录---{{id}}----{{name}}</h1>',
data(){
return {
id : '',
name : ''
}
},
created(){ // 组件的生命周期钩子函数
this.id = this.$route.query.id ; // this.$route.query 可以获取到路由传递的参数
this.name = this.$route.query.name;
}
}
这种方式,有缺点,参数名,和参数值全部暴露在浏览器地址栏中了
使用params方式传递参数
很像restful风格
this.$route.params
可以获取到路由传递的参数,一般在组件的生命周期函数 created
函数中获取路由传递的参数
- 修改路由规则
/:参数名
routes : [
{path:'/login/:id/:name',component : login}
],
- 在路由跳转时,传递参数
<router-link to="/login/20/zs">登录</router-link>
- 获取参数
var login = {
template : '<h1>登录----{{id}}---{{name}}</h1>',
data(){
return {
id : '',
name : ''
}
},
created(){
this.id = this.$route.params.id; // 通过 this.$route.params 获取路由传递的参数
this.name = this.$route.params.name;
}
}
16.6 使用children属性实现路由嵌套
子组件 路由路径要写在父组件路径下
在children中 定义的子组件的路由规则 path属性 不要加 /
<template id="temp">
<div>
<h1>这是account组件</h1>
<!-- 子组件 路由路径要写在父组件路径下 -->
<router-link to="/account/login">登录</router-link>
<router-link to="/account/register">注册</router-link>
<!--在子组件中写上 router-view -->
<router-view></router-view>
</div>
</template>
<div id="app">
<router-link to="/account">Account</router-link>
<router-view></router-view>
</div>
<script>
var account = {
template : '#temp'
}
var login = {
template : '<h1>登录</h1>'
}
var register = {
template : '<h1>注册</h1>'
}
var routerObj = new VueRouter({
routes : [
{
path : '/account',
component : account,
children : [
// 定义子组件路由 path 不用加 /
{path:'login',component:login},
{path:'register',component:register}
]
}
]
});
var vm = new Vue({
el : '#app',
data : {
},
components : {
account,
login,
register
},
router : routerObj
})
</script>
16.7 使用命名视图实现经典布局
在一个页面需要多个组件时,就需要给<router-view>
标签添加name属性,来指定对应的组件
在定义路由规则时,访问 /
根路径时,将多个组件共同显示出来,就要使用components
来定义一个路径下多个组件
<div id="app">
<router-view></router-view>
<router-view name="left"></router-view>
<router-view name="main"></router-view>
</div>
<script>
var header = {
template : '<h1>Header头部区域</h1>'
}
var leftBox = {
template : '<h1>leftBox左侧侧边栏区域</h1>'
}
var mainBox = {
template : '<h1>mainBox右侧主体区域</h1>'
}
var routerObj = new VueRouter({
routes :[{
path : '/',
components :{
'default' : header,
'left': leftBox,
'main' : mainBox
}
}]
});
var vm = new Vue({
el : '#app',
components : {
header,
leftBox,
mainBox
},
router : routerObj
});
</script>
17. slot 插槽
<template id="temp">
<div>
<h1>组件一</h1>
<slot name="s1"></slot>
<slot name="s2"></slot>
</div>
</template>
<div id="app">
<com>
<s1 slot="s1"></s1>
<s2 slot="s2"></s2>
</com>
</div>
<script>
Vue.component('com',{
template : '#temp'
})
Vue.component('s1',{
template : '<h1>s1s1s1</h1>'
})
Vue.component('s2',{
template : '<h1>s2s2s2</h1>'
})
var vm = new Vue({
el : '#app'
});
</script>
18. watch监听
18.1 监听data数据变化
使用这个属性 可以监视data中指定数据的变化,然后触发这个watch中对应的function处理函数
<div id="app">
<input type="text" placeholder="firstname" v-model="firstname" >
<input type="text" placeholder="lastname" v-model="lastname">
<input type="text" placeholder="name" v-model="name">
</div>
<script>
var vm = new Vue({
el : '#app',
data : {
lastname : '',
firstname: '',
name : ''
},
watch : {
// 使用这个属性 可以监视data中指定数据的变化,然后触发这个watch中对应的function处理函数
firstname : function(){
// 当firstname的值改变时,就会执行该函数
this.name = this.firstname + this.lastname;
},
lastname : function(){
// 当lastname的值改变时,就会执行该函数
this.name = this.firstname + this.lastname;
}
}
});
</script>
watch
属性中的方法中 有两个参数:
参数一:newVal
参数二:oldVal
watch : {
// 使用这个属性 可以监视data中指定数据的变化,然后触发这个watch中对应的function处理函数
firstname : function(newVal,oldVal){
// 当firstname的值改变时,就会执行该函数
this.name = newVal + this.lastname;
},
lastname : function(newVal,oldVal){
// 当lastname的值改变时,就会执行该函数
this.name = newVal + this.lastname;
}
}
18.2 监听非DOM元素的改变
监听路由的改变
watch : {
'$route.path' : function(newVal,oldVal){
console.log(newVal);
console.log(oldVal);
if(newVal == '/login'){
console.log("欢迎进入登录页面~~");
}
}
}
19. nrm的使用
作用:提供了一些最常用的NPM包镜像地址,能够让我们快速的切换安装包时候的服务器地址
什么是镜像:原来包刚一开始是只存在于国外的NPM服务器,但是由于网络原因,经常访问不到,这时候,我们可以在国内,创建一个和官网完全一样的NPM服务器,只不过,数据都是从人家那里拿过来的,除此之外,使用方式完全一样
- 运行
npm i nrm -g
全局安装nrm
包 - 使用
nrm ls
查看当前所有可用的镜像地址以及当前所使用的镜像的地址 - 使用
nrm use npm
或nrm use taobao
切换不同的镜像源地址
换成国内的淘宝镜像,可以提高装包时的速度
注意:nrm只是单纯的提供了几个常用的下载包的URL地址,并能够让我们在这几个地址之间很方便的进行切换,但是我们每次装包的时候,使用的装包工具还是npm
npm i cnpm -g
安装 cnpm 装包工具
20. webpack
20.1 概念
在网页中会引用哪些常见的静态资源?
- JS
- .js .jsx .coffee .ts(TypeScript)
- CSS
- .css .less .sass(.scss)
- Image
- .jpg .png .gif .bmp
- 字体文件(Fonts)
- .svg .ttf .eot .woff .woff2
- 模板文件
- .ejs .jade .vue(这是在webpack中自定义组件的方式,推荐)
网页中引入的静态资源多了以后有什么问题?
- 网页加载速度慢,我们要发起很多的二次请求
- 要处理错综复杂的依赖关系
如何解决上诉问题?
- 合并,压缩,精灵图,图片的Base64编码
- 可以使用requestJS,也可以使用webpack
什么是webpack?
-
webpack 是前端的项目构建工具,它是基于Node.js开发出来的前端工具
-
我们可以使用webpack这个前端自动化构建工具,可以完美实现资源的合并,打包,压缩,混淆等诸多功能
webpack安装的两种方式
- 运行
npm i webpack -g
是全局安装webpack,这样能在全局使用webpack的指令 - 在项目根目录中运行
npm i webpack --save-dev
安装到项目依赖中
20.2 webpack最基本的使用方式
- 手动创建目录结构,dist目录代表发表的产品级的目录,main.js 是我们项目的JS入口文件
- 运行
npm init
初始化项目,会生成一个package.json
的文件
-
在index.html中我们只引入main.js,不再引入其他js文件,在main.js中,引入其他所有的js文件
在index.html中引入main.js
-
使用
cnpm i jquery --save
安装jquery类库
这样就会生成一个node_modules
目录和package-lock.json
文件,jquery就在node_modules
中
-
在
mian.js
中引入jqueryimport xx from xx
为ES6中导入模块的方式import $ from 'jquery'
这样我们就可以使用jquery来编写代码了
-
由于
import xx from xx
属于ES6的语法,浏览器不能够解析,所以我们需要使用webpackwebpack 要打包的资源路径 输出路径
,如webpack .\src\main.js .\dist\bundle.js
,将 main.js打包,输出到dist目录下,命名为bundle.js这样,在index.html中,我们就不能够直接引入main.js,我们需要引入
bundle.js
注意:如果是webpack4 可能会出现错误!
解决错误:
- 以管理员身份运行vs code
- 执行:get-ExecutionPolicy,显示Restricted,表示状态是禁止的
- 执行:set-ExecutionPolicy RemoteSigned
- 这时再执行get-ExecutionPolicy,就显示RemoteSigned
如果提示
Cannot find module 'webpack-cli'
我们需要先全局安装
webpack-cli
,执行命令npm i webpack-cli -g
这样在执行
webpack .\src\main.js -o .\dist\bundle.js
就成功了!这样就在dist目录下,生成了bundle.js文件
-
webpack处理了JS文件的相互依赖关系
-
webpack能够处理js的兼容性问题,把高级的,浏览器不识别的语法,转为低级的浏览器能正常识别的语法
20.3 webpack最基本的配置文件的使用
我们想只执行webpack
命令,就可以让其将main.js
打包成bundle.js
我们需要在项目根目录下建一个配置文件webpack.config.js
在配置文件中进行配置
// 这个配置文件,其实就是一个js文件,通过Node中的模块操作,向外暴露了一个配置对象
const path = require('path')
module.exports = {
// 指定入口,main.js
entry : path.join(__dirname,'./src/main.js'), // 入口,表示要使用webpack打包哪个文件
output : {
path : path.join(__dirname,'./dist'), // 指定打包好的文件,输出到哪个目录中去
filename : 'bundle.js' // 指定输出的文件的名称
}
}
这样,我们只需要输入webpack
,就会自动帮我们打包main.js
,输出到bundle.js
20.4 webpack-dev-server的基本使用
使用webpack-dev-server这个工具,来实现自动打包编译的功能
-
在项目内执行命令
npm i webpack-dev-server -D
把这个工具安装到项目的本地开发依赖 -
安装完毕后,这个工具的的用法和webpack的命令的用法完全一样
-
由于,我们是在项目本地安装的
webpack-dev-server
,所以无法把它当作脚本命令在powershell终端中直接运行 (只有那些安装到全局 -g 的工具才能在终端中执行) -
所以,我们可以在
package.json
文件中进行配置
-
配置完成后,我们直接执行命令
npm rund dev
,就可以通过localhost:8080进行访问了 -
但是,这是,我们在index.html引入的js文件不能再是
../dist/bundle.js
了,要改为<script src="/bundle.js"></script>
这样,我们再修改
main.js
中的代码时,就不用再去单独的执行打包命令了!!webpack-dev-server帮我们打包生成的bandle.js文件,并没有存放到实际的物理磁盘上,直接托管到了内存中!
20.5 webpack-dev-server的常用参数命令
方式一(推荐)
在package.json
中的配置
-
"dev":webpack-dev-server --open
,自动打开浏览器 -
"dev":webpack-dev-server --open --port 3000
,自动打开浏览器,同时运行的端口为 3000 -
"dev":webpack-dev-server --open --port 3000 --contentBase src
,自动打开浏览器,同时运行的端口为 3000,并且打开页面为src下的index.html -
"dev":webpack-dev-server --open --port 3000 --contentBase src --hot
,自动打开浏览器,同时运行的端口为 3000,并且打开页面为src下的index.html,不重新生成bundle.js文件,只生成局部更新的文件
方式二
不在package.json
中进行配置,在webpack.config.js
中进行配置
devServer : {
// 这是配置dev-server命令参数的第二种形式
open : true, // 自动打开浏览器
port : 3000, // 设置启动时的运行端口
contentBase : 'src', // 指定托管的根目录
hot : true // 启用热部署
}
20.6 html-webpack-plugin的基本使用
作用:在内存中,根据我们的index模板页面生成一个内存中的首页,我们就可以访问内存中的页面了
运行npm i html-webpack-plugin --save-dev
安装到开发环境
修改配置文件webpack.config.js
// 导入在内存生成html页面的插件
// 只要是插件,都一定要放到plugins节点中去
const htmlWebpackPlugin = require('html-webpack-plugin');
在配置文件webpack.config.js,中的plugins节点中配置
plugins : [
new htmlWebpackPlugin({
// 创建一个在内存中生成html页面的插件
template : path.join(__dirname,'./src/index.html'), // 指定模板页面,将来会根据指定的页面路径,去生成内存中的页面
filename : 'index.html' // 指定生成的页面的名称
})
当使用了html-webpack-plugin之后,我们不需要手动处理bundle.js路径了,因为这个插件,已经帮我们自动创建了一个合适的script标签,并且,引用了正确的路径
20.7 loader配置处理css样式
-
如果想要打包处理css文件,需要安装
npm i style-loader css-loader -D
-
打开webpack.config.js配置文件,在里面新增一个配置节点
module
,它是一个对象,在这个对象上,有一个rules
属性,这个属性是个数组,这个数组中,存放了所有第三方文件 匹配和处理规则module : { // 这个节点,用于配置所有的第三方模块加载器 rules : [ // 所有第三发模块的匹配规则 {test: /\.css$/,use:['style-loader','css-loader']}, //配置处理.css文件的第三方loader规则 ] }
-
在main.js中导入css文件
import './css/index.css'
这样,我们的css文件就被打包一起导入了
loader配置处理less文件
-
安装less
npm i less-loader -D
和npm i less -D
-
打开webpack.config.js配置文件,在里面进行配置
module : { // 这个节点,用于配置所有的第三方模块加载器 rules : [ // 所有第三发模块的匹配规则 {test: /\.css$/,use:['style-loader','css-loader']}, //配置处理.css文件的第三方loader规则 {test: /\.less$/,use:['style-loader','css-loader','less-loader']}, //配置处理.css文件的第三方loader规则 ] }
-
在main.js中导入less文件
import './css/index.less'
20.8 url-loader 的使用
默认情况下,webpacke无法处理css文件中的url地址,不管是图片还是字体库,只要是url地址都无法处理
安装url-loader和file-loader npm i url-loader file-loader -D
在webpack.config.js中进行配置
{test:/\.(jpg|png|gif|bmp|jpeg)$/,use:'url-loader?limit=1024&name=[hash:8]-[name].[ext]'} // 处理图片路径的loader,通过?传递参数,当图片小于1024kb的时候,以base64编码的方式加载,大于1024kb的时候以url的方式加载;图片的名字等于打包之前图片的名字前加上一个八位的hash且后缀名也相同
20.9 webpack中babel的配置
作用:处理高级的ES6语法
在webpack中,默认只能处理一部分ES6的新语法,一些更高级的ES6语法或者ES7语法,webpack是无法处理的,这时候就要借助于第三方的loader,把高级的语法转为低级的语法后,会把结果交给webpack去打包到bundle.js
通过bable可以帮我们将高级的语法转换为低级的语法
在webpack中,可以运行如下两套命令,安装两套包,去安装babel相关的loader功能:
第一套包
npm i babel-core babel-loader babel-plugin-transform-runtime -D
第二套包
npm i babel-preset-env babel-preset-stage-0 -D
在webpack的配置文件中,在moudle节点下的rules数组中,添加一个新的匹配规则
{test:/\.js$/,use:'babel-loader',exclude:/node_modules/} // 排除掉node——modules中的包
在项目根目录中,新建一个叫做.babelrc 的babel配置文件,这个配置文件属于JSON格式,所以在写.babelrc配置的时候,必须符合json语法规范:不能写注释,字符串必须用双引号
在.babelrc写如下配置:
{
"presets":["env","stage-0"],
"plugins":["transform-runtime"]
}
21. Vue实例的render方法渲染组件
使用components 的话,是将组件放入id为app的容器内,使用render是把id为app的容器替换为指定的组件
<div id="app">
<login></login>
</div>
<script>
var login = {
template : '<h1>这是登录组件</h1>'
}
var vm = new Vue({
el : '#app',
render : function(createElements){ //createElements 是一个方法,调用它能把指定的组件模板渲染为html结构
return createElements(login); // 注意:这里的return的结果,会替换页面中el指定的那个容器
// 使用components 的话,是将组件放入id为app的容器内,使用render是把id为app的容器替换为指定的组件
}
});
</script>
22.Vue 和 Webpack
22.1 webpack中导入vue
安装vue npm i vue -S
在main.js中导入vue
import Vue from 'vue'
注意:在webpack中,使用import Vue from ‘vue’ 导入的Vue构造函数,功能不完整,只提供了 runtime-only的方式,并没有提供像script标签导入vue那样的使用方式
解决办法:
方法一:在package.json配置文件中修改main的值"main":"dist/vue.js"
方法二:在导入时,指定vue的路径import Vue from '../node/node_modules/vue/dist/vue.js'
方法三:在webpack.config.js中,新增一个节点resolve,进行配置
resolve : {
alias : {
// 修改vue被导入时候的包的路径
'vue$':'vue/dist/vue.js'
}
}
22.2 vue中结合render函数渲染指定组件到容器中
在login.vue中
<template>
<div>
<h1>这是登录组件,使用.vue文件定义出来的</h1>
</div>
</template>
<script>
</script>
<style>
</style>
在main.js中
import Vue from 'vue'
import login from './login.vue'
var vm = new Vue({
el : '#app',
data : {
msg : '123'
},
render :function(createElements) {
// 在webpack中,如果想要通过vue,把一个组件放入页面中去展示,vm实例中的render函数可以实现
return createElements(login);
}
})
默认情况下webpack无法打包.vue文件,需要安装相关的loader
npm i vue-loader vue-template-compiler -D
在配置文件webpack.config.js中,新增loader配置对象
{test : '/\.vue$/',use : 'vue-loader'} // 处理 .vue文件的loader
22.3 export default 和 export 的使用方式
使用exprot default 和 export 向外暴露成员
在.vue中
<script>
export default {
data () {
return {
msg : '123'
}
},
methods : {
show(){
console.log("调用了login.vue的show方法")
}
}
}
</script>
注意:export default 向外暴露的成员,可以使用任意的变量来接收
在一个模块中,export default 只允许向外暴露一次
在一个模块中,可以同时使用和export default 和 export 向外暴露成员
export var name : 'zs'
export var age : '23'
在接收时,
import {name,age} from 'xxx'
注意:使用export向外暴露的成员,只能使用{ }
的形式来接收,这种形式,叫做【按需导出】
注意:使用export导出的成员,必须严格按照导出时候的名称,来使用 { }
按需接收
可以使用 as
来取别名
import {name as name123} from 'xxx'
22.4 在webpack中使用vue-router
导包 npm install vue-router
如果在一个模块化工程中使用它,必须要通过Vue.use()
明确地安装路由功能
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
如果使用全局的script
标签,则无需手动安装
22.5 组件中style标签
scoped:使style标签内的样式,只作用于该组件
<style scoped></style>
普通的style标签只支持普通的样式,如果要启用scss或less,需要为style标签元素,添加lang属性
<style lang="less"></style>
这样,在组件中的style中,就可以写less了
22.6 webpack中使用axios
导入 axios npm i axios -S
import Vue from 'vue'; //导入vue模块
import axios from 'axios'; //导入axios
axios.defaults.baseURL = 'http://www.xxxxx.cn'; //配置根域名
//Vue.prototype.$ajax = axios; //把axios挂载到Vue的原型上
22.Vue 和 Webpack
22.1 webpack中导入vue
安装vue npm i vue -S
在main.js中导入vue
import Vue from 'vue'
注意:在webpack中,使用import Vue from ‘vue’ 导入的Vue构造函数,功能不完整,只提供了 runtime-only的方式,并没有提供像script标签导入vue那样的使用方式
解决办法:
方法一:在package.json配置文件中修改main的值"main":"dist/vue.js"
方法二:在导入时,指定vue的路径import Vue from '../node/node_modules/vue/dist/vue.js'
方法三:在webpack.config.js中,新增一个节点resolve,进行配置
resolve : {
alias : {
// 修改vue被导入时候的包的路径
'vue$':'vue/dist/vue.js'
}
}
22.2 vue中结合render函数渲染指定组件到容器中
在login.vue中
<template>
<div>
<h1>这是登录组件,使用.vue文件定义出来的</h1>
</div>
</template>
<script>
</script>
<style>
</style>
在main.js中
import Vue from 'vue'
import login from './login.vue'
var vm = new Vue({
el : '#app',
data : {
msg : '123'
},
render :function(createElements) {
// 在webpack中,如果想要通过vue,把一个组件放入页面中去展示,vm实例中的render函数可以实现
return createElements(login);
}
})
默认情况下webpack无法打包.vue文件,需要安装相关的loader
npm i vue-loader vue-template-compiler -D
在配置文件webpack.config.js中,新增loader配置对象
{test : '/\.vue$/',use : 'vue-loader'} // 处理 .vue文件的loader
22.3 export default 和 export 的使用方式
使用exprot default 和 export 向外暴露成员
在.vue中
<script>
export default {
data () {
return {
msg : '123'
}
},
methods : {
show(){
console.log("调用了login.vue的show方法")
}
}
}
</script>
注意:export default 向外暴露的成员,可以使用任意的变量来接收
在一个模块中,export default 只允许向外暴露一次
在一个模块中,可以同时使用和export default 和 export 向外暴露成员
export var name : 'zs'
export var age : '23'
在接收时,
import {name,age} from 'xxx'
注意:使用export向外暴露的成员,只能使用{ }
的形式来接收,这种形式,叫做【按需导出】
注意:使用export导出的成员,必须严格按照导出时候的名称,来使用 { }
按需接收
可以使用 as
来取别名
import {name as name123} from 'xxx'
22.4 在webpack中使用vue-router
导包 npm install vue-router
如果在一个模块化工程中使用它,必须要通过Vue.use()
明确地安装路由功能
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
如果使用全局的script
标签,则无需手动安装
22.5 组件中style标签
scoped:使style标签内的样式,只作用于该组件
<style scoped></style>
普通的style标签只支持普通的样式,如果要启用scss或less,需要为style标签元素,添加lang属性
<style lang="less"></style>
这样,在组件中的style中,就可以写less了
22.6 webpack中使用axios
导入 axios npm i axios -S
import Vue from 'vue'; //导入vue模块
import axios from 'axios'; //导入axios
axios.defaults.baseURL = 'http://www.xxxxx.cn'; //配置根域名
//Vue.prototype.$ajax = axios; //把axios挂载到Vue的原型上
23. Vuex
23.1 vuex概述
组件之间共享数据的方式
父向子传值:v-bind 属性绑定
子向父传值:v-on 事件绑定 或 this.$refs
兄弟组件之间数据共享:EventBus
Vuex是什么?
Vuex是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据的共享
使用Vuex统一管理状态的好处:
- 能够在vuex中集中管理共享的数据,易于开发和后期维护
- 能够高效地实现组件之间的数据共享,提高开发效率
- 存储在vuex中的数据都是响应式的,能够实时保持数据与页面的同步
什么样的数据适合存储到vuex中?
一般情况下,只有组件之间共享的数据,才有必要存储到vuex中,对于组件中私有的数据,依旧存储在组件自身的data中即可
23.2 vuex的基本使用
安装vuex的依赖包 npm install vuex --save
导入vuex包
import Vuex from 'vuex'
Vue.use(Vuex)
创建store对象
const store = new Vuex.Store({
// state 中存放的就是全局共享的数据
state : {count : 0}
})
将stroe对象挂载到vue实例中
import store from '...'
new Vue({
el : '#app',
data : {},
router,
// 将创建的共享数据对象,挂载到Vue实例中
// 所有的组件,就可以直接从store中获取全局的数据了
store
})
安装vue-cli
npm install @vue/cli -g
使用vue ui
来创建一个项目
23.3 State
State 提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state : {
// State 提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储
count : 0
},
mutations :{},
actions : {}
})
组件中访问State中数据的第一种方式:
this.$store.state.全局数据名称
<h1>当前最新的count值为:{{$store.state.count}}</h1>
第二种方式:
从vuex中按需导入mapState函数
通过刚才导入的mapState函数,将当前组件需要的全局数据,映射为当前组件的computed计算属性
import {mapState} from 'vuex'
export default {
data(){
return {
}
},
computed : {
...mapState(['count']) // ...为展开运算符,这样就拿到了全局数据 count
}
}
</script>
23.4 Mutation
Mutation用于变更Store中的数据
- 只能通过mutation变更Store数据,不可以直接操作Store中的数据
- 通过这种方式虽然操作起来稍微繁琐,但是可以集中监控所有数据的变化
组件通过调用Mutation中的函数来实现对全局数据的改变
export default new Vuex.Store({
state : {
// State 提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储
count : 0
},
mutations :{
// 用mutation来操作数据,组件电泳mutations中的方法来操作数据
add(state){
state.count++
},
del(state){
state.count--
}
},
actions : {}
})
调用Mutation中的方法
this.$store.commit(‘函数名字’);
export default {
data(){
return {
}
},
methods : {
add(){
this.$store.commit('add'); // 调用mutation中的函数
}
}
}
Mutation传递参数
在Mutation中定义了方法
mutations :{
// 用mutation来操作数据,组件电泳mutations中的方法来操作数据
addN(state,step){ // 第二个是要传递的参数
state.count=state.count+step
}
},
组件中调用Mutation中方法时,传递参数
addn(){
this.$store.commit('addN',3); // 传递了一个参数 3
}
在组件中调用Mutation中函数的第二种方式
从vuex中按需导入mapMutations函数
通过刚才导入的mapMutations函数,将需要的mutations函数,映射为当前组件的methods方法
methods:{
...mapMutations(['del']),
deleteNum(){
this.del();
}
}
注意:在Mutations的函数中不能写异步的代码!!
23.5 Action
Action用于处理异步任务
如果通过异步操作变更数据,必须通过Action,而不能使用Mutation,但是在Action中还是要通过触发Mutation的方式间接变更数据
actions : {
addAsync(context){
setTimeout(function(){ // 延时1秒执行add函数
context.commit('add')
},1000)
}
}
触发Action
methods : {
add(){
this.$store.dispatch('addAsync')
}
}
触发Action异步任务时传递函数
actions : {
addAsync(context,arg){
setTimeout(function(){ // 延时1秒执行add函数
context.commit('add',arg)
},1000)
}
}
触发Action
methods : {
add(){
this.$store.dispatch('addAsync',5)
}
}
触发Action函数的第二种方式
从vuex中按需导入mapActions 函数
import {mapActions} from 'vuex'
methods : {
...mapActions(['addAsync'])
}
23.6 Getter
Getter 用于对Store中的数据进行加工处理形成新的数据
- Getter可以对Store中已有的数据加工处理之后形成新的数据,类似Vue的计算属性
- Store中的数据发生变化,Getter的数据也会跟着变化
定义Getter
new Vue.Store({
state: {
count = 0
},
getters : {
showNum : state => {
return '当前最新的数量是【'+state.count+'】'
}
}
})
使用getter的第一种方式:
this.$store.getters.名称
this.$store.getters.showNum
使用getter的第二种方式:
import {mapGetters} from 'vuex'
computed : {
...mapGetters(['showNum'])
}