Vue——May(1)

VUE

  • 一、vue基础
    • 1.1 引用
    • 1.2 基础应用
    • 1.3 模板语法
    • 1.4 数据处理
    • 1.5 el与 .$mount
    • 1.6 data的函数式写法
    • 1.7 架构模型——MVVM模型
    • 1.8 数据代理Object.defineproperty
    • 1.9 理解数据代理
    • 1.10 事件处理
      • 1.10.1 参数
      • 1.10.2 this
      • 1.10.3 简写
      • 1.10.4 传参
    • 1.11 事件修饰符
    • 1.12 键盘事件
    • 1.13 *姓名案例
    • 1.14 计算属性
      • 简写(只读不写数据时使用)
    • 1.15 *天气案例
    • 1.16 监视属性watch
      • 简写
    • 1.17 深度监视
      • 1.17.1 监测多级结构中某个属性的变化
      • 1.17.2 监测多级结构中所有属性的变化
    • 1.18 计算&监听(名字案例)
      • only watch can do delay
    • 1.19 绑定class样式
      • 1.19.1 字符串写法
      • 1.19.1数组写法
      • 1.19.3 对象写法
    • 1.20 style样式绑定
    • 1.21 条件渲染
      • 1.21.1 v-show
      • 1.21.2 v-if 、 v-else-if 、 v-else
    • 1.22 列表渲染
      • 1.22.1 遍历数组
      • 1.22.2 遍历对象
    • 1.23 key的作用与原理
    • 1.24 列表过滤
      • 1.24.1用watch实现
      • 1.24.2 用computed实现
    • 1.25 列表排序
    • 1.26 vue监测数据改变的原理
      • 1.26.1监测对象改变
      • 1.26.2监测数组改变
        • * 过滤后新放入一个数组
      • 1.26.3 总结监视数据
    • 1.27 set()
    • 1.28 收集表单数据
    • 1.29 过滤器(时间戳)
      • (2)局部过滤器
      • (2)全局过滤器
    • 1.30 指令
      • v-text指令
      • v-html指令
      • *cookie简略图示
      • v-cloak指令
      • v-once指令
      • v-pre指令
    • 1.31自定义指令
      • 变成全局式指令
      • 函数式
      • 对象式
  • 二、Vue核心
    • 2.1 生命周期(回调函数)
        • * 透明度案例
    • 2.2 组件
      • 2.2.1 非单文件组件 .js
      • (1)组件的嵌套
      • (2)Vuecomponent
      • (3)vue实例与组件实例
      • (4)重要的内置关系
      • 2.2.2 单文件组件 .vue
        • (1) 快速创建
  • 三、vue-cli
    • 3.1 安装应用
    • 3.2 分析vue-cli结构
    • 3.3 ref
    • 3.4 props
    • 2.5 mixin 混入
    • 2.6 plugins插件
    • 2.7 scoped 样式
    • 2.8 less 嵌套
    • * 案例todolist
      • (1)静态准备
      • (2) 添加
        • a、 儿子给父亲传信
      • (3) 勾选
      • (4)删除
      • (5)底部互动
        • (6) 总结
    • 2.9 webStorage
      • 2.9.1 localStorage
        • 创建
        • 读取删除
        • 清空
      • 2.9.2 sessionStorage
    • * todo本地存储
    • 2.10 组件的自定义事件
      • 绑定
      • 解绑
      • 总结
    • * todo子给父传
    • 2.11全局事件总线
    • 2.12消息订阅与发布
    • * todo案例编辑
    • 2.13 nextTick
    • 2.14 css样式
      • (1) 多个元素过渡
      • (2)动画库

常见报错

  1. 没加
    Vue——May(1)_第1张图片
  2. 数据外面没有data:{ }包裹
    Vue——May(1)_第2张图片

一、vue基础

特点:

  • 采用组件化模式,提高代码复用率,更好维护
  • 声明式编码,无需直接操作DOM,提高开发效率

1.1 引用

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>

关闭报错:
Vue——May(1)_第3张图片

1.2 基础应用

Vue——May(1)_第4张图片
Vue——May(1)_第5张图片

<body>
    <div id="fang">hello,{{name}},{{age}}</div>
    <script>
        new Vue({
            el:'#fang',
            data:{
                name:'haiying'
            }
        })
    </script>
</body>

Vue——May(1)_第6张图片

1.3 模板语法

Vue——May(1)_第7张图片
Vue——May(1)_第8张图片

也就是相当于要读取 url这个变量
v-bind:可简写为:

<body>
    <div id="fang" >hello,{{name}},{{detail.sex}}</div>
    <a v-bind: href="url">dianwo</a>
    <script>
        new Vue({
            el:'#fang',
            url:'https://v2.vuejs.org/v2/guide/deployment.html',
            data:{
                name:'haiying',
                detail:{
                    age:18,
                    sex:'female'
                }
            }
        })
    </script>
</body>

Vue——May(1)_第9张图片

1.4 数据处理

不是所有标签都能使用v-model,只能用于表单元素

<body>
    <div id="fang">
        单向绑定 <input type="text" v-bind:value="name">
        双向绑定 <input type="text" v-model="name">
    </div>
   <script>
    new Vue({
        el:'#fang',
        data:{
            name:'haiying'
        }
    })
   </script>
</body>

1.5 el与 .$mount

在这里插入图片描述

<body>
    <div id="fang">
        {{name}}
    </div>
   <script>
    const a=new Vue({
        // el:'#fang',
        data:{
            name:'haiying'
        }
    })
    setTimeout(()=>{
        a.$mount('#fang')  
    },1000) 
   </script>
</body>

使用¥mount更灵活,上述代码意思为刷新页面1s后,挂载vue实例
Vue——May(1)_第10张图片

1.6 data的函数式写法

这种写法必须有返回值,返回值便是想要的(组件式写法必须用函数式)
Vue——May(1)_第11张图片

  • 可以简写为data(){ }
  • 但是不能写成箭头式,这样的this指向windows,而不是指向vue这个对象
<body>
    <div id="fang">
        {{name}}
    </div>
   <script>
    const a=new Vue({
        el:'#fang',
        // data:{
        //     name:'haiying'
        // }
        data:function(){
            return{
                name:'haiying'
            }
        }
    })
    
   </script>
</body>

1.7 架构模型——MVVM模型

Vue——May(1)_第12张图片
Vue——May(1)_第13张图片
Vue——May(1)_第14张图片
Vue——May(1)_第15张图片

实例中有的属性可以直接在插入模板中使用

<body>
    <div id="fang">
        {{$options}}
    </div>
   <script>
    const a=new Vue({
        el:'#fang',
        // data:{
        //     name:'haiying'
        // }
        data:{}
    })
    
   </script>
</body>

Vue——May(1)_第16张图片


1.8 数据代理Object.defineproperty

非直接写有以下效果
Vue——May(1)_第17张图片

<body>
    <script>
        let person={
            name:'lucy',
            sex:'female',
        }
        // 给person对象,添加age属性,{}内的为配置项
        Object.defineProperty(person,'age',{
            value:18
        })
        console.log(person)
    </script>
</body>

输入 console.log(Object.keys(person))

Vue——May(1)_第18张图片

        for(let i in person){
            console.log('#',person[i])
        }

Vue——May(1)_第19张图片


<body>
    <script>
        let person={
            name:'lucy',
            sex:'female',
        }
        // 给person对象,添加age属性,{}内的为配置项
        Object.defineProperty(person,'age',{
            value:18,
            enumerable:true //表示可被枚举的
        })
        for(let i in person){
            console.log('#',person[i])
        }
    </script>
</body>

Vue——May(1)_第20张图片

当有人读取person的age属性时,get函数就会被调用,且返回值就是age的值
Vue——May(1)_第21张图片

<script>
        let number=18
        let person={
            name:'lucy',
            sex:'female',
        }
        // 给person对象,添加age属性,{}内的为配置项
        Object.defineProperty(person,'age',{
           get:function(){
            console.log('age被读取才会显示age值')
            return number
           }
        })
        console.log(person)
        
    </script>

Vue——May(1)_第22张图片

1.9 理解数据代理

在这里插入图片描述

    <script>
        let obj1={x:100}
        let obj2={y:200}
        Object.defineProperty(obj2,'x',{
            get(){
                console.log('通过obj2代理,读取obj1的数据')
                return obj1.x
            },
            set(value){
                obj1.x=value
            }
        })
    </script>

Vue——May(1)_第23张图片
Vue——May(1)_第24张图片


Vue——May(1)_第25张图片
Vue——May(1)_第26张图片
Vue——May(1)_第27张图片

数据劫持=>响应式

1.10 事件处理

Vue——May(1)_第28张图片

<body>
    <div id="fang">
        <h2>hello,{{name}}</h2>
        <button v-on:click="show">点我提示信息</button>
    </div>
    <script>
      new Vue({
        el:'#fang',
        data:{
            name:'lucy'
        },
        methods:{
            show(){
                alert('你好')
            }
        }
      })
    </script>
</body>

Vue——May(1)_第29张图片

1.10.1 参数

参数就是此事件
Vue——May(1)_第30张图片

1.10.2 this

此处的this就是vm实例Vue——May(1)_第31张图片
箭头函数的this是window

被vue管理的函数最好写成普通函数,不要写箭头函数

1.10.3 简写

Vue——May(1)_第32张图片
v-on=>@

1.10.4 传参

Vue——May(1)_第33张图片
想传参就加小括号

1.11 事件修饰符

可以连续写
Vue——May(1)_第34张图片

阻止网页跳转的默认行为:

    <body>
    <div id="fang" @click="show">
        <a @click="show" href="https://mp.csdn.net/mp_blog/creation/success/129392286">blog</a>
    </div>
    <script>
      new Vue({
        el:'#fang',
        data:{
            name:'lucy'
        },
        methods:{
            show(){
                alert('点完确定后,会跳转到a链接的网站处')
            }
        }
      })
    </script>
</body>

Vue——May(1)_第35张图片

在这里插入图片描述

阻止冒泡

<body>
    <div id="fang" @click="show">
        <button @click.stop="show" >blog</button>
    </div>
    <script>
      new Vue({
        el:'#fang',
        data:{
            name:'lucy'
        },
        methods:{
            show(){
                alert('因为给div下的button添加了.stop事件修饰符,所以此弹窗只弹一次,不会触及div的冒泡机制')
            }
        }
      })

Vue——May(1)_第36张图片

1.12 键盘事件

Vue——May(1)_第37张图片
key按键名
keyCode按键编码

<body>
    <div id="fang">
        <input type="text" placeholder="按键盘 输出键名及其编码" @keyup="show">
    </div>
    <script>
        new Vue({
            el:'#fang',
            methods:{
                show(e){console.log(e.key,e.keyCode)
                }
            }
        })
    </script>
</body>

Vue——May(1)_第38张图片

tab键 在按下不抬起时,便可以转移光标(焦点),所以要绑定@keydown.tab

