node.js+captchapng+jsonwebtoken实现登录验证示例


Posted in Javascript onAugust 17, 2017

提到登录验证,大家肯定能想到的就是12306的验证码了吧。12306为了防止刷票,可以说是煞费苦心,验证码也越来越难识别,最终即使是人也可能识别不了了。

今天,小编就给大家说一下node如何实现图片验证码,以及使用token验证登录。学习本文你将学到:

1.使用captchapng生成图片验证码

2.使用jsonwebtoken实现登录验证

一、图片验证码生成(最后有全部代码)

首先,我们理一下流程,第一步服务器要随机生成一组四位数。

第二步,将这四位数用canvas绘图生成图片。

第三步,我们要将这四位数存起来,以便用户返回数据时做对比。

那么存到哪里呢?很明显为了区分用户,存到session最为稳妥。

第一步,先要有一个登录页面。在这里我们依然用react,

login.tsx

import * as React from 'react'
import * as ReactDom from 'react-dom'
import {Link, browserHistory} from 'react-router';
import * as axios from 'axios';
export default class Login extends React.Component<any,any>{
  constructor(props){
    super(props)
    this.state = {
      userName : '',
      password : '',
      yzNoId  : '',
      hash : Math.random()
    }
  }
  handleUserName(e) : any {
    this.setState({
      userName : e.target.value
    })
  }
  handlePassword(e) : any {
    this.setState({
      password : e.target.value
    })
  }
  handleYzId(e) : any {
    this.setState({
      yzNoId : e.target.value
    })
  }
  render(){
    const { userName, password, yzNoId } = this.state;
    return(
      <div>
        <div className="nav-wrap">
          <ul className="nav">
            <li><Link to="/home">首页</Link></li>
            <li><Link to="/imgLoad">上传</Link></li>
            <li><Link to="/login">登陆</Link></li>
          </ul>
        </div>
        <div className="content">
          <div className="login-warp">
            <p>
              <input type="text" className="username" value={userName} onChange={this.handleUserName.bind(this)} placeholder="用户名"/>
            </p>
            <p>
              <input type="text" className="password" value={password} onChange={this.handlePassword.bind(this)} placeholder="密码"/>  
            </p>  
            <p>
              <input type="text" className="yz" value={yzNoId} onChange={this.handleYzId.bind(this)} placeholder="验证码"/>
              <img src={"http://localhost:3000/captcha"} className="yz-img" />  
            </p>
            <p>
              <input type="button" className="submit" value="登陆" onClick={this.sbumit.bind(this,{userName:userName,password:password,captcha:yzNoId})} /> 
            </p>  
          </div>
        </div>
      </div>
    )
  }
}

页面是这样的

node.js+captchapng+jsonwebtoken实现登录验证示例

我们需要通过服务器给一张验证图片。

router/index.js 添加如下代码

var Login = require('./controller/login');
var login = new Login;
router.get('/captcha', login.captcha);
router.post('/login',login.loginer);
login是定义在控制器的一个类的实例,captcha,loginer是它的方法。分别是返回验证图片、登录验证。
controller/login.js

var rf = require('fs');
var captchapng = require('captchapng');
class Login {
  constructor(){}
  captcha(req, res, next) {
    var str = parseInt(Math.random()*9000+1000);  //随机生成数字
    req.session.captcha = str;  // 存入session
    var p = new captchapng(80, 30, str); //生成图片
    p.color(0, 0, 0, 0);
    p.color(80, 80, 80, 255);
    var img = p.getBase64();
    var imgbase64 = new Buffer(img, 'base64');
    res.writeHead(200, {
      'Content-Type': 'image/png'
    });
    res.end(imgbase64);
  }
  loginer(req, res, next) {
    let captcha = req.body.captcha;
    let userName = req.body.userName;
    let password = req.body.password;
    if (captcha != req.session.captcha) {
      res.status(400).send({
        message: '验证码错误'
      });
    }else if(userName == "chenxuehui" && password == "123321"){
      res.json({"code":100,"verson":true,"msg":"登陆成功","token":token});
    }else{
      res.json({"code":0,"verson":false,"msg":"密码错误"});
    }
  }
}
module.exports = Login

captcha方法是生成一张含四位数字的图片,然后将图片保存到session中。

将此方法在 router/index.js 中引用

router.get('/captcha', login.captcha);

也就是说我们访问localhost:3000/captcha就会返回一张图片。

node.js+captchapng+jsonwebtoken实现登录验证示例

