目录
Vue框架入门系列(一)
/    

Vue框架入门系列(一)

vue官网https://cn.vuejs.org/v2/guide/installation.html

vue的几种安装方式

  1. 直接去官网引入js
  2. 使用引入cdn
  3. npm安装vue脚手架

vue分生产环境和开发环境,当我们开发的时候使用开发环境的版本,便于我们查阅源代码(有注释),

上线时候使用生产环境。

CDN

对于制作原型或学习,你可以这样使用最新版本:

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

对于生产环境,我们推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏:

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>

如果你使用原生 ES Modules,这里也有一个兼容 ES Module 的构建文件:

<script type="module">
  import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.esm.browser.js'
</script>
  import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.esm.browser.js'
</script></script>

基本骨架

<div id="app">

</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script>
	const app = new Vue({
		el: '#app',
		data: {

		}
	})
</script>

hello.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.jsdelivr.net/npm/vue@2.6.0"></script> -->
    <style>
        [v-cloak]{
            display: none;
        }
    </style>
</head>
<body>
    <!-- 将来new的vue实例, 会控制这个元素中的所有内容 -->
    <div id="app">
        <!-- 使用v-cloak能够解决插值表达式闪烁的问题 -->
        <p v-cloak>---{{ msg }}+++</p>
        <h4 v-text="msg">---+++</h4>
        <!-- 默认 v-text是没有闪烁问题的 -->
        <!-- v-text会覆盖元素中原本的内容, 但是插值表达式只会替换自己的
         占位符, 不会把整个内容清空-->
        <div>{{ msg2 }}</div>
        <div v-text="msg2"></div>
        <div v-html="msg2"></div>
        <input type="button" value="按钮" v-bind:title="mytitle + '新增加的字符串'" @mouseover="mouse">
        <!-- v-bind: 是vue中, 提供的用于绑定属性的指令 -->
        <input type="button" value="按钮2" v-bind:title="'v-bind:可以简写成:' + mytitle">
        <!-- v-bind中, 可以写合法的js表达式 -->
        <!-- vue中提供了v-on: 事件绑定机制 -->
        <br><br>
        <!-- 浏览器常用的事件都可以绑定 mouseover, mouseout, blur-->
        <input type="button" value="点击事件弹窗" v-on:click="show">
    </div>
    <script src="lib/vue.js"></script>

    <script>

    // 创建一个vue实例
    // 当我们导入包之后 在浏览器的内存中, 就多了一个vue构造函数
    var vm = new Vue({
        el: '#app', // 表示 当前我们new的这个vue实例, 要控制页面上的哪个区域
        data: {
            msg: '欢迎学习vue', // 通过vue提供的命令. 很方便的就能把数据渲染到页面上
            // 前端的vue之类的框架, 不提倡我们去手动操作dom元素
            msg2: "<h1>嘿嘿嘿嘿, 一级标题</h1>",
            mytitle: "这是一个自己定义的title"
        },
        methods: {  // 这个methods属性中定义了当前实例所有可用的方法
                show: function(){
                    alert('hello !')
                },
                mouse: function(){
                    alert('v-on的缩写形式为@')
                }
            }
    })  
    </script>
</body>
</html>

<!-- 1. 如何定义一个基本的Vue代码结构 -->
<!-- 2. 插值表达式 和 v-text -->
<!-- 3. v-cloak -->
<!-- 4. v-html -->
<!-- 5. v-bind Vue提供的属性绑定机制  缩写是 : -->
<!-- 6. v-on  Vue提供的事件绑定机制  缩写是 @ -->

总结:

  • 绑定属性 v-bind: 简写:':'。 例如 v-bind:href = :href
  • 监听事件 v-on: 简写:'@'。例如 v-on:click = @click
  • v-text 和 v-html的区别:
    • v-text 不会解析 html标签
    • v-html会解析html标签

if 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>
    <script src="lib/vue.js"></script>
</head>
<body>
    <div id="app">
        {{message}} <br>
        {{msg}} <br>
        <span v-bind:title="time">
           鼠标停留在这几秒看看......
        </span>
        <br>

        <h1 v-if="str === 'A'">v-if的使用</h1>
        <h1 v-else-if="str === 'B'">v-else-if的使用</h1>
        <h1 v-else="str === 'C'">v-else的使用</h1>

        <h2 v-show="str === 'B' || str === 'C'">v-show的使用,它只是简单操作css的display样式</h2>
        <hr>
        <ul>
            <li v-for="todo in todos">
                {{todo.text}}
            </li>
        </ul>

    </div>

    <script>
        var strs = ['A', 'B', 'C']
        var str = strs[Math.round(Math.random() * 3)]
        let app = new Vue({
            el: '#app',
            data: {
                message : "Hello Vue !!!",
                msg : '你好, Vue。',
                time : '页面加载于....' + new Date().toLocaleString(),
                str : str,
                todos : [
                    // 控制台输入app.todos.push({text : "哈哈哈哈哈"}),可以动态添加内容
                    {text : '学习JavaScript'},
                    {text : '学习Java'},
                    {text : '学习Vue'},
                    {text : '啥都不想学'}
                ]
            }
        });
        var t = setInterval(function(){
            str = strs[Math.round(Math.random() * 2)];
            console.log(str)
            app.str = str;
        }, 1000);

    </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="/lib/vue.js"></script>
</head>
<body>
    <div id="app">
        <input type="button" value="浪一波" @click="lang"> 
        <input type="button" value="低调" @click="stop"> 

        <p>{{ msg }}</p>
    </div>
  
    <script>
        /*
            在vm实例, 会监听自己身上的 data中所有数据的改变, 只要数据一发生变化, ,
            就会自动把最新的数据, 从data上同步到页面上去; 
        */
        var vm = new Vue({
            el: "#app",
            data: {
                msg: "猥琐发育, 别浪~~~",
                timer: null // 在data上定义定时器的id
            },
            methods: {
                lang(){
                    // var _this = this 这种方式不建议使用, 可以使用箭头函数
                    /*
                        箭头函数没有this, 会绑定当前作用域的this
                        而function的this会在调用时动态绑定this
                    */
                    if (this.timer != null) return;
                    this.timer = setInterval(() => {
                        console.log(this.msg)
                        var start = this.msg.substring(0, 1)
                        var end = this.msg.substring(1)
                        this.msg = end + start
                    }, 200)
                },
                stop(){
                    console.log("停止")
                    clearInterval(this.timer)
                    // 每当清除了定时器之后, 需要把timer设置为null
                    this.timer = null
                }
            }
        })
    </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="lib/vue.js"></script>
    <style>
        *{
            padding: 0;
            margin: 0;
        }
        #app{
            margin: 20px auto;
            height: 500px;
            width: 500px;
            background-color: rgb(61, 0, 230);
        }
    </style>
</head>
<body>
    <!-- 冒泡机制 -->
    <!-- 使用.stop 阻止冒泡 -->
    <div id="app" @click="div">
        <input type="button" value="点击按钮" @click.stop="btn">
    </div>

    <a href="http://www.baidu.com" @click.prevent="linkClick">有问题去百度</a>

    <script>
        var vm = new Vue({
            el: "#app",
            data: {
        
            },
            methods: {
                btn(){
                    console.log("按钮被点击了......")
                },
                div(){
                    console.log("div被点击了......")
                },
                linkClick(){
                    console.log("触发了链接的点击事件")
                }

            }
        })
    </script>
</body>
</html>

vue的生命周期

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
        .active {
            color: red;
        }
    </style>
</head>
<body>

<div id="app">

    <button v-on:click="changeColor">变色</button>

    <!--这里class中是一个对象,键 为类名,值为 布尔型,为true则插入到类中,反之移除-->
    <h2 class="title" v-bind:class="{active: true, line: true}">{{message}}</h2>
    <h2 class="title" v-bind:class="getClasses()">{{message}}</h2>
    <img v-bind:src="imgURL" alt=""> <br>
    <a v-bind:href="href">{{word}}</a>

    <!--绑定的缩写形式-->
    <img :src="imgURL" alt=""> <br>
    <a :href="href">{{word}}</a>
</div>

<script src="lib/vue.js"></script>

<script>

    /*
    * var 定义的变量 全局的
    * let 定义的变量 有作用域
    * const 定义的是常量,不可修改
    * */

    const app = new Vue({
        el: '#app',
        data: {
            message: '你好,Vue',
            imgURL: 'https://tx-live-cover.msstatic.com/huyalive/94525224-2460685313-10568562945082523648-2789274524-10057-A-0-1/20200327105831.jpg?sign=JGU6iHSf0svbUe7Fh+SfYC6qxQ1hPTEyNTM0OTg3MDEmaz1BS0lEeG56NjRXQTU3eGQ4VUVxVXlIT0tuQUxRTEN1UkN6NTUmZT0xNTkzMDUzOTExJnQ9MTU4NTI3NzkxMSZyPTEyMzQ1Njc4JmY9L2h1eWFsaXZlLzk0NTI1MjI0LTI0NjA2ODUzMTMtMTA1Njg1NjI5NDUwODI1MjM2NDgtMjc4OTI3NDUyNC0xMDA1Ny1BLTAtMS8yMDIwMDMyNzEwNTgzMS5qcGcmYj1odXlhLXNjcmVlbnNob3RzLXJldmlldy0xMjUzNDk4NzAx',
            href: 'http://www.baidu.com',
            word: '百度一下,你就知道',
            /*
            * class 类名,值为布尔值
            * */
            classes: {
                active: true,
                line: false
            }
        },
        methods: {
            changeColor: function () {
                this.classes.active = !this.classes.active
            },
            getClasses: function () {
                console.log(this.classes);
                return this.classes;
            }
        },
        created: function () {
            console.log('created');
        },
        mounted: function () {
            console.log('mounted');
        }
    })

</script>

</body>
</html>

总结:

  • 先执行create函数,然后执行其他的 例如methods, 最后执行mounted
  • 当绑定 class属性时, 该属性的值可以为一个对象,键为类名,值为布尔类型,当为true时,说明当前键对应的类名生效,否则无效。

bind的作业

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>

        .active {
            color: red;
            background-color: yellow;
        }

        li:hover {
            background-color: yellow;
            cursor: pointer;
        }

    </style>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="(m, index) in movies"
            :class="{active: index == currentIndex?true:false}"
            v-on:click="getRed(index)">
            {{m}}
        </li>
    </ul>
</div>

<script src="lib/vue.js"></script>

<script>

    const app = new Vue({
        el: '#app',
        data: {
            movies: ['七龙珠', '火影忍者', '蜡笔小新', '千与千寻', '哈尔移动的城堡', '钢铁侠',   'I\'m a Iron man '],
            isActive: false,
            currentIndex: 0
        },
        methods: {
            getRed: function (index) {
                this.currentIndex = index
            }
        }
    })

</script>

</body>
</html>

默认第一个 被选中,有颜色。

然后点击哪个,哪个就被选中,其他则不被选中。

computed计算属性

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>



</head>
<body>
<div id="app">
    <h2>{{message}}</h2>

    <h3>{{getFullName()}}</h3>

    <h4>{{fullName}}</h4>
</div>

<script src="lib/vue.js"></script>

<script>

    const app = new Vue({
        el: '#app',
        data: {
            message: '你好,Vue',
            lastName: '聂',
            firstName: '保华'
        },
        methods: {
            getFullName: function () {
                return this.lastName + " " + this.firstName
            }
        },
        computed: {
            fullName: function () {
                return this.lastName + " " + this.firstName
            }
        }
    })

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

当我们直接调用函数的时候,需要加括号。

如果使用computed中的,不需要加括号。

computed的使用实例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
        [v-cloak]{
            display: none;
        }
    </style>
</head>
<body>
<div id="app">
    <h2>{{message}}</h2>

    <h3 v-cloak>总价钱为: {{totalPrice}}</h3>
</div>

<script src="lib/vue.js"></script>

<script>

    const app = new Vue({
        el: '#app',
        data: {
            message: '你好,Vue',
            bookes: [
                {id: 110, name: 'Unix编程艺术', price: 119},
                {id: 111, name: '代码大全', price: 107},
                {id: 112, name: '微服务实战', price: 199},
                {id: 113, name: '深入理解Java虚拟机', price: 188}
            ]
        },
        computed: {
            totalPrice: function () {
                let sum = 0;
                for (let i = 0; i < this.bookes.length; i++) {
                    sum += this.bookes[i].price;
                }

                // 这里的i是下标
                for (let i in this.bookes) {
                    console.log('i的值为:' + i);
                    console.log(this.bookes[i].name)
                }
                console.log("--------------------");

                // 这里的book是books中的单个对象
                for (let book of this.bookes) {
                    console.log('i的值为:' + book);
                    console.log(book.name)
                }
                return sum;
            }
        }
    })

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

有几种for循环要记住。

  • for直接遍历用下标,这个非常普遍。
  • for (let i in books) 使用in,这里的i也是下标的意思。books[i]
  • for (let book of books) 使用of,这里的book是books的单个元素。

登录切换案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <span v-if="isUser">
        <label for="username">用户账号:</label>
        <input type="text" id="username" placeholder="用户账号:" key="不复用">
    </span>
    <span v-else>
        <label for="mail">用户邮箱:</label>
        <input type="text" id="mail" placeholder="用户邮箱">
    </span>
    <button @click="isUser = !isUser">切换类型</button>

</div>

<script src="lib/vue.js"></script>

<script>

    const app = new Vue({
        el: '#app',
        data: {
            isUser: true
        }
    })

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

上面的key属性,页面中,我们输入一个账号的时候,再去切换登录框,则原来input中的数据会被清空

高级js函数的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    /*
    * filter/map/reduce
    * filter中的回调函数有一个要求:必须返回一个boolean值
    *   如果返回true时,函数内部会自动将这次回调的n加入到新的数组中
    *   如果返回false,函数内部会过滤掉这次n
    *   最后全部结束,会返回一个新的数组
    *
    * 例子:有一组数,取出其中大于50的数
    * */
    const nums = [23, 45, 67, 88, 112, 124, 43, 23];
    let newNums = nums.filter(function (n) {
        return n > 50;      // 返回值类型:布尔型。 如果为真,留下当前元素,否则删除当前元素。
    });
    // 最终filter内执行完毕之后,返回一个新的 数组。
    // [67, 88, 112, 124]
    console.log(newNums);

    /*
    * map函数的使用
    *   所有返回的值会组一个新的数组
    * */
    let new2Nums = newNums.map(function (n) {
        return n * 2;   // n为 数组中的元素,逐个遍历。
    });
    // [134, 176, 224, 248]
    // 134 + 176 + 224 + 248 = 782
    console.log(new2Nums);

    /*
    * reduce函数的使用 reduce(回调函数,初始值)
    * 作用:对数组中的所有的内容进行汇总
    * 回调函数的参数:
    *   previousSum 之前值的总和
    *   currentValue 当前的 值
    *
    * */

    let total = new2Nums.reduce(function (previousSum, currentValue) {
        return previousSum + currentValue;
    }, 0);
    // 782
    console.log(total);

    /* -- 综上可得 高阶函数使用:-- */
    let sum = nums.filter(function (n) {
        return n > 50;
    }).map(function (n) {
        return n * 2;
    }).reduce(function (preSum, currentValue) {
        return preSum + currentValue;
    }, 0);
    console.log(sum);

    /* 更加高级的箭头函数 */
    let sum2 = nums.filter(n => n > 50).map(n => n * 2).reduce(((pre, cur) => pre + cur), 0);
    console.log(sum2);

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

  • filter函数: 根据需要,过滤数组的元素,返回一个数组
  • map函数: 可以遍历数组中的每个元素,返回一个新的数组
  • reduce函数: 一把对于数组的求和。

v-model双向绑定

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">


    <!--
        v-model其实是一个语法糖,它的背后本质上是包含两个操作的:
        1. v-bind绑定一个value属性
        2. v-on指令给当前元素绑定input事件

    -->
    第一种
    <input type="text" v-model="message">
    <h2>{{message}}</h2>
    第二种 
    <!-- @ 或 v-on 表示监听, 这里表示监听input框 -->
    <input type="text" :value="message" @input="message = $event.target.value"> <br> <br>
    第三种
    <input type="text" :value="message" v-on:input="showValue"> <br> <br>

    <label for="male">
        <input type="radio" value="男" v-model="sex" name="sex" id="male" checked> 男
    </label>
    <label for="female">
        <input type="radio" value="女" v-model="sex" name="sex" id="female"> 女
    </label>
    <h3>sex的值为:{{sex}}</h3>

    <label v-for="item in originalHobbies" :for="item">
        <input type="checkbox" name="hobby" v-model="hobbies" :value="item" :id="item"> {{item}}
    </label>
    <h3>选择的爱好为:{{hobbies}}</h3>
    <select name="do" multiple  v-model="mySelects">
        <option v-for="item in selects">{{item}}</option>
    </select>
    <h3>你的选择:{{mySelects}}</h3>

    <!-- 修饰符lazy    懒加载 -->
    <input type="text" v-model.lazy="message">
    <h2>{{message}}</h2>

    <!--修饰符number    v-model-->
    <input type="number" v-model="age">
    <h3>age的值{{age}}, 类型是{{typeof age}}</h3>
    <!--使用number之后的-->
    <input type="number" v-model.number="age">

    <!--修饰符 trim 去除两边的空格   v-model.trim  -->

</div>

<script src="lib/vue.js"></script>

<script>

    const app = new Vue({
        el: '#app',
        data: {
            message: '你好,Vue',
            sex: '男',
            hobbies: [],
            originalHobbies: ['篮球', '乒乓球', '羽毛球', '排球', '高尔夫球'],
            selects: ['钢琴', '小提琴', '大提琴', '吉他', '唢呐'],
            mySelects: [],
            age: 18
        },
        methods: {
            showValue: function (event) {
                this.message = event.target.value
            }
        }
    })

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

v-model相当于 v-bind绑定一个value属性,v-on给当前元素绑定input事件

$event 是 window对象

一个函数,参数是用event接收, 调用此函数时,若参数为空,则event是当前window对象,

也可以传参$event

组件的基本使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">

    <my-cpn></my-cpn>
    <div>
        <my-cpn></my-cpn>
    </div>
    <my-cpn></my-cpn>
<!--    <mycomp></mycomp>-->
</div>
<hr>
<div id="app2">
    <mycomp></mycomp>
    <mycomp></mycomp>
    <mycomp></mycomp>
    <my-cpn></my-cpn>

</div>

<script src="lib/vue.js"></script>

<script>
    // ES6语法 ``可以直接换行

    // 1. 创建组件构造器对象
    const componentConstruct = Vue.extend({
       template: `
        <div>
            <h2>我是标题</h2>
            <p>我是内容,哈哈哈哈</p>
            <p>我是内容,呵呵呵呵</p>
        </div>
       `
    });

    const mycomp = Vue.extend({
        template: `
        <div>
            <p>局部组件----~~~~</p>
            <p>我是内容~~~~~~~~</p>
        </div>
        `
    })
    // 2. 注册组件  全局组件
    Vue.component('my-cpn', componentConstruct);

    const app = new Vue({
        el: '#app',
        data: {
            message: '你好,Vue'
        }
    })

    // 局部组件
    let app2 = new Vue({
        el: '#app2',
        components: {
            mycomp: mycomp
        }
    })

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

