Vue3选项式-基础部分篇

news2024/10/7 12:27:48

Vue3选项式风格-基础部分篇

  • 简介
  • 模板语法
    • 文本插值
    • 原始HTML
    • Attribute 绑定
    • 使用 JavaScript 表达式
    • 调用函数
    • 全局组件调用
    • 内置指令
    • 动态参数注意事项
  • data()
    • data()深度响应
  • methods
    • 有状态的methods(防抖)
  • DOM更新时机
  • 计算属性
  • class和style绑定
  • 条件渲染
  • 列表渲染
  • 数组变换侦听
  • 事件处理
  • 表单输入绑定
  • 生命周期钩子
  • 侦听器watch
  • 模板引用ref
  • 组件基础
    • 组件注册
    • 项目文件结构
      • RootApp.vue
      • main.js
      • ChildApp.vue
      • ChildApp1.vue
      • ChildApp2.vue
      • 其他注意事项

简介

本篇知识点主要来自Vue官网,是对基础部分知识的学习总结,适用于对vue稍微了解的人群,可以更加规范的使用Vue相关语法,本次代码是脚手架搭建后修改根组件后的代码,具体内容看最后的截图,最后会附上修改和编辑后文件的全部代码。

模板语法

我们先对一个SFC(单文件组件)的组成部分做一个了解。

<template>
	<!-- 组件模板内容 -->
</template>
<script>
	export default(){
		//当前组件实例
	}
</script>
<style>
	组件模板样式
</style>

文本插值

<template>
	<!-- 1.双花括号语法,里面是javascript表达式,组件的data数据发生变化时也会动态改变 -->
	<h1>{{ msg + ":" + name }}</h1>
</template>

原始HTML

<template>
	<!-- 2.html动态语言 -->
    {{htmls}}:
    <span v-html="htmls"></span>
</template>

Attribute 绑定

<template>
	<!-- 3.1 Attribute 绑定 一般的Attribute如果绑定的为假值,则id 被移除-->
  	<!-- <div v-bind:id="id"> -->
  	<!-- v-bind:id简写成:id <div:id="id"> -->
  	<!-- 3.3 v-bind不指定Attribute可用接收Attribute对象 -->
  	<div v-bind="attributes"></div>
</template>

使用 JavaScript 表达式

<template>
	<!-- 4.更加复杂的javascript表达式 -->
    <div :id="`list-${id}`">
	    {{number + 1}}
	    {{ok ? "是":"否"}}
	    {{msg.split("").reverse().join("")}}
	</div>
</template>

调用函数

<template>
	 <!-- 5.直接调用函数时,当页面重新渲染,就会再次被调用函数,有几个直接调用函数就会调用几次
	 ,当页面中的数据发生变化时,就会重新渲染 -->
      <div>{{getNumber()}}</div>
</template>

全局组件调用

import { createApp } from 'vue'
import App from './RootApp.vue'

let name = "吕懿洋"
// createApp(组件,组件的props对象)
let app = createApp(App, { name: name })

// 6.设置全局变量,可被任意组件调用
app.config.globalProperties.rootObj = {
    author: 'lvyy'
}
app.mount('#app')

<template>
	 <!-- 6.全局组件可用直接调用 -->
      {{rootObj.author}}
</template>

内置指令

	  <!-- 7.1 v-text -->
      <span v-text="msg"></span>
      <span>{{msg}}</span>
      <br />
      
      <!-- 7.2 v-html -->
      <span v-html="htmls"></span>
      <span>
        <font color="red">红色文字</font>
      </span>
      <br />
      
      <!-- 7.3 v-show -->
      <span v-show="false">测试v-show</span>
      <span style="display:none;">测试等价v-show</span>
      
      <!-- 7.4 v-if v-else v-else-if -->
      <span v-if="number == 1">number == 1</span>
      <span v-else-if="number == 2">number == 2</span>
      <span v-else>number != 1 且 number != 2</span>
      
      <!-- 7.5 v-for key用于指定排序,key要求时字符串,且不要重复-->
      <!-- 7.5.1 v-for = (value, index) in array -->
      <div v-for="(peo,index) in forList" :key="index">
        <!-- 7.5.2 v-for = (value, key, index) in obj -->
        <span v-for="(value,key,index1) in peo" :key="index + key + index1">
          {{`${index1} ${key} : ${value}`}}
          <br />
        </span>
        <br />
      </div>
      
      <!-- 7.6 v-on: -->
      <!-- 7.6.1 v-on:的缩写是@,如下是基本用法 -->
      <button v-on:click="sayHello">hello1</button>
      <br />
      <button @click="sayHello">hello2</button>
      <br />

      <!-- 7.6.2 []可以赋予动态参数的事件类型, 注意动态参数应该只可能时字符串或者null,为null将移除该事件 -->
      <button v-on:[event]="sayHello">hello3</button>
      <br />
      <button @[event]="sayHello">hello4</button>
      <br />

      <!-- 7.6.3 $event是内联声明的参数,按照个人理解:$event就是事件触发时,
      这个绑定这个事件的标签的各种信息,包括了触发的方式鼠标还是键盘 -->
      <button v-on:[event]="getMsg('hello',$event)">hello5</button>
      <br />
      <button @[event]="getMsg('hello',$event)">hello6</button>
      <br />

      <!-- 7.6.4 事件修饰符 -->
      <!-- 7.6.4.1 stop 暂无应用场景 -->
      <button v-on:[event].stop="getMsg('hello',$event)">hello7</button>
      <br />
      <button @[event].stop="getMsg('hello',$event)">hello8</button>
      <br />

      <!-- 7.6.4.2 prevent 暂无应用场景 -->
      <button v-on:[event].prevent="getMsg('hello',$event)">hello9</button>
      <br />
      <button @[event].prevent="getMsg('hello',$event)">hello10</button>
      <br />

      <!-- 7.6.4.3 capture 暂无应用场景 -->
      <button v-on:[event].capture="getMsg('hello',$event)">hello11</button>
      <br />
      <button @[event].capture="getMsg('hello',$event)">hello12</button>
      <br />

      <!-- 7.6.4.4 self 暂无应用场景 -->
      <button v-on:[event].self="getMsg('hello',$event)">hello13</button>
      <br />
      <button @[event].self="getMsg('hello',$event)">hello14</button>
      <br />

      <!-- 7.6.4.5 once 应用场景只需要点击一次的事件 -->
      <button v-on:[event].once="getMsg('hello',$event)">hello15</button>
      <br />
      <button @[event].once="getMsg('hello',$event)">hello16</button>
      <br />

      <!-- 7.6.4.6 left,right,middle 经测试,在IE浏览器点击没有效果 -->
      <button v-on:[event].left="getMsg('hello',$event)">hello17</button>
      <br />
      <button @[event].left="getMsg('hello',$event)">hello18</button>
      <br />
      <button v-on:[event].right="getMsg('hello',$event)">hello19</button>
      <br />
      <button @[event].right="getMsg('hello',$event)">hello20</button>
      <br />
      <button v-on:[event].middle="getMsg('hello',$event)">hello21</button>
      <br />
      <button @[event].middle="getMsg('hello',$event)">hello22</button>
      <br />

      <!-- 7.6.4.7 键盘按键指定触发:keyup.键盘字母或其他 -->
      <button v-on:keyup.x.once="getMsg('hello',$event)">hello23</button>
      <br />
      <button @keyup.y="getMsg('hello',$event)">hello24</button>
      <br />
      
      <!-- 7.6.4.8 子组件事件 -->
      <ChildApp v-on:myEvent="getMsg('hello',$event)"></ChildApp>
      <br />
      <ChildApp @myEvent="getMsg('hello',$event)"></ChildApp>
      <br />

      <!-- 7.7 v-bind: -->
      <!-- 7.7.1 绑定 静态attribute -->
      <img v-bind:src="imageSrc" />
      <!-- 7.7.2 绑定 动态attribute -->
      <img v-bind:[attribute1]="imageSrc" />
      <!-- 7.7.3 绑定缩写 -->
      <img :[attribute1]="imageSrc" />
      <!-- 7.7.4 内联字符串拼接 -->
      <div :id="app4+'4'"></div>

      <!-- 7.7.5 class 绑定 -->
      <!-- 7.7.5.1 {静态class名:boolean...} -->
      <div :class="{red:isRed}">文字1</div>
      <!-- 7.7.5.2 [动态class1,动态class2...] -->
      <div :class="[className1,className2]">文字2</div>
      <!-- 7.7.5.3 2混用1 -->
      <div :class="[className1,{className2:true}]">文字3</div>

      <!-- 7.7.6 style 绑定 -->
      <!-- 7.7.6.1 {静态style样式:值...} -->
      <div :style="{fontSize: 28 + 'px'}">文字1</div>
      <!-- 7.7.6.2 [动态style样式obj1,...] -->
      <div :style="[style1,style2]">文字2</div>

      <!-- 7.7.7 绑定对象形式的attribute -->
      <div v-bind="attributes1">测试绑定ObjAttribute</div>

      <!-- 7.7.8 单个prop 绑定 要求id,name都在子组件声明-->
      <ChildApp1 :id="id" :name="name"></ChildApp1>

      <!-- 7.7.8 整个props 绑定 -->
      <ChildApp1 v-bind="{id:'app6',name:name}"></ChildApp1>

