1.什么是vue
是一套前端框架,简化前端大量的jquery,原生js的重复DOM操作代码。用来简化开发。
是一套数据驱动视图的前端框架,基于MVVM架构,M:model数据,V:view视图,vm:viewModel视图数据监听器,当数据改变,修改视图;当视图发生变化,修改model数据。是一种数据双向绑定监听的实现。
是国人尤雨溪开发。
示例:
在HBuilder中新建vue项目,起名day19_vue
先用之前的方式写一个例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<h1 id="mytitle">mytitle</h1>
<input type="button" value="测试" onclick="changeTitle()" />
</body>
<script>
function changeTitle(){
document.getElementById("mytitle").innerHTML = "测试标题";
}
</script>
</html>
这是命令式的js代码,先从dom结构找到元素,再找元素属性,再确定要赋的值,最后把内容显示到页面中。最后一步叫页面渲染render,不是我们控制的。
如果编写页面效果,比如改变标题等等,要改变的内容比较多时,以上过程会一遍一遍的走。
再看以下代码:
<script>
var myobj={};
myobj.a="abc";
console.log(myobj.a); //abc
//赋值操作是包含在Object里的一个方法
Object.defineProperty(myobj,"b",{ //js给对象定义属性,第三个参数是描述信息desc,用json设置扩展对象时的相关参数,最关键的一个就是value
value:"jack"
})
console.log(myobj.b); //jack
</script>
在这里也可进行数据的读写,get和set方法。
<script>
var myobj={};
Object.defineProperty(myobj,"b",{
get:function(){
console.log("尝试获取b的值");
},
set:function(){ //给b赋值时该函数会触发
console.log("尝试设置b的值");
}
})
console.log(myobj.b); //undefined
myobj.b = 10;
</script>
用一个公共变量去写:
<script>
var myobj={};
var temp;
Object.defineProperty(myobj,"b",{
get:function(){
console.log("尝试获取b的值");
return temp;
},
set:function(newVal){
console.log("尝试设置b的值 改成"+newVal);
temp = newVal;
}
})
myobj.b = 10;
console.log(myobj.b);
</script>
结果,控制台输出:
尝试设置b的值 改成10
尝试获取b的值
10
用它来写上面的按钮的例子:
<body>
<h1 id="mytitle">mytitle</h1>
<input type="button" value="测试" onclick="changeTitle()" />
</body>
<script>
var myobj={};
var temp;
Object.defineProperty(myobj,"b",{ //相当于一套工具,重写get,set方法
get:function(){
console.log("尝试获取b的值");
return temp;
},
set:function(newVal){
console.log("尝试设置b的值 改成"+newVal);
temp = newVal; //设置值
document.getElementById("mytitle").innerHTML = temp; //渲染页面
}
})
function changeTitle(){
myobj.b = "jack";
}
</script>
这就是MVVM模式,把核心要干的事想明白。这里核心的事是js里改数据,页面里元素展现的效果要变。将他们写成公共代码,只需改变数据,自动在页面中变成相应的效果。
以上就是vue底层的一部分工作。
前端框架还会使用虚拟dom,用于提升页面渲染效果。
<script>
function changeTitle(){
document.getElementById("mytitle").innerHTML = "测试标题1";
document.getElementById("mytitle").innerHTML = "测试标题2";
document.getElementById("mytitle").innerHTML = "测试标题3";
document.getElementById("mytitle").innerHTML = "测试标题4";
}
</script>
触发按钮后,最终页面显示的是测试标题4,实际上做了四次页面渲染。
可以用变量去改进:
<script>
function changeTitle(){
var temp
temp = "测试标题1";
temp = "测试标题2";
temp = "测试标题3";
temp = "测试标题4";
document.getElementById("mytitle").innerHTML = temp;
}
</script>
这样,只会渲染一次。
实际上是有一套标准:
<script>
function changeTitle(){
var temp
temp = "测试标题1";
temp = "测试标题2";
temp = "测试标题3";
temp = "测试标题4";
//document.getElementById("mytitle").innerHTML = temp;
console.log(document); //document对象就是dom树对象
}
</script>
最终调取渲染页面的代码时不让自己调取,由底层自己找到dom树,由我们告知操作哪个元素,剩下的事底层做。
以上就是虚拟dom 的底层原理,由虚拟dom过渡底层的修改,最终只做一次渲染。
总结:使用MVVM模式,只关注数据;不直接操作dom树,页面渲染效率提升。
前端框架有三个:vue,react,angular。他们的语法完全不同,但思想都是接近的,都是MVVM模式和虚拟dom。
2.vue的使用
2.1vue写法
在项目下新建vue介绍.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/v2.6.10/vue.js" type="text/javascript" charset="UTF-8"></script> <!-- 引入vue -->
</head>
<body>
<div id="mydiv">
测试数据{{myval}}
</div>
</body>
<script>
new Vue({ //创建vue对象,参数是json格式
//要告知指定要监控的页面数据的分标签,然后帮助做自动化的响应式数据的流程。 响应式数据就是数据一遍,页面元素自动一遍。
el:"#mydiv",
data:{
myval:"abc"
}
})
</script>
</html>
运行,结果页面输出:测试数据abc
两个大括号(叫差值表达式)就是vue将变量值输出到页面的指令。
vue对象在使用时经常会读取:
<script>
var myvue = new Vue({
el:"#mydiv",
data:{
myval:"abc"
}
})
console.log(myvue);
</script>
在控制台:

