Skip to content

1207915357/yuanhui.live

Repository files navigation

vue+node+mongodb 搭建博客(2019-1-22)

项目规划

123

计划实现功能(五大模块)

一. 博客界面(首页)

  1. 文章内容展示,分类导航,作者头像简单介绍
  2. 首屏动画(rainbow)
  3. 背景音乐播放器

二. 生活(分享)

  1. 有趣的事
  2. 尤克里里音乐
  3. 篮球视频

三. 留言(相互交流学习)

四. 关于(作者历程详细介绍)

五. 管理后台 react

  1. 文章发布 markdown
  2. 评论审核
  3. 账号管理
  4. 参考掘金
  5. 我的主页

六. 功能

  1. 文章顶置,点击回到顶部
  2. 加载动画
  3. logo
  4. 流量统计,字数统计,文章阅读便捷功能
  5. 登陆注册
  6. 文章搜索
  7. pwa应用 lavas
  8. 双向通信 socket.io
  9. ,移动端适配,rem px2rem
  10. 点赞,评论
  11. 分类与归档
  12. egg.js
  13. node 爬虫

一、 前端构建 (vue+element)

1.初始化vue项目

1.1 vue中使用iconfont中的svg图标
  • 根据 vue-cli3 从搭建到优化 配置
  • iconfont 下载的svg图片它会自带一个fill属性,外部的css改变颜色不起作用,需要设置一个全局的css样式 path { fill: inherit !important } 或者使用 svgo 格式化下 !
1.2 axios 的封装
1.3 实现登录注册
1.4 引入markdown

2. 前端路由管理

  • 新建api文件夹管理路由
    • -api
    • -index.js //主文件 api接口的统一出口
    • -base.js //基础域名配置
    • -user.js // 用户模块路由
    • -article.js //文章模块路由
//index.js  
//api接口的统一出口

//用户模块
import user from '@/api/user';
// 文章模块接口
import article from '@/api/article';
// 其他模块的接口……

// 导出接口
export default {    
    user,
    article,
    // ……
}

-----------------------------------------------
//base.js
/**
 * 接口域名的管理
 */
const base = {    
    dev: 'http://127.0.0.1:3000',    
    product: ''
}

export default base;


--------------------------------------------
//user.js
  
import base from './base'; // 导入接口域名列表
import axios from '@/axios/request'; // 导入request中创建的axios实例
import qs from 'qs'; // 根据需求是否导入qs模块

const user = {
    login (params) {        
        return axios.post(`${base.dev}/login`,qs.stringify(params));    
    },
    register (params) {        
        return axios.post(`${base.dev}/register`,qs.stringify(params));    
    }
}

export default user
  • main.js中导入和挂在api
import api from './api' // 导入api接口
Vue.prototype.$api = api; // 将api挂载到vue的原型上
  • 使用
this.$api.user.login()
  .then((data)=>{
  ...
})

二、后台搭建(node+koa+mongodb)

1.初始化node项目

  • npm init
  • npm koa -S koa中文文档
  • npm i nodemon -S 热重启, 使用 nodemon ./app 命令启动项目 nodemon

2. 安装一系列依赖

  • npm i koa2-cors (解决跨域) koa2-cors
  • npm i koa-bodyparser (解析接收的参数) koa-bodyparser
  • npm i koa-static (开放静态资源) koa-static
  • npm i mongoose (操作MongoDB) mongoose
  • npm i uuid (生成唯一id插入数据库) uuid

3. 注册登录接口

3.1 使用nodejs自带的 crypto模块加密
const crypto = require('crypto')
const cryptoPwd = function(password,salt){
    const saltPassword = password + ':' + salt
    const md5 = crypto.createHash('md5')
    const result = md5.update(saltPassword).digest('hex')
    return result;
}
3.2 使用Token进行身份验证 | 缓存options请求

2.路由接口

路由模块管理