动态参数注意事项

	  <!-- 8 动态参数的语法限定 -->
      <!-- 8.1 参数数据类型应当是字符串或者为null,null是将事件移除,其他情况会报警告-->
      <!-- 8.2 复杂的参数表达式类型应当用计算属性来代替-->
      <!-- 8.3 参数名应当全部小写,如果大写也会被识别为小写 -->

data()

	 <script>
	 	data(){return {...}}
	 </script>
      <!-- 9.1 data选项的格式是一个函数,该函数返回一个obj数据  -->
      <!-- 9.2 当组件被创建时,会调用data函数,返回的obj中的顶层属性都会被代理到组件实例中,通过this调用,动态的渲染数据 -->
      <!-- 9.3 需要被代理的属性应该一开始就要放在data中,一开始不确认默认值时,可以赋null、undefined或其他值 -->
      <!-- 9.4 vue暴露出来的内置api会以$开头,暴露出来的内置属性以_开头,因此我们自定义的属性和方法不要以符号开头 -->

注意: 当一个obj赋值给this.obj时,赋值后this.obj是响应式状态,而obj不是,因此他们两个===比较为false

data()深度响应

      <!-- 12.1 data中obj中的obj的属性发生变化,也可以进行响应渲染 -->
      <!-- 12.2 data中obj中的arr的发生变化,也可以进行响应渲染 -->

methods

	  <!-- 11 methods:{} methods是一个包含所有方法的对象-->
      <!-- 11.1 methods中的方法永远指向组件实例的this -->
      <!-- 11.2 methods定义方法的时候,不可以用箭头函数,因为箭头函数没有自己this上下文 -->

有状态的methods(防抖)

		<!-- 14 预置防抖的事件处理器 -->
      	<!-- 14.4 防抖函数调用 -->
      	<button @click="sayDelayHello1">防抖响应</button>
	// 14.1 引入防抖函数(需要npm i lodash)
	import { debounce } from "lodash";
	
	export default(){
		data(){
			return{
			}
		},
		methods:{
			// 14.2 引入防抖函数(需要npm i lodash)
			sayHello1() {
		      console.log("防抖函数2");
		    },
		},
		created(){
			// 14.3 created中自定义的属性,貌似不用再data中声明,为了遵循规范还是再data中定义一下
    		this.sayDelayHello1 = debounce(this.sayHello1, 1000);
		}
	}

DOM更新时机

      <!-- 13.1 当响应式状态发生变化,DOM就会更新 -->
      <!-- 13.2 DOM更新不是同步的,而是进行周期渲染,
      		到下一个周期前把响应式状态改变的存入缓存队列,到下一个周期后更新缓存队列 -->
      <!-- 13.3 周期执行完毕,可以再次执行回调函数 -->