在vue对象的data部分,设置了myval值,自动生成了get,set方法。也能看到配置的el对应的属性。data里的数据myval其实直接放在了第一层,在data前两行也可看到myval。
vue里配置属性都是以$符号加对应属性名进行的。该符号在jquery里表示jquery对象,故使用vue时不能使用jquery。
可读取配置的属性:
读根标签:
console.log(myvue.$el);
控制台输出:
<div id="mydiv">测试数据abc</div>
读数据:
console.log(myvue.$data);
改数据:
console.log(myvue.$data.myval="jack");
操作模板里的键值对时,$data可省略:(仅仅是对data数据的优化,因为data的value放在了第一层)
console.log(myvue.myval="jack");
从现在开始会写一些HTML(ES6)之后的新语法。
①.之前定义变量会用var,ES6之后多了两个。
先看let:
<script>
var temp1 = 10;
let temp2 = 10;
console.log(temp1); //10
console.log(temp2); //10
for(var i=0;i<10;i++){
console.log(i);
}
console.log(i); //10 循环外读i,会读到10。10是循环最后走完不满足判断条件之后出来的。从循环外读循环内使用的变量,从语法上并不合理。
for(let j=0;i<10;i++){ //let多了函数块内的作用域
console.log(j);
}
console.log(j); //此时报错,在循环外读j,会告知没有定义。let语法上会更严格,有块级作用域。
</script>
js里变量的作用域有:全局,函数内。let多加了一个块级作用域,指代码块,如上循环的代码就是一个典型的代码块。它使变量只在块内生效。
const:
<script> const temp3 = 10; //用来定义常量 temp3 = 11; //报错,告知是常量,不可写操作 </script>
②.ES6之后提供的拼接的语法
console.log("我的字符串"+temp3+"!!!!"); //我的字符串10!!!! 之前用+拼接
console.log(`我的字符串${temp3}!!!!`); //我的字符串10!!!!
这和vue没关系,是js里的功能。
③.定义匿名函数,ES6之后也可简化:
Object.defineProperty(myobj,"b",{
get:function(){
console.log("尝试获取b的值");
return temp;
},
set:function(newVal){
console.log("尝试设置b的值 改成"+newVal);
temp = newVal;
document.getElementById("mytitle").innerHTML = temp;
}
})
//简化:
Object.defineProperty(myobj,"b",{
get(){
console.log("尝试获取b的值");
return temp;
},
set(newVal){
console.log("尝试设置b的值 改成"+newVal);
temp = newVal;
document.getElementById("mytitle").innerHTML = temp;
}
})
2.2vue常用指令
1.差值表达式{{}}
作用就是把变量显示在页面中。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/v2.6.10/vue.js" type="text/javascript" charset="UTF-8"></script>
</head>
<body>
<div id="mydiv">
{{myval}}
</div>
</body>
<script>
var option = {
el:"#mydiv",
data:{
myval:"测试"
}
};
new Vue(option);
</script>
</html>
结果:页面输出测试
2.v-html
可解析html标签。和差值表达式的主要区别是里面可以加标签。
<body>
<div id="mydiv">
{{myval}}
<div v-html="myhtml">
</div>
</div>
</body>
<script>
var option = {
el:"#mydiv",
data:{
myval:"测试",
myhtml:"<h1>测试</h1>"
}
};
new Vue(option);
</script>
使用的不多,可用以下代码代替:
<h1>{{myval}}</h1>
3.v-bind
给元素的属性绑定变量
之前:
<input type="text" value="abc" />
如果想让变量和输入框里的值有关联:
<body>
<div id="mydiv">
<input type="text" v-bind:value="mytest" />
</div>
</body>
<script>
var option = {
el:"#mydiv",
data:{
mytest:"abc123"
}
};
let myvue = new Vue(option);
myvue.mytest = "jack";
</script>
结果:页面文本框默认值变为jack
class属性也可绑定,但较特殊:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/v2.6.10/vue.js" type="text/javascript" charset="UTF-8"></script>
<style>
.cls1{
background-color:lightcoral;
}
.cls2{
border:1px solid black;
}
.cls3{
width:100px;
height:100px;
}
</style>
</head>
<body>
<div id="mydiv">
<div v-bind:class="mycls">mydiv</div>
</div>
</body>
<script>
var option = {
el:"#mydiv",
data:{
mycls:"cls1"
}
};
let myvue = new Vue(option);
myvue.mycls = "jack";
</script>
</html>
但是class属性允许多值:
<script>
var option = {
el:"#mydiv",
data:{
mycls:"cls1 cls2"
}
};
let myvue = new Vue(option);
</script>
此时就是两个样式。
但样式切换比较麻烦:
<body>
<div id="mydiv">
<div v-bind:class="{'cls1':mycls1,'cls2':mycls2,'cls3':mycls3}">mydiv</div>
</div>
</body>
<script>
var option = {
el:"#mydiv",
data:{
mycls1:true,
mycls2:true,
mycls3:true
}
};
let myvue = new Vue(option);
</script>
此时三个样式全部生效,不想让哪个生效,改成false即可。
想用一个搞定也行:
<body>
<div id="mydiv">
<div v-bind:class="{'cls1':mycls1,'cls2':mycls1,'cls3':mycls1}">mydiv</div>
</div>
</body>
<script>
var option = {
el:"#mydiv",
data:{
mycls1:true
}
};
let myvue = new Vue(option);
</script>
还可以使用多个class属性,需要控制的样式用v-bind控制,不需要控制的不用加v-bind
<head>
<meta charset="utf-8">
<title></title>
<script src="js/v2.6.10/vue.js" type="text/javascript" charset="UTF-8"></script>
<style>
.cls1{
background-color:lightcoral;
}
.cls2{
border:1px solid black;
}
.cls3{
width:100px;
height:100px;
}
.cls4{
background-color:lightgreen;
}
</style>
</head>
<body>
<div id="mydiv">
<div class="cls4" v-bind:class="{'cls1':mycls1,'cls2':mycls2,'cls3':mycls3}">mydiv</div>
</div>
</body>
<script>
var option = {
el:"#mydiv",
data:{
mycls1:false,
mycls2:true,
mycls3:true
}
};
let myvue = new Vue(option);
</script>
属性绑定时也可简化,不写v-bind,只保留冒号。
4.v-if
控制页面元素是否可见
<body>
<div id="mydiv">
<div v-if="divshow">
mydiv2
</div>
</div>
</body>
<script>
var option = {
el:"#mydiv",
data:{
divshow:false
}
};
let myvue = new Vue(option);
</script>
此时页面不显示mydiv2
底层是用注释标签<!---->替换掉元素标签。
v-show也是控制元素是否显示:
主要区别是底层不改结构,而是给标签加样式
<div style="display:none;">mydiv2</div>
总结:v-if控制页面元素是否存在(<!-- -->);v-show控制页面元素是否隐藏(display)
除了v-if还有v-else-if和v-else:
<body>
<div id="mydiv">
<div v-if="divshow==1"> <!--通过v-if后面的逻辑条件返回布尔值 -->
测试1
</div>
<div v-else-if="divshow==2">
测试2
</div>
<div v-else>
测试3
</div>
</div>
</body>
<script>
var option = {
el:"#mydiv",
data:{
divshow:2
}
};
let myvue = new Vue(option);
</script>
三个里走一个,如果v-if匹配成功,则页面显示测试1;如果v-else-if匹配成功,则页面显示测试2;如果前两个都不匹配,则走v-else,页面输出测试3。
5.v-on
绑定事件
<body>
<div id="mydiv">
<input type="button" value="测试按钮" v-on:click="myFun()" />
</div>
</body>
<script>
var option = {
el:"#mydiv",
methods:{
myFun:function(){
console.log("按钮被点击了");
}
}
};
let myvue = new Vue(option);
</script>
也有简写:
<body>
<div id="mydiv">
<input type="button" value="测试按钮" @click="myFun()" />
</div>
</body>
<script>
var option = {
el:"#mydiv",
methods:{
myFun(){
console.log("按钮被点击了");
}
}
};
let myvue = new Vue(option);
</script>
多个函数间是逗号隔开。
vue报错的几种常见原因:
1.格式错误(比如多个函数没用逗号隔开)
2.根标签里写的所有变量(key)一定要和下面对应,即上面使用的东西下面必须有定义。但下面定义出来上面没有用是可以的。
一个简单的示例:点击按钮,改变标题文本:
<body>
<div id="mydiv">
<h1>{{myval}}</h1>
<input type="button" value="测试按钮" @click="myFun()" @mouseover="myFun2()" />
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
myval:"测试"
},
methods:{
myFun(){
console.log("按钮被点击了");
this.myval = "新测试"; //this指代生成这些函数的对象,即vue对象
},
myFun2(){
console.log("鼠标过来了");
}
}
});
</script>
6.v-model
双向绑定
点击按钮,改变文本框的值,并且鼠标移过去读文本框的值。
<body>
<div id="mydiv">
<h1>{{myval}}</h1>
<input type="text" :value="mytest" />
<input type="button" value="测试按钮" @click="myFun()" @mouseover="myFun2()" />
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
myval:"测试",
mytest:"abc123"
},
methods:{
myFun(){
console.log("按钮被点击了");
this.mytest = "新测试";
},
myFun2(){
console.log("鼠标过来了");
console.log(this.mytest);
}
}
});
</script>
换一种做法:
自己在页面的文本框输入abc123456,鼠标飘到测试按钮,却并不是abc123。原因是做属性绑定做的是单向的绑定,即可以给元素赋值,但元素里的内容发生改变,而vue对象里mytest:"abc123"却并未发生改变。
这里用双向绑定:
<body>
<div id="mydiv">
<h1>{{myval}}</h1>
<input type="text" v-model="mytest" />
<input type="button" value="测试按钮" @click="myFun()" @mouseover="myFun2()" />
</div>
</body>
双向绑定在不同的表单元素上用法不太一样。上面是文本框的使用。
以下是单选,多选,下拉列表的使用:
<body>
<div id="mydiv">
<input type="radio" value="1" v-model="gender" />男
<input type="radio" value="2" v-model="gender" />女
{{gender}}
<br />
<input type="checkbox" :value="1" v-model="hobby" />爬山 <!--value不加冒号,最后展现的是字符串 -->
<input type="checkbox" :value="2" v-model="hobby" />游泳
<input type="checkbox" :value="3" v-model="hobby" />开车
{{hobby}}
<br />
<select v-model="area" @change="getVal()">
<option value="110">北京</option>
<option value="120">上海</option>
<option value="130">深圳</option>
</select>
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
gender:1 , //通过这个值设置哪个按钮选中
hobby:[1,3],
area:"120"
},
methods:{
getVal(){
console.log(this.area); //当选项改变时,将对应的value打印到控制台
}
}
});
</script>
7.v-for
哪个元素需要重复生成,就在哪个元素上加v-for。
<body>
<div id="mydiv">
<ul>
<li v-for="mynews in newlist" >{{mynews.content}}</li>
</ul>
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
newlist:[{"content":"新闻1"},{"content":"新闻2"},{"content":"新闻3"}]
}
});
</script>
页面输出:
-
新闻1
-
新闻2
-
新闻3
如果有定值要写:
<li v-for="mynews in newlist" >劲爆!!!{{mynews.content}}</li>
页面输出:
-
劲爆!!!新闻1
-
劲爆!!!新闻2
-
劲爆!!!新闻3
其他几种遍历:
<body>
<div id="mydiv">
<table border="1">
<tr>
<th>用户编号</th>
<th>用户名</th>
<th>手机号</th>
</tr>
<tbody>
<tr v-for="user in userlist">
<td>{{user.userId}}</td>
<td>{{user.userName}}</td>
<td>{{user.userPhone}}</td>
</tr>
</tbody>
</table>
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
userlist:[{"userId":"001","userName":"小明","userPhone":"13838383838"},
{"userId":"002","userName":"小强","userPhone":"13838383838"},
{"userId":"003","userName":"小黄","userPhone":"13838383838"}]
}
});
</script>
页面显示:
| 用户编号 | 用户名 | 手机号 |
|---|---|---|
| 001 | 小明 | 13838383838 |
| 002 | 小强 | 13838383838 |
| 003 | 小黄 | 13838383838 |
<body>
<div id="mydiv">
<select v-model="myarea" >
<option value="999">----请选择----</option>
<option v-for="area in arealist" :value="area.areaId" >{{area.areaName}}</option>
</select>
{{myarea}}
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
arealist:[{"areaId":"001","areaName":"北京"},
{"areaId":"002","areaName":"上海"},
{"areaId":"003","areaName":"深圳"}],
myarea:"999"
}
});
</script>
增加option选项,并且下拉列表最开始显示----请选择----
3.使用axios发送ajax请求 跨区处理
3.1使用axios发送ajax请求
页面加载结束后,想使用一些功能,vue提供了一些入口,这些入口称为钩子函数。
类似javaEE中的监听器。
vue允许在这些钩子函数里写代码。

