Vue 所有代码资料仓库

# Vue2.0 的使用

# src - 分析脚手架

# 脚手架文件结构

├── node_modules: node 模块
├── public: 编译后的文件
│   ├── favicon.ico: 页签图标
│   └── index.html: 主页面
├── src: 源文件
│   ├── assets: 存放静态资源
│   │   └── logo.png: logo
│   │── component: 存放组件
│   │   └── HelloWorld.vue: vue 组件
│   │── router: 路由配置
│   │   └── index.js: 路由配置文件
│   │── App.vue: 汇总所有组件
│   │── main.js: 入口文件
├── .gitignore: git 版本管制忽略的配置
├── babel.config.js: babel 的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json: 包版本控制文件
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
/**
 * 关于不同版本的 Vue:
      1.vue.html 与 vue.runtime.xxx.html 的区别:
          (1).vue.html 是完整版的 Vue,包含:核心功能 + 模板解析器
          (2).vue.runtime.xxx.html 是运行版的 Vue,只包含:核心功能;没有模板解析器。
      2. 因为 vue.runtime.xxx.html 没有模板解析器,所以不能使用 template 配置项,需要使用 render 函数接收到的 createElement 函数去指定具体内容。
 */
new Vue({
  render: h => h(App),
}).$mount('#app')
App.vue
<template>
    <div>
        <img src="./assets/logo.png">
        <School></School>
        <Student></Student>
    </div>
</template>
<script>
// 引入组件
import School from "./components/School";
import Student from "./components/Student";
//export default: 暴露组件可以让别人导入此文件
export default {
  name: "App",
  components: {
    School,
    Student,
  },
};</script>
<style>
</style>
School.vue
<template>
    <div class="demo">
        <h2>学校名称:{ { sName } }</h2>
        <h2>学校地址:{ { access } }</h2>
        <button @click="show">点我提示信息</button>
    </div>
</template>
<script>
// 组件交互相关的代码(数据、方法等等)
export default {
  name: "School",
  data() {
    return {
      sName: "尚硅谷",
      access: "北京",
    };
  },
  methods: {
    show() {
      alert(this.sName);
    },
  },
};</script>
<style>
/* 组件的样式 */
.demo {
  background-color: aqua;
}
</style>
Student.vue
<template>
    <div>
        <h2>学生姓名:{ { stName } }</h2>
        <h2>学生年龄:{ { age } }</h2>
    </div>
</template>
<script>
// 组件交互相关的代码(数据、方法等等)
export default {
  data() {
    return {
      stName: "小明",
      age: 18,
    };
  },
};</script>

# src-ref 属性

ref 属性介绍:

  • 被用来给元素或子组件注册引用信息(id 的替代者)。
  • 应用在 html 标签上获取的是真实 DOM 元素,应用在组件标签上是组件实例对象(vc)。
  • 使用方式:
    • 打标识:
      • <h1 ref="xxx">.....</h1>
      • <School ref="xxx"></School>
    • 获取: this.$refs.xxx
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
}).$mount('#app')
App.vue
<template>
    <div>
        <h1 v-text="msg" ref="title"></h1>
        <button ref="btn" @click="showDemo">点我获取上方Demo</button>
        <School ref="sch"></School>
    </div>
</template>
<script>
import School from "./components/School";
export default {
  name: "App",
  components: {
    School,
  },
  data() {
    return {
      msg: "欢迎学习Vue",
    };
  },
  methods: {
    showDemo() {
      console.log(this.$refs.title);// 真实 Demo 元素
      console.log(this.$refs.btn);// 真实 Demo 元素
      console.log(this.$refs.sch);//School 组件实例对象 (Vc)
    },
  },
};</script>
<style>
</style>
School.vue
<template>
    <div class="school">
        <h2>学校名称:{ { name } }</h2>
    </div>
</template>
<script>
export default {
  name: "School",
  data() {
      return {
          name:'尚硅谷',
      }
  },
};</script>
<style>
.school{
    background-color: deeppink;
}
</style>

# src-props 配置

props 配置项介绍:

  • 功能:让组件接收外部传过来的数据
  • 传递数据: <Demo name="xxx"/>
  • 接收数据:
    • 第一种方式(只接收): props:['name']

    • 第二种方式(限制类型): props:{name:String}

    • 第三种方式(限制类型、限制必要性、指定默认值):

      props:{
         name:{
             type:String, // 类型
             required:true, // 必要性
             default:'老王' // 默认值
         }
      }

备注:props 是只读的,Vue 底层会监测你对 props 的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制 props 的内容到 data 中一份,然后去修改 data 中的数据。

main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
}).$mount('#app')
App.vue
<template>
    <div>
        <Student name="李四" sex="" :age="20"></Student>
    </div>
</template>
<script>
import Student from "./components/Student";
export default {
  name: "App",
  components: {
    Student,
  },
};</script>
<style>
</style>
Student.vue
<template>
    <div class="school">
        <h1>{ { msg } }</h1>
        <h2>学生姓名:{ { name } }</h2>
        <h2>学生性别:{ { sex } }</h2>
        <h2>学生年龄:{ { myAge + 1 } }</h2>
        <button @click="updateAge">点我更新年龄</button>
    </div>
</template>
<script>
export default {
  name: "Student",
  data() {
    return {
      msg: "我是尚硅谷的学生",
      myAge: this.age,
    };
  },
  methods:{
    updateAge(){
      this.myAge++;
    }
  },
  //props:['name','sex','age'], // 方式一  props 配置接收 App.vue 传来的外部参数
  // 方式二 接收的同时对数据:进行类型限制
  /*  props:{
    name:String,
    sex:String,
    age:Number,
  } */
  // 方式三 接收的同时对数据:进行类型限制 + 默认值的指定 + 必要性的限制
  props: {
    name: {
      type: String, //name 的类型
      required: true, //name 是必须的
    },
    age: {
      type: Number,
      default: 18, // 不传参数默认值 18
    },
    sex: {
      type: String,
      require: true,
    },
  },
};</script>
<style>
.school {
  background-color: deeppink;
}</style>

# src-mixin (混入 \ 混合)

mixin (混入) 介绍:

  • 功能:可以把多个组件共用的配置提取成一个混入对象
  • 使用方式:
    • 第一步定义混合:
      {
        data(){....},
        methods:{....}
        ....
      }
    • 第二步使用混入:
      • 全局混入: Vue.mixin(xxx)
      • 局部混入: mixins:['xxx']
main.js
import Vue from 'vue'
import App from './App.vue'
// 导入混合
import {mixin} from './mixin'
Vue.config.productionTip = false
// 配置全局混合
Vue.mixin(mixin)
new Vue({
  render: h => h(App),
}).$mount('#app')
App.vue
<template>
    <div>
        <Student></Student>
        <School></School>
    </div>
</template>
<script>
import Student from "./components/Student";
import School from "./components/School";
export default {
  name: "App",
  components: {
    Student,School,
  },
};</script>
<style>
</style>
mixin.js
export const mixin = { 
    methods: {
        demo() {
            alert(this.name);
        }
    },
}
School.vue
<template>
    <div class="school">
        <h2 @click="demo">学校名称:{ { name } }</h2>
        <h2>学校地址:{ { access } }</h2>
    </div>
</template>
<script>
// 引入一个混合 (局部配置混合)
//import { mixin } from "../mixin";
export default {
  name: "School",
  data() {
    return {
      name: "尚硅谷",
      access: "北京",
    };
  },
  // 使用混合
  //mixins: [mixin],
};</script>
<style>
.school {
  background-color: deeppink;
}</style>
<template>
    <div class="Student">
        <h2 @click="demo">学生姓名:{ { name } }</h2>
        <h2>学生性别:{ { sex } }</h2>
    </div>
</template>
<script>
// 引入一个混合 (局部配置混合)
//import {mixin} from '../mixin'
export default {
  name: "Student",
  data() {
    return {
      name: "雨",
      sex: "未知",
    };
  },
  // 使用混合
  //mixins:[mixin]
};</script>
<style>
.Student {
  background-color: deeppink;
}</style>

# src - 插件

插件介绍:

  • 功能:用于增强 Vue
  • 本质:包含 install 方法的一个对象,install 的第一个参数是 Vue,第二个以后的参数是插件使用者传递的数据。
  • 定义插件:
    js配置
    对象.install = function (Vue, options) {
          // 1. 添加全局过滤器
          Vue.filter(....)
      
          // 2. 添加全局指令
          Vue.directive(....)
      
          // 3. 配置全局混入 (合)
          Vue.mixin(....)
      
          // 4. 添加实例方法
          Vue.prototype.$myMethod = function () {...}
          Vue.prototype.$myProperty = xxxx
      }
  • 使用插件: Vue.use()
main.js
import Vue from 'vue'
import App from './App.vue'
// 引入插件
import plugin from './plugins'
Vue.config.productionTip = false
// 应用插件
Vue.use(plugin)
new Vue({
  render: h => h(App),
}).$mount('#app')
plugins.js
export default {
    install(Vue){//install:定义插件函数
        console.log('使用插件成功!',Vue);
        // 可以配置很多全局方法
    }
}
App.vue
<template>
    <div>
        <Student></Student>
        <School></School>
    </div>
</template>
<script>
import Student from "./components/Student";
import School from "./components/School";
export default {
  name: "App",
  components: {
    Student,School,
  },
};</script>
<style>
</style>
School.vue
<template>
    <div class="school">
        <h2>学校名称:{ { name } }</h2>
        <h2>学校地址:{ { access } }</h2>
    </div>
</template>
<script>
export default {
  name: "School",
  data() {
    return {
      name: "尚硅谷",
      access: "北京",
    };
  },
};</script>
<style>
.school {
  background-color: deeppink;
}</style>
Student.vue
<template>
    <div class="Student">
        <h2>学生姓名:{ { name } }</h2>
        <h2>学生性别:{ { sex } }</h2>
    </div>
</template>
<script>
export default {
  name: "Student",
  data() {
    return {
      name: "雨",
      sex: "女",
    };
  },
};</script>
<style>
.Student {
  background-color: deeppink;
}</style>

# src_scoped 样式

scoped 样式介绍

  1. 作用:让样式在局部生效,防止冲突。
  2. 写法: <style scoped>
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
}).$mount('#app')
App.vue
<template>
    <div>
        <Student></Student>
        <School></School>
    </div>
</template>
<script>
import Student from "./components/Student";
import School from "./components/School";
export default {
  name: "App",
  components: {
    Student,
    School,
  },
};</script>
<style lang="less">
.demo2 {
  background-color: rgb(255, 0, 0);
  .demo3{
    font-size: 50px;
  }
}</style>
School.vue
<template>
    <div class="demo2">
        <h2 class="demo3">学校名称:{ { name } }</h2>
        <h2>学校地址:{ { access } }</h2>
    </div>
</template>
<script>
export default {
  name: "School",
  data() {
    return {
      name: "尚硅谷",
      access: "北京",
    };
  },
};</script>
/* scoped:局部限制css名字防止项目整合时出现命名冲突 */
<style scoped>
.demo {
  background-color: rgb(7, 186, 199);
}</style>
Student.vue
<template>
    <div class="demo">
        <h2>学生姓名:{ { name } }</h2>
        <h2>学生性别:{ { sex } }</h2>
    </div>
</template>
<script>
export default {
  name: "Student",
  data() {
    return {
      name: "雨",
      sex: "女",
    };
  },
};</script>
<style scoped>
.demo {
  background-color: deeppink;
}</style>

# src-TodoList 案例

main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
}).$mount('#app')
App.vue
<template>
    <div id="root">
        <div class="todo-container">
            <div class="todo-wrap">
                <MyHeader :receive="addTodo"/>
                <List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
                <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>
            </div>
        </div>
    </div>
</template>
<script>
import MyHeader from "./components/MyHeader";
import List from "./components/List";
import MyFooter from "./components/MyFooter";
export default {
  name: "App",
  components: {
    MyHeader,
    List,
    MyFooter,
  },
  data() {
    return {
      // 定义数据
      todos: [
        { id: "001", title: "吃饭", done: true },
        { id: "002", title: "喝酒", done: false },
        { id: "003", title: "睡觉", done: true },
      ],
    };
  },
  methods: {
    // 添加一个 todo
    addTodo(val) {
      this.todos.unshift(val); //unshift:在最前面添加一条数据
    },
    // 勾选一个 todo or 取消勾选一个 todo
    checkTodo(id) {
      this.todos.forEach((todo) => {
        if (todo.id === id) {
          todo.done = !todo.done;
          console.log("修改成功", id);
        }
      });
    },
    // 根据 id 删除 todo 的一个对象
    deleteTodo(id) {
      if (confirm("确定要删除吗")) {
        // 使用过滤器过滤掉需要删除的 id 然后重新赋值给 todos
        this.todos = this.todos.filter((todo) => {
          return todo.id != id; //confirm 确定和取消的弹窗
        });
      }
    },
    // 全选和取消全选
    checkAllTodo(done) {
      this.todos.forEach((todo) => {
        todo.done = done;
      });
    },
    // 清除所有已经完成的 todo
    clearAllTodo() {
      this.todos = this.todos.filter((todo) => {
        return !todo.done
      });
    },
  },
};</script>
<style lang="css">
/*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-danger:hover {
  color: #fff;
  background-color: #bd362f;
}
.btn:focus {
  outline: none;
}
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}</style>
Item.vue
<template>
    <li>
        <label>
            <input type="checkbox" :checked="todo.done" @click="itemCheck(todo.id)"/>
            <!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了 props -->
            <!-- <input type="checkbox" v-model="todo.done"/> -->
            <span>{ {todo.title} }</span>
        </label>
        <button class="btn btn-danger" @click="deleteButton(todo.id)">删除</button>
    </li>
</template>
<script>
export default {
    name:'Item',
	// 接收数据
	props:['todo','checkTodo','deleteTodo'],
	methods:{
		itemCheck(id){
			// 通知 App 组件将对应的 done 属性取反
			this.checkTodo(id)
		},
		deleteButton(id){
			// 通知 App 组件删除对应的 todo 对象
			this.deleteTodo(id)
		}
	}
}</script>
<style scoped>
/*item*/
	li {
		list-style: none;
		height: 36px;
		line-height: 36px;
		padding: 0 5px;
		border-bottom: 1px solid #ddd;
	}
	li label {
		float: left;
		cursor: pointer;
	}
	li label li input {
		vertical-align: middle;
		margin-right: 6px;
		position: relative;
		top: -1px;
	}
	li button {
		float: right;
		display: none;
		margin-top: 3px;
	}
	li:before {
		content: initial;
	}
	li:last-child {
		border-bottom: none;
	}
	li:hover{
		background-color: rgb(5, 238, 255);
	}
	
	li:hover button{
		display: block;
	}</style>
List.vue
<template>
    <ul class="todo-main">
        <Item
                v-for="todoObj in todos"
                :key="todoObj.id"
                :todo="todoObj"
                :checkTodo="checkTodo"
                :deleteTodo="deleteTodo"
        />
    </ul>
</template>
<script>
import Item from "./Item";
export default {
  name: "List",
  components: {
    Item,
  },
  props: ["todos", "checkTodo", "deleteTodo"],
};</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>
MyFooter.vue
<template>
    <div class="todo-footer" v-show="total">
        <label>
            <!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
            <!--       <input type="checkbox" @change="checkAll" :checked="isAll" /> -->
            <input type="checkbox" v-model="isAll"/>
        </label>
        <span>
      <span>已完成{ { doneTotal } }</span> / 全部{ { total } }
    </span>
        <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
    </div>
</template>
<script>
export default {
  name: "MyFooter",
  props: ["todos", "checkAllTodo","clearAllTodo"],
  computed: {
    total() {
      return this.todos.length;
    },
    doneTotal() {
      // 判断 方法一
      /*  let i = 0;
      this.todos.forEach(e => {
        if (e.done) i++;
      });
      return i; */
      return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0);
    },
    isAll: {
      get() {
        return this.doneTotal === this.total && this.total > 0;
      },
      set(checked) {
        this.checkAllTodo(checked);
      },
    },
  },
  methods: {
    checkAll(e) {
      this.checkAllTodo(e.target.checked);
    },
	clearAll(){
		this.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>
MyHeader.vue
<template>
    <div class="todo-header">
        <input
                type="text"
                placeholder="请输入你的任务名称,按回车键确认"
                v-model="title"
                @keyup.enter="add"
        />
    </div>
</template>
<script>
import { nanoid } from "nanoid";
export default {
  name: "MyHeader",
  props: ["receive"],
  data() {
    return {
      title: "",
    };
  },
  methods: {
    add() {
      // 判断输入框是否为空
      if (!this.title.trim) return alert("请输入");
	  // 将用户输入的包装成一个对象
      const todoObj = { id: nanoid(), title: this.title, done: false };
	  // 通知 App 组件去添加一个 todo 对象
      this.receive(todoObj);
	  // 清空输入框
      this.title = "";
    },
  },
};</script>
<style scoped>
/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}
.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
    0 0 8px rgba(82, 168, 236, 0.6);
}</style>

