Vue2基础

目录

一、初识Vue

二、Vue模板语法({{}} / v-bind)

三、数据绑定(v-model)

四、el与data的两种写法

五、MVVM模型

六、数据代理

1、Object.defineProperty

2、Vue中的数据代理

七、事件处理(v-on: / @)

1、事件的基本使用

2、事件修饰符

3、键盘事件

4、总结

八、计算属性(computed)

1、完整写法

2、简写

九、监视属性(watch)

1、基本使用

2、深度监视

3、简写

4、watch和computed的对比

十、绑定样式

十一、条件渲染

十二、列表渲染(v-for)

1、基本列表

2、key的作用与原理

十三、Vue监测数据的原理

十四、收集表单数据

十五、过滤器

十六、内置指令

1、已经学过的指令

2、v-text

3、v-html

4、v-clock

5、v-once

6、v-pre

十七、自定义指令

1、简写(函数式) 

2、完整写法(对象式)

3、细节

(1)、指令名用-分隔

(2)、this指向window

(3)、全局指令

(4)、总结

十八、生命周期

十九、组件化开发

1、非单文件组件

(1)、基本使用

(2)、注意细节

(3)、组件嵌套

(4)、VueComponent

(5)、一个内置关系

2、单文件组件

二十、Vue脚手架(cli)

1、创建

2、render函数

3、脚手架默认配置

(1)、不可更改的配置

(2)、修改配置(vue.config.js)

4、ref属性​编辑

5、props配置

(1)、基本使用

(2)、用于简单的父子通信 

6、mixin(混入)

7、插件

8、scoped

9、浏览器本地存储

10、组件的自定义事件(子传父)

(1)、基本使用

(2)、解绑自定义事件

(3)、父组件的this指向

(4)、给组件绑定原生事件(native)

(5)、总结

​编辑

11、全局事件总线(任意组件间通信)

12、消息订阅与发布(pubsub-js 任意组件通信)

(1)、订阅消息(接收消息)

(2)、发布消息(发送消息)

13、$nextTick

14、动画与过度

(1)、动画

(2)、过度

(3)、多个元素有相同的动画(transition-group)

​编辑 (4)、第三方动画库(animate.css)

(5)、总结

15、插槽

(1)、默认插槽

(2)、具名插槽

(3)、作用域插槽

16、Vuex

(1)、配置

(2)、基本使用

(3)、getters配置项

(4)、mapState

(5)、mapGetters

(6)、mapActions

(7)、mapMutations

(8)、Vuex模块化和命名空间

17、路由

(1)、基本使用

(2)、注意点

(3)、嵌套路由(多级路由)

(4)、路由传参(query参数)

(5)、命名路由

(6)、路由传参(params参数)

(7)、路由的props配置

(8)、的replace属性

(9)、编程式路由导航

(10)、缓存路由组件

(11)、生命周期钩子activated、deactivated

(12)、路由守卫(全局守卫)

(13)、路由守卫(独享守卫)

(14)、路由守卫(组件内守卫)

(15)、解决history路由刷新404问题(node.js)

18、组件库

(1)、移动端常用 UI 组件库

(2)、PC 端常用 UI 组件库

(3)、element UI

二十一、Vue3


一、初识Vue

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>第一次使用vue</title>
    <!--vue js生产版本production版本-->
    <script src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
     <!--插值语法-->
     <!--vue模版-->
        <h1>hello, {{ name }}</h1>
    </div>
    <div class="test">
        <!--注意双花括号中可以写js表达式-->
        <!--
          js表达式:一个表达式生成一个值,放在任何一个需要值的地方
                例如 (1).a (2) 1+b (3) demo(1) (4) x===y ? 1 : 0

          js语句: if while for...
        -->
        <h1>{{name.toUpperCase()}},{{address}} {{1+1}} {{Date.now()}}</h1>
    </div>
    <script type="text/javascript">
       //hello小案例
       Vue.config.productionTip = false; //默认不提示
       //创建vue事例对象
       /**
        * 注意:一个vue实例不可能去接管多个容器,如果有多个容器的情况,vue事例永远只接管第一个容器
        * 不能多个vue实例同时来接管同一个容器,如果有多个的情况下永远是第一个vue实例来接管该容器
        * vue实例与容器直接的对应关系是一对一的
        */
       new Vue({
           //配置对象
           el: '#root',// document.getElementById('root') //element el指定当前vue实例为哪一个容器服务,值通常为css选择器格式
           data: {
               //data用来存储数据,以后会用函数来表示
               name:'root',
               age:21
           }
       });

       new Vue({
           el: '.test',
           data:{
               name: 'test',
               address: 'sfaef'
           }
       })
    </script>
</body>
</html>

二、Vue模板语法({{}} / v-bind)

插值语法、指令语法

v-bind:可以简写成:

三、数据绑定(v-model)

v-model         v-model:value可以简写成v-model

四、el与data的两种写法

或者直接写data(),不能写箭头函数

注意:

五、MVVM模型

六、数据代理

1、Object.defineProperty

2、Vue中的数据代理

七、事件处理(v-on: / @)

1、事件的基本使用

2、事件修饰符

3、键盘事件

4、总结

事件修饰符可以连着写

ctrl可以和其他键组合写

八、计算属性(computed)

1、完整写法

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>vue计算属性语法实现</title>
    <script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
    <!--
			计算属性:
					1.定义:要用的属性不存在,要通过已有属性计算得来。
					2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
					3.get函数什么时候执行?
								(1).初次读取时会执行一次。
								(2).当依赖的数据发生改变时会被再次调用。
					4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
					5.备注:
							1.计算属性最终会出现在vm上,直接读取使用即可。
							2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
		 -->
    <!--v-model双向绑定-->
    姓:<input type="text" v-model="firstName"/>
    <br/><br/>
    名:<input type="text" v-model="lastName"/>
    <br/><br/>
    测试:<input type="text" v-model="test"/>
    <br/><br/>
    全名: <span>{{ fullName }}</span>
