vue基础知识大全

知识清单

Vue3.x

compositionAPI-setup

setup函数的执行时机:在beforeCreate之前,拿不到实例
setup里面可以使用this吗? 不可以,this是undefined
在methods里面可以调用setup函数

this.$options.setup();
<!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>setup</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script>
    const app = Vue.createApp({
      template: `
        <div @click="handleClick">{{age}}</div>
      `,
      methods: {
        test() {
          console.log(this.$options.setup());
        },
      },
      mounted() {
        this.test();
      },
      //setup 是在beforeCreate之前执行的
      setup(props, context) {
        const handleClick = () => {
          alert('handleClick');
        };
        //return出去的数据和方法可以直接在template里面使用
        return {
          age: 23,
          handleClick,
        };
      },
      beforeCreate() {
        console.log('beforeCreate');
      },
      created() {
        console.log('created');
      },
    });
    const vm = app.mount('#root');
  </script>
</html>
ref

ref 处理基础类型的数据
原理:通过proxy对数据进行封装,当数据变化时,触发模板等内容的更新

<!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>ref处理基础类型的数据</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script>
    const app = Vue.createApp({
      template: `
        <div>{{age}}</div>
      `,
      //使用ref来将基础类型的数据变成响应式数据,
      //proxy, 23 -> proxy({value:23}), 在template里面不用加.value,js操作需要加.value
      setup(props, context) {
        const { ref } = Vue;
        let age = ref(23);
        setTimeout(() => {
          age.value = 30;
        }, 2000);
        return { age };
      },
    });
    const vm = app.mount('#root');
  </script>
</html>
reactive

reactive 处理引用类型的数据

<!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>reactive处理引用类型的数据</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script>
    const app = Vue.createApp({
      template: `
        <div>{{arr[1]}}</div>
      `,
      //使用reactive来将引用类型的数据变成响应式数据,
      //proxy, {name:'andy'} -> proxy({name:'andy'})这样的一个响应式引用
      setup(props, context) {
        const { reactive } = Vue;
        // let obj = reactive({ name: 'andy' });
        let arr = reactive(['andy', 'ted']);
        setTimeout(() => {
          // obj.name = 'ted';
          arr[1] = 'feng';
        }, 2000);
        return { arr };
      },
    });
    const vm = app.mount('#root');
  </script>
</html>
toRefs 处理解构的情况(解构后的值也具有响应式)
<!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>toRefs</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script>
    const app = Vue.createApp({
      template: `
        <div>{{name}}</div>
      `,
      setup(props, context) {
        const { reactive, toRefs } = Vue;
        let obj = reactive({ name: 'andy' });
        setTimeout(() => {
          obj.name = 'ted';
        }, 2000);
        //直接这样解构就不具有响应式了
        // const { name } = obj;
        //使用toRefs
        const { name } = toRefs(obj);
        return { name };
      },
    });
    const vm = app.mount('#root');
  </script>
</html>

toRefs做了什么

proxy({name:'andy',age:23})

toRefs处理之后 
{
name:proxy({value:'andy}),
age:proxy({value:23})
}
setup的参数
<!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>context</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script>
    const app = Vue.createApp({
      template: `
        <child :parentName="name" :parentAge="age" @change="change"/>
      `,
      setup(props, context) {
        const { reactive, ref } = Vue;
        let name = ref('andy');
        let age = ref(23);
        const change = (num) => {
          console.log('change', num);
        };
        return { name, age, change };
      },
    });

    app.component('child', {
      template: `<div @click="handleClick">child</div>`,
      props: ['parentName'],
      setup(props, context) {
        //如果在props里面接收了,props里面就有值
        console.log('props', props);
        const { attrs, slots, emit } = context;
        //没有在props里面接收的就可以通过attrs拿到(None props)
        console.log(attrs);
        const handleClick = () => {
          //emit 和vue2里面的this.$emit功能一样
          emit('change', 666);
        };

        return { handleClick };
      },
    });
    const vm = app.mount('#root');
  </script>
</html>

watch和watchEffect的区别

watch

  • 具备一定惰性lazy,可以设置immediate:true消除惰性
  • 参数可以拿到原始值和当前值
  • 可以侦听多个数据变化,用一个侦听器承载
  • 可以取消侦听
<!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>watch</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script>
    const app = Vue.createApp({
      setup() {
        const { ref, reactive, watch, watchEffect, toRefs } = Vue;
        // const name = ref('方剑鹏');
        //监听ref数据
        // watch(name, (curName, preName) => {
        //   console.log(curName, preName);
        // });

        const person = reactive({ name: '方剑鹏', age: 23 });
        //具备一定惰性lazy,可以设置immediate:true消除惰性
        // 参数可以拿到原始值和当前值
        // 可以侦听多个数据变化,用一个侦听器承载
        //可以取消侦听
        const stop = watch(
          [() => person.name, () => person.age],
          ([curName, curAge], [preName, preAge]) => {
            console.log(curName, preName, '-----', curAge, preAge);
            setTimeout(() => {
              stop();
            }, 5000);
          },
          {
            immediate: true,
          }
        );

        const { name, age } = toRefs(person);
        return { name, age };
      },
      template: `
        <div>
          name: <input v-model="name"/>
        </div>
        <div>
          name is : {{name}}
        </div>
        <div>
          age: <input v-model="age"/>
        </div>
        <div>
          age is : {{age}}
        </div>
      `,
    });
    const vm = app.mount('#root');
  </script>
</html>

watchEffect

  • 立即执行,没有惰性
  • 不需要传递要侦听的内容,会自动分析代码依赖,只需要传递一个回调函数
  • 不能获取数据之前的值,只能获取最新的值
  • 可以取消侦听
<!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>watch</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script>
    const app = Vue.createApp({
      setup() {
        const { ref, reactive, watch, watchEffect, toRefs } = Vue;

        //立即执行,没有惰性
        //不需要传递要侦听的内容,会自动分析代码依赖,只需要传递一个回调函数
        //不能获取数据之前的值,只能获取最新的值
        //可以取消侦听
        const stop = watchEffect(() => {
          console.log(person.name);
          console.log(person.age);
          setTimeout(() => {
            stop();
          }, 5000);
        });

        const { name, age } = toRefs(person);
        return { name, age };
      },
      template: `
        <div>
          name: <input v-model="name"/>
        </div>
        <div>
          name is : {{name}}
        </div>
        <div>
          age: <input v-model="age"/>
        </div>
        <div>
          age is : {{age}}
        </div>
      `,
    });
    const vm = app.mount('#root');
  </script>
</html>

Vue2

初识vue

1.什么是Vue?
Vue.js 是一套构建用户界面的框架,它不仅易于上手,还可以与其它第三方库整合(Swiper、IScroll、…)。

2.框架和库的区别?
框架:是一套完整的解决方案;对项目的侵入性较大,项目如果需要更换框架,则需要重构整个项目。
库(插件):提供某一个小功能,对项目的侵入性较小,如果某个库无法完成某些需求,可以很容易切换到其它库实现需求。
例如: 从jQuery 切换到 Zepto, 无缝切换
从IScroll切换到ScrollMagic, 只需要将用到IScroll的代码替换成ScrollMagic代码即可

3.为什么要学习框架?
提升开发效率:在企业中,时间就是效率,效率就是金钱;
前端提高开发效率的发展历程:原生JS -> jQuery之类的类库 -> 前端模板引擎 -> Vue / React / Angular

4.框架有很多, 为什么要先学Vue
Vue、Angular、React一起,被称之为前端三大主流框架!
但是Angular、React是老外编写的, 所以所有的资料都是英文的
而Vue是国人编写的, 所以所有的资料都是中文的, 并且Vue中整合了Angular、React中的众多优点
所以为了降低我们的学习难度, 我们先学Vue, 学完之后再学习Angular和React

5.使用Vue有哪些优势?
5.1Vue的核心概念之一:
通过数据驱动界面更新, 无需操作DOM来更新界面
使用Vue我们只需要关心如何获取数据, 如何处理数据, 如何编写业务逻辑代码,
我们只需要将处理好的数据交给Vue, Vue就会自动将数据渲染到模板中(界面上)
5.2Vue的核心概念之二:
组件化开发,我们可以将网页拆分成一个个独立的组件来编写
将来再通过封装好的组件拼接成一个完整的网页
https://cn.vuejs.org/images/components.png

vue基本模版

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>02-Vue基本模板</title>
    <!--1.下载导入Vue.js-->
    <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
    <p>{{ name }}</p>
</div>
<script>
    // 2.创建一个Vue的实例对象
    let vue = new Vue({
        // 3.告诉Vue的实例对象, 将来需要控制界面上的哪个区域
        el: '#app',
        // 4.告诉Vue的实例对象, 被控制区域的数据是什么
        data: {
            name: "Andy"
        }
    });
</script>
</body>
</html>

<!--
1.Vue框架使用方式
1.1传统下载导入使用
1.2vue-cli安装导入使用

2.Vue框架使用步骤
2.1下载Vue框架
2.2导入Vue框架
2.3创建Vue实例对象
2.4指定Vue实例对象控制的区域
2.5指定Vue实例对象控制区域的数据
-->
数据单向传递
<!--
1.MVVM设计模式
在MVVM设计模式中由3个部分组成
M : Model      数据模型(保存数据, 处理数据业务逻辑)
V : View       视图(展示数据, 与用户交互)
VM: View Model 数据模型和视图的桥梁(M是中国人, V是美国人, VM就是翻译)

MVVM设计模式最大的特点就是支持数据的双向传递
数据可以从 M -> VM -> V
也可以从   V -> VM -> M

2.Vue中MVVM的划分
Vue其实是基于MVVM设计模式的
被控制的区域: View
Vue实例对象 : View Model
实例对象中的data: Model

3.Vue中数据的单向传递
我们把"数据"交给"Vue实例对象", "Vue实例对象"将数据交给"界面"
      Model  ->  View Model     ->   View
-->
<!--这里就是MVVM中的View-->
<div id="app">
    <p>{{ name }}</p>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            name: "Andy"
        }
    });
</script>
chrome调试工具
1.Vue调试工具安装
如果你能打开谷歌插件商店, 直接在线安装即可
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=zh-CN

由于国内无法打开谷歌国外插件商店, 所以可以离线安装
https://www.chromefor.com/vue-js-devtools_v5-3-0/

2.安装步骤:
2.1下载离线安装包
2.2打开谷歌插件界面
2.3直接将插件拖入
2.4报错 程序包无效: "CRX_HEADER_INVALID"
   可以将安装包修改为rar后缀, 解压之后再安装
2.5重启浏览器
v-model 数据双向绑定
数据双向绑定
默认情况下Vue只支持数据单向传递 M -> VM -> V
但是由于Vue是基于MVVM设计模式的, 所以也提供了双向传递的能力
在<input><textarea><select> 元素上可以用 v-model 指令创建双向数据绑定

注意点: v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值
而总是将 Vue 实例的数据作为数据来源

<!--这里就是MVVM中的View-->
<div id="app">
    <p>{{ name }}</p>
    <input type="text" v-model="msg">
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            name: "Andy",
            msg: "可可爱爱"
        }
    });
</script>
v-model修饰符

v-model.trim 去除前后空格
v-model.number 将输入的值转换为数字
v-model.lazy

vue数据绑定的过程
1.Vue数据绑定过程
1.1会先将未绑定数据的界面展示给用户
1.2然后再根据模型中的数据和控制的区域生成绑定数据之后的HTML代码
1.3最后再将绑定数据之后的HTML渲染到界面上

常用指令

v-once 让界面不要跟着数据变化, 只渲染一次
<!--
1.什么是指令?
指令就是Vue内部提供的一些自定义属性,
这些属性中封装好了Vue内部实现的一些功能
只要使用这些指令就可以使用Vue中实现的这些功能

2.Vue数据绑定的特点
只要数据发生变化, 界面就会跟着变化

3.v-once指令:
让界面不要跟着数据变化, 只渲染一次
-->

<!--这里就是MVVM中的View-->
<div id="app">
    <p v-once>原始数据: {{ name }}</p>
    <p>当前数据: {{ name }}</p>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            name: "Andy",
        }
    });
</script>
v-cloak 数据渲染之后自动显示元素
<!--
1.Vue数据绑定过程
1.1会先将未绑定数据的界面展示给用户
1.2然后再根据模型中的数据和控制的区域生成绑定数据之后的HTML代码
1.3最后再将绑定数据之后的HTML渲染到界面上

正是在最终的HTML被生成渲染之前会先显示模板内容
所以如果用户网络比较慢或者网页性能比较差, 那么用户会看到模板内容

2.如何解决这个问题
利用v-cloak配合 [v-cloak]:{display: none}默认先隐藏未渲染的界面
等到生成HTML渲染之后再重新显示

3.v-cloak指令作用:
数据渲染之后自动显示元素
-->

<!--这里就是MVVM中的View-->
<div id="app">
    <p v-cloak>{{ name }}</p>
</div>
<!--
<div id="app">
    <p> Andy </p>
</div>
-->
<script src="js/vue.js"></script>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            name: "Andy ",
        }
    });
</script>
v-text 和 v-html对比

1.什么是v-text指令
v-text就相当于过去学习的innerText

2.什么是v-html指令
v-html就相当于过去学习的innerHTML

<div id="app">
    <!--插值的方式: 可以将指定的数据插入到指定的位置-->
<p>++++{{ name }}++++</p>
    <!--插值的方式: 不会解析HTML-->
<p>++++{{ msg }}++++</p>
    <!--v-text的方式: 会覆盖原有的内容-->
<p v-text="name">++++++++</p>
    <!--v-text的方式: 也不会解析HTML-->
<p v-text="msg">++++++++</p>
    <!--v-html的方式: 会覆盖原有的内容-->
<p v-html="name">++++++++</p>
    <!--v-html的方式:会解析HTML-->
<p v-html="msg">++++++++</p>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            name: "Andy",
            msg: "<span>我是span</span>"
        }
    });
