用Vue搭建一個應用盒子(一):todo-list

最近在研究vue的相關知識,最好的學習方法莫過於本身開發一個SPA,這樣帶着問題來學習,進步天然飛速。因而邊查邊寫差很少花了2周寫完了一個todo-list,功能不夠完備,可是麻雀雖小,卻也是五臟俱全,基本功能是能夠知足的了。話很少說,直接來看項目吧。

技術棧

  • vue全家桶(vue、vuex、vue-router)
  • webpack實現打包和熱加載
  • ES6
  • UI框架用的是bootstrap
  • rem方法完成適配移動端
  • localstorage實現數據的保存
  • node和npm(真是零基礎啊,npm都是現學現賣的...)

以上。css

接下來就是代碼分析了。html

1、用vue-cli配置一個項目

這一個步驟沒什麼好說的,網上教程一大堆,隨便找一個照着走就行了。
完成後,你應該有一個項目的文件夾,裏面應該有這幾個文件:
README.md、build、config、index.html、package.json、src、static
嗯,就這樣。vue

2、安裝相關依賴和一堆玩意。

配置vue-router和bootstrap

先安裝依賴,命令行到對應根目錄文件夾執行以下命令(推薦VS code,自帶命令行輸入,方便!)node

npm install

稍等片刻完成(若是太慢,推動啊淘寶鏡像的cnpm安裝)
安裝好以後,繼續安裝:jquery

npm install vuex vue-router bootstrap --save

安裝完成後,須要配置如下文件,確保可以使用。
打開:xx(項目文件夾)-src-main.js
以下:webpack

import Vue from 'vue'
import VueRouter from 'vue-router'

import App from './App'

import 'bootstrap/dist/css/bootstrap.css'

Vue.use(VueRouter)

const routes=[
  {
    path:'/',
    component:Home
  },
  {
    path:'/todolist',
    component:todolist
  }
];

const router=new VueRouter({routes});

/* eslint-disable no-new */

const app=new Vue({
  router,
  el:'#app',
  render:h=>h(App)   //ES6語法 
})

這裏配置了vue-router和bootstrap,項目中可使用了,接着咱們還須要配置vuex和jQuery。git

配置vuex和jQuery

首先在根目錄建立一個文件夾,命名爲vuex,在裏面建立一個store.js文件,
配置以下:github

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

const state={
    
}
const getters={
    
}
const mutations={
   
}

export default new Vuex.Store({
  state,
  getters,
  mutations
})
const是ES6的語法,這裏getters,state,mutations都不急着用,先配置好。
配置好store.js,回到main.js繼續配置。
增長一些內容:
import Vue from 'vue'
import VueRouter from 'vue-router'
import store from './vuex/store'

import App from './App'
import Home from './components/Home.vue'
import todolist from './components/todolist.vue'

import 'bootstrap/dist/css/bootstrap.css'

Vue.use(VueRouter)

const routes=[
  {
    path:'/',
    component:Home
  },
  {
    path:'/todolist',
    component:todolist
  }
];

const router=new VueRouter({routes});

/* eslint-disable no-new */

const app=new Vue({
  router,
  store,
  el:'#app',
  render:h=>h(App)   //ES6語法 
})

好了,vuex就配置完了。接着咱們配置JQuery,由於bootstrap依賴JQuery,因此這裏也必須放上去。
老規矩,先用npm安裝JQuery。web

npm install jquery --save

打開xx-build-webpack-base.conf.js,在module.exports裏面添加以下代碼:vue-router

plugins:[
    new webpack.optimize.CommonsChunkPlugin('common.js'),
    new webpack.ProvidePlugin({
         jQuery: "jquery",
         $: "jquery"
    })
  ]

打開main.js配置JQuery和bootstrap的動效。
添加一點內容:

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

OK,至此,全部的前期配置就完成了,能夠開始正式的代碼書寫了。

3、組件結構和實現

Vue最碉堡的地方就是它的組件式開發,因此這個思想是咱們在寫代碼式要時刻注意的,如何合理的劃分本身的組件,是一件很須要思考的事,接下來我將詳細介紹個人組件內容和實現的功能。

下面是個人組件結構:
在src文件夾裏,有一個主組件:app.vue,有一個組件文件夾:conponents,在這裏面我放了4個組件,以下:
Home.vue ———— 首頁
todolist.vue ———— todolist 應用主頁面
sidebar.vue ———— todolist任務列表
editor.vue ———— todolist任務編輯
我會一個個介紹功能。

app.vue

在首頁裏,咱們會用bootstrap寫一個導航,經過vue-router的路由導航到不一樣的應用。
代碼以下:

<template>
  <div id="app">
    <!--nav start-->
    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#"><i class="glyphicon glyphicon-home"></i></a>
        </div>
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="nav navbar-nav">
            <li><router-link to="/todolist">Todo List</router-link></li>
            <li><a href="#">開發中...</a></li>
          </ul>
        </div><!-- /.navbar-collapse -->
      </div><!-- /.container-fluid -->
    </nav>
    <!--nav end-->

    <!--content-->
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'app',
  data(){
    return{
      
    }
  }
}
</script>

<style>
</style>

首頁的上部分是一個導航,導航的UI和樣式用的是bootstrap,導航用路由實現連接到不一樣的應用,要注意的是,不一樣的應用咱們用不一樣的組件封裝,好比這個待辦事項的應用,咱們用的是todolist.vue。還要注意的是,這些組件的註冊和路由連接都須要在main.js中配置。不要忘記了。
返回查看main.js看看代碼是怎麼寫的。
配置完後,咱們的主頁面上只有一個導航。接着咱們配置主頁。

Home.vue

咱們在xx-src-components文件夾裏建立一個新的組件:Home.vue。這個組件是咱們的首頁內容,這裏我放了一張圖,和一句話:歡迎!這裏有你須要的App。這裏一樣用到了bootstrap的柵格系統,這樣就能夠兼容移動端了。
看代碼:

<template>
  <div class="Home">
    <div class="container">
      <div class="col-sm-8">
        <div class="jumbotron">
          <img src="../assets/home-l-img.jpg">
        </div>
      </div>
      <div class="col-sm-4">
        <div class="jumbotron">
          <h2>歡迎你!</h2>
          <p>這裏有你須要的app</p>
        </div>
      </div>
    </div>
    
  </div>
</template>

<script>
export default {
  name: 'Home',
  data () {
    return {
      
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  .col-sm-8 .jumbotron{
    padding: 0;
  }
  .jumbotron img{
    width: 100%;
  }
</style>

代碼不復雜,就不解釋了。接下來就是重頭戲了。

todolist.vue

這是這個應用的主組件,在這個組件裏還會包含兩個子組件:sidebar.vue和editor.vue,因此在這個組件裏,咱們要實現的是一個新建任務的功能。新建的任務會顯示在左邊的任務列表sidebar.vue組件裏,而後點擊某個任務的編輯按鈕,咱們能夠在右邊的任務編輯editor.vue組件裏修改。這就是這個應用的架構思路。下面來看看代碼:

<template>
  <div class="todolist">
    <div class="container addInput">
      <input type="text" v-model="taskText" class="form-control col-sm-12" placeholder="新建一個任務" v-on:keyup.enter="addTask">
    </div>
    <div class="container">
      <div class="col-sm-6">
        <sidebar></sidebar>
      </div>
      <div class="col-sm-6">
        <editor></editor>
      </div>
    </div>
  </div>
</template>

<script>
import sidebar from './sidebar.vue'
import editor from './editor.vue'

export default {
  name: 'todolist',
  data(){
    return{
      taskText:''
    }
  },
  components: {
    sidebar,
    editor
  },
  methods:{
    addTask(){
      if(this.taskText==''){
        alert('請輸入具體任務內容!')
      }else{
        this.$store.commit('addTask',this.taskText);
        this.taskText=''
      }
    }
  }
}
</script>
  

<style scoped>
  .addInput {
    margin-bottom: .75rem;
  }
</style>

代碼量不算大,除了一個輸入框以外就是兩個組件的標籤<sidebar></sidebar>、<editor></editor>了。這裏會有一個commit(),它裏面引號的內容是一個函數,這個函數在store.js裏面的mutations裏編寫。
要注意的點:
1.兩個子組件要在父組件裏面註冊引入才能使用。
2.這裏開始涉及到了Vuex的功能,簡單說明一下,咱們在輸入任務後,這個任務的相關數據會被保存到狀態管理store裏面,而後經過mutations的操做,把輸入的內容保存在子組件sidebar裏。

顯然設置完這裏仍是沒法使用新建任務的,咱們還須要兩步操做。

第一步:設置sidebar.vue:
這個子組件是完成任務列表的渲染。看代碼:

<template>
  <div class="sidebar">
    <div class="panel panel-default">
      <div class="panel-heading">
        <h4 class="text-left">
          任務列表
          <i class="glyphicon glyphicon-refresh pull-right"></i>
        </h4>   
      </div>
      <div class="panel-body sidebar-context">
        <div>
          <div v-for="(item,index) in items" class="panel panel-default">
            <div class="panel-heading">
              <h4>
                <input type="checkbox">
                {{item.task}}
                <i class="glyphicon glyphicon-remove pull-right"></i>
                <i class="glyphicon glyphicon-edit pull-right"></i>
              </h4>
            </div>
            <div class="panel-body">
              <p>
                {{item.setTime}}<br>
                {{item.details}}
              </p>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'sidebar',
    data() {
      return {
        
      }
    },
    computed:{
      items(){
        return this.$store.getters.items;
      }
    },
    methods:{
      
    }
  }

</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  ul {
    list-style: none;
  }
  .sidebar-context{
    max-height: 8rem;
    overflow: scroll; 
  }
  i.glyphicon{
    font-size: .275rem;
    cursor: pointer;
    margin-left: .25rem;
  }

</style>

能夠仔細考慮一下代碼的書寫,在<script> 裏設置computed,不然保存在store.js的值沒法輸出到子組件sidebar.vue裏。

第二步,設置store.js:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

const state={
    items:[]
}
const getters={
    items:state=>state.items
}
const mutations={
    addTask(state,task){
        var myDate=new Date();
        var y=myDate.getFullYear();
        var m,
            mm=myDate.getMonth()+1;
        if(mm<10){
            m="0"+mm;
        }else{
            m=mm;
        }
        var d,
            dd=myDate.getDate();
        if(dd<10){
            d="0"+dd;
        }else{
            d=dd;
        }
        var currentTime=y+m+d;
        state.items.push({
            task,
            isFinished:false,
            details:"this is a new task",
            setTime:currentTime
        })
    }
   
}

export default new Vuex.Store({
  state,
  getters,
  mutations
})

這一段代碼,咱們會把輸入的值做爲一項新建任務的task值保存,並渲染到sidebar上,固然,做爲一項待辦任務,只有名稱是不夠的,因此咱們爲它添加了默認的描述details和默認的完成時間setTime,setTime是當前日期。

看到commit相關的函數了嗎?

OK,至此,這個todolist應用最基本的新建任務就完成了。

editor.vue

接着,咱們完成編輯、刪除、標記已完成任務的功能。編輯功能在editor.vue組件內操做,刪除功能則在sidebar.vue裏面完成便可。讓咱們先編輯sidebar.vue。
下面這一段代碼我會一次性增長刪除編輯和編輯已完成的功能,你能夠先敲出來,思考一下原理,也能夠跳過這一段,跟着後面的步驟一步步添加功能。

<template>
  <div class="sidebar">
    <div class="panel panel-default">
      <div class="panel-heading">
        <h4 class="text-left">
          任務列表
          <i class="glyphicon glyphicon-refresh pull-right" v-on:click="reList"></i>
        </h4>   
      </div>
      <div class="panel-body sidebar-context">
        <div>
          <div v-for="(item,index) in items" class="panel panel-default">
            <div class="panel-heading">
              <h4>
                <input @click="itemCheck(index)" type="checkbox">
                {{item.task}}
                <i class="glyphicon glyphicon-remove pull-right" v-on:click="deleteTask(index)"></I>    
                <i class="glyphicon glyphicon-edit pull-right" v-on:click="clickTask(item)"></i>
              </h4>
            </div>
            <div class="panel-body">
              <p>
                {{item.setTime}}<br>
                {{item.details}}
              </p>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'sidebar',
    data() {
      return {
        
      }
    },
    computed:{
      items(){
        return this.$store.getters.items;
      },
      activeTask(){
        return this.$store.getters.activeTask;
      }
    },
    methods:{
      deleteTask(index){
        this.$store.commit('deleteTask',index);
      },
      itemCheck(index){
        this.$store.commit('toggleCheck',index);
      },
      clickTask(item){
        this.$store.commit('setActivetask',item);
      },
      reList(){
        this.$store.commit('reList')
      }
    }
  }

</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  ul {
    list-style: none;
  }
  .sidebar-context{
    max-height: 8rem;
    overflow: scroll; 
  }
  i.glyphicon{
    font-size: .275rem;
    cursor: pointer;
    margin-left: .25rem;
  }

</style>

在這裏,我一次性把編輯、刪除、標記已完成任務的功能三個功能都加上了(偷懶...)我會一個個來解釋。

1.刪除
看名字能看的出來,我在一個刪除圖標(仍是bootstrap啦~)上綁定了一個點擊事件deleteTask,裏面的內容是執行store.js裏一個deleteTask的函數,同時傳入一個index的參數。而在相關的store.js裏,咱們在mutations裏添加deleteTask函數相關的代碼:

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

刪除對應的數組數據。也就是刪除指定index的相關數據。任務就搞定了。

2.編輯
這是一個難點。涉及到數據在兄弟組件之間的交換,咱們仍然使用vuex。

先說說思路,咱們點擊sidebar組件裏的編輯按鈕(刪除旁邊的按鈕),而後把這個任務設置爲活動任務,而後顯示在右邊的編輯組件editor上。在editor上編輯完成後,點擊完成按鈕,修改的任務被更新到左邊的任務列表sidebar上。

好了,看上面sidebar.vue的代碼,咱們先要完成設置活動任務(activeTask):
computed裏面須要設置方法:

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

而後在編輯按鈕上綁定點擊事件,把當前的任務設置爲活動任務:

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

sidebar.vue裏的內容就編輯完了。接着咱們須要在components文件夾建立一個組件editor.vue(忘記前面有沒有建立了...),開始寫這一段的代碼:

這裏須要提早聲明的是,下面這一段的代碼是我最沒把握的代碼,由於我不肯定這一段是否是有效率的,可是它確定是能跑起來的。

<template>
  <div class="sidebar">
    <div class="panel panel-default">
      <div class="panel-heading">
        <h3 class="text-left">任務編輯</h3>
      </div>
      <div class="panel-body">
         <p>任務名稱</p>
         <input type="text" class="form-control task-input" placeholder="任務名稱" v-bind:value="task" v-on:input="saveTask">
         <p>任務詳情</p>
         <textarea class="form-control details-input" rows="3" placeholder="任務詳情" v-bind:value="details" v-on:input="saveDetails"></textarea>
         <p>任務期限</p>
         <input type="text" class="form-control settime-input" placeholder="格式:20170606" v-bind:value="setTime" v-on:input="saveSettime">
         <h3><i class="glyphicon glyphicon-ok pull-right" v-on:click="save"></i></h3>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'editor',
    data() {
      return {

      }
    },
    computed:{
      items(){
        return this.$store.getters.items;
      },
      task(){
        this.taskInput=this.$store.getters.activeTask.task;
        return this.$store.getters.activeTask.task;
      },
      details(){
        this.detailsInput=this.$store.getters.activeTask.details;
        return this.$store.getters.activeTask.details;
      },
      setTime(){
        this.settimeInput=this.$store.getters.activeTask.setTime;
        return this.$store.getters.activeTask.setTime;
      }
    },
    methods:{
      saveTask(e){
        this.taskInput=e.target.value;
      },
      saveDetails(e){
        this.detailsInput=e.target.value;
      },
      saveSettime(e){
        this.settimeInput=e.target.value;
      },
      save(){
        this.$store.commit('editTask',this.taskInput);
        this.$store.commit('editDetails',this.detailsInput);
        this.$store.commit('editSettime',this.settimeInput);
        this.$store.commit('clearAll');
      }
    }
  }

</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
 input,textarea{
     margin-bottom: .3rem;
 }
 i{
   cursor: pointer;
   margin-right: .2rem;
 }
</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。
咱們添加一下內容:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

const state={
    items:[],
    activeTask:{}
}
const getters={
    items:state=>state.items,
    activeTask:state=>state.activeTask
}
const mutations={
    addTask(state,task){
        var myDate=new Date();
        var y=myDate.getFullYear();
        var m,
            mm=myDate.getMonth()+1;
        if(mm<10){
            m="0"+mm;
        }else{
            m=mm;
        }
        var d,
            dd=myDate.getDate();
        if(dd<10){
            d="0"+dd;
        }else{
            d=dd;
        }
        var currentTime=y+m+d;
        state.items.push({
            task,
            isFinished:false,
            details:"this is a new task",
            setTime:currentTime
        })
    },
    deleteTask(state,index){
        state.items.splice(index,1)
    },
    setActivetask(state,item){
        state.activeTask=item
    },
    editTask(state,task){
        state.activeTask.task=task;
        for(let i in state.items){
            if(i==state.activeTask){
                i.task=task;
            }
        }
    },
    editDetails(state,details){
        state.activeTask.details=details;
        for(let i in state.items){
            if(i==state.activeTask){
                i.details=details;
            }
        }
    },
    editSettime(state,settime){
        state.activeTask.setTime=settime;
        for(let i in state.items){
            if(i==state.activeTask){
                i.setTime=settime;
            }
        }
    }
}

