src/mian.js:
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
el:"#app",
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this
}
})
src/components/App.vue
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<MyHeader @addTodo="addTodo"/>
<MyList :todos="todos"/>
<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:{
//添加一个todo
addTodo(todoObj){
this.todos.unshift(todoObj)
},
//勾选or取消勾选一个todo
checkTodo(id){
this.todos.forEach((todo)=>{
if(todo.id === id) todo.done = !todo.done
})
},
//删除一个todo
deleteTodo(id){
this.todos = this.todos.filter(todo => todo.id !== id)
},
//全选or取消勾选
checkAllTodo(done){
this.todos.forEach(todo => todo.done = done)
},
//删除已完成的todo
clearAllTodo(){
this.todos = this.todos.filter(todo => !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','deleteTodo'])
}
}
</script>
<style>
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>
src/components/MyItem.vue:
<template>
<li>
<label>
<input type="checkbox" :checked="todo.done" @click="handleCheck(todo.id)"/>
<span>{{todo.title}}</span>
</label>
<button class="btn btn-danger" @click="handleDelete(todo.id,todo.title)">删除</button>
</li>
</template>
<script>
export default {
name:'MyItem',
props:['todo'],
methods:{
handleCheck(id){
this.$bus.$emit('checkTodo',id)
},
handleDelete(id,title){
if(confirm("确定删除任务:"+title+"吗?")){
this.$bus.$emit('deleteTodo',id)
}
}
}
}
</script>
<style scoped>
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: #eee;
}
li:hover button{
display: block;
}
</style>
src/components/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('我是School组件,收到了数据:',data)
}
},
mounted() {
this.pubId = pubsub.subscribe('demo',this.demo) //订阅消息
},
beforeDestroy() {
pubsub.unsubscribe(this.pubId) //取消订阅
}
}
</script>
<style scoped>
.school{
background-color: skyblue;
padding: 5px;
}
</style>
src/components/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:'JOJO',
sex:'男',
}
},
methods: {
sendStudentName(){
pubsub.publish('demo',this.name) //发布消息
}
}
}
</script>
<style scoped>
.student{
background-color: pink;
padding: 5px;
margin-top: 30px;
}
</style>
一种组件间通信的方式,适用于任意组件间通信。
使用步骤:
安装pubsub:npm i pubsub-js
引入: import pubsub from 'pubsub-js'
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods(){
demo(data){......}
}
......
mounted() {
this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
}
提供数据:pubsub.publish('xxx',数据)
最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)
去取消订阅。
src/App.vue:
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<MyHeader @addTodo="addTodo"/>
<MyList :todos="todos"/>
<MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/>
</div>
</div>
</div>
</template>
<script>
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:{
//添加一个todo
addTodo(todoObj){
this.todos.unshift(todoObj)
},
//勾选or取消勾选一个todo
checkTodo(_,id){
this.todos.forEach((todo)=>{
if(todo.id === id) todo.done = !todo.done
})
},
//删除一个todo
deleteTodo(id){
this.todos = this.todos.filter(todo => todo.id !== id)
},
//全选or取消勾选
checkAllTodo(done){
this.todos.forEach(todo => todo.done = done)
},
//删除已完成的todo
clearAllTodo(){
this.todos = this.todos.filter(todo => !todo.done)
}
},
watch:{
todos:{
deep:true,
handler(value){
localStorage.setItem('todos',JSON.stringify(value))
}
}
},
mounted(){
this.pubId = pubsub.subscribe('checkTodo',this.checkTodo)
this.$bus.$on('deleteTodo',this.deleteTodo)
},
beforeDestroy(){
pubsub.unsubscribe(this.pubId)
this.$bus.$off('deleteTodo')
}
}
</script>
<style>
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>
src/components/myItem.vue:
<template>
<li>
<label>
<input type="checkbox" :checked="todo.done" @click="handleCheck(todo.id)"/>
<span>{{todo.title}}</span>
</label>
<button class="btn btn-danger" @click="handleDelete(todo.id,todo.title)">删除</button>
</li>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name:'MyItem',
props:['todo'],
methods:{
handleCheck(id){
pubsub.publish('checkTodo',id)
},
handleDelete(id,title){
if(confirm("确定删除任务:"+title+"吗?")){
this.$bus.$emit('deleteTodo',id)
}
}
}
}
</script>
<style scoped>
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: #eee;
}
li:hover button{
display: block;
}
</style>
<button class="btn btn-edit" @click="HandleEdit(todo)">编辑</button>
.btn-edit {
color: #fff;
background-color: skyblue;
border: 1px solid rgb(89, 141, 161);
margin-right: 5px;
}
<span v-show="!todo.isEdit">{{todo.title}}</span>
<input v-show="todo.isEdit" type="text" :value="todo.title" @blur="handleBlur(todo,$event)" ref="inputTitle">
//编辑
HandleEdit(todo){
if(todo.hasOwnProperty('isEdit')){
todo.isEdit = true
}else{
this.$set(todo,'isEdit',true)
}
// 当Vue重新编译模板之后执行$nextTick()中的回调函数
this.$nextTick(function(){
// 使input框获取焦点
this.$refs.inputTitle.focus()
}
)
},
<input v-show="todo.isEdit" type="text" :value="todo.title" @blur="handleBlur(todo,$event)" ref="inputTitle">
handleBlur(todo,e){
// this.$set(todo,'isEdit',false)
todo.isEdit = false
console.log('updateTodo',todo.id,e.target.value)
if(!e.target.value.trim()) return alert('输入不能为空')
this.$bus.$emit('updateTodo',todo.id,e.target.value)
},
updateTodo(id,title){
this.todos.forEach((todo)=>{if(todo.id === id) todo.title = title
})
},
mounted() {
this.$refs.myheader.$on('addTodo',this.addTodo)
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','deleteTodo','updateTodo'])
pubsub.unsubscribe(this.pubId)
},
if(!e.target.value.trim()) return alert('输入不能为空')
MyItem.vue
<template>
<li>
<label>
<input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
<span v-show="!todo.isEdit">{{todo.title}}</span>
<input v-show="todo.isEdit" type="text" :value="todo.title" @blur="handleBlur(todo,$event)" ref="inputTitle">
</label>
<button class="btn btn-danger" @click="HandleDelete(todo.id)">删除</button>
<button class="btn btn-edit" @click="HandleEdit(todo)">编辑</button>
</li>
</template>
<script>
import pubsub from 'pubsub-js';
export default {
name:'MyItem',
props:['todo','checkTodo','deleteTodo',],
methods: {
handleCheck(id){
// console.log(id)
// this.checkTodo(id)
this.$bus.$emit('checkTodo',id)
},
HandleDelete(id){
// console.log('deleteTodo',id)
// this.deleteTodo(id)
// this.$bus.$emit('deleteTodo',id)
pubsub.publish('deleteTodo',id)
},
//编辑
HandleEdit(todo){
if(todo.hasOwnProperty('isEdit')){
todo.isEdit = true
}else{
this.$set(todo,'isEdit',true)
}
// 当Vue重新编译模板之后执行$nextTick()中的回调函数
this.$nextTick(function(){
// 使input框获取焦点
this.$refs.inputTitle.focus()
}
)
},
handleBlur(todo,e){
// this.$set(todo,'isEdit',false)
todo.isEdit = false
console.log('updateTodo',todo.id,e.target.value)
if(!e.target.value.trim()) return alert('输入不能为空')
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 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: #eee;
}
li:hover button{
display: block;
}
</style>
App.vue
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<!-- <MyHeader @addTodo="addTodo"/> -->
<MyHeader ref="myheader"/>
<!-- <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 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:[
// {id:'001',title:'抽烟',done:true,},
// {id:'002',title:'喝酒',done:false,},
// {id:'003',title:'开车',done:true,},
// ],
todos:JSON.parse(localStorage.getItem('todos')) || [{id:'001',title:'抽烟',done:true,}]
}
},
methods: {
addTodo(x){
this.todos.unshift(x)
},
checkTodo(id){
this.todos.forEach((todo)=>{if(todo.id === id) todo.done = !todo.done
})
},
deleteTodo(_,id){
this.todos = this.todos.filter((todo)=>(todo.id != id))
// console.log('deleteTodo',this.todos)
},
checkAllTodo(done){
this.todos.forEach(
(todo)=>{todo.done = done}
)
},
clearAllTodo(){
this.todos = this.todos.filter(todo=>todo.done!=true)
},
updateTodo(id,title){
this.todos.forEach((todo)=>{if(todo.id === id) todo.title = title
})
},
},
watch:{
todos:{
deep:true,
handler(newValue,oldValue){
localStorage.setItem('todos',JSON.stringify(newValue))
}
}
},
mounted() {
this.$refs.myheader.$on('addTodo',this.addTodo)
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','deleteTodo','updateTodo'])
pubsub.unsubscribe(this.pubId)
},
}
</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(89, 141, 161);
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>
$nextTick(回调函数)可以将回调延迟到下次 DOM 更新循环之后执行。
方法1:setTimeout
setTimeout(() => {
this.$refs.inputTitle.focus()
}, 100);
方法2:$nextTick
this.$nextTick(function(){
this.$refs.inputTitle.focus()
})
src/App.vue:
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<MyHeader @addTodo="addTodo"/>
<MyList :todos="todos"/>
<MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/>
</div>
</div>
</div>
</template>
<script>
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:{
//添加一个todo
addTodo(todoObj){
this.todos.unshift(todoObj)
},
//勾选or取消勾选一个todo
checkTodo(_,id){
this.todos.forEach((todo)=>{
if(todo.id === id) todo.done = !todo.done
})
},
//删除一个todo
deleteTodo(id){
this.todos = this.todos.filter(todo => todo.id !== id)
},
//更新一个todo
updateTodo(id,title){
this.todos.forEach((todo)=>{
if(todo.id === id) todo.title = title
})
},
//全选or取消勾选
checkAllTodo(done){
this.todos.forEach(todo => todo.done = done)
},
//删除已完成的todo
clearAllTodo(){
this.todos = this.todos.filter(todo => !todo.done)
}
},
watch:{
todos:{
deep:true,
handler(value){
localStorage.setItem('todos',JSON.stringify(value))
}
}
},
mounted(){
this.pubId = pubsub.subscribe('checkTodo',this.checkTodo)
this.$bus.$on('deleteTodo',this.deleteTodo)
this.$bus.$on('updateTodo',this.updateTodo)
},
beforeDestroy(){
pubsub.unsubscribe(this.pubId)
this.$bus.$off('deleteTodo')
this.$bus.$off('updateTodo')
}
}
</script>
<style>
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: #e04e49;
border: 1px solid #bd362f;
}
.btn-danger:hover {
color: #fff;
background-color: #bd362f;
}
.btn-info {
color: #fff;
background-color: rgb(50, 129, 233);
border: 1px solid rgb(1, 47, 212);
margin-right: 5px;
}
.btn-info:hover {
color: #fff;
background-color: rgb(1, 47, 212);
}
.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>
src/components/MyItem.vue:
<template>
<li>
<label>
<input type="checkbox" :checked="todo.done" @click="handleCheck(todo.id)"/>
<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,todo.title)">删除</button>
<button class="btn btn-info" v-show="!todo.isEdit" @click="handleEdit(todo)">编辑</button>
</li>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name:'MyItem',
props:['todo'],
methods:{
handleCheck(id){
pubsub.publish('checkTodo',id)
},
handleDelete(id,title){
if(confirm("确定删除任务:"+title+"吗?")){
this.$bus.$emit('deleteTodo',id)
}
},
handleEdit(todo){
// 如果todo自身有isEdit属性就将isEdit改成true
if(Object.prototype.hasOwnProperty.call(todo,'isEdit')){
todo.isEdit = true
}else{
// 如果没有就向todo中添加一个响应式的isEdit属性并设为true
this.$set(todo,'isEdit',true)
}
// 当Vue重新编译模板之后执行$nextTick()中的回调函数
this.$nextTick(function(){
// 使input框获取焦点
this.$refs.inputTitle.focus()
})
},
// 当input框失去焦点时更新
handleBlur(todo,event){
todo.isEdit = false
if(!event.target.value.trim()) return alert('输入不能为空!')
this.$bus.$emit('updateTodo',todo.id,event.target.value)
}
}
}
</script>
<style scoped>
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: #eee;
}
li:hover button{
display: block;
}
</style>
$nextTick:
this.$nextTick(回调函数)
src/components/MyAnimation:
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition name="jojo" appear>
<h1 v-show="isShow">你好啊!</h1>
</transition>
</div>
</template>
<script>
export default {
name:'MyTitle',
data() {
return {
isShow:true
}
}
}
</script>
<style scoped>
h1{
background-color: orange;
}
.jojo-enter-active{
animation: jojo 0.5s linear;
}
.jojo-leave-active{
animation: jojo 0.5s linear reverse;
}
@keyframes jojo {
from{
transform: translateX(-100%);
}
to{
transform: translateX(0px);
}
}
</style>
src/components/MyTransition.vue:
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition name="jojo" appear>
<h1 v-show="isShow">你好啊!</h1>
</transition>
</div>
</template>
<script>
export default {
name:'MyTransition',
data() {
return {
isShow:true
}
}
}
</script>
<style scoped>
h1{
background-color: orange;
}
.jojo-enter,.jojo-leave-to{
transform: translateX(-100%);
}
.jojo-enter-to,.jojo-leave{
transform: translateX(0);
}
.jojo-enter-active,.jojo-leave-active{
transition: 0.5s linear;
}
</style>
src/components/MyTransitionGroup.vue:
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition-group name="jojo" appear>
<h1 v-show="isShow" key="1">你好啊!</h1>
<h1 v-show="!isShow" key="2">大笨蛋</h1>
</transition-group>
</div>
</template>
<script>
export default {
name:'MyTitle',
data() {
return {
isShow:true
}
}
}
</script>
·
<style scoped>
h1{
background-color: orange;
}
.jojo-enter,.jojo-leave-to{
transform: translateX(-100%);
}
.jojo-enter-to,.jojo-leave{
transform: translateX(0);
}
.jojo-enter-active,.jojo-leave-active{
transition: 0.5s linear;
}
</style>
官网请见:Animate.csshttps://animate.style/
SOP:1.安装npm install animate.css 2. 引入import ‘animate.css’ 3.transition 或者 transition-group标签 4.写入属性appear, name,enter-active-class,leave-active-class
src/components/ThirdPartAnimation:
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition-group
appear
name="animate__animated animate__bounce"
enter-active-class="animate__backInUp"
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:'MyTitle',
data() {
return {
isShow:true
}
}
}
</script>
<style scoped>
h1{
background-color: orange;
}
</style>
MyItem.vue
<template>
<transition
appear
name="animate__animated animate__bounce"
enter-active-class="animate__swing"
leave-active-class="animate__backOutUp"
>
<li>
<label>
<input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
<span v-show="!todo.isEdit">{{todo.title}}</span>
<input v-show="todo.isEdit" type="text" :value="todo.title" @blur="handleBlur(todo,$event)" ref="inputTitle"/>
</label>
<button class="btn btn-danger" @click="HandleDelete(todo.id)">删除</button>
<button class="btn btn-edit" @click="handleEdit(todo)">编辑</button>
</li>
</transition>
</template>
<script>
import 'animate.css'
import pubsub from 'pubsub-js';
export default {
name:'MyItem',
props:['todo','checkTodo','deleteTodo',],
methods: {
handleCheck(id){
// console.log(id)
// this.checkTodo(id)
this.$bus.$emit('checkTodo',id)
},
HandleDelete(id){
// console.log('deleteTodo',id)
// this.deleteTodo(id)
// this.$bus.$emit('deleteTodo',id)
pubsub.publish('deleteTodo',id)
},
handleEdit(todo){
this.$set(todo,'isEdit',true)
this.$nextTick(function(){
this.$refs.inputTitle.focus()
})
// setTimeout(() => {
// this.$refs.inputTitle.focus()
// }, 100);
},
handleBlur(todo,e){
if(!e.target.value.trim()) return alert('输入不能为空')
this.$set(todo,'isEdit',false)
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 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: #eee;
}
li:hover button{
display: block;
}
</style>
目前最流行的 5 大 Vue 动画库,使用后太炫酷了https://blog.csdn.net/ImagineCode/article/details/124914137
作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。
写法:
准备好样式:
使用
包裹要过度的元素,并配置name属性:
你好啊!
备注:若有多个元素需要过度,则需要使用:
,且每个元素都要指定key
值。
name属性:hello改成jojo
.jojo-enter,.jojo-leave-to{
transform: translateX(-100%);
}
.jojo-enter-to,.jojo-leave{
transform: translateX(0);
}
.jojo-enter-active,.jojo-leave-active{
transition: 0.5s linear;
}
本案例需要下载axios库:npm install axios
vue.config.js:
module.exports = {
pages: {
index: {
entry: 'src/main.js',
},
},
lintOnSave:false,
// 开启代理服务器(方式一)
// devServer: {
// proxy:'http://localhost:5000'
// }
//开启代理服务器(方式二)
devServer: {
proxy: {
'/jojo': {
target: 'http://localhost:5000',
pathRewrite:{'^/jojo':''},
// ws: true, //用于支持websocket,默认值为true
// changeOrigin: true //用于控制请求头中的host值,默认值为true
},
'/atguigu': {
target: 'http://localhost:5001',
pathRewrite:{'^/atguigu':''},
// ws: true, //用于支持websocket,默认值为true
// changeOrigin: true //用于控制请求头中的host值,默认值为true
}
}
}
}
src/App.vue:
<template>
<div id="root">
<button @click="getStudents">获取学生信息</button><br/>
<button @click="getCars">获取汽车信息</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
name:'App',
methods: {
getStudents(){
axios.get('http://localhost:8080/jojo/students').then(
response => {
console.log('请求成功了',response.data)
},
error => {
console.log('请求失败了',error.message)
}
)
},
getCars(){
axios.get('http://localhost:8080/atguigu/cars').then(
response => {
console.log('请求成功了',response.data)
},
error => {
console.log('请求失败了',error.message)
}
)
}
}
}
</script>
在vue.config.js中添加如下配置:
devServer:{
proxy:"http://localhost:5000"
}
说明:
编写vue.config.js配置具体代理规则:
module.exports = {
devServer: {
proxy: {
'/api1': {// 匹配所有以 '/api1'开头的请求路径
target: 'http://localhost:5000',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api1': ''}
},
'/api2': {// 匹配所有以 '/api2'开头的请求路径
target: 'http://localhost:5001',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
}
}
}
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
changeOrigin默认值为true
*/
说明:
App.vue
<template>
<div id="root">
<button @click="getClassroom">获取教室信息</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
name:'App',
methods: {
getClassroom(){
axios.get('http://localhost:8080/tc1/bookingDef/2022-12-13/').then(
response => {
console.log('请求成功了',response.data)
},
error => {
console.log('请求失败了',error.message)
}
)
},
}
}
</script>
vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
runtimeCompiler: true,
lintOnSave:false,//关闭语法检查
// 开启代理服务器(方式二)
devServer: {
proxy: {
'/tc1': { // 匹配所有以 '/api1'开头的请求路径
target: 'https://wzs-tc1-booking.k8sprd-wzs.k8s.wistron.com.cn',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/tc1': ''}
},
}
}
})
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>我的Vue</title>
<!-- 配置页签图标 -->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!-- 引入第三方样式 -->
<link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">
</head>
<body>
<div id="root"></div>
</body>
</html>
<template>
<div class="row">
<div class="card">
<a href="https://github.com/xxxxxx" target="_blank">
<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
</a>
<p class="card-text">xxxxxx</p>
</div>
<div class="card">
<a href="https://github.com/xxxxxx" target="_blank">
<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
</a>
<p class="card-text">xxxxxx</p>
</div>
<div class="card">
<a href="https://github.com/xxxxxx" target="_blank">
<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
</a>
<p class="card-text">xxxxxx</p>
</div>
<div class="card">
<a href="https://github.com/xxxxxx" target="_blank">
<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
</a>
<p class="card-text">xxxxxx</p>
</div>
<div class="card">
<a href="https://github.com/xxxxxx" target="_blank">
<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
</a>
<p class="card-text">xxxxxx</p>
</div>
</div>
</template>
<script>
export default {
name:'MyList',
}
</script>
<style scoped>
.album {
min-height: 50rem; /* Can be removed; just added for demo purposes */
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
}
.card {
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}
.card > img {
margin-bottom: .75rem;
border-radius: 100px;
}
.card-text {
font-size: 85%;
}
</style>
MySearch.vue
<template>
<section class="jumbotron">
<h3 class="jumbotron-heading">Search Github Users</h3>
<div>
<input type="text" placeholder="enter the name you search"/> <button>Search</button>
</div>
</section>
</template>
<script>
export default {
name:'MySearch',
}
</script>
<style scoped>
</style>
App.vue
<template>
<div id="app">
<div class="container">
<MySearch/>
<MyList/>
</div>
</div>
</template>
<script>
import MyList from './components/MyList.vue'
import MySearch from './components/MySearch.vue'
export default {
name:'App',
components:{
MyList,
MySearch,
}
}
</script>
<style>
</style>
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>我的Vue</title>
<!-- 配置页签图标 -->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!-- 引入第三方样式 -->
<link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">
</head>
<body>
<div id="root"></div>
</body>
</html>
关键点在于使用v-for
MySearch.vue
<template>
<section class="jumbotron">
<h3 class="jumbotron-heading">Search Github Users</h3>
<div>
<input type="text" placeholder="enter the name you search" v-model="keyWord"/>
<button @click="searchUsers">Search</button>
</div>
</section>
</template>
<script>
import axios from 'axios'
export default {
name:'MySearch',
data() {
return {
keyWord:'',
}
},
methods: {
searchUsers(){
axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response => {
console.log('请求成功了',response.data.items)
this.$bus.$emit('getUsers',response.data.items)
},
error => {
console.log('请求失败了',error.message)
}
)
}
},
}
</script>
<style scoped>
</style>
MyList.vue
<template>
<div class="row">
<div class="card" v-for="user in users" :key="user.id">
<a :href="user.html_url" target="_blank">
<img :src="user.avatar_url" style="width: 100px"/>
</a>
<p class="card-text">{{user.login}}</p>
</div>
</div>
</template>
<script>
export default {
name:'MyList',
data() {
return {
users:'',
}
},
methods: {
getUsers(users){
console.log('我是List组件,收到数据:',users)
this.users = users
},
},
mounted() {
this.$bus.$on('getUsers',this.getUsers)
},
}
</script>
<style scoped>
.album {
min-height: 50rem; /* Can be removed; just added for demo purposes */
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
}
.card {
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}
.card > img {
margin-bottom: .75rem;
border-radius: 100px;
}
.card-text {
font-size: 85%;
}
</style>
MyList.vue
<template>
<div class="row">
<!-- 展示用户列表 -->
<div class="card" v-show="info.users.length" v-for="user in info.users" :key="user.id">
<a :href="user.html_url" target="_blank">
<img :src="user.avatar_url" style='width: 100px'/>
</a>
<h4 class="card-title">{{user.login}}</h4>
</div>
<!-- 展示欢迎词 -->
<h1 v-show="info.isFirst">欢迎使用!</h1>
<!-- 展示加载中 -->
<h1 v-show="info.isLoading">加载中...</h1>
<!-- 展示错误信息 -->
<h1 v-show="info.errMsg">{{errMsg}}</h1>
</div>
</template>
<script>
export default {
name:'List',
data() {
return {
info:{
isFirst:true,
isLoading:false,
errMsg:'',
users:[]
}
}
},
mounted(){
this.$bus.$on('updateListData',(dataObj)=>{
//动态合并两个对象的属性
this.info = {...this.info,...dataObj}
})
},
beforeDestroy(){
this.$bus.$off('updateListData')
}
}
</script>
<style scoped>
.album {
min-height: 50rem; /* Can be removed; just added for demo purposes */
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
}
.card {
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}
.card > img {
margin-bottom: .75rem;
border-radius: 100px;
}
.card-text {
font-size: 85%;
}
</style>
MySearch.vue
<template>
<section class="jumbotron">
<h3 class="jumbotron-heading">Search Github Users</h3>
<div>
<input type="text" placeholder="enter the name you search" v-model="keyWord"/>
<button @click="getUsers">Search</button>
</div>
</section>
</template>
<script>
import axios from 'axios'
export default {
name:'Search',
data() {
return {
keyWord:''
}
},
methods: {
getUsers(){
//请求前更新List的数据
this.$bus.$emit('updateListData',{isLoading:true,errMsg:'',users:[],isFirst:false})
axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response => {
console.log('请求成功了')
//请求成功后更新List的数据
this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items})
},
error => {
//请求后更新List的数据
this.$bus.$emit('updateListData',{isLoading:false,errMsg:error.message,users:[]})
}
)
}
}
}
</script>
MyList.vue
<template>
<div class="row">
<div class="card" v-for="user in users" :key="user.id">
<a :href="user.html_url" target="_blank">
<img :src="user.avatar_url" style="width: 100px"/>
</a>
<p class="card-text">{{user.login}}</p>
</div>
<h1 v-show="isFirst">欢迎使用</h1>
<h1 v-show="isLoading">数据查询中</h1>
<h1 v-show="errMsg">{{errMsg}}</h1>
</div>
</template>
<script>
export default {
name:'MyList',
data() {
return {
isFirst:true,
isLoading:false,
errMsg:'',
users:[],
}
},
methods: {
getUsers(isFirst,isLoading,errMsg,users){
console.log('我是List组件,收到数据:',users)
this.isFirst = isFirst
this.isLoading = isLoading
this.errMsg = errMsg
this.users = users
},
},
mounted() {
this.$bus.$on('getUsers',this.getUsers)
},
beforeDestroy(){
this.$bus.$off('getUsers')
}
}
</script>
<style scoped>
.album {
min-height: 50rem; /* Can be removed; just added for demo purposes */
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
}
.card {
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}
.card > img {
margin-bottom: .75rem;
border-radius: 100px;
}
.card-text {
font-size: 85%;
}
</style>
MySearch.vue
<template>
<section class="jumbotron">
<h3 class="jumbotron-heading">Search Github Users</h3>
<div>
<input type="text" placeholder="enter the name you search" v-model="keyWord"/>
<button @click="searchUsers">Search</button>
</div>
</section>
</template>
<script>
import axios from 'axios'
export default {
name:'MySearch',
data() {
return {
keyWord:'',
}
},
methods: {
searchUsers(){
this.$bus.$emit('getUsers',false,true,'',[])
axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response => {
console.log('请求成功了',response.data.items)
this.$bus.$emit('getUsers',false,false,'',response.data.items)
},
error => {
console.log('请求失败了',error.message)
this.$bus.$emit('getUsers',false,false,error.message,[])
}
)
}
},
}
</script>
<style scoped>
</style>
MySearch.vue
<template>
<section class="jumbotron">
<h3 class="jumbotron-heading">Search Github Users</h3>
<div>
<input type="text" placeholder="enter the name you search" v-model="keyWord"/>
<button @click="searchUsers">Search</button>
</div>
</section>
</template>
<script>
import axios from 'axios'
export default {
name:'MySearch',
data() {
return {
keyWord:'',
}
},
methods: {
searchUsers(){
this.$bus.$emit('getUsers',{isFirst:false,isLoading:true,errMsg:'',users:[]})
axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response => {
console.log('请求成功了',response.data.items)
this.$bus.$emit('getUsers',{isFirst:false,isLoading:false,errMsg:'',users:response.data.items})
},
error => {
console.log('请求失败了',error.message)
this.$bus.$emit('getUsers',{isFirst:false,isLoading:false,errMsg:error.message,users:[]},)
}
)
}
},
}
</script>
<style scoped>
</style>
MyList.vue
<template>
<div class="row">
<div class="card" v-for="user in info.users" :key="user.id">
<a :href="user.html_url" target="_blank">
<img :src="user.avatar_url" style="width: 100px"/>
</a>
<p class="card-text">{{user.login}}</p>
</div>
<h1 v-show="info.isFirst">欢迎使用</h1>
<h1 v-show="info.isLoading">数据查询中</h1>
<h1 v-show="info.errMsg">{{info.errMsg}}</h1>
</div>
</template>
<script>
export default {
name:'MyList',
data() {
return {
info:{
isFirst:true,
isLoading:false,
errMsg:'',
users:[],
}
}
},
methods: {
getUsers(dataObj){
console.log('我是List组件,收到数据:',dataObj)
this.info.isFirst = dataObj.isFirst
this.info.isLoading = dataObj.isLoading
this.info.errMsg = dataObj.errMsg
this.info.users = dataObj.users
},
},
mounted() {
this.$bus.$on('getUsers',this.getUsers)
},
beforeDestroy(){
this.$bus.$off('getUsers')
}
}
</script>
<style scoped>
.album {
min-height: 50rem; /* Can be removed; just added for demo purposes */
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
}
.card {
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}
.card > img {
margin-bottom: .75rem;
border-radius: 100px;
}
.card-text {
font-size: 85%;
}
</style>
关键点1:三个状态的判断
关键点2:用对象封装会更简单
关键点3:Vue批量控制data中设置的参数技巧 链接
this.info = {…this.info,…dataObj}
下载 vue-resource库:npm i vue-resource
src/main.js:
import Vue from 'vue'
import App from './App.vue'
import vueResource from 'vue-resource'
Vue.config.productionTip = false
Vue.use(vueResource)
new Vue({
el:"#app",
render: h => h(App),
beforeCreate(){
Vue.prototype.$bus = this
}
})
src/App.vue:
<template>
<div class="container">
<Search/>
<List/>
</div>
</template>
<script>
import Search from './components/Search.vue'
import List from './components/List.vue'
export default {
name:'App',
components:{Search,List},
}
</script>
src/components/Search.vue:
<template>
<section class="jumbotron">
<h3 class="jumbotron-heading">Search Github Users</h3>
<div>
<input type="text" placeholder="enter the name you search" v-model="keyWord"/>
<button @click="getUsers">Search</button>
</div>
</section>
</template>
<script>
export default {
name:'Search',
data() {
return {
keyWord:''
}
},
methods: {
getUsers(){
//请求前更新List的数据
this.$bus.$emit('updateListData',{isLoading:true,errMsg:'',users:[],isFirst:false})
this.$http.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response => {
console.log('请求成功了')
//请求成功后更新List的数据
this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items})
},
error => {
//请求后更新List的数据
this.$bus.$emit('updateListData',{isLoading:false,errMsg:error.message,users:[]})
}
)
}
}
}
</script>
src/components/List.vue:
<template>
<div class="row">
<!-- 展示用户列表 -->
<div class="card" v-show="info.users.length" v-for="user in info.users" :key="user.id">
<a :href="user.html_url" target="_blank">
<img :src="user.avatar_url" style='width: 100px'/>
</a>
<h4 class="card-title">{{user.login}}</h4>
</div>
<!-- 展示欢迎词 -->
<h1 v-show="info.isFirst">欢迎使用!</h1>
<!-- 展示加载中 -->
<h1 v-show="info.isLoading">加载中...</h1>
<!-- 展示错误信息 -->
<h1 v-show="info.errMsg">{{errMsg}}</h1>
</div>
</template>
<script>
export default {
name:'List',
data() {
return {
info:{
isFirst:true,
isLoading:false,
errMsg:'',
users:[]
}
}
},
mounted(){
this.$bus.$on('updateListData',(dataObj)=>{
this.info = {...this.info,...dataObj}
})
},
beforeDestroy(){
this.$bus.$off('updateListData')
}
}
</script>
<style scoped>
.album {
min-height: 50rem; /* Can be removed; just added for demo purposes */
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
}
.card {
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}
.card > img {
margin-bottom: .75rem;
border-radius: 100px;
}
.card-text {
font-size: 85%;
}
</style>
vue项目常用的两个Ajax库:
main.js
import App from './App.vue'
import Vue from 'vue'
import VueResource from 'vue-resource'
Vue.config.productionTip = false
Vue.use(VueResource)
new Vue({
el:'#root',
render: h => h(App),
beforeCreate(){
Vue.prototype.$bus = this
}
})
App.vue
<template>
<div id="root">
<button @click="getClassroom">获取教室信息</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
name:'App',
methods: {
getClassroom(){
// axios.get('http://localhost:8080/tc1/bookingDef/2022-12-13/').then(
this.$http.get('http://localhost:8080/tc1/bookingDef/2022-12-13/').then(
response => {
console.log('请求成功了',response.data)
},
error => {
console.log('请求失败了',error.message)
}
)
},
}
}
</script>
src/App.vue:
<template>
<div class="container">
<Category title="美食" >
<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
</Category>
<Category title="游戏" >
<ul>
<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ul>
</Category>
<Category title="电影">
<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category},
data() {
return {
games:['植物大战僵尸','红色警戒','空洞骑士','王国']
}
},
}
</script>
<style scoped>
.container{
display: flex;
justify-content: space-around;
}
</style>
src/components/Category.vue:
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title']
}
</script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
video{
width: 100%;
}
img{
width: 100%;
}
</style>
src/App.vue:
<template>
<div class="container">
<Category title="美食" >
<img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
<a slot="footer" href="http://www.atguigu.com">更多美食</a>
</Category>
<Category title="游戏" >
<ul slot="center">
<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ul>
<div class="foot" slot="footer">
<a href="http://www.atguigu.com">单机游戏</a>
<a href="http://www.atguigu.com">网络游戏</a>
</div>
</Category>
<Category title="电影">
<video slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
<template v-slot:footer>
<div class="foot">
<a href="http://www.atguigu.com">经典</a>
<a href="http://www.atguigu.com">热门</a>
<a href="http://www.atguigu.com">推荐</a>
</div>
<h4>欢迎前来观影</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category},
data() {
return {
games:['植物大战僵尸','红色警戒','空洞骑士','王国']
}
},
}
</script>
<style>
.container,.foot{
display: flex;
justify-content: space-around;
}
h4{
text-align: center;
}
</style>
src/components/Category.vue:
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
<slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title']
}
</script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
video{
width: 100%;
}
img{
width: 100%;
}
</style>
1.通过:games="games"
把数据从子组件传出。
2.父组件通过template来用scope="jojo"
接收。
3.子组件不要用name属性,name="center"请删除。
<template>
<div class="category">
<h3>{{title}}分类</h3>
<slot :games="games" name="center">我是默认值1</slot>
</div>
</template>
src/App.vue:
<template>
<div class="container">
<Category title="游戏" >
<template scope="jojo">
<ul>
<li v-for="(g,index) in jojo.games" :key="index">{{g}}</li>
</ul>
</template>
</Category>
<Category title="游戏" >
<template scope="jojo">
<ol>
<li v-for="(g,index) in jojo.games" :key="index">{{g}}</li>
</ol>
</template>
</Category>
<Category title="游戏" >
<template scope="jojo">
<h4 v-for="(g,index) in jojo.games" :key="index">{{g}}</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category}
}
</script>
<style>
.container,.foot{
display: flex;
justify-content: space-around;
}
h4{
text-align: center;
}
</style>
src/components/Category.vue:
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot :games="games">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title'],
data() {
return {
games:['植物大战僵尸','红色警戒','空洞骑士','王国']
}
},
}
</script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
video{
width: 100%;
}
img{
width: 100%;
}
</style>
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件 。
分类:默认插槽、具名插槽、作用域插槽
使用方式:
默认插槽:
父组件中:
html结构1
子组件中:
插槽默认内容...
具名插槽:
父组件中:
html结构1
html结构2
子组件中:
插槽默认内容...
插槽默认内容...
作用域插槽:
理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
具体编码:
父组件中:
- {{g}}
{{g}}
子组件中:
概念:专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信
<template>
<div class="container">
<Count/>
</div>
</template>
<script>
import Count from './components/Count'
export default {
name:'App',
components:{Count}
}
</script>
src/components/Count.vue:
<template>
<div>
<h1>当前求和为:{{sum}}</h1>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementOdd">当前求和为奇数再加</button>
<button @click="incrementWait">等一等再加</button>
</div>
</template>
<script>
export default {
name:'Count',
data() {
return {
n:1, //用户选择的数字
sum:0 //当前的和
}
},
methods: {
increment(){
this.sum += this.n
},
decrement(){
this.sum -= this.n
},
incrementOdd(){
if(this.sum % 2){
this.sum += this.n
}
},
incrementWait(){
setTimeout(()=>{
this.sum += this.n
},500)
},
},
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
目的:给VC创建$store
1.下载 Vuex:npm i vuex
npm i vuex@3
2.创建src/store/index.js:
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——响应组件中用户的动作、处理业务逻辑
const actions = {}
//准备mutations对象——修改state中的数据
const mutations = {}
//准备state对象——保存具体的数据
const state = {}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
3.在src/main.js中创建 vm 时传入store配置项:
import Vue from 'vue'
import App from './App.vue'
import Vuex from 'vuex'
import store from './store'
Vue.config.productionTip = false
Vue.use(Vuex)
new Vue({
el:"#app",
render: h => h(App),
store
})
坑:import是会首先执行的。
1.原理:
留意输出参数。
context是上下文,是个miniStore,具有API。
Mutations是大写JIA,Actions是小写jia。
一般有异步才放Actions。
2.代码
src/components/Count.vue:
<template>
<div>
<h1>当前求和为:{{$store.state.sum}}</h1>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementOdd">当前求和为奇数再加</button>
<button @click="incrementWait">等一等再加</button>
</div>
</template>
<script>
export default {
name:'Count',
data() {
return {
n:1, //用户选择的数字
}
},
methods: {
increment(){
this.$store.commit('ADD',this.n)
},
decrement(){
this.$store.commit('SUBTRACT',this.n)
},
incrementOdd(){
this.$store.dispatch('addOdd',this.n)
},
incrementWait(){
this.$store.dispatch('addWait',this.n)
},
},
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
src/store/index.js:
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——响应组件中用户的动作
const actions = {
addOdd(context,value){
console.log("actions中的addOdd被调用了")
if(context.state.sum % 2){
context.commit('ADD',value)
}
},
addWait(context,value){
console.log("actions中的addWait被调用了")
setTimeout(()=>{
context.commit('ADD',value)
},500)
},
}
//准备mutations对象——修改state中的数据
const mutations = {
ADD(state,value){
state.sum += value
},
SUBTRACT(state,value){
state.sum -= value
}
}
//准备state对象——保存具体的数据
const state = {
sum:0 //当前的和
}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
1.vuex开发者工具跟Vue开发者工具是集成的。
2.Actions中的context.dispatch可以继续调用Actions的方法。==>可以避免业务逻辑过于复杂。
3.在Ations里面直接修改state,会让vuex开发者工具失效而无法捕获变化,因为开发者工具跟Mutations连接。
初始化数据、配置actions
、配置mutations
,操作文件store.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//引用Vuex
Vue.use(Vuex)
const actions = {
//响应组件中加的动作
jia(context,value){
// console.log('actions中的jia被调用了',miniStore,value)
context.commit('JIA',value)
},
}
const mutations = {
//执行加
JIA(state,value){
// console.log('mutations中的JIA被调用了',state,value)
state.sum += value
}
}
//初始化数据
const state = {
sum:0
}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
})
组件中读取vuex中的数据:$store.state.sum
组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)
或 $store.commit('mutations中的方法名',数据)
备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写
dispatch
,直接编写commit
src/Count.vue:
<template>
<div>
<h1>当前求和为:{{$store.state.sum}}</h1>
<h3>当前求和的10倍为:{{$store.getters.bigSum}}</h3>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementOdd">当前求和为奇数再加</button>
<button @click="incrementWait">等一等再加</button>
</div>
</template>
<script>
export default {
name:'Count',
data() {
return {
n:1, //用户选择的数字
}
},
methods: {
increment(){
this.$store.commit('ADD',this.n)
},
decrement(){
this.$store.commit('SUBTRACT',this.n)
},
incrementOdd(){
this.$store.dispatch('addOdd',this.n)
},
incrementWait(){
this.$store.dispatch('addWait',this.n)
},
},
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
src/store/index.js: 记得在src/store/index.js:新增getters对象,并引用getters
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——响应组件中用户的动作
const actions = {
addOdd(context,value){
console.log("actions中的addOdd被调用了")
if(context.state.sum % 2){
context.commit('ADD',value)
}
},
addWait(context,value){
console.log("actions中的addWait被调用了")
setTimeout(()=>{
context.commit('ADD',value)
},500)
},
}
//准备mutations对象——修改state中的数据
const mutations = {
ADD(state,value){
state.sum += value
},
SUBTRACT(state,value){
state.sum -= value
}
}
//准备state对象——保存具体的数据
const state = {
sum:0 //当前的和
}
//准备getters对象——用于将state中的数据进行加工
const getters = {
bigSum(){
return state.sum * 10
}
}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
1.使用…obj来合并对象。
2.map方法的目的是代替程序员写程式,就是个简写方法
3.mapState和mapGetters方法放在计算属性中。mapMutations和mapActions方法放在methods中。
4.三种写法分别是:①程序员亲自去
5.传参的陷阱
mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
6.我的代码MyCount.vue
<template>
<div>
<h2>当前求和为:{{sum}}</h2>
<h2>我的学校:{{school}}</h2>
<h2>我的课程:{{subject}}</h2>
<h2>求和倍数的10倍:{{bigSum}}</h2>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一会再加</button>
</div>
</template>
<script>
import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'
export default {
name:'MyCount',
data() {
return{
n:0,
}
},
computed:{
// school(){
// return this.$store.state.school
...mapState({sum:'sum',school:'school',subject:'subject',}),
...mapGetters(['bigSum']),
},
methods: {
// increment(){
// this.$store.dispatch('add',this.n)
// },
// decrement(){
// this.$store.commit('SUBTRACT',this.n)
// },
// incrementOdd(){
// this.$store.dispatch('addOdd',this.n)
// },
// incrementWait(){
// this.$store.dispatch('addWait',this.n)
// },
...mapActions({increment:'add',incrementOdd:'addOdd',incrementWait:'addWait',}),
...mapMutations({decrement:'SUBTRACT'})
},
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
src/store/index.js:
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——响应组件中用户的动作
const actions = {
addOdd(context,value){
console.log("actions中的addOdd被调用了")
if(context.state.sum % 2){
context.commit('ADD',value)
}
},
addWait(context,value){
console.log("actions中的addWait被调用了")
setTimeout(()=>{
context.commit('ADD',value)
},500)
},
}
//准备mutations对象——修改state中的数据
const mutations = {
ADD(state,value){
state.sum += value
},
SUBTRACT(state,value){
state.sum -= value
}
}
//准备state对象——保存具体的数据
const state = {
sum:0, //当前的和
name:'JOJO',
school:'尚硅谷',
}
//准备getters对象——用于将state中的数据进行加工
const getters = {
bigSum(){
return state.sum * 10
}
}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
src/components/Count.vue:
<template>
<div>
<h1>当前求和为:{{sum}}</h1>
<h3>当前求和的10倍为:{{bigSum}}</h3>
<h3>我是{{name}},我在{{school}}学习</h3>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementOdd">当前求和为奇数再加</button>
<button @click="incrementWait">等一等再加</button>
</div>
</template>
<script>
import {mapState,mapGetters} from 'vuex'
export default {
name:'Count',
data() {
return {
n:1, //用户选择的数字
}
},
methods: {
increment(){
this.$store.commit('ADD',this.n)
},
decrement(){
this.$store.commit('SUBTRACT',this.n)
},
incrementOdd(){
this.$store.dispatch('addOdd',this.n)
},
incrementWait(){
this.$store.dispatch('addWait',this.n)
},
},
computed:{
// 借助mapState生成计算属性(数组写法)
// ...mapState(['sum','school','name']),
// 借助mapState生成计算属性(对象写法)
...mapState({sum:'sum',school:'school',name:'name'}),
...mapGetters(['bigSum'])
}
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
src/components/Count.vue:
<template>
<div>
<h1>当前求和为:{{sum}}</h1>
<h3>当前求和的10倍为:{{bigSum}}</h3>
<h3>我是{{name}},我在{{school}}学习</h3>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一等再加</button>
</div>
</template>
<script>
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
export default {
name:'Count',
data() {
return {
n:1, //用户选择的数字
}
},
methods: {
// 借助mapActions生成:increment、decrement(对象形式)
...mapMutations({increment:'ADD',decrement:'SUBTRACT'}),
// 借助mapActions生成:incrementOdd、incrementWait(对象形式)
...mapActions({incrementOdd:'addOdd',incrementWait:'addWait'})
},
computed:{
// 借助mapState生成计算属性(数组写法)
// ...mapState(['sum','school','name']),
// 借助mapState生成计算属性(对象写法)
...mapState({sum:'sum',school:'school',name:'name'}),
...mapGetters(['bigSum'])
}
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
mapState方法:用于帮助我们映射state
中的数据为计算属性
computed: {
//借助mapState生成计算属性:sum、school、subject(对象写法)
...mapState({sum:'sum',school:'school',subject:'subject'}),
//借助mapState生成计算属性:sum、school、subject(数组写法)
...mapState(['sum','school','subject']),
},
mapGetters方法:用于帮助我们映射getters
中的数据为计算属性
computed: {
//借助mapGetters生成计算属性:bigSum(对象写法)
...mapGetters({bigSum:'bigSum'}),
//借助mapGetters生成计算属性:bigSum(数组写法)
...mapGetters(['bigSum'])
},
mapActions方法:用于帮助我们生成与actions
对话的方法,即:包含$store.dispatch(xxx)
的函数
methods:{
//靠mapActions生成:incrementOdd、incrementWait(对象形式)
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
//靠mapActions生成:incrementOdd、incrementWait(数组形式)
...mapActions(['jiaOdd','jiaWait'])
}
mapMutations方法:用于帮助我们生成与mutations
对话的方法,即:包含$store.commit(xxx)
的函数
methods:{
//靠mapActions生成:increment、decrement(对象形式)
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//靠mapMutations生成:JIA、JIAN(对象形式)
...mapMutations(['JIA','JIAN']),
}
备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
<template>
<div class="container">
<Count/>
<hr/>
<Person/>
</div>
</template>
<script>
import Count from './components/Count'
import Person from './components/Person'
export default {
name:'App',
components:{Count,Person}
}
</script>
src/store/index.js:
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——响应组件中用户的动作
const actions = {
addOdd(context,value){
console.log("actions中的addOdd被调用了")
if(context.state.sum % 2){
context.commit('ADD',value)
}
},
addWait(context,value){
console.log("actions中的addWait被调用了")
setTimeout(()=>{
context.commit('ADD',value)
},500)
},
}
//准备mutations对象——修改state中的数据
const mutations = {
ADD(state,value){
state.sum += value
},
SUBTRACT(state,value){
state.sum -= value
},
ADD_PERSON(state,value){
console.log('mutations中的ADD_PERSON被调用了')
state.personList.unshift(value)
}
}
//准备state对象——保存具体的数据
const state = {
sum:0, //当前的和
name:'JOJO',
school:'尚硅谷',
personList:[
{id:'001',name:'JOJO'}
]
}
//准备getters对象——用于将state中的数据进行加工
const getters = {
bigSum(){
return state.sum * 10
}
}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
src/components/Count.vue:
<template>
<div>
<h1>当前求和为:{{sum}}</h1>
<h3>当前求和的10倍为:{{bigSum}}</h3>
<h3>我是{{name}},我在{{school}}学习</h3>
<h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一等再加</button>
</div>
</template>
<script>
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
export default {
name:'Count',
data() {
return {
n:1, //用户选择的数字
}
},
methods: {
...mapMutations({increment:'ADD',decrement:'SUBTRACT'}),
...mapActions({incrementOdd:'addOdd',incrementWait:'addWait'})
},
computed:{
...mapState(['sum','school','name','personList']),,
...mapGetters(['bigSum'])
}
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
src/components/Person.vue:
<template>
<div>
<h1>人员列表</h1>
<h3 style="color:red">Count组件求和为:{{sum}}</h3>
<input type="text" placeholder="请输入名字" v-model="name">
<button @click="add">添加</button>
<ul>
<li v-for="p in personList" :key="p.id">{{p.name}}</li>
</ul>
</div>
</template>
<script>
import {nanoid} from 'nanoid'
export default {
name:'Person',
data() {
return {
name:''
}
},
computed:{
personList(){
return this.$store.state.personList
},
sum(){
return this.$store.state.sum
}
},
methods: {
add(){
const personObj = {id:nanoid(),name:this.name}
this.$store.commit('ADD_PERSON',personObj)
this.name = ''
}
}
}
</script>
PersonList.vue
<template>
<div>
<h2>人员列表</h2>
<h2>Count组件求和:{{sum}}</h2>
<input type="text" v-model="name" @keydown.enter="addPersonlist">
<button @click="addPersonlist">添加人员</button>
<ul>
<li v-for="p in personlist" :key="p.id">{{p.name}}</li>
</ul>
</div>
</template>
<script>
import { mapState } from 'vuex';
import { nanoid } from 'nanoid';
export default {
name:'MyCount',
methods: {
addPersonlist(){
let person = {id:nanoid(),name:this.name}
this.$store.commit('ADDPERSONLIST',person)
this.name = ''
}
},
data() {
return {
name:''
}
},
computed:{
...mapState(['sum','personlist',])
}
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
MyCount.vue
<template>
<div>
<h2>当前求和为:{{sum}}</h2>
<h2>我的学校:{{school}}</h2>
<h2>我的课程:{{subject}}</h2>
<h2>求和倍数的10倍:{{bigSum}}</h2>
<h2>PersonList组件人数:{{personlist.length}}</h2>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一会再加</button>
</div>
</template>
<script>
import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'
export default {
name:'MyCount',
data() {
return{
n:0,
}
},
computed:{
// school(){
// return this.$store.state.school
...mapState({sum:'sum',school:'school',subject:'subject',personlist:'personlist'}),
...mapGetters(['bigSum']),
},
methods: {
// increment(){
// this.$store.dispatch('add',this.n)
// },
// decrement(){
// this.$store.commit('SUBTRACT',this.n)
// },
// incrementOdd(){
// this.$store.dispatch('addOdd',this.n)
// },
// incrementWait(){
// this.$store.dispatch('addWait',this.n)
// },
...mapActions({increment:'add',incrementOdd:'addOdd',incrementWait:'addWait',}),
...mapMutations({decrement:'SUBTRACT'})
},
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
store/index.js
import Vue from "vue"
import Vuex from 'vuex'
Vue.use(Vuex)
const actions = {
add(context,value){
console.log('add',context,value)
context.commit('ADD',value)
},
addOdd(context,value){
if(context.state.sum % 2){
context.commit('ADD',value)
}
},
addWait(context,value){
setTimeout(()=>context.commit('ADD',value),500)
}
}
const mutations = {
ADD(state,value){
console.log('ADD',state,value)
state.sum += value
},
SUBTRACT(state,value){
console.log('SUBTRACT',state,value)
state.sum -= value
},
ADDPERSONLIST(state,value){
console.log('ADDPERSONLIST',state,value)
state.personlist.unshift(value)
}
}
const state = {
sum:0,
school:'尚硅谷',
subject:'前端',
personlist:[{
id:'001',name:'张三',
}],
}
const getters = {
bigSum(){
return state.sum * 10
}
}
export default new Vuex.Store({
actions,
mutations,
state,
getters,
})
src/store/index.js:
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//引入count
import countOptions from './count'
//引入person
import personOptions from './person'
//应用Vuex插件
Vue.use(Vuex)
//创建并暴露store
export default new Vuex.Store({
modules:{
countAbout:countOptions,
personAbout:personOptions,
}
})
src/store/count.js:
export default{
namespaced:true,
actions:{
addOdd(context,value){
console.log("actions中的addOdd被调用了")
if(context.state.sum % 2){
context.commit('ADD',value)
}
},
addWait(context,value){
console.log("actions中的addWait被调用了")
setTimeout(()=>{
context.commit('ADD',value)
},500)
}
},
mutations:{
ADD(state,value){
state.sum += value
},
SUBTRACT(state,value){
state.sum -= value
}
},
state:{
sum:0, //当前的和
name:'JOJO',
school:'尚硅谷',
},
getters:{
bigSum(state){
return state.sum * 10
}
}
}
src/store/person.js:
import axios from "axios"
import { nanoid } from "nanoid"
export default{
namespaced:true,
actions:{
addPersonWang(context,value){
if(value.name.indexOf('王') === 0){
context.commit('ADD_PERSON',value)
}else{
alert('添加的人必须姓王!')
}
},
addPersonServer(context){
axios.get('http://api.uixsj.cn/hitokoto/get?type=social').then(
response => {
context.commit('ADD_PERSON',{id:nanoid(),name:response.data})
},
error => {
alert(error.message)
}
)
}
},
mutations:{
ADD_PERSON(state,value){
console.log('mutations中的ADD_PERSON被调用了')
state.personList.unshift(value)
}
},
state:{
personList:[
{id:'001',name:'JOJO'}
]
},
getters:{
firstPersonName(state){
return state.personList[0].name
}
}
}
src/components/Count.vue:
<template>
<div>
<h1>当前求和为:{{sum}}</h1>
<h3>当前求和的10倍为:{{bigSum}}</h3>
<h3>我是{{name}},我在{{school}}学习</h3>
<h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一等再加</button>
</div>
</template>
<script>
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
export default {
name:'Count',
data() {
return {
n:1, //用户选择的数字
}
},
methods: {
...mapMutations('countAbout',{increment:'ADD',decrement:'SUBTRACT'}),
...mapActions('countAbout',{incrementOdd:'addOdd',incrementWait:'addWait'})
},
computed:{
...mapState('countAbout',['sum','school','name']),
...mapGetters('countAbout',['bigSum']),
...mapState('personAbout',['personList'])
}
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
src/components/Person.vue:
<template>
<div>
<h1>人员列表</h1>
<h3 style="color:red">Count组件求和为:{{sum}}</h3>
<h3>列表中第一个人的名字是:{{firstPersonName}}</h3>
<input type="text" placeholder="请输入名字" v-model="name">
<button @click="add">添加</button>
<button @click="addWang">添加一个姓王的人</button>
<button @click="addPerson">随机添加一个人</button>
<ul>
<li v-for="p in personList" :key="p.id">{{p.name}}</li>
</ul>
</div>
</template>
<script>
import {nanoid} from 'nanoid'
export default {
name:'Person',
data() {
return {
name:''
}
},
computed:{
personList(){
return this.$store.state.personAbout.personList
},
sum(){
return this.$store.state.countAbout.sum
},
firstPersonName(){
return this.$store.getters['personAbout/firstPersonName']
}
},
methods: {
add(){
const personObj = {id:nanoid(),name:this.name}
this.$store.commit('personAbout/ADD_PERSON',personObj)
this.name = ''
},
addWang(){
const personObj = {id:nanoid(),name:this.name}
this.$store.dispatch('personAbout/addPersonWang',personObj)
this.name = ''
},
addPerson(){
this.$store.dispatch('personAbout/addPersonServer')
}
},
}
</script>
index.js
import Vue from "vue"
import Vuex from 'vuex'
import countOptions from './count'
import personOptions from './person'
Vue.use(Vuex)
export default new Vuex.Store({
modules:{
countAbout:countOptions,
personAbout:personOptions,
}
})
count.js
export default{
namespaced:true,
state:{
sum:0,
school:'尚硅谷',
subject:'前端',
},
actions:{
add(context,value){
console.log('add',context,value)
context.commit('ADD',value)
},
addOdd(context,value){
if(context.state.sum % 2){
context.commit('ADD',value)
}
},
addWait(context,value){
setTimeout(()=>context.commit('ADD',value),500)
}
},
mutations:{
ADD(state,value){
console.log('ADD',state,value)
state.sum += value
},
SUBTRACT(state,value){
console.log('SUBTRACT',state,value)
state.sum -= value
},
},
getters:{
bigSum(state){
return state.sum * 10
}
},
}
person.js
export default{
namespaced:true,
actions:{},
mutations:{
ADDPERSONLIST(state,value){
console.log('ADDPERSONLIST',state,value)
state.personlist.unshift(value)
}
},
state:{
personlist:[{
id:'001',name:'张三',
}],
},
getters:{},
}
App.vue
<template>
<div class="container">
<MyCount/>
<PersonList/>
</div>
</template>
<script>
import MyCount from './components/MyCount.vue';
import PersonList from './components/PersonList.vue'
export default {
name:'App',
components:{
MyCount,
PersonList,
},
}
</script>
MyCount.vue
<template>
<div>
<h2>当前求和为:{{sum}}</h2>
<h2>我的学校:{{school}}</h2>
<h2>我的课程:{{subject}}</h2>
<h2>求和倍数的10倍:{{bigSum}}</h2>
<h2>PersonList组件人数:{{personlist.length}}</h2>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一会再加</button>
</div>
</template>
<script>
import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'
export default {
name:'MyCount',
data() {
return{
n:0,
}
},
computed:{
// school(){
// return this.$store.state.school
...mapState('countAbout',{sum:'sum',school:'school',subject:'subject'}),
...mapState('personAbout',{personlist:'personlist'}),
...mapGetters('countAbout',['bigSum']),
},
methods: {
// increment(){
// this.$store.dispatch('add',this.n)
// },
// decrement(){
// this.$store.commit('SUBTRACT',this.n)
// },
// incrementOdd(){
// this.$store.dispatch('addOdd',this.n)
// },
// incrementWait(){
// this.$store.dispatch('addWait',this.n)
// },
...mapActions('countAbout',{increment:'add',incrementOdd:'addOdd',incrementWait:'addWait',}),
...mapMutations('countAbout',{decrement:'SUBTRACT'})
},
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
PersonList.vue
<template>
<div>
<h2>人员列表</h2>
<h2>Count组件求和:{{sum}}</h2>
<input type="text" v-model="name" @keydown.enter="addPersonlist">
<button @click="addPersonlist">添加人员</button>
<ul>
<li v-for="p in personlist" :key="p.id">{{p.name}}</li>
</ul>
</div>
</template>
<script>
import { mapState } from 'vuex';
import { nanoid } from 'nanoid';
export default {
name:'MyCount',
methods: {
addPersonlist(){
let person = {id:nanoid(),name:this.name}
this.$store.commit('personAbout/ADDPERSONLIST',person)
this.name = ''
}
},
data() {
return {
name:''
}
},
computed:{
...mapState('personAbout',['personlist',]),
...mapState('countAbout',['sum',]),
}
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
bigSum()需要带参数state
const getters = {
bigSum(state){
return state.sum * 10
}
}
数据出错时,子组件无法渲染。
<h2>PersonList组件人数:{{personlist.length}}</h2>
目的:让代码更好维护,让多种数据分类更加明确。
修改store.js
const countAbout = {
namespaced:true,//开启命名空间
state:{x:1},
mutations: { ... },
actions: { ... },
getters: {
bigSum(state){
return state.sum * 10
}
}
}
const personAbout = {
namespaced:true,//开启命名空间
state:{ ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
countAbout,
personAbout
}
})
开启命名空间后,组件中读取state数据:
//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),
开启命名空间后,组件中读取getters数据:
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])
开启命名空间后,组件中调用dispatch
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
开启命名空间后,组件中调用commit
//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
1.什么是路由?
一个路由就是一组映射关系(key - value)
key 为路径,value 可能是 function 或 component
2.路由分类
后端路由:
理解:value 是 function,用于处理客户端提交的请求
工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据
前端路由:
理解:value 是 component,用于展示页面内容
工作过程:当浏览器的路径改变时,对应的组件就会显示
下载vue-router:npm i vue-router npm i vue-router@3
第一步,路由规则建立:路径为src/router/index.js
import Vue from "vue"
import VueRouter from "vue-router"
import MyAbout from '../components/MyAbout.vue'
import MyHome from '../components/MyHome.vue'
export default new VueRouter({
routes:[
{
path:'/about',
component:MyAbout,
},
{
path:'/home',
component:MyHome,
}
]
})
第二步,在src/main.js引入VueRouter、router文件,并注册router
import App from './App.vue'
import Vue from 'vue'
import VueRouter from 'vue-router'
import router from './router'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
el:'#root',
render: h => h(App),
router,
})
第三步,准备好对应的路由子组件
MyAbout.vue
<template>
<h2>我是About组件的内容</h2>
</template>
<script>
export default {
name:'MyAbout',
}
</script>
MyHome.vue
<template>
<h2>我是Home组件的内容</h2>
</template>
<script>
export default {
name:'MyHome',
}
</script>
第四步,修改App.vue
router-link标签替代a标签,to属性替代href属性,路由组件的显示预留位置用router-view标签。
<template>
<div>
<div class="row">
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header"><h2>Vue Router Demo</h2></div>
</div>
</div>
<div class="row">
<div class="col-xs-2 col-xs-offset-2">
<div class="list-group">
<!-- 原始html中我们使用a标签实现页面跳转 -->
<!-- <a class="list-group-item active" href="./about.html">About</a>
<a class="list-group-item" href="./home.html">Home</a> -->
<!-- Vue中借助router-link标签实现路由的切换 -->
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
</div>
</div>
<div class="col-xs-6">
<div class="panel">
<div class="panel-body">
<router-view></router-view>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name:'App',
}
</script>
安装vue-router,命令:npm i vue-router
应用插件:Vue.use(VueRouter)
编写router配置项:
//引入VueRouter
import VueRouter from 'vue-router'
//引入Luyou 组件
import About from '../components/About'
import Home from '../components/Home'
//创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home
}
]
})
//暴露router
export default router
实现切换(active-class可配置高亮样式)
About
指定展示位置
pages
文件夹,一般组件通常存放在components
文件夹。<template>
<h2>我是Home组件的内容</h2>
</template>
<script>
export default {
name:'MyHome',
}
</script>
src/pages/MyAbout.vue:
import Vue from "vue"
import VueRouter from "vue-router"
import MyAbout from '../pages/MyAbout.vue'
```javascript
<template>
<h2>我是About组件的内容</h2>
</template>
<script>
export default {
name:'MyAbout',
}
</script>
src/router/index.js:
import Vue from "vue"
import VueRouter from "vue-router"
import MyAbout from '../pages/MyAbout.vue'
import MyHome from '../pages/MyHome.vue'
export default new VueRouter({
routes:[
{
path:'/about',
component:MyAbout,
},
{
path:'/home',
component:MyHome,
}
]
})
src/components/MyBanner.vue:
<template>
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header"><h2>Vue Router Demo</h2></div>
</div>
</template>
<script>
export default {
name:'MyBanner',
}
</script>
src/App.vue:
<template>
<div>
<div class="row">
<MyBanner/>
</div>
<div class="row">
<div class="col-xs-2 col-xs-offset-2">
<div class="list-group">
<!-- 原始html中我们使用a标签实现页面跳转 -->
<!-- <a class="list-group-item active" href="./about.html">About</a>
<a class="list-group-item" href="./home.html">Home</a> -->
<!-- Vue中借助router-link标签实现路由的切换 -->
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
</div>
</div>
<div class="col-xs-6">
<div class="panel">
<div class="panel-body">
<router-view></router-view>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import MyBanner from './components/MyBanner.vue';
export default {
name:'App',
components:{
MyBanner,
}
}
</script>
src/pages/MyHome.vue:
<template>
<div>
<h2>我是Home组件的内容</h2>
<div>
<ul class="nav nav-tabs">
<li>
<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
</li>
<li>
<router-link class="list-group-item" active-class="active" to="/home/message">Message</router-link>
</li>
</ul>
<div>
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
export default {
name:'MyHome',
}
</script>
src/pages/MyNews.vue:
<template>
<ul>
<li>new001</li>
<li>new002</li>
<li>new003</li>
</ul>
</template>
<script>
export default {
name:'MyNews',
}
</script>
src/pages/MyMessage.vue:
<template>
<ul>
<li><a href="/message1">message001</a></li>
<li><a href="/message1">message001</a></li>
<li><a href="/message1">message001</a></li>
</ul>
</template>
<script>
export default {
name:'MyMessage',
}
</script>
src/router/index.js:x
import Vue from "vue"
import VueRouter from "vue-router"
import MyAbout from '../pages/MyAbout.vue'
import MyHome from '../pages/MyHome.vue'
import MyNews from '../pages/MyNews'
import MyMessage from '../pages/MyMessage'
export default new VueRouter({
routes:[
{
path:'/about',
component:MyAbout,
},
{
path:'/home',
component:MyHome,
children:[
{
path:'news',
component:MyNews,
},
{
path:'message',
component:MyMessage,
}
]
}
]
})
配置路由规则,使用children配置项:
routes:[
{
path:'/about',
component:About,
},
{
path:'/home',
component:Home,
children:[ //通过children配置子级路由
{
path:'news', //此处一定不要写:/news
component:News
},
{
path:'message',//此处一定不要写:/message
component:Message
}
]
}
]
跳转(要写完整路径):
News
第一步,定义路由规则:src/router.index.js
import Vue from "vue"
import VueRouter from "vue-router"
import MyAbout from '../pages/MyAbout.vue'
import MyHome from '../pages/MyHome.vue'
import MyNews from '../pages/MyNews'
import MyMessage from '../pages/MyMessage'
import MyDetail from '../pages/MyDetail'
export default new VueRouter({
routes:[
{
path:'/about',
component:MyAbout,
},
{
path:'/home',
component:MyHome,
children:[
{
path:'news',
component:MyNews,
},
{
path:'message',
component:MyMessage,
children:[
{
path:'detail',
component:MyDetail,
},
]
}
]
}
]
})
第二步,定义
和传参规则:src/pages/Message.vue
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id">
<router-link :to="{
path:'/home/message/detail',
query:{
id:m.id,
title:m.title,
}
}">
{{m.title}}
</router-link>
</li>
</ul>
<hr/>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'MyMessage',
data(){
return{
messageList:[
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'},
]
}
}
}
</script>
第三步,读取参数并展示:src/pages/Detail.vue
<template>
<ul>
<li>消息编码:{{$route.query.id}}</li>
<li>消息标题:{{$route.query.title}}</li>
</ul>
</template>
<script>
export default {
name:'MyDetail',
}
</script>
传递参数
跳转
跳转
接收参数:
$route.query.id
$route.query.title
1.作用:可以简化路由的跳转
2.如何使用:
2.1 给路由命名:
{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello' //给路由命名
path:'welcome',
component:Hello,
}
]
}
]
}
2.2 简化跳转:
<!--简化前,需要写完整的路径 -->
<router-link to="/demo/test/welcome">跳转</router-link>
<!--简化后,直接通过名字跳转 -->
<router-link :to="{name:'hello'}">跳转</router-link>
<!--简化写法配合传递参数 -->
<router-link
:to="{
name:'hello',
query:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
params最好使用路由的对象写法和name属性。
使用占位符声明接收params参数。
配置路由,声明接收params参数
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
component:Message,
children:[
{
name:'xiangqing',
path:'detail/:id/:title', //使用占位符声明接收params参数
component:Detail
}
]
}
]
}
传递参数
跳转
跳转
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
接收参数:
$route.params.id
$route.params.title
import Vue from "vue"
import VueRouter from "vue-router"
import MyAbout from '../pages/MyAbout.vue'
import MyHome from '../pages/MyHome.vue'
import MyNews from '../pages/MyNews'
import MyMessage from '../pages/MyMessage'
import MyDetail from '../pages/MyDetail'
export default new VueRouter({
routes:[
{
name:'guanyu',
path:'/about',
component:MyAbout,
},
{
path:'/home',
component:MyHome,
children:[
{
path:'news',
component:MyNews,
},
{
path:'message',
component:MyMessage,
children:[
{
name:'xiangqing',
path:'detail/:id/:title', //使用占位符声明接收params参数
component:MyDetail,
},
]
}
]
}
]
})
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id">
<router-link :to="{
name:'xiangqing',
// path:'/home/message/detail',
params:{
id:m.id,
title:m.title,
}
}">
{{m.title}}
</router-link>
</li>
</ul>
<hr/>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'MyMessage',
data(){
return{
messageList:[
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'},
]
}
}
}
</script>
<template>
<ul>
<li>消息编码:{{$route.params.id}}</li>
<li>消息标题:{{$route.params.title}}</li>
</ul>
</template>
<script>
export default {
name:'MyDetail',
mounted() {
console.log('MyDetail',this)
},
}
</script>
props有三种方法,首选第三种函数写法。
1.props的第一种写法,值为对象,该对象中的所有key-value都会以props的形式传给Detail组件。
2.props的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件。
3.第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
1.作用:让路由组件更方便的收到参数
2.路由的props配置
{
name:'xiangqing',
path:'detail/:id',
component:Detail,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
// props:true
//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
3.用props属性接收数据
<script>
export default {
name:'Detail',
props:['id','title']
}
</script>
src/router/index.js
import Vue from "vue"
import VueRouter from "vue-router"
import MyAbout from '../pages/MyAbout.vue'
import MyHome from '../pages/MyHome.vue'
import MyNews from '../pages/MyNews'
import MyMessage from '../pages/MyMessage'
import MyDetail from '../pages/MyDetail'
export default new VueRouter({
routes:[
{
name:'guanyu',
path:'/about',
component:MyAbout,
},
{
path:'/home',
component:MyHome,
children:[
{
path:'news',
component:MyNews,
},
{
path:'message',
component:MyMessage,
children:[
{
name:'xiangqing',
path:'detail/:id/:title', //使用占位符声明接收params参数
component:MyDetail,
// props:{a:1,b:2}
// props:true,
props(route){
return {
id:route.params.id,
title:route.params.title,
}
}
},
]
}
]
}
]
})
src/pages/MyMessage.vue
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id">
<router-link :to="{
name:'xiangqing',
// path:'/home/message/detail',
params:{
id:m.id,
title:m.title,
}
}">
{{m.title}}
</router-link>
</li>
</ul>
<hr/>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'MyMessage',
data(){
return{
messageList:[
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'},
]
}
}
}
</script>
src/pages/MyDetail.vue
<template>
<ul>
<li>消息编码:{{id}}</li>
<li>消息标题:{{title}}</li>
</ul>
</template>
<script>
export default {
name:'MyDetail',
mounted() {
console.log('MyDetail',this)
},
props:['id','title']
}
</script>
历史记录操作分为push模式和replace模式。
的replace属性push
和replace
,push
是追加历史记录,replace
是替换当前记录。路由跳转时候默认为push
replace
模式:News
src/pages/MyMessage.vue在增加replace属性,无法按返回键。
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id">
<router-link :to="{
name:'xiangqing',
// path:'/home/message/detail',
params:{
id:m.id,
title:m.title,
}
}"
replace
>
{{m.title}}
</router-link>
</li>
</ul>
<hr/>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'MyMessage',
data(){
return{
messageList:[
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'},
]
}
}
}
</script>
使用场景:遇到button标签需要跳转(不用router-link标签来跳转)。
编程式路由导航一共有5个API,分别是push、replace、forward、back、go。
作用:不借助
实现路由跳转,让路由跳转更加灵活
具体编码:
//$router的两个API
this.$router.push({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退
src/components/MyBanner.vue 展示forward、back、go等3个API。
<template>
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header">
<h2>Vue Router Demo</h2>
<button @click="back">后退</button>
<button @click="forward">前进</button>
<button @click="test">走3数</button>
</div>
</div>
</template>
<script>
export default {
name:'MyBanner',
methods: {
back(){
this.$router.back()
},
forward(){
this.$router.forward()
},
test(){
this.$router.go(3)
}
},
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
src/pages/MyMessage.vue 展示push、replace等2个API。
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id">
<router-link :to="{
name:'xiangqing',
// path:'/home/message/detail',
params:{
id:m.id,
title:m.title,
}
}"
replace
>
{{m.title}}
</router-link>
<button @click="showPush(m)">push</button>
<button @click="showReplace(m)">replace</button>
</li>
</ul>
<hr/>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'MyMessage',
data(){
return{
messageList:[
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'},
]
}
},
methods: {
showPush(m){
this.$router.push({
name:'xiangqing',
params:{
id:m.id,
title:m.title,
}
})
},
showReplace(m){
this.$router.replace({
name:'xiangqing',
params:{
id:m.id,
title:m.title,
}
})
}
},
}
</script>
作用:让不展示的路由组件保持挂载,不被销毁。
具体编码:使用keep-alive标签,使组件不执行销毁流程。News为组件名,如果去掉include="News"则默认保存全部子组件。
src/pages/MyNews.vue增加input框方便试验缓存数据
<template>
<ul>
<li>new001<input type="text"></li>
<li>new002<input type="text"></li>
<li>new003<input type="text"></li>
</ul>
</template>
<script>
export default {
name:'MyNews',
}
</script>
src/pages/MyHome.vue使用keep-alive标签
<template>
<div>
<h2>我是Home组件的内容</h2>
<div>
<ul class="nav nav-tabs">
<li>
<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
</li>
<li>
<router-link class="list-group-item" active-class="active" to="/home/message">Message</router-link>
</li>
</ul>
<div>
<keep-alive include="MyNews">
<router-view></router-view>
</keep-alive>
</div>
</div>
</div>
</template>
<script>
export default {
name:'MyHome',
}
</script>
activated
路由组件被激活时触发。deactivated
路由组件失活时触发。src/pages/MyNews.vue
<template>
<ul>
<li :style="{opacity}">欢迎学习Vue</li>
<li>new001<input type="text"></li>
<li>new002<input type="text"></li>
<li>new003<input type="text"></li>
</ul>
</template>
<script>
export default {
name:'MyNews',
data() {
return {
opacity:1,
}
},
activated(){
console.log('News组件被激活')
this.timer = setInterval(() => {
this.opacity -= 0.01
console.log(this.opacity)
if(this.opacity <= 0) this.opacity = 1
},16)
},
deactivated(){
console.log('News组件失效了')
clearInterval(this.timer)
}
}
</script>
作用:对路由进行权限控制
分类:全局守卫、独享守卫、组件内守卫
全局守卫:
//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
console.log('beforeEach',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
next() //放行
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next() //放行
}
})
//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
console.log('afterEach',to,from)
if(to.meta.title){
document.title = to.meta.title //修改网页的title
}else{
document.title = 'vue_test'
}
})
独享守卫:
beforeEnter(to,from,next){
console.log('beforeEnter',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){
next()
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next()
}
}
组件内守卫:
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
Season的一些话:
src/router/index.js:
import Vue from "vue"
import VueRouter from "vue-router"
import MyAbout from '../pages/MyAbout.vue'
import MyHome from '../pages/MyHome.vue'
import MyNews from '../pages/MyNews'
import MyMessage from '../pages/MyMessage'
import MyDetail from '../pages/MyDetail'
const router = new VueRouter({
routes:[
{
name:'guanyu',
path:'/about',
component:MyAbout,
meta:{title:'关于'}
},
{
name:'zhuye',
path:'/home',
component:MyHome,
meta:{title:'主页'},
children:[
{
name:'xinwen',
path:'news',
component:MyNews,
meta:{isAuth:true,title:'新闻'}
},
{
path:'message',
component:MyMessage,
children:[
{
name:'xiangqing',
path:'detail/:id/:title',
component:MyDetail,
meta:{isAuth:true,title:'详情'},
props(route){
return {
id:route.params.id,
title:route.params.title,
}
}
},
]
}
]
}
]
})
router.beforeEach((to,from,next)=>{
console.log('前置路由守卫',to,from)
if(to.meta.isAuth){
if(localStorage.getItem('school')==='atguigu'){
next()
}else{
alert('学校名不对,无访问权限')
}
}else{
next()
}
})
router.afterEach((to,from)=>{
console.log('后置路由守卫',to,from)
document.title = to.meta.title || '硅谷系统'
})
export default router
独享路由守卫只有前置,没有后置。
src/router/index.js:
import Vue from "vue"
import VueRouter from "vue-router"
import MyAbout from '../pages/MyAbout.vue'
import MyHome from '../pages/MyHome.vue'
import MyNews from '../pages/MyNews'
import MyMessage from '../pages/MyMessage'
import MyDetail from '../pages/MyDetail'
const router = new VueRouter({
routes:[
{
name:'guanyu',
path:'/about',
component:MyAbout,
meta:{title:'关于'}
},
{
name:'zhuye',
path:'/home',
component:MyHome,
meta:{title:'主页'},
children:[
{
name:'xinwen',
path:'news',
component:MyNews,
meta:{isAuth:true,title:'新闻'},
beforeEnter(to,from,next){
console.log('前置路由守卫',to,from)
if(to.meta.isAuth){
if(localStorage.getItem('school')==='atguigu'){
next()
}else{
alert('学校名不对,无访问权限')
}
}else{
next()
}
}
},
{
path:'message',
component:MyMessage,
children:[
{
name:'xiangqing',
path:'detail/:id/:title',
component:MyDetail,
meta:{isAuth:true,title:'详情'},
props(route){
return {
id:route.params.id,
title:route.params.title,
}
}
},
]
}
]
}
]
})
// router.beforeEach((to,from,next)=>{
// console.log('前置路由守卫',to,from)
// if(to.meta.isAuth){
// if(localStorage.getItem('school')==='atguigu'){
// next()
// }else{
// alert('学校名不对,无访问权限')
// }
// }else{
// next()
// }
// })
router.afterEach((to,from)=>{
console.log('后置路由守卫',to,from)
document.title = to.meta.title || '硅谷系统'
})
export default router
通过路由规则,离开该组件时被调用。
通过路由规则,离开该组件时被调用。
src/pages/MyAbout.vue:
<template>
<h2>我是About组件的内容</h2>
</template>
<script>
export default {
name:'MyAbout',
//通过路由规则,离开该组件时被调用
beforeRouteEnter(to,from,next){
console.log('beforeRouteEnter',to,from)
if(localStorage.getItem('school')==='atguigu'){
next()
}else{
alert('学校名不对,无权限查看!')
}
},
//通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
console.log('beforeRouteLeave',to,from)
next()
}
}
</script>
我的链接https://blog.csdn.net/m0_46629123/article/details/128371149
npm run build
1.准备demo文件夹,并CMD输入npm init
2.CMD输入npm i express
3.新建并编辑server.js
server.js
const express = require('express')
const app = express()
app.get('/person',(req,res)=>{
res.send({
namme:'tom',
age:18,
})
})
app.listen(5005,(err)=>{
if(!err) console.log('服务器启动成功了!')
})
4.CMD输入node server
5.输入http://127.0.0.1:5005/person/,服务器正常运行。
6.新建static,新建demo.html
7.让服务器认识static文件夹
server.js
const express = require('express')
const app = express()
app.use(express.static(__dirname+'/static'))
app.get('/person',(req,res)=>{
res.send({
namme:'tom',
age:18,
})
})
app.listen(5005,(err)=>{
if(!err) console.log('服务器启动成功了!')
})
8.务必重启服务器,访问http://127.0.0.1:5005/demo.html,呈现页面如下
9.服务器准备到这个程度,就可以准备部署了。
1.将dist文件夹中的css、js、favicon.ico、index.html复制到static文件夹中。
2.务必重启服务器 node server
,访问http://127.0.0.1:5005,呈现页面如下。
1.在router/index.html标记mode:'history'
router/index.html
const router = new VueRouter({
// mode:'hash',
mode:'history',
...
})
2.安装兼容库npm i connect-history-api-fallback
3.修改server.js,引入connect-history-api-fallback插件
server.js
const express = require('express')
const history = require('connect-history-api-fallback')
const app = express()
app.use(history())
app.use(express.static(__dirname+'/static'))
app.get('/person',(req,res)=>{
res.send({
namme:'tom',
age:18,
})
})
app.listen(5005,(err)=>{
if(!err) console.log('服务器启动成功了!')
})
4.重新打包npm run build
,拷贝到static文件夹,重启服务器node server
1.移动端常用UI组件库
Vant
Cube UI
Mint UI
京东NutUI
2.PC端常用UI组件库
Element UI
IView UI
官方文档
1.安装 element-ui:npm i element-ui -S
2.src/main.js:
import Vue from 'vue'
import App from './App.vue'
//引入ElementUI组件库
import ElementUI from 'element-ui';
//引入ElementUI全部样式
import 'element-ui/lib/theme-chalk/index.css';
Vue.config.productionTip = false
//使用ElementUI
Vue.use(ElementUI)
new Vue({
el:"#app",
render: h => h(App),
})
3.src/App.vue:
<template>
<div>
<br>
<el-row>
<el-button icon="el-icon-search" circle></el-button>
<el-button type="primary" icon="el-icon-edit" circle></el-button>
<el-button type="success" icon="el-icon-check" circle></el-button>
<el-button type="info" icon="el-icon-message" circle></el-button>
<el-button type="warning" icon="el-icon-star-off" circle></el-button>
<el-button type="danger" icon="el-icon-delete" circle></el-button>
</el-row>
</div>
</template>
<script>
export default {
name:'App',
}
</script>
1.安装 babel-plugin-component:npm install babel-plugin-component -D
2.修改 babel-config-js:
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
["@babel/preset-env", { "modules": false }]
],
plugins: [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
3.src/main.js:
import Vue from 'vue'
import App from './App.vue'
//按需引入
import { Button,Row } from 'element-ui'
Vue.config.productionTip = false
Vue.component(Button.name, Button);
Vue.component(Row.name, Row);
/* 或写为
* Vue.use(Button)
* Vue.use(Row)
*/
new Vue({
el:"#app",
render: h => h(App),
})