VUE基础

这篇博客详细介绍了Vue的基础知识,包括计算属性、内置指令、数组更新、表单与v-model的使用、组件化、props数据传递、slot内容分发以及自定义指令。重点讲解了计算属性的缓存特性和如何避免不必要的视图更新,以及组件间的通信和数据流动。此外,还提到了Render函数在Vue中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//HTML
<div id="app">
	<input type="text" v-model="name" placeholder="你的名字">
	<h1>你好,{{ name }}</h1>
</div>

<script>
	var abc= new Vue({
	//创建一个Vue对象,指定一个页面中已存在的DOM元素来挂载Vue实例,就是关联起来
		el: '#app',
		data:{
		//data中可以声明应用内需要双向绑定的数据,建议所有会用到的数据都预先在data内声明
			name: ''
		}
	})
</script>

//关联成功后可以通过abc.$el来访问该元素,Vue提供了很多常用的实例属性与方法,都以$开头
//v-model="它的值对应于我们创建的Vue实例的data选项中的字段名,这样就进行了数据绑定"
//v-html 输出HTML,而不是将数据解释后的纯文本,如果将用户产生的内容使用v-html输出后,有可能 
//导致XSS攻击,所以要在服务端对用户端对用户提交的内容进行处理,一般可将"<>"转义
//如果想显示{{}}而不进行替换,使用v-pre

v-bind 动态更新HTML元素上的属性 v-bind:href="" v-bind:src=""
v-on 绑定事件监听器,v-on:click=“写在Vue实例的methods属性内的方法名,函数内的
this指向的是当前Vue实例本身,因此可以直接使用this.xxx的形式来访问或修改数据”

v-bind 可以直接写成 :
v-on 可以写成 @

计算属性 computed

遇到复杂的逻辑时应用使用计算属性,说白了就是放到方法里
所有的计算属性都以函数的形式写在Vue实例内的computed选项内,最终返回计算后的结果
在一个计算属性里可以完成各种复杂的逻辑,包括运算、函数调用等,只要最终返回一个结果就可以
写在methods中也可以实现,为什么要写在computed中呢?
因为计算属性是基于它的依赖缓存的。一个计算属性所依赖的数据发生变化时,它才会重新取值,所以text只要不改变,计算属性也就不更新。但是methods不同,只要重新渲染,它就会被调用,因此函数也会被执行

内置指令

v-once 一个不需要表达式的指令,作用是定义它的元素或组件只渲染一次,包括元素或组件的所有子节点。首次渲染后,不再随数据的变化重新渲染,将被视为静态内容
<div id="app">
	<span v-once>{{message}}</div>
</div>
v-once在业务中也很少使用,当你需要进一步优化性能时,可能会用到

<p v-if="status===1"></p>
<p v-else-if="status===2"></p>
<p v-else></p>

v-show的用法与v-if基本一致,只不过v-show改变元素的CSS属性display,当表达式的值为false时,元素会隐藏

列表渲染指令v-for
当需要将一个数组遍历或枚举一个对象循环显示时,就会用到列表渲染指令v-for
<div id="app">
	<ul>
		<li v-for = "book in books">{{ book.name }} </li>
	</ul>
</div>
<script>
	var app = new Vue({
		el : '#app',
		data:{
			books:[
				{name: '三国演义'},
				{name: '水浒传'},
				{name: '红楼梦'},
			]
		}
	})
</script>

数组更新

Vue的核心是数据与视图的双向绑定,Vue包含了一组观察数组变异的方法,使用它们改变数组也会触发视力更新
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
在以下变动的数组中,Vue是不能检测到的,也不会触发视图更新
通过索引直接设置项,比如app.books[3]={}
修改数组长度,比如app.books.length = 1
解决第一个问题可以用两种方法实现同样的效果
1.使用Vue内置的set方法
Vue.set(app.books,3,{
name: ‘《CSS揭秘》’,
author: ‘’
});
如果是在webpack中使用组件化的方式,默认是没能导入Vue的,可以使用
this.$set({
});
2. app.books.splice(3,1,{});
第二个问题也可以直接用splice来解决
app.books.splice(1);

