一、简介

  Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
在这里插入图片描述

二、vue 的安装

  Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

  最简单的安装方式是创建一个 .html 文件,然后通过如下方式引入 Vue:

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

  当然也可以使用 Node.js 的构建工具安装 vue-cli。这种方式主要针对专业前端开发,这里便不再赘述。

  为了使 idea 有更好的代码提示效果,建议安装 vue.js 插件。
在这里插入图片描述
  Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统,以操作数据的方式操作 DOM 节点,Vue 不建议使用者直接操作节点内容,目前大部分开发平台都倡导以修改数据的方式操作节点,操作节点被认为是一种落后的技术。

<div id="app">
  {{ message }}
</div>

  渲染代码:

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

  如此便成功创建了第一个 Vue 应用!看起来这跟渲染一个字符串模板非常类似,但是 Vue 在背后做了大量工作。现在数据和 DOM 已经被建立了关联,所有东西都是响应式的。
  通过浏览器控制台输入 app.message=“测试” 可以修改vue的数据,会发现一旦数据变化了,vue 管理的页面也发生变化。
  为了让用户和你的应用进行交互,我们可以用 v-on 指令添加一个事件监听器,通过它调用在 Vue 实例中定义的方法:

<div id="app-5">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage">反转消息</button>
</div>

  渲染代码:

var app5 = new Vue({
  el: '#app-5',
  data: {
    message: 'Hello Vue.js!'
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    }
  }
})

  注意在 reverseMessage 方法中,更新了应用的状态,但没有触碰 DOM。所有的 DOM 操作都由 Vue 来处理,编写的代码只需要关注逻辑层面即可。

练习:

1.在页面中编写一个点击次数统计的功能,点击一次值增加一。

参考代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>练习</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app" style="text-align: center">
    <h1>{{count}}</h1>
    <input type="button" value="增加" v-on:click="add()">
</div>
<script>
    new Vue({
        el: "#app",
        data: {
            count: 0
        },
        methods: {
            add() {
                this.count++;
            }
        }
    })
</script>
</body>
</html>

三、生命周期钩子

  每个 Vue 实例在被创建时都要经过一系列的初始化过程。例如:需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
  比如 created 钩子可以用来在一个实例被创建之后执行代码:

new Vue({
  data: {
    a: 1
  },
  created: function () {
    // `this` 指向 vm 实例
    console.log('a is: ' + this.a)
  }
})
// => "a is: 1"

  也有一些其它的钩子,在实例生命周期的不同阶段被调用,如 mounted、updated 和 destroyed。生命周期钩子的 this 上下文指向调用它的 Vue 实例。

  下图展示了实例的生命周期。销毁的事件需要在 vue 脚手架中才能使用。
在这里插入图片描述
  这是官方文档给出的一个组件从被创建出来到最后被销毁所要经历的一系列过程,所以这个过程也叫做一个组件的生命周期图。从图中我们可以看到,一个组件从被创建到最后被销毁,总共要经历以下8个过程:
  beforeCreate:组件创建之前
  created:组件创建完毕
  beforeMount:组件挂载之前
  mounted:组件挂载完毕
  beforeUpate:组件更新之前
  upated:组件更新完毕
  beforeDestoy:组件销毁之前
  destoyed:组件销毁完毕

练习:

1.在上一个练习中使用 created 生命周期函数完成数据从缓存中读取。

参考代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>练习</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app" style="text-align: center">
    <h1>{{count}}</h1>
    <input type="button" value="增加" v-on:click="add()">
</div>
<script>
    new Vue({
        el: "#app",
        data: {
            count: 0
        },
        methods: {
            add() {
                this.count++;
                sessionStorage.count = this.count;
            }
        },
        created() {
            if (sessionStorage.count) {
                this.count = sessionStorage.count
            }
        }
    })
</script>
</body>
</html>

四、模板语法

  1.插值

  数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:

<span>Message: {{ msg }}</span>

  Mustache 标签将会被替代为对应数据对象上 msg 属性的值。无论何时,绑定的数据对象上 msg 属性发生了改变,插值处的内容都会更新。

  通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:

<span v-once>这个将不会改变: {{ msg }}</span>

  2.原始 HTML

  双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令:

<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

  这个 span 的内容将会被替换成为属性值 rawHtml,直接作为 HTML——会忽略解析属性值中的数据绑定。

  3.Attribute

  Mustache 语法不能作用在 HTML attribute 上,遇到这种情况应该使用 v-bind 指令:

<div v-bind:id="dynamicId"></div>

  对于布尔 attribute (它们只要存在就意味着值为 true),v-bind 工作起来略有不同,在这个例子中:

<button v-bind:disabled="isButtonDisabled">Button</button>

  如果 isButtonDisabled 的值是 null、undefined 或 false,则 disabled attribute 甚至不会被包含在渲染出来的 button 元素中。

  4.使用 JavaScript 表达式

  迄今为止,在我们的模板中,我们一直都只绑定简单的属性键值。但实际上,对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。

{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

<div v-bind:id="'list-' + id"></div>

练习:

1.制作一个点击一次在页面增加一张照片的按钮。每次点击都将插入一张相同的照片到页面。

参考代码:

<div id="app" style="text-align: center">
    <p v-html="content"></p>
    <input type="button" value="增加" v-on:click="add()">
</div>
<script>
    new Vue({
        el: "#app",
        data: {
            content: ""
        },
        methods: {
            add() {
                this.content += "<img src=http://58.42.239.163:8888/static/img/noteBook/PAZrEvLF.jpg width='100'>";
            }
        }
    })
</script>

五、常用指令

  指令(Directives)是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。回顾前面看到的例子:

<p v-if="seen">现在你看到我了</p>

  这里,v-if 指令将根据表达式 seen 的值的真假来插入/移除元素。

  一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind 指令可以用于响应式地更新 HTML attribute:

<a v-bind:href="url">...</a>

  在这里 href 是参数,告知 v-bind 指令将该元素的 href 与 attribute 与表达式 url 的值绑定。

  另一个例子是 v-on 指令,它用于监听 DOM 事件:

<a v-on:click="doSomething">...</a>

  在这里参数是监听的事件名。我们也会更详细地讨论事件处理。

  从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数,但是变量名会被 vue 转为小写,所以建议使用下划线命名。

<a v-bind:[attribute_name]="url"> ... </a>

  这里的 attribute_name 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,Vue 实例有一个 data 属性 attribute_name,其值为 “href”,那么这个绑定将等价于 v-bind:href。
  同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:

<a v-on:[event_name]="doSomething"> ... </a>

  在这个示例中,当 eventName 的值为 “focus” 时,v-on:[eventName] 将等价于 v-on:focus。

  指令缩写

  1.v-bind 缩写

<!-- 完整语法 -->
<a v-bind:href="url">...</a>

<!-- 缩写 -->
<a :href="url">...</a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>

  2.v-on 缩写

<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>

<!-- 缩写 -->
<a @click="doSomething">...</a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>

  它们看起来可能与普通的 HTML 略有不同,但 : 与 @ 对于 attribute 名来说都是合法字符,在所有支持 Vue 的浏览器都能被正确地解析。而且,它们不会出现在最终渲染的标记中。缩写语法是完全可选的。

练习:

1.在页面上有一张照片和一个按钮,按钮用于控制照片的显示和隐藏,点击后按钮的文字也变成相应的文字“隐藏”或“显示”。

参考代码:

<div id="app" style="text-align: center">
    <p><img v-show="show" src=http://58.42.239.163:8888/static/img/noteBook/PAZrEvLF.jpg width='100'></p>
    <button v-on:click="add()">{{show?'隐藏':'显示'}}</button>
</div>
<script>
    new Vue({
        el: "#app",
        data: {
            show: true
        },
        methods: {
            add() {
                this.show = !this.show;
            }
        }
    })
</script>

六、数据侦听器

  当需要监听数据变化时就需要侦听器。Vue 通过 watch 选项提供了一套更通用的方法,来响应数据的变化。

<div id="watch-example">
  <p>
    Ask a yes/no question:
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
</div>

  渲染代码:

var watchExampleVM = new Vue({
  el: '#watch-example',
  data: {
    question: '',
    answer: 'I cannot give you an answer until you ask a question!'
  },
  watch: {
    // 如果 `question` 发生改变,这个函数就会运行
    question: function (newQuestion, oldQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.debouncedGetAnswer()
    }
  }
})

练习:

1.做一个输入框文本长度校验,当长度大于 5 后文字变成红色,同时 alert 弹窗警告。

参考代码:

<div id="app" style="text-align: center">
    <p><input v-model="input" v-bind:style="inputStyle"></p>
</div>
<script>
    new Vue({
        el: "#app",
        data: {
            input: "",
            inputStyle: {
                color: "black"
            }
        },
        watch: {
            input(newStr, oldStr) {
                if (newStr.length > 5) {
                    alert("超出范围");
                    this.inputStyle.color = "red"
                }
            }
        }
    })
</script>

七、Class、Style 绑定

  我们可以传给 v-bind:class 一个对象,以动态地切换 class:

<div v-bind:class="{ active: isActive }"></div>

  上面的语法表示 active 这个 class 存在与否将取决于数据 property isActive 的 truthiness。 你可以在对象中传入更多字段来动态切换多个 class。此外,v-bind:class 指令也可以与普通的 class attribute 共存。当有如下模板:

<div class="static"
  v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>

  和如下 data:

data: {
  isActive: true,
  hasError: false
}

  结果渲染为:

<div class="static active"></div>

  当 isActive 或者 hasError 变化时,class 列表将相应地更新。例如,如果 hasError 的值为 true,class 列表将变为 “static active text-danger”。

  绑定的数据对象不必内联定义在模板里:

<div v-bind:class="classObject"></div>
data: {
  classObject: {
    active: true,
    'text-danger': false
  }
}

  v-bind:style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

  渲染模板数据:

data: {
  activeColor: 'red',
  fontSize: 30
}

  直接绑定到一个样式对象通常更好,这会让模板更清晰:

<div v-bind:style="styleObject"></div>

  渲染模板数据:

data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

  v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上:

<div v-bind:style="[baseStyles, overridingStyles]"></div>

练习:

1.按照如下设计图完成练习,点击对应的按钮完成如下的星级评分模块。
在这里插入图片描述
参考代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>练习</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0
        }

        .stars {
            margin: 10px;
            padding: 10px;
            border: 1px saddlebrown solid;
        }

        .stars span {
            float: left;
            height: 30px;
            line-height: 30px;
        }

        .stars i {
            width: 30px;
            height: 30px;
            line-height: 30px;
            float: left;
            margin-right: 30px;
            background: #ccc;
            color: #fff;
            text-align: center;
            cursor: default;
            font-style: normal;
        }

        .stars .on {
            color: #a71417;
        }

    </style>
</head>
<body>
<div id="app">
    <div class="stars">
        <span>商品评价:</span>
        <i v-bind:class="stars1>=1?'on':''" v-on:click="stars1=1"></i>
        <i v-bind:class="stars1>=2?'on':''" v-on:click="stars1=2"></i>
        <i v-bind:class="stars1>=3?'on':''" v-on:click="stars1=3"></i>
        <i v-bind:class="stars1>=4?'on':''" v-on:click="stars1=4"></i>
        <i v-bind:class="stars1>=5?'on':''" v-on:click="stars1=5"></i>
        <input type="text" v-model="stars1"/>
    </div>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            stars1: 0,

        },
        methods: {}
    })
</script>
</body>
</html>

八、条件渲染

  1.v-if 用于条件限制某节点的渲染与否。

<h1 v-if="awesome">Vue is awesome!</h1>

  也可以用 v-else 添加一个“else 块”:

<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no </h1>

  vue 2.1.0 新增 v-else-if,顾名思义,充当 v-if 的“else-if 块”,可以连续使用:

<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>

  2.v-show 用于根据条件展示元素的指令。用法与 v-if 大致一样。

<h1 v-show="ok">Hello!</h1>

  不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。

练习:

1.分别使用 v-show 与 v-if 完成如下操作,点击对应的盒子使其隐藏。
在这里插入图片描述
参考代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>练习</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
        #app {
            width: 80%;
        }
        .box {
            width: 100px;
            height: 100px;
            background: yellow;
            margin: 10px;
            float: left;
            text-align: center;
            line-height: 100px;
            font-size: 30px;
        }
    </style>
