{"id":14973676,"url":"https://github.com/meckodo/vue-tutorial","last_synced_at":"2025-04-12T18:46:56.787Z","repository":{"id":127703765,"uuid":"57094522","full_name":"MeCKodo/vue-tutorial","owner":"MeCKodo","description":"vue2.0-tutorial","archived":false,"fork":false,"pushed_at":"2017-03-11T13:38:22.000Z","size":63,"stargazers_count":1137,"open_issues_count":5,"forks_count":272,"subscribers_count":72,"default_branch":"next","last_synced_at":"2025-04-03T21:12:40.108Z","etag":null,"topics":["vue-router","vue2","vuex2"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MeCKodo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-04-26T03:23:58.000Z","updated_at":"2025-03-19T08:10:52.000Z","dependencies_parsed_at":"2023-05-19T16:15:22.854Z","dependency_job_id":null,"html_url":"https://github.com/MeCKodo/vue-tutorial","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/MeCKodo%2Fvue-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MeCKodo%2Fvue-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MeCKodo%2Fvue-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MeCKodo%2Fvue-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MeCKodo","download_url":"https://codeload.github.com/MeCKodo/vue-tutorial/tar.gz/refs/heads/next","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248617696,"owners_count":21134196,"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":["vue-router","vue2","vuex2"],"created_at":"2024-09-24T13:49:12.805Z","updated_at":"2025-04-12T18:46:56.759Z","avatar_url":"https://github.com/MeCKodo.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 前言\n\n我们将会选择使用一些vue周边的库 `vue-cli`, `vue-router`,`vue-resource`,`vuex`\n\n\u003e 1.使用vue-cli创建项目\n\n\u003e 2.使用vue-router实现单页路由\n\n\u003e 3.用vuex管理我们的数据流\n\n\u003e 4.使用vue-resource请求我们的node服务端\n\n\u003e 5.使用.vue文件进行组件化的开发\n\n\u003e PS：本文node v6.2.2 npm v3.9.5 vue v2.1.0 vue-router v2.0.3 vuex v2.0.0 \n\n\u003e 若你要看vue1.0构建单页应用最佳实战 请 前往 master 分支  [vue1.0构建单页应用最佳实战](https://github.com/MeCKodo/vue-tutorial/tree/master)\n\n最终我们将会构建出一个小demo，不废话，直接上图。\n\n\n![](http://7xim8z.com1.z0.glb.clouddn.com/vue2spa-1.png)\n\n\n## 安装\n\n1.我们将会使用webpack去为我们的模块打包，预处理，热加载。如果你对webpack不熟悉，它就是可以帮助我们把多个js文件打包为1个入口文件，并且可以达到按需加载。这就意味着，我们不用担心由于使用太多的组件，导致了过多的HTTP请求，这是非常有益于产品体验的。但我们并不只是为了这个而使用webpack，我们需要用webpack去编译.vue文件，如果没有使用一个loader去转换我们.vue文件里的style、js和html，那么浏览器就无法识别。\n\n2.模块热加载是webpack的一个非常碉堡的特性，将会为我们的单页应用带来极大的便利。\n通常来说，当我们修改了代码刷新页面，那应用里的所有状态就都没有了。这对于开发一个单页应用来说是非常痛苦的，因为需要重新在跑一遍流程。如果有模块热加载，当你修改了代码，你的代码会直接修改，页面并不会刷新，所以状态也会被保留。\n\n3.Vue也为我们提供了CSS预处理，所以我们可以选择在.vue文件里写LESS或者SASS去代替原生CSS。\n\n4.我们过去通常需要使用npm下载一堆的依赖，但是现在我们可以选择Vue-cli。这是一个vue生态系统中一个伟大创举。这意味着我们不需要手动构建我们的项目，而它就可以很快地为我们生成。\n\n首先，安装vue-cli。(确保你有node和npm)\n\n`npm i -g vue-cli` \n\n然后创建一个webpack项目并且下载依赖\n\n`vue init webpack vue-tutorial`\n\n![](http://7xim8z.com1.z0.glb.clouddn.com/vue2spa-0.png)\n\n`cd vue-tutorial`\n\n`npm i`\n\n接着使用 `npm run dev` 在热加载中运行我们的应用\n\n这一行命令代表着它会去找到`package.json`的`scripts`对象，执行`node bulid/dev-server.js`。在这文件里，配置了Webpack，会让它去编译项目文件，并且运行服务器，我们在`localhost:8080`即可查看我们的应用。\n\n![](http://7xim8z.com1.z0.glb.clouddn.com/vue2spa-4.png)\n\n这些都准备好后，我们需要为我们的路由、XHR请求、数据管理下载三个库，我们可以从vue的官网中找到他们。另外我们使用`bootstrap`作为我的UI库\n\n`npm i vue-resource vue-router vuex bootstrap --save`\n\n## 初始化（main.js）\n\n查看我们的应用文件，我们可以在src目录下找到`App.vue`和`main.js`。`main.js`将会作为我们应用的入口文件而`App.vue`会作为我们应用的初始化组件。先让我们来完善下`main.js`\n\n```javascript\n// src/main.js\nimport Vue from 'vue'\nimport VueRouter from 'vue-router'\nimport VueResource from 'vue-resource'\n\nimport App from './App'\nimport Home from './components/Home'\nimport 'bootstrap/dist/css/bootstrap.css'\n\nVue.use(VueRouter)\nVue.use(VueResource)\n\nconst routes = [{\n  path : '/',\n  component : Home\n},{\n  path : '/home',\n  component : Home\n}];\n\nconst router = new VueRouter({\n  routes\n});\n\n/* eslint-disable no-new */\n// 实例化我们的Vue\nvar app = new Vue({\n  el: '#app',\n  router,\n  ...App,\n});\n```\n\n**这有两个与1.0不同的地方**\n\n\u003e 一、`vue-router`路由的参数由对象统一变为了数组要注意。还有则是实例化vue的`el`参数已经不能设置`html`和`body`了，因为在`vue2`中是会替换我们指定的标签\n\n\u003e 二、我们必须在实例化vue的时候指定渲染什么组件，以前我们是通过路由来指定如`router.start(App, '#app')`，而在vue2中则不需要了\n\n可以发现我们在`main.js`里使用了两个组件`App.vue`和`Home.vue`，稍后让我们具体实现它们的内容。\n\n而我们的`index.html`只需要保留`\u003cdiv id=\"app\"\u003e\u003c/div\u003e`即可，我们的Vue在实例化时设置了`el : '#app'` 所以会替换这标签，为我们`App`组件的内容\n\n```html\n//index.html\n\u003cdiv id=\"app\"\u003e\u003c/div\u003e\n```\n\n我们的初始化就到这结束了，接下来让我们开始创建组件。\n\n\n## 创建首页组件\n\n首先我们在App.vue里为我们的应用写个顶部导航。\n\n```js\n// src/App.vue\n\n\u003ctemplate\u003e\n  \u003cdiv id=\"wrapper\"\u003e\n    \u003cnav class=\"navbar navbar-default\"\u003e\n      \u003cdiv class=\"container\"\u003e\n        \u003ca class=\"navbar-brand\" href=\"#\"\u003e\n          \u003ci class=\"glyphicon glyphicon-time\"\u003e\u003c/i\u003e\n          计划板\n        \u003c/a\u003e\n        \u003cul class=\"nav navbar-nav\"\u003e\n          \u003cli\u003e\u003crouter-link to=\"/home\"\u003e首页\u003c/router-link\u003e\u003c/li\u003e\n          \u003cli\u003e\u003crouter-link to=\"/time-entries\"\u003e计划列表\u003c/router-link\u003e\u003c/li\u003e\n        \u003c/ul\u003e\n      \u003c/div\u003e\n    \u003c/nav\u003e\n    \u003cdiv class=\"container\"\u003e\n      \u003cdiv class=\"col-sm-3\"\u003e\n        \n      \u003c/div\u003e\n      \u003cdiv class=\"col-sm-9\"\u003e\n        \u003crouter-view\u003e\u003c/router-view\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n\n```\n\n除了我们的`navbar`以外，我们还需要一个`.container`去放我们其余需要展示的信息。\n并且在这里我们要放一个`router-view`标签，`vue-router`的切换就是通过这个标签开始显现的。\n\n**在这有个与1.0不同的地方**\n\n\u003e 以前我们可以直接通过写a标签 然后写v-link属性进行路由跳转，在vue2中改为了写`\u003crouter-link\u003e`标签再写对应属性（to）进行跳转\n\n\n接着，我们需要创建一个`Home.vue`作为我们的首页\n\n```js\n// src/components/Home.vue\n\n\u003ctemplate\u003e\n  \u003cdiv class=\"jumbotron\"\u003e\n    \u003ch1\u003e任务追踪\u003c/h1\u003e\n    \u003cp\u003e\n      \u003cstrong\u003e\n        \u003crouter-link to=\"/time-entries\"\u003e创建一个任务\u003c/router-link\u003e\n      \u003c/strong\u003e\n    \u003c/p\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n\n不出意外的话，你可以看见如下效果\n\n![](http://7xim8z.com1.z0.glb.clouddn.com/vue-tutorial-1.png)\n\n## 创建侧边栏组件\n\n目前我们首页左侧还有一块空白，我们需要它放下一个侧边栏去统计所有计划的总时间。\n\n```js\n// src/App.vue\n\n  //...\n\n  \u003cdiv class=\"container\"\u003e\n    \u003cdiv class=\"col-sm-3\"\u003e\n      \u003csidebar\u003e\u003c/sidebar\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"col-sm-9\"\u003e\n      \u003crouter-view\u003e\u003c/router-view\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\n  //...\n```\n\n\n```js\n\u003cscript\u003e\n  import Sidebar from './components/Sidebar.vue'\n\n  export default {\n    components: { 'sidebar': Sidebar },\n  }\n\u003c/script\u003e\n```\n\n在`Sidebar.vue`我们需要通过store去获取总时间，我们的总时间是共享的数据\n\n```\n// src/components/Sidebar.vue\n\u003ctemplate\u003e\n  \u003cdiv class=\"panel panel-default\"\u003e\n    \u003cdiv class=\"panel-heading\"\u003e\n      \u003ch1 class=\"text-center\"\u003e已有时长\u003c/h1\u003e\n    \u003c/div\u003e\n\n    \u003cdiv class=\"panel-body\"\u003e\n      \u003ch1 class=\"text-center\"\u003e{{ time }} 小时\u003c/h1\u003e\n    \u003c/div\u003e\n\n  \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript\u003e\n  export default {\n    computed: {\n        time() {\n          return this.$store.state.totalTime\n        }\n      }\n  }\n\u003c/script\u003e\n```\n\n## 创建计划列表组件\n\n然后我们需要去创建我们的时间跟踪列表。\n\n```js\n\n// src/components/TimeEntries.vue\n\n\u003ctemplate\u003e\n  \u003cdiv\u003e\n    //`v-if`是vue的一个指令\n    //`$route.path`是当前路由对象的路径，会被解析为绝对路径当\n    //`$route.path !== '/time-entries/log-time'`为`true`是显示，`false`，为不显示。\n    //to 路由跳转地址\n    \u003crouter-link\n      v-if=\"$route.path !== '/time-entries/log-time'\"\n      to=\"/time-entries/log-time\"\n      class=\"btn btn-primary\"\u003e\n      创建\n    \u003c/router-link\u003e\n\n    \u003cdiv v-if=\"$route.path === '/time-entries/log-time'\"\u003e\n      \u003ch3\u003e创建\u003c/h3\u003e\n    \u003c/div\u003e\n\n    \u003chr\u003e\n\n    \u003crouter-view\u003e\u003c/router-view\u003e\n\n    \u003cdiv class=\"time-entries\"\u003e\n      \u003cp v-if=\"!plans.length\"\u003e\u003cstrong\u003e还没有任何计划\u003c/strong\u003e\u003c/p\u003e\n\n      \u003cdiv class=\"list-group\"\u003e\n      \u003c--\n        v-for循环，注意参数顺序为(item,index) in items\n      --\u003e\n        \u003ca class=\"list-group-item\" v-for=\"(plan,index) in plans\"\u003e\n          \u003cdiv class=\"row\"\u003e\n            \u003cdiv class=\"col-sm-2 user-details\"\u003e\n            \n            \u003c--\n            `:src`属性，这个是vue的属性绑定简写`v-bind`可以缩写为`:`\n             比如a标签的`href`可以写为`:href`\n            并且在vue的指令里就一定不要写插值表达式了(`:src={{xx}}`)，vue自己会去解析\n            --\u003e\n            \n              \u003cimg :src=\"plan.avatar\" class=\"avatar img-circle img-responsive\" /\u003e\n              \u003cp class=\"text-center\"\u003e\n                \u003cstrong\u003e\n                  {{ plan.name }}\n                \u003c/strong\u003e\n              \u003c/p\u003e\n            \u003c/div\u003e\n\n            \u003cdiv class=\"col-sm-2 text-center time-block\"\u003e\n              \u003ch3 class=\"list-group-item-text total-time\"\u003e\n                \u003ci class=\"glyphicon glyphicon-time\"\u003e\u003c/i\u003e\n                {{ plan.totalTime }}\n              \u003c/h3\u003e\n              \u003cp class=\"label label-primary text-center\"\u003e\n                \u003ci class=\"glyphicon glyphicon-calendar\"\u003e\u003c/i\u003e\n                {{ plan.date }}\n              \u003c/p\u003e\n            \u003c/div\u003e\n\n            \u003cdiv class=\"col-sm-7 comment-section\"\u003e\n              \u003cp\u003e{{ plan.comment }}\u003c/p\u003e\n            \u003c/div\u003e\n\n            \u003cdiv class=\"col-sm-1\"\u003e\n              \u003cbutton\n                class=\"btn btn-xs btn-danger delete-button\"\n                @click=\"deletePlan(index)\"\u003e\n              X\n              \u003c/button\u003e\n            \u003c/div\u003e\n\n          \u003c/div\u003e\n        \u003c/a\u003e\n\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n\n关于template的解释，都写在一起了，再看看我们的`script`\n\n```js\n// src/components/TimeEntries.vue\n\n\u003cscript\u003e\n    export default {\n        name : 'TimeEntries',\n        computed : {\n          plans () {\n            // 从store中取出数据\n            return this.$store.state.list\n          }\n        },\n        methods : {\n          deletePlan(idx) {\n            // 稍后再来说这里的方法\n            // 减去总时间\n            this.$store.dispatch('decTotalTime',this.plans[idx].totalTime)\n            // 删除该计划\n            this.$store.dispatch('deletePlan',idx)\n          }\n        }\n    }\n\u003c/script\u003e\n```\n\n别忘了为我们的组件写上一些需要的样式\n```js\n// src/components/TimeEntries.vue\n\n\u003cstyle\u003e\n  .avatar {\n    height: 75px;\n    margin: 0 auto;\n    margin-top: 10px;\n    margin-bottom: 10px;\n  }\n  .user-details {\n    background-color: #f5f5f5;\n    border-right: 1px solid #ddd;\n    margin: -10px 0;\n  }\n  .time-block {\n    padding: 10px;\n  }\n  .comment-section {\n    padding: 20px;\n  }\n\u003c/style\u003e\n```\n\n既然我们的数据是共享的，所以我们需要把数据存在`store`里\n\n我们在src下创建个目录为`store`\n\n在`store`下分别创建4个js文件`actions.js`,`index.js`,`mutation-types.js`,`mutations.js`\n\n看名字也就知道这4个分别是做啥用的了，建议大家多阅读阅读`vuex`的文档，多姿势多动手实践，慢慢的也就能理解了。\n\n```js\n// src/store/index.js\nimport Vue from 'vue'\nimport Vuex from 'vuex'\n\nVue.use(Vuex);\n\n// 先写个假数据\nconst state = {\n  totalTime: 0,\n  list: [{\n    name : '二哲',\n    avatar : 'https://sfault-avatar.b0.upaiyun.com/147/223/147223148-573297d0913c5_huge256',\n    date : '2016-12-25',\n    totalTime : '6',\n    comment : '12月25日晚上，陪女朋友一起过圣诞节需要6个小时'\n  }]\n};\n\nexport default new Vuex.Store({\n  state,\n})\n```\n\n由于新增了页面和store 在我们的入口js文件里配置下\n```javascript\n// src/main.js\nimport store from './store'\nimport TimeEntries from './components/TimeEntries.vue'\n//... \n\nconst routes = [{\n  path : '/',\n  component : Home\n},{\n  path : '/home',\n  component : Home\n},{\n  path : '/time-entries',\n  component : TimeEntries,\n}];\n\nvar app = new Vue({\n  el: '#app',\n  router,\n  store,\n  ...App,\n});\n\n```\n\n不出意外的话，你可以在`/time-entries` 路由下看见这样的页面\n\n![](http://7xim8z.com1.z0.glb.clouddn.com/vue2spa-7.png)\n\n通过`vue-Devtools`我们可以发现我们的store已经构造好了并且成功从store获取了数据\n\n## 创建任务组件\n\n这个比较简单我们直接给出代码\n\n```js\n// src/components/LogTime.vue\n\n\u003ctemplate\u003e\n  \u003cdiv class=\"form-horizontal\"\u003e\n    \u003cdiv class=\"form-group\"\u003e\n      \u003cdiv class=\"col-sm-6\"\u003e\n        \u003clabel\u003e日期\u003c/label\u003e\n        \u003cinput\n          type=\"date\"\n          class=\"form-control\"\n          v-model=\"date\"\n          placeholder=\"Date\"\n        /\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"col-sm-6\"\u003e\n        \u003clabel\u003e时间\u003c/label\u003e\n        \u003cinput\n          type=\"number\"\n          class=\"form-control\"\n          v-model=\"totalTime\"\n          placeholder=\"Hours\"\n        /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"form-group\"\u003e\n      \u003cdiv class=\"col-sm-12\"\u003e\n        \u003clabel\u003e备注\u003c/label\u003e\n        \u003cinput\n          type=\"text\"\n          class=\"form-control\"\n          v-model=\"comment\"\n          placeholder=\"Comment\"\n        /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cbutton class=\"btn btn-primary\" @click=\"save()\"\u003e保存\u003c/button\u003e\n    \u003crouter-link to=\"/time-entries\" class=\"btn btn-danger\"\u003e取消\u003c/router-link\u003e\n    \u003chr\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript\u003e\n  export default {\n        name : 'LogTime',\n        data() {\n            return {\n                date : '',\n                totalTime : '',\n                comment : ''\n            }\n        },\n        methods:{\n          save() {\n            const plan = {\n              name : '二哲',\n              image : 'https://sfault-avatar.b0.upaiyun.com/888/223/888223038-5646dbc28d530_huge256',\n              date : this.date,\n              totalTime : this.totalTime,\n              comment : this.comment\n            };\n            this.$store.dispatch('savePlan', plan)\n            this.$store.dispatch('addTotalTime', this.totalTime)\n            this.$router.go(-1)\n          }\n        }\n    }\n\u003c/script\u003e\n\n```\n\n这个组件很简单就3个input输入而已，然后就两个按钮，保存我们就把数据push进我们store的列表里\n\n`LogTime`属于我们`TimeEntries`组件的一个子路由，所以我们依旧需要配置下我们的路由，并且利用webpack让它懒加载，减少我们首屏加载的流量\n\n```js\n// src/main.js\n//...\nconst routes = [{\n  path : '/',\n  component : Home\n},{\n  path : '/home',\n  component : Home\n},{\n  path : '/time-entries',\n  component : TimeEntries,\n  children : [{\n    path : 'log-time',\n    // 懒加载\n    component : resolve =\u003e require(['./components/LogTime.vue'],resolve),\n  }]\n}];\n\n//...\n```\n\n## vuex部分\n\n**在vue2.0中废除了使用事件的方式进行通信，所以在小项目中我们可以使用Event Bus，其余最好都使用vuex，本文我们使用Vuex来实现数据通信**\n\n相信你刚刚已经看见了我写了很多`this.$store.dispatch('savePlan', plan)` 类似这样的代码，我们再次统一说明。\n\n仔细思考一下，我们需要两个全局数据，一个为所有计划的总时间，一个是计划列表的数组。\n\n`src/store/index.js` 没啥太多可介绍的，其实就是传入我们的`state`,`mutations`,`actions`来初始化我们的Store。如果有需要的话我们还可能需要创建我们的`getter`在本例中就不用了。\n\n接着我们看`mutation-types.js`，既然想很明确了解数据，那就应该有什么样的操作看起，当然这也看个人口味哈\n\n```js\n// src/store/mutation-types.js\n\n// 增加总时间或者减少总时间\nexport const ADD_TOTAL_TIME = 'ADD_TOTAL_TIME';\nexport const DEC_TOTAL_TIME = 'DEC_TOTAL_TIME';\n\n// 新增和删除一条计划\nexport const SAVE_PLAN = 'SAVE_PLAN';\nexport const DELETE_PLAN = 'DELETE_PLAN';\n\n```\n\n```js\n// src/store/mutations.js\nimport * as types from './mutation-types'\n\nexport default {\n    // 增加总时间\n  [types.ADD_TOTAL_TIME] (state, time) {\n    state.totalTime = state.totalTime + time\n  },\n  // 减少总时间\n  [types.DEC_TOTAL_TIME] (state, time) {\n    state.totalTime = state.totalTime - time\n  },\n  // 新增计划\n  [types.SAVE_PLAN] (state, plan) {\n    // 设置默认值，未来我们可以做登入直接读取昵称和头像\n    const avatar = 'https://sfault-avatar.b0.upaiyun.com/147/223/147223148-573297d0913c5_huge256';\n    \n    state.list.push(\n      Object.assign({ name: '二哲', avatar: avatar }, plan)\n    )\n  },\n  // 删除某计划\n  [types.DELETE_PLAN] (state, idx) {\n    state.list.splice(idx, 1);\n  }\n};\n\n```\n最后对应看我们的`actions`就很明白了\n\n```js\n// src/store/actions.js\n\nimport * as types from './mutation-types'\n\nexport default {\n  addTotalTime({ commit }, time) {\n    commit(types.ADD_TOTAL_TIME, time)\n  },\n  decTotalTime({ commit }, time) {\n    commit(types.DEC_TOTAL_TIME, time)\n  },\n  savePlan({ commit }, plan) {\n    commit(types.SAVE_PLAN, plan);\n  },\n  deletePlan({ commit }, plan) {\n    commit(types.DELETE_PLAN, plan)\n  }\n};\n\n```\n我们的`actions`其实就是去触发事件和传入参数啦\n\n加了这三个文件后我们的store终于完整了，更新下我们的代码\n\n```javascript\n// src/store/index.js 完整代码\n\nimport Vue from 'vue'\nimport Vuex from 'vuex'\nimport mutations from './mutations'\nimport actions from './actions'\n\nVue.use(Vuex);\n\nconst state = {\n  totalTime: 0,\n  list: []\n};\n\nexport default new Vuex.Store({\n  state,\n  mutations,\n  actions\n})\n\n```\n\n`this.$store.dispatch('savePlan', plan)`当执行了这样的方法就会调用`actions.js`里的`savePlan`方法，而`savePlan`又会触发 `mutations`里的 `types.SAVE_PLAN` 最后修改数据视图更新\n\n\u003e PS：在这有个技巧就是，在`mutations`里都是用大写下划线连接，而我们的`actions`里都用小写驼峰对应。\n\n个人理解这其实就是一个发布订阅的模式\n\n`mutation-types` 记录我们所有的事件名\n\n`mutations` 注册我们各种数据变化的方法\n\n`actions` 则可以编写异步的逻辑或者是一些逻辑，再去`commit`\n我们的事件\n\n如果有`getter` 我们可以把一些需要处理返回的数据放在这即可，不进行业务操作\n\n最后别忘了在我们的`main.js`里使用我们的`store`\n\n```js\n// src/store/main.js\n\nimport store from './store'\n// ...\n\nvar app = new Vue({\n  el: '#app',\n  router,\n  store,\n  ...App,\n});\n```\n\n开始体验下你自己的任务计划板吧！\n\n## 最后\n\n通过本文，我们可以学习到许多关于vue的特性。\n\n1.了解了vue-cli脚手架\n\n2.初步对webpack有了一些了解和认识\n\n3.如何用.vue愉快的开发\n\n4.使用vuex进行组件通信\n\n5.路由（子路由）的应用\n\n6.使用 vue-devtools 观察我们的数据\n\n---\n\n个人网站 ：http://www.meckodo.com\n\ngithub地址：https://github.com/MeCKodo/vue-tutorial\n\n\u003e Have a nice day\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeckodo%2Fvue-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmeckodo%2Fvue-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeckodo%2Fvue-tutorial/lists"}