</script>
v-if
1.什么是v-if指令
条件渲染: 如果v-if取值是true就渲染元素, 如果不是就不渲染元素

2.v-if特点:
如果条件不满足根本就不会创建这个元素(重点)

3.v-if注意点
v-if可以从模型中获取数据
v-if也可以直接赋值一个表达式

4.v-else指令
v-else指令可以和v-if指令配合使用, 当v-if不满足条件时就执行v-else就显示v-else中的内容

5.v-else注意点
v-else不能单独出现
v-if和v-else中间不能出现其它内容

6.v-else-if指令
v-else-if可以和v-if指令配合使用, 当v-if不满足条件时就依次执行后续v-else-if, 哪个满足就显示哪个

7.v-else-if注意点
和v-else一样


<!--这里就是MVVM中的View-->
<div id="app">
<!--    <p v-if="show">我是true</p>-->
<!--    <p v-if="hidden">我是false</p>-->
<!--    <p v-if="true">我是true</p>-->
<!--    <p v-if="false">我是false</p>-->
<!--    <p v-if="age >= 18">我是true</p>-->
<!--    <p v-if="age < 18">我是false</p>-->

<!--    <p v-if="age >= 18">成年人</p>-->
<!--    <p>中间的内容</p>-->
<!--    <p v-else>未成年人</p>-->

    <p v-if="score >= 80">优秀</p>
    <p v-else-if="score >= 60">良好</p>
    <p v-else></p>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            show: true,
            hidden: false,
            age: 17,
            score: 50
        }
    });
</script>
v-show以及与v-if的区别
1.什么是v-show指令
v-show和v-if的能够一样都是条件渲染, 取值为true就显示, 取值为false就不显示

2.v-if和v-show区别
v-if: 只要取值为false就不会创建元素
v-show: 哪怕取值为false也会创建元素, 只是如果取值是false会设置元素的display为none

3.v-if和v-show应用场景
由于取值为false时v-if不会创建元素, 所以如果需要切换元素的显示和隐藏, 每次v-if都会创建和删除元素
由于取值为false时v-show会创建元素并设置display为none, 所有如果需要切换元素的显示和隐藏,
不会反复创建和删除, 只是修改display的值
所以: 如果企业开发中需要频繁切换元素显示隐藏, 那么推荐使用v-show, 否则使用v-if
<!--这里就是MVVM中的View-->
<div id="app">
<!--    <p v-show="show">我是true</p>-->
<!--    <p v-show="hidden">我是false</p>-->
<!--    <p v-show="true">我是true</p>-->
<!--    <p v-show="false">我是false</p>-->
<!--    <p v-show="age >= 18">我是true</p>-->
<!--    <p v-show="age < 18">我是false</p>-->
    <p v-show="show">我是段落1</p>
    <p v-show="hidden">我是段落2</p>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            show: true,
            hidden: false,
            age: 18
        }
    });
</script>
v-for
<!--
1.什么是v-for指令
相当于JS中的for in循环, 可以根据数据多次渲染元素

2.v-for特点
可以遍历 数组, 字符, 数字, 对象
-->

<!--这里就是MVVM中的View-->
<div id="app">
    <ul>
<!--        <li v-for="(value, index) in list">{{index}}-&#45;&#45;{{value}}</li>-->
<!--        <li v-for="(value, index) in 'abcdefg'">{{index}}-&#45;&#45;{{value}}</li>-->
<!--        <li v-for="(value, index) in 6">{{index}}-&#45;&#45;{{value}}</li>-->
        <li v-for="(value, key) in obj">{{key}}---{{value}}</li>
    </ul>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            list: ["张三", "李四", "王五", "赵六"],
            obj: {
                name: "lnj",
                age: 33,
                gender: "male",
                class: "web"
            }
        }
    });
</script>
v-for-key

1.v-for注意点
1.1v-for为了提升性能, 在更新已渲染过的元素列表时,会采用“就地复用”策略。
也正是因为这个策略, 在某些时刻会导致我们的数据混乱
例如: 在列表前面新增了内容
1.2为了解决这个问题, 我们可以在渲染列表的时候给每一个元素加上一个独一无二的key
v-for在更新已经渲染过的元素列表时, 会先判断key是否相同, 如果相同则复用, 如果不同则重新创建

2.key属性注意点
不能使用index的作为key,因为当列表的内容新增或者删除时index都会发生变化

<!--这里就是MVVM中的View-->
<div id="app">
    <form>
        <input type="text" v-model="name">
        <input type="submit" value="添加" @click.prevent="add">
    </form>
    <ul>
<!--        <li v-for="(person,index) in persons" :key="person.id">-->
        <li v-for="(person,index) in persons" :key="index">
            <input type="checkbox">
            <span>{{index}} --- {{person.name}}</span>
        </li>
    </ul>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            persons: [
                {name: "zs", id: 1},
                {name: "ls", id: 2},
                {name: "ww", id: 3}
                ],
            name: ""
        },
        // 专门用于存储监听事件回调函数
        methods: {
            add(){
                let lastPerson = this.persons[this.persons.length - 1];
                let newPerson = {name: this.name, id: lastPerson.id + 1};
                // this.persons.push(newPerson);
                this.persons.unshift(newPerson);
                this.name = "";
            }
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
v-bind 给元素的属性绑定数据
1.什么是v-bind指令
在企业开发中想要给"元素"绑定数据, 我们可以使用{{}}, v-text, v-html
但是如果想给"元素的属性"绑定数据, 就必须使用v-bind
所以v-bind的作用是专门用于给"元素的属性"绑定数据的

2.v-bind格式
v-bind:属性名称="绑定的数据"
:属性名称="绑定的数据"

3.v-bind特点
赋值的数据可以是任意一个合法的JS表达式
例如: :属性名称="age + 1"

<!--这里就是MVVM中的View-->
<div id="app">
<!--    <p>{{name}}</p>-->
<!--    <p v-text="name"></p>-->
<!--    <p v-html="name"></p>-->
    <!--注意点: 如果要给元素的属性绑定数据, 那么是不能够使用插值语法的-->
<!--    <input type="text" value="{{name}}">-->
    <!--注意点: 虽然通过v-model可以将数据绑定到input标签的value属性上
                但是v-model是有局限性的, v-model只能用于input/textarea/select
                但是在企业开发中我们还可能需要给其它标签的属性绑定数据-->
<!--    <input type="text" v-model="name">-->
<!--    <input type="text" v-bind:value="name">-->
<!--    <input type="text" :value="name">-->
    <input type="text" :value="age + 1">
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            name: "Andy",
            age: 18
        }
    });
</script>
v-bind 绑定类名

在这里插入图片描述


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>12-常用指令-绑定类名</title>
    <script src="js/vue.js"></script>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .size{
            font-size: 100px;
        }
        .color{
            color: red;
        }
        .active{
            background: skyblue;
        }
    </style>
</head>
<body>

1.v-bind指令的作用
v-bind指令给"任意标签"的"任意属性"绑定数据
对于大部分的属性而言我们只需要直接赋值即可, 例如:value="name"
但是对于class和style属性而言, 它的格式比较特殊

2.通过v-bind绑定类名格式
:class="['需要绑定类名', ...]"

3.注意点:
3.1直接赋值一个类名(没有放到数组中)默认回去Model中查找
:class="需要绑定类名"
2.2数组中的类名没有用引号括起来也会去Model中查找
:class="[需要绑定类名]"
2.3数组的每一个元素都可以是一个三目运算符按需导入
:class="[flag?'active':'']"
2.4可以使用对象来替代数组中的三目运算符按需导入
:class="[{'active': true}]"
2.5绑定的类名太多可以将类名封装到Model中
obj: {
    'color': true,
    'size': true,
    'active': false,
}

4.绑定类名企业应用场景
从服务器动态获取样式后通过v-bind动态绑定类名
这样就可以让服务端来控制前端样式
常见场景: 618 双11等


<!--这里就是MVVM中的View-->
<div id="app">
<!--    <p class="size color active">我是段落</p>-->
    <!--
    注意点:
    如果需要通过v-bind给class绑定类名, 那么不能直接赋值
    默认情况下v-bind会去Model中查找数据, 但是Model中没有对应的类名, 所以无效, 所以不能直接赋值
    -->
<!--    <p :class="size">我是段落</p>-->
    <!--
    注意点:
    如果想让v-bind去style中查找类名, 那么就必须把类名放到数组中
    但是放到数组中之后默认还是回去Model中查找
    -->
<!--    <p :class="[size]">我是段落</p>-->
    <!--
    注意点:
    将类名放到数组中之后, 还需要利用引号将类名括起来才会去style中查找
    -->
<!--    <p :class="['size', 'color', 'active']">我是段落</p>-->
    <!--
    注意点:
    如果是通过v-bind类绑定类名, 那么在绑定的时候可以编写一个三目运算符来实现按需绑定
    格式: 条件表达式 ? '需要绑定的类名' : ''
    -->
<!--    <p :class="['size', 'color', flag ? 'active' : '']">我是段落</p>-->
    <!--
    注意点:
    如果是通过v-bind类绑定类名, 那么在绑定的时候可以通过对象来决定是否需要绑定
    格式: {'需要绑定的类名' : 是否绑定}
    -->
<!--    <p :class="['size', 'color',{'active' : false}]">我是段落</p>-->
    <!--
    注意点:
    如果是通过v-bind类绑定类名, 那么还可以使用Model中的对象来替换数组
    -->
    <p :class="obj">我是段落</p>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            flag: false,
            obj:{
                'size': false,
                'color': false,
                'active': true,
            }
        }
    });
</script>
</body>
</html>
v-bind 绑定样式


1.如何通过v-bind给style属性绑定数据
1.1将数据放到对象中
:style="{color:'red','font-size':'50px'}"
1.2将数据放到Model对象中
obj: {
    color: 'red',
    'font-size': '80px',
}

2.注意点
2.1如果属性名称包含-, 那么必须用引号括起来
2.2如果需要绑定Model中的多个对象, 可以放到一个数组中赋值


<!--这里就是MVVM中的View-->
<div id="app">
<!--    <p style="color: red">我是段落</p>-->
    <!--
    注意点:
    和绑定类名一样, 默认情况下v-bind回去Model中查找, 找不到所以没有效果
    -->
<!--    <p :style="color: red">我是段落</p>-->
    <!--
    注意点:
    我们只需要将样式代码放到对象中赋值给style即可
    但是取值必须用引号括起来
    -->
<!--    <p :style="{color: 'red'}">我是段落</p>-->
    <!--
    注意点:
    如果样式的名称带-, 那么也必须用引号括起来才可以
    -->
<!--    <p :style="{color: 'red', 'font-size': '100px'}">我是段落</p>-->
<!--    <p :style="obj">我是段落</p>-->
    <!--
    注意点:
    如果Model中保存了多个样式的对象 ,想将多个对象都绑定给style, 那么可以将多个对象放到数组中赋值给style即可
    -->
    <p :style="[obj1, obj2]">我是段落</p>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            obj1:{
                "color": "blue",
                "font-size": "100px"
            },
            obj2: {
                "background-color": "red"
            }
        }
    });
</script>
v-on

1.什么是v-on指令?
v-on指令专门用于给元素绑定监听事件

2.v-on指令格式
v-on:事件名称="回调函数名称"
@事件名称="回调函数名称"

3.v-on注意点:
v-on绑定的事件被触发之后, 会去Vue实例对象的methods中查找对应的回调函数
-->

<!--这里就是MVVM中的View-->
<div id="app">
<!--    <button οnclick="alert('lnj')">我是按钮</button>-->
    <!--
    注意点:
    1.如果是通过v-on来绑定监听事件, 那么在指定事件名称的时候不需要写on
    2.如果是通过v-on来绑定监听事件, 那么在赋值的时候必须赋值一个回调函数的名称
    -->
<!--    <button v-on:click="alert('lnj')">我是按钮</button>-->
    <!--
    注意点:
    当绑定的事件被触发后, 会调用Vue实例的methods对象中对应的回调函数
    -->
<!--    <button v-on:click="myFn">我是按钮</button>-->
    <button @click="myFn">我是按钮</button>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        methods: {
            myFn(){
                alert('lnj')
            }
        }
    });
</script>
v-on 修饰符
1.v-on修饰符
在事件中有很多东西需要我们处理, 例如事件冒泡,事件捕获, 阻止默认行为等
那么在Vue中如何处理以上内容呢, 我们可以通过v-on修饰符来处理

2.常见修饰符
.once    - 只触发一次回调。
.prevent - 调用 event.preventDefault()。
.stop    - 调用 event.stopPropagation()。
.self    - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.capture - 添加事件侦听器时使用 capture 模式。


<!--这里就是MVVM中的View-->
<div id="app">
    <!--注意点: 默认情况下事件的回调函数可以反复的执行, 只要事件被触发就会执行-->
<!--    <button v-on:click="myFn">我是按钮</button>-->
    <!--如果想让事件监听的回调函数只执行一次, 那么就可以使用.once修饰符-->
<!--    <button v-on:click.once ="myFn">我是按钮</button>-->
    <!--如果想阻止元素的默认行为, 那么可以使用.prevent修饰符-->
<!--    <a href="http://www.it666.com" v-on:click.prevent="myFn">我是A标签</a>-->
    <!--
    默认情况下载嵌套的元素中, 如果都监听了相同的事件, 那么会触发事件冒泡
    如果想阻止事件冒泡, 那么可以使用.stop修饰符
    -->
    <!--<div class="a" @click="myFn1">
        <div class="b" @click.stop="myFn2">
            <div class="c" @click="myFn3"></div>
        </div>
    </div>-->
    <!--
    如果想让回调只有当前元素触发事件的时候才执行, 那么就可以使用.self修饰符
    -->
    <!--<div class="a" @click="myFn1">
        <div class="b" @click.self="myFn2">
            <div class="c" @click="myFn3"></div>
        </div>
    </div>-->
    <!--
    默认情况下是事件冒泡, 如果想变成事件捕获, 那么就需要使用.capture修饰符
    -->
    <div class="a" @click.capture="myFn1">
        <div class="b" @click.capture="myFn2">
            <div class="c" @click.capture="myFn3"></div>
        </div>
    </div>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
            myFn(){
                alert('lnj');
            },
            myFn1(){
                console.log("爷爷");
            },
            myFn2(){
                console.log("爸爸");
            },
            myFn3(){
                console.log("儿子");
            }
        }
    });
