{"id":25944823,"url":"https://github.com/raoul1996/vue-vote","last_synced_at":"2025-03-04T08:19:21.624Z","repository":{"id":46897294,"uuid":"94951519","full_name":"Raoul1996/vue-vote","owner":"Raoul1996","description":"rebuild vote as a side project","archived":false,"fork":false,"pushed_at":"2024-06-28T02:11:47.000Z","size":6642,"stargazers_count":5,"open_issues_count":16,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-06-28T03:25:46.199Z","etag":null,"topics":["axios","bithound","cdn-vue","nginx","spa","vote","vue","vue-vote","vuex","webpack"],"latest_commit_sha":null,"homepage":"https://votes.raoul1996.cn/","language":"Vue","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Raoul1996.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-21T01:49:09.000Z","updated_at":"2024-06-28T03:26:24.899Z","dependencies_parsed_at":"2024-06-28T03:25:48.389Z","dependency_job_id":null,"html_url":"https://github.com/Raoul1996/vue-vote","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Raoul1996%2Fvue-vote","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Raoul1996%2Fvue-vote/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Raoul1996%2Fvue-vote/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Raoul1996%2Fvue-vote/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Raoul1996","download_url":"https://codeload.github.com/Raoul1996/vue-vote/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241809532,"owners_count":20023787,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["axios","bithound","cdn-vue","nginx","spa","vote","vue","vue-vote","vuex","webpack"],"created_at":"2025-03-04T08:19:20.805Z","updated_at":"2025-03-04T08:19:21.603Z","avatar_url":"https://github.com/Raoul1996.png","language":"Vue","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Vue-vote —— 在线投票系统 (side project)\n\n[![Build Status](https://travis-ci.org/Raoul1996/vue-vote.svg?branch=master)](https://travis-ci.org/Raoul1996/vue-vote)\n[![Maintainability](https://api.codeclimate.com/v1/badges/b68da294651f58e8d2f7/maintainability)](https://codeclimate.com/github/Raoul1996/vue-vote/maintainability)\n[![bitHound Overall Score](https://www.bithound.io/github/Raoul1996/vue-vote/badges/score.svg)](https://www.bithound.io/github/Raoul1996/vue-vote)\n[![bitHound Dependencies](https://www.bithound.io/github/Raoul1996/vue-vote/badges/dependencies.svg)](https://www.bithound.io/github/Raoul1996/vue-vote/a606c4cfb7944fe7edce9ff1d05474fc3986b640/dependencies/npm)\n[![bitHound Dev Dependencies](https://www.bithound.io/github/Raoul1996/vue-vote/badges/devDependencies.svg)](https://www.bithound.io/github/Raoul1996/vue-vote/a606c4cfb7944fe7edce9ff1d05474fc3986b640/dependencies/npm)\n[![bitHound Code](https://www.bithound.io/github/Raoul1996/vue-vote/badges/code.svg)](https://www.bithound.io/github/Raoul1996/vue-vote)\n[![Test Coverage](https://api.codeclimate.com/v1/badges/b68da294651f58e8d2f7/test_coverage)](https://codeclimate.com/github/Raoul1996/vue-vote/test_coverage)\n\n\u003e 使用 Vue 重构了 Vote 项目，实践各种东西\n\n## 文件结构\n```\n.\n├── CODE_OF_CONDUCT.md\n├── CONTRIBUTING.md\n├── ISSUE_TEMPLATE.md\n├── LICENSE\n├── README.md\n├── build\n│   ├── build.js\n│   ├── check-versions.js\n│   ├── logo.png\n│   ├── utils.js\n│   ├── vue-loader.conf.js\n│   ├── webpack.base.conf.js\n│   ├── webpack.dev.conf.js\n│   ├── webpack.prod.conf.js\n│   └── webpack.test.conf.js\n├── config\n│   ├── dev.env.js\n│   ├── index.js\n│   ├── prod.env.js\n│   └── test.env.js\n├── dist\n│   ├── index.html\n│   ├── static\n│   └── statistics.html\n├── fjpublish.config.js\n├── gh-pages.config.js\n├── helper\n│   └── unitTest.md\n├── id_rsa.enc\n├── index.css\n├── index.html\n├── package-lock.json\n├── package.json\n├── src\n│   ├── App.vue\n│   ├── assets\n│   ├── components\n│   ├── config\n│   ├── main.js\n│   ├── pages\n│   ├── router\n│   ├── service\n│   ├── store\n│   └── utils.js\n├── static\n└── test\n    ├── e2e\n    └── unit\n\n17 directories, 31 files\n\n```\n## 技术栈\n\n1. Vue\n2. Vuex\n3. pug\n4. element-ui\n5. axios\n6. Promise\n\n## 存在问题\n\n- ~~20171217：由于使用了状态树持久化，诞生出一个新的问题：在需要密码才能投票的页面进行刷新，会提示需要输入密码，但是组件的数据还存在。需要想办法解决：~~\n  - ~~思路1：在刷新的时候不重新拉取数据，不进行 v-if 判断，直接使用原来页面的数据~~\n  - ~~思路2：对 detail 中的数据不进行持久化，但是会增大开销~~\n## 优秀资料\n## 未学习资料\n## 实际操作\n- [vue项目中对axios的二次封装](https://juejin.im/post/5a293e50f265da432153f190)\n- [为什么我们要做三份 Webpack 配置文件](https://zhuanlan.zhihu.com/p/29161762),生产环境下移除 console.log\n## 实现了些什么？\n\n- 使用 pug 模板简化代码（练习jade）[html2pug](http://html2jade.vida.io/)\n- 使用 element-ui 进行快速开发\n- 使用 Travis CI 进行自动化编译、打包、以及部署到服务器\n- 使用 codeclimate 进行代码检查，使用 bitHound 进行代码检查，依赖检查\n- [用自己的后台](https://github.com/Raoul1996/koa-vote)，吼吼\n- 二次封装 axios，实现错误的集中处理\n- 使用 CDN 引入 vue.min\n- 使用 `vuex` 进行状态集中管理，请求集中管理\n- 实现状态持久化\n- 开发环境下移除 console.log，console.info\n- 头像上传以及用户自定义\n\n## 将会实现些什么？\n\n1. 自己写组件\n2. 完成投票查询模块\n3. 个人信息维护\n4. 个人投票维护\n5. 分享功能\n6. 邮箱自动补全功能，这个查了一晚上，并没有找到什么好的解决方案\n7. 已经参加过的投票将不能再进行投票\n8. 支持用户进行头像裁剪等\n\n## 学习笔记\n### 20171206(学习朋友的方法，时刻总结) 打包分析插件\n- 安装开发环境下的 webpack 分析插件：`BundleAnalyzerPlugin`,可以在开发环境的 [8888](http://localhost:8888) 端口实时查看webpack构建情况\n\n### 20171210 Vue-dev-tools 不能启动\n\n- 使用 CDN 引入 Vue 的时候会导致 vue-dev-tools 无法使用,参见[这个 issue](https://github.com/vuejs/vue-devtools/issues/190)\n\n### 20171213 数据持久化\n\n- 状态已经成功集中管理，但是有一个问题就是：**状态的持久化**，这个就比较恶心了。状态集中管理的好处就是可以不必费力进行组件间的传值，但是在 vuex 中存储的数据会在刷新之后丢失。\n使用 vuex-persist 这个包做数据持久化可以避免每次 mutation data 时候手动存入 localStorage，读取数据的时候再从 localStorage 中读取的麻烦\n\n### 20171217 过早优化是万恶之源\n\n- 因为今天用 babel-transform-remove-console 插件去掉了 console.log 和 info，所以导致今天很纠结为什么 console.log 没用。过早优化真的是万恶之源。\n- 在 detail 组件是所有组件共用的组件，所以在 detail 组件销毁的时候将状态销毁。使用 beforeDestroy 生命周期钩子，在销毁之前将 detail.options 清空\n\n### 20171217 组件刷新不触发生命周期钩子函数导致的 bug\n\n1.  在昨天晚上发现的 bug 中，由于控制密码框渲染的状态是由组件内部控制的，所以在 vuex 状态树持久化之后就会出现问题：刷新的时候组件内部的状态已经没有了，但是已经放置到 vuex 状态树种的数据并不会因为刷新而消失。\n  - 解决办法： 把控制组件渲染的状态同样放到 vuex 的状态树中，在组件被销毁的时候将 detail 的状态清空，防止造成其他副作用。\n2. 但是这样又会出现一个问题，就是由于 detail 中的状态分了两部分： vote 和 problem。vote 是从列表中通过props继承到了vote-item 的组件中，然后再放到 vuex 的状态树中，这就导致了如果用户更改 param 进入对应的投票就会出错。\n  - 解决办法： 在 detail 组件中维护一个私有的状态 ：returnList，如果vote为空，那么就提示用户进入 vote-list 界面。后期可能会考虑出一个单独的接口，根据 vote-id 获取状态。\n\n### 20171218 使用 v-if 值得注意的一个小点\n\n- 在使用 v-if 的时候有根据上一个选择框的状态来决定其他选项渲染状态的要求，会涉及到下面一个比较细节的小点—— JavaScript 中的强制类型转换：\n\n```\n!! {} === true\n!! Symbol() === true\n!! 0 === false\n!! \"0\" === true\n!! [] === true\n!! \"\" === false\n!! null === false\n!! undefined === false // 当然没有给 undefined 赋其他值\n!! true === true\n```\n### 20171224 登录图片验证码坑多\n- 祝自己平安夜快乐，没有女盆友只能自己和代码瞎几把过。没有女朋友的过圣诞节，有女朋友的就可以过 shengdan 节了。\n- 验证码的刷新问题：点击的时候，可以通过改动 img 的src 属性的值进行触发。我选择了把当前时间作为 queryString 加到地址上来触发验证码的刷新\n- 返回的验证码的 response.type 定义成了 image/png，所以不可以按照其他请求的方式进行集中处理，而且状态树中没有必要也不应该去保存验证码图片。具体参见[这个 commit](https://github.com/Raoul1996/vue-vote/commit/fac8bc175ccb913241ab666f9b4638552d776d31)\n- 可以使用 position 结合 padding 来实现左右布局。在el-input组件的label会受到 flex 的影响，比较麻烦。注意在 wrapper 容器中进行一下相对定位，防止排版错乱（有方法避免）\n### 20171224 投票功能实现\n- 投票完成之后需要禁止用户再次投票吗？\n- 平安夜快乐，睡觉了。\n- action 传参数的时候需要将所有参数封装到一个对象中\n### 20171227\n- 决定将展示型组件拆分到 component 中，容器型组件放到 page 中，每个对应一个路由\n### 20180102-20180103\n- 为域名部署 SSL 证书，过程比较坎坷。使用免费的个人单域名证书，意思就是每个域名都需要自己去配置一个单独的 SSL 证书，这里提供 nginx 配置文件\n  1. 可以使用 service nginx restart 重启nginx 服务，使更改的配置生效\n  2. 如果不能重启，可以使用 nginx -t 查看配置文件错误\n```\n# /etc/nginx/conf.d/vote.conf\n# 部署时候服务器为 Ubuntu 12.04， Nginx 版本为 1.4.6\nserver {\n    # nginx 监听的端口，即为 http 协议访问的端口\n    listen 80;\n    # 域名\n    server_name vote.raoul1996.cn;\n\n    index index.html index.htm index.php;\n\n    add_header Strict-Transport-Security max-age=2592000;\n    # 将 http://vote.raoul1996.cn 的请求转到 https://vote.raoul1996.cn\n    rewrite ^/.*$ https://$host$request_uri? permanent;\n\n    location ~ .*\\.(htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)$ {\n        expires      30d;\n    }\n    location ~ .*\\.(js|css)?$ {\n        expires      1h;\n    }\n\n}\nserver {\n\tlisten 443;\n\tserver_name vote.raoul1996.cn;\n\tssl on;\n\tssl_certificate /etc/ssl/nginx/1_vote.raoul1996.cn_bundle.crt;\n\tssl_certificate_key /etc/ssl/nginx/2_vote.raoul1996.cn.key;\n\n\tssl_session_timeout 5m;\n\n\tssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;\n\tssl_ciphers \"HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES\";\n\tssl_prefer_server_ciphers on;\n\n\tlocation / {\n\t\t# 当前域名映射的目录\n\t\troot /usr/share/nginx/html/vote/;\n\t\t# proxy_pass    https://vote.raoul1996.cn;\n        \t# force timeouts if the backend dies\n        \tproxy_next_upstream error timeout invalid_header http_500 http_502 http_503;\n        \t# set headers\n        \tproxy_set_header Host $host;\n       \t\tproxy_set_header X-Real-IP $remote_addr;\n        \tproxy_set_header X-Forward-For $proxy_add_x_forwarded_for;\n        \t# Let the OpenERP web service know that we're using HTTPS, otherwise\n        \t# it will generate URL using http:// and not https://\n        \tproxy_set_header X-Forwarded-Proto https;\n        \t# by default, do not forward anything\n        \tproxy_redirect off;\n    # 解决二级路由 404\n\t\ttry_files $uri $uri /index.html;\n\t}\n}\n```\n### 20180118\n- 通过 `process.env.NODE_ENV` 切换开发环境和生产环境。\n\n### 20180119\n- 使用 [v-charts](https://elemefe.github.io/v-charts/) 库处理投票统计\n- v-charts 在 create 生命周期中完成数据的初始化，所以在父组件中为其传入 props，完成数据的渲染。但是由于所有的数据请求已经集中处理，所以使用 Promise 将 response 传递到下一个 then。\n\n### 20180122\n- 文件上传功能前端没什么需要注意的，只不过后端有比较多的问题\n\n### 20180123\n- 使用 `vue-image-crop-upload` 完成头像上传功能，并同时修改状态树中头像的 url\n\n### 20180305\n- 使用 eggjs 重构后端并切换 api\n\n### 20180317\n\n- 使用 [qiniu-webpack-plugin](https://www.npmjs.com/package/qiniu-webpack-plugin) 将打包好的 assets 上传到七牛\n- 由于开启了全站强制 HTTPS，故添加 [asset.raoul1996.cn](https://asset.raoul1996.cn)为七牛云融合 CDN 加速域名\n- 收费服务，慎重\n\n### 20180318\n\n- 添加 favicon，优化性能\n- 重新整理 webpack 配置文件\n\n### 20180322\n\n- 上传七牛增加关闭 map 文件生成选项，节约金钱\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraoul1996%2Fvue-vote","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fraoul1996%2Fvue-vote","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraoul1996%2Fvue-vote/lists"}