<body>
    <div id="fang">
        <input type="text" placeholder="按下回车键 提示输入信息" @keyup.enter="show">
    </div>
    <script>
        new Vue({
            el:'#fang',
            methods:{
                show(e){
                    console.log(e.target.value)
                }
            }
        })
    </script>
</body>

Vue——May(1)_第39张图片

按ctrl+y才会输出信息在这里插入图片描述

1.13 *姓名案例

1.插值语法实现

Vue——May(1)_第40张图片

v-model后的相当于变量,变量的内容存在data里

<body>
    <div id="fang">
        姓:<input type="text" v-model="surName" >
        <br>
        名:<input type="text" v-model="name">
        <br>
        全名:{{surName.slice(0,3)}}-{{name}}
    </div>
    <script>
       
        new Vue({
            el:'#fang',
            data:{
                surName:'吴臭屁屁屁',
                name:'小平'
            }
        })
    </script>
</body>

2.methods方法实现
Vue——May(1)_第41张图片
Vue——May(1)_第42张图片

    <div id="fang">
        姓:<input type="text" v-model="surName" >
        <br>
        名:<input type="text" v-model="name">
        <br>
        全名:{{fullname()}}
    </div>
    <script>
       
        new Vue({
            el:'#fang',
            data:{
                surName:'吴臭屁屁屁',
                name:'小平'
            },
            methods:{
                fullname(){
                    return this.surName+'-'+this.name
                }
            }
            
        })

会重新解析模板

1.14 计算属性

Vue——May(1)_第43张图片

Vue——May(1)_第44张图片

<body>
    <div id="fang">
        姓:<input type="text" v-model="surName" >
        <br>
        名:<input type="text" v-model="name">
        <br>
        全名:{{fullname}}
        <br>
        全名:{{fullname}}
        <br>
        全名:{{fullname}}
        <br>
        全名:{{fullname}}
        <br>
    </div>
    <script>
        const vm=new Vue({
            el:'#fang',
            data:{
                surName:'吴臭屁屁屁',
                name:'平'
            },
            computed:{
                //fullname是计算属性
                fullname:{
  //当有人读取fullname时,get就会被调用,且返回值就作为fullname的值
                    get(){
                        console.log('get被调用了')
                        return this.surName+'-'+this.name
                    }
                }
            }
        })
    </script>
</body>

在这里插入图片描述

Vue——May(1)_第45张图片

  • get被调用的时机:
  • 初次读取fullname时
  • 所依赖的数据发生改变时

Vue——May(1)_第46张图片

Vue——May(1)_第47张图片

<body>
    <div id="fang">
        姓:<input type="text" v-model="surName" >
        <br>
        名:<input type="text" v-model="name">
        <br>
        全名:{{fullname}}
        <br>
        全名:{{fullname}}
        <br>
        全名:{{fullname}}
        <br>
        全名:{{fullname}}
        <br>
    </div>
    <script>
        const vm =new Vue({
            el:'#fang',
            data:{
                surName:'吴臭屁屁屁',
                name:'平'
            },
            computed:{
                //fullname是计算属性
                fullname:{
                    get(){
                        console.log('get被调用了')
                        return this.surName+'-'+this.name
                    },
                    set(value){
                        const arr=value.split('-')
                        this.surName=arr[0]
                        this.name=arr[1]
                    }
                }
            }
        })
    </script>
</body>

Vue——May(1)_第48张图片

简写(只读不写数据时使用)

<body>
    <div id="fang">
        姓:<input type="text" v-model="surName" >
        <br>
        名:<input type="text" v-model="name">
        <br>
        全名:{{fullname}}
        <!-- 简写形式,即使fullname看起来像函数,后面也不要加小括号
        以上{{fullname}}实际上表示的是fullname执行后的值 -->
    </div>
</body>
    <script>
        const vm =new Vue({
            el:'#fang',
            data:{
                surName:'吴臭屁屁屁',
                name:'平'
            },
            computed:{
                fullname(){
                    // 上面一行代码相当于
                    // fullname:function(){
                    console.log('这一大块就相当于getter,我又被运行啦,运行结果是',this.surName+'-'+this.name)
                    return this.surName+'-'+this.name
                }
            }
        })
    </script>

Vue——May(1)_第49张图片

1.15 *天气案例

Vue——May(1)_第50张图片

<body>
    <div id="fang">
        <h2>今天天气很{{wea}}</h2>
        <button @click="changeweather">點我切換天气</button>
    </div>
</body>
<script>
const vm=new Vue({
    el:'#fang',
    data:{
        ishot:true,
    },
    computed:{
        wea(){
            return this.ishot?'炎热':'凉爽'
        }
    },
    methods: {
        changeweather(){
            this.ishot=!this.ishot
        }
    }
})
</script>

1.16 监视属性watch

Vue——May(1)_第51张图片

<body>
    <div id="fang">
        <h2>今天天气很{{wea}}</h2>
        <button @click="changeweather">點我切換天气</button>
    </div>
</body>
<script>
const vm=new Vue({
    el:'#fang',
    data:{
        ishot:true,
    },
    computed:{
        wea(){
            return this.ishot?'炎热':'凉爽'
        }
    },
    methods: {
        changeweather(){
            this.ishot=!this.ishot
        }
    },
    //监视data数据中ishot的值
    watch:{
        //要监视的对象
        ishot:{
            //当ishot发生改变时,handler被调用
            handler(a,b){
                console.log('ishot被修改了',a,b)
            }
        }
    }
})
</script>
body部分不變,script另一種写法

<script>
const vm=new Vue({
    el:'#fang',
    data:{
        ishot:true,
    },
    computed:{
        wea(){
            return this.ishot?'炎热':'凉爽'
        }
    },
    methods: {
        changeweather(){
            this.ishot=!this.ishot
        }
    },
})
vm.$watch('ishot',{
    handler(a,b){
        console.log('ishot被修改了',a,b)
    }
})

Vue——May(1)_第52张图片

简写

配置项中只含有handler

<script>
const vm=new Vue({
    el:'#fang',
    data:{
        ishot:true,
    },
    computed:{
        wea(){
            return this.ishot?'炎热':'凉爽'
        }
    },
    methods: {
        changeweather(){
            this.ishot=!this.ishot
        }
    },
    watch:{
        ishot(a,b){
                console.log('ishot被修改了',a,b)
            }
        }
})
</script>
vm.$watch('ishot',function(a,b){
console.log('ishot被修改了',a,b)
})

1.17 深度监视

Vue——May(1)_第53张图片

1.17.1 监测多级结构中某个属性的变化

<body>
    <div id="fang">
        <h2>a的值是{{numbers.a}}</h2>
        <button @click="numbers.a++">點我让a +1</button>
    </div>
</body>
<script>
const vm=new Vue({
    el:'#fang',
    data:{
       numbers:{
        a:1,
        b:1
       }
    },
    computed:{

    },
    methods: {
    
    },
    //目的:只监测a,but回调函数不能直接监视a,需要通过numbers
    watch:{
        'numbers.a':{
            handler(){
                console.log('a的值改变为',this.numbers.a)
            }
        }
    }
})
</script>

Vue——May(1)_第54张图片

1.17.2 监测多级结构中所有属性的变化

    <div id="fang">
        <h2>a的值是{{numbers.a}}</h2>
        <button @click="numbers.a++">點我让a +1</button>
        <h2>b的值是{{numbers.b}}</h2>
        <button @click="numbers.b++">點我让b +1</button>
    </div>
</body>
<script>
const vm=new Vue({
    el:'#fang',
    data:{
       numbers:{
        a:1,
        b:1
       }
    },
    computed:{
    },
    methods: {
    },
    //目的:只监测a,but回调函数不能直接监视a,需要通过numbers
    watch:{
        numbers:{
            deep:true,
            handler(){
                console.log('number改变了')
            }
        }
    }
})
</script>

Vue——May(1)_第55张图片

1.18 计算&监听(名字案例)

Vue——May(1)_第56张图片

<body>
    <div id="fang">
        姓:<input type="text" v-model="surName" >
        <br>
        名:<input type="text" v-model="name">
        <br>
        全名:{{fullname}}
    </div>
</body>
    <script>
        const vm =new Vue({
            el:'#fang',
            data:{
                surName:'吴臭屁屁屁',
                name:'平',
                fullname:'吴臭屁屁屁-平'
            },
            watch:{
                surName(newSur,oldSur){
                    this.fullname=newSur+'-'+this.name
                    console.log(newSur,oldSur)
                },
                name(newname,oldname){
                    this.fullname=this.surName+'-'+newname
                    console.log(newname,oldname)
                }
            }
            // computed:{
            //     fullname(){
            //         // 上面一行代码相当于
            //         // fullname:function(){
            //         console.log('这一大块就相当于getter,我又被运行啦,运行结果是',this.surName+'-'+this.name)
            //         return this.surName+'-'+this.name
            //     }
            // }
        })
    </script>

Vue——May(1)_第57张图片

only watch can do delay

计算属性中,不可以开启异步任务

<body>
    <div id="fang">
        姓:<input type="text" v-model="surName">
        <br>
        名:<input type="text" v-model="name">
        <br>
        全名:{{fullname}}
    </div>
</body>
<script>
    const vm = new Vue({
        el: '#fang',
        data: {
            surName: '吴臭屁屁屁',
            name: '平',
            fullname: '吴臭屁屁屁-平'
        },
        watch: {
            surName(newSur, oldSur) {
                setTimeout(()=>{
                //此时,箭头函数没有自己的this,只能向外找,找到surName这个普通函数,并且这个surName由vue管理,所以,this指向这个vm实例
                    this.fullname = newSur + '-' + this.name
                    console.log("新值",newSur,"旧值" ,oldSur)
                }, 1000)
                // 下面写法页面中的全名不改变
                // setTimeout(function() {
                定时器是由js调的,这里的this指向window
                //     this.fullname = newSur + '-' + this.name
                //     console.log(newSur, oldSur)
                // }, 1000)
            },
            name(newname, oldname) {
                this.fullname = this.surName + '-' + newname
                console.log(newname, oldname)
            }
        }
        
    })
</script>

Vue——May(1)_第58张图片

1.19 绑定class样式

Vue——May(1)_第59张图片

style部分

<style>
        .font{
            color: rgb(205, 215, 241);
            font-size: 30px;
        }
        .border{
            border-radius: 50px;
        }
        .back{
            background-color: rgb(143, 99, 106);
        }
        .shadow{
            box-shadow: 5px 5px 5px black;
        }
        .basic{
            width: 200px;
            height: 100px;
            border: 1px solid black;
        }
        .textlocate{
            text-align: center;
        }
        .green{
            background-color: aquamarine;
        }
    </style>

1.19.1 字符串写法

<body>
   <div id="fang">
    <!-- 加:后,“”中的值为变量 -->
    <div :class="added" class="basic" @click="changeCss">{{name}}</div>
   </div>
</body>
<script>
   new Vue({
    el:"#fang",
    data:{
        name:'hello Vue',
        added:''
    },
    methods:{
        changeCss(){
            //点击后加上指定的样式
            this.added='textlocate back'
        }
    }
   })
</script>

Vue——May(1)_第60张图片