router

  • api文件夹存放接口路由

    const Router = require('koa-router');
    const router = new Router();
    const article_controller = require('../controllers/article_controller');
    
    router.post('/article/publish', article_controller.publish);
    
    module.exports = router;
  • controllers文件夹存放业务逻辑

    const config = require('../config');
    const Article_col = require('../model/article');
    const uuidV1 = require('uuid/v1')
    
    //发布文章
    const publish = async (ctx, next) => {
        const req = ctx.request.body
        console.log(req,'article')
        const id = uuidV1();
        const newArticle = await Article_col.create({
            id,
            value: req.value,
            title: req.title,
            pictureUrl: req.pictureUrl
        })
    
        if (newArticle) {
            ctx.body = {
                code: 1,
                msg: 'success!',
                data: {}
            };
        } else {
            ctx.body = {
                code: 0,
                msg: 'failed!'
            };
        }
    }
    
    module.exports = {
        publish,
    }
  • model文件夹存放schema表

    const mongoose = require('mongoose')
    const Schema = mongoose.Schema
    
    const articleSchema = new Schema({
        id:{
        	type: String,
        	required: true
        },
        value: {
            type: String,
            required: true
        },
        title: {
            type: String,
            required: true
        },
        pictureUrl: {
            type: String
        },
        created_time: {
            type: Date,
            default: Date.now
        },
        eye:{
            type: Number,
            default: 0
        },
        like: {
            type: Number,
            default: 0
        },
        comments: {
            type: Number,
            default: 0
        },
    
    })
    
    module.exports = mongoose.model('Article', articleSchema)
  • 同时在app.js中导入api中的路由,再使用路由

    const Koa = require('koa')
    const cors = require('koa2-cors');
    const bodyParser = require('koa-bodyparser');
    const koaStatic = require('koa-static');
    const mongoose = require('mongoose');
    const config = require('./config.js');
    const user_router = require('./api/user-router.js');
    const article_router = require('./api/article-router.js');
    const app = new Koa();
    
    mongoose.connect(config.db, {useNewUrlParser:true}, (err) => {
        if (err) {
            console.error('Failed to connect to database');
        } else {
            console.log('Connecting database successfully');
        }
    });
    
      app
    
      //跨域
      .use(cors())
      //开放静态资源
      .use(koaStatic(__dirname + '/public'))
      //请求参数格式化
      .use(bodyParser())
      //路由
      .use(user_router.routes()).use(user_router.allowedMethods())
      .use(article_router.routes()).use(article_router.allowedMethods())
      //启动服务监听端口
      .listen(config.port,function(){
          console.log('server is running ')
      });
2.1 登陆注册
  • 普通用户登陆权限(留言,评论,点赞,)

  • 管理员权限(发布)

  • 超级管理员(对文章评论进行管理,删除)

    2.2 文章
名称 接口 参数 返回值
文章发布 /article/publish value(html源码),title,pictureUrl null
文章保存到草稿 /article/save value(md源码),title,pictureUrl null
草稿列表 /article/draftList null data([{title,mdcode}...])
查看草稿 /article/lookDraft id data(md源码)
文章列表 /article/articleList userId data([{title,htmlcode,puctureUrl,eye, like,comments,time}...])
文章编辑 /aritcle/edit id null
2.3 图片的上传

【参考】

