尚硅谷视频:https://www.bilibili.com/video/BV1Zy4y1K7SH/?spm_id_from=333.999.0.0&vd_source=cec13bbe100bddfff8baf29d18ed8994
 
文章目录
- 模板语法
- data与el的2种写法
- MVVM模型
- 事件
- 事件修饰符
- 事件的基本使用
 
- 计算属性
- 简写形式
 
- 监视属性
- 绑定样式
- 条件渲染
- 列表渲染
- 收集表单数据
- 生命周期函数
- 非单文件组件
- 单文件组件
- Vue脚手架
- main.js
- App.vue
- 其他组件
- ToDoList总结
- vue组件什么是会被销毁
- Vuex
- 创建store
- 使用
 
模板语法
- 插值语法
<h1>
    {{name}}
</h1>
- 指令语法
单向数据绑定
<h1 v-bind:href = "js表达式"></h1>
<h1 :href = "js表达式"></h1>
双向数据绑定 一般用在表单类元素上 默认收集value值
<input  type = "text" v-model:value="name"/>
<input  type = "text" v-model="name"/>
data与el的2种写法
-  el有2种写法 - (1).new Vue时候配置el属性。
- (2).先创建Vue实例,随后再通过vm.$mount(‘#root’)指定el的值。
 
-  data有2种写法 -  (1).对象式 data: { name: 'shanghai' }
-  (2).函数式 data (){ console.log('@@@', this); //此时this是Vue return { name: 'shanghai' } }
 
-  
如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
- 一个重要的原则:
 由Vue管理的函数(例如data),一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
MVVM模型
-  MVVM模型 - M:模型(Model) :data中的数据
- V:视图(View) :模板代码
- VM:视图模型(ViewModel):Vue实例
 
-  观察发现: - data中所有的属性,最后都出现在了vm身上。
- vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用。
 
事件
<h1 @click="showInfo" >点我提示信息</h1>
事件修饰符
1.prevent:阻止默认事件(常用);
<a href="https://www.baidu.com" @click.prevent="showInfo" >点我提示信息</a>
2.stop:阻止事件冒泡(常用);
修饰符也可以连用,例如下面事例是即阻止冒泡同时也阻止默认行为
<div class="demo1" @click="showMsg(1)">
<button @click.stop="showMsg(2)">点我提示信息</button>
<!--修饰符也可以连用,例如下面事例是即阻止冒泡同时也阻止默认行为-->
<!--<a href="https://www.google.com.tw" @click.prevent.stop="showInfo">谷歌台湾</a>-->
</div>
3.once:事件只触发一次(常用);
4.capture:使用事件的捕获模式;
<!-- capture事件的捕获模式 让事件以捕获的方式来处理(先捕获再冒泡)-->
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">div2</div>
</div>
       
5.self:只有event.target是当前操作的元素时才触发事件;
<!-- self 只有event.target是当前操作的元素时才触发事件(变相阻止冒泡)-->
<div class="demo1" @click.self="showInfo">
<button @click="showInfo">点我提示信息</button>
</div>
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
<!--passive:事件的默认行为立即执行,无需等待事件回调执行完毕;-->
<!--scroll滚动条一滚动就会触发的事件 wheel鼠标滚轮事件-->
<ul class="list" @scroll.passive="demo">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>
正常输出2、1,@click.stop输出2, @click.capture输出1,2(先捕获再冒泡

事件的基本使用
- 使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名
- 事件的回调需要配置在methods对象中,最终会在vm上
- methods中配置的函数,不要用箭头函数!否则this就不是vm了
- methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象
- @click=“demo” 和 @click=“demo($event)” 效果一致,但后者可以传参
<button v-on:click="showInfo1">点我提示信息1</button>
<!--简写形式 @click-->
<button @click="showInfo2($event,66)">点我提示信息2</button>
showInfo2(e,num){
    console.log(e.target);//
    console.log(`接收参数:${num}`);
}
计算属性
- 定义:要用的属性不存在,要通过已有属性计算得来。
- 原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
- get函数什么时候执行? 
  - 初次读取时会执行一次。
- 当依赖的数据发生改变时会被再次调用。
 
- set函数什么时候执行? 
  - 修改计算属性所依赖的普通属性(放在data里面的)
 
- 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
- 备注: 
  - 计算属性最终会出现在vm上,直接读取使用即可。
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
 
<div>
姓:<input type="text" v-model="firstName"/>
    <br/><br/>
    名:<input type="text" v-model="lastName"/>
    <br/><br/>
    测试:<input type="text" v-model="test"/>
    <br/><br/>
    全名: <input type="text" v-model="fullName"/>
    <br/><br/>
    全名: <span>{{ fullName }}</span>
</div>
<script type="text/javascript">
    const vm = new Vue({
        el: '#root',
        data: {
            //data里的都是属性
            firstName: '张',
            lastName: '三',
            test:'test'
        },
        //计算属性--> 旧属性加工
        computed: {
            fullName: {
                //get的作用,当读取fullName时get就会被调用,且返回值就做为fullName的值
                //defineProperty
                //注意vm._data是不存在计算属性的
                //计算属性算完之后直接丢到了viewModel实例对象身上
                /**
                 * get的调用时机
                 * 1.初次读取计算属性时
                 * 2.所依赖的数据(data中的属性)发生变化时,不改变的话直接读缓存
                 * 3.methods没有缓存,读几次就调用几次无论要用的数据是否发生变化
                 *  @returns {string}
                 */
                get(){
                    //此时get函数中的this指向是vm
                    console.log('get调用了', this);
                    return this.firstName + '-' + this.lastName
                },
                /**
                 * set什么时候调用
                 *   1.当fullName被修改时
                 * @param value
                 */
                set(value){
                    console.log('set调用了', value);
                    //修改计算属性所依赖的普通属性(放在data里面的)
                    //this === vm
                    const [ firstName, lastName ] = value.split('-');
                    console.log('firstName', firstName);
                    console.log('lastName', lastName);
                    this.firstName = firstName;
                    this.lastName = lastName; //依赖属性发生改变之后,计算属性自动改变
                }
            }
        }
    });
 </script>
简写形式
computed: {
    //简写形式
    //前提:计算属性只考虑读取不考虑修改 set丢了
    //简写计算书写为一个函数(这个函数当成getter使用), 注意不要写箭头函数
    //执行完getter之后,vm直接保存返回的数据为fullName属性的属性值,此时vm.fullName不是函数而是一个确切的计算值
    fullName: function (){
    	return this.firstName + '--' + this.lastName;
    }
}
监视属性
-  当被监视的属性变化时, 回调函数自动调用, 进行相关操作 
-  监视的属性必须存在,才能进行监视 
-  监视的两种写法: - new Vue时传入watch配置
- 通过vm.$watch监视
 
-  handler函数 - 当被监视属性发生改变就会调用该函数
- 接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
 
-  深度监视: - Vue中的watch默认不监测对象内部值的改变(一层)
- 配置deep:true可以监测对象内部值改变(多层)
 
-  简写形式,不需要immediate、deep属性 watch:{ isHot(newValue, preValue){ console.log(newValue,preValue); } }
案例:
<div id="root">
    <h1>今天天气很 {{ info }}</h1>
    <button @click="changeWeather">
        切换
    </button>
    <h3>b的值是:{{ numbers.b }}</h3>
    <button @click="numbers.b++">
        点我让b加一
    </button>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    const vm = new Vue({
        el:'#root',
        data:{
            isHot: true,
            numbers:{
                a:1,
                b:1,
                d:{
                    e:2
                }
            }
        },
        //计算属性
        computed: {
            info(){
                return this.isHot ? '炎热' : '凉爽';
            }
        },
        //改变模版数据的方法
        methods:{
            changeWeather(){
                this.isHot = !this.isHot;
            }
        },
        // watch:{
        //     //监视的配置对象
        //     //watch不仅能监视data的普通属性,也可以检测计算属性
        //     isHot:{
        //         immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
        //         //handler 什么时候调用呢
        //         //当isHot发生改变就会调用该函数
        //         //handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
        //         handler(newValue, preValue){
        //             console.log('ishot 被修改了');
        //             console.log(`newValue: ${newValue}, preValue: ${preValue}`);
        //         }
        //     },
            //深度监视numbers中的所有属性
                numbers:{
                    deep: true, //监视多级结构的属性
                    handler(){
                        console.log('numbers 发生改变')
                    }
                }
        // }
    });
    //watch的第二种写法,直接采用vm对象实例
    vm.$watch('isHot', {
        immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
        //handler 什么时候调用呢
        //当isHot发生改变就会调用该函数
        //handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
        handler(newValue, preValue){
            console.log('ishot 被修改了');
            console.log(`newValue: ${newValue}, preValue: ${preValue}`);
        }
    });
</script>
绑定样式
- class样式 
  - 写法:class=“xxx” xxx可以是字符串、对象、数组
- 字符串写法适用于:类名不确定,要动态获取。
- 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定
- 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
 
- style样式 
  - :style="{fontSize: xxx}"其中xxx是动态值。
- :style="[a,b]"其中a、b是样式对象。
 
条件渲染
- v-if 
  - 写法:
 (1).v-if=“表达式”
 (2).v-else-if=“表达式”
 (3).v-else=“表达式”
- 适用于:切换频率较低的场景。
- 特点:不展示的DOM元素直接被移除。
- 注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。
 
- 写法:
- v-show 
  - 写法:v-show=“表达式”
- 适用于:切换频率较高的场景
- 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
 
- 备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
列表渲染
语法:
- 遍历数组、指定次数
 v-for=“(item, index) in xxx” :key=“yyy”
-  遍历对象、字符串 v-for=“(val, key) of xxx” :key=“yyy” v-for=“(c, index) of xxx” :key=“yyy” 
-  在Vue修改数组中的某个元素一定要用如下方法,直接修改的话Vue不监听,模板不改变 - 使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
- Vue.set() 或 vm.$set()
- 原理:
 Vue会监视data中所有层次的数据- 对象中后追加的属性,Vue默认不做响应式处理
- 利用vue.set(或者vm. s e t ( ) ) a p i 能够添加的属性变为响应式属性 V u e . s e t ( t a r g e t , p r o p e r t y N a m e / i n d e x , v a l u e ) 或 v m . set())api能够添加的属性变为响应式属性Vue.set(target,propertyName/index,value) 或 vm. set())api能够添加的属性变为响应式属性Vue.set(target,propertyName/index,value)或vm.set(target,propertyName/index,value)
- 特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
 
 
methods:{
    updateM(){
        // this.persons[1].name = '马老师';  //奏效
        // this.persons[1].age = 50;      //奏效
        // this.persons[1].sex = '男'; //奏效
        // this.persons[1] = { id: '002', name: '马老师', age: 50, sex:'男' }; //这样修改vue是无法监测数据的
        this.persons.splice(1,1,{ id: '002', name: '马老师', age: 50, sex:'男' });
    },
    addSex(){
    //这里this === vm
    //利用vue.set(或者vm.$set())api能够添加的属性变为响应式属性
    //注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
    Vue.set(this.stu, 'sex', '男')
    // this.$set(this.stu, 'sex', '男');
     }
}
“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码。例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。
Vue数据响应式与双向数据绑定原理区分
数据响应式是指通过数据驱动DOM视图的变化,是单向的过程,而双向数据绑定的数据和DOM是一个双向的关系https://blog.csdn.net/forever__fish/article/details/127163227
收集表单数据
- 若:,则v-model收集的是value值,用户输入的就是value值。
- 若:,则v-model收集的是value值,且要给标签配置value值。
- 若:
- 没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
- 配置input的value属性: 
  - v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
- v-model的初始值是数组,那么收集的的就是value组成的数组
 
- 备注:v-model的三个修饰符: 
  - lazy:失去焦点再收集数据
- number:输入字符串转为有效的数字
- trim:输入首尾空格过滤
 
<div id="root">
    <form @submit.prevent="demo">
        <!--写了label则点击它也能使指定的input获取焦点 for属性的值为指定元素的id-->
        <label for="demo">账号:</label>
        <!--v-model主要用来双向绑定输入类表单value值-->
        <input type="text" id="demo" v-model.trim="userInfo.account"/>
        <br/>
        密码: <input type="password" v-model="userInfo.password"/>
        <br/>
        性别:
        <!--一组radio单选框的name值一定要相同 设置value值好让v-model去双向绑定-->
        男:<input type="radio" v-model="userInfo.sex" name="sex" value="male"/>
        女:<input type="radio" v-model="userInfo.sex" name="sex" value="female"/>
        <br/>
        年龄: <input type="number" v-model.number="userInfo.age"/>
        <br/>
        爱好:
        <!--如果没有value值则v-model收集checked元素-->
        学习 <input v-model="userInfo.hobby" type="checkbox" value="study"/>
        打游戏 <input v-model="userInfo.hobby" type="checkbox" value="game"/>
        吃饭 <input v-model="userInfo.hobby" type="checkbox" value="eat" />
        <br/>
        所属校区
        <select v-model="userInfo.city">
            <option value="">请选择校区</option>
            <option value="Beijing">北京</option>
            <option value="Shanghai">上海</option>
            <option value="Shenzhen">深圳</option>
            <option value="Wuhan">武汉</option>
        </select>
        <br/>
        其他信息<textarea v-model.lazy="userInfo.other"></textarea>
        <br/>
        <input type="checkbox" v-model="userInfo.ifAgree"/>阅读并接受<a href="https://www.google.com.tw">《用户协议》</a>
        <button>提交数据</button>
    </form>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    const vm = new Vue({
        el: '#root',
        data:{
            userInfo:{
                account: '',
                password: '',
                sex: 'male',
                age:'',
                hobby:[],
                city:'',
                other:'',
                ifAgree:''
            }
        },
        methods:{
            demo(){
                //json转换为string
                console.log(JSON.stringify(this.userInfo));
            }
        }
    })
</script>
生命周期函数

<script>
export default {
  name: "demo",
  data() {
    return {
      msg: "Welcome to Your Vue.js App",
      msg1: "Welcome to Your Vue.js App1",
    }
  },
  methods: {
    reverseMessage() {
      this.msg = this.msg.split("").reverse().join("")
    }
  },
  computed: {
    fullMessage: function() {
      return this.msg + " " + this.msg
    },
    fullMessage2: {
      get() {
        console.log("get 调用了")
      },
      set(value) {
        console.log("set 调用了");
        this.msg = value.split(" ").slice(0, 1).join("");
      }
    }
  },
  watch: {
    msg(val, oldVal) {
      console.log("watch msg 调用了", val, oldVal)
    },
    msg1:{
      immediate:true,
      deep:true,
      handler(val,oldVal){
        console.log("watch 调用了 msg1", val, oldVal)
      }
    }
  }
}
</script>
非单文件组件
-  Vue.extend() 是vc对象 
-  new vue()是vm对象 
-  const school = Vue.extend(options) 可简写为:const school = options
-  为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。所以vm与vc属性配置并不是一模一样,尽管vc底层复用了很多vm的逻辑 
-  一个重要的内置关系:VueComponent.prototype.proto === Vue.prototype 
-  为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。 
<script type="text/javascript">
    Vue.config.productionTip = false;
    const school = Vue.extend({
        template: `
          <div>
             <h1>学校名称:{{ name }}</h1>
             <h1>学校地址:{{ address }}</h1>
             <button @click="showname">点我提示学校名</button>
          </div>
        `,
        data(){
            return {
                name: '武汉科技大学',  //vuecomponent的实例对象
                address:'武汉'
            }
        },
        methods:{
            showname(){
              console.log(this);
              console.log(this.name);
           }
        }
    });
    // console.log(typeof school, school); //所谓的组件就是构造函数(VueComponent);
    //测试组件
    const test = Vue.extend({
       template:  `<h1>panyue</h1>`
    });
    //hello组件
    const hello = Vue.extend({
        template:`
          <div>
          <h1>{{ msg }}</h1>
          <test></test>
        </div>`,
        data(){
            return {
                msg: '你好'
            }
        },
        components: {
            test
        }
    })
    const vm = new Vue({
        el:"#root",
        components:{
            school,
            hello
        }
    });
    //验证school与hello并不是同一个VueComponent构造函数
    // school.a = 99;
    // console.log('@', school);
    // console.log('#', hello);
    // console.log(school === hello);
    // console.log(school.a, hello.a);
</script>
单文件组件
-  普通组件School.veu - template 写HTML
- script 创建vc 暴露vc export default {}
- style 写css
 
-  App组件 汇总其他组件 components:{School, Student} 
-  main.js 创建vm //创建vm import App from './App'; //如果文件 new Vue({ el: '#root', template:`<App></App>`, components:{ App } });
-  index.html 准备root容器 
mixin

Vue脚手架
- npm install -g @vue/cli
- vue create xxx
- 启动项目 npm run serve
- 打包项目 npm run build

main.js
入口文件
import {createApp} from 'vue'
import App from './App.vue'
import * as VueRouter from 'vue-router';
import routes from "./config/route";
import Vant from 'vant';
import 'vant/lib/index.css';
import '../global.css'
const app = createApp(App);
const router = VueRouter.createRouter({
    // 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
    history: VueRouter.createWebHistory(),
    routes, // `routes: routes` 的缩写
})
app.use(Vant);
app.use(router);
app.mount('#app')
App.vue
//vue2
<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>
//vue3
<template>
  <BasicLayout />
</template>
<script setup lang="ts">
import BasicLayout from "./layouts/BasicLayout.vue";
</script>
<template>
  <BasicLayout />
</template>
<style>
</style>
其他组件
<template>
  <van-nav-bar
      :title="title"
      left-arrow
      @click-left="onClickLeft"
      @click-right="onClickRight"
  >
    <template #right>
      <van-icon name="search" size="18"/>
    </template>
  </van-nav-bar>
  <div id="content">
    <router-view/>
  </div>
  <van-tabbar route>
    <van-tabbar-item to="/" icon="home-o" name="index">主页</van-tabbar-item>
    <van-tabbar-item to="/team" icon="search" name="team">队伍</van-tabbar-item>
    <van-tabbar-item to="/user" icon="friends-o" name="user">个人</van-tabbar-item>
  </van-tabbar>
</template>
<script setup lang="ts">
import { useRouter } from "vue-router";
import {ref} from "vue";
import routes from "../config/route";
const router = useRouter();
const DEFAULT_TITLE = '伙伴匹配';
const title = ref(DEFAULT_TITLE);
/**
 * 根据路由切换标题
 */
router.beforeEach((to, from) => {
  const toPath = to.path;
  const route = routes.find((route) => {
    return toPath == route.path;
  })
  title.value = route?.title ?? DEFAULT_TITLE;
})
const onClickLeft = () => {
  router.back();
};
const onClickRight = () => {
  router.push('/search')
};
</script>
<style scoped>
#content {
  padding-bottom: 50px;
}
</style>
ToDoList总结