</script>
v-on 注意点
1.v-on注意点
1.1绑定回调函数名称的时候, 后面可以写()也可以不写
v-on:click="myFn"
v-on:click="myFn()"

1.2可以给绑定的回调函数传递参数
v-on:click="myFn('lnj', 33)"

1.3如果在绑定的函数中需要用到data中的数据必须加上this

<!--这里就是MVVM中的View-->
<div id="app">
    <button @click="myFn('lnj', 33, $event)">我是按钮</button>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            gender: "man"
        },
        // 专门用于存储监听事件回调函数
        methods: {
            myFn(name, age, e){
                // alert('lnj');
                // console.log(name, age, e);
                console.log(this.gender);
            }
        }
    });
</script>
v-on 按键修饰符

1.什么是按键修饰符
我们可以通过按键修饰符监听特定按键触发的事件
例如: 可以监听当前事件是否是回车触发的, 可以监听当前事件是否是ESC触发的等

2.按键修饰符分类
2.1系统预定义修饰符
2.2自定义修饰符

<!--这里就是MVVM中的View-->
<div id="app">
<!--    <input type="text" @keyup.enter="myFn">-->
    <input type="text" @keyup.f2="myFn">
</div>
<script>
    Vue.config.keyCodes.f2 = 113;
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
            myFn(){
                alert("lnj");
            }
        }
    });
</script>
render函数

可以不使用template模板,而是使用render函数
template编译渲染流程:
template -> render -> h -> VNode -> 真实Dom -> 绘制渲染
render编译渲染流程:
render -> h -> VNode -> 真实Dom -> 绘制渲染

mixin

mixin 混入
组件 data优先级高于mixin data的优先级
生命周期函数 先执行mixin里面的,再执行组件里面的
组件的方法优先级高于mixin 方法的优先级
自定义属性,组件里的优先级高于mixin里面的优先级

<!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>Document</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script>
    // mixin 混入
    // 组件 data优先级高于mixin data的优先级
    // 生命周期函数 先执行mixin里面的,再执行组件里面的
    // 组件的方法优先级高于mixin 方法的优先级
    // 自定义属性,组件里的优先级高于mixin里面的优先级
    const myMixin = {
      number: 1,
    };

    const app = Vue.createApp({
      mixins: [myMixin],
      number: 2, //自定义属性
      template: `
      <div>
        <div>{{this.$options.number}}</div>
      </div>
      `,
    });

    //自定义混入的优先级(策略变更)
    app.config.optionMergeStrategies.number = (mixinVal, appVal) => {
      return mixinVal || appVal;
    };

    const vm = app.mount('#root');
  </script>
</html>

自定义全局指令

1.自定义全局指令
在Vue中除了可以使用Vue内置的一些指令以外, 我们还可以自定义指令

2.自定义全局指令语法
ue.directive(‘自定义指令名称’, {
生命周期名称: function (el) {
指令业务逻辑代码
}
});

3.指令生命周期方法
自定义指令时一定要明确指令的业务逻辑代码更适合在哪个阶段执行
例如: 指令业务逻辑代码中没有用到元素事件, 那么可以在bind阶段执行
例如: 指令业务逻辑代码中用到了元素事件, 那么就需要在inserted阶段执行

4.自定义指令注意点
使用时需要加上v-, 而在自定义时不需要加上v-

<!--这里就是MVVM中的View-->
<div id="app">
<!--    <p v-color>我是段落</p>-->
    <input type="text" v-focus>
</div>
<script>
    /*
    directive方法接收两个参数
    第一个参数: 指令的名称
    第二个参数: 对象
    注意点: 在自定义指令的时候, 在指定指令名称的时候, 不需要写v-
    注意点: 指令可以在不同的生命周期阶段执行
    bind: 指令被绑定到元素上的时候执行
    inserted: 绑定指令的元素被添加到父元素上的时候执行
    * */
    Vue.directive("color", {
        // 这里的el就是被绑定指令的那个元素
        bind: function (el) {
            el.style.color = "red";
        }
    });
    Vue.directive("focus", {
        // 这里的el就是被绑定指令的那个元素
        inserted: function (el) {
            el.focus();
        }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        }
    });
</script>
自定义指令参数

1.自定义指令参数
在使用官方指令的时候我们可以给指令传参
例如: v-model=“name”
在我们自定义的指令中我们也可以传递传递

2.获取自定义指令传递的参数
在执行自定义指令对应的方法的时候, 除了会传递el给我们, 还会传递一个对象给我们
这个对象中就保存了指令传递过来的参数

<!--这里就是MVVM中的View-->
<div id="app">
<!--    <p v-color="'blue'">我是段落</p>-->
    <p v-color="curColor">我是段落</p>
</div>
<script>
    Vue.directive("color", {
        // 这里的el就是被绑定指令的那个元素
        bind: function (el, obj) {
            // el.style.color = "red";
            el.style.color = obj.value;
        }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            curColor: 'green'
        },
        // 专门用于存储监听事件回调函数
        methods: {
        }
    });
</script>
自定义局部指令

1.自定义全局指令的特点
在任何一个Vue实例控制的区域中都可以使用

2.自定义局部指令的特点
只能在自定义的那个Vue实例中使用

3.如何自定义一个局部指令
给创建Vue实例时传递的对象添加

directives: {
    // key: 指令名称
    // value: 对象
    'color': {
        bind: function (el, obj) {
            el.style.color = obj.value;
        }
    }
}
<!--这里就是MVVM中的View-->
<div id="app1">
    <p v-color="'blue'">我是段落</p>
</div>
<div id="app2">
    <p v-color="'red'">我是段落</p>
</div>
<script>
    /*
    Vue.directive("color", {
        // 这里的el就是被绑定指令的那个元素
        bind: function (el, obj) {
            el.style.color = obj.value;
        }
    });
     */
    // 这里就是MVVM中的View Model
    let vue1 = new Vue({
        el: '#app1',
        // 这里就是MVVM中的Model
        data: {},
        // 专门用于存储监听事件回调函数
        methods: {}
    });
    // 这里就是MVVM中的View Model
    let vue2 = new Vue({
        el: '#app2',
        // 这里就是MVVM中的Model
        data: {},
        // 专门用于存储监听事件回调函数
        methods: {},
        // 专门用于定义局部指令的
        directives: {
            "color": {
                // 这里的el就是被绑定指令的那个元素
                bind: function (el, obj) {
                    el.style.color = obj.value;
                }
            }
        }
    });
</script>
计算属性

1.插值语法特点
可以在{{}}中编写合法的JavaScript表达式

2.在插值语法中编写JavaScript表达式缺点
2.1没有代码提示
2.2语句过于复杂不利于我们维护

3.如何解决?
对于任何复杂逻辑,你都应当使用计算属性

<!--这里就是MVVM中的View-->
<div id="app">
    <p>{{name}}</p>
    <p>{{age + 1}}</p>
    <p>{{msg.split("").reverse().join("")}}</p>
   
    注意点:
    虽然在定义计算属性的时候是通过一个函数返回的数据
    但是在使用计算属性的时候不能在计算属性名称后面加上()
    因为它是一个属性不是一个函数(方法)
   
    <p>{{msg2}}</p>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            name: "lnj",
            age: 18,
            msg: "abcdef"
        },
        // 专门用于存储监听事件回调函数
        methods: {},
        // 专门用于定义计算属性的
        computed: {
            msg2: function () {
                let res = "abcdef".split("").reverse().join("");
                return res;
            }
        }
    });
</script>
计算属性与函数的对比

1.计算属性和函数
通过计算属性我们能拿到处理后的数据, 但是通过函数我们也能拿到处理后的数据
那么计算属性和函数有什么区别呢?
2.1函数"不会"将计算的结果缓存起来, 每一次访问都会重新求值
2.2计算属性"会"将计算的结果缓存起来, 只要数据没有发生变化, 就不会重新求值

2.计算属性应用场景
计算属性:比较适合用于计算不会频繁发生变化的的数据

自定义全局过滤器

1.什么是过滤器?
过滤器和函数和计算属性一样都是用来处理数据的
但是过滤器一般用于格式化插入的文本数据

2.如何自定义全局过滤器
Vue.filter(“过滤器名称”, 过滤器处理函数):

3.如何使用全局过滤器(只有这两种使用方式)
{{msg | 过滤器名称}}
:value=“msg | 过滤器名称”

4.过滤器注意点
4.1只能在插值语法和v-bind中使用
4.2过滤器可以连续使用

<!--这里就是MVVM中的View-->
<div id="app">
    <!--Vue会把name交给指定的过滤器处理之后, 再把处理之后的结果插入到指定的元素中-->
    <p>{{name | formartStr1 | formartStr2}}</p>
