回顾
我的博客有好几个月没更新了,最近想要把自己之前记在笔记里面的知识点,整理起来,然后发到博客,也要为春招作准备了,所以开始复习以前学过的知识点,还好有做笔记,很多东西回顾起来,还比较容易。所以,我把自己之前做的个人博客也进行了维护,说起来也惭愧。虽然,我有2个博客平台,一个是我自己做的博客平台,一个我用hexo 在 github 搭建的博客平台,但到现在为止,我也没正经的写过几篇博客。主要原因,还是因为我学习比较随意,大部分的笔记都记在了有道云笔记本上了,所以博客也就没怎么写了。我用hexo 在 github 上搭建博客的原因,也是因为 个人博客的搭建后留下的bug 有点多,要真正使用起来,还是不行,所以才在github上搭建了个博客,不过后来一想,把自己做的这个博客项目写进简历里面,也挺不错的,毕竟这个是一个宝贵的项目经验(可能我的项目比较少…),于是我就开始把老博客拿来踩坑了.
开始行动
使用图床
我的博客优化,最先遇到的第一个问题就是图片的加载问题,因为我在我的网站上写博客的时候,我是把博客从markdown 格式转为 html 格式 ,然后把 markdown 格式和 html 格式都上传到后台,然后存放到mysql 中,然后博客查看详细的文章的时候,就把html 文本返回回来,虽然这样做也没什么问题,但我一开始为了图方便,写博客的时候,博客的图片是没有使用外链的,而是直接放到文章里,也就是图片最终以base64的格式存在markdown 转换后html中,这样问题就大了,怎样直接就导致了文章的字符串太大(因为base64是存放在html字符串里面的),本身我的服务器带宽就只有1M,我打开个文章要等待很长时间,才能在前端的页面上显示出来,所以为了解决这个问题,我开始使用图床。
刚开始是使用github当图床,原因也很简单,我有github账号,配置的话也比较简单,在github上新建个仓库,然后本地客户端去连接github,我用的是PicGo,经过一番配置后就能把图片上传到github了,但也有问题,问题就是图片放在github的仓库,然后使用http 去打开图片的时候,在电脑上会出现图片打不开的情况,原因是DNS污染,得修改本地机器上的hosts文件 才能正常访问图片.这就很烦了,如果别人的电脑上没有修改hosts文件,那么访问我的博客时,博客的图片不是打不开了吗,于是只能令找出路.
接下来,我又看到了个新的平台,就是七牛云了,不过七牛云的配置就繁琐很多了,又要注册账号,又要实名认证,最终还要绑定域名,因为我之前已经给我的服务器配置了域名,所以我就开了个二级域名,用来解析七牛云的chame,具体原理,就是链接会先使用我的域名解析到七牛云的域名,再由chame 解析到七牛云的服务器上,详细的配置过程就不赘述了,做好了这些,我就可以先在本地,把图片用PicGo上传到七牛云,然后再使用外链的方式把图片放到博客里了.
页面刷新处理
我的博客的前端页面是使用vue开发的单页面,所以当我刷新页面的时候,就会出现问题了,首先说明下vue的路由模式有2种hash和history,我之前使用的是history,history需要后端nginx的支持,hash则对后端完全没有影响。遇到的问题就是如果我从文章的列表页进入文章的详细页,然后网页刷新,这个时候网页数据无法重新刷新出来,我这里使用的vue 路由传参是使用params,而不是query,为了让刷新后的路由参数不丢失(参数就是文章的id,然后vue组件在created 阶段通过params获取参数,然后去后端请求数据),我在router.js中对路由配置进行了处理。
{// 这是router.js 中对article这个组件的路由配置 path:'/article/:id', name:'article', component:resolve => require(['./pages/article.vue'],resolve)}
created() { let id = this.$route.params.id // 获取参数id this.getData(id) // 根据id,去后台获取数据 }
这样就解决了页面刷新,路由参数丢失的问题。不过这样问题,还是没解决,因为我使用了keep-alive ,所以实际上article组件的created 的生命周期只会在第一次创建(即第一次打开文章)的时候被执行,所以,必须要在article组件中进行路由的变化检测,这样当检测到路由发生变化的时候就获取路由参数的传过来的文章id,然后去后端获取数据
watch:{// 观察路由发生变化的时候,id是否存在,存在的话就去后端获取数据 $route(to,from){ let id = this.$route.params.id if(id){ this.getData(id) } }}
由刷新引起的错误还有导航栏的定位问题,我导航栏用的是个header组件,然后把header组件放到App组件中,页面一刷新,导航栏的定位就回到默认的首页,就算当前刷新的路由不是在首页,依旧会定位到首页。为了解决这个问题,我一样在header组件中加入了路由变化监听,刷新页面的时候,会触发该监听函数,然后根据监听到的path,进行定位当前的导航。还有如果是刷新首页的话,那么路由变化的监听函数,就不会触发,于是mounted的生命周期里面,把路由定位定位到首页,刷新页面的时候,会先执行mounted生命周期,再执行路由的监听。这样问题就解决了
watch:{ // 进行路由变化的监听 $route(to,from){ let path = ["/home","/archive","/about","/login"] for(let i of path){ if(i === to.path){ this.activeIndex = to.path; } }}},// 解决首页刷新,定位丢失的问题mounted(){this.activeIndex = "/home";},
文章的异常显示处理
我的博客文章是使用v-html 指令渲染上去的,但是博客的图片会有显示溢出的问题,而且代码块的显示也会有溢出的问题,经过多种尝试后,最终发现使用>>> 操作符调整css ,可以使得博客中的img 和 code 正常显示
// show是文章的父标签的id#show{ width: 100%;}#show >>> img{ max-width: 100%; max-height:100%;} #show >>> code{ max-width: 100%; font-size: 16px;}
同时,为了使得文章能够高亮显示,我又引入了highlight.js 这个插件,并使用vue的自定义指令进行注册
import hljs from 'highlight.js' //导入代码高亮文件import 'highlight.js/styles/monokai-sublime.css' //导入代码高亮样式Vue.directive('highlight',function (el) { let highlight = el.querySelectorAll('pre code'); highlight.forEach((block)=>{ hljs.highlightBlock(block) }) })
<div v-html="data.html" v-highlight id="show"></div>
同时,为了适配移动端,我还使用elment-ui 调整了布局,这样下来,文章终于能正常展示了.
日期存储的bug
我在我的博客底部弄了个本站已经稳定运行的时间,具体逻辑是服务端在启动的时候就保存了日期,然后浏览器访问网页的时候,把这个日期从服务端获取了,然后计算出时间差,并使用定时器在这个时间差的基础上进行运算,但问题就是我那时候为了图方便,就直接把日期保存在node的全局对象global上,但是因为node服务有挂了的风险(服务端使用了pm2,node服务挂了,就把服务重启),所以时间并不准确,而且之前node连接mysql偶尔还会报连接错误: Error: read ECONNRESET(后来使用了mysql2这个模块才解决了这个问题).
所以把日期存在global是不行的,于是我把日期存在了redis 中,后端服务启动的时候先判断redis中是否已经存在了日期,如果不存在,就把日期存进去,如果存在,则不进行处理,这样,当linux主机关闭时日期才会失效,这样,这个日期才比较有意义.
const RedisClient = require('./db/redis')RedisClient.get('time', (err, num) => { // 如果不存在,则进行赋值 if (err || num === null) { RedisClient.set('time', Date.now() + ""); return; } return;})
nginx双网站的配置
我的服务器一开始是只部署了我的博客系统的,不过,后来把易解也部署到服务器了,那时候为了方便访问,就把原先server 的 / 路径部署成易解的网页了,然后再server下面的子路由 /blog 下面部署我的博客系统,让访问这个地址的时候,把我博客系统的html页面返回给浏览器,为了做到这个效果,我把webpack的output 配置中的publicPath 修改成了 blog,这样打包出来的 js文件 就带上了 blog这个前缀,还把 vue 路由的 base 设置成 /blog ,这样前端路由的页面才会加上/blog 这个前缀,再使用alias 进行nginx的配置,这样下来终于完成了配置。
虽然这样也可以,但是页面刷新的时候就会出问题,所以,我这次换了另外一种思路,我开了个二级域名,然后在nginx的配置文件中写了2个server,把新的server的hostname配置成我新创建的二级域名,这样就实现了nginx虽然只监听80端口,但却能根据不同的域名部署多个网站。
user root;worker_processes 1;error_log /var/log/nginx/error.log warn;pid /var/run/nginx.pid;events { worker_connections 1024;}http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; gzip on; gzip_min_length 1k;gzip_comp_level 1;gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript ; gzip_http_version 1.1;gzip_vary on;#include /etc/nginx/conf.d/*.conf;server { listen 80; server_name cxtao666.github.io; #charset koi8-r; #access_log logs/host.access.log main; // 部署易解location /{root /home/ftpuser/index; index index.html; if (!-e $request_filename) { rewrite ^/(.*) /index.html last; break; }add_header Cache-Control max-age=86400; } location /port/ { proxy_pass http://127.0.0.1:4000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;} location /u/ { proxy_pass http://127.0.0.1:7000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;} location /update/ { proxy_pass http://127.0.0.1:4000; proxy_set_header Host $host;proxy_buffer_size 4000k;proxy_buffering on;proxy_buffers 4 44000k;proxy_max_temp_file_size 1024m;client_max_body_size 100m;}location /download/ { root /home/ftpuser; }location /png/ { root /home/ftpuser/blog/blog-express;}}server{ listen 80; server_name blog.cxtao666.github.io; location /png/ { root /home/ftpuser/blog/blog-express;}// 部署博客系统location /{ root /home/ftpuser/blog/blog/dist/; index index.html; if (!-e $request_filename) { rewrite ^/(.*) /index.html last; break; }add_header Cache-Control max-age=2592000;} location /api/ { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host;proxy_buffer_size 4000k;proxy_buffering on;proxy_buffers 4 44000k;proxy_max_temp_file_size 1024m; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;client_max_body_size 100m;}}}
解决文章中单引号引发的mysql错误
这个问题我一开始是没注意到的,后来再上传文章的时候,发现后台会报错,仔细观察才发现是单引号的问题。因为我后台是直接使用sql语句进行文章的插入的,使用insert 语句 进行 sql 的拼接的时候,插入的值会带个单引号,于是如果我的文章中也出现单引号的话,这样就会导致数据插入异常。
const sql = "insert into blogs (title,content,createtime,author,label,number,html,imgcode,markdown) values ('" + title +"','" + content + "','" + createTime +"',' " + author + "',' " + label + "',0,'"+ html + "',' " + name + "',' "+markdown+"');"
为了解决这个问题,就需要把博客中的 ‘ 进行处理,把它替换成\‘的形式,这样就能成功插入数据了
markdown = markdown.replace(/[']/g, "\\'"); // 对markdown 中的'进行处理
解决keep-alive导致的标签详情页数据不刷新问题
我的标签分类打开后,会进入tag组件显示当前分类下的博客列表,但是因为我使用了keep-alive,所以如果获取数据在created 生命周期中写的话,那么组件只有在第一次被打开后会执行,所以为了正常显示数据,我又在update生命周期里面,把获取数据的逻辑也写了进去。
name: 'archive',data() {return { activities: [],num:0,name:" "};},methods: { // 获取数据getData(name){// 使用了箭头函数,所以this指向函数外的作用域this.$http.get("/api/blog/getlabel",{params : {name}}).then(res => { this.activities = res.data.data; this.num = res.data.data.length; });},// 判断当前的参数是否存在,如果存在且发生了改变,就获取数据judge(){let name = this.$route.params.name if( name !== undefined && name !== this.name){ this.name = name ;this.getData(name); } }},// 在created的钩子函数中去获取参数created(){ this.judge(); },// 在uodated的钩子函数中去获取参数updated(){this.judge();}
打包优化
关于前端页面的打包优化,其实在一开始的时候就进行了,包括使用动态路由加载,按需引入组件。nginx也开启了gzip 的 js 代码压缩,和开启了http缓存,为了简单,就直接使用了强缓存,把vue全家桶使用cdn形式导入到html页面中。不过做了这些,还是觉得力度不够,因为我这次博客更新,又引入了代码高亮插件,之前的element-ui因为尝试失败了,也没有成功使用外链的形式导入,所以这次一打包,bundle.js 直接2MB了,再加上我服务器本来带宽就小,结果首屏渲染花了5s,感觉这性能不能忍,于是我又开始了优化。
我这次优化的主要目的还是把element-ui给打包出来,还有就是把css也给单独打包出来。于是我开始了配置webpack,build出html,放到服务器上,浏览器ctrl+f5尝试刷新的无限循环。这里推荐个插件webpack-bundle-analyzer ,使用它可以对bundle.js进行模块分析,这样就知道了哪个模块占了比较大的空间,然后对这几个大模块进行优化,就可以了
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
plugins: [ // 所有webpack 插件的配置节点new BundleAnalyzerPlugin(), //优化分析插件new htmlWebpackPlugin({template: path.join(__dirname, './src/index.html'), // 指定模板文件路径filename: 'index.html' // 设置生成的内存页面的名称}),new UglifyJSPlugin({minimize: true,sourceMap: false,compress: {// 在UglifyJs删除没有用到的代码时不输出警告warnings: false,// 删除所有的 `console` 语句,可以兼容ie浏览器drop_console: true,// 内嵌定义了但是只用到一次的变量collapse_vars: true,// 提取出出现多次但是没有定义成变量去引用的静态值reduce_vars: true,},output: {// 最紧凑的输出beautify: false,// 删除所有的注释comments: false,}}),new Ex("styles.css")],
首先说下,单独打包出css,一开始我的css是打包在bundle.js文件里面的,为了解决这个问题,我使用了extract-text-webpack-plugin 这个插件,因为我的webpack是3.0的(老项目了…),如果是webpack4.0的话,做法可能会有所不同。经过一番配置,就可以把css文件单独单独打包出来了
const Ex = require("extract-text-webpack-plugin");
module: { // 配置所有第三方loader 模块的rules: [ // 第三方模块的匹配规则{test: /\.css$/,use: Ex.extract({fallback: "style-loader",use: "css-loader"} )//use: ["style-loader","css-loader"]}, // 处理 CSS 文件的 loader},
接下来,就是element-ui的优化,不得不吐槽,element-ui是真的大,虽然可以选择按需引入组件,但我还是决定使用cdn把element-ui 外链进来。具体的操作过程是这样的,把element的script标签写在vue的script标签的后面,记住得是后面才行,因为要先引入vue,才能使用element,不然会出错的,再在webpack的配置文件中的externals中把element-ui加进去,然后,还要去main.js,把原先有关element-ui的导入配置都删除了(切记要把代码删掉或注释掉,不然的话,webpack的配置不起作用),做完了这些,就成功了.
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script><script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script><script src="https://cdn.bootcss.com/vuex/2.3.1/vuex.min.js"></script><script src="https://cdn.bootcss.com/vue-resource/1.5.1/vue-resource.min.js"></script><script src="https://unpkg.com/element-ui/lib/index.js"></script>
// 这是在webpack中的配置externals: {'vue': 'Vue','vue-router': 'VueRouter','vuex': 'Vuex','babel-polyfill': 'window','vue-resource': 'VueResource','element-ui': 'ELEMENT'},
// 在main.js中,把这些代码都注释掉//import element from "element-ui"//import 'element-ui/lib/theme-chalk/index.css'// Vue.use(element);
经过上面一番操作之后,我的首页打开速度从5s变成3s,bundle.js 从2MB 变成了1MB,可以接受了,所以也就优化到了这里。
结尾
坑就先踩到这里了,踩了这么多坑,博客的稳定性和实用性终于有了很大的提升,这里记录了我踩的几个比较大的坑,其它一些小坑就不再这里赘述了,不得不说,项目要落地是真的难,做个demo是容易的,但是要真正使用起来的话,就会发现很多的坑等着去优化,今天的博客记录就到这里了,完结,撒花!