</head>
<body>
<div id="app">
    <div v-show="box1" v-on:click="box1=false">1</div>
    <div v-show="box2" v-on:click="box2=false">2</div>
    <div v-show="box3" v-on:click="box3=false">3</div>
    <div v-show="box4" v-on:click="box4=false">4</div>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            box1: true,
            box2: true,
            box3: true,
            box4: true
        },
    })
</script>
</body>
</html>

参考2:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
        #app {
            width: 80%;
        }
        .box {
            width: 100px;
            height: 100px;
            background: yellow;
            margin: 10px;
            float: left;
            text-align: center;
            line-height: 100px;
            font-size: 30px;
        }
    </style>
</head>
<body>
<div id="app">
    <h1 class="box" v-for="d in 4"  @click="show(d)" v-if="!redIndex.includes(d)">{{d}}</h1>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            redIndex:[],
        },
        methods: {
            show(data) {
                this.redIndex.push(data);
            }
        }
    });
</script>
</body>
</html>

九、列表渲染

  我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。

<ul id="example-1">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>

  渲染代码:

var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})

  在 v-for 还支持一个可选的第二个参数,即当前项的索引。

<ul id="example-2">
  <li v-for="(item, index) in items">
    - {{ index }} - {{ item.message }}
  </li>
</ul>

  你也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法:

<div v-for="item of items"></div>

  在 v-for 里使用对象,也可以用 v-for 来遍历一个对象的属性。

<ul id="v-for-object">
  <li v-for="value in object">
    {{ value }}
  </li>
</ul>

  渲染代码:

new Vue({
  el: '#v-for-object',
  data: {
    object: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
})

  你也可以提供第二个的参数为 property 名称 (也就是键名):

<div v-for="(value, name) in object">
  {{ name }}: {{ value }}
</div>

  还可以用第三个参数作为索引:

<div v-for="(value, name, index) in object">
  {{ index }}. {{ name }}: {{ value }}
</div>

十、表单输入绑定

  你可以用 v-model 指令在表单 input、textarea 及 select 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。

  v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:

  • text 和 textarea 元素使用 value 属性和 input 事件
  • checkbox 和 radio 使用 checked 属性和 change 事件
  • select 字段将 value 作为 prop 并将 change 作为事件

  1.单行文本

<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>

  2.多行文本

<p>Message is: {{ message }}</p>
<textarea v-model="message" placeholder="add multiple lines"></textarea></p><p>3.复选框</p><p>单个复选框,绑定到布尔值:</p><pre><code><xmp><input type="checkbox" v-model="checked">
<p>选中:{{ checked }}</p>

  多个复选框,绑定到同一个数组:

<div id='example-3'>
  <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
  <label for="jack">Jack</label>
  <input type="checkbox" id="john" value="John" v-model="checkedNames">
  <label for="john">John</label>
  <input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
  <label for="mike">Mike</label>
  
  <span>Checked names: {{ checkedNames }}</span>
</div>

  4.单选按钮绑定到普通字符串

<div id="example-4">
  <input type="radio" id="one" value="One" v-model="picked">
  <label for="one">One</label>
  
  <input type="radio" id="two" value="Two" v-model="picked">
  <label for="two">Two</label>
  
  <span>Picked: {{ picked }}</span>
</div>

  5.选择框

  单选时:

<div id="example-5">
  <select v-model="selected">
    <option disabled value="">请选择</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <span>Selected: {{ selected }}</span>
</div>

  多选时 (绑定到一个数组):

<div id="example-6">
  <select v-model="selected" multiple>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  
  <span>Selected: {{ selected }}</span>
</div>

  用 v-for 渲染的动态选项:

<select v-model="selected">
  <option v-for="option in options" v-bind:value="option.value">
    {{ option.text }}
  </option>
</select>
<span>Selected: {{ selected }}</span>

  渲染数据:

new Vue({
  el: '...',
  data: {
    selected: 'A',
    options: [
      { text: 'One', value: 'A' },
      { text: 'Two', value: 'B' },
      { text: 'Three', value: 'C' }
    ]
  }
})

练习:

1.某扑克牌有 25 张扑克,使用 vue 完成扑克牌第一次点击会往上抽出,再点一次将会被程序移除。
在这里插入图片描述
参考代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
        .box {
            margin: 100px auto;
            width: 500px;
            height: 200px;
            display: flex;
            justify-content: center;
        }

        .item {
            width: 20px;
            height: 90px;
        }

        .item img {
            width: 75px;
        }

        .active {
            margin-top: -10px;
        }
    </style>
</head>
<body>
<div id="app" class="box">
    <div class="item" :class="index==activeIndex?'active':''"
         v-for="(d,index) in list" @click="sendPoker(index)">
        <img :src="'poker/'+d+'.png'"></div>
</div>
<script>
    new Vue({
        el: "#app",
        data: {
            list: ["0_0", "1_0", "h_6", "h_6", "c_1", "s_1", "h_9", "c_1",
                "s_7", "h_2", "d_8", "s_3", "h_9", "c_4", "d_6", "h_7", "d_3",
                "s_5", "h_2", "d_4", "s_6", "h_7", "c_3", "d_5", "h_3"],
            activeIndex: -1,
        },
        created() {
            this.list.sort();
        }
        , methods: {
            sendPoker(index) {
                if (this.activeIndex === index) {
                    this.list.splice(index, 1)
                    this.activeIndex = -1;
                    return
                }
                this.activeIndex = index;
            }
        }
    });
</script>

</body>
</html>

十一、组件

  组件(Component)是 Vue.js 最强大的功能之一。 组件可以扩展 HTML 元素,封装可重用的代码。 组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:
在这里插入图片描述
  注册一个全局组件语法格式如下:

Vue.component(tagName, options)

  tagName 为组件名,options 为配置选项。

  注册后,我们可以使用以下方式来调用组件:

<tagName></tagName>

  组件虽然在单独使用 vue.js 时使用不多,但在 vue 单页面应用即 vue 脚手架中使用颇多。

<div id="app">
    <runoob></runoob>
</div>
 
<script>
// 注册
Vue.component('runoob', {
  template: '<h1>自定义组件!</h1>'
})
// 创建根实例
new Vue({
  el: '#app'
})
</script>

  Prop

  prop 是子组件用来接受父组件传递过来的数据的一个自定义属性。 父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 “prop”:

<div id="app">
    <child message="hello!"></child>
</div>
 
<script>
// 注册
Vue.component('child', {
  // 声明 props
  props: ['message'],
  // 同样也可以在 vm 实例中像 "this.message" 这样使用
  template: '<span>{{ message }}</span>'
})
// 创建根实例
new Vue({
  el: '#app'
})
</script>

  动态 Prop

  类似于用 v-bind 绑定 HTML 特性到一个表达式,也可以用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件。

<div id="app">
    <div>
        <input v-model="parentMsg">
        
        <child v-bind:message="parentMsg"></child>
    </div>
</div>

<script>
    Vue.component('child', {
        props: {'message': {type: String, default: ""}},
        template: '<span>{{ message }}</span>'
    })
    new Vue({
        el: '#app',
        data: {
            parentMsg: '父组件内容'
        }
    })
</script>

注意:

  1.prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。

  2.props 里面的键必须是中划线命名,不能使用驼峰命名。

  当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。 type 可以是下面原生构造器: String Number Boolean Array Object Date Function Symbol type 也可以是一个自定义构造器,使用 instanceof 检测。

  子组件向父组件传值,子组件向父组件传值是通过 emit 方法的方式。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <h1>{{formSon}}</h1>
    <com2 @func="show"></com2>
    <com2 @func="show"></com2>
</div>

<template id="tmpl">
    <div>
        <p>这是子组件</p>
        <input type="button" :value="son.age" @click="sonClick()">
    </div>
</template>

<script>

    Vue.component('com2', {
        template: '#tmpl',
        data() {
            return {
                son: {
                    name: '小头儿子',
                    age: 6
                }
            }
        },
        methods: {
            sonClick() {
                this.son.age++;
                this.$emit('func', this.son)
            }
        }
    });


    let vm = new Vue({
        el: '#app',
        data: {
            formSon: {}
        },
        methods: {
            show(data) {
                this.formSon = data
            }
        }
    });
</script>
</body>
</html>

练习:

1.如下商品列表,灰色部分为子组件内容,在子组件中点击按钮可以增加购买数量,每次子组件点击都修改父组件的总价。
在这里插入图片描述
参考代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <h1>商品列表</h1>
    <h3>总价:{{allPrice}}</h3>
    <p v-for="item in goods">
        <span>{{item.name}}</span>
        <button-counter :price="item.price" @increment="increment"></button-counter>
    </p>
</div>

<script>

    Vue.component('button-counter', {
        template: `
            <div style="background-color: #eee">
                <p>价格:{{price}},花费:{{price * count}}
                    <button v-on:click="incrementHandler">数量:{{ count }}</button>
                </p>
            </div>`,
        data: function () {
            return {
                count: 0
            }
        },
        methods: {
            incrementHandler: function () {
                this.count += 1
                this.$emit('increment', this.price)
            }
        },
        props: {
            price: {
                type: Number,
                default: 1
            }
        }
    })
    let app = new Vue({
        el: '#app',
        data: {
            allPrice: 0,
            goods: [
                {name: "商品1", price: 1},
                {name: "商品2", price: 2},
                {name: "商品3", price: 3},
            ]
        },
        methods: {
            increment(data) {
                this.allPrice += data;
            }
        }
    })
</script>
</body>
</html>

十二、插槽

  插槽就是子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的标签。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <h1>{{message}}</h1>
    <com2>
        <div>
            <h1>父组件给子组件传代码块1</h1>
        </div>
    </com2>
    <com2>
        <div>
            <h1>父组件给子组件传代码块2</h1>
        </div>
    </com2>
</div>

<template id="tmpl">
    <div>
        <p>这是子组件</p>
        <slot></slot>
    </div>
</template>

<script>

    Vue.component('com2', {
        template: '#tmpl',
        data() {
            return {}
        }
    });


    let vm = new Vue({
        el: '#app',
        data: {
            message: "演示插槽"
        },
        methods: {}
    });
</script>
</body>
</html>

  注意:如果子组件没有使用插槽,父组件如果需要往子组件中填充模板或者html, 是没法做到的。

  具名插槽案例:

  具名插槽其实就是给插槽取个名字。一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时,可以根据这个名字把内容填充到对应插槽中,父组件通过 v-slot:[name] 的方式指定到对应的插槽中。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <h1>{{message}}</h1>
    <com2>
        <template v-slot:slot1>
            <h1>父组件给子组件传代码块1</h1>
        </template>
        <template v-slot:slot2>
            <h1>父组件给子组件传代码块2</h1>
        </template>
    </com2>
    <com2>
        <template v-slot:slot2>
            <h1>父组件给子组件传代码块2</h1>
        </template>
        <template v-slot:slot1>
            <h1>父组件给子组件传代码块1</h1>
        </template>
    </com2>
</div>

<template id="tmpl">
    <div>
        <p>这是子组件</p>
        <slot name="slot1"></slot>
        <slot name="slot2"></slot>
    </div>
</template>

<script>

    Vue.component('com2', {
        template: '#tmpl',
        data() {
            return {}
        }
    });


    let vm = new Vue({
        el: '#app',
        data: {
            message: "演示插槽"
        },
        methods: {}
    });
</script>
</body>
</html>
Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