vue3基础入门-p03-数据属性

案例:todomvc

https://todomvc.com/

todos-项目初始化

  1. 下载todos基础模板

    git clone https://github.com/tastejs/todomvc-app-template
  2. 安装todos依赖

    cd todomvc-app-template
    npm install 
  3. 安装vue.js

    npm install vue 
  4. 项目中引入vue.js
    index.html

    <!-- Scripts here. Don't remove ↓ -->
    <script src="node_modules/vue/dist/vue.global.js"></script>
    <script src="js/app.js"></script>

    js/app.js:

    (function (window) {
    	const app = Vue.createApp({
    		data(){
    			return {
    				// 任务列表
    				todos: [
    					{id: 1, name: '吃饭', flag: true},
    					{id: 2, name: '喝酒', flag: false},
    					{id: 3, name: '唱歌', flag: false}
    				]
    			}
    		}
    	})
        // 挂载
    	const vm = app.mount('.todoapp')
        
        // 非必要,方便于浏览器控制台通过vm调用vue实例对象
    	window.vm = vm
    
    })(window);

todos-任务的列表渲染

  1. 使用v-for指令把列表动态渲染 , 指定key
  2. 内容写死了, 使用插值表达式或者v-text完成 {{item.name}}
  3. 任务完成状态 通过控制completed类型 :class="{completed: item.flag}"
  4. 复选框选中的问题 <input class="toggle" type="checkbox" v-model="item.flag">

列表渲染效果

<ul class="todo-list">
    <!-- 显示任务列表 -->
    <!-- v-for 遍历todos列表,渲染到li标签内-->
    <!--
        如果任务完成了,应该给li添加一个completed类
        如果任务正在被编辑,应该给li添加一个editing的类
        任务完成状态  通过控制completed类型  :class="{completed: item.flag}"
    -->
    <!-- 复选框根据任务状态item.flag 来显示选中状态,双向绑定 -->
    <li v-for="item in todos" :key="item.id" :class="{completed:item.flag}">
        <div class="view">
            <input class="toggle" type="checkbox" v-model="item.flag">
            <label>{{ item.name }}</label>
            <button class="destroy"></button>
        </div>
        <input class="edit" value="Create a TodoMVC template">
    </li>

</ul>

todos-任务的删除

  1. 给删除按钮注册点击事件
  2. 获取到删除任务id
  3. 根据id把list中对应的任务删除即可

任务的删除

<ul class="todo-list">
    <li v-for="item in todos" :key="item.id" :class="{completed:item.flag}">
        <div class="view">
            <input class="toggle" type="checkbox" v-model="item.flag">
            <label>{{ item.name }}</label>
            <!--do:给删除按钮注册点击事件-->
            <button class="destroy"  @click="delTodo(item.id)"></button>
        </div>
        <input class="edit" value="Create a TodoMVC template">
    </li>

</ul>

在methods中添加delTodo函数

delTodo(id) {
    // 过滤  只要把id不同留下来
    this.todos = this.todos.filter(item => item.id !== id)
}

todos-任务的添加

  1. 获取到输入的内容
  2. 要注册keyup事件,回车的时候添加任务
  3. 把获取到内容添加到list中,最前面

任务的添加

<header class="header">
    <h1>todos</h1>
    <!--输入框与数据双向绑定-->
    <!--注册回车事件-->
    <input class="new-todo" placeholder="What needs to be done?" v-model="todoName" @keyup.enter="addTodo" autofocus>
</header>

在methods中添加addTodo函数

addTodo(){
    this.todos.unshift({
        id: +new Date(),
        name:this.todoName,
        flag: false

    })
    this.todoName=''
}

todos-任务的修改

  1. 给每一个lable都注册双击事件 dblclick
  2. 让当前这个li有editing这个类, input框就能显示出来
    难点:怎么让点击的这个li有editing类
  3. 首先需要一个属性now,记录下来点击了任务的id ,初始 = -1
  4. 双击的时候,修改now为点击的任务的id
  5. 给li中的editing类添加判断 {editing: item.id === now}
  6. 给input框双向数据绑定 <input class="edit" v-model="item.name">
  7. 回车的时候,注册keyup事件 ,只需要editing类为false 只需要让now为-1

编辑任务

<ul class="todo-list">
    <!-- 显示任务列表 -->
    <!--add:如果任务正在被编辑,应该给li添加一个editing的类-->
    <li v-for="item in todos" :key="item.id" :class="{completed:item.flag,editing:item.id===now}">
        <div class="view">
            <input class="toggle" type="checkbox" v-model="item.flag">
            <!-- add: 添加双击监听,双击后显示编辑输入框-->
            <label @dblclick="showEdit(item.id)">{{ item.name }}</label>
            <button class="destroy"  @click="delTodo(item.id)"></button>
        </div>
        <!--add:添加回车监听,回车后,输入框隐藏-->
        <input class="edit" @keyup.enter="editTodo" v-model="item.name">
    </li>
