异步与同步是相反的
简单来理解就是:同步按你的代码顺序执行,异步不按照代码顺序执行,异步的执行效率更高。
在前端编程中(甚至后端有时也是这样),我们在处理一些简短、快速的操作时,例如计算 1 + 1 的结果,往往在主线程中就可以完成。主线程作为一个线程,不能够同时接受多方面的请求。所以,当一个事件没有结束时,界面将无法处理其他请求。
现在有一个按钮,如果我们设置它的 onclick 事件为一个死循环,那么当这个按钮按下,整个网页将失去响应。
为了解决类似这种问题,JavaScript 中的异步操作函数往往通过回调函数来实现异步任务的结果处理。
回调函数就是一个函数,它是在我们启动一个异步任务的时候就告诉它:等你完成了这个任务之后要干什么。这样一来主线程几乎不用关心异步任务的状态了,他自己会善始善终。
例如:
-
<!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>
-
</head>
-
<body>
-
<button>点击</button>
-
<p></p>
-
<script>
-
var btn = document.querySelector('button');
-
function fn1(){
-
btn.onclick = function(){
-
document.querySelector('p').innerHTML = '我出现了!'
-
}
-
}
-
setTimeout(fn1,1000)
-
</script>
-
</body>
-
</html>
它的第一个参数是个回调函数,第二个参数是毫秒数,这个函数执行之后会产生一个子线程,子线程会等待 1 秒,然后执行回调函数 "fn1",在命令行输出 "我出现了!"。
语法格式:
-
new Promise(function (resolve, reject) {
-
console.log("Run");
-
});
resolve和reject是参数
再看这个例子:
-
new Promise(function (resolve, reject) {
-
var a = 0;
-
var b = 1;
-
if (b == 0) reject("Divide zero");
-
else resolve(a / b);
-
}).then(function (value) {
-
console.log("a / b = " + value);
-
}).catch(function (err) {
-
console.log(err);
-
}).finally(function () {
-
console.log("End");
-
});
这段打印结果为:
这三个方法的参数都是一个函数,
.then() 可以将参数中的函数添加到当前 Promise 的正常执行序列,
.catch() 则是设定 Promise 的异常处理序列,
.finally() 是在 Promise 执行的最后一定会执行的序列。 .then() 传入的函数会按顺序依次执行,有任何异常都会直接跳到 catch 序列:
-
new Promise(function (resolve, reject) {
-
console.log(1111);
-
resolve(2222);
-
}).then(function (value) {
-
console.log(value);
-
return 3333;
-
}).then(function (value) {
-
console.log(value);
-
throw "An error";
-
}).catch(function (err) {
-
console.log(err);
-
});
打印结果为:
1111 2222 3333 An error
resolve() 中可以放置一个参数用于向下一个 then 传递一个值,then 中的函数也可以返回一个值传递给 then。但是,如果 then 中返回的是一个 Promise 对象,那么下一个 then 将相当于对这个返回的 Promise 进行操作,这一点从刚才的计时器的例子中可以看出来。
reject() 参数中一般会传递一个异常给之后的 catch 函数用于处理异常。
但是请注意以下两点:
分享此文一切功德,皆悉回向给文章原作者及众读者.
转自:csdn
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
本文重点
函数案例
函数作为参数使用
arguments
作用域:写代码要注意变量的在哪声明和使用的问题
预解析:什么叫预解析, 什么东西发生了什么事
声明的变量是使用var声明的,那么这个变量就是全局变量,
全局变量可以在页面的任何位置使用
除了函数以外,其他的任何位置定义的变量都是全局变量
如果页面不关闭,那么就不会释放,就会占空间,消耗内存
全局作用域:全局变量的使用范围
在函数内部定义的变量,是局部变量,外面不能使用
局部作用域:局部变量的使用范围
注意:
块级作用域:一对大括号就可以看成是一块,在这块区域中定义的变量,只能在这个区域中使用,但是在js中在这个块级作用域中定义的变量,外面也能使用;
说明:js没有块级作用域,只有函数除外
隐式全局变量:声明的变量没有var,就叫隐式全局变量
全局变量是不能被删除的,隐式全局变量是可以被删除的
定义变量使用var是不会被删除的,没有var是可以删除的
function f1() { number=1000;//是隐式全局变量 }f1();console.log(number); var num1=10; num2=20; delete num1;//把num1删除了 delete num2;//把num2删除了 console.log(typeof num1); console.log(num1+10); console.log(typeof num2); num=100; console.log(num); function f1() { var num=100; num+=10; }f1();//这个函数结束之后 { var num=10; console.log(num);//10 }console.log(num); if(true){ var num=10; }console.log(num); for(var i=0;i<5;i++){ var number=20; }console.log(number); var i=0; while (i<5){ var num=100; i++; } console.log(num); function f1() { var num=10; }f1(); console.log(num); var num=10; console.log(num);//10
var num=10; function f1() { var num=20; function f2() { var num=30; function f3() { var num=50; console.log(num); } f3(); } f2(); } f1();
function getMax(num1, num2) { return num1 > num2 ? num1 : num2; } console.log(getMax(10,20)); //console.log(getMax);//函数的代码 function getMax(num1, num2) { return num1 > num2 ? num1 : num2; } var num1=10; var num2=20; //函数外面的num1和函数的形参num1不是同一个变量 var result=getMax(num1,num2); console.log(result); console.log(getMax);//函数的代码
function getThreeMax(x, y, z) { return x > y ? (x > z ? x : z) : (y > z ? y : z); } console.log(getThreeMax(10,2,24));
判断一个数是否是素数(质数),只能被1和自身整除,质数是从2开始
用这个数字和这个数字前面的所有的数字整除一次(没有1的,没有自身的)
function isPrimeNumber(num) { for (var i = 2; i < num; i++) { if (num % i == 0) { //说明有一个数字整除了,就没有必要向后继续整除了, //此时就已经验证出不是质数 return false; } } return true; }// console.log(isPrimeNumber(7) ? "yyyyyes" : "nnnnnno") var aa = isPrimeNumber(17); if (aa) { console.log("yes"); } else { console.log("no"); } function isPrimeNumber(num) { for(var i=2;i<num;i++){ if(num%i==0){ //说明有一个数字整除了,就没有必要向后继续整除了,此时就已经验证出不是质数 return false; } } return true; } console.log(isPrimeNumber(8)?"是质数":"不是质数"); var result=isPrimeNumber(2); if(result){ console.log("这个数字是质数"); }else{ console.log("这个数字不是质数"); } function aaa(x,y){ return x-y; } console.log(aaa(99,88))
function reverseArray(arr) { for (var i = 0; i < arr.length / 2; i++) { var temp = arr[i]; arr[i] = arr[arr.length - 1 - i]; arr[arr.length - 1 - i] = temp; } return arr; } console.log(reverseArray([1, 2, 3, 4, 5]));
function sortArray(arr) { //控制比较的轮数 for (var i = 0; i < arr.length - 1; i++) { //控制每一轮的比较次数 for (var j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { var temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; }//end if }//end for }//end for return arr; } console.log(sortArray([0, 19, 34, 10, 100, 2]));
function getJieCheng(num) { var result = 1; for (var i = 1; i <= num; i++) { result *= i; } return result; }console.log(getJieCheng(5));//1*2*3*4*5
function getFib(num){ var num1=1; var num2=1; var num3=0; for(var i=3;i<=nnum;i++){ sum=num1+num2; num1=num2; num2=sum; } return sum; } console.log(getFib(12))
函数是有数据类型,数据类型:是function类型的
函数可以作为参数使用,如果一个函数作为参数,那么我们说这个参数(函数)可以叫回调函数 只要是看到一个函数作为参数使用了,那就是回调函数
function sayHi(fn) { console.log("GOOD"); fn();//fn此时应该是一个函数}function suSay() { console.log("HELLO"); }sayHi(suSay); function f1(x,y) { console.log(x+y); }f1(10,20); function f2(x,y) { console.log(x+y); }f2("HLS","MISS"); function f3(x) { console.log(x); }f3(true);
定义一个函数,如果不确定用户是否传入了参数,或者说不知道用户传了几个参数,没办法计算,但是如果在函数中知道了参数的个数,等于也知道了每个参数的值.
//定义function f1() { //获取的是函数在调用的时候,传入了几个参数 //console.log(arguments.length); //使用arguments对象可以获取传入的每个参数的值 console.log(arguments); } f1(10,20,30,40,100,200);//调用
function f1() { //arguments----->数组使用------伪数组--- var sum=0; for(var i=0;i<arguments.length;i++){ sum+=arguments[i]; } return sum; } console.log(f1(10,20,30));
预解析:就是在解析代码之前,预解析做什么事?
把变量的声明提前了----提前到当前所在的作用域的最上面
函数的声明也会被提前—提前到当前所在的作用域的最上面
函数调用的时候, 把会函数的声明提升到作用域的上面
f1();//调用 var num=20;//这个变量的声明会提升到变量使用之前 function f1() { console.log(num); //var num=10; } function f1() { console.log("小苏好猥琐"); } f1(); function f1() { console.log("小苏没有助教猥琐"); } f1();
var num; console.log(num); num=10; function f1() { console.log("哈哈,助教好猥琐哦"); } //f1();//报错
注意:预解析中,变量的提升,只会在当前的作用域中提升,提前到当前的作用域的最上面
function f1() { console.log(num);//undefined var num = 10; } f1(); console.log(num);//报错
函数中的变量只会提前到函数的作用域中的最前面,不会出去
预解析会分段(多对的script标签中函数重名,预解析的时候不会冲突)
function f1() { console.log(num);//undefined var num=10; } f1(); console.log(num);// function f1() { console.log("哈哈"); }
分享此文一切功德,皆悉回向给文章原作者及众读者.
转自:csdn
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
在前面项目的基础上继续,因为我们的项目进来页面主题就是用户数据,所以我们想让它一进来就是高亮状态,那么我们可以这样做,同时我们再加上一个菜单:
<template> <div> <el-menu
style="width: 200px; min-height: calc(100vh - 50px)" default-active="user" :default-openeds="[1]" class="el-menu-vertical-demo"> <!--这是两个函数,我们可以先不写@open="handleOpen"--> <!--@close="handleClose"--> <el-sub-menu index="1"> <template #title>系统管理</template> <el-menu-item index="user" :route-="{path:'/'}">用户管理</el-menu-item> </el-sub-menu> <el-menu-item index="data" :route-="{path:'/'}">数据管理</el-menu-item> </el-menu> </div> </template> <script> export default { name: "Aside" } </script> <style scoped> </style>
访问效果如下:

可以看到一进来因为页面主体是用户管理界面,所以“用户管理”菜单栏是默认高亮的,然后还多了一个数据管理的菜单栏。
菜单之间怎么跳转一会儿再讲,我们现在先写注册和登录。
在写注册和登录之前,我们先讲一下路由,可以看到我们这个路由文件下的文件中,默认的“/”访问的是Home文件:

Home页面写的就是我们这个用户表的增删改查,然后Home页面呢是在我们的App.vue中给它嵌入进去了的,<router-view>就是展示的Home,还有Header啊Aside等

首先我们尝试一下可不可以直接在路由文件下写上一个Login路由,同时我们在views页面下创建一个Login.vue,看看能不能成功:

注意引入路由的写法要特别注意,不能直接像下面这样写:
而应该用引入的方式;

现在我们重启访问/login:

成功;
但是同时我们也看到了问题,我们明明是登录界面,为什么进到了后台主页了,而我们想要的登录界面应该是一个非常独立的界面,所以我们的这个路由是有问题的。因为我们之前是直接使用App.vue作为项目的框架,其实这个App.vue在我们的main.js里面是引入进来了的:

引进来之后呢直接作为createApp的根节点来使用,所以这个App.vue这个界面呢,不适合用来作为我们的这个后台骨架来使用:

它应该是一个全局的根节点,所以我们需要把App.vue里面的东西把它给挪走,挪到另一个界面,我们要把App.vue呢给它空出来,让App.vue可以直接访问我们所有的界面。
怎么做呢?
我们在src目录下新建一个layout文件夹,这个文件夹呢就用来做我们项目的骨架部分,再在这个文件下建一个Layout组件:

然后把之前在App.vue的东西copy过来:

然后Header啊Aside啊那些组件我们需要在这个组件里面进行引入,然后App里面的那些原来引入的就删掉就行了:

然后现在我们就完成了迁移,现在我们要去配置一下我们的路由,实现我们后台的一个访问。
路由怎么配置呢?非常简单。
我们来讲解一下刚才这个什么意思,App.vue里面我们只写了一个router-view,而这个router-view呢就作为我们这个全局的一个根节点访问,
这个router-view里面既可以是登录界面也可以是注册界面也可以是后台主体,就是根据它是路由进行一个展示。当我们访问到我们的后台主体的时候,会进行一个二次的嵌套路由,那这个嵌套路由怎么写呢?我们先配置登录页面的路由和后台管理布局的路由:
此时访问/和访问/login都能到达对应的页面:

但是访问/时,Home主体页面并没有出来,只出来了/对应的页面骨架的路由,怎么让这个Home主体页面出来呢,这就涉及到了嵌套路由,像下面这样写,children属性是一个数组属性,意味着其可以嵌套多个路由:

访问/下的home路由,可以看到如下页面:
vue-router给我们提供了重定向属性,可以让我们在访问某个路由页面时自动重定向到一个我们指定的路由页面:

现在我们直接访问/试试,可以看到依然是访问的home页面:

现在路由问题解决之后我们就可以去写我们的登录页面了。
<template> <!--将整个浏览器页面放在一个大div里
width: 100%表示这个让div撑满全屏
height: 100vh同上,一个撑满高度一个撑满宽度--> <div style="width: 100%; height: 100vh;background: darkslateblue; overflow: hidden"> <!--margin: 参数1 参数2; 参数1表示上下距离,参数2表示左右距离,auto表示自动匹配
如果页面产生了空白,就在外层最大的div上加一个overflow,设置为hidden即可--> <div style="width: 400px; margin: 150px auto"> <!--font-size表示字体大小,text-align表示字体居中--> <div style="color: #cccccc; font-size: 30px; text-align: center; padding: 30px 0"> 欢迎登录 </div> <!--然后去element上copy一个表单--> <el-form ref="form" :model="form" size="normal"> <el-form-item> <el-input prefix-icon="el-icon-user-solid" v-model="form.username"></el-input> </el-form-item> </el-form> <el-form ref="form" :model="form" size="normal"> <el-form-item> <el-input prefix-icon="el-icon-lock" v-model="form.password" show-password></el-input> </el-form-item> </el-form> <el-form ref="form" :model="form" size="normal"> <el-button style="width: 100%" type="primary" @click="login">登录</el-button> </el-form> </div> </div> </template> <script> import request from "../utils/request"; export default { name: "Login", data(){ return{ form:{} } }, methods: { login() { request.post("/api/user/login",this.form).then(res => { if(res.code === "0"){ this.$messageBox({ type: "success", message: "登录成功" }) // 登录成功之后进行页面跳转,跳转到主页 this.$router.push("/") }else{ this.$messageBox({ type: "error", message: res.msg }) } }) } } } </script> <style scoped> </style>
然后现在去写我们后端的接口:
//用户登录 @PostMapping("/login") public Result login(@RequestBody User user){ User res = userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUsername,user.getUsername()).eq(User::getPassword,user.getPassword())); if(res != null){ //登录成功 return Result.success(); }else{ //登录失败 return Result.error("-1","用户名或密码错误"); } }
现在数据库中的数据有:

我们试着访问登录:

可以看到登录成功:

然后我们再去我们的网页头部右侧的用户信息栏写一下退出系统的操作:

点击则路由跳转到登录页面。
然后还有注册页面,也一起写了,注册页面其实逻辑和登录界面差不多,我们copy一个登录组件进行更改即可。

然后进行更改就行了,注册不过就比登录多了一个确认密码的操作。
<template> <!--将整个浏览器页面放在一个大div里
width: 100%表示这个让div撑满全屏
height: 100vh同上,一个撑满高度一个撑满宽度--> <div style="width: 100%; height: 100vh;background: darkslateblue; overflow: hidden"> <!--margin: 参数1 参数2; 参数1表示上下距离,参数2表示左右距离,auto表示自动匹配
如果页面产生了空白,就在外层最大的div上加一个overflow,设置为hidden即可--> <div style="width: 400px; margin: 150px auto"> <!--font-size表示字体大小,text-align表示字体居中--> <div style="color: #cccccc; font-size: 30px; text-align: center; padding: 30px 0"> 欢迎登录 </div> <!--然后去element上copy一个表单--> <el-form ref="form" :model="form" size="normal" :rules="rules"> <el-form-item prop="username"> <el-input prefix-icon="el-icon-user-solid" v-model="form.username"></el-input> </el-form-item> <el-form-item prop="password"> <el-input prefix-icon="el-icon-lock" v-model="form.password" show-password></el-input> </el-form-item> </el-form> <el-form ref="form" :model="form" size="normal"> <el-button style="width: 100%" type="primary" @click="login">登录</el-button> </el-form> </div> </div> </template> <script> import request from "../utils/request"; export default { name: "Login", data(){ return{ form:{}, rules:{ username:[ {required: true,message:"请输入用户名",trigger:'blur'}, ], password:[ {required: true,message:"请输入密码",trigger:'blur'}, ] } } }, methods: { login() { //发送请求之前先加这个判断,不为空且满足规则时才发送请求 this.$refs['form'].validate((valid) => { if (valid) { request.post("/api/user/login",this.form).then(res => { if(res.code === "0"){ this.$messageBox({ type: "success", message: "登录成功" }) // 登录成功之后进行页面跳转,跳转到主页 this.$router.push("/") }else{ this.$messageBox({ type: "error", message: res.msg }) } }) } }) } } } </script> <style scoped> </style>
其中我们还要注意在请求数据之前要先判断表单内部是否数据符合要求,否则不予请求,同样在Login页面内也要加入该判断:
<template> <!--将整个浏览器页面放在一个大div里
width: 100%表示这个让div撑满全屏
height: 100vh同上,一个撑满高度一个撑满宽度--> <div style="width: 100%; height: 100vh;background: darkslateblue; overflow: hidden"> <!--margin: 参数1 参数2; 参数1表示上下距离,参数2表示左右距离,auto表示自动匹配
如果页面产生了空白,就在外层最大的div上加一个overflow,设置为hidden即可--> <div style="width: 400px; margin: 150px auto"> <!--font-size表示字体大小,text-align表示字体居中--> <div style="color: #cccccc; font-size: 30px; text-align: center; padding: 30px 0"> 欢迎登录 </div> <!--然后去element上copy一个表单--> <el-form ref="form" :model="form" size="normal" :rules="rules"> <el-form-item prop="username"> <el-input prefix-icon="el-icon-user-solid" v-model="form.username"></el-input> </el-form-item> <el-form-item prop="password"> <el-input prefix-icon="el-icon-lock" v-model="form.password" show-password></el-input> </el-form-item> </el-form> <el-form ref="form" :model="form" size="normal"> <el-button style="width: 100%" type="primary" @click="login">登录</el-button> </el-form> </div> </div> </template> <script> import request from "../utils/request"; export default { name: "Login", data(){ return{ form:{}, rules:{ username:[ {required: true,message:"请输入用户名",trigger:'blur'}, ], password:[ {required: true,message:"请输入密码",trigger:'blur'}, ] } } }, methods: { login() { //发送请求之前先加这个判断,不为空且满足规则时才发送请求 this.$refs['form'].validate((valid) => { if (valid) { request.post("/api/user/login",this.form).then(res => { if(res.code === "0"){ this.$messageBox({ type: "success", message: "登录成功" }) // 登录成功之后进行页面跳转,跳转到主页 this.$router.push("/") }else{ this.$messageBox({ type: "error", message: res.msg }) } }) } }) } } } </script> <style scoped> </style>
然后现在去后端写register的接口:
//用户注册 @PostMapping("/register") public Result register(@RequestBody User user){ User res = userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUsername,user.getUsername())); if(res != null){ //说明用户名重复 return Result.success(); } //该用户不存在,予以注册 if(user.getPassword() == null){ user.setPassword("123456"); } userMapper.insert(user); return Result.success(); }
就此我们的注册页面也写完啦。
1
分享此文一切功德,皆悉回向给文章原作者及众读者.
转自:csdn
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
1.在搭建项目之前,先安装淘宝镜像和命令行工具,可能需要等待一段时间(电脑安装过一遍之后,以后建项目时就不需要再安装):
a.Win+R打开命令提示行cmd;
b.进入命令行cmd,设置淘宝镜像;
npm config set registry https://registry.npm.taobao.org
c.安装可生成脚手架代码的命令行工具;
npm i -g @vue/cli
当命令行窗口显示 [ + @vue/cli@版本号 ] 时说明安装成功。
2.开始创建项目
a.先决定要把项目文件夹保存在哪个位置;此处以test文件夹为例;
b.进入test文件夹后,shift+鼠标右键打开power shell窗口;
c.运行 vue create 自定义项目名;此处以项目名为test为例,出现提示后按以下过程进行选择:
1)? Please pick a preset:
2)? Check the features needed for your project:
3)? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) N
4)? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): (Use arrow keys)
5)? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
6)? Save this as a preset for future projects? (y/N) N
最后命令行窗口显示Successfully created project xzvue说明项目创建成功。
此时我们便可在test文件夹中看到整个项目。
3.使用vscode打开并运行脚手架项目
(1)右键单击package.json文件,选择"在集成终端中打开";
(2)在终端窗口中输入:npm run serve,等待后出现App running at : - Local :http://localhost:8080/,如下:
(3)按住Ctrl,点local:右侧的连接,即可自动打开浏览器。
需要的时候就可以在集成终端中输入npm i、npm i vant -S 等命令下载node_modules包、安装vant等等。
注意vue采用的是热编译,修改后无需停止或重启项目,只要一修改,就会立刻自动重新编译,重新运行,自动在界面上显示新内容。
分享此文一切功德,皆悉回向给文章原作者及众读者.
转自:csdn
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
定义:持续触发事件,一定时间内没有触发事件,事件处理函数只会执行一次,
当设定的时间内触发过一次事件后会重新开始延时。
例:输入框的事件(2s显示内容,不是实时刷新显示内容),对比输入框的内容事件。
实时刷新的效果
<div class="container"> <div class="left"> <p>实时刷新显示内容</p> <input type="text" id="leftInput" /> <div class='textDiv' id="textShow"></div> </div> <div class="right"></div> </div> <script> var inputDom=document.getElementById('leftInput'); inputDom.addEventListener('keyup',function(e){ var textDom=document.getElementById('textShow'); console.log(e.target.value); textDom.innerText=e.target.value; }) </script>
输入123456会依次打印123456的金字塔
防抖(1s内显示输入内容)
<div class="container"> <div class="left"> <p>防抖(1s内显示输入内容)</p> <input type="text" id="leftInput" /> <div class='textDiv' id="textShow"></div> </div> <div class="right"></div> </div> <script> // 防抖 var inputDom = document.getElementById('leftInput'); // 函数柯里化 function debounce(delay, callback) { let timer return function(value) { //闭包内存泄漏 clearTimeout(timer) timer = setTimeout(function() { //执行 callback(value) }, delay) } } // 显示内容的函数 function showText(value) { var textDom = document.getElementById('textShow'); console.log(value) textDom.innerText = value; } var debounceFunc = debounce(1000, showText); inputDom.addEventListener('keyup', function(e) { let value = e.target.value debounceFunc(value) }) </script>
1s内输入123456只会打印一次123456
定义:一段时间直只调用一次事件处理函数
实际用例:提交事件 、游戏的技能cd(在游戏cd中点击n次都不会发动技能)
// 节流
var skillDom = document.getElementById('skillTriger');
function throttle(wait,callback) {
let timeOut;
return function(value) {
if (!timeOut) {
timeOut = setTimeout(function() {
callback(value);
//执行一次,时间段内的都不知执行
timeOut = null;
}, wait)
}
}
}
function skillEvent(value){
var textDom = document.getElementById('skillEventId');
console.log(value)
++count
textDom.innerText = value+count;
}
var skillAc=throttle(3000,skillEvent)
var count=0
skillDom.addEventListener('click', function(e) {
let value = e.target.value
skillAc(value)
})
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>防抖与节流</title> </head> <style> * { margin: 0; padding: 0; } .container { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 600px; height: 400px; background: #262626; display: flex; } .left { position: relative; width: 50%; height: 100%; background: #00cec9; box-sizing: border-box; overflow: hidden; } .right { position: relative; width: 50%; height: 100%; background: #b2bec3; } </style> <body> <div class="container"> <div class="left"> <p>防抖(1s内显示输入内容)</p> <input type="text" id="leftInput" /> <div class='textDiv' id="textShow"></div> </div> <div class="right"> <p>节流(3s内触发一次)</p> <input type="submit" id="skillTriger" value="发动技能" /> <div class='skillEvent' id="skillEventId"></div> </div> </div> <script> // 防抖 var inputDom = document.getElementById('leftInput'); // 函数柯里化 function debounce(delay, callback) { let timer return function(value) { //闭包内存泄漏 clearTimeout(timer) timer = setTimeout(function() { //执行 callback(value) }, delay) } } // 显示内容的函数 function showText(value) { var textDom = document.getElementById('textShow'); console.log(value) textDom.innerText = value; } var debounceFunc = debounce(1000, showText); inputDom.addEventListener('keyup', function(e) { let value = e.target.value debounceFunc(value) }) // 节流 var skillDom = document.getElementById('skillTriger'); function throttle(wait, callback) { let timeOut; return function(value) { if (!timeOut) { timeOut = setTimeout(function() { callback(value); //执行一次,时间段内的都不知执行 timeOut = null; }, wait) } } } function skillEvent(value) { var textDom = document.getElementById('skillEventId'); console.log(value) ++count
textDom.innerText = value + count; } var skillAc = throttle(3000, skillEvent) var count = 0 skillDom.addEventListener('click', function(e) { let value = e.target.value skillAc(value) }) </script> </body> </html>
1
分享此文一切功德,皆悉回向给文章原作者及众读者.
转自:csdn
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
提供的接口:http://localhost:7001/setUserList
2、新建:app / controller / new / user.js
1、初始化查询列表数据:页面展示列表,列表数据mouted获取
2、页面点击新增:展示弹框,并将弹框内容去除掉,点击新增,将弹框内容发送给后端
3、页面点击列表里面具体数据的编辑:弹框,并回填数据,修改将当前数据的id和表格数据传给后端
4、删除按钮,点击出现二次确认弹框,点击确认将当前数据的id给后端就行
-
/**
-
* 路由配置
-
* @param app
-
*/
-
module.exports = app => {
-
const { router, controller } = app;
-
//--------------------------------------------------
-
// server接口
-
//--------------------------------------------------
-
app.get('/setUserList', controller.new.user.getUserList);
-
app.post('/setUserList', controller.new.user.postUserList);
-
app.put('/setUserList', controller.new.user.putUserList);
-
app.delete('/setUserList', controller.new.user.deleteUserList);
-
-
}
select * from user where name like ? % 内容 %
this.app.mysql.insert('表名',内容)
UPDATE loginlist SET `password` = 'Ad123456' WHERE id = 2
delete from user where id = 1
-
/**
-
* 用户信息路由
-
* @param app
-
* @returns {HomeController}
-
*/
-
const Controller = require('egg').Controller;
-
class NewsController extends Controller {
-
async getUserList() {
-
//查询库里的user表
-
let params = this.ctx.query //获取路径后面的参数
-
console.log('用户的参数:');
-
console.log(params);
-
let sql = 'select * from user'
-
let initSql = sql
-
-
let content = [];//参数
-
let isMore = false;//是否有多个查询参数
-
/**
-
* @模糊查询-量大的时候效率低
-
* select * from user where name like ? % 内容 %
-
* 在user表中全局查找name值 == 内容的
-
* % 内容 % 全局查找内容
-
* 内容 % 查找以 内容 开头的数据
-
* */
-
if(params.name){
-
sql += " where name like ?";
-
content.push( "%"+params.name+"%" );
-
isMore = true;
-
}
-
if(params.age){
-
if(isMore){//true代表有多个参数
-
sql += "and age LIKE ?";//and是两个条件都必须满足,or是或的关系
-
}else{
-
sql += " WHERE age LIKE ?";
-
}
-
content.push( "%"+params.age+"%" )
-
isMore = true;
-
}
-
if(params.address){
-
if(isMore){//true代表有多个参数
-
sql += "and address LIKE ?";//and是两个条件都必须满足,or是或的关系
-
}else{
-
sql += " WHERE address LIKE ?";
-
}
-
content.push( "%"+params.address+"%" )
-
isMore = true;
-
}
-
if(params.phone){
-
if(isMore){//true代表有多个参数
-
sql += "and phone LIKE ?";//and是两个条件都必须满足,or是或的关系
-
}else{
-
sql += " WHERE phone LIKE ?";
-
}
-
content.push( "%"+params.phone+"%" )
-
}
-
-
//开启分页
-
if(params.page || params.pageSize){
-
let current = params.page;//当前页码
-
let pageSize = params.pageSize;//一页展示多少条数据
-
sql += " limit ?,?";
-
content.push((current-1)*pageSize,parseInt(pageSize));
-
}
-
-
let allList = await this.app.mysql.query(initSql);
-
let userList= await this.app.mysql.query(
-
sql,content
-
);
-
this.ctx.body = {
-
code:200,
-
masg:'success',
-
data: {
-
list:userList,
-
total:allList.length
-
}
-
};
-
}
-
/**
-
* 新增用户信息
-
* INSERT INTO loginlist (aaa,bbb,ccc) VALUES ('"'+111+'","'+222+'","'+333+'"')
-
* this.app.mysql.insert('表名',内容)
-
* this.app.mysql.insert('表名',{
-
* name:123,
-
* age:1,
-
* address:西安
-
* })
-
*/
-
//
-
async postUserList() {
-
console.log(this.ctx.request.body);
-
//新增数据-user表
-
let data = this.ctx.request.body
-
data.id = parseInt(Math.random()*100000)
-
let insertResult = await this.app.mysql.insert(
-
'user', data
-
);
-
this.ctx.body = {
-
code:200,
-
masg:'success',
-
data:insertResult
-
};
-
}
-
/**
-
* 修改用户信息
-
* UPDATE loginlist SET `password` = 'Ad123456' WHERE id = 2(唯一值),修改一个
-
* UPDATE loginlist SET username = 'admins', `password` = 'Ad123456' WHERE id = 2,修改多个
-
*/
-
async putUserList(){
-
//新增数据-user表
-
let id = this.ctx.query.id //获取路径后面的参数
-
let data = this.ctx.request.body
-
let sql = 'update user set '
-
let isMore = false;//是否有多个查询参数
-
if(!id){
-
this.ctx.body = {
-
code:200,
-
masg:'warning',
-
data:'id没有传'
-
};
-
return
-
}
-
//姓名
-
if(data.name){
-
sql += 'name = "'+data.name+'"'
-
isMore = true
-
}
-
//年龄
-
if(data.age){
-
if(isMore){
-
sql += ',age = "'+data.age+'"'
-
}else{
-
sql += 'age = "'+data.age+'"'
-
}
-
isMore = true
-
}
-
//地址
-
if(data.address){
-
if(isMore){
-
sql += ',address = "'+data.address+'"'
-
}else{
-
sql += 'address = "'+data.address+'"'
-
}
-
isMore = true
-
}
-
//手机号
-
if(data.phone){
-
if(isMore){
-
sql += ',phone = "'+data.phone+'"'
-
}else{
-
sql += 'phone = "'+data.phone+'"'
-
}
-
isMore = true
-
}
-
//邮箱地址
-
if(data.email){
-
if(isMore){
-
sql += ',email = "'+data.email+'"'
-
}else{
-
sql += 'email = "'+data.email+'"'
-
}
-
}
-
sql += ' where id = ' + id
-
-
let insertResult = await this.app.mysql.query(sql)
-
this.ctx.body = {
-
code:200,
-
masg:'success',
-
data:insertResult
-
};
-
}
-
/**
-
* 注销用户接口
-
* DELETE FROM loginlist WHERE username = 'superman'
-
*/
-
async deleteUserList(){
-
//查询库里的user表
-
let params = this.ctx.query //获取路径后面的参数
-
console.log('用户的参数:');
-
console.log(params);
-
let sql = 'delete from user where id = '+ params.id
-
let res = await this.app.mysql.query(
-
sql
-
);
-
this.ctx.body = {
-
code:200,
-
masg:'success',
-
data:res
-
};
-
-
}
-
-
/**
-
* 模糊查询封装方法
-
* @params = auth
-
* */
-
async query( auth ) {
-
const TABLE_NAME = 'user';
-
const QUERY_STR = 'id, name, age, phone, address';
-
let sql = `select ${QUERY_STR} from ${TABLE_NAME} where authName like "%${auth.authName}%"`;
-
const row = await this.app.mysql.query(sql);
-
return row;
-
}
-
-
-
}
-
module.exports = NewsController;
-
-
<el-table
-
:data="tableInfo.list"
-
style="width: 100%">
-
<el-table-column
-
type="index"
-
label="序号"
-
align="center">
-
</el-table-column>
-
<el-table-column
-
prop="name"
-
label="姓名"
-
align="center">
-
</el-table-column>
-
<el-table-column
-
prop="age"
-
label="年龄"
-
align="center">
-
</el-table-column>
-
<el-table-column
-
prop="address"
-
label="地址">
-
</el-table-column>
-
<el-table-column
-
prop="phone"
-
label="手机号">
-
</el-table-column>
-
<el-table-column
-
prop="email"
-
label="邮箱">
-
</el-table-column>
-
<el-table-column
-
label="操作"
-
width="100">
-
<template slot-scope="scope">
-
<el-button type="text" size="small" @click="editFun(scope.row)">编辑</el-button>
-
<el-button type="text" size="small" @click="deleteFun(scope.row)">删除</el-button>
-
</template>
-
</el-table-column>
-
</el-table>
-
mounted(){
-
this.initPage()
-
},
-
methods: {
-
/***
-
* 获取列表数据
-
*/
-
initPage() {
-
let params = {
-
page: this.tableInfo.page, //当前页
-
pageSize: this.tableInfo.pageSize //一页展示多少条
-
}
-
this.$axios.get('http://localhost:7001/setUserList',{
-
params
-
}).then(res => {
-
this.tableInfo.list = res.data.data.list
-
this.tableInfo.total = res.data.data.total
-
}).catch(error => {
-
this.$message(error.data.message)
-
})
-
},
-
-
}
-
<template>
-
<div class="pro_body">
-
<el-button type='success' @click="addModal">新增</el-button>
-
<el-table
-
:data="tableInfo.list"
-
style="width: 100%">
-
<el-table-column
-
type="index"
-
label="序号"
-
align="center">
-
</el-table-column>
-
<el-table-column
-
prop="name"
-
label="姓名"
-
align="center">
-
</el-table-column>
-
<el-table-column
-
prop="age"
-
label="年龄"
-
align="center">
-
</el-table-column>
-
<el-table-column
-
prop="address"
-
label="地址">
-
</el-table-column>
-
<el-table-column
-
prop="phone"
-
label="手机号">
-
</el-table-column>
-
<el-table-column
-
prop="email"
-
label="邮箱">
-
</el-table-column>
-
<el-table-column
-
label="操作"
-
width="100">
-
<template slot-scope="scope">
-
<el-button type="text" size="small" @click="editFun(scope.row)">编辑</el-button>
-
<el-button type="text" size="small" @click="deleteFun(scope.row)">删除</el-button>
-
</template>
-
</el-table-column>
-
</el-table>
-
<el-pagination
-
class="work_pagination"
-
background
-
:layout="'total,prev, pager, next,sizes'"
-
:total="tableInfo.total"
-
:hide-on-single-page="false"
-
:current-page="tableInfo.page"
-
:page-size="tableInfo.pageSize"
-
@current-change="pageChange"
-
@size-change="pageSizeChange"
-
>
-
</el-pagination>
-
-
-
<!--弹框部分-->
-
<el-dialog
-
custom-class="modalName"
-
:visible.sync="modal.show"
-
:width="modal.type === 'delete'? '470px' : '800px'">
-
<h3 v-if="modal.type === 'add'" slot="title">新增</h3>
-
<h3 v-if="modal.type === 'edit'" slot="title">修改</h3>
-
<h3 v-if="modal.type === 'delete'" slot="title">提示</h3>
-
<div v-if="modal.type !== 'delete'" class="editNameBody">
-
<el-form class="editNameFrom" :model="modalFrom" label-width="120px" label-position="right">
-
<el-form-item label="姓名:">
-
<el-input v-model="modalFrom.name"></el-input>
-
</el-form-item>
-
<el-form-item label="年纪:">
-
<el-input v-model="modalFrom.age"></el-input>
-
</el-form-item>
-
<el-form-item label="手机号:">
-
<el-input v-model="modalFrom.phone"></el-input>
-
</el-form-item>
-
<el-form-item label="邮箱:">
-
<el-input v-model="modalFrom.email"></el-input>
-
</el-form-item>
-
<el-form-item label="地址:">
-
<el-input v-model="modalFrom.address" type="textarea"></el-input>
-
</el-form-item>
-
</el-form>
-
</div>
-
<div v-if="modal.type === 'delete'">
-
<i class="el-icon-warning" style="margin-right:7px;color:#FFAA00;font-size: 16px;"></i>请确认是否删除
-
</div>
-
<div slot="footer" class="dialog-footer">
-
<el-button @click="modal.show = false">取 消</el-button>
-
<el-button v-if="modal.type === 'add'" type="primary" @click="addUserList">新增</el-button>
-
<el-button v-if="modal.type === 'edit'" type="primary" @click="editUserList">修改</el-button>
-
<el-button v-if="modal.type === 'delete'" type="primary" @click="deleteUserList">确 认</el-button>
-
</div>
-
</el-dialog>
-
-
-
</div>
-
</template>
-
-
<script>
-
export default {
-
data() {
-
return {
-
tableInfo:{
-
columns:[
-
{
-
title: '序号',
-
type: 'index',
-
},{
-
title: '姓名',
-
prop: 'name',
-
align: 'center'
-
},
-
{
-
title: '年龄',
-
prop: 'age',
-
align: 'center'
-
},{
-
title: '地址',
-
prop: 'address',
-
align: 'center'
-
},{
-
title: '邮箱',
-
prop: 'email',
-
align: 'center'
-
},{
-
title: '手机号',
-
prop: 'phone',
-
align: 'center'
-
},
-
{//内容slot
-
slot: 'operateT'
-
}
-
],
-
list:[],
-
page:1,
-
pageSize:10,
-
total:0,
-
},
-
modal:{
-
show:false,
-
type:'add'
-
},
-
modalFrom:{
-
name:'',
-
age:'',
-
phone:'',
-
address:'',
-
email:''
-
}
-
};
-
},
-
mounted(){
-
this.initPage()
-
},
-
methods: {
-
/***
-
* 获取列表数据
-
*/
-
initPage() {
-
let params = {
-
page: this.tableInfo.page, //当前页
-
pageSize: this.tableInfo.pageSize //一页展示多少条
-
}
-
this.$axios.get('http://localhost:7001/setUserList',{
-
params
-
}).then(res => {
-
this.tableInfo.list = res.data.data.list
-
this.tableInfo.total = res.data.data.total
-
}).catch(error => {
-
this.$message(error.data.message)
-
})
-
},
-
/**
-
* 当前页发生改变
-
* */
-
pageChange(page) {
-
this.tableInfo.page = page
-
this.initPage() //刷新列表数据
-
},
-
/**
-
* 当前第几页发生改变
-
* */
-
pageSizeChange(pageSize) {
-
this.tableInfo.page = 1
-
this.tableInfo.pageSize = pageSize
-
this.initPage() //刷新列表数据
-
},
-
//新增弹框
-
addModal(){
-
this.modalFrom ={
-
name:'',
-
age:'',
-
phone:'',
-
address:'',
-
email:''
-
}
-
this.modal.show = true
-
this.modal.type = 'add'
-
},
-
//修改弹框
-
editFun(row){
-
this.modalFrom = row
-
this.modal.show = true
-
this.modal.type = 'edit'
-
},
-
//删除弹框
-
deleteFun(row){
-
this.modalFrom = row
-
this.modal.show = true
-
this.modal.type = 'delete'
-
},
-
addUserList(){
-
this.$axios({
-
method:'post',
-
url:'http://localhost:7001/setUserList',
-
data:this.modalFrom
-
}).then(res => {
-
this.$message.success('新增成功')
-
this.initPage() //刷新数据
-
this.modal.show = false
-
})
-
},
-
editUserList(){
-
this.$axios({
-
method:'put',
-
url:'http://localhost:7001/setUserList',
-
params:{
-
id:this.modalFrom.id
-
},
-
data:this.modalFrom
-
}).then(res => {
-
this.$message.success('修改成功')
-
this.initPage() //刷新数据
-
this.modal.show = false
-
})
-
},
-
deleteUserList(){
-
this.$axios({
-
method:'delete',
-
url:'http://localhost:7001/setUserList',
-
params:{
-
id:this.modalFrom.id
-
}
-
}).then(res => {
-
this.$message.success('删除成功')
-
this.initPage() //刷新数据
-
this.modal.show = false
-
})
-
}
-
}
-
-
}
-
</script>
-
-
<style lang='scss'>
-
.modalName{
-
h3{
-
padding:10px;
-
}
-
}
-
</style>
分享此文一切功德,皆悉回向给文章原作者及众读者.
转自:csdn
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
在我们了解JSONP 和 CORS 之前我们先明确一下:
实际上,cors和jsonp都是用于解决跨域问题,当两个页面的协议、域名、端口号中有一个不一致时就存在了跨域,一旦出现跨域,浏览器发送跨域请求后,请求回来的数据都会被浏览器所拦截,准备一张图给大家看看:
现下实现跨域数据请求,最主要的两种解决方案分别是 JSONP 和 CORS 。
JSONP(⭐⭐⭐)
JSONP (JSON with Padding) 是 JSON 的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
JSONP的实现原理(⭐⭐⭐)
概念:浏览器端通过 <script> 标签的 src 属性,请求服务器上的数据,同时,服务器返回一个函数的调用。这种请求数据的方式叫做 JSONP
特点:
JSONP 不属于真正的 Ajax 请求,因为它没有使用 XMLHttpRequest 这个对象
JSONP 仅支持 GET 请求,不支持 POST、PUT、DELETE 等请求
CORS (跨域资源共享) 由一系列 HTTP 响应头组成,这些 HTTP 响应头决定浏览器 是否阻止前端 JS 代码跨域获取资源
浏览器的同源安全策略默认会阻止网页“跨域”获取资源。但如果接口服务器配置了 CORS 相关的 HTTP 响应头,就可以解除浏览器端的跨域访问限制
index.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>Document</title>
-
<script src="jquery.min.js"></script>
-
</head>
-
-
<body>
-
<button class="get">get请求</button>
-
<button class="post">post请求</button>
-
<script>
-
$('.get').on('click', function() {
-
$.ajax({
-
method: 'get',
-
url: 'http://127.0.0.1/api/get?name=hua&age=18',
-
success: function(res) {
-
console.log(res);
-
}
-
})
-
})
-
$('.post').on('click', function() {
-
$.ajax({
-
method: 'post',
-
url: 'http://127.0.0.1/api/post',
-
data: {
-
name: 'lajitong',
-
age: '111'
-
},
-
success: function(res) {
-
console.log(res);
-
}
-
})
-
})
-
</script>
-
</body>
-
-
</html>
此时会出现跨域问题,我们需要使用 cors 中间件解决跨域问题
cors 是 Express 的一个第三方中间件。通过安装和配置 cors 中间件,可以很方便地解决跨域问题
使用步骤
安装中间件: npm install cors
导入中间件: const cors = require('cors')
配置中间件: 在路由之前调用app.use(cors())
express接口案例代码
-
// 导入 express 模块
-
const express = require('express')
-
-
// 创建 express 的服务器实例
-
const app = express()
-
-
// 导入中间件
-
const cors = require('cors')
-
// 配置中间件
-
app.use(cors())
-
-
// 配置解析表单数据的中间件
-
app.use(express.urlencoded({ extended: false }))
-
-
// 导入路由模块(被单独分离后导入)
-
const router = require('./apiRouter')
-
// 把路由模块,注册到 app 上
-
app.use('/api', router)
-
-
// 调用 app.listen 方法,指定端口号并启动 web 服务器
-
app.listen(80, () => {
-
console.log('http://127.0.0.1')
-
})
apiRouter路由文件代码:
-
const express = require('express');
-
const router = express.Router();
-
-
router.get('/get', (req, res) => {
-
const query = req.query;
-
res.send({
-
status: 0,
-
msg: 'get请求成功',
-
data: query
-
})
-
})
-
router.post('/post', (req, res) => {
-
// const body = req.body; //获取客户端请求的数据
-
res.send({
-
status: 0,
-
msg: 'post请求成功',
-
data: req.body
-
})
-
})
-
module.exports = router;
在终端中运行express接口代码后打开index.html文件并点击get及post按钮得到请求结果:
创建 JSONP 接口的注意事项
如果项目中已经配置了 CORS 跨域资源共享,为了防止冲突,必须在配置 CORS 中间件之前声明 JSONP 的接口
否则 JSONP 接口会被处理成开启了 CORS 的接口
实现步骤:
(1)获取客户端发送过来的回调函数的名字
(2)得到要通过 JSONP 形式发送给客户端的数据
(3)根据前两步得到的数据,拼接出一个函数调用的字符串
(4)把上一步拼接得到的字符串,响应给客户端的 <script> 标签进行解析执行
案例代码如下:
-
//导入express模块
-
const express = require('express');
-
//创建express服务器实例
-
const app = express();
-
//挂载路由
-
app.get('/jsonp', (req, res) => {
-
// 通过解构req.query客户端通过查询字符串的形式发送到客户端的参数fn
-
const { callback } = req.query
-
//在服务器端定义一个obj对象
-
const obj = {
-
uname: 'zjj',
-
age: '18'
-
}
-
//obj对象转为res.send可处理的字符串形式后从服务器端相应回调函数至客户端
-
res.send(`${callback}(${JSON.stringify(obj)})`)
-
})
-
app.listen(80, () => {
-
console.log('http://127.0.0.1');
-
})
创建jsonp.html客户端来接收服务器端响应过来的回调函数,代码如下:
url中callback=fn为客户端发送请求携带的参数 既服务器端中的req.query.callback
-
<script>
-
function fn(res) {
-
console.log(res);
-
}
-
</script>
-
<script src="http://127.0.0.1/jsonp?callback=fn"></script>
分享此文一切功德,皆悉回向给文章原作者及众读者.
转自:csdn
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
移动端浏览器兼容性较好,不需要考虑以前 JS 的兼容性问题,可以放心的使用原生 JS 书写效果,但是移动端也有自己独特的地方。比如触屏事件 touch(也称触摸事件),Android 和 IOS 都有。
touch 对象代表一个触摸点。触摸点可能是一根手指,也可能是一根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。
常见的触屏事件:

触摸事件对象(TouchEvent)
TouchEvent 是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少,等等
touchstart、touchmove、touchend 三个事件都会各自有事件对象。
触摸事件对象常见对象列表:

因为平时都是给元素注册触摸事件,所以重点记住 targetTocuhes
移动端拖动元素JS代码实现:
// (1) 触摸元素 touchstart: 获取手指初始坐标,同时获得盒子原来的位置 // (2) 移动手指 touchmove: 计算手指的滑动距离,并且移动盒子 // (3) 离开手指 touchend: var div = document.querySelector('div'); var startX = 0; //获取手指初始坐标 var startY = 0; var x = 0; //获得盒子原来的位置 var y = 0; div.addEventListener('touchstart', function(e) { // 获取手指初始坐标 startX = e.targetTouches[0].pageX; startY = e.targetTouches[0].pageY; x = this.offsetLeft; y = this.offsetTop; }); div.addEventListener('touchmove', function(e) { // 计算手指的移动距离: 手指移动之后的坐标减去手指初始的坐标 var moveX = e.targetTouches[0].pageX - startX; var moveY = e.targetTouches[0].pageY - startY; // 移动我们的盒子 盒子原来的位置 + 手指移动的距离 this.style.left = x + moveX + 'px'; this.style.top = y + moveY + 'px'; e.preventDefault(); // 阻止屏幕滚动的默认行为 });
classList属性是HTML5新增的一个属性。返回元素的类名,该属性用在元素中添加、移除及切换CSS类
<style> .bg { background-color: black; } </style> <body> <div class="one two"></div> <button> 开关灯</button> <script> // classList 返回元素的类名 var div = document.querySelector('div'); // console.log(div.classList[1]); // 1. 添加类名 是在后面追加类名不会覆盖以前的类名 注意前面不需要加. div.classList.add('three'); // 2. 删除类名 div.classList.remove('one'); // 3. 切换类 var btn = document.querySelector('button'); btn.addEventListener('click', function() { document.body.classList.toggle('bg'); }) </script> </body>
移动端 要求的是快速开发,所以经常会借助于一些插件来帮完成操作
JS 插件是 js 文件,它遵循一定规范编写,方便程序展示效果,拥有特定功能且方便调用。如轮播图和瀑布流插件
插件的使用:
特点: 它一般是为了解决某个问题而专门存在,其功能单一,并且比较小。比如移动端常见插件:iScroll、Swiper、SuperSlider
offset 翻译过来就是偏移量, 使用 offset 系列相关属性可以 动态 的得到该元素的位置(偏移)、大小等。
常用属性:

图示:
offset与style区别:
| offset | style |
|---|---|
| 可以得到任意样式表中的样式值 | 只能得到行内样式表中的样式值 |
| offset系列获得的数值是没有单位的 | style.width 获得的是带有单位的字符串 |
| offsetWidth 包含padding+border+width | style.width 获得不包含padding和border 的值 |
| offsetWidth 等属性是只读属性,只能获取不能赋值 | style.width 是可读写属性,可以获取也可以赋值 |
| 获取元素大小位置,用offset更合适 | 元素更改值,则需要用style改变 |
案例——获取鼠标在盒子内的坐标:
效果展示:
实现代码(JS):
// 在盒子内点击, 想要得到鼠标距离盒子左右的距离。 // 首先得到鼠标在页面中的坐标( e.pageX, e.pageY) // 其次得到盒子在页面中的距离(box.offsetLeft, box.offsetTop) // 用鼠标距离页面的坐标减去盒子在页面中的距离, 得到 鼠标在盒子内的坐标 var box = document.querySelector('.box'); box.addEventListener('mousemove', function(e) { // console.log(e.pageX); // console.log(e.pageY); // console.log(box.offsetLeft); var x = e.pageX - this.offsetLeft; var y = e.pageY - this.offsetTop; this.innerHTML = 'x坐标是' + x + ' y坐标是' + y; })
案例——模态拖拽框:
效果展示:
实现代码:
<head lang="en"> <meta charset="UTF-8"> <title></title> <style> .login-header { width: 100%; text-align: center; height: 30px; font-size: 24px; line-height: 30px; } ul,li,ol,dl,dt,dd,div,p,span,h1,h2,h3,h4,h5,h6,a { padding: 0px; margin: 0px; } .login { display: none; width: 512px; height: 280px; position: fixed; border: #ebebeb solid 1px; left: 50%; top: 50%; background: #ffffff; box-shadow: 0px 0px 20px #ddd; z-index: 9999; transform: translate(-50%, -50%); } .login-title { width: 100%; margin: 10px 0px 0px 0px; text-align: center; line-height: 40px; height: 40px; font-size: 18px; position: relative; cursor: move; } .login-input-content { margin-top: 20px; } .login-button { width: 50%; margin: 30px auto 0px auto; line-height: 40px; font-size: 14px; border: #ebebeb 1px solid; text-align: center; } .login-bg { display: none; width: 100%; height: 100%; position: fixed; top: 0px; left: 0px; background: rgba(0, 0, 0, .3); } a { text-decoration: none; color: #000000; } .login-button a { display: block; } .login-input input.list-input { float: left; line-height: 35px; height: 35px; width: 350px; border: #ebebeb 1px solid; text-indent: 5px; } .login-input { overflow: hidden; margin: 0px 0px 20px 0px; } .login-input label { float: left; width: 90px; padding-right: 10px; text-align: right; line-height: 35px; height: 35px; font-size: 14px; } .login-title span { position: absolute; font-size: 12px; right: -20px; top: -30px; background: #ffffff; border: #ebebeb solid 1px; width: 40px; height: 40px; border-radius: 20px; } </style> </head> <body> <div class="login-header"><a id="link" href="javascript:;">点击,弹出登录框</a></div> <div id="login" class="login"> <div id="title" class="login-title">登录会员 <span><a id="closeBtn" href="javascript:void(0);" class="close-login">关闭</a></span> </div> <div class="login-input-content"> <div class="login-input"> <label>用户名:</label> <input type="text" placeholder="请输入用户名" name="info[username]" id="username" class="list-input"> </div> <div class="login-input"> <label>登录密码:</label> <input type="password" placeholder="请输入登录密码" name="info[password]" id="password" class="list-input"> </div> </div> <div id="loginBtn" class="login-button"><a href="javascript:void(0);" id="login-button-submit">登录会员</a></div> </div> <!-- 遮盖层 --> <div id="bg" class="login-bg"></div> <script> // 1. 获取元素 var login = document.querySelector('.login'); var mask = document.querySelector('.login-bg'); var link = document.querySelector('#link'); var closeBtn = document.querySelector('#closeBtn'); var title = document.querySelector('#title'); // 2. 点击弹出层这个链接 link 让mask 和login 显示出来 link.addEventListener('click', function() { mask.style.display = 'block'; login.style.display = 'block'; }) // 3. 点击 closeBtn 就隐藏 mask 和 login closeBtn.addEventListener('click', function() { mask.style.display = 'none'; login.style.display = 'none'; }) // 4. 开始拖拽 // (1) 当我们鼠标按下, 就获得鼠标在盒子内的坐标 title.addEventListener('mousedown', function(e) { var x = e.pageX - login.offsetLeft; var y = e.pageY - login.offsetTop; // (2) 鼠标移动的时候,把鼠标在页面中的坐标,减去 鼠标在盒子内的坐标就是模态框的left和top值 document.addEventListener('mousemove', move) function move(e) { login.style.left = e.pageX - x + 'px'; login.style.top = e.pageY - y + 'px'; } // (3) 鼠标弹起,就让鼠标移动事件移除 document.addEventListener('mouseup', function() { document.removeEventListener('mousemove', move); }) }) </script> </body>
client 翻译过来就是客户端,使用 client 系列的相关属性来获取元素可视区的相关信息。通过 client 系列的相关属性可以动态的得到该元素的边框大小、元素大小等。
常用属性:

client和offset最大的区别就是 :不包含边框
图示:
scroll 翻译过来就是滚动的,使用 scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等。
常用属性:

图示:
滚动条:
案例——固定右侧侧边栏:
效果展示:
实现代码:
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> .slider-bar { position: absolute; left: 50%; top: 300px; margin-left: 600px; width: 45px; height: 130px; background-color: pink; } .w { width: 1200px; margin: 10px auto; } .header { height: 150px; background-color: purple; } .banner { height: 250px; background-color: skyblue; } .main { height: 1000px; background-color: yellowgreen; } span { display: none; position: absolute; bottom: 0; } </style> </head> <body> <div class="slider-bar"> <span class="goBack">返回顶部</span> </div> <div class="header w">头部区域</div> <div class="banner w">banner区域</div> <div class="main w">主体部分</div> <script> //1. 获取元素 var sliderbar = document.querySelector('.slider-bar'); var banner = document.querySelector('.banner'); // banner.offestTop 就是被卷去头部的大小 一定要写到滚动的外面 var bannerTop = banner.offsetTop // 当侧边栏固定定位之后应该变化的数值 var sliderbarTop = sliderbar.offsetTop - bannerTop; // 获取main 主体元素 var main = document.querySelector('.main'); var goBack = document.querySelector('.goBack'); var mainTop = main.offsetTop; // 2. 页面滚动事件 scroll document.addEventListener('scroll', function() { // window.pageYOffset 页面被卷去的头部 // console.log(window.pageYOffset); // 3 .当页面被卷去的头部大于等于了 172 此时 侧边栏就要改为固定定位 if (window.pageYOffset >= bannerTop) { sliderbar.style.position = 'fixed'; sliderbar.style.top = sliderbarTop + 'px'; } else { sliderbar.style.position = 'absolute'; sliderbar.style.top = '300px'; } // 4. 当我们页面滚动到main盒子,就显示 goback模块 if (window.pageYOffset >= mainTop) { goBack.style.display = 'block'; } else { goBack.style.display = 'none'; } }) </script> </body>
三大系列作用区别:
它们主要用法:
| 系列 | 作用 | 属性 |
|---|---|---|
| offset | 用于获得元素位置 | offsetLeft offsetTop |
| client | 用于获取元素大小 | clientWidth clientHeight |
| scroll | 用于获取滚动距离 | scrollTop scrollLeft |
注意:页面滚动的距离通过 window.pageXOffset 获得
核心原理:通过定时器 setInterval() 不断移动盒子位置
实现步骤:
简单动画函数封装:
// 简单动画函数封装obj目标对象 target 目标位置 function animate(obj, target) { var timer = setInterval(function() { if (obj.offsetLeft >= target) { // 停止动画 本质是停止定时器 clearInterval(timer); } //每次均匀向右移动1px obj.style.left = obj.offsetLeft + 1 + 'px'; }, 30); }
缓动效果原理:
缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来
// 缓动动画函数封装obj目标对象 target 目标位置 // 思路: // 1. 让盒子每次移动的距离慢慢变小, 速度就会慢慢落下来。 // 2. 核心算法:(目标值 - 现在的位置) / 10 做为每次移动的距离 步长 // 3. 停止的条件是: 让当前盒子位置等于目标位置就停止定时器 function animate(obj, target) { // 先清除以前的定时器,只保留当前的一个定时器执行 clearInterval(obj.timer); obj.timer = setInterval(function() { // 步长值写到定时器的里面 var step = (target - obj.offsetLeft) / 10; if (obj.offsetLeft == target) { // 停止动画 本质是停止定时器 clearInterval(obj.timer); } // 把每次加1 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置) / 10 obj.style.left = obj.offsetLeft + step + 'px'; }, 15); }
多个目标值之间移动:
当开始移动时候,判断步长是正值还是负值
动画函数封装到单独JS文件: animate.js
function animate(obj, target, callback) { // 先清除以前的定时器,只保留当前的一个定时器执行 clearInterval(obj.timer); obj.timer = setInterval(function() { // 步长值写到定时器的里面 // 把步长值改为整数 不要出现小数的问题 // var step = Math.ceil((target - obj.offsetLeft) / 10); var step = (target - obj.offsetLeft) / 10; step = step > 0 ? Math.ceil(step) : Math.floor(step); if (obj.offsetLeft == target) { // 停止动画 本质是停止定时器 clearInterval(obj.timer); // 回调函数写到定时器结束里面 // if (callback) { // // 调用函数 // callback(); // } callback && callback(); } // 把每次加1 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置) / 10 obj.style.left = obj.offsetLeft + step + 'px'; }, 15); }
分享此文一切功德,皆悉回向给文章原作者及众读者.
转自:csdn
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
首先我们为每个vue属性用Object.defineProperty()实现数据劫持,为每个属性分配一个订阅者集合的管理数组dep;然后在编译的时候在该属性的数组dep中添加订阅者,v-model会添加一个订阅者,{{}}也会,v-bind也会,只要用到该属性的指令理论上都会,接着为input会添加监听事件,修改值就会为该属性赋值,触发该属性的set方法,在set方法内通知订阅者数组dep,订阅者数组循环调用各订阅者的update方法更新视图。
一小段手写实现代码
仅供参考
1
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
Nodemailer 是一个基于Node的邮件服务模块。
使用 Nodemailer 完成一个发邮件功能非常简单,只需3步:
首先安装 Nodemailer
npm install nodemailer
引入
var mailer = require('nodemailer');
创建 transport 使用 Nodemailer 的createTransport方法,需要配置一下邮件服务。
首先,要在邮箱设置里开启 SMTP 服务。
然后,设置一个客户端授权密码。
最后,写一个配置文件(conf/mail.js)。以126邮箱为例:
module.exports = { host: 'smtp.126.com', auth: { user: 'user@126.com', pass: '******' } }
host字段配置刚才开启的服务地址。
auth里的user字段配置邮箱账号,pass字段配置刚才设置的授权密码。
这样创建一个 transport:
var mailConf = require('conf/mail'); var transport = mailer.createTransport(mailConf);
发邮件使用 Nodemailer 的sendMail方法,需要配置一下邮件内容。
这里配置了发件人,收件人,标题和正文:
var mailOptions = { from: mailConf.auth.user, to: 'receiver@xxx.com', subject: 'Hi, there', text: 'Mail from Node!' }
可以像官方文档一样定义一个回调函数:
function mailCallback(error, info){ if(error){ return console.log(error); } console.log('Message sent: ' + info.response); }
然后,就可以发送邮件了:
transport.sendMail(mailOptions, mailCallback);
这样就完成了最基本的发邮件功能。
Nodemailer 文档非常详细,可以探索实现更多功能。
分享此文一切功德,皆悉回向给文章原作者及众读者.
转自:简书
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
蓝蓝设计的小编 http://www.lanlanwork.com