总结:

  • 组件可分为全局组件和局部组件
    • 全局组件可以在所有vue管理的范围内使用
    • 局部组件只能在对应的vue管理div中使用
  • 首先
        // 1. 创建组件构造器对象
        const componentConstruct = Vue.extend({
           template: `
            <div>
                <h2>我是标题</h2>
                <p>我是内容,哈哈哈哈</p>
                <p>我是内容,呵呵呵呵</p>
            </div>
           `
        });
    
  • 注册全局组件
        // 2. 注册组件  全局组件
        Vue.component('my-cpn', componentConstruct);
    
  • 注册局部组件
        // 局部组件
        let app2 = new Vue({
            el: '#app2',
            components: {
                mycomp: mycomp  // 这里可以直接省略写成 mycomp
            }
        })
    
  • 使用
    <div id="app2">
        <mycomp></mycomp>
        <mycomp></mycomp>
        <mycomp></mycomp>
        <my-cpn></my-cpn>
    
    </div>
    
    

父子组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
   <parent></parent>
    <br> <br>
    <new></new>
</div>

<script src="lib/vue.js"></script>

<script>
    let son = Vue.extend({
        template: `
        <div>
            <h3>子组件-----------首</h3>
            <p>啦啦啦啦啦啦</p>
            <h3>子组件------------尾</h3>
        </div>
        `
    });
    let parent = Vue.extend({
        template: `
        <div>
            <h3>父组件-------首</h3>
                <son></son>
            <p>嘿嘿嘿嘿嘿嘿嘿或或和或或或或或或或或或或或或或或</p>
            <h3>父组件--------尾</h3>
        </div>
        `,
        components: {
            son: son
        }
    });

     // 全局组件
    Vue.component('parent', parent)

    // 使用语法糖的方式 注册全局组件
    Vue.component('new', {
        template: `
        <div>
            <h2>另外一种 创建组件的方式</h2>
        </div>
        `
    })

    const app = new Vue({
        el: '#app',
        data: {
            message: '你好,Vue'
        },
        // 局部组件
        // components: {
        //     parent: parent
        // }
    })

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

组件模板的抽离template

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>


<body>
<div id="app">
    <cpn></cpn>
    <br> <br>
    <comp></comp>
</div>

<script src="lib/vue.js"></script>
<script type="text/x-template" id="cpn01">
    <div>
        <h3>组件模板分离--第一种方式</h3>
    </div>
</script>

<template id="cpn02">
    <div>
        <h3>组件模块分离------第二种方式</h3>
        <h4>{{title}}</h4>
    </div>
</template>

<script>
    /*
        * 组件的数据存放
    * 组件对象也有一个data属性,也可以有methods属性,
    * 只是这个data属性是一个函数
    * 而且这个函数返回一个对象,对象内部保存着数据
    * */

    Vue.component('cpn', {
        template: '#cpn01'
    });

    Vue.component('comp', {
        template: '#cpn02',
        data() {
            return {
                title: '我是标题。。。。。。。'
            }
        }
    })

    const app = new Vue({
        el: '#app',
        data: {
            message: '你好,Vue'
        }
    })

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

组件中的数据存放问题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <h2>{{message}}</h2>

    <br>

    <mycpn></mycpn>
    <br>
    <hr>
    <mycpn02></mycpn02>
</div>

<template id="cpn">
    <div>
        <h3>{{title}}</h3>
        <p>{{content}}</p>
    </div>
</template>

<script type="text/x-template" id="cpn02">
    <div>
        <h4>{{title}}</h4>
        <p>{{content}}</p>
    </div>
</script>

<script src="lib/vue.js"></script>

<script>

    /*
    * 为什么组件中的data必须是一个函数?
    *   因为要保证 每个组件拥有属于它自己的状态,函数每次返回的对象都是一个新的对象。
    *   例如 创建组件  计数器, 然后使用多个 该组件
    * */

    const app = new Vue({
        el: '#app',
        data: {
            message: '你好,Vue'
        },
        components: {
            mycpn: {
                template: '#cpn',
                data() {
                    return {
                        title: '这是标题。。。。',
                        content: '这是内容。。。。'
                    }
                }
            },
            mycpn02: {
                template: '#cpn02',
                data() {
                    return {
                        title: '这是第二个组件的标题。。。',
                        content: '这是第二个组件的内容。。。'
                    };
                }
            }
        }
    })

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

父组件与子组件的数据通信

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <!--需要绑定一下-->
    <cpn v-bind:cmovies="movies" :cmessage="message"></cpn>
</div>

<script src="lib/vue.js"></script>

<template id="cpn">
    <div>
        <p>{{cmovies}}</p>
        <p>{{cmessage}}</p>
        <p>{{name}}</p>
        <ul>
            <li v-for="item in cmovies">
                {{item}}
            </li>
        </ul>
    </div>
</template>