过滤与排序
当你不想改变原数组,想通过一个数组的副本来做过滤或排序的显示时,可以使用计算属性来返回过滤或排序后的数组

表单与v-model

使用v-model时,如果是用中文输入法输入中文,一般在没有选定词组前,也就是在拼音阶段,Vue是不会更新数据的,当敲下汉字时才会触发更新,如果希望总是实时更新,可以用@import来替代v-model
事实上,v-model也是一个特殊的语法糖,只不过它会在不同的表单上智能处理
单选按钮radio
单选按钮在单独使用时,不需要v-model,直接使用v-bind绑定一个布尔类型的值,为真时选中,为否是不选

<div id="app">
	<input type="radio" :checked="picked">
	<label>单选按钮</label>
</div>
<script>
	var app = new Vue({
		el:'#app',
		data:{
			picked:true
		}
	})
</script>

复选框checkbox
<input type="checkbox" :checked="picked">
选择列表
//给select 添加属性multiple就可以多选了,此时v-model绑定的是一个数组
<select v-model="">
	<option value="">
		option是备选项,如果含有value属性,v-model就会优先匹配value的值
	</option>
</select>

组件Component

Vue.js最核心的功能,也是整个框架设计最精彩的地方
Vue.js的组件就是提高重用性的,让代码可复用
组件需要注册后才可以使用,注册有全局注册和局部注册两种方式
全局注册后,任何Vue实例都可以使用
Vue.component(‘my-component’,{
//选项
})
组件自定义标签名称,推荐使用小写加减号分割的形式命名
要在new Vue实例中使用组件,必须要在实例创建前注册

局部注册组件 components 可以局部注册组件
组件中也可以使用components来注册组件,使组件可以嵌套

<div id="app">
	<my-component></my-component>
</div>
<script>
	var Child={
		template: '<div>局部注册组件的内容</div>'
	}
	var app = new Vue({
		el: '#app'
		components:{
			'my-component':Child
		}
	})
</script>

Vue组件的模板在某些情况下会受到HTML的限制,比如<table>内规定只允许是<tr><td><th>
等这些表格元素,这种情况下,可以使用特殊的is属性来挂载组件
如果使用的是字符串模板,是不受限制的,比如后面章节介绍的.vue单文件用法等

除了template选项外,组件中还可以像Vue实例那样使用其他的选项,比如
data、computed、methods等,但是在使用data时,data必须是函数,然后将数据return出去

<div id = "app">
	<my-component></my-component>
</div>
<script>
	Vue.component('my-component',{
		template:'<div>{{message}}</div>',
		data:function(){
			return {
				message:'组件内容'
			}
		}
	});
	var app = new Vue({
		el:'#app'
	})
</script>

使用props传递数据

组件不仅仅是要把模板的内容进行复用,更重要的是组件间要进行通信。
通常父组件的模板中包含子组件,父组件要正向地向子组件传递数据或参数,子组件接收到后根据参数的不同来渲染不同的内容或执行操作,这个下身传递数据的过程就是通过prop来实现的
在组件中,使用选项props来声明需要从父级接收的数据,props的值可以是两种,一种是字符串数组,一种是对象

<div id="app">
	<my-component abc="数据"></my-component>
</div>
<scrtip>
	Vue.component('my-component',{
		//接收来自父组件的数据
		props:['abc'],
		//使用接收到的数据abc
		template:'<div>{{abc}}</div>'
	});
	var app = new Vue({
		el:'#app'
	})
</script>

props中声明的数据与组件data函数return的数据主要区别是props来自父级
data中的是组件自己的数据,作用域是组件本身
这两种数据都可以在模板template计算属性computed方法methods中使用

