3、Vue 笔记(axios、Vue 动画、Vue 组件、使用 this.$refs 来获取元素和组件、Vue 组件中 data 和 props 的区别)

axios 的使用

Axios 是一个基于 promise 网络请求库,作用于node.js和浏览器中,它是 isomorphic 的(即同一套代码可以运行在浏览器和 node.js 中)。在服务端它使用原生 node.js http 模块, 而在客户端(浏览端)则使用XMLHttpRequests。

1、axios 基本使用

axios 必须先导入才可以使用
使用 get 或 post 方法即可发送对应的请求
then 方法中的回调函数会在请求成功或失败时触发(成功是第一个,失败时第二个函数)
通过回调函数的形参可以获取响应内容,或错误信息

代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <input type="button" value="get请求" class="get">
    <input type="button" value="post请求" class="post">
    <!-- 官网提供的 axios 在线地址 -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        /*
            接口1:随机笑话
            请求地址:https://autumnfish.cn/api/joke/list
            请求方法:get
            请求参数:num(笑话条数,数字)
            响应内容:随机笑话
        */
        document.querySelector(".get").onclick = function () {
            axios.get("https://autumnfish.cn/api/joke/list?num=3")
                .then(function (response) {
                    console.log(response);
                }, function (err) {
                    console.log(err);
                })
        }
        /*
            接口2:用户注册
            请求地址:https://autumnfish.cn/api/user/reg
            请求方法:post
            请求参数:username(用户名,字符串)
            响应内容:注册成功或失败

        */
        document.querySelector(".post").onclick = function () {
            axios.post("https://autumnfish.cn/api/user/reg", { username: "jack" })
                .then(function (response) {
                    console.log(response);
                }, function (err) {
                    console.log(err);
                })
        }

    </script>
</body>

</html>

2、vue 中的 axios

代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <input type="button" value="获取笑话" @click="getJoke">
        <p>{{joke}}</p>
    </div>
    <!-- 官网提供的 axios 在线地址 -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script src=" https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
    <script>
        /*
            接口1:随机笑话
            请求地址:https://autumnfish.cn/api/joke
            请求方法:get
            请求参数:无
            响应内容:随机笑话
        */
        var app = new Vue({
            el: "#app",
            data: {
                joke: "joke"
            },
            methods: {
                getJoke: function () {
                    console.log(this);
                    var that = this;
                    axios.get("https://autumnfish.cn/api/joke").then(
                        function (response) {
                            // console.log(response);
                            console.log(response.data);
                            that.joke = response.data;
                        }, function (err) {
                            console.log(err);
                        }
                    )
                }
            },
        })

    </script>
</body>

</html>
axios 回调函数中的 this 已经改变,无法访问到 data 中数据
把 this 保存起来,回调函数中直接使用保存的 this 即可
和本地应用的最大区别就是改变了数据来源

Vue中的动画

链接:Vue 动画

为什么要有动画:动画能够提高用户的体验,帮助用户更好的理解页面中的功能;

1、使用过渡类名实现动画

代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 2.自定义两组样式,来控制 transition 内部的元素实现动画 -->
    <style>
        /* v-enter [这是一个时间点] 是进入之前,元素的起始状态,此时还没有开始进入 */
        /* v-leave-to [这是一个时间点] 是动画离开之后,离开的终止状态,此时,元素动画已经结束了 */
        .v-enter,
        .v-leave-to {
            opacity: 0;
            transform: translateX(150px);
        }

        /* v-enter-active 【入场动画的时间段】 */
        /* v-leave-active 【离场动画的时间段】 */
        .v-enter-active,
        .v-leave-active {
            transition: all 0.8s ease;
        }
    </style>
</head>

<body>
    <div id="app">
        <input type="button" value="toggle" @click="flag=!flag">
        <!-- 需求: 点击按钮,让 h3 显示 ,再点击,让 h3 隐藏 -->
        <!-- 1. 使用 transition 元素,把需要动画控制的元素,包裹起来 -->
        <!-- transition 元素,是Vue官方提供的 -->
        <transition>
            <h3 v-if="flag">这是一个H3</h3>
        </transition>

    </div>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                flag: false
            },
            methods: {}
        });
    </script>
