zoukankan      html  css  js  c++  java
  • VUE基础篇

    前奏:

    关于vue中,vscode实用的插件: vetur和vue vscode snippets

    Vue的设计思想

    Vue的设计思想有以下:

    • 数据驱动应用

    • MVVM模式的践行者

    MVVM框架的三要素:响应式、模板引擎及其渲染

    响应式:vue如何监听数据变化?

    模版:vue的模版如何编写和解析?

    渲染:vue如何将模板转换为html?

    安装

    引入vue

    vue官方安装地址

    使用

    官方地址

    每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的。

    创建第一个vue程序:显示hello world

    	// html中
    	<!-- 根节点(宿主容器) -->
      <div id="app">
      	<!-- Mustache语法不能作用在属性上,应该使用v-bind指令 -->
        <h2 v-bind:title='title'>  // 可以直接简写为:title='xxx'
          <!-- 插值文本: Mustache语法(双大括号)来进行数据绑定  -->
          {{title}} // 使用的是data中返回的数据对象中的title属性
        </h2>
      </div>
    	
    	
    	<script src="vueJs所在路径"></script>
    	<script>
        // 创建vue实例
       const vm = new Vue({
            el: '#app', // 提供一个在页面上已存在的DOM元素作为Vue实例的挂载目标。可以是CSS选择器,也可以是HTMLElement实例。(挂载到上面html中id为app的容器中)
            data() { // Vue实例的数据对象
              return {
                title: 'hello, vue!'
              }
            },
        });
        
        
        // 使用实例对象vm
        setTimeout(() => {
          vm.title = '看,我变了'
        }, 1000);
    	</script>
    

    Mustache表达式

    • 语法:

      {{ 表达式 }}   // 表达式:变量或能够输出唯一结果的。 注意:if else之类的不是
      
    • 作用: 把动态数据直接输出渲染到html中

    • 用法:

      	// html中
      	<!-- 根节点(宿主容器) -->
        <div id="app">
          <h2>
            <!-- 插值文本: Mustache语法(双大括号)来进行数据绑定  -->
            {{title}} // 使用的是data中返回的数据对象中的title属性
          </h2>
        </div>
        
        
        	<script src="vueJs所在路径"></script>
          <script>
            // 创建vue实例
           new Vue({
                el: '#app', // 提供一个在页面上已存在的DOM元素作为Vue实例的挂载目标。可以是CSS选择器,也可以是HTMLElement实例。(挂载到上面html中id为app的容器中)
                data() { // Vue实例的数据对象
                  return {
                    title: 'hello, vue!'
                  }
                },
            });
          </script>
      

    指令

    概念: 写在标签上的一种,以 “v-” 开头的自定义属性。一些指令能够接收一个“参数”,在指令名称之后以冒号表示。

    作用: 帮助我们操作html,动态的获取或者渲染数据。

    官方指令地址

    v- bind

    v-bind 指令可以用于响应式地更新HTMLattribute。

    HTML特性不能用Mustache 语法,应该使用v-bind指令。

    	// html中
      <div id="app">
      	<!-- Mustache语法不能作用在特性、属性值上,应该使用v-bind指令 -->
        <h2 v-bind:title='title'>  // 可以直接简写为:title='xxx'
          {{title}}
        </h2>
      </div>
    	
    	
    	<script src="vueJs所在路径"></script>
    	<script>
       new Vue({
            el: '#app', 
            data() {
              return {
                title: 'hello, vue!'
              }
            },
        });
    	</script>
    

    class与style绑定

    操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是属性,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将 v-bind用于 class 和 style 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。

    使用:

    <style> .active { background-color: red; } </style>
    
    
    <ul>
      <!-- class绑定 --> 
      <li v-for="item in goodList" 
        :class="{active: (selected === item)}" 
        @click="selected = item">{{item}}</li> 
        <!-- style绑定 --> 
        <!-- <li v-for="item in goodList" 
    					:style="{backgroundColor: (selected === item)?'#ddd':'transparent'}" 								@click="selectedCourse = item">{{item}}</li> --> 
    </ul>
    
    
    
    <script> 
    	new Vue({ 
    		data: { 
    		// 保存选中项 
    		selected: '', 
    		goodList: ['花生','瓜子','啤酒']
    	},
    }) </script>
    
    

    v-for

    作用: 用于循环渲染,我们可以用 v-for 指令循环一个数组或者对象。

    语法:

    <标签 v-for="(值,索引) in 要循环的源数据数组"></标签>
    
    <标签 v-for="(值,键名, 索引) in 要循环的源数据对象"></标签>
    

    用法:

    	// html中
      <div id="app">
        <ul>
        	<li v-for="item in goodList">{{item}}</li> 
        </ul>
      </div>
      
      
      	<script src="vueJs所在路径"></script>
        <script>
         new Vue({
              el: '#app', 
              data() {
                return {
                  goodList: ['花生','瓜子','啤酒']
                }
              },
          });
        </script>
    

    注意:在js中for...in循环,只能获得对象的键名(数组中就是索引),不能直接获取键值。但是在vue中使用v-for... inv-for... of去循环数组,第一个参数都是获取的数组元素。但是官方有一句话:你也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法。

    v-model

    v-model 本质上是语法糖。它将转换为输入事件以更新数据,并对一些极端场景进行一些特殊处理。也就是说可以让data中的数据,和表单的数据双向绑定。但是需要注意的是只能用于表单元素。

    		<!-- 表单元素输入绑定 --> 
    		<input v-model="good" type="text"/>
    		<span>{{good}}</span>
    
    		<script src="vueJs所在路径"></script>
        <script>
         new Vue({
              el: '#app', 
              data() {
                return {
                  goodList: ['花生','瓜子','啤酒'],
                  good: ''
                }
              },
          });
        </script>
    
    

    v-on

    官方事件处理

    我们可以使用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。

    语法:

    <标签 v-on:事件类型="表达式或函数名或函数名()"></标签>
    

    例如:

    <标签 v-on:click="count++"></标签>
    <标签 v-on:click="increase"></标签>
    <标签 v-on:click="increase(实参)"></标签>
    
    //  v-on可以简写为@
    <标签 @事件类型="表达式或函数名或函数名()"></标签>
    

    使用:

    		<!-- 表单元素输入绑定 --> 
    		<input v-model="good" type="text" v-on:keydown.enter="addGood"/>
    		<button @click="addGood">添加商品</button>
    
    		<script src="vueJs所在路径"></script>
        <script>
         new Vue({
              el: '#app', 
              data() {
                return {
                  goodList: ['花生','瓜子','啤酒'],
                  good: ''
                }
              },
              methods: { 
              	addGood() { 
              		this.goodList.push(this.good); 
              	} 
              },
          });
        </script>
    

    v-if

    v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。

    作用: 控制元素的显示渲染逻辑。

    概念: 逻辑和JS是一样的,从上往下,找到第一个满足条件的渲染,其他的不渲染。

    <div v-if="Math.random() > 0.5">
      Now you see me
    </div>
    <div v-else-if="Math.random() > 0.3"> // v-else-if 必须紧跟在带v-if或者v-else-if的元素之后。
      Now you can see me
    </div>
    <div v-else> // v-else元素必须紧跟在带v-if或者v-else-if的元素的后面,否则它将不会被识别。
      Now you don't
    </div>
    

    因为 v-if 是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个<template> 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。

    <template v-if="ok">
      <h1>Title</h1>
      <p>Paragraph 1</p>
      <p>Paragraph 2</p>
    </template>
    

    v-show

    另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:

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

    注意,v-show 不支持 <template> 元素。

    v-if vs v-show

    v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

    v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。(v-show 只是简单地切换元素的display,所以 v-show 的元素始终会被渲染并保留在 DOM 中)。

    一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

    v-if vs v-for

    v-ifv-for 一起使用时,v-for 具有比 v-if 更高的优先级。所以不推荐在同一元素上同时使用 v-ifv-for

    当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。

    当你只想为部分项渲染节点时,这种优先级的机制会十分有用。

    // 只渲染未完成的 todo。
    <li v-for="todo in todos" v-if="!todo.isComplete">
      {{ todo }}
    </li>
    

    如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素上 (如果没有父级,那么可以在外层加一个 template标签。同样地,template也不会渲染出来) 。

    <ul v-if="todos.length">
      <li v-for="todo in todos">
        {{ todo }}
      </li>
    </ul>
    
    

    计算属性和监听器

    模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,此时就可以考虑计算属性和监听器。

    计算属性computed

    概念:computed的写法和methods一样,也是一个函数,但是computed中的函数,必须有一个返回值。而且这个函数的名字,就可以拿到返回值。

    		<p> 
          <!-- 计算属性 -->
          商品总数:{{total}}
      	</p>
    
    		<script src="vueJs所在路径"></script>
        <script>
         new Vue({
              el: '#app', 
              data() {
                return {
                  goodList: ['花生','瓜子','啤酒'],
                }
              },
              computed: {
                  total() {// 和上面使用计算属性total,名字保存一致
                  		// 一般把比较复杂的逻辑计算写在这里 最终返回结果
                      return this.goodList.length + '种'
                  }
              }
          });
        </script>
    

    计算属性computed,有依赖缓存,只要所依赖的数据发生变化,才会重新计算,否则,直接使用缓存,页面不会重新渲染,性能之高,令人发指。

    监听器(侦听器)

    		<p> 
         <!-- 监听器 --> 
         课程总数:{{totalCount}}
      	</p>
    
    		<script src="vueJs所在路径"></script>
        <script>
         new Vue({
              el: '#app', 
              data() {
                return {
                  goodList: ['花生','瓜子','啤酒'],
                  totalCount: 0
                }
              },
              // 这种不能生效,因为初始化时不会触发 
              // watch: { 
              	// goodList(newValue, oldValue) { 
              		// this.totalCount = newValue.length + '种' 
              	// } 
              // },
             watch: { 
               goodList: { // 监控goodList,值发生变化时,才执行
                  immediate: true, // 立即执行一次
                  // deep: true,  // 深层次监听,例如数组对象
                  handler(newValue, oldValue) { 
                    this.totalCount = newValue.length + '种' 
                  } 
               } 
            },
          });
        </script>
    

    计算属性 vs 监听器

    监听器更通用,理论上计算属性能实现的监听器也能实现。

    处理数据的场景不同,监听器适合一个数据影响多个数据,计算属性适合一个数据受多个数

    据影响

    计算属性有缓存性,计算所得的值如果没有变化不会重复执行。

    监听器适合执行异步操作或较大开销操作的情况。

    官方地址

    组件

    官方地址

    定义:组件是可复用的 Vue 实例,准确讲它们是VueComponent的实例,继承自Vue。

    优点:组件化可以增加代码的复用性、可维护性和可测试性。

    使用场景:

    • 通用组件:实现最基本的功能,具有通用性、复用性,例如按钮组件、输入框组件、布局组件等。

    • 业务组件:它们完成具体业务,具有一定的复用性,例如登录组件、轮播图组件。

    • 页面组件:组织应用各部分独立内容,需要时在不同页面组件间切换,例如列表页、详情页组件

    如何使用组件:

    • 定义:Vue.component(),components选项,sfc
    • 分类:有状态组件,functional,abstract
    • 通信:props,$emit()/$on(),provide/inject,$children/$parent/$root/$attrs/$listeners
    • 内容分发:<slot><template>v-slot
    • 使用及优化:is,keep-alive,异步组件

    组件的本质:

    vue中的组件经历如下过程: 组件配置 => VueComponent实例 => render() => Virtual DOM=> DOM

    所以组件的本质是产生虚拟DOM。

    需要注意的是,组件名、prop和自定义事件时,尽量使用‘羊肉串’的格式。特别是自定义事件,v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的)。所以例如 v-on:myEvent 将会变成 v-on:myevent,所以如果你使用 myEvent 事件句柄就不可能被监听到。因此推荐使用羊肉串格式:@my-event事件名。

    组件组册

    之前的代码是这样的:

    		<div id="app">
            <ul>
              <li v-for="item in goodList" 
                :class="{active: (selected === item)}" 
                @click="selected = item">{{item}}</li> 
            </ul>
        </div>
    
        <script src="vue.js"></script>
        <script>
          new Vue({
              el: '#app', 
              data() {
                return {
                  selected: '', 
                  goodList: ['花生','瓜子','啤酒'],
                }
              },
          });
    
         </script>
    

    我们想把列表渲染,提出去成一个组件,那么我们怎么做呢。

     		<div id="app">
            <!-- 组件列表 -->  
          <!-- goodList是父组件的数据对象中的属性, goodlist是子组件中用于接收数据的字段,在props中去设置类型和默认值和接收 -->
     	  	<good-list :goodlist='goodList'></good-list>
        </div>
    
        <script src="vue.js"></script>
        <script>
         Vue.component('good-list',{
            data(){
              return {
                selected: '', // 这个属性是这个组件自己的,所以就从父组件中提到这个组件来维护
              }
            },
            props: {
              // 一定要注意,如果上面写:goodList='goodList',这儿用goodList接收不到,还是要使用goodlist
              // 因为 HTML 是大小写不敏感的,会被自动转换为全小写 
              'goodlist': { 
                type: Array,
                default: []
              }
            },
            template: `
              <ul>
                <!-- class绑定 --> 
                <li v-for="item in goodlist" 
                  :class="{active: (selected === item)}" 
                  @click="selected = item">{{item}}</li> 
              </ul>
            `
          });
         new Vue({
          el: '#app',
          data() {
            return {
              goodList: ['花生','瓜子','啤酒'],
            }
          },
        })
         </script>
    

    自定义事件及其监听

    新增时,原本的代码是:

    			<input v-model="good" type="text" v-on:keydown.enter="addGood"/>
          <button @click="addGood">添加商品</button>
    
          <ul>
            <li v-for="item in goodList">{{item}}</li> 
          </ul>
          
        <script src="vue.js"></script>
        <script>
          new Vue({
               el: '#app',
               data() {
                 return {
                  goodList: ['花生','瓜子','啤酒'],
                  good: ''
                 }
               },
               methods: { 
              	addGood() {
              		this.goodList.push(this.good); 
              	} 
              },
           });
    
         </script>
    
    

    把新增功能提出去成组件之后:

     		<div id="app">
           <good-add @good-add-handle='addGood'></good-add>
      
            <ul>
              <li v-for="item in goodList">{{item}}</li> 
            </ul>
        </div>
    
        <script src="vue.js"></script>
        <script>
          Vue.component('good-add',{
            data(){
              return {
                good: '', //  将good从父组件提取到自己去维护
              }
            },
            template: `
            <div>
              <input v-model="good" type="text" v-on:keydown.enter="add"/>
              <button @click="add">添加商品</button>
            </div>
            `,
            methods: {
              add(){
                // 发送自定义事件通知父组件新增商品(派发事件)
                // 注意事件名称定义时不要有大写字母出现
                this.$emit('good-add-handle', this.good);
              }
            }
          })
    
          new Vue({
              el: '#app',
              data() {
                return {
                  goodList: ['花生','瓜子','啤酒'],
                }
              },
              methods: {
                addGood(good) { // 接收子组件传递过来的参数 然后维护goodList
                  this.goodList.push(good); 
                } 
            },
          });
         </script>
    

    组件实现v-model

    v-model默认转换是:value和@input,如果想要修改这个行为,可以通过定义model选项。

    	Vue.component('good-add',{
            model: {
              prop: 'value',
              event: 'change'
            },
      })
    

    代码如下:

    <div id="app">
          <!-- 子组件v-model的是父组件中的good状态 -->
          <!-- 组件支持v-model需要实现内部input的:value和@input -->
           <good-add v-model='good' @good-add-handle='addGood'></good-add>
      
            <ul>
              <li v-for="item in goodList">{{item}}</li> 
            </ul>
        </div>
    
        <script src="vue.js"></script>
        <script>
          Vue.component('good-add',{
            model: {
              prop: 'value',
              event: 'change'
            },
            // 接收父组件传递value,不需要额外维护good了
            props: {
              value: String,
              default: ''
            },
            template: `
            <div>
              <input 
                @change="$emit('change', $event.target.value)"
                 :value=value  
                 type="text" 
                 @keydown.enter='add'
              />
              <button @click="add">添加商品</button>
            </div>
            `,
            methods: {
              add(){
                // 派发事件不再需要传递数据
                this.$emit('good-add-handle');
              }
            }
          })
    
          new Vue({
              el: '#app',
              data() {
                return {
                  goodList: ['花生','瓜子','啤酒'],
                  good: '' // good是父组件来维护
                }
              },
              methods: {
                addGood() {
                  this.goodList.push(this.good); // 拿自己的good,因为子组件绑定的就是自己的这个属性
                } 
            },
          });
         </script>
    

    slot

    官方地址

    通过插槽分发内容,也就是说子组件预留位置,让父组件在使用子组件的时候,可以把数据插入进去。

    通过使用vue提供的 slot 可以给组件传递内容.

    		<div id="app">
          <!-- 只是传递true测试时,需要加上v-bind代表表达式,否则会当成字符串 -->
          <!-- <message :show="true">新增成功!!</message> -->
    
          <!-- 
            方式1: 声明一个closeMessage方法去修改状态
            <message @close='closeMessage' :show="isShowMessgae">新增成功!!</message> 
          -->
          <!-- 方式2: 直接修改isShowMessgae状态 -->
          <message @close='isShowMessgae=$event' :show="isShowMessgae">新增成功!!</message> 
    
          <good-add v-model='good' @good-add-handle='addGood'></good-add>
          <good-list :goodlist='goodList'></good-list>
        </div>
    
        <script src="vue.js"></script>
        <script>
          // 弹窗
          Vue.component('message',{
            props: {
              show: {
                type: Boolean,
                default: false
              }
            },
            template: `
              <div class='message-box' v-if='show'>
                <!--通过slot获取传入内容-->
                <!--slot作为占位符(占坑位)-->
                <slot></slot>
                <span class='message-box-close' @click='$emit("close",false)'>X</span>
              </div>
            `
          })
          // 新增商品
          Vue.component('good-add',{
            model: {
              prop: 'value',
              event: 'change'
            },
            // 接收父组件传递value,不需要额外维护good了
            props: {
              value: String,
              default: ''
            },
            template: `
              <div>
                <input
                  @change="$emit('change', $event.target.value)"
                  :value=value
                  type="text"
                  @keydown.enter='add'
                  />
                <button @click="add">添加商品</button>
              </div>
            `,
            methods: {
              add(){
                // 派发事件不再需要传递数据
                this.$emit('good-add-handle');
              }
            }
          })
          //商品列表
          Vue.component('good-list',{
            data(){
              return {
                selected: '', // 这个属性是这个组件自己的,所以就从父组件中提到这个组件来维护
              }
            },
            props: {
              // 一定要注意,如果上面写:goodList='goodList',这儿用goodList接收不到,还是要使用goodlist
              // 因为 HTML 是大小写不敏感的,会被自动转换为全小写 
              'goodlist': { 
                type: Array,
                default: []
              }
            },
            template: `
              <ul>
                <!-- class绑定 --> 
                <li v-for="item in goodlist" 
                  :class="{active: (selected === item)}" 
                  @click="selected = item">{{item}}</li> 
              </ul>
            `
          });
          
          new Vue({
              el: '#app',
              data() {
                return {
                  goodList: ['花生','瓜子','啤酒'],
                  good: '',
                  isShowMessgae: false, // 控制弹窗是否显示
                }
              },
              methods: {
                addGood() {
                  this.goodList.push(this.good); 
                  this.isShowMessgae = true; // 新增成功时,打开弹窗
                },
                closeMessage($evnet){
                  this.isShowMessgae = $evnet; // 点击弹窗组件中的X时,父组件修改状态为false
                }
            },
          });
        </script>
    

    .sync 修饰符

    .sync官方地址

    双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件都没有明显的变更来源。因此官方推荐使用update:xxxx的模式触发事件取而代之。

    因此,上面的代码修改两处:

    <message @update:show='isShowMessgae=$event' :show="isShowMessgae">新增成功!!</message> 
    
    
    // js 弹框组件message -> template中(弹框的X)
    <span class='message-box-close' @click='$emit("update:show",false)'>X</span>
    

    官方为了方便起见,为这种模式提供一个缩写,即 .sync 修饰符。

    在上面的基础上,修改html中message组件:

    <message :show.sync="isShowMessgae">新增成功!!</message> 
    

    注意: js 弹框组件message -> template中,依然且必须是@click='$emit("update:show",false)

    <span class='message-box-close' @click='$emit("update:show",false)'>X</span>
    

    具名slot

    如果存在多个独立内容要分发,可以使用具名插槽v-slot:name(可以简写为#name)

    // html中 修改组件message
    <message :show.sync="isShowMessgae">
            <template v-slot:title>
              <strong>
                恭喜你,
              </strong>
            </template>
            <!-- 当不写v-slot时,其实默认v-slot值为default <template v-slot:default>新增成功!!</template> -->
            <template>新增成功!!</template>
          </message> 
          
          
     // js中 message组件 ->  template
     	template: `
              <div class='message-box' v-if='show'>
                <!--通过slot获取传入内容-->
                
                <!--具名插槽-->
                <slot name="title"></slot>
                <!--slot作为占位符(占坑位)-->
                <slot></slot>
                <span class='message-box-close' @click='$emit("update:show",false)'>X</span>
              </div>
            `
    

    插槽总结

    匿名插槽:

    // 父组件 【使用插槽的时候 父组件必须是双标签】
    <子组件标签>我是要插入插槽的内容</子组件标签>
    
    // 子组件
    <template>
        <div>
           <h1>我是子组件</h1>
           <div class="content">
               <slot><slot />  // 插槽: 预留位置 将来父组件把内容插入此位置。
           </div>
        </div>
    </template>
    

    具名插槽:

    // 父组件
    <Son>
      <p slot="header">我是头部内容 aaa</p>
      <p slot="main">我是主体内容 bbb</p>
      <p slot="footer">我是尾部内容 ccc</p>
    </Son>
    
    // 子组件
      <!-- 具名插槽 -->
      <div class="header">
        <slot name="header" />
      </div>
      <div class="main">
        <slot name="main" />
      </div>
      <div class="footer">
        <slot name="footer" />
      </div>
    

    作用域插槽:

    // 父组件
    <p slot="box" slot-scope="scope">
        我是外部数据:
        <br />
        {{ scope.msg }}
        {{ scope.news }}
    </p>
    
    // 子组件
    <div class="box">
       <slot name="box" :msg="sonMsg" :news="news" />
    </div>
    
    export default {
      data() {
        return {
          sonMsg: "我是son数据",
          news: "我是新闻",
        };
      },
    };
    

    v-slot:

    // 父组件
     <!-- 指令v-slot -->
    <template #header>
        <p>header 哈哈哈</p>
    </template>
    
    <template #footer="scope">
        <p>footer 嘻嘻嘻 : {{ scope.news }}</p>
    </template>
    
    // 子组件
    <!-- v-slot -->
    <div class="header">
        <slot name="header" />
    </div>
    
    <div class="footer">
        <slot name="footer" :news="news" />
    </div>
    

    组件通信总结

    概念: 组件通信就是组件之间相互传递数据

    常见的方式:

    • 父传子

    • 子传父

    • bus

    • vuex状态机

    父传子

    // 父组件
    <子组件标签 属性名1="属性值" :属性名2="动态数据"></子组件标签>
    
    // 子组件
    export default {
        props: ['属性名1', '属性名2']  // 数组格式
        
        // 封装组件  使用对象写法 更严谨
        props: {
            属性名1: {
            		type: String, // 限制类型  Number Array Object Boolean
                default: 'a', // 默认值
                required: true, // 必填
            },
            属性名2: {
                
            }
        }
    }
    

    子传父

    // 父组件
    <子组件标签 @自定义事件="函数名"></子组件标签>
    export default {
        methods: {
            函数名(data) {
                // data就是子组件传递过来的数据
            }
        }
    }
    
    // 子组件
    export default {
        methods: {
            toFa() {
                // 把数据传递给父组件
                this.$emit('自定义事件', 数据)
            }
        }
    }
    

    bus

    // 1. 在main.js入口文件中, 创建一个Vue的空的实例对象,挂载在Vue的原型上
    Vue.prototype.$bus = new Vue()
    
    // 2. 在 A 组件把数据传递出去
    export default { // new Vue({})
       methods: {
           sendMsg() {
               this.$bus.$emit('自定义事件', 数据)
           }
       } 
    }
    // 3. 在 B 组件 接收数据
    export default {
        created() {
            this.$bus.$on('自定义事件', (data) => {
                // data就是接收到的数据
            })
        }
    }
    

    必会API

    数据相关API

    Vue.set

    向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。

    使用方法: Vue.set(target, propertyName/index, value)

    官方地址

    在之前例子上改造,增加一个批量修改价格功能:

    		<div id="app">
           <good-add @good-add-handle='addGood'></good-add>
      
           <!-- 批量修改价格功能 -->
           <div>
             <!-- 加上.number修饰符,得到的值就是number类型,否则默认得到字符串类型 -->
              <input v-model.number="price"> 
              <button @click="batchUpdatePrice">批量更新价格</button>
           </div>
    
            <ul>
              <li v-for="item in goodList">{{item.name}} - ¥{{item.price}}</li> 
            </ul>
        </div>
    
        <script src="vue.js"></script>
        <script>
          Vue.component('good-add',{
            data(){
              return {
                good: '', //  将good从父组件提取到自己去维护
              }
            },
            template: `
            <div>
              <input v-model="good" type="text" v-on:keydown.enter="add"/>
              <button @click="add">添加商品</button>
            </div>
            `,
            methods: {
              add(){
                // 发送自定义事件通知父组件新增商品(派发事件)
                // 注意事件名称定义时不要有大写字母出现
                this.$emit('good-add-handle', this.good);
              }
            }
          })
    
          new Vue({
              el: '#app',
              data() {
                return {
                  goodList: [{name: '花生'},{name: '瓜子'},{name: '啤酒'}],
                  price: 0
                }
              },
              methods: {
                addGood(good) { // 接收子组件传递过来的参数 然后维护goodList
                   // this指向的是实例对象
                  // 需要注意 数组需要变异方法,才能被检测更新
              		this.goodList.push({name: good}); 
    
                  // Vue 不能检测以下数组的变动: 1. 当你利用索引直接设置一个数组项时  2.当你修改数组的长度时
                  // this.goodList[this.goodList.length] = this.good; // 不会更新视图
                  // console.log(this.goodList); // 数据是变化了的
                },
                // 添加批量更新价格方法
                batchUpdatePrice(){
                  this.goodList.forEach(item => {
                    // item.price = this.price; // 不能更新视图,但是数据是修改了的
                    // 方式1
                    // Vue.set(item, 'price', this.price); // 全局方法 效果都是一样的
                    // 方式2
                    this.$set(item, 'price', this.price); // 推荐使用
                  });
                }
            },
          });
         </script>
    

    Vue.delete

    删除对象的属性。如果对象是响应式的,确保删除能触发更新视图。

    使用方法: Vue.delete(target, propertyName/index)

    Vue.nextTick

    官方地址

    事件相关API

    vm.$on

    监听当前实例上的自定义事件。事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的

    额外参数。

    vm.$on('事件名', function (payload) { console.log(payload) })
    // 我们常在template中,以v-on(简写为@)指令去监听事件
    

    vm.$emit

    触发当前实例上的事件。附加参数都会传给监听器回调。

    vm.$emit('事件名', 参数)
    

    事件总线bus

    通过在Vue原型上添加一个Vue实例作为事件总线,实现组件间相互通信,而且不受组件间关系的影响(无论层级有多深,不再局限于父子组件之间通信)。

    Vue.prototype.$bus = new Vue();
    // 我们可以在任意组件中使用this.$bus访问到该Vue实例
    

    在具名slot上修改:

    1. style上把弹框的样式,分为成功和失败

    2. 新增一个message组件,添加class为error(之前那个添加为success),把里面文本内容修改成错误提示

    3. 添加事件总线,在第一行添加Vue.prototype.$bus = new Vue();

    4. 在弹窗组件message中,监听关闭事件

               mounted () { 
                this.$bus.$on('close-message', () => { 
                  this.$emit('update:show', false) 
                }); 
              },
      
    5. 新增商品good-add组件中,新增一个按钮,监听点击事件。

      				removeAllMessage(){
                  this.$bus.$emit('close-message'); // 名字和上面,$on中事件句柄名close-message一致
                }
      
     		 <style>
          .active {
          background-color: red;
        }
        .message-box {
          padding: 10px 20px;
          
        }
        .success{
          background: #4fc08d;
          border: 1px solid #42b983;
        }
        .error{
          background: red;
          border: 1px solid red;
        }
        .message-box-close {
          float: right;
        }
        </style>
        
        
     		
     		<div id="app">
          <!-- 错误时的消息提示 -->
          <message :show.sync="isShowMessgaeError" class="error">
            <template #title>
              <strong>
                输入有误!
              </strong>
            </template>
            <template>内容不能为空</template>
          </message> 
          <!-- 成功的消息提示 -->
          <message :show.sync="isShowMessgae" class="success">
            <!-- v-slot:可以简写为# -->
            <template #title>
              <strong>
                恭喜你,
              </strong>
            </template>
            <template>新增成功!!</template>
          </message> 
    
          <good-add v-model='good' @good-add-handle='addGood'></good-add>
          <good-list :goodlist='goodList'></good-list>
        </div>
    
        <script src="vue.js"></script>
        <script>
          // 放到第一行
          Vue.prototype.$bus = new Vue();
          // 弹窗
          Vue.component('message',{
            props: {
              show: {
                type: Boolean,
                default: false
              }
            },
            template: `
              <div class='message-box' v-if='show'>
                <!--通过slot获取传入内容-->
                
                <!--具名插槽-->
                <slot name="title"></slot>
                <!--slot作为占位符(占坑位)-->
                <slot></slot>
                <span class='message-box-close' @click='$emit("update:show",false)'>X</span>
              </div>
            `,
             // 监听关闭事件
             mounted () { 
              this.$bus.$on('close-message', () => { 
                this.$emit('update:show', false) 
              }); 
            },
          })
          // 新增商品
          Vue.component('good-add',{
            model: {
              prop: 'value',
              event: 'change'
            },
            props: {
              value: String,
              default: ''
            },
            template: `
              <div>
                <input
                  @change="$emit('change', $event.target.value)"
                  :value=value
                  type="text"
                  @keydown.enter='add'
                  />
                <button @click="add">添加商品</button>
                <button @click="removeAllMessage">清空提示框</button>
              </div>
            `,
            methods: {
              add(){
                this.$emit('good-add-handle');
              },
              removeAllMessage(){
                this.$bus.$emit('close-message');
              }
            },
           
          })
          //商品列表
          Vue.component('good-list',{
            data(){
              return {
                selected: '',
              }
            },
            props: {
              'goodlist': { 
                type: Array,
                default: []
              }
            },
            template: `
              <ul>
                <!-- class绑定 --> 
                <li v-for="item in goodlist" 
                  :class="{active: (selected === item)}" 
                  @click="selected = item">{{item}}</li> 
              </ul>
            `
          });
          
          new Vue({
              el: '#app',
              data() {
                return {
                  goodList: ['花生','瓜子','啤酒'],
                  good: '',
                  isShowMessgae: false,
                  isShowMessgaeError: false,
                }
              },
              methods: {
                addGood() {
                  if(this.good){ // 如果输入有内容
                    this.goodList.push(this.good); 
                    this.isShowMessgae = true;
                  } else {
                    this.isShowMessgaeError = true;
                  }
                  
                },
                closeMessage($evnet){
                  this.isShowMessgae = $evnet;
                }
            },
          });
        </script>
    

    vm.$once

    监听一个自定义事件,但是只触发一次。一旦触发之后,监听器就会被移除。

    vm.$once('事件名', function (payload) { console.log(payload) })
    

    vm.$off

    移除自定义事件监听器。

    • 如果没有提供参数,则移除所有的事件监听器;

    • 如果只提供了事件,则移除该事件所有的监听器;

    • 如果同时提供了事件与回调,则只移除这个回调的监听器。

    vm.$off() // 移除所有的事件监听器 
    vm.$off('事件名') // 移除该事件所有的监听器 
    vm.$off('事件名', callback) // 只移除这个回调的监听器
    

    元素引用

    ref和vm.$refs

    ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向组件实例。

    例如:

    		<div id="app">
           <!-- 错误时的消息提示 -->
           <message ref='error-message' class="error">
              <template #title>
                <strong>
                  输入有误!
                </strong>
              </template>
              <template>内容不能为空</template>
            </message> 
            <!-- 成功的消息提示 -->
            <message ref='success-message' class="success">
              <!-- v-slot:可以简写为# -->
              <template #title>
                <strong>
                  恭喜你,
                </strong>
              </template>
              <template>新增成功!!</template>
            </message> 
    
           <good-add @good-add-handle='addGood'></good-add>
      
            <ul>
              <li v-for="item in goodList">{{item}}</li> 
            </ul>
        </div>
    
        <script src="vue.js"></script>
        <script>
           // 弹窗
           Vue.component('message',{
            data() {
              return {
                show: false
              }
            },
            template: `
              <div class='message-box' v-if='show'>
                <!--通过slot获取传入内容-->
                
                <!--具名插槽-->
                <slot name="title"></slot>
                <!--slot作为占位符(占坑位)-->
                <slot></slot>
                <span class='message-box-close' @click='changeShow'>X</span>
              </div>
            `,
            methods: {
              changeShow() {
                this.show = !this.show;
              }
            },
          })
    
          Vue.component('good-add',{
            data(){
              return {
                good: '',
              }
            },
            template: `
            <div>
              <input ref='addInput' v-model="good" type="text" v-on:keydown.enter="add"/>
              <button @click="add">添加商品</button>
            </div>
            `,
            mounted () {
              // mounted及之后才能访问到ref
              // 在普通的dom元素上使用,指向dom元素
              this.$refs.addInput.focus();
            },
            methods: {
              add(){
                // 发送自定义事件通知父组件新增商品(派发事件)
                // 注意事件名称定义时不要有大写字母出现
                this.$emit('good-add-handle', this.good);
              }
            }
          })
    
          new Vue({
              el: '#app',
              data() {
                return {
                  goodList: ['花生','瓜子','啤酒'],
                }
              },
              methods: {
                addGood(good) {
                  if(good){
                    this.goodList.push(good); 
                    // 在子组件上使用,指向组件实例
                    this.$refs['success-message'].changeShow();
                  } else {
                    this.$refs['error-message'].changeShow();
                  }
                } 
            },
          });
         </script>
    

    注意:

    1. ref 是作为渲染结果被创建的,在初始渲染时不能访问它们

    2. $refs 不是响应式的,不要试图用它在模板中做数据绑定

    3. 当 v-for 用于元素或组件时添加ref,引用信息将是包含 DOM 节点或组件实例的数组。

    进阶知识

    过滤器filters

    作用: 过滤处理数据的格式,可被用于一些常见的文本格式化。

    使用场景:过滤器可以用在两个地方:双花括号插值和v-bind表达式,注意过滤器要被添加在表达式的尾部,由“管道”符号|表示。

    语法:

    		<!-- 在双花括号中 -->
    		<div>{{ msg | 函数名 }}</div>
    		
    		<!-- 在 `v-bind` 中 --> 
    		<div v-bind:id="msg | 函数名"></div>
    
    
     	// 过滤器
        filters: {
            函数名(msg) {
                return 过滤结果
            }
        }
    

    例如:

    <div id="app">
          <ul>
            <li v-for='item of goodList'>
            	<!-- 不使用过滤器时这么写,但是货币符号是固定的 -->
              <!-- {{item.name}} -  ${{item.price}} -->
              {{item.name}} -  {{item.price | symbol}}
            </li>
          </ul>
        </div>
    
        <script src="vue.js"></script>
        <script>
        
        new Vue({
          el: '#app',
          data() {
            return {
              goodList: [{name: '花生', price: 10},{name: '瓜子', price: 40},{name: '啤酒', price: 90}],
            }
          },
          filters: {
            symbol: function(value) {
              return '$' + value;
            }
          }
        });
    

    把上面的例子稍微改造一下,符号可以动态传递而不是写死的。

    		<div id="app">
          <ul>
            <li v-for='item of goodList'>
              <!-- 和方法一样调用,传递参数 -->
              {{item.name}} - {{item.price | symbol('¥')}}
            </li>
          </ul>
        </div>
    
        <script src="vue.js"></script>
        <script>
        
        new Vue({
          el: '#app',
          data() {
            return {
              goodList: [{name: '花生', price: 10},{name: '瓜子', price: 40},{name: '啤酒', price: 90}],
            }
          },
          filters: {
            symbol: function(value, sym = '$') { // 第一个参数,理解为上面的item.price  第二个参数,就是('¥')中传递过来的符号,  为了容错处理 默认值给个$
              return sym + value;
            }
          }
        });
        </script>
    

    自定义指令

    除了核心功能默认内置的指令 ,Vue 允许注册自定义指令。有的情况下,仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。

    官方地址

    例如官方输入框自动获取焦点例子:

    		<div id="app">
          <input type="text" v-focus>
        </div>
    
        <script src="vue.js"></script>
        <script>
          // 注册一个全局自定义指令 `v-focus`
          Vue.directive('focus', {
            // 当被绑定的元素插入到 DOM 中时……
            // binding很重要,详细信息参看官网
            inserted: function (el, binding) {
              // 聚焦元素
              el.focus()
            }
          });
    
          new Vue({
            el: '#app',
          });
        </script>
    

    然后我们再来自定义做一个,根据当前登陆用户级别,来做权限设置。

    		<div id="app">
          <input type="text" v-focus>
    
          <!-- 特别需要注意: 指令里,""中是表达式,如果需要传递字符串,则需要加上字符串 -->
          <button v-permission="'superAdmin'">删除</button>
    
        </div>
    
        <script src="vue.js"></script>
        <script>
          // 假设当前登陆用户是会员
          const user = 'member';
    
          // 注册一个全局自定义指令 `v-focus`
          Vue.directive('focus', {
            // 当被绑定的元素插入到 DOM 中时……
            // binding很重要,详细信息参看官网
            inserted: function (el, binding) {
              // 聚焦元素
              el.focus()
            }
          });
          
          // 第一个参数: 指令名,注意使用时要加上v-
          // 第二个参数: 配置项
          Vue.directive('permission', {
            inserted: function (el, binding) {
              console.log(binding);
               // 若指定用户角色和当前用户角色不匹配则删除当前指令绑定的元素
              if (user !== binding.value) {
                el.parentElement.removeChild(el)
              }
            }
          });
    
          new Vue({
            el: '#app',
          });
        </script>
    

    渲染函数

    官方地址

    Vue 推荐在绝大多数情况下使用模板来创建HTML。然而在一些场景中,真的需要 JavaScript 的完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器。

    基础:

    render: function (createElement) { 
    	// createElement函数返回结果是VNode(虚拟DOM) 
    	return createElement( // 接收三个参数
        tagname, // 标签名称 
        data, // 传递数据 
        children // 子节点数组
    	) 
    }
    

    基于官网的例子:

    		<div id="app">
          <!-- 用render实现一个组件 : 实现标题 -->
          <!-- level是指需要生成h1-6哪一个标签 -->
          <my-head :level='1' :title='title'>{{title}}</my-head>
          <my-head :level='3' :title='title'>我是另一个我</my-head>
    
          <!-- <h2 :title='title'>
            {{title}}
          </h2> -->
        </div>
    
        <script src="vue.js"></script>
        <script>
          Vue.component('my-head',{
            props: ['level', 'title'],        
            // render函数接收一个 createElement参数,我们一般简写为h    h === createElement
            // 因为Vdom底层的算法是snabbdom算法,这个算法里面生成虚拟dom的方法名就叫h
            render(h){ 
              // 注意这儿一定要有return, return出createElement返回的Vnode。
             return h(
                'h'+this.level, // 参数1:标签名字
                {attr:  { title: this.title }},// 参数2
                this.$slots.default, // 参数3: 子节点数组(虚拟节点)   标签之间的内容,需要使用默认插槽来获取
              )
            }
          });
    
          new Vue({
              el: '#app', 
              data() { 
                return {
                  title: 'hello, vue!'
                }
              },
          });
        </script>
    

    然后我们再来进阶来试一试:

    当用户使用组件时,

    <my-head :level='1' :title='title' icon='Food'>{{title}}</my-head>
    

    我们希望渲染成:

    			<!-- 阿里矢量图使用方式 -->
    			<h1 :title='title'>
              <svg class="icon"><use xlink:href="#icon-iconfinder_Food_C_"></use></svg>
            {{title}}
          </h1>
    

    最终代码为:

    		<div id="app">
          <my-head :level='1' :title='title' icon='Food'>{{title}}</my-head>
    
          <!-- <h3 :title='title'>
              <svg class="icon"><use xlink:href="#icon-iconfinder_Food_C_"></use></svg>
            {{title}}
          </h3> -->
        </div>
    
        <script src="./iconfont.js"></script>
        <script src="vue.js"></script>
        <script>
          Vue.component('my-head',{
            props: ['level', 'title', 'icon'],        
            render(h){ 
              let children = [];           
              // 思路: 第一步,把用户传入的icon,生成<svg class="icon"><use xlink:href="#icon-icon名"></use></svg>添加到children数组中
              // 第二步: 把默认插槽内容this.$slots.default放到children数组中
              // 第三步:h函数参数3就替换为children数组
              // 第一步: 生成svg,添加图标   同样是调用h函数生成
              const svgVnode = h(
                'svg',
                { class: 'icon' }, // 添加固定类名为icon  详见官网createElement参数2
                [h('use',{attrs: {"xlink:href": `#icon-iconfinder_${this.icon}_C_`}})] // 参数3: 子节点数组(虚拟节点),svg还有个子级use,所以再调用h方法生成use,需要注意的是需要是数组,所以将返回的vnode放到一个数组中
              );
              children = [svgVnode, ...this.$slots.default];
    
             return h(
                'h'+this.level, // 参数1:标签名字
                {attrs:  { title: this.title }},// 参数2
                children, // 参数3: 子节点数组(虚拟节点)   标签之间的内容,需要使用默认插槽来获取
              )
            }
          });
    
          new Vue({
              el: '#app', 
              data() { 
                return {
                  title: 'hello, vue!'
                }
              },
          });
        </script>
    

    模板语法是如何实现的

    在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。

    之前的例子中原本代码如下:

     <!-- 宿主容器(根节点) -->
      <div id="app">
        <ul>
          <!-- class绑定 --> 
          <li v-for="item in goodList" 
            :class="{active: (selected === item)}" 
            @click="selected = item">{{item}}</li> 
            <!-- style绑定 --> 
            <!-- <li v-for="item in goodList" 
                  :style="{backgroundColor: (selected === item)?'#ddd':'transparent'}" 								@click="selectedCourse = item">{{item}}</li> --> 
        </ul>
      </div>
      
      
      <script src="vueJs所在路径"></script>
        <script>
         const vm = new Vue({
              el: '#app', 
              data() {
                return {
                  goodList: ['花生','瓜子','啤酒'],
                  selected: ''
                }
              },
          });
        </script>
    

    然后我们去输出vue替我们生成的渲染函数 :

    执行代码: console.log(vm.$options.render)

    我们看到输出信息:

    (function anonymous(
    ) {
    with(this){return _c('div',{attrs:{"id":"app"}},[_c('ul',_l((goodList),function(item){return _c('li',{class:{active: (selected === item)},on:{"click":function($event){selected = item}}},[_v(_s(item))])}),0)])}
    })
    

    然后我们基于这一个点,改写为渲染函数版本。

     <!-- 宿主容器(根节点) -->
      <div id="app"></div>
      
      
      // 创建vue实例
      new Vue({
          el: '#app',
          data() {
            return {
              goodList: ['花生','瓜子','啤酒'],
              selected: ''
            }
          },
          methods: {},
          render() {
            with(this){
              return _c('div',{attrs:{"id":"app"}},[_c('ul',_l((goodList),function(item){return _c('li',{class:{active: (selected === item)},on:{"click":function($event){selected = item}}},[_v(_s(item))])}),0)])}
          }
        })
    

    我们可以看到,结果是一样的。

    结论:Vue通过它的编译器将模板编译成渲染函数,在数据发生变化的时候再次执行渲染函数,通过对比两次执行结果得出要做的dom操作,模板中的神奇魔法得以实现。

    函数式组件

    官方地址

    没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法。实际上,它只是一个接受一些 prop 的函数。在这样的场景下,我们可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。

    修改上一个例子为函数式组件:

    		<div id="app">
          <my-head :level='1' :title='title' icon='Food'>{{title}}</my-head>
        </div>
    
        <script src="./iconfont.js"></script>
        <script src="vue.js"></script>
        <script>
          Vue.component('my-head',{
            functional: true,  // 1. functional设置为true,标示是函数式组件
            props: ['level', 'title', 'icon'],   
            // 在函数式组件中,没有this
            // 所以render函数,提供第二个参数作为上下文             
            render(h, context){ 
              // 之前从this上拿取'level', 'title', 'icon',就要变化了
              // 2. 从context.props上去拿取
              const { level, title, icon } = context.props;
              let children = [];           
              const svgVnode = h(
                'svg',
                { class: 'icon' },
                [h('use',{attrs: {"xlink:href": `#icon-iconfinder_${icon}_C_`}})] 
              );
              // 3. 子元素获取: 增加context参数,并将this.$slots.default更新为context.children,然后将this.level更新为context.props.level。
              children = [svgVnode, ...context.children];
    
             return h(
                'h'+level, 
                {attrs:  { title: title }},
                children,
              )
            }
          });
    
          new Vue({
              el: '#app', 
              data() { 
                return {
                  title: 'hello, vue!'
                }
              },
          });
        </script>
    

    混入

    官方地址

    混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

    // 定义一个混入对象 
    let myMixin = { 
    	created: function () { 
    		this.hello() 
    	},
    	methods: { 
    		hello: function () { 
    			console.log('hello from mixin!') 
    		} 
    	} 
    }
    // 定义一个使用混入对象的组件 
    Vue.component('mycomponent', { mixins: [myMixin] })
    

    插件

    官方地址

    Vue.js 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一

    个可选的选项对象.

    const MyPlugin = { 
    	install (Vue, options) { 
    		Vue.component('my-head', {...}) 
    	} 
    }
    if (typeof window !== 'undefined' && window.Vue) { 
    	window.Vue.use(MyPlugin) 
    }
    

    例如把上面的标题组件,封装成插件。

    首先新建个js文件,存放插件代码:

    const MyPlugin = { 
      // 插件需要install方法
    	install (Vue, options) { 
    		Vue.component('my-head',{
          functional: true,  // 1. functional设置为true,标示是函数式组件
          props: ['level', 'title', 'icon'],   
          // 在函数式组件中,没有this
          // 所以render函数,提供第二个参数作为上下文             
          render(h, context){ 
            // 之前从this上拿取'level', 'title', 'icon',就要变化了
            // 2. 从context.props上去拿取
            const { level, title, icon } = context.props;
            let children = [];           
            const svgVnode = h(
              'svg',
              { class: 'icon' },
              [h('use',{attrs: {"xlink:href": `#icon-iconfinder_${icon}_C_`}})] 
            );
            // 3. 子元素获取: 增加context参数,并将this.$slots.default更新为context.children,然后将this.level更新为context.props.level。
            children = [svgVnode, ...context.children];
    
           return h(
              'h'+level, 
              {attrs:  { title: title }},
              children,
            )
          }
        });
    	} 
    }
    // 判断当前环境  并且判断是否已经存在Vue
    if (typeof window !== 'undefined' && window.Vue) { 
    	window.Vue.use(MyPlugin);
    }
    

    然后在页面上,直接使用插件即可。

     		<div id="app">
          <my-head :level='1' :title='title' icon='Food'>{{title}}</my-head>
        </div>
    
        <script src="./iconfont.js"></script>
        <script src="vue.js"></script>
        <script src="./plugins/head.js"></script>
        <script>   
          new Vue({
              el: '#app', 
              data() { 
                return {
                  title: 'hello, vue!'
                }
              },
          });
        </script>
    

    代码github地址

  • 相关阅读:
    解决“Caused by: org.gradle.api.plugins.UnknownPluginException: Plugin with id 'org.springframework.boot' not found.”
    linux随机生成密码
    NFS网络共享文件系统
    shell实现带颜色输出的进度条
    【AtCoder】 ARC 101
    【AtCoder】 ARC 102
    【AtCoder】 ARC 103
    20190814校内模拟赛
    「2019-8-13提高模拟赛」树 (tree)
    [PA2014]Fiolki
  • 原文地址:https://www.cnblogs.com/zz-zrr/p/14438068.html
Copyright © 2011-2022 走看看