理解Vue
介绍
Vue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的渐进式框架。他的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。只关注视图层,它不仅易于上手,还便于与第三方库或项目整合。
初识
在此涉及到的一些知识点后面会解释
首先我们先简单看看如何使用vue
- 创建一个 .html 文件,引入vue,就像引入普通的js文件一样
<!-- 格式 -->
<script src="vue.js文件所在位置"></script>
<!-- 三种方式进行引入 -->
<!-- cdn引入 网络不好的话不推荐-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- npm引入 这种方式需要基于node.js的npm npm:就是node.js的包管理器 -->
<!-- npm install vue -->
<!-- 把vue.js文件下载到本地进行引用 -->
<!-- 访问此链接保存此文件即可
https://cdn.jsdelivr.net/npm/vue/dist/vue.js
-->
- 在html中写入代码
<body>
<div id="app">
<h1>{
{message}}</h1>
</div>
</body>
- 在js中的代码
const vm = new Vue({
el:"#app",
data:function(){
return{
//在此定义数据
message:"Hello Vue!",
}
}
});
- 得出结果
注:篇幅有点长,但请耐心看完,肯定会有很大的收获!!!
Mustache语法{ {}}
也叫差值表达式用于构造html页面内容。在实际工作中,当同一个模板中想要调用不同的函数来渲染画面,在已经自定义好了的前提下,可以在渲染页面时对传入的参数进行手动判断。
支持变量和表达式
在vue中使用:{ {message}}
message 相当于在js中定义的变量,然后在此语法中进行使用,显示的是对应的值。
MVVM
mvvm(Model View ViewModel)译为模型、视图、视图模型,其实就是一种软件架构模式。
- View层
即视图层,在我们前端开发中,通常就是DOM层,主要作用是给用户展示各种信息 - Model层
即数据层,数据可能是我们固定的死数据,更多的是来自我们服务器,从网络上请求下来的数据 - ViewModel层
即视图模型层,是View和Model沟通的桥梁,一方面它实现了Data Binging,也就是数据绑定,将Model的改变实时的反应到View中,另一方面他实现了DOM Listener,也就是DOM监听,当DOM发生一些事件(点击、滚动等)时,可以监听到,并在需要的情况下改变对应的Data
这里是简单的理解一下,以后长时间的使用这种模式,自然就会理解
指令
就是vue事先定义好的,就是对dom元素下命令,拿来用即可
注意:指令都是以 v-** 格式开头
<body>
<div v-**=""></div>
</body>
-
v-text
相当于原生js中的innerText属性,将指定的值填充到标签中,会覆盖之前的内容 -
v-html
相当于原生js中的innerHTML属性,将指定的值填充到标签中,会覆盖之前的内容,可解析标签元素 -
v-show
根据条件是真或假,来判断元素是否显示,改变的是css样式display:none|block,它只支持布尔值 -
v-if
根据条件是真或者假,来判断元素是否被渲染,true可以看到这个节点,false该节点不存在,通常与一下两个指令相配合使用(理解成原生js的if…else if…else即可)- v-else-if
当v-if条件不成立此指令接着执行判断 - v-else
当v-if和v-else-if都不成立执行此指令
- v-else-if
-
v-for
可对一组数据进行迭代输出(可迭代的数据都有下标)<!-- html代码 --> <div id="app"> <h1 v-for="item in datas">{ {item}}</h1> </div>
// js代码 <script src="js/vue.js"></script> <script type="text/javascript"> new Vue({ el:"#app", data(){ return{ // 存放一个数组,可根据名字访问值 datas:["数据1","数据2","数据3"], } } }) </script>
解析运行结果:
以下是各种值的使用:- 值是数组
<p v-for="(item,index) in list" :key="index">--索引值--{ {i}} --每一项--{ {item}}</p>
- 值是数组对象
<p v-for="(user,index) in listObj" :key="index">--id--{ {user.id}} --姓名--{ {user.name}}</p> <!-- user 是迭代出的每个对象 user.id 则是获取这个对象里的属性 -->
- 值是对象
<p v-for="(val,key,index) in user" :key="index">--键是--{ {key}}--值是--{ {val}}</p> <!-- 与其它迭代不一样的是,迭代对象可以得出三个 val 对象里属性对应的值 key 对象里的属性也称为键 index 就是下标了 -->
- 值是数值
<p v-for="(count,index) in 10" :key="index">这是第{ {count}}次循环</p> <!-- 注意:如果使用v-for迭代数字的话,前面 count 的值从 1 开始 -->
- 值是字符串
<p v-for="(value,index) in str" :key="index">{ {value}}---{ {index}}</p>
注意:当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key(即上面代码里的 :key=“index”) -
v-on
用于为一个元素绑定事件 可简写为“@”(原生js的所有事件去掉on都可以使用)
格式:v-on:click=”方法名”,
简写:@click=”方法名”,
还有一些常用的修饰符:
.stop 相当于调用event.stopPropagation()
.prevent 相当于调用event.preventDefault()
.self 当子元素身上事件触发后向外扩散,扩散到带有self修饰符的事件不会触发本事件
.once 表示只触发一次此事件的回调 -
v-bind
用于为元素动态绑定属性 可简写为“:” (绑定的属性它的值是动态变化的)
被绑定的属性它的值支持javascript表达式
格式: v-bind:属性名=”属性值”,
简写: :属性名=”属性值”,
其中在绑定 class 或 style attribute(属性) 时,支持其它类型的值,如数组或对象。- 绑定class属性
参数可支持对象,数组
有几点要注意下:
1、 普通class属性可以添加多个值,但被绑定以后只能添加一个,可通过数组形式添加多个
2、 参数是对象,对象中属性值是一个布尔值,表示是否留下该类名,true会把属性留下,false则相反 - 绑定style属性
参数可支持对象,数组
注意:
1、CSS属性名必须用驼峰命名法(官方文档写的是既可以用驼峰也可以用 短横分隔命名法),但是用短横分隔是会报错的
2、当用对象时属性名必须是css样式的驼峰命名,值则是在vue实例中定义的样式
- 绑定class属性
-
v-model
实现了双向绑定,可以获取表单中输入的value值注意:只限制在<input>、<select>、<textarea>、components中使用
常用的修饰符
.lazy 相当于原生js的onchange事件
.number 输入字符串转为有效的数字
.trim 输入首尾空格过滤 -
v-slot
是一个插槽,可以理解为,可以在引用组件标签中写入内容放在指定的位置。它是为了让我们封装的组件更具有扩展性,决定了组件内部的一些内容到底展示什么。
注意:这在组件(component),中使用,可以先跳过,看完组件在回来看
相当于占位符在组件引用的模板中定义< slot> 标签进行占位,当引用该组件时,在自定义组件标签中添加内容就可以进行填充slot标签
具名插槽,如果想把内容放在指定位置的插槽内,必须在模板中的插槽标签中添加name属性做个标记,然后使用时在需要显示的标签上添加 slot=”name名”
插槽使用例图:
-
v-pre
表示该元素差值表达式不会进行编译,“{ {}}”这里面的东西不会进行编译,原样输出(直接放入标签中即可,不用给值) -
v-cloak
可以使用 v-cloak 指令设置样式,这些样式会在 Vue 实例编译结束时,从绑定的 HTML 元素上被移除。
当网络较慢,网页还在加载 Vue.js ,而导致 Vue 来不及渲染,这时页面就会显示出 Vue 源代码。我们可以使用 v-cloak 指令来解决这一问题。还可以解决闪烁问题:
当数据量大时未及时渲染会出现闪烁问题,使用属性选择器[v-cloak]{display:none};(直接放入标签中即可,不用给值) -
v-once
表示该元素只执行一次,当数据再次渲染值不会改变(直接放入标签中即可,不用给值)
自定义指令
自定义指令有全局和局部之分
首先明白两个概念:
全局指令:写在vue实例外面,每个vue实例挂载元素的范围内都可以使用。
例子:
// xxx这里表示定义的指令名称,就比如 v-model、v-on、v-bind,就是v-**这里定义完使用时必须加前缀"v-**"
Vue.directive('xxx', {
//这里是钩子函数,下面会仔细讲解
inserted: function (el) {
//配置指令
}
});
局部指令:写在vue实例里面,只能指定一个vue实例挂载元素的范围内都可以使用。
var app = new Vue({
el: '#app', //挂载元素
//vue实例里面的所有属性都是固定写法
directives: {
// 与全局指令中的xxx一样,也是表示指令的名字
xxx: {
//添加钩子函数进行配置
}
}
});
扩展———钩子函数
钩子函数是个函数,是在一个事件触发的时候,在系统级捕获到了他,然后做一些操作。一段用以处理系统消息的程序。
“钩子”就是在某个阶段给你一个做某些处理的机会。
简单理解,就是不用自己去做,而是有东西帮你做了,就是在相应的时机下帮你处理了一些功能
自定义指令中的钩子函数:
使用格式
//-------全局自定义指令下-------
Vue.directive("test",{
bind:function(){
//逻辑
},
//es6写法
inserted(){
//逻辑
}
});
//-------局部自定义指令下-------
new Vue({
el:"#app",
data(){
return{
}
},
directives:{
//双引号可省略
"test":{
bind:function(){
//逻辑
},
//es6写法
inserted(){
//逻辑
}
}
}
});
函数 | 解析 |
---|---|
bind | 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置 |
inserted | 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 |
update | 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode(虚拟 DOM 的 JavaScript 对象) 更新之前。指令的值可能发生了改变,也可能没有。 |
componentUpdated | 指令所在组件的 VNode 及其子 VNode 全部更新后调用。 |
unbind | 只调用一次,指令与元素解绑时调用。 |
钩子函数中的参数
参数 | 解析 |
---|---|
el | 指令所绑定的元素,可以用来直接操作 DOM。 |
binding | 该参数是一个对象,在下面进行解析该对象中的相关属性 |
vnode | Vue 编译生成的虚拟节点。详解 |
oldVnode | 上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。 |
参数binding对象的属性
属性 | 解析 |
---|---|
name | 获取指令名,不包括 v- 前缀。 |
value | 指令的绑定值,例如:v-my-directive=“1 + 1” 中,绑定值为 2 |
oldValue | 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。 |
expression | 字符串形式的指令表达式。例如 v-my-directive=“1 + 1” 中,表达式为 “1 + 1”。 |
arg | 传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 “foo”。 |
modifiers | 一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。 |
问题:bind和inserted的区别?
共同点:
dom插入都会调用,bind在inserted之前
不同点:
bind 执行时父节点为 null
inserted 执行时父节点存在。
bind是在dom树绘制前调用,inserted在dom树绘制后调用
计算属性和侦听器
计算属性
简单理解,就是用来存放一些复杂的处理逻辑,一般就是用来通过其他的数据算出一个新数据。
特点:具有缓存能力,当计算属性中引用的值未发生改变时就一直会拿缓存中的数据不会再次执行,提高了运行的速度。
计算属性格式:
new Vue({
el:"#app", //挂载元素,指定该vue对象渲染的范围
data(){
return{
}}, // 定义渲染需要用到的数据
methods:{
}, // 渲染时调用的普通方法
directives:{
}, // 用于设置自定义局部指令
//--------
computed:{
// 使用计算属性,存放复杂逻辑需要计算的方法
// 定义方法
// 注意:方法中必须有return,调用时不加括号
},
})
我理解的计算属性本质:
之所以叫计算属性,其实它本身就是一个属性,调用时不要带“()”,可能到这里有点蒙(是不是想着跟methods中定义的方法一样,确实一样,但是它不是方法就是属性),它的本质就是,一个对象中的属性只不过这个属性有getter和setter方法,一般不会对它进行设置值,只是用来计算获取值,而进行了简写
之所以调用时不用括号的原因也就是这样
侦听器
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。
当你需要在数据变化响应时,执行异步操作,或高性能消耗的操作,那么watch为最佳选择。
(简单理解,就是就是看着一个东西它有没有改变,改变就执行对应的方法,没有就不执行)
侦听器格式
new Vue({
el:"#app", //挂载元素,指定该vue对象渲染的范围
data(){
return{
}}, // 定义渲染需要用到的数据
methods:{
}, // 渲染时调用的普通方法
directives:{
}, // 用于设置自定义局部指令
computed:{
}// 使用计算属性,存放复杂逻辑需要计算的方法
//--------
watch:{
监听的谁:function(newValue,oldValue){
}
// newValue 修改后的内容
//oldValue 修改前的内容
}
})
相关面试题
计算属性中computed的方法和methods中的方法的区别?
- 计算属性中的方法调用时不用加括号,而methods的方法调用时需要加
- 计算属性的每一个计算方法都会被缓存起来,只有当计算属性里面依赖的一个或多个属性变化了,才会重新计算当前计算属性的值。而相反methods中的方法不会缓存。
过滤器
这个其实很好理解(其实就是例如当一个人进入了一家店,然后换了身衣服出来了,俗话说人衬衣服,马衬鞍,然后出来就变了个样)
过滤器有全局过滤器和局部过滤器之分,常用于格式化文本数据
过滤器可以用在两个地方:
- 双花括号插值
<!-- 在双花括号中使用 -->
{
{ message | filterName}}
- v-bind表达式中。
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | filterName"></div>
全局过滤器
在所有vue对象中挂载的范围内都可以使用
格式:
Vue.filter( filterName,function(msg){
//msg传入的需要过滤的值
return // 数据处理结果 返回出去
});
/*
在模板中使用过滤器时,过滤器会把“|”这个符号前面的值作为第一个参数,传到过滤器中进行相关的操作。
也可指定给过滤器进行传值,例如:
{
{ message | filterName('张三',20)}}
Vue.filter( filterName,function(msg,name,age){
// msg 就是上面这个“|”左边的值
// name,age就是分别对应上面指定传入的值(张三,20)
return msg+ name + age;
});
*/
举例:
<div id="app">
<h3>{
{viewContent | addNamePrefix}}</h3>
</div>
<script>
Vue.filter("addNamePrefix",(value)=>{
return "my name is" + value
})
let vm = new Vue({
el:"#app",
data(){
return{
viewContent:"张三" ,
}
}
})
</script>
局部过滤器
定义在每个vue实例中,只有在定义过滤器所在的vue对象挂载的范围内可以使用
(其实都差不多,就是位置上有点不一样,功能都是一样的)
举例:
<div id="app">
<p>电脑价格:{
{price | addPriceIcon}}</p>
</div>
<script>
let vm = new Vue({
el:"#app",
data:{
price:200
},
filters:{
//处理函数
addPriceIcon(value){
console.log(value)//200
return '¥' + value
}
}
})
</script>
组件(component)
其实组件是vue强大功能之一,可以认为它是对html标签进行扩展,可以把相同的代码进行复用。
注意:因为组件是可复用的vue实例所以它们与new Vue实例接收相同的选项(可以认为vue实例自身就是一个组件)
组件也有全局组件和局部组件之分
- 全局组件
然后在vue实例挂载的元素中使用//格式---- Vue.component("组件名",{ //这里面也可以使用vue实例里面的选项,比如像methods、computed、watch、filters、... data(){ return{ //要渲染的内容` } }, template:"<div>这是一个自定义组件</div>" ,//模板内容 /* template 可以指定显示的html标签,就是模板用于显示到页面上 */ });
<div id=”app”> <组件名></组件名> <div>
- 局部组件
局部组件使用时与全局组件使用方法一致new Vue({ el:”#app”, //挂载元素,指定该vue对象渲染的范围 data(){ return{ }}, // 定义渲染需要用到的数据 methods:{ }, // 渲染时调用的普通方法 directives:{ }, // 用于设置自定义局部指令 computed:{ },// 使用计算属性,存放复杂逻辑需要计算的方法 watch:{ },//用于监听一个数据的变化 //-------- // 在vue实例中加入 components:{ // 注册组件 //组件名 "组件名":{ // 引用模板(外部使用template标签创建模板) //可指定选择器,也可在这里直接写 template:"[selector|直接写入]", //局部组件上面的选项在这里面也可以使用 } }, })
组件复用
可以将组件进行任意次数的复用
<div id=”app”>
<组件名></组件名>
<组件名></组件名>
<组件名></组件名>
…
<div>
注意事项
a. 组件的模板中的html元素必须有一个元素包裹着
b. 组件中的data必须是一个函数
c. 组件如果是驼峰命名,使用时必须把大写换成-小写
实例
html代码
<div id="app">
<my-div></my-div>
</div>
<!-- 组件需要的模板定义在外面 -->
<template id="test">
<!-- 注意:模板中的元素必须都包裹在一个块元素中 -->
<div>
<h2>组件模板结构---{
{message}}</h2>
<ul>
<li>列表1</li>
<li>列表2</li>
<li>列表3</li>
<li>列表4</li>
</ul>
<button @click="changeMsg">修改msg</button>
</div>
</template>
js代码
<script>
new Vue({
el:"#app", //挂载元素
data(){
//存放渲染需要的数据
return{
}
},
// 注册局部组件
components:{
"my-div":{
// 引用模板
template:"#test",
data(){
return{
message:"hello component"
}
},
methods:{
changeMsg:function(){
this.message = "修改后的msg";
console.log(this);//VueComponent
}
},
},
},
});
</script>
运行结果
组件之间的通信
父组件传递子组件
父组件可以使用 props 把数据传给子组件。
步骤
a. 在父组件中引用子组件时,在子组件标签上绑定自定义属性,其属性值是父组件里的data方法中的数据,如 :自定义名=“父组件值”(注:自定义属性名不能有大写字母)
b. 在子组件中通过props接收绑定的属性,如果有多个可用 对象或数组进行存储
// 注册全局组件
Vue.component("hg-son",{
/*
props 可以是数组或对象,用于接收来自父组件的数据。
props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义校验和设置默认值
*/
// 使用props进行接收
props:["msg","num"], //接收在组件上绑定的属性,数组形式
// 引入模板
template:"#hg"
})
c. 然后在子组件的模板中使用props中的值,即可
附:草图
子组件传递父组件
子组件可以使用 $ emit() 触发父组件的自定义事件。
a. 在子组件中广播数据 使用$emit(“自定义事件”,数据) 创建事件并携带子组件中的数据
b. 然后在引入的子组件标签上绑定此自定义的事件,此事件的值是父组件的方法,该自定义事件所携带的数据可以通过父组件方法中data参数接收
html
<!--子组件传值给父组件
1. 在子组件中中广播数据
2. 在使用子组件的时候绑定自定义事件
-->
<!-- 定义模板 -->
<template id="hg">
<div>
<h2>子组件向父组件传递值</h2>
<ul>
<li>子组件列表</li>
</ul>
<button @click="sendParentfn">子组件中的按钮-给父组件传值</button>
</div>
</template>
<!-- 父组件 -->
<div id="app">
<p>接收子组件传递的数据:{
{parentMsg}}</p>
<!-- 2. 引用子组件
使用自定义事件,值是想要把数据传入到哪
-->
<hg-son @toparent="getSonfn"></hg-son>
</div>
js
<script>
// 注册全局组件
Vue.component("hg-son",{
//引用模板
template:"#hg",
data(){
return{
sonMessage:"msg是子组件中的数据"
}
},
methods:{
sendParentfn(){
console.log(this);
//1. 使用$emit广播数据,toparent是一个自定义事件
// sendParentfn事件被触发后该自定义事件自动触发
this.$emit("toparent",this.sonMessage);
}
}
});
// 创建vue实例
new Vue({
el:"#app",
data(){
return{
parentMsg:"msg是父组件中的数据"
}
},
methods:{
// 自定义事件会将值传入此方法的data中
getSonfn(data){
this.parentMsg = data;
}
}
});
</script>
结合代码,多读几遍步骤,仔细理解
生命周期(钩子函数)
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数。(这里只说下常用的)
使用格式:
new Vue({
el:"#app", //挂载元素,指定该vue对象渲染的范围
data(){
return{
}}, // 定义渲染需要用到的数据
methods:{
}, // 渲染时调用的普通方法
directives:{
}, // 用于设置自定义局部指令
computed:{
}// 使用计算属性,存放复杂逻辑需要计算的方法
watch:{
} // 监听vue实例中数据的变化
//--------
//普通写法
beforeCreate:function(){
//...
},
//ES6写法
beforeCreate(){
//...
},
//...
})
- beforeCreate()
此生命周期是vue实例创建之前自动调用,因为实例还没有创建,相关数据都还没有初始化,所以不能在此生命周期中进行数据的修改和使用。 - created()
此生命周期是vue实例创建完成后自动调用,此时实例中数据已经初始化完毕,可以对数据进行修改和使用。但是实例还没有进行挂载元素 - beforeMount()
此生命周期是vue实例编译之前自动调用,此时实例中元素已经挂载,但还没有渲染到页面上 - mounted()
此生命周期是vue实例编译完成后自动调用,vue实例的所有工作都已完成,在此可以进行dom的操作和ajax的请求。但mounted不会保证所有的子组件也都一起被挂载,如果希望全部被挂载后操作,可以在该生命周期中使用实例的vm.$nextTick()
格式:mounted: function () { this.$nextTick(function () { //在此处操作 }) }
- beforeUpdate()
此生命周期是vue实例的模板更新之前自动调用,第一次的渲染不会调用,只有渲染完等模板上的内容再次修改之后进行调用。
这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。 - updated()
此生命周期是vue实例的模板更新完成后自动调用。 - beforeDestroy()
此生命周期是vue实例销毁之前自动调用。在这一步。实例还可以使用。 - destroyed()
此生命周期是vue实例销毁完成后自动调用。对应vue实例的所有指令都会被解绑。所有事件都会被移除。所有子实例都会被销毁。
可以使用**vm.$destroy()**手动销毁
到这里就结束了,后续还会更新vue全家桶相关,还请持续关注!
感谢阅读,若有错误可以在下方评论区留言哦!!!
来源:oschina
链接:https://my.oschina.net/u/4419414/blog/4869347