</body>

</html>

2、可修改 v- 前缀

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 2.自定义两组样式,来控制 transition 内部的元素实现动画 -->
    <style>
        /* v-enter [这是一个时间点] 是进入之前,元素的起始状态,此时还没有开始进入 */
        /* v-leave-to [这是一个时间点] 是动画离开之后,离开的终止状态,此时,元素动画已经结束了 */
        .v-enter,
        .v-leave-to {
            opacity: 0;
            transform: translateX(150px);
        }

        /* v-enter-active 【入场动画的时间段】 */
        /* v-leave-active 【离场动画的时间段】 */
        .v-enter-active,
        .v-leave-active {
            transition: all 0.8s ease;
        }



        .my-enter,
        .my-leave-to {
            opacity: 0;
            transform: translateY(70px);
        }

        .my-enter-active,
        .my-leave-active {
            transition: all 0.8s ease;
        }
    </style>
</head>

<body>
    <div id="app">
        <input type="button" value="toggle" @click="flag=!flag">
        <!-- 需求: 点击按钮,让 h3 显示 ,再点击,让 h3 隐藏 -->
        <!-- 1. 使用 transition 元素,把需要动画控制的元素,包裹起来 -->
        <!-- transition 元素,是Vue官方提供的 -->
        <transition>
            <h3 v-if="flag">这是一个H3</h3>
        </transition>


        <hr>
        <input type="button" value="toggle2" @click="flag2=!flag2">
        <transition name="my">
            <h6 v-if="flag2">这是一个H6</h6>
        </transition>

    </div>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                flag: false,
                flag2: false
            },
            methods: {}
        });
    </script>
</body>

</html>

3、使用第三方类库实现动画

1)导入动画类库:

<link rel="stylesheet" type="text/css" href="./lib/animate.css">

2)定义 transition 及属性:

        <transition 
         enter-active-class="bounceIn" 
         leave-active-class="bounceOut" 
         :duration="{enter: 200, leave: 400}">
            <h3 v-if="flag" class="animated">这是一个H3</h3>
        </transition>

代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 引入在线的animate库 -->
    <link rel="stylesheet" href="https://unpkg.com/animate.css@3.5.2/animate.min.css">
    <!-- 入场 bounceIn      离场 bounceOut -->
</head>

<body>
    <div id="app">
        <input type="button" value="toggle" @click="flag=!flag">
        
        <!-- 使用 :duration="{enter: 200, leave: 400}" 来分别设置 入场时长 和 离场时长 -->
        <transition enter-active-class="bounceIn" leave-active-class="bounceOut" :duration="{enter: 200, leave: 400}">
            <h3 v-if="flag" class="animated">这是一个H3</h3>
        </transition>

    </div>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                flag: false
            },
            methods: {}
        });
    </script>
</body>

</html>

4、使用钩子函数实现小球的半场动画

代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .ball {
            width: 15px;
            height: 15px;
            border-radius: 50%;
            background-color: red;
        }
    </style>
</head>

<body>
    <div id="app">
        <input type="button" value="快到碗里来" @click="flag=!flag">
        <!-- 1. 使用 transition 元素把 小球包裹起来 -->
        <!-- 定义 transition 组件以及三个钩子函数 -->
        <transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
            <div class="ball" v-show="flag"></div>
        </transition>

    </div>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                flag: false
            },
            methods: {
            	// 定义三个 methods 钩子方法:
                // 注意: 动画钩子函数的第一个参数: el ,表示 要执行动画的那个 DOM 元素 是原生的JS DOM对象
                // 大家可以认为 , el 是通过 document.getElementById('') 方式获取到的原生JS DOM 对象
                beforeEnter(el) {
                    // beforeEnter 表示动画入场之前,此时,动画尚未开始,可以在 beforeEnter 中,设置元素开始动画之前的起始样式
                    // 设置小球开始动画之前的,起始位置
                    el.style.transform = "translate(0,0)"
                },
                enter(el, done) {
                    // 这句话,没有实际的作用,但是,如果不写,出不来动画效果
                    // 可以认为 el.offsetWidth 会强制动画刷新
                    el.offsetWidth
                    // enter 表示动画 开始之后的样式,这里,可以设置小球完成动画之后的 结束状态
                    el.style.transform = "translate(150px,450px)"
                    el.style.transition = "all 1s ease"

                    // 这里的done,其实就是afterEnter 这个函数,也就是说:done 是 afterEnter 函数的引用
                    done()
                },
                afterEnter(el) {
                    // 动画完成之后,会调用 afterEnter 
                    this.flag = !this.flag
                }
            }
        });
    </script>