# 浏览器本地储存

webStorage 介绍

  1. 存储内容大小一般支持 5MB 左右(不同浏览器可能还不一样)
  2. 浏览器端通过 Window.sessionStorageWindow.localStorage 属性来实现本地存储机制。
  3. 相关 API:
    1. xxxxxStorage.setItem('key', 'value');
      该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
    2. xxxxxStorage.getItem('person');
      该方法接受一个键名作为参数,返回键名对应的值。
    3. xxxxxStorage.removeItem('key');
      该方法接受一个键名作为参数,并把该键名从存储中删除。
    4. xxxxxStorage.clear()
      该方法会清空存储中的所有数据。
  4. 备注:
    1. SessionStorage 存储的内容会随着浏览器窗口关闭而消失。
    2. LocalStorage 存储的内容,需要手动清除才会消失。
    3. xxxxxStorage.getItem(xxx) 如果 xxx 对应的 value 获取不到,那么 getItem 的返回值是 null
    4. JSON.parse(null) 的结果依然是 null
localStorage.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>localStorage</title>
</head>
<body>
<h1>localStorage</h1>
<button onclick="savaData()">点我保存信息</button>
<button onclick="readData()">点我获取信息</button>
<button onclick="deleteData()">点我删除数据</button>
<button onclick="deleteAllData()">点我清空数据</button>
<script>
        // 本地保存搜索信息
        function savaData() {
            localStorage.setItem('msg', '张三');
            localStorage.setItem('msg2', '李四');
        }
        // 读取缓存信息
        function readData() {
            console.log(localStorage.getItem('msg'));
        }
        // 删除一条信息
        function deleteData() {
            localStorage.removeItem('msg');
        }
        // 清空
        function deleteAllData() {
            localStorage.clear();
        }</script>
</body>
</html>
sessionStorage.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>sessionStorage</title>
</head>
<body>
<h1>sessionStorage</h1>
<button onclick="savaData()">点我保存信息</button>
<button onclick="readData()">点我获取信息</button>
<button onclick="deleteData()">点我删除数据</button>
<button onclick="deleteAllData()">点我清空数据</button>
<script>
        // 本地保存搜索信息
        function savaData() {
            sessionStorage.setItem('msg', '张三');
            sessionStorage.setItem('msg2', '李四');
        }
        // 读取缓存信息
        function readData() {
            console.log(sessionStorage.getItem('msg'));
        }
        // 删除一条信息
        function deleteData() {
            sessionStorage.removeItem('msg');
        }
        // 清空
        function deleteAllData() {
            sessionStorage.clear();
        }</script>
</body>
</html>

# src - 完整版 TodoList 案例

总结 TodoList 案例

  1. 组件化编码流程:
    • 拆分静态组件:组件要按照功能点拆分,命名不要与 html 元素冲突。
    • 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
      • 一个组件在用:放在组件自身即可。
      • 一些组件在用:放在他们共同的父组件上( <span style="color:red"> 状态提升 </span> )。
      • 实现交互:从绑定事件开始。
  2. props 适用于:
    • 父组件 ==> 子组件 通信
    • 子组件 ==> 父组件 通信(要求父先给子一个函数)
  3. 使用 v-model 时要切记:v-model 绑定的值不能是 props 传过来的值,因为 props 是不可以修改的!
  4. props 传过来的若是对象类型的值,修改对象中的属性时 Vue 不会报错,但不推荐这样做。
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
}).$mount('#app')
App.vue
<template>
    <div id="root">
        <div class="todo-container">
            <div class="todo-wrap">
                <MyHeader :receive="addTodo"/>
                <List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
                <MyFooter
                        :todos="todos"
                        :checkAllTodo="checkAllTodo"
                        :clearAllTodo="clearAllTodo"
                />
            </div>
        </div>
    </div>
</template>
<script>
import MyHeader from "./components/MyHeader";
import List from "./components/List";
import MyFooter from "./components/MyFooter";
export default {
  name: "App",
  components: {
    MyHeader,
    List,
    MyFooter,
  },
  data() {
    return {
      // 定义数据
      todos: JSON.parse(localStorage.getItem("todos")) || [],
    };
  },
  methods: {
    // 添加一个 todo
    addTodo(val) {
      this.todos.unshift(val); //unshift:在最前面添加一条数据
    },
    // 勾选一个 todo or 取消勾选一个 todo
    checkTodo(id) {
      this.todos.forEach((todo) => {
        if (todo.id === id) {
          todo.done = !todo.done;
          console.log("修改成功", id);
        }
      });
    },
    // 根据 id 删除 todo 的一个对象
    deleteTodo(id) {
      if (confirm("确定要删除吗")) {
        // 使用过滤器过滤掉需要删除的 id 然后重新赋值给 todos
        this.todos = this.todos.filter((todo) => {
          return todo.id != id; //confirm 确定和取消的弹窗
        });
      }
    },
    // 全选和取消全选
    checkAllTodo(done) {
      this.todos.forEach((todo) => {
        todo.done = done;
      });
    },
    // 清除所有已经完成的 todo
    clearAllTodo() {
      this.todos = this.todos.filter((todo) => {
        return !todo.done;
      });
    },
  },
  watch: {
    todos:{
      deep: true, // 深度监视
      handler(value) {
        localStorage.setItem("todos", JSON.stringify(value));
      },
    },
  },
};
</script>
<style lang="css">
/*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-danger:hover {
  color: #fff;
  background-color: #bd362f;
}
.btn:focus {
  outline: none;
}
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>
Item.vue
<template>
    <li>
        <label>
            <input type="checkbox" :checked="todo.done" @click="itemCheck(todo.id)"/>
            <!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了 props -->
            <!-- <input type="checkbox" v-model="todo.done"/> -->
            <span>{ {todo.title} }</span>
        </label>
        <button class="btn btn-danger" @click="deleteButton(todo.id)">删除</button>
    </li>
</template>
<script>
export default {
    name:'Item',
	// 接收数据
	props:['todo','checkTodo','deleteTodo'],
	methods:{
		itemCheck(id){
			// 通知 App 组件将对应的 done 属性取反
			this.checkTodo(id)
		},
		deleteButton(id){
			// 通知 App 组件删除对应的 todo 对象
			this.deleteTodo(id)
		}
	}
}
</script>
<style scoped>
/*item*/
	li {
		list-style: none;
		height: 36px;
		line-height: 36px;
		padding: 0 5px;
		border-bottom: 1px solid #ddd;
	}
	li label {
		float: left;
		cursor: pointer;
	}
	li label li input {
		vertical-align: middle;
		margin-right: 6px;
		position: relative;
		top: -1px;
	}
	li button {
		float: right;
		display: none;
		margin-top: 3px;
	}
	li:before {
		content: initial;
	}
	li:last-child {
		border-bottom: none;
	}
	li:hover{
		background-color: rgb(5, 238, 255);
	}
	
	li:hover button{
		display: block;
	}
</style>
List.vue
<template>
    <ul class="todo-main">
        <Item
                v-for="todoObj in todos"
                :key="todoObj.id"
                :todo="todoObj"
                :checkTodo="checkTodo"
                :deleteTodo="deleteTodo"
        />
    </ul>
</template>
<script>
import Item from "./Item";
export default {
  name: "List",
  components: {
    Item,
  },
  props: ["todos", "checkTodo", "deleteTodo"],
};
</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>
MyFooter.vue
<template>
    <div class="todo-footer" v-show="total">
        <label>
            <!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
            <!--       <input type="checkbox" @change="checkAll" :checked="isAll" /> -->
            <input type="checkbox" v-model="isAll"/>
        </label>
        <span>
      <span>已完成{ { doneTotal } }</span> / 全部{ { total } }
    </span>
        <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
    </div>
</template>
<script>
export default {
  name: "MyFooter",
  props: ["todos", "checkAllTodo","clearAllTodo"],
  computed: {
    total() {
      return this.todos.length;
    },
    doneTotal() {
      // 判断 方法一
      /*  let i = 0;
      this.todos.forEach(e => {
        if (e.done) i++;
      });
      return i; */
      return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0);
    },
    isAll: {
      get() {
        return this.doneTotal === this.total && this.total > 0;
      },
      set(checked) {
        this.checkAllTodo(checked);
      },
    },
  },
  methods: {
    checkAll(e) {
      this.checkAllTodo(e.target.checked);
    },
	clearAll(){
		this.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>
MyHeader.vue
<template>
    <div class="todo-header">
        <input
                type="text"
                placeholder="请输入你的任务名称,按回车键确认"
                v-model="title"
                @keyup.enter="add"
        />
    </div>
</template>
<script>
import { nanoid } from "nanoid";
export default {
  name: "MyHeader",
  props: ["receive"],
  data() {
    return {
      title: "",
    };
  },
  methods: {
    add() {
      // 判断输入框是否为空
      if (!this.title.trim()) return alert("请输入");
      // 将用户输入的包装成一个对象
      const todoObj = { id: nanoid(), title: this.title, done: false };
      // 通知 App 组件去添加一个 todo 对象
      this.receive(todoObj);
      // 清空输入框
      this.title = "";
    },
  },
};
</script>
<style scoped>
/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}
.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
    0 0 8px rgba(82, 168, 236, 0.6);
}
</style>

# src - 自定义事件绑定

组件的自定义事件

  1. 一种组件间通信的方式,适用于: <strong style="color:red"> 子组件 ===> 父组件 </strong>
  2. 使用场景:A 是父组件,B 是子组件,B 想给 A 传数据。
  3. 那么就要在 A 中给 B 绑定自定义事件( <span style="color:red"> 事件的回调在 A 中 </span> )。
  4. 绑定自定义事件:
    1. 第一种方式,在父组件中:
      • <Demo @atguigu="test"/>
      • <Demo v-on:atguigu="test"/>
    2. 第二种方式,在父组件中:
      <Demo ref="demo"/>
      ......
      mounted(){
         this.$refs.xxx.$on('atguigu',this.test)
      }
    3. 若想让自定义事件只能触发一次,可以使用 once 修饰符,或 $once 方法。
  5. 触发自定义事件: this.$emit('atguigu',数据)
  6. 解绑自定义事件 this.$off('atguigu')
  7. 组件上也可以绑定原生 DOM 事件,需要使用 native 修饰符。
  8. 注意:通过 this.$refs.xxx.$on('atguigu',回调) 绑定自定义事件时,回调 <span style="color:red">
    要么配置在 methods 中 </span><span style="color:red"> 要么用箭头函数 </span> ,否则 this 指向会出问题!
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
}).$mount('#app')
App.vu
<template>
    <div>
        <!-- 通过父组件给子组件传递函数类型的 props 实现:子给父传递数据 -->
        <School :getStudentName="getStudentName"></School>
        <!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据 (第一种写法 @或 v-on)-->
        <Student v-on:rain="getRain" @demo="m1"></Student>
        <!-- 第二种写法 -->
        <!-- <Student ref="student"></Student> -->
    </div>
</template>
<script>
import Student from "./components/Student";
import School from "./components/School";
export default {
  name: "App",
  components: {
    Student,
    School,
  },
  methods: {
    getStudentName(name) {
      alert("提交成功:" + name);
    },
    getRain(name) {
      console.log("getStudent被调用了", name);
    },
    m1(){
      console.log("demo被调用了");
    }
  },
  mounted(){// App 挂着完毕后调用 mounted 函数
    //this.$refs.student.$on ('rain',this.getRain);// 绑定自定义事件
    // 只能点击一次
    //this.$refs.student.$once ('rain',this.getRain);// 绑定自定义事件 (一次性)
    // 延迟一秒调用
    /* setTimeout(()=>{
      this.$refs.student.$on('rain',this.getRain);
    },1000); */
  }
};</script>
<style lang="less">
.demo2 {
  background-color: rgb(255, 0, 0);
  .demo3 {
    font-size: 50px;
  }
}</style>
School.vue
<template>
    <div class="demo2">
        <h2 class="demo3">学校名称:{ { name } }</h2>
        <h2>学校地址:{ { access } }</h2>
        <button @click="sendStudentNameButton">将学校名字提交给App</button>
    </div>
</template>
<script>
export default {
  name: "School",
  props:['getStudentName'],
  data() {
    return {
      name: "尚硅谷",
      access: "北京",
    };
  },
  methods: {
    sendStudentNameButton() {
      this.getStudentName(this.name);
    },
  },
};
</script>
/* scoped:局部限制css名字防止项目整合时出现命名冲突 */
<style scoped>
.demo {
  background-color: rgb(7, 186, 199);
}
</style>
Student.vue
<template>
    <div class="demo">
        <h2>学生姓名:{ { name } }</h2>
        <h2>学生性别:{ { sex } }</h2>
        <button @click="sendStudentNameButton2">自定义事件绑定</button>
        <button @click="deleteStudentNameButton">解绑自定义事件</button>
    </div>
</template>
<script>
export default {
  name: "Student",
  data() {
    return {
      name: "雨",
      sex: "女",
    };
  },
  methods: {
    sendStudentNameButton2() {
      // 触发 Student 组件实例身上的 rain 事件
      this.$emit("rain", this.name);
      this.$emit("demo")
    },
    deleteStudentNameButton(){
      //this.$off ('rain');// 解绑一个事件
       this.$off(['rain','demo']);// 解绑多个事件
        this.$off();// 解绑所有自定义事件
    }
  },
};
</script>
<style scoped>
.demo {
  background-color: deeppink;
}</style>

# src_组件自定义事件

main.js
// 引入 Vue
import Vue from 'vue'
// 引入 App
import App from './App.vue'
// 关闭 Vue 的生产提示
Vue.config.productionTip = false
// 创建 vm
new Vue({
	el:'#app',
	render: h => h(App),
	/* mounted() {
		setTimeout(()=>{
			this.$destroy()
		},3000)
	}, */
})
App.vue
<template>
	<div class="app">
		<h1>,学生姓名是:</h1>
		<!-- 通过父组件给子组件传递函数类型的 props 实现:子给父传递数据 -->
		<School :getSchoolName="getSchoolName"/>
		<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用 @或 v-on) -->
		<!-- <Student @atguigu="getStudentName" @demo="m1"/> -->
		<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用 ref) -->
		<Student ref="student" @click.native="show"/>
	</div>
</template>
<script>
	import Student from './components/Student'
	import School from './components/School'
	export default {
		name:'App',
		components:{School,Student},
		data() {
			return {
				msg:'你好啊!',
				studentName:''
			}
		},
		methods: {
			getSchoolName(name){
				console.log('App收到了学校名:',name)
			},
			getStudentName(name,...params){
				console.log('App收到了学生名:',name,params)
				this.studentName = name
			},
			m1(){
				console.log('demo事件被触发了!')
			},
			show(){
				alert(123)
			}
		},
		mounted() {
			this.$refs.student.$on('atguigu',this.getStudentName) // 绑定自定义事件
			//this.$refs.student.$once ('atguigu',this.getStudentName) // 绑定自定义事件(一次性)
		},
	}
</script>
<style scoped>
	.app{
		background-color: gray;
		padding: 5px;
	}
</style>
School.vue
<template>
	<div class="school">
		<h2>学校名称:</h2>
		<h2>学校地址:</h2>
		<button @click="sendSchoolName">把学校名给App</button>
	</div>
</template>
<script>
	export default {
		name:'School',
		props:['getSchoolName'],
		data() {
			return {
				name:'尚硅谷',
				address:'北京',
			}
		},
		methods: {
			sendSchoolName(){
				this.getSchoolName(this.name)
			}
		},
	}
</script>
<style scoped>
	.school{
		background-color: skyblue;
		padding: 5px;
	}
</style>
Student.vue
<template>
	<div class="student">
		<h2>学生姓名:</h2>
		<h2>学生性别:</h2>
		<h2>当前求和为:</h2>
		<button @click="add">点我number++</button>
		<button @click="sendStudentlName">把学生名给App</button>
		<button @click="unbind">解绑atguigu事件</button>
		<button @click="death">销毁当前Student组件的实例(vc)</button>
	</div>
</template>
<script>
	export default {
		name:'Student',
		data() {
			return {
				name:'张三',
				sex:'男',
				number:0
			}
		},
		methods: {
			add(){
				console.log('add回调被调用了')
				this.number++
			},
			sendStudentlName(){
				// 触发 Student 组件实例身上的 atguigu 事件
				this.$emit('atguigu',this.name,666,888,900)
				// this.$emit('demo')
				// this.$emit('click')
			},
			unbind(){
				this.$off('atguigu') // 解绑一个自定义事件
				//this.$off (['atguigu','demo']) // 解绑多个自定义事件
				//this.$off () // 解绑所有的自定义事件
			},
			death(){
				this.$destroy() // 销毁了当前 Student 组件的实例,销毁后所有 Student 实例的自定义事件全都不奏效。
			}
		},
	}