</div>
<script type="text/javascript">
    const vm = new Vue({
        el: '#root',
        data: {
            //data里的都是属性
            firstName: '张',
            lastName: '三',
            test:'test'
        },
        //计算属性--> 旧属性加工
        computed: {
            fullName: {
                //get的作用,当读取fullName时get就会被调用,且返回值就做为fullName的值
                //defineProperty
                //注意vm._data是不存在计算属性的
                //计算属性算完之后直接丢到了viewModel实例对象身上
                /**
                 * get的调用时机
                 * 1.初次读取计算属性时
                 * 2.所依赖的数据(data中的属性)发生变化时,不改变的话直接读缓存
                 * 3.methods没有缓存,读几次就调用几次无论要用的数据是否发生变化
                 *  @returns {string}
                 */
                get(){
                    //此时get函数中的this指向是vm
                    console.log('get调用了', this);
                    return this.firstName + '--' + this.lastName
                },
                /**
                 * set什么时候调用
                 *   1.当fullName被修改时
                 * @param value
                 */
                set(value){
                    //修改计算属性所依赖的普通属性(放在data里面的)
                    //this === vm
                    const [ firstName, lastName ] = value.split('-');
                    this.firstName = firstName;
                    this.lastName = lastName; //依赖属性发生改变之后,计算属性自动改变
                }
            }
        }
    });
</script>
</body>
</html>

2、简写

只getter不setter时可以使用

computed: {
            //简写形式
            //前提:计算属性只考虑读取不考虑修改 set丢了
            //简写计算书写为一个函数(这个函数当成getter使用), 注意不要写箭头函数
            //执行完getter之后,vm直接保存返回的数据为fullName属性的属性值,此时vm.fullName不是函数而是一个确切的计算值
            fullName: function (){
                return this.firstName + '--' + this.lastName;
            }
}

九、监视属性(watch)

1、基本使用

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>天气案例_监视属性</title>
    <script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
         <!--
				监视属性watch:
					1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作
					2.监视的属性必须存在,才能进行监视!!
					3.监视的两种写法:
							(1).new Vue时传入watch配置
							(2).通过vm.$watch监视
		 -->
    <h1>今天天气很 {{ info }}</h1>
    <button @click="changeWeather">
        切换
    </button>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    const vm = new Vue({
        el:'#root',
        data:{
            isHot: true
        },
        //计算属性
        computed: {
            info(){
                return this.isHot ? '炎热' : '凉爽';
            }
        },
        //改变模版数据的方法
        methods:{
            changeWeather(){
                this.isHot = !this.isHot;
            }
        },
        // watch:{
        //     //监视的配置对象
        //     //watch不仅能监视data的普通属性,也可以检测计算属性
        //     isHot:{
        //         immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
        //         //handler 什么时候调用呢
        //         //当isHot发生改变就会调用该函数
        //         //handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
        //         handler(newValue, preValue){
        //             console.log('ishot 被修改了');
        //             console.log(`newValue: ${newValue}, preValue: ${preValue}`);
        //         }
        //     }
        // }
    });
    //watch的第二种写法,直接采用vm对象实例
    vm.$watch('isHot', {
        immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
        //handler 什么时候调用呢
        //当isHot发生改变就会调用该函数
        //handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
        handler(newValue, preValue){
            console.log('ishot 被修改了');
            console.log(`newValue: ${newValue}, preValue: ${preValue}`);
        }
    });
</script>
</body>
</html>

2、深度监视

watch:{
            //监测多级结构里面的属性 深度监视
            // 'numbers.a':{
            //     handler(){
            //       console.log('a发生改变了')
            //     }
            // }
            //深度监视numbers中的所有属性
            numbers:{
                deep: true, //监视多级结构的属性
                handler(){
                    console.log('numbers 发生改变')
                }
            }
        }

3、简写

不需要immediate/deep属性时

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>天气案例_深度监视简写形式</title>
    <script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
    <h1>今天天气很 {{ info }}</h1>
    <button @click="changeWeather">
        切换
    </button>
    <hr/>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    const vm = new Vue({
        el:'#root',
        data:{
            isHot: true,
        },
        //计算属性
        computed: {
            info(){
                return this.isHot ? '炎热' : '凉爽';
            }
        },
        //改变模版数据的方法
        methods:{
            changeWeather(){
                this.isHot = !this.isHot;
            }
        },
        // watch:{
        //     //监视的配置对象
        //     //watch不仅能监视data的普通属性,也可以检测计算属性
        //     // isHot:{
        //     //     //immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
        //     //     //handler 什么时候调用呢
        //     //     //当isHot发生改变就会调用该函数
        //     //     //handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
        //     //     //deep: true  //简写
        //     //     handler(newValue, preValue){
        //     //         console.log('ishot 被修改了');
        //     //         console.log(`newValue: ${newValue}, preValue: ${preValue}`);
        //     //     }
        //     // }
        //     //简写:
        //     //简写的前提watch的属性不需要immediate和deep属性的时候
        //     // isHot(newValue, preValue){
        //     //     console.log(newValue,preValue);
        //     // }
        // }
    });
    //完整写法
    // vm.$watch('isHot', {
    //     deep: true,
    //     immediate: true,
    //     handler(newValue, preValue){
    //         console.log(newValue, preValue);
    //     }
    // });
    //简写 (代价就是不能配置其他配置项deep immediate)
    vm.$watch('isHot', function (newValue, preValue){
        //this === vm
        console.log(newValue, preValue);
    })
</script>
</body>
</html>

4、watch和computed的对比

十、绑定样式

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>vue绑定样式</title>
    <style>
        .basic{
            width: 400px;
            height: 100px;
            border: 1px solid black;
        }

        .happy{
            border: 4px solid red;;
            background-color: rgba(255, 255, 0, 0.644);
            background: linear-gradient(30deg,yellow,pink,orange,yellow);
        }
        .sad{
            border: 4px dashed rgb(2, 197, 2);
            background-color: gray;
        }
        .normal{
            background-color: skyblue;
        }

        .atguigu1{
            background-color: yellowgreen;
        }
        .atguigu2{
            font-size: 30px;
            text-shadow:2px 2px 10px red;
        }
        .atguigu3{
            border-radius: 20px;
        }

    </style>
    <script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
    <!--
			绑定样式:
					1. class样式
								写法:class="xxx" xxx可以是字符串、对象、数组。
										字符串写法适用于:类名不确定,要动态获取。
										对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
										数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
					2. style样式
							:style="{fontSize: xxx}"其中xxx是动态值。
							:style="[a,b]"其中a、b是样式对象。
	-->

    <!--:class 绑定class样式字符串写法 适用于样式的类名不确定需要动态琢磨的状况-->
    <div class="basic" :class="mood"  @click="changeMood">{{ name }}</div>
    <hr/>
    <!--:class 绑定class样式数组写法 适用于要绑定的样式个数不确定,名字也不确定的状况-->
    <div class="basic" :class="classArr">{{ name }}</div>
    <hr/>
    <!--:class 绑定class样式对象写法 适用于要绑定的样式个数确定,名字确定,但动态决定要不要用的状况-->
    <div class="basic" :class="classObj" >{{ name }}</div>
    <hr/>
    <!--绑定style样式 对象写法-->
    <div class="basic" :style="styleObj">
        {{ name }}
    </div>
    <hr/>
    <!--绑定style样式 数组写法-->
    <div class="basic" :style="[styleObj, styleObj2]">
        {{ name }}
    </div>
    <hr/>
    <div class="basic" :style="styleArr">
        {{ name }}
    </div>
</div>
<script type="text/javascript">
    new Vue({
       el:'#root',
       data: {
           name:'test',
           mood:null,
           classArr:['atguigu1', 'atguigu2', 'atguigu3'],
           classObj:{
               atguigu1: false,
               atguigu2: false
           },
           styleObj:{
               fontSize: '40px',
               color: 'red',
           },
           styleObj2:{
               background: 'green'
           },
           styleArr:[
               { color: 'orange' },
               { background: 'grey' }
           ]
       },
       methods:{
         changeMood(){
             //vue绑定事件
             //不要亲手操作dom
             //随机切换心情
             const moodsArr = ['normal', 'happy', 'sad'];
             const random = Math.floor(Math.random() * moodsArr.length);
             this.mood = moodsArr[random];
         }
       }
    });
</script>
</body>
</html>

十一、条件渲染

使用后没有template的结构

十二、列表渲染(v-for)

1、基本列表

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>基本列表</title>
    <script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
    <!--
			v-for指令:
						1.用于展示列表数据
						2.语法:v-for="(item, index) in xxx" :key="yyy"
						3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
	-->
    <h2>人员列表</h2>
    <ul>
        <!--遍历数组-->
        <!--循环列表的方法 类似与for in循环遍历-->
        <!--:代表v-bind 属性key让每一个li有了唯一的标识,key一定不要重复-->
        <!--v-for(for in// for of)可以接受到两个参数,一个是当前的元素另一个是当前元素的索引 类似于下面的person,index-->
        <li v-for='(person, index) in persons' :key="index">
            <!--p可能来自形参,也可能来自于写在data里的属性,更可能来自于计算属性 computed-->
            {{person.name}} - {{ person.age }}
        </li>
    </ul>
    <!--遍历对象-->
    <h2>汽车信息</h2>
    <!--注意遍历对象的时候先收到的是每一项的属性的value,第二项是对应的键名:key-->
    <ul v-for="(val, key) of car" :key="key">
         <li>{{ key }} -- {{ val }}</li>
    </ul>
    <!--遍历字符串 用的不多-->
    <h2>测试遍历字符串</h2>
    <!--注意遍历字符串的时候先收到的是字符串中每一个字符,第二项是其对应的索引index-->
    <ul v-for="(c, index) of str" :key="index">
        <li>{{ index }} -- {{ c }}</li>
    </ul>
    <!--遍历指定次数-->
    <h2>遍历指定次数</h2>
    <!--注意遍历指定次数的时候先收到的是number(例如5,则number是1,2,3,4,5),第二项是对应index(从0开始计数,则是0,1,2,3,4)-->
    <ul v-for="(num, index) in 5" :key="index">
        <li>{{ index }} -- {{ num }}</li>
    </ul>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    new Vue({
        el: '#root',
        data: {
            //数组
            persons: [
                {id: '001', name: '张三', age: 18},
                {id: '002', name: '李四', age: 19},
                {id: '003', name: '王五', age: 20}
            ],
            car: {
                name: '奥迪a8',
                price: '70w',
                color: '黑色'
            },
            str: 'hello'
        }
    })
</script>
</body>
</html>

2、key的作用与原理

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>基本列表</title>
    <script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
  <!--
  面试题:react、vue中的key有什么作用?(key的内部原理)

    1. 虚拟DOM中key的作用:
      key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,
      随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

    2.对比规则:
      (1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
            ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
            ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

      (2).旧虚拟DOM中未找到与新虚拟DOM相同的key
            创建新的真实DOM,随后渲染到到页面。

    3. 用index作为key可能会引发的问题:
      1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
              会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

      2. 如果结构中还包含输入类的DOM:
              会产生错误DOM更新 ==> 界面有问题。

    4. 开发中如何选择key?:
      1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
      2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,
        使用index作为key是没有问题的。
  -->
    <h2>人员列表</h2>
    <button @click.once="add">添加一个老刘</button>
    <ul>
        <!--key唯一标识: 身份证,属性key是被vue给征用的,并不反应在真实dom上-->
        <li v-for='(person, index) in persons' :key="person.id">
            {{person.name}} - {{ person.age }}
            <!--为了看到key值的不正确滥用所导致的问题,我们添加一个input框-->
            <input type="text" />
        </li>
    </ul>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    new Vue({
        el: '#root',
        data: {
            //数组
            persons: [
                {id: '001', name: '张三', age: 18},
                {id: '002', name: '李四', age: 19},
                {id: '003', name: '王五', age: 20}
            ],
        },
        methods:{
            add(){
                //往数组的头添加元素
                this.persons.unshift({
                    id:'004',
                    name:'老刘',
                    age: 40
                })
            }
        }
    })
</script>
</body>
</html>

十三、Vue监测数据的原理

十四、收集表单数据

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>vue中表单数据的收集</title>
    <script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
    <form @submit.prevent="demo">
        <!--写了label则点击它也能使指定的input获取焦点 for属性的值为指定元素的id-->
        <label for="demo">账号:</label>
        <!--v-model主要用来双向绑定输入类表单value值-->
        <input type="text" id="demo" v-model.trim="userInfo.account"/>
        <br/>
        密码: <input type="password" v-model="userInfo.password"/>
        <br/>
        性别:
        <!--一组radio单选框的name值一定要相同 设置value值好让v-model去双向绑定-->
        男:<input type="radio" v-model="userInfo.sex" name="sex" value="male"/>
        女:<input type="radio" v-model="userInfo.sex" name="sex" value="female"/>
        <br/>
        年龄: <input type="number" v-model.number="userInfo.age"/>
        <br/>
        爱好:
        <!--如果没有value值则v-model收集checked元素-->
        学习 <input v-model="userInfo.hobby" type="checkbox" value="study"/>
        打游戏 <input v-model="userInfo.hobby" type="checkbox" value="game"/>
        吃饭 <input v-model="userInfo.hobby" type="checkbox" value="eat" />
        <br/>
        所属校区
        <select v-model="userInfo.city">
            <option value="">请选择校区</option>
            <option value="Beijing">北京</option>
            <option value="Shanghai">上海</option>
            <option value="Shenzhen">深圳</option>
            <option value="Wuhan">武汉</option>
        </select>
        <br/>
        其他信息<textarea v-model.lazy="userInfo.other"></textarea>
        <br/>
        <input type="checkbox" v-model="userInfo.ifAgree"/>阅读并接受<a href="https://www.google.com.tw">《用户协议》</a>
        <button>提交数据</button>
    </form>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    const vm = new Vue({
        el: '#root',
        data:{
            userInfo:{
                account: '',
                password: '',
                sex: 'male',
                age:'',
                hobby:[],
                city:'',
                other:'',
                ifAgree:''
            }
        },
        methods:{
            demo(){
                //json转换为string
                console.log(JSON.stringify(this.userInfo));
            }
        }
    })
</script>
</body>
</html>

十五、过滤器

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>vue中过滤器</title>
    <script src="../js/vue.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/dayjs.min.js"></script>
</head>
<body>
<div id="root">
    <h1>显示格式化后的时间</h1>
    <!--计算属性实现-->
    <h2>现在是:{{ fmtTime }}</h2>
    <!--methods实现-->
    <h2>现在是{{ getFmtTime() }}</h2>
    <!--过滤器实现-->
    <h2>现在是:{{ time |  timeFormater }}</h2>
    <!--过滤器也可以传递参数-->
    <h2>现在是:{{ time | timeFormater('YYYY-MM-DD') | mySlice }}</h2>
    <h3 :x="msg | mySlice">你好,世界</h3>
</div>
<div id="root2">
   <h2>{{ msg | mySlice }}</h2>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    //全局过滤器的配置
    //注意配置一定要new vue实例之前确定
    Vue.filter('mySlice', function (val){
        return val.slice(0,4);
    });
    new Vue({
        el: "#root",
        data:{
            time: 1631808423062,
            msg: "你好,世界"
        },
        computed:{
            fmtTime(){
                return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
            }
        },
        methods:{
            getFmtTime(){
                return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
            }
        },
        //局部过滤器
        filters:{
            //过滤器本质上也是一个函数
            timeFormater(val, formate = 'YYYY-MM-DD HH:mm:ss'){
                return dayjs(val).format(formate)
            },
        }
    });

    const vm2 = new Vue({
        el:"#root2",
        data:{
            msg:'welcome'
        }
    })
</script>
</body>
</html>

十六、内置指令

1、已经学过的指令

2、v-text

3、v-html

4、v-clock

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>v-cloak指令</title>
    <style>
        [v-cloak]{
            display:none;
        }
    </style>
    <!-- 引入Vue -->
</head>
<body>
<!--
        v-cloak指令(没有值):
                1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
                2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
-->
<!-- 准备好一个容器-->
<div id="root">
    <h2 v-cloak>{{name}}</h2>
</div>
<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
</body>

<script type="text/javascript">
    console.log(1)
    Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

    new Vue({
        el:'#root',
        data:{
            name:'尚硅谷'
        }
    })
</script>
</html>

5、v-once

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>v-once指令</title>
    <script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
    <!--v-once-->
    <!--
		v-once指令:
		1.v-once所在节点在初次动态渲染后,就视为静态内容了。
		2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
	-->
    <h2 v-once>初始化n的值为:{{ n }}</h2>
    <h2>当前的n值为:{{ n }}</h2>
    <button @click="n++">带我n+1</button>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    new Vue({
        el:"#root",
        data:{
           n:1
        }
    })
</script>
</body>
</html>

6、v-pre

十七、自定义指令

1、简写(函数式) 

2、完整写法(对象式)

 

3、细节

(1)、指令名用-分隔

 

(2)、this指向window

所有指令函数里的this都指向window

(3)、全局指令

(4)、总结

十八、生命周期

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>分析vue中生命周期</title>
    <script src="../js/vue.js"></script>
</head>
<body>
<div id="root" :x="n">
    <h1>当前的n值是{{ n }}</h1>
    <h1 v-text="n"></h1>
    <button @click="add">点我+1</button>
    <button @click="bye">点我销毁vm</button>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    new Vue({
        el:"#root",
        //template模版字符串只能有一个根结点
        // template:'<div><h1>当前的n值是{{ n }}</h1>\n' +
        //     '<button @click="add">点我+1</button></div>',
        //注意template是不能作为根标签来使用的,不能去骗vue,可以用fragment(vue3新加,模仿react)
        data: {
            n: 1
        },
        methods:{
            add(){
                console.log('add')
                this.n++;
            },
            bye(){
                //实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的(自定义)事件监听器被移除,所有的子实例也都被销毁。
                console.log('bye');
                this.$destroy();
            }
        },
        watch:{
            n(){
                console.log('n变了');
            }
        },
        beforeCreate(){
            console.log('beforeCreate');
            // console.log(this);

        },
        created(){
            console.log('created');
            // console.log(this);
        },
        beforeMount(){
            console.log('beforeMount');
            // console.log(this);
        },
        mounted(){
            console.log('mounted');
            console.log(this);
            // document.querySelector('h1').innerText = 'hahah';
        },
        beforeUpdate(){
            console.log('beforeUpdate');
            //console.log(this.n); //此时数据是新的,页面还是旧的,vue还没根据新的数据去生成新的虚拟dom,去比较旧的虚拟dom
        },
        updated(){
            console.log('updated');
            console.log(this.n); //此时数据是新的,页面也是新的,同步
        },
        beforeDestroy(){
            //仍然可以使用data,methods,关闭定时器,取消订阅消息,解绑自定义事件等收尾工作,移除watchers
            console.log('beforeDestroy');
            console.log(this.n);
            // this.add(); //记住一旦到了beforeDestroy或者destroyed钩子,即使你拿到数据想要更新它也不会走更新的路了(beforeUpdate,updated)
        },
        //destroyed钩子几乎不用
        destroyed(){
          console.log('destroyed');
        }
    });


</script>
</body>
</html>

十九、组件化开发

1、非单文件组件

(1)、基本使用

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>非单文件组件的基本使用</title>
    <script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
    <h1>
        {{ msg }}
    </h1>
    <hello></hello>
    <!--使用组件-->
    <school></school>
    <hr/>
    <student></student>
    <hr/>
</div>
<div id="root2">
   <h2>root2容器</h2>
    <hello></hello>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;

    //全部注册

    /**
     * 想用组件的三个步骤
     * 1.创建组件
     * 2.注册组件
     * 3.使用组件
     */
        //创建school组件
    const school = Vue.extend({
            template: `
              <div>
              <h2>学校名称:{{ schoolName }}</h2>
              <h2>学校地址:{{ address }}</h2>
              <button @click="showName">点我提示学校名</button>
              </div>
            `,
            //组件定义不要写el配置项,因为最终所有的组件都要被vm所管理,由vm决定服务于哪个容器
            //这里data必须写成函数形式 避免多次使用组件导致共用data对象导致一个问题
            data() {
                //注意这里不要写箭头函数
                return {
                    schoolName: '武汉科技大学',
                    address: '武汉',
                }
            },
            methods:{
                showName(){
                    alert(this.schoolName)
                }
            }
        })
    //创建school组件
    const student = Vue.extend({
        template: `
            <div>
              <h2>学生名字:{{ studentName }}</h2>
              <h2>学生年龄:{{ age }}</h2>
            </div>
        `,
        data() {
            return {
                studentName: 'Jone',
                age: 18
            }
        }
    });


    const hello = Vue.extend({
        template:`
          <div>
            <h2>你好世界,{{ name }}</h2>
          </div>
        `,
        data(){
            return {
                name: 'panyue'
            }
        }
    });

    //全局注册hello组件
    Vue.component('hello', hello); //全局注册hello 就代表所有的vm都可以用hello组件了

    // 创建vm
    new Vue({
        el: "#root",
        //配置组件(局部注册)
        data:{
            msg: 'hello world'
        },
        components: {
            school,
            student
        },
    })

    new Vue({
       el: '#root2',
    });

</script>
</body>
</html>

(2)、注意细节

 

(3)、组件嵌套

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>组件的嵌套</title>
    <script src="../js/vue.js"></script>
</head>
<body>
<div id="root" :x="x">
    <!--组件的嵌套-->
<!--    <school>-->
<!--    </school>-->
<!--    <hello></hello>-->
<!--    <app></app>-->
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;

    const student = Vue.extend({
        template: `
           <div>
              <h2>学生姓名:{{ name }}</h2>
              <h2>学生年龄:{{ age }}</h2>

           </div>
        `,
        data(){
            return {
                name: 'JONE',
                age:13
            }
        },
    });

    const school = Vue.extend({
        template: `
          <div>
             <h1>学校名称:{{ name }}</h1>
             <h1>学校地址:{{ address }}</h1>
             <!--子组件注册给哪个父组件,就嵌套在哪个副组件里面--->
             <student></student>
          </div>
        `,

        data(){
            return {
                name: 'xxx大学',
                address:'xxxxx'
            }
        },

        //组件嵌套
        //这里也是局部注册组件
        components: {
           student
        }
    });


      const hello = Vue.extend({
          template: `<h1>{{ msg }}</h1>`,
          data(){
              return {
                  msg: 'hello, my vue world',
              }
          },
      });


      const app = Vue.extend({
          template:`<div>
            <school></school>
            <hello></hello>
          </div>`,
          components:{
              school,
              hello
          }
      });

        new Vue({
        template: `<app></app>`,
        el:"#root",
        //注册组件(局部)
        components:{
            // //schoo组件与hello组件平级
            // school,
            // hello
            app,
        },
        data:{
            x:1,
        }
    });


</script>
</body>
</html>

(4)、VueComponent

(5)、一个内置关系

2、单文件组件

school.vue

<template>
<!--  组件的交互-->
   <div class="demo">
     <h2>学校名称:{{ name }}</h2>
     <h2>学校地址:{{ address }}</h2>
     <button @click="showName">点我提示学校名</button>
   </div>
</template>

<style>
  /*css样式*/
  .demo{
    background: skyblue;
  }
</style>


<script>
  //组件交互的代码
  //export default school分别暴露
  export default {
    name: 'School', //开发者工具最终呈现的名字为School
    data(){
      return {
        name:'xxx大学',
        address: 'xxxxx'
      }
    },
    methods:{
      showName(){
        alert(this.name);
      }
    }
  };

  //统一暴露
  // export { school };
  // export default school //默认暴露
</script>

二十、Vue脚手架(cli)

1、创建

第一步(仅第一次执行):全局安装@vue/cli

npm install -g @vue/cli

第二步:切换到你要创建项目的目录,然后使用命令创建项目

vue create 项目名

第三步:启动项目

npm run served

第四步:如果慢可以切换淘宝镜像

npm config set registry https://registry.npmmirror.com/

 可以查看当前的镜像

npm config get registry

2、render函数

import Vue from 'vue' //这里引入的是残缺版的vue,是没有模版解析器是不能写template的 vue.runtime.esm.js
import App from './App.vue'
// import Vue from 'vue/dist/vue';

Vue.config.productionTip = false;

/*
	关于不同版本的Vue:

		1.vue.js与vue.runtime.xxx.js的区别:
				(1).vue.js是完整版的Vue,包含:核心功能+模板解析器。
				(2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。

		2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用
			render函数接收到的createElement函数去指定具体内容。
*/

new Vue({
  // template: `<App></App>`,
  // components: { App },
  // render: h => h(App),
  el:'#app',
  render: h => h(App),
  //vue 传递帮你调render函数并传递了一个名为createElement的函数,这里的第一个参数代表h1元素,第二个参数是h1的内容
  // render:(createElement) => createElement('h1',"迟缓")
  // el:'#app',
  // template: '<h1>你好</h1>'
}).$mount('#app')

3、脚手架默认配置

(1)、不可更改的配置

一共5个

(2)、修改配置(vue.config.js)

vue.config.js配置文件
使用vue inspect>output.js可以查看到Vue脚手架的默认配置。
使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh

4、ref属性

<template>
   <div>
     <h1 v-text="msg" ref="title"></h1>
     <button @click="showH">点我输出上方的dom元素</button>
     <School ref="sch"/>
   </div>
</template>

<script>
import School from "./components/School";

export default {
  name: "App",
  data(){
    return {
      msg: '欢迎学习Vue',
    }
  },
  methods:{
    showH(e){
      console.log(this.$refs.title); //this ==> vc(app组件)
      console.log(e.target); //发生事件的dom元素m
      console.log(this.$refs.sch); //可以是school组件加refs属性 获得的是组件事例对象vc
    }
  },
  components:{
    School,
  },
}
</script>

5、props配置

(1)、基本使用

<template>
   <div class="demo">
     <!--注意props传递过来的值是不能改的(尽量避免去改,控制台会有警告)-->
     <h1>{{ msg }}</h1>
     <h2>姓名:{{ myName }}</h2>
     <h2>年龄: {{  age }}</h2>
     <h2>性别: {{ sex }}</h2>
     <button @click="updateName">尝试修改名字</button>
   </div>
</template>

<script>
export default {
  name: "Student",
  // props: ['name', 'sex', 'age'], //简单声明接收
  data(){
    console.log(this);
    return {
      //如果props和data存在同名的属性,会报错,但已props传递的属性值为主
      //注意props属性名不能是vue底层已征用的属性名(比如key, ref等等)
      msg: '我是一名普通的大学生',
      myName: this.name //把props传递过来的值当成vc的状态,这样改name是不会出问题的,因为你没有直接去修改props
    }
  },
  methods:{
    updateName(){
      this.myName = 'sss';
    }
  },
  //限制props中属性的类型 类型错误了会提示错误信息
  // props: {
  //   name: String,
  //   sex: String,
  //   age: Number
  // }

  //接收时不仅限制类型还加上默认值的指定并指定它的必须性
  props:{
    name:{
      type: String, //类型
      required: true //必要的
    },
    age:{
      type: Number,
      default: 99 //默认值
    },
    sex:{
      type: String,
      required: true
    }
  }
}
</script>
<template>
   <div>
      <!---多个vc互不影响-->
      <!--v-bind的作用重要 获取后面表达式值的赋给props的属性-->
      <Student name="panyue" sex="男" :age='12'/>
      <hr/>
   </div>
</template>

<script>
import Student from "@/components/Student";

export default {
  name: "App",
  components:{
    Student,
  },
}
</script>

(2)、用于简单的父子通信 

父:app.vue

<template>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <MyHeader :addTodo="addTodo"/>
        <List
            :todos="todos"
            :checkTodo="checkTodo"
            :deleteTodo="deleteTodo"  //传函数
        />
        <MyFooter
            :todos="todos"
            :checkAllTodo="checkAllTodo"
            :clearAllDoneTodo="clearAllDoneTodo"
        />
      </div>
    </div>
  </div>
</template>

<script>
import MyHeader from "@/components/MyHeader";
import List from "@/components/List";
import MyFooter from '@/components/MyFooter';
export default {
  name: "App",
  components:{
    List,
    MyFooter,
    MyHeader
  },
  data() {
    return {
      todos: [
        {id: '001', title: '吃饭', done: false},
        {id: '002', title: "睡觉", done: true},
        {id: '003', title: '打代码', done: false}
      ]
    }
  },
  methods:{
    //添加的todo
    addTodo(todo){
      console.log('我是app组件,我收到了数据');
      this.todos.unshift(todo);
    },
    checkTodo(id){
      const todo = this.todos.find(todo => todo.id === id);
      todo.done = !todo.done;
    },
    deleteTodo(id){
      this.todos = this.todos.filter(todo => todo.id !== id);
    },
    checkAllTodo(done){
      this.todos.forEach(todo => todo.done = done);
    },
    clearAllDoneTodo(){
      this.todos = this.todos.filter(todo => !todo.done)
    }
  }
}
</script>

<style>
/*base*/
body {
  background: #fff;
}

.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}

.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.btn:focus {
  outline: none;
}

.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}

</style>

 子 list.vue

<template>
  <ul class="todo-main">
    <Item
        v-for="todoObj in todos"
        :key="todoObj.id"
        :todo="todoObj"
        :checkTodo="checkTodo"
        :deleteTodo="deleteTodo"
    />
  </ul>
</template>

<script>
import Item from "@/components/Item";

export default {
  name: "List",
  components: {
    Item,
  },
  props:['todos', 'checkTodo', 'deleteTodo']
}
</script>

<style scoped>
/*main*/
.todo-main {
  margin-left: 0;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}

.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
</style>

6、mixin(混入)

school.vue 

<template>
   <div >
     <h2 @click="showName">学校名称:{{  name }}</h2>
     <h2>学校地址: {{ address }}</h2>
   </div>
</template>

<script>
//引入混合
import {  mixin, shareData } from "@/mixin";
export default {
  name: "School",
  data(){
    console.log(this);
    return {
       name: 'wust',
       address: 'xxx大学'
    }
  },
  mixins:[ mixin, shareData ]
}
</script>

student.vue

<template>
   <div >
     <h2 @click="showName">姓名:{{  name }}</h2>
     <h2>性别: {{ sex }}</h2>
   </div>
</template>

<script>
import {  mixin,  shareData } from "@/mixin";
export default {
  name: "Student",
  data(){
    console.log(this);
    return {
       name: '张三',
       sex: '男',
       x:666 //如果混合中配置了与data(或者配置了相同的methods)相同的属性值,则以你的配置的属性为主(而不以mixin为主)
    }
  },
  mounted() {
    console.log('你好啊啊!!!!')  //但对于生命周期钩子是都会保存的(混合的钩子比你配置的钩子先跑)
  },
  mixins:[ mixin, shareData ]
}
</script>