</div>
<script>
    /*
    如何自定义一个全局过滤器
    通过Vue.filter();
    filter方法接收两个参数
    第一个参数: 过滤器名称
    第二个参数: 处理数据的函数
    注意点: 默认情况下处理数据的函数接收一个参数, 就是当前要被处理的数据
    * */
    Vue.filter("formartStr1", function (value) {
        // console.log(value);
        value = value.replace(/学院/g, "大学");
        console.log(value);
        return value;
    });
    Vue.filter("formartStr2", function (value) {
        // console.log(value);
        value = value.replace(/大学/g, "幼儿园");
        console.log(value);
        return value;
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            name: "知播渔学院, 指趣学院, 前端学院, 区块链学院"
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
自定义局部过滤器

1.自定义全局过滤器的特点
在任何一个Vue实例控制的区域中都可以使用

2.自定义局部过滤器的特点
只能在自定义的那个Vue实例中使用

3.如何自定义一个局部指令
给创建Vue实例时传递的对象添加

filters: {
    // key: 过滤器名称
    // value: 过滤器处理函数
    'formartStr': function (value) {}
}
<!--这里就是MVVM中的View-->
<div id="app1">
    <p>{{name | formartStr}}</p>
</div>
<div id="app2">
    <p>{{name | formartStr}}</p>
</div>
<script>
    /*
    Vue.filter("formartStr", function (value) {
        // console.log(value);
        value = value.replace(/学院/g, "大学");
        // console.log(value);
        return value;
    });
    */
    // 这里就是MVVM中的View Model
    let vue1 = new Vue({
        el: '#app1',
        // 这里就是MVVM中的Model
        data: {
            name: "知播渔学院, 指趣学院, 前端学院, 区块链学院"
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
    // 这里就是MVVM中的View Model
    let vue2 = new Vue({
        el: '#app2',
        // 这里就是MVVM中的Model
        data: {
            name: "知播渔学院, 指趣学院, 前端学院, 区块链学院"
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部过滤器的
        filters: {
            "formartStr": function (value) {
                // console.log(value);
                value = value.replace(/学院/g, "大学");
                // console.log(value);
                return value;
            }
        }
    });
</script>
格式化时间的过滤器练习
<!--
需求: 利用过滤器对时间进行格式化
-->
<!--这里就是MVVM中的View-->
<div id="app">
    <p>{{time | dateFormart("yyyy-MM-dd")}}</p>
</div>
<script>
    /*
    注意点: 在使用过滤器的时候, 可以在过滤器名称后面加上()
            如果给过滤器的名称后面加上了(), 那么就可以给过滤器的函数传递参数
    * */
    Vue.filter("dateFormart", function (value, fmStr) {
        // console.log(fmStr);
        let date = new Date(value);
        let year = date.getFullYear();
        let month = date.getMonth() + 1 + "";
        let day = date.getDate() + "";
        let hour = date.getHours() + "";
        let minute = date.getMinutes() + "";
        let second = date.getSeconds() + "";
        if(fmStr && fmStr === "yyyy-MM-dd"){
            return `${year}-${month.padStart(2, "0")}-${day.padStart(2, "0")}`;
        }
        return `${year}-${month.padStart(2, "0")}-${day.padStart(2, "0")} ${hour.padStart(2, "0")}:${minute.padStart(2, "0")}:${second.padStart(2, "0")}`;
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            time: Date.now()
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
过渡动画

1.如何给Vue控制的元素添加过渡动画
1.1将需要执行动画的元素放到transition组件中
1.2当transition组件中的元素显示时会自动查找.v-enter/.v-enter-active/.v-enter-to类名
当transition组件中的元素隐藏时会自动查找.v-leave/ .v-leave-active/.v-leave-to类名
1.3我们只需要在.v-enter和.v-leave-to中指定动画动画开始的状态
在.v-enter-active和.v-leave-active中指定动画执行的状态
即可完成过渡动画


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>29-Vue-过渡动画</title>
    <script src="js/vue.js"></script>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .box{
            width: 200px;
            height: 200px;
            background: red;
        }
        .one-enter{
            opacity: 0;
        }
        .one-enter-to{
            opacity: 1;
            margin-left: 500px;
        }
        .one-enter-active{
            transition: all 3s;
        }
        .two-enter{
            opacity: 0;
        }
        .two-enter-to{
            opacity: 1;
            margin-top: 500px;
        }
        .two-enter-active{
            transition: all 3s;
        }
    </style>
</head>
<body>

1.transition注意点:
transition中只能放一个元素, 多个元素无效
如果想给多个元素添加过渡动画, 那么就必须创建多个transition组件

2.初始动画设置
默认情况下第一次进入的时候没没有动画的
如果想一进来就有动画, 我们可以通过给transition添加appear属性的方式
告诉Vue第一次进入就需要显示动画

3.如何给多个不同的元素指定不同的动画
如果有多个不同的元素需要执行不同的过渡动画,那么我们可以通过给transition指定name的方式
来指定"进入之前/进入之后/进入过程中, 离开之前/离开之后/离开过程中"对应的类名
来实现不同的元素执行不同的过渡动画


<!--这里就是MVVM中的View-->
<div id="app">
    <button @click="toggle">我是按钮</button>
    <transition appear name="one">
        <div class="box" v-show="isShow"></div>
<!--        <div class="box" v-show="isShow"></div>-->
    </transition>
    <transition appear name="two">
        <div class="box" v-show="isShow"></div>
    </transition>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            isShow: true
        },
        // 专门用于存储监听事件回调函数
        methods: {
            toggle(){
                this.isShow = !this.isShow;
            }
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
</body>
</html>

通过Vue提供的JS钩子来实现过渡动画


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>30-Vue-过渡动画</title>
    <script src="js/vue.js"></script>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .box{
            width: 200px;
            height: 200px;
            background: red;
        }
        .v-enter{
            opacity: 0;
        }
        .v-enter-to{
            opacity: 1;
            margin-left: 500px;
        }
        .v-enter-active{
            transition: all 3s;
        }
    </style>
</head>
<body>

1.当前过渡存在的问题
通过transition+类名的方式确实能够实现过渡效果
但是实现的过渡效果并不能保存动画之后的状态
因为Vue内部的实现是在过程中动态绑定类名, 过程完成之后删除类名
正式因为删除了类名, 所以不能保存最终的效果

2.在Vue中如何保存过渡最终的效果
通过Vue提供的JS钩子来实现过渡动画
v-on:before-enter="beforeEnter"  进入动画之前
v-on:enter="enter"  进入动画执行过程中
v-on:after-enter="afterEnter"  进入动画完成之后
v-on:enter-cancelled="enterCancelled"  进入动画被取消

v-on:before-leave="beforeLeave" 离开动画之前
v-on:leave="leave"  离开动画执行过程中
v-on:after-leave="afterLeave" 离开动画完成之后
v-on:leave-cancelled="leaveCancelled" 离开动画被取消

3.JS钩子实现过渡注意点
3.1在动画过程中必须写上el.offsetWidth或者el.offsetHeight
3.2在enter和leave方法中必须调用done方法, 否则after-enter和after-leave不会执行
3.3需要需要添加初始动画, 那么需要把done方法包裹到setTimeout方法中调用


<!--这里就是MVVM中的View-->
<div id="app">
    <button @click="toggle">我是按钮</button>
    <!--
    注意点: 虽然我们是通过JS钩子函数来实现过渡动画
            但是默认Vue还是回去查找类名, 所以为了不让Vue去查找类名
            可以给transition添加v-bind:css="false"
    -->
    <transition appear
                v-bind:css="false"
                v-on:before-enter="beforeEnter"
                v-on:enter="enter"
                v-on:after-enter="afterEnter">
        <div class="box" v-show="isShow"></div>
    </transition>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            isShow: true
        },
        // 专门用于存储监听事件回调函数
        methods: {
            toggle(){
                this.isShow = !this.isShow;
            },
            beforeEnter(el){
                // 进入动画开始之前
                console.log("beforeEnter");
                el.style.opacity = "0";
            },
            enter(el, done){
                // 进入动画执行过程中
                console.log("enter");
                /*
                注意点: 如果是通过JS钩子来实现过渡动画
                        那么必须在动画执行过程中的回调函数中写上
                        el.offsetWidth / el.offsetHeight
                * */
                // el.offsetWidth;
                el.offsetHeight;
                el.style.transition = "all 3s";
                /*
                注意点: 动画执行完毕之后一定要调用done回调函数
                        否则后续的afterEnter钩子函数不会被执行
                * */
                // done();
                /*
                注意点: 如果想让元素一进来就有动画, 那么最好延迟以下再调用done方法
                * */
                setTimeout(function () {
                    done();
                }, 0);
            },
            afterEnter(el){
                // 进入动画执行完毕之后
                console.log("afterEnter");
                el.style.opacity = "1";
                el.style.marginLeft = "500px";
            }
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
</body>
</html>

配合Velocity实现过渡动画


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>31-Vue-过渡动画</title>
    <script src="js/vue.js"></script>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .box{
            width: 200px;
            height: 200px;
            background: red;
        }
    </style>
</head>
<body>

1.配合Velocity实现过渡动画
在Vue中我们除了可以自己实现过渡动画以外, 还可以结合第三方框架实现过渡动画

1.1导入Velocity库
1.2在动画执行过程钩子函数中编写Velocity动画

<!--这里就是MVVM中的View-->
<div id="app">
    <button @click="toggle">我是按钮</button>
    <transition appear
                v-bind:css="false"
                v-on:before-enter="beforeEnter"
                v-on:enter="enter"
                v-on:after-enter="afterEnter">
        <div class="box" v-show="isShow"></div>
    </transition>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            isShow: true
        },
        // 专门用于存储监听事件回调函数
        methods: {
            toggle(){
                this.isShow = !this.isShow;
            },
            beforeEnter(el){
            },
            enter(el, done){
                Velocity(el, {opacity: 1, marginLeft: "500px"}, 3000);
                done();
            },
            afterEnter(el){

            }
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
</body>
</html>

自定义类名动画


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>32-Vue-过渡动画</title>
    <script src="js/vue.js"></script>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .box{
            width: 200px;
            height: 200px;
            background: red;
        }
        .a{
            opacity: 0;
        }
        .b{
            opacity: 1;
            margin-left: 500px;
        }
        .c{
            transition: all 3s;
        }
    </style>
</head>
<body>
1.自定义类名动画
在Vue中除了可以使用 默认类名(v-xxx)来指定过渡动画
       除了可以使用 自定义类名前缀(yyy-xx)来指定过渡动画(transition name="yyy")
       除了可以使用 JS钩子函数来指定过渡动画以外
还可以使用自定义类名的方式来指定过渡动画

enter-class  // 进入动画开始之前
enter-active-class // 进入动画执行过程中
enter-to-class // 进入动画执行完毕之后
leave-class  // 离开动画开始之前
leave-active-class // 离开动画执行过程中
leave-to-class // 离开动画执行完毕之后

<!--这里就是MVVM中的View-->
<div id="app">
    <button @click="toggle">我是按钮</button>
    <transition appear
                enter-class="a"
                enter-active-class="c"
                enter-to-class="b">
        <div class="box" v-show="isShow"></div>
    </transition>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            isShow: true
        },
        // 专门用于存储监听事件回调函数
        methods: {
            toggle(){
                this.isShow = !this.isShow;
            }
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
</body>
</html>

配合Animate.css实现过渡动画


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>33-Vue-过渡动画</title>
    <script src="js/vue.js"></script>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .box{
            width: 200px;
            height: 200px;
            background: red;
        }
    </style>
    <link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
</head>
<body>
<!--
1.配合Animate.css实现过渡动画
1.1导入Animate.css库
1.2在执行过程中的属性上绑定需要的类名
-->

<!--这里就是MVVM中的View-->
<div id="app">
    <button @click="toggle">我是按钮</button>
    <transition appear
                enter-class=""
                enter-active-class="animated bounceInRight"
                enter-to-class="">
        <div class="box" v-show="isShow"></div>
    </transition>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            isShow: true
        },
        // 专门用于存储监听事件回调函数
        methods: {
            toggle(){
                this.isShow = !this.isShow;
            }
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
</body>
</html>

同时给多个元素添加动画


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>35-Vue-过渡动画</title>
    <script src="js/vue.js"></script>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .v-enter{
            opacity: 0;
        }
        .v-enter-to{
            opacity: 1;
        }
        .v-enter-active{
            transition: all 3s;
        }
        .v-leave{
            opacity: 1;
        }
        .v-leave-to{
            opacity: 0;
        }
        .v-leave-active{
            transition: all 3s;
        }
    </style>
</head>
<body>
<!--
1.如何同时给多个元素添加过渡动画
通过transition可以给单个元素添加过渡动画
如果想给多个元素添加过渡动画, 那么就必须通过transition-group来添加

transition-group和transition的用法一致, 只是一个是给单个元素添加动画, 一个是给多个元素添加动画而已
-->
<!--这里就是MVVM中的View-->
<div id="app">
    <form>
        <input type="text" v-model="name">
        <input type="submit" value="添加" @click.prevent="add">
    </form>
    <ul>
        <transition-group appear>
        <li v-for="(person,index) in persons" :key="person.id" @click="del(index)">
            <input type="checkbox">
            <span>{{index}} --- {{person.name}}</span>
        </li>
        </transition-group>
    </ul>
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            persons: [
                {name: "zs", id: 1},
                {name: "ls", id: 2},
                {name: "ww", id: 3}
                ],
            name: ""
        },
        // 专门用于存储监听事件回调函数
        methods: {
            add(){
                let lastPerson = this.persons[this.persons.length - 1];
                let newPerson = {name: this.name, id: lastPerson.id + 1};
                // this.persons.push(newPerson);
                this.persons.unshift(newPerson);
                this.name = "";
            },
            del(index){
                this.persons.splice(index, 1);
            }
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
</body>
</html>

transition-group注意点以及动画混乱的原因


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>36-Vue-过渡动画</title>
    <script src="js/vue.js"></script>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .v-enter{
            opacity: 0;
        }
        .v-enter-to{
            opacity: 1;
        }
        .v-enter-active{
            transition: all 3s;
        }
        .v-leave{
            opacity: 1;
        }
        .v-leave-to{
            opacity: 0;
        }
        .v-leave-active{
            transition: all 3s;
        }
    </style>
</head>
<body>
<!--
1.transition-group注意点:
默认情况下transition-group会将动画的元素放到span标签中
我们可以通过tag属性来指定将动画元素放到什么标签中

2.transition-group动画混乱问题
一般情况下组动画出现动画混乱都是因为v-for就地复用导致的
我们只需要保证所有数据key永远是唯一的即可
-->
<!--这里就是MVVM中的View-->
<div id="app">
    <form>
        <input type="text" v-model="name">
        <input type="submit" value="添加" @click.prevent="add">
    </form>
<!--    <ul>-->
        <transition-group appear tag="ul">
        <li v-for="(person,index) in persons" :key="person.id" @click="del(index)">
            <input type="checkbox">
            <span>{{index}} --- {{person.name}}</span>
        </li>
        </transition-group>
<!--    </ul>-->
</div>
<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            persons: [
                {name: "zs", id: 1},
                {name: "ls", id: 2},
                {name: "ww", id: 3}
                ],
            name: "",
            id: 3
        },
        // 专门用于存储监听事件回调函数
        methods: {
            add(){
                this.id++;
                // let lastPerson = this.persons[this.persons.length - 1];
                let newPerson = {name: this.name, id: this.id};
                // this.persons.push(newPerson);
                this.persons.unshift(newPerson);
                this.name = "";
            },
            del(index){
                this.persons.splice(index, 1);
            }
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
</body>
</html>
自定义全局组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>37-Vue组件-自定义全局组件</title>
    <script src="js/vue.js"></script>
</head>
<body>

Vue两大核心: 1.数据驱动界面改变 2.组件化
1.什么是组件? 什么是组件化?
1.1在前端开发中组件就是把一个很大的界面拆分为多个小的界面, 每一个小的界面就是一个组件
1.2将大界面拆分成小界面就是组件化

2.组件化的好处
2.1可以简化Vue实例的代码
2.2可以提高复用性

3.Vue中如何创建组件?
3.1创建组件构造器
3.2注册已经创建好的组件
3.3使用注册好的组件

<!--这里就是MVVM中的View-->
<div id="app">
    <!--// 3.3使用注册好的组件-->
    <abc></abc>
</div>
<script>
    // 3.1创建组件构造器
    let Profile = Vue.extend({
        // 注意点: 在创建组件指定组件的模板的时候, 模板只能有一个根元素
        template: `
           <div>
                <img src="images/fm.jpg"/>
                <p>我是描述信息</p>
            </div>
        `
    });
    // 3.2注册已经创建好的组件
    // 第一个参数: 指定注册的组件的名称
    // 第二个参数: 传入已经创建好的组件构造器
    Vue.component("abc", Profile );

    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
</body>
</html>

创建组件的其他方式


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>38-Vue组件-自定义全局组件</title>
    <script src="js/vue.js"></script>
</head>
<body>
<!--
1.创建组件的其它方式
1.1在注册组件的时候, 除了传入一个组件构造器以外, 还可以直接传入一个对象
1.2在编写组件模板的时候, 除了可以在字符串模板中编写以外, 还可以像过去的art-template一样在script中编写
1.3在编写组件模板的时候, 除了可以在script中编写以外, vue还专门提供了一个编写模板的标签template
-->
<!--这里就是MVVM中的View-->
<div id="app">
    <!--// 3.3使用注册好的组件-->
    <abc></abc>
</div>
<!--
<script id="info" type="text/html">
    <div>
        <img src="images/fm.jpg"/>
        <p>我是描述信息</p>
    </div>
</script>
-->
<template id="info">
    <div>
        <img src="images/fm.jpg"/>
        <p>我是描述信息</p>
    </div>
</template>
<script>
    // 3.1创建组件构造器
    /*
    let Profile = Vue.extend({
        // 注意点: 在创建组件指定组件的模板的时候, 模板只能有一个根元素
        template: `
           <div>
                <img src="images/fm.jpg"/>
                <p>我是描述信息</p>
            </div>
        `
    });
     */
    /*
    let obj = {
        // 注意点: 在创建组件指定组件的模板的时候, 模板只能有一个根元素
        template: `
           <div>
                <img src="images/fm.jpg"/>
                <p>我是描述信息</p>
            </div>
        `
    };
     */
    // 3.2注册已经创建好的组件
    // 第一个参数: 指定注册的组件的名称
    // 第二个参数: 传入已经创建好的组件构造器
    // Vue.component("abc", Profile );
    // Vue.component("abc", obj );
    /*
    Vue.component("abc", {
        // 注意点: 在创建组件指定组件的模板的时候, 模板只能有一个根元素
        template: `
           <div>
                <img src="images/fm.jpg"/>
                <p>我是描述信息</p>
            </div>
        `
    });
     */
    Vue.component("abc", {
        // 注意点: 在创建组件指定组件的模板的时候, 模板只能有一个根元素
        template: "#info"
    });

    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
</body>
</html>
自定义局部组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>39-Vue组件-自定义局部组件</title>
    <script src="js/vue.js"></script>
</head>
<body>

1.自定义全局组件特点
在任何一个Vue实例控制的区域中都可以使用

2.自定义局部组件特点
只能在自定义的那个Vue实例控制的区域中可以使用

3.如何自定义一个局部组件
在vue实例中新增components: {}
在{}中通过key/vue形式注册组件
components:{
   abc: {}
}

<!--这里就是MVVM中的View-->
<div id="app1">
    <abc></abc>
</div>
<div id="app2">
    <abc></abc>
</div>
<template id="info">
    <div>
        <img src="images/fm.jpg"/>
        <p>我是描述信息</p>
    </div>
</template>
<script>
    /*
    // 自定义全局组件
    Vue.component("abc", {
        // 注意点: 在创建组件指定组件的模板的时候, 模板只能有一个根元素
        template: "#info"
    });
     */
    // 这里就是MVVM中的View Model
    let vue1 = new Vue({
        el: '#app1',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
    // 这里就是MVVM中的View Model
    let vue2 = new Vue({
        el: '#app2',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
            "abc": {
                // 注意点: 在创建组件指定组件的模板的时候, 模板只能有一个根元素
                template: "#info"
            }
        }
    });
</script>
</body>
</html>
组件中使用data的注意点(在自定义组件中使用data必须赋值一个函数, 然后通过函数的返回值来定义有哪些数据)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>40-Vue组件-组件中的data和methods</title>
    <script src="js/vue.js"></script>
</head>
<body>

1.自定义组件中的data和methods
Vue实例控制的区域相当于一个大的组件, 在大组件中我们可以使用data和methods
而我们自定义的组件也是一个组件, 所以在自定义的组件中也能使用data和methods

2.自定义组件中data注意点
在自定义组件中不能像在vue实例中一样直接使用data
而是必须通过返回函数的方式来使用data

<!--这里就是MVVM中的View-->
<div id="app">
    <!--
    由于我们是在Vue实例控制的区域中使用的函数
    所以系统回去Vue实例中查找有没有对应的方法
    所以我们需要在Vue实例中实现对应的方法
    -->
    <button @click="appFn">我是按钮</button>
    <!--
    由于我们是在Vue实例控制的区域中使用了数据
    所以系统回去Vue实例中查找有没有对应的数据
    所以我们需要在Vue实例中添加对应的数据
    -->
    <p>{{appMsg}}</p>
    <abc></abc>
</div>
<template id="info">
    <div>
        <img src="images/fm.jpg"/>
        <!--
        由于我们是在自定义组件中使用了函数
        所以系统会去自定义的组件中查找有没有对应的方法
        所以我们需要在自定义的组件中实现对应的方法
        -->
        <button @click="abcFn">我是按钮</button>
        <p>{{abcMsg}}</p>
    </div>
</template>
<script>
    // 自定义全局组件
    Vue.component("abc", {
        // 注意点: 在创建组件指定组件的模板的时候, 模板只能有一个根元素
        template: "#info",
        methods: {
            abcFn(){
                alert("abcFn");
            }
        },
        // data: {
        //     abcMsg: "学院"
        // }
        // 注意点: 虽然在自定义的组件中也可以使用data, 但是在使用的时候, 使用的方式和Vue实例中不太一样
        在自定义组件中使用data必须赋值一个函数, 然后通过函数的返回值来定义有哪些数据
        data: function () {
            return {
                abcMsg: "学院"
            }
        }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            appMsg:"知播渔"
        },
        // 专门用于存储监听事件回调函数
        methods: {
            appFn(){
                alert("appFn");
            }
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
</body>
</html>
组件中的data为什么是一个函数

自定义组件中的datadata为什么是一个函数?
因为自定义组件可以复用, 为了保证复用时每个组件的数据都是独立的, 所以必须是一个函数


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>41-Vue组件-组件中的data为什么是一个函数</title>
    <script src="js/vue.js"></script>
</head>
<body>

<!--这里就是MVVM中的View-->
<div id="app">
    <abc></abc>
    <abc></abc>
    <abc></abc>
</div>
<template id="info">
    <div>
        <button @click="add">累加</button>
        <p>{{number}}</p>
    </div>
</template>
<script>
    // 自定义全局组件
    Vue.component("abc", {
        template: "#info",
        /*
        组件中的data如果不是通过函数返回的, 那么多个组件就会公用一份数据, 就会导致数据混乱
        如果组件中的data是通过函数返回的, 那么每创建一个新的组件, 都会调用一次这个方法
        将这个方法返回的数据和当前创建的组件绑定在一起, 这样就有效的避免了数据混乱
        * */
        data: function () {
            return {
                number: 0
            }
        },
        methods: {
            add(){
                this.number++;
            }
        }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
</body>
</html>
组件切换

组件切换
对于普通的元素我们可以通过v-if来实现切换
对于组件我们也可以通过v-if来实现切换
因为组件的本质就是一个自定义元素

动态组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>43-Vue组件-动态组件</title>
    <script src="js/vue.js"></script>
</head>
<body>
<!--
1.什么是动态组件?
通过v-if/v-else-if/v-else确实能够切换组件
但是在Vue中切换组件还有一种更专业的方式
<component v-bind:is="需要显示组件名称"></component>
component我们称之为动态组件, 也就是你让我显示谁我就显示谁

2.为什么可以通过v-if切换还要有component
因为component可以配合keep-alive来保存被隐藏组件隐藏之前的状态
-->
<!--这里就是MVVM中的View-->
<div id="app">
    <button @click="toggle">切换</button>
    <!--<p v-if="isShow">我是首页</p>
    <img v-else src="images/fm.jpg" alt="">-->
    <!--<home v-if="isShow"></home>
    <photo v-else></photo>-->
    <keep-alive>
        <component v-bind:is="name"></component>
    </keep-alive>

</div>
<template id="home">
    <div>
        <p>我是首页</p>
        <input type="checkbox">
    </div>
</template>
<template id="photo">
    <div>
        <img src="images/fm.jpg" alt="">
    </div>
</template>
<script>
    // 自定义全局组件
    Vue.component("home", {
        template: "#home",
    });
    Vue.component("photo", {
        template: "#photo",
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
            isShow: true,
            name: "home"
        },
        // 专门用于存储监听事件回调函数
        methods: {
            toggle(){
                this.isShow = !this.isShow;
                this.name = this.name === "home" ? "photo" : "home";
            }
        },
        // 专门用于定义计算属性的
        computed: {
        }
    });
</script>
</body>
</html>
组件动画

1.如何给组件添加动画?
给组件添加动画和过去给元素添加动画一样
如果是单个组件就使用transition
如果是多个组件就使用transition-group

2.过渡动画注意点
默认情况下进入动画和离开动画是同时执行的, 如果想一个做完之后再做另一个, 需要指定动画模式

父子组件

1.什么是父子组件?
在一个组件中又定义了其它组件就是父子组件
其实局部组件就是最简单的父子组件, 因为我们说过可以把Vue实例看做是一个大组件
我们在Vue实例中定义了局部组件, 就相当于在大组件里面定义了小组件, 所以实局部组件就是最简单的父子组件

2.如何定义其它的父子组件
前面我们说过, 自定义组件中可以使用data, 可以使用methods. 当然自定义组件中也可以使用components
所以我们也可以在自定义组件中再定义其它组件

父子组件的数据传递

1.父子组件数据传递?
在Vue中子组件是不能访问父组件的数据的,
如果子组件想要访问父组件的数据, 必须通过父组件传递

2.如何传递数据
2.1在父组件中通过v-bind传递数据
传递格式 v-bind:自定义接收名称 = “要传递数据”
2.2在子组件中通过props接收数据
接收格式 props: [“自定义接收名称”]

父子组件的方法传递

1.父子组件方法传递?
在Vue中子组件是不能访问父组件的方法的,
如果子组件想要访问父组件的方法, 必须通过父组件传递

2.如何传递方法
2.1在父组件中通过v-on传递方法
传递格式 v-on:自定义接收名称 = “要传递方法”
2.2在子组件中自定义一个方法
2.3在自定义方法中通过 this.$emit(‘自定义接收名称’);触发传递过来的方法

注意点: 和传递数据不同, 如果传递的是方法, 那么在子组件中不需要接收
如果传递的是方法, 那么需要在子组件中自定义一个方法
如果传递的是方法, 那么在子组件中直接使用自定义的方法即可
如果传递的是方法, 那么需要在子组件自定义的方法中通过
this.$emit(“自定义接收的名称”)的方法来触发父组件传递过来的方法


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>49-Vue组件-子组件传递数据给父组件</title>
    <script src="js/vue.js"></script>
</head>
<body>

1.如何将子组件数据传递给父组件
既然我们可以将父组件的方法传递给子组件
既然我们可以在子组件中调用父组件中的方法,
那么我们就可以在调用方法的时候给方法传递参数
传递的参数, 就是我们需要传递的数据

<!--这里就是MVVM中的View-->
<div id="app">
    <father></father>
</div>
<template id="father">
    <div>
        <button @click="say">我是按钮</button>
        <!--这里通过parentsay将父组件的say方法传递给了子组件-->
        <son @parentsay="say"></son>
    </div>
</template>
<template id="son">
    <div>
        <button @click="sonFn">我是按钮</button>
    </div>
</template>
<script>
    // 父组件
    Vue.component("father", {
        template: "#father",
        methods: {
            say(data){
                // alert("www.it666.com");
                console.log(data);
            }
        },
        // 子组件
        components: {
            "son": {
                template: "#son",
                methods: {
                    sonFn(){
                        // 第一个参数: 需要调用的函数名称
                        // 后续的参数: 给调用的函数传递的参数
                        this.$emit("parentsay", "知播渔");
                    }
                }
            }
        }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
        }
    });
</script>
</body>
</html>
组件中的命名注意点

1.组件中的命名注意点
1.1注册组件的时候使用了"驼峰命名", 那么在使用时需要转换成"短横线分隔命名"
例如: 注册时: myFather -> 使用时: my-father
1.2在传递参数的时候如果想使用"驼峰名称", 那么就必须写"短横线分隔命名"
例如: 传递时: parent-name=“name” -> 接收时: props: [“parentName”]
1.3在传递方法的时候不能使用"驼峰命名", 只能用"短横线分隔命名"
@parent-say=“say” -> this.$emit(“parent-say”);

数据和方法的多级传递

1.数据和方法的多级传递
在Vue中如果儿子想使用爷爷的数据, 必须一层一层往下传递
在Vue中如果儿子想使用爷爷的方法, 必须一层一层往下传递


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>51-Vue组件-数据和方法的多级传递</title>
    <script src="js/vue.js"></script>
</head>
<body>

<!--这里就是MVVM中的View-->
<div id="app">
    <grandfather></grandfather>
</div>
<template id="grandfather">
    <div>
        <p>{{name}}</p>
        <button @click="say">我是按钮</button>
        <father :gfname="name" @gfsay="say"></father>
    </div>
</template>
<template id="father">
    <div>
        <p>{{gfname}}</p>
        <button @click="fatherFn">我是按钮</button>
        <son :fname="gfname" @fsay="fatherFn"></son>
    </div>
</template>
<template id="son">
    <div>
        <p>{{fname}}</p>
        <button @click="sonFn">我是按钮</button>
    </div>
</template>
<script>
    // 爷爷组件
    Vue.component("grandfather", {
        template: "#grandfather",
        data: function(){
            return {
                name: "lnj"
            }
        },
        methods: {
            say(){
                console.log("我是爷爷的方法");
            }
        },
        // 爸爸组件
        components: {
            "father": {
                template: "#father",
                props: ["gfname"],
                methods:{
                    fatherFn(){
                        this.$emit("gfsay");
                    }
                },
                // 儿子组件
                components: {
                    "son": {
                        template: "#son",
                        props: ["fname"],
                        methods: {
                            sonFn(){
                                this.$emit("fsay");
                            }
                        },
                    }
                }
            }
        }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
        }
    });
</script>
</body>
</html>
匿名插槽

1.什么是插槽?
默认情况下使用子组件时在子组件中编写的元素是不会被渲染的
如果子组件中有部分内容是使用时才确定的, 那么我们就可以使用插槽
插槽就是在子组件中放一个"坑", 以后由父组件来"填"

1.什么是匿名插槽
没有名字的插槽, 会利用使用时指定的能容替换整个插槽
注意点: 如果有多个匿名插槽, 每一个匿名插槽都会被指定的内容替换
虽然写多个匿名插槽不会报错, 但是在企业开发中推荐只能有一个匿名插槽


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>52-Vue组件-匿名插槽</title>
    <script src="js/vue.js"></script>
</head>
<body>

<!--这里就是MVVM中的View-->
<div id="app">
    <father></father>
</div>
<template id="father">
    <div>
        <!--需求: 在使用子组件的时候给子组件动态的添加一些内容-->
        <son>
            <!--注意点: 默认情况下是不能在使用子组件的时候, 给子组件动态的添加内容的
                        如果想在使用子组件的时候, 给子组件动态的添加内容, 那么就必须使用插槽-->
            <div>我是追加的内容1</div>
            <div>我是追加的内容2</div>
            <div>我是追加的内容3</div>
        </son>
    </div>
</template>
<template id="son">
    <div>
        <div>我是头部</div>
        <!--这里的slot标签就是插槽, 插槽其实就是一个坑
            只要有了这个坑, 那么以后使用者就可以根据自己的需要来填这个坑-->
        <!--注意点: 插槽可以指定默认数据, 如果使用者没有填这个坑, 那么就会显示默认数据
                    如果使用者填了这个坑, 那么就会利用使用者天坑的内容替换整个插槽-->
        <!--注意点: 插槽是可以指定名称的, 默认情况下如果没有指定名称, 我们称之为 匿名插槽-->
        <!--匿名插槽的特点: 有多少个匿名插槽, 填充的数据就会被拷贝几份
                            虽然我们可以指定多个匿名插槽, 但是在企业开发中推荐只写一个匿名插槽-->
        <slot>我是默认数据</slot>
<!--        <slot>我是默认数据</slot>-->
        <div>我是底部</div>
    </div>
</template>
<script>
    // 父组件
    Vue.component("father", {
        template: "#father",
        // 子组件
        components: {
            "son": {
                template: "#son",
            }
        }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
        }
    });
</script>
</body>
</html>
具名插槽

1.什么是具名插槽
默认情况下有多少个匿名插槽, 我们填充的数据就会被拷贝多少份
这导致了所有插槽中填充的内容都是一样的
那么如果我们想给不同的插槽中填充不同的内容怎么办呢?
这个时候就可以使用具名插槽

2.具名插槽使用
通过插槽的name属性给插槽指定名称
在使用时可以通过slot="name"方式, 指定当前内容用于替换哪一个插槽

注意点: 如果没有指定要替换哪个插槽中的内容, 则不会被替换


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>53-Vue组件-具名插槽</title>
    <script src="js/vue.js"></script>
</head>
<body>

<!--这里就是MVVM中的View-->
<div id="app">
    <father></father>
</div>
<template id="father">
    <div>
        <son>
            <!--这里通过slot属性告诉Vue,当前的内容是要填充到哪一个插槽中的-->
            <div slot="one">我是追加的内容1</div>
            <div slot="one">我是追加的内容11</div>
            <div slot="two">我是追加的内容2</div>
            <div slot="two">我是追加的内容22</div>
        </son>
    </div>
</template>
<template id="son">
    <div>
        <div>我是头部</div>
        <!--可以在定义插槽的时候给插槽添加一个name属性, 通过这个name属性来指定插槽的名称
            如通插槽指定了名称, 那么我们就称之为具名插槽-->
        <!--注意点: 默认情况下填充的内容是不会被填充到具名插槽中的,
            只有给填充的内容指定了要填充到哪一个具名插槽之后,
            才会将填充的内容填充到具名插槽中-->
        <slot name="one">我是默认内容</slot>
        <slot name="two">我是默认内容</slot>
        <div>我是底部</div>
    </div>
</template>
<script>
    // 父组件
    Vue.component("father", {
        template: "#father",
        // 子组件
        components: {
            "son": {
                template: "#son",
            }
        }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
        }
    });
</script>
</body>
</html>
v-slot指令

1.什么是v-slot指令?
v-slot指令是Vue2.6中用于替代slot属性的一个指令
在Vue2.6之前, 我们通过slot属性告诉Vue当前内容填充到哪一个具名插槽
从Vue2.6开始, 我们通过v-slot指令告诉Vue当前内容填充到哪一个具名插槽

注意点: v-slot指令只能用在template标签上
可以使用#号替代v-slot:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>54-Vue组件-v-slot指令</title>
    <script src="js/vue.js"></script>
</head>
<body>

<!--这里就是MVVM中的View-->
<div id="app">
    <father></father>
</div>
<template id="father">
    <div>
        <son>
            <!--这里通过slot属性告诉Vue,当前的内容是要填充到哪一个插槽中的-->
            <!--
            <div slot="one">我是追加的内容1</div>
            <div slot="one">我是追加的内容11</div>
            <div slot="two">我是追加的内容2</div>
            <div slot="two">我是追加的内容22</div>
            -->
            <!--
            <template v-slot:one>
                <div>我是追加的内容1</div>
                <div>我是追加的内容11</div>
            </template>
            <template v-slot:two>
                <div>我是追加的内容2</div>
                <div>我是追加的内容22</div>
            </template>
            -->
            <!--v-bind: :  v-on: @-->
            <template #one>
                <div>我是追加的内容1</div>
                <div>我是追加的内容11</div>
            </template>
            <template #two>
                <div>我是追加的内容2</div>
                <div>我是追加的内容22</div>
            </template>
        </son>
    </div>
</template>
<template id="son">
    <div>
        <div>我是头部</div>
        <slot name="one">我是one默认内容</slot>
        <slot name="two">我是two默认内容</slot>
        <div>我是底部</div>
    </div>
</template>
<script>
    // 父组件
    Vue.component("father", {
        template: "#father",
        // 子组件
        components: {
            "son": {
                template: "#son",
            }
        }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
        }
    });
</script>
</body>
</html>
作用域插槽

1.什么是作用域插槽
作用域插槽就是带数据的插槽, 就是让父组件在填充子组件插槽内容时也能使用子组件的数据

2.如何使用作用域插槽
2.1在slot中通过 v-bind:数据名称=“数据名称” 方式暴露数据
2.2在父组件中通过 接收数据
2.3在父组件的中通过 作用域名称.数据名称 方式使用数据


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>55-Vue组件-作用域插槽</title>
    <script src="js/vue.js"></script>
</head>
<body>

<!--这里就是MVVM中的View-->
<div id="app">
    <father></father>
</div>
<template id="father">
    <div>
        <son>
<!--            <div>我是填充的内容 {{names}}</div>-->
            <!--
            slot-scope="abc"作用: 接收子组件插槽暴露的数据
            -->
            <!--
            作用域插槽的应用场景: 子组件提供数据, 父组件决定如何渲染
            -->
            <template slot-scope="abc">
<!--                <div>我是填充的内容 {{abc.names}}</div>-->
                <li v-for="(name, index) in abc.names">{{name}}</li>
            </template>
        </son>
    </div>
</template>
<template id="son">
    <div>
        <div>我是头部 {{names}}</div>
        <!--
        v-bind:names="names"作用: 将当前子组件的names数据暴露给父组件
        -->
        <slot v-bind:names="names">我是默认内容 {{names}}</slot>
        <div>我是底部</div>
    </div>
</template>
<script>
    // 父组件
    Vue.component("father", {
        template: "#father",
        // 子组件
        components: {
            "son": {
                template: "#son",
                data:function () {
                    return {
                        names: ["zs", "ls", "ww", "zl"]
                    }
                }
            }
        }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
        }
    });
</script>
</body>
</html>
v-slot指令取代slot-scope

在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。
它取代了 slot 和 slot-scope

也就是说我们除了可以通过v-slot指令告诉Vue内容要填充到哪一个具名插槽中
还可以通过v-slot指令告诉Vue如何接收作用域插槽暴露的数据

v-slot:插槽名称=“作用域名称”


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>56-Vue组件-v-slot指令</title>
    <script src="js/vue.js"></script>
</head>
<body>

<!--这里就是MVVM中的View-->
<div id="app">
    <father></father>
</div>
<template id="father">
    <div>
        <son>
            <!--
            <template slot-scope="abc">
                <li v-for="(name, index) in abc.names">{{name}}</li>
            </template>
            -->
            <!--
            <template v-slot:default="abc">
                <li v-for="(name, index) in abc.names">{{name}}</li>
            </template>
            -->
            <!--
            <template #default="abc">
                <li v-for="(name, index) in abc.names">{{name}}</li>
            </template>
            -->
            <template #one="abc">
                <li v-for="(name, index) in abc.names">{{name}}</li>
            </template>
        </son>
    </div>
</template>
<template id="son">
    <div>
        <div>我是头部 {{names}}</div>
<!--        <slot v-bind:names="names">我是默认内容 {{names}}</slot>-->
        <slot name="one" v-bind:names="names">我是默认内容 {{names}}</slot>
        <div>我是底部</div>
    </div>
</template>
<script>
    // 父组件
    Vue.component("father", {
        template: "#father",
        // 子组件
        components: {
            "son": {
                template: "#son",
                data:function () {
                    return {
                        names: ["zs", "ls", "ww", "zl"]
                    }
                }
            }
        }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
        }
    });
</script>
</body>
</html>
Vuex共享数据

1.什么是Vuex?
vuex 是 Vue 配套的 公共数据管理工具,它可以把一些共享的数据,保存到 vuex 中,
方便整个程序中的任何组件直接获取或修改我们的公共数据

注意点:
只有需要共享的才放到vuex上, 不需要共享的数据依然放到组件自身的data上

1.当前在企业开发中我们遇到了两个问题:
1.如果想要在子组件中使用祖先组件中的数据, 那么就必须一层一层的传递(非常麻烦)
2.兄弟组件之间不能直接传递数据, 如果兄弟组件之间想要传递数据, 那么就必须借助父组件(非常麻烦)
解决方案: 使用Vuex

2.什么是Vuex?
vuex 是 Vue 配套的 公共数据管理工具,我们可以将共享的数据保存到 vuex 中,
方便整个程序中的任何组件都可以获取和修改vuex中保存的公共数据

注意点:
必须在引入Vue之后再引入Vuex
只有需要共享的才放到vuex上, 不需要共享的数据依然放到组件自身的data上

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>58-Vuex-共享数据</title>
    <script src="js/vue.js"></script>
    <!--1.导入Vuex-->
    <!--注意点: 在导入Vuex之前必须先导入Vue-->
    <script src="js/vuex.js"></script>
</head>
<body>

<!--这里就是MVVM中的View-->
<div id="app">
    <grandfather></grandfather>
</div>
<template id="grandfather">
    <div>
        <p>{{this.$store.state.msg}}</p>
        <father></father>
    </div>
</template>
<template id="father">
    <div>
        <!--4.在使用Vuex中保存的共享数据的时候, 必须通过如下的格式来使用-->
        <p>{{this.$store.state.msg}}</p>
        <son></son>
    </div>
</template>
<template id="son">
    <div>
        <p>{{this.$store.state.msg}}</p>
    </div>
</template>

<script>
    // 2.创建Vuex对象
    const store = new Vuex.Store({
        // 这里的state就相当于组件中的data, 就是专门用于保存共享数据的
        state: {
            msg: "知播渔"
        },
    });
    // 爷爷组件
    Vue.component("grandfather", {
        template: "#grandfather",
        // 3.在祖先组件中添加store的key保存Vuex对象
        // 只要祖先组件中保存了Vuex对象 , 那么祖先组件和所有的后代组件就可以使用Vuex中保存的共享数据了
        store: store,
        // 爸爸组件
        components: {
            "father": {
                template: "#father",
                // 儿子组件
                components: {
                    "son": {
                        template: "#son",
                    }
                }
            }
        }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
        },
    });
</script>
</body>
</html>
修改数据
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>59-Vuex-修改共享数据</title>
    <script src="js/vue.js"></script>
    <script src="js/vuex.js"></script>
</head>
<body>

1.当前在企业开发中我们遇到了两个问题:
1.如果想要在子组件中使用祖先组件中的数据, 那么就必须一层一层的传递(非常麻烦)
2.兄弟组件之间不能直接传递数据, 如果兄弟组件之间想要传递数据, 那么就必须借助父组件(非常麻烦)
解决方案: 使用Vuex

2.什么是Vuex?
vuex 是 Vue 配套的 公共数据管理工具,它可以把一些共享的数据,保存到 vuex 中,
方便整个程序中的任何组件直接获取或修改我们的公共数据

注意点:
必须在引入Vue之后再引入Vuex
只有需要共享的才放到vuex上, 不需要共享的数据依然放到组件自身的data上

<!--这里就是MVVM中的View-->
<div id="app">
    <father></father>
</div>
<template id="father">
    <div>
        <son1></son1>
        <son2></son2>
    </div>
</template>
<template id="son1">
    <div>
        <!--需求: 在第一个子组件中添加两个按钮和一个输入框, 要求通过按钮控制输入框中的数据+1和-1-->
        <button @click="add">增加</button>
        <button @click="sub">减少</button>
        <input type="text" :value="this.$store.state.count">
    </div>
</template>
<template id="son2">
    <div>
<!--        <p>{{this.$store.state.count}}</p>-->
        <button @click="add">增加</button>
        <button @click="sub">减少</button>
        <input type="text" :value="this.$store.state.count">
    </div>
</template>

<script>
    const store = new Vuex.Store({
        // state: 用于保存共享数据
        state: {
            count: 0
        },
         mutations: 用于保存修改共享数据的方法
        mutations: {
            // 注意点: 在执行mutations中定义的方法的时候, 系统会自动给这些方法传递一个state参数
            //         state中就保存了共享的数据
            mAdd(state){
                state.count = state.count + 1;
            },
            mSub(state){
                state.count = state.count - 1;
            }
        }
    });
    // 爸爸组件
    Vue.component("father", {
        template: "#father",
        store: store,
        // 儿子组件
        components: {
            "son1": {
                template: "#son1",
                methods: {
                    add(){
                        // 注意点: 在Vuex中不推荐直接修改共享数据
                        // this.$store.state.count = this.$store.state.count + 1;
                        this.$store.commit("mAdd");
                    },
                    sub(){
                        // this.$store.state.count = this.$store.state.count - 1;
                        this.$store.commit("mSub");
                    }
                }
            },
            "son2": {
                template: "#son2",
                methods: {
                    add(){
                        // 注意点: 在Vuex中不推荐直接修改共享数据
                        // 如果多个组件都修改了共享的数据, 那么后期数据发生了错误, 我们如果需要去调试错误
                        // 就需要把每一个修改了共享数据的组件都检查一遍, 这样非常低效, 非常的不利于我们去维护
                        // this.$store.state.count = this.$store.state.count + 1;
                        this.$store.commit("mAdd");
                    },
                    sub(){
                        // this.$store.state.count = this.$store.state.count - 1;
                        this.$store.commit("mSub");
                    }
                }
            }
        }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
        }
    });
</script>
</body>
</html>
Vuex里面的getters

1.什么是Vuex的getters?
Vuex的getters属性就和组件的计算属性一样, 会将数据缓存起来, 只有数据发生变化才会重新计算

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>60-Vuex-getters</title>
    <script src="js/vue.js"></script>
    <script src="js/vuex.js"></script>
</head>
<body>
<!--
1.什么是Vuex的getters?
Vuex的getters属性就和组件的计算属性一样, 会将数据缓存起来, 只有数据发生变化才会重新计算
-->
<!--这里就是MVVM中的View-->
<div id="app">
    <father></father>
</div>
<template id="father">
    <div>
<!--        {{formart}}-->
<!--        {{formart}}-->
<!--        {{formart}}-->
<!--        {{this.$store.state.msg}} "www.it666.com"-->
<!--        {{this.$store.state.msg}} "www.it666.com"-->
<!--        {{this.$store.state.msg}} "www.it666.com"-->
        {{this.$store.getters.formart}}
        {{this.$store.getters.formart}}
        {{this.$store.getters.formart}}
    </div>
</template>

<script>
    const store = new Vuex.Store({
        // state: 用于保存共享数据
        state: {
            msg: "知播渔"
        },
        // mutations: 用于保存修改共享数据的方法
        mutations: {
        },
        getters: {
            formart(state){
                console.log("getters方法被执行了");
                return state.msg + "www.it666.com"
            }
        }
    });
    // 爸爸组件
    Vue.component("father", {
        template: "#father",
        store: store,
        // data: function () {
        //     return {
        //         msg: "知播渔"
        //     }
        // },
        // computed: {
        //     formart(){
        //         console.log("计算属性的方法被执行了");
        //         return this.msg + "www.it666.com";
        //     }
        // }
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
        }
    });
</script>
</body>
</html>
Vue Router

1.什么是Vue Router?
Vue Router和v-if/v-show一样, 是用来切换组件的显示的
v-if/v-show是标记来切换(true/false)
Vue Router用哈希来切换(#/xxx)
比v-if/v-show强大的是Vue Router不仅仅能够切换组件的显示, 还能够在切换的时候传递参数

2.Vue Router使用
2.1导入Vue Router
2.2定义路由规则
2.3根据路由规则创建路由对象
2.4将路径对象挂载到Vue实例中
2.5修改URL哈希值
2.6通过渲染匹配的组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>62-VueRouter-基本使用</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .onepage, .twopage{
            width: 500px;
            height: 500px;
        }
        .onepage{
            background: pink;
        }
        .twopage{
            background: skyblue;
        }
        /*.router-link-active{*/
        /*    background: red;*/
        /*}*/
        .nj-active{
            background: skyblue;
        }
    </style>
    <script src="js/vue.js"></script>
    <!--1.导入Vue Router-->
    <!--注意点: 必须先导入Vue之后再导入Vue Router-->
    <script src="js/vue-router.js"></script>
</head>
<body>
<!--
1.什么是router-link?
通过a标签确实能设置URL的hash,但是这种方式并不专业
在Vue Router中提供了一个专门用于设置hash的标签 router-link

2.router-link特点
默认情况下Vue会将router-link渲染成a标签, 但是我们可以通过tag来指定到底渲染成什么

3.给router-link设置选中样式
默认情况下我们可以通过重写router-link-active类名来实现设置选中样式
但是我们也可以通过linkActiveClass来指定选中样式

4.重定向路由
{ path: '被重定向值', redirect: '重定向之后的值' }
-->
<!--这里就是MVVM中的View-->
<div id="app">
   <!-- <a href="#/one">切换到第一个界面</a>
    <a href="#/two">切换到第二个界面</a>-->
    <!--
    如果是通过router-link来设置URL的HASH值, 那么不用写#, 那么是通过to属性来设置HASH值
    -->
    <!--
    默认情况下Vue在渲染router-link的时候, 是通过a标签来渲染的
    如果在企业开发中不想使用a标签来渲染, 那么可以通过tag属性来告诉vue通过什么标签来渲染
    -->
    <router-link to="/one" tag="button">切换到第一个界面</router-link>
    <router-link to="/two" tag="button">切换到第二个界面</router-link>
    <!-- 路由出口 -->
    <!-- 路由匹配到的组件将渲染在这里 -->
    <router-view></router-view>
</div>
<template id="one">
    <div class="onepage">
        <p>我是第一个界面</p>
    </div>
</template>
<template id="two">
    <div class="twopage">
        <p>我是第二个界面</p>
    </div>
</template>
<script>

    // 1.定义组件
    const one = {
        template: "#one"
    };
    const two = {
        template: "#two"
    };
    // 2.定义切换的规则(定义路由规则)
    const routes = [
        // 重定向路由
        { path: '/', redirect: '/two' },
        // 数组中的每一个对象就是一条规则
        { path: '/one', component: one },
        { path: '/two', component: two }
    ];
    // 3.根据自定义的切换规则创建路由对象
    const router = new VueRouter({
        routes: routes,
        // 指定导航激活状态样式类名
        linkActiveClass: "nj-active"
    });

    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 4.将创建好的路由对象绑定到Vue实例上
        router: router,
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
            one: one,
            two: two
        }
    });