</script>
<style lang="less" scoped>
	.student{
		background-color: pink;
		padding: 5px;
		margin-top: 30px;
	}
</style>

# src-TodoList 自定义事件

main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
}).$mount('#app')
App.vue
<template>
    <div id="root">
        <div class="todo-container">
            <div class="todo-wrap">
                <MyHeader @receive="addTodo"/>
                <List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
                <MyFooter
                        :todos="todos"
                        @checkAllTodo="checkAllTodo"
                        @clearAllTodo="clearAllTodo"
                />
            </div>
        </div>
    </div>
</template>
<script>
import MyHeader from "./components/MyHeader";
import List from "./components/List";
import MyFooter from "./components/MyFooter";
export default {
  name: "App",
  components: {
    MyHeader,
    List,
    MyFooter,
  },
  data() {
    return {
      // 定义数据
      todos: JSON.parse(localStorage.getItem("todos")) || [],
    };
  },
  methods: {
    // 添加一个 todo
    addTodo(val) {
      this.todos.unshift(val); //unshift:在最前面添加一条数据
    },
    // 勾选一个 todo or 取消勾选一个 todo
    checkTodo(id) {
      this.todos.forEach((todo) => {
        if (todo.id === id) {
          todo.done = !todo.done;
          console.log("修改成功", id);
        }
      });
    },
    // 根据 id 删除 todo 的一个对象
    deleteTodo(id) {
      if (confirm("确定要删除吗")) {
        // 使用过滤器过滤掉需要删除的 id 然后重新赋值给 todos
        this.todos = this.todos.filter((todo) => {
          return todo.id != id; //confirm 确定和取消的弹窗
        });
      }
    },
    // 全选和取消全选
    checkAllTodo(done) {
      this.todos.forEach((todo) => {
        todo.done = done;
      });
    },
    // 清除所有已经完成的 todo
    clearAllTodo() {
      this.todos = this.todos.filter((todo) => {
        return !todo.done;
      });
    },
  },
  watch: {
    todos:{
      deep: true, // 深度监视
      handler(value) {
        localStorage.setItem("todos", JSON.stringify(value));
      },
    },
  },
};</script>
<style lang="css">
/*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-danger:hover {
  color: #fff;
  background-color: #bd362f;
}
.btn:focus {
  outline: none;
}
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}</style>
Item.vue
<template>
    <li>
        <label>
            <input type="checkbox" :checked="todo.done" @click="itemCheck(todo.id)"/>
            <!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了 props -->
            <!-- <input type="checkbox" v-model="todo.done"/> -->
            <span>{ {todo.title} }</span>
        </label>
        <button class="btn btn-danger" @click="deleteButton(todo.id)">删除</button>
    </li>
</template>
<script>
export default {
    name:'Item',
	// 接收数据
	props:['todo','checkTodo','deleteTodo'],
	methods:{
		itemCheck(id){
			// 通知 App 组件将对应的 done 属性取反
			this.checkTodo(id)
		},
		deleteButton(id){
			// 通知 App 组件删除对应的 todo 对象
			this.deleteTodo(id)
		}
	}
}
</script>
<style scoped>
/*item*/
	li {
		list-style: none;
		height: 36px;
		line-height: 36px;
		padding: 0 5px;
		border-bottom: 1px solid #ddd;
	}
	li label {
		float: left;
		cursor: pointer;
	}
	li label li input {
		vertical-align: middle;
		margin-right: 6px;
		position: relative;
		top: -1px;
	}
	li button {
		float: right;
		display: none;
		margin-top: 3px;
	}
	li:before {
		content: initial;
	}
	li:last-child {
		border-bottom: none;
	}
	li:hover{
		background-color: rgb(5, 238, 255);
	}
	
	li:hover button{
		display: block;
	}
</style>
List.vue
<template>
    <ul class="todo-main">
        <Item
                v-for="todoObj in todos"
                :key="todoObj.id"
                :todo="todoObj"
                :checkTodo="checkTodo"
                :deleteTodo="deleteTodo"
        />
    </ul>
</template>
<script>
import Item from "./Item";
export default {
  name: "List",
  components: {
    Item,
  },
  props: ["todos", "checkTodo", "deleteTodo"],
};
</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>
MyFooter.vue
<template>
    <div class="todo-footer" v-show="total">
        <label>
            <!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
            <!--       <input type="checkbox" @change="checkAll" :checked="isAll" /> -->
            <input type="checkbox" v-model="isAll"/>
        </label>
        <span>
      <span>已完成{ { doneTotal } }</span> / 全部{ { total } }
    </span>
        <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
    </div>
</template>
<script>
export default {
  name: "MyFooter",
  props: ["todos", "checkAllTodo","clearAllTodo"],
  computed: {
    total() {
      return this.todos.length;
    },
    doneTotal() {
      // 判断 方法一
      /*  let i = 0;
      this.todos.forEach(e => {
        if (e.done) i++;
      });
      return i; */
      return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0);
    },
    isAll: {
      get() {
        return this.doneTotal === this.total && this.total > 0;
      },
      set(checked) {
        //this.checkAllTodo(checked);
        this.$emit("checkAllTodo", checked);
      },
    },
  },
  methods: {
    checkAll(e) {
      // this.$emit("checkAllTodo", e.target.checked);
      //this.checkAllTodo(e.target.checked);
    },
	clearAll(){
		//this.clearAllTodo()
    this.$emit('clearAllTodo')
	}
  },
};
</script>
<style scoped>
/*footer*/
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}
.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}
.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}
.todo-footer button {
  float: right;
  margin-top: 5px;
}
</style>
MyHeader.vue
<template>
    <div class="todo-header">
        <input
                type="text"
                placeholder="请输入你的任务名称,按回车键确认"
                v-model="title"
                @keyup.enter="add"
        />
    </div>
</template>
<script>
import { nanoid } from "nanoid";
export default {
  name: "MyHeader",
  data() {
    return {
      title: "",
    };
  },
  methods: {
    add() {
     
      // 判断输入框是否为空
      if (!this.title.trim()) return alert("请输入");
      // 将用户输入的包装成一个对象
      const todoObj = { id: nanoid(), title: this.title, done: false };
      // 通知 App 组件去添加一个 todo 对象
       this.$emit('receive',todoObj)
      // 清空输入框
      this.title = "";
    },
  },
};
</script>
<style scoped>
/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}
.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
    0 0 8px rgba(82, 168, 236, 0.6);
}
</style>

# src - 安装全局事件总线

全局事件总线(GlobalEventBus)

  1. 一种组件间通信的方式,适用于 <span style="color:red"> 任意组件间通信 </span>
  2. 安装全局事件总线:
    new Vue({
    	......
    	beforeCreate() {
    		Vue.prototype.$bus = this // 安装全局事件总线,$bus 就是当前应用的 vm
    	},
        ......
    })
  3. 使用事件总线:
    1. 接收数据:
      • A 组件想接收数据,则在 A 组件中给 $bus 绑定自定义事件
      • 事件的 <span style="color:red"> 回调留在 A 组件自身 </span>
      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.$bus.$on('xxxx',this.demo)
      }
    2. 提供数据: this.$bus.$emit('xxxx',数据)
  4. 最好在 beforeDestroy 钩子中,用 $off 去解绑 <span style="color:red"> 当前组件所用到的 </span> 事件。
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div>
        <School></School>
        <Student></Student>
    </div>
</template>
<script>
import Student from "./components/Student";
import School from "./components/School";
export default {
  name: "App",
  components: {
    Student,
    School,
  },
};
</script>
<style lang="less">
.demo2 {
  background-color: rgb(255, 0, 0);
  .demo3 {
    font-size: 50px;
  }
}</style>
School.vue
<template>
    <div class="demo2">
        <h2 class="demo3">学校名称:{ { name } }</h2>
        <h2>学校地址:{ { access } }</h2>
    </div>
</template>
<script>
export default {
  name: "School",
  props: ["getStudentName"],
  data() {
    return {
      name: "尚硅谷",
      access: "北京",
    };
  },
  mounted() {
    this.$bus.$on("hello", (data) => {
      console.log("我是School,我收到了数据", data);
    });
  },
  beforeDestroy() { //beforeDestroy:当前组件销毁之前处理后事
    this.$bus.$off("hello");
  },
};</script>
/* scoped:局部限制css名字防止项目整合时出现命名冲突 */
<style scoped>
.demo {
  background-color: rgb(7, 186, 199);
}
</style>
Student.vue
<template>
    <div class="demo">
        <h2>学生姓名:{ { name } }</h2>
        <h2>学生性别:{ { sex } }</h2>
        <button @click="sendStudentName">将学生名字传给School组件</button>
    </div>
</template>
<script>
export default {
  name: "Student",
  data() {
    return {
      name: "雨",
      sex: "女",
    };
  },
  mounted() {
    console.log("Student", this.$bus);
  },
  methods:{
    sendStudentName(){
      this.$bus.$emit('hello',this.name);
    }
  }
};
</script>
<style scoped>
.demo {
  background-color: deeppink;
}
</style>

# src-TodoList 全局事件总线

main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div id="root">
        <div class="todo-container">
            <div class="todo-wrap">
                <MyHeader @receive="addTodo"/>
                <List :todos="todos"/>
                <MyFooter
                        :todos="todos"
                        @checkAllTodo="checkAllTodo"
                        @clearAllTodo="clearAllTodo"
                />
            </div>
        </div>
    </div>
</template>
<script>
import MyHeader from "./components/MyHeader";
import List from "./components/List";
import MyFooter from "./components/MyFooter";
export default {
  name: "App",
  components: {
    MyHeader,
    List,
    MyFooter,
  },
  data() {
    return {
      // 定义数据
      todos: JSON.parse(localStorage.getItem("todos")) || [],
    };
  },
  methods: {
    // 添加一个 todo
    addTodo(val) {
      this.todos.unshift(val); //unshift:在最前面添加一条数据
    },
    // 勾选一个 todo or 取消勾选一个 todo
    checkTodo(id) {
      this.todos.forEach((todo) => {
        if (todo.id === id) {
          todo.done = !todo.done;
          console.log("修改成功", id);
        }
      });
    },
    // 根据 id 删除 todo 的一个对象
    deleteTodo(id) {
      if (confirm("确定要删除吗")) {
        // 使用过滤器过滤掉需要删除的 id 然后重新赋值给 todos
        this.todos = this.todos.filter((todo) => {
          return todo.id != id; //confirm 确定和取消的弹窗
        });
      }
    },
    // 全选和取消全选
    checkAllTodo(done) {
      this.todos.forEach((todo) => {
        todo.done = done;
      });
    },
    // 清除所有已经完成的 todo
    clearAllTodo() {
      this.todos = this.todos.filter((todo) => {
        return !todo.done;
      });
    },
  },
  watch: {
    todos: {
      deep: true, // 深度监视
      handler(value) {
        localStorage.setItem("todos", JSON.stringify(value));
      },
    },
  },
  mounted(){
    this.$bus.$on('checkTodo',this.checkTodo);
     this.$bus.$on('deleteTodo',this.deleteTodo);
  },
   beforeDestroy() { //beforeDestroy:当前组件销毁之前处理后事
    this.$bus.$off("checkTodo");
    this.$bus.$off("deleteTodo");
  },
};
</script>
<style lang="css">
/*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-danger:hover {
  color: #fff;
  background-color: #bd362f;
}
.btn:focus {
  outline: none;
}
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>
Item.vue
<template>
    <li>
        <label>
            <input type="checkbox" :checked="todo.done" @click="itemCheck(todo.id)"/>
            <!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了 props -->
            <!-- <input type="checkbox" v-model="todo.done"/> -->
            <span>{ { todo.title } }</span>
        </label>
        <button class="btn btn-danger" @click="deleteButton(todo.id)">删除</button>
    </li>
</template>
<script>
export default {
  name: "Item",
  // 接收数据
  props: ["todo"],
  methods: {
    itemCheck(id) {
      // 通知 App 组件将对应的 done 属性取反
      this.$bus.$emit("checkTodo", id);
    },
    deleteButton(id) {
      // 通知 App 组件删除对应的 todo 对象
      this.$bus.$emit("deleteTodo", id);
    },
  },
};
</script>
<style scoped>
/*item*/
li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}
li label {
  float: left;
  cursor: pointer;
}
li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}
li button {
  float: right;
  display: none;
  margin-top: 3px;
}
li:before {
  content: initial;
}
li:last-child {
  border-bottom: none;
}
li:hover {
  background-color: rgb(5, 238, 255);
}
li:hover button {
  display: block;
}
</style>
List.vue
<template>
    <ul class="todo-main">
        <Item
                v-for="todoObj in todos"
                :key="todoObj.id"
                :todo="todoObj"
        />
    </ul>
</template>
<script>
import Item from "./Item";
export default {
  name: "List",
  components: {
    Item,
  },
  props: ["todos"],
};
</script>
<style scoped>
/*main*/
.todo-main {
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}
.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
</style>
MyFooter.vue
<template>
    <div class="todo-footer" v-show="total">
        <label>
            <!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
            <!--       <input type="checkbox" @change="checkAll" :checked="isAll" /> -->
            <input type="checkbox" v-model="isAll"/>
        </label>
        <span>
      <span>已完成{ { doneTotal } }</span> / 全部{ { total } }
    </span>
        <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
    </div>
</template>
<script>
export default {
  name: "MyFooter",
  props: ["todos", "checkAllTodo","clearAllTodo"],
  computed: {
    total() {
      return this.todos.length;
    },
    doneTotal() {
      // 判断 方法一
      /*  let i = 0;
      this.todos.forEach(e => {
        if (e.done) i++;
      });
      return i; */
      return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0);
    },
    isAll: {
      get() {
        return this.doneTotal === this.total && this.total > 0;
      },
      set(checked) {
        //this.checkAllTodo(checked);
        this.$emit("checkAllTodo", checked);
      },
    },
  },
  methods: {
    checkAll(e) {
      // this.$emit("checkAllTodo", e.target.checked);
      //this.checkAllTodo(e.target.checked);
    },
	clearAll(){
		//this.clearAllTodo()
    this.$emit('clearAllTodo')
	}
  },
};
</script>
<style scoped>
/*footer*/
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}
.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}
.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}
.todo-footer button {
  float: right;
  margin-top: 5px;
}
</style>
MyHeader.vue
<template>
    <div class="todo-header">
        <input
                type="text"
                placeholder="请输入你的任务名称,按回车键确认"
                v-model="title"
                @keyup.enter="add"
        />
    </div>
</template>
<script>
import { nanoid } from "nanoid";
export default {
  name: "MyHeader",
  data() {
    return {
      title: "",
    };
  },
  methods: {
    add() {
     
      // 判断输入框是否为空
      if (!this.title.trim()) return alert("请输入");
      // 将用户输入的包装成一个对象
      const todoObj = { id: nanoid(), title: this.title, done: false };
      // 通知 App 组件去添加一个 todo 对象
       this.$emit('receive',todoObj)
      // 清空输入框
      this.title = "";
    },
  },
};
</script>
<style scoped>
/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}
.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
    0 0 8px rgba(82, 168, 236, 0.6);
}
</style>

# src - 消息订阅与发布

消息订阅与发布 (pubsub)

  1. 一种组件间通信的方式,适用于 <span style="color:red"> 任意组件间通信 </span>
  2. 使用步骤:
    1. 安装 pubsubnpm i pubsub-js
    2. 引入: import pubsub from 'pubsub-js'
    3. 接收数据:A 组件想接收数据,则在 A 组件中订阅消息,订阅的 <span style="color:red"> 回调留在 A 组件自身 </span>
      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.pid = pubsub.subscribe('xxx',this.demo) // 订阅消息
      }
    4. 提供数据: pubsub.publish('xxx',数据)
    5. 最好在 beforeDestroy 钩子中,用 PubSub.unsubscribe(pid)<span style="color:red">
      取消订阅 </span>
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
}).$mount('#app')
App.vue
<template>
    <div>
        <School></School>
        <Student></Student>
    </div>
</template>
<script>
import Student from "./components/Student";
import School from "./components/School";
export default {
  name: "App",
  components: {
    Student,
    School,
  },
};</script>
<style lang="less">
.demo2 {
  background-color: rgb(255, 0, 0);
  .demo3 {
    font-size: 50px;
  }
}
</style>
School.vue
<template>
    <div class="demo2">
        <h2 class="demo3">学校名称:{ { name } }</h2>
        <h2>学校地址:{ { access } }</h2>
    </div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
  name: "School",
  props: ["getStudentName"],
  data() {
    return {
      name: "尚硅谷",
      access: "北京",
    };
  },
  mounted() {
    // 接收数据
   this.pubId = pubsub.subscribe("hello",function(msg,value){
     console.log("School回调成功执行!",value)
   })
  },
  beforeDestroy() { //beforeDestroy:当前组件销毁之前处理后事
    //this.$bus.$off("hello");
    // 取消订阅
    pubsub.unsubscribe(pubId)
  },
};</script>
/* scoped:局部限制css名字防止项目整合时出现命名冲突 */
<style scoped>
.demo {
  background-color: rgb(7, 186, 199);
}</style>
Student.vue
<template>
    <div class="demo">
        <h2>学生姓名:{ { name } }</h2>
        <h2>学生性别:{ { sex } }</h2>
        <button @click="sendStudentName">将学生名字传给School组件</button>
    </div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
  name: "Student",
  data() {
    return {
      name: "雨",
      sex: "女",
    };
  },
  methods:{
    sendStudentName(){
      // 发送数据
      pubsub.publish('hello',this.name)
    }
  }
};</script>
<style scoped>
.demo {
  background-color: deeppink;
}</style>