mixin.js

export const mixin = {
    methods:{
        showName(){
            alert(this.name)
        }
    },
    //挂载完毕就执行
    mounted() {
        console.log('你好啊')
    }
}

export const shareData = {
    data(){
        return {
            x:100,
            y:200
        }
    }
}

main.js 全局混入

//引入Vue
import Vue from "vue";
//引入App
import App from './App';

// import { mixin, shareData } from "@/mixin";

//关闭Vue的生产提示
Vue.config.productionTip = false;
//全局混合
// Vue.mixin(mixin);
// Vue.mixin(shareData);

new Vue({
    el:'#app',
    render: h => h(App)
});

7、插件

 src/plugins.js

//vm和vc都可以用
export default {
    install(Vue){
        //vue帮你调用install方法
        // console.log('install');
        // console.log(Vue); //vm的构造函数Vue

        //全局过滤器
        Vue.filter('mySlice', function (val){
            return val.slice(0,4);
        });

        //全局指令
        Vue.directive('fbind', {
            bind(el, binding){
                // console.log('bind')
                el.value = binding.value;
            },
            //指令被插入页面时
            inserted(el, binding){
                // console.log('inserted')
                el.focus();
            },
            //指令所在模版被重新解析时
            update(el, binding){
                // console.log('update');
                el.value = binding.value;
            }
        });


        //全局混入
        Vue.mixin({
            data(){
                return {
                    x:1,
                    y:2
                }
            }
        });

        //给vue原型上添加一个方法 vc/vm都可以使用
        Vue.prototype.hello = function (){
            alert('hello')
        }
    }
}