有了这个连接后我们就可以通过图片的src属性拿到该图片,但是当点击图片的时候要重新刷新,所以我们需要给图片添加一个点击刷新事件。将下面代码插入到login.tsx中

setHash() {
    this.setState({
      hash : Math.random()
    })
}

img标签也变成了

<img src={"http://localhost:3000/captcha?aaa="+this.state.hash}  className="yz-img" onClick={this.setHash.bind(this)} />  
 

此时login.tsx全部代码:

import * as React from 'react'
import * as ReactDom from 'react-dom'
import {Link, browserHistory} from 'react-router';
import * as axios from 'axios';
export default class Login extends React.Component<any,any>{
  constructor(props){
    super(props)
    this.state = {
      userName : '',
      password : '',
      yzNoId  : '',
      hash : Math.random()
    }
  }
  public async sbumit(params : any) : Promise<any>{
    let res = await axios.post('http://localhost:3000/login',params);
  }
  handleUserName(e) : any {
    this.setState({
      userName : e.target.value
    })
  }
  handlePassword(e) : any {
    this.setState({
      password : e.target.value
    })
  }
  handleYzId(e) : any {
    this.setState({
      yzNoId : e.target.value
    })
  }
  setHash() {
    this.setState({
      hash : Math.random()
    })
  }
  render(){
    const { userName, password, yzNoId } = this.state;
    return(
      <div>
        <div className="nav-wrap">
          <ul className="nav">
            <li><Link to="/home">首页</Link></li>
            <li><Link to="/imgLoad">上传</Link></li>
            <li><Link to="/login">登陆</Link></li>
          </ul>
        </div>
        <div className="content">
          <div className="login-warp">
            <p>
              <input type="text" className="username" value={userName} onChange={this.handleUserName.bind(this)} placeholder="用户名"/>
            </p>
            <p>
              <input type="text" className="password" value={password} onChange={this.handlePassword.bind(this)} placeholder="密码"/>  
            </p>  
            <p>
              <input type="text" className="yz" value={yzNoId} onChange={this.handleYzId.bind(this)} placeholder="验证码"/>
              <img src={"http://localhost:3000/captcha?aaa="+this.state.hash} className="yz-img" onClick={this.setHash.bind(this)} />  
            </p>
            <p>
              <input type="button" className="submit" value="登陆" onClick={this.sbumit.bind(this,{userName:userName,password:password,captcha:yzNoId})} /> 
            </p>  
          </div>
        </div>
      </div>
    )
  }
}

这样只要点击img,就会随机生成一个hash,然后就会调用新的图片出来。

接着我们进行登录验证。

loginer方法就是进行登录验证的。

拿到用户的用户名信息,密码以及验证码一次对比,最后返回登录是否成功数据。

当用户登陆成功以后,下次登录就不需要再次登录了,以往的方法可以选则session或者cookie的方式,在这里我们使用token。因为现在已经实现了前后端分离开发,我们更倾向于构建单页面配合ajax构建应用。而token最适合这种开发模式不过了。

token登录验证

token是一串经过加密的字符串,登录成功以后返回给用户保存,然后用户在请求接口时,都带这个token。所以我们需要对token进行加密。

Json Web Token就是专门解决这个问题的,原理就不做详解了,其实就是按照一定的方式得到一个字符串,然后在通过某种方式解开。

我们要做的第一步就是

当用户登录成功后,创建一个token返回给用户。

第二步:用户拿到token后应该把token存到本地。

第三步:需要写一个中间层,每次用户请求时我们验证用户携带的token是否正确。正确返回数据,不正确返回警告。
用户每次请求数据的时候要在header里把token带上。

第一步:还是controller/login.js

var rf = require('fs');
var jwt = require('jsonwebtoken');
var captchapng = require('captchapng');
var Tokens = require('../middleware/token')
var t = new Tokens;
class Login {
  constructor(){}
  captcha(req, res, next) {
    var str = parseInt(Math.random()*9000+1000);  //随机生成数字
    req.session.captcha = str;  // 存入session
    var p = new captchapng(80, 30, str); //生成图片
    p.color(0, 0, 0, 0);
    p.color(80, 80, 80, 255);
    var img = p.getBase64();
    var imgbase64 = new Buffer(img, 'base64');
    res.writeHead(200, {
      'Content-Type': 'image/png'
    });
    res.end(imgbase64);
  }
  loginer(req, res, next) {
    let captcha = req.body.captcha;
    let userName = req.body.userName;
    let password = req.body.password;
    if (captcha != req.session.captcha) {
      res.status(400).send({
        message: '验证码错误'
      });
    }else if(userName == "chenxuehui" && password == "123321"){
      // 设置token
      var datas = {userName:"chenxuehui"}
        //调用../middleware/token 下方法设置
      var token = t.setToken('cxh',300,datas)
      res.json({"code":100,"verson":true,"msg":"登陆成功","token":token});
    }else{
      res.json({"code":0,"verson":false,"msg":"密码错误"});
    }
  }
}

