现有的Vue项目改造成Nuxt项目并做SEO优化

现有的Vue项目改造成Nuxt
摘要:每天进步一点点... 之前的Vue项目使用过prerender-spa-plugin&vue-meta-info做seo,但对于博客类、新闻类的网站,详情页还是无法被爬取到,只能做固定的页面,今天把自己的Vue项目改造成Nuxt项目。 一、前期准备 1、首先使用以下命令安装nuxt项目 ```js npx create-nuxt-app demo ``` 安装时会出现类似Vue安装时出现的选项,自己根据需求选择就好 ![1.png](https://api.lpya.cn/HtBlog/public/upload/article/20210320/ca866ee930632caf11d082c3d90a7d98.png) 2、使用下面命令安装依赖并启动项目,出现下面的就说明nuxt安装成功 ```js cnpm install npm run dev ``` ![2.png](https://api.lpya.cn/HtBlog/public/upload/article/20210320/ae0781ed8866f2d1d05bb1479edfb5a4.png) 3、项目结构对比Vue项目,两个目录结构很相似,无非Vue是在src目录下,Nuxt是在根目录 ![3.png](https://api.lpya.cn/HtBlog/public/upload/article/20210320/e6a882785d63f103ab075defdaae6291.png) 二、如何修改 **1、main.js** 把Vue里面的main.js文件放到Nuxt根目录下,去掉new Vue({}),并在nuxt.config.js文件引入,其它不变 ```js //main.js文件 import Vue from 'vue' import bus from "@/assets/bus/bus" Vue.prototype.$bus = bus; import VueCookies from 'vue-cookies' Vue.use(VueCookies) import mavonEditor from "mavon-editor"; Vue.use(mavonEditor) Vue.prototype.$host = "https://www.lpya.cn" // new Vue({ // render: h => h(App), // }).$mount('app') //nuxt.config.js export default { ... plugins: [ "@/main",//引入main.js,其它js文件基本上是在这里引入 ], } ``` **2、store.js文件** 在store目录下创建index.js文件,引入后在页面直接this.$store调用 ```js //index.js export const state = () => ({ isHeader: false, }) const mutations = { isHeader(state, val) { state.isHeader = val; }, } export default { state, mutations } //nuxt.config.js export default { ... plugins: [ "@/store/index",//引入store ], } ``` **3、组件** 1)把Vue components目录下的全部放到Nuxt components目录下就行,Nuxt的组件会自动注册,可以不使用import注册直接使用 2)把组件复制过来后,可能会出现scss的错误(我Vue项目使用的是SCSS),使用下面命令安装 ```js npm install --save-dev node-sass sass-loader @nuxtjs/style-resources ``` 配置SCSS的全局变量 ```js //nuxt.config.js export default { ... css: [ '@/assets/scss/common.scss',//一般的可以放到这里 ], styleResources: { scss: '~assets/scss/app.scss', }, modules: [ '@nuxtjs/style-resources', ], } ``` **4、路由** 把Vue views目录下的全部放到Nuxt pages目录下,文件名就是路由,值得注意的是,传参的时候和Vue有一些不同。比如我博客网站文章详情的路由是/detail,要传一个id过去,跳转链接可以写成下面这样 ```js <nuxt-link :to="{ name: 'detail-id', params: { id: articleId } }"> 现有的Vue项目改造成Nuxt项目并做SEO优化 </nuxt-link> ``` ![5.png](https://api.lpya.cn/HtBlog/public/upload/article/20210320/23a12ead7758755ef535ad141c1d9ecb.png) **5、布局** Nuxt可以自定义多个布局,比如说一个布局需要Header和Footer,一个布局不需要,另一个需要空白,我们可以在layouts定义布局,并在pages页面下调用即可 ```js export default { layout: function () { //index1是在layouts目录下创建的文件名,记得加上<Nuxt /> return "index1"; }, } ``` **6、设置网站title** 使用head()给每个页面设置title和description ```js export default { head() { return { title: "网页设计,模板分享,源码下载 - 糊涂博客", meta: [ { hid: "keywords", name: "keywords", content: "个人博客,网页设计,微信小程序,seo优化,源码下载,模板分享", }, { hid: "description", name: "description", content: "糊涂个人博客,一位编程爱好者的成长地。专注于前后端的学习,不定期更新分享踩坑过程,学习记录、网页模板、demo源码等,也希望借此能够认识更多的朋友。", }, ], }; }, } ``` 三、SEO优化 配置到这里,Vue项目基本上已经改造成Nuxt项目,但是和Vue的区别不大,详情页面还是无法被爬取到,这里我们需要使用asyncData,它可以在初始化页面前先得到数据,然后设置title。其它作用可以自己查。。。 ```js import { articleDetail } from "@/assets/api/api"; export default { asyncData({ app, params }, callback) { return articleDetail(params.id).then((res) => { const { code, data } = res; if (code === 1) { app.head.title =data.title; const meta = [ { charset: "utf-8", }, { name: "viewport", content: "width=device-width, initial-scale=1", }, { hid: "keywords", name: "keywords", content:data.keywords, }, { hid: "description", name: "description", content: data.content, }, ]; app.head.meta = meta; } callback(null, {}); }); }, } ``` 四、项目部署 1、使用下面命令打包项目,会创建.nuxt目录,把下面文件放到服务器上 ```js npm run build ``` ![7.png](https://api.lpya.cn/HtBlog/public/upload/article/20210320/6da298467c86ae78f3664365c4d6a1c0.png) 2、使用下面命令安装依赖并运行 ```js cnpm install npm run start ``` ![8.png](https://api.lpya.cn/HtBlog/public/upload/article/20210320/f0a1c16d7609d7de9e023b4ab14bd418.png) 3、配置域名访问,我使用的是bt面板,找到配置文件在里面添加下面内容,使用域名就可以访问刚部署的内容了 ```js upstream lpya { server 0.0.0.0:7000; } server { location / { proxy_pass http://lpya; proxy_set_header Origin ''; index index.html index.htm; } } ``` 4、如果我们关闭终端,服务就关闭了,现在需要开户守护进程,让它在后台一直运行,创建pm2.config.json文件 ```js { "apps": { "name": "sever", "script": "npm run start", "ignore_watch": [ "node_modules" ] } } //然后运行 //pm2 start pm2.config.json启动 //pm2 stop pm2.config.json关闭 ``` 五、爬取效果 使用bd站长工具的抓取诊断,可以查看做之前和之后的一个对比。下图可以明显看出,没改之前只能抓取到固定的信息,改之后可以动态抓取到内容(重点是使用了asyncData) ![9.png](https://api.lpya.cn/HtBlog/public/upload/article/20210320/eae73ea8392d71ad71755b9feef97ee1.png) 六、sitemap.xml网站地图 Sitemap可以方便我们告诉搜索引擎网站上有哪些可供抓取的网页。 1、使用命令安装@nuxtjs/sitemap ```js npm install @nuxtjs/sitemap ``` 2、创建config目录,并在里面添加sitemap.js文件 ```js //sitemap.js import axios from 'axios' const sitemap = { path: '/sitemap.xml', hostname: 'https://www.lpya.cn/', cacheTime: 1000 * 60 * 60 * 24, gzip: true, generate: false, exclude: [ '/404' ], defaults: { changefreq: 'daily', lastmod: new Date() }, routes: async () => { const getUrl = `https://www.lpya.cn/article/get` let res = await axios.post(getUrl) let { data } = res.data let routes = [{ url: "/", changefreq: "daily", lastmod: new Date() }] if (data.length !== 0) { //拼接 let arr = data.map(item => ({ url: "/detail/" + item.id, lastmod: item.created_at, changefreq: "daily" })) routes.push(...arr) } return routes } } export default sitemap ``` 3、在nuxt.config.js引入,部署时记得把config目录也上传服务器 ```js //nuxt.config.js import sitemap from "./config/sitemap" export default { ... modules: [ '@nuxtjs/style-resources', ], sitemap: sitemap,//调用 } ``` 4、部署后访问https://.../sitemap.xml链接就可以查看效果 ![10.png](https://api.lpya.cn/HtBlog/public/upload/article/20210320/a5ccd87bf58708d32b1a7ee45e69d496.png) 七、总结 虽然看起来多,实际也就几步操作,我也是第一次接触nuxt,有些地方可能弄不好或许也有更好的方法,有大神知道的可以吱一声。。。有需要的可以自己慢慢尝试,bug总会被解决的(或许)。

基于Vue实现变色方块(点灯、黑白迭代)休闲小游戏

基于Vue实现变色方块(点灯、
摘要:每天进步一点点... 之前看到一个小游戏,玩起来挺有意思的,就自己重新做了一个,实现起来挺容易,就是玩起来前面还好,后面的有点费脑... [github下载地址](https://github.com/lpyhutu/vue-discoloration) HTML ```js <template> <div class="discoloration"> <div class="header" :style="{ width: `${contentWidth}px` }"> <div class="item">关卡:{{ level }}</div> <div class="item">当前步数:{{ currentSteps }}</div> <div class="item">总步数:{{ steps }}</div> </div> <div class="content"> <div class="item item-color1" :class="item == 0 ? 'item-color1' : 'item-color2'" :style="{ height: `${(contentWidth - level) / level}px`, width: `${(contentWidth - level) / level}px`, }" v-for="(item, key) in list" :key="key" @click="handleCurrentIndex(key)" ></div> </div> </div> </template> ``` JavaScript ```js <script> export default { name: "Discoloration", data() { return { contentWidth: 500, list: [], successList: [], level: 1, steps: 0, currentSteps: 0, isLoading: true, }; }, methods: { /** * 赋值 */ handleList() { this.list = []; for (let i = 0; i < this.level * this.level; i++) { this.list.push(0); } this.isFlag = true; }, handleCurrentIndex(currentIndex) { this.steps++; //当前 this.setList(currentIndex); // 上一级 if (currentIndex > this.level - 1) { const upLevelIndex = currentIndex - this.level; this.setList(upLevelIndex); } // 下一级 if (currentIndex < (this.level - 1) * this.level) { const downLevelIndex = currentIndex + this.level; this.setList(downLevelIndex); } //左 if (currentIndex % this.level !== 0) { const leftLevelIndex = currentIndex - 1; this.setList(leftLevelIndex); } //右 if ((currentIndex + 1) % this.level !== 0) { const rightLevelIndex = currentIndex + 1; this.setList(rightLevelIndex); } this.checkList(); }, checkList() { let isSuccess = true; for (let i = 0; i < this.list.length; i++) { if (this.list[i] === 0) { isSuccess = false; } } if (isSuccess) { this.level++; this.handleList(); } }, /** * 修改 */ setList(index) { this.$set(this.list, index, this.handleVal(this.list[index])); }, /** * 判断1还是0 */ handleVal(value) { let val = value === 1 ? 0 : 1; return val; }, }, created() { this.handleList(); }, }; </script> ``` CSS ```css <style lang="scss"> .discoloration { position: fixed; height: 100%; width: 100%; display: flex; justify-content: center; flex-flow: column; align-items: center; background: edf2f6; .header { display: flex; justify-content: space-between; padding-bottom: 10px; } .content { height: 500px; width: 500px; display: flex; flex-wrap: wrap; .item { border-right: 1px solid eee; border-bottom: 1px solid eee; cursor: pointer; } .item-color1 { background: 07be91; } .item-color2 { background: e0c007; } } .footer { display: flex; justify-content: space-between; padding-top: 10px; button { margin-bottom: 7px; } } } </style> ``` 实现效果 自己可以根据实际需要再添加其它难度或玩法 ![GIF.gif](https://api.lpya.cn/HtBlog/public/upload/article/20210310/e3ebce47bddeb1f3855eb092c143593d.gif)

Vue使用prerender-spa-plugin做简单的SEO优化

Vue使用prerender-
摘要:每天进步一点点... 自己弄了个博客网站,运行了很久,一直没被收录,才发现SEO对单页面不太友好。上网学习并实践了一下,我们可以使用prerender-spa-plugin+vue-meta-info做一个预渲染。 一、安装插件 使用下面命令安装对应插件 ```js npm install prerender-spa-plugin --save npm install vue-meta-info --save ``` 二、修改配置 1、创建vue.config.js文件,并添加下面内容 ```js const PrerenderSPAPlugin = require('prerender-spa-plugin') const Renderer = PrerenderSPAPlugin.PuppeteerRenderer const path = require('path') module.exports = { configureWebpack: () => { if (process.env.NODE_ENV !== 'production') return return { plugins: [ new PrerenderSPAPlugin({ staticDir: path.join(__dirname, 'dist'), routes: ['/', '/message', '/link', '/about', '/detail/id', "/search"], renderer: new Renderer({ inject: { foo: 'bar' }, headless: false, renderAfterDocumentEvent: 'render-event' }) }) ] } }, } ``` 2、修改main.js里面的内容 ```js //引入metainfo import MetaInfo from 'vue-meta-info' Vue.use(MetaInfo) new Vue({ router, store, render: h => h(App), //添加下面这行 mounted() { document.dispatchEvent(new Event('render-event')) } }).$mount('app') ``` 3、在对应页面添加下面内容 ```js export default { name: "About", metaInfo: { title: "网页设计,模板分享,源码下载 - 糊涂博客", meta: [ { name: "keyWords", content: "网页,简介,个人,小程序,博客", }, { name: "description", content: "糊涂个人博客,一位编程爱好者的成长地。专注于前后端的学习,不定期更新分享踩坑过程,学习记录、网页模板、demo源码等,也希望借此能够认识更多的朋友。", }, ], }, } ``` 4、使用npm run build命令打包后,会生成对应的目录 ![1.png](https://api.lpya.cn/HtBlog/public/upload/article/20210310/dbd67f766b6342d1f0fea6627a07ec4a.png) 三、收录效果 ![2.png](https://api.lpya.cn/HtBlog/public/upload/article/20210310/14919b6d79c4f74f12029130119923ac.png)

微信小程序web-view内嵌H5使用PHP完成支付功能

微信小程序web-view内嵌
摘要:每天进步一点点... 在开发过程中,遇到使用微信小程序web-view内嵌H5完成支付的业务,我们该怎么去实现? 一、设计思路 1、使用PHP请求接口获取wx_data_package支付包数据 2、使用jweixin.js把wx_data_package数据传到小程序 3、小程序获取数据,调起支付界面完成支付,并返回支付是否成功 4、后台根据返回的数据判断支付是否成功,然后进行相关业务处理 二、前期准备 1、创建PHP文件,index.php主界面,success.php支付成功,error.php支付失败 ![1.png](https://api.lpya.cn/HtBlog/public/upload/article/20210217/c6dc3ee0bf5467b66a6bbef57aeb4960.png) 2、新建WeChat项目,并创建下面目录 ![2.png](https://api.lpya.cn/HtBlog/public/upload/article/20210217/4d6f0598d3db430791def8e6b264fe15.png) 三、实现代码 1、index.php ```php <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>支付测试</title> <script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script> <style> .btn { padding: 10px 20px; text-align: center; background: 647357; color: fff; } </style> </head> <body> <?php // 假设wx_data_package是请求接口返回的支付数据包 $wx_data_package = '{"package":"prepay_id=wx164122404495953ff5f88672224070000","appid":"wxa5dasdfasdf2231","sign":"m8i6LKiu+adJJouVx6XasdaEOr+K5FIuV/luTEgrqE+APuO/oEVK6Fadfn4NiJMqGasdfhlgdj2FDasdf9oHSAnmHaPKiZgK37oCVe+3HyGGirDWZLOSdpI4hVvasdLpPBbmbvM6Xvg2TLwyO41Fg3fjIEasdrhkxCJss2B5uXa3BrRyL3LEC01CbJfC05uDwRRC+a8erHzRjoscB+Kjepnhcfp/PVO9v+N6b5oKmTUCdhaF85n3U2PSFS7yzp6473oEAPi8jNiwM/dtZZvo+y7z4urWoebeFfO/5l9Bx2EWaRstQqEXgh+lLsVxNiccHal3nfDcqNkw==","signType":"RSA","noncestr":"45bd4991f99048badew9528dqfca9b7","timestamp":"1613488960"}'; ?> <div class="btn" onclick="pay()">支付</div> <script> // 把wx_data_package传到小程序 function pay() { const url = `../wePay/wePay?wxPageData=<?php echo urlencode($wx_data_package); ?>` wx.miniProgram.navigateTo({ url: url }) } </script> </body> </html> ``` 2、success.php和error.php两文件随便添加点东西进去做一个区别就可以了 3、webview.wxml ```html // pages/webview/webview.wxml <web-view src="{{src}}"></web-view> ``` 4、webview.js ```js // pages/webview/webview.js Page({ /** * 页面的初始数据 */ data: { src: "http://php.com/" }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { if (options.payFlag !== undefined) if (options.payFlag == "true") { this.setData({ src: "http://php.com/success.php" }) } else { this.setData({ src: "http://php.com/error.php" }) } }, }) ``` 5、wePay.js ```js // pages/wePay/wePay.js Page({ data: { }, payment(data) { wx.requestPayment({ 'timeStamp': data.timestamp, 'nonceStr': data.noncestr, 'package': data.package, 'signType': data.signType, 'paySign': data.sign, 'success': function (res) { wx.navigateTo({ url: '../webview/webview?payFlag=true', }) }, 'fail': function (res) { wx.navigateTo({ url: '../webview/webview?payFlag=false', }) } }) }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { let wxPageData = JSON.parse(decodeURIComponent(options.wxPageData)) console.log(wxPageData) this.payment(wxPageData) }, }) ``` 四、实现效果 1、主界面 ![3.png](https://api.lpya.cn/HtBlog/public/upload/article/20210217/36450da155863f8daeaf67c907cd801c.png) 2、调起支付时 ![4.png](https://api.lpya.cn/HtBlog/public/upload/article/20210217/2995a94f7ef594b6217b63cac3d4db57.png) 3、支付成功或失败后 ![5.png](https://api.lpya.cn/HtBlog/public/upload/article/20210217/8893a829c6e264be797b2d3a4759f922.png)

如何使用一个域名配置多个Laravel项目?

如何使用一个域名配置多个Lar
摘要:每天进步一点点... 这是自己在学习过程中遇到的一个问题。申请了一个二级域名(api.demo.com),想实现(api.demo.com/blog)就是我博客项目,(api.demo.com/test)就是我测试项目,奈何技术有限,又是首次接触nginx,捣鼓了很久才弄好。 我用的是宝塔面板、php7.3、nginx1.17和要部署laravel项目,首先先添加站点,然后在/www/wwwroot/api.demo.com目录下存放blog和test项目。最后在网站配置伪静态中添加以下代码就可以实现: ```nginx location ^~ /blog { rewrite /blog/(.*)$ /blog/index.php?=$1 last; break; location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:/tmp/php-cgi-73.sock; include fastcgi_params; fastcgi_param SCRIPT_FILENAME /www/wwwroot/api.demo.com/blog/public/index.php; } } location ^~ /test { rewrite /blog/(.*)$ /blog/index.php?=$1 last; break; location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:/tmp/php-cgi-73.sock; include fastcgi_params; fastcgi_param SCRIPT_FILENAME /www/wwwroot/api.demo.com/test/public/index.php; } } ```

JavaScript实现轮播图效果

JavaScript实现轮播图
摘要:每天进步一点点... 使用原生JS实现基本的轮播图效果,自己可以根据需求再对应修改。 HTML代码 ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>轮播图</title> <link rel="stylesheet" href="./css/index.css"> <script src="./js/index.js"></script> </head> <body> <div id="app"> <!-- 轮播效果一 --> <div class="title">轮播图</div> <div class="swiper_1" id="swiper_1"> <!-- 图片 --> <ul class="pic_1"> <li><img src="img/1.jpg" alt=""></li> <li><img src="img/2.jpg" alt=""></li> <li><img src="img/3.jpg" alt=""></li> </ul> <!-- 导航 --> <ol class="list_1"> <li class="current_1"></li> <li></li> <li></li> </ol> </div> </div> </body> </html> ``` CSS样式 ```css * { margin: 0px; padding: 0px; list-style: none; } app { display: flex; flex-flow: column; align-items: center; } .title { padding: 20px 0; font-size: 20px; } /* 轮播效果一 */ .swiper_1 { position: relative; height: 300px; width: 500px; } /* 图片 */ .swiper_1 .pic_1 { display: flex; overflow: hidden; } .swiper_1 .pic_1 li { transition: all 0.5s; } .swiper_1 .pic_1 li img { height: 300px; width: 500px; } /* 导航 */ .swiper_1 .list_1 { position: absolute; bottom: 0px; right: 0px; width: 100%; display: flex; justify-content: center; } .swiper_1 .list_1 { display: flex; padding: 10px 0; } .swiper_1 .list_1 li { transition: all 0.3s; height: 7px; width: 7px; background: rgba(255, 255, 255, 0.7); margin-right: 5px; border-radius: 2px; cursor: pointer; } .swiper_1 .list_1 .current_1 { width: 13px; background: rgba(255, 255, 255, 1); } ``` JS代码 ```js window.onload = function () { let index = 0, timer = null, swiper_1 = document.getElementById("swiper_1"), pic_1 = document.getElementsByClassName("pic_1")[0].children, list_1 = document.getElementsByClassName("list_1")[0].children; autoPlay(); timer = setInterval(autoPlay, 2000); // 鼠标划过停止播放 swiper_1.onmouseover = function () { clearInterval(timer); } // 鼠标离开继续播放 swiper_1.onmouseout = function () { timer = setInterval(autoPlay, 2000); } //自动播放 function autoPlay() { handleCurrentList() handleCurrentPic() } //当前图片 function handleCurrentPic() { pic_1[0].style.marginLeft = `-${ index*100}%` if (index >= pic_1.length - 1) { index = 0; } else { index++ } } //当前导航样式 function handleCurrentList() { for (let i = 0; i < list_1.length; i++) { //点击导航切换图片 list_1[i].onclick = function () { index = i; handleCurrentList() handleCurrentPic() } if (i === index) { list_1[i].classList.add("current_1") } else { list_1[i].classList.remove("current_1") } } } } ``` 轮播效果 ![GIF.gif](https://api.lpya.cn/HtBlog/public/upload/article/20210204/38f68883986c3455e7614e738be17db2.gif)

Vue封装组件实现验证码功能

Vue封装组件实现验证码功能
摘要:每天进步一点点... 对于些限制比较低、要求不高的情况下,可以在前端使用验证码做一个简单验证 ```html <template> <div class="code"> <canvas @click="changeCode" id="canvas"></canvas> </div> </template> <script> export default { name: "VerificationCode", props: { //宽 width: { type: Number, default: 100, }, //高 height: { type: Number, default: 35, }, //验证码长度 codeLength: { type: Number, default: 4, }, //字体大小px codeFontSize: { type: Number, default: 25, }, //字体 family: { type: String, default: "微软雅黑", }, //干扰点数 point: { type: Number, default: 10, }, //干扰线数 line: { type: Number, default: 3, }, }, methods: { changeCode() { this.draw(); }, draw() { let code = "", canvas = document.getElementById("canvas"), context = canvas.getContext("2d"), text = "qwertyuipadfghjkxvbnmQWERTYUPASDFGHJKZXVBNM123456789"; canvas.width = this.width; canvas.height = this.height; for (let i = 0; i < this.codeLength; i++) { let j = Math.floor(Math.random() * text.length); let deg = Math.random() - 0.5; let txt = text[j]; code = code + txt.toLowerCase(); let x = 10 + i * 20; let y = 20 + Math.random() * (this.height / 2 - 10); context.font = `${this.codeFontSize}px ${this.family}`; context.translate(x, y); context.rotate(deg); context.fillStyle = this.randomColor(); context.fillText(txt, 0, 0); context.rotate(-deg); context.translate(-x, -y); } console.log(code); //把该值返回调用的页面进行判断 for (let i = 0; i <= this.line; i++) { context.strokeStyle = this.randomColor(); context.beginPath(); context.moveTo( Math.random() * this.width, Math.random() * this.height ); context.lineTo( Math.random() * this.width, Math.random() * this.height ); context.stroke(); } for (let i = 0; i <= this.point; i++) { context.strokeStyle = this.randomColor(); context.beginPath(); let x = Math.random() * this.width; let y = Math.random() * this.height; context.moveTo(x, y); context.lineTo(x + 1, y + 1); context.stroke(); } }, randomColor() { let r = this.randomNum(170), g = this.randomNum(170), b = this.randomNum(170); return "rgb(" + r + "," + g + "," + b + ")"; }, randomNum(maxNum) { return Math.floor(Math.random() * maxNum); }, }, mounted() { this.draw(); }, }; </script> <style> canvas { cursor: pointer; border: 1px solid rgb(155, 155, 155); background-color: rgba(182, 182, 182, 0.26); } </style> ``` 调用组件,把code值传过来做个匹配就可以了 ![QQ截图20210129211620.jpg](https://api.lpya.cn/HtBlog/public/upload/article/20210129/abe08e32391e23847a7188fcbf792651.jpg)

Laravel结合Redis发送邮箱验证码

Laravel结合Redis发
摘要:每天进步一点点... 我们在某些业务上,需要注册邮箱,为避免邮箱注册混乱、乱用他人邮箱,这就需要对邮箱进行验证。 一、设计思路 1、用户输入邮箱,点击验证 2、后端接收到邮箱后,随机生成一个code,用邮箱作为key,code为值保存到redis中,并使用laravel封装的Mail类发送给用户 3、用户收到邮件后,获取验证码并输入,后端把email、code和redis保存的值做一个匹对 二、前期准备 1、修改.env配置文件里面email部分的内容 MAIL_MAILER=smtp MAIL_HOST=smtp.qq.com MAIL_PORT=465 MAIL_USERNAME=xxxx@qq.com MAIL_PASSWORD=xxxxxxxxxxxxxxxxx MAIL_ENCRYPTION=ssl MAIL_FROM_ADDRESS=xxxx@qq.com MAIL_FROM_NAME=糊涂 2、使用命令创建`EmailVerifyController.php`控制器 ``` php artisan make:controller Index/EmailVerifyController ``` 3、在routes/web.php中注册相关路由 ```php <?php use Illuminate\Support\Facades\Route; Route::get("sendEmailCode", "Index\EmailVerifyController@sendEmailCode"); Route::get("verify", "Index\EmailVerifyController@verify"); ``` 4、在resoures/views/mail创建`SendEmailCode.blade.php`模块文件 三、实现代码 1、EmailVerifyController.php控制器 ```php <?php namespace App\Http\Controllers\Index; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use Illuminate\Mail\Message; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Redis; class EmailVerifyController extends Controller { /** * 发送邮箱验证码 * @param Request $request * @return array */ public function sendEmailCode(Request $request) { $email = $request->input("email"); $code = rand(100000, 999999);//验证码 $expireTime = 60 * 3;//过期时间 $EMAIL_VERIFY_CODE = "email:verify:code:" . $email;//redis key Redis::setex($EMAIL_VERIFY_CODE, $expireTime, $code); Mail::send("mail.SendEmailCode", ["code" => $code, "email" => $email], function (Message $message) use ($email) { $message->to($email); $message->subject("糊涂个人博客——邮箱验证"); }); if (Mail::failures()) { return ["code" => 0, "msg" => "warning"]; } return ["code" => 1, "msg" => "success"]; } /** * 验证 * @param Request $request * @return array */ public function verify(Request $request) { $email = $request->input("email"); $code = $request->input("code"); $EMAIL_VERIFY_CODE = "email:verify:code:" . $email;//redis key $isCode = Redis::get($EMAIL_VERIFY_CODE); if ($isCode && $isCode === $code) { return ["code" => 1, "msg" => "success"]; } return ["code" => 0, "msg" => "warning"]; } } ``` 2、SendEmailCode.blade.php模块 ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> app { line-height: 150%; } </style> </head> <body> <div id="app"> <div>亲爱的用户:</div> <br/> <div>您好!</div> <br/> <div>感谢您使用糊涂个人博客开发平台。您正在进行邮箱验证,请在验证码输入框中输入此次验证码<span style="color: red">{{$code}}</span>(3分钟内有效)以完成验证。</div> <div>如非本人操作,请忽略此邮件,由此给您带来的不便请谅解!</div> <br/> <div>糊涂个人博客开发平台</div> </div> </body> </html> ``` 四、实现效果 自己可以根据相关需要修改样式代码 ![QQ截图20210129223551.jpg](https://api.lpya.cn/HtBlog/public/upload/article/20210129/07a8c8e23f2bfc8f6ddeb27608ae831e.jpg)

Linux系统部署reids集群

Linux系统部署reids集
摘要:每天进步一点点... 1、首先安装好redis 2、进行redis安装目录下,执行==mkdir cluster==创建存放集群配置文件的目录 3、进入cluster目录执行==mkdir redis6380==创建redis6380-6388目录 ![83c401dc746e8fd61aab1010fac2b23b.png](https://api.lpya.cn/HtBlog/public/upload/article/20210108/3c984458bfccbba18443f65783d5f1fd.png) 4、复制配置文件 ```nginx cp redis.conf ./cluster/redis6380/redis.conf ``` 5、进入redis6380目录修改redis.conf配置文件,有注释就去掉 ```nginx port 6380 pidfile "/www/server/redis/cluster/redis6380/redis.pid" dir "/www/server/redis/cluster/redis6380" cluster-enabled yes cluster-config-file "nodes-6380.conf" cluster-node-timeout 15000 cluster-replica-validity-factor 10 cluster-migration-barrier 1 cluster-require-full-coverage yes cluster-replica-no-failover no ``` 6、在cluster目录下执行命令==touch setport.sh==创建批量修改脚本,添加下面内容 ```nginx sed "s/6380/6381/g" redis6380/redis.conf > ./redis6381/redis.conf sed "s/6380/6382/g" redis6380/redis.conf > ./redis6382/redis.conf sed "s/6380/6383/g" redis6380/redis.conf > ./redis6383/redis.conf sed "s/6380/6384/g" redis6380/redis.conf > ./redis6384/redis.conf sed "s/6380/6385/g" redis6380/redis.conf > ./redis6385/redis.conf sed "s/6380/6386/g" redis6380/redis.conf > ./redis6386/redis.conf sed "s/6380/6387/g" redis6380/redis.conf > ./redis6387/redis.conf sed "s/6380/6388/g" redis6380/redis.conf > ./redis6388/redis.conf ``` 在该目录下先执行==chmod +x setport.sh==,然后执行./setport.sh,进入redis6381-redis6388目录里面,查看redis.conf是否添加并修改成功 7、找到==redis-server==启动命令的目录(一般在src或bin),在该目录下执行命令==touch start.sh==创建批量启动脚本,添加下面内容 ```nginx ./redis-server /www/server/redis/cluster/redis6380/redis.conf ./redis-server /www/server/redis/cluster/redis6381/redis.conf ./redis-server /www/server/redis/cluster/redis6382/redis.conf ./redis-server /www/server/redis/cluster/redis6383/redis.conf ./redis-server /www/server/redis/cluster/redis6384/redis.conf ./redis-server /www/server/redis/cluster/redis6385/redis.conf ./redis-server /www/server/redis/cluster/redis6386/redis.conf ./redis-server /www/server/redis/cluster/redis6387/redis.conf ./redis-server /www/server/redis/cluster/redis6388/redis.conf ``` 在该目录下执行==chmod +x start.sh==命令,然后执行./start.sh命令,使用==ps -ef |grep redis==查看是否启动成功 ![2.png](https://api.lpya.cn/HtBlog/public/upload/article/20210108/c20d55331f5127c6954a2f37f06f2687.png) 8、分配槽位 ```nginx ./redis-cli --cluster create 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6385 127.0.0.1:6386 127.0.0.1:6387 127.0.0.1:6388 --cluster-replicas 2 ``` ![3.png](https://api.lpya.cn/HtBlog/public/upload/article/20210108/95d5893fff7a2f440464d10935712734.png) 9、使用客户端启动验证 ```nginx ./redis-cli -h 127.0.0.1 -p 6380 -c ``` 先执行==cluster nodes==命令,看到下面内容 ![4.png](https://api.lpya.cn/HtBlog/public/upload/article/20210108/38cc284c7ea2fa43331a12464cf24e22.png) 然后添加key,获取Key,可以看到从6380端口变成6381,就说明已经部署成功 ![6.png](https://api.lpya.cn/HtBlog/public/upload/article/20210108/074e89e650f799db64fa3e0e381466e8.png) 10、批量关闭脚本,借鉴==6、7==创建shutdown.sh文件,添加下面内容 ```nginx ./redis-cli -h 127.0.0.1 -p 6380 shutdown ./redis-cli -h 127.0.0.1 -p 6381 shutdown ./redis-cli -h 127.0.0.1 -p 6382 shutdown ./redis-cli -h 127.0.0.1 -p 6383 shutdown ./redis-cli -h 127.0.0.1 -p 6384 shutdown ./redis-cli -h 127.0.0.1 -p 6385 shutdown ./redis-cli -h 127.0.0.1 -p 6386 shutdown ./redis-cli -h 127.0.0.1 -p 6387 shutdown ./redis-cli -h 127.0.0.1 -p 6388 shutdown ```

Laravel结合Redis实现黑名单、倒计时、防刷功能

Laravel结合Redis实
摘要:每天进步一点点... 新建的博客,我们如何限制别人恶意攻击、频繁请求接口,导致数据库崩溃?我们可以使用Redis对请求的IP做一个简单的限制。 一、设计思路 1、Redis中使用有序set表存放黑名单列表、频繁请求列表。 2、用户访问,设置一个锁,数值为1,过期时间10秒。 3、用户每次请求接口1次,锁的数值加1。在10秒内接口访问次数超过20次,则把该用户IP或uid添加到频繁请求列表中,score的值为当前时间,数据库表频繁请求次加1。 4、若频繁请求次数超过设定次数,则添加到redis黑名单列表中。 二、前期准备 1、创建RedisKey.php ```php <?php namespace App\Http\Common; class RedisKey { public static $USER_BLACK_LIST = "user:black:list";//黑名单列表 public static $USER_FREQUENT_REQUEST_LIST = "user:frequent:request:list";//频繁请求列表 public static $USER_FREQUENT_REQUEST_LOCK = "user:frequent:request:lock";//锁 } ``` 2、创建数据库表 ![微信截图_20210107224919.png](https://api.lpya.cn/HtBlog/public/upload/article/20210107/f8470485b3609d9b4c65840131a9295b.png) 3、使用命令创建CheckRequest.php路由中间件 ``` php artisan make:middleware CheckRequest ``` 三、实现代码 ```php <?php namespace App\Http\Middleware; use App\Http\Common\Code; use App\Http\Common\RedisKey; use App\Models\FrontUser; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Redis; class CheckRequest { /** * Handle an incoming request. * * @param Request $request * @param Closure $next * @return mixed */ public function handle($request, Closure $next) { $ip = request()->ip(); //查看redis黑名单中是否存在该IP $isBlack = Redis::zscore(RedisKey::$USER_BLACK_LIST, $ip); if ($isBlack) { return ["code" => Code::$USER_BLACK, "msg" => "由于您近期异常请求过于频繁,已限制访问。如需取消限制,请联系管理员:邮箱1048672466@qq.com!"]; } $user = new FrontUser(); $isUser = $user->where(["ip" => $ip])->first(); //数据库中已设置为黑名单或频繁请求数大于等于10 if ($isUser) { //查看数据库表中频繁请求次数是否超过10次,是就把该用户列入黑名单并修改相关字段 if ($isUser->black_list === 1 || $isUser->frequent_num >= 10) { $isUser->black_list = 1; $isUser->save(); Redis::zadd(RedisKey::$USER_BLACK_LIST, 1, $isUser->ip); return ["code" => Code::$USER_BLACK, "msg" => "由于您近期异常请求过于频繁,已限制访问。如需取消限制,请联系管理员:邮箱1048672466@qq.com!"]; } } $time = 5;//锁过期时间 $limit = 20;//5秒内请求次数,超过就触发防刷机制 $lock_time = 60;//每次防刷的锁60秒 //redis频繁请求列表 $start_time = Redis::zscore(RedisKey::$USER_FREQUENT_REQUEST_LIST, $ip); //如果redis频繁请求列表中存在该用户IP,且间隔当前时间少于60秒 if (time() - $start_time < $lock_time) { //返回剩余时间 return response(["code" => Code::$USER_FREQUENT, "msg" => "频繁请求!", "data" => $lock_time - (time() - $start_time)]); } $frequentLock = RedisKey::$USER_FREQUENT_REQUEST_LOCK . ":" . $ip; $isFrequent = Redis::get($frequentLock); if (!$isFrequent) { Redis::setex($frequentLock, $time, 1); //设置锁 return $next($request); } Redis::incr($frequentLock);//锁过期时间类数值自增 //设定时间内请求次数大于设定次数,触发防刷机制 if ($isFrequent > $limit) { Redis::zadd(RedisKey::$USER_FREQUENT_REQUEST_LIST, time(), $ip); Redis::expire(RedisKey::$USER_FREQUENT_REQUEST_LIST, 60 * 5); $isUser->increment("frequent_num"); $isUser->save(); } return $next($request); } } ```
网站信息
博主: 糊涂
Github: Github
联系邮箱:1048672466@qq.com
在线人数: 0
文章数量: 10
访问总量: 3362