</body>

</html>

5、列表动画

代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        li {
            border: 1px dashed #999;
            margin: 5px;
            line-height: 35px;
            padding-left: 5px;
            font-size: 12px;
            width: 100%;
        }

        li:hover {
            background-color: pink;
            transition: all 0.6s ease;
        }


        .v-enter,
        .v-leave-to {
            opacity: 0;
            transform: translateY(80px);
        }

        .v-enter-active,
        .v-leave-active {
            transition: all 0.6s ease;
        }

        /* 下面的 .v-move 和 .v-leave-active 配合使用,能够实现列表后续元素,渐渐地飘上来的效果 */
        .v-move {
            transition: all 0.6s ease;
        }

        .v-leave-active {
            position: absolute;
        }
    </style>
</head>

<body>
    <div id="app">

        <div>
            <label for="">
                Id:
                <input type="text" v-model="id">
            </label>

            <label for="">
                Name:
                <input type="text" v-model="name">
            </label>

            <input type="button" value="增加" @click="add">

        </div>

        <!-- <ul> -->
        <!-- 在实现列表过渡的时候,如果需要过渡的元素,是通过 v-for 循环渲染出来的,不能使用transition包裹,需要使用transitionGroup -->
        <!-- 如果要为 v-for 循环创建的元素设置动画,必须为每一个 元素 设置 :key 属性 -->
        <!-- 给transition-group 添加 appear 属性,实现页面刚展示出来,入场的时候的效果 -->
        <!-- 通过为 transition-group 元素,设置tag属性,指定 transition-group 渲染为指定的元素,如果不指定 tag 属性,默认
                渲染为 span 标签 -->
        <transition-group appear tag="ul">
            <li v-for="(item,i) in list" :key="item.id" @click="del(i)">
                {{item.id}} --- {{item.name}}
            </li>
        </transition-group>

        <!-- </ul> -->

    </div>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                id: '',
                name: '',
                list: [
                    { id: 1, name: '周哥' },
                    { id: 2, name: '李哥' },
                    { id: 3, name: '张哥' },
                    { id: 4, name: '沈哥' }
                ]
            },
            methods: {
                add() {
                    this.list.push({ id: this.id, name: this.name })
                    this.id = this.name = ''
                },
                del(i) {
                    this.list.splice(i, 1)
                }
            }
        });
    </script>
</body>

</html>

Vue 组件

定义 Vue 组件

  • 什么是模块化:模块化是从代码的角度出发,分析项目,把项目中一些功能类似的代码,单独的抽离为一个个的模块;那么为了保证大家以相同的方式去封装模块,于是我们就创造了模块化的规范(CommonJS规范);
    • 模块化的好处:方便项目的开发;和后期的维护与扩展;今后如果需要某些固定功能的模块,则直接拿来引用就行,提高了项目开发效率!
  • 什么是组件化:从UI的角度出发考虑问题,把页面上有重用性的UI解构和样式,单独的抽离出来,封装为单独的组件;
    • 组件化的好处:随着项目规模的发展,我们手里的组件,会越来越多,这样,我们今后一个页面中的UI,几乎都可以从手中拿现成的组件拼接出来;方便项目的开发和维护;

全局组件定义的三种方式

第一种方式:

1、先调用 Vue.extend() 得到组件的构造函数:

// 创建全局组件的第一种方式:   component
 const com1 = Vue.extend({
   template: '<h1>这是创建的第一个全局组件</h1>' // template 属性,表示这个组件的 UI 代码解构
 })

2、通过 Vue.component(‘组件的名称’, 组件的构造函数) 来注册全局组件:

 // 使用 Vue.component 向全局注册一个组件
 // Vue.component('组件的名称', 组件的构造函数)
 Vue.component('mycom1', com1)