由于HTML特性不区分大小写,当使用DOM模板时,驼峰命名的props名称要转为短横分隔命名,例如
appName app-name
有时候,传送的数据并不是直接写死的,而是来自父级的动态数据,这时可以使用指令v-bind来动态绑定props的值

当父组件的数据变化时,也会传递给子组件
<div id="app">
	<input type="text" v-model="parentMessage">
	//使用指令v-bind来动态绑定props的值
	<my-component :message="parentMessage"></my-component>
</div>
<script>
	Vue.component('my-component',{
		props:['message'],
		template:'<div>{{message}}</div>'
	});
	var app = new Vue({
		el:'#app',
		data:{
			parentMessage:''
		}
	})
</script>

组件关系可分为父子组件通信,兄弟组件通信、跨级组件通信
子组件用$emit()来触发事件,父组件用 $on()来监听子组件的事件

父组件也可以直接在子组件的自定义标签上使用v-on来监听子组件触发的自定义事件
<div id="app">
	<p>总数:{{total}}</p>
	<my-component
			@increase="handleGetTotal"
			@reduce="handleGetTotal">
	</my-component>
</div>
<script>
	Vue.component('my-component',{
	//template是展示到html页面上的
		template:'\
		<div>\
			<button @click="handleIncrease">+1</button>\
			<button @click="handleReduce">-1</button>\
		</div>',
		data:function(){
			return {
				counter:0
			}
		},
		methods:{
			handleIncrease:function(){
				this.counter++;
				//改变组件的counter后通过$emit()再把它传递给父组件
				//第一个参数是自定义事件的名称,后面是要传递的数据
				this.$emit('increase',this.counter);
			},
			handleReduce:function(){
				this.counter--;
				this.$emit('reduce',this.counter);
			}
		}
	});
var app = new Vue({
	el:'#app',
	data:{
		total:0
	},
	methods:{
		handleGetTotal:function(total){
			this.total = total;
		}
	}
})
</script>

非父子组件通信
使用一个空的Vue实例作为中央事件总线(bus)

<div id="app">
	{{message}}
	<component-a></component-a>
</div>
<script>
	var bus = new Vue();
	Vue.component('component-a',{
		template:'<button @click="handleEvent">传递事件</button>',
		methods:{
			handleEvent:function(){
			//点击按钮会通过 bus把事件on-message发出去,此时app就会接收到来自bus的事件
			//进而在回调里完成自己的业务逻辑
				bus.$emit('on-message','来自组件component-a的内容');
			}
		}
	});
	var app = new Vue({
		el:'#app',
		data:{
			message:''
		},
		mounted:function(){
			var _this = this;
			//在app实例初始化时,监听来自bus实例的事件
			bus.$on('on-message',function (msg){
				_this.message = msg;
			});
		}

	})
</script>

使用slot分发内容

当需要让组件组合使用,混合父组件的内容与子组件的模板时,就会用到slot
这个过程叫作内容分发(transclusion)
props传递数据,events触发事件和slot内容分发构成了Vue组件的3个API来源,再复杂的组件也是由这3个部分构成的

编译的作用域
比如父组件中有如下模板
<child-component>
	{{message}}
</child-component>
这里的message就是一个slot,但是它绑定的是父组件的数据,而不是组件<child-commponent>的数据
父组件模板的内容是在父组件作用域内编译
子组件模板的内容是在子组件作用域内编译


<div id = "app">
	<child-component v-show="showChild"></child-component>
</div>
<script>
	Vue.componetn('child-component',{
		template:'<div>子组件</div>'
	});
	var app = new Vue({
		el:"#app",
		data:{
			showChild:true
		}
	})
</script>
//以上的showChild绑定的是父组件的数据,因为访问的是外层app里的showChild
如果想在子组件上绑定,那应该是
<div id="app">
	<child-component></child-component>