module.exports = Login

这次在loginer方法里面我们加入设置token,并返回给用户。setToken方法是设置token的方法。

第二步:用户拿到后保存。

在login.tsx就变成如下

import * as React from 'react'
import * as ReactDom from 'react-dom'
import {Link, browserHistory} from 'react-router';
import * as axios from 'axios';
export default class Login extends React.Component<any,any>{
  constructor(props){
    super(props)
    this.state = {
      userName : '',
      password : '',
      yzNoId  : '',
      hash : Math.random()
    }
  }
  public async sbumit(params : any) : Promise<any>{
    let res = await axios.post('http://localhost:3000/login',params);
    if(res.data.verson){
      sessionStorage.setItem('token',res.data.token);
      browserHistory.push("/home")
    }
  }
  handleUserName(e) : any {
    this.setState({
      userName : e.target.value
    })
  }
  handlePassword(e) : any {
    this.setState({
      password : e.target.value
    })
  }
  handleYzId(e) : any {
    this.setState({
      yzNoId : e.target.value
    })
  }
  setHash() {
    this.setState({
      hash : Math.random()
    })
  }
  render(){
    const { userName, password, yzNoId } = this.state;
    return(
      <div>
        <div className="nav-wrap">
          <ul className="nav">
            <li><Link to="/home">首页</Link></li>
            <li><Link to="/imgLoad">上传</Link></li>
            <li><Link to="/login">登陆</Link></li>
          </ul>
        </div>
        <div className="content">
          <div className="login-warp">
            <p>
              <input type="text" className="username" value={userName} onChange={this.handleUserName.bind(this)} placeholder="用户名"/>
            </p>
            <p>
              <input type="text" className="password" value={password} onChange={this.handlePassword.bind(this)} placeholder="密码"/>  
            </p>  
            <p>
              <input type="text" className="yz" value={yzNoId} onChange={this.handleYzId.bind(this)} placeholder="验证码"/>
              <img src={"http://localhost:3000/captcha?aaa="+this.state.hash} className="yz-img" onClick={this.setHash.bind(this)} />  
            </p>
            <p>
              <input type="button" className="submit" value="登陆" onClick={this.sbumit.bind(this,{userName:userName,password:password,captcha:yzNoId})} /> 
            </p>  
          </div>
        </div>
      </div>
    )
  }
}

在sbumit方法里我们将token放到了sessonstorage里面。

第三步:设置中间件每次请求接口时,验证token,如果解析成功加入到请求头里面。

./middleware/token.js

var jwt = require('jsonwebtoken');
class Tokens {
  constructor(){}
  testToken(req,res,next) {
    var token = req.body.token || req.query.token || req.headers['x-access-token'];
       
    if(token) {
            //存在token,解析token
      jwt.verify(token, 'cxh' , function(err,decoded) {
        if(err) {
                   // 解析失败直接返回失败警告
          return res.json({success:false,msg:'token错误'})
        }else {
                   //解析成功加入请求信息,继续调用后面方法
          req.userInfo = decoded;
          next()
        }
      })
    }else {
      return res.status(403).send({success:false,msg:"没有token"})
    }
  }
  setToken(name,time,data) {
    var jwtSecret = name;
    var token = jwt.sign(data, jwtSecret, {
      expiresIn: time
    })
    return token;
  }
}
module.exports = Tokens

testToken方法是验证token,setToken是设置token方法

假如没有登录请求是这样的

node.js+captchapng+jsonwebtoken实现登录验证示例

在 router/index.js

var express = require('express');
var router = express.Router();
var rf = require('fs');
var Login = require('./controller/login');
var Tokens = require('./middleware/token')
var t = new Tokens;
var login = new Login;
//主页
router.get('/', function(req, res, next) {
  res.render("wap/index")
});
//获取图片验证码
router.get('/captcha', login.captcha);
//登录验证
router.post('/login',login.loginer);
//请求数据时 t.testToken 验证token
router.post('/list',t.testToken,function(req, res, next){
  res.json({
    //在请求信息里面拿到数据
    username : req.userInfo.userName,
    success : true,
    result : [
      {
        name:'1111111'
      },
      {
        name :'22222'
      }
    ]
  })
})
module.exports = router;