1.19.1数组写法

<body>
   <div id="fang">
    <!-- 加:后,“”中的值为变量 -->
    <div :class="addArr" class="basic" @click="changeCss">{{name}}</div>
   </div>
</body>
<script>
   vm=new Vue({
    el:"#fang",
    data:{
        name:'hello Vue',
        addArr:['textlocate','font','back','border','shadow']
    },
    methods:{
        changeCss(){
        //每点击div一次,去掉一个class样式
          this.addArr.pop()
        }
    }
   })
</script>

Vue——May(1)_第61张图片
Vue——May(1)_第62张图片
添加
Vue——May(1)_第63张图片

1.19.3 对象写法

<body>
   <div id="fang">
    <!-- 加:后,“”中的值为变量 -->
    <div :class="addObj" class="basic">{{name}}</div>
   </div>
</body>
<script>
   vm=new Vue({
    el:"#fang",
    data:{
        name:'hello Vue',
        addObj:{
            back:true,
            textlocate:true
        }
    }
   })
</script>

1.20 style样式绑定

Vue——May(1)_第64张图片

对象,数组写法

<body>
    <div id="fang">
        <!-- 加:后,“”中的值为变量 -->
        <div :style="styObj" class="basic">{{name}}</div>
    </div>
</body>
<script>
    vm = new Vue({
        el: "#fang",
        data: {
            name: 'hello Vue',
            styObj:{
                fontSize:'30px',
                color:'red',
                backgroundColor:'blue'
                // backgroundcolor:'blue' 这个写法是错误的
            }
        }
    })
</script>

Vue——May(1)_第65张图片
数组

<script>
    vm = new Vue({
        el: "#fang",
        data: {
            name: 'hello Vue',
            styArr:[
            {
                fontSize:'30px',
                color:'red',
                backgroundColor:'blue'
                // backgroundcolor:'blue' 这个写法是错误的
            },
            {
                borderRadius:'20px'
            }
            ]
        }
    })
</script>

Vue——May(1)_第66张图片

1.21 条件渲染

Vue——May(1)_第67张图片

1.21.1 v-show

也就是调整display
两种写法

<body>
    <div id="fang">
        <h2 v-show="false"> 欢迎~</h2>
        //写成表达式也可以
        //

欢迎~

</div> </body> <script> vm = new Vue({ el: "#fang", }) </script>
<body>
    <div id="fang">
        <h2 v-show="a"> 欢迎~</h2>
    </div>
</body>
<script>
    vm = new Vue({
        el: "#fang",
        data:{
            a:false
        }
    })
</script>

Vue——May(1)_第68张图片

1.21.2 v-if 、 v-else-if 、 v-else

直接决定这个元素是否存在
data中的数据发生改变,整个模板重新解析
template包裹元素,不会破坏结构,只能与v-if配合使用

<body>
    <div id="fang">
        <h2>当前的n值是{{n}}</h2>
        <button @click="n++">点我 n+1</button>
        <template v-if="n===1">
            <!-- 想让以下三个元素同时出现或者消失,但是不破坏结构 -->
            <h3>n=1时,出现以下内容</h3>
            <h3>hello</h3>
            <h3>vue</h3>
            </template>
    </div>
    
</body>
<script>
    vm = new Vue({
        el: "#fang",
        data:{
            n:0
        }
    })
</script>

Vue——May(1)_第69张图片

1.22 列表渲染

想生成谁,就在谁身上放指令

