Vue、Axios、Router之快速入门使用总结
一、Vue概述
什么是Vue.js
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue也完全能够为复杂的单页应用提供驱动。
Vue.js是一个构建数据驱动的 web 界面的渐进式框架。Vue.js 的目标是通过尽可能简单的 API实现响应的数据绑定和组合的视图组件。
MVVM模式
MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。
MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model)
Vue.js 是一个提供了 MVVM 风格的双向数据绑定的 Javascript 库,专注于View 层。它的核心是 MVVM 中的 VM,也就是 ViewModel。 ViewModel负责连接 View 和 Model,保证视图和数据的一致性,这种轻量级的架构让前端开发更加高效、便捷。
MVVM拆分解释为:
Model:负责数据存储
View:负责页面展示
View Model:负责业务逻辑处理(比如Ajax请求等),对数据进行加工后交给视图展示
MVVM要解决的问题是将业务逻辑代码与视图代码进行完全分离,使各自的职责更加清晰,后期代码维护更加简单
VM(ViewModel)可以把view视图和Model模型解耦合,VM的要做的工作就是vue.js所承担的。
vue.js拥有的功能
1)声明式渲染: Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统。
比如:使用vue.js的插值表达式放在Dom的任意地方, 差值表达式的值将被渲染在Dom中。
2)条件与循环: dom中可以使用vue.js提供的v-if、v-for等标签,方便对数据进行判断、循环。
3)双向数据绑定: Vue 提供v-model指令,它可以轻松实现Dom元素和数据对象之间双向绑定,即修改Dom元素中的值自动修改绑定的数据对象,修改数据对象的值自动修改Dom元素中的值。
4)处理用户输入: 为了让用户和你的应用进行交互,我们可以用 v-on指令添加一个事件监听器,通过它调用在 Vue 实例中定义的 方法
5)组件化应用构建 :vue.js可以定义一个一个的组件,在vue页面中引用组件,这个功能非常适合构建大型应用。
Vue.js的使用
1)在html页面使用script引入vue.js的库即可使用。
2)使用Npm管理依赖,使用webpack打包工具对vue.js应用打包。
3)Vue-CLI脚手架,使用vue.js官方提供的CLI脚本架会很方便的创建vue.js工程雏形。
VueJS快速入门
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Vue快速入门</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--Vue的插值表达式,把data中定义的数据显示到此处-->
<!--相当于是MVVM中的View这个角色-->
{{message}}
</div>
</body>
<script>
//view model
//创建Vue对象
//vm :是MVVM中的 View Model
var vm=new Vue({
//由vue接管id为app区域
el:"#app",
data:{
message:"Hello Vue!",// 相当于是MVVM中的Model这个角色
}
});
</script>
</html>
二、模板语法
1.插值表达式
数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值。Mustache 标签将会被替代为对应数据对象上 msg 属性的值。无论何时,绑定的数据对象上 msg 属性发生了改变,插值处的内容都会更新。
<div id="app">
<!--Vue的插值表达式,把data中定义的数据显示到此处-->
{{message}}
<!-- 三元运算符 -->
{{ tag ? "true" : "false" }}
<!-- 数学运算-->
{{number*3.14}}
</div>
<script>
//创建Vue对象
new Vue({
//vue接管id为app区域
el:"#app",
data:{
message:"Hello Vue!",
tag:true,
number:100,
}
});
</script>
表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。限制每个绑定都只能包含单个表达式。
<!--插值表达式不支持-->
{{var a = 1;}}
{{if(a = 1){}}}
2.指令
指令 (Directives) 是带有 v- 前缀的特殊特性。指令特性的值预期是单个 JavaScript 表达式 (v-for是例外)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
1.v-if
<p v-if="seen">现在你看到我了</p>
v-if 指令将根据表达式 seen 的值的真假来插入/移除 p元素。
2.v-text与v-html
双大括号会将数据解释为普通文本,而非 HTML 代码。使用 v-html 指令,输出真正的 HTML
v-text可以将一个变量的值渲染到指定的元素中,它可以解决插值表达式闪烁的问题
<div id="app">
<div v-text="message"></div>
<div v-html="message"></div>
<div id="div1"></div>
<div id="div2"></div>
</div>
<script>
new Vue({
el:"#app",
data:{
message:"<h1>Hello Vue</h1>"
}
});
//传统js的innerText和innerHTML
window.onload = function(){
document.getElementById("div1").innerHTML="<h1>Hello</h1>";
document.getElementById("div2").innerText="<h1>Hello</h1>";
}
</script>
3.v-bind
v-bind 指令能够接收一个“参数”,在指令名称之后以冒号表示。用于响应式地更新 HTML 。
这里 href 是参数, v-bind 指令将该元素的 href 特性与表达式 url 的值绑定。
<a v-bind:href="url">...</a>
## v-bind
插值语法不能作用在 HTML 特性上,遇到这种情况应该使用 v-bind指令
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
<div id="app">
<font size="5" v-bind:color="red">Hello Vue!</font>
<font size="5" :color="green">Hello Vue!</font>
</div>
<script>
//插值表达式不能用于html标签的属性取值
//要想给html标签的属性设置变量的值,需要使用v-bind
//v-bind也可以简化写法 直接使用:
new Vue({
el:"#app",
data:{
red:"red",
green:"green"
}
})
</script>
v-on 指令,它用于监听 DOM 事件
<a v-on:click="doSomething">...</a>
3.缩写
v- 前缀作为一种视觉提示,用来识别模板中 Vue 特定的特性。: 与 @ 对于特性名来说都是合法字符,在所有支持 Vue 的浏览器都能被正确地解析。
1.v-bind 缩写
v‐bind可以将数据对象绑定在dom的任意属性中。
v‐bind可以给dom对象绑定一个或多个特性,例如动态绑定style和class
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<div v‐bind:style="{ fontSize: size + 'px' }"></div>
<!-- 缩写 -->
<a :href="url">...</a>
<div :style="{ fontSize: size + 'px' }"></div>
2.v-on 缩写
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
三、计算属性和侦听器
1.计算属性
模板内的表达式的初衷是用于简单运算。在模板中放入太多的逻辑会让模板过重且难以维护。对于任何复杂逻辑,都应当使用计算属性。计算属性是基于它们的响应式依赖进行缓存的,只在相关响应式依赖发生改变时它们才会重新求值。
反例:
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
正例:
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
2.侦听器
Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
watch侦听属性值,当值发生变化是执行函数
new Vue({
el: '#example',
data: {
message: 'Hello',
},
methods:{
},
watch: {
//当message值发生变化时执行此函数
message: function (newValue, oldValue) {
console.log(newValue)
console.log(oldValue)
}
}
})
四、Class与Style绑定
1.绑定 HTML Class
对象语法
给 v-bind:class绑定 一个对象,动态地切换 class。
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError}"></div>
<div v-bind:class="classObject"></div>
</div>
data: {
isActive: true,
hasError:false,
classObject: { active: true, 'text-danger': false }
},
//渲染结果:
<div class="static active"></div>
绑定一个返回对象的计算属性
<div v-bind:class="classObject"></div>
data: {
isActive: true,
hasError:false,
},
computed: {
classObject: function () {
return {
active: this.isActive
'text-danger': this.error
}
}
}
//渲染结果:
<div class="static active"></div>
数组语法
给 v-bind:class绑定 一个数组,以应用一个 class 列表。
<div v-bind:class="[activeClass, errorClass]"></div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
//渲染结果:
<div class="active text-danger"></div>
根据条件切换列表中的 class,可以用三元表达式
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
data: {
isActive : true,
}
//渲染结果:
<div class="activeClass "></div>
用在组件上
组件上使用 class 属性时,这些类将被添加到该组件的根元素上面
Vue.component('my-component', {
template: '<p class="foo bar">Hi</p>'
})
<my-component v-bind:class="{ active: isActive }"></my-component>
data: {
isActive : true,
}
//渲染结果:
<p class="foo bar active">Hi</p>
2.绑定内联样式
对象语法
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div v-bind:style="styleObject"></div>
data: {
activeColor: 'red',
fontSize: 30,
styleObject: { color: 'red', fontSize: '13px' }
}
数组语法
v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上
<div v-bind:style="[baseStyles, overridingStyles]"></div>
data: {
baseStyles: 'red',
overridingStyles: { color: 'red', fontSize: '13px' }
}
五、条件渲染
1.v-if
v-if 指令用于条件性地渲染一块内容。因为 v-if 是一个指令,所以必须将它添加到一个元素上
<div v-if="type === 'A'">
A
</div>
<!--v-else标签需要紧跟在v-if的标签之后,中间不能有其他标签-->
<div v-else-if="type === 'B'">
B
</div>
<div v-else>
Not A/B
</div>
2.用key管理可复用的元素
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。只需添加一个具有唯一值的 key 属性,表达两个及以上元素是完全独立的,不需要复用。
添加key后,两个input输入框都是独立的,每次v-if判断后都会重新渲染
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
3.v-show
v-show 指令,用于根据条件展示元素。带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的CSS 属性 display。
<h1 v-show="ok">Hello!</h1>
4.v-if与v-show
v-if 会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
v-show 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。
<body>
<div id="app">
<span v-if="flag">Heelo</span>
<span v-show="flag">Vue</span>
<button @click="fun">切换</button>
</div>
</body>
<script>
new Vue({
el:"#app",
data:{
flag:false
},
methods:{
fun:function(){
this.flag = !this.flag;
}
}
})
</script>
六、列表渲染
v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。
尽可能在使用 v-for 时提供 key,以获取性能上的提升
<body>
<div id="app">
<ul>
<li :key="index "v-for="(item,index) in arr ">{{item}}={{index}} </li>
</ul>
</div>
</body>
<script>
//view model
new Vue({
el:"#app",
data:{
arr:[1,2,3,4,5]
}
})
</script>
在组件上使用 v-for
<div id="example">
<item-list v-for="(item,index) in arr" v-bind:title="item" :key="index"></item-list>
</div>
<script>
Vue.component('item-list', {
template: '<li> {{ title }}</li>',
props: ['title']
})
new Vue({
el: '#example',
data: {
arr:[1,2,3,4,5]
},
})
</script>
v-for操作对象
<body>
<div id="app">
<ul>
<li v-for="(value, name, index) in product " v-bind:key="index"> {{ index }}. {{ name }}: {{ value }} </li>
</ul>
</div>
</body>
<script>
//view model
new Vue({
el:"#app",
data:{
product:{
id:1,
name:"电脑",
price:5555
}
}
})
</script>
操作对象数组
<body>
<div id="app">
<table border="1">
<tr>
<td>序号</td>
<td>编号</td>
<td>名称</td>
<td>价格</td>
</tr>
<tr v-for="(product,index) in products ">
<td>{{index}}</td>
<td>{{product.id}}</td>
<td>{{product.name}}</td>
<td>{{product.price}}</td>
</tr>
</table>
</div>
</body>
<script>
//view model
new Vue({
el:"#app",
data:{
products:[
{ id:1,name:"笔记本电脑",price:5000 },
{ id:2,name:"手机",price:3000 },
{ id:3,name:"电视",price:4000 }
]
}
})
</script>
操作一个整数
<span v-for="n in 10">{{ n }} </span>
七、事件处理
监听事件
v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。
<button v-on:click="counter += 1">Add 1</button>
data: {
counter: 0
}
事件处理方法
v-on 可以接收一个需要调用的方法名称,处理逻辑写在方法中。
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
<div id="app">
{{message}}
<button v-on:click="fun('v-on')">vue的onclick</button>
</div>
<script>
new Vue({
el:"#app",
data:{
message:"Hello Vue!"
},
methods:{
fun:function(msg){
alert("Hello Vue!");
this.message = msg;
}
}
});
</script>
使用特殊变量 $event,在处理方法中访问原始的 DOM 事件
v-on:keydown
<div id="app">
Vue方式:<input type="text" v-on:keydown="fun($event)">
<hr/>
传统JS方式:<input type="text" onkeydown="showKeyCode()"/>
</div>
<script>
new Vue({
el:"#app",
methods:{
// $event是vue中的事件对象,和传统js的event对象是一样的
fun:function(event){
var keyCode = event.keyCode;
if(keyCode < 48 || keyCode > 57){
//不让键盘的按键起作用
event.preventDefault();
}
alert(keyCode);
if(event.keyCode == 13){
alert("按的是回车");
}
}
}
});
//传统js的键盘按下事件
function showKeyCode(){
//event对象和document对象以及window对象是一样的,可以直接使用
var keyCode = event.keyCode;
//keyCode=48(0) keyCode=57(9) 0-9 => 48-57
if(keyCode < 48 || keyCode > 57){
//不让键盘的按键起作用
event.preventDefault();
}
alert(keyCode);
if(event.keyCode == 13){
alert("按的是回车");
}
}
</script>
v-on:mouseover
<div id="app">
<div @mouseover="fun1" style="width: 200px;height: 200px;background-color: aqua">
<textarea @mouseover="fun2($event)">这是一个文本域</textarea>
</div>
<div onmouseover="divmouseover()" style="width: 200px;height: 200px;background-color: aquamarine">
<textarea onmouseover="textareamouseover()">这是一个文本域</textarea>
</div>
</div>
<script>
/**
* @事件名称 就是 v-on:事件名称的简写方式
* @mouseover等同于v-on:mouseover
*/
new Vue({
el:"#app",
methods:{
fun1:function(){
alert("鼠标悬停在div上!");
},
fun2:function(event){
alert("鼠标悬停在textarea上!");
//阻止冒泡,不阻止则执行fun2后,同时会执行fun1
event.stopPropagation();
}
}
});
//传统的js方式
function divmouseover(){
alert("鼠标移动到了div上!");
}
function textareamouseover(){
alert("鼠标移动到了textarea上!");
//阻止冒泡,不阻止则执行textareamouseover()后会同时执行divmouseover();
event.stopPropagation();
}
</script>
事件修饰符
Vue.js 为 v-on 提供了事件修饰符来处理 DOM 事件细节,如: event.preventDefault() 或event.stopPropagation()。
Vue.js通过由点(.)表示的指令后缀来调用修饰符。
.stop
.prevent
.capture
.self
.once
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
<div id="app">
<form @submit.prevent action="http://www.baidu.com" method="post" >
<input type="submit" value="提交">
</form>
<form action="http://www.baidu.com" method="post" onsubmit="return checkForm()">
<input type="submit" value="提交">
</form>
<hr/>
<div @mouseover="fun1" id="div">
<textarea @mouseover.stop="fun2($event)">这是一个文本域</textarea>
</div>
</div>
<script>
new Vue({
el:"#app",
methods:{
fun1:function(){
alert("鼠标悬停在div上");
},
fun2:function(event){
alert("鼠标悬停在textarea上,且阻止了冒泡事件");
}
}
});
//传统js方式
function checkForm(){
alert(1);
//表单验证必须有一个明确的boolean类型返回值
//在应用验证方法时必须加上 return 方法名称
return false;
}
</script>
按键修饰符
Vue 允许为 v-on 在监听键盘事件时添加按键修饰符。
全部的按键码别名:
.enter
.tab
.delete (捕获 "删除" 和 "退格" 键)
.esc
.space
.up
.down
.left
.right
.ctrl
.alt
.shift
.meta
<div id="app">
<input type="text" v-on:keyup.enter="fun">
</div>
<script>
new Vue({
el:'#app',
methods:{
fun:function(){
alert("按了回车");
}
}
});
</script>
八、表单输入绑定
v-model 指令在表单 、 及 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。
v-model
<body>
<div id="app">
<form action="" method="post">
用户名:<input type="text" name="username" v-model="user.username"><br/>
密码:<input type="text" name="password" v-model="user.password"><br/>
</form>
</div>
</body>
<script>
new Vue({
el:"#app",
data:{
user:{
username:"test",
password:"1234"
}
}
})
</script>
九、Vue生命周期
每个 Vue 实例在被创建之前都要经过一系列的初始化过程.
vue在生命周期中有这些状态,beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed。
Vue在实例化的过程中,会调用这些生命周期的钩子,给我们提供了执行自定义逻辑的机会。
生命周期图示
vue实例从创建到销毁的过程
创建vue实例(初始化data、加载el)
数据挂载(将vue实例data中的数据渲染到网页HTML标签)
重新渲染(当vue的data数据发生变化,会重新渲染到HTML标签)
销毁实例
生命周期演示编码
<body>
<div id="app">
{{message}}
</div>
</body>
<script>
var vm = new Vue({
el: "#app",
data: {
message: 'hello world'
},
beforeCreate: function() {
//data初始化之前执行,不能操作data
console.log(this);
showData('创建vue实例前', this);
},
created: function() {
//data初始化之后执行,模板加载之前,可以修改/获取data中的值
//this.message= "created";
showData('创建vue实例后', this);
},
beforeMount: function() {
//模板加载之后,数据初始渲染(挂载)之前,可以修改/获取data中的值
//this.message= "beforeMount";
showData('挂载到dom前', this);
},
mounted: function() {
//数据初始渲染(挂载)之后,可以对data中的变量进行修改,但是不会影响v-once的渲染
//this.message= "mounted";
showData('挂载到dom后', this);
},
beforeUpdate: function() {
//数据渲染之后,当data中的数据发生变化触发重新渲染,渲染之前执行此函数
//data数据被修改之后,重新渲染到页面之前
//this.message= "beforeUpdate";
showData('数据变化更新前', this);
},
updated: function() {
//data数据被修改之后,重新渲染到页面之后
//this.message= "updated";
showData('数据变化更新后', this);
},
beforeDestroy: function() {
showData('vue实例销毁前', this);
},
destroyed: function() {
showData('vue实例销毁后', this);
}
});
function realDom() {
console.log('真实dom结构:' + document.getElementById('app').innerHTML);
}
function showData(process, obj) {
console.log(process);
console.log('data 数据:' + obj.message)
console.log('挂载的对象:')
console.log(obj.$el)
realDom();
console.log('------------------')
console.log('------------------')
}
// vm.message = "good...";
vm.$destroy();
</script>
钩子函数
为了便于开发者在vue实例生命周期的不同阶段进行特定的操作,vue在生命周期四个阶段的前后分别提供了一个函数,这个函数无需开发者调用,当vue实例到达生命周期的指定阶段会自动调用对应的函数。
vue对象初始化过程中,会执行到beforeCreate,created,beforeMount,mounted这几个钩子
beforeCreate
数据还没有监听,没有绑定到vue对象实例,同时也没有挂载对象
created
数据已经绑定到了对象实例,但是还没有挂载对象
beforeMount
模板已经编译好了,根据数据和模板已经生成了对应的元素对象,将数据对象关联到了对象的el属性,el属性是一个HTMLElement对象,也就是这个阶段,vue实例通过原生的createElement等方法来创建这个html片段,准备注入到vue实例指明的el属性所对应的挂载点
mounted
将el的内容挂载到了el,相当于在jquery执行了(el).html(el),生成页面上真正的dom,然后就会发现dom的元素和el的元素是一致的。在此之后,就能够用方法来获取到el元素下的dom对象,并进行各种操作
当data发生改变时,会调用beforeUpdate和updated方法
beforeUpdate
数据更新到dom之前,我们可以看到$el对象已经修改,但是我们页面上dom的数据还没有发生改变
updated:
dom结构会通过虚拟dom的原则,找到需要更新页面dom结构的最小路径,将改变更新到dom上面,完成更新
实例的销毁
beforeDestroy,destroed
实例的销毁,vue实例还是存在的,只是解绑了事件的监听还有watcher对象数据与view的绑定,即数据驱动
十、组件使用
通常一个应用会以一棵嵌套的组件树的形式来组织, 应用可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
组件注册
为了能在模板中使用,组件必须先注册以便 Vue 能够识别。
有两种组件的注册类型:全局注册和局部注册。
组件通过 Vue.component 全局注册
button-counter.js
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>',
methods:{
test:function(){
alert("组件中定义的函数");
}
}
})
全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
data
定义组件的模板渲染的数据
template
组件的HTML模块(HTML标签\css样式)
methods
定义组件中的标签事件绑定的JS函数
组件引用
定义组件需要依赖vue.js,在引用自定义组件的js文件之前要先引用vue.js
组件的引用必须在vue实例el指定的容器中
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="container">
<button-counter></button-counter>
</div>
<script type="text/javascript" src="js/vue.js" ></script>
<script type="text/javascript" src="js/button-counter.js" ></script>
<script type="text/javascript">
var vm = new Vue({
el:"#container",
});
</script>
</body>
</html>
组件通信
vue实例本身就是一个组件 , 在vue实例指定的el容器中引用的组件称为子组件 ,当前vue实例就是父组件
父传子
通过 Prop 向子组件传递数据 , Prop是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。
vue实例引用组件的时候,传递数据到引用的组件中
子组件
button-counter.js
一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
props: ['title'],
template: '<h3>{{ title }}</h3>',
methods:{
test:function(){
alert("组件中定义的函数");
}
}
})
父组件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="container">
<button-counter title="Blogging with Vue"></button-counter>
<!--使用 v-bind 来动态传递 prop-->
<button-counter :title=text></button-counter>
</div>
<script type="text/javascript" src="js/vue.js" ></script>
<script type="text/javascript" src="js/button-counter.js" ></script>
<script type="text/javascript">
var vm = new Vue({
el:"#container",
data(){
text:"text",
},
});
</script>
</body>
</html>
子传父
通过子组件的按钮“调用”父组件的函数,通过函数传值
子组件
button-counter.js
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 123
}
},
template: '<div><button @click=change()>子传父</button></div>',
methods:{
change(){
this.$emit("my-event",this.count);
}
}
})
父组件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="container">
<button-counter @my-event=myEvent></button-counter>
{{text}}
</div>
<script type="text/javascript" src="js/vue.js" ></script>
<script type="text/javascript" src="js/button-counter.js" ></script>
<script type="text/javascript">
var vm = new Vue({
el:"#container",
data(){
text:"text",
},
methods:{
change(data){
this.text=data;
}
}
});
</script>
</body>
</html>
十一、组件插槽
当自定义vue组件时,允许组件中的部分内容在调用组件时进行定义 , 称之为插槽。
插槽的使用
在自定义组件时通过slot
标签在组件的模版中定义插槽
子组件
button-counter.js
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
props: ['title'],
template: '<h2>1111111</h3>
<slot></slot>
<h4>33333333</h4>',
})
父组件
在父组件中调用此组件时,指定插槽填充的模版
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="container">
<button-counter >
<!--组件标签包含的HTML默认为填充到插槽的模版-->
<h3>2222222</h3>
</button-counter>
</div>
<script type="text/javascript" src="js/vue.js" ></script>
<script type="text/javascript" src="js/button-counter.js" ></script>
<script type="text/javascript">
var vm = new Vue({
el:"#container",
data(){
text:"text",
},
});
</script>
</body>
</html>
具名插槽
当组件中的插槽数量>1时,需要给组件中的slot标签添加name属性指定插槽的名字
子组件
button-counter.js
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
props: ['title'],
template: '<h2>1111111</h3>
<slot name="slot1"></slot>
<slot name="slot2"></slot>
<h4>33333333</h4>',
})
父组件
在父组件中调用此组件时,指定插槽填充的模版
<!DOCTYPE html>
<html>
<body>
<div id="container">
<button-counter >
<!--定义一个模版,填充到组件的name=slot1的插槽-->
<template slot="slot1">
<h3>slot1</h3>
</template>
<!--定义一个模版,填充到组件的name=slot2的插槽-->
<template slot="slot2">
<h3>slot2</h3>
</template>
</button-counter>
</div>
<script type="text/javascript" src="js/vue.js" ></script>
<script type="text/javascript" src="js/button-counter.js" ></script>
<script type="text/javascript">
var vm = new Vue({
el:"#container",
data(){
text:"text",
},
});
</script>
</body>
</html>
插槽作用域
子组件
button-counter.js
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
msg: "hello"
}
},
props: ['title'],
template: '<h2>1111111</h3>
<slot name="slot1" v-bind:title="msg"></slot>
<h4>33333333</h4>',
})
父组件
在填充插槽的模版上使用slot-scopt
属性获取插槽绑定的值
<!DOCTYPE html>
<html>
<body>
<div id="container">
<button-counter >
<!--在使用模版填充组件插槽时,可以使用slot-scope属性获取组件插槽绑定的数据-->
<template slot="slot1" slot-scope="res">
<h3>{{res.title}}</h3>
</template>
</button-counter>
</div>
<script type="text/javascript" src="js/vue.js" ></script>
<script type="text/javascript" src="js/button-counter.js" ></script>
<script type="text/javascript">
var vm = new Vue({
el:"#container",
data(){
text:"text",
},
});
</script>
</body>
</html>
十二、Axios
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中
vue本身不具备通信能力,通常结合axios(一个专注于异步通信的js框架)来使用
axios 数据通信
vue 数据渲染
引入Axios
1.安装axios模块
npm install axios
import axios from 'axios';
2.用script引入
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
get请求
axios.get('/user/findUserById?id=1')
.then(function (response) {
console.log(response);
}).catch(function (err) {
console.log(err);
});
axios.get('/user/findUserById', {
params: {
id: 1
}
}).then(function (response) {
console.log(response);
}).catch(function (err) {
console.log(err);
});
post请求
axios.post('/user/login', {
username: 'admin',
password: 'admin'
})
.then(function (res) {
console.log(res);
})
.catch(function (err) {
console.log(err);
});
自定义请求
自定义请求方式、请求参数、请求头、请求体(post)
axios({
url:"http://localhost:8888/user/login",
method:"post",
params:{
//设置请求行传值
username:"admin",
password: "admin"
},
headers:{
//设置请求头
},
data:{
//设置请求体(post/put)
}
}).then(function(res){
console.log(res)
});
并发请求
methods:{
test:function(){
//发送异步请求
axios.all([test1(),test2()]).then(axios.spread(function (r1, r2) {
// 两个请求现在都执行完成
console.log(r1);
console.log(r2);
}));
}
}
});
function test1() {
return axios.get('http://localhost:8888/user/findUserById?id=1');
}
function test2() {
return axios.get('http://localhost:8888/user/findUserById?id=2');
}
}
Axios使用示例
methods:{
findUserList:function(){
//定义一个变量,表明是vue对象
var _this = this;
axios.get('/user/findUserList')
.then(function (response) {
_this.userList = response.data;//响应数据给userList赋值
console.log(_this.userList);
})
.catch(function (error) {
console.log(error);
})
},
findUserById:function (userid) {
var _this = this;
axios.get('/user/findUserById',{params:{id:userid}})
.then(function (response) {
_this.user = response.data;
console.log(_this.user);
})
.catch(function (error) {
console.log(error);
})
},
updateUser:function (user) {
var _this = this;
axios.post('/user/updateUser', _this.user)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
},
//当页面加载的时候触发请求
created:function() {
this.findUserList();
}
箭头函数
axios回调函数的参数res 并不是接口返回的数据,而是表示一个响应对象;res.data才表示接口响应的数据
test:function(){
//发送异步请求
axios.get("http://localhost:8888/user/findUserById?id=2").then( (res)=>{
if(res.data.code == 200){
console.log(res.data.data);
}
});
}
十三、路由router
router是由vue官方提供的用于实现组件跳转的插件
1.引用router
npm安装
npm install vue-router
通过 Vue.use() 明确地安装路由功能
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
在线CDN
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
离线方式
<script type="text/javascript" src="js/vue.js" ></script>
<script type="text/javascript" src="js/vue-router.js"></script>
2.router例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<h1>Hello App!</h1>
<button @click="routeParams">控制台打印router信息</button>
<button @click="goBack">返回上一次路由</button>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
<script type="text/javascript">
// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)
// 1. 定义 (路由) 组件。
// 可以从其他文件 import 进来
const Foo = {template: '<div>foo</div>'}
const Bar = {template: '<div>bar</div>'}
// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
{path: '/foo', component: Foo},
{path: '/bar', component: Bar}
]
// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
})
// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
router,
methods: {
goBack() {
window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')
},
routeParams() {
console.log(this.$route)
}
}
}).$mount('#app')
// 现在,应用已经启动了!
</script>
</div>
</body>
</html>
3.动态路由匹配
通配符
通配符,*
可以匹配任意路径,使用通配符定义路径,需要注意路由声明的顺序
/user-*
匹配所有以user-
开头的任意路径/*
匹配所有路径
const routes = [
{path: '/foo-*', component: Foo},
{path: '/*', component: Bar}
]
路由参数
/user/:id, 可以匹配 /user/
开头的路径
<router-link to="/user/123">Go to User</router-link>
const routes = [
// 动态路径参数 以冒号开头
{path: '/user/:id', component: User },
]
像 /user/foo 和 /user/bar 都将映射到相同的路由
一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。于是,我们可以更新 User 的模板,输出当前用户的 ID
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}
路由中设置多段“路径参数”
模式| 匹配路径| $route.params
|—|–|–|–|–
/user/:username| /user/evan| { username: ‘evan’ }
/user/:username/post/:post_id| /user/evan/post/123 |{ username: ‘evan’, post_id: ‘123’ }
优先级
同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:路由定义得越早,优先级就越高。
嵌套路由
在一级路由的组件中显示二级路由
组件来导航
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/user/123">Go to User</router-link>
<router-link to="/user/foo">Go to Foo</router-link>
<router-link to="/user/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- <router-view> 是最顶层的出口,渲染最高级路由匹配到的组件 -->
<router-view></router-view>
定义 (路由) 组件
const Foo = {template: '<div>foo</div>'}
const Bar = {template: '<div>bar</div>'}
<!--个被渲染组件同样可以包含自己的嵌套 <router-view>-->
const User = {
template: `
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
`
}
定义路由
<!--在嵌套的出口中渲染组件,需要在 VueRouter 的参数中使用 children 配置-->
const routes = [
{
path: '/user/:id',
component: User,
children: [
{
// 当 /user/:id/foo 匹配成功,
// Foo 会被渲染在 User 的 <router-view> 中
path: 'foo',
component: Foo
},
{
// 当 /user/:id/bar 匹配成功
// Bar 会被渲染在 User 的 <router-view> 中
path: 'bar',
component: Bar
}
]
}
]
4.编程式导航
除了使用 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。
在 Vue 实例内部,可以通过 $router 访问路由实例。因此可以调用 this.$router.push。
想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
当点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="..."> 等同于调用 router.push(...)。
声明式 | 编程式 |
---|---|
<router-link :to='..' > |
router.push(…) |
push()
点击事件
<button @click="goUser">Go to User</button>
导航
<router-link to="/user/bar">Go to Bar</router-link>
<router-view></router-view>
路由组件
const User = {
template: `
<div>
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
`
}
路由规则
{path: '/user/:id',component: User}
点击事件出发路由规则
methods: {
goUser() {
//代码实现路由跳转:编程式导航
this.$router.push("/user/456");
},
}
push()参数
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
replace()
跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
声明式 | 编程式 |
---|---|
<router-link :to="..." replace> |
router.replace(…) |
go()
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似window.history.go(n)。
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 前进 3 步记录
router.go(3)
// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)
goBack() {
window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')
},
5.命名路由
在定义路由的时候可以给路由指定name,在进行路由导航时可以通过路由的名字导航
<div id="app">
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
<router-view></router-view>
<script type="text/javascript">
const User = {
template: `
<div>
<h2>User {{ $route.params.userId }}</h2>
</div>
`
}
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
const app = new Vue({
router,
}).$mount('#app')
</script>
</div>
等同于代码调用 router.push()
router.push({ name: 'user', params: { userId: 123 } })
6.命名路由视图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="container">
<router-link to="/a">aa</router-link>
<router-link to="/b">bb</router-link>
<!--路由视图-->
<!--如果有一个以上的路由视图router-view,需要给router-view指定name,在路由中使用components映射多个组件根据name设置组件与router-view绑定关系-->
<router-view name="v1"></router-view>
<router-view name="v2"></router-view>
<!--如果 router-view 没有设置名字,那么默认为 default。-->
<router-view ></router-view>
</div>
<script type="text/javascript">
const component1 = {
template: "<div>component1---v1---路由视图</div>"
};
const component2 = {
template: "<div>component2---v2---路由视图</div>"
};
const component3 = {
template: "<div>component3---v1---路由视图</div>"
};
const component4 = {
template: "<div>component4---v2---路由视图</div>"
};
const component5 = {
template: "<div>component5---default---路由视图</div>"
};
const router = new VueRouter({
routes: [
{
path: "/a",
components: {
default: component5,
v1: component1,
v2: component2
}
},
{
path: "/b",
components: {
default: component5,
v1: component3,
v2: component4
}
}
]
});
var vm = new Vue({
el: "#container",
router: router
});
</script>
</body>
</html>
7.重定向和别名
当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b
重定向
从 /a 重定向到 /b
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
路由命名重定向
重定向的目标也可以是一个命名的路由
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
路由别名
/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
8.路由组件传参
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
通过/url/:attr
方式实现路由传值给组件,存在$route耦合
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
routes: [{ path: '/user/:id', component: User }]
})
通过 props 解耦
<router-link to="/user/123">User</router-link>
<router-view></router-view>
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true },
// 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
})
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/137054.html