Vue.js学习笔记五

170 阅读6分钟

父组件向子组件传值

  1. 组件实例定义方式,注意:一定要使用 props属性来定义父组件传递过来的数据:
<script>
        var vm=new Vue({
            el:'#app',
            data () {
                return{
                    msg:'这是父组件中的消息'
                }
            },
            methods:{

            },
            components:{
                son:{
                    template:'<h1>这是子组件 --- {{finfo}}</h1>',
                    props:['finfo']
                }
            }
        });
    </script>
  1. 使用 v-bind或简化指令,将数据传递到子组件中:
<div id="app">
    <son :finfo="msg"></son>
</div>

代码实例

<!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>
    <script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
</head>

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

    <script>
        var vm = new Vue({
            el: '#app',
            data() {
                //注意:子组件中的data数据,并不是通过父组件传递过来的,而是子组件自身私有的
                //data上的数据,都是可读可写的
                return {
                    msg: '123 啊-父组件中的数据'
                }
            },
            methods: {

            },
            components: {
                //结论:经过演示,发现子组件中,默认无法访问到父组件中的data上的数据和methods中的方法
                com1: {
                    template: '<h1 @click="change">这是子组件 --- {{parentmsg}}</h1>',
                    //注意:组件中的所有props中的数据,都是通过父组件传递给子组件的
                    //props中的数据,都是只读的,无法重新赋值
                    props: ['parentmsg'],    //把父组件传递过来的parentmsg属性,在props数组中定义一下,这样才能使用这个数据
                    methods:{
                        change(){
                            this.parentmsg = "被修改了"
                        }
                    }
                }
            },
        });
    </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>
    <script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
</head>

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

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

    <script>

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

        var vm=new Vue({
            el:'#app',
            data () {
                return{
                    datamsgFormSon:null
                }
            },
            methods:{
                show(data){
                    console.log("调用了父组件身上的show方法:---" + data)
                    this.datamsgFormSon = data
                }
            },
            components:{
                com2
            }
        });
    </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>
    <script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</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="tmp1">
        <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>

        let commentBox = {
            template: '#tmp1',
            data(){
                return {
                    user:'',
                    content:'',
                }
            },
            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()
                    // this.list.push()
                    let comment = {id:Date.now, user:this.user, content:this.content}
                    //从localStorage中获取所有评论
                    let 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() {
                return {
                    
                    list: [
                        { id: Date.now(), user: '李白', content: '天生我材必有用' },
                        { id: Date.now(), user: '江小白', content: '劝君更尽一杯酒' },
                        { id: Date.now(), user: '小马', content: '我姓马,风吹草低见牛羊的马' },
                    ]
                }
            },
            methods: {
                loadComments(){//从本地 localStorage加载评论列表
                    let list = JSON.parse(localStorage.getItem('cmts') || '[]')
                    this.list = list
                }
            },
            components: {
                'cmt-box': commentBox
            },
            created() {
                this.loadComments()
            },
        });
    </script>
</body>

</html>

最终效果

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

可以直接使用this.$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>
    <script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
</head>

<body>
    <div id="app">
        <input type="button" value="获取元素" @click="getElement" ref="btn">
        <h3 ref='myh3'>哈哈哈,今天天气太好了!!!</h3>

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


    <script>

        var login = {
            template: '<h1>登录组件</h1>',
            data(){
                return {
                    msg:'son msg'
                }
            },
            methods:{
                show(){
                    console.log('调用了子组件的方法')
                }
            }
        }

        var vm = new Vue({
            el: '#app',
            data() {
                return {

                }
            },
            methods: {
                getElement() {
                    // ref 是英文单词 [reference] 值类型和引用类型  referenceError
                    // console.log(this.$refs.myh3.innerText)
                    // console.log(this.$refs.mylogin.msg)
                    this.$refs.mylogin.show()
                }
            },
            components: {
                login
            }
        });
    </script>
</body>

</html>

什么是路由

  1. 后端路由:对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源;
  2. 前端路由:对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;
  3. 在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由);

在Vue中使用vue-router

  1. 导入vue-router组件类库
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
  1. 使用router-link组件来导航
<!-- router-link 默认渲染为一个a标签,使用tag可以更改默认渲染标签  -->
<router-link to="/login">登录</router-link>
<router-link to="/register">注册</router-link>
  1. 使用router-view组件来显示匹配到的组件