vue组件什么是会被销毁
页面关闭、路由跳转、v-if和改变key值
Vuex

创建store
./src/store/index.js
import {createStore} from "vuex";
import {nanoid} from "nanoid";
const state = {
    todos: JSON.parse(localStorage.getItem('todos')) || []
};
const mutations = {
    saveTodos(state, value) {
        localStorage.setItem('todos', JSON.stringify(value))
    },
    addTodo(state, input) {
        state.todos.unshift({
            id: nanoid(),
            label: input,
            done: false
        })
    },
    allChecked(state, checked){
        for(let i=0;i<state.todos.length;i++){
            state.todos.splice(i,1,{...state.todos[i], done: checked})
        }
    },
    delDone(state){
        console.log("delDone:", state.todos)
        const todoss = state.todos.filter(item => !item.done);
        // 有两个值时,第一个值为删除的位置,第二个值为删除的个数;
        state.todos.splice(0,state.todos.length)
        // 有三个或者多个值时,第一个值为插入元素的位置,第二个值为替换的个数,后面的值都为插入的新元素;
        state.todos.splice(0,todoss.length,...todoss)
    },
    editTodo(state, [id, label]){
        const index = state.todos.findIndex(item => item.id === id);
        state.todos[index].label = label;
    }
};
const getters = {
    done(state) {
        return state.todos.filter(item => item.done).length;
    },
    total(state) {
        return state.todos.length;
    }
};
// 创建一个新的 store 实例
const store = createStore({
    state,
    mutations,
    getters
});
export default store;
使用
main.js
app.use(store);
./src/App.vue
<template>
  <Layout/>