在这里插入图片描述
:key让每个li都有唯一的标识
![在这里插入图片描述](https://img-blog.csdnimg.cn/8ec4796819244731a706f80ccab689fe.png

1.22.1 遍历数组

<body>
    <div id="fang">
        <ul>人员列表
            <li v-for="p in persons" :key="p.id">
                {{p.name}}-{{p.age}}
            </li>
        </ul>
    </div>
    
</body>
<script>
    vm = new Vue({
        el: "#fang",
        data:{
            persons:[
                {id:1,name:'Lucy',age:18},
                {id:2,name:'Bob',age:19},
                {id:3,name:'Jack',age:19},
            ]
        }
    })
</script>

Vue——May(1)_第70张图片

<body>
    <div id="fang">
        <ul>人员列表
            <li v-for="(p,index) in persons" :key="index">
                {{p.name}}-{{p.age}}[{{index}}]
            </li>
        </ul>
    </div>
    
</body>
<script>
    vm = new Vue({
        el: "#fang",
        data:{
            persons:[
                {id:1,name:'Lucy',age:18},
                {id:2,name:'Bob',age:19},
                {id:3,name:'Jack',age:19},
            ]
        }
    })
</script>

Vue——May(1)_第71张图片

1.22.2 遍历对象

<body>
    <div id="fang">
        <ul>人员信息
            <li v-for="(value,k) in persons" :key="k">
                键:{{k}} ——值:{{value}}
            </li>
        </ul>
    </div>
    
</body>
<script>
    vm = new Vue({
        el: "#fang",
        data:{
            persons:{
                id:1,
                name:'Lucy',
                age:18}
        }
    })
</script>

Vue——May(1)_第72张图片
还可以遍历字符串——遍历每个字母
遍历指定次数

1.23 key的作用与原理

Vue——May(1)_第73张图片

Vue——May(1)_第74张图片

index作为key

Vue——May(1)_第75张图片
效率低的原因
Vue——May(1)_第76张图片

id作为key
Vue——May(1)_第77张图片

index为key

<body>
    <div id="fang">
        <button @click="add">点我新增一个人</button> 
        <ul>人员列表
            <li v-for="(p,index) in persons" :key="index">
                {{p.name}}-{{p.age}}
                <input type="text">
            </li>
        </ul>
    </div>
    
</body>
<script>
    vm = new Vue({
        el: "#fang",
        data:{
            persons:[
                {id:1,name:'Lucy',age:18},
                {id:2,name:'Bob',age:19},
                {id:3,name:'Jack',age:19},
            ]
        },
        methods:{
            add(){
                const newP={id:4,name:'小吴',age:21}
                //在数组的前面加一个元素
                this.persons.unshift(newP)
            }
        }
    })
</script>

Vue——May(1)_第78张图片

  • ,不会出现问题
    Vue——May(1)_第79张图片

    注意不能写成:key="id",否则会报错Vue——May(1)_第80张图片

    1.24 列表过滤

    1.24.1用watch实现

    <body>
        <div id="fang">
            <input v-model="keywords" type="text" placeholder="请输入要查询的关键词">
            <ul>人员列表
                <br>
                <li v-for="p in filterPersons" :key="p.id">
                    {{p.name}}-{{p.age}}-{{p.sex}}
                </li>
            </ul>
        </div>
    </body>
    <script>
        vm = new Vue({
            el: "#fang",
            data: {
                persons: [
                    { id: 1, name: '小张', age: 18, sex: '女' },
                    { id: 2, name: '张三', age: 19, sex: '男' },
                    { id: 3, name: '张张', age: 19, sex: '男' },
                    { id: 4, name: '三三', age: 18, sex: '女' },
                    { id: 5, name: '小吴', age: 19, sex: '男' },
                ],
                keywords: '',
                filterPersons: []
            },
            watch: {
                //侦听keywords,返回值keyword为输入框更改后的值
                keywords: {
                    // 自动调用一次handler,若没有下面这条代码,则初始什么都不显示
                    //因为indexOf 什么都不输入 的结果总是0 所以相当于过滤了一次
                    immediate: true,
                    //keyword为输入框更改后的值
                    handler(keyword) {
                        //filter返回的是一个新数组,所有要重新赋值,但是不能直接改原数组,否则会造成数据丢失,越过滤越少
                        this.filterPersons = this.persons.filter((p) => {
                            //选择列表中 与关键词可以匹配的
                            //indexOf方法 返回-1时就是找不到
                            return p.name.indexOf(keyword) !== -1
                        })
                        console.log('value被改成了', keyword)
                    }
    
                }
            }
        })
    
    </script>
    

    Vue——May(1)_第81张图片
    Vue——May(1)_第82张图片

    1.24.2 用computed实现

    注意
    Vue——May(1)_第83张图片

    <body>
        <div id="fang">
            <input v-model="keywords" type="text" placeholder="请输入要查询的关键词">
            <ul>人员列表
                <br>
                <li v-for="p in filterPersons" :key="p.id">
                    {{p.name}}-{{p.age}}-{{p.sex}}
                </li>
            </ul>
        </div>
    </body>
    <script>
        vm = new Vue({
            el: "#fang",
            data: {
                persons: [
                    { id: 1, name: '小张', age: 18, sex: '女' },
                    { id: 2, name: '张三', age: 19, sex: '男' },
                    { id: 3, name: '张张', age: 19, sex: '男' },
                    { id: 4, name: '三三', age: 18, sex: '女' },
                    { id: 5, name: '小吴', age: 19, sex: '男' },
                ],
                keywords: '',
            },
            computed:{
                filterPersons() {
                    //此return是filter'需要返回的
                    return this.persons.filter((p) => {
                        //此return是计算属性需要返回的
                        //直接拿用户输入的值
                        return p.name.indexOf(this.keywords) !== -1
                    })
                }
            },
    
        })
    
    </script>
    

    Vue——May(1)_第84张图片

    1.25 列表排序

    sort的使用
    Vue——May(1)_第85张图片

    <body>
        <div id="fang">
            <input v-model="keywords" type="text" placeholder="请输入要查询的关键词">
            <button @click="sortType=1">年龄升序</button>
            <button @click="sortType=-1">年龄降序</button>
            <button @click="sortType=0">原顺序</button>
            <ul>人员列表
                <br>
                <li v-for="p in filterPersons" :key="p.id">
                    {{p.name}}-{{p.age}}-{{p.sex}}
                </li>
            </ul>
        </div>
    </body>
    <script>
        vm = new Vue({
            el: "#fang",
            data: {
                persons: [
                    { id: 1, name: '小张', age: 18, sex: '女' },
                    { id: 2, name: '张三', age: 23, sex: '男' },
                    { id: 3, name: '张张', age: 19, sex: '男' },
                    { id: 4, name: '三三', age: 18, sex: '女' },
                    { id: 5, name: '小吴', age: 21, sex: '男' },
                ],
                keywords: '',
                sortType:0
            },
            computed:{
                filterPersons() {
                    //注意排序时,不要着急返回
                    //先把过滤了关键词的保到一个数组中
                    const arr= this.persons.filter((p) => {
                        //此return是计算属性需要返回的
                        //直接拿用户输入的值
                        return p.name.indexOf(this.keywords) !== -1
                    })
                    //判断是否需要排序,如下,意为sortType!==0
                    if(this.sortType){
                        //对过滤好的数组进行排序
                        arr.sort((a,b)=>{
                            //当sortType不等于0时,1为升序,其他为降序(-1)
                            return this.sortType===1?a.age-b.age:b.age-a.age
                        })
                    }
                    //返回排好序的
                    return arr
                }
            },
    
        })
    
    </script>
    

    Vue——May(1)_第86张图片

    1.26 vue监测数据改变的原理

    <body>
        <div id="fang">
            <button @click="updateL">点我更新lucy的数据</button>
            <ul>人员列表
                <li v-for="p in persons" :key="p.id">
                    {{p.name}}-{{p.age}}
                </li>
            </ul>
        </div>
        
    </body>
    <script>
        vm = new Vue({
            el: "#fang",
            data:{
                persons:[
                    {id:1,name:'Lucy',age:18},
                    {id:2,name:'Bob',age:19},
                    {id:3,name:'Jack',age:19},
                ]
            },
            methods:{
                updateL(){
                    // this.persons[0].name='L-Lucy'
                    // this.persons[0].age=22
                    //下面这个方法实现,在页面上不奏效
                    this.persons[0]={name:'L-lucy',age:22}
                }
            }
        })
    </script>
    

    Vue——May(1)_第87张图片

    解决方法Vue——May(1)_第88张图片

    1.26.1监测对象改变

    Vue——May(1)_第89张图片
    Vue——May(1)_第90张图片
    会为每个对象都配置setter和getter,不论是嵌套里面的还是数组里面的

    1.26.2监测数组改变

    不给数组添加set,get,所以数组里的数据改变,不是响应式,无法影响到页面
    Vue——May(1)_第91张图片
    只有能够影响到原数组的方法被调用,才可以检测到数据变化
    Vue——May(1)_第92张图片

    <body>
        <div id="fang">
            学生信息:
            <button @click="addSex">点我 添加性别属性</button>
            <br>
            姓名:{{stu.name}}
            <br>
            年龄:{{stu.age}}
            <br>
            <span v-if="stu.sex">性别:{{stu.sex}}</span>
            <ul>爱好1:
                <li v-for="(h1,index) in stu.hobby1" :key="index">
                    {{h1}}
            </ul>
            <ul>爱好2:
                <li v-for="(h2,index) in stu.hobby2" :key="index">
                    {{h2}}
            </ul>
        </div>
    </body>
    <script>
        vm = new Vue({
            el: "#fang",
            data: {
                stu: {
                    name: '张三三',
                    age: 18,
                    // hobby1:{
                    //     h1:'看书',
                    //     h2:'刷视频',
                    //     h3:'运动',
                    // },
                    hobby2:['看书','刷视频','运动']
                }
            },
            methods:{
                addSex(){
                    Vue.set(this.stu,'sex','女')
                }
            }
        })
    </script>
    

    Vue——May(1)_第93张图片

    我们刚刚调用的push,pop已经不是 原来的数组原型
    Vue——May(1)_第94张图片

    里的了

    * 过滤后新放入一个数组

    Vue——May(1)_第95张图片

    1.26.3 总结监视数据

    Vue——May(1)_第96张图片

    按要求自己写的

    <body>
        <div id="fang">
            学生信息:<br>
            <button @click="addAge">年龄加1</button>
            <button @click="addSex">添加性别:男</button>
            <button @click="addFri">在列表首位添加一个朋友</button>
            <br>
            <button @click="addHob">添加一个爱好</button>
            <button @click="reHob">修改第一个爱好为:开车</button>
            <br>
            姓名:{{stu.name}}
            <br>
            年龄:{{stu.age}}
            <br>
            <span v-if="n===1">性别:{{stu.sex}}</span>
            <br>
            <ul>爱好:
                <li v-for="(h,index) in stu.hobby" :key="index">
                    {{h}}
            </ul>
        </div>
    </body>
    <script>
        vm = new Vue({
            el: "#fang",
            data: {
                stu: {
                    name: '张三三',
                    age: 18,
                    hobby:['看书','刷视频','运动']
                },
                n:'',
            },
            methods:{
                addAge(){
                    this.stu.age++
                },
                addSex(){
                    this.n=1
                    Vue.set(vm.stu,'sex','男')
                },
                addFri(){
                    this.stu.hobby.unshift('张三')
                },
                addHob(){
                    this.stu.hobby.push('学习')
                },
                reHob(){
                    this.stu.hobby.splice(0,1,'开车')
                }
                
            }
        })
    </script>
    

    数据监测练习

    修改完善后的

    <body>
        <div id="fang">
            学生信息:<br>
            <button @click="addAge">年龄加1</button>
            <button @click="addSex">添加性别:男</button>
            <button @click="addFri">在列表首位添加一个朋友</button>
            <br>
            <button @click="updateFri">修改最后一个朋友信息</button>
            <button @click="addHob">添加一个爱好</button>
            <button @click="reHob">修改第一个爱好为:开车</button>
            <br>
            姓名:{{stu.name}}
            <br>
            年龄:{{stu.age}}
            <br>
            <!-- 不用专设置一个数据,直接用stu.sex当,只要是真,就可以 -->
            <!-- <span v-if="n===1">性别:{{stu.sex}}</span> -->
            <span v-if="stu.sex">性别:{{stu.sex}}</span>
            <br>
            <ul>爱好:
                <li v-for="(h,index) in stu.hobby" :key="index">
                    {{h}}
            </ul>
            <ul>朋友:
                <li v-for="(f,index) in stu.friend" :key="index">
                   <!-- 注意,这里不能直接写{{f}} -->
                    {{f.name}}-{{f.age}}
            </ul>
        </div>
    </body>
    <script>
        vm = new Vue({
            el: "#fang",
            data: {
                stu: {
                    name: '张三三',
                    age: 18,
                    hobby: ['看书', '刷视频', '运动'],
                    friend: [
                        { name: 'lucy', age: 18 },
                        { name: 'jack', age: 19 }
                    ]
                },
    
            },
            methods: {
                addAge() {
                    this.stu.age++
                },
                addSex() {
                    // this.n=1
                    // Vue.set(vm.stu,'sex','男')
                    Vue.set(this.stu, 'sex', '男')
                },
                addFri() {
                    //这时新加的对象也是响应式的
                    this.stu.friend.unshift({ name: '小new', age: 6 })
                },
                //尽管friend是数组数据类型
                //修改朋友信息,此时friend内的元素是对象数据类型,所有接下来可以直接改
                updateFri(){
                    this.stu.friend[this.stu.friend.length-1].name='吴臭屁'
                },
                addHob() {
                    this.stu.hobby.push('学习')
                },
                reHob() {
                    //切片再添加
                    // this.stu.hobby.splice(0, 1, '开车')
                    //根据set(),加上索引值,直接改
                    Vue.set(this.stu.hobby,0,'开车')
                    this.$set(this.stu.hobby,0,'开车')
                }
    
            }
        })
    </script>
    

    数据监测练习2

    1.27 set()

    使后续添加的数据也具有响应式功能

    !!!!
    Vue.set(vm.stu,'sex','女')也就是Vue.set(vm._data.stu,'sex','女'),这是 数据代理的原因
    在这里插入图片描述Vue——May(1)_第97张图片

    <body>
        <div id="fang">
            学生信息:
            <button @click="addSex">点我 添加性别属性</button>
            <br>
            姓名:{{stu.name}}
            <br>
            年龄:{{stu.age}}
            <br>
            <span v-if="stu.sex">性别:{{stu.sex}}</span>
            <ul>朋友:
                <li v-for="f in stu.friends" :key="f.name">
                    {{f.name}}-{{f.age}}
                </li>
            </ul>
            
        </div>
    </body>
    <script>
        vm = new Vue({
            el: "#fang",
            data: {
                stu: {
                    name: '张三三',
                    age: 18,
                    friends:[
                        {name:'lucy',age:18},
                        {name:'jack',age:19},
                    ]
                }
            },
            methods:{
                addSex(){
                    Vue.set(this.stu,'sex','女')
                }
            }
        })
    </script>
    

    Vue——May(1)_第98张图片

    只能添加data里的对象的对象,这一层级
    Vue——May(1)_第99张图片

    1.28 收集表单数据

    Vue——May(1)_第100张图片

    <body>
    <div id="fang">
        <!-- 点击提交按钮后,阻止跳转页面的默认行为 -->
        <form @submit.prevent="sub">
            <!-- 下面一行label标签的意思是,点击“账号”文字时,输入框也会获取光标 -->
            <label for="account">账号:</label>
            <input type="text" id="account" v-model="account">
            <br>
            密码:<input type="password" v-model="password">
            <br>
            性别:
            <!-- name属性控制这两个选择中 只能选一个 --><input type="radio" name="sex" v-model="sex" value="male"><input type="radio" name="sex" v-model="sex" value="female">
            <br>
            <!-- type="number"限制输入框只能输入数字 -->
            <!-- v-model.number控制输出结果为数字,而不是字符串 -->
            年龄:<input type="number" v-model.number="age">
            <br>
            爱好:
            学习<input type="checkbox" v-model="hobby" value="study">
            读书<input type="checkbox" v-model="hobby" value="read">
            吃饭<input type="checkbox" v-model="hobby" value="eat">
            <br>
            校区:
            <select title="1" v-model="city">
                <option value="北京">北京</option>
                <option value="杭州">杭州</option>
                <option value="苏州">苏州</option>
                <option value="徐州">徐州</option>
            </select>
            <br>
            其他信息:
            <br>
            <textarea cols="30" rows="10" v-model="other"></textarea>
            <br>
            <input type="checkbox" v-model="agree"> 阅读并接受<a href="">《用户协议》</a>
            <button>submit</button>
        </form>
    </div>
    </body>
    <script>
    new Vue({
        el:'#fang',
        data:{
            account:'',
            password:'',
            sex:'male',
            // hobb的初始值影响data里的数据类型
            hobby:[],
            city:'',
            other:'',
            age:'',
            agree:true
        },
        methods:{
            sub(){
                console.log(JSON.stringify(this._data))
            }
        }
    })
    </script>
    

    Vue——May(1)_第101张图片
    在这里插入图片描述

    在这里插入图片描述

    1.29 过滤器(时间戳)

    Vue——May(1)_第102张图片

    应用于
    在这里插入图片描述

    (2)局部过滤器

    cdn引入dayjs
    Vue——May(1)_第103张图片


    <template>
    <div>
        {{ a|turnAtoB}}
    </div> 
      
    </template>
    
    <script>
    
    export default {
        data(){
        return{
            a:'123'
        }
      },
      filters: {
        turnAtoB(value) {
          return 'hello'
        },
      },
    }
    </script>
    

    Vue——May(1)_第104张图片
    Vue——May(1)_第105张图片

    Vue——May(1)_第106张图片
    Vue——May(1)_第107张图片

    将filter部分中的内容改为,且对应输出{{ a|turnAtoB |myslice}}

    filters: {
        turnAtoB(value) {
          return 'hello'
        },
        myslice(val){
          return val.slice(0,3)
        }
      },
    

    输出结果为Vue——May(1)_第108张图片

    (2)全局过滤器

    Vue.filter('myslice',function(value){
        return val.slice(0,3)
    })
    

    1.30 指令

    Vue——May(1)_第109张图片

    v-text指令

    在这里插入图片描述

    <body>
    <div id="fang" v-text="name"></div>
    </body>
    <script>
    new Vue({
        el:'#fang',
        data:{
            name:'hello'
        }
    })
    </script>
    

    在这里插入图片描述

    内置指令——拿过来直接可以用的v-on,v-bind

    v-html指令

    支持结构的解析

    <body>
    <div id="fang" v-html="name"></div>
    </body>
    <script>
    new Vue({
        el:'#fang',
        data:{
            name:'

    哈哈哈

    '
    } }) </script>

    Vue——May(1)_第110张图片

    *cookie简略图示

    Vue——May(1)_第111张图片
    不可跨浏览器读cookie
    cookie就相当于一个人的身份标识
    Vue——May(1)_第112张图片

    xss冒充用户杀手

    <body>
    <div id="fang" v-html="str"></div>
    </body>
    <script>
    new Vue({
        el:'#fang',
        data:{
            str:'快点我!!!'
        }
    })
    </script>
    

    Vue——May(1)_第113张图片

    v-cloak指令

    <body>
    <div id="fang">{{name}}</div>
    </body>
    <script>
    new Vue({
        el:'#fang',
        data:{
            name:'{{hello vue}}'
        }
    })
    </script>
    

    Vue——May(1)_第114张图片

    在vue实例接管容器的一瞬间,v-cloak就会被删掉
    在这里插入图片描述

    以下代码效果:不让未经解析的模板跑到页面上
    <style>
            [v-cloak] {
                display: none;
            }
        </style>
    </head>
    
    <body>
        <div v-cloak id="fang">{{name}}</div>
    </body>
    <script>
        new Vue({
            el: '#fang',
            data: {
                name: '{{hello vue}}'
            }
        })
    </script>
    
    

    v-once指令

    <body>
        <div  id="fang">
            <div v-once>初始化n的值为:{{n}}</div>
            <div>当前的n的值为:{{n}}</div>
            <button @click="n++">点我n++</button>
        </div>
    </body>
    <script>
        new Vue({
            el: '#fang',
            data: {
                n: '1'
            }
        })
    </script>
    

    Vue——May(1)_第115张图片
    在这里插入图片描述

    v-pre指令

    用于给普通节点加,加快效率
    在这里插入图片描述

    因为

    当前的n的值为:{{n}}
    使用了v-pre所以不再解析这个模板,页面呈现如下

    Vue——May(1)_第116张图片

    与事件修饰符.once 不同


    1.31自定义指令

    Vue——May(1)_第117张图片

    directives里的this都是指向windows’的

    指令中含有多个字母时,用-链接,写函数时,要用‘’包裹
    Vue——May(1)_第118张图片>

    'fbind’不加双引号一直是我们的简写形式
    Vue——May(1)_第119张图片
    在这里插入图片描述

    变成全局式指令

    Vue——May(1)_第120张图片
    Vue——May(1)_第121张图片

    <body>
        <div  id="fang">
            当前的值为: <span v-text="n"></span>
            <!-- 使用自定义指令 -->
            <br>
            <button @click="n++">点我n+1</button>
            <br>
            <input type="text" v-fbind:value="n">
        </div>
        <div id="hai"><input type="text" v-fbind:value="x"></div>
    </body>
    <script>
        Vue.directive('fbind',{
            bind(elem,binding){
                        //指令与元素绑定成功时
                        elem.value=binding.value
                        console.log('bind指令与元素绑定成功')
                    },
                    //指令所在元素被插入页面时
                    inserted(elem,binding){
                        //使页面刷新后,焦点在输入框上
                        elem.focus()
                    },
                    update(elem,binding){
                        //指令所在元素被重新解析时
                        elem.value=binding.value
                        console.log('按钮被按下,n值更新啦')
                    }
        })
        new Vue({
            el: '#fang',
            data:{
                n:1,
            },
        })
        new Vue({
            el: '#hai',
            data:{
                x:1,
            },
        })
    </script>
    

    函数式

    弊端:不能处理细节问题

    <body>
        <div  id="fang">
            当前的值为: <span v-text="n"></span>
            <br>
            放大十倍后的值为: <span v-big:value="n"></span>
            <!-- 使用自定义指令 -->
            <br>
            <button @click="n++">点我n+1</button>
            <br>
        </div>
    </body>
    <script>
        new Vue({
            el: '#fang',
            data:{
                n:1,
            },
            //定义指令需要的配置项directives
            directives:{
                big(elem,binding){
                    elem.innerText=binding.value*10
                }
            }
        })
    </script>
    

    Vue——May(1)_第122张图片

    对象式

    需要判断 调用的时机
    Vue——May(1)_第123张图片
    n值改变,整个模板会重新解析
    函数式写法无法满足此要求

    <body>
        <div  id="fang">
            当前的值为: <span v-text="n"></span>
            <!-- 使用自定义指令 -->
            <br>
            <button @click="n++">点我n+1</button>
            <br>
            <input type="text" v-fbind:value="n">
        </div>
    </body>
    <script>
        new Vue({
            el: '#fang',
            data:{
                n:1,
            },
            //定义指令需要的配置项directives
            directives:{
                fbind:{
                    bind(elem,binding){
                        //指令与元素绑定成功时
                        elem.value=binding.value
                        console.log('bind指令与元素绑定成功')
                    },
                    //指令所在元素被插入页面时
                    inserted(elem,binding){
                        //使页面刷新后,焦点在输入框上
                        elem.focus()
                    },
                    update(elem,binding){
                        //指令所在元素被重新解析时
                        elem.value=binding.value
                        console.log('按钮被按下,n值更新啦')
                    }
                }
            }
        })
    </script>
    

    Vue——May(1)_第124张图片

    二、Vue核心

    2.1 生命周期(回调函数)

    Vue——May(1)_第125张图片
    Vue——May(1)_第126张图片

    1. 生命周期就是一些在关键函数运行的关键的特殊的函数
    2. 里面包含的this指向是vm实例
    3. Vue——May(1)_第127张图片

    js的对象 表达式,可以简写成在这里插入图片描述
    原式为:style=“{opacity:opacity}”

    debugger断点的使用
    Vue——May(1)_第128张图片
    挂载
    Vue——May(1)_第129张图片

    销毁
    Vue——May(1)_第130张图片

    * 透明度案例

    Vue——May(1)_第131张图片
    Vue——May(1)_第132张图片

    Vue——May(1)_第133张图片

    2.2 组件

    在这里插入图片描述

    组件可以嵌套
    Vue——May(1)_第134张图片
    data写成函数式Vue——May(1)_第135张图片
    改变x1的值不会影响x2,但是写成对象式就会相互影响

    2.2.1 非单文件组件 .js

    一个组件中包含n个组件

    组件的使用过程

    1. 创建
    2. 注册
    3. 使用

    局部组件

    <body>
        <div id="fang">
            <school></school>
            <hr>
            <stu></stu>
            <stu></stu>
        </div>
    </body>
    <script>
        // 创建school组件
        const school = Vue.extend({
            template:
                `
    学校名称:{{schoolName}}
    学校地址:{{address}}
    `
    , data() { return { schoolName: '尚硅谷', address: '北京' } } }) const stu = Vue.extend({ template:`
    学生名称:{{stuName}}
    学生年龄:{{age}}
    `
    , data() { return { stuName: 'Lucy', age: '18' } } }) new Vue({ el: '#fang', //注册组件(局部) components: { //可以简写(键值相同时) school, stu } }) </script>

    Vue——May(1)_第136张图片

    全局组件
    全局组件可以在任意模块使用

    <body>
        <div id="fang">
            <hello></hello>
        </div>
        <div id="hai">
    
        </div>
    </body>
    <script>
        //创建hello组件
        const hello = Vue.extend({
            template: `
            
    {{hi}}
    `
    , data() { return { hi: '你好~~~' } } }) //注册hello组件 Vue.component('hello', hello) //不能先挂载实例,把上面代码放到最后,会导致,vue找不到组件,(因为未注册 new Vue({ el: '#fang', }) new Vue({ el: '#hai' }) </script>

    Vue——May(1)_第137张图片

    组件注意事项
    Vue——May(1)_第138张图片

    (1)组件的嵌套

    子组件要写在父组件代码的上方,要提前准备好

    <body>
    <div id="fang">
        <school></school>
    </div>
    </body>
    <script>
        // 定义stu组件
        const stu = {
            name: 'stu',
            template:`
            
    学生姓名:{{stuName}}
    学生地址:{{age}}
    `
    , data(){ return{ stuName:'Lucy', age:'18' } }, } // 定义school组件 const school = { name: 'school', template:`
    学校姓名:{{schoolName}}
    学校地址:{{address}}
    `
    , data(){ return{ schoolName:'尚硅谷', address:'北京' } }, components:{ stu } } new Vue({ el:'#fang', components:{ school } }) </script>

    Vue——May(1)_第139张图片

    <body>
        
        
    <div id="fang">
        <hello></hello>
        <school></school>
        <hello></hello>
        <app></app>
    </div>
    </body>
    <script>
        // 定义stu组件
        const stu = {
            name: 'stu',
            template:`
            
    学生姓名:{{stuName}}
    学生地址:{{age}}
    `
    , data(){ return{ stuName:'Lucy', age:'18' } }, } // 定义school组件 const school = { name: 'school', template:`
    学校姓名:{{schoolName}}
    学校地址:{{address}}
    `
    , data(){ return{ schoolName:'尚硅谷', address:'北京' } }, components:{ stu } } // const hello={ name:'hello', template:`
    你好
    `
    , // template:`
    {{hell}}
    `,
    // data(){ // return{ // hell:'你好' // } // } } //定义app组件 const app={ template:`
    `
    , components:{ school, hello } } new Vue({ el:'#fang', components:{ school, hello, app } }) </script>

    在这里插入图片描述

    (2)Vuecomponent

    在这里插入图片描述
    Vue——May(1)_第140张图片
    在这里插入图片描述
    长的一样

    (3)vue实例与组件实例

    组件是组件是可复用的vue实例
    Vue——May(1)_第141张图片

    (4)重要的内置关系

    Vue——May(1)_第142张图片

    Vue——May(1)_第143张图片
    在这里插入图片描述

    __proto__指向缔造者的原型对象 Vue——May(1)_第144张图片
    Vue——May(1)_第145张图片
    在这里插入图片描述

    <body>
        <div id="fang">
            
            <school></school>
        </div>
    </body>
    <script>
    Vue.prototype.x=99999
    const school=Vue.extend({
        template:`
            
    `
    , methods: { show(){ //vc是组件实例对象,可以省略__proto__直接加.x console.log(this.x) } }, }) const vm=new Vue({ el:'#fang', components:{ school } }) </script>

    Vue——May(1)_第146张图片

    2.2.2 单文件组件 .vue

    一个文件只包含1个组件

    Vue——May(1)_第147张图片

    选择一个暴露方式

    1. 默认暴露:在文章最后写上一行export default school
      或者
      (下面省略了vue.extend{ })Vue——May(1)_第148张图片

    引入时:import ??? from ???

    1. 统一暴露
    2. 分别暴露

    (1) 快速创建

    +回车键

    main.js里的Vue——May(1)_第149张图片
    可以代替 index.html里的
    在这里插入图片描述

    三、vue-cli

    3.1 安装应用

    command line interface——命令行接口工具
    脚手架向下可以兼容
    Vue——May(1)_第150张图片

    先进入此文件,再npm run serve
    Vue——May(1)_第151张图片
    Vue——May(1)_第152张图片
    按ctrl+cVue——May(1)_第153张图片

    第一回在vscode里运行好!!!!!!!!!!!!!!!!!!!!!!!!!!Vue——May(1)_第154张图片
    Vue——May(1)_第155张图片

    Vue——May(1)_第156张图片

    3.2 分析vue-cli结构

    1. v_test\stu\src\assets 用于放静态资源
      Vue——May(1)_第157张图片

    (2)配置文件

    const { defineConfig } = require('@vue/cli-service')
    module.exports = defineConfig({
      transpileDependencies: true,
      //取消eslint报错
      lintOnSave:false
    })
    

    3.3 ref

    Vue——May(1)_第158张图片

    //main.js
    import Vue from 'vue'
    import App from './App.vue'
    
    Vue.config.productionTip=false
    new Vue({
        el:'#fang',
        render:h=>h(App)
    })
    
    //App.vue
    <template>
      <div>
        <Stu/>
        <Stu/>
        <Stu/>
      </div>
    </template>
    
    <script>
    import Stu from './components/Stu.vue'
    
    export default {
    name:'App',
    //为引入的组件注册
    components:{
      Stu
    }
    }
    
    </script>
    
    <style>
    
    </style>
    
    //Stu.vue
    <template>
      <div>
        学生姓名:{{ name }}
        <br>
        学生年龄{{ age }}
      </div>
    </template>
    
    <script>
    export default{
    name:'Student',
    data(){
      return {
        name:'lucy',
        age:18
      }
    }
    }
    </script>
    
    <style>
    
    </style>
    
    //vue.config.js
    const { defineConfig } = require('@vue/cli-service')
    module.exports = defineConfig({
      transpileDependencies: true,
      //取消eslint报错
      lintOnSave:false
    })
    
    

    Vue——May(1)_第159张图片

    Vue——May(1)_第160张图片
    Vue——May(1)_第161张图片

      methods: {
        show() {
          console.log(this.$refs.title);
        },
      },
    

    Vue——May(1)_第162张图片

    //App.vue
    <template>
      <div>
        <h2 ref="title" v-text="a"></h2>
        <button ref="btn  " @click="show">点我查看上方DOM信息</button>
        <Stu />
        <Stu />
        <Stu ref="sch" />
      </div>
    </template>
    
    <script>
    import Stu from "./components/Stu.vue";
    export default {
      name: "App",
      //为引入的组件注册
      components: {
        Stu,
      },
      data() {
        return {
          a: "学生信息",
        };
      },
      methods: {
        show() {
          console.log(this.$refs);
        },
      },
    };
    </script>
    
    <style>
    </style>
    

    Vue——May(1)_第163张图片

    3.4 props

    三个组件中的数据互不影响,因为data是函数写法,每次都会调用

    //App.vue
    <template>
      <div>
        <!-- 传入数据 -->
        <stu name='lucy' age='18' sex="女"  />
        <stu name='jack' age='19' sex="男"  />
        
        <stu/>
      </div>
    </template>
    
    <script>
    import Stu from "./components/Stu.vue";
    export default {
      name: "App",
      //为引入的组件注册
      components: {
        Stu,
      },
    };
    </script>
    
    //Stu.vue
    <template>
      <div>
        <h2>{{ msg}}</h2>
        学生姓名:{{ name }}
        <br>
        学生性别:{{ sex }}
        <br>
        学生年龄{{ age }}
      </div>
    </template>
    
    <script>
    export default{
    name:'Student',
    data(){
      return {
    msg:'学生信息'
      }
    },
    //接收数据(一般接收)
    //props:['name','age','sex']
    //对数据进行限制的接收
    props:{
      name:String,
      age:Number,
      sex:String
    }
    //
    
    }
    </script>
    
    

    因为传入的age是字符串18,所以会报错在这里插入图片描述
    可以改变传入数据


    这样:age后面的’'里的内容为一个变量,可以放表达式

    //App.vue
    <template>
      <div>
        <!-- 传入数据 -->
        <stu name='lucy' :age='18' sex="女"  />
        <stu name='jack' :age='19' sex="男"  />
        <stu/>
      </div>
    </template>
    
    <script>
    import Stu from "./components/Stu.vue";
    export default {
      name: "App",
      //为引入的组件注册
      components: {
        Stu,
      },
    };
    </script>
    
    //Stu.vue
    <template>
      <div>
        <h2>{{ msg }}</h2>
        学生姓名:{{ name }}
        <br />
        学生性别:{{ sex }}
        <br />
        学生年龄{{ age }}
      </div>
    </template>
    
    <script>
    export default {
      name: "Student",
      data() {
        return {
          msg: "学生信息",
        };
      },
      // props:['name','age','sex']
      props: {
        name: {
          type: String,
          //若不传如则会报错
          required: true,
        },
        age: {
          type: Number,
          //若不传时,给出的默认值
          default: 99,
        },
        sex: {
          type: String,
          required: false,
        },
      },
    };
    </script>
    

    Vue——May(1)_第164张图片

    props优先级高于data中的数据

    想修改传入的信息,但是不想报错

    //App.vue
    <template>
      <div>
        <!-- 传入数据 -->
        <stu name='lucy' :age='18' sex="女"  />
        <stu name='jack' :age='19' sex="男"  />
        
      </div>
    </template>
    
    <script>
    import Stu from "./components/Stu.vue";
    export default {
      name: "App",
      //为引入的组件注册
      components: {
        Stu,
      },
    };
    </script>
    
    //Stu.vue
    <template>
      <div>
        <h2>{{ msg }}</h2>
        学生姓名:{{ name }}
        <br />
        学生性别:{{ sex }}
        <br />
        学生年龄:{{ updateAge }}
        <br>
        <button @click="update">点我 学生的年龄加1</button>
      </div>
    </template>
    
    <script>
    export default {
      name: "Student",
      data() {
        return {
          msg: "学生信息",
          updateAge:this.age
        };
      },
      methods:{
        update(){
          this.updateAge++
               console.log(this.name,'的年龄加1')
        }
      },
      props:['name','age','sex']
     
    };
    </script>
    

    Vue——May(1)_第165张图片
    Vue——May(1)_第166张图片

    2.5 mixin 混入

    Vue——May(1)_第167张图片

    用于每个组件都要用的配置,可以写在一起

    局部引入

    Vue——May(1)_第168张图片
    Vue——May(1)_第169张图片

    全局混入Vue——May(1)_第170张图片

    2.6 plugins插件

    本质是对象
    Vue——May(1)_第171张图片

    Vue——May(1)_第172张图片

    2.7 scoped 样式

    scoped 局部的
    样式最终都会到一个地方,所以会出现覆盖情况
    Vue——May(1)_第173张图片
    Vue——May(1)_第174张图片

    解决方法

    <style scoped>
    .s{
      background-color: rgb(159, 243, 215);
    }
    </style>
    

    Vue——May(1)_第175张图片

    相当于给元素加上特殊标识

    Vue——May(1)_第176张图片

    2.8 less 嵌套

    lang=‘ ’不写默认是css

    //安装7版本
    npm i less-loader@7
    

    Vue——May(1)_第177张图片
    Vue——May(1)_第178张图片

    //Stu.vue
    
    <template>
      <div class="s">
        <h3>{{ msg }}</h3>
        学生姓名:{{ name }}
        <br>
        <div class="sex">
          学生性别:{{ sex }}
        </div>
        学生年龄:{{ age }}
        <hr>
      </div>
    </template>
    <script>
    
    export default {
      name: "Student",
      data() {
        return {
          msg: "学生信息",
          name:'Wucpp',
          age:21,
          sex:'女'
        }
      }
    };
    </script>
    <style lang="less">
    .s{
      background-color: rgb(240, 209, 248);
      .sex{
        color:red;
      }
    }
    </style>
    

    Vue——May(1)_第179张图片

    * 案例todolist

    (1)静态准备

    Vue——May(1)_第180张图片

    Vue——May(1)_第181张图片

    上面位置创建错了,移动了一下
    在这里插入图片描述


    Vue——May(1)_第182张图片

    (2) 添加

    1.1 uuid => nanoid
    Vue——May(1)_第183张图片

    methods:{
          add(e){
            // console.log(this.title)  v-model也可
            // console.log(e.target.value)
            const newTodo={id:nanoid(),title:e.target.value,done:false}
            console.log(newTodo)
          }
        }
    

    Vue——May(1)_第184张图片


    Vue——May(1)_第185张图片

    a、 儿子给父亲传信

    父亲提前写一个函数,在儿子里调用函数
    Vue——May(1)_第186张图片
    Vue——May(1)_第187张图片

    //Top.vue
    
    <template>
     
     <div class="todo-header">
            <input type="text"  @keyup.enter="add" placeholder="请输入你的任务名称,按回车键确认"/>
          </div>
    </template>
    
    <script>
    import {nanoid} from 'nanoid'
    export default {
        name:'Top',
        data(){
          return {
          }
        },
        //接收app传的函数receive
        props:['addTodo'],
        methods:{
          add(newInput){
            //将用户的输入包装成一个对象
            // console.log(this.title)  v-model也可
            // console.log(e.target.value)
            const newTodo={id:nanoid(),title:newInput.target.value,done:false}
            // console.log(newTodo)
            this.addTodo(newTodo)
            //每次添加后清空
            newInput.target.value=''
          }
        },
        
    }
    </script>
    
    <style scoped>
    /*header*/
    .todo-header input {
      width: 560px;
      height: 28px;
      font-size: 14px;
      border: 1px solid #ccc;
      border-radius: 4px;
      padding: 4px 7px;
    }
    
    .todo-header input:focus {
      outline: none;
      border-color: rgba(82, 168, 236, 0.8);
      box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
    }
    
    </style>
    
    //mylist.vue
    
    
    <template>
      <div>
        <ul class="todo-main">
          <!-- todoData用于传数据 :把list的传给item-->
          <Item v-for="todosListEach in todosMyList" :key="todosListEach.id" :todoItem="todosListEach" />
        </ul>
        
      </div>
    </template>
    
    <script>
    import Item from './Item.vue'
    export default {
        name:'MyList',
        components:{
          Item
        },
        //从App收入的所有列表数据是vc,可以直接使用
        props:['todosMyList']
    }
    </script>
    
    <style scoped>
    /*main*/
    .todo-main {
      margin-left: 0px;
      border: 1px solid #ddd;
      border-radius: 2px;
      padding: 0px;
    }
    
    .todo-empty {
      height: 40px;
      line-height: 40px;
      border: 1px solid #ddd;
      border-radius: 2px;
      padding-left: 5px;
      margin-top: 10px;
    }
    </style>
    
    //App.vue
    
    <template>
      <div id="root">
        <div class="todo-container">
          <div class="todo-wrap">
            <!-- 把app的数据传给top -->
            <Top :addTodo="addTodo" />
            <!-- 把app的数据传给list -->
            <MyList :todosMyList="todosApp" />
            <Bottom />
          </div>
        </div>
      </div>
    </template>
    
    <script>
    import Top from "./components/Top.vue";
    import Bottom from "./components/Bottom.vue";
    import MyList from "./components/MyList.vue";
    export default {
      name: "App",
      components: {
        Top,
        Bottom,
        MyList,
      },
      data() {
        return {
          todosApp: [
            { id: "001", title: "吃饭", done: false },
            { id: "002", title: "睡觉", done: false },
            { id: "003", title: "学习", done: true },
          ],
        };
      },
      methods: {
        addTodo(newTodo) {
          // console.log('我是App组件,我收到了数据:',x)
          //下面一行操作了data中的数据
          this.todosApp.unshift(newTodo);
        },
      },
    };
    </script>
    
    <style>
    /*base*/
    * {
      text-decoration: none;
      list-style-type: none;
    }
    body {
      background: #fff;
    }
    
    .btn {
      display: inline-block;
      padding: 4px 12px;
      margin-bottom: 0;
      font-size: 14px;
      line-height: 20px;
      text-align: center;
      vertical-align: middle;
      cursor: pointer;
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),
        0 1px 2px rgba(0, 0, 0, 0.05);
      border-radius: 4px;
    }
    
    .btn-danger {
      color: #fff;
      background-color: #da4f49;
      border: 1px solid #bd362f;
    }
    
    .btn-danger:hover {
      color: #fff;
      background-color: #bd362f;
    }
    
    .btn:focus {
      outline: none;
    }
    
    .todo-container {
      width: 600px;
      margin: 0 auto;
    }
    .todo-container .todo-wrap {
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 5px;
    }
    </style>
    
    
    //item.vue
    
    <template>
      <div><li>
              <label>
                <input type="checkbox" :checked="todoItem.done" />
                <span>{{todoItem.title}}</span>
              </label>
              <button class="btn btn-danger" style="display:none">删除</button>
            </li></div>
    </template>
    <script>
    export default {
        name:'Item',
        //声明接收TODO对象(从MyList接收的)
        props:['todoItem']
    }
    </script>
    
    <style scoped>
    /*item*/
    li {
      list-style: none;
      height: 36px;
      line-height: 36px;
      padding: 0 5px;
      border-bottom: 1px solid #ddd;
    }
    
    li label {
      float: left;
      cursor: pointer;
    }
    
    li label li input {
      vertical-align: middle;
      margin-right: 6px;
      position: relative;
      top: -1px;
    }
    
    li button {
      float: right;
      display: none;
      margin-top: 3px;
    }
    
    li:before {
      content: initial;
    }
    
    li:last-child {
      border-bottom: none;
    }
    
    </style>
    

    Vue——May(1)_第188张图片

    (3) 勾选

    Vue——May(1)_第189张图片

    (4)删除

    Vue——May(1)_第190张图片

    (5)底部互动

    Vue——May(1)_第191张图片

    //App.vue
    
    <template>
      <div id="root">
        <div class="todo-container">
          <div class="todo-wrap">
            <!-- 把app的数据传给top -->
            <Top :addTodo="addTodo" />
            <!-- 把app的数据传给list -->
            <MyList :todosMyList="todosApp" 
            :checkList="checkApp"
            :delList="delApp"
            />
            <Bottom :todosBottom="todosApp" 
            :checkAllList="checkAllApp"
            :clearAlldone="clearAlldone" />
          </div>
        </div>
      </div>
    </template>
    
    <script>
    import Top from "./components/Top.vue";
    import Bottom from "./components/Bottom.vue";
    import MyList from "./components/MyList.vue";
    export default {
      name: "App",
      components: {
        Top,
        Bottom,
        MyList,
      },
      data() {
        return {
          todosApp: [
            { id: "001", title: "吃饭", done: false },
            { id: "002", title: "睡觉", done: false },
            { id: "003", title: "学习", done: true },
          ],
        };
      },
      methods: {
        //添加todo
        addTodo(newTodo) {
          // console.log('我是App组件,我收到了数据:',x)
          //下面一行操作了data中的数据
          this.todosApp.unshift(newTodo);
          
        },
        //勾选或者取消选择
        checkApp(id){
            this.todosApp.forEach((todoItem)=>{
              if(todoItem.id===id) todoItem.done=!todoItem.done
            })
          },
          //删除todo
          delApp(id){
            //别忘记赋值给新数组
            this.todosApp=this.todosApp.filter((todoItem) =>{
               return todoItem.id !== id
            })
          },
          //全选或者全不选
          checkAllApp(done){
            this.todosApp.forEach((todoeach)=>{
              todoeach.done=done
            })
          },
          clearAlldone(){
            //filter不影响原数组,要赋值回去
            this.todosApp=this.todosApp.filter((todoItem)=>{
              return !todoItem.done
            })
          }
      },
    };
    </script>
    
    <style>
    /*base*/
    * {
      text-decoration: none;
      list-style-type: none;
    }
    body {
      background: #fff;
    }
    
    .btn {
      display: inline-block;
      padding: 4px 12px;
      margin-bottom: 0;
      font-size: 14px;
      line-height: 20px;
      text-align: center;
      vertical-align: middle;
      cursor: pointer;
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),
        0 1px 2px rgba(0, 0, 0, 0.05);
      border-radius: 4px;
    }
    
    .btn-danger {
      color: #fff;
      background-color: #da4f49;
      border: 1px solid #bd362f;
    }
    
    .btn-danger:hover {
      color: #fff;
      background-color: #bd362f;
    }
    
    .btn:focus {
      outline: none;
    }
    
    .todo-container {
      width: 600px;
      margin: 0 auto;
    }
    .todo-container .todo-wrap {
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 5px;
    }
    </style>
    
    
    //Bottom.vue
    
    <template>
      <div class="todo-footer">
        <label>
          <!-- <input type="checkbox" v-model="isAll" @change="checkAll"/> -->
          <input type="checkbox" v-model="isAll" />
        </label>
        <span>
          <span>已完成{{ todosDone }}</span> / 全部{{ todosBottom.length }}
        </span>
        <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
      </div>
    </template>
    
    <script>
    export default {
      name: "Bottom",
      props: ["todosBottom", "checkAllList", "clearAlldone"],
      computed: {
        // todosDone(){
        //   let i=0
        //   this.todosBottom.forEach((todoItem)=>{
        //     if(todoItem.done)
        //     i++
        //   })
        //   return i
        // }
    
        // ES6条件统计reduce
        //返回的是上次调用后的结果
        // todosDone(){
        //   this.todosBottom.reduce((pre,current)=>{
        //     // console.log(current)//打印出三个todo对象
        //     return pre+(current.done ? 1 : 0)
        //   },0)
        //简写
        todosDone() {
          return this.todosBottom.reduce(
            (pre, current) => pre + (current.done ? 1 : 0),
            0
          );
        },
        //isAll不可以简写
        //写了get,set就不用写checkAllList了
        isAll: {
          get() {
            return this.todosDone === this.todosBottom.length && this.todosDone > 0;
          },
          set(e) {
            this.checkAllList(e);
          },
        },
        // isAll(){
        //   return this.todosDone===this.todosBottom.length&&this.todosDone>0
        // }
      },
      methods: {
        //   checkAll(e){
        //     console.log(e.target.checked)
        //      this.checkAllList(e.target.checked)
        //   }
        clearAll() {
          //注意加this!!!!!!!!!!
          if (confirm("确定要清除所有已经完成的待办吗?")) this.clearAlldone();
        },
      },
    };
    </script>
    
    <style scoped>
    /*footer*/
    .todo-footer {
      height: 40px;
      line-height: 40px;
      padding-left: 6px;
      margin-top: 5px;
    }
    
    .todo-footer label {
      display: inline-block;
      margin-right: 20px;
      cursor: pointer;
    }
    
    .todo-footer label input {
      position: relative;
      top: -1px;
      vertical-align: middle;
      margin-right: 5px;
    }
    
    .todo-footer button {
      float: right;
      margin-top: 5px;
    }
    </style>
    

    (6) 总结

    Vue——May(1)_第192张图片

    2.9 webStorage

    Vue——May(1)_第193张图片

    2.9.1 localStorage

    浏览器关闭数据也不会消失

    创建

    <button onclick="localData()">点我保存一个数据</button>
        <script>
            function localData() {
                //都得是字符串
                window.localStorage.setItem('a', 'b')
                localStorage.setItem('msg', 'hello')
                //下面的会覆盖上面的
                localStorage.setItem('msg', 'c')
            }
        </script>
    
    
    
        <button onclick="localData()">点我保存一个数据</button>
        <script>
             let per1={name:'lucy',age:18,sex:'女'}
            function localData() {
                window.localStorage.setItem('person1',JSON.stringify(per1))
            }
        </script>
    

    Vue——May(1)_第194张图片

    读取删除

        <button onclick="localData()">点我保存一个数据</button>
        <button onclick="readData()">点我读取一个数据</button>
        <button onclick="delData()">点我删除一个数据</button>
        <script>
             let per1={name:'lucy',age:18,sex:'女'}
            function localData() {
                window.localStorage.setItem('person1',JSON.stringify(per1))
            }
            function readData() {
                console.log(window.localStorage.getItem('person1'))
                console.log(JSON.parse(window.localStorage.getItem('person1')))
            }
            function delData() {
                localStorage.removeItem('person1')
            }
        </script>
    

    Vue——May(1)_第195张图片

    清空

    function clearData() {
                localStorage.clear()
            }
    

    2.9.2 sessionStorage

    浏览器关闭,信息就消失

        <button onclick="createData()">点我保存一个数据</button>
        <button onclick="readData()">点我读取一个数据</button>
        <button onclick="clearData()">点我清空数据</button>
        <script>
             let per1={name:'lucy',age:18,sex:'女'}
             let per2={name:'jack',age:18,sex:'女'}
            function createData() {
                window.sessionStorage.setItem('person1',JSON.stringify(per1))
                window.sessionStorage.setItem('person2',JSON.stringify(per2))
            }
            function readData() {
                console.log(window.sessionStorage.getItem('person1'))
                console.log(window.sessionStorage.getItem('person2'))
                console.log(JSON.parse(window.sessionStorage.getItem('person1')))
                console.log(JSON.parse(window.sessionStorage.getItem('person1')))
            }
            function clearData() {
                sessionStorage.clear()
            }
        </script>
    

    在这里插入图片描述

    * todo本地存储

    Vue——May(1)_第196张图片

    2.10 组件的自定义事件

    Vue——May(1)_第197张图片

    绑定

    方法1
    Vue——May(1)_第198张图片

    this.$emit(“事件名”’)触发

    方法2
    Vue——May(1)_第199张图片

    改写成this.$refs.student.$once('fang',this.getStuName)或者
    若想传一堆参数,用...接收
    Vue——May(1)_第200张图片

    Vue——May(1)_第201张图片

    解绑

    Vue——May(1)_第202张图片

    Vue——May(1)_第203张图片

    Vue——May(1)_第204张图片

    但是原生的事件依然奏效

    总结

    若要在组件中,将click当成点击事件,则需要在事件后面加上.native修饰符。
    在这里插入图片描述

    * todo子给父传

    Vue——May(1)_第205张图片
    Vue——May(1)_第206张图片

    2.11全局事件总线

    1. 让所有组件都能看到Vue——May(1)_第207张图片

    2. 使其有$on,$emit等方法Vue——May(1)_第208张图片

    2.12消息订阅与发布

    第三方库pubsub.js
    Vue——May(1)_第209张图片
    Vue——May(1)_第210张图片

    在这里插入图片描述

    * todo案例编辑

    Vue——May(1)_第211张图片

    //item.vue
    
    <template>
      <div>
        <li>
          <label>
            <input
              type="checkbox"
              :checked="todoItem.done"
              @change="checkFn(todoItem.id)"
            />
            <!-- //变化频繁 -->
            <span v-show="!todoItem.isEdit" @click="addEdit(todoItem)">{{todoItem.title}}</span>
            <input type="text"
              v-show="todoItem.isEdit"
              :value="todoItem.title"
              @blur="blurFn(todoItem, $event)"
              ref="inputTitle"
            />
          </label>
          <!-- 调用删除元素的函数时,记得要传入每个元素的id -->
          <button class="btn btn-danger" @click="delFn(todoItem.id)">删除</button>
          <button class="btn btn-edit" @click="addEdit(todoItem)">编辑</button>
        </li>
      </div>
    </template>
    <script>
    export default {
      name: "Item",
      //声明接收TODO对象(从MyList接收的)
      props: ["todoItem"],
      methods: {
        //编辑,添加编辑属性
        addEdit(a) {
          if (a.hasOwnProperty("isEdit")) {
            this.todoItem.isEdit = true;
          } else {
            this.$set(this.todoItem, "isEdit", true);
          }
          // 使一点击编辑按钮就锁定光标
          //nextTick所指定的回调,会在dom节点更新完毕后再执行
          this.$nextTick(function () {
            this.$refs.inputTitle.focus();
          });
        },
        //失去焦点(真正执行修改逻辑)
        blurFn(todoItem, e) {
          this.todoItem.isEdit = false;
          if (!e.target.value.trim()) return alert("输入不能为空");
          //触发app.vue里的editApp函数
          //并传入这个todo的id和输入值
          this.$bus.$emit("blurFnName", todoItem.id, e.target.value);
        },
    
        //勾选
        checkFn(id) {
          // console.log(id)
          // 对app里的数据进行取反操作
          // this.checkItem(id);
    
          //使用事件总线
          this.$bus.$emit("checkApp", id);
        },
        //删除
        delFn(id) {
          if (confirm("确定删除吗")) {
            // this.delItem(id)
            this.$bus.$emit("delApp", id);
          }
        },
      },
    };
    </script>
    
    <style scoped>
    /*item*/
    li {
      list-style: none;
      height: 36px;
      line-height: 36px;
      padding: 0 5px;
      border-bottom: 1px solid #ddd;
    }
    
    li label {
      float: left;
      cursor: pointer;
    }
    
    li label li input {
      vertical-align: middle;
      margin-right: 6px;
      position: relative;
      top: -1px;
    }
    
    li button {
      float: right;
      display: none;
      margin-top: 3px;
    }
    
    li:before {
      content: initial;
    }
    
    li:last-child {
      border-bottom: none;
    }
    li:hover {
      background-color: rgba(116, 101, 102, 0.068);
    }
    li:hover button {
      display: block;
    }
    </style>
    
    //App.vue
    
    <template>
      <div id="root">
        <div class="todo-container">
          <div class="todo-wrap">
            <!-- 把输入的内容加到表格中,用自定义事件完成 -->
            <!-- 事件名、回调名  -->
            <Top @addTodoEvent="addTodo" />
            <!-- 把app的数据传给list -->
            <MyList :todosMyList="todosApp" />
            <Bottom
              :todosBottom="todosApp"
              @checkAllList="checkAllApp"
              @clearAlldone="clearAlldone"
            />
          </div>
        </div>
      </div>
    </template>
    <script>
    import Top from "./components/Top.vue";
    import Bottom from "./components/Bottom.vue";
    import MyList from "./components/MyList.vue";
    export default {
      name: "App",
      components: {
        Top,
        Bottom,
        MyList,
      },
      data() {
        return {
          // todosApp: [
          //   { id: "001", title: "吃饭", done: false },
          //   { id: "002", title: "睡觉", done: false },
          //   { id: "003", title: "学习", done: true },
          // ],
          //下一行若不加||[]则会影响到Bottom。vue
          //Bottom.vue中使用了.length,而当数据为空时,localStorage.getItem读出来的是null
          //而null没有.length方法会导致报错
          todosApp: JSON.parse(localStorage.getItem("todosApp")) || [],
        };
      },
      methods: {
        //编辑,失去焦点后,不是输入框形式,并保存输入的值
        editApp(id, title) {
          this.todosApp.forEach((todoItem) => {
            if (todoItem.id === id) todoItem.title = title;
          });
        },
        //添加todo
        addTodo(newTodo) {
          // console.log('我是App组件,我收到了数据:',x)
          //下面一行操作了data中的数据
          this.todosApp.unshift(newTodo);
        },
        //勾选或者取消选择
        checkApp(id) {
          this.todosApp.forEach((todoItem) => {
            if (todoItem.id === id) todoItem.done = !todoItem.done;
          });
        },
        //删除todo
        delApp(id) {
          //别忘记赋值给新数组
          this.todosApp = this.todosApp.filter((todoItem) => {
            return todoItem.id !== id;
          });
        },
        //全选或者全不选
        checkAllApp(d) {
          this.todosApp.forEach((todoeach) => {
            todoeach.done = d;
          });
        },
        clearAlldone() {
          //filter不影响原数组,要赋值回去
          this.todosApp = this.todosApp.filter((todoItem) => {
            return !todoItem.done;
          });
        },
      },
      //使用监听属性,把todo存在本地存储中
      //若想要监测到任务是否完成(todo.done)对象里面的属性,需要使用深度监视
      watch: {
        // todosApp(newtodo){
        //   localStorage.setItem('todosApp',JSON.stringify(newtodo))
        // }
        //深度监视写法
        todosApp: {
          deep: true,
          handler(newtodo) {
            localStorage.setItem("todosApp", JSON.stringify(newtodo));
          },
        },
      },
      //创建事件总线
      mounted() {
        this.$bus.$on("checkApp", this.checkApp);
        this.$bus.$on("delApp", this.delApp);
        this.$bus.$on("blurFnName", this.editApp);
      },
      beforeDestroy() {
        this.$bus.$off("checkApp");
        this.$bus.$off("blurFnName");
      },
    };
    </script>
    
    <style>
    /*base*/
    * {
      text-decoration: none;
      list-style-type: none;
    }
    body {
      background: #fff;
    }
    
    .btn {
      display: inline-block;
      padding: 4px 12px;
      margin-bottom: 0;
      font-size: 14px;
      line-height: 20px;
      text-align: center;
      vertical-align: middle;
      cursor: pointer;
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),
        0 1px 2px rgba(0, 0, 0, 0.05);
      border-radius: 4px;
    }
    
    .btn-danger {
      color: #fff;
      background-color: #da4f49;
      border: 1px solid #bd362f;
    }
    .btn-edit {
      color: #fff;
      background-color: #51b1b8;
      border: 1px solid #bd362f;
      margin-right: 5px;
    }
    .btn-edit:hover {
      color: #fff;
      background-color: #398e94;
      border: 1px solid #bd362f;
      margin-right: 5px;
    }
    
    .btn-danger:hover {
      color: #fff;
      background-color: #bd362f;
    }
    
    .btn:focus {
      outline: none;
    }
    
    .todo-container {
      width: 600px;
      margin: 0 auto;
    }
    .todo-container .todo-wrap {
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 5px;
    }
    </style>
    
    

    2.13 nextTick

    Vue——May(1)_第212张图片
    用定时器也可以
    Vue——May(1)_第213张图片

    2.14 css样式

    动画

    <template>
      <div>
        <button @click="show = !show">显示/隐藏</button>
        <transition name="hai" appear>
          <h3 v-show="show" >hello</h3>
        </transition>
      </div>
    </template>
    <script>
    export default {
      name: "Test",
      data() {
        return {
          show: true,
        };
      },
    };
    </script>
    
    <style>
    h3{
      background-color: pink;
    }
    .hai-inter-active{
      animation: fang 1s ;
    }
    .hai-leave-active{
      animation: fang 1s reverse;
    }
    @keyframes fang{
      from{
        transform: translateX(-100px);
      }
      to{
        transform: translateX(0px);
      }
    }
    </style>
    

    Vue——May(1)_第214张图片

    过渡

    <template>
      <div>
        <button @click="show = !show">显示/隐藏</button>
        <transition name="hai" appear>
          <h3 v-show="show" >hello</h3>
        </transition>
      </div>
    </template>
    <script>
    export default {
      name: "Test2",
      data() {
        return {
          show: true,
        };
      },
    };
    </script>
    
    <style>
    h3{
      background-color: pink;
      transition: 0.5s linear ;
    }
    .hai-enter,.hai-leave-to{
      transform: translateX(-100%);
    }
    .hai-enter-to,.hai-leave{
      transform: translateX(0);
    }
    
    </style>
    

    (1) 多个元素过渡

    <transition-group name="hai" >
            <h3 v-show="show"  key="1">hello</h3>
            <h3 v-show="show" key="2">vue</h3>
          </transition-group>
    

    (2)动画库

    Vue——May(1)_第215张图片

    chatgpt
    我看到你在样式中使用了 animation 属性来创建动画,实现了元素进入时的移动效果。但是你还需要同时使用 transition 事件来实现元素离开时的过渡效果。建议你按照以下步骤修改代码,实现离开过渡效果:

    1. v-if 改为 v-show。因为 v-if 会在元素删除时直接删除元素,没有过渡效果,而 v-show 只是控制了元素的显示或隐藏,可以实现过渡。

    2. li 元素外层添加 transition 标签,并设置 nameappear 属性。这里的 name 属性是用来指定过渡名称的,需要在样式中定义对应的过渡类。appear 属性是用来指定是否在第一次渲染时就产生过渡效果的,需要设置为 true。

    3. 在样式中定义对应的过渡类 .todo-enter.todo-leave-to.todo-enter 定义了进入过渡的样式,.todo-leave-to 定义了离开过渡的样式。

    下面是修改后的代码片段:

    
    
    
    
    
    

    注意,在删除时,需要将 isDelete 属性设置为 true,然后等待过渡效果完成后再触发删除操作。这样可以确保元素顺利过渡完成,避免可能的错误。同时,你还需要在父组件中接收delApp 事件并进行对应的操作,例如删除数据源中对应的元素,从而完成元素的删除。

  • 你可能感兴趣的:(框架,vue.js,javascript,前端)