注意:

如果使用 Vue.component 定义全局组件的时候,组件名称使用了驼峰命名,则在引用组件的时候,需要把大写的驼峰改为小写的字母同时,两个单词之间,使用 - 链接
如果不使用驼峰,则直接拿名称来使用即可

以上两步可写为一步:

Vue.component('myCom1', Vue.extend({
    template: '<h3>这是使用 Vue.extend 创建的组件</h3>'
}))

3、把注册好的全局组件名称,以标签形式引入到页面中即可:

<!-- 如何引入一个全局的Vue组件呢? 直接把 组件的名称,以标签的形式,放到页面上就好了! -->
 <mycom1></mycom1>
第二种方式:

直接使用 Vue.component(‘组件名称’, { 组件模板对象 })

const com2Obj = {
   // 1. template 属性中,不能单独放一段文本,必须用标签包裹起来;
   // 2. 如果在 template 属性中,想要放多个元素了,那么,在这些元素外,必须有唯一的一个根元素进行包裹;
   template: '<div><h2>这是直接使用 Vue.component 创建出来的组件</h2><h3>红红火火</h3></div>'
 }

 // 定义全局的组件
 // Vue.component 的第二个参数,既接收 一个 组件的构造函数, 同时,也接受 一个对象
 Vue.component('mycom2', com2Obj)

或者:

Vue.component('mycom2', {
    // 注意: 不论是哪种方式创建出来的组件,组件的 template 属性指向的模板内容,必须有且只能有唯一的一个根元素
    template: '<div><h3>这是直接使用 Vue.component 创建出来的组件的组件</h3><span>123</span></div>'
})
第三种方式:

1、先使用 template 标签定义一个模板的代码解构:

<!-- 定义一个 template 标签元素  -->
<!-- 使用 Vue 提供的 template 标签,可以定义组件的UI模板解构 -->
<template id="tmpl">
 <div>
   <h3>哈哈,这是在外界定义的组件UI解构</h3>
   <h3>我是来捣乱的</h3>
 </div>
</template>

2、使用 Vue.component 注册组件:

 // 这是定义的全局组件
 Vue.component('mycom3', {
   template: '#tmpl'
 })

注意: 从更抽象的角度来说,每个组件,就相当于是一个自定义的元素; 注意: 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹!

私有组件定义

私有组件只能当前定义的 VM 实例中可以使用

代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <login></login>
    </div>

    <template id="tmpl2">
        <div>
            <h1>这是私有的 login 组件</h1>
        </div>
    </template>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {},
            methods: {},
            components: { //定义实例内部私有组件
                login: {
                    template: "#tmpl2"
                }
            }
        });
    </script>
</body>

</html>

组件中展示数据和响应事件

1.组件可以有自己的 data 数据
2.组件的 data 和实例的 data 有点不一样,实例中的 data 可以为一个对象,但是组件中的 data 必须是一个方法
3.组件中的 data 除了必须为一个方法之外,这个方法内部,还必须返回一个对象才行
4.组件中的 data 数据使用方式,和实例中的 data 使用方式完全一样!!!

   Vue.component('mycom1', {
       template: "<h1>这是全局组件 --- {{ msg }}</h1>",
       data: function () {
           return {
               msg: '这是组件中的data定义的数据'
           }
       }
   })

计数器案例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <counter></counter>
        <hr>
        <counter></counter>
        <hr>
        <counter></counter>
    </div>

    <template id="tmpl">
        <div>
            <input type="button" value="+1" @click="increment">
            <h3>{{ count }}</h3>
        </div>
    </template>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var dataObj = { count: 0 }
        // 这是一个计数器组件,身上有个按钮,每当点击按钮,让 data 中的 count 值 +1
        Vue.component('counter', {
            template: '#tmpl',
            data: function () {
                // return dataObj
                return { count: 0 }
            },
            methods: {
                increment() {
                    this.count++
                }
            }
        })

        var vm = new Vue({
            el: '#app',
            data: {},
            methods: {}
        });
    </script>
</body>

</html>

以上案例可以解释为什么 组件中的 data 必须是一个方法 以及 组件中的 data 除了必须为一个方法之外,这个方法内部,还必须返回一个对象才行