# src-TodoList 案例 - nextTick

nextTick

  1. 语法: this.$nextTick(回调函数)
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。
  3. 什么时候用:当改变数据后,要基于更新后的新 DOM 进行某些操作时,要在 nextTick 所指定的回调函数中执行。
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div id="root">
        <div class="todo-container">
            <div class="todo-wrap">
                <MyHeader @receive="addTodo"/>
                <List :todos="todos"/>
                <MyFooter
                        :todos="todos"
                        @checkAllTodo="checkAllTodo"
                        @clearAllTodo="clearAllTodo"
                />
            </div>
        </div>
    </div>
</template>
<script>
import MyHeader from "./components/MyHeader";
import List from "./components/List";
import MyFooter from "./components/MyFooter";
export default {
  name: "App",
  components: {
    MyHeader,
    List,
    MyFooter,
  },
  data() {
    return {
      // 定义数据
      todos: JSON.parse(localStorage.getItem("todos")) || [],
    };
  },
  methods: {
    // 添加一个 todo
    addTodo(val) {
      this.todos.unshift(val); //unshift:在最前面添加一条数据
    },
    // 勾选一个 todo or 取消勾选一个 todo
    checkTodo(id) {
      this.todos.forEach((todo) => {
        if (todo.id === id) {
          todo.done = !todo.done;
          console.log("修改成功", id);
        }
      });
    },
    // 根据 id 删除 todo 的一个对象
    deleteTodo(id) {
      if (confirm("确定要删除吗")) {
        // 使用过滤器过滤掉需要删除的 id 然后重新赋值给 todos
        this.todos = this.todos.filter((todo) => {
          return todo.id != id; //confirm 确定和取消的弹窗
        });
      }
    },
    // 全选和取消全选
    checkAllTodo(done) {
      this.todos.forEach((todo) => {
        todo.done = done;
      });
    },
    // 清除所有已经完成的 todo
    clearAllTodo() {
      this.todos = this.todos.filter((todo) => {
        return !todo.done;
      });
    },
    updateTodo(id,title) {
      this.todos.forEach((todo) => {
        if (todo.id === id) {
          todo.title = title
          console.log("修改成功",id,title);
        }
      });
    }
  },
  watch: {
    todos: {
      deep: true, // 深度监视
      handler(value) {
        localStorage.setItem("todos", JSON.stringify(value));
      },
    },
  },
  mounted(){
    this.$bus.$on('checkTodo',this.checkTodo);
     this.$bus.$on('deleteTodo',this.deleteTodo);
     this.$bus.$on('updateTodo',this.updateTodo);
  },
   beforeDestroy() { //beforeDestroy:当前组件销毁之前处理后事
    this.$bus.$off("checkTodo");
    this.$bus.$off("deleteTodo");
    this.$bus.$off("updateTodo");
  },
};
</script>
<style lang="css">
/*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-danger:hover {
  color: #fff;
  background-color: #bd362f;
}
.btn-edit {
  color: #fff;
  background-color: #00ff40;
  border: 1px solid #0fa334;
  margin: 5px;
}
.btn-edit:hover {
  color: #fff;
  background-color: #00ff40;
}
.btn:focus {
  outline: none;
}
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>
Item.vue
<template>
    <li>
        <label>
            <input type="checkbox" :checked="todo.done" @click="itemCheck(todo.id)"/>
            <!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了 props -->
            <!-- <input type="checkbox" v-model="todo.done"/> -->
            <span v-show="!todo.isEdit">{ { todo.title } }</span>
            <input
                    v-show="todo.isEdit"
                    type="text"
                    :value="todo.title"
                    @blur="blur(todo, $event)"
                    ref="inputTitle"
            />
        </label>
        <button class="btn btn-danger" @click="deleteButton(todo.id)">删除</button>
        <button v-show="!todo.isEdit" class="btn btn-edit" @click="edit(todo)">
            编辑
        </button>
    </li>
</template>
<script>
export default {
  name: "Item",
  // 接收数据
  props: ["todo"],
  methods: {
    // 失去焦点回调 (真正执行修改逻辑)
    blur(todo, e) {
      todo.isEdit = false;
      if (!e.target.value.trim()) return alert("输入不能为空!");
      this.$bus.$emit("updateTodo", todo.id, e.target.value);
    },
    // 编辑
    edit(todo) {
      // 判断是否有 isEdit 属性
      if (todo.hasOwnProperty("isEdit")) {
        todo.isEdit = true;
      } else {
        this.$set(todo, "isEdit", true);
      }
      // 方法一 自动获取焦点
      /*  setTimeout (() => {
        this.$refs.inputTitle.focus (); //focus ():自动获取焦点
      }, 200); */
      // 方法二
      this.$nextTick(function () {
        this.$refs.inputTitle.focus(); //focus ():自动获取焦点
      });
    },
    // 勾选和取消勾选
    itemCheck(id) {
      // 通知 App 组件将对应的 done 属性取反
      this.$bus.$emit("checkTodo", id);
    },
    // 删除
    deleteButton(id) {
      // 通知 App 组件删除对应的 todo 对象
      this.$bus.$emit("deleteTodo", id);
    },
  },
};
</script>
<style scoped>
/*item*/
li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}
li label {
  float: left;
  cursor: pointer;
}
li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}
li button {
  float: right;
  display: none;
  margin-top: 3px;
}
li:before {
  content: initial;
}
li:last-child {
  border-bottom: none;
}
li:hover {
  background-color: rgb(5, 238, 255);
}
li:hover button {
  display: block;
}
</style>
List.vue
<template>
    <ul class="todo-main">
        <Item
                v-for="todoObj in todos"
                :key="todoObj.id"
                :todo="todoObj"
        />
    </ul>
</template>
<script>
import Item from "./Item";
export default {
  name: "List",
  components: {
    Item,
  },
  props: ["todos"],
};
</script>
<style scoped>
/*main*/
.todo-main {
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}
.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
</style>
MyFooter.vue
<template>
    <div class="todo-footer" v-show="total">
        <label>
            <!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
            <!--       <input type="checkbox" @change="checkAll" :checked="isAll" /> -->
            <input type="checkbox" v-model="isAll"/>
        </label>
        <span>
      <span>已完成{ { doneTotal } }</span> / 全部{ { total } }
    </span>
        <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
    </div>
</template>
<script>
export default {
  name: "MyFooter",
  props: ["todos", "checkAllTodo","clearAllTodo"],
  computed: {
    total() {
      return this.todos.length;
    },
    doneTotal() {
      // 判断 方法一
      /*  let i = 0;
      this.todos.forEach(e => {
        if (e.done) i++;
      });
      return i; */
      return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0);
    },
    isAll: {
      get() {
        return this.doneTotal === this.total && this.total > 0;
      },
      set(checked) {
        //this.checkAllTodo(checked);
        this.$emit("checkAllTodo", checked);
      },
    },
  },
  methods: {
    checkAll(e) {
      // this.$emit("checkAllTodo", e.target.checked);
      //this.checkAllTodo(e.target.checked);
    },
	clearAll(){
		//this.clearAllTodo()
    this.$emit('clearAllTodo')
	}
  },
};
</script>
<style scoped>
/*footer*/
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}
.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}
.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}
.todo-footer button {
  float: right;
  margin-top: 5px;
}
</style>
MyHeader.vue
<template>
    <div class="todo-header">
        <input
                type="text"
                placeholder="请输入你的任务名称,按回车键确认"
                v-model="title"
                @keyup.enter="add"
        />
    </div>
</template>
<script>
import { nanoid } from "nanoid";
export default {
  name: "MyHeader",
  data() {
    return {
      title: "",
    };
  },
  methods: {
    add() {
     
      // 判断输入框是否为空
      if (!this.title.trim()) return alert("请输入");
      // 将用户输入的包装成一个对象
      const todoObj = { id: nanoid(), title: this.title, done: false };
      // 通知 App 组件去添加一个 todo 对象
       this.$emit('receive',todoObj)
      // 清空输入框
      this.title = "";
    },
  },
};
</script>
<style scoped>
/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}
.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
    0 0 8px rgba(82, 168, 236, 0.6);
}
</style>

# src - 过渡与动画

Vue 封装的过度与动画

  1. 作用:在插入、更新或移除 DOM 元素时,在合适的时候给元素添加样式类名。
  2. 图示:
    • <img src="https://img04.sogoucdn.com/app/a/100520146/5990c1dff7dc7a8fb3b34b4462bd0105" style="width:60%" />
  3. 写法:
    1. 准备好样式:
      • 元素进入的样式:
        1. v-enter:进入的起点
        2. v-enter-active:进入过程中
        3. v-enter-to:进入的终点
      • 元素离开的样式:
        1. v-leave:离开的起点
        2. v-leave-active:离开过程中
        3. v-leave-to:离开的终点
    2. 使用 <transition> 包裹要过度的元素,并配置 name 属性:
      vue文件
      <transition name="hello">
          <h1 v-show="isShow"> 你好啊!</h1>
      </transition>
    3. 备注:若有多个元素需要过度,则需要使用: <transition-group> ,且每个元素都要指定 key 值。
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div id="root">
        <Test/>
        <Test2/>
        <Test3/>
    </div>
</template>
<script>
import Test from "./components/Test";
import Test2 from "./components/Test2";
import Test3 from "./components/Test3";
export default {
  name: "App",
  components: {
    Test,
    Test2,
    Test3,
  },
};
</script>
<style lang="css">
/*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-danger:hover {
  color: #fff;
  background-color: #bd362f;
}
.btn-edit {
  color: #fff;
  background-color: #00ff40;
  border: 1px solid #0fa334;
  margin: 5px;
}
.btn-edit:hover {
  color: #fff;
  background-color: #00ff40;
}
.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>
Test.vue
<template>
    <div>
        <button @click="isShow = !isShow">显示/隐藏</button>
        <transition name="msg" :appear="true">
            <h2 v-show="isShow">{ { msg } }</h2>
        </transition>
    </div>
</template>
<script>
export default {
  name: "Test",
  data() {
    return { msg: "你好啊", isShow: true };
  },
};
</script>
<style scoped>
h2 {
  background-color: aqua;
}
.msg-enter-active {
  animation: rain 1s;
}
.msg-leave-active {
  animation: rain 1s reverse;
}
@keyframes rain {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0px);
  }
}
</style>
Test2.vue
<template>
    <div>
        <button @click="isShow = !isShow">显示/隐藏</button>
        <transition-group name="msg" :appear="true">
            <h2 v-show="!isShow" key="1">{ { msg } }</h2>
            <h2 v-show="isShow" key="2"></h2>
        </transition-group>
    </div>
</template>
<script>
export default {
  name: "Test",
  data() {
    return { msg: "你好啊", isShow: true };
  },
};
</script>
<style scoped>
h2 {
  background-color: aqua;
}
/** 来的时候激活 *//** 走的时候激活 */
.msg-enter-active,
.msg-leave-active {
  transition: 1s linear;
}
/** 进入的起点 */ /** 离开的终点 */
.msg-enter,
.msg-leave-to {
  transform: translateX(-100%);
}
/** 进入的终点 */ /** 离开的起点 */
.msg-leave-active,
.msg-leave {
  transform: translateX(0);
}
</style>
Test3.vue
<template>
    <div>
        <button @click="isShow = !isShow">显示/隐藏</button>
        <transition-group name="animate__animated animate__bounce" :appear="true" enter-active-class="animate__swing"
                          leave-active-class="animate__bounceOut">
            <h2 v-show="!isShow" key="1">{ { msg } }</h2>
            <h2 v-show="isShow" key="2"></h2>
        </transition-group>
    </div>
</template>
<script>
import 'animate.css'
export default {
  name: "Test",
  data() {
    return { msg: "你好啊", isShow: true };
  },
};
</script>
<style scoped>
h2 {
  background-color: aqua;
}
</style>

# src-TodoList 动画

main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div id="root">
        <div class="todo-container">
            <div class="todo-wrap">
                <MyHeader @receive="addTodo"/>
                <List :todos="todos"/>
                <MyFooter
                        :todos="todos"
                        @checkAllTodo="checkAllTodo"
                        @clearAllTodo="clearAllTodo"
                />
            </div>
        </div>
    </div>
</template>
<script>
import MyHeader from "./components/MyHeader";
import List from "./components/List";
import MyFooter from "./components/MyFooter";
export default {
  name: "App",
  components: {
    MyHeader,
    List,
    MyFooter,
  },
  data() {
    return {
      // 定义数据
      todos: JSON.parse(localStorage.getItem("todos")) || [],
    };
  },
  methods: {
    // 添加一个 todo
    addTodo(val) {
      this.todos.unshift(val); //unshift:在最前面添加一条数据
    },
    // 勾选一个 todo or 取消勾选一个 todo
    checkTodo(id) {
      this.todos.forEach((todo) => {
        if (todo.id === id) {
          todo.done = !todo.done;
          console.log("修改成功", id);
        }
      });
    },
    // 根据 id 删除 todo 的一个对象
    deleteTodo(id) {
      if (confirm("确定要删除吗")) {
        // 使用过滤器过滤掉需要删除的 id 然后重新赋值给 todos
        this.todos = this.todos.filter((todo) => {
          return todo.id != id; //confirm 确定和取消的弹窗
        });
      }
    },
    // 全选和取消全选
    checkAllTodo(done) {
      this.todos.forEach((todo) => {
        todo.done = done;
      });
    },
    // 清除所有已经完成的 todo
    clearAllTodo() {
      this.todos = this.todos.filter((todo) => {
        return !todo.done;
      });
    },
    updateTodo(id,title) {
      this.todos.forEach((todo) => {
        if (todo.id === id) {
          todo.title = title
          console.log("修改成功",id,title);
        }
      });
    }
  },
  watch: {
    todos: {
      deep: true, // 深度监视
      handler(value) {
        localStorage.setItem("todos", JSON.stringify(value));
      },
    },
  },
  mounted(){
    this.$bus.$on('checkTodo',this.checkTodo);
     this.$bus.$on('deleteTodo',this.deleteTodo);
     this.$bus.$on('updateTodo',this.updateTodo);
  },
   beforeDestroy() { //beforeDestroy:当前组件销毁之前处理后事
    this.$bus.$off("checkTodo");
    this.$bus.$off("deleteTodo");
    this.$bus.$off("updateTodo");
  },
};
</script>
<style lang="css">
/*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-danger:hover {
  color: #fff;
  background-color: #bd362f;
}
.btn-edit {
  color: #fff;
  background-color: #00ff40;
  border: 1px solid #0fa334;
  margin: 5px;
}
.btn-edit:hover {
  color: #fff;
  background-color: #00ff40;
}
.btn:focus {
  outline: none;
}
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>
Item.vue
<template>
    <transition name="animate__animated animate__bounce" appear leave-active-class="animate__flash"
                enter-active-class="animate__bounce">
        <li>
            <label>
                <input
                        type="checkbox"
                        :checked="todo.done"
                        @click="itemCheck(todo.id)"
                />
                <!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了 props -->
                <!-- <input type="checkbox" v-model="todo.done"/> -->
                <span v-show="!todo.isEdit">{ { todo.title } }</span>
                <input
                        v-show="todo.isEdit"
                        type="text"
                        :value="todo.title"
                        @blur="blur(todo, $event)"
                        ref="inputTitle"
                />
            </label>
            <button class="btn btn-danger" @click="deleteButton(todo.id)">
                删除
            </button>
            <button v-show="!todo.isEdit" class="btn btn-edit" @click="edit(todo)">
                编辑
            </button>
        </li>
    </transition>
</template>
<script>
import 'animate.css'
export default {
  name: "Item",
  // 接收数据
  props: ["todo"],
  methods: {
    // 失去焦点回调 (真正执行修改逻辑)
    blur(todo, e) {
      todo.isEdit = false;
      if (!e.target.value.trim()) return alert("输入不能为空!");
      this.$bus.$emit("updateTodo", todo.id, e.target.value);
    },
    // 编辑
    edit(todo) {
      // 判断是否有 isEdit 属性
      if (todo.hasOwnProperty("isEdit")) {
        todo.isEdit = true;
      } else {
        this.$set(todo, "isEdit", true);
      }
      // 方法一 自动获取焦点
      /*  setTimeout (() => {
        this.$refs.inputTitle.focus (); //focus ():自动获取焦点
      }, 200); */
      // 方法二
      this.$nextTick(function () {
        this.$refs.inputTitle.focus(); //focus ():自动获取焦点
      });
    },
    // 勾选和取消勾选
    itemCheck(id) {
      // 通知 App 组件将对应的 done 属性取反
      this.$bus.$emit("checkTodo", id);
    },
    // 删除
    deleteButton(id) {
      // 通知 App 组件删除对应的 todo 对象
      this.$bus.$emit("deleteTodo", id);
    },
  },
};
</script>
<style scoped>
/*item*/
li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}
li label {
  float: left;
  cursor: pointer;
}
li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}
li button {
  float: right;
  display: none;
  margin-top: 3px;
}
li:before {
  content: initial;
}
li:last-child {
  border-bottom: none;
}
li:hover {
  background-color: rgb(5, 238, 255);
}
li:hover button {
  display: block;
}
</style>
List.vue
<template>
    <ul class="todo-main">
        <Item
                v-for="todoObj in todos"
                :key="todoObj.id"
                :todo="todoObj"
        />
    </ul>
