一、 Vue概念
- Vue是一个结合了React的虚拟DOM和Angular的模块化开发的一个渐进式js框架
- 渐进式是指先使用vue核心库,在vue核心库的基础上,根据自己需要再去逐渐增加功能
- 其核心是只专注于视图层,实现了后端的mvc
- Vue的官网:Vue.js
1.1 MVVM的概念
-
MVVM(Model-View-ViewModel)是一种软件架构模式,是一种简化用户界面的事件驱动编程方式。
-
MVVM源自于经典的MVC(Model-View-Controller)模式,MVVM的核心是ViewModel层,负责转换Model中的数据对象来让数据变得更容易管理和使用。其作用如下
- 该层向上与视图层进行双向数据绑定
- 向下与Model层通过接口请求进行数据交互
-
当下流行的MVVM框架有Vue.js、Angular.js。
1.2 MVVM的优点
MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model)
- **低耦合:**视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
- 可复用:你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
- 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewMode),设计人员可以专注于页面设计。
- 可测试:界面素来是比较难以测试的,而现在测试可以针对ViewModel来写。
1.3 Vue的优点
- 轻量级, 体积小是一个重要指标。Vue.js压缩后有只有20多kb(Angular压缩后56kb+,React压缩后44kb+)
- 移动优先。更适合移动端, 比如移动端的Touch事件
- 易上手,学习曲线平稳,文档齐全
- 吸取了Angular(模块化) 和React(虚拟DOM) 的长处, 并拥有自己独特的功能,如:计算属性
- 开源,社区活跃度高
1.4 引入
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<!-- 脚手架创建 -->
npm install @vue/cli -g
<!-- 创建项目 -->
vue create app
1.5 Vue入门
<div id='app'>
{{ message }}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app', //绑定元素的ID
data: { //数据对象
message: 'Hello Vue!'
}
})
</script>
二、Vue的基本语法
2.1 Vue的生命周期
-
概念:
Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
-
作用:
Vue生命周期中有多个事件钩子,让我们在控制整个Vue实例过程时更容易形成好的逻辑。
-
阶段:
可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/后。
- 第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
- DOM 渲染在 mounted 中就已经完成了。
- 生命周期钩子的一些使用方法:
- beforecreate : 可以在此阶段加loading事件,在加载实例时触发;
- created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用;
- mounted : 挂载元素,获取到DOM节点;
- updated : 如果对数据统一处理,在这里写上相应函数;
- beforeDestroy : 可以做一个确认停止事件的确认框;
- nextTick : 更新数据后立即操作dom;
2.2 钩子函数
-
概念:
钩子函数,其实就是Vue提前定义好的时间,其作用类似与Servlet中的init和distory方法 -
语法:
<script type="text/javascript"> var app = new Vue({ el:"#app", //钩子函数created,该方法在页面显示之后,自动执行 created() { console.log("created..."); } }); </script>
2.3 插值表达式
-
概述:
插值表达式用户把Vue中所定义的数据,显示在页面中,插值表达式允许用户输入"JS代码片段" -
语法:
{{ 变量名/对象.属性名 }} -
案例:
<html lang="en"> <head> <meta charset="UTF-8"> <title>vue插值表达式</title> <script src="node_modules/vue/dist/vue.js"></script> </head> <body> <div id="app"> <h1>欢迎来到-->{{ name }}</h1> </div> <script type="text/javascript"> //创建vue对象 var app = new Vue({ //让vue接管div标签 el:"#app", //定义数据,里边包含一个属性name,值为"白大锅" data:{ name:"白大锅" } }); </script> </body> </html>
2.4 显示数据(v-text和v-html)
-
概述:
v-text和v-html专门用来展示数据,其作用和插值表达式类似。v-text和v-html可以避免插值闪烁。
插值闪烁:在数据未加载完成时,页面会显示原始的{{}},过一会才显示正常数据。
-
语法:
v-text:<span v-text="msg"></span><!-- 相当于<span>{{msg}}</span> --> v-html:<span v-html="msg"></span><!-- 相当于<span>{{msg}}</span> -->
-
区别:
- v-text:把数据当作纯文本显示
- v-html:遇到html标签,会正常解析
2.5 判断语法(v-if和v-show)
-
概述:
- v-if与v-show可以根据条件的结果,来决定是否显示指定内容
- v-if:条件不满足时,元素不会存在
- v-show:条件不满足时,元素不会显示(但仍然存在)
-
案例:
<div id="app"> <button @click="show = !show">点我</button> <h1 v-if="show">Hello v-if.</h1> <h1 v-show="show">Hello v-show.</h1> </div> <script> var app = new Vue({ el:"#app", data: { show:true } }); </script>
2.6 事件处理(v-on)
-
概述:
Vue中也可以给页面元素绑定事件 -
语法:
<!--完整写法--> <button v-on:事件名="函数名/vue表达式">点我</button> <!--简化写法--> <button @事件名="函数名/vue表达式">点我</button>
-
注意:
Vue支持html中所有已知事件,如:@click,@submit等,只不过事件的名称不带on
-
案例:
<html lang="en"> <head> <meta charset="UTF-8"> <title>vue事件处理</title> <script src="node_modules/vue/dist/vue.js"></script> </head> <body> <div id="app"> <button @click="show">点我</button> </div> <script type="text/javascript"> //创建vue对象 var app = new Vue({ //获取id为app的元素,该元素被vue对象所管理.只有被vue对象管理的标签,其内部才允许书写vue语法 el:"#app", //定义vue的方法 methods:{ //定义show方法,弹出提示框 show() { alert("Hello Vue!!!"); } } }); </script> </body> </html>
-
事件修饰符:
事件修饰符主要对事件的发生范围进行限定 -
语法:
<button @事件名.事件修饰符="函数名/vue表达式">点我</button> 事件修饰符: .stop :阻止事件冒泡, 也就是当前元素发生事件,但当前元素的父元素不发生该事件 .prevent :阻止默认事件发生 .capture :使用事件捕获模式, 主动获取子元素发生事件, 把获取到的事件当自己的事件执行 .self :只有元素自身触发事件才执行。(冒泡或捕获的都不执行) .once :只执行一次
2.7 循环遍历(v-for)
2.9.1 遍历数组
-
语法:
v-for="item in items" v-for="(item,index) in items"
items:要遍历的数组
item:存储数组元素的变量名
index:迭代到的当前元素索引,从0开始 -
代码:
<div id="app"> <ul> <li v-for="(user, index) in users"> {{index}}--{{user.name}}--{{user.age}}--{{user.gender}} </li> </ul> </div> <script> var app = new Vue({ el:"#app",//el即element,要渲染的页面元素 data: { users:[ {"name":"白卓冉","age":8,"gender":"男"}, {"name":"白大锅","age":12,"gender":"女"}, {"name":"白仙女","age":4,"gender":"男"} ] } }); </script>
2.9.2 遍历对象
- 语法:
value:对象的值v-for="value in object" v-for="(value,key) in object" v-for="(value,key,index) in object"
key:对象的键
index:索引,从0开始 - 代码:
<div id="app"> <ul> <li v-for="(value,key,index) in person"> {{index}}--{{key}}--{{value}} </li> </ul> </div> <script> var app = new Vue({ el:"#app",//el即element,要渲染的页面元素 data: { person:{"name":"白大锅", "age":3, "address":"中国"} } }); </script>
2.9.3 key
-
概述:
:key一般配合v-for一起使用,用在特定的情况下,保证被遍历数组中的元素的顺序 -
案例:
<div id="app"> <button @click="add">添加</button> <ul> <li v-for="name in list"> <input type="checkbox"> {{name}} </li> </ul> </div> <script> var app = new Vue({ el: '#app', data: { list: ["孙悟空", "猪八戒", "沙和尚"] }, methods: { add() { //注意这里是unshift,向数组的头部添加一个元素 this.list.unshift("唐僧"); } } }); </script>
-
点击添加后
-
解决方案:
<div id="app"> <button @click="add">添加</button> <ul> <!-- 添加:key即可. 注意,key中的值必须是唯一且不会改变的值--> <li v-for="name in list" :key="name"> <input type="checkbox"> {{name}} </li> </ul> </div>
三、Vue的绑定和其他语法
3.1 数据的绑定(v-bind和v-model)
-
概述:
- 单向绑定(v-bind):数据只能从data流向页面
- 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data
-
语法:
<!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="../js/vue.js"></script> </head> <body> <div id="root"> 单向数据绑定:<input type="text" v-bind:value="name"><br> 双向数据绑定:<input type="text" v-model:value="name"> </div> <script> Vue.config.productionTip = false new Vue({ el:'#root', data:{ name:'JOJO' } }) </script> </body> </html>
-
注意:
- 双向绑定一般都应用在表单类元素上(如input、select、textarea等)
- v-model : value 可以简写为v-model ,因为v-model默认收集的就是value值
- v-bind:value可以简写为 : value
- 双向绑定文本框/单选按钮/textarea,绑定的数据都是字符串,单个复选框,绑定的是boolean类型,多个复选框,绑定的是数组,select单选对应字符串,多选对应的也是数组
3.2 计算属性
-
概述:
计算属性就是一个提前定义好的方法, 该方法可以看作是一个特殊的值, 可以在插值表达式中使用 -
语法:
var app = new Vue({ el:"#app", //计算属性必须放在Vue的computed中 computed:{ //定义计算属性 属性名(){ return "返回值"; } } });
-
案例:
<div id="app"> <h1>{{birth}}</h1> <h1 v-text="birth"></h1> <h1 v-html="birth"></h1> </div> <script type="text/javascript"> var app = new Vue({ el:"#app", computed:{ //定义一个birth方法,该方法就是一个计算属性,可以在插值表达式中使用 birth(){ let date = new Date(); let year = date.getFullYear(); let month = date.getMonth()+1; let day = date.getDay(); return year + "-" + month + "-" + day; } } }); </script>
3.3 watch监控
-
概述:
- watch可以监听简单属性值及其对象中属性值的变化
- watch类似于onchange事件,可以在属性值修改的时候,执行某些操作
-
语法:
<script type="text/javascript"> var app = new Vue({ el:"#app", data:{ message:"白大锅", person:{"name":"heima", "age":13} }, //watch监听 watch:{ //监听message属性值,newValue代表新值,oldValue代表旧值 message(newValue, oldValue){ console.log("新值:" + newValue + ";旧值:" + oldValue); }, //监控person对象的值,对象的监控只能获取新值 person: { //开启深度监控;监控对象中的属性值变化 deep: true, //获取到对象的最新属性数据(obj代表新对象) handler(obj){ console.log("name = " + obj.name + "; age=" + obj.age); } } } }); </script>
3.4 插槽(slot)
-
概念:
将父组件利用 插槽 将结构
传送到子组件中 -
插槽的本质:
是组件标签中间传递结构,实现公共代码的提取 -
语法:
//父组件 <子 组件名> //传输 结构/数据 <p>数据</p> </子 组件名> //子组件 <slot>接收结构/数据</slot>
-
案例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue插槽</title> </head> <body> <div id="el"> <my-div> <my-title slot="my-title" :title="tle"></my-title> <my-item slot="my-item" v-for="(item,index) in list" :item="item" :index="index"></my-item> </my-div> </div> <script src=" https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script> Vue.component("my-div", { template: ` <div> <slot name='my-title'></slot> <ul> <slot name='my-item'></slot> </ul> </div>` }); Vue.component('my-title', { props: ['title'], template: `<div>{{title}}</div>` }); Vue.component("my-item", { props: ["item", "index"], template: "<li>{{index+1}},{{item}}</li>" }); var vm = new Vue({ el: "#el", data() { return { tle: "前端基础课程", list: ['HTML', 'CSS', 'JavaScript'] } } }) </script> </body> </html>
-
图解:
四、Vue组件
4.1 基本使用
-
概述:
组件类似于模板,模块。在项目需要重用某个模块(头部、尾部、导航…)的时候,可以将模块抽取成组件,其他页面中注册组件并引用。 -
案例:
<div id="app"> <!--使用组件(组件名称),如果组件名称中有大写字母,如"myList",则这里需要书写<my-list>--> <counter></counter> <counter></counter> </div> <script type="text/javascript"> //定义组件 const counterTemp = { //定义组件的模版 template:`<button @click='num++'>你点击了{{num}}次</button>`, //定义组件中使用到的数据属性 data(){ return { num:0 } } }; //全局注册组件:在所有的vue实例中都可以使用组件 //参数1:组件名称,参数2:具体的组件 //Vue.component("counter", counterTemp); var app = new Vue({ el:"#app", //局部注册组件: 只能在当前Vue实例中使用 components:{ //组件名称:具体组件 counter: counterTemp } }); </script>
-
注意:
- 组件的模块中,只能书写一个跟标签
- 组件的定义必须放在Vue创建对象之前,否则报错
4.2 父组件向子组件通信
-
概述:
子组件无法直接使用父组件中的数据, 如果需要使用, 则必须由父组件把数据传递给子组件才可以 -
本质:
让子组件中的属性与父组件中的属性进行关联绑定, 然后子组件使用该属性, 这样才能做到数据传递 -
意义:
可以把父组件中的数据, 更新传递到子组件 -
示例:
<div id="app"> <!-- 把父组件中的count传递给子组件的number属性,把父arr传递给子ids,把父p传递给子person --> <aaa :number="count" :ids="arr" :person="p"></aaa> </div> <script> var aaa = { //定义组件的模版 template: `<h2>{{num}}---{{number}}--{{ids}}--{{person}}</h2>`, //定义组件中使用到的数据属性 data() { return { num: 0 } }, //给组件添加属性 props: { //普通属性number number: "", //数组属性ids ids: [], //对象属性person person: {} /* * //以上属性还可以书写为以下格式 * items:{ * //数据类型,如果是数组则是Array,如果是对象则是Object * type:Array, * //默认值 * default:[] * } */ } }; //注册:全局注册 Vue.component("aaa", aaa); var app = new Vue({ el: "#app", data: { count: 5, arr: [1, 2, 3], p: {username: "zhangsan", age: 23} } }); </script>
4.3 子组件向父组件通信
-
概述:
子组件无法直接给父组件传递数据. 也无法操作父组件中的数据, 更无法调用父组件中的方法。所以, 所谓的子组件向父组件通讯, 其实就是想办法让子组件调用父组件的方法. 进而响应到父组件中的数据。 -
意义:
子组件可以调用父组件中的方法 -
示例:
<div id="app"> <h1>父组件中:app_num={{app_num}}</h1> <!-- 把父组件的add方法,绑定给子组件的aaa属性,绑定方法使用@属性名="方法名" --> <!-- 把父组件的rem方法,绑定给子组件的bbb属性,绑定方法使用@属性名="方法名 --> <!-- 把父组件的app_num变量,绑定给子组件的counter_num属性,绑定变量使用:属性名="方法名 --> <counter @aaa="add" @bbb="rem" :counter_num="app_num"></counter> </div> <script> //定义一个组件(模版) let counter = { template: ` <div> <h1>子组件中:counter_num={{counter_num}}</h1> <input type="button" @click="fun1" value="+"/> <input type="button" @click="fun2" value="-"/> </div> `, props:{ //定义属性counter_num,用来接收父组件传递的数据 counter_num:null, //定义aaa属性,用来绑定父组件的方法,当然,该定义也可以省略 aaa:function(){}, //定义bbb属性,用来绑定父组件的方法,当然,该定义也可以省略 bbb:function(){}, }, methods:{ fun1(){ //找到aaa属性所绑定的那个方法,执行那个方法 return this.$emit("aaa"); }, fun2(){ //找到bbb属性所绑定的那个方法,执行那个方法 return this.$emit("bbb"); } } } var app = new Vue({ el: '#app', data: { app_num: 0 }, components: { counter }, methods:{ add(){ this.app_num++; }, rem(){ this.app_num--; } } }); </script>
五、axios异步请求
5.1 axios概述
-
概述:
axios是一个基于promise的HTTP库,主要用于:发送异步请求获取数据
-
常见的方法:
axios(config)
axios.get(url,[config])
axios.post(url,[data]) -
发送数据config常用参数:
{ url: '请求的服务器', method: '请求方式', // 默认是 get // GET请求参数 params: { 参数名: 参数值 }, // POST请求参数, 如果使用axios.post,则参数在url之后直接书写,不需要该位置传递参数 data: { 参数名: 参数值 }, // 响应数据格式,默认json responseType: 'json' }
-
响应数据常用参数:
{ data: {}, //真正的响应数据(响应体) status: 200, //响应状态码 statusText: 'OK', //响应状态描述 headers: {}, //响应头 config: {} //其他配置信息 }
5.2 get请求
var app = new Vue({
el: "#app",
data: {
user: {}
},
//当页面加载完毕后
created() {
//发送GET请求axios.get("请求路径",{ config });
axios.get("请求路径",{
//get请求参数
params: {
name:"zhangsan",
age:23
},
//响应数据格式为"json"
responseType: 'json'
}).then(res => {
//打印响应数据
console.log(res);
//把响应数据赋值给Vue中的user属性
app.user = res.data;
}).catch(err => {
//打印响应数据(错误信息)
console.log(err);
});
}
});
5.3 post请求
var app = new Vue({
el: "#app",
data: {
user: {}
},
//当页面加载完毕后
created() {
//发送POST请求axios.post("请求路径",{ 参数 });
axios.post("请求路径",{
name:"zhangsan",
age:23
}).then(res => {
console.log(res);
app.user = res.data;
}).catch(err => {
console.log(err);
});
}
});
5.4 跨域请求
跨域请求:在前端js中如果发送异步请求的话,请求的地址与当前服务器的ip或者端口号不同都是跨域请求.
- 跨域请求需要在服务提供方, 开启允许跨域请求