export default new Vuex.Store({
  state,
  getters,
  mutations
})

ok,主要是在state和getters裏添加了活動任務,mutations裏添加了前面提到的相關函數,對比一下看看有沒有遺漏。

到這裏,這個todolist最核心的任務就完成了。可是咱們還須要一些細節:
1.修改任務後,把右邊的活動任務清空,回覆初始狀態。
2.修改任務的完成時間後,須要知道哪些任務更加緊急,須要讓任務列表從新排序。
3.修改任務完成狀態。

咱們來依次完成:
1.這個比較簡單,咱們只要修改完後點擊完成按鈕時觸發一個設置活動任務(activeTask)爲空的方法就行了。
在editor.vue組件的save()方法添加:

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

在store.js的mutations添加:

clearAll(state){
        state.activeTask={};
},

done!

2.咱們在左邊的任務列表上放一個刷新按鈕,點擊時,會根據任務數據的setTime屬性從新排序,時間近的在前面,時間遠的在後面。
sidebar.vue的methods添加:

reList(){
        this.$store.commit('reList')
}

在store.js的mutations添加:

reList(state){
        function compare(propertyName){
            return function(obj1,obj2){
                var value1=obj1[propertyName];
                var value2=obj2[propertyName];
                if(value2<value1){
                    return 1;
                }else if(value2>value1){
                    return -1;
                }else{
                    return 0;
                }
            }
        }

        state.items.sort(compare('setTime'));
}