</template>
<script>
import Item from "./Item";
export default {
  name: "List",
  components: {
    Item,
  },
  props: ["todos"],
};
</script>
<style scoped>
/*main*/
.todo-main {
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}
.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
</style>
MyFooter.vue
<template>
    <div class="todo-footer" v-show="total">
        <label>
            <!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
            <!--       <input type="checkbox" @change="checkAll" :checked="isAll" /> -->
            <input type="checkbox" v-model="isAll"/>
        </label>
        <span>
      <span>已完成{ { doneTotal } }</span> / 全部{ { total } }
    </span>
        <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
    </div>
</template>
<script>
export default {
  name: "MyFooter",
  props: ["todos", "checkAllTodo","clearAllTodo"],
  computed: {
    total() {
      return this.todos.length;
    },
    doneTotal() {
      // 判断 方法一
      /*  let i = 0;
      this.todos.forEach(e => {
        if (e.done) i++;
      });
      return i; */
      return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0);
    },
    isAll: {
      get() {
        return this.doneTotal === this.total && this.total > 0;
      },
      set(checked) {
        //this.checkAllTodo(checked);
        this.$emit("checkAllTodo", checked);
      },
    },
  },
  methods: {
    checkAll(e) {
      // this.$emit("checkAllTodo", e.target.checked);
      //this.checkAllTodo(e.target.checked);
    },
	clearAll(){
		//this.clearAllTodo()
    this.$emit('clearAllTodo')
	}
  },
};
</script>
<style scoped>
/*footer*/
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}
.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}
.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}
.todo-footer button {
  float: right;
  margin-top: 5px;
}
</style>
MyHeader.vue
<template>
    <div class="todo-header">
        <input
                type="text"
                placeholder="请输入你的任务名称,按回车键确认"
                v-model="title"
                @keyup.enter="add"
        />
    </div>
</template>
<script>
import { nanoid } from "nanoid";
export default {
  name: "MyHeader",
  data() {
    return {
      title: "",
    };
  },
  methods: {
    add() {
     
      // 判断输入框是否为空
      if (!this.title.trim()) return alert("请输入");
      // 将用户输入的包装成一个对象
      const todoObj = { id: nanoid(), title: this.title, done: false };
      // 通知 App 组件去添加一个 todo 对象
       this.$emit('receive',todoObj)
      // 清空输入框
      this.title = "";
    },
  },
};
</script>
<style scoped>
/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}
.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
    0 0 8px rgba(82, 168, 236, 0.6);
}
</style>

# src - 代理服务

vue 脚手架配置代理 v2 版 方式一

  1. 在 vue.config.js 中添加如下配置:
    devServer:{
         proxy:"http://localhost:5000"
     }
  2. 说明:
    1. 优点:配置简单,请求资源时直接发给前端(8080)即可。
    2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
    3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)

vue 脚手架配置代理 v2 版 方式二

  1. 编写 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
    */
  2. 说明:
    1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
    2. 缺点:配置略微繁琐,请求资源时必须加前缀。
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div id="app">
        <button @click="getStudents">获取学生信息</button>
        <button @click="getCars">获取汽车信息</button>
    </div>
</template>
<script>
import axios from "axios";
export default {
  name: "App",
  methods: {
    getStudents() {
      axios.get("http://localhost:8080/rain/students").then(
        (response) => {
          console.log("请求成功了", response.data);
        },
        (error) => {
          console.log("请求失败了", error.message);
        }
      );
    },
      getCars() {
      axios.get("http://localhost:8080/demo/cars").then(
        (response) => {
          console.log("请求成功了", response.data);
        },
        (error) => {
          console.log("请求失败了", error.message);
        }
      );
    },
  },
};
</script>
List.vue
<template>
</template>
<script>
export default {
    name:'List',
}
</script>
<style>
</style>
Search.vue
<template>
</template>
<script>
export default {
    name:'Search',
}
</script>
<style>
</style>

# src-GitHub 搜索案例

main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div class="container">
        <Search></Search>
        <List></List>
    </div>
</template>
<script>
import axios from "axios";
import List from "./components/List";
import Search from "./components/Search";
export default {
  name: "App",
  components: { List, Search },
  methods: {},
};</script>
List.vue
<template>
    <div class="row">
        <!-- 展示用户列表 -->
        <div
                v-show="info.users.length"
                class="card"
                v-for="user in info.users"
                :key="user.login"
        >
            <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: "List",
  data() {
    return {
      info: {
        isFirst: true,
        isLoading: false,
        errMsg: "",
        users: [],
      },
    };
  },
  mounted() {
    this.$bus.$on("updataListData", (dataObj) => {
      this.info = {...this.info,...dataObj};
    });
  },
};
</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: 0.75rem;
  margin-bottom: 2rem;
  border: 1px solid #efefef;
  text-align: center;
}
.card > img {
  margin-bottom: 0.75rem;
  border-radius: 100px;
}
.card-text {
  font-size: 85%;
}
</style>
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"
            />&nbsp;<button @click="getGitHub">Search</button>
        </div>
    </section>
</template>
<script>
import axios from 'axios'
export default {
    name:'Search',
    data(){
        return{
            keyWord:''
        }
    },
    methods:{
        getGitHub(){
            // 请求前更新 List 的数据
            this.$bus.$emit('updataListData',{isFirst:false,isLoading:true,errMsg:"", users:[]})
            axios.get(`https://api.github.com/search/users?q=${this.keyword}`).then(result =>{
                // 请求成功后更新 List 的数据
                this.$bus.$emit('updataListData',{isLoading:false,errMsg:"", users:result.data.items})
            },error =>{
                 // 请求失败后更新 List 的数据
                this.$bus.$emit('updataListData',{isLoading:false,errMsg:error.message, users:[]})
            })
        }
    },
}
</script>
<style>
</style>

# 插槽

插槽介绍:

  1. 作用:让父组件可以向子组件指定位置插入 html 结构,也是一种组件间通信的方式
  2. 适用于 <strong style="color:red"> 父组件 ===> 子组件 </strong>
  3. 分类:具名插槽默认插槽作用域插槽

# src - 具名插槽

具名插槽介绍:

父组件中:
<Category>
    <template slot="center">
        <div>html结构1</div>
    </template>
    <template v-slot:footer>
        <div>html结构2</div>
    </template>
</Category>
子组件中:
<template>
    <div>
        <!-- 定义插槽 -->
        <slot name="center">插槽默认内容...</slot>
        <slot name="footer">插槽默认内容...</slot>
    </div>
</template>
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div class="container">
        <Category title="美食">
            <img slot="conter" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt=""/>
            <a slot="footer" href="https://s3.ax1x.com/">点我跳转</a>
        </Category>
        <Category title="游戏">
            <ul slot="conter">
                <li v-for="(g, index) in games" :key="index">{ { g } }</li>
            </ul>
            <div slot="footer">
                <a>游戏</a>
                <a>游戏</a>
            </div>
        </Category>
        <Category title="电影">
            <video slot="conter" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"/>
            <div slot="footer">
                <a slot="footer">电影</a>
            </div>
        </Category>
    </div>
</template>
<script>
import Category from "./components/Category";
export default {
  name: "App",
  components: { Category },
  data() {
    return {
      foods: ["火锅", "烧烤", "小龙虾"],
      games: ["红色警戒", "穿越火线", "劲舞团"],
      films: ["《教父》", "《拆弹专家》", "《你好,李焕英》"],
    };
  },
};</script>
<style>
.container {
  display: flex;
  justify-content: space-around;
}
img {
  width: 100%;
}
video{
  width: 60%;
}</style>
Category.vue
<template>
    <div class="category">
        <h3>{ {title} }分类</h3>
        <!-- 默认插槽 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
        <slot name='conter'>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
        <slot name='footer'>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
    </div>
</template>
<script>
export default {
    name:'Category',
    props:['title'],
}</script>
<style scoped>
    .category{
        background-color: aqua;
        widows: 200px;
        height: 300px;
    }
    h3{
        background-color:rgb(0, 81, 255);
        text-align: center;
    }</style>

# src - 默认插槽

默认插槽介绍:

父组件中:
<Category>
    <div>html结构1</div>
</Category>
子组件中:
<template>
    <div>
        <!-- 定义插槽 -->
        <slot>插槽默认内容...</slot>
    </div>
</template>
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
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"/>
        </Category>
    </div>
</template>
<script>
import Category from "./components/Category";
export default {
  name: "App",
  components: { Category },
  data() {
    return {
      foods: ["火锅", "烧烤", "小龙虾"],
      games: ["红色警戒", "穿越火线", "劲舞团"],
      films: ["《教父》", "《拆弹专家》", "《你好,李焕英》"],
    };
  },
};
</script>
<style scoped>
.container {
  display: flex;
  justify-content: space-around;
}
img {
  width: 100%;
}
</style>
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: aqua;
        widows: 200px;
        height: 300px;
    }
    h3{
        background-color:rgb(0, 81, 255);
        text-align: center;
    }
</style>

# src - 作用域插槽

作用域插槽介绍:

  • 理解:
    • <span style="color:red"> 数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定 </span>
    • games 数据在 Category 组件中,但使用数据所遍历出来的结构由 App 组件决定。
  • 具体编码:
父组件中:
<Category>
   <template scope="scopeData">
       <!-- 生成的是 ul 列表 -->
       <ul>
           <li v-for="g in scopeData.games" :key="g"></li>
       </ul>
   </template>
</Category>
<Category>
   <template slot-scope="scopeData">
       <!-- 生成的是 h4 标题 -->
       <h4 v-for="g in scopeData.games" :key="g"></h4>
   </template>
</Category>
子组件中:
<template>
   <div>
       <slot :games="games"></slot>
   </div>
</template>
<script>
      export default {
         name:'Category',
         props:['title'],
         // 数据在子组件自身
         data() {
            return {
               games:['红色警戒','穿越火线','劲舞团','超级玛丽']
            }
         },
      }
   
</script>
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
Vue.prototype.$bus = this // 安装全局事件总线
},
}).$mount('#app')
App.vue
<template>
    <div class="container">
        <Category title="游戏">
            <template scope="rain">
                <ul>
                    <li v-for="(g,index) in rain.games" :key="index">{ {g} }</li>
                </ul>
            </template>
        </Category>
        <Category title="游戏">
            <template scope="rain">
                <ol>
                    <li v-for="(g,index) in rain.games" :key="index">{ {g} }</li>
                </ol>
            </template>
        </Category>
    </div>
</template>
<script>
import Category from "./components/Category";
export default {
  name: "App",
  components: { Category },
  data() {
    return {
    };
  },
};
</script>
<style>
.container {
  display: flex;
  justify-content: space-around;
}
img {
  width: 100%;
}
video{
  width: 60%;
}
</style>
Category.vue
<template>
    <div class="category">
        <h3>{ { title } }分类</h3>
        <!-- 作用域插槽 -->
        <slot :games="games">我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
    </div>
</template>
<script>
export default {
  name: "Category",
  props: ["title"],
  data() {
    return {
      games: ["红色警戒", "穿越火线", "劲舞团"],
    };
  },
};
</script>
<style scoped>
.category {
  background-color: aqua;
  widows: 200px;
  height: 300px;
}
h3 {
  background-color: rgb(0, 81, 255);
  text-align: center;
}
</style>

# src - 求和案例 mapState-mapGetters

main.js
import Vue from 'vue'
import App from './App.vue'
// 引入 Store
import store from './store/index'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  store:store,
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div>
        <Count/>
    </div>
</template>
<script>
import Count from "./components/Count";
export default {
  name: "App",
  components: { Count },
  data() {
    return {
    };
  },
};
</script>
<style>
</style>

新建 store 文件夹并创建一个 index.js

index.js
// 改文件用于创建 Vuex 中最为核心的 store
import Vue from 'vue'
// 引入 Vuex
import Vuex from 'vuex'
// 使用插件
Vue.use(Vuex)
// 准备 actions—— 用于响应组件中的动作
const actions = {
    // 加法
    //2. 创建函数 接收 dispatch 传来的数据
    /* increment (context, value) {
        // 将数据交给 mutations 执行数据的修改
        context.commit ('increment', value)
        console.log ('actions 被调用了 ', value)
    },
    // 减法
    decrement (context, value) {
        context.commit ('decrement', value)
    }, */
     // 当前求和为奇数再加
    incrementoOdd(context, value) {
        if (context.state.sum % 2) {
            context.commit('increment', value)
        }
    },
    // 等等再加
    incrementTime(context, value) {
        setTimeout(() => {
            context.commit('increment', value)
        }, 500);
    }
}
// 准备 mutations—— 用于操作数据 (state)
const mutations = {
    // 加法
    //3. 创建函数 接收 actions 数据
    increment(state, value) {
        // 修改数据
        state.sum += value
        console.log('mutations被调用了', value)
    },
    // 减法
    decrement(state, value) {
        state.sum -= value
    },
}
// 准备 stat 存数据
const state = {
    // 当前的和
    sum: 0,
    school:'尚硅谷',
    subject:'JAVA'
}
// 准备 getterse—— 用于将 state 中的数据进行加工
const getters = {
    bigSum(state){
        return state.sum*10;
    }
}
// 创建并暴露 store
export default new Vuex.Store({
    actions, mutations, state,getters
})
Count.vue
<template>
    <div>
        <h1>当前求和为:{ { sum } }</h1>
        <h1>放大十倍的和:{ { bigSum } }</h1>
        <h1>{ { school } }--{ { subject } }</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>
import { mapState, mapGetters } from "vuex";
export default {
  name: "Count",
  data() {
    return {
      n: 1,
    };
  },
  computed: {
    // 借助 mapState 生成计算属性,从 state 中读取数据。(对象写法)
    // 方式一
    //...mapState({sum:'sum',subject:'subject',school:'school'})
    // 方式二
    ...mapState(["sum", "subject", "school"]),
    /* ********************************************************************* */
    // 借助 mapGetters 生成计算属性,从 getters 中读取数据。(对象写法)
    // 方式一
    //...mapGetters({ bigSum: "bigSum" }),
    // 方式二
    ...mapGetters(['bigSum'])
  },
  methods: {
    // 加法
    increment() {
      //1. 调用 store.dispatch 方法执行加法操作
      //this.$store.dispatch("increment", this.n);
      this.$store.commit("increment", this.n);
    },
    // 减法
    decrement() {
      //this.$store.dispatch("decrement", this.n);
      this.$store.commit("decrement", this.n);
    },
    // 当前求和为奇数再加
    incrementodd() {
      this.$store.dispatch("incrementoOdd", this.n);
    },
    // 等等再加
    incrementwait() {
      this.$store.dispatch("incrementTime", this.n);
    },
  },
  mounted() {
    console.log(this);
  },
};
</script>
<style>
</style>

# src - 求和案例 mapMutations-mapActions

main.js
import Vue from 'vue'
import App from './App.vue'
// 引入 Store
import store from './store/index'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  store:store,
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div>
        <Count/>
    </div>
</template>
<script>
import Count from "./components/Count";
export default {
  name: "App",
  components: { Count },
  data() {
    return {
    };
  },
};
</script>
<style>
</style>

新建 store 文件夹并创建一个 index.js