“组件中的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。”

组件切换

切换方式一(使用flag标识符结合v-if和v-else切换组件)

代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <a href="" @click.prevent="flag=true">登录</a>
        <a href="" @click.prevent="flag=flase">注册</a>

        <login v-if="flag"></login>
        <register v-else="flag"></register>
    </div>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        Vue.component('login', {
            template: '<h3>登录组件</h3>'
        })

        Vue.component('register', {
            template: '<h3>注册组件</h3>'
        })

        var vm = new Vue({
            el: '#app',
            data: {
                flag: true
            },
            methods: {}
        });
    </script>
</body>

</html>
切换方式二(使用component标签的:is属性来切换组件,并添加动画)

代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <a href="" @click.prevent="comName='login'">登录</a>
        <a href="" @click.prevent="comName='register'">注册</a>

        <!-- Vue 提供了 component ,来展示对应名称的组件 -->
        <!-- component 是一个占位符,:is 属性,可以用来指定要展示的组件名称 -->
        <component :is="comName"></component>

    </div>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        // 组件名称是 字符串
        Vue.component('login', {
            template: '<h3>登录组件</h3>'
        })

        Vue.component('register', {
            template: '<h3>注册组件</h3>'
        })

        var vm = new Vue({
            el: '#app',
            data: {
                comName: 'login'  //当前 component 中的 :is 绑定的组件名称
            },
            methods: {}
        });
    </script>
</body>

</html>

组件切换时切换动画

代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .v-enter,
        .v-leave-to {
            opacity: 0;
            transform: translateX(150px);
        }

        .v-enter-active,
        .v-leave-active {
            transition: all 0.5s ease;
        }
    </style>
</head>

<body>
    <div id="app">
        <a href="" @click.prevent="comName='login'">登录</a>
        <a href="" @click.prevent="comName='register'">注册</a>

        <!-- 通过 mode 属性,设置组件切换时候的模式 -->
        <transition mode='out-in'>
            <component :is="comName"></component>
        </transition>

    </div>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        // 组件名称是 字符串
        Vue.component('login', {
            template: '<h3>登录组件</h3>'
        })

        Vue.component('register', {
            template: '<h3>注册组件</h3>'
        })

        var vm = new Vue({
            el: '#app',
            data: {
                comName: 'login'  //当前 component 中的 :is 绑定的组件名称
            },
            methods: {}
        });
    </script>
</body>

</html>

注意:通过 mode 属性,设置组件切换时候的模式 非常重要,可以使动画看起来非常流畅。

父组件向子组件传值

步骤:
1、把要传递给子组件的数据,作为 自定义属性,通过 v-bind: 绑定到子组件身上
2、在子组件中,不能直接使用父组件传递过来的数据,需要先使用 props 数组来接收一下
3、注意:在接收父组件传递过来的 props 的时候,接收的名称,一定要和父组件传递过来的自定义属性,名称保持一致!
代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <!-- 父组件可以在 引用子组件的时候,通过 属性绑定(v-bind:)的形式,把需要传递给子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用 -->
        <com1 v-bind:parentmsg="msg"></com1>
    </div>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                msg: '123 父组件中的数据'
            },
            methods: {},
            components: {

                // 结论: 经过演示,发现,子组件中,默认无法访问到 父组件中的data 上的数据 和methods 中的方法
                com1: {
                    data() {  //注意: 子组件中的data数据,并不是通过 父组件传递过来的,而是子组件自身私有的,
                        // 比如:子组件通过Ajax,请求回来的数据,都可以放到 data 身上
                        // data 上的数据,都是可读可写的
                        return {
                            title: '123',
                            content: 'qqq'
                        }
                    },
                    template: '<h1 @click="changed">这是子组件 --- {{ parentmsg }}</h1>',
                    // 注意: 组件中的 所有 props 中的数据,都是通过 父组件传递给 子组件的
                    // props 中的数据,都是只读的,无法重新赋值
                    props: ['parentmsg'],  //把父组件传递过来的parentmsg 属性,现在props 数组中定义一下,这样才能使用这个数据
                    directives: {},
                    filters: {},
                    components: {},
                    methods: {
                        changed() {
                            this.parentmsg = '被修改了'
                        }
                    },
                }
            }
        });
    </script>