我们在另一个页面调用list接口试一下

import * as axios from 'axios';
import { transToken } from '../decorator/index'


class Home extends React.Component<any,any>{
  constructor(props){
    super(props)
    this.state = {
      data : ''
    }
  }
  async getList(): Promise<any>{
    let token = sessionStorage.getItem('token');
    const config = {
     // 请求头信息
     headers: {'x-access-token': token}      
    }
    let res = await axios.post('http://localhost:3000/list',{},config);
    if(!res.data.success){
      browserHistory.push('/login');
      return;
    }
    this.setState({
      data : res.data
    })
  }
  render(){
    const { data } = this.state;
    return(
      <div>
        <div className="nav-wrap">
          <ul className="nav">
            <li><Link to="/home">首页</Link></li>
            <li><Link to="/imgLoad">上传</Link></li>
            <li><Link to="/login">登陆</Link></li>
          </ul>
        </div>
        <div className="content">
          Home
          <span onClick={this.getList.bind(this)}>获取数据</span>
          <div>{
            data ? data.result.map( (val,k) => {
              return <li key = {k}>{val.name}</li>
            }) : null
          }</div>
        </div>
      </div>
      
    )
  }
}
export default Home

当调用getList时,如果此时没有登录res.data.success就会为false,则跳到登录页。

全部代码

node.js

app.js

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require("express-session");
var ejs = require('ejs');

var index = require('./routes/index');


var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
// app.set('view engine', 'jade');
app.engine('html', ejs.__express);
app.set('view engine', 'html');
app.use(session({
  secret:"dabao",
  resave:false,
  saveUninitialized:true,
  cookie:{}
}));
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser({limit: 5000000}));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, '')));

app.use('/', index);


// catch 404 and forward to error handler
app.use(function(req, res, next) {
 var err = new Error('Not Found');
 err.status = 404;
 next(err);
});

// error handler
app.use(function(err, req, res, next) {
 // set locals, only providing error in development
 res.locals.message = err.message;
 res.locals.error = req.app.get('env') === 'development' ? err : {};

 // render the error page
 res.status(err.status || 500);
 res.render('error');
});

module.exports = app;

index.js

var express = require('express');
var router = express.Router();
var rf = require('fs');
var Login = require('./controller/login');
var Tokens = require('./middleware/token')
var t = new Tokens;
var login = new Login;
/* GET home page. */
router.get('/', function(req, res, next) {
  res.render("wap/index")
});
router.post('/upLoadImg',function(req,res,next){
  let imgData = req.body.imgData;
  console.log(imgData)
  let base64Data = imgData.replace(/^data:image\/\w+;base64,/, "");
  let dataBuffer = new Buffer(base64Data, 'base64');
  let timer = Number( new Date() );
  console.log(timer)
  rf.writeFile("views/images/artCover"+timer+".png",dataBuffer, function(err) {
    if(err) {
     res.json({"code":400,"verson":false,"msg":err});
    }else {
     res.json({"code":100,"verson":true,"url":"views/src/common/images/artCover/"+timer+".png"});
    }
  });
})
router.get('/captcha', login.captcha);
router.post('/login',login.loginer);
router.post('/list',t.testToken,function(req, res, next){
  // 先解析token 
  console.log(req.userInfo)
  res.json({
    username : req.userInfo.userName,
    success : true,
    result : [
      {
        name:'1111111'
      },
      {
        name :'22222'
      }
    ]
  })
})
module.exports = router;

controller/login.js

var rf = require('fs');
var jwt = require('jsonwebtoken');
var captchapng = require('captchapng');
var Tokens = require('../middleware/token')
var t = new Tokens;
class Login {
  constructor(){}
  captcha(req, res, next) {
    var str = parseInt(Math.random()*9000+1000);  //随机生成数字
    req.session.captcha = str;  // 存入session
    var p = new captchapng(80, 30, str); //生成图片
    p.color(0, 0, 0, 0);
    p.color(80, 80, 80, 255);
    var img = p.getBase64();
    var imgbase64 = new Buffer(img, 'base64');
    res.writeHead(200, {
      'Content-Type': 'image/png'
    });
    res.end(imgbase64);
  }
  loginer(req, res, next) {
    let captcha = req.body.captcha;
    let userName = req.body.userName;
    let password = req.body.password;
    if (captcha != req.session.captcha) {
      res.status(400).send({
        message: '验证码错误'
      });
    }else if(userName == "chenxuehui" && password == "123321"){
      // 设置token
      var datas = {userName:"chenxuehui"}
      var token = t.setToken('cxh',300,datas)
      res.json({"code":100,"verson":true,"msg":"登陆成功","token":token});
    }else{
      res.json({"code":0,"verson":false,"msg":"密码错误"});
    }
  }
}
module.exports = Login