index.js
// 改文件用于创建 Vuex 中最为核心的 store
import Vue from 'vue'
// 引入 Vuex
import Vuex from 'vuex'
// 使用插件
Vue.use(Vuex)
// 准备 actions—— 用于响应组件中的动作
const actions = {
    // 加法
    //2. 创建函数 接收 dispatch 传来的数据
    /* increment (context, value) {
        // 将数据交给 mutations 执行数据的修改
        context.commit ('increment', value)
        console.log ('actions 被调用了 ', value)
    },
    // 减法
    decrement (context, value) {
        context.commit ('decrement', value)
    }, */
     // 当前求和为奇数再加
    incrementoOdd(context, value) {
        if (context.state.sum % 2) {
            context.commit('increment', value)
        }
    },
    // 等等再加
    incrementTime(context, value) {
        setTimeout(() => {
            context.commit('increment', value)
        }, 500);
    }
}
// 准备 mutations—— 用于操作数据 (state)
const mutations = {
    // 加法
    //3. 创建函数 接收 actions 数据
    increment(state, value) {
        // 修改数据
        state.sum += value
        console.log('mutations被调用了', value)
    },
    // 减法
    decrement(state, value) {
        state.sum -= value
    },
}
// 准备 stat 存数据
const state = {
    // 当前的和
    sum: 0,
    school:'尚硅谷',
    subject:'JAVA'
}
// 准备 getterse—— 用于将 state 中的数据进行加工
const getters = {
    bigSum(state){
        return state.sum*10;
    }
}
// 创建并暴露 store
export default new Vuex.Store({
    actions, mutations, state,getters
})
Count.vue
<template>
    <div>
        <h1>当前求和为:{ { sum } }</h1>
        <h1>放大十倍的和:{ { bigSum } }</h1>
        <h1>{ { school } }--{ { subject } }</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(n)">+</button>
        <button @click="decrement(n)">-</button>
        <button @click="incrementoOdd(n)">当前求和为奇数再加</button>
        <button @click="incrementTime(n)">等一等再加</button>
    </div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
export default {
  name: "Count",
  data() {
    return {
      n: 1,
    };
  },
  computed: {
    // 借助 mapState 生成计算属性,从 state 中读取数据。(对象写法)
    // 方式一
    //...mapState({sum:'sum',subject:'subject',school:'school'})
    // 借助 mapState 生成计算属性,从 state 中读取数据。(数组写法)
    // 方式二
    ...mapState(["sum", "subject", "school"]),
    /* ********************************************************************* */
    // 借助 mapGetters 生成计算属性,从 getters 中读取数据。(对象写法)
    // 方式一
    //...mapGetters({ bigSum: "bigSum" }),
    // 借助 mapGetters 生成计算属性,从 getters 中读取数据。(数组写法)
    // 方式二
    ...mapGetters(["bigSum"]),
  },
  methods: {
    // 借助 mapMutations 生成对应的方法,从 mutations 中读取数据。(对象写法)
    // 方式一
    //...mapMutations({ increment: "increment", decrement: "decrement" }),
    // 借助 mapMutations 生成对应的方法,从 mutations 中读取数据。(对象写法)
    // 方式二
    ...mapMutations(["increment", "decrement"]),
    /* ********************************************************************** */
    // 借助 mapActions 生成对应的方法,从 actions 中读取数据。(对象写法)
    // 方式一
    //...mapActions({incrementodd:'incrementoOdd',incrementwait:'incrementTime'})
    // 借助 mapActions 生成对应的方法,从 actions 中读取数据。(数组写法)
    // 方法二
    ...mapActions(["incrementoOdd", "incrementTime"]),
  },
  mounted() {
    console.log(this);
  },
};
</script>
<style>
</style>

# src - 组件共享数据

组件共享数据需要使用 Vuex 插件

# 案例代码

main.js
import Vue from 'vue'
import App from './App.vue'
// 引入 Store
import store from './store/index'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  store:store,
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div>
        <Count/>
        <Person/>
    </div>
</template>
<script>
import Count from "./components/Count";
import Person from "./components/Person";
export default {
  name: "App",
  components: { Count,Person },
  data() {
    return {
    };
  },
};
</script>
<style>
</style>

新建 store 文件夹创建一个 index.js

index.js
// 改文件用于创建 Vuex 中最为核心的 store
import Vue from 'vue'
// 引入 Vuex
import Vuex from 'vuex'
// 使用插件
Vue.use(Vuex)
// 准备 actions—— 用于响应组件中的动作
const actions = {
    // 当前求和为奇数再加
    incrementoOdd(context, value) {
        if (context.state.sum % 2) {
            context.commit('increment', value)
        }
    },
    // 等等再加
    incrementTime(context, value) {
        setTimeout(() => {
            context.commit('increment', value)
        }, 500);
    },
    personList(context, value) {
        console.log('#######', value, this)
        context.commit('personList', value)
    }
}
// 准备 mutations—— 用于操作数据 (state)
const mutations = {
    // 加法
    //3. 创建函数 接收 actions 数据
    increment(state, value) {
        // 修改数据
        state.sum += value
        console.log('mutations被调用了', value)
    },
    // 减法
    decrement(state, value) {
        state.sum -= value
    },
    personList(state, value) {
        console.log('personList', value)
        state.personList.unshift(value)
    }
}
// 准备 stat 存数据
const state = {
    // 当前的和
    sum: 0,
    school: '尚硅谷',
    subject: 'JAVA',
    personList: [
        { id: '01', name: '雨' }
    ],
}
// 准备 getterse—— 用于将 state 中的数据进行加工
const getters = {
    bigSum(state) {
        return state.sum * 10;
    }
}
// 创建并暴露 store
export default new Vuex.Store({
    actions, mutations, state, getters
})
Count.vue
<template>
    <div>
        <h1>当前求和为:{ { sum } }</h1>
        <h1>
            <ul>
                姓名:
                <li v-for="personLists in personList" :key="personLists.id">
                    { { personLists.name } }
                </li>
            </ul>
            <h1 style="color: red">下方组件总人数:{ { personList.length } }</h1>
        </h1>
        <h1>放大十倍的和:{ { bigSum } }</h1>
        <h1>{ { school } }--{ { subject } }</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(n)">+</button>
        <button @click="decrement(n)">-</button>
        <button @click="incrementoOdd(n)">当前求和为奇数再加</button>
        <button @click="incrementTime(n)">等一等再加</button>
    </div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
export default {
  name: "Count",
  data() {
    return {
      n: 1,
    };
  },
  computed: {
    // 借助 mapState 生成计算属性,从 state 中读取数据。(对象写法)
    // 方式一
    //...mapState({sum:'sum',subject:'subject',school:'school'})
    // 借助 mapState 生成计算属性,从 state 中读取数据。(数组写法)
    // 方式二
    ...mapState(["sum", "subject", "school", "personList"]),
    /* ********************************************************************* */
    // 借助 mapGetters 生成计算属性,从 getters 中读取数据。(对象写法)
    // 方式一
    //...mapGetters({ bigSum: "bigSum" }),
    // 借助 mapGetters 生成计算属性,从 getters 中读取数据。(数组写法)
    // 方式二
    ...mapGetters(["bigSum"]),
  },
  methods: {
    // 借助 mapMutations 生成对应的方法,从 mutations 中读取数据。(对象写法)
    // 方式一
    //...mapMutations({ increment: "increment", decrement: "decrement" }),
    // 借助 mapMutations 生成对应的方法,从 mutations 中读取数据。(对象写法)
    // 方式二
    ...mapMutations(["increment", "decrement"]),
    /* ********************************************************************** */
    // 借助 mapActions 生成对应的方法,从 actions 中读取数据。(对象写法)
    // 方式一
    //...mapActions({incrementodd:'incrementoOdd',incrementwait:'incrementTime'})
    // 借助 mapActions 生成对应的方法,从 actions 中读取数据。(数组写法)
    // 方法二
    ...mapActions(["incrementoOdd", "incrementTime"]),
  },
  mounted() {
    console.log(this);
  },
};
</script>
<style>
</style>
Person.vue
<template>
    <div>
        <h1>人员列表</h1>
        <input type="text" placeholder="请输入名字" v-model="name"/>
        <button @click="add">添加</button>
        <ul>
            <li v-for="person in $store.state.personList" :key="person.id">{ {person.name} }</li>
        </ul>
    </div>
</template>
<script>
import {nanoid} from 'nanoid';
export default {
    name:'Person',
    data(){
        return{
            name:''
        }
    },
    methods:{
        add(){
            const p = {id:nanoid(),name:this.name}
            this.name=''
            this.$store.dispatch('personList',p);
        }
    }
}
</script>
<style>
</style>

# src - 模块化数据共享

main.js
import Vue from 'vue'
import App from './App.vue'
// 引入 Store
import store from './store/index'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  store:store,
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div>
        <Count/>
        <hr>
        <hr>
        <hr>
        <Person/>
    </div>
</template>
<script>
import Count from "./components/Count";
import Person from "./components/Person";
export default {
  name: "App",
  components: { Count, Person},
  data() {
    return {
    };
  },
};
</script>
<style>
</style>

新建 store 文件夹创建一个 index.js、peiqi.js、qiaozhi.js

index.js
// 改文件用于创建 Vuex 中最为核心的 store
import Vue from 'vue'
// 引入 Vuex
import Vuex from 'vuex'
import peiqi from './peiqi'
import qiaozhi from './qiaozhi'
// 使用插件
Vue.use(Vuex)
// 创建并暴露 store
export default new Vuex.Store({
    // 开启模块化分类
    modules: {
        countAbout: peiqi,
        personAbout: qiaozhi
    }
})
peiqi.js
// 求和功能相关的配置
export default {
    namespaced: true,// 开启命名空间
    actions: {    // 当前求和为奇数再加
        incrementoOdd(context, value) {
            if (context.state.sum % 2) {
                context.commit('increment', value)
            }
        },
        // 等等再加
        incrementTime(context, value) {
            setTimeout(() => {
                context.commit('increment', value)
            }, 500);
        },
    },
    mutations: { // 加法
        //3. 创建函数 接收 actions 数据
        increment(state, value) {
            // 修改数据
            state.sum += value
            console.log('mutations被调用了', value)
        },
        // 减法
        decrement(state, value) {
            state.sum -= value
        },
    },
    state: {// 当前的和
        sum: 0,
        school: '尚硅谷',
        subject: 'JAVA',
    },
    getters: {
        bigSum(state) {
            return state.sum * 10;
        }
    },
}
qiaozhi.js
// 人员管理功能相关的配置
import axios from 'axios';
import { nanoid } from 'nanoid';
export default {
    namespaced: true,// 开启命名空间
    actions: {
        personList(context, value) {
            console.log('#######', value, this)
            if (value.name.indexOf('雨') === 0) {
                context.commit('personList', value)
            } else {
                alert('第一个字必须是雨')
            }
        },
        // 发送 AJAX 请求
        addServer(context) {
            axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(response => {
                context.commit('personList', { id: nanoid(), name: response.data })
            }, err => {
                alert('获取数据失败了!'+err.message)
            })
        }
    },
    mutations: {
        personList(state, value) {
            console.log('personList', value)
            state.personList.unshift(value)
        }
    },
    state: {
        personList: [
            { id: '01', name: '雨' }
        ],
    },
    getters: {
        firstPersonName(state) {
            return state.personList[0].name
        }
    },
}
Count.vue
<template>
    <div>
        <h1>当前求和为:{ { sum } }</h1>
        <h1>
            <ul>
                姓名:
                <li v-for="personLists in personList" :key="personLists.id">
                    { { personLists.name } }
                </li>
            </ul>
            <h1 style="color: red">下方组件总人数:{ { personList.length } }</h1>
        </h1>
        <h1>放大十倍的和:{ { bigSum } }</h1>
        <h1>{ { school } }--{ { subject } }</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(n)">+</button>
        <button @click="decrement(n)">-</button>
        <button @click="incrementoOdd(n)">当前求和为奇数再加</button>
        <button @click="incrementTime(n)">等一等再加</button>
    </div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
export default {
  name: "Count",
  data() {
    return {
      n: 1,
    };
  },
  computed: {
    // 借助 mapState 生成计算属性,从 state 中读取数据。(对象写法)
    // 方式一
    //...mapState({sum:'sum',subject:'subject',school:'school'})
    // 借助 mapState 生成计算属性,从 state 中读取数据。(数组写法)
    // 方式二
    ...mapState('countAbout',["sum", "subject", "school"]),
    ...mapState('personAbout',["personList"]),
    /* ********************************************************************* */
    // 借助 mapGetters 生成计算属性,从 getters 中读取数据。(对象写法)
    // 方式一
    //...mapGetters({ bigSum: "bigSum" }),
    // 借助 mapGetters 生成计算属性,从 getters 中读取数据。(数组写法)
    // 方式二
    ...mapGetters('countAbout',["bigSum"]),
  },
  methods: {
    // 借助 mapMutations 生成对应的方法,从 mutations 中读取数据。(对象写法)
    // 方式一
    //...mapMutations({ increment: "increment", decrement: "decrement" }),
    // 借助 mapMutations 生成对应的方法,从 mutations 中读取数据。(对象写法)
    // 方式二
    ...mapMutations('countAbout',["increment", "decrement"]),
    /* ********************************************************************** */
    // 借助 mapActions 生成对应的方法,从 actions 中读取数据。(对象写法)
    // 方式一
    //...mapActions({incrementodd:'incrementoOdd',incrementwait:'incrementTime'})
    // 借助 mapActions 生成对应的方法,从 actions 中读取数据。(数组写法)
    // 方法二
    ...mapActions('countAbout',["incrementoOdd", "incrementTime"]),
  },
  mounted() {
    console.log(this);
  },
};
</script>
<style>
</style>
Person.vue
<template>
    <div>
        <h1>人员列表</h1>
        <h2>列表中的第一个人的名字是:{ {firstPersonName} }</h2>
        <input type="text" placeholder="请输入名字" v-model="name"/>
        <button @click="add">添加</button>
        <button @click="addserver">发送axios请求</button>
        <ul>
            <li v-for="person in $store.state.personAbout.personList" :key="person.id">{ {person.name} }</li>
        </ul>
    </div>
</template>
<script>
import {nanoid} from 'nanoid';
export default {
    name:'Person',
    data(){
        return{
            name:''
        }
    },
    computed:{
        firstPersonName(){
            return this.$store.getters['personAbout/firstPersonName']
        }
    },
    methods:{
        add(){
            console.log(this)
            const p = {id:nanoid(),name:this.name}
            this.name=''
            this.$store.dispatch('personAbout/personList',p);
        },
        addserver(){
            this.$store.dispatch('personAbout/addServer')
        }
    }
}
</script>
<style>
</style>

# src - 路由基本使用

路由介绍及使用

# 案例代码

main.js
import Vue from 'vue'
import App from './App.vue'
// 引入 Vue 路由
import VueRouter from 'vue-router'
// 引入路由器
import router from './router/index'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
  render: h => h(App),
  router: router,
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div>
        <div class="row">
            <Banner/>
        </div>
        <div class="row">
            <div class="col-xs-2 col-xs-offset-2">
                <div class="list-group">
                    <!--  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 Banner from './components/Banner.vue'
export default {
  name: "App",
  components:{
    Banner
  }
};
</script>
<style>
</style>

新建 router 放置路由组件配置

index.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件啊
import Aboout from '../pages/About'
import Home from '../pages/Home'
// 创建一个路由器
export default new VueRouter({
    routes: [
        {
            path: '/about',
            component: Aboout
        },
        {
            path: '/home',
            component: Home
        },
    ]
});

新建 pages 文件夹

About.vue
<template>
    <h2>我是About的内容</h2>
</template>
<script>
export default {
    name:'About'
}
</script>
<style>
</style>
Home.vue
<template>
    <h2>我是Home的内容</h2>
</template>
<script>
export default {
    name:'Home'
}
</script>
<style>
</style>

下面放在 components 文件夹下

Banner.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:'Banner'
};
</script>
<style>
</style>

# src - 二级路由

main.js
import Vue from 'vue'
import App from './App.vue'
// 引入 Vue 路由
import VueRouter from 'vue-router'
// 引入路由器
import router from './router/index'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
  render: h => h(App),
  router: router,
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div>
        <div class="row">
            <Banner/>
        </div>
        <div class="row">
            <div class="col-xs-2 col-xs-offset-2">
                <div class="list-group">
                    <!--  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 Banner from "./components/Banner.vue";
export default {
  name: "App",
  components: {
    Banner,
  },
};
</script>
<style>
</style>

新建 router 文件夹

index.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件啊
import Aboout from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
// 创建一个路由器
export default new VueRouter({
    // 一级路由配置
    routes: [
        {
            path: '/about',
            component: Aboout
        },
        {
            path: '/home',
            component: Home,
            children: [
                // 耳机路由配置
                {
                    path: 'news',
                    component: News,
                },
                {
                    path: 'message',
                    component: Message,
                }
            ]
        },
    ]
});

新建 pages 文件夹

About.vue
<template>
    <h2>我是About的内容</h2>
</template>
<script>
export default {
    name:'About'
}
</script>
<style>
</style>
Home.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>
            <router-view></router-view>
        </div>
    </div>
</template>
<script>
export default {
  name: "Home",
};
</script>
<style>
</style>
Message.vue
<template>
    <div>
        <ul>
            <li><a href="/message1">message001</a>&nbsp;&nbsp;</li>
            <li><a href="/message2">message002</a>&nbsp;&nbsp;</li>
            <li><a href="/message/3">message003</a>&nbsp;&nbsp;</li>
        </ul>
    </div>
