赞
踩
props 适用于:
(1)父组件 ===> 子组件 通信
(2)子组件 ===> 父组件 通信(要求父先给子一个函数)
使用 v-model 时要切记:
v-model 绑定的值不能是 props 传过来的值,因为 props 是不可修改的!
props 传过来的若是对象类型的值,修改对象中的属性时 Vue 不会报错,但不推荐这样做。
存储内容大小一般支持 5MB 左右(不同浏览器可能不一样)
浏览器通过 Window.sessionStorage 和 Window.localStorage 属性实现本地存储机制。
相关 API:
(1)xxxxxStorage.setItem('key', 'value');
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
(2)xxxxxStorage.getItem('person');
该方法接受一个键名作为参数,返回键名对应的值。
(3)xxxxxStorage.removeItem('key');
该方法接受一个键名作为参数,并把该键名从存储中删除。
(4)xxxxxStorage.clear();
该方法会清空存储中的所有数据。
备注:
(1)SessionStorage 存储的内容会随着浏览器窗口关闭而消失。
(2)LocalStorage 存储的内容,需要手动清除才会消失。
(3)xxxxxStorage.getItem(xxx);
如果 xxx 对应的 value 获取不到,那么 getItem 的返回值是 null。
(4)JSON.parse(null)
的结果依然是 null。
自定义事件是一种组件间通信的方式,适用于:【子组件 ===> 父组件】
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)
绑定自定义事件:
(1)第一种方式,通过 v-on
,在父组件中:Demo @atguigu="test"
或 Demo v-on:atguigu="test"
(2)第二种方式,通过 ref
,在父组件中:
<demo ref="demo">
methods:{
test(){
...
}
},
mounted(){
this.$refs.demo.$on('ayguigu', this.test)
}
(3)若想让自定义事件只能触发一次,可以用 .once
修饰符,或 $once
方法。
触发自定义事件:this.$emit('atguigu', 传给父的参数数据)
解绑自定义事件:解绑一个 this.$off('atguigu')
、解绑多个 this.$off(['atguigu', 'demo', ...])
、解绑全部 this.$off()
组件上也可以绑定原生 DOM 事件,需要使用 native
修饰符。<student @click.native="show"></student>
注意:通过 this.$refs.demo.$on('ayguigu', 回调)
绑定自定义时间时,回调要么配置在 methods 中,要么用箭头函数,否则回调中的 this 会指向子组件。
全局事件总线是一种组件间通信的方式,适用于任意组件间的通信。
安装全局事件总线:
使用全局事件总线:
(1)接收数据:A组件想接收数据,则在A组件中给 $bus 绑定自定义事件,事件的回调留在A组件自身。
methods() {
demo(data){......}
}
......
mounted() {
this.$bus.$on('xxx', this.demo)
}
(2)提供数据:this.$bus.$emit('xxx', 数据)
最好在 beforeDestroy 钩子中,用 $off
去解绑当前组件所用到的事件。
消息订阅与发布是一种组件间通信的方式,适用于任意组件间通信。
使用步骤:
(1)安装第三方库 pubsub:npm i pubsub-js
(2)引入:import pubsub from "pubsub-js";
(3)接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods() {
demo(data){......}
}
......
mounted() {
this.pid = pubsub.subscribe('xxx', this.demo) // 订阅消息
}
(4)提供数据:pubsub.publish('xxx', 数据)
最好在 beforeDestroy 钩子中,用 pubsub.unsubscribe(this.pid);
去取消订阅。
语法:this.$nextTick(回调函数)
作用:nextTick 是一个生命周期钩子,在下一次 DOM 更新结束后执行其指定的回调。
什么时候用:当改变数据后,要基于更新后的新 DOM 进行某些操作时,要在 nextTick 所指定的回调函数中执行。
作用:在插入、更新或移除 DOM 元素时,在合适的时候给元素添加样式类名。
图示:
写法:
(1)准备好样式:
元素进入的样式:
① v-enter:进入的起点
② v-enter-active:进入过程中
① v-enter-to:进入的终点
元素离开的样式:
① v-leave:离开的起点
② v-leave-active:离开过程中
① v-leave-to:离开的终点
使用 <transition>
包裹要过度的元素,并配置 name 属性:
<transition>
<h1 v-show="isShow">你好啊!</h1>
</transition>
备注:若有多个元素需要过度,则需要使用:<transition-group>
,且每个元素都要指定 key
值。
<template> <div> <transition-group name="hello" appear> <h2 v-show="show" key="1">学校名称:{{ name }}</h2> <h2 v-show="show" key="2">学校地址:{{ address }}</h2> </transition-group> </div> </template> <style lang="css" scoped> h2 { background-color: pink; } .hello-enter-active { animation: identifier 1s; } .hello-leave-active { animation: identifier 1s reverse; } @keyframes identifier { from { transform: translateX(-100%); } to { transform: translateX(0); } } </style>
<template> <div> <transition-group name="hello" appear> <h2 v-show="show" key="1">学校名称:{{ name }}</h2> <h2 v-show="show" key="2">学校地址:{{ address }}</h2> </transition-group> </div> </template> <style lang="css" scoped> h2 { background-color: pink; } .hello-enter, .hello-leave-to { transform: translateX(-100%); } .hello-enter-to, .hello-leave { transform: translateX(0); } .hello-enter-active, .hello-leave-active { transition: 1s; } </style>
安装
npm i animate.css
引入,在 <script>
中引入
<script>
import 'animate.css'
</script>
使用
<template> <div> <transition-group name="animate__animated animate__bounce" enter-active-class="animate__jello" leave-active-class="animate__backOutUp" appear > <h2 v-show="!show" key="1">学校名称:{{ name }}</h2> <h2 v-show="show" key="2">学校地址:{{ address }}</h2> </transition-group> </div> </template> <script> import "animate.css"; </script> <style lang="css" scoped> h2 { background-color: pink; } </style>
<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: "AddHeader", data() { return { title: "", }; }, methods: { add() { if (!this.title.trim()) return alert("输入不能为空"); // 包装成对象 const todo = { id: nanoid(), title: this.title, done: false, }; this.$emit('headerAddTodo',todo) 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>
<template> <ul class="todo-main" v-if="todo.length"> <transition-group name="animate__animated animate__bounce" enter-active-class="animate__bounceInRight" leave-active-class="animate__backOutRight" appear > <todo-item v-for="item in todo" :key="item.id" :todo="item" /> </transition-group> </ul> </template> <script> import "animate.css"; import TodoItem from "./TodoItem.vue"; export default { components: { TodoItem }, name: "TodoList", props: ["todo"], }; </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>
<template> <li> <label> <input type="checkbox" :checked="todo.done" @change="handle(todo.id)" /> <span v-show="!todo.isEdit"> {{ todo.title }}</span> </label> <button class="btn btn-danger" @click="ListDelete(todo.id)">删除</button> <button v-show="!todo.isEdit" class="btn btn-edit" @click="editItem(todo)">编辑</button> <input v-show="todo.isEdit" type="text" :value="todo.title" ref="itemInput" @keyup.enter="inputBlur(todo, $event)" @blur="inputBlur(todo, $event)" /> </li> </template> <script> export default { name: "TodoItem", props: ["todo"], methods: { handle(id) { this.$bus.$emit("checkTodo", id); }, ListDelete(id) { if (confirm("确定删除吗?")) { this.$bus.$emit("deleteTodo", id); } }, editItem(todo) { if (todo.hasOwnProperty("isEdit")) { todo.isEdit = true; } else { this.$set(todo, "isEdit", true); } this.$nextTick(function () { this.$refs.itemInput.focus(); }); }, inputBlur(todo, e) { todo.isEdit = false; // console.log(todo.id, e.target.value); if (e.target.value.trim() === "") return alert("你没有输入值"); this.$bus.$emit("todotitle", 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 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>
<template> <div class="todo-footer" v-if="todo.length"> <label> <input type="checkbox" v-model="checkAll" /> </label> <span> <span>已完成{{ doneTotal }}</span> / 全部{{ todo.length }} </span> <button class="btn btn-danger" @click="clearAll">清除已完成任务</button> </div> </template> <script> export default { name: "SelectFooter", props: ["todo"], computed: { doneTotal() { return this.todo.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0); }, checkAll: { get() { return this.todo.length === this.doneTotal; }, set(value) { this.$emit("checkAllTodo", value); }, }, }, methods: { clearAll() { 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; } .todo-footer label input { position: relative; top: -1px; vertical-align: middle; margin-right: 5px; } .todo-footer button { float: right; margin-top: 5px; } </style>
<template> <div id="app"> <div class="todo-container"> <div class="todo-wrap"> <add-header @headerAddTodo="addTodo" /> <todo-list :todo="todo" /> <select-footer :todo="todo" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo" /> </div> </div> </div> </template> <script> import AddHeader from "./components/AddHeader"; import SelectFooter from "./components/SelectFooter"; import TodoList from "./components/TodoList"; export default { name: "App", components: { AddHeader, SelectFooter, TodoList, }, data() { return { todo: JSON.parse(localStorage.getItem("todo")) || [], }; }, methods: { // 添加一个todo addTodo(todo) { this.todo.unshift(todo); }, // 勾选or取消勾选 checkTodo(id) { this.todo.forEach((todo) => { if (id === todo.id) todo.done = !todo.done; }); }, // 修改 editTodo(id, val) { this.todo.forEach((todo) => { if (id === todo.id) todo.title = val.trim(); }); }, // 删除 deleteTodo(id) { this.todo = this.todo.filter((todo) => todo.id !== id); }, checkAllTodo(check) { this.todo.forEach((todo) => (todo.done = check)); }, clearAllTodo() { const x = this.todo.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0); if (x <= 0) { alert("没有勾选任何TODO"); } else { if (confirm("确定删除所有已完成吗?")) { this.todo = this.todo.filter((todo) => !todo.done); } } }, }, watch: { todo: { deep: true, handler(value) { localStorage.setItem("todo", JSON.stringify(value)); }, }, }, created() { this.todo.forEach((todo) => { if (todo.hasOwnProperty("isEdit")) { todo.isEdit = false; } }); }, mounted() { this.$bus.$on("checkTodo", this.checkTodo); this.$bus.$on("deleteTodo", this.deleteTodo); this.$bus.$on("todotitle", this.editTodo); }, beforeDestroy() { this.$bus.$off(["checkTodo", "deleteTodo", "todotitle"]); }, }; </script> <style> /*base*/ 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: skyblue; border: 1px solid rgb(106, 160, 182); 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>
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this;
},
}).$mount('#app')
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。