<script>


    /*
    * 如何进行父子组件通信呢 ? Vue官方提到
    *  - 父  -->  子 通过props(属性) 传递数据
    *  - 子  -->  父 通过事件发送消息
    * */

    // 从父 向 子传数据,通过props
    const cpn = {
        template: '#cpn',
        data() {
            return {
                name: "子组件中的数据"
            }
        },
        methods: {

        },
        // 1. props: ['cmovies', 'cmessage'] // 对应 进行接收数据
        // 2. 第二种 使用对象
        props: {
            // cmovies: Array, // 定义类型
            // cmessage: String
            //3. 定义类型,还有可以设置默认值
            cmovies: {
                type: Array,
                // 类型是 一个 对象 或者 数组时, 默认值必须是一个函数,否则会报错。 经测试2.6.11版本没有报错
                // default: ['如果没有', '传值过来,', '这个将作', '为默认值渲', '染到界面上']
                default() {
                    return ['如果没有', '传值过来,', '这个将作', '为默认值渲', '染到界面上'];
                }
            },
            cmessage: {
                type: String,
                default: 'cmessage的默认值设定',
                required: true  // 必须要传的值
            }
        }
    };

    const app = new Vue({
        el: '#app',
        data: {
            message: '你好,Vue',
            movies: ['七龙珠', '鬼吹灯', '旋风小子', '钢铁侠']
        },
        components: {
            cpn: cpn
        }
    })

    // // 省略写法
    // let name = "124";
    // a = {
    //     name : name
    // }
    // // 可以简写成
    // b = {
    //  name
    // }

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

总之,子组件 通过props属性获取 父组件的数据。

组件通信 -- 父传子 (自定义属性)

使用的时候,绑定 props中对应变量,如果是驼峰标识,绑定的时候需要用 ‘ - ‘ 代替。

例如:

根vue实例

new Vue({
    el: '#app',

    data: {
        info: {
            name: '张三',
            gender: '男',
            age: '22'
        },
        message: '你好,Vue',
        isMyMessage: '这是的一条信息'
    }
})
props: {
    cInfo: {
        type: Object,
            default () {
                return [];
            }
    },
    cIsMyMessage: {
        type: String,
            default: ''
    }
}

组件

<template id="cpn">
    <!--当使用多个标签式,最好有一个 根 标签-->
    <div>
        <p>{{cInfo}}</p>
        <p>{{cIsMyMessage}}</p>
    </div>
</template>

我们使用的时候

<div id="app">
    <!--这里驼峰标识 要 改写成 ' - '  -->
    <cpn :c-info="info" :c-is-my-message="isMyMessage"></cpn>
</div>

v-bind:c-info="info", 这里的Info,是vue根实例中的变量名。

等于是将 父组件中变量info的值,绑定到 子组件中的 cInfo。

全部代码如下

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

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>

<body>
<div id="app">
    <!--这里驼峰标识 要 改写成 ' - '  -->
    <cpn :c-info="info" :c-is-my-message="isMyMessage"></cpn>
</div>

<script src="lib/vue.js"></script>
<template id="cpn">
    <!--当使用多个标签式,最好有一个 根 标签-->
    <div>
        <p>{{cInfo}}</p>
        <p>{{cIsMyMessage}}</p>
    </div>
</template>
<script>
    /*
     * props中使用驼峰标识接收, 会有问题
     * */

    // 全局组件
    Vue.component('cpn', {
        template: '#cpn',
        props: {
            cInfo: {
                type: Object,
                default() {
                    return [];
                }
            },
            cIsMyMessage: {
                type: String,
                default: ''
            }
        }
    });
    const app = new Vue({
        el: '#app',

        data: {
            info: {
                name: '张三',
                gender: '男',
                age: '22'
            },
            message: '你好,Vue',
            isMyMessage: '这是的一条信息'
        }
    })
</script>
</body>

</html>

组件通信 -- 子传父 (自定义事件)

  • 子组件

    <template id="son">
        <div>
            <ul>
                <li v-for="item in categories">
                    <button @click="itemClick(item)">{{item.name}}</button>
                </li>
            </ul>
        </div>
    </template>
    
  • 子组件中的数据

    const son = {
            template: '#son',
            data() {
                return {
                    categories: [{
                        id: 1,
                        name: '热门推荐'
                    }, {
                        id: 2,
                        name: '手机数码'
                    }, {
                        id: 3,
                        name: '家用家电'
                    }, {
                        id: 4,
                        name: '电脑办公'
                    },]
                }
            },
            methods: {
                itemClick(item) {
                    console.log('子组件', item.name);
                    // emit 发射事件:(自定义事件) 当前 函数名, 不能用驼峰命名,   传参数item
                    this.$emit('item-click', item)
                }
            }
        };
    

    当子组件中的 点击事件 触发后,进入函数。通过 this.$emit('当前函数名', 变量)如果当前函数名为驼峰标识,使用-代替。例如当前可以写成 item-click 。 到这里 自定义 事件已经完成,然后父组件 绑定 该事件即可。

  • 父组件

    <div id="app">
        <cpn v-on:item-click="cpnClick"></cpn>
    </div>
    

    引入子组件,并绑定 item-click事件,触发之后进入函数 cpnClick。

  • 父组件中的函数

    const app = new Vue({
        el: '#app',
        data: {
            message: '你好,Vue'
        },
        components: {
            cpn: son
        },
        methods: {
            cpnClick(item) {
                console.log('父组件----', item.name)
            }
        }
    })
    

    函数 中的参数,接收 来自 子组件传递来 的数据。

    完整代码

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    
    <body>
    <div id="app">
        <cpn v-on:item-click="cpnClick"></cpn>
    </div>
    <template id="son">
        <div>
            <ul>
                <li v-for="item in categories">
                    <button @click="itemClick(item)">{{item.name}}</button>
                </li>
            </ul>
        </div>
    </template>
    <script src="lib/vue.js"></script>
    
    
    <script>
        /**
         * 当子组件需要向父组件传递数据时,就要用到自定义事件了。
         * 之前学习的v-on 或 @ 不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件
         *
         * 自定义事件的流程:
         *      - 在子组件中,通过$emit()来触发事件
         *      - 在父组件中,通过v-on来监听事件
         *
         */
    
        const son = {
            template: '#son',
            data() {
                return {
                    categories: [{
                        id: 1,
                        name: '热门推荐'
                    }, {
                        id: 2,
                        name: '手机数码'
                    }, {
                        id: 3,
                        name: '家用家电'
                    }, {
                        id: 4,
                        name: '电脑办公'
                    },]
                }
            },
            methods: {
                itemClick(item) {
                    console.log('子组件', item.name);
                    // emit 发射事件:(自定义事件) 当前 函数名, 不能用驼峰命名,   传参数item
                    this.$emit('item-click', item)
                }
            }
        };
    
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好,Vue'
            },
            components: {
                cpn: son
            },
            methods: {
                cpnClick(item) {
                    console.log('父组件----', item.name)
                }
            }
        })
    </script>
    </body>
    
    </html>
    
    