</template>
<script>
export default {
  name: "Message",
};
</script>
<style>
</style>
News.vue
<template>
    <ul>
        <li>news001</li>
        <li>news002</li>
        <li>news003</li>
    </ul>
</template>
<script>
export default {
  name: "News",
};
</script>
<style>
</style>

下面的放在 components

Banner.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:'Banner'
};
</script>
<style>
</style>

# src - 路由的 query 参数和三级路由

main.js
import Vue from 'vue'
import App from './App.vue'
// 引入 Vue 路由
import VueRouter from 'vue-router'
// 引入路由器
import router from './router/index'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
  render: h => h(App),
  router: router,
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div>
        <div class="row">
            <Banner/>
        </div>
        <div class="row">
            <div class="col-xs-2 col-xs-offset-2">
                <div class="list-group">
                    <!--  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 Banner from "./components/Banner.vue";
export default {
  name: "App",
  components: {
    Banner,
  },
};
</script>
<style>
</style>

新建 router 文件夹

index.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件啊
import Aboout from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'
// 创建一个路由器
export default new VueRouter({
    // 一级路由配置
    routes: [
        {
            path: '/about',
            component: Aboout
        },
        {
            path: '/home',
            component: Home,
            children: [
                // 耳机路由配置
                {
                    path: 'news',
                    component: News,
                },
                {
                    path: 'message',
                    component: Message,
                    // 三级路由配置
                    children:[
                        {
                            path: 'detail',
                            component:Detail
                        }
                    ]
                }
            ]
        },
    ]
});

新建 pages 文件夹

About.vue
<template>
    <h2>我是About的内容</h2>
</template>
<script>
export default {
    name:'About'
}
</script>
<style>
</style>
Detail.vue
<template>
    <ul>
        <li>消息编号:{ {$route.query.id} }</li>
        <li>消息标题:{ {$route.query.title} }</li>
    </ul>
</template>
<script>
export default {
    name:'Detail',
    mounted(){
        console.log('detail',this)
    }
}
</script>
<style>
</style>
Home.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>
            <router-view></router-view>
        </div>
    </div>
</template>
<script>
export default {
  name: "Home",
};
</script>
<style>
</style>
Message.vue
<template>
    <div>
        <ul>
            <li v-for="me in message" :key="me.id">
                <!-- 跳转路由并携带 query 参数,to 的字符串写法 -->
                <!--  <router-link
                  :to="`/home/message/detail?id=${me.id}&title=${me.title}`"
                  >{ { me.title } }</router-link
                > -->
                <!-- 跳转路由并携带 query 参数,to 的对象写法 -->
                <router-link
                        :to="{
            path: '/home/message/detail',
            query: { id: me.id, title: me.title },
          }"
                >{ { me.title } }
                </router-link
                >
            </li>
        </ul>
        <hr/>
        <router-view></router-view>
    </div>
</template>
<script>
export default {
  name: "Message",
  data() {
    return {
      message: [
        { id: "001", title: "你好啊1" },
        { id: "002", title: "你好啊2" },
        { id: "003", title: "你好啊3" },
      ],
    };
  },
};
</script>
<style>
</style>
News.vue
<template>
    <ul>
        <li>news001</li>
        <li>news002</li>
        <li>news003</li>
    </ul>
</template>
<script>
export default {
  name: "News",
};
</script>
<style>
</style>

下面放在 components 文件夹下

Banner.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:'Banner'
};
</script>
<style>
</style>

# src - 从 query 中读取数据简化版

mian.js
import Vue from 'vue'
import App from './App.vue'
// 引入 Vue 路由
import VueRouter from 'vue-router'
// 引入路由器
import router from './router/index'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
  render: h => h(App),
  router: router,
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div>
        <div class="row">
            <Banner/>
        </div>
        <div class="row">
            <div class="col-xs-2 col-xs-offset-2">
                <div class="list-group">
                    <!--  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 Banner from "./components/Banner.vue";
export default {
  name: "App",
  components: {
    Banner,
  },
};
</script>
<style>
</style>

新建 router 文件夹

index.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件啊
import Aboout from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'
// 创建一个路由器
export default new VueRouter({
    // 一级路由配置
    routes: [
        {
            name: 'guanyu',
            path: '/about',
            component: Aboout
        },
        {
            path: '/home',
            component: Home,
            children: [
                // 耳机路由配置
                {
                    path: 'news',
                    component: News,
                },
                {
                    path: 'message',
                    component: Message,
                    // 三级路由配置
                    children:[
                        {
                            name: 'xiangqing',
                            path: 'detail',
                            component:Detail,
                            
                            // 第三种写法 常用 值为函数
                            props($route){
                                return {id:$route.query.id,title:$route.query.title}
                            }   
                           /*  props (query){// 解构赋值
                                return {id:query.id,title:query.title}
                            }   */ 
                           /*  props ({query:{id,title} }){// 连续解构赋值
                                return {id,title}
                            }   */
                        }
                    ]
                }
            ]
        },
    ]
});

新建 pages 文件夹

About.vue
<template>
    <h2>我是About的内容</h2>
</template>
<script>
export default {
    name:'About'
}
</script>
<style>
</style>
Detail.vue
<template>
    <ul>
        <li>消息编号:{ {id} }</li>
        <li>消息标题:{ {title} }</li>
    </ul>
</template>
<script>
export default {
    name:'Detail',
    props:['id', 'title'],
    mounted(){
        console.log('detail',this)
    }
}
</script>
<style>
</style>
Home.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>
            <router-view></router-view>
        </div>
    </div>
</template>
<script>
export default {
  name: "Home",
};
</script>
<style>
</style>
Message.vue
<template>
    <div>
        <ul>
            <li v-for="me in message" :key="me.id">
                <router-link
                        :to="{
            path: '/home/message/detail',
            query: { id: me.id, title: me.title },
          }"
                >{ { me.title } }
                </router-link
                >
                <!-- 跳转路由并携带 params 参数,to 的字符串写法 -->
                <!-- <router-link
                  :to="`/home/message/detail/${me.id}/${me.title}`"
                  >{ { me.title } }</router-link
                > -->
                <!-- 跳转路由并携带 params 参数,to 的对象写法,不能使用 Path 路径 -->
                <!--  <router-link
                  :to="{
                      name:'xiangqing',
                      params:{
                          id:me.id,title:me.title
                      }
                  }"
                  >{ { me.title } }</router-link
                > -->
            </li>
        </ul>
        <hr/>
        <router-view></router-view>
    </div>
</template>
<script>
export default {
  name: "Message",
  data() {
    return {
      message: [
        { id: "001", title: "你好啊1" },
        { id: "002", title: "你好啊2" },
        { id: "003", title: "你好啊3" },
      ],
    };
  },
};
</script>
<style>
</style>
News.vue
<template>
    <ul>
        <li>news001</li>
        <li>news002</li>
        <li>news003</li>
    </ul>
</template>
<script>
export default {
  name: "News",
};
</script>
<style>
</style>

下面的放在 components 文件夹内

Banner.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:'Banner'
};
</script>
<style>
</style>

# src - 路由缓存

main.js
import Vue from 'vue'
import App from './App.vue'
// 引入 Vue 路由
import VueRouter from 'vue-router'
// 引入路由器
import router from './router/index'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
  render: h => h(App),
  router: router,
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div>
        <div class="row">
            <Banner/>
        </div>
        <div class="row">
            <div class="col-xs-2 col-xs-offset-2">
                <div class="list-group">
                    <!--  Vue 中借助 router-link 标签实现路由的切换 -->
                    <router-link :replace="true" class="list-group-item" active-class="active" to="/about"
                    >About
                    </router-link
                    >
                    <router-link :replace="true" 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 Banner from "./components/Banner.vue";
export default {
  name: "App",
  components: {
    Banner,
  },
};
</script>
<style>
</style>

新建 router 文件夹

index.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件啊
import Aboout from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'
// 创建一个路由器
export default new VueRouter({
    // 一级路由配置
    routes: [
        {
            name: 'guanyu',
            path: '/about',
            component: Aboout
        },
        {
            path: '/home',
            component: Home,
            children: [
                // 耳机路由配置
                {
                    path: 'news',
                    component: News,
                },
                {
                    path: 'message',
                    component: Message,
                    // 三级路由配置
                    children:[
                        {
                            name: 'xiangqing',
                            path: 'detail',
                            component:Detail,
                            
                            // 第三种写法 常用 值为函数
                            props($route){
                                return {id:$route.query.id,title:$route.query.title}
                            }   
                           /*  props (query){// 解构赋值
                                return {id:query.id,title:query.title}
                            }   */ 
                           /*  props ({query:{id,title} }){// 连续解构赋值
                                return {id,title}
                            }   */
                        }
                    ]
                }
            ]
        },
    ]
});

新建 pages 文件夹

About.vue
<template>
    <h2>我是About的内容</h2>
</template>
<script>
export default {
    name:'About'
}
</script>
<style>
</style>
Detail.vue
<template>
    <ul>
        <li>消息编号:{ {id} }</li>
        <li>消息标题:{ {title} }</li>
    </ul>
</template>
<script>
export default {
    name:'Detail',
    props:['id', 'title'],
    mounted(){
        console.log('detail',this)
    }
}
</script>
<style>
</style>
Home.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>
            <!-- 路由缓存标签 keep-alive include: 指定需要缓存的组件名字 或者写成数组 -->
            <!--  <keep-alive include='News'>
              <router-view></router-view>
            </keep-alive> -->
            <!-- ------------------------------------------------------------------------ -->
            <!-- 多组件路由缓存 -->
            <keep-alive :include="['News', 'Message']">
                <router-view></router-view>
            </keep-alive>
        </div>
    </div>
</template>
<script>
export default {
  name: "Home",
};
</script>
<style>
</style>
Message.vue
<template>
    <div>
        <ul>
            <li v-for="me in message" :key="me.id">
                <router-link
                        :to="{
            path: '/home/message/detail',
            query: { id: me.id, title: me.title },
          }"
                >{ { me.title } }
                </router-link
                >
                <button @click='pushShow(me)'>push查看</button>
                <button @click='replaceShow(me)'>replace查看</button>
            </li>
        </ul>
        <hr/>
        <router-view></router-view>
    </div>
</template>
<script>
export default {
  name: "Message",
   beforeDestroy(){
    console.log('Message即将被销毁')
  },
  data() {
    return {
      message: [
        { id: "001", title: "你好啊1" },
        { id: "002", title: "你好啊2" },
        { id: "003", title: "你好啊3" },
      ],
    };
  },
  methods:{
    pushShow(me){
      this.$router.push({
         path: '/home/message/detail',
            query: { id: me.id, title: me.title },
      })
    },
    replaceShow(me){
       this.$router.replace({
         path: '/home/message/detail',
            query: { id: me.id, title: me.title },
      })
    }
  },
 
};
</script>
<style>
</style>
News.vue
<template>
    <ul>
        <li>news001<input type="text"></li>
        <li>news002<input type="text"></li>
        <li>news003<input type="text"></li>
    </ul>
</template>
<script>
export default {
  name: "News",
   beforeDestroy(){
    console.log('News即将被销毁')
  },
};
</script>
<style>
</style>

下面的放在 components 文件夹内

Banner.vue
<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>
        </div>
    </div>
</template>
<script>
export default {
    name:'Banner',
    methods:{
      back(){
        this.$router.back();
      },
      forward(){
        this.$router.forward();
      }
    }
};
</script>
<style>
</style>

# src - 路由生命周期钩子

main.js
import Vue from 'vue'
import App from './App.vue'
// 引入 Vue 路由
import VueRouter from 'vue-router'
// 引入路由器
import router from './router/index'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
  render: h => h(App),
  router: router,
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div>
        <div class="row">
            <Banner/>
        </div>
        <div class="row">
            <div class="col-xs-2 col-xs-offset-2">
                <div class="list-group">
                    <!--  Vue 中借助 router-link 标签实现路由的切换 -->
                    <router-link :replace="true" class="list-group-item" active-class="active" to="/about"
                    >About
                    </router-link
                    >
                    <router-link :replace="true" 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 Banner from "./components/Banner.vue";
export default {
  name: "App",
  components: {
    Banner,
  },
};
</script>
<style>
</style>

新建 router 文件夹

index.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件啊
import Aboout from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'
// 创建一个路由器
export default new VueRouter({
    // 一级路由配置
    routes: [
        {
            name: 'guanyu',
            path: '/about',
            component: Aboout
        },
        {
            path: '/home',
            component: Home,
            children: [
                // 耳机路由配置
                {
                    path: 'news',
                    component: News,
                },
                {
                    path: 'message',
                    component: Message,
                    // 三级路由配置
                    children:[
                        {
                            name: 'xiangqing',
                            path: 'detail',
                            component:Detail,
                            
                            // 第三种写法 常用 值为函数
                            props($route){
                                return {id:$route.query.id,title:$route.query.title}
                            }   
                           /*  props (query){// 解构赋值
                                return {id:query.id,title:query.title}
                            }   */ 
                           /*  props ({query:{id,title} }){// 连续解构赋值
                                return {id,title}
                            }   */
                        }
                    ]
                }
            ]
        },
    ]
});

新建 pages 文件夹

About.vue
<template>
    <h2>我是About的内容</h2>
</template>
<script>
export default {
    name:'About'
}
</script>
<style>
</style>
Detail.vue
<template>
    <ul>
        <li>消息编号:{ {id} }</li>
        <li>消息标题:{ {title} }</li>
    </ul>
</template>
<script>
export default {
    name:'Detail',
    props:['id', 'title'],
    mounted(){
        console.log('detail',this)
    }
}
</script>
<style>
</style>
Home.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>
            <!-- 路由缓存标签 keep-alive include: 指定需要缓存的组件名字 或者写成数组 -->
            <!--  <keep-alive include='News'>
              <router-view></router-view>
            </keep-alive> -->
            <!-- ------------------------------------------------------------------------ -->
            <!-- 多组件路由缓存 -->
            <keep-alive :include="['News', 'Message']">
                <router-view></router-view>
            </keep-alive>
        </div>
    </div>
</template>
<script>
export default {
  name: "Home",
};
</script>
<style>
</style>
Message.vue
<template>
    <div>
        <ul>
            <li v-for="me in message" :key="me.id">
                <router-link
                        :to="{
            path: '/home/message/detail',
            query: { id: me.id, title: me.title },
          }"
                >{ { me.title } }
                </router-link
                >
                <button @click='pushShow(me)'>push查看</button>
                <button @click='replaceShow(me)'>replace查看</button>
            </li>
        </ul>
        <hr/>
        <router-view></router-view>
    </div>
</template>
<script>
export default {
  name: "Message",
   beforeDestroy(){
    console.log('Message即将被销毁')
  },
  data() {
    return {
      message: [
        { id: "001", title: "你好啊1" },
        { id: "002", title: "你好啊2" },
        { id: "003", title: "你好啊3" },
      ],
    };
  },
  methods:{
    pushShow(me){
      this.$router.push({
         path: '/home/message/detail',
            query: { id: me.id, title: me.title },
      })
    },
    replaceShow(me){
       this.$router.replace({
         path: '/home/message/detail',
            query: { id: me.id, title: me.title },
      })
    }
  },
 
};
</script>
<style>
</style>
News.vue
<template>
    <ul>
        <li :style="{ opacity }">欢迎学习Vue</li>
        <li>news001<input type="text"/></li>
        <li>news002<input type="text"/></li>
        <li>news003<input type="text"/></li>
    </ul>
</template>
<script>
export default {
  name: "News",
  data() {
    return {
      opacity: 1,
    };
  },
  beforeDestroy() {
    console.log("News即将被销毁");
  },
  activated() {
    console.log("News被激活了");
    this.timer = setInterval(() => {
      console.log("@");
      this.opacity -= 0.01;
      if (this.opacity <= 0) this.opacity = 1;
    }, 16);
  },
  deactivated() {
    console.log("News失活了");
    clearInterval(this.timer);
  },
};
</script>
<style>
</style>

下面放在 components 文件夹内

Banner.vue
<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>
        </div>
    </div>
</template>
<script>
export default {
    name:'Banner',
    methods:{
      back(){
        this.$router.back();
      },
      forward(){
        this.$router.forward();
      }
    }
};
</script>
<style>
</style>

# src_两个新的生命周期钩子

main.js
// 引入 Vue
import Vue from 'vue'
// 引入 App
import App from './App.vue'
// 引入 VueRouter
import VueRouter from 'vue-router'
// 引入路由器
import router from './router'
// 关闭 Vue 的生产提示
Vue.config.productionTip = false
// 应用插件
Vue.use(VueRouter)
// 创建 vm
new Vue({
	el:'#app',
	render: h => h(App),
	router:router
})
<template>
  <div>
    <div class="row">
      <Banner/>
    </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 Banner from './components/Banner'
	export default {
		name:'App',
		components:{Banner}
	}
</script>

新建 router 文件夹