</ul>

在methods中添加showEdit,editTodo函数

// 显示修改框
showEdit(id){
    this.now = id
},
// 修改任务
editTodo(){
    this.now=-1
}

todos-底部的显示和隐藏

  1. 当任务列表的长度为0,没任务,底部就隐藏
  2. 当任务列表的长度不为0,底部就显示

显示隐藏footer

<footer class="footer" v-show="isShowFooter">

vue实例中添加computed计算属性

computed:{
    isShowFooter() {
        // 当任务数量超过0,就显示footer
        return this.todos.length > 0
    }

}

todos-统计未完成的任务

<span class="todo-count"><strong>{{ unCompletedCount }}</strong> item left</span>

vue实例中添加computed计算属性

// 统计未完成的任务
unCompletedCount(){
    // 过滤出未完成的任务
    // this.todos.filter(item => !item.flag)
    return this.todos.filter(item => !item.flag).length
}

todos-清空已经完成的任务

  1. 控制清空按钮的显示隐藏
  2. 点击清空按钮,把所有已经完成的任务清除

<!-- Hidden if no completed items are left ↓ -->
<!--add: 控制隐藏,监听点击事件-->
<button class="clear-completed" v-show="isShowClear" @click="clearTodo">Clear completed</button>

在计算属性computed中添加属性

// 控制清空按钮的显示隐藏
isShowClear(){
    // 如果没有已完成任务,就隐藏
    // 如果存在已完成任务,便显示
    return this.todos.some(item => item.flag)
}

在方法methods中添加方法

// 清空已完成任务
clearTodo(){
    // 过滤保留未完成任务
    this.todos = this.todos.filter(item => !item.flag)
}

计算属性

计算属性:当计算属性依赖的数据发生改变的时候,计算属性会重新计算一次.
如果计算属性依赖的属性没有发生改变,那么计算属性就不会重新计算。

  1. 计算属性写到computed属性中
  2. 计算属性和data一样,也是用来提供的属性,computed中提供的属性是需要一定的计算才能得到结果的属性
  3. 计算属性写法上是一个函数,计算属性实质上是一个属性,对应的函数的返回值。
  4. 计算属性只会算一次,就会把结果存储起来,非常的高效
  5. 计算属性只有当依赖的数据发生了改变,就会重新计算
  6. 什么时候使用计算属性:需要在插值表达式或者指令中书写复杂的逻辑,都应该由计算属性来提供

基本使用

const app = Vue.createApp({
    // data 提供普通属性
    data() {
        // Data函数应该返回一个对象
        return {
            n1:'',
            n2:''
          }
    },
    // methods 提供事件函数
    methods: {
        
    },
    // computed 提供计算属性
    computed:{
        //n3依赖与n1和n2的值,当n1 和 n2发生改变的时候,这个函数就会执行。
        //返回值就是n3的值
        n3(){
          return +this.n1 + +this.n2;
        }
    }
})

const vm = app.mount('#app')

计算属性是基于它们的依赖项进行缓存的

如果页面中需要使用多次计算属性的值,只会计算一次,计算属性只有在它的相关依赖发生改变时才会重新求值。

计算属性不能与data中的属性同名,因为无论是data中的属性还是计算属性,最终都是挂载到vm上的

计算属性的完整形态get/set

  1. 计算属性默认情况只提供了get,只能获取
  2. 计算属性默认不能修改,非要修改,比如v-model绑定的是一个计算属性,必须用到计算属性的完整形态,提供 set函数 (set的逻辑需要自己提供)

计算属性get/set

<body>
<div id="app">
    <input type="text" placeholder="请输入你的姓" v-model="lastName"> +
    <input type="text" placeholder="请输入你的名" v-model="firstName"> =
    <input type="text" placeholder="你的名字是" v-model="fullName">
</div>

<script src="./node_modules/vue/dist/vue.global.js"></script>

<script>
    const app = Vue.createApp({
        // data 提供了vue中使用的数据
        data() {
            return {
                lastName: '',
                firstName: ''
            }
        },
        // methods 对象: 提供vue中使用到的方法
        methods: {},
        // 计算属性的完整形态;如果需要修改计算属性的值,如果需要对计算属性使用双向绑定
        computed: {
            // 默认提供的仅仅是getter
            // fullName() {
            //   return this.lastName + ' ' + this.firstName
            // }
            fullName: {
                get() {
                    return this.lastName + ' ' + this.firstName
                },
                set(value) {
                    // value修改的全名
                    console.log(value)
                    this.lastName = value.split(' ')[0]
                    this.firstName = value.split(' ')[1]
                }
            }
        }
    })

    const vm = app.mount('#app')

    window.vm = vm
</script>
</body>

案例:todomvc