实现思路

  • 前端上传图片请求服务器,我用的时element里的上传组件

    <template>
      <div class="publish">
        
        <el-upload
                   class="avatar-uploader"
                   :action="`${baseUrl}/article/uploadCover`"
                   :show-file-list="false"
                   :on-success="handleAvatarSuccess"
                   :before-upload="beforeAvatarUpload">
          <img v-if="imageUrl" :src="imageUrl" class="avatar">
          <i v-else class="el-icon-plus avatar-uploader-icon"></i>
        </el-upload>
        
      </div>
    </template>
    <script>
      export default {
        name:'publish',
        data () {
          return {
            baseUrl:base.dev, //服务器地址
            imageUrl: '',
            pictureUrl: '',
          };
        },
        methods: {
    
          //文章发布
          publish(){
            this.$api.article.publish({
                  value:this.mdCode,
                  title:this.title,
                  pictureUrl:this.pictureUrl,  
                }).then((data)=>{
                  this.$router.push({path:'/'})
                })
           },
           //图片上传
          handleAvatarSuccess(res, file) {
             this.imageUrl = URL.createObjectURL(file.raw);  
             this.pictureUrl = res.pictureUrl;  //返回的图片路径
          },
          beforeAvatarUpload(file) {
            const isLt2M = file.size / 1024 / 1024 < 1;           
            if (!isLt2M) {
              this.$message.error('上传头像图片大小不能超过 1MB!');
            }
            return  isLt2M;
          }
        },
    </script>
  • 后端使用koa-multer把图片存储在public下的一个articleCover文件夹目录

    • article-router.js 配置multer
    const Router = require('koa-router');
    const router = new Router();
    const article_controller = require('../controllers/article_controller');
    
    const multer = require('koa-multer')
    const path = require('path')
    var storage = multer.diskStorage({
        //文件保存路径
        destination: function (req, file, cb) {
            cb(null, 'public/articleCover/') //path.resolve('public/phoneManageSystem')
        },
        //修改文件名称
        filename: function (req, file, cb) {
            var fileFormat = (file.originalname).split("."); //以点分割成数组,数组的最后一项就是后缀名
            cb(null, Date.now() + "." + fileFormat[fileFormat.length - 1]);
        }
    
    })
    //加载配置
    var upload = multer({
        storage: storage,
        limits: {
           fileSize: 1024 * 1024 / 2 // 限制512KB
        }
    });
    
    router.post('/article/uploadCover', upload.single('file'), article_controller.uploadCover);
    
    module.exports = router;
  • aritcle_controller.js 接口

    //图片上传
       const uploadCover = async(ctx, next)=>{
           let pictureUrl = `${config.severUrl}:${config.port}/articleCover/${ctx.req.file.filename}`
           ctx.body = {
               filename: ctx.req.file.filename, //返回文件名
               pictureUrl: pictureUrl  //前端直接调用的地址
           }
       }
  • 必须要开放public这个文件夹作为静态资源

    app.use(koaStatic(__dirname + '/public'))
  • 前端调用图片

markdown中图片的上传


三、技术梳理 | 知识拓展 | 问题记录

  • less:hover .other{}不起作用 .less{ ::before}为什么选中了全部子元素

  • vuejs 失去焦点,点击全局隐藏某些浮动元素 https://segmentfault.com/q/1010000007444595

  • markdown插入图片的几种方法

  • 时间格式化 momentjs

    vue总结 vue总结2

  • less深度选择器 .layout /deep/ .content css深度选择 >>>

  • 路由懒加载

    {
      path: '/',
      name: 'home',
      component: resolve => require(['@/view/home/home.vue'], resolve)
    }
  • 开启gzip

  • hiper 性能分析工具

  • v-model 实现父子组件通信,双向绑定数据

  • 页面刷新时,url导航地址栏的路由和当前页面显示的不一致(注意区分route:对象和router:方法)

  • 解决 vue刷新路由跳到首页的问题

    // 1.根组件中通过watch监听路由地址的改变
    // 2.使用钩子函数,改变路由
    
     export default {
        name: 'App',
        data(){
          return {
            msg:'App.vue',
            currentRouter:'/'
          }
        },
        methods:{
         
        },
        watch:{
          '$route':function(to,from){
              this.currentRouter=to.path;
          }
        },
        beforeMount(){
            this.currentRouter=this.$route.path;
        }
      }
  • 参考博客:http://120.79.10.11:8001/ http://biaochenxuying.cn/

  • 闭包应用

    function sendRequest(urls, max, callback) {
      const len = urls.length;
      let idx = 0;
      let counter = 0;
    
      function _request() {
        // 有请求,有通道
        while (idx < len && max > 0) {
          max--; // 占用通道
          fetch(urls[idx++]).finally(() => {
            max++; // 释放通道
            counter++;
            if (counter === len) {
              return callback();
            } else {
              _request();
            }
          });
        }
      }
      _request();
    }

3.1评论功能

  1. 前端界面实现

  2. mongdb表的设计

  3. 时间格式化 moment

  4. 头像插件 vue-avatar

  5. 关联github 获取用户信息头像 参考 未完成

  6. 支持表情包功能 emoji-mart-vue

  7. bug :点击其他地方不能关闭评论框

3.2 文章分类,归档,标签功能

3.3 换皮肤,主题 (暂未实现)

3.4 vuex全局加载动画

  • 动画组件 引入 codepen 里的动画

3.5 词云标签

3.6 消息通知系统

3.6.1 后台node新建独立通知模块
  • model/user.jsschema 中添加参数对象

  • 新建路由文件 notice-router.js

    • 引入koa-router
    • 新建路由
    • 接口挂载到路由
    • 导出路由
    const Router = require('koa-router');
    const router = new Router();
    const notice_controller = require('../controllers/notice_controller');
    
    router.post('/notice/publishNotice', notice_controller.publishNotice);
    
    module.exports = router;
  • 新建控制器文件 notice_controller.js

    • 导入需要用到的model文件
    • 新建方法对象
    • 导出对象
    const Article_col = require('../model/article');
    const User_col = require('../model/user');
    
    //发布通知消息
    const publishNotice = async (ctx, nest) => { }
    
    module.exports = {
         publishNotice,
    }
  • 最后在 app.js 中 引入 notice路由模块,再挂载到 app 上

    const notice_router = require('./api/notice-router.js');
    app.use(notice_router.routes()).use(notice_router.allowedMethods())
3.6.2 前端vue新建独立api模块
  • src/api 新建 notice.js 文件

    // 通知消息模块api
    import base from './base'; // 导入接口域名列表
    import axios from '@/axios/request'; // 导入request中创建的axios实例
    import qs from 'qs'; // 根据需求是否导入qs模块
    
    const notice = {
        publishNotice(params) {
            return axios.post(`${base.dev}/notice/publishNotice`,qs.stringify(params));
        },
    }
    
    export default notice
  • api/index.js 中引入和导出

    //通知模块
    import notice from '@/api/notice';
    // 导出接口
    export default {    
        notice,
        // ……
    }
  • Notice.vue中使用

     this.$api.notice.publishNotice({userId:this.userId})
         .then((data)=>{})
3.6.3 功能实现
  • 用户评论文章,通知作者;

  • 回复用户通知被回复用户;

  • 用户第一次登陆默认发送一个欢迎的通知,注册时插入一条数据

  • vue 中使用 vue-socket stock.io

  • 新发布一片文章时给用户发送一条通知

     const updateAllUser = await User_col.updateMany(
            {userId: {$ne: userId}},
            {
                '$inc': {
                    'unreadNum': 1
                },
                '$push': {
                    'commentNotice': noticeObj
                }
            }
        )
  • 流程设计:

    用户评论文章,存储用户ID,文章作者ID,文章ID,用户评论到一个表(我直接存储到了用户信息的表里commentNotice 数组里)同时把记录通知的值加一,用户登录自动请求获取用户信息接口,如果用户信息里的commentNotice 数组里有值表示有消息通知并显示这些列表。用户查看通知把记录通知的值再赋值为0;

3.7 侧边栏吸顶效果

参考 4 种滚动吸顶实现方式的比较

3.8 添加路由权限

参考 带你用vue撸后台 系列二(登录权限篇)

补充: export 和 export default 的区别

  • export与export default均可用于导出常量、函数、文件、模块等
  • 在一个文件或模块中,export、import可以有多个,export default仅有一个
  • 通过export方式导出,在导入时要加{ },export default则不需要
  • export能直接导出变量表达式,export default不行。

3.9 骨架屏

为vue项目添加骨架屏

vue-skeleton-webpack-plugin

vue-cli3配置骨架屏方案

  • 采坑: npm install vue-skeleton-webpack-plugin 报错: Error: EPERM: operation not permitted, rename ... errno: -4048 表示没有权限,可以把vue页面的服务先关掉,然后再npm,我的是这样解决的!

  • vue.config.js 配置 附上一张配置清单

    const path = require('path')
    const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin');
    function resolve(dir) {
      return path.join(__dirname, './', dir)
    }
    module.exports = {
      chainWebpack: config => {},
      configureWebpack: {
        plugins: [
          new SkeletonWebpackPlugin({
            webpackConfig: {
              entry: {
                app: resolve('src/assets/js/skeleton.js'),
              },
            },
            minimize: true,
            quiet: true,
          }),
        ],
      },
     // css相关配置
     css: {
       // 是否使用css分离插件 ExtractTextPlugin
       extract: true,
       // 开启 CSS source maps?
       sourceMap: false,
       // 启用 CSS modules for all css / pre-processor files.
       modules: false
     }  
    }
  • 配置完后需要重启服务才能生效,有时哪个地方配置错误,可能会出启动编译错误或编译到百分之几十的时候就停止了!这是需要检查配置的文件路径及代码是否规范错误!

  • 如果出现错误实在不知从哪里解决,可以搭一个新的测试vue项目,再配置骨架屏!测试项目可以显示,再跟自己项目做对比找出错误!(一开始我也找不到哪里的问题一直编译错误,然后我是这么解决的!哈哈...)

3.10 TypeScript 应用


四、博客后台管理系统 vue-element-admin

4.1功能

  • 用户管理 , 用户分析 , 流量统计 ,用户权限设置 , 用户评论数分析

  • 文章管理 , 草稿 , 编辑

  • 评论管理 , 审核

  • 发布通知

  • 标签管理

五、ECS服务器部署

把Node.js项目部署到阿里云服务器(CentOs)

node+mongodb项目部署在服务器

将nodejs项目部署到阿里云ESC服务器,linux系统配置80端口,实现公网IP访问

  1. node 的安装

  2. mongoDB 的安装 | 数据备份导入 | mongoDB 基本命令 | 配置环境 更改默认端口 ?? | 连接线上数据库 端口防火墙开放 | 开机启动?启动与关闭 conf文件启动 | 建立密码连接 |

  3. pm2 生产进程管理器 | 可视化监控

  4. ssl https 证书

  5. nginx | nginx.conf 配置 | vue history 配置 |

    #user  nobody;
    worker_processes  1;
    #error_log  logs/error.log;
    #error_log  logs/error.log  notice;
    #error_log  logs/error.log  info;
    #pid        logs/nginx.pid;
    events {
        worker_connections  1024;
    }
    http {
        include       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  logs/access.log  main;
        sendfile        on;
        #tcp_nopush     on;
        #keepalive_timeout  0;
        keepalive_timeout  65;
        port_in_redirect off;
        #后台admin代理      
        server {
    		    listen     8888;
                server_name  localhost;
                #charset koi8-r;
                #access_log  logs/host.access.log  main;
                location / {
                    root   /home/yuanhui-blog/yuanhui-admin/;
                    index  index.html index.html;
                    try_files $uri $uri/ /index.html;
                    autoindex on;
                }
                location @router{
                    rewrite ^.*$ /index.html last;
                }
    
                location /api/ {
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_pass http://106.14.174.233:8888/;
                }
                gzip on;
                gzip_buffers 32 4k;
                gzip_comp_level 6;
                gzip_min_length 200;
                gzip_types text/css text/xml application/javascript;
                gzip_vary on;
                #error_page  404              /404.html;
                # redirect server error pages to the static page /50x.html
                error_page   500 502 503 504  /50x.html;
              }
      #前台代理
      server {
     		listen          80;
            server_name     localhost;
            location / {
                    root  /home/yuanhui-blog/yuanhui-live/;
                    index  index.html index.html;
                    try_files $uri $uri/ /index.html;
                    autoindex on;
            }
            location @router{
                    rewrite ^.*$ /index.html last;
            }
            location /api/ {
                     proxy_set_header X-Real-IP $remote_addr;
                     proxy_pass http://106.14.174.233:80/;
                    }
            gzip on;
            gzip_buffers 32 4k;
            gzip_comp_level 6;
            gzip_min_length 200;
            gzip_types text/css text/xml application/javascript;
            gzip_vary on;
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
          }
            # proxy the PHP scripts to Apache listening on 127.0.0.1:80
            #location ~ \.php$ {
            #    proxy_pass   http://127.0.0.1;
      #location ~ \.php$ {
            #    proxy_pass   http://127.0.0.1;
            #}
            # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
            #
            #location ~ \.php$ {
            #    root           html;
            #    fastcgi_pass   127.0.0.1:9000;
            #    fastcgi_index  index.php;
            #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
            #    include        fastcgi_params;
            #}
            # deny access to .htaccess files, if Apache's document root
            # concurs with nginx's one
            #
            #location ~ /\.ht {
            #    deny  all;
            #}
    
        # another virtual host using mix of IP-, name-, and port-based configuration
        #
        #server {
        #    listen       8000;
        #    listen       somename:8080;
        #    server_name  somename  alias  another.alias;
        #    location / {
        #        root   html;
        #        index  index.html index.htm;
        #    }
        #}
        # HTTPS server
        #
        #server {
        #    listen       443 ssl;
        # HTTPS server
        #
        #server {
        #    listen       443 ssl;
        #    server_name  localhost;
        #    ssl_certificate      cert.pem;
        #    ssl_certificate_key  cert.key;
        #    ssl_session_cache    shared:SSL:1m;
        #    ssl_session_timeout  5m;
        #    ssl_ciphers  HIGH:!aNULL:!MD5;
        #    ssl_prefer_server_ciphers  on;
        #    location / {
        #        root   html;
        #        index  index.html index.htm;
        #    }
        #}
    }

  6. linux | vim 基本命令 | 防火墙 端口开放 | 路由刷新404

  7. vue生产环境和开发环境配置

    vue+node+mongodb部署到服务器的一次实践

六、小程序版本 Taro mpVue

六、日志

6.1 项目完成进度

  • 2019

  • 2.27 完成了文章点赞 git上传

  • 2.28 样式优化,滚动条 Vuescroll.js

  • 3.01 评论模块

  • 3.06 完成评论功能

  • 3.11 分类,归档,标签功能

  • 3.13 完成归档标签分类功能

  • 3.14 添加返回顶部功能 | vuex全局加载Loading动画.

  • 3.15 优化loading动画

  • 3.18 文章列表接口分页处理 | 滚动加载更多

  • 3.19 文章搜索, siderBar

  • 3.25 分类标签词云d3 async的使用

  • 3.26 消息通知系统设计

  • 4.01 - 4.03 完成评论消息通知功能

  • 4.04 优化通知系统

  • 4.08 独立通知模块,增加发布文章通知全部用户功能

  • 4.09 使用更新器$inc $push(原子操作) 优化 mongoDB update 语法

    • 左侧栏添加最热文章
    • router-link路由跳转只局部渲染更改了的页面(diff算法),a标签跳转会重新渲染整个页面
    • 路由的滚动 scrollBehavior
  • 4.10 详情页作者介绍

  • 4.11 添加作者个性化vip身份标识

  • 4.15 ...

  • 4.16 后台管理搭建 vue-element-admin 了解

  • 4.17- 4.18 api添加token认证 |

    • 如何避免options?(跨域预请求非简单请求是无法避免,可以缓存让它只请求一次) 设置 Access-Control-Max-Age:86400 // 缓存预检请求
  • 4.19 notice通知模块使用vuex优化

  • 4.22 添加404页面 | 路由权限

  • 4.23 骨架屏搭建 | 401页面

  • 4.24 骨架屏实现-

  • ...work

  • 5.06 骨架屏实现 (未实现,配置??)

  • 5.07 首页骨架屏实现

  • 5.08 添加分类category


  • 5.09 后台管理搭建

  • 5.10 后台 文章管理 | 发布 | 更新添加字段 | 用户管理 |

  • ...work

  • 5.23 后台添加发布通知 (一次通知多个用户)

  • 5.24 后台评论管理

  • 5.27 后台评论管理优化 | 文章筛选 | 分页 | 评论审核 | 评论接口分离

    • 问题: table树的children怎么拿到整个父级的row? | mongoDB怎么更新一个数组内的一个元素?
    • 时间格式化优化
  • 5.28 前台获取文章评论分离使用单独接口 | 评论删除 | 评论回复

    • ~~ 新评论通知 (实时跟进用户评论)~~ (待实现)
  • 5.29 添加登陆

  • 5.30 添加操作权限

    • 个人中心设置(待实现)

  • 6.3 服务器部署

  • 6.4 服务器部署

  • 6.5 服务器部署

  • 6.6 服务器部署完成

  • 路由动态渲染原理

  • 百度统计 (PV>100)

  • 首屏渲染,骨架屏优化

  • 详情页大纲导航栏 markdown样式

  • 图片存储方式

  • 移动端适配 | 响应式 rem 转换 | vh vw | 媒体查询

  • 打包部署 pm2 | webpack配置 | 单元测试 | 服务器部署环境

  • swagger | mock 数据

  • 微信小程序 trao mpvue | typeScript 构建

  • 云服务器

  • scss | less 语法

6.2 遗留bug

  • 登陆或注销状态 cookie不能及时刷新
  • 定位使用vuescroll
  • 点击其他地方不能关闭评论框
  • 归档:时间划线问题 github 时间选择菜单
  • 菜单导航不根据 路由动态变化 跳转时去掉导航激活状态
  • 详情页代码界面样式需要刷新才加载

(图片存储base64)

About

vue+element 搭建的个人博客

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages