当前位置:   article > 正文

用Vue搭建一个应用盒子(一):todo-list

vue 插入一个document创建的盒子
最近在研究vue的相关知识,最好的学习方法莫过于自己开发一个SPA,这样带着问题来学习,进步自然飞速。于是边查边写差不多花了2周写完了一个todo-list,功能不够完备,但是麻雀虽小,却也是五脏俱全,基本功能是可以满足的了。话不多说,直接来看项目吧。

技术栈

  • vue全家桶(vue、vuex、vue-router)
  • webpack实现打包和热加载
  • ES6
  • UI框架用的是bootstrap
  • rem方法完成适配移动端
  • localstorage实现数据的保存
  • node和npm(真是零基础啊,npm都是现学现卖的...)

以上。

接下来就是代码分析了。

一、用vue-cli配置一个项目

这一个步骤没什么好说的,网上教程一大堆,随便找一个照着走就好了。
完成后,你应该有一个项目的文件夹,里面应该有这几个文件:
README.md、build、config、index.html、package.json、src、static
嗯,就这样。

二、安装相关依赖和一堆玩意。

配置vue-router和bootstrap

先安装依赖,命令行到对应根目录文件夹执行如下命令(推荐VS code,自带命令行输入,方便!)

npm install

稍等片刻完成(如果太慢,推进啊淘宝镜像的cnpm安装)
安装好之后,继续安装:

npm install vuex vue-router bootstrap --save

安装完成后,需要配置以下文件,确保能够使用。
打开:xx(项目文件夹)-src-main.js
如下:

  1. import Vue from 'vue'
  2. import VueRouter from 'vue-router'
  3. import App from './App'
  4. import 'bootstrap/dist/css/bootstrap.css'
  5. Vue.use(VueRouter)
  6. const routes=[
  7. {
  8. path:'/',
  9. component:Home
  10. },
  11. {
  12. path:'/todolist',
  13. component:todolist
  14. }
  15. ];
  16. const router=new VueRouter({routes});
  17. /* eslint-disable no-new */
  18. const app=new Vue({
  19. router,
  20. el:'#app',
  21. render:h=>h(App) //ES6语法
  22. })

这里配置了vue-router和bootstrap,项目中可以使用了,接着我们还需要配置vuex和jQuery。

配置vuex和jQuery

首先在根目录创建一个文件夹,命名为vuex,在里面创建一个store.js文件,
配置如下:

  1. import Vue from 'vue'
  2. import Vuex from 'vuex'
  3. Vue.use(Vuex);
  4. const state={
  5. }
  6. const getters={
  7. }
  8. const mutations={
  9. }
  10. export default new Vuex.Store({
  11. state,
  12. getters,
  13. mutations
  14. })
const是ES6的语法,这里getters,state,mutations都不急着用,先配置好。
配置好store.js,回到main.js继续配置。
增加一些内容:
  1. import Vue from 'vue'
  2. import VueRouter from 'vue-router'
  3. import store from './vuex/store'
  4. import App from './App'
  5. import Home from './components/Home.vue'
  6. import todolist from './components/todolist.vue'
  7. import 'bootstrap/dist/css/bootstrap.css'
  8. Vue.use(VueRouter)
  9. const routes=[
  10. {
  11. path:'/',
  12. component:Home
  13. },
  14. {
  15. path:'/todolist',
  16. component:todolist
  17. }
  18. ];
  19. const router=new VueRouter({routes});
  20. /* eslint-disable no-new */
  21. const app=new Vue({
  22. router,
  23. store,
  24. el:'#app',
  25. render:h=>h(App) //ES6语法
  26. })

好了,vuex就配置完了。接着我们配置JQuery,因为bootstrap依赖JQuery,所以这里也必须放上去。
老规矩,先用npm安装JQuery。

npm install jquery --save

打开xx-build-webpack-base.conf.js,在module.exports里面添加如下代码:

  1. plugins:[
  2. new webpack.optimize.CommonsChunkPlugin('common.js'),
  3. new webpack.ProvidePlugin({
  4. jQuery: "jquery",
  5. $: "jquery"
  6. })
  7. ]

打开main.js配置JQuery和bootstrap的动效。
添加一点内容:

  1. import $ from 'jquery'
  2. import 'bootstrap/dist/js/bootstrap.min.js'

OK,至此,所有的前期配置就完成了,可以开始正式的代码书写了。

三、组件结构和实现

Vue最碉堡的地方就是它的组件式开发,所以这个思想是我们在写代码式要时刻注意的,如何合理的划分自己的组件,是一件很需要思考的事,接下来我将详细介绍我的组件内容和实现的功能。

下面是我的组件结构:
在src文件夹里,有一个主组件:app.vue,有一个组件文件夹:conponents,在这里面我放了4个组件,如下:
Home.vue ———— 首页
todolist.vue ———— todolist 应用主页面
sidebar.vue ———— todolist任务列表
editor.vue ———— todolist任务编辑
我会一个个介绍功能。

app.vue

在首页里,我们会用bootstrap写一个导航,通过vue-router的路由导航到不同的应用。
代码如下:

  1. <template>
  2. <div id="app">
  3. <!--nav start-->
  4. <nav class="navbar navbar-default">
  5. <div class="container-fluid">
  6. <div class="navbar-header">
  7. <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
  8. <span class="sr-only">Toggle navigation</span>
  9. <span class="icon-bar"></span>
  10. <span class="icon-bar"></span>
  11. <span class="icon-bar"></span>
  12. </button>
  13. <a class="navbar-brand" href="#"><i class="glyphicon glyphicon-home"></i></a>
  14. </div>
  15. <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
  16. <ul class="nav navbar-nav">
  17. <li><router-link to="/todolist">Todo List</router-link></li>
  18. <li><a href="#">开发中...</a></li>
  19. </ul>
  20. </div><!-- /.navbar-collapse -->
  21. </div><!-- /.container-fluid -->
  22. </nav>
  23. <!--nav end-->
  24. <!--content-->
  25. <router-view></router-view>
  26. </div>
  27. </template>
  28. <script>
  29. export default {
  30. name: 'app',
  31. data(){
  32. return{
  33. }
  34. }
  35. }
  36. </script>
  37. <style>
  38. </style>

首页的上部分是一个导航,导航的UI和样式用的是bootstrap,导航用路由实现链接到不同的应用,要注意的是,不同的应用我们用不同的组件封装,比如这个待办事项的应用,我们用的是todolist.vue。还要注意的是,这些组件的注册和路由链接都需要在main.js中配置。不要忘记了。
返回查看main.js看看代码是怎么写的。
配置完后,我们的主页面上只有一个导航。接着我们配置主页。

Home.vue

我们在xx-src-components文件夹里创建一个新的组件:Home.vue。这个组件是我们的首页内容,这里我放了一张图,和一句话:欢迎!这里有你需要的App。这里同样用到了bootstrap的栅格系统,这样就可以兼容移动端了。
看代码:

  1. <template>
  2. <div class="Home">
  3. <div class="container">
  4. <div class="col-sm-8">
  5. <div class="jumbotron">
  6. <img src="../assets/home-l-img.jpg">
  7. </div>
  8. </div>
  9. <div class="col-sm-4">
  10. <div class="jumbotron">
  11. <h2>欢迎你!</h2>
  12. <p>这里有你需要的app</p>
  13. </div>
  14. </div>
  15. </div>
  16. </div>
  17. </template>
  18. <script>
  19. export default {
  20. name: 'Home',
  21. data () {
  22. return {
  23. }
  24. }
  25. }
  26. </script>
  27. <!-- Add "scoped" attribute to limit CSS to this component only -->
  28. <style scoped>
  29. .col-sm-8 .jumbotron{
  30. padding: 0;
  31. }
  32. .jumbotron img{
  33. width: 100%;
  34. }
  35. </style>

代码不复杂,就不解释了。接下来就是重头戏了。

todolist.vue

这是这个应用的主组件,在这个组件里还会包含两个子组件:sidebar.vue和editor.vue,所以在这个组件里,我们要实现的是一个新建任务的功能。新建的任务会显示在左边的任务列表sidebar.vue组件里,然后点击某个任务的编辑按钮,我们可以在右边的任务编辑editor.vue组件里修改。这就是这个应用的架构思路。下面来看看代码:

  1. <template>
  2. <div class="todolist">
  3. <div class="container addInput">
  4. <input type="text" v-model="taskText" class="form-control col-sm-12" placeholder="新建一个任务" v-on:keyup.enter="addTask">
  5. </div>
  6. <div class="container">
  7. <div class="col-sm-6">
  8. <sidebar></sidebar>
  9. </div>
  10. <div class="col-sm-6">
  11. <editor></editor>
  12. </div>
  13. </div>
  14. </div>
  15. </template>
  16. <script>
  17. import sidebar from './sidebar.vue'
  18. import editor from './editor.vue'
  19. export default {
  20. name: 'todolist',
  21. data(){
  22. return{
  23. taskText:''
  24. }
  25. },
  26. components: {
  27. sidebar,
  28. editor
  29. },
  30. methods:{
  31. addTask(){
  32. if(this.taskText==''){
  33. alert('请输入具体任务内容!')
  34. }else{
  35. this.$store.commit('addTask',this.taskText);
  36. this.taskText=''
  37. }
  38. }
  39. }
  40. }
  41. </script>
  42. <style scoped>
  43. .addInput {
  44. margin-bottom: .75rem;
  45. }
  46. </style>

代码量不算大,除了一个输入框之外就是两个组件的标签<sidebar></sidebar>、<editor></editor>了。这里会有一个commit(),它里面引号的内容是一个函数,这个函数在store.js里面的mutations里编写。
要注意的点:
1.两个子组件要在父组件里面注册引入才能使用。
2.这里开始涉及到了Vuex的功能,简单说明一下,我们在输入任务后,这个任务的相关数据会被保存到状态管理store里面,然后通过mutations的操作,把输入的内容保存在子组件sidebar里。

显然设置完这里还是无法使用新建任务的,我们还需要两步操作。

第一步:设置sidebar.vue:
这个子组件是完成任务列表的渲染。看代码:

  1. <template>
  2. <div class="sidebar">
  3. <div class="panel panel-default">
  4. <div class="panel-heading">
  5. <h4 class="text-left">
  6. 任务列表
  7. <i class="glyphicon glyphicon-refresh pull-right"></i>
  8. </h4>
  9. </div>
  10. <div class="panel-body sidebar-context">
  11. <div>
  12. <div v-for="(item,index) in items" class="panel panel-default">
  13. <div class="panel-heading">
  14. <h4>
  15. <input type="checkbox">
  16. {{item.task}}
  17. <i class="glyphicon glyphicon-remove pull-right"></i>
  18. <i class="glyphicon glyphicon-edit pull-right"></i>
  19. </h4>
  20. </div>
  21. <div class="panel-body">
  22. <p>
  23. {{item.setTime}}<br>
  24. {{item.details}}
  25. </p>
  26. </div>
  27. </div>
  28. </div>
  29. </div>
  30. </div>
  31. </div>
  32. </template>
  33. <script>
  34. export default {
  35. name: 'sidebar',
  36. data() {
  37. return {
  38. }
  39. },
  40. computed:{
  41. items(){
  42. return this.$store.getters.items;
  43. }
  44. },
  45. methods:{
  46. }
  47. }
  48. </script>
  49. <!-- Add "scoped" attribute to limit CSS to this component only -->
  50. <style scoped>
  51. ul {
  52. list-style: none;
  53. }
  54. .sidebar-context{
  55. max-height: 8rem;
  56. overflow: scroll;
  57. }
  58. i.glyphicon{
  59. font-size: .275rem;
  60. cursor: pointer;
  61. margin-left: .25rem;
  62. }
  63. </style>

可以仔细考虑一下代码的书写,在<script> 里设置computed,否则保存在store.js的值无法输出到子组件sidebar.vue里。

第二步,设置store.js:

  1. import Vue from 'vue'
  2. import Vuex from 'vuex'
  3. Vue.use(Vuex);
  4. const state={
  5. items:[]
  6. }
  7. const getters={
  8. items:state=>state.items
  9. }
  10. const mutations={
  11. addTask(state,task){
  12. var myDate=new Date();
  13. var y=myDate.getFullYear();
  14. var m,
  15. mm=myDate.getMonth()+1;
  16. if(mm<10){
  17. m="0"+mm;
  18. }else{
  19. m=mm;
  20. }
  21. var d,
  22. dd=myDate.getDate();
  23. if(dd<10){
  24. d="0"+dd;
  25. }else{
  26. d=dd;
  27. }
  28. var currentTime=y+m+d;
  29. state.items.push({
  30. task,
  31. isFinished:false,
  32. details:"this is a new task",
  33. setTime:currentTime
  34. })
  35. }
  36. }
  37. export default new Vuex.Store({
  38. state,
  39. getters,
  40. mutations
  41. })

这一段代码,我们会把输入的值作为一项新建任务的task值保存,并渲染到sidebar上,当然,作为一项待办任务,只有名称是不够的,所以我们为它添加了默认的描述details和默认的完成时间setTime,setTime是当前日期。

看到commit相关的函数了吗?

OK,至此,这个todolist应用最基本的新建任务就完成了。

editor.vue

接着,我们完成编辑、删除、标记已完成任务的功能。编辑功能在editor.vue组件內操作,删除功能则在sidebar.vue里面完成即可。让我们先编辑sidebar.vue。
下面这一段代码我会一次性增加删除编辑和编辑已完成的功能,你可以先敲出来,思考一下原理,也可以跳过这一段,跟着后面的步骤一步步添加功能。

  1. <template>
  2. <div class="sidebar">
  3. <div class="panel panel-default">
  4. <div class="panel-heading">
  5. <h4 class="text-left">
  6. 任务列表
  7. <i class="glyphicon glyphicon-refresh pull-right" v-on:click="reList"></i>
  8. </h4>
  9. </div>
  10. <div class="panel-body sidebar-context">
  11. <div>
  12. <div v-for="(item,index) in items" class="panel panel-default">
  13. <div class="panel-heading">
  14. <h4>
  15. <input @click="itemCheck(index)" type="checkbox">
  16. {{item.task}}
  17. <i class="glyphicon glyphicon-remove pull-right" v-on:click="deleteTask(index)"></I>
  18. <i class="glyphicon glyphicon-edit pull-right" v-on:click="clickTask(item)"></i>
  19. </h4>
  20. </div>
  21. <div class="panel-body">
  22. <p>
  23. {{item.setTime}}<br>
  24. {{item.details}}
  25. </p>
  26. </div>
  27. </div>
  28. </div>
  29. </div>
  30. </div>
  31. </div>
  32. </template>
  33. <script>
  34. export default {
  35. name: 'sidebar',
  36. data() {
  37. return {
  38. }
  39. },
  40. computed:{
  41. items(){
  42. return this.$store.getters.items;
  43. },
  44. activeTask(){
  45. return this.$store.getters.activeTask;
  46. }
  47. },
  48. methods:{
  49. deleteTask(index){
  50. this.$store.commit('deleteTask',index);
  51. },
  52. itemCheck(index){
  53. this.$store.commit('toggleCheck',index);
  54. },
  55. clickTask(item){
  56. this.$store.commit('setActivetask',item);
  57. },
  58. reList(){
  59. this.$store.commit('reList')
  60. }
  61. }
  62. }
  63. </script>
  64. <!-- Add "scoped" attribute to limit CSS to this component only -->
  65. <style scoped>
  66. ul {
  67. list-style: none;
  68. }
  69. .sidebar-context{
  70. max-height: 8rem;
  71. overflow: scroll;
  72. }
  73. i.glyphicon{
  74. font-size: .275rem;
  75. cursor: pointer;
  76. margin-left: .25rem;
  77. }
  78. </style>

在这里,我一次性把编辑、删除、标记已完成任务的功能三个功能都加上了(偷懒...)我会一个个来解释。

1.删除
看名字能看的出来,我在一个删除图标(还是bootstrap啦~)上绑定了一个点击事件deleteTask,里面的内容是执行store.js里一个deleteTask的函数,同时传入一个index的参数。而在相关的store.js里,我们在mutations里添加deleteTask函数相关的代码:

  1. deleteTask(state,index){
  2. state.items.splice(index,1)
  3. }

删除对应的数组数据。也就是删除指定index的相关数据。任务就搞定了。

2.编辑
这是一个难点。涉及到数据在兄弟组件之间的交换,我们仍然使用vuex。

先说说思路,我们点击sidebar组件里的编辑按钮(删除旁边的按钮),然后把这个任务设置为活动任务,然后显示在右边的编辑组件editor上。在editor上编辑完成后,点击完成按钮,修改的任务被更新到左边的任务列表sidebar上。

好了,看上面sidebar.vue的代码,我们先要完成设置活动任务(activeTask):
computed里面需要设置方法:

  1. activeTask(){
  2. return this.$store.getters.activeTask;
  3. }

然后在编辑按钮上绑定点击事件,把当前的任务设置为活动任务:

  1. clickTask(item){
  2. this.$store.commit('setActivetask',item);
  3. }

sidebar.vue里的内容就编辑完了。接着我们需要在components文件夹创建一个组件editor.vue(忘记前面有没有创建了...),开始写这一段的代码:

这里需要提前声明的是,下面这一段的代码是我最没把握的代码,因为我不确定这一段是不是有效率的,但是它肯定是能跑起来的。

  1. <template>
  2. <div class="sidebar">
  3. <div class="panel panel-default">
  4. <div class="panel-heading">
  5. <h3 class="text-left">任务编辑</h3>
  6. </div>
  7. <div class="panel-body">
  8. <p>任务名称</p>
  9. <input type="text" class="form-control task-input" placeholder="任务名称" v-bind:value="task" v-on:input="saveTask">
  10. <p>任务详情</p>
  11. <textarea class="form-control details-input" rows="3" placeholder="任务详情" v-bind:value="details" v-on:input="saveDetails"></textarea>
  12. <p>任务期限</p>
  13. <input type="text" class="form-control settime-input" placeholder="格式:20170606" v-bind:value="setTime" v-on:input="saveSettime">
  14. <h3><i class="glyphicon glyphicon-ok pull-right" v-on:click="save"></i></h3>
  15. </div>
  16. </div>
  17. </div>
  18. </template>
  19. <script>
  20. export default {
  21. name: 'editor',
  22. data() {
  23. return {
  24. }
  25. },
  26. computed:{
  27. items(){
  28. return this.$store.getters.items;
  29. },
  30. task(){
  31. this.taskInput=this.$store.getters.activeTask.task;
  32. return this.$store.getters.activeTask.task;
  33. },
  34. details(){
  35. this.detailsInput=this.$store.getters.activeTask.details;
  36. return this.$store.getters.activeTask.details;
  37. },
  38. setTime(){
  39. this.settimeInput=this.$store.getters.activeTask.setTime;
  40. return this.$store.getters.activeTask.setTime;
  41. }
  42. },
  43. methods:{
  44. saveTask(e){
  45. this.taskInput=e.target.value;
  46. },
  47. saveDetails(e){
  48. this.detailsInput=e.target.value;
  49. },
  50. saveSettime(e){
  51. this.settimeInput=e.target.value;
  52. },
  53. save(){
  54. this.$store.commit('editTask',this.taskInput);
  55. this.$store.commit('editDetails',this.detailsInput);
  56. this.$store.commit('editSettime',this.settimeInput);
  57. this.$store.commit('clearAll');
  58. }
  59. }
  60. }
  61. </script>
  62. <!-- Add "scoped" attribute to limit CSS to this component only -->
  63. <style scoped>
  64. input,textarea{
  65. margin-bottom: .3rem;
  66. }
  67. i{
  68. cursor: pointer;
  69. margin-right: .2rem;
  70. }
  71. </style>

说明如下:
1.computed里的方法分别对应的是items任务数组(包含所有建立的任务数据)、task任务名称数据、details任务描述数据、setTime任务时间数据。这几个方法,除了第一个,其他的功能都是在对应的input表单显示活动任务(activeTask)的值。

2.methods里的方法,前三个都是在对应的input输入新值时触发事件,它会把新输入的值分别保存在一个地方。(比如task值就会保存在this.taskInput,taskInput是类名为task-input的input,其他两个以此类推)第四个save(),会把前面保存的三个值赋给活动任务。这也是我不敢确定的地方,因为代码这样写,有多少个不同的值就要用多少个函数,很不环保。

这些commit里的函数都放在store.js的mutations里,下面再说。

以上就把editor.vue的代码编辑完了,接着编辑store.js。
我们添加一下内容:

  1. import Vue from 'vue'
  2. import Vuex from 'vuex'
  3. Vue.use(Vuex);
  4. const state={
  5. items:[],
  6. activeTask:{}
  7. }
  8. const getters={
  9. items:state=>state.items,
  10. activeTask:state=>state.activeTask
  11. }
  12. const mutations={
  13. addTask(state,task){
  14. var myDate=new Date();
  15. var y=myDate.getFullYear();
  16. var m,
  17. mm=myDate.getMonth()+1;
  18. if(mm<10){
  19. m="0"+mm;
  20. }else{
  21. m=mm;
  22. }
  23. var d,
  24. dd=myDate.getDate();
  25. if(dd<10){
  26. d="0"+dd;
  27. }else{
  28. d=dd;
  29. }
  30. var currentTime=y+m+d;
  31. state.items.push({
  32. task,
  33. isFinished:false,
  34. details:"this is a new task",
  35. setTime:currentTime
  36. })
  37. },
  38. deleteTask(state,index){
  39. state.items.splice(index,1)
  40. },
  41. setActivetask(state,item){
  42. state.activeTask=item
  43. },
  44. editTask(state,task){
  45. state.activeTask.task=task;
  46. for(let i in state.items){
  47. if(i==state.activeTask){
  48. i.task=task;
  49. }
  50. }
  51. },
  52. editDetails(state,details){
  53. state.activeTask.details=details;
  54. for(let i in state.items){
  55. if(i==state.activeTask){
  56. i.details=details;
  57. }
  58. }
  59. },
  60. editSettime(state,settime){
  61. state.activeTask.setTime=settime;
  62. for(let i in state.items){
  63. if(i==state.activeTask){
  64. i.setTime=settime;
  65. }
  66. }
  67. }
  68. }
  69. export default new Vuex.Store({
  70. state,
  71. getters,
  72. mutations
  73. })

