博客列表 >vue学习之路(组件)

vue学习之路(组件)

培(信仰)
培(信仰)原创
2021年02月20日 22:18:57602浏览

vue学习之路(组件)

什么是组件

组件是可复用的 Vue 实例,且带有一个名字。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用

组件与组件树

  1. <!-- 挂载点:是隐式声明根组件 -->
  2. <div id="root">
  3. <child-component></child-component>
  4. <child-component></child-component>
  5. <child-component></child-component>
  6. </div>
  7. <script>
  8. // 组件:从形式上看就是一个自定义html标签
  9. // 组件是可复用的vue实例,构造函数Vue的子类
  10. // 1. 创建
  11. // const childComponent = Vue.extend({
  12. // template: "<h2>hello world</h2>",
  13. // });
  14. // 2. 注册
  15. // 使用Vue.component()静态方法注册的是 全局组件
  16. // Vue.component(组件名,对象字面量表示的组件配置项)
  17. // 组件名:自定义的html标签
  18. // Vue.component("child-component", childComponent);
  19. Vue.component("child-component", {
  20. template: "<h2>hello world</h2>",
  21. });
  22. // 3. 挂载
  23. // 将标签添加到页面渲染
  24. const vm = new Vue({
  25. el: document.querySelector("#root"),
  26. });
  27. </script>

全局组件

全局组件:全局可见,声明在Vue实例外部

  1. <div id="root">
  2. <button-inc></button-inc>
  3. </div>
  4. <hr />
  5. <div id="app">
  6. <button-inc></button-inc>
  7. </div>
  8. <script>
  9. // 全局组件:全局可见,声明在Vue实例外部
  10. Vue.component("button-inc", {
  11. // 组件中的模板代码:允许存在数据占位符的html字符串
  12. // 模板内容必须写到一对父元素标签中
  13. template: `
  14. <div>
  15. <button @click="count++">点赞:+{{count}}</button>
  16. </div>
  17. `,
  18. data() {
  19. return {
  20. count: 0,
  21. };
  22. },
  23. });
  24. const vm = new Vue({
  25. el: document.querySelector("#root"),
  26. });
  27. const vm1 = new Vue({
  28. el: document.querySelector("#app"),
  29. });
  30. // 全局组件可以在多个Vue实例共享,通常一个项目只有一个vue实例,全局组件尽可能不用,使用局部组件。
  31. </script>

还可以将组件中的 template 提出去到html中。

  1. <div id="root">
  2. <button-inc></button-inc>
  3. </div>
  4. <hr />
  5. <div id="app">
  6. <button-inc></button-inc>
  7. </div>
  8. <!-- 模板开始 -->
  9. <template id="inc">
  10. <div>
  11. <button @click="count++">点赞:+{{count}}</button>
  12. </div>
  13. </template>
  14. <!-- 模板结束 -->
  15. <script>
  16. Vue.component("button-inc", {
  17. template: "#inc",
  18. // 组件中必须使用函数data()来声明组件变量
  19. data() {
  20. return {
  21. count: 0,
  22. };
  23. },
  24. });
  25. const vm = new Vue({
  26. el: document.querySelector("#root"),
  27. });
  28. const vm1 = new Vue({
  29. el: document.querySelector("#app"),
  30. });
  31. // 全局组件可以在多个Vue实例共享,通常一个项目只有一个vue实例,全局组件尽可能不用,使用局部组件。
  32. </script>

局部组件

局部组件是属于vue实例

  1. <div id="root">
  2. <my-child></my-child>
  3. </div>
  4. <template id="hello">
  5. <p>hello {{userName}}</p>
  6. </template>
  7. <script>
  8. const vm = new Vue({
  9. el: document.querySelector("#root"),
  10. // 局部组件是属于vue实例
  11. components: {
  12. "my-child": {
  13. template: "#hello",
  14. data() {
  15. return {
  16. userName: "TJ",
  17. };
  18. },
  19. },
  20. },
  21. });
  22. </script>