idnex.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'
// 创建并暴露一个路由器
export default new VueRouter({
	routes:[
		{
			name:'guanyu',
			path:'/about',
			component:About
		},
		{
			path:'/home',
			component:Home,
			children:[
				{
					path:'news',
					component:News,
				},
				{
					path:'message',
					component:Message,
					children:[
						{
							name:'xiangqing',
							path:'detail',
							component:Detail,
							//props 的第一种写法,值为对象,该对象中的所有 key-value 都会以 props 的形式传给 Detail 组件。
							// props:{a:1,b:'hello'}
							//props 的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有 params 参数,以 props 的形式传给 Detail 组件。
							// props:true
							//props 的第三种写法,值为函数
							props($route){
								return {
									id:$route.query.id,
									title:$route.query.title,
									a:1,
									b:'hello'
								}
							}
						}
					]
				}
			]
		}
	]
})

新建 pages 文件夹

About.vue
<template>
	<h2>我是About的内容</h2>
</template>
<script>
	export default {
		name:'About',
		/* beforeDestroy () {
			console.log ('About 组件即将被销毁了 ')
		},*/
		/* mounted () {
			console.log ('About 组件挂载完毕了 ',this)
			window.aboutRoute = this.$route
			window.aboutRouter = this.$router
		},  */
	}
</script>
Detail.vue
<template>
	<ul>
		<li>消息编号:{ {id} }</li>
		<li>消息标题:{ {title} }</li>
	</ul>
</template>
<script>
	export default {
		name:'Detail',
		props:['id','title'],
		computed: {
			// id(){
			// 	return this.$route.query.id
			// },
			// title(){
			// 	return this.$route.query.title
			// },
		},
		mounted() {
			// console.log(this.$route)
		},
	}
</script>
Home.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>
			<keep-alive include="News">
				<router-view></router-view>
			</keep-alive>
		</div>
	</div>
</template>
<script>
	export default {
		name:'Home',
		/* beforeDestroy () {
			console.log ('Home 组件即将被销毁了 ')
		}, */
		/* mounted () {
			console.log ('Home 组件挂载完毕了 ',this)
			window.homeRoute = this.$route
			window.homeRouter = this.$router
		},  */
	}
</script>
Message.vue
<template>
	<div>
		<ul>
			<li v-for="m in messageList" :key="m.id">
				<!-- 跳转路由并携带 params 参数,to 的字符串写法 -->
				<!-- <router-link :to="`/home/message/detail/${m.id}/${m.title}`">{ {m.title} }</router-link>&nbsp;&nbsp; -->
				<!-- 跳转路由并携带 params 参数,to 的对象写法 -->
				<router-link :to="{
					name:'xiangqing',
					query:{
						id:m.id,
						title:m.title
					}
				}">
					{ {m.title} }
				</router-link>
				<button @click="pushShow(m)">push查看</button>
				<button @click="replaceShow(m)">replace查看</button>
			</li>
		</ul>
		<hr>
		<router-view></router-view>
	</div>
</template>
<script>
	export default {
		name:'Message',
		data() {
			return {
				messageList:[
					{id:'001',title:'消息001'},
					{id:'002',title:'消息002'},
					{id:'003',title:'消息003'}
				]
			}
		},
		methods: {
			pushShow(m){
				this.$router.push({
					name:'xiangqing',
					query:{
						id:m.id,
						title:m.title
					}
				})
			},
			replaceShow(m){
				this.$router.replace({
					name:'xiangqing',
					query:{
						id:m.id,
						title:m.title
					}
				})
			}
		},
		beforeDestroy() {
			//console.log ('Message 组件即将被销毁了 ')
		},
	}
</script>
News.vue
<template>
	<ul>
		<li :style="{opacity}">欢迎学习Vue</li>
		<li>news001 <input type="text"></li>
		<li>news002 <input type="text"></li>
		<li>news003 <input type="text"></li>
	</ul>
</template>
<script>
	export default {
		name:'News',
		data() {
			return {
				opacity:1
			}
		},
		/* beforeDestroy () {
			console.log ('News 组件即将被销毁了 ')
			clearInterval (this.timer)
		}, */
		/* mounted(){
			this.timer = setInterval(() => {
				console.log('@')
				this.opacity -= 0.01
				if(this.opacity <= 0) this.opacity = 1
			},16)
		}, */
		activated() {
			console.log('News组件被激活了')
			this.timer = setInterval(() => {
				console.log('@')
				this.opacity -= 0.01
				if(this.opacity <= 0) this.opacity = 1
			},16)
		},
		deactivated() {
			console.log('News组件失活了')
			clearInterval(this.timer)
		},
	}
</script>

下面放在 components 文件夹内

Banner.vue
<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">测试一下go</button>
		</div>
	</div>
</template>
<script>
	export default {
		name:'Banner',
		methods: {
			back(){
				this.$router.back()
				// console.log(this.$router)
			},
			forward(){
				this.$router.forward()
			},
			test(){
				this.$router.go(3)
			}
		},
	}
</script>

# src - 全局路由守卫

main.js
import Vue from 'vue'
import App from './App.vue'
// 引入 Vue 路由
import VueRouter from 'vue-router'
// 引入路由器
import router from './router/index'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
  render: h => h(App),
  router: router,
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div>
        <div class="row">
            <Banner/>
        </div>
        <div class="row">
            <div class="col-xs-2 col-xs-offset-2">
                <div class="list-group">
                    <!--  Vue 中借助 router-link 标签实现路由的切换 -->
                    <router-link :replace="true" class="list-group-item" active-class="active" to="/about"
                    >About
                    </router-link
                    >
                    <router-link :replace="true" 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 Banner from "./components/Banner.vue";
export default {
  name: "App",
  components: {
    Banner,
  },
};
</script>
<style>
</style>

新建 router 文件夹

index.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件啊
import Aboout from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'
// 创建一个路由器
const router = new VueRouter({
    // 一级路由配置
    routes: [
        {
            name: 'guanyu',
            path: '/about',
            component: Aboout,
            meta:{title:'关于'},
        },
        {
            name: 'zhouye',
            path: '/home',
            component: Home,
            meta:{title:'主页'},
            children: [
                // 耳机路由配置
                {
                    name:'xinwen',
                    path: 'news',
                    component: News,
                    meta:{isAuth: true,title:'新闻'}
                },
                {
                    name: 'xiaoxi',
                    path: 'message',
                    component: Message,
                    meta:{isAuth: true,title:'消息'},
                    // 三级路由配置
                    children: [
                        {
                            name: 'xiangqing',
                            path: 'detail',
                            component: Detail,
                            meta:{isAuth: true,title:'详情'},
                            // 第三种写法 常用 值为函数
                            props($route) {
                                return { id: $route.query.id, title: $route.query.title }
                            }
                            /*  props (query){// 解构赋值
                                 return {id:query.id,title:query.title}
                             }   */
                            /*  props ({query:{id,title} }){// 连续解构赋值
                                 return {id,title}
                             }   */
                        }
                    ]
                }
            ]
        },
    ]
});
// 适用于判断是否登陆
// 全局前置路由守卫 —— 初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to, from, next) => {
    //console.log(to, from, next);
    console.log(to.meta.isAuth);
    if (to.meta.isAuth) {
        //localStorage.getItem ():获取当前 cooker 里面的 school
        if (localStorage.getItem('school') === 'rain') {
            next()// 放行
        }else{
            alert('cooke信息不对')
        }
    }else{
        next()
    }
})
// 全局后置路由守卫 —— 初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to, from)=>{
    console.log('后置路由守卫',to,from)
    document.title = to.meta.title || '硅谷系统'
})
export default router

新建 pages 文件夹

About.vue
<template>
    <h2>我是About的内容</h2>
</template>
<script>
export default {
    name:'About'
}
</script>
<style>
</style>
Detail.vue
<template>
    <ul>
        <li>消息编号:{ {id} }</li>
        <li>消息标题:{ {title} }</li>
    </ul>
</template>
<script>
export default {
    name:'Detail',
    props:['id', 'title'],
    mounted(){
        console.log('detail',this)
    }
}
</script>
<style>
</style>
Home.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>
            <!-- 路由缓存标签 keep-alive include: 指定需要缓存的组件名字 或者写成数组 -->
            <!--  <keep-alive include='News'>
              <router-view></router-view>
            </keep-alive> -->
            <!-- ------------------------------------------------------------------------ -->
            <!-- 多组件路由缓存 -->
            <keep-alive :include="['News', 'Message']">
                <router-view></router-view>
            </keep-alive>
        </div>
    </div>
</template>
<script>
export default {
  name: "Home",
};
</script>
<style>
</style>
Message.vue
<template>
    <div>
        <ul>
            <li v-for="me in message" :key="me.id">
                <router-link
                        :to="{
            path: '/home/message/detail',
            query: { id: me.id, title: me.title },
          }"
                >{ { me.title } }
                </router-link
                >
                <button @click='pushShow(me)'>push查看</button>
                <button @click='replaceShow(me)'>replace查看</button>
            </li>
        </ul>
        <hr/>
        <router-view></router-view>
    </div>
</template>
<script>
export default {
  name: "Message",
   beforeDestroy(){
    console.log('Message即将被销毁')
  },
  data() {
    return {
      message: [
        { id: "001", title: "你好啊1" },
        { id: "002", title: "你好啊2" },
        { id: "003", title: "你好啊3" },
      ],
    };
  },
  methods:{
    pushShow(me){
      this.$router.push({
         path: '/home/message/detail',
            query: { id: me.id, title: me.title },
      })
    },
    replaceShow(me){
       this.$router.replace({
         path: '/home/message/detail',
            query: { id: me.id, title: me.title },
      })
    }
  },
 
};
</script>
<style>
</style>
News.vue
<template>
    <ul>
        <li :style="{ opacity }">欢迎学习Vue</li>
        <li>news001<input type="text"/></li>
        <li>news002<input type="text"/></li>
        <li>news003<input type="text"/></li>
    </ul>
</template>
<script>
export default {
  name: "News",
  data() {
    return {
      opacity: 1,
    };
  },
  beforeDestroy() {
    console.log("News即将被销毁");
  },
  activated() {
    console.log("News被激活了");
    this.timer = setInterval(() => {
      console.log("@");
      this.opacity -= 0.01;
      if (this.opacity <= 0) this.opacity = 1;
    }, 16);
  },
  deactivated() {
    console.log("News失活了");
    clearInterval(this.timer);
  },
};
</script>
<style>
</style>

下面的放在 components 文件夹内

Banner.vue
<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>
        </div>
    </div>
</template>
<script>
export default {
    name:'Banner',
    methods:{
      back(){
        this.$router.back();
      },
      forward(){
        this.$router.forward();
      }
    }
};
</script>
<style>
</style>

# src - 独享路由守卫

main.js
import Vue from 'vue'
import App from './App.vue'
// 引入 Vue 路由
import VueRouter from 'vue-router'
// 引入路由器
import router from './router/index'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
  render: h => h(App),
  router: router,
  beforeCreate() { //beforeCreate: 第一个生命周期钩子,所有 Vue 以及数据代理还未开始执行之前
    Vue.prototype.$bus = this // 安装全局事件总线
  },
}).$mount('#app')
App.vue
<template>
    <div>
        <div class="row">
            <Banner/>
        </div>
        <div class="row">
            <div class="col-xs-2 col-xs-offset-2">
                <div class="list-group">
                    <!--  Vue 中借助 router-link 标签实现路由的切换 -->
                    <router-link :replace="true" class="list-group-item" active-class="active" to="/about"
                    >About
                    </router-link
                    >
                    <router-link :replace="true" 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 Banner from "./components/Banner.vue";
export default {
  name: "App",
  components: {
    Banner,
  },
};
</script>
<style>
</style>

新建 router 文件

index.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件啊
import Aboout from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'
// 创建一个路由器
const router = new VueRouter({
    // 一级路由配置
    routes: [
        {
            name: 'guanyu',
            path: '/about',
            component: Aboout,
            meta:{title:'关于'},
        },
        {
            name: 'zhouye',
            path: '/home',
            component: Home,
            meta:{title:'主页'},
            children: [
                // 耳机路由配置
                {
                    name:'xinwen',
                    path: 'news',
                    component: News,
                    meta:{isAuth: true,title:'新闻'},
                    beforeEnter: (to, from, next) => {
                        if (to.meta.isAuth) {
                            //localStorage.getItem ():获取当前 cooker 里面的 school
                            if (localStorage.getItem('school') === 'rain') {
                                next()// 放行
                            }else{
                                alert('cooke信息不对')
                            }
                        }else{
                            next()
                        }
                    }
                },
                {
                    name: 'xiaoxi',
                    path: 'message',
                    component: Message,
                    meta:{isAuth: true,title:'消息'},
                    // 三级路由配置
                    children: [
                        {
                            name: 'xiangqing',
                            path: 'detail',
                            component: Detail,
                            meta:{isAuth: true,title:'详情'},
                            // 第三种写法 常用 值为函数
                            props($route) {
                                return { id: $route.query.id, title: $route.query.title }
                            }
                            /*  props (query){// 解构赋值
                                 return {id:query.id,title:query.title}
                             }   */
                            /*  props ({query:{id,title} }){// 连续解构赋值
                                 return {id,title}
                             }   */
                        }
                    ]
                }
            ]
        },
    ]
});
// 适用于判断是否登陆
// 全局前置路由守卫 —— 初始化的时候被调用、每次路由切换之前被调用
/* router.beforeEach ((to, from, next) => {
    console.log (to, from, next);
    
    if (to.meta.isAuth) {
        //localStorage.getItem ():获取当前 cooker 里面的 school
        if (localStorage.getItem ('school') === 'rain') {
            next ()// 放行
        } else {
            alert ('cooke 信息不对 ')
        }
    } else {
        next ()
    }
}) */
// 全局后置路由守卫 —— 初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to, from)=>{
    console.log('后置路由守卫',to,from)
    document.title = to.meta.title || '硅谷系统'
})
export default router

新建 pages 文件夹

About.vue
<template>
    <h2>我是About的内容</h2>
</template>
<script>
export default {
    name:'About'
}
</script>
<style>
</style>
Detail.vue
<template>
    <ul>
        <li>消息编号:{ {id} }</li>
        <li>消息标题:{ {title} }</li>
    </ul>
</template>
<script>
export default {
    name:'Detail',
    props:['id', 'title'],
    mounted(){
        console.log('detail',this)
    }
}
</script>
<style>
</style>
Home.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>
            <!-- 路由缓存标签 keep-alive include: 指定需要缓存的组件名字 或者写成数组 -->
            <!--  <keep-alive include='News'>
              <router-view></router-view>
            </keep-alive> -->
            <!-- ------------------------------------------------------------------------ -->
            <!-- 多组件路由缓存 -->
            <keep-alive :include="['News', 'Message']">
                <router-view></router-view>
            </keep-alive>
        </div>
    </div>
</template>
<script>
export default {
  name: "Home",
};
</script>
<style>
</style>
Message.vue
<template>
    <div>
        <ul>
            <li v-for="me in message" :key="me.id">
                <router-link
                        :to="{
            path: '/home/message/detail',
            query: { id: me.id, title: me.title },
          }"
                >{ { me.title } }
                </router-link
                >
                <button @click='pushShow(me)'>push查看</button>
                <button @click='replaceShow(me)'>replace查看</button>
            </li>
        </ul>
        <hr/>
        <router-view></router-view>
    </div>
</template>
<script>
export default {
  name: "Message",
   beforeDestroy(){
    console.log('Message即将被销毁')
  },
  data() {
    return {
      message: [
        { id: "001", title: "你好啊1" },
        { id: "002", title: "你好啊2" },
        { id: "003", title: "你好啊3" },
      ],
    };
  },
  methods:{
    pushShow(me){
      this.$router.push({
         path: '/home/message/detail',
            query: { id: me.id, title: me.title },
      })
    },
    replaceShow(me){
       this.$router.replace({
         path: '/home/message/detail',
            query: { id: me.id, title: me.title },
      })
    }
  },
 
};
</script>
<style>
</style>
News.vue
<template>
    <ul>
        <li :style="{ opacity }">欢迎学习Vue</li>
        <li>news001<input type="text"/></li>
        <li>news002<input type="text"/></li>
        <li>news003<input type="text"/></li>
    </ul>
</template>
<script>
export default {
  name: "News",
  data() {
    return {
      opacity: 1,
    };
  },
  beforeDestroy() {
    console.log("News即将被销毁");
  },
  activated() {
    console.log("News被激活了");
    this.timer = setInterval(() => {
      console.log("@");
      this.opacity -= 0.01;
      if (this.opacity <= 0) this.opacity = 1;
    }, 16);
  },
  deactivated() {
    console.log("News失活了");
    clearInterval(this.timer);
  },
};</script>
<style>
</style>

下面放在 components 文件夹内

Banner.vue
<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>
        </div>
    </div>
</template>
<script>
export default {
    name:'Banner',
    methods:{
      back(){
        this.$router.back();
      },
      forward(){
        this.$router.forward();
      }
    }
};
</script>
<style>
</style>

下一篇