<!-- 这是 vue-router提供的元素,专门用来当做占位符的,
    将来路由规则匹配到的组件,就会展示到这个vue-router中去,所以我们可以把它认为是一个占位符-->
<router-view></router-view>
  1. 创建使用 VueRouter 创建组件
//2. 创建一个路由对象,当导入vue-router包之后,在window全局对象中,就有了一个路由的构造函数,叫做VueRouter
        // 在new路由对象的时候,可以为
        const routerObj = new VueRouter({
            //route这个配置对象中的route表示【路由匹配规则】的意思
            routes: [//路由匹配规则
                //每个路由都是一个对象,这个规则对象,身上有必须的属性
                //属性1是path,表示监听哪个路由连接地址
                //属性2是component,表示如果路由是前面匹配到的path,则展示component属性对应的那个组件
                //注意:component的属性值必须是一个组件的模板对象,不能是属性的名称
                { path: "/login", component: login },
                { path: "/register", component: register },
            ]

        })
        
        var vm = new Vue({
            el: '#app',
            data() {
                return {

                }
            },
            methods: {

            },
            router:routerObj//将路由规则对象,注册到我们的vm实例上,用来监听URL地址变化,然后展示对应的组件
        });

实现路由默认高亮的代码实例

<!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>
    <script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
    <!-- 1. 安装vue-router路由模块 -->
    <script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
    <style>
        .router-link-active, .myactive{
            color: red;
            font-weight: 800;
            font-style: ittalic;
            font-size: 80px;
            text-decoration: underline;
            background-color: green;
        }
    </style>
</head>

<body>
    <div id="app">

        <!-- <a href="#/login">登录</a>
        <a href="#/register">注册</a> -->
        <!-- router-link 默认渲染为一个a标签,使用tag可以更改默认渲染标签  -->
        <router-link to="/login">登录</router-link>
        <router-link to="/register">注册</router-link>

        <!-- 这是 vue-router提供的元素,专门用来当做占位符的,
            将来路由规则匹配到的组件,就会展示到这个vue-router中去,所以我们可以把它认为是一个占位符-->
        <router-view></router-view>
    </div>

    <script>

        let login = {
            template: '<h1>登录组件</h1>'
        }

        let register = {
            template: '<h1>注册组件</h1>'
        }

        //2. 创建一个路由对象,当导入vue-router包之后,在window全局对象中,就有了一个路由的构造函数,叫做VueRouter
        // 在new路由对象的时候,可以为
        const routerObj = new VueRouter({
            //route这个配置对象中的route表示【路由匹配规则】的意思
            routes: [//路由匹配规则
                //每个路由都是一个对象,这个规则对象,身上有必须的属性
                //属性1是path,表示监听哪个路由连接地址
                //属性2是component,表示如果路由是前面匹配到的path,则展示component属性对应的那个组件
                //注意:component的属性值必须是一个组件的模板对象,不能是属性的名称
                { path: "/", redirect:'/login' },//进行重定向,这里的redirect和Node中的redirect完全是两码事
                { path: "/login", component: login },
                { path: "/register", component: register },
            ],
            linkActiveClass:'myactive'

        })
        var vm = new Vue({
            el: '#app',
            data() {
                return {

                }
            },
            methods: {

            },
            router:routerObj//将路由规则对象,注册到我们的vm实例上,用来监听URL地址变化,然后展示对应的组件
        });
    </script>
</body>

</html>

为路由添加切换动画

  1. 使用transition包裹router-view标签
        <transition mode="out in">
            <router-view></router-view>
        </transition>
  1. 定义固定的动画样式
        .v-enter,
        .v-leave-to {
            opacity: 0;
            transform: translateX(140px);
        }

        .v-enter-active,
        .v-laeve-active {
            transition: all 0.5s ease;
        }

路由规则中实现参数的传递

方式一:使用query去获取参数

<!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>
    <script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
    <script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
</head>

<body>
    <div id="app">
        <!-- 如果在路由中,使用查询字符串,给路由传递参数,则不需要修改路由规则的path属性 -->
        <router-link to="/login?=10&name=zs">登录</router-link>
        <router-link to="/register">注册</router-link>
        <router-view></router-view>
    </div>

    <script>

        let login = {
            template: '<h1>登录 --- {{$route.query.id}} --- {{$route.query.name}}</h1>',
            created() {//组件的生命周期钩子函数
                console.log(this.$route.query.id)
            },
            data(){
                return{
                    id : this.$route.query.id
                }
            }
        }

        let register = {
            template: '<h1>注册</h1>'
        }

        let router = new VueRouter({
            routes: [
                { path: '/login', component: login },
                { path: '/register', component: register }
            ]
        })

        var vm = new Vue({
            el: '#app',
            data() {
                return {

                }
            },
            methods: {

            },
            router
        });
    </script>