ok,主要是在state和getters里添加了活动任务,mutations里添加了前面提到的相关函数,对比一下看看有没有遗漏。

到这里,这个todolist最核心的任务就完成了。但是我们还需要一些细节:
1.修改任务后,把右边的活动任务清空,回复初始状态。
2.修改任务的完成时间后,需要知道哪些任务更加紧急,需要让任务列表重新排序。
3.修改任务完成状态。

我们来依次完成:
1.这个比较简单,我们只要修改完后点击完成按钮时触发一个设置活动任务(activeTask)为空的方法就好了。
在editor.vue组件的save()方法添加:

this.$store.commit('clearAll');

在store.js的mutations添加:

  1. clearAll(state){
  2. state.activeTask={};
  3. },

done!

2.我们在左边的任务列表上放一个刷新按钮,点击时,会根据任务数据的setTime属性重新排序,时间近的在前面,时间远的在后面。
sidebar.vue的methods添加:

  1. reList(){
  2. this.$store.commit('reList')
  3. }

在store.js的mutations添加:

  1. reList(state){
  2. function compare(propertyName){
  3. return function(obj1,obj2){
  4. var value1=obj1[propertyName];
  5. var value2=obj2[propertyName];
  6. if(value2<value1){
  7. return 1;
  8. }else if(value2>value1){
  9. return -1;
  10. }else{
  11. return 0;
  12. }
  13. }
  14. }
  15. state.items.sort(compare('setTime'));
  16. }

这里用到了一个基础的比较大小重新排序的功能。done!

3.这个也不复杂,我们在任务列表组件sidebar.vue组件的多选框上添加点击事件,methods里添加:

  1. itemCheck(index){
  2. this.$store.commit('toggleCheck',index);
  3. },

在store.js的mutations添加:

  1. toggleCheck(state,index){
  2. state.items[index].isFinished=!state.items[index].isFinished
  3. },

因为在创建任务时,我们会添加一个isFinished的属性,默认为false,通过以上方法,我们就可以切换isFinished的值,也就是切换任务的是否完成状态。搞定。

到这里,这个todo-list基本就算完成了。

四、适配移动端和储存功能

接下来还有一些需要调整的,首先是移动端的适配,我们要先打开根目录的index.html,添加一下内容:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name = "viewport" content = "width = device-width, initial-scale = 1.0, maximum-scale = 1.0, user-scalable = 0" />
  6. <title>todolist</title>
  7. </head>
  8. <body>
  9. <div id="app"></div>
  10. <!-- 下面是我添加的 -->
  11. <script>
  12. let html = document.documentElement;
  13. window.rem = html.getBoundingClientRect().width / 25 ;
  14. html.style.fontSize = window.rem + 'px';
  15. </script>
  16. </body>
  17. </html>

添加meta和一段JS完成bootstrap的适应和rem方法自适应大小。

第二是localstorage实现存储。毕竟是一个todolist应用,每次打开都要重新建任务怎么行?
这里我用了一个vue的localstorage插件:地址
npm安装插件:

npm install vue-localstorage --save

