二、Vue常用操作
Vue常用操作
一、Vue指令
内容渲染指令
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
<style>
[v-clock] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<!-- v-tetx相当于innerText,把username 对应的值渲染到第一个p标签中 -->
<p v-text="username">姓名</p>
<!-- {{}}插值运算把gender对应的值渲染第二个p标签中-->
<p>性别:{{gender}}</p>
<!-- v-html相当于innerHTML,把包含HTML标签的字符串渲染到页面上-->
<p v-html="hobby">爱好</p>
<hr />
<!-- v-once元素和组件只会被渲染一次 -->
<p v-once>原始值:{{msg}}</p>
<p>后面值:{{msg}}</p>
<input type="text" v-model="msg" />
<!-- v-pre跳过元素和子元素的编译过程,原始展示 -->
<p v-pre>{{msg}}}</p>
<!-- 解决因为网络延迟导致的插值运算值还没有及时渲染时情况 -->
<p v-clock>{{msg}}</p>
</div>
<script>
const vm = new Vue({
el:'#app',
data:{
username:'张三',
gender:'男',
hobby:'<font color="red">王者荣耀</font>',
msg:'这是一条测试消息'
}
});
</script>
</body>
</html>
属性绑定指令
v-bind动态地绑定一个或多个特性。给一个标签动态绑定一个属性、样式、一些类,或者将一个对象绑定到一个标签上。鉴于v-bind使用较多,官方推荐使用简写 :属性。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
<style>
.active {
background-color: orange;
}
.fontActive {
font-size: 20px;
}
</style>
</head>
<body>
<div id="app">
<!-- 绑定一个属性 ,v-bind可以简写-->
<img v-bind:src="imgSrc" height="100" width="100" />
<img :src="imgSrc" height="100" width="100"/>
<!-- 绑定一个样式 -->
<p :style='{color:fontColor}'>我爱Vue</p>
<!-- 绑定一个类 -->
<p v-for="(course,index) in courses" :class='index==acticeIndex?"active":""'>
{{course}}
</p>
<p :class=['active',{'fontActive':bool}]>多个样式</p>
<!-- 绑定一个有属性的对象-->
<p v-bind="{id:1,name:'mytext'}">绑定有属性的对象</p>
</div>
<script>
const vm = new Vue({
el:'#app',
data:{
//绑定属性
imgSrc:'img/1.jpeg',
//绑定样式
fontColor:'red',
bool:true,
//绑定class
courses:['html','css','javascript'],
acticeIndex:2
}
});
</script>
</body>
</html>
- 事件绑定指令
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
<style>
.active {
background-color: orange;
}
.fontActive {
font-size: 20px;
}
</style>
</head>
<body>
<div id="app">
<h3>count的值为: {{count}}</h3>
<!-- "v-on:事件"完整写法 -->
<button v-on:click="addCount">+1</button>
<!-- "v-on:事件"简写形式,把v-on:简写为 @符号 -->
<!-- 如果事件处理函数中的代码足够简单,只有一行代码,则可以简写到行内 -->
<button @click="count+=1">+1</button>
</div>
<script>
const vm = new Vue({
el:'#app',
data:{
count:0
},
methods:{
addCount(){
this.count+=1;
}
}
});
</script>
</body>
</html>
1.绑定事件并传参
2.$event
$event使用
3.事件修饰符
事件修饰符 | 说明 |
.prevent | 阻止默认行为(例如:阻止a链接的跳转、阻止表单的提交等) |
.stop | 阻止事件冒泡 |
.capture | 以捕获模式触发当前的事件处理函数 |
.once | 绑定的事件只能触发一次 |
.self | 只有在event.target 是当前元素自身时触发事件处理函数 |
4.按键修饰符
双向绑定指令
事件修饰符 | 说明 | 示例 |
.number | 自动将用户的输入值转为数值类型 | <input v-model.number="age" /> |
.trim | 自动过滤用户输入的首尾空白字符 | <input v-model.trim="msg" /> |
.lazy | 在“change”时而非“input”时更新 | <input v-model.lazy="msg" /> |
条件渲染指令
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
<div v-show="score<60">差</div>
<div v-show="score>=60&&score<90">良</div>
<div v-show="score>=90">优秀</div>
<!-- v-if、v-else-if、v-else相当于if、else if、else,只会展示符合条件的元素,其他元素均移除 -->
<div v-if="score<60">差</div>
<div v-else-if="score>=60&&score<90">良</div>
<div v-else>优秀</div>
</div>
<script>
const vm = new Vue({
el:'#app',
data:{
score:80
}
});
</script>
</body>
</html>
⚫ v-if 指令会动态地创建或移除 DOM 元素,从而控制元素在页面上的显示与隐藏;⚫ v-show 指令会动态为元素添加或移除 style="display: none;" 样式,从而控制元素的显示与隐藏;
v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此:⚫ 如果需要非常频繁地切换,则使用 v-show 较好⚫ 如果在运行时条件很少改变,则使用 v-if 较好
列表渲染指令
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
<!--遍历数组-->
<p v-for="(score,index) in scores">{{index +':'+score}}</p>
<!-- 遍历对象数组 -->
<table border="1" align="center" width="300">
<thead style="background-color: orange;">
<tr align="center">
<td>姓名</td>
<td>年龄</td>
<td>性别</td>
</tr>
</thead>
<tbody>
<tr v-for="p in persons" align="center">
<td>{{p.name}}</td>
<td>{{p.age}}</td>
<td>{{p.sex}}</td>
</tr>
</tbody>
</table>
<!-- 对字符串进行遍历 -->
<p v-for ="str in hobby">
{{str}}
</p>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
scores: [100, 90, 80, 70],
persons: [
{ name: '张三', age: 23, sex: '男' },
{ name: '李四', age: 24, sex: '女' },
{ name: '王五', age: 25, sex: '男' },
{ name: '赵六', age: 26, sex: '女' },
],
hobby:'篮球'
}
});
</script>
</body>
</html>
二、过滤器
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
<!-- 私有过滤器使用 -->
<p>{{sex | sexFormatter}}</p>
<!-- 全局过滤器使用 -->
<p>{{str | capitalize}}</p>
<!-- 连续调用多个过滤器 -->
<p>{{message | filterA | filterB}}</p>
<!-- 过滤器传参 -->
<p>{{message | maxLength(5)}}</p>
</div>
<script>
//全局过滤器 -独立于每个vm实例之外,必须定义在vm实例之前
//Vue.filter()方法接受两个参数:
//第一个参数,是全局过滤器的"名字"
//第二个参数,是全局过滤器的"处理函数"
Vue.filter('capitalize',function(value){
return value.toUpperCase().split('').reverse().join('');
});
//全局过滤器 - 控制文本的最大长度
Vue.filter('maxLength',function(str,len = 4){
if(str.length<=len) return str;
return str.slice(0,len)+'...';
});
const vm = new Vue({
el:'#app',
data:{
sex:0,
str: 'abc',
message:'chensheng'
},
filters:{ //局部过滤器
sexFormatter(sex){
if(sex==0){
return "男";
}else{
return "女";
}
},
filterA(value){ //将首字母大写
return value.charAt(0).toUpperCase()+value.slice(1);
},
filterB(value){ //将字符串反转
return value.split('').reverse().join('');
}
}
});
</script>
</body>
</html>
三、监听器
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
firstName:<input type="text" v-model="firstName" />
lastName:<input type="text" v-model="lastName" />
<h2>{{fullName}}</h2>
person:<input type="text" v-model="person.username" />
</div>
<script>
const vm = new Vue({
el:'#app',
data:{
firstName:'',
lastName:'',
fullName:'',
person:{username:'admin'}
},
watch:{
firstName(newVal,oldVal){
//在vue中,操作data、method必须使用this
this.fullName = this.firstName + this.lastName;
},
lastName(newVal,oldVal){
this.fullName = this.firstName + this.lastName;
},
person:{ //监听对象内属性变化,handler是固定写法,当username值变化时,自动调用handler处理函数
handler(newVal){
console.info('如果监听对象内属性变化,则使用这种方式'+newVal.username);
},
deep: true, //如果watch侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到,需要使用deep选项才能侦听
immediate:true //表示页面初次渲染好之后,就立即触发当前的watch侦听器
},
'person.username':{
handler(newVal){
console.info('如果只想监听对象单个属性变化,则使用这种方式'+newVal)
}
}
}
});
</script>
</body>
</html>
watch监听器使用
四、计算属性
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
<p @click='show()'>计算属性: {{reverseMsg}}</p>
</div>
<script>
const vm = new Vue({
el:'#app',
data:{
msg: 'hello world'
},
computed: {
//计算属性的getter
reverseMsg: function() {
//this指向vm实例
return this.msg.split('').reverse().join('');
}
},
methods:{
show(){
console.info(this.reverseMsg); //在methods中调用属性
}
}
});
</script>
</body>
</html>
计算属性和method区别?
- 当页面重新渲染(不是刷新)的时候,计算属性不会变化,直接读取缓存使用,适合较大量的计算和改变频率较低的属性;而method就是当页面重新渲染的时候(页面元素的data变化,页面就会重新渲染),都会重新调用method。
- 计算属性是一个属性,而不是方法。虽然写法是方法,但是我们使用的时候是直接视为一个属性去操作的,使用方式和data一致。
- 计算属性中使用到的data中的数据只要发生了变化,计算属性就会重新计算。如果两次获取计算属性的时候,里面使用到的属性没有发生变化,计算属性会直接使用之前缓存的值。
注意点:
- 计算属性。在computed中,可以定义一些属性,这些属性叫做计算属性,他本质上就是一个方法,但是,我们使用的时候是当做属性来使用。计算属性使用的过程中,一定不要加(),它就是一个属性,和data里的属性用法一样。
- 只要极速暗属性中所用到的data中的属性发生了变化,就会立即去重新计算属性的值。
- 计算属性的结果会被缓存起来,方便下次调用,如果计算属性中用到的data都没有变化,就不会重新求值。
- 在计算属性中,无论如何都需要return一个值,用来代表这个属性的值。
计算属性和监听器的区别:
- Computed用法视为一个属性,并且结果会被缓存。
- Methods表示方法,表示一个具体的操作,主要用来写业务逻辑
- Watch是一个对象,key是需要监听的表达式,value是对应的回调函数,主要用来监听某些数据的变化,从而执行某些具体的业务逻辑。可以看做是计算属性和methods的结合体。
五、全局组件
组件是可复用的Vue实例,且带一个名字,可以复用。我们搭建的页面其实是一个个组件组装而成,可以分为局部组件和全局组件。
- 局部组件:只属于当前Vue的实例,其他实例无法共享这个局部组件。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
<my-date></my-date>
</div>
<script>
//1、创建组件构造器
let partialTemplate = Vue.extend({
template:`
<div>
<input type='date'/>
<p>今天天气好晴朗!</p>
</div>
`
});
const vm = new Vue({
el: '#app',
// 注册局部组件,只属于当前的vue实例
components:{
'my-date':partialTemplate
}
});
</script>
</body>
</html>
局部组件简洁写法:
const vm = new Vue({
el: '#app',
// 注册局部组件,只属于当前的vue实例
components:{
'my-date':{
template:`
<div>
<input type='date'/>
<p>今天天气好晴朗!</p>
</div>
`
}
}
});
测试结果:
- 全局组件:属于所有的Vue实例所有。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
<my-date></my-date>
</div>
<script>
//1. 创建组件构造器
let ProFile = Vue.extend({
//1.1 模板选项
template:`
<div>
<input type="date"/>
<p>今天天气好晴朗!</p>
</div>
`
});
//2. 注册全局组件,参数1自定义组件名称,参数2组件
Vue.component('my-date',ProFile);
const vm = new Vue({
el: '#app'
});
</script>
</body>
</html>
六、父子组件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
<parent></parent>
</div>
<script>
// 1. 子组件构造器
let Child1 = Vue.extend({
template: `<img src="img/estar.ico" width="200">`
});
let Child2 = Vue.extend({
template: `<p>孤独的星星!</p>`
});
// 2. 父组件构造器
Vue.component('parent', {
components: { //注册子组件
'my-child1': Child1,
'my-child2': Child2
},
template: ` //组装父组件
<div>
<my-child1></my-child1>
<my-child2></my-child2>
</div>
`
});
new Vue({
el: '#app'
});
</script>
</body>
</html>
- template标签和script标签注册组件
一、使用template来注册组件【常用】
<body>
<div id="app">
<!-- 使用父组件 -->
<my-div></my-div>
</div>
<template id="parent">
<!-- 只能有一个出口,最外层必须有标签包裹 -->
<div>
<img src="img/estar.ico" width="200">
<p>孤独的星星!</p>
</div>
</template>
<script>
//实例化组件
Vue.component('my-div', {
template: '#parent'
});
new Vue({
el: '#app'
});
</script>
</body>
二、使用script来注册组件,必须声明类型type=”text/template” 【不推荐使用】
<body>
<div id="app">
<!-- 使用父组件 -->
<my-div></my-div>
</div>
<script type="text/template" id="parent">
<!-- 只能有一个出口,最外层必须有标签包裹 -->
<div>
<img src="img/estar.ico" width="200">
<p>孤独的星星!</p>
</div>
</script>
<script>
//实例化组件
Vue.component('my-div', {
template: '#parent'
});
new Vue({
el: '#app'
});
</script>
</body>
- 组件中data使用
u 我们将data定义成函数,这样每个按钮点击后都会产生自己独享的变量,而不是共享的变量。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
<!-- 使用组件 -->
<my-btn></my-btn>
<my-btn></my-btn>
<my-btn></my-btn>
</div>
<template id="btn_group">
<button @click="counter++">按钮点击次数{{counter}}</button>
</template>
<script>
//实例化组件
Vue.component('my-btn', {
template: '#btn_group',
data() {
return { counter: 0 }
}
});
new Vue({
el: '#app'
});
</script>
</body>
</html>
测试结果:(描述,我们点击不同的按钮统计的counter都是自己的)
u 如果我们将counter定义到外面来
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
<!-- 使用组件 -->
<my-btn></my-btn>
<my-btn></my-btn>
<my-btn></my-btn>
</div>
<template id="btn_group">
<button @click="counter++">按钮点击次数{{counter}}</button>
</template>
<script>
//组件将共享一个countDate
let countDate = { counter: 0 };
Vue.component('my-btn', {
template: '#btn_group',
data() {
return countDate;
}
});
new Vue({
el: '#app'
});
</script>
</body>
</html>
测试结果:(描述:我们点击其中一个按钮其他按钮点击次数也会随之改变,共享一个counter值)
七、组件通信
父子组件之间的通信,父组件通过 prop 给子组件下发数据,子组件通过$emit触发事件给父组件发送消息,即
prop 向下传递,事件向上传递。
父组件向子组件传值(简单的单层组件之间的值传递)
<body>
<div id="app">
<my-div :msg='孤独的星星' imgsrc='img/estar.ico'></my-div>
</div>
<template id="my_div">
<div>
<h1>{{msg}}</h1>
<img :src="imgsrc" width="100" height="100" />
</div>
</template>
<script>
//实例化组件
Vue.component('my-div', {
template: '#my_div',
props:['msg','imgsrc'] //父组件通过props向子组件传递值
});
new Vue({
el: '#app'
});
</script>
</body>
或者使用绑定的方式将父组件的值传递给子组件:
<body>
<div id="app">
<!-- 使用绑定的方式,将父组件的数据绑定给子组件 -->
<my-div :msg='msg' :imgsrc='imgsrc'></my-div>
</div>
<template id="my_div">
<div>
<h1>{{msg}}</h1>
<img :src="imgsrc" width="100" height="100" />
</div>
</template>
<script>
//实例化组件
const myCom = Vue.component('my-div', {
template: '#my_div',
props:['msg','imgsrc'] //父组件通过props向子组件传递值,或者写法如下
//props: {
// msg: {
// type: String, //type支持的类型有: String、Number、Boolean、Array、Object、Date、Function、Symbol
// default: '我是默认值,父组件没有传给我msg'
// }
// }
});
new Vue({
el: '#app',
data:{
msg:'孤独的星星',
imgsrc:'img/estar.ico'
},
components:{
myCom
}
});
</script>
</body>
父组件向子组件传值(多层组件之间的值传递)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
<!-- 使用父组件 -->
<my-parent :imgtitle="title" :imgsrc="img"></my-parent>
</div>
<!-- 定义子组件 -->
<template id="my_img">
<img :src="imgsrc" width="200" />
</template>
<template id="my_title">
<h2>{{title}}</h2>
</template>
<!-- 定义父组件 -->
<template id="my_parent">
<div>
<child1 :imgsrc="imgsrc"></child1>
<child2 :title="imgtitle"></child2>
</div>
</template>
<script>
//1.子组件的实例
let child1 = Vue.extend({
template: '#my_img',
props: ['imgsrc']
});
let child2 = Vue.extend({
template: '#my_title',
props: ['title']
});
//2.注册父组件
Vue.component('my-parent', {
props: ['imgtitle', 'imgsrc'],
components: {
'child1': child1,
'child2': child2
},
template: '#my_parent'
});
new Vue({
el: '#app',
data: {
title: '孤独的星星',
img: 'img/estar.ico'
}
});
</script>
</body>
</html>
父组件向子组件传递事件
我们知道父组件是使用props传递数据给子组件,但如果子组件要把数据传递回去,则应该使用自定义事件来完成!
描述: 我们在父组件中自定义一个total事件,当我们触发这个total()事件的时候通过$emit('total')来触发外界定义的@total="allcounter()"中
的具体的方法allcounter()。注意在此案例中按钮与按钮之间是相互独立的,互不影响。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
<!—自定义事件-->
<my-btn @total="allcounter()"></my-btn>
<my-btn @total="allcounter()"></my-btn>
<my-btn @total="allcounter()"></my-btn>
<my-btn @total="allcounter()"></my-btn>
<p>所有按钮一共点击了{{totalCounter}}次</p>
</div>
<template id="my_btn">
<button @click="total()">点击了{{counter}}次</button>
</template>
<script>
Vue.component('my-btn', {
template: '#my_btn',
data() {
return {
counter: 0
}
},
methods: {
total() {
this.counter += 1; //不同的按钮享有不同的counter
// 通知外界,我调用了这个方法
this.$emit('total');
}
}
});
new Vue({
el: '#app',
data: {
totalCounter: 0 //公共的数据
},
methods: {
allcounter() {
this.totalCounter += 1;
}
}
});
</script>
</body>
</html>
测试结果:(描述我们低级不同的按钮分别统计不同的按钮点击次数,互不影响)
父组件调用子组件方法
$refs是和ref一起使用的。通过ref给某个子组件绑定一个特定的ID,然后我们使用$refs.ID就可以访问到子组件了。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
<button @click="countAdd">点我</button>
<!-- 第一步,给子组件绑定一个ref -->
<my-com ref="myComp"></my-com>
</div>
<template id="myTemp">
<div>
{{count}}
</div>
</template>
<script>
let myCom = Vue.extend({
template: '#myTemp',
data() {
return {
count: 1
}
},
methods: {
addCount() {
this.count++
}
}
})
let app = new Vue({
el: '#app',
methods: {
countAdd() {
// 第二步,在父组件中使用this.$refs.id就行了
console.log(this.$refs.myComp.count)
this.$refs.myComp.addCount()
}
},
components: {
myCom
}
})
</script>
</body>
</html>
八、插槽
Slot插槽实现内容分发,结合日常生活中的电路板,有指定插孔,我们可以插入任何电器插头(匿名插槽),也有专门针对手机的USB插头(实名插槽),无法插入其他电器插头。
- 匿名插槽
<body>
<div id="app">
<my-slot>
<img src="img/estar.ico" width="200" alt="">
<p>{{msg}}</p>
</my-slot>
</div>
<template id="my_slot">
<div>
<header>插槽的头部</header>
<!--预留一个插槽,没有则显示该插槽内容-->
<slot>可以替换任何标签,默认显示提示的内容</slot>
<footer>插槽的尾部</footer>
</div>
</template>
<script>
Vue.component('my-slot', {
template: '#my_slot'
});
new Vue({
el: '#app',
data: {
msg: '孤独的星星'
}
});
</script>
</body>
测试结果:匿名插槽
- 实名插槽
<body>
<div id="app">
<my-computer>
<div slot="cpu">core-i7 6700HQ处理器</div>
<div slot="memory">Kingston-32G内存条</div>
</my-computer>
</div>
<template id="my_computer">
<div>
<slot name="cpu">默认插cpu的</slot>
<slot name="memory">默认插内存条的</slot>
<slot name="hard-drive">默认插硬盘的</slot>
</div>
</template>
<script>
Vue.component('my-computer', {
template: '#my_computer'
});
new Vue({
el: '#app'
});
</script>
测试结果:实名插槽
九、路由
- 后端路由:对于普通的网站,所有的超链接都是url地址,所有url都对应服务器上对应的资源
- 前端路由:对于单页面应用程序来说,主要通过url的hash(#)来实现不同页面的切换,同时hash还有一个特点HTTP请求中不会包含hash相关的内容,所以单页面程序中的页面跳转主要用hash实现
在单页面应用程序中这种通过hash来改变页面的方式称作前端路由区别于后端路由
- 引入vue-router
- 路由入门
创建一个路由对象,当导入vue-router包之后,在window全局对象中就有一个路由的构造函数VueRouter在new路由对象的时候可以传递一个配置对象,这个配置对象的route表示路由器的匹配规则每个路由规则都是一个对象,这个规则对象身上必须有两个属性
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="lib/vue-2.6.12.js"></script>
<script src="lib/vue-router.js"></script>
<style>
ul li {
float: left;
width: 300px;
height: 70px;
box-sizing: border-box;
background-color: pink;
list-style: none;
line-height: 70px;
text-align: center;
}
ul li a {
text-decoration: none;
font-size: 20px;
}
.content {
clear: both;
width: 900px;
box-sizing: border-box;
border: 1px solid blue;
padding-left: 20px;
margin-left: 40px;
}
</style>
</head>
<body>
<div id="app">
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<ul>
<li>
<router-link to="/h5">HTML5学院</router-link>
</li>
<li>
<router-link to="/python">Python学院</router-link>
</li>
<li>
<router-link to="/java">Java学院</router-link>
</li>
</ul>
<div class="content">
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
</div>
<template id="h5">
<div>
<h2>HTML5学院</h2>
<p>掌握面向未来的神技!</p>
</div>
</template>
<template id="python">
<div>
<h2>Python学院</h2>
<p>人工智能的最佳选择!</p>
</div>
</template>
<template id="java">
<div>
<h2>Java学院</h2>
<p>掌握面向工资编程!</p>
</div>
</template>
<script>
//1.创建组件
const Html5 = Vue.extend({
template: '#h5'
});
const Python = Vue.extend({
template: '#python'
});
const Java = Vue.extend({
template: '#java'
});
//2.定义路由
const routes = [
{ path: '/h5', component: Html5 },
{ path: '/python', component: Python },
{ path: '/java', component: Java },
//配置根路由,默认显示
{ path: '/', redirect: '/h5' }
];
//3.创建路由实例
const router = new VueRouter({
routes
});
//4.创建Vue实例并挂载
new Vue({
router
}).$mount('#app');
</script>
</body>
</body>
</html>
测试结果: (描述:我们点击不同的tab切换页能实现展示不同的内容)
<body>
<div id="app">
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<ul>
<li><router-link :to="{name:'h5'}">HTML5学院</router-link></li>
<li><router-link :to="{name:'python'}">Python学院</router-link></li>
<li><router-link :to="{name:'java'}">Java学院</router-link></li>
</ul>
<div class="content">
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
</div>
<template id="h5">
<div>
<h2>HTML5学院</h2>
<p>掌握面向未来的神技!</p>
</div>
</template>
<template id="python">
<div>
<h2>Python学院</h2>
<p>人工智能的最佳选择!</p>
</div>
</template>
<template id="java">
<div>
<h2>Java学院</h2>
<p>掌握面向工资编程!</p>
</div>
</template>
<script>
//1.创建组件
const Html5 = Vue.extend({
template: '#h5'
});
const Python = Vue.extend({
template: '#python'
});
const Java = Vue.extend({
template: '#java'
});
//2.定义路由
const routes = [
{ path: '/h5', component: Html5,name:'h5' },
{ path: '/python', component: Python,name:'python' },
{ path: '/java', component: Java ,name:'java'},
//配置根路由,默认显示
{ path: '/', redirect: '/h5' }
];
//3.创建路由实例
const router = new VueRouter({
routes
});
//4.创建Vue实例并挂载
new Vue({
router
}).$mount('#app');
</script>
</body>
多级子路由
<body> <div id="app"> <!-- 使用 router-link 组件来导航. --> <!-- 通过传入 `to` 属性指定链接. --> <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 --> <ul> <li><router-link to="/h5">HTML5学院</router-link></li> <li><router-link to="/python">Python学院</router-link></li> <li><router-link to="/java">Java学院</router-link></li> </ul> <div class="content"> <!-- 路由出口 --> <!-- 路由匹配到的组件将渲染在这里 --> <router-view></router-view> </div> </div> <template id="h5"> <div> <h2>HTML5学院</h2> <p>掌握面向未来的神技!</p> </div> </template> <template id="python"> <div> <h2>Python学院</h2> <p>人工智能的最佳选择!</p> </div> </template> <template id="java"> <div> <h2>Java学院</h2> <p>掌握面向工资编程!</p> <!--子组件路由--> <router-link to="/java/base">基础班</router-link> <router-link to="/java/advance">进阶班</router-link> <div> <!-- 子组件出口 --> <router-view></router-view> </div> </div> </template> <!-- 定义Java组件下的子组件 --> <template id="base"> <div> <h3>Java基础班</h3> <p>快速掌握Java的三大特性!</p> </div> </template> <template id="advance"> <div> <h3>Java进阶班</h3> <p>掌握高并发高可用的架构!</p> </div> </template> <script> //1.创建组件 const Html5 = Vue.extend({ template: '#h5' }); const Python = Vue.extend({ template: '#python' }); const Java = Vue.extend({ template: '#java' }); //创建Java的子组件 const base = Vue.extend({ template: '#base' }); const advance = Vue.extend({ template: '#advance' }); //2.定义路由 const routes = [ { path: '/h5', component: Html5 }, { path: '/python', component: Python }, { path: '/java', component: Java, children: [ //使用children属性实现路由嵌套,子路由path前不要加/,否则永远以根路径开始请求 {path:'base', component: base}, {path:'advance', component: advance}, // 配置Java组件的根路由,默认显示 {path:'/', redirect: 'base'} ] }, //配置根路由,默认显示 { path: '/', redirect: '/h5' } ]; //3.创建路由实例 const router = new VueRouter({ routes }); //4.创建Vue实例并挂载 new Vue({ router }).$mount('#app'); </script> </body>
测试结果:
- 路由传参
- 如果使用查询字符串 给路由传递参数则不需要修改路由规则的path属性,直接在组件当中使用{$route.query.字段名}}即可获取值。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="lib/vue-2.6.12.js"></script>
<script src="lib/vue-router.js"></script>
</head>
<body>
<div id="app">
<!-- 路由入口 -->
<router-link to="/login?username=zs&password=123">登录</router-link>
<div>
<!-- 路由出口 -->
<router-view></router-view>
</div>
</div>
<template id="userInfo">
<div>
用户:{{$route.query.username}},密码:{{$route.query.password}}
</div>
</template>
<script>
//1.创建组件
const userInfo = Vue.extend({
template: '#userInfo'
});
//2.定义路由
const routes = [
{ path: '/login', component: userInfo },
//配置根路由,默认显示
{ path: '/', redirect: 'login' }
];
//3.创建路由实例
const router = new VueRouter({
routes
});
//4.创建Vue实例并挂载
new Vue({
router
}).$mount('#app');
</script>
</body>
</html>
<router-link to="/login/zs">登录</router-link>
{ path: '/login/:username', component: userInfo }
<template id="userInfo">
<div>
用户:{{$route.params.username}}
</div>
</template>
十、过渡动画
Vue中为我们提供了简单的过渡动画效果,只需要按照vue提供的标准去写一套固定的CSS即可。
<style>
/* v-enter 这个是一个时间点,表示动画进入之前,元素的起始状态,此时还没有进入 */
/* v-leave-to 这是一个时间点,是动画离开之后,元素的终止状态,此时,元素动画已经结束了 */
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(50px);
}
/* v-enter-active 表示入场动画的时间段 */
/* v-leave-active 表示离场动画的时间段 */
.v-enter-active,
.v-leave-active {
transition: all 0.8s ease;
}
</style>
<body>
<div id="app">
<button @click="flag = !flag">点击</button>
<transition>
<div v-show="flag">这个是第一个div</div>
</transition>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
flag: true
},
methods: {
}
})
</script>
<transition>
<div v-show="flag">这个是第一个div</div>
</transition>
<transition>
<div v-show="!flag">这个是第二个div</div>
</transition>
如果div1和div2不想公用一套动画,应该怎么办?这时候我们可以给transition命名,使用name属性即可。定义了name之后,两个transition则使用自己独立的动画效果。定义了name之后,动画的相关class,要以name开头,不能以v开头。
<transition name="div1">
<div v-show="flag">这个是第一个div</div>
</transition>
<transition name="div2">
<div v-show="!flag">这个是第二个div</div>
</transition>
<style>
/* v-enter 这个是一个时间点,表示动画进入之前,元素的起始状态,此时还没有进入 */
/* v-leave-to 这是一个时间点,是动画离开之后,元素的终止状态,此时,元素动画已经结束了 */
.div1-enter,
.div1-leave-to {
opacity: 0;
transform: translateX(50px);
}
/* v-enter-active 表示入场动画的时间段 */
/* v-leave-active 表示离场动画的时间段 */
.div1-enter-active,
.div1-leave-active {
transition: all 0.8s ease;
}
/* v-enter 这个是一个时间点,表示动画进入之前,元素的起始状态,此时还没有进入 */
/* v-leave-to 这是一个时间点,是动画离开之后,元素的终止状态,此时,元素动画已经结束了 */
.div2-enter,
.div2-leave-to {
opacity: 0;
transform: translateX(50px);
}
/* v-enter-active 表示入场动画的时间段 */
/* v-leave-active 表示离场动画的时间段 */
.div2-enter-active,
.div2-leave-active {
transition: all 0.2s ease;
}
</style>
十一、生命周期
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="lib/vue-2.6.12.js"></script>
</head>
<body>
<div id="app">
<div id="divId">页面还没有渲染 --- {{msg}}</div>
</div>
</body>
<script>
var vue = new Vue({
el: '#app',
data: {
msg: '页面渲染完毕'
},
methods: {},
filters: {},
beforeCreate() {
// 这是我们第一个遇到的生命周期函数,实例被创建出来,还没有data、methods等时,会执行它
let content = document.getElementById('divId')
console.log('beforeCreate:', content.innerText)
// 在js中,null和undefined是不同的含义。null表示有这个对象,但是这个对象的值是null。undefined表示压根就没有这个对象
console.log('beforeCreate', this.msg)
},
created() {
// Vue实例创建完毕,methods、data、filters等已经挂载到vue实例上。如果需要调用methods,使用data,最早只能在这里操作
let content = document.getElementById('divId')
console.log('created', content.innerText)
console.log('created', this.msg)
},
beforeMount() {
// Vue实例创建完毕,页面尚未重新渲染
let content = document.getElementById('divId')
console.log('beforeMounte', content.innerText)
console.log('beforeMounte', this.msg)
},
mounted() {
// 页面渲染完毕
let content = document.getElementById('divId')
console.log('mounted', content.innerText)
console.log('mounted', this.msg)
}
})
</script>
</html>
十二、实战演练
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!-- 引入layui样式 -->
<link rel="stylesheet" href="lib/layui/css/layui.css" />
<!--引入vue.js-->
<script src="lib/vue-2.6.12.js"></script>
<!-- 引入layui.js -->
<script src="lib/layui/layui.all.js"></script>
</head>
<body>
<div id="app">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
<legend>员工管理</legend>
</fieldset>
<!-- 搜索栏 -->
<form class="layui-form">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">姓 名:</label>
<div class="layui-input-inline">
<input type="text" v-model="searchName" placeholder="请输入姓名" class="layui-input">
</div>
</div>
<div class="layui-inline">
<button type="button" class="layui-btn layui-btn-normal layui-icon layui-icon-search" @click="searchUser">查询</button>
<button type="reset" class="layui-btn layui-btn-warm layui-icon layui-icon-refresh">重置</button>
<button type="button" class="layui-btn layui-btn layui-icon layui-icon-addition" @click="saveOrUpdateDiv()">添加</button>
</div>
</div>
</form>
<!-- 数据表格 -->
<table class="layui-table">
<thead>
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>备注</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in userList" :key="item.id">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.gender | handlerGender}}</td>
<td>{{item.remark}}</td>
<td>
<button type="button" class="layui-btn layui-btn-normal" @click="saveOrUpdateDiv(item)">编辑</button>
<button type="button" class="layui-btn layui-btn-danger" @click="delUser(item.id)">删除</button>
</td>
</tr>
</tbody>
</table>
<!-- 添加和修改的弹出层开始 -->
<div style="display: none;padding: 20px" id="saveOrUpdateDiv">
<form class="layui-form">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">用户id</label>
<div class="layui-input-inline">
<input type="text" v-model="user.id" placeholder="输入用户id" class="layui-input" readonly>
</div>
<label class="layui-form-label">用户姓名</label>
<div class="layui-input-inline">
<input type="text" v-model="user.name" placeholder="请输入姓名" class="layui-input">
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">性别</label>
<div class="layui-input-inline">
<input type="text" v-model="user.gender" placeholder="请输入性别" class="layui-input">
</div>
<label class="layui-form-label">备注</label>
<div class="layui-input-inline">
<input type="text" v-model="user.remark" placeholder="请输入备注" class="layui-input">
</div>
</div>
</div>
<div class="layui-form-item" style="text-align: center;">
<div class="layui-input-block">
<button type="button" class="layui-btn layui-btn-normal layui-btn-sm layui-icon layui-icon-release" v-if="!user.id" @click="addUser">新增</button>
<button type="button" class="layui-btn layui-btn-normal layui-btn-sm layui-icon layui-icon-release" v-else @click="editUser(user)">修改</button>
<button type="reset" class="layui-btn layui-btn-warm layui-btn-sm layui-icon layui-icon-refresh">重置</button>
</div>
</div>
</form>
</div>
<!-- 添加和修改的弹出层结束 -->
</div>
</body>
<script>
var mainIndex;
const vm = new Vue({
el: '#app',
data: {
userList: [], //用户列表
data: [ //数据库,存放用户数据
{ id: 1, name: '张三', gender: 1, remark: '姓张名三' },
{ id: 2, name: '李四', gender: 2, remark: '姓李名四' },
{ id: 3, name: '王五', gender: 1, remark: '姓王名五' },
{ id: 4, name: '赵六', gender: 2, remark: '姓赵名六' }
],
user: {},
searchName: '' //搜索用的名称
},
filters: {
handlerGender(gender) {
if(gender == 1) {
return '男'
} else {
return '女'
}
}
},
methods: {
saveOrUpdateDiv(user) { //打开新增或者修改的弹出层
if(!user) {
title = '添加用户';
//清空新增弹出层数据
this.user = {};
} else {
title = '编辑'+user.name+'信息';
//回显编辑的数据,注意不能直接 this.user = user;
this.user = { ...user };
//等价于this.user = {id: user.id, name: user.name,gender: user.gender,remark: user.remark}
}
mainIndex = layui.layer.open({
type: 1,
title: title,
content: layui.jquery("#saveOrUpdateDiv"),
area: ['700px', '250px'],
maxmin: true
});
},
getUserList(name) { //加载用户数据
if(!name) {
name = '';
}
const dataList = this.data.filter(e => e.name.indexOf(name) >= 0);
this.userList = dataList;
},
addUser() { //添加用户
const user = { ...this.user };
//等价于 const user = {id: this.user.id, name: this.user.name,gender: this.user.gender,remark: this.user.remark};
//随机生成id,范围在1-100之间
user.id = Math.round(Math.random() * (100 - 1) + 1)
//添加到数据库里
this.data.push(user);
//关闭弹出层
layui.layer.close(mainIndex);
//重新加载数据
this.getUserList();
},
delUser(id) {
// 查到指定id所在数组索引下标
const index = this.userList.findIndex(e => e.id === id)
// 删除指定索引位置的数据
this.data.splice(index, 1)
// 删除成功后,重新家在数据
this.getUserList()
},
editUser(user){ //修改用户
const index = this.data.findIndex(e => e.id === user.id);
this.data[index] = user;
//关闭弹出层
layui.layer.close(mainIndex);
//重新加载数据
this.getUserList();
},
searchUser() { // 搜索用户
// 遍历用户,找到所有复合条件的用户
this.getUserList(this.searchName)
}
},
created() {
// 进入页面,组件创建之后,获取数据
this.getUserList()
}
})
</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>品牌列表案例</title>
<!-- 引入bootstrap样式-->
<link rel="stylesheet" href="lib/bootstrap.css">
<style>
body {
padding: 15px;
user-select: none;
}
</style>
</head>
<body>
<div id="app">
<!-- 卡片区域 -->
<div class="card">
<div class="card-header">
添加品牌
</div>
<div class="card-body">
<!-- 添加品牌的表单区域 -->
<!-- form 表单元素有 submit 事件 -->
<form @submit.prevent="add">
<div class="form-row align-items-center">
<div class="col-auto">
<div class="input-group mb-2">
<div class="input-group-prepend">
<div class="input-group-text">品牌名称</div>
</div>
<input type="text" class="form-control" placeholder="请输入品牌名称" v-model.trim="brand">
</div>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary mb-2">添加</button>
</div>
</div>
</form>
</div>
</div>
<!-- 表格区域 -->
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">品牌名称</th>
<th scope="col">状态</th>
<th scope="col">创建时间</th>
<th scope="col">操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in list" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>
<div class="custom-control custom-switch">
<!-- 使用 v-model 实现双向数据绑定 -->
<input type="checkbox" class="custom-control-input" :id="'cb' + item.id" v-model="item.status">
<!-- 使用 v-if 结合 v-else 实现按需渲染 -->
<label class="custom-control-label" :for="'cb' + item.id" v-if="item.status">已启用</label>
<label class="custom-control-label" :for="'cb' + item.id" v-else>已禁用</label>
</div>
</td>
<td>{{ item.time | dateFormat }}</td>
<td>
<a href="javascript:;" @click="remove(item.id)">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 只要导入了 dayjs 的库文件,在 window 全局,就可以使用 dayjs() 方法了 -->
<script src="./lib/dayjs.min.js"></script>
<script src="./lib/vue-2.6.12.js"></script>
<script>
// 声明格式化时间的全局过滤器
Vue.filter('dateFormat', function(time) {
// 1. 对 time 进行格式化处理,得到 YYYY-MM-DD HH:mm:ss
// 2. 把 格式化的结果,return 出去
// 直接调用 dayjs() 得到的是当前时间
// dayjs(给定的日期时间) 得到指定的日期
const dtStr = dayjs(time).format('YYYY-MM-DD HH:mm:ss')
return dtStr
})
const vm = new Vue({
el: '#app',
data: {
// 用户输入的品牌名称
brand: '',
// nextId 是下一个,可用的 id
nextId: 4,
// 品牌的列表数据
list: [
{ id: 1, name: '宝马', status: true, time: new Date() },
{ id: 2, name: '奔驰', status: false, time: new Date() },
{ id: 3, name: '奥迪', status: true, time: new Date() },
],
},
methods: {
show(item){
console.info(item)
},
// 点击链接,删除对应的品牌信息
remove(id) {
this.list = this.list.filter(item => item.id !== id)
},
// 阻止表单的默认提交行为之后,触发 add 方法
add() {
// 如果判断到 brand 的值为空字符串,则 return 出去
if(this.brand === '') return alert('必须填写品牌名称!')
// 如果没有被 return 出去,应该执行添加的逻辑
// 1. 先把要添加的品牌对象,整理出来
const obj = {
id: this.nextId,
name: this.brand,
status: true,
time: new Date()
}
// 2. 往 this.list 数组中 push 步骤 1 中得到的对象
this.list.push(obj)
// 3. 清空 this.brand;让 this.nextId 自增 +1
this.brand = ''
this.nextId++
}
},
})
</script>
</body>
</html>
附件列表