middleware/token.js

var jwt = require('jsonwebtoken');

class Tokens {
  constructor(){}
  testToken(req,res,next) {
    var token = req.body.token || req.query.token || req.headers['x-access-token'];

    if(token) {
      jwt.verify(token, 'cxh' , function(err,decoded) {
        if(err) {
          return res.json({success:false,msg:'token错误'})
        }else {
          req.userInfo = decoded;
          next()
        }
      })
    }else {
      return res.status(403).send({success:false,msg:"没有token"})
    }
  }
  setToken(name,time,data) {
    var jwtSecret = name;
    var token = jwt.sign(data, jwtSecret, {
      expiresIn: time
    })
    return token;
  }
}
module.exports = Tokens

react部分

login.tsx

import * as React from 'react'
import * as ReactDom from 'react-dom'
import {Link, browserHistory} from 'react-router';
import * as axios from 'axios';
export default class Login extends React.Component<any,any>{
  constructor(props){
    super(props)
    this.state = {
      userName : '',
      password : '',
      yzNoId  : '',
      hash : Math.random()
    }
  }
  public async sbumit(params : any) : Promise<any>{
    let res = await axios.post('http://localhost:3000/login',params);
    if(res.data.verson){
      sessionStorage.setItem('token',res.data.token);
      browserHistory.push("/home")
    }
  }
  handleUserName(e) : any {
    this.setState({
      userName : e.target.value
    })
  }
  handlePassword(e) : any {
    this.setState({
      password : e.target.value
    })
  }
  handleYzId(e) : any {
    this.setState({
      yzNoId : e.target.value
    })
  }
  setHash() {
    this.setState({
      hash : Math.random()
    })
  }
  render(){
    const { userName, password, yzNoId } = this.state;
    return(
      <div>
        <div className="nav-wrap">
          <ul className="nav">
            <li><Link to="/home">首页</Link></li>
            <li><Link to="/imgLoad">上传</Link></li>
            <li><Link to="/login">登陆</Link></li>
          </ul>
        </div>
        <div className="content">
          <div className="login-warp">
            <p>
              <input type="text" className="username" value={userName} onChange={this.handleUserName.bind(this)} placeholder="用户名"/>
            </p>
            <p>
              <input type="text" className="password" value={password} onChange={this.handlePassword.bind(this)} placeholder="密码"/>  
            </p>  
            <p>
              <input type="text" className="yz" value={yzNoId} onChange={this.handleYzId.bind(this)} placeholder="验证码"/>
              <img src={"http://localhost:3000/captcha?aaa="+this.state.hash} className="yz-img" onClick={this.setHash.bind(this)} />  
            </p>
            <p>
              <input type="button" className="submit" value="登陆" onClick={this.sbumit.bind(this,{userName:userName,password:password,captcha:yzNoId})} /> 
            </p>  
          </div>
        </div>
      </div>
    )
  }
}

home.js 获取列表信息

