Vue是目前最火的前端框架,与Angular、React并称为前端三大框架。是一个轻量级的MVVM框架,通过数据驱动和组件化进行前端开发,通过简单地API就能实现响应式的数据绑定和组合的视图组件。
- Vue是目前非常火的一个前端框架,是前端程序员必备的一个技能。
- Vue发展迅速,在大多数后端程序员中,也需要会Vue。
- Vue简单易学,并且性能高效,可以很大程度上提高代码开发效率。
一、准备工作
1.vscode的下载与安装教程:https://blog.csdn.net/weixin_44195615/article/details/113856706
另附:VSCode-win32-x64-1.53.2的下载百度网盘链接
链接:https://pan.baidu.com/s/1aHQVIWfOlmgCX89PLI2e6A
提取码:dr1j
2.推荐使用火狐浏览器,并安装插件Vue.js devtools
(1)搜索:vue
点击Vue.js devtools
点击安装
3.在vscode安装Open in Browser插件
(1)点击拓展,输入Open in Browser,然后点击安装
二、快速上手
2.1 HelloVue
2.1.1编写项目
(1)新建文件HelloVue.html,然后按!选择第一个选项
(2)引入js文件
<script src="./lib/vue-2.4.0.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<h2>Hello {{name}}</h2>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
name: 'Vue'
}
})
</script>
</html>
知识讲解:
- 创建了一个Vue对象,此时在浏览器的内存中就有了一个Vue。
- 创建Vue的时候,传入了一个参数,这个参数是一个对象options
- 参数中有el属性。该属性决定了这个Vue对象挂载到哪一个元素上。
- 参数中有data属性,该属性定义存储大部分的数据。这些数据可以自己直接定义,也可以从网络中获取。
2.1.2运行:右键1.HelloVue.html,选择Open with default application
注意:是在安装插件Vue.js devtools的浏览器打开
2.1.3.运行结果
2.2 列表案例
2.2.1编写代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="booksId">
<ul>
<li v-for="content in books">{{content}}</li>
</ul>
</div>
</body>
<script>
let app = new Vue({
el: '#booksId',
data: {
books: ['Vue从入门到精通', 'java', 'html从入门到精通','mysql从入门到精通']
}
})
</script>
</html>
知识讲解:
(1)将一个列表展示到HTML代码中,应该使用v-for指令。
(2)传统的操作方式是使用JavaScript/JQuery获取到ul的dom,然后构造li的dom,拼接到ul中进行渲染。而Vue中则只需要在data中定义这个数组,我们就可以使用v-for指令在页面中循环输出。更重要的是,它是响应式的
2.2.2 运行及结果
2.3 计数器案例
2.3.1 新建文件
然后按!选择第一个选项
2.3.2 引入vue-2.4.0.js文件
<script src="./lib/vue-2.4.0.js"></script>
2.3.3 编写代码
<!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>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="countId">
<h2>当前计数是:{{count}}</h2>
<button @click="addCount">加</button>
<button @click="delCount">减</button>
</div>
</body>
<script>
let app = new Vue({
el: '#countId',
data: {
count: 0
},
methods:{
addCount(){
this.count++
},
delCount(){
this.count--
}
}
})
</script>
</html>
知识讲解:
使用到了@click,这是vue中监听点击事件的方式,而@click需要指定发生点击时所执行的方法,该方法是在methods中定义的。
Methods是vue实例中options的一个属性,和data平级,该属性用于定义Vue对象中的方法。
2.3.4 运行及运行结果
项目代码百度网盘链接:
链接:https://pan.baidu.com/s/1N-F-hTbzQtiqvt9eXk7UoA
提取码:pt3y
三、前端框架的发展历程
3.1 什么是框架
框架:半成品的项目,提高开发效率。
3.2 前端框架发展
DOM:原生JS->ExtJS、JQuery->easyui、layui等等(简化DOM操作,但是降低了ajax请求的复用性)
MVVM:Angular、Vue、React。能够帮助我们减少不必要的DOM操作,提高渲染效率。数据绑定(单向、双向),通过框架提供的一些指令,我们只需要关注数据的业务逻辑,不需要关注DOM是如何渲染的。在vue中,一个最核心的思想就是不让用户去操作DOM元素。
3.3 MVVM
Model:对应数据层的域模型,它主要做数据的管理,通过ajax/fetch/axios等api完成客户端和服务端的Model同步,在层间关系里,它主要用于抽象出ViewModel中的Model。
View:View是作为视图模板,用于定义结构、布局。它自己不处理数据,只是将ViewModel中的数据展现出来。
ViewModel:ViewModel起着链接View和Model的作用,同时用于处理View中的逻辑。ViewModel从Model中取数据,渲染到View中。
四、基础语法
4.1插值操作
4.1.1 插值运算符
语法:{{数据}}
插值运算符可以对数据进行显示,也可以在插值运算符中进行表达式计算。
<!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>
<script src="./lib/vue-2.4.0.js"></script>
<style>
[v-clock]{
display: none;
}
</style>
</head>
<body>
<div id="dataId">
<h1>{{name}}</h1>
<h1>{{firstName}}{{lastName}}</h1>
<h1>{{count*5}}</h1>
<h2 v-once>{{firstName}}{{lastName}}</h2>
<h2 v-html="htmlData">hello</h2>
<h1 v-text="name"></h1>
<h2 v-clock>{{firstName}}{{lastName}}</h2>
</div>
</body>
<script>
let app = new Vue({
el:'#dataId',
data:{
name:'vue',
firstName:'张',
lastName:'三',
count:20,
htmlData:'<p style="color: red;">html代码</P>'
}
})
</script>
</html>
分析:
(1)v-once
该指令后面不需要跟任何的表达式,该指令表示元素和组件之间只会渲染一次,不会随着数据的改变而改变。
<h2 v-once>{{firstName}}{{lastName}}</h2>
(2)v-html
在某些情况下,从服务端请求到的数据本身是一个HTML代码。
若直接使用{{}}输出,会将HTML代码一起输出。
使用html格式进行输出,可使用v-html指令。
<h2 v-html="htmlData">hello</h2>
(3)v-text
v-text作用和插值运算符比较类似,都是用于将数据显示在页面中。通常情况下,v-text接受一个string类型。v-text会替换标签内的内容。
<h1 v-text="name"></h1>
(4)v-clock
在某些情况下,浏览器可能会直接显示出未编译的插值运算符。
<style>
[v-clock]{
display: none;
}
</style>
<h2 v-clock>{{firstName}}{{lastName}}</h2>
(5)过滤器
Filters属性与data和methods平级,用于定义过滤器。过滤器本质上是一个方法,接收一个参数。在页面中,使用插值运算符,语法:{{数据 | 过滤器名称}},不需要写参数,不需要写括号,过滤器默认会把前面的数据作为参数传递给方法。
过滤器的方法需要返回一个数据,返回的数据作为最终结果渲染到页面中。
<!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>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="appId">
<h1>{{count1|filterEven}}</h1>
<h1>{{count2|filterEven}}</h1>
</div>
</body>
<script>
let app = new Vue({
el:'#appId',
data:{
count1:10,
count2:11
},
filters:{
filterEven(num){
if(num%2 == 0){
return num;
}else{
return '非法数字';
}
}
}
})
</script>
</html>
4.2 属性绑定
4.2.1 v-bind 绑定a标签
前面我们学习的指令主要作用是将数据插入到我们模板的内容中,但是 除了内容需要动态决定以外,某些属性我们也希望动态来绑定,比如a标签的href、img标签的src等。这时候我们可以使用v-bind指令。
v-bind指令用于绑定一个值或者多个值
v-bind有简写方式,直接使用冒号:
<!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>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="dataId">
<a v-bind:href="baidu">百度</a>
</div>
</body>
<script>
let app = new Vue({
el:'#dataId',
data:{
baidu:'https://www.baidu.com/'
}
})
</script>
</html>
4.2.2 v-bind 绑定class
v-bind绑定class,可以接受一个对象,也可以接受一个数组。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-bind绑定class</title>
<script src="/lib/vue-2.4.0.js"></script>
<style>
.active {
color: red;
}
.size {
font-size: 30px;
}
.h2-border {
border: 1px solid red;
}
</style>
</head>
<body>
<div id="app">
<!-- 方式1:直接通过{}绑定一个类 -->
<h2 v-bind:class="{'active':isActive}">Hello Vue</h2>
<!-- 方式2:这种方式可以通过判断,传入多个值 -->
<h2 v-bind:class="{'active':isActive, 'size': big}">Hello Vue</h2>
<!-- 方式3:和普通类同时存在,并不冲突 -->
<h2 class="h2-border" v-bind:class="{'active':isActive, 'size': big}">Hello Vue</h2>
<!-- 方式4:如果过于复杂,可以放到一个data中 -->
<h2 class="h2-border" v-bind:class="classes">Hello Vue</h2>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
isActive: true,
big: true,
classes: ['active', 'size']
}
})
</script>
</html>
4.2.3 v-bind绑定style
通过:style的方式动态的绑定一些css的内联样式。在写CSS属性名的时候,比如font-size,我们在v-bind需要转换为驼峰规则来编写,或者使用横线分隔,使用横线的情况下,需要加上引号。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-bind绑定style</title>
<script src="/lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<!-- 方式1:直接绑定对象 -->
<h2 :style="{color: 'red', fontSize: '15px'}">Hello Vue</h2>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
}
})
</script>
</html>
4.2.4 v-model
Vue中使用v-model来实现表单元素和数据的双向绑定。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-model</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<input type="text" name="name" v-model="name">
<h2>{{name}}</h2>
<textarea v-model="name"></textarea>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
name: '张三'
}
})
</script>
</html>
当我们输入表单中的数据时,data中也会随之改变,data被改变后,插值运算符中的显示内容也会随之改变。
4.2.5 v-model绑定select
选中option的时,会将它对应的value赋值给mySelect。当mySelect的值改变时,option会自动选中对应value的那一项
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-model绑定select</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<select v-model="mySelect">
<option value="java">Java从入门到精通</option>
<option value="python">python从入门到精通</option>
<option value="php">php从入门到精通</option>
<option value="mysql">mysql从删库到跑路</option>
</select>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
mySelect: 'php'
}
})
</script>
</html>
4.3 监听器
监听器的属性名称是watch,和data平级。监听器中的数据是方法,接收两个参数,分别为改变后的值和改变前的值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>监听器</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="user.firstName">
<input type="text" v-model="user.lastName">
<h2>{{user.fullName}}</h2>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
user: {
firstName: '张',
lastName: '三',
fullName: ''
}
},
watch: {
'user.firstName': function (newVal, oldVal) {
this.user.fullName = this.user.firstName + this.user.lastName
}
}
})
</script>
</html>
运行及运行结果
4.4 计算属性
某些情况,可能需要对数据进行一些转换后再现实,或者需要将多个数据结合起来进行现实。
(1)代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>计算属性</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="firstName">
<input type="text" v-model="lastName">
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
computed: {
fullName() {
console.log('进入了计算属性')
// 写法是个方法,但是使用的时候是作为属性使用的,和data一致。
return this.firstName + this.lastName
}
}
})
</script>
</html>
(2)运行
(3)运行结果
分析:
计算属性是一个属性,而不是方法。虽然写法是方法,但是使用时是直接视为一个属性去操作的,使用方式和data一致。
计算属性中使用到的data中的数据只要发生了变化,计算属性就会重新计算。如果两次获取计算属性的时候,里面使用到的属性没有发生变化,计算属性会直接使用之前缓存的值。
4.4.2 计算属性的getter和setter
每一个计算属性都包含get方法和set方法。在上面的例子中,只是使用get去读取计算属性的值,在某些情况下,可能会需要人工改变计算属性的数据,这时就需要使用setter方法。
计算属性如果需要定义set和get方法,这时候计算属性就使用对象进行编写。
(1)代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>计算属性的get和set</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="firstName">
<input type="text" v-model="lastName">
<input type="text" v-model="fullName">
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
computed: {
fullName: {
get() {
console.log('调用了get方法')
return this.firstName+' '+this.lastName
},
set(val) {
console.log('调用了set方法')
const names = val.split(' ')
this.firstName = names[0]
this.lastName = names[1]
}
}
}
})
</script>
</html>
(2)运行
(3)运行结果
分析
计算属性。在computed中,可以定义一些属性,这些属性叫做计算属性,他本质上就是一个方法,但是,我们使用的时候是当做属性来使用。计算属性使用的过程中,一定不要加(),它就是一个属性,和data里的属性用法一样。
只要极速暗属性中所用到的data中的属性发生了变化,就会立即去重新计算属性的值。
计算属性的结果会被缓存起来,方便下次调用,如果计算属性中用到的data都没有变化,就不会重新求值。
在计算属性中,无论如何都需要return一个值,用来代表这个属性的值。
计算属性和监听器的区别:
Computed用法视为一个属性,并且结果会被缓存。
Methods表示方法,表示一个具体的操作,主要用来写业务逻辑
Watch是一个对象,key是需要监听的表达式,value是对应的回调函数,主要用来监听某些数据的变化,从而执行某些具体的业务逻辑。可以看做是计算属性和methods的结合体。
4.5 事件监听
4.5.1 v-on
v-on也有简写方式,直接写成@
(1)代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>按钮</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<h2>{{count}}</h2>
<input type="text" v-on:focus="alertMsg">
<button v-on:click="addCount">点我</button>
<!-- v-on的简写方式 -->
<!-- <button @click="addCount">点我</button> -->
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
addCount() {
this.count++
},
alertMsg() {
alert('获取了焦点')
}
}
})
</script>
</html>
(2)运行
(3)运行结果
分析:
当通过methods定义方法,以供@click调用时,需要注意参数问题
1.如果该方法不需要参数,那么方法后的括号不可以添加。因为存在某些框架,如果加了括号之后,可能在页面渲染完毕后立即加载一遍该方法。
2.如果需要传入某个参数,那么这个时候需要括号,并且需要在括号中传入参数。
4.6 条件判断
4.6.1 v-if
v-if、v-else-if、v-else。
这三个指令与JS中的条件判断if、else if、else类似。
v-if可以根据表达式中的值渲染或者销毁元素和组件。
(1)代码
(2)运行
(3)运行结果
4.6.2 v-show
v-show的用法与v-if类似,也是用于条件判断的。
(1)代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>条件判断v-show</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<div v-show="score >= 90">优秀</div>
<div v-show="score >= 75 && score < 90">良好</div>
<div v-show="score >= 60 && score < 75">及格</div>
<div v-show="score < 60">不及格</div>
<button @click="delScore">减分</button>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
score: 100
},
methods: {
delScore() {
this.score -= 10
}
}
})
</script>
</html>
(2)运行
(3)运行结果
二者的区别:
v-if是完全不创建DOM元素,而v-show则是创建了DOM元素,仅仅是使用display:none隐藏了该元素。
当需要频繁的显示、隐藏一些内容时,使用v-show。当我们仅有一次切换,某些v-if场景根本不会显示出来的时候,用v-if。
4.7 循环遍历
4.7.1 遍历数组
当有一组数据需要进行渲染时,可使用v-for完成。
v-for类似于JS中的for循环。
格式:item in items。后面的items代表着你需要遍历的数组,item则表示每一项的名称。
(1)代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-for遍历数组</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item, index) in dataList">{{index + ':' + item}}</li>
</ul>
<ul>
<li v-for="(value, key, index) in myInfo">{{index}}:{{key}}:{{value}}</li>
</ul>
<ul>
<li v-for="item in userList">{{item.id}}--{{item.name}}--{{item.age}}</li>
</ul>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
dataList: [
'Java从入门到精通',
'php从入门到精通',
'mysql从删库到跑路',
'Vue从单身到脱单'
],
myInfo: {
id: 3,
name: '张三',
age: 23,
hobby: '唱跳rap篮球'
},
userList: [
{id: 3, name: '张三', age: 23},
{id: 4, name: '李四', age: 24},
{id: 5, name: '王五', age: 25},
{id: 6, name: '赵六', age: 26}
]
}
})
</script>
</html>
(2)运行
(3)运行结果
4.7.2 遍历对象
v-for可以遍历对象。比如某个对象中存储着的信息,希望用列表的形式显示出来。
语法:v-for="(value, key, index) in myInfo"
(1)代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-for遍历对象</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="user.id" placeholder="请输入id">
<input type="text" v-model="user.name" placeholder="请输入name">
<input type="text" v-model="user.age" placeholder="请输入age">
<button @click="addUser">添加</button>
<div v-for="item in userList" :key="item.id">
<input type="radio" name="user" :value="item.id"> {{item.name}}
</div>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
userList: [
{id: 3, name: '张三', age: 23},
{id: 4, name: '李四', age: 24},
{id: 5, name: '王五', age: 25},
{id: 6, name: '赵六', age: 26}
],
user: {}
},
methods: {
addUser() {
this.userList.unshift(this.user)
}
}
})
</script>
</html>
(2)运行
(3)运行结果
Vue官方建议,若使用v-for去遍历一个对象、数组的话,建议给对应的元素或者组件上加一个key属性,并且要保证key的唯一。
4.8 过渡动画
Vue提供了简单的过渡动画效果,只需要按照vue提供的标准去写一套固定的CSS即可。
(1)代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
<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>
</head>
<body>
<div id="app">
<button @click="flag = !flag">点击</button>
<transition name="div1">
<div v-show="flag">这个是第一个div</div>
</transition>
<transition name="div2">
<div v-show="!flag">这个是第二个div</div>
</transition>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
flag: true
},
methods: {
}
})
</script>
</html>
(2)运行
(3)运行结果
4.9 Vue生命周期
(1)代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue生命周期</title>
<script src="./lib/vue-2.4.0.js"></script>
<style>
</style>
</head>
<body>
<div id="app">
<div id="myId">页面还没有被渲染 --- {{msg}}</div>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
msg: '页面渲染完毕'
},
methods: {
},
beforeCreate() {
console.log('beforeCreate')
var div = document.getElementById('myId')
// 页面没有被渲染,如插值运算符等属于vue实例的东西暂时都视为字符串
console.log(div.innerHTML)
// data还没有被挂载到vue,此时的vue只是一个空对象。
console.log(this.msg)
},
created() {
console.log('created')
var div = document.getElementById('myId')
// 页面没有被渲染,如插值运算符等属于vue实例的东西暂时都视为字符串
console.log(div.innerHTML)
// data成功被挂载到vue中,我们可以在这里使用到data中的数据。
console.log(this.msg)
},
beforeMount() {
console.log('beforeMount')
var div = document.getElementById('myId')
// 页面没有被渲染,如插值运算符等属于vue实例的东西暂时都视为字符串
console.log(div.innerHTML)
// data成功被挂载到vue中,我们可以在这里使用到data中的数据。
console.log(this.msg)
},
mounted() {
console.log('mounted')
var div = document.getElementById('myId')
// 页面重新渲染
console.log(div.innerHTML)
// data成功被挂载到vue中,我们可以在这里使用到data中的数据。
console.log(this.msg)
}
})
</script>
</html>
(2)运行
(3)运行结果
五、layui增删改查
(1)代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>layui增删改查</title>
<script src="./lib/vue-2.4.0.js"></script>
<link rel="stylesheet" href="./lib/layui.css">
</head>
<body>
<div id="app">
<div class="layui-form-item">
<div class="layui-input-inline" style="width: 100px;">
<input type="text" v-model="user.id" placeholder="请输入编号" class="layui-input">
</div>
<div class="layui-input-inline" style="width: 100px;">
<input type="text" v-model="user.name" placeholder="请输入姓名" class="layui-input">
</div>
<div class="layui-input-inline" style="width: 100px;">
<input type="text" v-model="user.age" placeholder="请输入年龄" class="layui-input">
</div>
<div class="layui-input-inline" style="width: 100px;">
<input type="text" v-model="user.gender" placeholder="请输入性别" class="layui-input">
</div>
<div class="layui-input-inline" style="width: 100px;">
<input type="text" v-model="user.remark" placeholder="请输入备注" class="layui-input">
</div>
<div class="layui-input-inline" style="width: 100px;">
<button type="button" class="layui-btn" @click="addUser">添加</button>
</div>
<div class="layui-input-inline" style="width: 100px;">
<input type="text" v-model="searchName" placeholder="请输入姓名" class="layui-input">
</div>
<div class="layui-input-inline" style="width: 100px;">
<button type="button" class="layui-btn" @click="searchUser">搜索</button>
</div>
</div>
<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.age}}</td>
<td>{{item.gender | handlerGender}}</td>
<td>{{item.remark}}</td>
<td>
<button type="button" class="layui-btn layui-btn-danger" @click="delUser(item.id)">删除</button>
</td>
</tr>
</tbody>
</table>
</div>
</body>
<script>
let app = new Vue({
el: '#app',
data: {
userList: [ // 用户列表
],
data: [ // 数据库,存放用户数据
{ id: 1, name: '张三', gender: 1, age: 23, remark: '姓张名三' },
{ id: 2, name: '李四', gender: 2, age: 24, remark: '姓李名四' },
{ id: 3, name: '王五', gender: 1, age: 25, remark: '姓王名五' },
{ id: 4, name: '赵六', gender: 2, age: 26, remark: '姓赵名六' },
{ id: 5, name: '张5qi', gender: 1, age: 18, remark: '低调的学霸' }
],
user: {},
searchName: '' // 搜索用的名称
},
filters: {
handlerGender(gender) {
if (gender == 1) {
return '男'
} else {
return '女'
}
}
},
methods: {
getUserList(name) { // 加载用户数据
// 如果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.....}
// 添加到数据库里
this.data.push(user)
// 重新加载数据
this.getUserList()
},
delUser(id) {
// 查到指定id所在数组索引下标
const index = this.userList.findIndex(e => e.id === id)
// 删除指定索引位置的数据
this.data.splice(index, 1)
// 删除成功后,重新家在数据
this.getUserList()
},
searchUser() { // 搜索用户
// 遍历用户,找到所有复合条件的用户
this.getUserList(this.searchName)
}
},
created() {
// 进入页面,组件创建之后,获取数据
this.getUserList()
}
})
</script>
</html>
(2)运行
(3)运行结果
六、组件化开发
6.1 什么是组件化
6.2 Vue组件化思想
组件化是Vue的一个重要概念,它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构建我们的应用。
任何的应用都会被抽象成一颗组件树
6.3 注册组件
组件的使用分成三个步骤:创建组件构造器、注册组件、使用组件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>注册组件</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<my-component></my-component>
<my-component2></my-component2>
<my-component3></my-component3>
<my-component4></my-component4>
</div>
</body>
<template id="temp">
<div>
<p>我是p标签</p>
<h1>我是h1标签</h1>
<div style="color: red;">哈哈哈</div>
</div>
</template>
<script>
var num = 10
/**
* 1 使用Vue.extend来注册 组件
* 按照了Java的开发思想,变量名往往是驼峰规则。
* 但是使用组件的时候一般不使用驼峰规则
* 而是将驼峰规则全部改成小写,然后中间用-连接
*/
Vue.component('myComponent', Vue.extend({
// template就是组件要展示的内容,可以是html标签
template: '<h3>用extend注册的组件</h3>'
}))
/**
* 2.不使用extend去注册组件
*/
Vue.component('myComponent2', {
// template就是组件要展示的内容,可以是html标签
template: '<div><h3>不用extend注册的组件</h3><h3>我是第二个h3</h3></div>'
})
// 不论使用哪一种注册方式,template属性只能有一个,并且有且仅有一个根节点。
Vue.component('myComponent3', {
// template就是组件要展示的内容,可以是html标签
template: `<div><h3>组件中使用外部变量num:${num}</h3></div>`
})
// 3.使用template
Vue.component('myComponent4', {
template: '#temp'
})
let app = new Vue({
el: '#app'
})
</script>
</html>
6.4 私有组件
上面使用Vue.component注册组件时,注册的是全局的组件。这意味着可再任意的Vue实例中,使用该组件。
如果想要注册一个局部的私有组件,可以将组件挂载到某个实例上。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>私有组件</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app1">
<my-component></my-component>
<my-comp></my-comp>
</div>
<div id="app2">
<my-component></my-component>
</div>
</body>
<script>
/**
* 注册的是全局组件
*/
Vue.component('myComponent', Vue.extend({
// template就是组件要展示的内容,可以是html标签
template: '<h3>用extend注册的组件</h3>'
}))
let myComp = Vue.extend({
// template就是组件要展示的内容,可以是html标签
template: '<h3>私有组件</h3>'
})
let app = new Vue({
el: '#app1',
components: {
myComp
}
})
let app2 = new Vue({
el: '#app2'
})
</script>
</html>
6.5 父组件与子组件
组件和组件之间存在层级关系,这就是父组件与子组件。组件中也有components关键字,同样是使用components将子组件注册到父组件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>父组件与子组件</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<parent-com></parent-com>
</div>
</body>
<script>
// 1.创建一个子组件
let childCom = Vue.extend({
template: `<div>子组件内容。</div>`
})
// 2.创建一个父组件
let parentCom = Vue.extend({
template:
`<div>
<h1>父组件内容</h1>
<child-com></child-com>
</div>
`,
components: {
childCom
}
})
let app = new Vue({
el: '#app',
components: {
parentCom
}
})
</script>
</html>
6.6 组件的数据
组件是一个单独的功能模块的封装,这个模块有属于自己的HTML模板,也应该有属于自己的data。
6.6.1 组件的数据
组件也有个data属性、methods、filters等等等等,使用方式与Vue一致(data不一样。)
Data必须是一个方法,返回一个对象。其他的与Vue实例使用方式一样。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>组件的数据</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<my-com></my-com>
</div>
</body>
<script>
let myCom = Vue.extend({
template: `<div>组件{{msg}}</div>`,
data() {
return {
msg: '子组件的msg'
}
}
})
let app = new Vue({
el: '#app',
data: {
msg: '哈哈哈'
},
components: {
myCom
}
})
</script>
</html>
6.6.2 为什么组件的data必须是一个方法
首先,如果不是一个方法,Vue直接会报错。
其次,原因在于Vue让每一个组件对象都返回一个新的对象,如果是同一个对象的,组件会在多次使用后相互影响。如果不写成方法返回对象的话,那么就是所有组件公用一个data。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>为什么组件的data必须是方法</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<my-com></my-com>
<my-com></my-com>
<my-com></my-com>
<my-com></my-com>
</div>
</body>
<template id="myTemp">
<div>
<button @click="addCount">点击</button>
<span>当前数量:{{count}}</span>
</div>
</template>
<script>
let myCom = Vue.extend({
template: '#myTemp',
data() {
return {
count: 0
}
},
methods: {
addCount() {
this.count++
}
}
})
let app = new Vue({
el: '#app',
components: {
myCom
}
})
</script>
</html>
6.7 组件的通信
组件中,可使用props来声明需要从父级接受到的数据。
首先在父组件中使用v-bind将数据绑定给子组件,再在子组件中,使用props接收。
Props和data、methods平级,有两种使用方式。
字符串数组,数组中的字符串就是传递时的名称。
6.7.1 父组件向子组件传值
组件中,可以使用props来声明需要从父级接受到的数据。
首先在父组件中使用v-bind将数据绑定给子组件,再在子组件中,使用props接收。
Props和data、methods平级,有两种使用方式。
1.字符串数组,数组中的字符串就是传递时的名称。
2.对象,对象可以设置传递时的类型和默认值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>父组件向子组件传值</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<!-- 第一步,用绑定的方式,将父组件的数据绑定给子组件 -->
<my-com :msg="msg"></my-com>
</div>
</body>
<template id="myTemp">
<div>
<span>当前数量:{{count}}</span>
<div>{{msg}}</div>
</div>
</template>
<script>
let myCom = Vue.extend({
template: '#myTemp',
data() {
return {
count: 0
}
},
//第一种 字符串数组
// props: [
// // 第二步,使用props接收.
// 'msg'
// ]
// 第二种 对象
props: {
msg: {
type: String,
default: '默认值,父组件没有传msg'
}
}
})
let app = new Vue({
el: '#app',
data: {
msg: '父组件的msg'
},
components: {
myCom
}
})
</script>
</html>
Type支持的类型:
String、Number、Boolean、Array、Object、Date、Function、Symbol
6.7.2 父组件向子组件传递方法
Props用于父组件向子组件传递数据,还有一种比较常见的就是子组件传递数据或者事件到父组件中。
可使用自定义事件来完成。
父组件调用子组件的方法,使用$emit来实现。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>父组件向子组件传递方法</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<!-- 第一步,在子组件上,使用@符号为该组件绑定一个事件 -->
<my-com @alert-msg="alertMsg"></my-com>
</div>
</body>
<template id="myTemp">
<div>
<button @click="handlerMethod">点击</button>
</div>
</template>
<script>
let myCom = Vue.extend({
template: '#myTemp',
methods: {
handlerMethod() {
// 第一个参数表示要调用的方法。
// 第二个参数往后,就是我们需要传递的参数
this.$emit('alert-msg', '调用')
}
}
})
let app = new Vue({
el: '#app',
data: {
msg: '父组件的msg'
},
methods: {
alertMsg(msg) {
alert(msg)
}
},
components: {
myCom
}
})
</script>
</html>
6.7.3 父组件调用子组件方法
$ refs
$ refs是和ref一起使用的。通过ref给某个子组件绑定一个特定的ID,然后使用$refs.ID就可以访问到子组件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>父组件调用子组件的方法</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<button @click="countAdd">点击</button>
<!-- 第一步,给子组件绑定一个ref -->
<my-com ref="myComp"></my-com>
</div>
</body>
<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>
</html>
6.8 插槽
6.8.1 为什么使用插槽
Slot翻译为插槽。
在生活中我们很多地方都有插槽,必读电脑的USB插槽,插板的电源插槽。
插槽的目的是让设备具备更多的扩展性。
组件的插槽:
组建的插槽也是为了让封装的组件更具有扩展性。让使用者可以决定组件内部的一些内容要展示什么。
比如移动web中的导航栏。导航栏的内容可能不一样,因此这里就需要使用插槽了。
3.8.2 如何封装
如何去封装这类组件?
它们有很多的区别,但是也有很多的共性,
如果每一个都单独去封装一个组件,显然不合适。但是如果只封装一个,也有些不合理。有些导航栏有左侧菜单,有些是返回按钮,有些是搜索框,有些是文字。
封装的思想就是,抽取共性,保留不同。将共性抽取到组件中,将不同的区域暴露为插槽,供用户自己去设置。
6.8.3 Slot基本使用
在子组件中,使用特殊的标签 < slot > 就可以为子组件开启一个插槽。该插槽中插入什么内容取决于父组件的使用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>solt</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<my-com>
<h2>替换插槽的内容</h2>
<ul>
<li>我也是</li>
<li>我也是的</li>
<li>俺也一样</li>
</ul>
</my-com>
</div>
</body>
<template id="myTemp">
<div>
哈哈哈哈
<slot>插槽中的默认内容</slot>
哈哈哈
</div>
</template>
<script>
let myCom = Vue.extend({
template: '#myTemp',
})
let app = new Vue({
el: '#app',
components: {
myCom
}
})
</script>
</html>
6.8.3 具名插槽
当子组件功能复杂时,子组件的插槽可能不止一个。比如封装导航栏的组件,可能就需要三个插槽,分别代表左中右。那么外面在给我们插槽插入内容时,如何区分插入的是哪一个呢?
这时就需要使用具名插槽。具名插槽就是在slot上定义一个名字。而在父组件中如果想要给指定位置的插槽插入内容的话,应该给标签加上slot属性,比如< span slot=”left” >
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>具名插槽</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<my-com>
<span slot="left">返回按钮</span>
<span slot="center">搜索框</span>
<span slot="right">菜单内容</span>
</my-com>
</div>
</body>
<template id="myTemp">
<div>
<slot name="left">左边</slot>
<slot name="center">中间</slot>
<slot name="right">右边</slot>
</div>
</template>
<script>
let myCom = Vue.extend({
template: '#myTemp',
})
let app = new Vue({
el: '#app',
components: {
myCom
}
})
</script>
</html>
七、模块化开发
7.1
组件化开发,一般是将重复的代码抽取成一个组件,供其他地方复用,一般情况下,提到组件化开发,都是指前端的组件化开发。
模块化开发,一般是将同一类功能模块的代码放到一起统一进行管理,是基于代码层面的,一般情况下,提到模块化开发,都是指后端。
7.1.1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模块化</title>
<script src="./js/aaa.js"></script>
<script src="./js/bbb.js"></script>
<script>
console.log('flag的值是', moduleB)
</script>
</head>
<body>
</body>
</html>
var moduleA = (function() {
var obj = {}
var flag = true
obj.flag = flag
return obj
})()
var moduleB = (function() {
var obj = {}
var flag = false
obj.flag = flag
return obj
})()
7.1.2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="export和import" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./js/info.js" type="module"></script>
<script src="./js/info2.js" type="module"></script>
<script src="./js/main.js" type="module"></script>
</head>
<body>
</body>
</html>