一、Vue.js开发基础
1、Vue简介
Vue 是一套用于构建用户界面的渐进式框架,与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
2、Vue特点
1. 遵循MVVM模式
2. 编码简洁, 体积小, 运行效率高, 适合移动/PC 端开发
3. 它本身只关注 UI, 也可以引入其它第三方库开发项目
3、Vue的基本使用
el:挂载
data:模型数据
msg:数据
<body>
<div id="app">
<!-- {{ }}输出变量的值 -->
<p>{{ msg }}</p>
</div>
<!-- 下面这个div没有被vue管理 -->
<div>
<p>{{ msg }}</p>
</div>
<!-- 导入vue.js -->
<script src="./vue.js"></script>
<script>
// 实例化一个vue
//挂载
new Vue({
// 找到某个盒子,并把这个盒子交给vue来管理
// 可以传入任何选择器,但是一般只用id选择器
el:"#app",
// vue里放数据的地方
// 在这里声明的都是vue里的变量
data:{
msg:"马杀鸡"
}
})
</script>
</body>
4、插值语法的使用
用于解析标签体内容
<body>
<div class="app">
<!-- 输出age结果:16 -->
<p>{{ age }}</p>
<!-- 16 + 10 -->
<p>{{ age + 10}}</p>
<!-- 没有大于21,false -->
<p>{{ age >= 21}}</p>
<!-- ture -->
<p>{{ age <= 21}}</p>
<!-- 三元表达式 -->
<!-- 得到未成年 -->
<!-- 字符串要用引号 -->
<p>{{ age >= 18? '成年':'未成年'}}</p>
</div>
<script src="./vue.js"></script>
<script>
new Vue({
el:'.app',
data:{
age:16
}
})
</script>
</body>
5、v-text与v-html
<body>
<div id="app">
<!-- 遇到标签不解析,纯文本 -->
<div v-text="msg">原本的内容</div>
<!-- 遇到标签会解析 -->
<div v-html="msg">原本的内容</div>
<!-- 插值语法遇到标签也不解析,纯文本 -->
<!-- 他不会覆盖原本内容,他只会在自己写{{}}的位置做输出 -->
<div>
原本的内容{{ msg }},我在后面
</div>
</div>
<script src="./vue.js"></script>
<script>
new Vue({
el:'#app',
data:{
msg:'<h3>你好<h3>',
}
})
</script>
</body>
6、v-model
v-model指令可以在表单 input、textarea以及select元素上创建双向数据绑定
<body>
<div id="app">
<button @click="msg = '你好' ">修改msg的值</button>
<!-- 获取输入的内容 -->
<input type="text" v-model="msg">
<p>{{ msg }}</p>
<!-- 下拉框获得选中的value值 -->
<select v-model="fruit">
<option value="apple">苹果</option>
<option value="orange">橘子</option>
<option value="banana">香蕉</option>
</select>
<!-- 要吗选中,要么不选中 -->
<!-- 得到true或false -->
<input type="checkbox" v-model="chk">
</div>
<script src="./vue.js"></script>
<script>
new Vue({
//挂载
el:'#app',
//数据存放
data:{
msg:'',
fruit:'',
chk:true
}
})
</script>
</body>
7、v-on事件绑定命令的使用
给元素注册点击事件
<body>
<div id="app">
<!-- 当点击这个按钮,就调用fn1函数 -->
<button v-on:click="fn1">点我啊</button>
<!-- 当双击这个按钮,就调用fn2函数 -->
<button v-on:dblclick="fn2">啊啊啊啊啊啊</button>
<!-- 当移入这个按钮,就调用fn2函数 -->
<button v-on:mouseenter="fn2">移入我</button>
<h4>以下是简写形式</h4>
<hr>
<!-- 当单击这个按钮,就调用fn1函数 -->
<button @click="fn1">点我啊</button>
<!-- 当双击这个按钮,就调用fn2函数 -->
<button @dblclick="fn2">啊啊啊啊啊啊</button>
<!-- 当移入这个按钮,就调用fn2函数 -->
<button @mouseenter="fn2">移入我</button>
</div>
<script src="./vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
},
methods: {
fn1() {
alert('点我干嘛')
},
fn2() {
alert('哈哈哈哈哈哈哈哈哈哈哈')
},
}
})
</script>
</body>
8、v-bind
用于绑定数据和元素属性
<body>
<!-- v-bind:让行内属性不写死
简写 : -->
<div id="app">
<a href="http://www.baidu.com">跳转</a>
<a v-bind:href="url">跳转</a>
<button @click="change" target="_blank">跳转</button>
</div>
<script src="../vue.js"></script>
<script>
new Vue({
el:'#app',
data:{
url:'http://taobao.com'
},
methods:{
change(){
this.url='http://xiaomi.com'
}
}
})
</script>
</body>
9、生命周期
每一个vue实例从创建到销毁的过程
<body>
<script>
new Vue({
el:'#example',//挂载
data:{
message:'match'
},
//beforeCreate组件实例被创建之初
//不能获得DOM节点。data、methods、watch上的方法和数据均不能访问(loading)
beforeCreate(){
console.log('beforeCreate');
},
//created组件实例已经完全创建(结束loading事件)
//可以访问到data、methodes上的方法和数据
created(){
console.log('creaded');
},
//beforeMount组件挂载之前
//得不到具体的DOM节点,Vue挂载的根节点已经创建(一个项目用两次)
beforeMount(){
console.log('beforeMount');
},
//mounted组件挂载到实例上去之后
//完成挂载DOM和渲染,可以对挂载的DOM进行操作(发起后端ajax请求)
mounted(){
console.log('mounted');
},
//beforeUpdate组件数据发生变化更新之前
//数据时新的,但界面是旧的,即页面尚未与数据保存同步(访问现有DOM)
beforeUpdate(){
console.log('beforeUpdate');
},
//updated组件数据更新之后
//数据是新的,页面也是新的,即页面和数据保持同步
updated(){
console.log('updated');
//组件更新后调用$destroyed函数,进行销毁
this.$destory();
},
//beforeDestroy组件实例销毁之前
//(可做一些删除提示,如:您确定删除XX吗?)
beforeDestroy(){
console.log('beforeDestroy');
},
//destroyed组件销毁之后
//销毁后,当前组件已被删除,无法操作里面的任何东西了
destroyed(){
console.log('destroyed');
},
})
</script>
</body>
二、结构渲染
条件渲染指令v-show、v-if
(1)v-show与v-if的区别
v-show指令的作用是:根据真假值切换元素的显示状态,是响应式的
1.本质就是标签display设置为none,控制隐藏。只是基于CSS进行切换。
2.v-show有更高的初始渲染消耗。
3.v-show适合频繁切换的情况。
v-if指令的作用:根据表达式的真假切换元素的显示状态
1.动态的向DOM树内添加或者删除DOM元素。
2.v-if有更高的切换消耗。
3.v-if适合运行条件很少改变的情况。
<body>
<!-- v-show和v-if都可以用来控制标签的显示与隐藏
<标签 v-show='布尔值'></标签>
<标签 v-if='布尔值'></标签>
当布尔值为true,他们就显示;当布尔值为false,他们就隐藏.
-->
<div id="app">
<div v-show="isShow">我是由v-show控制的</div>
<div v-if="isShow">我是由v-if控制的</div>
</div>
<script src="../../../vue.js"></script>
<script>
new Vue ({
el:'#app',
data:{
isShow:none
}
})
</script>
</body>
(2)v-if
<body>
<div id="app">
<h3>根据班长有多少钱,决定今天晚上吃什么</h3>
<input type="text" v-model="money">
<div v-if="money >= 10000">请全班同学吃豪华海鲜自助</div>
<div v-else-if="money >= 5000">请全班同学吃火锅</div>
<div v-else-if="money >= 1000">请全班同学吃粉</div>
<div v-else-if="money >= 500">请全班同学吃ad</div>
<div v-else>啥也么有</div>
<!-- 双分支使用v-if -->
<div v-if="age > 18">成年了</div>
<div v-else>未成年</div>
</div>
<script src="/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
money:5,
age:11
}
})
</script>
</body>
列表渲染指令v-for
(1)v-for遍历对象
<body>
<!-- v-for遍历对象 -->
<!-- 语法:<标签 v-for="(val,key) in 对象"></标签>
<标签 v-for="val in 对象"></标签> -->
<div id="app">
<ul>
<li v-for="item in obj">{{item}}</li>
<hr>
<!-- (属性值,属性名) 第一个参数是属性值,第二个参数是属性名-->
<!-- 跟名字无关,也可以简写只拿属性值 -->
<li v-for="(key,nam) in obj">{{key}}---{{nam}}</li>
</ul>
</div>
<script src="/vue.js"></script>
<script>
new Vue({
el:'#app',
data:{
obj:{
name:'佳佳',
age:18,
sex:'女'
}
}
})
</script>
</body>
(2)v-for遍历数字
<body>
<div id="app">
<ul>
<!-- 指定数字 -->
<li v-for="num in 66">{{num}}</li>
</ul>
</div>
<script src="../vue.js"></script>
<script>
new Vue({
el:"#app",
})
</script>
</body>
(3)v-for遍历数组
<body>
<!-- 语法:<标签 v-for='item in 数组'></标签> -->
<div id="app">
<button @click="add">在最后添加一个</button>
<ul>
<!-- <li v-for="item in list">{{item}}</li> -->
<li v-for="one in msg">{{one}}</li>
<!-- 第一个参数是元素,第二个参数是下标。与名字无关 -->
<li v-for="(item,index) in list">{{ item }}---{{index}}</li>
</ul>
</div>
<script src="../vue.js"></script>
<script>
new Vue({
el:'#app',
data:{
list:['小王','小陈','小林','小莫'],
msg:['as','assd']
},
methods:{
add(){
this.list.push('彦子')
}
}
})
// [原生写法]
// for(let i = 0 ; i < list.length; i++){
// let li = document.createElement('li')
// li.innerHTML = list[i]
// ul.appendChild(li)
// }
</script>
</body>
(4)v-for中的key
现象:当我勾选了彦子,在下标1的位置添加了小赵,结果发现勾给了小赵,李四自己没勾了
原因:v-for会尝试最大限度的复用当前元素,导致状态绑定错乱
解决:在v-for后加上一个key属性,key绑定这个数据的唯一值(一般是id,不能是字符串和数字类型)
<body>
<div id="app">
<button @click="list.splice(1,0,{id:4,name:'小赵'})">添加</button>
<ul>
<li v-for="item in list" :key="item.id">
<input type="checkbox">
<span>{{ item.name }}</span>
</li>
</ul>
</div>
<!-- 现象:当我勾选了彦子,在下标1的位置添加了小赵,结果发现勾给了小赵,李四自己没勾了 -->
<!-- 原因:v-for会尝试最大限度的复用当前元素,导致状态绑定错乱 -->
<!-- 解决:在v-for后加上一个key属性,key绑定这个数据的唯一值(一般是id,不能是字符串和数字类型) -->
<script src="../vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
list:[
{id:1,name:'小莫'},
{id:2,name:'彦子'},
{id:3,name:'小王'},
]
}
})
</script>
</body>
三、计算属性与侦听器
侦听器
作用:用来倾听数据有没有变化,一旦有变化就调用函数
语法:在和data、methods这些平级的位置写一个watch
new Vue({
el:'',
data:{},
methods:{
},
// 侦听器
watch:{
// 参数一:改变后的值
// 参数二:改变前的值
要侦听的数据(newValue,oldValue){
}
}
})
(1)侦听器的基本使用
对数组进行侦听
数组是引用类型,存在比较复杂的侦听规则。
从理论上说,修改一个数组的内容,比如修改数组中某个元素的值,或者给数组添加新的元素,都不会修改数组本身的地址
为此,Vue.js对数组做了特殊处理,使得使用标准的数组操作方法对数组所做的修改,都可以被侦听到。
1.Vue不能检测以下数组的变动:
a.当你利用索引值直接设置一个数组时,例如:vm.items[indexofItem]=newValue
b.当你修改数组的长度时,例如:vm.items.length= newLength
2.使用标准方法修改数组可以被侦听到
push()尾部添加
pop()尾部删除
unshift()头部添加
shift()头部删除
splice()删除、添加、替换
sort()排序
reverse()逆序
Vue将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新,以上就是被包裹的方法。
在数组中使用侦听器总结:
1.彻底替换为一个新数组,那么可以被侦听到。
2.如果使用了push()等标准的数组操作方法,那么可以被侦听到
3.如果直接修改数组的元素,那么无法被侦听到。
(解决方法:使用$set()方法修改元素的值。Vue3语法。)
不要使用length属性来修改数组长度,而改用其他标准方法显示数组长度的变化
<div id="app">
<button @click="list.push('蜡笔小新')">添加一个项</button>
<button @click="list.pop()">删除一个项</button>
<!-- 利用索引值改变数组时,不能被侦听到 -->
<button @click="list[0] ='病猫' ">修改第一项</button>
<button @click="list.splice(1,0,'天气之子')">第二项添加</button>
<ul>
<li v-for="item in list">
{{item}}
</li>
</ul>
</div>
<script src="../vue.js"></script>
<script>
new Vue({
el:'#app',
data:{
list:['龙猫','名侦','千与千寻','你的名字']
},
watch:{
list(){
console.log('数组改变了');
}
}
})
</script>
(2)侦听器完整写法
<body>
<!-- 对对象进行侦听 -->
<div id="app">
<p>{{ obj.name }} --- {{ obj.age }} --- {{obj.height}}</p>
<button @click = "obj.name = 'rose' ">修改obj的name属性</button>
<button @click = "obj.age = 30">修改obj的age属性</button>
<button @click = "obj ={name:'andy',age:99} ">修改obj的指向</button>
</div>
<script src="../vue.js"></script>
<script>
new Vue({
el:'#app',
data:{
obj:{
name:'jack',
age:16,
height:175
}
},
watch:{
//这种写法侦听不到obj对象内部属性的变化,
//只能侦听到obj对象指向的变化。
// obj(){
// console.log('obj变化了');
// },
//如果想侦听到对象里属性的变化一
// 侦听obj里name的变化(要记得加引号)
// "obj.name"(){
// console.log('name变化了') ;
// },
//可是这样写只能侦听一个属性。
//如果希望侦听到对象里所有属性的变化,就要开启深度侦听。
obj:{
// 当有变化会调用的函数
handler(){
console.log('obj的属性变化了');
},
// 开启深度侦听
deep:true,
// 要不要当前页面一打开就立即调用handler函数
// 给true表示立即调用,默认是flase。(代表只有改动了才调用)
immediate:true
}
}
})
</script>
</body>
四、存储方法
1.cookie
使用场景:
1:记住密码,下次自动登录。
2:记录用户浏览数据,进行商品(广告)推荐。
特点:
1: cookie保存在浏览器端。
2: 单个cookie保存的数据不能超过4KB。
3: cookie中的数据是以域名的形式进行区分的。
4: cookie中的数据是有过期时间的,超过时间数据会被浏览器自动删除 |
5:cookie中的数据会随着请求被自动发送到服务器端。
由于cookie存储机制有很多缺点,HTML5不再使用它,转而使用改良后的WebStorage存储机制
(localStorage和sessionStorage)
2.localStorage
什么是localStorage?
在HTML中,新加入了一个localStorage特性,这个特性主要用来作为本地存储来使用
它解决了cookie存储空间不足的问题,每条cookie的存储空间为4K,localStorage一般为5M
localstorage的生命周期
LocalStorage生命周期是永久,这意味着除非用户在显示在浏览器提供的UI上清除localStorage信息
否则这些信息将永久存在。
localStorage的局限
a.在IE8以上的IE版本才支持localstorage这个属性。
b.目前所有的浏览器中都会被localStorage的值类型限定为string类型,
对我们日常比较常见的JSON对象类型需要一个转换。
判断浏览器是否支持localStorage这个属性
if(window.localStorage){
alert('浏览器支持localStorage')
}
localStorage的写入
!表示取反
if(!window.localStorage){
alert('浏览器不支持localStorage')
}else{
var storage = window.localStorage;
//写入a字段
storage['a'] = 1;
//写入b字段
storage.b = 2;
//写入c字段
storage.setItem('c',3)
console.log(typeof storage['a']);
console.log(typeof storage['b']);
console.log(typeof storage['c']);
}
3.sessionStorage
SessionStorage的生命周期
SessionStorage生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了
那么所有通过Session存储的数据也就被清空了。
五、JSON转换
JSON字符串
JSON和JSON对象的关系:
JSON是JS对象的字符串表示法,它使用文本表示一个JS对象的信息,本质是一个字符串
<body>
<script>
//这个一个对象
var obj = {a:"hello", b:"world"};
console.log(typeof(obj)); //object
//这个一个JSON字符串,本质是一个字符串
var json = '{"a":"hello","b":"world"}';
console.log(typeof(json)); //string
</script>
</body>
JSON和JS对象互换
<body>
<script>
//【JSON和JS对象互转】
//JSON.parse()方法 实现从JSON字符串转换为JS对象
var obj = JSON.parse('{"a":"hello","b":"world"}')
console.log(obj); //object
//JSON.stringify()方法 实现从JS对象转换为JSON字符串
var json = JSON.stringify({a:"hello",b:"world"})
console.log(json);
</script>
</body>
六、控制页面css样式
绑定class属性
<style>
.box{
width: 300px;
height: 300px;
border: 5px solid #000;
}
.bg{
background-color: pink;
}
</style>
</head>
<body>
<div id="app">
<!-- 想要动态的设置class,也是给一个对象 -->
<!-- 属性名:就是类名 -->
<!-- 属性值是布尔值,如果给true,代表有这个类目;如果给false,代表没有这个类名 -->
<div class="box" v-bind:class="{bg:isBg}">
</div>
<button @click="btn" >更改背景颜色</button>
</div>
<script src="../vue.js"></script>
<script>
new Vue({
el:'#app',
data:{
isBg:true
},
methods:{
btn(){
if (this.isBg == true){
this.isBg = false;
}else{
this.isBg = true;
}
}
}
})
</script>
</body>
绑定style样式
<body>
<div id="app">
<div style=" color:rgb(231, 176, 207); font-size :24px">WOSGHI</div>
<!-- 把样式属性改造成对象属性:
要用大括号包裹起来;
把样式转换成属性值,用引号引起来;
把分号改成逗号:
样式名转换成属性名 -->
<div v-bind:style="{ color:'red',fontSize:'24px'}">wohsi </div>
<!-- 把对象值改变成变量 -->
<div v-bind:style="{ color:color,fontSize:size }">1111</div>
</div>
<script src="../vue.js"></script>
<script>
new Vue({
el:'#app',
data:{
color:'red',
fontSize:'24px',
}
})
</script>
</body>
七、事件处理
事件与事件流
事件:
JavaScript中的事件,可以理解就是在HTML文档或者浏览器中发生的一种交互操作,使得网页具备互动性。
常见的有加载事件、鼠标事件。
事件流:
由于DOM是一个树结构,如果在父子节点绑定事件时候,当触发子节点的时候,就存在一个顺序问题,这就涉及到了事件流
页面触发一个事件时,会按照一定的顺序来响应事件,事件的响应过程为事件流。
js的事件流三阶段:
事件捕捉阶段(capture phrase):事件开始由顶层对象触发,然后逐级向下传播,直到目标元素:
处于目标阶段(target phrase):处于绑定事件的元素上;
事件冒泡阶段(bubbling phrase):事件由具体的元素先接收,然后逐级向上传播,直到不具体的元素;
(事件捕获是从上到下,而事件冒泡,是从下到上。)
事件冒泡
微软提出了名为事件冒泡(event bubbling)的事件流。
事件冒泡可以形象的比喻成把一颗石头投入水中,泡泡会一直从水底冒出水面。
也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。
因此在事件冒泡的概念下在button按钮发生click事件的顺序应该是 button→div→body→html→document.
事件捕获
网景提出另一种事件流名为事件捕获(event capturing)。
与事件冒泡相反,事件会从最外层开始发生,直到具体的元素。
因此在事件捕获的概念下在button按钮发生click事件的顺序应该是document→html→body→div→button。
事件方法
@click:当单击元素时,发生click事件。
@dbclick:当双击元素时,发生dbclick事件。
@focus:当元素获得焦点时,发生focus事件。
@blur:当元素失去焦点时发生blur事件。
@submit:当提交表单时,发生submit事件。
@keydown:当键盘被按下时,发生keydown事件。
@keyup:当键盘被松开,发生keyups事件。
@mouse enter: 当鼠标指针穿过(进入)被选元素时,会发生mousedown事件。
@mouse down:当鼠标指针移动到元素上方,并按下鼠标左键,会发生mousedown事件。
@mouse leave: 当鼠标离开被选元素时,会发生mouseleave事件。
@mouse move:当鼠标指针在指定的元素中移动时,会发生 mousemove 事件。
@mouse out:在鼠标指针离开被选元素或任意子元素时都会被触发。
@mouse over:当鼠标指针位于元素上方时,会发生 mouseover 事件。
@mouse up:当鼠标指针移动到元素上方,并松开鼠标左键时,会发生 mouseup 事件。
事件修饰符
<body>
<div id="app">
<ul @click.self="ulClick">
<!-- 事件修饰符.self:当事件的目标是当前元素自身,才会触发处理函数。-->
<!-- .self可以理解为跳过冒泡事件和捕获事件,只有直接作用在该元素上的事件才可以执行。 -->
<!-- <ul @click.self="ulclick">
<li @click="liClick">111</li> -->
<!-- 使用vue方式阻止事件冒泡 -->
<!-- 事件修饰符stop:阻止事件冒泡 -->
<li @click.stop="liClick">111</li>
<li @click="liClick">222</li>
<li>333</li>
</ul>
</div>
<script src="../vue.js"></script>
<script>
new Vue({
el:'#app',
methods:{
// liclick(){
// console.log('li');
// },
ulClick(){
console.log('ul');
// evt.stopPropagation()
},
//使用JS方法阻止事件冒泡
liClick(evt){
console.log('li');
// evt.stopPropagation()
},
}
})
</script>
</body>
八、AJAX
Ajax就是让浏览器跟服务器交互的一套API。
它的作用就是让浏览器和服务器进行交互。
是向服务器请求数据的局部刷新页面的技术。
<body>
<script>
//1.创建一个请求对象
let xhr = new XMLHttpRequest()
//2.调用xhr的open方法,开启请求
//传入请求方式和请求地址
xhr.open('get','https://autumnfish.cn/api/joke')
//3.设置请求成功后的回调函数
xhr.onload = function (){
console.log(xhr.response);
}
//调用send方法
xhr.send()
</script>
</body>
ajax发送post请求
1.创建对象,实例化一个ajax对象
2.调用open方法,设置请求方式和地址
3.设置请求头
<script>
//1.创建请求对象,实例化一个ajax对象
let xhr = new XMLHttpRequest();
//2.调用open方法,设置请求
xhr.open('post', 'https://autumnfish.cn/api/user/register')
//3.设置请求头
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
//4.设置请求成功后的回调函数
xhr.onload = function () {
console.log(xhr.response);
}
//5.使用send()方法去发送ajax请求
xhr.send('username=王紫妮11111')
</script>
ajax发送get请求
<body>
<input type="button" value="点我看笑话" class="getJoke">
<div class="joke-container"></div>
<script>
document.querySelector('.getJoke').onclick = function () {
//1.创建一个请求对象
let xhr = new XMLHttpRequest()
//2.调用xhr的open方法,开启请求
//传入请求方式和请求地址
xhr.open('get', 'https://autumnfish.cn/api/joke')
//3.设置请求成功后的回调函数
xhr.onload = function () {
//把响应的内容打印到控制台里
// console.log(xhr.response);
//把响应的内容显示在div区域
document.querySelector('.joke-container').innerHTML = xhr.response
}
//调用send方法
xhr.send()
}
</script>
</body>
get请求提交参数
<body>
<script>
//AJAX发送get请求,通过url地址拼接传递参数
//url地址? key=value
//url地址? key1=value1&key2=value2
// 1.创建请求对象
let xhr = new XHLHttpRequest()
// 2.调用open,设置请求方式和请求地址
xhr.open('get','https://autumnfish.cn/api/joke/list?num=5')
// 3.设置请求成功后的回调函数
xhr.onload = function(){
console.log(xhr.response);
}
// 4.发送请求
xhr.send()
</script>
</body>