另一种简写方案

  1. <div id="root">
  2. <hello></hello>
  3. </div>
  4. <script>
  5. const hello={
  6. template: `<p>hello {{userName}}</p>`,
  7. data() {
  8. return {
  9. userName: "TJ",
  10. },
  11. }
  12. };
  13. const vm = new Vue({
  14. el: document.querySelector("#root"),
  15. // 局部组件是属于vue实例
  16. components: {hello},
  17. },
  18. });
  19. </script>

组件之间的传参:父组件向子组件传参

父组件是通过自定义属性的方式将参数传到子组件中的

  1. <div id="app">
  2. <!-- 父组件是通过自定义属性的方式将参数传到子组件中的 -->
  3. <btn-inc :username="username" :count="count"></btn-inc>
  4. </div>
  5. <script>
  6. const vm = new Vue({
  7. el: document.querySelector("#app"),
  8. data() {
  9. return {
  10. username: "TJ",
  11. count: 0,
  12. };
  13. },
  14. //局部组件
  15. components: {
  16. btnInc: {
  17. props: ["username","count"],
  18. // 报错原因,组件之间的数据传递是单向的,不允许在子组件中更新父组件的数据。
  19. template: `
  20. <div>
  21. <button @click="inc(1)">点赞:+{{num}}</button>
  22. <span>{{username}}</span>
  23. </div>
  24. `,
  25. data(){
  26. return {
  27. num:this.count,
  28. }
  29. },
  30. methods:{
  31. inc(n) {
  32. this.num += n;
  33. }
  34. }
  35. },
  36. },
  37. });
  38. </script>

组件之间的传参:子组件向父组件传参

  1. <div id="app">
  2. <!-- 子组件中的更新父组件数据是通过自定义同名事件完成的-->
  3. <btn-inc
  4. :username="username"
  5. :count="count"
  6. @click-count="handle"
  7. ></btn-inc>
  8. </div>
  9. <script>
  10. const vm = new Vue({
  11. // 子组件向父组件传参是通过声明同名事件来实现
  12. el: document.querySelector("#app"),
  13. data() {
  14. return {
  15. username: "TJ",
  16. count: 0,
  17. };
  18. },
  19. //局部组件
  20. components: {
  21. btnInc: {
  22. props: ["username", "count"],
  23. // 报错原因,组件之间的数据传递是单向的,不允许在子组件中更新父组件的数据。
  24. // $emit(父组件中要使用的方法名称,子组件要传给父组件的值)
  25. template: `
  26. <div>
  27. <button @click="$emit('click-count',10)">点赞:+{{count}}</button>
  28. <span>{{username}}</span>
  29. </div>
  30. `,
  31. methods: {
  32. inc(n) {
  33. this.num += n;
  34. },
  35. },
  36. },
  37. },
  38. // 父组件更新数据的方法
  39. methods: {
  40. handle(value) {
  41. this.count += value;
  42. this.username = "T.,j";
  43. console.log(vm.count);
  44. },
  45. },
  46. });
  47. // 总结:
  48. // 1. 父组件 向 子组件 传参: 自定义属性
  49. // 2. 子组件 向 父组件 传参: 自定义方法
  50. </script>

组件之间的双向传参

  1. <div id="demo">
  2. <input type="text" :value="value" @input="value = $event.target.value" />
  3. <p>{{value}}</p>
  4. </div>
  5. <script>
  6. new Vue({
  7. el: "#demo",
  8. data() {
  9. return {
  10. value: 123,
  11. };
  12. },
  13. });
  14. </script>
  15. <hr />
  16. <div id="app">
  17. <p>父组件 单价:{{price}} 元</p>
  18. <p>
  19. <span>子组件</span
  20. ><my-input :my-price="price" @input-text="handle"></my-input>
  21. </p>
  22. </div>
  23. <script>
  24. const vm = new Vue({
  25. // 子组件向父组件传参是通过声明同名事件来实现
  26. el: document.querySelector("#app"),
  27. data() {
  28. return {
  29. price: 4588,
  30. };
  31. },
  32. //局部组件
  33. components: {
  34. myInput: {
  35. props: ["my-price"],
  36. template: `
  37. <input type="text" :value="myPrice" @input="$emit('input-text',$event.target.value)" />
  38. `,
  39. },
  40. },
  41. methods: {
  42. handle(value) {
  43. this.price = value;
  44. },
  45. },
  46. });
  47. </script>