vue中提供了一些钩子函数
-
vue对象创建 create
vue挂载 mount
vue属性改变 update
vue销毁 destory
如果页面加载结束做一些初始化设计,用mount比较合适。
示例:
新建vue钩子函数
<body>
<div id="mydiv">
{{myval}}
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
myval:"",
},
methods:{
},
mounted(){
console.log("页面加载后执行代码");
//页面加载结束后 发ajax请求 取后台数据
this.myval = "jack"; //页面加载结束后赋初始值
},
created(){
console.log("vue对象创建出来了");
}
})
</script>
经常配合页面发ajax请求去后台取数据,取回数据,可直接给页面的元素赋值。
发ajax请求不使用jquery,而使用axios。
1.axios介绍
Axios 是一个基于 promise 的网络请求库,可以用于浏览器和 node.js
promise 对象是一个异步调用对象,ajax请求本身就是异步请求,需要配回调函数。
Axios 基于promise 对象封装ajax请求后,可以使用.then(表示成功的回调)和.catch(表示失败的回调)
在axios中文文档有详细的说明。
目前只需记住简化的方法:
// 向给定ID的用户发起请求
axios.get('/user?ID=12345')
.then(function (response) {
// 处理成功情况
console.log(response);
})
.catch(function (error) {
// 处理错误情况
console.log(error);
})
.then(function () {
// 总是会执行
});
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
打开BootCDN,在这里引入axios(也可下载它的js)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/v2.6.10/vue.js" type="text/javascript" charset="UTF-8"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.27.2/axios.js"></script>
</head>
<body>
<div id="mydiv">
{{myval}}
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
myval:"",
},
methods:{
},
mounted(){
console.log("页面加载后执行代码");
this.myval = "jack";
console.log(axios);
axios.get("/xxx").then(function(ret){
console.log(ret);
}).catch(function(err){
console.log(err);
})
},
created(){
console.log("vue对象创建出来了");
}
})
</script>
</html>
运行,在开发者工具中打开network,可看到xxx对应的Type是xhr,表示的就是ajax的异步请求。
以上就发出了ajax异步请求。
接下来就是后台的处理:
新建项目day9_vueDemo
新建src.com.javasm.controller.AjaxServlet
@WebServlet("/myajax")
public class AjaxServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("请求进来了");
String myVal = req.getParameter("myVal");
myVal += "my dear!!!";
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.print("{\"returnMsg\":\""+myVal+"\"}");
writer.flush();
writer.close();
}
}
启动服务器,访问http://localhost:8080/day9/myajax?myVal=jack
页面显示:{"returnMsg":"jackmy dear!!!"}
服务接口没问题。
将HBuilder中的vue钩子函数和day19_vue中的js复制到web包下:

现在使用的vue版本主要是v2.6.10,因为v3.2.8版本还没有全面铺开。
将vue钩子函数的地址xxx修改:(get方式,参数直接在后面拼)
/day9/myajax?myVal=rose
重新部署,运行,访问:http://localhost:8080/day9/vue钩子函数.html
前端代码里,发送ajax成功后,回调函数拿到后端返回的数据,然后把数据打印了出来。(console.log(ret);)
会发现,这里返回的数据和以前有些不同:

真正的数据其实在返回的json对象里的data,故应该用返回的数据再点data,才是返回的真正的数据,其他都是数据传输的一些相关参数。响应的写法上:
mounted() {
console.log("页面加载后执行代码");
console.log(axios);
axios.get("/day9/myajax?myVal=rose").then(function(ret){
console.log(ret.data);
}).catch(function(err){
console.log(err);
})
}
get方式的参数直接拼在请求地址后面即可。post方式的参数有些不同:
mounted() {
console.log("页面加载后执行代码");
console.log(axios);
axios.post("/day9/myajax","myVal=rose").then(function(retdata){
console.log(retdata.data);
}).catch(function (err) {
console.log(err);
})
}
3.2跨区处理
前面是HBUdiler写好页面,复制到idea。idea的web端发ajax请求,idea的服务端再接收请求,发送响应。
能否直接从HBUdiler直接发Ajax请求,不用复制到idea去做?
在HBUdiler,将请求地址改为:/day9/myajax?myVal=rose,跳出的页面的控制台输出:

这是Hbuilder的404页面,在其network的Hearders可看到访问路径

访问地址不对。我们使用相对根路径是在当前服务器跳转,当前是HBUilder内嵌的一个小服务器,想访问的是Tomcat的服务器。
将HBUdiler请求地址改为:http://localhost:8080/day9/myajax?myVal=jack。因为之前页面输入该地址就能访问到Tomcat服务器。结果弹出的页面报错。

执行了一个不符合要求的跨域请求,即被CORS policy规则阻挡。
CORS policy叫同源策略,通过js发请求和在浏览器里发请求不同。使用js发请求要求同源,即ip,协议,端口一致。即同一个服务器随便取数据,不同服务器不能随便取人家的数据,人家不同意就取不到。出现跨域额访问,同源策略会阻止这种请求。
什么是跨域,http:// (协议) localhost:(地址) 8080(端口) 。这三个地方有一个不同就叫跨域。
刚才就是跨了端口。
是否允许跨域是由请求和响应报文体现的。每次发请求都会知道请求的来源地址(Origin)和
请求的目的地址(Host)。

处理是处理响应报文:
@WebServlet("/myajax")
public class AjaxServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/* 允许跨域的主机地址 */
resp.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8848");
/* 允许跨域的请求方法GET, POST, HEAD 等 */
resp.setHeader("Access-Control-Allow-Methods", "*");
/* 重新预检验跨域的缓存时间 (s) */
resp.setHeader("Access-Control-Max-Age", "3600");
/* 允许跨域的请求头 */
resp.setHeader("Access-Control-Allow-Headers", "*");
/* 是否携带cookie */
resp.setHeader("Access-Control-Allow-Credentials", "true");
System.out.println("请求进来了");
String myVal = req.getParameter("myVal");
myVal += "my dear!!!";
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.print("{\"returnMsg\":\""+myVal+"\"}");
writer.flush();
writer.close();
}
}
允许跨域的主机地址从报错信息拿或者network的请求报文拿。
此时再用HBUilder访问就可以访问通了。
当你发送请求时,浏览器会自动发送一个method=option的请求,检测服务器返回的报文中是否包含允许跨域访问的响应头(域检请求)。在重写后台的方法时,是可以看到的:
@Override
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doOptions(req, resp);
}
这个不要在服务端重写,浏览器会通过它发送域检请求。
总结:跨域请求何时产生?当前页面是服务器A,要访问的数据是服务器B时。
4.省市县级联
后端的代码不变。day5的AreaServlet,先配置项目。
然后在重写的service方法里加跨域访问的请求头:
/* 允许跨域的主机地址 */
resp.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8848");
/* 允许跨域的请求方法GET, POST, HEAD 等 */
resp.setHeader("Access-Control-Allow-Methods", "*");
/* 重新预检验跨域的缓存时间 (s) */
resp.setHeader("Access-Control-Max-Age", "3600");
/* 允许跨域的请求头 */
resp.setHeader("Access-Control-Allow-Headers", "*");
/* 是否携带cookie */
resp.setHeader("Access-Control-Allow-Credentials", "true");
在HBUilder里新建 省市县级联菜单.html:
①.从后台拿数据
引入vue,axios和qs对应的js
根标签还是id=mydiv的div标签。
在body标签加入省市县的下拉列表,放在根标签里。
在script标签新建vue对象。
先测试,在mounted()的axios里用post方法,并将参数设置,看是否能取到数据。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/v2.6.10/vue.min.js" type="text/javascript" charset="utf-8"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/qs/6.10.3/qs.js"></script>
</head>
<body>
<div id="mydiv">
<select id="prov">
<option disabled selected>------------请选择-------------</option>
</select>省
<select id="city">
<option disabled selected>------------请选择-------------</option>
</select>市
<select id="coun">
<option disabled selected>------------请选择-------------</option>
</select>县/区
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
},
methods:{
},
mounted() {
console.log("页面加载后取省数据");
axios.post("http://localhost:8080/day5/getArea","areaid=00101").then(function(ret){
console.log(ret.data);
}).catch(function(err){
console.log(err);
})
}
})
</script>
</html>
结果控制台输出:
页面加载后取省数据 并输出相应的数据。
②.将拿到的数据填到页面
就是在select标签里添option标签。
请选择所在option保留,不去管,在下面增加option。
返回的数据是省数组,要遍历,用v-for。遍历省数组也要在data里写明,初始值给一个空数组。测试数据一般先用假数据。将返回的数据填到空数组中。
请求先不发送,测试一下假数据是否好用。
<body>
<div id="mydiv">
<select id="prov">
<option disabled selected>------------请选择-------------</option>
<option v-for="prov in provlist" :value="prov.areaId">{{prov.areaName}}</option>
</select>省
<select id="city">
<option disabled selected>------------请选择-------------</option>
</select>市
<select id="coun">
<option disabled selected>------------请选择-------------</option>
</select>县/区
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
provlist:[{areaId: "0010101",areaName: "金水区"},{areaId: "002",areaName: "黑背"}]
},
methods:{
},
mounted() {
console.log("页面加载后取省数据");
/* axios.post("http://localhost:8080/day5/getArea","areaid=00101").then(function(ret){
console.log(ret.data);
}).catch(function(err){
console.log(err);
}) */
}
})
</script>
省的下拉列表出现假数据,没问题。下一步填真数据。
真数据在retData:

mounted() {
console.log("页面加载后取省数据");
axios.post("http://localhost:8080/day5/getArea","areaid=00101").then(function(ret){
console.log(ret.data.retData);
this.provlist = ret.data.retData;
}).catch(function(err){
console.log(err);
})
控制台打印出了真数据,但下拉列表没生效。
原因:这里this的指向改变了。axios对象和vue对象没关系,axios里的this指向window对象,指当前整个页面。
可以在外面建一个this,就可以在axios里使用了。
mounted() {
console.log("页面加载后取省数据");
let that = this;
axios.post("http://localhost:8080/day5/getArea","areaid=00101").then(function(ret){
console.log(ret.data.retData);
that.provlist = ret.data.retData;
}).catch(function(err){
console.log(err);
})
运行后真实数据出现在了省的下拉列表中。
不过有更好的处理方式,直接写成箭头函数,替代匿名函数使用(类似之前的lomdar表达式):
使用箭头函数的好处:不是匿名函数,不改变箭头的指向
mounted() {
console.log("页面加载后取省数据");
axios.post("http://localhost:8080/day5/getArea","areaid=00101").then(ret=>{
console.log(ret.data.retData);
this.provlist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
给省做双向绑定。
当省改变,要知道当前选中的是哪个省,故在data里给出省编号的数据。省编号给到请选择的身上。
<body>
<div id="mydiv">
<select v-model="provCode" @change="changeCity()">
<option :value="999" disabled selected>------------请选择-------------</option>
<option v-for="prov in provlist" :value="prov.areaId">{{prov.areaName}}</option>
</select>省
<select >
<option disabled selected>------------请选择-------------</option>
</select>市
<select >
<option disabled selected>------------请选择-------------</option>
</select>县/区
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
provCode:"999",
provlist:[{areaId: "0010101",areaName: "金水区"},{areaId: "002",areaName: "黑背"}]
},
methods:{
changeCity(){
console.log(this.provCode);
}
},
mounted() {
console.log("页面加载后取省数据");
axios.post("http://localhost:8080/day5/getArea","areaid=0").then(ret=>{
console.log(ret.data.retData);
this.provlist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
})
</script>
接下来还是发请求取数据,还是先在data里建假数据。在市对应的select里加option。测试假数据:
<body>
<div id="mydiv">
<select v-model="provCode" @change="changeCity()">
<option :value="999" disabled selected>------------请选择-------------</option>
<option v-for="prov in provlist" :value="prov.areaId">{{prov.areaName}}</option>
</select>省
<select >
<option disabled selected>------------请选择-------------</option>
<option v-for="city in citylist" :value="city.areaId">{{city.areaName}}</option>
</select>市
<select >
<option disabled selected>------------请选择-------------</option>
</select>县/区
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
provCode:"999",
provlist:[{areaId: "0010101",areaName: "金水区"},{areaId: "002",areaName: "黑背"}],
citylist:[{areaId: "001",areaName: "金水区2"},{areaId: "002",areaName: "黑背2"}]
},
methods:{
changeCity(){
console.log(this.provCode);
}
},
mounted() {
console.log("页面加载后取省数据");
axios.post("http://localhost:8080/day5/getArea","areaid=0").then(ret=>{
console.log(ret.data.retData);
this.provlist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
})
</script>
然后将真数据填入市的下拉列表:
<script>
new Vue({
el:"#mydiv",
data:{
provCode:"999",
provlist:[{areaId: "0010101",areaName: "金水区"},{areaId: "002",areaName: "黑背"}],
citylist:[]
},
methods:{
changeCity(){
console.log(this.provCode);
axios.post("http://localhost:8080/day5/getArea","areaid="+this.provCode).then(ret=>{
console.log(ret.data.retData);
this.citylist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
},
mounted() {
console.log("页面加载后取省数据");
axios.post("http://localhost:8080/day5/getArea","areaid=0").then(ret=>{
console.log(ret.data.retData);
this.provlist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
})
</script>
换了省之后,市的下拉列表应该换成请选择。
<body>
<div id="mydiv">
<select v-model="provCode" @change="changeCity()">
<option value="999" disabled selected>------------请选择-------------</option>
<option v-for="prov in provlist" :value="prov.areaId">{{prov.areaName}}</option>
</select>省
<select v-model="cityCode">
<option value="999" disabled selected>------------请选择-------------</option>
<option v-for="city in citylist" :value="city.areaId">{{city.areaName}}</option>
</select>市
<select >
<option disabled selected>------------请选择-------------</option>
</select>县/区
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
provCode:"999",
cityCode:"999",
provlist:[{areaId: "0010101",areaName: "金水区"},{areaId: "002",areaName: "黑背"}],
citylist:[]
},
methods:{
changeCity(){
console.log(this.provCode);
this.cityCode = "999"; //省选项发生改变,市的下拉列表主动回到请选择
axios.post("http://localhost:8080/day5/getArea","areaid="+this.provCode).then(ret=>{
console.log(ret.data.retData);
this.citylist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
},
mounted() {
console.log("页面加载后取省数据");
axios.post("http://localhost:8080/day5/getArea","areaid=0").then(ret=>{
console.log(ret.data.retData);
this.provlist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
})
</script>
至此省市的级联就做完了。
③.和县区的级联
同样先拿假数据测试:
<body>
<div id="mydiv">
<select v-model="provCode" @change="changeCity()">
<option value="999" disabled selected>------------请选择-------------</option>
<option v-for="prov in provlist" :value="prov.areaId">{{prov.areaName}}</option>
</select>省
<select v-model="cityCode">
<option value="999" disabled selected>------------请选择-------------</option>
<option v-for="city in citylist" :value="city.areaId">{{city.areaName}}</option>
</select>市
<select >
<option disabled selected>------------请选择-------------</option>
<option v-for="coun in counlist" :value="coun.areaId">{{coun.areaName}}</option>
</select>县/区
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
provCode:"999",
cityCode:"999",
provlist:[{areaId: "0010101",areaName: "金水区"},{areaId: "002",areaName: "黑背"}],
citylist:[],
counlist:[{areaId: "0010101",areaName: "金水区"},{areaId: "002",areaName: "黑背"}]
},
methods:{
changeCity(){
console.log(this.provCode);
this.cityCode = "999"; //省选项发生改变,市的下拉列表主动回到请选择
axios.post("http://localhost:8080/day5/getArea","areaid="+this.provCode).then(ret=>{
console.log(ret.data.retData);
this.citylist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
},
mounted() {
console.log("页面加载后取省数据");
axios.post("http://localhost:8080/day5/getArea","areaid=0").then(ret=>{
console.log(ret.data.retData);
this.provlist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
})
</script>
假数据没问题,清空假数据。当市改变时,发请求拿县区的真实数据,在city所在select加change事件:
<body>
<div id="mydiv">
<select v-model="provCode" @change="changeProv()">
<option value="999" disabled selected>------------请选择-------------</option>
<option v-for="prov in provlist" :value="prov.areaId">{{prov.areaName}}</option>
</select>省
<select v-model="cityCode" @change="changeCity">
<option value="999" disabled selected>------------请选择-------------</option>
<option v-for="city in citylist" :value="city.areaId">{{city.areaName}}</option>
</select>市
<select >
<option disabled selected>------------请选择-------------</option>
<option v-for="coun in counlist" :value="coun.areaId">{{coun.areaName}}</option>
</select>县/区
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
provCode:"999",
cityCode:"999",
provlist:[{areaId: "0010101",areaName: "金水区"},{areaId: "002",areaName: "黑背"}],
citylist:[],
counlist:[]
},
methods:{
changeProv(){
console.log(this.provCode);
this.cityCode = "999"; //省选项发生改变,市的下拉列表主动回到请选择
axios.post("http://localhost:8080/day5/getArea","areaid="+this.provCode).then(ret=>{
console.log(ret.data.retData);
this.citylist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
},
changeCity(){
console.log(this.cityCode);
axios.post("http://localhost:8080/day5/getArea","areaid="+this.cityCode).then(ret=>{
console.log(ret.data.retData);
this.counlist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
},
mounted() {
console.log("页面加载后取省数据");
axios.post("http://localhost:8080/day5/getArea","areaid=0").then(ret=>{
console.log(ret.data.retData);
this.provlist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
})
</script>
当市的选项发生改变时,县区的选项变为请选择:
<body>
<div id="mydiv">
<select v-model="provCode" @change="changeProv()">
<option value="999" disabled selected>------------请选择-------------</option>
<option v-for="prov in provlist" :value="prov.areaId">{{prov.areaName}}</option>
</select>省
<select v-model="cityCode" @change="changeCity">
<option value="999" disabled selected>------------请选择-------------</option>
<option v-for="city in citylist" :value="city.areaId">{{city.areaName}}</option>
</select>市
<select v-model="counCode">
<option value="999" disabled selected>------------请选择-------------</option>
<option v-for="coun in counlist" :value="coun.areaId">{{coun.areaName}}</option>
</select>县/区
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
provCode:"999",
cityCode:"999",
counCode:"999",
provlist:[{areaId: "0010101",areaName: "金水区"},{areaId: "002",areaName: "黑背"}],
citylist:[],
counlist:[]
},
methods:{
changeProv(){
console.log(this.provCode);
this.cityCode = "999"; //省选项发生改变,市的下拉列表主动回到请选择
axios.post("http://localhost:8080/day5/getArea","areaid="+this.provCode).then(ret=>{
console.log(ret.data.retData);
this.citylist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
},
changeCity(){
console.log(this.cityCode);
this.counCode = "999";
axios.post("http://localhost:8080/day5/getArea","areaid="+this.cityCode).then(ret=>{
console.log(ret.data.retData);
this.counlist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
},
mounted() {
console.log("页面加载后取省数据");
axios.post("http://localhost:8080/day5/getArea","areaid=0").then(ret=>{
console.log(ret.data.retData);
this.provlist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
})
</script>
当省选项改变,县区的选项也需要变为请选择,且选项也要清空。
changeProv(){
console.log(this.provCode);
this.cityCode = "999"; //省选项发生改变,市的下拉列表主动回到请选择
this.counCode = "999"; //省选项发生改变,县区的下拉列表主动回到请选择
this.counlist = [];
axios.post("http://localhost:8080/day5/getArea","areaid="+this.provCode).then(ret=>{
console.log(ret.data.retData);
this.citylist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
总结:使用vue要抛弃掉找元素,改属性,自己页面渲染的思想。这些vue框架都帮着做了,我们所要关注的就是数据怎么去变。
④.qs.js
上面发送参数时用的是比较原始的url拼接的格式:
axios.post("http://localhost:8080/day5/getArea","areaid=0")
以前用jquery时可用json格式:
axios.post("http://localhost:8080/day5/getArea",{"areaid":"0"})
这样去写,参数并没有传过去。原因:使用axios方式传数据,如果是json格式,会用json格式原封不动的传入后台。

后台不使用工具就解析不出来。
若后台不用工具,前台就想发json格式数据,就要用到qs,一个把json对象转成字符串拼接格式的工具。
axios.post("http://localhost:8080/day5/getArea",window.Qs.stringify({"areaid":"0"}))
最终的代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/v2.6.10/vue.min.js" type="text/javascript" charset="utf-8"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/qs/6.10.3/qs.js"></script>
</head>
<body>
<div id="mydiv">
<select v-model="provCode" @change="changeProv()">
<option value="999" disabled selected>------------请选择-------------</option>
<option v-for="prov in provlist" :value="prov.areaId" >{{prov.areaName}}</option>
</select>省
<select v-model="cityCode" @change="changeCity">
<option value="999" disabled selected>------------请选择-------------</option>
<option v-for="city in citylist" :value="city.areaId" >{{city.areaName}}</option>
</select>市
<select v-model="counCode">
<option value="999" disabled selected>------------请选择-------------</option>
<option v-for="coun in counlist" :value="coun.areaId" >{{coun.areaName}}</option>
</select>县/区
</div>
</body>
<script>
new Vue({
el:"#mydiv",
data:{
provCode:"999",
cityCode:"999",
counCode:"999",
provlist:[{areaId: '001', areaName: '河南2'},{areaId: '002', areaName: '河北2'}],
citylist:[],
counlist:[]
},
methods:{
changeProv(){
console.log(this.provCode);
this.cityCode = "999";
this.counCode = "999";
this.counlist = [];
axios.post("http://localhost:8080/day5/getArea","areaid="+this.provCode)
.then(ret=>{
//使用箭头函数 替代匿名函数
//箭头函数不改变this的指向 可以直接使用到vue对象
console.log(ret.data.retData);
this.citylist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
},
changeCity(){
console.log(this.cityCode);
this.counCode = "999";
axios.post("http://localhost:8080/day5/getArea","areaid="+this.cityCode)
.then(ret=>{
//使用箭头函数 替代匿名函数
//箭头函数不改变this的指向 可以直接使用到vue对象
console.log(ret.data.retData);
this.counlist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
}
},
mounted() {
console.log(window.Qs.stringify({"areaid":"0"}));
console.log("页面加载后取省数据");
//console.log(this);
//let that = this;
axios.post("http://localhost:8080/day5/getArea",window.Qs.stringify({"areaid":"0"}))
.then(ret=>{
//使用箭头函数 替代匿名函数
//箭头函数不改变this的指向 可以直接使用到vue对象
console.log(ret.data.retData);
this.provlist = ret.data.retData;
}).catch(err=>{
console.log(err);
})
/* .then(function(ret){
console.log(ret.data.retData);
console.log(this);
that.provlist = ret.data.retData;
}).catch(function(err){
console.log(err);
}) */
}
})
</script>
</html>
vue基本用法&spm=1001.2101.3001.5002&articleId=124786432&d=1&t=3&u=156f4f5b809e4ea7aba195f8c552ed8a)
1279

被折叠的 条评论
为什么被折叠?