main.js

//引入Vue
import Vue from "vue";
//引入App
import App from './App';

//引入插件
import plugins from './plugins';

//关闭Vue的生产提示
Vue.config.productionTip = false;

Vue.use(plugins); //vue应用插件

new Vue({
    el:'#app',
    render: h => h(App)
});

8、scoped

9、浏览器本地存储

10、组件的自定义事件(子传父)

(1)、基本使用

适用于子传父

父组件

第二种方法时

<template>
   <div class="app">
      <h1>{{ msg }},学生姓名是:{{ studentName }}</h1>
      <!--通过绑定一个自定义事件实现了子给父传递数据(自定义事件绑在子组件上) 第一种写法使用@或v-on-->
      <!--once代表改事件只执行一次-->
<!--      <Student @personalEvent="getStudentName" @demo="demo"/>-->
      <!--第二种写法使用ref绑定事件--->
      <Student ref="student" @click.native="show"/>
      <!--通过父组件给子组件传递函数类型的props实现了子给父传递数据-->
      <School :getSchoolName="getSchoolName"/>
   </div>
</template>

<script>
import Student from "@/components/Student";
import School from "@/components/School";
export default {
  name: "App",
  components:{
    School,
    Student,
  },
  data(){
    return {
      msg: 'helloこんにちは',
      studentName:''
    }
  },
  methods:{
    getSchoolName(name){
      console.log(`app收到了学校名,${name}`);
    },
    getStudentName(name, ...params){
      console.log('自定义');
      console.log(`app收到了学生名, ${name}`);
      this.studentName = name;
      console.log(`剩余参数,${params}`);
    },
    demo(){
      console.log('demo事件被触发了');
    },
    show(){
      console.log(`123`);
    }
  },
  mounted() {
    //可以通过ref拿到组件实例对象
    // setTimeout(() => {
    //   this.$refs.student.$on('personalEvent', this.getStudentName); //当App组件一挂载完毕经过三秒我就在Student组件上绑定事件
    //   //this.$refs.student.$once('personalEvent', this.getStudentName); //注意此时事件只执行一次就不执行了(once),一次性
    // },3000)
    //注意这里回调要写成剪头函数,不然this会丢失,这个this就是(vc)app,而不是(vc)student
    this.$refs.student.$on('personalEvent', (name) => {
       console.log(this);
       console.log(name);
       this.studentName = name;
    });
  }
}
</script>