</body>

</html>

方式二:使用params去获取数据

<!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>
    <script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
    <script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
</head>

<body>
    <div id="app">
        <!-- 如果在路由中,使用查询字符串,给路由传递参数,则不需要修改路由规则的path属性 -->
        <router-link to="/login/10/lisi">登录</router-link>
        <router-link to="/register">注册</router-link>
        <router-view></router-view>
    </div>

    <script>

        let login = {
            template: '<h1>登录 --- {{this.$route.params.id}} --- {{this.$route.params.name}}</h1>',
            created() {//组件的生命周期钩子函数
                console.log(this.$route)
            },
            data(){
                return{
                    // id : this.$route
                }
            }
        }

        let register = {
            template: '<h1>注册</h1>'
        }

        let router = new VueRouter({
            routes: [
                { path: '/login/:id/:name', component: login },
                { path: '/register', component: register }
            ]
        })

        var vm = new Vue({
            el: '#app',
            data() {
                return {

                }
            },
            methods: {

            },
            router
        });
    </script>
</body>

</html>

路由的嵌套

有可能我们在需求里面需要用到在一个路由里面嵌套其他的路由,这时候我们需要使用children去嵌套路由规则

<!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>
    <script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
    <script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
</head>

<body>
    <div id="app">
        <router-link to="/account">Account</router-link>

        <router-view></router-view>
    </div>

    <template id="tmp1">
        <div>
            <h1>这是Account组件</h1>

            <router-link to="/account/login">登录</router-link>
            <router-link to="/account/register">注册</router-link>

            <router-view></router-view>
        </div>
    </template>


    <template id="tmp2">
        <div>
            <h3>这是登录组件</h3>
        </div>
    </template>

    <template id="tmp3">
        <div>
            <h3>这是注册组件</h3>
        </div>
    </template>

    <script>

        //组件的模板对象
        let account = {
            template: "#tmp1"
        }

        let login = {
            template: "#tmp2"
        }

        let register = {
            template: "#tmp3"
        }

        const router = new VueRouter({
            routes: [
                {
                    path: '/account',
                    component: account,
                    //使用children实现子路由,同时子路由的path前面,不要带 / ,
                    //否则永远以根路径开始请求,这样不方便我们用户去理解URL地址
                    children: [
                        { path: 'login', component: login },
                        { path: 'register', component: register },
                    ]
                },
                // { path: '/account/login', component: login },
                // { path: '/account/register', component: register },

            ]
        })

        var vm = new Vue({
            el: '#app',
            data() {
                return {

                }
            },
            methods: {

            },
            router
        });
    </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>
    <script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
    <script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>

    <style>

        html, body{
            margin: 0;
            padding: 0;
        }

        .header{
            background-color: orange;
            height: 80px;
        }

        .left{
            background-color: lightgreen;
            flex:2
        }

        .main{
            background-color: lightpink;
            flex:8
        }

        h1{
            margin: 0;
            padding: 0;
            font-size: 16px;
        }

        .container{
            display: flex;
            height: 600px;
        }
    </style>
</head>

<body>
    <div id="app">
        <router-view></router-view>

        <div class = "container">
            <router-view name = "left"></router-view>
            <router-view name = "main"></router-view>
        </div>
    </div>

    <script>

        let header = {
            template: '<h1 class = "header">Header头部区域</h1>'
        }

        let leftBox = {
            template: '<h1 class = "left">Left头部区域</h1>'
        }

        let mainBox = {
            template: '<h1 class = "main">Main头部区域</h1>'
        }

        //创建路由对象
        const router = new VueRouter({
            routes: [
                // {path:'/', component:header},
                // {path:'/left', component:leftBox},
                // {path:'/main', component:mainBox},

                { path: '/', components: {
                        'default':header,
                        'left':leftBox,
                        'main':mainBox
                    } 
                },
            ]
        })

        var vm = new Vue({
            el: '#app',
            data() {
                return {

                }
            },
            methods: {

            },
            router
        });
    </script>
</body>

</html>

代码效果