自定义事件 练习 案例。

<!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">
        <apn @increase-click="increase" @less-click="less" :total2="total">

        </apn>
    </div>
    a
    <template id="son">
        <div>
            <p>{{total2}}</p>
            <p><button @click="increaseClick">增加</button> <button @click="lessClick">减少</button></p>
        </div>
    </template>

    <script src="lib/vue.js"></script>
    <script>
        const son = {
            template: '#son',

            methods: {
                increaseClick() {
                    this.$emit('increase-click')
                },
                lessClick() {
                    this.$emit('less-click')
                }
            },
            props: {
                total2: {
                    type: Number,
                    default: 0
                }
            }
        }
        const app = new Vue({
            el: '#app',
            data: {
                total: 1
            },
            methods: {
                increase() {
                    this.total += 1;
                },
                less() {
                    this.total -= 1;
                }
            },
            components: {
                apn: son
            }
        })

        /**
         * 总结:
         *      父 传 子 等于是 自定义 属性,
         *          v-bind:绑定子组件中的props的属性='父组件中data数据的名称' 
         *          即如代码中的 :total2="total" 或者 v-bind:total2="total"
         * 
         *      子 传 父 等于是 自定义 事件
         *          v-on:监听子组件方法中this.$emit提交的函数名="父组件方法中的方法名"
         *          即如代码中的 @increase-click="increase"       
         * 
         */
    </script>

</body>

</html>

watch 的使用

可以用来监听 某个 变量的 变化

<!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">
        <h2>num: {{number1}}</h2>
        <cpn :dnumber1="number1" @numchange="numchange"></cpn>
    </div>

    <template id="cpn">
        <div>
            <h3>{{dnumber1}}</h3>
            <input type="text" v-model="dnumber1">
        </div>
    </template>

    <script src="./lib/vue.js"></script>
    <script>
        /* 
                                                                                                                                                            watch函数,监听data中的数据变化,当值发生变化的时候,执行函数,该函数名就是变量名
                                                                                                                                                        */
        const app = new Vue({
            el: '#app',
            data: {
                number1: 1,
            },
            methods: {
                numchange(value) {
                    this.number1 = value;
                }
            },
            watch: {
                number1(newValue, oldValue) {
                    console.log('父组件中:旧数据是', oldValue, '---新数据是', newValue);
                }
            },
            components: {
                cpn: {
                    template: '#cpn',

                    props: {
                        number1: {
                            type: Number
                        }
                    },
                    data() {
                        return {
                            dnumber1: this.number1
                        };
                    },
                    watch: {
                        dnumber1(newValue, oldValue) {
                            console.log('子组件中:旧数据是', oldValue, '---新数据是', newValue);
                            this.$emit('numchange', newValue);
                        },
                        immediate: false
                    },
                }
            }
        })
    </script>
</body>

</html>

父子组件的访问方式