计算属性

      <!-- 15.1 计算属性的应用场景: 一个复杂的javascript表达式再组件中被多处使用,
      		应该定义在计算属性中代替显示,简化代码,增加复用性 -->
      {{ reverseMsg }}
      <!-- 15.2 计算属性和方法的区别调用的时机不同 -->
      <!-- 15.2.1 计算属性调用的情况发生在里面依赖的数据改变的时候,
      		在多处使用的情况下,只会调用一次,更新缓存空间数据,页面调用的是缓存中的数据-->
      <!-- 15.2.2 非事件类型的方法调用,是一旦页面重新渲染时,就会调用,调用的次数和显示的次数一致-->
      <!-- 15.2.3 因此,当我们可以用计算属性代替方法的时候,优先使用计算属性的方式 -->
      <!-- 15.2.4 get方法里面不要有副作用,计算属性一般不直接修改 -->
	export default(){
		data(){
			return{
				msg1:'hello'
			}
		},
		computed: {
		    // 计算属性看上去像一个方法,其实他是一个属性,如果里面只写get方法,可以简写成这样
		    reverseMsg() {
		      return this.msg1
		        .split("")
		        .reverse()
		        .join("");
		    },
	}

class和style绑定

      <!-- 16.1 class静态 -->
      <span class="err">结果</span>
      <!-- 16.2 class动态,是否绑定err -->
      <span :class="{err:!isSuccess}">结果</span>
      <!-- 16.3 class数组, 是否绑定errClass对应的class值  -->
      <span :class="[errClass]">结果</span>
      <!-- 16.4 计算属性应用  -->
      <button @click="isSuccess = !isSuccess">{{buttonName}}</button>
      <span v-bind:class="spanClass">结果</span>
      <!-- 16.5 组件元素使用class 单个根元素直接绑定,多个根元素可以在$attrs.class,接收父组件传入的class -->
      <ChildApp1 :class="{success:isSuccess}"></ChildApp1>
      <!-- 16.6 style静态 -->
      <span style="color: red; font-size: 28px;">结果</span>
      <!-- 16.7 style动态obj -->
      <span :style="{color: 'red','font-size': 28+'px'}">结果</span>
      <!-- 16.8 style动态obj数组 -->
      <span :style="[{color: 'red'},{'font-size': 28+'px'}]">结果</span>

条件渲染

      <!-- 17.1 v-if v-else v-else-if判断是否渲染当前元素 -->
      <!-- 17.2 切换多个元素,可以用template元素 -->
      <!-- 17.3 切换多个元素,可以用template元素 -->
      <!-- 17.4 v-show 相当于display:none; 不可以用于修饰template元素,没有v-else -->
      <!-- 17.5 v-if是真实的,也是惰性的,true才渲染元素,初始若false,则不会渲染元素,而v-show则是一开始就渲染元素,只是设置是否可见 -->

列表渲染

      <!-- 18.1 数组: v-for="(item[,index]) in items" -->
      <!-- 18.2 对象数组: v-for="({prop1, prop2}[,index]) in items" -->
      <span
        v-for="({id,name,age}, index) in forList"
        :key="id+name+index"
      >{{id + "-" + name + "-" + age + "\t"}}</span>
      <!-- 18.3 对象: v-for="(value [,key ,index]) in obj" -->
      <!-- 18.4 数值: v-for="n in number" n从1开始-->
      <!-- 18.5 原则上v-for修饰的元素和内部都是可以直接使用v-for里面的数据,原因是v-for的优先级要高于其他attribute,但是因为v-for的优先级低于v-if,则v-if用不了v-for的数据,不建议v-if和v-for一个元素并用-->
      <!-- 18.6 key是特殊的attribute, 可用于重用和排序元素,如果没有key修饰,则是就地更新,key的值一般是字符串或者数值,不重复的 -->
      <!-- 18.7 在for循环中建议加入key值 -->
      <!-- 18.8 for循环作用于组件时, 数据不会自动传入给组件,需要手动将数据指定给props, 我们需要注意传入给组件的obj或数组类型的数据,组件里发生变化,父组件也会跟着变化,我们要子组件和父组件明确我们是否要共用数据(非常不建议)-->

数组变换侦听

	  <!-- 19.1 Vue可以数据侦听的变更方法 -->
      <!-- 19.1.1 push(data1[,data2,data3...]) -> length: 尾部追加一条或多条数据,返回新长度 -->
      <button @click="pushDatas">尾部追加</button>
      <!-- 19.1.2 pop() -> data: 尾部移除一条数据,返回移除数据 -->
      <button @click="popData">尾部删除</button>
      <!-- 19.1.3 shift() -> data:头部移除一条数据,返回移除数据  -->
      <button @click="shiftData">头部删除</button>
      <!-- 19.1.4 unshift() -> length: 头部追加一条或多条数据,返回新长度 -->
      <button @click="unshiftDatas">头部追加</button>
      <!-- 19.1.5 splice(index,deleteLen[,addData]) -> deleteData: 从指定下标开始删除,删除指定的数目,删除后替换指定数据 -->
      <button @click="spliceDatas">替换</button>
      <!-- 19.1.6 sort((a,b)=>{return a.xxx-b.xxx}) -> 排序,排序的方法前者大于后者时,排序为正序(从小到大排列),排序的方法后者大于前者时,排序为逆序(从大到小排列) -->
      <button @click="sortAscDatas">正序</button>
      <button @click="sortDescDatas">逆序</button>
      <!-- 19.1.7 reverse(): 数组反转 -->
      <button @click="reverseDatas">反转</button>

      <!-- 19.1.8 filter(item=>{return ...}) -> newArray: 过滤符合条件的数据,返回新的数组,旧的数组不发生变化 -->
      <!-- 19.1.9 concat(array) -> newArray: 合并两个数组,返回新的数组,旧的数组不发生变化 -->
      <!-- 19.1.10 slice(startIndex, endIndex) -> newArray: 截取指定下标范围的数组,不包括endIndex,旧的数组不发生变化 -->
      <!-- 19.2 如果我们要对现有列表进行过滤,不应该考虑v-for+v-if,而是创建一个计算属性过滤现有的列表,v-for遍历计算属性解决,如果计算属性无法解决,用于方法替换计算属性 -->

事件处理

	  <!-- 20.1 内联监听事件时一个表达式就可处理简单的事情 -->
      <!-- 20.2 方法事件处理,有个$event参数,记录事件触发元素的详细信息-->
      <!-- 20.3 省略部分:事件修饰符,键盘修饰符,鼠标按键修饰符(实际业务开发应用较少) -->

表单输入绑定

	  <!-- 21.1 常规方式 -->
      <input :value="text" @input="event => text = event.target.value" />
      <!-- 21.2 v-model 是当输入框内容变动时,text也会跟着改变-->
      <input v-model="text" />
      <!-- 21.2.1 v-model.lazy  是输入框内容输入完毕,失焦后,text才会改变-->
      <input v-model.lazy="text" />
      <!-- 21.2.2 v-model.number 如果text可以被parseFloat,则会转换成number类型的数据,否则保留原样-->
      <input v-model.number="text" />
      <!-- 21.2.3 v-model.trim 输入框自动去除前后空格-->
      <input v-model.trim="text" />

      <!-- 21.3 textarea -->
      <textarea :value="text" @input="event => text=event.target.value"></textarea>
      <textarea v-model="text"></textarea>

      <!-- 21.4 checkbox input checkbox v-model单个情况-->
      <!-- 21.4.1 value默认情况是true/false,可以自定义true-value和false-value attribute自定义选中和不选中的value值 -->
      {{checked}}
      <input type="checkbox" v-model="checked" true-value="yes" false-value="no" />
      <!-- 21.5 checkbox input checkbox v-model相同情况,定义成数组-->
      {{names}}
      <input type="checkbox" v-model="names" :value="name1" />
      <input type="checkbox" v-model="names" :value="name2" />
      <input type="checkbox" v-model="names" :value="name3" />

      <!-- 21.6 radio单选 -->
      {{radioValue}}
      <input type="radio" v-model="radioValue" value="one" />
      <input type="radio" v-model="radioValue" value="two" />

      <!-- 21.7 selected选择框 单选 -->
      {{selected}}
      <select v-model="selected">
        <option value disabled="true">请选择</option>
        <option>A</option>
        <option>B</option>
        <option>C</option>
      </select>

      <!-- 21.8 selected选择框 多选 -->
      {{selected1}}
      <select v-model="selected1" multiple>
        <option value="A">A.上班</option>
        <option value="B">B.吃饭</option>
        <option value="C">C.睡觉</option>
      </select>

      <!-- 21.9 selected选择框的option中value可以绑定obj,v-model获取的也就是obj -->
      {{selectObj}}
      <select v-model="selectObj">
        <option
          v-for="option in selectedOptions"
          :value="option"
          :key="option.number"
        >{{option.number}}</option>
      </select>

生命周期钩子

 <!-- 22 生命周期钩子执行顺序:
 beforeCreate() created() beforeMount() mounted() beforeUpdate() updated() beforeUnmount() unmounted() -->

侦听器watch

<!-- 23.1 侦听器相比较于计算属性,
	计算属性里面不希望有DOM数据的变动或者异步请求,
	侦听器则是处理需要有DOM数据变动或者异步请求的发送 -->

模板引用ref

      <!-- 24.1 refs在mounted()生命周期函数之后使用,之前都是为null的 -->
      <!-- 24.2 this.$refs.refName 表示当前的底层DOM -->
      聚焦输入框:
      <input ref="input" v-model="userId1" />
      <!-- 24.3 组件上应用ref时,子组件可以通过expose:["data属性名",...] 暴露出子组件的属性,父组件可以通过 				this.$refs.refName.属性名 获取到-->
      <ChildApp1 ref="childApp1"></ChildApp1>
	export default(){
		data(){
			return{
				userId:""
			}
		},
		watch: {
		    userId: {
		      handler(newValue, oldValue) {
		        // this.searchDelayUserId();
		        this.searchUserId();
		      },
		      // 23.2 immediate的触发时机是在created生命周期钩子之前,所以created中定义的有状态的方法不能被调用
		      immediate: true
		    }
  		},
	}
	// 聚焦输入框
	this.$refs["input"].focus();
	// 组件上应用ref时,子组件可以通过expose:["data属性名",...] 暴露出子组件的属性,
	// 父组件可以通过this.$refs.refName.属性名 获取到
    console.log(this.$refs.childApp1.childName);
    console.log(this.$refs.childApp1.childId);

组件基础

      <!-- 25.1 创建一个SFC -->
      <!-- 25.2 引入SFC import xxx from "xxx.vue" -->
      <!-- 25.3 注册SFC components:{xxx} -->

      <!-- 25.4 如果在SFC中使用模板,标签建议使用驼峰命名方式,prop和属性用是xxx-xxx, 元素可以使用闭合标签 -->
      <!-- 25.5 如果在HTML文件中使用模板, 都应该使用xxx-xxx, 元素不可以使用闭合标签 -->

      <!-- 25.6 监听事件 this.$emit("事件名",参数...) -->
      <!-- 25.7 emits:["事件名"] 可以暴露要监听的事件-->

      <!-- 25.8 插槽 <slot/> -->
      <!-- 26.9 动态组件适用于来回切换组件的情况 -->
      <!-- 26.10 不用keep-alive修饰时,切换注销组件,用keep-alive修饰,暂存组件 -->
      <button @click="componentName = componentName == 'ChildApp'?'ChildApp1':'ChildApp'">切换</button>
      <keep-alive>
        <component :is="componentName"></component>
      </keep-alive>

组件注册

	  <!-- 27.1 在根组件App注册组件,可以全局使用 -->
      <ChildApp2></ChildApp2>
      <!-- 27.2 在SFC中注册组件,后代组件不能使用 -->
      <!-- 27.3 Props应当是向下传递,因此当传递的是对象或数组时,应当注意不应该直接改变,影响父组件的数据值,可以选择事件回调,交由父组件操作 -->

项目文件结构

在这里插入图片描述
黄色截图就是修改和新增的相关文件

RootApp.vue

<template>
  <!-- 3.1 Attribute 绑定 一般的Attribute如果绑定的为假值,则id 被移除-->
  <!-- <div v-bind:id="id"> -->
  <!-- v-bind:id简写成:id <div:id="id"> -->
  <!-- 3.3 v-bind不指定Attribute可用接收Attributes对象 -->
  <div v-bind="attributes">
    <!-- 1.双花括号语法,里面是javascript表达式,组件的data数据发生变化时也会动态改变 -->
    <h1>{{ msg + ":" + name }}</h1>

    <!-- 2.html动态语言 -->
    {{htmls}}:
    <span v-html="htmls"></span>
    <br />
    <!-- 3.2 对于接收真假值的Attribute 不会被移除 -->
    <button v-bind:disabled="disabled" @click="setNumber">不可点击按钮</button>
    <!-- 4.更加复杂的javascript表达式 -->
    {{number + 1}}
    {{ok ? "是":"否"}}
    {{msg.split("").reverse().join("")}}
    <div :id="`list-${id}`">
      <!-- 5.直接调用函数时,当页面重新渲染,就会再次被调用函数,有几个直接调用函数就会调用几次,当页面中的数据发生变化时,就会重新渲染 -->
      {{getNumber()}}
      {{getNumber()}}
      <br />
      <!-- 6.全局组件可用直接调用 -->
      {{rootObj.author}}
      <br />
      <!-- 7.内置指令 -->
      <!-- 7.1 v-text -->
      <span v-text="msg"></span>
      <span>{{msg}}</span>
      <br />
      <!-- 7.2 v-html -->
      <span v-html="htmls"></span>
      <span>
        <font color="red">红色文字</font>
      </span>
      <br />
      <!-- 7.3 v-show -->
      <span v-show="false">测试v-show</span>
      <span style="display:none;">测试等价v-show</span>
      <!-- 7.4 v-if v-else v-else-if -->
      <span v-if="number == 1">number == 1</span>
      <span v-else-if="number == 2">number == 2</span>
      <span v-else>number != 1 且 number != 2</span>
      <!-- 7.5 v-for key用于指定排序,key要求时字符串,且不要重复-->
      <!-- 7.5.1 v-for = (value, index) in array -->
      <div v-for="(peo,index) in forList" :key="index">
        <!-- 7.5.2 v-for = (value, key, index) in obj -->
        <span v-for="(value,key,index1) in peo" :key="index + key + index1">
          {{`${index1} ${key} : ${value}`}}
          <br />
        </span>
        <br />
      </div>
      <!-- 7.6 v-on: -->
      <!-- 7.6.1 v-on:的缩写是@,如下是基本用法 -->
      <button v-on:click="sayHello">hello1</button>
      <br />
      <button @click="sayHello">hello2</button>
      <br />

      <!-- 7.6.2 []可以赋予动态参数的事件类型, 注意动态参数应该只可能时字符串或者null,为null将移除该事件 -->
      <button v-on:[event]="sayHello">hello3</button>
      <br />
      <button @[event]="sayHello">hello4</button>
      <br />

      <!-- 7.6.3 $event是内联声明的参数,按照个人理解:$event就是事件触发时,这个绑定这个事件的标签的各种信息,包括了触发的方式鼠标还是键盘 -->
      <button v-on:[event]="getMsg('hello',$event)">hello5</button>
      <br />
      <button @[event]="getMsg('hello',$event)">hello6</button>
      <br />

      <!-- 7.6.4 事件修饰符 -->
      <!-- 7.6.4.1 stop 暂无应用场景 -->
      <button v-on:[event].stop="getMsg('hello',$event)">hello7</button>
      <br />
      <button @[event].stop="getMsg('hello',$event)">hello8</button>
      <br />

      <!-- 7.6.4.2 prevent 暂无应用场景 -->
      <button v-on:[event].prevent="getMsg('hello',$event)">hello9</button>
      <br />
      <button @[event].prevent="getMsg('hello',$event)">hello10</button>
      <br />

      <!-- 7.6.4.3 capture 暂无应用场景 -->
      <button v-on:[event].capture="getMsg('hello',$event)">hello11</button>
      <br />
      <button @[event].capture="getMsg('hello',$event)">hello12</button>
      <br />

      <!-- 7.6.4.4 self 暂无应用场景 -->
      <button v-on:[event].self="getMsg('hello',$event)">hello13</button>
      <br />
      <button @[event].self="getMsg('hello',$event)">hello14</button>
      <br />

      <!-- 7.6.4.5 once 应用场景只需要点击一次的事件 -->
      <button v-on:[event].once="getMsg('hello',$event)">hello15</button>
      <br />
      <button @[event].once="getMsg('hello',$event)">hello16</button>
      <br />

      <!-- 7.6.4.6 left,right,middle 经测试,在IE浏览器点击没有效果 -->
      <button v-on:[event].left="getMsg('hello',$event)">hello17</button>
      <br />
      <button @[event].left="getMsg('hello',$event)">hello18</button>
      <br />
      <button v-on:[event].right="getMsg('hello',$event)">hello19</button>
      <br />
      <button @[event].right="getMsg('hello',$event)">hello20</button>
      <br />
      <button v-on:[event].middle="getMsg('hello',$event)">hello21</button>
      <br />
      <button @[event].middle="getMsg('hello',$event)">hello22</button>
      <br />

      <!-- 7.6.4.7 键盘按键指定触发:keyup.键盘字母或其他 -->
      <button v-on:keyup.x.once="getMsg('hello',$event)">hello23</button>
      <br />
      <button @keyup.y="getMsg('hello',$event)">hello24</button>
      <br />
      <!-- 7.6.4.8 子组件事件 -->
      <ChildApp v-on:myEvent="getMsg('hello',$event)"></ChildApp>
      <br />
      <ChildApp @myEvent="getMsg('hello',$event)"></ChildApp>
      <br />

      <!-- 7.7 v-bind: -->
      <!-- 7.7.1 绑定 静态attribute -->
      <img v-bind:src="imageSrc" />
      <!-- 7.7.2 绑定 动态attribute -->
      <img v-bind:[attribute1]="imageSrc" />
      <!-- 7.7.3 绑定缩写 -->
      <img :[attribute1]="imageSrc" />
      <!-- 7.7.4 内联字符串拼接 -->
      <div :id="app4+'4'"></div>

      <!-- 7.7.5 class 绑定 -->
      <!-- 7.7.5.1 {静态class名:boolean...} -->
      <div :class="{red:isRed}">文字1</div>
      <!-- 7.7.5.2 [动态class1,动态class2...] -->
      <div :class="[className1,className2]">文字2</div>
      <!-- 7.7.5.3 2混用1 -->
      <div :class="[className1,{className2:true}]">文字3</div>

      <!-- 7.7.6 style 绑定 -->
      <!-- 7.7.6.1 {静态style样式:值...} -->
      <div :style="{fontSize: 28 + 'px'}">文字1</div>
      <!-- 7.7.6.2 [动态style样式obj1,...] -->
      <div :style="[style1,style2]">文字2</div>

      <!-- 7.7.7 绑定对象形式的attribute -->
      <div v-bind="attributes1">测试绑定ObjAttribute</div>

      <!-- 7.7.8 单个prop 绑定 要求id,name都在子组件声明-->
      <ChildApp1 :id="id" :name="name"></ChildApp1>

      <!-- 7.7.8 整个props 绑定 -->
      <ChildApp1 v-bind="{id:'app6',name:name}"></ChildApp1>

      <!-- 8 动态参数的语法限定 -->
      <!-- 8.1 参数数据类型应当是字符串或者为null,null是将事件移除,其他情况会报警告-->
      <!-- 8.2 复杂的参数表达式类型应当用计算属性来代替-->
      <!-- 8.3 参数名应当全部小写,如果大写也会被识别为小写 -->

      <!-- 9 data(){}  -->
      <!-- 9.1 data选项的格式是一个函数,该函数返回一个obj数据  -->
      <!-- 9.2 当组件被创建时,会调用data函数,返回的obj中的顶层属性都会被代理到组件实例中,通过this调用,动态的渲染数据 -->
      <!-- 9.3 需要被代理的属性应该一开始就要放在data中,一开始不确认默认值时,可以赋null、undefined或其他值 -->
      <!-- 9.4 vue暴露出来的内置api会以$开头,暴露出来的内置属性以_开头,因此我们自定义的属性和方法不要以符号开头 -->

      <!-- 10 当一个obj赋值给this.obj时,注意:赋值后this.obj是响应式状态,而obj不是,因此他们两个===比较为false -->

      <!-- 11 methods:{} methods是一个包含所有方法的对象-->
      <!-- 11.1 methods中的方法永远指向组件实例的this -->
      <!-- 11.2 methods定义方法的时候,不可以用箭头函数,因为箭头函数没有自己this上下文 -->

      <!-- 12 深度响应 -->
      <!-- 12.1 data中obj中的obj的属性发生变化,也可以进行响应渲染 -->
      <!-- 12.2 data中obj中的arr的发生变化,也可以进行响应渲染 -->

      <!-- 13 DOM更新时机 -->
      <!-- 13.1 当响应式状态发生变化,DOM就会更新 -->
      <!-- 13.2 DOM更新不是同步的,而是进行周期渲染,到下一个周期前把响应式状态改变的存入缓存队列,到下一个周期后更新 -->
      <!-- 13.3 周期执行完毕,可以再次执行回调函数 -->

      <!-- 14 预置防抖的事件处理器 -->
      <!-- 14.3 防抖函数调用 -->
      <button @click="sayDelayHello">防抖响应</button>

      <!-- 14.4 在methods里设置有防抖状态的函数,如果有多处调用,会相互影响 -->
      <!-- 14.4.1 再面对一个函数,多处调用,且并不是每处调用都需要防抖时,需要如下设置 -->
      <!-- 14.4.2 methods里定义没有状态的逻辑处理函数 -->
      <!-- 14.4.3 防抖函数再created钩子中声明 -->
      <!-- 14.4.4 这样防抖函数和没有状态的逻辑处理函数就分开了,需要防抖调用,就用created中的防抖函数,不需要则使用methods中的函数 -->
      <button @click="sayDelayHello1">防抖响应</button>
      <button @click="sayHello1">非防抖响应</button>

      <!-- 15. 计算属性 -->
      <!-- 15.1 计算属性的应用场景: 一个复杂的javascript表达式再组件中被多处使用,应该定义在计算属性中代替显示,简化代码,增加复用性 -->
      {{ reverseMsg }}
      {{ reverseMsg }}
      <br />
      {{ reverseMsg }}
      {{ reverseMsg }}
      <br />
      <!-- 15.2 计算属性和方法的区别调用的时机不同 -->
      <!-- 15.2.1 计算属性调用的情况发生在里面依赖的数据改变的时候,在多处使用的情况下,只会调用一次,更新缓存空间数据,页面调用的是缓存中的数据-->
      <!-- 15.2.2 非事件类型的方法调用,是一旦页面重新渲染时,就会调用,调用的次数和显示的次数一致-->
      <!-- 15.2.3 因此,当我们可以用计算属性代替方法的时候,优先使用计算属性的方式 -->
      <!-- 15.2.4 get方法里面不要有副作用,计算属性一般不直接修改 -->
      {{ reverseMsg1 }}
      {{ reverseMsg1 }}
      <br />
      {{ reverseMsg1 }}
      {{ reverseMsg1 }}
      <br />
      <!-- 16. class和style绑定 -->
      <!-- 16.1 class静态 -->
      <span class="err">结果</span>
      <!-- 16.2 class动态,是否绑定err -->
      <span :class="{err:!isSuccess}">结果</span>
      <!-- 16.3 class数组, 是否绑定errClass对应的class值  -->
      <span :class="[errClass]">结果</span>
      <!-- 16.4 计算属性应用  -->
      <button @click="isSuccess = !isSuccess">{{buttonName}}</button>
      <span v-bind:class="spanClass">结果</span>
      <!-- 16.5 组件元素使用class 单个根元素直接绑定,多个根元素可以在$attrs.class,接收父组件传入的class -->
      <ChildApp1 :class="{success:isSuccess}"></ChildApp1>
      <!-- 16.6 style静态 -->
      <span style="color: red; font-size: 28px;">结果</span>
      <!-- 16.7 style动态obj -->
      <span :style="{color: 'red','font-size': 28+'px'}">结果</span>
      <!-- 16.8 style动态obj数组 -->
      <span :style="[{color: 'red'},{'font-size': 28+'px'}]">结果</span>

      <!-- 17. 条件渲染 -->
      <!-- 17.1 v-if v-else v-else-if判断是否渲染当前元素 -->
      <!-- 17.2 切换多个元素,可以用template元素 -->
      <!-- 17.3 切换多个元素,可以用template元素 -->
      <!-- 17.4 v-show 相当于display:none; 不可以用于修饰template元素,没有v-else -->
      <!-- 17.5 v-if是真实的,也是惰性的,true才渲染元素,初始若false,则不会渲染元素,而v-show则是一开始就渲染元素,只是设置是否可见 -->

      <!-- 18 列表渲染 -->
      <!-- 18.1 数组: v-for="(item[,index]) in items" -->
      <!-- 18.2 对象数组: v-for="({prop1, prop2}[,index]) in items" -->
      <span
        v-for="({id,name,age},index) in forList"
        :key="id+name+index"
      >{{id + "-" + name + "-" + age + "\t"}}</span>
      <br />
      <!-- 18.3 对象: v-for="(value [,key ,index]) in obj" -->
      <!-- 18.4 数值: v-for="n in number" n从1开始-->
      <!-- 18.5 原则上v-for修饰的元素和内部都是可以直接使用v-for里面的数据,原因是v-for的优先级要高于其他attribute,但是因为v-for的优先级低于v-if,则v-if用不了v-for的数据,不建议v-if和v-for一个元素并用-->
      <!-- 18.6 key是特殊的attribute, 可用于重用和排序元素,如果没有key修饰,则是就地更新,key的值一般是字符串或者数值,不重复的 -->
      <!-- 18.7 在for循环中建议加入key值 -->
      <!-- 18.8 for循环作用于组件时, 数据不会自动传入给组件,需要手动将数据指定给props, 我们需要注意传入给组件的obj或数组类型的数据,组件里发生变化,父组件也会跟着变化,我们要子组件和父组件明确我们是否要共用数据-->

      <!-- 19 数组变换侦听 -->
      <!-- 19.1 Vue可以数据侦听的变更方法 -->
      <!-- 19.1.1 push(data1[,data2,data3...]) -> length: 尾部追加一条或多条数据,返回新长度 -->
      <button @click="pushDatas">尾部追加</button>
      <!-- 19.1.2 pop() -> data: 尾部移除一条数据,返回移除数据 -->
      <button @click="popData">尾部删除</button>
      <!-- 19.1.3 shift() -> data:头部移除一条数据,返回移除数据  -->
      <button @click="shiftData">头部删除</button>
      <!-- 19.1.4 unshift() -> length: 头部追加一条或多条数据,返回新长度 -->
      <button @click="unshiftDatas">头部追加</button>
      <!-- 19.1.5 splice(index,deleteLen[,addData]) -> deleteData: 从指定下标开始删除,删除指定的数目,删除后替换指定数据 -->
      <button @click="spliceDatas">替换</button>
      <!-- 19.1.6 sort((a,b)=>{return a.xxx-b.xxx}) -> 排序,排序的方法前者大于后者时,排序为正序(从小到大排列),排序的方法后者大于前者时,排序为逆序(从大到小排列) -->
      <button @click="sortAscDatas">正序</button>
      <button @click="sortDescDatas">逆序</button>
      <!-- 19.1.7 reverse(): 数组反转 -->
      <button @click="reverseDatas">反转</button>

      <!-- 19.1.8 filter(item=>{return ...}) -> newArray: 过滤符合条件的数据,返回新的数组,旧的数组不发生变化 -->
      <!-- 19.1.9 concat(array) -> newArray: 合并两个数组,返回新的数组,旧的数组不发生变化 -->
      <!-- 19.1.10 slice(startIndex, endIndex) -> newArray: 截取指定下标范围的数组,不包括endIndex,旧的数组不发生变化 -->
      <!-- 19.2 如果我们要对现有列表进行过滤,不应该考虑v-for+v-if,而是创建一个计算属性过滤现有的列表,v-for遍历计算属性解决,如果计算属性无法解决,用于方法替换计算属性 -->

      <!-- 20 事件处理 -->
      <!-- 20.1 内联监听事件时一个表达式就可处理简单的事情 -->
      <!-- 20.2 方法事件处理,有个$event参数,记录事件触发元素的详细信息-->
      <!-- 20.3 省略部分:事件修饰符,键盘修饰符,鼠标按键修饰符(实际业务开发应用较少) -->

      <!-- 21 表单输入绑定 -->
      <!-- 21.1 常规方式 -->
      <input :value="text" @input="event => text = event.target.value" />
      <!-- 21.2 v-model 是当输入框内容变动时,text也会跟着改变-->
      <input v-model="text" />
      <!-- 21.2.1 v-model.lazy  是输入框内容输入完毕,失焦后,text才会改变-->
      <input v-model.lazy="text" />
      <!-- 21.2.2 v-model.number 如果text可以被parseFloat,则会转换成number类型的数据,否则保留原样-->
      <input v-model.number="text" />
      <!-- 21.2.3 v-model.trim 输入框自动去除前后空格-->
      <input v-model.trim="text" />

      <!-- 21.3 textarea -->
      <textarea :value="text" @input="event => text=event.target.value"></textarea>
      <textarea v-model="text"></textarea>

      <!-- 21.4 checkbox input checkbox v-model单个情况-->
      <!-- 21.4.1 value默认情况是true/false,可以自定义true-value和false-value attribute自定义选中和不选中的value值 -->
      {{checked}}
      <input type="checkbox" v-model="checked" true-value="yes" false-value="no" />
      <!-- 21.5 checkbox input checkbox v-model相同情况,定义成数组-->
      {{names}}
      <input type="checkbox" v-model="names" :value="name1" />
      <input type="checkbox" v-model="names" :value="name2" />
      <input type="checkbox" v-model="names" :value="name3" />

      <!-- 21.6 radio单选 -->
      {{radioValue}}
      <input type="radio" v-model="radioValue" value="one" />
      <input type="radio" v-model="radioValue" value="two" />

      <!-- 21.7 selected选择框 单选 -->
      {{selected}}
      <select v-model="selected">
        <option value disabled="true">请选择</option>
        <option>A</option>
        <option>B</option>
        <option>C</option>
      </select>

      <!-- 21.8 selected选择框 多选 -->
      {{selected1}}
      <select v-model="selected1" multiple>
        <option value="A">A.上班</option>
        <option value="B">B.吃饭</option>
        <option value="C">C.睡觉</option>
      </select>

      <!-- 21.9 selected选择框的option中value可以绑定obj,v-model获取的也就是obj -->
      {{selectObj}}
      <select v-model="selectObj">
        <option
          v-for="option in selectedOptions"
          :value="option"
          :key="option.number"
        >{{option.number}}</option>
      </select>

      <!-- 22 生命周期钩子:beforeCreate() created() beforeMount() mounted() beforeUpdate() updated() beforeUnmount() unmounted() -->

      <!-- 23 侦听器watch -->
      <!-- 23.1 侦听器相比较于计算属性,计算属性里面不希望有DOM数据的变动或者异步请求, 侦听器则是处理需要有DOM数据变动或者异步请求的发送 -->
      工号:
      <input v-model="userId" />

      <!-- 24 模板引用ref -->
      <!-- 24.1 refs在mounted()周期函数之后使用,之前都是为null的 -->
      <!-- 24.2 this.$refs.refName 表示当前的底层DOM -->
      聚焦输入框:
      <input ref="input" v-model="userId1" />
      <!-- 24.3 组件上应用ref时,子组件可以通过expose:["data属性名",...] 暴露出子组件的属性,父组件可以通过 this.$refs.refName.属性名 获取到-->
      <ChildApp1 ref="childApp1"></ChildApp1>

      <!-- 25 组件基础 -->
      <!-- 25.1 创建一个SFC -->
      <!-- 25.2 引入SFC import xxx from "xxx.vue" -->
      <!-- 25.3 注册SFC components:{xxx} -->

      <!-- 25.4 如果在SFC中使用模板,元素建议使用驼峰命名方式,prop和元素可以是xxx-xxx, 元素可以使用闭合标签 -->
      <!-- 25.5 如果在HTML文件中使用模板,prop, prop和元素应该使用xxx-xxx, 元素不可以使用闭合标签 -->

      <!-- 25.6 监听事件 this.$emit("事件名",参数...) -->
      <!-- 25.7 emits:["事件名"] 可以暴露要监听的事件-->

      <!-- 25.8 插槽 <slot/> -->
      <!-- 26.9 动态组件适用于来回切换组件的情况 -->
      <!-- 26.10 不用keep-alive修饰时,切换注销组件,用keep-alive修饰,暂存组件 -->
      <button @click="componentName = componentName == 'ChildApp'?'ChildApp1':'ChildApp'">切换</button>
      <keep-alive>
        <component :is="componentName"></component>
      </keep-alive>

      <!-- 27.1 在根组件App注册组件,可以全局使用 -->
      <ChildApp2></ChildApp2>
      <!-- 27.2 在SFC中注册组件,后代组件不能使用 -->
      <!-- 27.3 Props应当是向下传递,因此当传递的是对象或数组时,应当注意不应该直接改变,影响父组件的数据值,可以选择事件回调,交由父组件操作 -->
      <!-- 27.4 组件标签采用驼峰命名,组件中的Attribute采用xxx-xxx方式使用,命名使用驼峰命名 -->
    </div>
  </div>
</template>

<style scoped>
.red {
  font-size: 28px;
}
.className1 {
  font-size: 28px;
}
.className2 {
  color: red;
}
.success {
  color: green;
}
.err {
  color: red;
}
</style>

<script>
// 引入一个子组件
import ChildApp from "./components/ChildApp.vue";
import ChildApp1 from "./components/ChildApp1.vue";
// 引入一张图片
import img1 from "./img/1.png";
// 14.1 引入防抖函数(需要npm i lodash)
import { debounce } from "lodash";
export default {
  // 父组件传入的数据
  props: {
    name: String
  },
  // 子组件
  components: { ChildApp: ChildApp, ChildApp1: ChildApp1 },
  // 本组件定义的数据
  data() {
    return {
      id: "app2",
      msg: "hello world",
      htmls: "<font color='red'>红色文字</font>",
      disabled: false,
      attributes: {
        id: "app3",
        name: "div1"
      },
      number: 1,
      ok: true,
      forList: [
        {
          id: 1,
          name: "张三",
          age: 18
        },
        {
          id: 2,
          name: "李四",
          age: 20
        }
      ],
      event: "click",
      imageSrc: img1,
      attribute1: "src",
      app4: "app",
      isRed: true,
      className1: "className1",
      className2: "className2",
      style1: {
        fontSize: "28px"
      },
      style2: {
        color: "red"
      },
      attributes1: {
        id: "app5",
        "other-attr": "hello"
      },
      name: "美少女",
      sayDelayHello1: null,
      msg1: "i love you",

      submit: "提交",
      revert: "还原",
      isSuccess: false,
      errClass: "err",
      text: "hello",
      checked: "no",
      name1: "张三",
      name2: "李四",
      name3: "王五",
      names: [],
      radioValue: "",
      selected: "",
      selected1: [],
      selectedOptions: [{ number: 1 }, { number: 2 }, { number: 3 }],
      selectObj: "",
      userId: "",
      userId1: "",
      componentName: "ChildApp"
    };
  },
  created() {
    // created中自定义的属性,貌似不用再data中声明,为了遵循规范还是再data中定义一下
    this.sayDelayHello1 = debounce(this.sayHello1, 1000);
    console.log(this.sayDelayHello1);
    this.user = "name";
    console.log(this.user);

    this.searchDelayUserId = debounce(this.searchUserId, 1000);
  },
  computed: {
    // 计算属性看上去像一个方法,其实他是一个属性,如果里面只写get方法,可以简写成这样
    reverseMsg() {
      console.log("hello");
      return this.msg1
        .split("")
        .reverse()
        .join("");
    },
    // 非简写形式
    reverseMsg1: {
      get() {
        return this.msg1
          .split("")
          .reverse()
          .join("");
      },

      set(newValue) {
        console.log("改变了");
      }
    },

    // 复杂的表达式写在计算属性中
    spanClass: {
      get() {
        return {
          success: this.isSuccess,
          err: !this.isSuccess
        };
      }
    },

    buttonName: {
      get() {
        return this.isSuccess ? this.revert : this.submit;
      }
    }
  },
  watch: {
    userId: {
      handler(newValue, oldValue) {
        // this.searchDelayUserId();
        this.searchUserId();
      },
      // 23.2 immediate的触发时机是在created之前,所以created中定义的有状态的方法不能被调用
      immediate: true
    }
  },
  mounted() {
    this.$refs["input"].focus();
    console.log(this.$refs.childApp1.childName);
    console.log(this.$refs.childApp1.childId);
  },
  // 本组件定义的方法
  methods: {
    setNumber() {
      this.number = this.number + 1;
    },

    getNumber() {
      console.log("msg改变就会触发事件");
      return this.number;
    },

    sayHello() {
      console.log("hello world!");
      console.log(this.user);
    },

    getMsg(param1, event) {
      console.log(param1);
      console.log(event);
    },
    // 14.2 设置防抖函数 调用函数名:debounce(业务处理函数, 防抖毫秒数)
    sayDelayHello: debounce(function() {
      console.log("防抖函数");
    }, 1000),

    sayHello1() {
      console.log("防抖函数2");
    },

    // 尾部追加多个数据
    pushDatas() {
      let data1 = { id: 3, name: "王五", age: 22 };
      let data2 = { id: 4, name: "秦六", age: 33 };
      let length = this.forList.push(data1, data2);
      console.log("追加后长度为:" + length);
    },
    // 尾部删除一个数据
    popData() {
      let data = this.forList.pop();
      console.log("删除尾部数据:", data);
    },
    // 头部追加一条或多条数据
    unshiftDatas() {
      let data1 = { id: 3, name: "王五", age: 22 };
      let data2 = { id: 4, name: "秦六", age: 33 };
      let length = this.forList.unshift(data1, data2);
      console.log("头部追加后长度为:" + length);
    },
    // 头部删除数据
    shiftData() {
      let data = this.forList.shift();
      console.log("头部删除数据为:", data);
    },
    // 替换数据
    spliceDatas() {
      let data1 = { id: 3, name: "王五", age: 22 };
      let data2 = { id: 4, name: "秦六", age: 33 };
      let data = this.forList.splice(0, 1, data1, data2);
      console.log("替换前的数据为:", data);
    },
    // 正序
    sortAscDatas() {
      this.forList.sort((a, b) => a.id - b.id);
    },
    // 逆序
    sortDescDatas() {
      this.forList.sort((a, b) => b.id - a.id);
    },
    // 反转
    reverseDatas() {
      this.forList.reverse();
    },
    searchUserId() {
      console.log("检索userId");
    }
  }
};
</script>

main.js

import { createApp } from 'vue'

import App from './RootApp.vue'
import ChildApp2 from './components/ChildApp2.vue'

let name = "吕懿洋"
// createApp(组件,组件的props对象)
let app = createApp(App, { name: name })

// 貌似没有效果
app.config.errorHandler = (err) => {
    /* 处理错误 */
    console.log(err)
}

// 6.设置全局变量,可被任意组件调用
app.config.globalProperties.rootObj = {
    author: 'lvyy'
}

app.component("ChildApp2",ChildApp2)

app.mount('#app')

ChildApp.vue

<template>
  <div>
    <input v-model="data" />
    <button @click="childClick">子组件按钮</button>
  </div>
</template>

<script>
export default {
  name: "ChildApp",
  data() {
    return {
      data: ""
    };
  },
  methods: {
    childClick() {
      this.$emit("myEvent");
    }
  }
};
</script>

ChildApp1.vue

<template>
  <div>
    <input v-model="data" />
    <div :class="$attrs.class">{{id + "-" + name}}</div>
  </div>
</template>

<script>
export default {
  name: "ChildApp1",
  props: {
    id: String,
    name: String
  },
  data() {
    return {
      childName: "ChildApp1",
      childId: "ChildApp1",
      data: ""
    };
  },
  expose: ["childName"]
};
</script>

ChildApp2.vue

<template>
  <div>
    <input v-model="data" />
  </div>
</template>

<script>
export default {
  name: "ChildApp2",
  data() {
    return {
      childName: "ChildApp2",
      childId: "ChildApp2",
      data: ""
    };
  },
  expose: ["childName"]
};
</script>

其他注意事项

  1. 图片自行填充
  2. 需要执行命令 npm i lodash 再执行npm install ,最后 npm run dev,主要是为了下载防抖函数的插件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1296909.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全网最新最全【Kali Linux】 渗透测试技术大全

利矛出击&#xff1a;Kali Linux 渗透测试大法 在网络攻防实践中&#xff0c;系统的防御措施可以称为“盾”&#xff0c;而渗透测试则是“矛”。二者同等重要&#xff0c;因为“矛”可以试探出“盾”有多坚固&#xff0c;并且及时发现问题&#xff0c;修补漏洞&#xff0c;提升…

k8s之镜像拉取时使用secret

k8s之secret使用 一、说明二、secret使用2.1 secret类型2.2 创建secret2.3 配置secret 一、说明 从公司搭建的网站镜像仓库&#xff0c;使用k8s部署服务时拉取镜像失败&#xff0c;显示未授权&#xff1a; 需要在拉取镜像时添加认证信息. 关于secret信息,参考: https://www.…

[英语学习][10][Word Power Made Easy]的精读与翻译优化

[序言] 下面这段话, 译者翻译没有太大问题, 就是某些单词上, 跟他理解得不一样. 另外还有一个关键的定语从句, 我认为译者理解不到位, 导致翻译不够通顺. [英文学习的目标] 提升自身的英语水平, 对日后编程技能的提升有很大帮助. 希望大家这次能学到东西, 同时加入我的社区讨…

修改正点原子综合实验的NES模拟器按键控制加横屏

​​​​​​​ 开发板&#xff1a;stm32f407探索者开发板V2 屏幕是4.3寸-800-480-MCU屏 手头没有V3开发板&#xff0c;只有V2&#xff0c;所以没法测试 所以只讲修改哪里&#xff0c;请自行修改 先改手柄部分&#xff0c;把手柄改成按键 找到左边的nes文件夹中的nes_mai…

matlab RGB三元组和十六进制的转换

matlab画柱状图改颜色的时候&#xff0c;用三元组的形式&#xff0c;范围是[0&#xff0c;1] 我们获得了十六进制 到网站转换为[0,255] https://c.runoob.com/front-end/55/ 然后将得到的值/255 输入matlab就可以了

VS2009和VS2022的错误列表可复制粘贴为表格

在VS2019或VS2022中&#xff0c;可看到如下错误列表&#xff1a; 如果复制这两行错误信息&#xff1a; 然后把它粘贴到word文件&#xff0c;就可以看到以下表格&#xff1a; 严重性 代码 说明 项目 文件 行 禁止显示状态 错误(活动) E0020 未定义标识符 "dd"…

Docker 部署 2FAuth 服务

拉取最新版本的 2FAuth 镜像&#xff1a; $ sudo docker pull 2fauth/2fauth:latest在本地预先创建好 2fauth 目录, 用于映射 2FAuth 容器内的 /2fauth 目录。 使用以下命令, 在 前台 运行 2FAuth 容器: $ sudo docker run -it --rm --name 2fauth -p 10085:8000/tcp -v /ho…

从简单到入门,一文掌握jvm底层知识文集。

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

Qt基础-组件的添加、删除或更新

本文介绍如何在Qt中组件的添加、删除或更新。 概述 有时安装完qt后发现当前的组件需要进一步调整,这时就需要进一步操作安装的文件。 QT的组件管理软件并没有在开始菜单或者桌面添加快捷方式(5.9版本),也没有在代码编辑界面设置相关的选项,藏的比较深。 操作步骤 找到…

【工程实践】使用modelscope下载大模型文件

前言 Modelscope&#xff08;魔搭社区&#xff09;是阿里达摩院的一款开源模型平台&#xff0c;里面提供了很多的热门模型供使用体验&#xff0c;其中的模型文件可以通过git clone 快速下载。并且为模型提供了Notebook的快速开发体验&#xff0c;使用阿里云服务&#xff0c;不需…

arm平台编译so文件回顾

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、几个点二、回顾过程 1.上来就执行Makefile2.编译第三方开源库.a文件 2.1 build.sh脚本2.2 Makefile3.最终编译三、其它知识点总结 前言 提示&#xff1a;这…

一天一个设计模式---生成器模式

概念 生成器模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;用于构建复杂对象。它允许您逐步构造一个对象&#xff0c;将构建过程与最终对象的表示分离开来。这种模式通常用于创建复杂的对象&#xff0c;这些对象可能有多个部分组成&#xff0c…

MongoDB的插入文档、更新文档语句

本文主要介绍MongoDB的插入文档、更新文档语句。 目录 MongoDB插入文档MongoDB更新文档 MongoDB插入文档 在MongoDB中&#xff0c;可以通过使用insertOne或insertMany方法向集合中插入文档。 insertOne方法可以插入一个文档&#xff0c;例如&#xff1a; db.collection.inse…

条码生成器与Zint使用

文章目录 目的条形码zint支持条形码种类下载编译qt pro配置code保存条形码目的 1: 了解条形码数据理论知识 2: 了解zint第三方库相关, 如何编译引用到项目中 条形码 条形码(Barcode)一维码 和二维码(QR code)都是用于存储信息的图形化表示方式,通常应用于商品标识、库…

LeetCode(52)最小栈【栈】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 最小栈 1.题目 设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void…

【大数据分析】

系列文章目录 文章目录 系列文章目录前言一、数据分析框架二、数据分析方法1.数据清洗&数据探索2.数据清洗之异常值判别3.数据清洗之缺失值处理4.数据探索5.结构优化 三、大数据可视化1.大数据可视化概念1.1 定义1.2 数据可视化的意义 2.可视化类型和模型2.1 科学可视化2.2…

试块二维码制作

记号笔传统方式对试块进行标识&#xff0c;试块表面空间有限能记录的内容不多&#xff0c;标识错误&#xff0c;导致难以辨识&#xff0c;防止混淆和拿错&#xff01;通过个试块进行二维码标识&#xff0c;微信扫码即可了解试块详细信息、生产日期、浇筑部位、强度等信息&#…

【AIGC】大语言模型的采样策略--temperature、top-k、top-p等

总结如下&#xff1a; 图片链接 参考 LLM解码-采样策略串讲 LLM大模型解码生成方式总结 LLM探索&#xff1a;GPT类模型的几个常用参数 Top-k, Top-p, Temperature

python爬取robomaster论坛文章数据,携带登录信息

一. 内容简介 python爬取robomaster论坛文章数据。 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3代码 三.主要流程 3.1 接口分析&#xff0c;以及网页结构分析 # 这是文章链接,其实id就是文章的id # https://bbs.robomaster.com/forum.php?modview…

数据库对象介绍与实践:视图、函数、存储过程、触发器和物化视图

文章目录 一、视图&#xff08;View&#xff09;1、概念2、基本操作1&#xff09;创建视图2&#xff09;修改视图3&#xff09;删除视图4&#xff09;使用视图 3、使用场景4、实践 二、函数&#xff08;Function&#xff09;1、概念2、基本操作1&#xff09;创建函数2&#xff…