</script>
</body>
</html>
VueRouter-参数传递

1.Vue Router传递参数
只要将Vue Router挂载到了Vue实例对象上, 我们就可以通过vue.$route拿到路由对象
只要能拿到路由对象, 就可以通过路由对象拿到传递的参数

方式一: 通过URL参数参数(?key=value&key=value), 通过this.$route.query获取
方式二: 通过占位符传递(路由规则中/:key/:key, 路径中/value/value), 通过this.$route.params获取
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>63-VueRouter-参数传递</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .onepage, .twopage{
            width: 500px;
            height: 500px;
        }
        .onepage{
            background: pink;
        }
        .twopage{
            background: skyblue;
        }
        /*.router-link-active{*/
        /*    background: red;*/
        /*}*/
        .nj-active{
            background: skyblue;
        }
    </style>
    <script src="js/vue.js"></script>
    <!--1.导入Vue Router-->
    <script src="js/vue-router.js"></script>
</head>
<body>
<!--
1.Vue Router传递参数
只要将Vue Router挂载到了Vue实例对象上, 我们就可以通过vue.$route拿到路由对象
只要能拿到路由对象, 就可以通过路由对象拿到传递的参数

方式一: 通过URL参数参数(?key=value&key=value), 通过this.$route.query获取
方式二: 通过占位符传递(路由规则中/:key/:key, 路径中/value/value), 通过this.$route.params获取
-->
<!--这里就是MVVM中的View-->
<div id="app">
    <!--
    第一种传递参数的方式: 通过URL参数的方式传递
    在指定HASH的时候, 通过?key=value&key=value的方式传递
    在传递的组件的生命周期方法中通过 this.$route.query的方式来获取
    -->
    <router-link to="/one?name=lnj&age=33" tag="button">切换到第一个界面</router-link>
    <!--
    第二种传递参数的方式: 通过路由规则中的占位符传递
    在指定路由规则的时候通过/:key/:key的方式来指定占位符
    在指定HASH的时候, 通过/value/value的方式来传递值
    在传递的组件的生命周期方法中通过 this.$route.params的方式来获取
    -->
    <router-link to="/two/zs/66" tag="button">切换到第二个界面</router-link>
    <!-- 路由出口 -->
    <!-- 路由匹配到的组件将渲染在这里 -->
    <router-view></router-view>