<style>
   /*
   全局的样式是不需要加scoped
   全局共享
   */
   .app{
     background: gray;
     padding: 5px;
   }
</style>

子组件

<template>
   <div class="student">
     <h2>姓名:{{  name }}</h2>
     <h2>性别: {{ sex }}</h2>
     <h2>当前求和为:{{ number }}</h2>
     <button @click="add">点我加一</button>
     <button @click="sendStudentName">把学生名传递给app</button>
     <button @click="unbind">解绑自定义(personalEvent)事件</button>
     <button @click="death">销毁当前student组件的实例对象</button>
   </div>
</template>

<script>

export default {
  name: "Student",
  data(){
    console.log(this);
    return {
       name: '张三',
       sex: '男',
       number: 0
    }
  },
  methods:{
    add(){
      console.log(`add回调被调用了`);
      this.number ++;
    },
    sendStudentName(){
      //emit触发绑定在指定vc上的自定义事件 vc实例对象可以使用该方法
      //后面多余参数演示es6的参数收集
      this.$emit('personalEvent',this.name,666,777,888);
      // this.$emit('demo'); //同时触发两个事件
      // this.$emit('click'); 如果在组件身上使用原生事件不加native修饰符则会让vue认为你这是自定义事件
    },
    unbind(){
      //解绑事件
      this.$off('personalEvent'); //这种写法只能解绑一种自定义事件
      //this.$off([ 'personalEvent', 'demo' ]);// 解绑多个事件,参数为包含多个事件名的数组
      // this.$off(); //比较暴力,有几个自定义事件就全给你解绑了
    },
    death(){
      this.$destroy(); //销毁当前组件实例,销毁后所有该实例的自定义事件都不奏效了
    }
  }
}
</script>

<style scoped>
  .student{
    background: orange;
    padding: 5px;
    margin-bottom: 10px;
  }
</style>

(2)、解绑自定义事件

子组件

(3)、父组件的this指向

正确(建议写法)

错误 

 正确

(4)、给组件绑定原生事件(native)

(5)、总结

11、全局事件总线(任意组件间通信)

12、消息订阅与发布(pubsub-js 任意组件通信)

npm i pubsub-js

(1)、订阅消息(接收消息)

(2)、发布消息(发送消息)

13、$nextTick

$nextTick内的方法会在更新节点后执行

需求:点击按钮后出现一个输入框,输入框自动获得焦点

也可以写成下面,不推荐

14、动画与过度

需求:h1标签出现和退出的动画

(1)、动画

如果没有那么,那么默认的类名为v-enter-active,v-leave-active,有的话是name值-enter-active

appear控制一开始是否就触发动画

(2)、过度

 或

(3)、多个元素有相同的动画(transition-group)

 (4)、第三方动画库(animate.css)

npm i animate.css

(5)、总结

15、插槽

(1)、默认插槽

(2)、具名插槽

(3)、作用域插槽

父组件

<template>
   <div class="container">
     <Category title="游戏">
       <template scope="{games}">
         <!--这里data为插槽给你传递的对象包含你所需要的数据 包装成对象的原因就是你当时可能个插槽传递了多个数据-->
         <ul>
           <li v-for="(g , index) in games" :key="index">{{ g }}</li>
         </ul>
       </template>
     </Category>

     <Category title="游戏">
       <template scope="{games}">
         <!--这里data为插槽给你传递的对象包含你所需要的数据-->
         <ol>
           <li  v-for="(g , index) in games" :key="index">{{ g }}</li>
         </ol>
       </template>
     </Category>

     <Category title="游戏">
       <template slot-scope="{games}">
         <h4 v-for="(g , index) in games" :key="index">{{ g }}</h4>
       </template>
     </Category>
   </div>
</template>

<script>
import Category from "@/components/Category";
export default {
  name: "App",
  components:{
    Category
  },
}
</script>
<style lang="css" scoped>
   .container, .foot{
     display: flex;
     justify-content: space-around;
   }
</style>

子组件 