import * as React from 'react'
import * as ReactDom from 'react-dom'
import {Link, browserHistory} from 'react-router';
import * as axios from 'axios';
class Home extends React.Component<any,any>{
  constructor(props){
    super(props)
    this.state = {
      data : ''
    }
  }
  async getList(): Promise<any>{
    let token = sessionStorage.getItem('token');
    const config = {
     // 请求头信息
     headers: {'x-access-token': token}      
    }
    let res = await axios.post('http://localhost:3000/list',{},config);
    if(!res.data.success){
      browserHistory.push('/login');
      return;
    }
    this.setState({
      data : res.data
    })
  }
  render(){
    const { data } = this.state;
    return(
      <div>
        <div className="nav-wrap">
          <ul className="nav">
            <li><Link to="/home">首页</Link></li>
            <li><Link to="/imgLoad">上传</Link></li>
            <li><Link to="/login">登陆</Link></li>
          </ul>
        </div>
        <div className="content">
          Home
          <span onClick={this.getList.bind(this)}>获取数据</span>
          <div>{
            data ? data.result.map( (val,k) => {
              return <li key = {k}>{val.name}</li>
            }) : null
          }</div>
        </div>
      </div>
      
    )
  }
}
export default Home

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JS 类型转换常见方法小结
May 31 Javascript
将HTML格式的String转化为HTMLElement的实现方法
Aug 07 Javascript
2014 HTML5/CSS3热门动画特效TOP10
Dec 07 Javascript
mvvm双向绑定机制的原理和实现代码(推荐)
Jun 07 Javascript
Node.js的文件权限及读写flag详解
Oct 11 Javascript
bootstrap时间插件daterangepicker使用详解
Oct 19 Javascript
Node.js net模块功能及事件监听用法分析
Jan 05 Javascript
JS精确判断数据类型代码实例
Dec 18 Javascript
原生javascript如何实现共享onload事件
Jul 03 Javascript
原生js canvas实现鼠标跟随效果
Aug 02 Javascript
基于Vue全局组件与局部组件的区别说明
Aug 11 Javascript
vue-cli 关闭热更新操作
Sep 18 Javascript
javascript 日期相减-在线教程(附代码)
Aug 17 #Javascript
微信小程序引用公共js里的方法的实例详解
Aug 17 #Javascript
微信小程序中使用Promise进行异步流程处理的实例详解
Aug 17 #Javascript
史上最全JavaScript数组去重的十种方法(推荐)
Aug 17 #Javascript
AngularJS通过ng-Img-Crop实现头像截取的示例
Aug 17 #Javascript
Javascript中的getter和setter初识
Aug 17 #Javascript
简单实现js进度条加载效果
Mar 25 #Javascript
You might like
php网站来路获取代码(针对搜索引擎)
2010/06/08 PHP
PHP XML error parsing SOAP payload on line 1
2010/06/17 PHP
PHP之COOKIE支持详解
2010/09/20 PHP
php通过COM类调用组件的实现代码
2012/01/11 PHP
PHP 自动加载的简单实现(推荐)
2016/08/12 PHP
PHP实现使用DOM将XML数据存入数组的方法示例
2017/09/27 PHP
php实现微信支付之企业付款
2018/05/30 PHP
javascript中巧用“闭包”实现程序的暂停执行功能
2007/04/04 Javascript
防止网站内容被拷贝的一些方法与优缺点好处与坏处分析
2007/11/30 Javascript
JS判断对象是否存在的10种方法总结
2013/12/23 Javascript
javascript实现随时变化着的背景颜色
2015/04/02 Javascript
Vue.js实现拖放效果的实例
2016/09/30 Javascript
深入理解JavaScript创建对象的多种方式以及优缺点
2017/06/01 Javascript
JS中this的指向以及call、apply的作用
2018/05/06 Javascript
微信小程序关键字变色实现代码实例
2019/12/13 Javascript
详解vue父子组件状态同步的最佳方式
2020/09/10 Javascript
vant-ui组件调用Dialog弹窗异步关闭操作
2020/11/04 Javascript
[26:21]浴火之凤-TI4世界冠军Newbee战队纪录片
2014/08/07 DOTA
python获得图片base64编码示例
2014/01/16 Python
Python判断中文字符串是否相等的实例
2018/07/06 Python
numpy 计算两个数组重复程度的方法
2018/11/07 Python
如何基于python操作excel并获取内容
2019/12/24 Python
Python猜数字算法题详解
2020/03/01 Python
Python中qutip用法示例详解
2020/10/02 Python
CSS3动画animation实现云彩向左滚动
2014/05/09 HTML / CSS
不可轻视HTML5!App三年内将被html5顶替彻底消失
2015/11/18 HTML / CSS
EMPHASIS艾斐诗官网:周生生旗下原创精品珠宝品牌
2020/12/17 全球购物
西安启天科技有限公司网络工程师面试题笔试题
2016/06/12 面试题
捐款倡议书范文
2014/02/02 职场文书
成绩报告单家长评语
2014/12/30 职场文书
师范生小学见习总结
2015/06/23 职场文书
公司员工管理制度
2015/08/04 职场文书
《生物入侵者》教学反思
2016/02/16 职场文书
导游词之云南丽江-泸沽湖
2019/09/26 职场文书
python ansible自动化运维工具执行流程
2021/06/24 Python
微信小程序中使用vant框架的具体步骤
2022/02/18 Javascript