store.js配置:

  1. import Vue from 'vue'
  2. import Vuex from 'vuex'
  3. Vue.use(Vuex);
  4. import VueLocalStorage from 'vue-localstorage'
  5. Vue.use(VueLocalStorage)
  6. const STORAGE_KEY='myapp-todolist'
  7. const state={
  8. items:JSON.parse(window.localStorage.getItem(STORAGE_KEY) || '[]'),
  9. activeTask:{}
  10. }
  11. const getters={
  12. items:state=>state.items,
  13. activeTask:state=>state.activeTask
  14. }
  15. const mutations={
  16. addTask(state,task){
  17. var myDate=new Date();
  18. var y=myDate.getFullYear();
  19. var m,
  20. mm=myDate.getMonth()+1;
  21. if(mm<10){
  22. m="0"+mm;
  23. }else{
  24. m=mm;
  25. }
  26. var d,
  27. dd=myDate.getDate();
  28. if(dd<10){
  29. d="0"+dd;
  30. }else{
  31. d=dd;
  32. }
  33. var currentTime=y+m+d;
  34. state.items.push({
  35. task,
  36. isFinished:false,
  37. details:"this is a new task",
  38. setTime:currentTime
  39. })
  40. },
  41. deleteTask(state,index){
  42. state.items.splice(index,1)
  43. },
  44. toggleCheck(state,index){
  45. state.items[index].isFinished=!state.items[index].isFinished
  46. },
  47. setActivetask(state,item){
  48. state.activeTask=item
  49. },
  50. editTask(state,task){
  51. state.activeTask.task=task;
  52. for(let i in state.items){
  53. if(i==state.activeTask){
  54. i.task=task;
  55. }
  56. }
  57. },
  58. editDetails(state,details){
  59. state.activeTask.details=details;
  60. for(let i in state.items){
  61. if(i==state.activeTask){
  62. i.details=details;
  63. }
  64. }
  65. },
  66. editSettime(state,settime){
  67. state.activeTask.setTime=settime;
  68. for(let i in state.items){
  69. if(i==state.activeTask){
  70. i.setTime=settime;
  71. }
  72. }
  73. },
  74. clearAll(state){
  75. state.activeTask={};
  76. },
  77. reList(state){
  78. function compare(propertyName){
  79. return function(obj1,obj2){
  80. var value1=obj1[propertyName];
  81. var value2=obj2[propertyName];
  82. if(value2<value1){
  83. return 1;
  84. }else if(value2>value1){
  85. return -1;
  86. }else{
  87. return 0;
  88. }
  89. }
  90. }
  91. state.items.sort(compare('setTime'));
  92. }
  93. }
  94. const localStoragePlugin = store=>{
  95. store.subscribe((mutation,{items})=>{
  96. window.localStorage.setItem(STORAGE_KEY,JSON.stringify(items))
  97. })
  98. }
  99. export default new Vuex.Store({
  100. state,
  101. getters,
  102. mutations,
  103. plugins:[localStoragePlugin]
  104. })

要改的解放并不多,主要是把任务数据的数组转换成localstorage的格式。
完成了!撒花~~

五、源码和参考文献

这个todo-list实在是简陋不堪,但是基础不好的我也差不多花了2个星期才写完,在写这个应用的时候我是抱着诚恳的态度的,所以也确实花了很多的心血,包括学习命令行,npm、搭建项目脚手架、查找资料,中间的磨练和挫折也不必多说,一切的辛苦都在看着这个应用能够运行的那一刻烟消云散了。

写了一篇这么长的,我尽量用我认为最明白清楚的语言来表达,中间肯定有很多错误和疏漏,欢迎大神拍砖、指点。写这篇blog的目的也就是重新梳理一下代码的思路,我是抱着学习的态度来写的。中间的代码有很多重复的地方,在大神看来,无疑是有着占篇幅的嫌疑,但也是希望能更加清楚地表述,方便新人理解。我也是刚入此门,十分能理解代码不详细带来的困惑。

在起名字的时候,我没有直接说这是一个todo-list,而说是一个应用盒子,是因为我想把它作为一个我的代表项目,里面会集成许多的应用,todolist、音乐、计算器等等,一方面练手,一方面攒一个拿得出手的项目,方便以后求职。

说了这么多废话,下面是干货:
todo-list源码(我的github,欢迎star,fork)
Vue2.0 新手完全填坑攻略(如果你还没学过Vue,先看这个入门)
vue2.0 构建单页应用最佳实战(bootstrap+vue架构一个todolist)
Vue.js+Vuex:一个简单的记事本(介绍vuex项目实战最清楚的教程)
当然,官方文档也是非常重要的,看着教程你可以写出需要的代码,但是只有看着文档,你才能知道为什么要这样写。
vue官方文档
vuex官方文档

that's all~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/69840
推荐阅读
相关标签
  

闽ICP备14008679号