<template>
  <div class="category">
    <h3>{{ title }}</h3>
    <!--插槽,等着组件的使用者进行填充-->
    <slot :games="games">
      我是默认值
    </slot>
  </div>
</template>

<script>
export default {
  name: "Category",
  props:[ 'listData', 'title' ],
  data(){
    return {
      games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
    }
  }
}
</script>

<style scoped>
   .category{
     background: skyblue;
     width: 200px;
     height: 300px;
   }
   h3{
     text-align: center;
     background: orange;
   }
</style>

16、Vuex

(1)、配置

案例:

在main.js中

src/store/index.js

//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
/应用Vuex插件
Vue.use(Vuex)
/准备actions对象一响应组件中用户的动作
const actions = {}
//准备mutations对象一修改state中的数据
const mutations = {}
//准备state对象-保存具体的数据
const state = {}
/创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state
})

(2)、基本使用

初始化数据、配置 actions 、mutations 、state

//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//引用Vuex
Vue.use(Vuex)

const actions = {
	//响应组件中加的动作
	jia(context,value){
		context.commit('JIA',value)
	},

const mutations = {
    //执行加
	JIA(state,value){
		state.sum += value
	}
}

//初始化数据
const state = {
	sum:0
}
// 创建并暴露store
export default new Vuex.store({
    state,
    mutations,
    actions
})

main.js 配置 store

组件中读取vuex中的数据: $store.state.sum

组件中修改vuex中的数据: $store.dispatch('action中的方法名',数据) 或 $store.commit('mutations中的方法名',数据)

备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写 dispatch ,直接编写 commit

(3)、getters配置项

(4)、mapState

导入

import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'

用于帮助我们映射 state中的数据为计算属性 

// 获取store中state的 sum、school、subject,组件中直接使用{{sum}}...即可获取数据
computed:
{
	//借助mapState生成计算属性:sum、school、subject(对象写法)
	...mapState({sum:'sum',school:'school',subject:'subject'}),
        
	//借助mapState生成计算属性:sum、school、subject(数组写法)
	...mapState(['sum','school','subject']),
},

(5)、mapGetters

用于帮助我们映射 getters中的数据为计算属性

// 获取store中getters的 bigSum(),组件中直接使用{{bigSum}}即可获取数据
computed:
{
	//借助mapGetters生成计算属性:bigSum(对象写法)
	...mapGetters({bigSum:'bigSum'}),
        
	//借助mapGetters生成计算属性:bigSum(数组写法)
	...mapGetters(['bigSum'])
},

(6)、mapActions

用于帮助我们生成与 actions 对话的方法,即:包含$store .dispatch(xxx)的函数

// 获取store中actions的 jiaOdd()、jiaWait()
methods:{
	//靠mapActions生成:incrementOdd、incrementWait(对象形式)
	...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiawait'})
    
	//靠mapActions生成:incrementOdd、incrementWait(数组形式)
	...mapActions(['jiaOdd','jiaWait'])
}

(7)、mapMutations

用于帮助我们生成与mutations 对话的方法,即:包含$store.commit(xxx)的函数

// 获取store中mutations 的 JIA()、JIAN()
methods:{
	//靠mapActions生成:increment、decrement(对象形式)
	...mapMutations({increment:'JIA',decrement:'JIAN'}),
	//靠mapMutations生成:JIA、JIAN(对象形式)
	...mapMutations(['JIA','JIAN']),
}

(8)、Vuex模块化和命名空间

修改main.js

17、路由

vue2用vue-router3

vue3用vue-router4

npm i vue-router@3

(1)、基本使用

引入路由

main.js

定义路由组件

src/components/About

编写路由配置

src/router/index.js

//引入VueRouter
import VueRouter from 'vue-router'
//引入要路由的组件(路由组件应该放置于pages下,这里演示未分离组件写法)
import About from '../components/About'
import Home from '../components/Home'
//创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
	routes:[
		{
			path:'/about'		// 路径
			component:About     // 组件名
		},
		{
			path:'/home'
			component : Home
		}
	]
})
//暴露router
export default router

 实现路由切换

active-class="active"可以实现高亮

// App组件中指定点击切换的a标签改为<router-link> href改为to,注意路径
<router-link active-class="active" to="/about">About</router-link>
<router-link active-class="active" to="/home">home</router-link>

指定路由展示位置(路由出口)

// App组件中指定展示位置
<router-view></router-view>

(2)、注意点

(3)、嵌套路由(多级路由)

(4)、路由传参(query参数)

(5)、命名路由

(6)、路由传参(params参数)

 

(7)、路由的props配置

(8)、<router-link>的replace属性

(9)、编程式路由导航

(10)、缓存路由组件

1.作用:让不展示的路由组件保持挂载,不被销毁。

2.具体编码:

News是组件名,不写include的话,在此处展示的组件全部缓存,多个组件 :include="[xx,xx]"

<keep-alive include="News">       
	<router-view></router-view>
</keep-alive>

(11)、生命周期钩子activated、deactivated

 

(12)、路由守卫(全局守卫)

(13)、路由守卫(独享守卫)

// 在路由器配置项中加入beforeEnter
beforeEnter(to,from,next){
	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
        // 判断是否满足school=lhd
		if(localStorage.getItem('school')==='lhd'){
			next()     // 放行
    	}else{
			alert('暂无权限查看')
		}
	}else{
        // 不需要拦截,放行          
		next()
	}
}

(14)、路由守卫(组件内守卫)

注意:通过路由规则才调用

//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from, next){
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from,next){
}

(15)、解决history路由刷新404问题(node.js)

后端

npm i connect-history-api-fallback

18、组件库

(1)、移动端常用 UI 组件库

(1) Vant

(2) Cube Ul

(3) Mint Ul

(2)、PC 端常用 UI 组件库

(1)Element UI

(2) View UI

(3)、element UI

npm i element-ui

在main.js中引用(完整引用)

按需引入

npm i babel-plugin-component -D

在babel.config.js或.babelrc中追加

{
  "presets": [["es2015", { "modules": false }]],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

最新版脚手架可能要改成下面,否则报错

在main.js中按需引入

import Vue from 'vue';

import { Button, Select } from 'element-ui';  //按需引入

import App from './App.vue';

Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
/* 或写为
 * Vue.use(Button)
 * Vue.use(Select)
 */

new Vue({
  el: '#app',
  render: h => h(App)
});

二十一、Vue3

Vue3基础使用-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_71534259/article/details/139900842?spm=1001.2014.3001.5501

Vue3基础(二)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_71534259/article/details/139908780?spm=1001.2014.3001.5501 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值