</template>
<script setup>
import { reactive, provide ,watch, getCurrentInstance} from 'vue'
import Layout from "@/components/Layout.vue";
import {nanoid} from 'nanoid';
import { useStore } from 'vuex'
const proxy = getCurrentInstance();
console.log(proxy);
// const readStore = proxy.$store.state.todos;
const store = useStore()
const readStore = store.state.todos;
const todos = reactive(readStore)
watch(todos, (todos, prevTodos) => {
  store.commit('saveTodos', todos)
},{ deep: true })
</script>
<style>
</style>
./src/components/MyFooter.vue
<script setup>
import {ref, inject, getCurrentInstance, watch, computed} from 'vue'
import {useStore, mapGetters} from "vuex";
const store = useStore();
const todos = store.state.todos
// 使用computed保留响应式
const done = computed(()=>{
  return store.getters.done
});
const total = computed(()=>{
  return store.getters.total
})
// const delDone = inject('delDone');
const checked = ref(false)
// const done = ref(todos.filter(item => item.done).length)
// const total = ref(todos.length)
//
// watch(todos,(todos, prevTodos) => {
//     console.log("todos Change", todos);
//     console.log("done", done);
//     console.log("total", total);
//
//     done.value = todos.filter(item => item.done).length;
//     total.value = todos.length;
//
//     },
//     { deep: true }
// )
function allChecked() {
  store.commit("allChecked",checked.value);
  // checked.value =!checked.value;
}
function delDone(){
  store.commit("delDone");
  checked.value = false;
}
</script>
<template>
  <el-row :gutter="5">
    <el-col :span="10" >
      <el-checkbox label="全部完成" @change="allChecked()" v-model="checked"/>
    </el-col>
    <el-col class="mx-0" :span="10" >
      <el-text class="mx-1" size="default">完成情况:{{done}}/{{total}}</el-text>
    </el-col>
    <el-col :span="4">
      <el-button type="danger" size="small" @click="delDone">Del Done</el-button>
    </el-col>
  </el-row>
</template>
<style scoped>
.el-row {
  height: 100%;
  padding: 2px 2px;
}
.el-button{
  font-weight: normal;
  margin: 0 auto;
  width: 100%;
  height: 100%;
  text-align: center;
}
.mx-0 {
  text-align: center;
}
.mx-1 {
  height: 100%;
  width: 100%;
  margin: 0 auto;
}
</style>
使用computed保留getters响应式
// 使用computed保留响应式
const done = computed(()=>{
  return store.getters.done
});
const total = computed(()=>{
  return store.getters.total
})



