</div>
<template id="one">
    <div class="onepage">
        <p>我是第一个界面</p>
    </div>
</template>
<template id="two">
    <div class="twopage">
        <p>我是第二个界面</p>
    </div>
</template>
<script>
    // 1.定义组件
    const one = {
        template: "#one",
        created: function () {
            console.log(this.$route);
            console.log(this.$route.query.name);
            console.log(this.$route.query.age);
        }
    };
    const two = {
        template: "#two",
        created: function () {
            console.log(this.$route);
            console.log(this.$route.params.name);
            console.log(this.$route.params.age);
        }
    };
    // 2.定义切换的规则(定义路由规则)
    const routes = [
        // 数组中的每一个对象就是一条规则
        { path: '/one', component: one },
        { path: '/two/:name/:age', component: two }
    ];
    // 3.根据自定义的切换规则创建路由对象
    const router = new VueRouter({
        routes: routes,
        linkActiveClass: "nj-active"
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 4.将创建好的路由对象绑定到Vue实例上
        router: router,
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
            one: one,
            two: two
        }
    });
    // console.log(vue.$route);
</script>
</body>
</html>
嵌套路由

1.什么是嵌套路由?
嵌套路由也称之为子路由, 就是在被切换的组件中又切换其它子组件
例如: 在one界面中又有两个按钮, 通过这两个按钮进一步切换one中的内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>64-VueRouter-嵌套路由</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .onepage, .twopage{
            width: 500px;
            height: 500px;
        }
        .onepage{
            background: pink;
        }
        .twopage{
            background: skyblue;
        }
        .onesub1page, .onesub2page{
            width: 100%;
            height: 300px;
        }
        .onesub1page{
            background: orangered;
        }
        .onesub2page{
            background: blueviolet;
        }
        .nj-active{
            background: skyblue;
        }
    </style>
    <script src="js/vue.js"></script>
    <!--1.导入Vue Router-->
    <script src="js/vue-router.js"></script>