</body>

</html>

父组件向子组件传递方法

组件向子组件 传递方法,使用的是 事件绑定机制,v-on,当我们自定义了 一个 事件属性之后,那么,子组件就能够,通过某些方式,来调用 传递进去的这个方法了
代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <!-- 父组件向子组件 传递方法,使用的是 事件绑定机制,v-on,当我们自定义了 一个 事件属性之后,那么,子组件就能够,通过
            某些方式,来调用 传递进去的这个方法了 -->
        <com2 v-on:func="show"></com2>
    </div>

    <template id="tmpl">
        <div>
            <h1>这是 子组件</h1>
            <input type="button" value="这是子组件中的按钮 - 点击它,触发 父组件传递过来的 func 方法" @click="myclick">
        </div>
    </template>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>

        // 定义了一个字面量类型的 组件模板对象
        var com2 = {
            template: '#tmpl',  //通过指定一个Id,表示说,要去加载 这个指定Id的 template 元素中的内容,当做组件的HTML结构
            data() {
                return {
                    sonmsg: { name: '小头儿子', age: 6 }
                }
            },
            methods: {
                myclick() {
                    // console.log('ok')
                    // 当点击 子组件 按钮的时候,如何拿到父组件 传递过来的 func 方法,并调用这个方法?
                    // emit 英文原意:是触发,调用,发射的意思
                    // this.$emit('func', 123, 456)
                    this.$emit('func', this.sonmsg)
                }
            },
        }

        var vm = new Vue({
            el: '#app',
            data: {
                datamsgFormSon: null
            },
            methods: {
                show(data) {
                    // console.log('调用了父组件身上的 show 方法: --- ' + data)
                    console.log(data)
                    this.datamsgFormSon = data
                }
            },
            components: {
                com2
            }
        });
    </script>
</body>

</html>

更多组件之间的传值参考网站:子组件与父组件之间传值

组件案例-评论列表

分析:发表评论的业务逻辑
1.评论的数据存到哪里去??? 存放到 localStorage 中
2.先组织出一个最新的评论数据对象
3.想办法,把第二步中得到的评论对象,保存到 localStorage 中
3.1 localStorage 只支持存放字符串对象,要先调用 JSON.stringify
3.2 在保存最新的评论数据之前,要先从 localStorage 获取之前的评论数据( string ),转化为 一个 数组对象
然后,把最新的评论,push 到这个数组
3.3 如果获取到的 localStorage 中的 评论字符串,为空不存在,则 可以 返回一个 ‘[]’ 让 JSON.parse 去转换
3.4 把 最新的评论列表数组,再次调用 JSON.stringify 转为 数组字符串,然后调用 localStorage.setItem()

代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
</head>

