【组件自定义事件+全局事件总线+消息订阅与发布+TodoList案例——编辑+过度与动画】

组件自定义事件+全局事件总线+消息订阅与发布+TodoList案例——编辑+过度与动画

  • 1 组件自定义事件
    • 1.1 绑定
    • 1.2 解绑
    • 1.3 总结
    • 1.4 TodoList案例——自定义事件
  • 2 全局事件总线
    • 2.1 理解
    • 2.2 步骤
    • 2.3 TodoList案例——事件总线
  • 3 消息订阅与发布
    • 3.1 理解
    • 3.2 TodoList案例——消息的订阅与发布
  • 4 TodoList案例——编辑
    • 4.1 $nextTick
    • 4.2 代码
  • 5 过度与动画
    • 5.1 理解
    • 5.2 TodoList案例——动画

1 组件自定义事件

  • 区别于JS中的内置事件(如:click、keyup等)用于html中的元素,自定义事件用于组件。

1.1 绑定

  • App.vue:
<template><div class="app"><h1>{{msg}}</h1><!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 --><School :getSchoolName="getSchoolName"/><!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on) --><Student v-on:atguigu="getStudentName"/> <!-- 由于v-on在Student组件标签上,所以是给Student组件的实例对象vc身上绑定了一个事件atguigu,如果有人触发了此事件,那么getStudentName函数将会被调用 --><!-- 若要让按钮只能触发一次 --><!-- <Student v-on:atguigu.once="getStudentName"/> --><!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref) --><!-- <Student ref="student"/> --></div>
</template><script>// 引入Student组件import Student from './components/Student.vue' // 引入School组件import School from './components/School.vue'export default {name:'App',components:{School, Student},data() {return {msg:"你好啊!"}},methods: {getSchoolName(name) {console.log('App收到了学校名:',name);},getStudentName(name) {console.log('App收到了学生名:',name);}/* getStudentName(name,...params) {console.log('App收到了学生名:',name,params); // params收集剩余参数} */},// 用于第二种写法ref// mounted() {// 绑定自定义事件// this.$refs.student是Student组件的实例对象// this.$refs.student.$on('atguigu',this.getStudentName)// 绑定自定义事件且让他等三秒钟返回/* setTimeout(()=>{this.$refs.student.$on('atguigu',this.getStudentName)},3000) */// 绑定自定义事件且让按钮只能触发一次// this.$refs.student.$once('atguigu',this.getStudentName)// }}
</script><style scoped>.app {background-color: gray;padding: 5px;}
</style>
  • School.vue:
<template><div class="school"><h2>学校名称:{{name}}</h2><h2>学校地址:{{address}}</h2><button @click="sendSchoolName">把学校名给App</button></div>
</template><script>export default {name:'School',props:['getSchoolName'],data() {return {name:'霍格沃兹魔法学院',address:'苏格兰高地'}},methods: {sendSchoolName() {this.getSchoolName(this.name)}}}
</script><style scoped>.school {background-color: pink;padding: 5px;}
</style>
  • Student.vue:
<template><div class="student"><h2>学生姓名:{{name}}</h2><h2>学生性别:{{sex}}</h2><button @click="sendStudentName">点我把学生名给App</button></div>
</template><script>export default {name:'Student',data() {return {name:'小王',sex:'女'}},methods: {sendStudentName() {// 触发Student组件实例身上的atguigu事件this.$emit('atguigu',this.name)// this.$emit('atguigu',this.name,666,888,900)}}}
</script><style scoped>.student{background-color: orange;padding: 5px;margin-top: 30px;}
</style>

在这里插入图片描述

1.2 解绑

  • App.vue:
<template><div class="app"><h1>{{msg}}</h1><!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 --><School :getSchoolName="getSchoolName"/><!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on) --><!-- <Student v-on:atguigu="getStudentName"/> 由于v-on在Student组件标签上,所以是给Student组件的实例对象vc身上绑定了一个事件atguigu,如果有人触发了此事件,那么getStudentName函数将会被调用 --><Student v-on:atguigu="getStudentName" @demo="m1"/><!-- 若要让按钮只能触发一次 --><!-- <Student v-on:atguigu.once="getStudentName"/> --><!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref) --><!-- <Student ref="student"/> --></div>
</template><script>// 引入Student组件import Student from './components/Student.vue' // 引入School组件import School from './components/School.vue'export default {name:'App',components:{School, Student},data() {return {msg:"你好啊!"}},methods: {getSchoolName(name) {console.log('App收到了学校名:',name);},getStudentName(name) {console.log('App收到了学生名:',name);},/* getStudentName(name,...params) {console.log('App收到了学生名:',name,params); // params收集剩余参数} */m1() {console.log("demo事件被触发了");}},// 用于第二种写法ref// mounted() {// 绑定自定义事件// this.$refs.student是Student组件的实例对象// this.$refs.student.$on('atguigu',this.getStudentName)// 绑定自定义事件且让他等三秒钟返回/* setTimeout(()=>{this.$refs.student.$on('atguigu',this.getStudentName)},3000) */// 绑定自定义事件且让按钮只能触发一次// this.$refs.student.$once('atguigu',this.getStudentName)// }}
</script><style scoped>.app {background-color: gray;padding: 5px;}
</style>
  • Student.vue:
<template><div class="student"><h2>学生姓名:{{name}}</h2><h2>学生性别:{{sex}}</h2><button @click="sendStudentName">点我把学生名给App</button><button @click="unbind">解绑atguigu事件</button></div>
</template><script>export default {name:'Student',data() {return {name:'小王',sex:'女'}},methods: {sendStudentName() {// 触发Student组件实例身上的atguigu事件this.$emit('atguigu',this.name)// this.$emit('atguigu',this.name,666,888,900)// this.$emit('demo')},unbind() {this.$off('atguigu') // 只适用于解绑一个自定义事件// this.$off(['atguigu','demo']) // 解绑多个自定义事件// this.$off() // 解绑所有的自定义事件}}}
</script><style scoped>.student{background-color: orange;padding: 5px;margin-top: 30px;}
</style>

1.3 总结

  • 一种组件间通信的方式,适用于:子组件 —> 父组件
  • 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
  • 绑定自定义事件:
    1> 第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>
    2> 第二种方式,在父组件中:
    在这里插入图片描述
    3> 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。
  • 触发自定义事件:this.$emit('atguigu',数据)
  • 解绑自定义事件:
    1> this.$off('atguigu'):只适用于解绑一个自定义事件
    2> this.$off(['atguigu','demo']):解绑多个自定义事件
    3> this.$off() :解绑所有的自定义事件
  • 注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中要么用箭头函数,否则this指向会出问题!
  • 组件上也可以绑定原生DOM事件,需要使用native修饰符。
  • App.vue:
<template><div class="app"><h1>{{msg}},学生姓名是:{{studentName}}</h1><!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 --><School :getSchoolName="getSchoolName"/><!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on) --><!-- <Student v-on:atguigu="getStudentName"/> 由于v-on在Student组件标签上,所以是给Student组件的实例对象vc身上绑定了一个事件atguigu,如果有人触发了此事件,那么getStudentName函数将会被调用 --><Student v-on:atguigu="getStudentName" @demo="m1"/><!-- 若要让按钮只能触发一次 --><!-- <Student v-on:atguigu.once="getStudentName"/> --><!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref) --><!-- <Student ref="student"/> --><!-- 组件上也可以绑定原生DOM事件 需要使用native修饰符 --><!-- <Student ref="student" @click.native="show"/> --></div>
</template><script>// 引入Student组件import Student from './components/Student.vue' // 引入School组件import School from './components/School.vue'export default {name:'App',components:{School, Student},data() {return {msg:"你好啊!",studentName:''}},methods: {getSchoolName(name) {console.log('App收到了学校名:',name);},// 要么配置在methods中getStudentName(name) {console.log('App收到了学生名:',name);this.studentName = name},/* getStudentName(name,...params) {console.log('App收到了学生名:',name,params); // params收集剩余参数} */m1() {console.log("demo事件被触发了");},/* show() {alert(123)} */},// 用于第二种写法ref// mounted() {// 绑定自定义事件// this.$refs.student是Student组件的实例对象// this.$refs.student.$on('atguigu',this.getStudentName)// 要么用箭头函数/* this.$refs.student.$on('atguigu',(name,...params)=>{console.log('App收到了学生名:',name,params);console.log(this)this.studentName = name}) */// 绑定自定义事件且让他等三秒钟返回/* setTimeout(()=>{this.$refs.student.$on('atguigu',this.getStudentName)},3000) */// 绑定自定义事件且让按钮只能触发一次// this.$refs.student.$once('atguigu',this.getStudentName)// }}
</script><style scoped>.app {background-color: gray;padding: 5px;}
</style>

1.4 TodoList案例——自定义事件

  • App.vue:
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><!-- <MyHeader :addTodo="addTodo"/> --><!-- 采用自定义事件方法改为: --><MyHeader @addTodo="addTodo"/><MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/> <!--传递数据--><!-- <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/> --><!-- 采用自定义事件方法改为: --><MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import MyList from './components/MyList.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{ MyHeader,MyList,MyFooter},data() {return {todos:JSON.parse(localStorage.getItem('todos')) || []}},methods:{// 添加一个todoaddTodo(todoObj) {// console.log('我是App组件,我收到了数据:',x);this.todos.unshift(todoObj)},// 勾选or取消勾选一个todocheckTodo(id) {this.todos.forEach((todo)=>{if(todo.id === id) todo.done = !todo.done})},// 删除一个tododeleteTodo(id) {this.todos = this.todos.filter((todo)=>{return todo.id !== id})},// 全选or取消全选checkAllTodo(done) {this.todos.forEach((todo)=>{todo.done = done})},// 清除所有已经完成的todoclearAllTodo() {this.todos = this.todos.filter((todo)=>{return !todo.done})}},watch: {todos: {// 深度监视deep:true,handler(value) {localStorage.setItem('todos',JSON.stringify(value))}}}}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 5px;margin-left: 285px;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>
  • MyHeader.vue:
<template><div class="todo-header"><input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/></div>
</template><script>import {nanoid} from 'nanoid'export default {name:'MyHeader',// 采用自定义事件方法改为:// props:['addTodo'],data() {return{title:''}},methods: {add() {// 校验数据if(!this.title.trim()) return alert('输入不能为空') // 如果输入为空 敲回车就没反应 trim()去掉前后空格// console.log(e.target.value);// 将用户的输入包装成为一个todo对象const todoObj = {id:nanoid(),title:this.title,done:false}// console.log(todoObj)// 通知App组件去添加一个todo对象// this.addTodo(todoObj)// 采用自定义事件方法改为:this.$emit('addTodo',todoObj)// 清空输入this.title = ''}}}
</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>
  • MyFooter.vue:
<template><div class="todo-footer" v-show="total"><label><!-- <input type="checkbox" :checked="doneTotal === total"/> --><!-- 写法一 --><!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> --><!-- 写法二 --><input type="checkbox" v-model="isAll"/></label><span><!-- <span>已完成{{doneTotal}}</span> / 全部{{todos.length}} --><span>已完成{{doneTotal}}</span> / 全部{{total}}</span><button class="btn btn-danger" @click="clearAll">清除已完成任务</button></div>
</template><script>export default {name:'MyFooter',// props:['todos','checkAllTodo','clearAllTodo'],// 采用自定义事件方法改为:props:['todos'],computed:{total() {return this.todos.length},doneTotal() {/* const x =  this.todos.reduce((pre,current)=>{console.log('@',pre,current)return pre + (current.done ? 1 : 0)},0)console.log('###',x); */// 简写为:return this.todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0) ,0)},// 写法一/* isAll() {return this.doneTotal === this.total && this.total > 0}, */// 写法二isAll: {get(){return this.doneTotal === this.total && this.total > 0},set(value){// this.checkAllTodo(value)// 采用自定义事件方法改为:this.$emit('checkAllTodo',value)}}},methods: {/* checkAll(e) {// console.log(e.target.checked);this.checkAllTodo(e.target.checked)} */clearAll() {// this.clearAllTodo()// 采用自定义事件方法改为:this.$emit('clearAllTodo')}}}
</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; /* 网页浏览时用户鼠标指针的样式或图形形状为一只手 */}
</style>

2 全局事件总线

2.1 理解

  • 全局事件总线:一种组件间通信的方式,适用于任意组件间通信
    在这里插入图片描述
  • Vue 原型对象上包含事件处理的方法:
    1> $on(eventName, listener): 绑定自定义事件监听
    2> $emit(eventName, data): 分发自定义事件
    3> $off(eventName): 解绑自定义事件监听
    4> $once(eventName, listener): 绑定事件监听, 但只能处理一次
  • 所有组件实例对象的原型对象的原型对象就是 Vue 的原型对象。
    1> 所有组件对象都能看到 Vue 原型对象上的属性和方法。
    2> Vue.prototype.$bus = new Vue(), 所有的组件对象都能看到$bus这个属性对象。

2.2 步骤

  • 安装全局事件总线:
    在这里插入图片描述
  • 使用事件总线:
    1> 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身
    最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
    在这里插入图片描述
    2> 提供数据:
    在这里插入图片描述

2.3 TodoList案例——事件总线

  • main.js代码:
// 引入Vue
import Vue from 'vue'
// 引入App
import App from './App.vue'
// 关闭Vue的生产提示
Vue.config.productionTip = false// 创建vm
new Vue({el:'#app',render: h => h(App),beforeCreate(){Vue.prototype.$bus = this}
})
  • App.vue代码:
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><!-- <MyHeader :addTodo="addTodo"/> --><!-- 采用自定义事件方法改为: --><MyHeader @addTodo="addTodo"/><!-- <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/> 传递数据--><!-- 采用全局事件总线方法改为: --><MyList :todos="todos"/><!-- <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/> --><!-- 采用自定义事件方法改为: --><MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import MyList from './components/MyList.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{ MyHeader,MyList,MyFooter},data() {return {todos:JSON.parse(localStorage.getItem('todos')) || []}},methods:{// 添加一个todoaddTodo(todoObj) {// console.log('我是App组件,我收到了数据:',x);this.todos.unshift(todoObj)},// 勾选or取消勾选一个todocheckTodo(id) {this.todos.forEach((todo)=>{if(todo.id === id) todo.done = !todo.done})},// 删除一个tododeleteTodo(id) {this.todos = this.todos.filter((todo)=>{return todo.id !== id})},// 全选or取消全选checkAllTodo(done) {this.todos.forEach((todo)=>{todo.done = done})},// 清除所有已经完成的todoclearAllTodo() {this.todos = this.todos.filter((todo)=>{return !todo.done})}},watch: {todos: {// 深度监视deep:true,handler(value) {localStorage.setItem('todos',JSON.stringify(value))}}},// 采用全局事件总线方法此处添加:mounted(){this.$bus.$on('checkTodo',this.checkTodo)this.$bus.$on('deleteTodo',this.deleteTodo)},beforeDestroy() {this.$bus.$off('checkTodo')this.$bus.$off('deleteTodo')}}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 5px;margin-left: 285px;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>
  • MyList.vue代码:
<template><ul class="todo-main"><MyItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" /> <!--遍历数组并且传递数据--> <!--采用全局事件总线方法此处删除了 :checkTodo="checkTodo" 和 :deleteTodo="deleteTodo"--></ul>
</template><script>import MyItem from './MyItem.vue'export default {name:'MyList',components: {MyItem},// props:['todos','checkTodo','deleteTodo'] // 接收数据// 采用全局事件总线方法改为:props:['todos']}
</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>
  • MyItem代码:
<template><li><label><!-- 写法一 --><!-- <input type="checkbox" :checked="todo.done" @click="handleCheck(todo.id)"/>  :checked="true" 给input指定一个checked选项,如为true,拥有checked,如为false,则没有checked--><!-- 写法二 --><input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/><!-- 写法三 此写法直接合并后两项 不用App插件将数据传给MyList再传给MyItem 不推荐此写法 因为props是只读的 不建议修改--><!-- <input type="checkbox" v-model="todo.done"/> --><span>{{todo.title}}</span></label><button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button></li>
</template><script>export default {name:'MyItem',// 声明接收todo对象// props:['todo','checkTodo','deleteTodo'],// 采用全局事件总线方法改为:props:['todo'],methods:{// 勾选or取消勾选handleCheck(id) {// console.log(id);// 通知App组件将对应的todo对象的done值取反// this.checkTodo(id)// 采用全局事件总线方法改为:this.$bus.$emit('checkTodo',id)},// 删除handleDelete(id) {// confirm根据用户的交互 确定布尔值为真还是假if(confirm('确定删除吗?')) {// console.log(id);// this.deleteTodo(id)// 采用全局事件总线方法改为:this.$bus.$emit('deleteTodo',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: #ddd;}li:hover button {display: block;}
</style>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3 消息订阅与发布

3.1 理解

  • 这种方式的思想与全局事件总线很相似,它包含以下操作:
    1> 订阅消息 --对应绑定事件监听
    2> 发布消息 --对应分发事件
    3> 取消消息订阅 --对应解绑事件监听
  • 需要引入一个消息订阅与发布的第三方实现库: PubSubJS
  • 报纸订阅与发布步骤:
    1> 订阅报纸:家庭住址
    2> 邮递员送报纸:报纸
  • 消息订阅与发布步骤:
    1> 订阅消息:消息名
    2> 发布消息:消息内容
    在这里插入图片描述
  • 一种组件间通信的方式,适用于任意组件间通信
  • 使用步骤:
    1> 安装pubsub:npm i pubsub-js
    2> 引入:import pubsub from 'pubsub-js'
    3> 接收数据(消息的订阅语法):A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身
    在这里插入图片描述
    4> 提供数据(消息的发布语法):pubsub.publish('xxx',数据)(第一个形参代表消息名,第二个形参代表传递的数据)
    5> 最好在beforeDestroy钩子中,用Pubsub.unsubscribe(pid)取消订阅
  • main.js代码:
// 引入Vue
import Vue from 'vue'
// 引入App
import App from './App.vue'
// 关闭Vue的生产提示
Vue.config.productionTip = false// 创建vm
new Vue({el:'#app',render: h => h(App),
})
  • School.vue代码:
<template><div class="school"><h2>学校名称:{{name}}</h2><h2>学校地址:{{address}}</h2></div>
</template><script>import pubsub from 'pubsub-js'export default {name:'School',data() {return {name:'霍格沃兹魔法学院',address:'苏格兰高地'}},methods: {demo(msgName,data){console.log('有人发布了hello消息,hello消息的回调执行了',data)}},mounted(){this.pubId = pubsub.subscribe('hello',this.demo)},beforeDestroy(){pubsub.unsubscribe(this.pubId)}}
</script><style scoped>.school {background-color: pink;padding: 5px;}
</style>
  • Student.vue代码:
<template><div class="student"><h2>学生姓名:{{name}}</h2><h2>学生性别:{{sex}}</h2><button @click="sendStudentName">把学生名给school组件</button></div>
</template><script>import pubsub from 'pubsub-js'export default {name:'Student',data() {return {name:'小王',sex:'女'}},methods:{sendStudentName(){pubsub.publish('hello',666)}}}
</script><style scoped>.student{background-color: orange;padding: 5px;margin-top: 30px;}
</style>

3.2 TodoList案例——消息的订阅与发布

  • App.vue代码:
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><!-- <MyHeader :addTodo="addTodo"/> --><!-- 采用自定义事件方法改为: --><MyHeader @addTodo="addTodo"/><!-- <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/> 传递数据--><!-- 采用全局事件总线方法改为: --><MyList :todos="todos"/><!-- <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/> --><!-- 采用自定义事件方法改为: --><MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/></div></div></div>
</template><script>// 引入pubsub库import pubsub from 'pubsub-js'import MyHeader from './components/MyHeader.vue'import MyList from './components/MyList.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{ MyHeader,MyList,MyFooter},data() {return {todos:JSON.parse(localStorage.getItem('todos')) || []}},methods:{// 添加一个todoaddTodo(todoObj) {// console.log('我是App组件,我收到了数据:',x);this.todos.unshift(todoObj)},// 勾选or取消勾选一个todocheckTodo(id) {this.todos.forEach((todo)=>{if(todo.id === id) todo.done = !todo.done})},// 删除一个todo// deleteTodo(msgName,id) {// 用下划线占个位deleteTodo(_,id) {this.todos = this.todos.filter((todo)=>{return todo.id !== id})},// 全选or取消全选checkAllTodo(done) {this.todos.forEach((todo)=>{todo.done = done})},// 清除所有已经完成的todoclearAllTodo() {this.todos = this.todos.filter((todo)=>{return !todo.done})}},watch: {todos: {// 深度监视deep:true,handler(value) {localStorage.setItem('todos',JSON.stringify(value))}}},// 采用全局事件总线方法此处添加:mounted(){this.$bus.$on('checkTodo',this.checkTodo)// this.$bus.$on('deleteTodo',this.deleteTodo) // 采用消息订阅与发布方法此处改为:this.pubId = pubsub.subscribe('deleteTodo',this.deleteTodo)},beforeDestroy() {this.$bus.$off('checkTodo')// this.$bus.$off('deleteTodo')// 采用消息订阅与发布方法此处改为:pubsub.unsubscribe(this.pubId)}}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 5px;margin-left: 285px;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>
  • MyItem.vue代码:
<template><li><label><!-- 写法一 --><!-- <input type="checkbox" :checked="todo.done" @click="handleCheck(todo.id)"/>  :checked="true" 给input指定一个checked选项,如为true,拥有checked,如为false,则没有checked--><!-- 写法二 --><input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/><!-- 写法三 此写法直接合并后两项 不用App插件将数据传给MyList再传给MyItem 不推荐此写法 因为props是只读的 不建议修改--><!-- <input type="checkbox" v-model="todo.done"/> --><span>{{todo.title}}</span></label><button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button></li>
</template><script>import pubsub from 'pubsub-js'export default {name:'MyItem',// 声明接收todo对象// props:['todo','checkTodo','deleteTodo'],// 采用全局事件总线方法改为:props:['todo'],methods:{// 勾选or取消勾选handleCheck(id) {// console.log(id);// 通知App组件将对应的todo对象的done值取反// this.checkTodo(id)// 采用全局事件总线方法改为:this.$bus.$emit('checkTodo',id)}, // 删除handleDelete(id) {// confirm根据用户的交互 确定布尔值为真还是假if(confirm('确定删除吗?')) {// console.log(id);// this.deleteTodo(id)// 采用全局事件总线方法改为:// this.$bus.$emit('deleteTodo',id)// 采用消息订阅与发布方法此处改为:pubsub.publish('deleteTodo',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: #ddd;}li:hover button {display: block;}
</style>

4 TodoList案例——编辑

4.1 $nextTick

  • 语法:this.$nextTick(回调函数)
  • 作用:在下一次 DOM 更新结束后执行其指定的回调。
  • 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

4.2 代码

  • MyItem.vue代码:
<template><li><label><!-- 写法一 --><!-- <input type="checkbox" :checked="todo.done" @click="handleCheck(todo.id)"/>  :checked="true" 给input指定一个checked选项,如为true,拥有checked,如为false,则没有checked--><!-- 写法二 --><input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/><!-- 写法三 此写法直接合并后两项 不用App插件将数据传给MyList再传给MyItem 不推荐此写法 因为props是只读的 不建议修改--><!-- <input type="checkbox" v-model="todo.done"/> --><span v-show="!todo.isEdit">{{todo.title}}</span><input type="text" v-show="todo.isEdit" :value="todo.title" @blur="handleBlur(todo,$event)" ref="inputTitle"></label><button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button><button v-show="!todo.isEdit" class="btn btn-edit" @click="handleEdit(todo)">编辑</button></li>
</template><script>import pubsub from 'pubsub-js'export default {name:'MyItem',// 声明接收todo对象// props:['todo','checkTodo','deleteTodo'],// 采用全局事件总线方法改为:props:['todo'],methods:{// 勾选or取消勾选handleCheck(id) {// console.log(id);// 通知App组件将对应的todo对象的done值取反// this.checkTodo(id)// 采用全局事件总线方法改为:this.$bus.$emit('checkTodo',id)}, // 删除handleDelete(id) {// confirm根据用户的交互 确定布尔值为真还是假if(confirm('确定删除吗?')) {// console.log(id);// this.deleteTodo(id)// 采用全局事件总线方法改为:// this.$bus.$emit('deleteTodo',id)// 采用消息订阅与发布方法此处改为:pubsub.publish('deleteTodo',id)}},// 编辑handleEdit(todo) {// todo.isEdit = true // 此写法可以改值 但没有getter和setter// 利用列表渲染中的vue.set// this.$set(todo,'isEdit',true)// 第一次加上isEdit属性 后面无需再加 因此使用if语句// 如果todo身上有isEdit 直接改 如果todo身上没有isEdit 先添加此属性再赋值if('isEdit' in todo) {todo.isEdit = true} else {this.$set(todo,'isEdit',true)}this.$nextTick(function() {// nextTick指定的回调 会在dom节点更新完毕后执行this.$refs.inputTitle.focus() // 获取焦点}) },// 失去焦点回调(真正执行修改逻辑)handleBlur(todo,e){todo.isEdit = falseif(!e.target.value.trim()) return alert('输入不能为空!') // trim()函数用于删除字符串的头尾空白符this.$bus.$emit('updateTodo',todo.id,e.target.value)}}}
</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 input {vertical-align: middle;margin-right: 6px;position: relative;top: -1px;}li button {float: right;display: none; margin-top: 3px; margin-left: 5px;}li:before {content: initial;}li:last-child {border-bottom: none;}li:hover {background-color: #ddd;}li:hover button {display: block;}
</style>
  • App.vue代码:
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><!-- <MyHeader :addTodo="addTodo"/> --><!-- 采用自定义事件方法改为: --><MyHeader @addTodo="addTodo"/><!-- <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/> 传递数据--><!-- 采用全局事件总线方法改为: --><MyList :todos="todos"/><!-- <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/> --><!-- 采用自定义事件方法改为: --><MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/></div></div></div>
</template><script>// 引入pubsub库import pubsub from 'pubsub-js'import MyHeader from './components/MyHeader.vue'import MyList from './components/MyList.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{ MyHeader,MyList,MyFooter},data() {return {todos:JSON.parse(localStorage.getItem('todos')) || []}},methods:{// 添加一个todoaddTodo(todoObj) {// console.log('我是App组件,我收到了数据:',x);this.todos.unshift(todoObj)},// 勾选or取消勾选一个todocheckTodo(id) {this.todos.forEach((todo)=>{if(todo.id === id) todo.done = !todo.done})},// 更新一个todoupdateTodo(id,title) {this.todos.forEach((todo)=>{if(todo.id === id) todo.title = title})},// 删除一个todo// deleteTodo(msgName,id) {// 用下划线占个位deleteTodo(_,id) {this.todos = this.todos.filter((todo)=>{return todo.id !== id})},// 全选or取消全选checkAllTodo(done) {this.todos.forEach((todo)=>{todo.done = done})},// 清除所有已经完成的todoclearAllTodo() {this.todos = this.todos.filter((todo)=>{return !todo.done})}},watch: {todos: {// 深度监视deep:true,handler(value) {localStorage.setItem('todos',JSON.stringify(value))}}},// 采用全局事件总线方法此处添加:mounted(){this.$bus.$on('checkTodo',this.checkTodo)this.$bus.$on('updateTodo',this.updateTodo)// this.$bus.$on('deleteTodo',this.deleteTodo) // 采用消息订阅与发布方法此处改为:this.pubId = pubsub.subscribe('deleteTodo',this.deleteTodo)},beforeDestroy() {this.$bus.$off('checkTodo')this.$bus.$off('updateTodo')// this.$bus.$off('deleteTodo')// 采用消息订阅与发布方法此处改为:pubsub.unsubscribe(this.pubId)}}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 5px;margin-left: 285px;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: skyblue;border: 1px solid rgb(23, 99, 129);}.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>

在这里插入图片描述
在这里插入图片描述

5 过度与动画

5.1 理解

  • 作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。
  • 图示:
    在这里插入图片描述
  • 写法:
    1> 准备好样式:
    元素进入的样式:
    1. v-enter:进入的起点
    2. v-enter-active:进入过程中
    3. v-enter-to:进入的终点
    元素离开的样式:
    1. v-leave:离开的起点
    2. v-leave-active:离开过程中
    3. v-leave-to:离开的终点
    2> 使用<transition>包裹要过度的元素,并配置name属性:
    在这里插入图片描述
    3> 备注:若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key
  • 代码示例:
    1> main.js代码:
// 引入Vue
import Vue from 'vue'
// 引入App
import App from './App.vue'
// 关闭Vue的生产提示
Vue.config.productionTip = false// 创建vm
new Vue({el:'#app',render: h => h(App)
})

2> App.vue代码:

<template><div><Test/><Test2/><Test3/></div>
</template><script>import Test from './components/Test.vue'import Test2 from './components/Test2.vue'import Test3 from './components/Test3.vue'export default {name:'App',components:{Test,Test2,Test3},}
</script>

3> Test.vue代码:

<template><div><button @click="isShow = !isShow">显示/隐藏</button><transition name="hello" :appear="true"> <!-- appear控制h1一上来就滑入 --><h1 v-show="isShow">你好啊!</h1></transition></div>
</template><script>export default {name:'Test',data() {return {isShow:true}}}
</script><style scoped>h1 {background-color: orange;}/* 动画样式 用动画写 *//* 来 */.hello-enter-active {animation: atguigu 1s linear; /* linear 匀速 */}/* 去 */.hello-leave-active {animation: atguigu 1s reverse; /* reverse 反转 */}@keyframes atguigu {from {transform: translateX(-100%);}to {transform: translateX(0);}}
</style>

在这里插入图片描述
4> Test2.vue代码:

<template><div><button @click="isShow = !isShow">显示/隐藏</button><!-- 单个元素过度 --><!-- <transition name="hello" :appear="true"> --> <!-- appear控制h1一上来就滑入 --><!-- <h1 v-show="isShow">你好啊!</h1> --><!-- </transition> --><!-- 多个元素过度 --><transition-group name="hello" :appear="true"> <!-- appear控制h1一上来就滑入 --><h1 v-show="isShow" key="1">你好啊!</h1><h1 v-show="isShow" key="2">小王几点了!</h1></transition-group></div>
</template><script>export default {name:'Test',data() {return {isShow:true}}}
</script><style scoped>h1 {background-color: orange;/* transition: 1s linear;  放在下面*/}/* 动画样式 用过度写 *//* 来 *//* 进入的起点、离开的终点 */.hello-enter, .hello-leave-to {transform: translateX(-100%);}/* 进入过程中 */.hello-enter-active, .hello-leave-active {transition: 1s linear;}/* 进入的终点、离开的起点 */.hello-enter-to, .hello-leave {transform: translateX(0);}/* 去 *//* 离开的起点 *//* .hello-leave {transform: translateX(0);} *//* 离开的终点 *//* .hello-leave-to {transform: translateX(-100%);} */</style>

在这里插入图片描述
5> Test3.vue代码:

<template><div><button @click="isShow = !isShow">显示/隐藏</button><!-- 多个元素过度 --><transition-group:appear="true" name="animate__animated animate__bounce" enter-active-class="animate__swing"leave-active-class="animate__backOutUp"><h1 v-show="isShow" key="1">你好啊!</h1><h1 v-show="isShow" key="2">小王几点了!</h1></transition-group></div>
</template><script>// 引入第三方库import 'animate.css'export default {name:'Test',data() {return {isShow:true}}}
</script><style scoped>h1 {background-color: orange;}    
</style>

在这里插入图片描述
在这里插入图片描述

5.2 TodoList案例——动画

  • 方法一 修改MyItem.vue代码:
<template><transition name="todo" appear="true"><li><label><!-- 写法一 --><!-- <input type="checkbox" :checked="todo.done" @click="handleCheck(todo.id)"/>  :checked="true" 给input指定一个checked选项,如为true,拥有checked,如为false,则没有checked--><!-- 写法二 --><input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/><!-- 写法三 此写法直接合并后两项 不用App插件将数据传给MyList再传给MyItem 不推荐此写法 因为props是只读的 不建议修改--><!-- <input type="checkbox" v-model="todo.done"/> --><span v-show="!todo.isEdit">{{todo.title}}</span><input type="text" v-show="todo.isEdit" :value="todo.title" @blur="handleBlur(todo,$event)" ref="inputTitle"></label><button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button><button v-show="!todo.isEdit" class="btn btn-edit" @click="handleEdit(todo)">编辑</button></li></transition>
</template><script>import pubsub from 'pubsub-js'export default {name:'MyItem',// 声明接收todo对象// props:['todo','checkTodo','deleteTodo'],// 采用全局事件总线方法改为:props:['todo'],methods:{// 勾选or取消勾选handleCheck(id) {// console.log(id);// 通知App组件将对应的todo对象的done值取反// this.checkTodo(id)// 采用全局事件总线方法改为:this.$bus.$emit('checkTodo',id)}, // 删除handleDelete(id) {// confirm根据用户的交互 确定布尔值为真还是假if(confirm('确定删除吗?')) {// console.log(id);// this.deleteTodo(id)// 采用全局事件总线方法改为:// this.$bus.$emit('deleteTodo',id)// 采用消息订阅与发布方法此处改为:pubsub.publish('deleteTodo',id)}},// 编辑handleEdit(todo) {// todo.isEdit = true // 此写法可以改值 但没有getter和setter// 利用列表渲染中的vue.set// this.$set(todo,'isEdit',true)// 第一次加上isEdit属性 后面无需再加 因此使用if语句// 如果todo身上有isEdit 直接改 如果todo身上没有isEdit 先添加此属性再赋值if('isEdit' in todo) {todo.isEdit = true} else {this.$set(todo,'isEdit',true)}this.$nextTick(function() {// nextTick指定的回调 会在dom节点更新完毕后执行this.$refs.inputTitle.focus() // 获取焦点}) },// 失去焦点回调(真正执行修改逻辑)handleBlur(todo,e){todo.isEdit = falseif(!e.target.value.trim()) return alert('输入不能为空!') // trim()函数用于删除字符串的头尾空白符this.$bus.$emit('updateTodo',todo.id,e.target.value)}}}
</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 input {vertical-align: middle;margin-right: 6px;position: relative;top: -1px;}li button {float: right;display: none; margin-top: 3px; margin-left: 5px;}li:before {content: initial;}li:last-child {border-bottom: none;}li:hover {background-color: #ddd;}li:hover button {display: block;}/* 添加动画效果 */.todo-enter-active {animation: atguigu 0.5s linear;}.todo-leave-active {animation: atguigu 0.5s linear reverse;}@keyframes atguigu {from {transform: translateX(100%);}to {transform: translateX(0);}}
</style>
  • 方法二 修改MyList.vue代码:
<template><ul class="todo-main"><transition-group><MyItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" /></transition-group></ul>
</template><script>import MyItem from './MyItem.vue'export default {name:'MyList',components: {MyItem},// props:['todos','checkTodo','deleteTodo'] // 接收数据// 采用全局事件总线方法改为:props:['todos']}
</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;}/* 添加动画效果 */.todo-enter-active {animation: atguigu 0.5s linear;}.todo-leave-active {animation: atguigu 0.5s linear reverse;}@keyframes atguigu {from {transform: translateX(100%);}to {transform: translateX(0);}}
</style>

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/187374.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT、GPT-4 Turbo接口调用(stream模式)

接口地址 https://chat.xutongbao.top/api/light/chat/createChatCompletion 请求方式 post 请求参数 model可选值&#xff1a; “gpt-3.5-turbo-1106”、 “gpt-3.5-turbo-16k” 、 “gpt-4”、“gpt-4-1106-preview”。 默认值为&#xff1a; “gpt-3.5-turbo-1106” to…

剪贴板管理软件 Paste Wizard mac中文版功能特色

Paste Wizard mac是一款剪贴板管理工具&#xff0c;它可以帮助用户更高效地管理剪贴板中的文本、图片、链接等内容。 Paste Wizard mac特色功能 提供了多种方式来保存和管理剪贴板中的内容。用户可以创建自定义的标签&#xff0c;将内容按照标签进行分类&#xff0c;方便快速查…

《QT从基础到进阶·十六》QT实现客户端和服务端的简单交互

QT版本&#xff1a;5.15.2 VS版本&#xff1a;2019 客户端程序主要包含三块&#xff1a;连接服务器&#xff0c;发送消息&#xff0c;关闭客户端 服务端程序主要包含三块&#xff1a;打开消息监听&#xff0c;接收消息并反馈&#xff0c;关闭服务端 1、先打开服务端监听功能 …

【Linux奇遇记】我和Linux的初次相遇

&#x1f308;个人主页: Aileen_0v0 &#x1f525;系列专栏:Linux奇遇记系列专栏&#x1f4ab;"没有罗马,那就自己创造罗马~" 目录 前端和后端的介绍 1.前端 2.后端 3.前后端区别 Linux在前后端开发中的角色 如何学习Linux 去进行程序开发 Linux的常见根目…

基于Amazon EC2和Amazon Systems Manager Session Manager的堡垒机设计和自动化实现

01 背景 在很多企业的实际应用场景中&#xff0c;特别是金融类的客户&#xff0c;大部分的应用都是部署在私有子网中。为了能够让客户的开发人员和运维人员从本地的数据中心中安全的访问云上资源&#xff0c;堡垒机是一个很好的选择。传统堡垒机的核心实现原理是基于 SSH 协议的…

高斯过程回归 | GPR高斯过程回归

高斯过程回归(Gaussian Process Regression, GPR)是一种强大的非参数回归方法,它通过假设数据是从一个高斯过程中生成的来预测新的数据点。 高斯过程是一种定义在连续输入空间上的随机过程,其中任何有限集合的观测值都呈多变量高斯分布。 实现GPR的Python代码import numpy …

RK3399平台开发系列讲解(内存篇)free 命令查看内存占用情况介绍

🚀返回专栏总目录 文章目录 一、free的使用二、free的内容📢free 指令会显示内存的使用情况,包括实体内存,虚拟的交换文件内存,共享内存区段,以及系统核心使用的缓冲区等。 一、free的使用 -b  以 Byte 为单位显示内存使用情况。-k  以 KB 为单位显示内存使用情况。…

数据的使用、表关系的创建、Django框架的请求生命周期流程图

目录 一、数据的增删改查 1. 用户列表的展示 2. 修改数据的逻辑分析 3. 删除功能的分析 二、如何创建表关系 三、Django的请求生命周期流程图 一、数据的增删改查 1. 用户列表的展示 把数据表中得用户数据都给查询出来展示在页面上 查询数据 def userlist(request):&qu…

在gitlab中指定自定义 CI/CD 配置文件

文章目录 1. 介绍2. 配置操作3. 配置场景3.1 CI/CD 配置文件在当前项目step1&#xff1a;在当前项目中创建目录&#xff0c;编写流水线文件存放在该目录中step2&#xff1a;在当前项目中配置step3&#xff1a;运行流水线测试 3.2 CI/CD 配置文件位于外部站点上step1&#xff1a…

【Linux】tree命令的独特用法

有关tree命令&#xff0c;我们只知道它可以将文件目录以树状图的形式展示&#xff0c;但其实还有很多有意思的功能可以使用。 一、tree命令的安装 各linux版本不同&#xff0c;但软件包名字就叫tree&#xff0c;直接安装即可 ubuntu&#xff1a; apt install tree centos&a…

为什么我一直是机器视觉调机仔,为什么一定要学一门高级语言编程?

​ 为什么我是机器视觉调机仔&#xff0c;为什么一定要学一门高级语言编程&#xff0c;以后好不好就业&#xff0c;待遇高不高&#xff0c;都是跟这项技术没关系&#xff0c;是跟这个技术背后的行业发展有关系。 你可以选择离机器视觉行业&#xff0c;也可以选择与高级语言相关…

fpga时序相关概念与理解

一、基本概念理解 对于数字系统而言&#xff0c;建立时间&#xff08;setup time&#xff09;和保持时间&#xff08;hold time&#xff09;是数字电路时序的基础。数字电路系统的稳定性&#xff0c;基本取决于时序是否满足建立时间和保持时间。 建立时间Tsu&#xff1a;触发器…

GPT-4-Turbo的128K长度上下文性能如何?超过73K Tokens的数据支持依然不太好!

本文原文来自DataLearnerAI官方网站&#xff1a;GPT-4-Turbo的128K长度上下文性能如何&#xff1f;超过73K Tokens的数据支持依然不太好&#xff01; | 数据学习者官方网站(Datalearner)https://www.datalearner.com/blog/1051699526438975 GPT-4 Turbo是OpenAI最新发布的号称…

海康Visionmaster-通讯管理:使用 Modbus TCP 通讯 协议与流程交互

使用 Modbus TCP 通讯协议与视觉通讯&#xff0c;当地址为 0000 的保持型寄存器(4x 寄存器)变为 1 时&#xff0c;触发视觉流程执行一次&#xff0c;同时视觉将地址为 0000 的寄存器复位&#xff08;也即写为 0&#xff09;&#xff0c;视觉流程执行完成后&#xff0c;将结果数…

RPC 框架 openfeign 介绍和学习使用总结

一、基本概念 RPC 远程过程调用&#xff08;Remote Procedure Call&#xff09;的缩写形式 Birrell 和 Nelson 在 1984 发表于 ACM Transactions on Computer Systems 的论文《Implementing remote procedure calls》对 RPC 做了经典的诠释。 RPC 是指计算机 A 上的进程&am…

Ps:选区的基本操作

在 Photoshop 中&#xff0c;选区是为处理局部图像而自行定义的一个区域。 定义选区后&#xff0c;操作被限制在选区之内。选区周围显示的虚线边框&#xff0c;俗称“蚂蚁线”。 全选 Select All Ps菜单&#xff1a;选择/全部 Select/All 快捷键&#xff1a;Ctrl A 提示&#…

虚幻C++基础 day3

常见的游戏机制 Actor机关门 创建一个Actor类&#xff0c;添加两个静态网格与一个触发器 UBoxComponentUStaticMeshComponent 头文件&#xff1a; #include “Components/BoxComponent.h”#include “Components/StaticMeshComponent.h” TriggerDoor.h // Fill out your …

SPI简介及FPGA通用MOSI模块实现

简介 SPI&#xff08;Serial Peripheral Interface&#xff0c;串行外围设备接口&#xff09;通讯协议&#xff0c;是Motorola公司提出的一种同步串行接口技术。是一种高速、全双工、同步通信总线。在芯片中只占用四根管脚用来控制及数据传输。 优缺点&#xff1a; SPI通讯协…

yolov5 利用Labelimg对图片进行标注

首先打开yolov5-master&#xff0c;在data文件中新建一个文件夹来存放你需要跑的数据&#xff0c;例如我这次跑的是羽毛球&#xff0c;文件把文件取名为badminton。使用其他文件夹例如images也可以&#xff0c;就是跑多了以后不好整理&#xff0c;然后点击 选中刚刚你存放数据的…

iOS应用加固方案解析:ipa加固安全技术全面评测

​ 在移动应用开发领域&#xff0c;iOS应用的安全性一直备受关注。ipaguard作为一款专业的iOS应用加固方案&#xff0c;采用混淆加密技术&#xff0c;旨在保护应用免受破解、逆向和篡改等风险。本文将深入探讨ipaguard的产品功能、安全技术及其在iOS应用加固领域中的核心优势和…