相关资源
0 Vue简介
基本介绍
vue是一套用于构建用户界面的渐进式js框架
vue特点
-
采用组件化,提高代码复用率、且让代码更好维护
-
声明式编码,无需直接操作DOM,提高开发效率
-
使用虚拟DOM+优秀的Diff算法,尽可能复用DOM节点
搭建vue环境
参考官方文档
Vue周边库
- vue-cli: vue 脚手架
- vue-resource
- axios
- vue-router: 路由
- vuex: 状态管理
- element-ui: 基于 vue 的 UI 组件库(PC 端)
Hello小案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>初识vue</title>
<!--引入vue-->
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<!--准备容器-->
<div id="root">
<h1>Hello,{{name}}</h1>
</div>
<script type="text/javascript">
// 创建vue实例
new Vue({
el:'#root', // el 用于指定当前vue实例为哪个容器服务,值通常为css选择器字符串
data:{ // data 用于存储数据,数据供el指定的容器使用
name:'Cyan'
}
})
</script>
</body>
</html>
小结
- 要让Vue工作,必须创建一个Vue实例,且要传入一个配置对象
- root容器中的代码依然符合html代码规范,仅混入Vue的语法而已
- root容器中的代码被称为[Vue模板]
- Vue实例和容器是一一对应得
- 真实开发中只有一个Vue实例,并且会配合组件一起使用
- {{XXX}}中的XXX要写js表达式,且XXX可以自动读取到data中的所有属性;
- 一旦data中的数据发生改变,那么模板中的用到该数据的地方会自动更新
注意区分:js表达式和js代码(语句)
1.表达式:一个表达式会产生一个值,可以放在需要值的地方
- a
- a+b
- demo(1)
- x===y ? ‘a’ : ‘b’
2.js代码(语句)
- if () {}
- for () {}
1 Vue核心
1.1 模板语法
插值语法
- 功能:用于解析标签体内容
- 写法:{{xxx}},xxx是js表达式,qie可以直接读取到data中的所有属性
<!--准备容器-->
<div id="root">
<h1>插值语法</h1>
<h1>Hello,{{name}}</h1>
</div>
<script type="text/javascript">
// 创建vue实例
new Vue({
el: '#root', // el 用于指定当前vue实例为哪个容器服务,值通常为css选择器字符串
data: { // data 用于存储数据,数据供el指定的容器使用
name: 'Cyan',
url:'https://www.baidu.com'
}
})
</script>
指令语法
- 功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)
- 写法:v-bind:href=“xxx” 或者 简写为 :href=“xxx”,xxx同样要写js表达式,且可以直接读取到data中的所有属性
<!--准备容器-->
<div id="root">
<h1>指令语法</h1>
<a v-bind:href="url">点我去百度</a>
<a :href="url">点我去百度</a>
</div>
<script type="text/javascript">
// 创建vue实例
new Vue({
el: '#root', // el 用于指定当前vue实例为哪个容器服务,值通常为css选择器字符串
data: { // data 用于存储数据,数据供el指定的容器使用
name: 'Cyan',
url:'https://www.baidu.com'
}
})
</script>
1.2 数据绑定
Vue中两种数据绑定方式:
单向数据绑定
- 语法:-bind:href=“xxx” 或者 简写为 :href=“xxx”
- 特点:数据只能从data流向页面
双向数据绑定
- 语法:v-mode:value=“xxx” 或简写为 v-model=“xxx”
- 特点:数据不仅能从data流向页面,还可以从页面流向data
备注
- 双向绑定一般都应用在表单元素上(如input、select等)
- v-model:value,简写为v-model,因为v-model默认收集的是value的
<!--准备容器-->
<div id="root">
单向数据绑定:<input type="text" v-bind:value="name"><br/>
双向数据绑定:<input type="text" v-model:value="name"></br>
<!-- 该代码是错误的,v-model只能应用在表单类元素上 -->
<h2 v-model:x="name">Hello</h2>
</div>
<script type="text/javascript">
// 创建vue实例
new Vue({
el: '#root', // el 用于指定当前vue实例为哪个容器服务,值通常为css选择器字符串
data: { // data 用于存储数据,数据供el指定的容器使用
name: 'Cyan',
url:'https://www.baidu.com'
}
})
</script>
补充:
el的两种写法
第一种:
const v = new Vue({
el: '#root', // el 用于指定当前vue实例为哪个容器服务,值通常为css选择器字符串
data: { // data 用于存储数据,数据供el指定的容器使用
name: 'Cyan',
url:'https://www.baidu.com'
}
});
console.log(v)
第二种:
v.$mount{'#root'}
data两种写法
第一种:
new Vue({
el: '#root', // el 用于指定当前vue实例为哪个容器服务,值通常为css选择器字符串
data: { // data 用于存储数据,数据供el指定的容器使用
name: 'Cyan',
url:'https://www.baidu.com'
}
});
第二种:
new Vue({
el: '#root', // el 用于指定当前vue实例为哪个容器服务,值通常为css选择器字符串
data:function() {
return {
name:'Cyan'
}
}
});
总结
- el两种写法
- new Vue时配置el属性
- 先创建Vue实例,随后通过vm.$mount(‘#root’)指定el的值
- data两种写法
- 对象式
- 函数式
- 使用组件data必须函数式,否则报错
- 由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this不再是Vue实例
1.3 MVVM 模型
- M:模型(Model) :对应 data 中的数据
- V:视图(View) :模板
- VM:视图模型(ViewModel) : Vue 实例对象
总结
- data中所有属性最终出现在vm身上
- vm身上所有的属性及Vue原型上所有属性,在Vue模板上中都可以直接使用
1.4 数据代理
<script type="text/javascript">
let person = {
name:"张三",
sex:'男',
age:18
}
Object.defineProperty(person, 'age',{
value:18,
enumerable:true, // 控制属性是否可以枚举,默认值false
writable:true, // 控制属性是否可以被修改,默认值false
configurable:true // 控制属性是否可以被删除,默认值false
})
</script>
数据代理
通过一个对象代理对另一个对象中属性值的操作(读/写)
let obj = {x:100}
let obj2 = {y:200}
Object.defineProperty(obj2,'x',{
get() {
return obj.x;
},
set(value) {
obj.x = value;
}
})
Vue中的数据代理
- 通过vm对象来代理data对象中属性的操作(读/写)
- Vue中数据代理的优点:方便操作data中的数据
- 基本原理
- 通过Object.defineProperty()把data对象中所有属性添加到vm上,为每个添加到vm上的属性,都指定一个getter和setter。在getter和setter内部去操作(读/写)data中对应的属性
1.5 事件处理
1.5.1 绑定监听
<body>
<!--准备容器-->
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- <button v-on:click="showInfo">点击我</button>-->
<button @click="showInfo">点击我</button>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el:"#root",
data:{
name:'Cyan'
},
methods:{
showInfo() {
// alert('Hello')
// console.log(this) 此处this时vm
}
}
})
</script>
事件的基本使用
- 使用
v-on:xxx
或@xxx
绑定事件,其中xxx是事件名 - 事件的回调需要配置在
methods
对象中,最终在vm上、 - methods中配置的函数,不要箭头函数,否则this就不是vm了
- methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象
@click="demo"
和@click="demo($event)"
效果一致,但后者可以传参
- v-on:xxx=“fun”
- @xxx=“fun”
- @xxx=“fun(参数)”
- 默认事件形参: event
- 隐含属性对象: $event
1.5.2 事件修饰符
Vue中的事件修饰符
.prevent
:阻止默认事件(常用).stop
:阻止事件冒泡(常用).once
:事件只触发一次.capture
:使用事件的捕获模式.self
:只有event.target
是当前操作的元素才触发事件.passive
:事件的默认行为立即执行,无需等待事件回调执行完毕
1.5.3 按键修饰符
keycode
: 操作的是某个 keycode 值的键keyName
: 操作的某个按键名的键(少部分
Vue中常用按键别名
enter
:回车
delete
:删除 (捕获“删除”和“退格”键)
esc
:退格
tab
:换行(必须配合keydown)
up
:上
down
:下
left
:左
right
:右
Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转换为
kebab-case(短横线命名)
系统修饰键(用法特殊):ctrl、alt、shift、meta(win)
- 配合
keyup
使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才能被触发、 - 配合
keydown
使用:正常触发事件
Vue.config.keyCodes 自定义键名=键码(定制按键别名)
1.5.4 计算属性
插值语法实现姓名案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>姓名案例-插值语法实现</title>
</head>
<script type="text/javascript" src="js/vue.js"></script>
<body>
<div id="root">
姓:<input type="text" v-model:value="firstName"><br>
名:<input type="text" v-model:value="lastName"><br>
姓名:<span>{{firstName}}-{{lastName}}</span>
</div>
</body>
<script type="text/javascript">
new Vue ({
el:'#root',
data:{
firstName:'张',
lastName:'三'
}
})
</script>
</html>
methods实现姓名案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>姓名案例-methods实现</title>
</head>
<script type="text/javascript" src="js/vue.js"></script>
<body>
<div id="root">
姓:<input type="text" v-model:value="firstName"><br>
名:<input type="text" v-model:value="lastName"><br>
姓名:<span>{{fullName()}}</span>
</div>
</body>
<script type="text/javascript">
new Vue ({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
methods:{
fullName() {
return this.firstName+this.lastName;
}
}
})
</script>
</html>
总结
计算属性:
- 定义:要用的属性不存在,需要通过已有属性计算得来
- 原理:底层借助
Object.defineproperty
方法提供的getter和setter - get函数调用时机:
- 初次读取时执行一次
- 当依赖的数据发生改变时再次调用
- 优势:与methods实现,内部有缓存机制(复用),效率更高,调试方便
- 备注:
- 计算属性最终出现在vm上,直接读取调用
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生
1.7 监视属性
监视属性介绍
天气案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>天气案例</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
isHot:true
},
computed:{
info() {
return this.isHot ? '炎热':'凉爽'
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot
}
},
watch:{
isHot:{
immediate:true,// 初始化让handler调用
// 当isHot发生改变时handler调用
handler(){
console.log('isHot is altered')
}
}
}
})
</script>
</html>
监视属性两种方式
...
watch:{
isHot:{
immediate:true,// 初始化让handler调用
// 当isHot发生改变时handler调用
handler(){
console.log('isHot is altered')
}
}
}
vm.$watch('isHot'.{
immediate:true,// 初始化让handler调用
// 当isHot发生改变时handler调用
handler(){
console.log('isHot is altered')
}
})
监视属性watch
- 当监视的属性变化时回调函数自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视
- 监视的两种写法:
- new Vue时传入watch配置
- 通过vm.$watch监视
深度监视
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>天气案例</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
<h3>a:{{numbers.a}}</h3>
<button @click="numbers.a++">点我a+1</button>
<h3>b:{{numbers.b}}</h3>
<button @click="numbers.b++">点我b+1</button>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
isHot:true,
numbers:{
a:1,
b:1
}
},
computed:{
info() {
return this.isHot ? '炎热':'凉爽'
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot
}
},
watch:{
isHot:{
// immediate:true,// 初始化让handler调用
// 当isHot发生改变时handler调用
handler(){
console.log('isHot is altered')
}
},
// 监视多级结构中所有属性的改变
numbers:{
deep:true,
hander(){
console.log('numbers altered')
}
}
}
})
</script>
</html>
深度监视:
- Vue中的watch默认不监视对象内部值的改变(一层)
- 配置deep:true可以监视对象内部值的改变(多层)
备注
- Vue自身可以监测内部值的改变,但Vue提供的watch默认不可以
- 使用watch时根据数据的具体结构,决定是否采用深度监视
计算属性与监视属性区别
- computed能完成的功能,watch都可以完成
- watch能完成的功能,computed不一定能完成
原则
- 被Vue管理的函数,最好写成普通函数,如此
this
的指向才是vm或者组件实例对象 - 所有不被Vue管理的函数(定时器的回调函数,ajax的回调函数等),最好写成箭头函数,这样
this
的指向才是vm或组件实例对象
1.8 绑定样式
-
在应用界面中, 某个(些)元素的样式是变化的
-
class/style 绑定就是专门用来实现动态样式效果的技术
class 绑定
- :class=‘xxx’
- 表达式是字符串: ‘classA’
- 表达式是对象: {classA:isA, classB: isB}
- 表达式是数组: [‘classA’, ‘classB’]
style 绑定
-
:style=“{ color: activeColor, fontSize: fontSize + ‘px’ }”
-
其中 activeColor/fontSize 是 data 属性
1.9 条件渲染
1.9.1 条件渲染指令
v-if
- v-if=“表达式”
- v-else-if=“表达式”
- v-else=“表达式”
适用于:切换频率较低的场景
特点:不展示的DOM元素直接被移除
注意:v-if可以和v-else-if、v-else一起使用,但要求结构不能被“打断”
v-show
- v-show=“表达式”
- 切换频率较高的场景
- 特点:不展示DOM元素未被移除,仅仅是使用样式隐藏掉
- 备注:使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到
1.9.2 比较v-if与v-show
- 如果需要频繁切换v-show较好
- 当条件不成立时, v-if 的所有子节点不会解析(项目中使用)
1.10 列表渲染
<body>
<div id="root">
<h2>人员列表</h2>
<ul>
<li v-for="p in persons" :key="p.id">
{{p.name}}-{{p.age}}
</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}
]
}
})
</script>
</body>
v-for指令
- 用于展示列表数据
- 语法:v-for=“(item,index) in xxx” :key=“yyy”
- 可遍历:数组、对象、字符串、指定次数
key作用与原理
index作为key的原理
id作为key的原理
面试题:ract、vue中的key作用(key的内部原理)
- 虚拟DOM中key的作用
- key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据[新数据]生成[新的虚拟DOM]
- 随后Vue进行[新虚拟DOM]与[旧虚拟DOM]的差异比较
- 对比规则:
- 旧虚拟DOM中找到了与新虚拟DOM相同key
- 若虚拟DOM中内容没变,直接使用之前的真实DOM
- 若虚拟DOM内容改变,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
- 旧虚拟DOM中未找到与新虚拟DOM相同的key
- 创建新的真实DOM,随后渲染到页面
- 旧虚拟DOM中找到了与新虚拟DOM相同key
- 用index作为key可能会引发的问题
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新(界面效果没问题,但效率低)
- 如果结构中还包含输入类的DOM:会阐述错误DOM更新(界面造成问题)
- 开发中如何选择key
- 最好使用每条数据的唯一标识作为key
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key没有问题
列表过滤
watch实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>基本列表</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyword">
<ul>
<li v-for="p in filPersons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
<script type="text/javascript">
// watch实现
new Vue({
el:'#root',
data:{
keyword:'',
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李赛',age:19},
{id:'003',name:'王五',age:20},
{id:'004',name:'老刘',age:22}
],
filPersons:[]
},
watch:{
keyword:{
immediate:true,
handler(val) {
this.persons = this.persons.filter((p)=>{
return p.name.indexOf(val) !== -1
})
}
}
}
</script>
</body>
</html>
computed实现
new Vue({
el: '#root',
data: {
keyword: '',
persons: [
{id: '001', name: '张三', age: 18},
{id: '002', name: '李赛', age: 19},
{id: '003', name: '王五', age: 20},
{id: '004', name: '老刘', age: 22}
],
filPersons: []
},
computed: {
filPersons() {
return this.persons = this.persons.filter((p) => {
return p.name.indexOf(this.keyword) !== -1
})
}
}
})
列表排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>基本列表</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyword">
<button @click="sortedType = 2">年龄升序</button>
<button @click="sortedType = 1">年龄降序</button>
<button @click="sortedType = 0">原顺序</button>
<ul>
<li v-for="p in filPersons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
sortedType:0, // 0原顺序,1降序 2升序
data: {
keyword: '',
persons: [
{id: '001', name: '张三', age: 18},
{id: '002', name: '李赛', age: 19},
{id: '003', name: '王五', age: 20},
{id: '004', name: '老刘', age: 22}
],
filPersons: []
},
computed: {
filPersons() {
const arr = this.persons = this.persons.filter((p) => {
return p.name.indexOf(this.keyword) !== -1
})
// 判断是否需要排序
if(this.sortedType) {
arr.sort((p1, p2)=>{
return this.sortedType === 1 ? p2.age-p1.age : p1.age - p2.age
})
}
return arr
}
}
})
</script>
</body>
</html>
1.11 Vue监测数据
Vue监视数据的原理
-
Vue会监视data中所有层次的数据
-
如何监测对象中的数据
通过settser实现监视,且要在new Vue时就传入要监测的数据
-
对象中后追加的属性,Vue默认不做响应式处理
-
如需给后添加的属性做响应式,请使用如下API
Vue.set(target, propertyName/index, value) 或 vm.$set(target, propertyName/index, value)
-
-
如何监测数组中的数据
通过包裹数组更新元素的方法实现,本质就是做了两件事:
- 调用原生对应的方法对数组进行更新
- 重新解析模板,进而更新页面
-
在Vue修改数组中的某个元素一定要用如下方法:
1.使用这些API;push()、pop()、shift()、unshift()、splice()、sort()、reverse() 2.Vue.set()或vm.$set()
特别注意
Vue.set()
和vm.$set()
不能给vm或者vm的根数据对象添加属性
1.12 收集表单数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>收集表单数据</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<div id="root">
<form @submit.prevent="demo">
账号:<input type="text" v-model="userInfo.account"><br/><br/>
密码:<input type="password" v-model="userInfo.password"><br/><br/>
性别:
男<input type="radio" name="sex" value="male" v-model="userInfo.sex">
女<input type="radio" name="sex" value="female" v-model="userInfo.sex"><br/><br/>
爱好:
学习<input type="checkbox" v-model="userInfo.hobby" value="study">
打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
<br/><br/>
所属校区:
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="nantong">南通</option>
<option value="haerbin">哈尔滨</option>
</select>
<br/><br/>
其他信息:
<textarea v-model="userInfo.other"></textarea><br/><br/>
<input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://baidu.com">《用户协议》</a>
<button>提交</button>
</form>
</div>
<script>
new Vue({
el:'#root',
data:{
userInfo:{
account:'',
password:'',
sex:'male',
hobby:[],
city:'shanghai',
other:'',
agree:''
}
},
methods:{
demo(){
console.log(JSON.stringify(this.userInfo))
}
}
})
</script>
</body>
</html>
- 若
<input type="text"/>
,则v-model收集的是value值,用户输入的就是value值 - 若
<input type="radio"/>
,则v-model收集的是value值,且要给标签配置value值 - 若
<input type="checkbox"/>
- 没有配置input的value属性,那么收集的就是checked(布尔值)
- 配置input的value属性
- v-model的初始值是非数组,那么收集的就是checked(布尔值)
- v-model的初始值是数组,那么收集的就是value构成的数组
- 备注 v-model的三个修饰符
lazy
失去焦点再加载数据number
输入字符串转为有效数字trim
输入首尾空格过滤
1.13 过滤器
格式化时间案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>过滤器</title>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript" src="js/dayjs.min.js"></script>
</head>
<body>
<div id="root">
<h2>显示格式化后的时间</h2>
<!-- 计算属性实现 -->
<h3>现在是:{{fmtTime}}</h3>
<!-- methods实现 -->
<h3>现在是:{{getFmtTime()}}</h3>
<!-- 过滤器实现 -->
<h3>现在是:{{time | timeFormater}}</h3>
<!-- 过滤器实现(传参) -->
<h3>现在是:{{time | timeFormater('YYY_MM_DD')}}</h3>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
time:1621561377603 // 时间戳
},
computed:{
fmtTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
methods:{
getFmtTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
filters:{
timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){
return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
}
}
})
</script>
</body>
</html>
过滤器
定义 对要显示的数据进行特定格式化后再显示
语法
- 注册过滤器
Vue.filter(name,callback)
或new Vue(filters:{})
- 使用过滤器
{{xxx | 过滤器名}}
或v-bind:属性="xxx | 过滤器名"
备注
- 过滤器也可以接受额外参数、多个过滤器可以串联
- 并没有改变原本的数据,是产生新的对应的数据
1.14 内置指令
指令回顾
v-bind:单向绑定解析表达式,简写成:xxx
v-model:双向数据绑定
v-for:遍历数组/对象/字符串
v-on:绑定事件监听,简写成@
v-if:条件渲染(动态控制节点是否存在)
v-else:条件渲染(动态控制节点是否存在)
v-show:条件渲染(动态控制节点是否展示)
v-text
- 作用:向其所在的节点中渲染文本内容
- 与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}不会
v-html
- 作用:向指定节点中渲染包含html结构的内容
- 与插值语法区别:
- v-html会替换掉节点所有内容 {{xx}}则不会
- v-html可以识别html结构
- 注意 v-html有安全性问题
- 在网站动态渲染任何HTML是危险的,容易导致XSS攻击
- 一定要在可信的内容上使用v-html,永不要用在用户提交的内容上
v-cloak(没有值)
- 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
- 使用css配合v-cloak可以解决网速慢时页面展示出{{xx}}的问题
v-once
- v-once所在节点在初次动态渲染后,就视为静态内容了
- 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
v-pre
- 跳过其所在节点的编译过程
- 可以利用它跳过:没有使用指令语法、没有使用插值语法的节点、会加快编译速度
1.15 自定义指令
定义语法
-
局部指令
new Vue({ directives:{指令名:配置对象} }) // 或者 new Vue({ directives(){} })
-
全局指令
Vue.directive(指令名, 配置对象) // 或者 Vue.directive(指令名, 回调函数)
配置对象中常用的三个回调
.bind
指令与元素成功绑定时调用.inserted
指令所在元素被插入页面时调用.update
指令所在模板结构被重新解析时调用
备注
- 指令定义时不加
v-
,但使用时要加v-
- 指令名如果是多个单词,要使用
kebab-case
命名方式,不要用camelCase
命名
1.16 Vue生命周期
生命周期
- 别名:生命周期回调函数、生命周期函数、生命周期钩子
- 概念:Vue关键时刻帮我们调用的一些特殊名称的函数
- 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
- 生命周期函数中的this指向是vm或组件实例对象
Vue生命周期
- 将要创建—>调用
beforeCreate
函数 - 创建完毕—>调用
created
函数 - 将要挂载—>调用
beforeMount
函数 - 挂载完毕—>调用
mounted
函数 [重要的钩子] - 将要更新–>调用
beforeUpdate
函数 - 更新完毕—>调用
update
函数 - 将要销毁—>调用
beforeDestroy
函数 [重要的钩子] - 销毁完毕—>调用
destroyed
函数
常用的生命周期钩子
mounted
发送ajax请求,启动定时器、绑定自定义事件、订阅消息 等初始化工作beforeDestroy
清除定时器、解绑自定义事件、取消订阅等 收尾工作
关于销毁Vue实例
- 销毁后借助Vue开发者工具看不到任何信息
- 销毁后自定义事件会失效,但原生DOM事件依然有效
- 一般不会在
beforeDestroy
操作数据,因为即便操作数据,也不会触发更新流程了
2 Vue组件化编程
2.1 模块与组件、模块化与组件化
模块
- 理解:向外提供特定功能的 js 程序, 一般就是一个 js 文件
- 原因:js文件很多很复杂
- 作用:复用 js, 简化 js 的编写, 提高 js 运行效率
组件的定义
- 组件:实现应用中局部功能代码和资源的集合
- 原因:界面功能复杂
- 作用:复用编码, 简化项目编码, 提高运行效率
模块化
当应用中的 js 都以模块来编写的,那这个应用就是一个模块化的应用。
组件化
当应用中的功能都是多组件的方式来编写的, 那这个应用就是一个组件化的应用
2.2 非单文件组件
定义
一个文件中包含N个组件
注意: 组件定义时,一定不要写el
配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务哪个对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>非单文件组件</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 第三步 编写组件标签 -->
<school></school>
<hr>
<student></student>
</div>
</body>
<script type="text/javascript">
// 第一步 创建school组件
const school = Vue.extend({
template:`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">点我提示学校名</button>
</div>
`,
data() {
return {
schoolName:'上交',
address:'上海'
}
},
methods:{
showName() {
alert(this.schoolName)
}
}
})
// 第一步 创建student组件
const student = Vue.extend({
template:`
<div>
<h2>学生姓名:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data() {
return {
ageName:'上交',
age:18
}
}
})
// 创建vm
new Vue({
el:'#root',
// 第二步 注册组件(局部注册)
components:{
school,
student
}
})
</script>
</html>
Vue使用组件的三大步骤
- 定义组件(创建组件)
- 注册组件
- 使用组件(组件标签)
如何定义一个组件
使用Vue.extend(options)
创建,其中options
和new Vue(options)
时传入的那个options
几乎一样,但有点区别
区别
el
不要写,因为最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器data
必须写成函数,因为避免复用组件时,数据存在引用关系- 备注 使用
template
可以配置组件结构
如何注册组件
- 局部注册:
new Vue
时传入component
选项 - 全局注册:
Vue.component('组件名',组件)
编写组件标签
<school></school>
注意点
- 关于组件名
- 一个单词组成
- 第一种写法(首字母小写)school
- 第二种写法(首字母大写)School
- 多个单词组成
- ·第一种写法(kebab-case命名):my-school
- 第二种写法(CamelCase命名):MySchool(需要Vue脚手架支持)
- 备注:
- 组件吗尽可能回避HTML中已经有的元素名称,如
h2
、H2
- 可以使用name配置项指定组件在开发者工具中呈现的名字
- 组件吗尽可能回避HTML中已经有的元素名称,如
- 一个单词组成
- 关于组件标签
- 第一种写法
<school></school>
- 第二种写法
<school/>
- 备注:不使用脚手架时,
<school/>
会导致后续组件不能渲染
- 第一种写法
- 一个简写方式
const school = Vue.extent(options)
简写为const school = options
组件的嵌套
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件的嵌套</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<div id="root">
<app></app>
</div>
</body>
<script type="text/javascript">
// 创建student组件
const student = Vue.extend({
template:`
<div>
<h2>学生姓名:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data() {
return {
name:'Cyan Chau',
age:18
}
}
})
// 创建school组件
const school = Vue.extend({
template:`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<student></student>
</div>
`,
data() {
return {
schoolName:'上交',
address:'上海'
}
},
components:{
student
}
})
// 创建hello组件
const hello = Vue.extend({
template:`<h1>{{msg}}</h1>`,
data() {
return {
msg:'欢迎来到我的博客!'
}
}
})
// 创建app组件
const app = Vue.extend({
template:`
<div>
<hello></hello>
<school></school>
</div>
`,
components:{
school,
hello
}
})
// 创建vm
new Vue({
el:'#root',
components:{
app
}
})
</script>
</html>
VueComponent
- school组件本质是一个名为VueComponent的构造函数,且不是我们定义的,是Vue.extend生成的
- 我们只需要写或者。Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行:
new VueComponent(options)
- 每次调用
Vue.extend
,返回的都是全新的VueComponent - this指向:
- 组件配置:data函数、methods中函数、watch中函数、computed中的函数,它们的this均是VueComponent实例对象
new Vue()
配置:data函数、methods中的函数、watch中的函数、computed中的函数,它们的this均是Vue实例对象
- VueComponent的实例对象,简称vc(组件实例对象),Vue的实例对象,简称vm
重要内置关系
组件实例对象(vc)可以访问到Vue原型上的属性,方法
2.3 单文件组件
定义
一个文件中只包含1一个组件
样例
<template>
<!-- 组件的结构 -->
</template>
<script>
// 组件的交互(数据、方法)
</script>
<style>
/*组件的样式*/
</style>
School.vue
<template> <!-- 组件的结构 --> <div class="demo"> <h2>学校名称:{{schoolName}}</h2> <h2>学校地址:{{address}}</h2> <button @click="showName">点我提示学校名</button> </div> </template> <script> // 组件的交互(数据、方法) export default { name:'School', data(){ return { schoolName:'上交', address:'上海' } }, methods:{ showName() { alert(this.schoolName) } } } </script> <style> /*组件的样式*/ .demo{ background-color: aquamarine; } </style>
Student.vue
<template> <!-- 组件的结构 --> <div> <h2>学生姓名:{{name}}</h2> <h2>学生年龄:{{age}}</h2> </div> </template> <script> // 组件的交互(数据、方法) export default { name:'Student', data(){ return { name:'张三', age: 18 } } } </script> <style> /*组件的样式*/ </style>
App.vue
<template> <div> <School></School> <Student></Student> </div> </template> <script> // 引入组件 import School from './School' import Student from './Student' export default { name: "APP", components:{ School, Student } } </script> <style> </style>
main.js
import App from './App' new Vue({ el:'#root', template:` <App></App>`, components:{ App } })
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>单文件组件</title> </head> <body> <div id="root"></div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript" src="main.js"></script> </body> </html>
3 Vue脚手架
入门
1.全局安装@vue/cli
npm install -g @vue/cli
2.切换到要创建项目的目录,使用命令创建目录
vue create xxxx
备注
- 出现下载缓慢,配置npm淘宝镜像
npm config set registry https://registry.npm.taobao.org
- Vue脚手架隐藏了所有webpack相关的配置,查看具体的配置,执行
vue inspect > output.js
关于不同版本的Vue
- vue.js与vue.runtime.xxx.js区别
- vue.js是完整版的Vue,包含核心功能+模板解析器
- vue.runtime.xxx.js是运行版的Vue,只包含:核心功能(没有模板解析器)
- 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数饥饿收到的createElement函数去指定具体内容
main.js
import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app')
ref属性
- 被用来给元素或子组件注册引用信息(id的替代者)
- 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
- 使用方式
- 打标识
<h1 ref="xxx">...<h1>
或<School ref="xxx"></School>
- 获取
this.$refs.xxx
- 打标识
props配置
功能:让组件接收外部传来的数据
传递数据:<Demo name="xxx"/>
接收数据:
-
简单接收
props:['name']
-
限制类型
props:{name:Number}
-
限制类型、限制必要性、指定默认值
props:{ name:{ type:String, // 类型String required:true // 必要性 default:'CYAN' // 默认值 } }
props是只读的,Vue底层会检测对props的修改,若进行修改,会发出警告,若必须修改,则复印props内容到data中一份,然后修改data中的数据
App.vue
<template>
<div>
<Student name="丽萨" sex="女" :age="18"/>
<Student name="卡拉" sex="男" :age="18"/>
</div>
</template>
<script>
// 引入组件
import School from '../src/components/Student'
import Student from '../src/components/School'
export default {
name: "App",
components:{
Student
}
}
</script>
<style>
</style>
Student.vue
<template>
<!-- 组件的结构 -->
<div>
<h2>{{msg}}</h2>
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
</template>
<script>
// 组件的交互(数据、方法)
export default {
name:'Student',
data(){
return {
msg:'我是一个学生'
}
},
// props:['name','sex','age'] 简单接收
// 接收的同时对数据进行类型限制
// props:{
// name:String,
// age:Number,
// sex:String
// }
// 接收的同时对数据进行类型限制+默认值指定+必要性限制
props:{
name:{
type:String, // 类型String
required:true // 必要的
},
age:{
type:Number,
default:99
},
sex:{
type:String, // 类型String
required:true // 必要的
}
}
}
</script>
<style>
/*组件的样式*/
</style>
mixin混入
功能:把多个组件共用的配置提取成一个混入对象
使用方式:
第一步,定义混合
{
data(){...},
methods:{...},
...
}
第二步,使用混入
全局混入:Vue.mixin(xxx)
局部混入:mixins:['xxx']
School.vue
<template>
<!-- 组件的结构 -->
<div>
<h2 @click="showName">学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</template>
<script>
// 引入混合
import {mix} from '../mixin'
// 组件的交互(数据、方法)
export default {
name:'School',
data(){
return {
name:'上交',
address:'上海'
}
},
mixins:[mix]
}
</script>
<style>
</style>
Student.vue
<template>
<!-- 组件的结构 -->
<div>
<h2 @click="showName">学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
</div>
</template>
<script>
// 引入混合
import {mix} from '../mixin'
// 组件的交互(数据、方法)
export default {
name:'Student',
data(){
return {
name:'Cyan',
sex:'female'
}
},
mixins:[mix]
}
</script>
<style>
/*组件的样式*/
</style>
mixin.js
export const mix = {
methods:{
showName() {
alert(this.name)
}
}
}
插件
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
定义插件:
对象.install = function (Vue, options) {
// 添加全局过滤器
Vue.filter(...)
// 添加全局指令
Vue.directive(...)
// 配置全局混入
Vue.mixin(...)
// 添加实例方法
Vue.prototype.$myMethod = function() {...}
Vue.prototype.$myProperty = xxx
}
使用插件
import plugins from './plugins'
Vue.use(plugins)
scoped样式
功能:让样式在局部生效,防止冲突
写法:
<style scoped>