另一种简化方案:将$emit()提出去,写到methods里

  1. <div id="app">
  2. <p>父组件 单价:{{price}} 元</p>
  3. <p>
  4. <span>子组件</span
  5. ><my-input :my-price="price" @input-text="handle"></my-input>
  6. </p>
  7. </div>
  8. <script>
  9. const vm = new Vue({
  10. // 子组件向父组件传参是通过声明同名事件来实现
  11. el: document.querySelector("#app"),
  12. data() {
  13. return {
  14. price: 4588,
  15. };
  16. },
  17. //局部组件
  18. components: {
  19. myInput: {
  20. props: ["my-price"],
  21. template: `
  22. <input type="text" :value="myPrice" @input="foo" />
  23. `,
  24. methods:{
  25. foo(ev){
  26. this.$emit('input-text',ev.target.value)
  27. }
  28. }
  29. },
  30. },
  31. methods: {
  32. handle(value) {
  33. this.price = value;
  34. },
  35. },
  36. });
  37. </script>

插槽:组件内容分发

  1. <div id="app">
  2. <my-comp>world</my-comp>
  3. </div>
  4. <script>
  5. new Vue({
  6. el: "#app",
  7. components: {
  8. myComp: {
  9. template: `
  10. <div>
  11. <h2>Hello <slot>user</slot></h2>
  12. </div>
  13. `,
  14. },
  15. },
  16. });
  17. </script>

注意:
难点是子组件向父组件传参通过同名事件完成 $emit(同名事件名,参数),如果把此处提出写到methods中要使用this.$emit(),因为是要指定是子组件
在手册中提到在组件中使用v-model

自定义事件也可以用于创建支持 v-model 的自定义输入组件。

  1. <input v-model="searchText">

等价于:

  1. <input
  2. v-bind:value="searchText"
  3. v-on:input="searchText = $event.target.value"
  4. >

当用在组件上时,v-model 则会这样:

  1. <custom-input
  2. v-bind:value="searchText"
  3. v-on:input="searchText = $event"
  4. ></custom-input>

为了让它正常工作,这个组件内的 <input> 必须:

将其 value attribute 绑定到一个名叫 value 的 prop 上
在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出

写成代码之后是这样的:

  1. Vue.component('custom-input', {
  2. props: ['value'],
  3. template: `
  4. <input
  5. v-bind:value="value"
  6. v-on:input="$emit('input', $event.target.value)"
  7. >
  8. `
  9. })

现在 v-model 就应该可以在这个组件上完美地工作起来了:

  1. <custom-input v-model="searchText"></custom-input>

完整代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <title>组件使用v-model</title>
  8. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  9. </head>
  10. <body>
  11. <div id="app">
  12. <p>父组件:{{searchText}}</p>
  13. <custom-input v-model="searchText"></custom-input>
  14. </div>
  15. <script>
  16. new Vue({
  17. el: "#app",
  18. data() {
  19. return {
  20. searchText: "test",
  21. };
  22. },
  23. components: {
  24. "custom-input": {
  25. props: ["value"],
  26. template: `
  27. <input
  28. v-bind:value="value"
  29. v-on:input="$emit('input', $event.target.value)"
  30. >
  31. `,
  32. },
  33. },
  34. });
  35. </script>
  36. </body>
  37. </html>

感觉组件使用v-model不是很灵活的样子

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议