</div>
<script>
	Vue.component('child-component',{
		template:'<div v-show="showChild">子组件</div>',
		data:function(){
			return {
				showChild:true
			}
		}
	});
	var app = new Vue({
		el:'#app'
	})
</script>
slot颁发内容,作用域是在父组件上的

slot用法
单个slot

在子组件内使用特殊的<slot>元素就可以为这个子组件开启一个slot(插槽),在父组件模板里,
插入在子组件标签内的所有内容将替代子组件的<slot>标签及它的内容
<div id="app">
	<child-component>
		<p>分发的内容</p>
		<p>更多分发的内容</p>
	</child-component>
</div>

<script>
	Vue.component('child-component',{
		template:'\
		<div>\
			<slot>\
				<p>如果父组件没有插入内容(没有使用slot时),我将作为默认出现</p>\
				</slot>\
			</div>'
	});
	var app = new Vue({
		el:'#app'
	})
</script>

自定义指令

自定义指令的注册方法和组件很像,也分全局注册和局部注册

//全局注册 	directive
Vue.directive('focus',{
	//指令选项
	bind:只调用一次,指令第一次绑定到元素时使用
	inserted:被绑定元素插入父节点时调用
	update:被绑定元素所在的模板更新时调用
	componentUpdated:被绑定元素所在模板完成一次更新周期时调用
	unbind:只调用一次,指令与元素解绑时调用
});
//局部注册 	directives
var app = new Vue({
	el:'#app',
	directives:{
		focus:{
			//指令选项
		}
	}
})

Render函数

正常的DOM节点在HTML中是这样的:
<div id="main">
	<p>文本内容</p>
	<p>文本内容</p>
</div>

用Virtual Dom创建的JavaScript对象一般会是这样的
var vNode={
	tag:'div',
	attributes:{
		id:'main'
	},
	children:{
		//p节点
	}
}
在Vue.js 2中,Virtual Dom就是通过一种VNode类表达的,每个DOM元素或组件都对应一个VNode对象
在Vue.js源码中是这样定义的
export interface VNode{
	tag?:string;//当前节点的标签名
	data?:VNodeData;//当前节点的数据对象
	children?:VNode[];//子节点,数组,也是VNode类型
	text?:string;//当前节点的文本,一般文本节点或注释节点会有该属性
	elm?:Node;//当前虚拟节点对应的真实的DOM节点
	ns?:string;//节点的namespace
	context?:Vue;//编译作用域
	key?:string|number;//节点的key属性,用于作为节点的标识,有利于patch优化
	componentOptions?:VNodeComponentOptions;//创建组件实例时会用到的选项信息
	componentInstance?:Vue;//
	parent?:VNode;//组件的点位节点
	raw?:boolean;//原始html
	isStatic?:boolean;//静态节点的标识
	isRootInsert:boolean;//是否作为根节点插入,被<transition>包裹的节点,该属性的值为false
	isComment:boolean;//当前节点是否是注释节点
}
VNodeData代码如下
export interface VNodeData{
	key?:string|number;
	slot?:string;
	scopedSlots?:{[key:string]:ScopedSlot};
	ref?:string;
	tag?:string;
	staticClass?:string;
	class?:any;
	staticStyle?:{[key:string]:any};
	style?:Object[]|Object;
	props?:{[key:string]:any};
	attrs?:{[key:String]:any};
	domProps?:{[key:string]:any};
	hook?:{[key:string]:Function};
	on?:{[key:string]:Function|Function[]};
	nativeOn?:{[key:string]:Function|function[]};
	transition?:Object;
	show?:boolean;
	inlineTemplate?:{
		render:Function;
		staticRenderFns:Function[];
	};
	directives?:VNodeDirective[];
	keepAlive:boolean;
}

VNode主要可以分为如下几类
TextVNode文本节点
ElementVNode普通元素节点
ComponentVNode组件节点
EmptyVNode没有内容的注释节点
CloneVNode克隆节点,可以是以上任意类型的节点,isCloned自发性为true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值