</head>
<body>
<!--
1.什么是嵌套路由?
嵌套路由也称之为子路由, 就是在被切换的组件中又切换其它子组件
例如: 在one界面中又有两个按钮, 通过这两个按钮进一步切换one中的内容
-->
<!--这里就是MVVM中的View-->
<div id="app">
    <router-link to="/one" tag="button">切换到第一个界面</router-link>
    <router-link to="/two" tag="button">切换到第二个界面</router-link>
    <!-- 路由出口 -->
    <!-- 路由匹配到的组件将渲染在这里 -->
    <router-view></router-view>
</div>
<template id="one">
    <div class="onepage">
        <p>我是第一个界面</p>
        <router-link to="/one/onesub1" tag="button">切换到第一个子界面</router-link>
        <router-link to="/one/onesub2" tag="button">切换到第二个子界面</router-link>
        <!-- 路由出口 -->
        <!-- 路由匹配到的组件将渲染在这里 -->
        <router-view></router-view>
    </div>
</template>
<template id="onesub1">
    <div class="onesub1page">
        <p>我是第一个界面子界面1</p>
    </div>
</template>
<template id="onesub2">
    <div class="onesub2page">
        <p>我是第一个界面子界面2</p>
    </div>
</template>
<template id="two">
    <div class="twopage">
        <p>我是第二个界面</p>
    </div>
</template>
<script>
    // 1.定义组件
    const onesub1 = {
        template: "#onesub1",
    };
    const onesub2 = {
        template: "#onesub2",
    };
    const one = {
        template: "#one",
        components:{
            onesub1:onesub1,
            onesub2: onesub2
        }
    };
    const two = {
        template: "#two"
    };
    // 2.定义切换的规则(定义路由规则)
    const routes = [
        // 数组中的每一个对象就是一条规则
        {
            path: '/one',
            component: one,
            children:[
                {
                    // 注意点: 如果是嵌套路由(子路由), 那么不用写一级路径的地址, 并且也不用写/
                    path: "onesub1",
                    component: onesub1
                },
                {
                    // 注意点: 如果是嵌套路由(子路由), 那么不用写一级路径的地址, 并且也不用写/
                    path: "onesub2",
                    component: onesub2
                }
            ]
        },
        // { path: '/one/onesub1', component: onesub1 },
        // { path: '/one/onesub2', component: onesub2 },
        { path: '/two', component: two }
    ];
    // 3.根据自定义的切换规则创建路由对象
    const router = new VueRouter({
        routes: routes,
        linkActiveClass: "nj-active"
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 4.将创建好的路由对象绑定到Vue实例上
        router: router,
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
            one: one,
            two: two
        }
    });
    // console.log(vue.$route);
</script>
</body>
</html>
命名视图
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>66-VueRouter-命名视图</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .onepage, .twopage{
            width: 200px;
            height: 200px;
        }
        .onepage{
            background: pink;
        }
        .twopage{
            background: skyblue;
        }
        .nj-active{
            background: skyblue;
        }
    </style>
    <script src="js/vue.js"></script>
    <!--1.导入Vue Router-->
    <script src="js/vue-router.js"></script>
</head>
<body>
<!--
1.什么是命名视图?
命名视图和前面讲解的具名插槽很像, 都是让不同的出口显示不同的内容
命名视图就是当路由地址被匹配的时候同时指定多个出口, 并且每个出口中显示的内容不同
-->
<!--这里就是MVVM中的View-->
<div id="app">
    <!-- 路由出口 -->
    <!-- 路由匹配到的组件将渲染在这里 -->
    <!--和匿名插槽一样, 如果指定了多个router-view, 那么当路由地址被匹配之后, 多个router-view中显示的内容是一样的-->
    <!--<router-view></router-view>
    <router-view></router-view>-->
    <!--和具名插槽一样, 如果想同时显示多个不同的组件, 那么可以给出口指定名称
        1.在路由规则中给组件起名称
        2.在出口中指定显示哪个名称的组件-->
    <router-view name="name1"></router-view>
    <router-view name="name2"></router-view>
</div>
<template id="one">
    <div class="onepage">
        <p>我是第一个界面</p>
    </div>
</template>
<template id="two">
    <div class="twopage">
        <p>我是第二个界面</p>
    </div>
</template>
<script>
    // 1.定义组件
    const one = {
        template: "#one",
    };
    const two = {
        template: "#two"
    };
    // 2.定义切换的规则(定义路由规则)
    const routes = [
        // 数组中的每一个对象就是一条规则
        {
            path: '/',
            components: {
                name1: one,
                name2: two
            }
        },
    ];
    // 3.根据自定义的切换规则创建路由对象
    const router = new VueRouter({
        routes: routes,
        linkActiveClass: "nj-active"
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 4.将创建好的路由对象绑定到Vue实例上
        router: router,
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
            one: one,
            two: two
        }
    });
    // console.log(vue.$route);
</script>
</body>
</html>
watch属性

1.什么是Watch属性?
Watch属性是专门用于监听数据变化的, 只要数据发生了变化, 就会自动调用对应数据的回调方法