<body>
    <div id="app">

        <cmt-box @func="loadComments"></cmt-box>


        <ul class="list-group">
            <li class="list-group-item" v-for="item in list" :key="item.id">
                <span class="badge">评论人 : {{ item.user }}</span>
                {{ item.content }}
            </li>

        </ul>

    </div>

    <template id="tmpl">
        <div>
            <div class="form-group">
                <label>评论人:</label>
                <input type="text" class="form-control" v-model="user">
            </div>

            <div class="form-group">
                <label>评论内容:</label>
                <textarea class="form-control" v-model="content"></textarea>
            </div>

            <div class="form-group">
                <input type="button" value="发表评论" class="btn btn-primary" @click="postComment">
            </div>
        </div>
    </template>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>

        var commentBox = {
            data() {
                return {
                    user: '',
                    content: ''
                }
            },
            template: '#tmpl',
            methods: {
                postComment() {  //发表评论的方法
                    // 分析:发表评论的业务逻辑
                    // 1.评论的数据存到哪里去??? 存放到 localStorage 中
                    // 2.先组织出一个最新的评论数据对象
                    // 3.想办法,把第二步中得到的评论对象,保存到 localStorage 中
                    // 3.1 localStorage 只支持存放字符串对象,要先调用 JSON.stringify
                    // 3.2 在保存最新的评论数据之前,要先从 localStorage 获取之前的评论数据( string ),转化为 一个 数组对象
                    //     然后,把最新的评论,push 到这个数组
                    // 3.3 如果获取到的 localStorage 中的 评论字符串,为空不存在,则 可以 返回一个 '[]'  让 JSON.parse 去转换
                    // 3.4 把 最新的评论列表数组,再次调用 JSON.stringify 转为 数组字符串,然后调用 localStorage.setItem()

                    var comment = { id: Date.now(), user: this.user, content: this.content }

                    // 从 localStorage 中获取所有的评论
                    var list = JSON.parse(localStorage.getItem('cmts') || '[]')
                    list.unshift(comment)
                    // 重新保存最新的评论数据
                    localStorage.setItem('cmts', JSON.stringify(list))

                    this.user = this.content = ''
                    // 子组件调用父组件方法
                    this.$emit('func')
                }
            },
        }

        var vm = new Vue({
            el: '#app',
            data: {
                list: [
                    { id: Date.now(), user: '李白', content: '天生我材必有用' },
                    { id: Date.now(), user: '小李白', content: '劝君更尽一杯酒' },
                    { id: Date.now(), user: '小李', content: '小李飞刀' },
                ]
            },
            beforeCreate() {  // 注意:这里不能调用 loadComments 方法,因为在执行这个钩子函数的时候,data 和 methods 都还没有被初始化好

            },
            created() {
                this.loadComments()
            },
            methods: {
                loadComments() {  //从本地的localStorage 中,加载评论列表
                    var list = JSON.parse(localStorage.getItem('cmts') || '[]')
                    this.list = list

                }
            },
            components: {
                'cmt-box': commentBox
            }
        });
    </script>
</body>

</html>

使用 this.$refs 来获取元素和组件

ref 介绍

ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM
元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向该子组件实例
通俗的讲,ref特性就是为元素或子组件赋予一个ID引用,通过 this.$refs.refName 来访问元素或子组件的实例

this.$refs 介绍

this.$refs 是一个对象,持有当前组件中注册过 ref 特性的所有 DOM 元素和子组件实例
注意: $refs只有在组件渲染完成后才填充,在初始渲染的时候不能访问它们,并且它是非响应式的,因此不能用它在模板中做数据绑定

实例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <input type="button" value="获取元素" @click="getElement" ref="mtbtn">

        <h3 id="myh3" ref="myh3">哈哈哈,今天天气真好!</h3>

        <hr>

        <login ref="mylogin"></login>
    </div>

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var login = {
            template: '<h1>登录组件</h1>',
            data() {
                return {
                    msg: 'son msg'
                }
            },
            methods: {
                show() {
                    console.log('调用了子组件方法')
                }
            },
        }


        var vm = new Vue({
            el: '#app',
            data: {},
            methods: {
                getElement() {
                    // console.log(document.getElementById('myh3').innerText)

                    // ref 是英文单词 【reference】  值类型 和 引用类型  referenceError 
                    // console.log(this.$refs.myh3.innerText)

                    // console.log(this.$refs.mylogin.msg)
                    this.$refs.mylogin.show()
                }
            },
            components: {
                login
            }
        });
    </script>
</body>

</html>

在 Vue 组件中 data 和 props 的区别

  1. data 在组件中,要被定义成 function 并返回一个对象
  2. props 在组件中,要被定义成数组,其中,数组的值都是字符串名,表示父组件传递过来的数据;
  3. props 的数据,不要直接拿来修改,如果想要修改,必须在 data 上重新定义一个 属性,然后把属性的值 从 this.props 拿过来;

data 上的数据,都是组件自己私有的, data 上的数据,都是可读可写的 props 数据,都是外界传递过来的数据, props 中的数据只能读取,不能重新写入

补充(localStorage 本地存储)

localstorage 存储对象分为两种:
① sessionStrage: session 即会话的意思,在这里的 session 是指用户浏览某个网站时,从进入网站到关闭网站这个时间段,session 对象的有效期就只有这么长。
② localStorage: 将数据保存在客户端硬件设备上,不管它是什么,意思就是下次打开计算机时候数据还在。
两者区别就是一个作为临时保存,一个长期保存。

localStorage 的存储,读取,删除方法
参考博客:localStorage的存储,读取,删除

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值