Vue
一、Vue概述
(一)Vue是什么
Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的渐进式框架。Vue 只关注视图层, 采用自底向上增量开发的设计。
Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。
Vue.JS是优秀的前端 JavaScript 框架
(二)为什么学习Vue
随着项目业务场景的复杂,传统模式(html+jquery)已无法满足需求,就出现了Angular/React/Vue等框架
企业需求、主流框架之一、易用、灵活、高效
(三)Vue能做什么
最大程度上解放了 DOM 操作
单页web项目开发
传统网站开发
什么是工具(库)、什么是框架
1.库(插件):是一种封装好的特定方法集合,对项目的侵入性较小,提供给开发者使用,控制权在使用者手中,如果某个库无法完成某些需求,可以很容易切换到其它库实现需求 (jquery)
2.框架:是一套架构,会基于自身特点向用户提供一套相当完整的解决方案,而且控制权在框架本身;对项目的侵入性较大,使用者要按照框架所规定的某种特定规范进行开发,项目如果需要更换框架,则需要重新架构整个项目
二、Vue核心特征
① 解耦视图与数据
② M-V-VM模型 关注模型和视图
M:即Model,模型,包括数据和一些基本操作。
V:即View,视图,页面渲染结果
VM:即View-Model,模型和视图间的双向操作
③ 双向数据绑定
(一)MVVM之前
开发人员从后端获取需要的数据模型,然后要通过DOM操作Model渲染到View中。而后当用户操作视图,我们还需要通过DOM获取View中的数据,然后同步到Model中
(二)MVVM之后
而MVVM中的VM要做的事情就是把DOM操作完全封装起来,开发人员不用再关心Model和View之间是如何互相影响的:
- 只要我们Model发生了改变,View上自然就会表现出来。
- 当用户修改了View,Model中的数据也会跟着改变
把开发人员从繁琐的DOM操作中解放出来,把关注点放在如何操作Model上
三、Vue入门
(一)下载安装
vue是一个前端框架,也是其实是一个js文件,下载vue.js文件并在页面中引入 vue.js的下载方式: ① 可以引入在线的vue.js(公共的CDN服务) <!-- 开发环境版本,包含了用帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 或 <!-- 生产环境版本,优化了尺寸和速度 --> <script src="https://cdn.jsdelivr.net/npm/vue"></script> ② 可以离线下载vue.js 开发版本: https://vuejs.org/js/vue.js 生产版本: https://vuejs.org/js/vue.min.js ③ npm包资源管理器,可以下载vue.js (前端专用) 初始化:npm init -y 安装vue:npm install vue --save 注:切记重启计算机
(二)第一个vue
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h2>{{name}},欢迎你!</h2> </div> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script type="text/javascript"> //生成一个Vue实例 var app=new Vue({ el:"#app",//el ,即是element。要渲染的页面元素 data:{//数据 name:"优就业" } }) </script> </body> </html> Vue参数详解: 1. body中,设置Vue管理的视图<div id="app"></div> 2. 引入vue.js 3. 实例化Vue对象 new Vue(); 4. 设置Vue实例的选项:如el、data... new Vue({选项:值}); 5. 在<div id='app'></div>中通过{{ }}使用data中的数据
四、Vue常见指令
指令 (Directives) 是带有 v- 前缀的特殊attribute。是Vue框架提供的语法,扩展了html标签的功能、大部分的指令的值是js的表达式。用于取代DOM操作
(一)v-text
和 v-html
类似innerText和innerHTML ① v-text:更新标签中的内容 ② v-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>v-text、v-html</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <!-- 插值表达式 {{}} v-text 不识别html文本们只能原样输出,直接覆盖原本标签的内容 v-html 能识别html文本,直接覆盖原本标签的内容 --> <p v-text="a">您好</p> <p v-html="b"></p> <p v-html="c"></p> <p>你好,{{msg}}</p> </div> </body> <script> let app = new Vue({ el:"#app", data:{ msg:"helloworld", a:'hi', b:'hello', c:'<h1>嗨</h1>' } }); </script> </html>
(二)v-if
和 v-show
根据表达式的boolean值进行判断是否渲染该元素 案例: <!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>Document</title> <script src="vue.js"></script> </head> <body> <!-- v-if v-show 都可以用于显示或者隐藏元素 区别: v-if 使用注释 隐藏元素 v-show 使用样式 display:none 隐藏元素 --> <div id="app"> <p v-if="show">{{name}}</p> <p v-show="show">{{name}}</p> </div> </body> <script> let app = new Vue({ el:"#app", data:{ name:'张三', show:true } }) </script> </html>
(三)v-on
① 作用:使用 v-on 指令绑定 DOM 事件,并在事件被触发时执行一些 JavaScript 代码。 ② 语法: v-on:事件名.修饰符 = "methods中的方法名"; v-on的简写方法: @事件名.修饰符 = "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>v-on</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <p>当前计数: {{count}}</p> <!-- <button type="button" v-on:click="jia()">+</button> <button type="button" v-on:click="jian()">-</button> --> <!-- <button type="button" @click="jia()">+</button> <button type="button" @click="jian()">-</button> --> <button type="button" @click="count++">+</button> <button type="button" @click="count--">-</button> </div> </body> <script> let app = new Vue({ el:"#app", data:{ count:0 }, methods:{ jia(){ this.count++; }, jian(){ this.count--; } } }); </script> </html>
v-on绑定事件案例:
练习-显示隐藏
内容显示时,按钮是隐藏功能。隐藏内容后,按钮是显示功能,显示、隐藏共用一个按钮.
代码
<!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>v-on</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<p v-show="condition">你能看见我吗</p>
<button @click="change()">{{btn}}</button>
</div>
</body>
<script>
let app = new Vue({
el:"#app",
data:{
btn:'隐藏',
condition:true
},
methods:{
change(){
this.btn = this.btn=="隐藏" ?"显示":"隐藏";
this.condition = this.btn=="隐藏"?true:false;
}
}
})
</script>
</html>
(四)v-for
① 作用:列表渲染,当遇到相似的标签结构时,就用v-for去渲染 ② 格式: (item,index) in 数组或集合 参数item:数组中的每个元素 参数index:数组中元素的下标 <!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>v-for</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <!-- 遍历固定次数、固定内容 <p v-for="(item, index) in 100">好好学习,天天向上</p> --> <!-- 遍历数组--> <p v-for="(stu, index) in stuArr">{{index}}--{{stu.name}}--{{stu.age}}</p> </div> </body> <script> let app = new Vue({ el:"#app", data:{ stuArr:[ { id:1, name:'张三', age:20 }, { id:2, name:'李四', age:22 }, { id:3, name:'王五', age:25 } ] } }); </script> </html>
(五)v-bind
① 作用: 可以绑定标签上的任何属性 ② 格式:v-bind:属性="值" ③ 简写格式::属性="值" ④ 属性值一部分进行替换的格式::属性="'常量值' + vue对象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>v-bind</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <!-- 如果直接写属性名 , 不认识vue中定义的变量 --> <a href="addr">京东</a> <!-- 通过v-bind绑定的属性,能识别vue中定义的变量、方法--> <a v-bind:href="addr">百度</a> <a :href="addr2">中公</a> <a :href="'http://'+addr3">淘宝</a> </div> </body> <script> let app = new Vue({ el:"#app", data:{ addr:"http://www.baidu.com", addr2:"http://www.offcn.com", addr3:"www.taobao.com" } }); </script> </html>
练习-点击换样式
蓝色
color:blue;
border:1px solid blue;
box-shadow:0px 0px 3px black;
红色
color:red;
border:1px solid red;
box-shadow:0px 0px 3px black;
(六)v-model
① 作用:表单元素的绑定 ② 特点:双向数据绑定 (1)vue对象中的数据发生变化可以更新到界面 (2)通过界面可以更改vue对象中数据 (3)v-model 会忽略所有表单元素的 value、 checked 、 selected 特性的初始值而总是将 Vue 实 例的数据作为数据来源。应该在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>v-model</title> <script src="vue.js"></script> </head> <body> <div id="app"> 请输入您的个人信息: 姓名:<input type="text" v-model="name" /> 年龄: <input type="text" v-model="age"> <hr> 请核对您的个人信息 <br> 姓名: {{name}} <br> 年龄: {{age}} </div> </body> <script> let app = new Vue({ el:"#app", data:{ name:'张三', age:20 } }) </script> </html>
(七)计算属性
计算属性可以时刻关注在vue中定义的关联的变量。 当这些关联的变量值发生变化,计算属性也会发生变化. 代码 <!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="app"> <input type="text" v-model="num1" /> + <input type="text" v-model="num2" /> <p> <span>{{num1}}</span> + <span>{{num2}}</span> = <span>{{sum}}</span> </p> <p> <span>{{num1}}</span> * <span>{{num2}}</span> = <span>{{cheng}}</span> </p> </div> </body> <script> const app = new Vue({ el:'#app', data:{ num1:0, num2:0 }, computed:{ sum(){ return this.num1*1 + this.num2*1; }, cheng(){ return this.num1 * this.num2; } } }); </script> </html>
案例说明:
1.修改数量、移除书籍后,总价会发生变化
2.当一本书籍都没有了,提示购物车为空,表格消失
代码:
<!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="vue.js"></script>
</head>
<body>
<div id="app">
<table v-show="items.length>0" width="700" style="text-align: center;" border="1" cellspacing="0" cellpadding="0">
<tr>
<th></th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
<tr v-for="(item, index) in items">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<td>¥{{item.price}}</td>
<td><button v-on:click="--item.count">-</button>{{item.count}}<button @click="++item.count">+</button></td>
<td><button @click="items.splice(index,1)">移除</button></td>
</tr>
</table>
<p v-show="items.length>0">总价:¥{{sum}}</p>
<p v-show="items.length==0">购物车为空</p>
</div>
</body>
<script>
let app = new Vue({
el:'#app',
data:{
items:[
{id:1,name:'<<算法导论>>',date:'2006-9',price:85,count:1},
{id:2,name:'<<UNIX编程艺术>>',date:'20016-2',price:59,count:1},
{id:3,name:'<<编程珠玑>>',date:'2003-1',price:39,count:1},
{id:4,name:'<<代码大全>>',date:'2012-7',price:128,count:1}
]
},
methods: {
jian(book){
if(book.count>1)
book.count--;
},
jia(book){
book.count++;
},
remove(index){
this.items.splice(index,1);
}
},
computed:{
sum(){
let sum = 0;
for(let i = 0 ; i < this.items.length;i++){
let book = this.items[i];
sum+=book.price * book.count;
}
return sum;
}
}
});
</script>
</html>
五、Vue的生命周期
每个 Vue 实例在被创建时都要经过一系列的初始化过程 :创建实例,装载模板,渲染模板等等。Vue为生命周期中的每个状态都设置了钩子函数(监听函数)。每当Vue实例处于不同的生命周期时,对应的函数就会被触发调用。
钩子函数: 如:created代表在vue实例创建后;我们可以在Vue中定义一个created函数,代表这个时期的构造函数:
六、组件
(一)定义全局组件
我们通过Vue的component方法来定义一个全局组件。 Vue.component(组件名,{组件参数}) <!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>Document</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <con></con> <con></con> </div> </body> <script> let con = Vue.component("con",{ template:`<div><p>次数{{count}}</p> <button type="button" @click="--count">-</button> <button type="button" @click="++count">+</button></div>`, data(){ return { count:0 } } }); let app = new Vue({ el:'#app' }) </script> </html> 特点: - 组件其实也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等 - 不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。 - 但是组件渲染需要html模板,所以增加了template属性,值就是HTML模板 - 全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。 - data的定义方式比较特殊,必须是一个函数。 注: 定义组件要在Vue对象之前声明 模板template中只能有一个root(根)
(二)定义局部组件
一旦全局注册,就意味着即便以后你不再使用这个组件,它依然会随着Vue的加载而加载。因此,对于一些并不频繁使用的组件,我们会采用局部注册。 <!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>Document</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> </div> <div id="app1"> <con></con> <con></con> </div> </body> <script> let con = { template: `<div><p>次数{{count}}</p> <button type="button" @click="--count">-</button> <button type="button" @click="++count">+</button></div>`, data() { return { count: 0 } } } let app = new Vue({ el: '#app' }) let app1 = new Vue({ el: '#app1', components: { con: con } }) </script> </html> - components就是当前vue对象组件集合。 - 其key就是子组件名称 - 其值就是组件对象的属性 - 效果与刚才的全局注册是类似的,不同的是,这个conn组件只能在当前的Vue实例中使用 注:定义组件要在Vue对象之前声明
七、Vue的Ajax(axios)
在Vue.js中发送网络请求本质还是ajax,我们可以使用插件方便操作。
(一)安装
下载axios.js <script src="https://unpkg.com/axios/dist/axios.min.js"></script> 用离线版的axios.js
(二)axios请求
GET请求
axios.get('/user?id=12345') .then(response => { console.log(response.data); }); axios.get('/user?id=12345').then(function(response){ })
POST请求
axios.post('/user', "name=迪丽热巴&age=23") .then(response => { console.log(response.data); });
(三)跨域问题
什么是跨域?
指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
什么是同源策略?
是指协议,域名,端口都要相同,其中有一个不同都会产生跨域,在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
跨域问题怎么出现的?
开发一些前后端分离的项目,比如使用 Servlet + Vue 开发时,后台代码在一台服务器上启动,前台代码在另外一台电脑上启动,此时就会出现问题。
比如:后台 地址为 http://192.168.70.77:8081
前台 地址为 http://192.168.70.88:8080
此时 ip 与 端口号不一致, 不符合同源策略,造成跨域问题。
处理思路
使用CORS,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
值得注意的是,目前主流浏览器都支持cors,会自动发出cors请求。在简单请求下,cors请求和同源请求的区别主要是浏览器的请求头信息中会多了一个origin字段。 这个字段表示本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这个请求。
因此在后续解决方案中,主要是服务器需要处理orgin这个请求头字段,同意跨域请求。
如何解决: 后台解决(自定义过滤器) package com.offcn.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebFilter("/*") public class CorsFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) servletResponse; HttpServletRequest request = (HttpServletRequest) servletRequest; // 不使用*,自动适配跨域域名,避免携带Cookie时失效 String origin = request.getHeader("Origin"); response.setHeader("Access-Control-Allow-Origin", origin); // 自适应所有自定义头 String headers = request.getHeader("Access-Control-Request-Headers"); response.setHeader("Access-Control-Allow-Headers", headers); response.setHeader("Access-Control-Expose-Headers", headers); // 允许跨域的请求方法类型 response.setHeader("Access-Control-Allow-Methods", "*"); // 预检命令(OPTIONS)缓存时间,单位:秒 response.setHeader("Access-Control-Max-Age", "3600"); // 明确许可客户端发送Cookie,不允许删除字段即可 response.setHeader("Access-Control-Allow-Credentials", "true"); filterChain.doFilter(request, response); } @Override public void destroy() { } }