這裏用到了一個基礎的比較大小從新排序的功能。done!

3.這個也不復雜,咱們在任務列表組件sidebar.vue組件的多選框上添加點擊事件,methods裏添加:

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

在store.js的mutations添加:

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

由於在建立任務時,咱們會添加一個isFinished的屬性,默認爲false,經過以上方法,咱們就能夠切換isFinished的值,也就是切換任務的是否完成狀態。搞定。

到這裏,這個todo-list基本就算完成了。

4、適配移動端和儲存功能

接下來還有一些須要調整的,首先是移動端的適配,咱們要先打開根目錄的index.html,添加一下內容:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name = "viewport" content = "width = device-width, initial-scale = 1.0, maximum-scale = 1.0, user-scalable = 0" />
    <title>todolist</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- 下面是我添加的 -->
    <script>
      let html = document.documentElement;
      window.rem = html.getBoundingClientRect().width / 25 ;
      html.style.fontSize = window.rem + 'px';
    </script>
  </body>
</html>

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

第二是localstorage實現存儲。畢竟是一個todolist應用,每次打開都要從新建任務怎麼行?
這裏我用了一個vue的localstorage插件:地址
npm安裝插件:

npm install vue-localstorage --save

store.js配置:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

import VueLocalStorage from 'vue-localstorage'
Vue.use(VueLocalStorage)

const STORAGE_KEY='myapp-todolist'
const state={
    items:JSON.parse(window.localStorage.getItem(STORAGE_KEY) || '[]'),
    activeTask:{}
}
const getters={
    items:state=>state.items,
    activeTask:state=>state.activeTask
}
const mutations={
    addTask(state,task){
        var myDate=new Date();
        var y=myDate.getFullYear();
        var m,
            mm=myDate.getMonth()+1;
        if(mm<10){
            m="0"+mm;
        }else{
            m=mm;
        }
        var d,
            dd=myDate.getDate();
        if(dd<10){
            d="0"+dd;
        }else{
            d=dd;
        }
        var currentTime=y+m+d;
        state.items.push({
            task,
            isFinished:false,
            details:"this is a new task",
            setTime:currentTime
        })
    },
    deleteTask(state,index){
        state.items.splice(index,1)
    },
    toggleCheck(state,index){
        state.items[index].isFinished=!state.items[index].isFinished
    },
    setActivetask(state,item){
        state.activeTask=item
    },
    editTask(state,task){
        state.activeTask.task=task;
        for(let i in state.items){
            if(i==state.activeTask){
                i.task=task;
            }
        }
    },
    editDetails(state,details){
        state.activeTask.details=details;
        for(let i in state.items){
            if(i==state.activeTask){
                i.details=details;
            }
        }
    },
    editSettime(state,settime){
        state.activeTask.setTime=settime;
        for(let i in state.items){
            if(i==state.activeTask){
                i.setTime=settime;
            }
        }
    },
    clearAll(state){
        state.activeTask={};
    },
    reList(state){
        function compare(propertyName){
            return function(obj1,obj2){
                var value1=obj1[propertyName];
                var value2=obj2[propertyName];
                if(value2<value1){
                    return 1;
                }else if(value2>value1){
                    return -1;
                }else{
                    return 0;
                }
            }
        }

        state.items.sort(compare('setTime'));
    }
}

const localStoragePlugin = store=>{
    store.subscribe((mutation,{items})=>{
        window.localStorage.setItem(STORAGE_KEY,JSON.stringify(items))
    })
}


export default new Vuex.Store({
  state,
  getters,
  mutations,
  plugins:[localStoragePlugin]
})

要改的解放並很少,主要是把任務數據的數組轉換成localstorage的格式。
完成了!撒花~~

5、源碼和參考文獻

這個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~

相關文章
相關標籤/搜索