使用 $children、$refs、$parent、$root调用 方法

  • 子组件

    <template id="cpn">
        <div>
            <h3>我是子组件</h3>
            <button @click="getParent">调用父组件</button>
        </div>
    </template>
    
    cpn: {
        template: '#cpn',
            data() {
            return {
                name: '我是子组件中name属性'
            }
        },
            methods: {
                hello() {
                    console.log('我是 子组件中的hello函数');
                },
                    getParent() {
                        // 访问父组件 $parent
                        console.log(this.$parent.parent())
                        // 访问根组件 $root
                    }
            },
    }
    
  • 父组件

    <div id="app">
        <cpn></cpn>
        <cpn ref="aaa"></cpn>
        <cpn></cpn>
        <button @click="hello">调用子组件</button>
    </div>
    
    {
        el: '#app',
            data: {},
                methods: {
                    hello() {
                        //1. 通过 $children,它 是一个数组
                        this.$children[0].hello()
                        for (let child of this.$children) {
                            console.log(child.name)
                        }
                        //2. 通过$refs, 它默认是一个空的对象类型。 这个需要 在cpn中定义属性ref
                        this.$refs.aaa.hello() // 调用指定的子组件
                    },
                        parent() {
                            console.log('这是父组件的函数,被子组件调用')
                        }
                }
    

插槽的使用

最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽
一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容
是搜索框,还是文字,还是菜单。由调用者自己来决定

  1. 插槽的基本使用
    <slot></slot>
    
  2. 插槽的默认值
    <slot><button>这个是默认值</button></slot>
    
  3. 如果有多个值,同时放入到组件进行替换时,一起作为替换元素
  • 子组件
    <template id="cpn">
    	<div>
            <h3>我是子组件</h3>
            <p>内容,子组件</p>
            <slot><button>这个是插槽的默认值</button></slot>
        </div>
    </template>
    
  • 父组件
    <div id="app">
        <!-- 
            1. 插槽的基本使用 <slot></slot>
            2. 插槽的默认值 <slot><button>这个是默认值</button></slot>
            3. 如果有多个值,同时放入到组件进行替换时,一起作为替换元素
        -->
        <cpn><button>插槽按钮</button></cpn>
        <cpn><a href="#">插槽超链接</a></cpn>
        <cpn>
            <p>插槽文字</p>
            <strong>我也时插槽中的文字</strong>
        </cpn>
        <cpn></cpn>
    </div>
    

使用子组件的时候,子组件标签中的值,会替换掉插槽中的值

具名插槽

就是给插槽起个名字,插入值的时候,可以插入指定的插槽。

  • 子组件
    <template id="cpn">
    	<div>
            <h2>我是子组件,我有很多插槽</h2>
            <slot name="left"><span>左边</span></slot>
            <slot name="center"><span>中间</span></slot>
            <slot name="right"><span>右边</span></slot>
        </div>
    </template>
    
  • 父组件
    <div id="app">
        <cpn>
            <h2>替换内容</h2>
            <!-- 这种只会 替换没有名字的插槽,有名字的插槽不会改变 -->
            <strong slot="center">我想替换中间的插槽</strong>
        </cpn>
    </div>
    

这样,中间插槽中的数据就被替换了。

作用域插槽

父组件 替换 插槽的标签 ,但是内容是由子组件来提供的

子组件 使用 :data绑定数据

<template id="cpn">
	<div>
        <slot :data="dLanguages">
            <ul>
                <li v-for="item in dLanguages">{{item}}</li>
            </ul>
        </slot>
    </div>
</template>
const app = new Vue({
    el: '#app',
    data: {},
    methods: {},
    components: {
        cpn: {
            template: '#cpn',
            data() {
                return {
                    dLanguages: ['JavaScript', 'Python', 'Java', 'C++', 'C#', 'C', 'Go']
                }
            }
        }
    }
});

父组件调用。slot-scope="slot"来取得作用域插槽 :data绑定的数据

<div id="app">
    <!-- 默认使用li进行遍历 -->
    <cpn></cpn>
  
    <!-- 使用span进行遍历 用 - 连接 -->
    <cpn>
        <!-- 低版本的只支持 template -->
        <template slot-scope="slot">
            <div>
                <span v-for="item in slot.data">{{item}} - </span>
            </div>
        </template>
    </cpn>


    <cpn>
        <div slot-scope="slot">
            <span v-for="item in slot.data">
                {{item}} * 
            </span>
        </div>
    </cpn>

    <cpn>
        <div slot-scope="slot">
            {{slot.data.join(' + ')}}
        </div>
    </cpn>

</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="./lib/vue.js"></script>
</head>

<body>
    <div id="app">
        <cpn></cpn>
        <cpn>
            <!-- 低版本的只支持 template -->
            <template slot-scope="slot">
                <div>
                    <span v-for="item in slot.data">{{item}} - </span>
                </div>
            </template>
        </cpn>

        <cpn>
            <div slot-scope="slot">
                <span v-for="item in slot.data">
                    {{item}} * 
                </span>
            </div>
        </cpn>

        <cpn>
            <div slot-scope="slot">
                {{slot.data.join(' + ')}}
            </div>
        </cpn>

    </div>

    <template id="cpn">
        <div>
            <slot :data="dLanguages">
                <ul>
                    <li v-for="item in dLanguages">{{item}}</li>
                </ul>
            </slot>
        </div>
    </template>

    <script>
        /* 
            父组件 替换 插槽的标签 ,但是内容是由子组件来提供的
            */

        const app = new Vue({
            el: '#app',
            data: {},
            methods: {},
            components: {
                cpn: {
                    template: '#cpn',
                    data() {
                        return {
                            dLanguages: ['JavaScript', 'Python', 'Java', 'C++', 'C#', 'C', 'Go']
                        }
                    }
                }
            }
        });
    </script>
</body>

</html>

标题:Vue框架入门系列(一)
作者:gitsilence
地址:http://blog.lacknb.cn/articles/2020/04/25/1587785902124.html