todos-通过计算属性控制全选

  1. 根据下面所有的任务来控制<input id="toggle-all" class="toggle-all" type="checkbox" >的check值:当下面所有的任务都完成,就选中,否则就不选中
  2. 选中toggle-all后,下面所有任务的checkbox均设置为选中;取消选中toggle-all后,下面所有任务的checkbox均设置为未选中

全选控制

<!-- 1. checkbox 选中后,将列表所有checkbox选中 ,取消选中后,将列表所有checkbox取消选中-->
<!-- 2. 列表所有checkbox都选中后 ,toggle-all自动选中-->
<input id="toggle-all" class="toggle-all" type="checkbox"  v-model="isCheckedAll">

在计算属性computed中添加属性

// toggle-all checkbox双向绑定值
// 改计算属性需要修改,提供set函数,不能直接写成函数。
isCheckedAll: {
    get() {
        // 当所有的任务都完成了,表示都选中了
        return this.todos.every(item => item.flag)
    },
    set(value) {
        // console.log(value)
        // value修改后的计算属性,,,,,,应该修改todos中所有的任务的flag
        this.todos.forEach(item => item.flag = value)
    }
}

todos-底部ALL/Active/Completed不同状态的任务切换

  1. 控制选中样式
  2. 根据选中的状态,显示相应状态的任务(注意点:通过计算属性来显示,不能直接修改原列表)

切换过滤条件

<!--给每个a标签添加一个点击事件,通过不同的参数控制样式-->
<ul class="filters">
	<li>
		<a @click.prevent="change('ALL')" :class="{selected:filterType==='ALL'}" href="#/">All</a>
	</li>
	<li>
		<a @click.prevent="change('Active')"  :class="{selected:filterType==='Active'}" href="#/active">Active</a>
	</li>
	<li>
		<a @click.prevent="change('Completed')"  :class="{selected:filterType==='Completed'}" href="#/completed">Completed</a>
	</li>
</ul>

data 中新增一个属性

// 过滤类型
filterType: 'ALL'

methods 中新增点击事件

// 切换过滤类型
change(filterType) {
    // 控制样式判断值
    this.filterType = filterType
}

computed 中新增计算属性来获取显示列表

// 用于显示的列表
showList() {
    // 计算属性showList 要根据 filterType
    if (this.filterType === 'Active') { // 返回未完成的任务
        return this.todos.filter(item => !item.flag)
    } else if (this.filterType === 'Completed') { // 返回已完成的任务
        return this.todos.filter(item => item.flag)
    } else { // 默认返回所有任务
        return this.todos
    }
}

修改原遍历列表为showList

<li v-for="item in showList" :key="item.id" :class="{completed:item.flag,editing:item.id===now}">

watch监视数据的变化

vue实例中提供了一个watch属性,用于监听vue实例中的属性的变化

watch对应了一个对象,键是观察的属性,值是对应的回调函数。

基本使用

  • 监听简单类型属性
watch: {
  // 1. 只要msg的值发生了改变,function就会执行
  // 2. 参数1:newValue 参数2:oldValue
  // 监视属性完整形态
  // 监视属性如果是复杂类型,比较麻烦
  msg: function(value, oldValue) {
    console.log('监听到msg发生变化了', value, oldValue)
  }
}

监视对象类型属性

监视对象的时候,需要加上deep: true

为了发现对象内部值的变化,可以在选项参数中指定 deep: true 

  • 如果是一个对象,无法监听到对象内部值的变化
  • 需要加上deep
watch: {
    user: {
        deep: true,
        handler: function(curr, old) {
            //注意:如果监听的是对象,新值与旧值都是相同的,因为指向了同一个对象。
            //https://cn.vuejs.org/v2/api/#vm-watch
            console.log(curr.age, curr.name);
        }
    }
}

案例:todomvc

todos-使用监听属性把数据存储到浏览器

目前的案例效果,初始任务todos是写死的记录,编辑任务刷新浏览器又会恢复成初始记录。本次任务是将任务的变更情况存储到浏览器缓存中。

  1. 初始todos从浏览器缓存中获取
  2. 监听todos属性变化,将todos存储到浏览器缓存

watch监听todos属性,存储到浏览器缓存

在Vue.createApp上方添加

// 从浏览器localStorage中提取todos列表
let todos = JSON.parse(localStorage.getItem('todos')) || []

在data的return中,调整todos

return {
    todos,
}

在watch中添加侦听

// 添加侦听
watch: {
    // 侦听todos属性的变化
    todos: {
        deep: true,
        handler(value) {
            // 把最新的todos存储到localStorage中
            localStorage.setItem('todos', JSON.stringify(value))
        }
    }
}

案例:todomvc-v1(已完结)

源码可参考:https://gitee.com/guanfuchang/todos_v1

第三天学习总结

vue第三天学习总结


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。
My Show My Code