VUE官方文档
VUE API
vue-cli
VSCode必备插件
- Vue VSCode Snippets:自动生成vue模板;
(1)vbase:vue基本骨架
(2)vfor:fot模板
(3)vwatch:watchi模板
(4)… - Vuter:语法高亮、代码补全等;
vue实例对象的参数
- el:表示,当前我们 new 的这个 Vue 实例,要控制页面上的哪个区域
- data:存放的是 el 中要用到的数据
- methods:定义了当前Vue实例所有可用的方法
注意:在 VM实例中,如果想要获取 data 上的数据,或者想要调用 methods 中的 方法,必须通过 this.数据属性名 或 this.方法名来进行访问,这里的this,就表示 我们 new 出来的VM 实例对象
VM实例,会监听自己身上 data 中所有数据的改变,只要数据一发生变化,就会自动把 最新的数据,从data 上同步到页面中去;【好处:程序员只需要关心数据,不需要考虑如何重新渲染DOM页面】 - filters过滤器:过滤器需要有过滤器名称和处理函数
过滤器调用的时候,采用的是就近原则,如果私有过滤器和全局过滤器名称一致了,这时候 优先调用私有过滤器 - directives:自定义私有指令
- watch:监视 data 中指定数据的变化,然后触发这个 watch 中对应的 function 处理函数,函数的第一参数是新数据,第二个参数是老数据
- computed:用来监控自己定义的变量,该变量不在data里面声明,直接在computed里面定义,然后就可以在页面上进行双向数据绑定展示出结果或者用作其他处理;
var vm = new Vue({
el: '#app',
data: {
name:""
},
methods: { },
filters: {
finame: function (dateStr, para1,para2) {
}
},
directives: {
'fontweight': {
bind: function (el, binding) {
el.style.fontWeight = binding.value
}
}
},
watch: {
'name': function (newVal, oldVal) {//name的属性改变了就触发function
console.log('ok')
},
computed: {
'hello': function () {
return this.name//name值改变时调用该函数
}
}
})
插值表达式渲染页面
{{内容}},替换自己的这个占位符,不会把整个元素的内容清空
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<!-- 插值表达式{{ msg }} -->
<p>{{ msg }}</p>
</div>
<script>
var vm = new Vue({
el: '#app', // 表示当前的Vue 实例要控制页面上的哪个区域
data: { // data 属性中,存放的是 el 中要用到的数据
msg: 'hello'
}
})
</script>
</body>
</html>
缺陷:插值会有闪烁问题,在加载完vue之前{{ msg }}处就会直接显示{{ msg }}
v-cloak
使用 v-cloak 能够解决插值表达式闪烁的问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
/*1.设置v-cloak隐藏 */
[v-cloak] {
display: none;
}
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<!-- 2.在标签内添加自定义属性v-cloak -->
<p v-cloak>{{ msg }} </p>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: 'hello'
}
})
</script>
</body>
</html>
v-text
默认 v-text 是没有闪烁问题的,但 v-text会覆盖元素中原本的内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<h4 v-text="msg">====</h4>
<!-- ===被覆盖,显示hello-->
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: 'hello'
}
})
</script>
</body>
</html>
v-html
插值表达式及v-text只能直接输出data的文本数据,若存在html标签也会自动转义后输出
若要输出具有html标签的内容应用html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<div>{{msg}}</div> <!-- <h1>哈哈</h1> -->
<div v-text="msg"></div> <!-- <h1>哈哈</h1> -->
<div v-html="msg">1212112</div> <!-- 哈哈 -->
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: '<h1>哈哈</h1>'
}
})
</script>
</body>
</html>
缺陷:会覆盖元素中原本的内容
注意:在单文件组件里,scoped 的样式不会应用在 v-html 内部,因为那部分 HTML 没有被 Vue 的模板编译器处理。因而导致VUE使用v-html获取文档后css样式不生效
官方关于v-html的解释如下:
解决方法:
1、去掉<style scoped>
中的scoped,(不建议使用,会改变布局,导致组件之间样式冲突)
<style>
.content img{width:100%;height:auto;margin:.5rem auto;}
.content .contentimg{width:100%;height:auto;margin:.5rem auto;}
</style>
2、通过 >>> 可以使得在使用scoped属性的情况下,穿透scoped,修改其他组件的值
<style scoped>
.content >>> img{width:100%;height:auto;margin:.5rem auto;}
.content >>> .contentimg{width:100%;height:auto;margin:.5rem auto;}
</style>
3、如果vue项目使用less或者sass的时候,>>>可能会失效,可以使用/deep/来代替
<style scoped>
.content /deep/ img{width:100%;height:auto;margin:.5rem auto;}
.content /deep/ .contentimg{width:100%;height:auto;margin:.5rem auto;}
</style>
v-bind
v-bind是 Vue中,提供的用于绑定属性的指令
v-bind会把属性后面引号的内容当做js代码去解析执行
v-bind的缩写是 :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<input type="button" value="按钮" v-bind:title="mytitle + '123'">
<!-- v-bind简写-->
<!-- <input type="button" value="按钮" :title="mytitle + '123'">-->
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
mytitle: '这是一个自己定义的title'
}
})
</script>
</body>
</html>
v-on:事件绑定机制
v-on的缩写是 @
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<!--<input type="button" value="按钮" v-on:click="show">-->
<input type="button" value="按钮" @click="show">
</div>
<script>
var vm = new Vue({
el: '#app',
methods: {
show: function () {
alert('Hello')
}
}
})
</script>
</body>
</html>
v-on事件修饰符:
- .stop 阻止冒泡
- .prevent 阻止默认事件
- .capture 添加事件侦听器时使用事件捕获模式
- .self 只当事件在该元素本身(比如不是子元素)触发时触发回调,只会阻止自己身上冒泡行为的触发,并不会真正阻止冒泡的行为
- .once 事件只触发一次
v-model
v-bind 只能实现数据的单向绑定,从 M 自动绑定到 V, 无法实现数据的双向绑定
使用 v-model 指令,可以实现 表单元素和 Model 中数据的双向数据绑定
注意: v-model 只能运用在 表单元素中
案例:计算器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="n1">
<select v-model="opt">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
<input type="text" v-model="n2">
<input type="button" value="=" @click="calc">
<input type="text" v-model="result">
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
n1: 0,
n2: 0,
result: 0,
opt: '+'
},
methods: {
calc() {
switch (this.opt) {
case '+':
this.result = parseInt(this.n1) + parseInt(this.n2)
break;
case '-':
this.result = parseInt(this.n1) - parseInt(this.n2)
break;
case '*':
this.result = parseInt(this.n1) * parseInt(this.n2)
break;
case '/':
this.result = parseInt(this.n1) / parseInt(this.n2)
break;
}
}
}
});
</script>
</body>
</html>
在Vue中使用class样式
(1) 数组
<h1 :class="['red', 'thin']">hello</h1>
(2)数组中使用三元表达式
<h1 :class="['red', 'thin', flag?'active':'']">hello</h1>
(3)数组中嵌套对象
<h1 :class="['red', 'thin', {'active': flag}]">hello</h1>
(4)直接使用对象
<h1 :class="{red:true, italic:true, active:true, thin:true}">hello</h1>
在Vue中使用内联样式
(1)直接在元素上通过 :style 的形式,书写样式对象
<h1 :style="{color: 'red', 'font-size': '40px'}">hello</h1>
(2)将样式对象,定义到 data 中,并直接引用到 :style 中
- 在data上定义样式:
data: {
h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' }
}
- 在元素中,通过属性绑定的形式,将样式对象应用到元素中:
<h1 :style="h1StyleObj">hello</h1>
(3)在 :style 中通过数组,引用多个 data 上的样式对象
- 在data上定义样式:
data: {
h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' },
h1StyleObj2: { fontStyle: 'italic' }
}
- 在元素中,通过属性绑定的形式,将样式对象应用到元素中:
<h1 :style="[h1StyleObj, h1StyleObj2]">hello</h1>
v-fot
(1)循环数据
<ul>
<li v-for="(item, i) in list">索引:{{i}} --- 姓名:{{item.name}} --- 年龄:{{item.age}}</li>
</ul>
(2)循环对象
<p v-for="(val, key, i) in user">值是: {{ val }} --- 键是: {{key}} -- 索引: {{i}}</p>
(3)迭代数字
<!-- 注意:如果使用 v-for 迭代数字的话,前面的 count 值从 1 开始 -->
<p v-for="count in 10">这是第 {{ count }} 次循环</p>
注意:
key 在使用的时候,必须使用 v-bind 属性绑定的形式,指定 key 的值
v-for 循环的时候,key 属性只能使用 number获取string
在组件中,使用v-for循环的时候,或者在一些特殊情况中,如果 v-for 有问题,必须 在使用 v-for 的同时,指定 唯一的 字符串/数字 类型 :key 值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<div>
<label>Id:
<input type="text" v-model="id">
</label>
<label>Name:
<input type="text" v-model="name">
</label>
<input type="button" value="添加" @click="add">
</div>
<p v-for="item in list" :key="item.id">
<input type="checkbox">{{item.id}} --- {{item.name}}
</p>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
id: '',
name: '',
list: [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
]
},
methods: {
add() {
this.list.unshift({ id: this.id, name: this.name })
}
}
});
</script>
</body>
</html>
v-if和v-else
<template>
<div>
<li>
<span v-if="del">222</span>
<span v-if="del">333</span>
</li>
</div>
</template>
v-if和v-show
v-if 的特点:每次都会重新删除或创建元素
v-show 的特点: 每次不会重新进行DOM的删除和创建操作,只是切换了元素的 display:none 样式
v-if 有较高的切换性能消耗
v-show 有较高的初始渲染消耗
如果元素涉及到频繁的切换,最好不要使用 v-if, 而是推荐使用 v-show
如果元素可能永远也不会被显示出来被用户看到,则推荐使用 v-if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<input type="button" value="toggle" @click="flag=!flag">
<h3 v-if="flag">这是用v-if控制的元素</h3>
<h3 v-show="flag">这是用v-show控制的元素</h3>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
flag: false
},
});
</script>
</body>
</html>
过滤器
Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示;
(1)私有过滤器
var vm = new Vue({
el: '#app',
data: { },
methods: { },
filters: {
finame: function (dateStr, para1,para2) {
}
},
})
(2)全局过滤器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<p>{{ msg | msgFormat('哥哥+1', '123') | test }}</p>
</div>
<script>
// 定义一个 Vue 全局的过滤器,名字叫做my
Vue.filter('my', function (msg, arg, arg2) {
return msg.replace(/哈哈/g, arg + arg2)
})
})
var vm = new Vue({
el: '#app',
data: {
msg: '嘻嘻哈哈呵呵嘻嘻哈哈呵呵'
},
methods: {}
});
</script>
</body>
</html>
渲染时间格式案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app2">
<h3>{{ dt | dateFormat }}</h3>
</div>
<script>
var vm2 = new Vue({
el: '#app2',
data: {
dt: new Date()
},
methods: {},
filters: {
dateFormat: function (dateStr, pattern = '') {
var dt = new Date(dateStr)
// yyyy-mm-dd
var y = dt.getFullYear()
var m = (dt.getMonth() + 1).toString().padStart(2, '0')
var d = dt.getDate().toString().padStart(2, '0')
if (pattern.toLowerCase() === 'yyyy-mm-dd') {
return `${y}-${m}-${d}`
} else {
//padStart()字符串操作,不满两补0
var hh = dt.getHours().toString().padStart(2, '0')
var mm = dt.getMinutes().toString().padStart(2, '0')
var ss = dt.getSeconds().toString().padStart(2, '0')
return `${y}-${m}-${d} ${hh}:${mm}:${ss} ~~~~~~~`
}
}
}
})
</script>
</body>
</html>
键盘修饰符以及自定义键盘修饰符
(1)自定义键盘修饰符
Vue.config.keyCodes.f2 = 113;
(2)使用自定义的按键修饰符
<input type="text" v-model="name" @keyup.f2="add">
(3)Vue 自带按键码的别名:
- enter
- tab
- delete (捕获“删除”和“退格”键)
- esc
- space
- up
- down
- left
- right
自定义指令
- 使用 Vue.directive() 定义全局的指令 v-focus
- 参数1 : 指令的名称,注意,在定义的时候,指令的名称前面,不需要加 v- 前缀,在调用的时候,必须 在指令名称前 加上 v- 前缀来进行调用
- 参数2: 是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作,如下:
(1)bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。和样式相关的操作,一般都可以在 bind 执行
(2)inserted:表示元素 插入到DOM中的时候,会执行 inserted 函数【触发1次】,和JS行为有关的操作,最好在 inserted 中去执行,放置 JS行为不生效,
(3)update:当VNode更新的时候,会执行 updated, 可能会触发多次
指令钩子函数会被传入以下参数:
(1)el:指令所绑定的元素,可以用来直接操作 DOM 。
(2)binding:一个对象,包含以下属性:
-name:指令名,不包括 v- 前缀。例如binding.name,值为color
-value:指令的绑定值,例如:v-my-directive=“1 + 1” 中,绑定值为 2,是计算后的值。又如例子中,v-color="‘bule’" 中,binding.value值为blue。
-expression:字符串形式的指令表达式。例如 v-my-directive=“1 + 1” 中,表达式为 “1 + 1”,是原样输出,不计算。
注binding中例子以一下自定义设置样式为例
设置自定义全局指令
//自定义元素自动获取焦点
Vue.directive('focus', {
bind: function (el) {
// 注意: 在每个 函数中,第一个参数,永远是 el ,表示 被绑定了指令的那个元素,这个 el 参数,是一个原生的JS对象
// 在元素 刚绑定了指令的时候,还没有 插入到 DOM中去,这时候,调用 focus 方法没有作用
// 因为,一个元素,只有插入DOM之后,才能获取焦点(错误的时机)
// el.focus()
},
inserted: function (el) { //JS相关操作应用该方法
el.focus()
}
})
//自定义设置样式color
Vue.directive('color', {
bind: function (el, binding) {
el.style.color = binding.value
}
})
设置自定义私有指令
var vm = new Vue({
el: '#app2',
directives: {
'fontweight': {
bind: function (el, binding) {
el.style.fontWeight = binding.value
}
}
},
})
调用
//调用自定义指令focus和color
<input type="text" id="search" v-focus v-color="'green'">
<div id="app2">
<h3 v-color="'pink'" v-fontweight="900">333</h3>
</div>
- 函数简写
在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如这样写:
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value
})
var vm = new Vue({
el: '#app2',
directives: {
'fontweight': function (el, binding) {
el.style.fontWeight = binding.value
}
},
})
//上面是下面的简写
// directives: {
// 'fontweight': {
// bind: function (el, binding) {
// el.style.fontWeight = binding.value
// }
// }
// }
自定义私有指令的常用场景
- 使用场景:
- 代码复用和抽象的主要形式是组件
- 当需要对普通 DOM 元素进行底层操作,此时就会用到自定义指令
- 但是,对于大幅度的 DOM变动,还是应该使用组件
-
案例
(1)输入框自动聚焦// 注册一个全局自定义指令 `v-focus` Vue.directive('focus', { // 当被绑定的元素插入到 DOM 中时 inserted: function (el) { // 聚焦元素 el.focus() } }) <input v-focus>
(2)下拉菜单
点击下拉菜单本身不会隐藏菜单,点击下拉菜单以外的区域隐藏菜单。Vue.directive('clickoutside', { bind(el, binding) { function documentHandler(e) { if (el.contains(e.target)) { return false } if (binding.expression) { binding.value(e) } } el.__vueMenuHandler__ = documentHandler document.addEventListener('click', el.__vueMenuHandler__) }, unbind(el) { document.removeEventListener('click', el.__vueMenuHandler__) delete el.__vueMenuHandler__ } }) new Vue({ el: '#app', data: { show: false }, methods: { handleHide() { this.show = false } } }) <div class="main" v-clickoutside="handleHide"> <button @click="show = !show">点击显示下拉菜单</button> <div class="dropdown" v-show="show"> <div class="item"><a href="#">选项 1</a></div> <div class="item"><a href="#">选项 2</a></div> <div class="item"><a href="#">选项 3</a></div> </div> </div>
(3)相对时间装换
<span v-relativeTime="time"></span> new Vue({ el: '#app', data: { time: 1565753400000 } }) Vue.directive('relativeTime', { bind(el, binding) { // Time.getFormatTime() 方法,自行补充 el.innerHTML = Time.getFormatTime(binding.value) el.__timeout__ = setInterval(() => { el.innerHTML = Time.getFormatTime(binding.value) }, 6000) }, unbind(el) { clearInterval(el.innerHTML) delete el.__timeout__ } })
(4)按钮级别的控制
页面级别:用户登录后,获取用户role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。最后通过router.addRoutes动态挂载。现在是通过获取到用户的role之后,在前端用v-if手动判断来区分不同权限对应的按钮的。
按钮级别:用户登录后,获取用户role,在前端用 v-if 或者封装一个自定义指令,手动判断来区分不同权限对应的按钮的。
<template> <div> <div v-permission="'user'"> 33 </div> </div> </template> <script> const role = 'user' export default { data() { return { role : 'user' } }, created() { }, directives: { 'permission': { inserted: function (el, binding) { if (role !== binding.value) { el.parentElement.removeChild(el) } } } }, } </script> <style scoped> </style>
渲染函数
Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的
完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器。
- 基础
render: function (createElement) {
// createElement函数返回结果是VNode
return createElement(
tag, // 标签名称
data, // 传递数据
children // 子节点数组
)
}
- 用render实现heading组件
实现:<h1 title:""></h1>
Vue.component('heading', {
props: ['level', 'title'],
render(h) {
return h(
'h' + level,
this.$slots.default
)
}
})
虚拟DOM
Vue 通过建立一个虚拟 DOM 来追踪自己要如何改变真实 DOM。
输出虚拟DOM观察期结构
const vnode = h(
'h' + level,
{ attrs: { title: this.title } }, // 之前省略了title的处理
this.$slots.default//插槽的默认内容
)
console.log(vnode);
范例:
Vue.component('heading', {
props: ['level', 'title', 'icon'],
render(h) {
let children = [];
// 添加图标功能
// <svg><use xlink:use="#icon-xxx"></use></svg>
if (this.icon) {
children.push(h(
'svg',
{ class: 'icon' },
[h('use', { attrs: { 'xlink:href': '#icon-' + this.icon } })]))
children = children.concat(this.$slots.default)
}
vnode = h(
'h' + level,
{ attrs: { this.title } }, // 之前省略了title的处理
children
)
console.log(vnode);
return vnode
}
})
函数式组件
组件没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法时,可以将组件标记为
functional ,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文),好处是更加轻量,消耗资源更少。
Vue.component('heading', {
functional: true, //标记函数式组件
props: ['level', 'title', 'icon'],
render(h, context) { //上下文传参
let children = [];
// 属性获取
const {icon, title, level} = context.props
if (icon) {
children.push(h(
'svg',
{ class: 'icon' },
[h('use', { attrs: { 'xlink:href': '#icon-' + icon } })]))
// 子元素获取
children = children.concat(context.children)//context.children是函数式组件获取插槽的内容的方式
}
vnode = h(
'h' + level,
{ attrs: { title } },
children
)
console.log(vnode);
return vnode
}
})
vue实例的生命周期
beforeCreate () {},//执行时组件实例还未创建,通常用于插件开发中执行一些初始化任务
created () {},//组件初始化完毕,各种数据可以使用,常用于异步数据获取
beforeMount () {},//未执行渲染、更新,dom未创建
mounted () {},//初始化结束,dom己创建,可用于获取访问数据和dom元素
beforeUpdate () {},//更新前,可用于获取更新前各种状态
updated () {},//更新后,所有状态已是最新
beforeDestroy () {},//销毁前,可用于一些定时器或订阅的取消
destroyed () {},//组件已销毁,作用同上
watch监听器
-
作用:监视 data 中指定数据的变化,然后触发这个 watch 中对应的 function 处理函数
-
第一次初始化时就调用一次的watch基本模板:
(1)handler:其值是一个回调函数。即监听到变化时应该执行的函数。
(2)deep:其值是true或false;确认是否深入监听。(一般监听时是不能监听到对象属性值的变化的,数组的值变化可以听到。)
(3)immediate:其值是true或false;确认是否以当前的初始值执行handler的函数。watch: { data: { immediate: true, deep: true, handler(newValue, oldValue) { } } },
-
默认情况初始化不执行watch的基本模板:
watch: { data(newValue, oldValue) { } },
优势:可以监视一些非dom元素的事件,如监听路由
- 案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue-2.4.0.js"></script> <script src="./lib/vue-router-3.0.1.js"></script> </head> <body> <div id="app"> <router-link to="/login">登录</router-link> <router-link to="/register">注册</router-link> <router-view></router-view> </div> <script> var login = { template: '<h3>这是登录子组件,这个组件是 奔波霸 开发的。</h3>' } var register = { template: '<h3>这是注册子组件,这个组件是 霸波奔 开发的。</h3>' } var router = new VueRouter({ routes: [ { path: '/', redirect: '/login' }, { path: '/login', component: login }, { path: '/register', component: register } ], linkActiveClass: 'myactive' }) var vm = new Vue({ el: '#app', router, watch: {//当路由地址发生变化时,则触发函数 '$route.path': function (newVal, oldVal) { if (newVal === '/login') { console.log('欢迎进入登录页面') } else if (newVal === '/register') { console.log('欢迎进入注册页面') } } } }); </script> </body> </html>
computed计算属性
-
基本模板
默认是getcomputed: { name() { return this.data } },
-
带get、set的模板
get:只要get中定义的变量发生变化就调用。
set:当手动改变计算属性的值时(下文是name)就会调用set。其参数val是name改变后的值。computed: { name:{ get:function(){ }, set:function(val){ } } },
-
功能
(1)只要计算属性,这个 function 内部,所用到的 任何 data 中的数据发送了变化,就会立即重新计算这个计算属性的值
(2)计算属性的求值结果,会被缓存起来,方便下次直接使用; 如果 计算属性方法中,所以来的任何数据,都没有发生过变化,则不会重新对计算属性求值;因此此方法性能较好。 -
注意事项
(1)计算属性,在引用的时候,一定不要加 () 去调用,直接把它当作普通属性去使用就好了;
(2)使用计算属性一定要rutern一个值 -
案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue-2.4.0.js"></script> </head> <body> <div id="app"> <input type="text" v-model="firstname"> + <input type="text" v-model="middlename"> + <input type="text" v-model="lastname"> = <input type="text" v-model="fullname"> <p>{{ fullname }}</p> <!-- 输出-- --> <p>{{ fullname }}</p> <!-- 输出-- --> </div> <script> var vm = new Vue({ el: '#app', data: { firstname: '', lastname: '', middlename: '' }, computed: { 'fullname': function () { return this.firstname + '-' + this.middlename + '-' + this.lastname } } }); </script> </body> </html>
- 带有有getter和setter的计算属性的案例:
初始化运行结果:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue计算属性get和set</title> <style> </style> <script src="https://cdn.bootcss.com/vue/2.4.4/vue.min.js"></script> </head> <body> <div id="app"> <p>a的值是:{{a}}</p> <p>b的值是:{{b}}</p> <button @click="change">按钮</button> </div> <script> new Vue({ el: '#app', data:{ a:1 }, computed: { b: { // getter get: function () { return this.a + 10 }, // setter set: function (val) { this.a = val } } }, methods:{ change() { this.b = 100 } } }) </script> </body> </html>
点击按钮后运行结果:
watch、computed和methods之间的对比
- computed
(1)属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算,相较于watch性能更好;
(2)不需要再data中定义变量就能用,可以直接当作属性来使用,写起来比较简单,如果是watch则要定义一个变量去接收改变后的值。
(3)适用场景:一个值由其它值得来,这些值变了我也要变,适合多个值影响一个值的清醒,一般情况能用computed就用computed。 - methods
methods方法表示一个具体的操作,主要书写业务逻辑; - watch
(1)watch一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是computed和methods的结合体;
(2)适用场景:一个值发生了变化,我要做些事情,适合做一个值影响多个值的情况。监听器选项提供了更通用的方法,适合执行异步操作或大开销操作的情况。侦听器案例
混入
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任
意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
// 定义一个混入对象
var myMixin = {
created: function () {
this.hello()
},
methods: {
hello: function () {
console.log('hello from mixin!')
}
}
}
// 定义一个使用混入对象的组件
Vue.component('comp', {
mixins: [myMixin]
})
插件
范例
//定义插件
const MyPlugin = {
install (Vue, options) {
Vue.component('heading', {...})
}
}
//使用插件
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(MyPlugin)
}
插件的使用:Vue.use(MyPlugin)
render
在 webpack 中,如果想要通过 vue, 把一个组件放到页面中去展示,vm 实例中的 render 函数可以实现;
通过el指定容器,把容器替换成render中的login。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<p>444444</p>
</div>
<script>
var login = {
template: '<h1>这是登录组件</h1>'
}
var vm = new Vue({
el: '#app',
render: function (createElements) { // createElements 是一个 方法,调用它,能够把 指定的 组件模板,渲染为 html 结构
return createElements(login)
//注意:这里 return 的结果,会替换页面中 el 指定的那个 容器
}
});
</script>
</body>
</html>
vue-loader——在webpack中引入以.vue结尾的外部文件vue-loader的方法
- 步骤
-
运行
cnpm i vue-loader vue-template-compiler -D
,vue-template-compiler
为vue-loader
的内部依赖,也要安装。 -
在
webpack.config.js
文件中配置插件节点和匹配规则const VueLoaderPlugin = require('vue-loader/lib/plugin') //第一步:引入插件 module.exports = { plugins: [ // 第二步:配置插件的节点 new VueLoaderPlugin() ], module: { // 第三步:配置以.vue结尾的匹配规则 rules: [ { test:/\.vue$/, use: 'vue-loader' } ] } };
-
在js入口文件
main,js
中引入以“.vue”结尾的vue文件index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <!-- <script src="/bundle.js"></script> --> </head> <body> <div id="app"> <login></login> </div> </body>
main.js
注意:如果引入的vue包为默认配置的包,即vue.runtime.common.js
时,则不支持以componets的形式引入组件,而应该使用render函数。import Vue from 'vue' import login from './login.vue' var vm = new Vue({ el: '#app', data: { msg: '123' }, // components: { // login // }, render: function (createElements) { return createElements(login) } })
login.vue
<template> <div> <h1>这是登录组件,使用 .vue 文件定义出来的 --- {{msg}}</h1> </div> </template> <script> export default { data() { // 注意:组件中的 data 必须是 function return { msg: "123" }; }, methods: { show() { console.log("调用了 login.vue 中的 show 方法"); } } }; </script> <style> </style>
-
webpack 中如何使用 vue :
- 安装vue的包:
cnpm i vue -D
- 由于 在 webpack 中,推荐使用 .vue 这个组件模板文件定义组件,所以,需要安装 能解析这种文件的 loader cnpm i vue-loader vue-template-complier -D
- 在 main.js 中,导入 vue 模块 import Vue from ‘vue’
- 定义一个 .vue 结尾的组件,其中,组件有三部分组成: template script style
- 使用 import login from ‘./login.vue’ 导入这个组件
- 创建 vm 的实例 var vm = new Vue({ el: ‘#app’, render: c => c(login) })
- 在页面中创建一个 id 为 app 的 div 元素,作为我们 vm 实例要控制的区域;
面试问题
1.组件通信常用方式
props
eventbus
vuex
自定义事件
-
List item
边界情况
$parent
$children
$root
$refs
provide/inject -
非prop特性
$attrs
$listeners
2.v-if和v-for哪个优先级高
1.显然v-for优先于v-if被解析,弄一个测试代码从源码中发现的。
2.如果同时出现,每次渲染都会先执行循环再判断条件,无论如何循环都不可避免,浪费了性能
3.要避免出现这种情况,则在外层嵌套template,在这一层进行v-if判断, 然后在内部进行v-for循环
3.为什么vue组件中的data要是函数的形式
Vue组件可能存在多个实例,如果使用对象形式定义data,则会导致它们共用一-个data对象,那么状态变更将会影响所有组件实例,这是不合理的;采用函数形式定义,在initData时会将其作为工厂函数返回全新data对象,有效规避多实例之间状态污染问题。而在Vue根实例创建过程中则不存在该限制,也是因为根实例只能有一个,不需要担心这种情况。
4.vue中的key的作用和原理
- key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。
- 另外,若不设置key还可能在列表更新时引发-些隐蔽的bug
- vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。