文章目录
一、Vue简介
1、定义
官网定义:
一套用于构建用户界面的渐进式JavaScript库
构建用户界面:拿到的数据,通过某种办法,变成用户可看见的界面
前端的职责:在合适的时机发送合适请求,把得到的响应数据展示在合适的位置
渐进式:Vue可以自底向上逐层的应用,即
简单应用:只需一个轻量小巧的核心库(100kb)
复杂应用:可以引入各式各样的Vue插件
作者:尤雨溪
2、特点
- 采用组件化模式,提高代码复用率,且让代码更好的维护
- 声明式编码,让编码人员无需直接操作dom,提高开发效率
- 使用虚拟dom+优秀的diff算法,尽量复用dom节点
ES6语法规范、ES6模块化、包管理器、原型/原型链、数组常用方法、axios、promise…
二、基本使用
官网:cn.vuejs.org
直接script引入
1、初始Vue
- 让Vue 工作,必须先创建一个Vue实例,且要传入一个配置对象{ el: ‘#root’, data: {} }
- root容器里的代码依然符合html规范,只不过混入一些特殊的Vue语法
- root容器里的代码,被称为【Vue模板】
- Vue实例和容器是一一对应的
- 真实开发中只有一个Vue实例,并且配合组件一起使用
- {{XXX}}中的XXX是表达式,且XXX可以自动读取到data中的所有属性值
- 一旦data中的数据发生改变,模板(页面)中用到该数据的地方也会自动更新
<html>
......
<body>
<div id="root">
<h1>
hello, {{name}}
</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
// 创建Vue实例
new Vue({
el: '#root', // 用于指定当前Vue实例为哪个容器服务,值通常为css值选择器字符串
data: { // 用于存储数据,数据供el所指定的容器去使用
name: 'cyr'
}
})
// 容器和实例,一一对应
</script>
</body>
</html>
注意区分: js表达式 和 js代码(语句)
-
表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
例:a、a + b、demo(1)、x === y ? ‘a’ : ‘b’
-
js代码(语句)
例:if(){}、for(){}
2、模板语法
-
插值语法(解析标签体内容)
{{表达式}}
-
指令语法(解析标签:标签属性、标签体内容、绑定事件)
v-bind(:)、v-model、v-on(@)、v-if、v-else、v-for、v-show
3、数据绑定
v-bind 单向数据绑定(数据只能从data流向页面)
v-model 双向数据绑定(数据不仅能从data流向页面,也能从页面流向data),只能应用在表单类元素(输入类元素)
其中,v-model:value可简写为v-model,因v-model默认收集的就是value值
4、_el与data的两种写法
<html>
......
<body>
<div id="root">
<h1>
hello, {{name}}
</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
// el的两种写法
// 方法一
// 创建Vue实例到对应的容器
new Vue({
el: '#root'
data: {
name: 'cyr'
}
})
// 方法二
// 创建Vue实例
const v = new Vue({
data: {
name: 'cyr'
}
})
console.log(v)
// 挂载到对应的容器
v.$mount('#root')
// data的两种写法
new Vue({
el: '#root',
// 第一种写法:对象式
data: {
name: 'cyr'
},
// 第二种写法:函数式
// 不可使用箭头函数,不然this就是指向Window
// data: function() {
data() {
console.log('此处的实例式Vue实例对象', this)
return {
name: 'cyr'
}
}
})
</script>
</body>
</html>
三、理解MVVM模型
1、M
模型Model => 对应data中的数据
2、V
视图View => 模板
3、VM
视图模型ViewModel => Vue实例对象
4、关系
VM
M --数据绑定(Data Bindings)–> V
V --数据建通(Dom Listeners)–> M
四、数据代理
1、回顾Object.defineProperty
给对象添加属性
let num = 18
const person = {
name: 'cyr',
sex: '女',
// age: 18
}
Object.defineProperty(person, 'age', {
// value: 18,
// enumerable: true // 控制属性是否可枚举(遍历),默认值是false
// writable: true // 属性是否可被修改,默认值是false
// configuable: true // 属性是否可被删除,默认值是false
// 当有人读取person的age属性时,get函数(getter)会被调用,且返回值就是get的值
get: function(){
console.log('有人读取age属性的值')
return num
},
// 当有人修改person的age属性时,set函数(setter)会被调用,且会收到修改的具体值
set: function(value){
console.log('有人修改age属性的值', value)
num = value
}
})
console.log(Object.keys(person))
2、理解数据代理
通过一个对象代理另一个对象中属性的操作
let obj = { x: 1}
let obj2 = { y: 2 }
Object.defineProperty(obj2, 'x', {
get() {
return obj.x
},
set(value) {
obj.x = value
}
})
3、Vue的数据代理
-
通过vm对象来代理data对象中属性值的操作(读/写)
-
更加方便的操作data中的数据
-
基本原理:
通过Object.defineProperty()把data对象中所有的属性添加到vm上
为每一个添加到vm上的属性,都指定一个getter/setter
在getter/setter内部去操作(读/写)data中对应的属性
其中,vm._data是定义Vue实例的options.data
五、事件
1、事件处理
基本使用
- 使用v-on:XXX或@XXX绑定事件,其中XXX是事件名称
- 事件的回调需要配置在methods对象上,最终会在vm上
- methods上配置的函数,不要用箭头函数,否则this就不是vm
- methods上配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对
- @click=“demo” 和 @click=“demo($event)” 效果一样,但后者可以传参
<html>
...
<body>
<div id="root">
<h2>
欢迎来到{{name}}的vue学习
</h2>
<button @click="showinfo1">
点我提示信息1(不传参)
</button>
<button @click="showinfo2($event, 66)">
点我提示信息2(传参)
</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data: {
name: 'cyr'
},
methods: {
showInfo1(event) {
console.log(event.target.innerText)
console.log(this, this === vm)
alert('你好')
},
showInfo: (event) => {
console.log(this, this === Window)
},
showInfo2(event, number) {
console.log(event.target.innerText)
console.log(this, this === vm)
console.log(event, number)
alert('你好' + number)
}
}
})
</script>
</html>
2、事件的修饰符
- prevent:阻止默认事件
- stop:阻止事件冒泡
- once:事件只触发一次
- capture:使用事件的捕获模式
- self:只有event.target是当前操作的元素才触发的事件
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕
3、键盘事件
-
常用的按键别名:回车enter、删除delete、退出esc、空格space、换行tab、上up、下down、左left、右right
-
Vue未提供别名的按键,可以使用按键最原始的key值去绑定,但注意要转为kebab-case(短横线命名)
-
系统修饰键(用法特殊):ctrl、alt、shift、meta
配合keyup使用:按下修饰键的同时,再按下其他键,事件才会被触发
配合keydown使用:正常触发事件
-
也可以使用keyCode去指定具体按键(不推荐)
-
Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
六、计算属性
1、计算属性:computed
-
定义:要用的属性不存在,要通过已有属性计算得来的
-
原理:底层借助了Object.defineproperty方法提供的getter和setter
-
get方法什么时候执行?1、初读时会执行一次 2、所依赖的数据发生变化时
-
优势:比methods实现相比,内部有缓存机制(复用),效率更高,调试更方便
-
备注:
1、计算属性最终会出现在vm上,直接读取使用即可。
2、如果计算属性要被修改,那必须写set函数去响应修改,且set函数要引起计算时所依赖的数据发生变化
const vm = new Vue({
el: '#root',
data: {
firstName: '陈',
lastName: '三'
},
// 计算属性
computed: {
// 完整写法
fullName: {
// 当有人读取时,get就会被调用,且返回值就作为fullName的值
// get什么时候调用?1、初次读取fullName时(有缓存) 2、所依赖的数据发生变化
get() {
// console.log(this) // 此处的this是vm
return this.firstName + '/' + this.lastName
},
// set什么时候调用?当fullName被修改时
set(val) {
const [this.firstName, lastName] = val.split('/')
}
}
// 简写(只用get, 不用set)
fullName11(){
// console.log(this) // 此处的this是vm
return this.firstName + '/' + this.lastName
}
}
})
2、同methods的区别
计算属性computed是有缓存的,只有初次读取的时候或所依赖的数据发生变化时,才会去调用get方法
方法methods是只要页面引用多少次,就会被调用多少次
七、监听
1、监听:watch
-
当被监视的属性变化时,回调函数自动调用,进行相关操作
-
监视的属性必须存在,才能进行监视
-
监视的两种写法
1、new Vue传入watch配置
2、通过vm.$watch监视
const vm = new Vue({
el: '#root',
data: {
isHot: true,
isHot1: true,
numbers: {
a: 1,
b: 2
}
},
watch: {
// 正常写法
isHot: {
immediate: true, // 初始化时让handler调用下
// handler什么时候调用?当isHot发生变化时
handler(newVal, oldVal) {
console.log('isHot被修改了', newVal, oldVal)
}
},
// 简写写法
isHot1(newVal, oldVal) {
console.log('isHot1被修改了', newVal, oldVal)
},
// 监视多级结构中某个属性的变化
'numbers.a': {
immediate: true, // 初始化时让handler调用下
// handler什么时候调用?当isHot发生变化时
handler(newVal, oldVal) {
console.log('isHot被修改了', newVal, oldVal)
}
},
// 监视多级结构中所有属性的变化
numbers: {
deep: true, // 开启深度监视
// handler什么时候调用?当isHot发生变化时
handler(newVal, oldVal) {
console.log('isHot被修改了', newVal, oldVal)
}
}
}
})
// 也可以用实例来进行监视
// 正常写法
vm.$watch('isHot', {
immediate: true, // 初始化时让handler调用下
// handler什么时候调用?当isHot发生变化时
handler(newVal, oldVal) {
console.log('isHot被修改了', newVal, oldVal)
}
})
// 简写写法
vm.$watch('isHot1', function (newVal, oldVal) {
console.log('isHot被修改了', newVal, oldVal)
})
2、深度监听
-
Vue中的watch默认不监测对象内部值的改变(一层)
-
配置deep: true可以监测对象内部值改变(多层)
-
备注:
1、Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
2、使用watch时根据数据的具体结构,决定是否采用深度监听
3、watch对比computed
-
computed能完成的功能,watch都可以完成
-
watch能完成的功能,computed不一定能完成。例如watch可以进行异步操作
-
两个重要小原则:
1、所被Vue管理的函数,最好写成普通函数,这样this指向才是vm 或 组件实例对象
2、所有不被Vue管理的函数(定时器的回调函数、ajax的回调函数、Promise的回调函数等),最好写成箭头函数,这样this的指向才时 vm 或 组件实例对象
八、绑定样式
1、绑定class样式
- :calss=‘xxx’
- 表达式是字符串:‘classA’
- 表达式是对象:{classA: true, classB: false}
- 表达式是数组:[‘calssA’, 'classB ']
2、绑定style样式
- :style=‘xxx’
- 表达式是字符串:‘font-size: 16px; color: red;’
- 表达式是对象:{fontSize: ‘16px’, color: red}
- 表达式是数组:[{fontSize: ‘16px’, color: red}, {backgroundColor: gray}]
九、列表
1、条件渲染
v-show
值:true/false
dom节点存在,调整样式display:none
v-if/v-else-if/v-else
值:true/false
不展示dom节点,直接被移除掉
切换频率大的话,建议使用v-show
2、列表渲染
<html>
...
<body>
<div id="root">
<!-- 遍历数组 -->
<h2>人员列表</h2>
<ul>
<li v-for="(p, index) of persons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
<!-- 遍历对象 -->
<h2>汽车信息</h2>
<ul>
<li v-for="(val, k) of car" :key="k">
{{k}}-{{val}}
</li>
</ul>
<!-- 遍历字符串 -->
<h2>测试遍历字符串</h2>
<ul>
<li v-for="(char, index) of str" :key="index">
{{char}}-{{index}}
</li>
</ul>
<!-- 遍历指定次数 -->
<h2>测试遍历指定次数</h2>
<ul>
<li v-for="(number, index) of 5" :key="index">
{{number}}-{{index}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data: {
persons:[
{id:'0001', name: 'cyr', age: 18},
{id:'0002', name: 'll', age: 19},
{id:'0003', name: 'yy', age: 20}
],
car: {
name: '奥迪',
price: '10万'
},
str: 'hello'
}
})
</script>
</html>
key的作用和原理(面试题:react、vue的key有什么作用?)–给节点做标识,就像人类的身份证
-
虚拟dom中key的作用:
key是虚拟dom对象的标识,当状态中的数据发生改变时,Vue会根据【新数据】生成【新的虚拟dom】,随后Vue进行【新的虚拟dom】的差异比较
-
差异对比规则:
- 旧虚拟dom中找到了与新虚拟dom相同的key
- 若虚拟dom中的内容没改变,直接使用之前的真实dom
- 若虚拟dom中的内容发生改变,则生成新的真实dom,随后替换页面中之前的真实dom
- 旧虚拟dom中未找到与新虚拟dom相同的key
- 创建新的真实dom,随后渲染到页面
- 旧虚拟dom中找到了与新虚拟dom相同的key
-
用index作为key可能会引发的问题
- 若对数据进行:逆序添加、逆序删除等破坏顺序的操作,会产生没有必要的真实dom更新(页面效果没问题,但是效率低)
- 如果结构中还包含输入类的dom,会产生错误的dom更新(界面有问题)
-
开发中如何选择key
- 最好使用每条数据的唯一标识作为key,比如id、手机号等唯一值
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表作于展示,使用index作为key是没有问题
3、列表过滤
<html>
...
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyWord" />
<ul>
<li v-for="(p, index) of filPersons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
// 用watch实现
const vm = new Vue({
el: '#root',
data: {
keyWord: '',
persons:[
{id:'0001', name: 'cyr', age: 18},
{id:'0002', name: 'll', age: 19},
{id:'0003', name: 'yy', age: 20}
],
filPersons: []
},
watch: {
keyWord: {
handler(val) {
immediate: true,
this.filPerson = this.persons.filter(e => {
return e.name.indexOf(val) !== 1
})
}
}
}
})
// 用computed实现
const vm1 = new Vue({
el: '#root',
data: {
keyWord: '',
persons:[
{id:'0001', name: 'cyr', age: 18},
{id:'0002', name: 'll', age: 19},
{id:'0003', name: 'yy', age: 20}
]
},
computed: {
filPersons(){
return this.persons.filter(e => {
return e.name.indexOf(this.keyWord) !== 1
})
}
}
})
</script>
</html>
4、列表排序
<html>
...
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyWord" />
<button @click="sortType = 2">年龄升序</button>
<button @click="sortType = 1">年龄降序</button>
<button @click="sortType = 0">原顺序</button>
<ul>
<li v-for="(p, index) of filPersons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data: {
sortType: 0, // 0:原顺序 1:降序 2:升序
keyWord: '',
persons:[
{id:'0001', name: 'cyr', age: 18},
{id:'0002', name: 'll', age: 19},
{id:'0003', name: 'yy', age: 20}
]
},
computed: {
filPersons(){
consr arr = this.persons.filter(e => {
return e.name.indexOf(this.keyWord) !== 1
})
if (this.sortType) {
arr.sort((p1, p2) => {
return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
})
}
return arr
}
}
})
</script>
</html>
十、监测数据的原理
1、监测对象的原理
<html>
...
<body>
<div id="root">
<h2>人员列表</h2>
<button @click="updateFirstInfo">
点击修改第一条信息
</button>
<ul>
<li v-for="(p, index) of persons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data: {
persons:[
{id:'0001', name: 'cyr', age: 18},
{id:'0002', name: 'll', age: 19},
{id:'0003', name: 'yy', age: 20}
]
},
methods: {
updateFirstInfo() {
// 有效更改
this.persons[0].name = 'test'
this.persons[0].age = 21
// 无效更改(Vue无法识别到更改,并响应到页面)
this.persons[0] = {id:'0001', name: 'test', age: 21}
}
}
})
</script>
</html>
原理理解:
let data = {
name: 'cyr',
address: '上海'
}
// 创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data)
console.log(obs)
// 准备一个vm实例对象
const vm ={}
vm._data = data = obs
function Observer(obj) {
// 汇总对象中所有的属性形成一个数组
const keys = Object.keys(obj)
// 遍历
keys.forEach((k) => {
Object.defineProperty(this, k, {
get() {
return obj[k]
},
set(val) {
console.log(`${k}被改了,我要去解析模板,生成虚拟dom,进行比较,生成真实dom,渲染页面......`)
obj[k] = val
}
})
})
}
2、_Vue.Set()方法
<html>
...
<body>
<div id="root">
<h2>人员列表</h2>
<button @click="addSex">
添加性别信息
</button>
<h5>
姓名: {{personInfo.name}}
</h5>
<h5>
年龄: {{personInfo.age}}
</h5>
<h5 v-if="personInfo.sex">
性别: {{personInfo.sex}}
</h5>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data: {
personInfo:{
id:'0001',
name: 'cyr',
age: 18
}
},
methods: {
addSex() {
// 无效更改(Vue无法识别到更改,并响应到页面)
this.personInfo.sex = '女'
vm._data.personInfo.sex = '女'
// 有效更改
this.$set(this.personInfo, 'sex', '女')
}
}
})
vm.$set === Vue.set
</script>
</html>
3、监测数组的原理
<html>
...
<body>
<div id="root">
<h2>人员列表</h2>
<button @click="updateFirstInfo">
点击修改第一条信息
</button>
<ul>
<li v-for="(p, index) of persons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data: {
persons:[
{id:'0001', name: 'cyr', age: 18},
{id:'0002', name: 'll', age: 19},
{id:'0003', name: 'yy', age: 20}
]
},
methods: {
updateFirstInfo() {
this.persons[0].splice(0, 1, {id:'0001', name: 'test', age: 21})
this.$set(this.persons, 0, {id:'0001', name: 'test', age: 21})
}
}
})
</script>
</html>
修改数组的方法(7个)
push()、pop()、shift()、unshift()、splice()、sort()、reverse()
只能操作这几个方法,才能监测数组的变化
Vue包装了这几个方法,来响应更新视图
4、总结
-
vue会监视data中所有层次的数据
-
如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据
- 对象后追加的属性,Vue默认不做响应式处理
- 如需给后添加的属性做响应式,请使用如下API:
Vue.set(target, propertyName/index, value)
或Vm.$set(target, propertyName/index, value)
-
如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事
- 调用原生对应的方法对数组进行更新
- 重新解析模板,进而更新页面
-
在vue修改数组中的某个元素一定要用如下方法:
- 使用这些API:
push()/pop()/shift()/unshift()/splice()/sort()/reverse()
Vue.set()
或vm.$set()
- 使用这些API:
-
特别注意:
Vue.set()
和vm.$set()
不能给vm或vm的跟数据对象,添加属性
十一、收集表单数据
<html>
...
<body>
<div id="root">
<form @submit.prevent="submit">
<label for="acount">账号</label>
<input type="text" id="acount" v-model.trim="userInfo.account" />
<br/><br/>
<label for="password">密码</label>
<input type="password" id="password" v-model="userInfo.password" />
<br/><br/>
<label for="age">年龄</label>
<input type="number" id="age" v-model.number="userInfo.age" />
<br/><br/>
<label for="sex">性别</label>
男<input type="radio" name="sex" id="sex" value="male" v-model="userInfo.sex" />
女<input type="radio" name="sex" id="sex" value="female" v-model="userInfo.sex" />
<br/><br/>
<label for="hoby">爱好</label>
学习<input type="checkbox" name="hoby" id="hoby" value="study" v-model="userInfo.hoby" />
打游戏<input type="checkbox" name="hoby" id="hoby" value="game" v-model="userInfo.hoby" />
吃饭<input type="checkbox" name="hoby" id="hoby" value="eat" v-model="userInfo.hoby" />
<br/><br/>
<label for="city">所属校区</label>
<select v-model="userInfo.city">
<option value="">请选择</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzheng">深圳</option>
</select>
<br/><br/>
<label for="other">其他信息</label>
<textarea id="other" v-model.lazy="userInfo.other" />
<br/><br/>
<input type="checkbox" v-model="userInfo.agree" />
阅读并接受<a href="https://baidun.com/">《用户协议》</a>
<br/><br/>
<button>提交</button>
</form>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data: {
userInfo: {
account: '',
password: '',
age: '',
sex: 'female',
hoby: [], // 多组勾选框,初始值必须为数组,不然后续的值是只有true/false的布尔型
city: '',
other: '',
agree: ''
}
},
methods: {
submit() {
console.log(JSON.stringfy(this.userInfo))
}
}
})
</script>
</html>
- 若
<input type='text' />
,则v-model收集的是value值,用户输入的就是value值 - 若
<input type='radio' />
,则v-model收集的是value值,且要给标签配置value值 - 若
<input type='checkbox' />
- 没有配置input的value值,那么收集的就是checked(勾选 or 未勾选,布尔值)
- 配置input的value属性:
- v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,布尔值)
- v-model的初始值是数组,那么收集的就是value组成的数组
- 备注:v-model的三个修饰符
- lazy:失去焦点再收集数据
- number:输入字符串转为有效数字
- trim:输入首尾空格过滤
十二、过滤器
<html>
...
<body>
<div id="root">
<h2>显示格式化后的时间</h2>
<h3>现在是(时间戳): {{time}}</h3>
<h3>现在是(计算属性实现): {{fmtTime}}</h3>
<h3>现在是(methods实现): {{getFmtTime()}}</h3>
<h3>现在是(过滤器实现): {{time | timeFormat}}</h3>
<h3>现在是(过滤器实现+传参): {{time | timeFormat('YYYY年MM月DD日')}}</h3>
<h3>现在是(过滤器实现+传参+多个): {{time | timeFormat('YYYY年MM月DD日') | mySlice}}</h3>
<h3 :x="name | mySlice">dom属性加过滤器</h3>
</div>
</body>
<script type="text/javascript" src='../js/dayjs.min.js'></script>
<script type="text/javascript">
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
// 全局过滤器
Vue.filter('mySliceAll', function(value) {
return value.slice(0, 5)
})
const vm = new Vue({
el: '#root',
data: {
time: 1685455602818,
name: 'hello word'
},
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: {
// 第一个参数永远是传入值,再跟上模板里定义的参数
timeFormat(value, str = 'YYYY-MM-DD HH:mm:ss') {
// console.log('@', value)
return dayjs(value).format(str)
},
mySlice(value) {
return value.slice(0, 5)
}
}
})
</script>
</html>
1、定义
对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
2、语法
- 注册过滤器:Vue.filter(name, callback) 或 new Vue({filters: {}})
- 使用过滤器: {{ xxx | 过滤器名}} 或 v-bind:属性 = " xxx | 过滤器名"
3、备注
- 过滤器也可以接收额外参数,多个过滤器也可以串联
- 并没有哦改变原本的数据,是产生新的对应数据
十三、指令
1、回顾
- v-bind:单向绑定解析表达式,可简写为 :xxx
- v-model:双向数据绑定
- v-for:遍历数组/对象/字符串
- v-on:绑定事件箭头,可简写为 @xxx
- v-if:条件渲染(动态控制节点是否存在)
- v-else:条件渲染(动态控制节点是否存在)
- v-show:条件渲染(动态控制节点是否展示)
2、v-text
- 作用:向其所在的节点渲染文本内容
- 与插值语法的区别:v-text会替换掉节点中的内容, {{xx}}则不会
3、v-html
- 作用:向指定节点中渲染包含html结构的内容
- 与插值语法的区别:
- v-html会替换掉节点中的内容, {{xx}}则不会
- v-html可以识别html结构
- 严重注意:v-html有安全性问题
- 在网站上动态渲染HTML是非常危险的,容易导致XSS攻击,冒充用户的身份(如:document.cookie可以获取到当前用户的登录凭证)
- 一定要在可信的内容上使用v-html,不要在用户提交的内容上
4、v-cloak
- 本身是没有值的
- 本质是一个特殊属性,Vue创建完毕并接管容器后,会删掉v-cloak属性
- 使用css配合v-cloak可以解决网速慢时,页面展示出{{xxx}}的问题
5、v-once
- 本身是没有值的
- v-once所在节点在初次动态渲染后,就视为静态内容了
- 以后数据的改变不会引起v-once所在结构的更新,可用于优化性能
6、v-pre
- 跳过其所在节点的编译过程
- 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
7、自定义指令
(1)函数式
<html>
...
<body>
<div id="root">
<h2>当前的n值:<span v-text="n"></span></h2>
<h2>放大十倍后的n值:<span v-big="n"></span></h2>
<h2>放大十倍后的n值1:<span v-big-number="n"></span></h2>
<button @click="n++">
点我n+1
</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
// 全局指令写法
Vue.directive('big-all', function(element, binding){
console.log('big', this) // this是指向window
console.dir(element)
console.log(element instanceof HTMLElement)
console.log(element, binding.value)
element.innerText = binding.value*10
})
const vm = new Vue({
el: '#root',
data: {
n: 1
},
// 局部指令
directives: {
// 何时会被调用:1、指令与函数成功绑定时。2、指令所在的模板被重新解析时
big(element, binding){
console.log('big', this) // this是指向window
console.dir(element)
console.log(element instanceof HTMLElement)
console.log(element, binding.value)
element.innerText = binding.value*10
},
'big-number'(element, binding){
console.dir(element)
console.log(element instanceof HTMLElement)
console.log(element, binding.value)
element.innerText = binding.value*10
},
}
})
</script>
</html>
(2)对象式
<html>
...
<body>
<div id="root">
<h2>当前的n值:<span v-text="n"></span></h2>
<h2>放大十倍后的n值:<span v-big="n"></span></h2>
<h2>放大十倍后的n值1:<span v-big-number="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; // 阻止vue在启动时生成生产提示
// 全局指令写法
Vue.directive('fbind-all', {
// 指令与函数成功绑定时
bind(element, binding){
console.log('bind')
element.value = binding.value
},
// 指令所在的元素被插入页面时
inserted(element, binding){
console.log('inserted')
element.focus()
},
// 指令所在的模板被重新解析时
update(element, binding) {
element.value = binding.value
}
})
const vm = new Vue({
el: '#root',
data: {
n: 1
},
// 局部指令
directives: {
// 何时会被调用:1、指令与函数成功绑定时。2、指令所在的模板被重新解析时
big(element, binding){
console.log('big', this) // this是指向window
console.dir(element)
console.log(element instanceof HTMLElement)
console.log(element, binding.value)
element.innerText = binding.value*10
},
'big-number'(element, binding){
console.dir(element)
console.log(element instanceof HTMLElement)
console.log(element, binding.value)
element.innerText = binding.value*10
},
// fbind(element, binding) {
// element.value = binding.value
// // 初始化,无法实现聚焦,因其真实dom还未渲染到页面
// element.focus()
// }
fbind: {
// 指令与函数成功绑定时
bind(element, binding){
console.log('bind')
element.value = binding.value
},
// 指令所在的元素被插入页面时
inserted(element, binding){
console.log('inserted')
element.focus()
},
// 指令所在的模板被重新解析时
update(element, binding) {
element.value = binding.value
}
}
}
})
</script>
</html>
(3)总结
- 定义语法:
- 局部指令:
new Vue({ directives: {指令名: 配置对象} 或 directives: {指令名: 回调函数} })
- 全局指令:
Vue.directives(指令名, 配置对象)
或Vue.directives(指令名, 回调函数)
- 局部指令:
- 配置对象中常用的3个回调:
- bind:指令与元素成功绑定时调用
- inserted:指令所在元素被插入页面时调用
- update:指令所在模板被重新解析时调用
- 备注:
- 指令定义时不加v-,但使用时要加v-
- 指令如果时多个单词,要使用kebab-case命名方式,不要使用camelCase命名
十四、生命周期
1、含义
- 又名:生命周期回调函数、生命周期函数、生命周期钩子
- 是什么:Vue在关键时刻帮我们调用的一些特殊名称函数
- 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
- 生命周期函数的this指向是vm 或组件实例对象
2、挂载流程
-
beforeCreate:初始化,生命周期、事件,但数据代理还未开始
此时,无法通过vm访问到data中的数据、methods中的方法
-
created:初始化,数据监测、数据代理
此时,可以通过vm访问到data中的数据、methods中的方法
-
beforeMount:此阶段Vue开始解析模板,生成虚拟DOM(内存中),页面还不能显示解析好的内容
此时,页面呈现的是未经Vue编译的DOM结构;所有对DOM的操作,最终都不奏效
-
mounted:将内存中的虚拟DOM转为真实DOM,并插入页面
此时,页面呈现的是经过Vue编译的DOM结构;对DOM的操作均有效(尽可能避免)
至此,初始化过程结束,一般在此进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件等初始化操作
其中:
(1) Has “el” option?
NO: when vm.$mount(el) is called
YES:
(2) Has “template” option?
NO: Compile el’s outerHTML as template
YES: Compile template into render function
3、更新流程
-
beforeUpdate:when data changes
此时,数据最新的,但页面是旧的,即页面尚未和数据保持同步
-
updated:根据新数据,生成新的虚拟DOM,随后与旧的虚拟DOM进行比较,最终完成页面更新,即完成Model -> View的更新
此时,数据最新的,页面也是最新的,即页面和数据保持同步
4、销毁流程
-
beforeDeatroy:when em.$destroy() is called
此时,vm中所有:data、methods、指令等等,都处于可用状态,马上要执行销毁过程
一般在此过程,关闭定时器、取消订阅消息、解绑自定义事件等收尾操作
-
destroyed:
5、总结
vm的一生:
- 将要创建 ===> 调用beforeCreate函数
- 创建完毕 ===> 调用created函数
- 将要挂载 ===> 调用beforeMount函数
- 挂载完毕 ===> 调用mounted函数
- 将要更新 ===> 调用beforeUpdate函数
- 更新完毕 ===> 调用updated函数
- 将要销毁 ===> 调用beforeDestroy函数
- 销毁完毕 ===> 调用destroyed函数
常用的生命周期钩子
- mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等初始化操作
- beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等收尾工作
关于销毁Vue实例
- 销毁后借助Vue开发者工具看不到任何消息
- 销毁后自定义事件会失效,但原生DOM事件依然有效
- 一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了