vue-todo应用

image.png

开始第一步

components文件夹里新建五个vue文件

  • header.vue//头部组件 也就是图中的jtodo这个几个字
  • footer.vue//底部组件,就是下面的 ‘written by 懒人漫游’
  • todo.vue // 整个todo应用
  • tabs.vue //列表,这个是循环出来的
  • item.vue //按钮的功能

App.vue:注册组件


image.png

注意:由于只是演示代码,所以是在app里注册,正确的应该是通过路由注册,写在 router/index.js这个文件里

子组件注册:这个写在todo.vue里


image.png

正式开始写代码

先来todo.vue

<section class="real-app">
     <input 
        type="text"
        class="add-input"
        autofocus="autofocus"  
        placeholder="接下来做什么"
        @keyup.enter="addTodo"  
        >
      <Item/>
      <Tabs/>
    </section>  
    <script>
    import Item from './item.vue'
    import Tabs from './tabs.vue'
    import '../assets/styles/todo.styl'
    let id = 0
    export default {
        data() {
            return {
                todos:{
                  id:0,
                  content:'this is todo',
                  completed:false
                },
                filter:"all"
            }
        },
      components: {
          Item,
          Tabs
      },
      computed:{
          filteredTodos(){
          },
      },
      methods: {
         addTodo(e){
         },
         deleteTodo(id){
         },
         toggleFilter(state){
         },
         clearAllCompleted(){
         }
      },
    }
    </script> 

autofocus="autofocus" 一进来自动选中输入框

@keyup.enter="addTodo" 等同与v-on:key="addTodo"  

.enter是修饰符,注意是 '.enter' !,监听键盘事件,回车是enter,


image.png

现在来写item.vue

 <div :class="['todo-item',todo.completed ? 'completed' :'' ]">
        <input 
        type="checkbox"
        class="toggle"
        v-model="todo.completed"
        >
        <label> {{todo.content}}</label>
        <button class="destroy" @click="deletetodo"> </button>
    </div>
<script>
export default {
  props: {
      todo: {
          type: Object,
          required: true
      }
  },
  methods : {
      deletetodo(){     
      }
  }
}
</script>

:class="['todo-item',todo.completed ? 'completed' :'' ]" class的动态写法
v-model="todo.completed" //双向数据绑定
<label> {{todo.content}}</label>// 就是你写入的todo的内容
<button class="destroy" @click="deletetodo"> </button>//后面删除按钮

props:通过 Prop 向子组件传递数据,todo相对于item.vue来说是父组件,所以用props来传递数据

$emit 方法并传入事件的名字,来向父级组件触发一个事件

@click="deletetodo"是简写,deletetodo事件函数名

<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>

<!-- 缩写 -->
<a @click="doSomething">...</a>

现在来写tabs.vue

    <div class="helper">
        <span class="left">2 items left</span>
        <span class="tabs">
            <span 
                v-for="state in states"
                :key="state"
                :class="[state,filter === state ? 'actived' :'']"
                @click="toggleFilter(state)"
            >
                {{state}}
            </span>
        </span>
        <span class="clear" @click="clearAllCompleted()">
            Clear completed
        </span>
    </div>
<script>
export default {
    props:{
        filter:{
            type:String,
            required:true,
        },
        todos: {
           type:Array,
           required: true,
        }
      },
      data(){
          return {
              states:['all','active','completed']
          }
      },
      methods:{
          toggleFilter(state){ 
          },
          clearAllCompleted(){
          }
       }
   }
</script>

这个组件主要就是
:class="[state,filter === state ? 'actived' :'']"能不能看懂
给class设置两个类名,如果filter === state,class ="state actived"
否则就是class ="state",这个和jquery一样,动态添加'active'

v-for="state in states" 
:key="state"

当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用 “就地复用” 策略。如果数据项的顺序被改变,Vue将不是移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x 的 track-by="$index" 。
这个默认的模式是有效的,但是只适用于不依赖子组件状态或临时 DOM 状态(例如:表单输入值)的列表渲染输出。

:key 总的来说就是,可以提高性能

image.png

当你写完的时候,页面是这个样子(css部分请到时候查看源码)

开始第二步:实现业务逻辑

todo.vue

<script>
import Item from './item.vue'
import Tabs from './tabs.vue'
import '../assets/styles/todo.styl'
let id = 0
export default {
    data() {
        return {
            todos:[],
            filter:"all"
        }
    },
  components: {
      Item,
      Tabs
  },
  computed:{
      filteredTodos(){
          if (this.filter === 'all'){
              return this.todos
          }
          const completed = this.filter === 'completed'
          return this.todos.filter(todo => completed ===todo.completed)
      },

  },
  methods: {
      addTodo(e){
         if(e.target.value==''){
             alert("你什么都没有添加!")
         }
         else{
             this.todos.unshift({
              id:id++,
              content: e.target.value.trim(),
              completed:false
          }),
          e.target.value =''
         }     
      },
     deleteTodo(id){
        //  console.log(todo => todo.id ===id);
         this.todos.splice(this.todos.findIndex(todo => todo.id ===id),1)
     },
     toggleFilter(state){
         this.filter =state
     },
     clearAllCompleted(){
         this.todos = this.todos.filter(todo => !todo.completed)
         
     }
  },
}
</script>
  • data()里的todos要改成空数组,在增加的时候,添加进去就可以了
  • 改写<Item/> <Tabs/>
 <Item 
  :todo="todo"
  v-for="todo in filteredTodos"
  :key="todo.id"
  @del="deleteTodo"
></Item>   
<Tabs 
  :filter="filter" 
  :todos="todos"
  @toggle="toggleFilter"
  @clearAll="clearAllCompleted"
></Tabs>

@del="deleteTodo"是子组件传过来的,写在父组件的好处是方便解耦。

  • 实现addTodo(e),在todo列表的最上面添加todo,id从0开始,每次添加,id++,内容就是每次input输入的值然后去掉空格,完了之后要把input的内容清空,所以是e.target.value =''

  • 删除deleteTodo(id),找到对应的下标,删除一个

  • 切换状态toggleFilter(state) :

this.filter =state
  • 清除完成的clearAllCompleted():
this.todos = this.todos.filter(todo => !todo.completed)

然后实现items.vue的业务逻辑

 methods : {
      deletetodo(){
          this.$emit("del",this.todo.id);
      }
  }

最后是tabs.vue的

computed: {
        unFinishedTodoLength() {   //计算left的items的长度
            return (this.todos.filter(todo => !todo.completed).length)
        }
    },
    data() {
        return {
            states: ['all', 'active', 'completed']
        }
    },
    methods: {
        toggleFilter(state) {
            this.$emit('toggle', state)
        },
        clearAllCompleted() {
            this.$emit('clearAll')
        }
    }

把html部分改写下,动态显示未完成的数字显示

<span class="left">{{unFinishedTodoLength}} items left</span>

写完todo应用之后对组件注册,事件处理函数,vue的相关指令,父子组件的通信有了进一步的了解,对vue使用有了更多的经验

源码
本文所写代码 来自慕课网

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容