2.Watch监听路由变化
Watch属性不仅仅能够监听数据的变化, 还能够监听路由地址的变化
在企业开发中我们可以通过Watch来判断当前界面是从哪个界面跳转过来的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>67-VueRouter-Watch属性</title>
    <script src="js/vue.js"></script>
    <script src="js/vue-router.js"></script>
</head>
<body>
<!--
1.什么是Watch属性?
Watch属性是专门用于监听数据变化的, 只要数据发生了变化, 就会自动调用对应数据的回调方法

2.Watch监听路由变化
Watch属性不仅仅能够监听数据的变化, 还能够监听路由地址的变化
在企业开发中我们可以通过Watch来判断当前界面是从哪个界面跳转过来的
-->
<!--这里就是MVVM中的View-->
<div id="app">
<!--    <input type="text" v-model="num1" @keyup="change1">-->
    <!--<input type="text" v-model="num1">
    <span>+</span>-->
<!--    <input type="text" v-model="num2" @keyup="change2">-->
    <!--<input type="text" v-model="num2">
    <span>=</span>
    <input type="text" disabled v-model="res">-->
    <a href="#/one">切换到第一个界面</a>
    <a href="#/two">切换到第二个界面</a>
    <router-view></router-view>
</div>
<template id="one">
    <div class="onepage">
        <p>我是第一个界面</p>
    </div>
</template>
<template id="two">
    <div class="twopage">
        <p>我是第二个界面</p>
    </div>
</template>

<script>
    // 1.定义组件
    const one = {
        template: "#one",
    };
    const two = {
        template: "#two"
    };
    // 2.定义切换的规则(定义路由规则)
    const routes = [
        // 数组中的每一个对象就是一条规则
        { path: '/one', component: one },
        { path: '/two', component: two }
    ];
    // 3.根据自定义的切换规则创建路由对象
    const router = new VueRouter({
        routes: routes
    });

    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 4.将创建好的路由对象绑定到Vue实例上
        router: router,
        watch: {
            // 可以通过watch监听Model中数据的变化, 只要数据发生变化, 就会自动调用对应的回调函数
          num1: function (newValue, oldValue) {
              // console.log(this.num1);
              // console.log(newValue, oldValue);
              this.res = parseInt(this.num1) + parseInt(this.num2)
          },
          num2: function (newValue, oldValue) {
              this.res = parseInt(this.num1) + parseInt(this.num2)
          },
            // 可以通过watch监听路由地址的变化, 只要路由地址发生变化, 就会自动调用对应的回调函数
          "$route.path": function (newValue, oldValue) {
              console.log(newValue, oldValue);
          }
        },
        // 这里就是MVVM中的Model
        data: {
            num1: 0,
            num2: 0,
            res: 0
        },
        // 专门用于存储监听事件回调函数
        methods: {
            change1(){
                this.res = parseInt(this.num1) + parseInt(this.num2)
            },
            change2(){
                this.res = parseInt(this.num1) + parseInt(this.num2)
            }
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
            one: one,
            two: two
        }
    });
    console.log(vue.$route);
</script>
</body>
</html>
生命周期方法

1.什么是生命周期方法?
和wbpack生命周期方法一样, 都是在从生到死的特定阶段调用的方法
PS: 生命周期钩子 = 生命周期函数 = 生命周期事件
2.Vue生命周期方法分类
2.1创建期间的生命周期方法
beforeCreate:在调用beforeCreate的时候, 仅仅表示Vue实例刚刚被创建出来
此时此刻还没有初始化好Vue实例中的数据和方法, 所以此时此刻还不能访问Vue实例中保存的数据和方法
created:在调用created的时候, 是我们最早能够访问Vue实例中保存的数据和方法的地方
beforeMount:在调用beforeMount的时候, 表示Vue已经编译好了最终模板, 但是还没有将最终的模板渲染到界面上
mounted:在调用mounted的时候, 表示Vue已经完成了模板的渲染, 表示我们已经可以拿到界面上渲染之后的内容了
2.2运行期间的生命周期方法
beforeUpdate:在调用beforeUpdate的时候, 表示Vue实例中保存的数据被修改了
注意点: 只有保存的数据被修改了才会调用beforeUpdate, 否则不会调用
注意点: 在调用beforeUpdate的时候, 数据已经更新了, 但是界面还没有更新
updated:在调用updated的时候, 表示Vue实例中保存的数据被修改了, 并且界面也同步了修改的数据了
也就是说: 数据和界面都同步更新之后就会调用updated
2.3销毁期间的生命周期方法
beforeDestroy:在调用beforeDestroy的时候, 表示当前组件即将被销毁了
注意点: 只要组件不被销毁, 那么beforeDestroy就不会调用
beforeDestroy函数是我们最后能够访问到组件数据和方法的函数
** destroyed**:在调用destroyed的时候, 表示当前组件已经被销毁了
注意点: 只要组件不被销毁, 那么destroyed就不会调用
不要在这个生命周期方法中再去操作组件中数据和方法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>68-Vue-生命周期方法</title>
    <script src="js/vue.js"></script>
</head>
<body>
<!--
1.什么是生命周期方法?
和wbpack生命周期方法一样, 都是在从生到死的特定阶段调用的方法
PS: 生命周期钩子 = 生命周期函数 = 生命周期事件
2.Vue生命周期方法分类
2.1创建期间的生命周期方法
    beforeCreate:
    created:
    beforeMount:
    mounted:
2.2运行期间的生命周期方法
    beforeUpdate:
    updated:
2.3销毁期间的生命周期方法
    beforeDestroy:
    destroyed:
-->
<!--这里就是MVVM中的View-->
<div id="app">
    <p>{{msg}}</p>
</div>
<!--
<div id="app">
    <p>知播渔</p>
</div>
-->

<script>
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        beforeCreate:function(){
            /*
            在调用beforeCreate的时候, 仅仅表示Vue实例刚刚被创建出来
            此时此刻还没有初始化好Vue实例中的数据和方法, 所以此时此刻还不能访问Vue实例中保存的数据和方法
            * */
            // console.log(this.msg);
            // console.log(this.say);
        },
        created:function(){
            /*
            在调用created的时候, 是我们最早能够访问Vue实例中保存的数据和方法的地方
            * */
            // console.log(this.msg);
            // console.log(this.say);
        },
        beforeMount:function(){
            /*
            在调用beforeMount的时候, 表示Vue已经编译好了最终模板, 但是还没有将最终的模板渲染到界面上
            * */
            // console.log(document.querySelector("p").innerHTML);
            // console.log(document.querySelector("p").innerText);
        },
        mounted:function(){
            /*
            在调用mounted的时候, 表示Vue已经完成了模板的渲染, 表示我们已经可以拿到界面上渲染之后的内容了
            * */
            console.log(document.querySelector("p").innerHTML);
            console.log(document.querySelector("p").innerText);
        },
        el: '#app',
        // 专门用于监听数据变化的
        watch: {
        },
        // 这里就是MVVM中的Model
        data: {
            msg: "知播渔"
        },
        // 专门用于存储监听事件回调函数
        methods: {
            say(){
                console.log("say");
            }
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
        }
    });
</script>
</body>
</html>
Vue插件(将通用的功能封装起来)
<!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>vue插件</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script>
    //插件 :将通用的功能封装起来
    const myPlugin = {
      install(app, options) {
        app.provide('name', 'andy');
        app.directive('focus', {
          mounted(el) {
            el.focus();
          },
        });
        app.mixin({
          mounted() {
            console.log('mixin');
          },
        });
        app.config.globalProperties.$sayHello = 'hello world';
      },
    };
    const app = Vue.createApp({
      template: `
      <my-title/>
      `,
    });

    app.component('my-title', {
      inject: ['name'],
      created() {
        console.log(this.$sayHello);
      },
      template: `
      <div>{{name}}<input v-focus/></div>
      `,
    });
    //使用插件
    app.use(myPlugin, { name: 'andy' });
    const vm = app.mount('#root');
  </script>
</html>
ref属性

1.Vue特殊特性
Vue特点: 数据驱动界面更新, 无需操作DOM来更新界面
也就是说Vue不推荐我们直接操作DOM, 但是在企业开发中有时候我们确实需要拿到DOM操作DOM
那么如果不推荐使用原生的语法获取DOM, 我们应该如何获取DOM?
在Vue中如果想要拿到DOM元素我们可以通过ref来获取

2.ref使用
2.1在需要获取的元素上添加ref属性. 例如:

我是段落</>
2.2在使用的地方通过 this. r e f s . x x x 获 取 , 例 如 t h i s . refs.xxx获取, 例如 this. refs.xxx,this.ref.myppp

3.ref特点
ref添加到元素DOM上, 拿到的就是元素DOM
ref添加到组件上, 拿到的就是组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>71-Vue-特殊特性</title>
    <script src="js/vue.js"></script>
</head>
<body>
<!--
1.Vue特殊特性
Vue特点: 数据驱动界面更新, 无需操作DOM来更新界面
也就是说Vue不推荐我们直接操作DOM, 但是在企业开发中有时候我们确实需要拿到DOM操作DOM
那么如果不推荐使用原生的语法获取DOM, 我们应该如何获取DOM?
在Vue中如果想要拿到DOM元素我们可以通过ref来获取

2.ref使用
2.1在需要获取的元素上添加ref属性. 例如: <p ref="mypp">我是段落</>
2.2在使用的地方通过 this.$refs.xxx获取, 例如 this.$ref.myppp

3.ref特点
ref添加到元素DOM上, 拿到的就是元素DOM
ref添加到组件上, 拿到的就是组件
-->
<!--这里就是MVVM中的View-->
<div id="app">
    <button @click="myFn">我是按钮</button>
    <p ref="myppp">我是原生的DOM</p>
    <one id="myOne" ref="myOne"></one>
</div>
<template id="one">
    <div>
        <p>我是组件</p>
    </div>
</template>
<script>
    Vue.component("one", {
        template: "#one",
        data: function(){
            return {
                msg: "知播渔"
            }
        },
        methods: {
            say(){
                console.log("say");
            }
        },
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        // 专门用于监听数据变化的
        watch: {
        },
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
            myFn(){
                // 注意点: 如果是通过原生的语法来获取元素, 无论是原生的元素还是自定义的组件, 拿到的都是原生的元素
                // 注意点: 并且VUE官方并不推荐我们这样获取
                // console.log(document.querySelector("p"));
                // console.log(document.querySelector("#myOne"));

                // 在Vue中如果想获取原生的元素或者获取自定义的组件, 可以通过ref来获取
                // 注意点: ref如果是添加给元素的元素, 那么拿到的就是元素的元素
                //         ref如果是添加给自定义的组件, 那么拿到的就是自定义的组件
                console.log(this.$refs);
                console.log(this.$refs.myppp);
                console.log(this.$refs.myOne);
                console.log(this.$refs.myOne.msg);
                console.log(this.$refs.myOne.say);
            }
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
        }
    });
</script>
</body>
</html>
组件的第二种渲染方式-render

1.Vue渲染组件的两种方式
1.1先定义注册组件, 然后在Vue实例中当做标签来使用
1.2先定义注册组件, 然后通过Vue实例的render方法来渲染

2.两种渲染方法的区别
1.1当做标签来渲染, 不会覆盖Vue实例控制区域
1.2通过render方法来渲染, 会覆盖Vue实例控制区域

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>72-Vue-组件渲染方式</title>
    <script src="js/vue.js"></script>
</head>
<body>
<!--
1.Vue渲染组件的两种方式
1.1先定义注册组件, 然后在Vue实例中当做标签来使用
1.2先定义注册组件, 然后通过Vue实例的render方法来渲染

2.两种渲染方法的区别
1.1当做标签来渲染, 不会覆盖Vue实例控制区域
1.2通过render方法来渲染, 会覆盖Vue实例控制区域
-->
<!--这里就是MVVM中的View-->
<div id="app">
<!--    <one></one>-->
</div>
<template id="one">
    <div>
        <p>我是组件222</p>
    </div>
</template>
<script>
    Vue.component("one", {
        template: "#one"
    });
    // 这里就是MVVM中的View Model
    let vue = new Vue({
        el: '#app',
        render: function(createElement){
            let html = createElement("one");
            return html;
        },
        // 专门用于监听数据变化的
        watch: {
        },
        // 这里就是MVVM中的Model
        data: {
        },
        // 专门用于存储监听事件回调函数
        methods: {
        },
        // 专门用于定义计算属性的
        computed: {
        },
        // 专门用于定义局部组件的
        components: {
        }
    });
</script>
</body>
</html>
Vue-CLI

1.什么是Vue-CLI(Command Line Interface)?
Vue-CLI是vue官方提供的脚手架工具,
默认已经帮我们搭建好了一套利用webpack管理vue的项目结构

2.如何安装Vue-cli和使用Vue-CLI?
安装脚手架工具: npm install -g @vue/cli
检查是否安装成功: vue --version
通过脚手架创建项目: vue create project-name

3.通过Vue-CLI生成的项目结构解读
3.1在Vue-CLI2.x中生成的项目结构中我们能够看到build文件夹和config文件夹
这两个文件夹中存储了webpack相关的配置, 我们可以针对项目需求修改webpack配置
3.2在Vue-CLI3以后生成的项目结构中已经没有了build文件夹和config文件夹
这么做的目的是为了化繁为简, 让初学者不用关心webpack, 只用关心如何使用Vue

node_modules文件夹: 存储了依赖的相关的包
public文件夹: 任何放置在 public 文件夹的静态资源都会被简单的复制,
而不经过 webpack。你需要通过绝对路径来引用它们
一般用于存储一些永远不会改变的静态资源或者webpack不支持的第三方库
src文件夹: 代码文件夹
|----assets文件夹: 存储项目中自己的一些静态文件(图片/字体等)
|----components文件夹: 存储项目中的自定义组件(小组件,公共组件)
|----views文件夹: 存储项目中的自定义组件(大组件,页面级组件,路由级别组件)
|----router文件夹: 存储VueRouter相关文件
|----store文件夹: 存储Vuex相关文件
|----App.vue:根组件
|----main.js:入口js文件

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值