Vue2学习知识点统计
容器与vue实例的关系
点击查看代码
<h3>Vue与容器是一对一绑定关系,即使有多个容器,Vue也只能处理一个,后续容器将不再处理</h3>
<!-- 准备容器 -->
<div class='anbin'>
{{age}}
</div>
<div class='anbin'>
{{age}}
</div>
<!-- Vue程序 -->
<script>
/* new Vue({
el: '.anbin',
data: {
age: 50
},
}) */
let option = {
el: '.anbin',
data: {
age: 'hello'
}
}
new Vue(option)
</script>
- new Vue得到的是vue的实例对象,vue文档中把这个实例命名为vm,所以一般说vm就是值vue的实例
- 容器就是要被渲染的HTML元素,例如上述的
<div class="anbin"></div>
,就是一个容器 - 容器与vue实例的关系是一 一对应的,可以理解为现代法律规则的一夫一妻制,不允许出现多个容器对应一个vm,也不允许出现多个vm对应一个容器,当容器/vm重复渲染时,vue只会编译渲染一个,后续的将不再编译。
- js中声明的变量,无法被vue识别使用
插值语法
点击查看代码
<!-- 容器 -->
<div id="anbin">
<div>
<p>渲染变量字符串:{{msg}}</p>
<p>
渲染变量函数: {{sayHello()}}
</p>
<p>
渲染变量常量: {{one}}
</p>
</div>
<br>
<div>
<code>表达式:能返回结果值的js代码</code>
<p>
渲染常量表达式 : {{1+10}}
</p>
<p>
渲染常量字符串拼接表达式 : {{'my ' + 'name ' + 'is ' + 'anbin'}}
</p>
<p>
渲染字符串和常量拼接表达式: {{'msg' + 1}}
</p>
<p>
渲染三元运算表达式:{{one ? '真' : '假'}}
</p>
<p>
渲染变量常量数值和常量数值计算表达式:{{one + 1}}
</p>
<p>
渲染方法表达式:{{msg.split('').reverse().join('')}}
</p>
</div>
<br>
<div>
<code>vue内置了一些全局变量的白名单,在白名单中的全局变量,可以配合其方法,用来当作表达式</code>
<br>
<p>
全局变量有这些:
<code>
Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,require
</code>
</p>
<p>调用某个全局变量Date的now方法值为:{{Date.now()}}</p>
</div>
<h3>
<code>使用let、var、const等js变量:不可以被vue识别渲染,控制台会报错:{{age}}---{{name}}--{{hobby}}</code>
</h3>
</div>
<!-- vue实例 -->
<script>
// 自定义一些全局变量,给上面测试使用
var age = 18;
let name = 'anbin';
const hobby = 'coding';
new Vue({
el: '#anbin',
data: {
msg: 'helloVue', //字符串
one: 1, //常量
sayHello: function say() { //函数
console.log('say sayHello Success!')
}
}
})
</script>
插值语法就是HTML中被花括号包裹的变量值
插值语法用于向页面插入数据,以供被页面正常渲染
一般来说数据来源由data
提供,但跳过data
直接在页面中使用常量也是可以的,例如上述的{{1+10}}将输出11
所以插值语法准确来说:是将任意的值插入到HTML页面中,同时支持任何表达式;为什么说支持表达式?因为表达式必须返回一个结果值,所有不能返回结果值的js代码都不被插值语法认可
插值语法支持多种变量类型的表达式,例如上述的变量+常量
,变量+变量
、三元运算
、变量使用方法
等等...
参考文档:插值
指令
点击查看代码
<div id="anbin">
<div>
<p>{{msg}}</p>
</div>
<div>
<p>
<code>v-once:让值只编译一次,后续当值发生变化时不重新编译,从而提升页面性能</code>
</p>
<p>
<code>v-if:if条件判断</code>
<code v-if=" ten>nine ? true : false ">{{res}}</code>
</p>
<p>
<code>v-bind:绑定动态属性,用于动态操作属性的改变</code>
<br>
<!-- v-bind绑定的src属性,改变image_path变量的值,动态更换图片路径,以实现页面图片的动态切换-->
<img v-bind:src="image_path" width="150">
</p>
<p>
v-model指令: <input type="text" v-model:value="nameTwo">
</p>
</div>
</div>
<script>
new Vue({
el: '#anbin',
data: {
msg: 'hello vue',
nine: 9,
ten: 10,
res: '你看到我了',
image_path: '/favicon.ico',
nameTwo: '李四',
}
})
</script>
- vue中所有指令都是以属性的方式存在
- v-once:用于只编译一次数据,后续更改数据将不会重新编译,即不会重新渲染这个值
- v-if:用于条件分支中的if判断; v-if的判断条件中要写表达式,例如 v-if="1>0"
- v-bind:用于动态的操作HTML元素属性的值,例如想动态的更改img标签中的src图片路径、动态更改超链接中的href网址,就可以使用到。
- v-model:用于表单操作,从view层响应式地更改数据到model层、有使用限制:只能用于:
input、textarea、select等html元素和component组件中
,其余地方使用无效;通俗易懂的来说,使用v-model必须得有一个更改数据的入口,如果没有那就不能使用。
参考文档:指令
函数默认对象
点击查看代码
<body>
<div id="anbin">
<p>
<button @click="layerOne">点我触发弹窗,不传递函数参数版本</button>
<br>
<code>
当不需要向函数中传递参数时,函数可以省略小括号(),此时函数只有一个参数<br/>
那就是event对象作为参数,注意是自动的隐示接收,直接在函数括号中声明event对象即可,参照layerOne <br>
但不建议这么做,会造成可读性极差后期难以维护
</code>
</p>
<p>
<button @click="layerTwo($event,'你好')">点我触发弹窗,函数需要开发者自定义传递参数版本</button>
<br>
<code>
此时在函数中要获取event对象,就必须使用$event来显示声明参数,参照layerTwo
</code>
</p>
</div>
<script src="/js/vue2.js"></script>
<script>
new Vue({
el: '#anbin',
data: {
},
methods: {
layerOne(event) {
console.log('button对应的event对象被传入了',event)
alert('隐示调用')
},
layerTwo(event, content) {
console.log(event)
alert('显示调用:' + content)
}
}
})
</script>
</body>
- 当模板中声明函数,不传任何参数且不写括号时,
会向函数中隐示传递传递event对象参数
,可以在对应的方法中接收并打印,例如此处的layerOne 不建议在模板中声明函数,使用这种不传递任何参数且省略括号的行为,这会造成可读性极差难以维护的情况
当模板中使用了括号传递参数时,要获取event对象,则需要在括号内使用$event来显示接收,且参数位置需一一对应
,例如此处的layerTwo
this指向与箭头函数
点击查看代码
<body>
<div id="anbin">
<h4>关于事件回调函数中的this指向问题</h4>
<p>
<button @click="addNumber">点我使用<code style="color: green;">普通函数</code>实现第一个计数器,<code
style="color: green;">正常运行</code></button>
<code>现在number的值是:{{number}}</code>
</p>
<p>
<button @click="addInt">点我使用<code style="color: red;">箭头函数</code>实现第二个计数器,发现<code
style="color: red;">无法成功</code></button>
<code>现在int的值是:{{int}}</code>
</p>
</div>
<script src="/js/vue2.js"></script>
<script>
// 此处vm是此处Vue实例对象
const vm = new Vue({
el: "#anbin",
data: {
number: 1,
int: 100
},
methods: {
// 普通函数
addNumber() {
alert('去查看控制台,观察信息')
console.log('this是vm吗:', this === vm);
console.log('既然this是vm,那他们取number值是一样的吗:', vm.number === this.number);
console.log('此处this对象是:', this)
this.number++;
},
// 箭头函数
addInt: () => {
alert('去查看控制台,观察信息')
console.log('this是vm吗:', this === vm);
console.log('既然this是vm,那他们取int值是一样的吗:', vm.int === this.int);
/*
此处无法成功
是因为vue中正常情况下,this实际是Vue实例对象本身,this代表的作用域也就是Vue实例对象作用域
而箭头函数本身没有作用域,this的作用域只能从父级继承获得,而箭头函数的父级就是整个页面的window对象,所以这里的this实际是window而不是vue
*/
console.log('此处this对象是:', this)
this.int++;
},
}
})
</script>
</body>
首先我们知道vm就是模板中的Vue实例对象
- 在普通函数中,
使用的this取值,这个this其实就是vm,也就是这个Vue实例对象,所以this的作用域是这个Vue实例对象的作用域
,也就可以正常的从Vue实例对象中取值了,例如此处this.number - 在箭头函数中
,箭头函数本身是没有this这个概念的,也就是说箭头函数本身没有作用域,
- 需要继承父级作用域来当做箭头函数本身的作用域,
那么此时this就不会是Vue实例对象了,而是最外层最原始js的window对象
,也就是说this的作用域就是window对象的作用域,那么使用this.int自然就取不到Vue实例对象中的int值了,因为二者作用域都不在一块
参考文档:
computed计算属性(完整写法)
点击查看代码
<div id="app">
<h3>{{msg}}</h3>
<p>
<b>需求:在input框中输入字符串,通过处理后在页面上渲染 字符串被反转后的 结果。</b>
</p>
<div>
输入的信息
<input type="text" v-model="content">
</div>
<br><br><br>
<div>
<b>第1种方式:methods实现(这里故意调用多次methods,查看console是否执行了多次运算)</b>
<div>反转后的信息
{{splitReverseStr()}}
</div>
{{splitReverseStr()}}
{{splitReverseStr()}}
</div>
<br><br><br>
<div>
<b>第2种方式:computed实现</b>
<div>反转后的信息
{{cReverseStr}}
</div>
</div>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
msg: '计算属性----反转字符串',
content: ''
},
methods: {
// 反转字符串
splitReverseStr() {
console.log('执行methods中的字符串运算')
return this.content.split('').reverse().join('')
}
},
computed: {
// 定义一个计算属性:cReverseStr
cReverseStr: {
get() {
console.log('触发了computed计算')
return this.content.split('').reverse().join('')
},
set(valuie) {
console.log(value)
}
}
}
})
</script>
- methods和compute都可以实现需求,
但更推荐使用computed
- methods方式实现较为死板,因为是methods是一个函数,所以在页面上调用多少次就会执行多少次。假如返回值是一个长期不变确定的值,
使用这种方式会造成效率低下原因(因为不会针对同一个值缓存,
而是呆板的重复运算,造成资源浪费
- computed则很好的解决了methods的这一痛点,computed底层会自行将执行的结果值存储起来,
当下一次调用改computed时,会优先判断本次结果值 是否与 上次产生的结果值相等
,
如若相等则直接返回上一次的结果值,不进行计算处理,有效的提升了性能与节省资源
参考文档:计算属性
计算属性缓存 vs 方法
computed计算属性的简写方式
点击查看代码
<div id="app">
<h3>{{msg}}</h3>
<p>
<b>
需求:在input框中输入字符串,通过处理后在页面上渲染 <br>
计算属性简写方式得到的 <br>
反转字符串的结果
</b>
</p>
<div>
输入的信息
<input type="text" v-model="content">
</div>
<br>
<div>
compuetd结果值:{{cReverseStr}}
</div>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
msg: '计算属性简写形式',
content: ''
},
computed: {
// 计算属性的简写
cReverseStr() {
console.log('触发了computed计算')
return this.content.split('').reverse().join('')
}
}
})
</script>
- 通过定义cReverseStr()的方式来简写computed的写法
这里的cReverseStr()是一个计算属性,而不是函数方法,请不要误解
- 什么时候使用computed的简写写法?
当只需要获取计算属性的值时(就是当get时
- 什么时候使用computed的完整写法?
当你不仅需要获取计算属性的值,还需要对计算属性的值进行修改时(不仅需要get,还需要set时
watch侦听属性(完整写法)
点击查看代码
<div id="app" v-cloak>
<h3>{{msg}}</h3>
<div>
<b>{{know}}</b>
<ul>
<li v-for="(item , key) in info">{{item}}</li>
</ul>
</div>
<div>
<p>
数字:{{number}}
</p>
<button @click="add">点我让数字递增,去控制台查看变化</button>
<br>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
msg: '侦听属性的变化',
know: '其余知识点',
info: ['watch的immediate配置项', 'watch可以监听(data和computed中)任意属性'],
number: 0,
},
methods: {
add() {
console.log('触发methods', 'add');
return this.number++;
}
},
computed: {
hehe() {
console.log('触发computed');
return ' 哈哈 ' + this.number
}
},
watch: {
// 这里监听了data中的普通属性值:number
number: {
immediate: true,
deep: true,
handler(newValue, oldVaule) {
console.log(`触发 number属性 的watch`, oldVaule, newValue);
}
},
// 这里监听了computed的计算属性值:hehe
hehe: {
immediate: true,
deep: true,
handler(newValue, oldVaule) {
console.log('触发 hehe属性 的watch', oldVaule, newValue);
}
}
},
})
</script>
-
watch侦听属性用于
监听一个属性值的变化
-
这里使用了watch侦听属性监听了data属性中的number属性,也监听了computed的hehe属性,所以我们知道
watch不仅可以监听普通data中的属性,还可以监听computed的属性,没有局限场景
-
watch侦听某个属性,
当属性值发生后,会自动向该属性的watch中传入两个参数
第一个是发生变化后的新值,第二个是变化前的旧值,参数名可以随意定义
这里为了更好的理解,我定义了newValue表示新值,oldVue表示旧值 -
watch的完整写法中,有2个配置项可以进行配置,这是可选的,并不是一定要写
-
第一个配置项:
immediate
作用:当页面初始化(一打开页面开始加载的时候)就执行watch侦听某个属性
,哪个属性配置了immediate就一开始监听,没配置的则不进行监听 -
第二个配置项:
deep
作用:当你的data数据是一个多层级数据时,进行该配置项的配置,可以直接监听到最深层级的对象值
需要注意的是,当对象的层级太多时,会产生监听不到旧值的情况
(这与vue的浅拷贝和深拷贝相关,涉及到内存,不甚了解 -
handler
作用:watch侦听属性,侦听到属性发生变化后,需要做一些逻辑操作,而逻辑操作就是在这个hanlder方法中实现的
需要注意的是:完整写法中 处理逻辑的方法名称必须是hanlder,不可以是其他
参考文档:侦听器
watch侦听属性的简写方式
点击查看代码
<div id="app">
<h3>{{msg}}</h3>
<div>
<b>{{know}}</b>
<ul>
<li v-for="(item , key) in info">{{item}}</li>
</ul>
</div>
<div>
<p>
数字:{{number}}
</p>
<button @click="add">点我让数字递增,去控制台查看变化</button>
<br>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
msg: '侦听属性的变化',
know: '其余知识点',
info: ['watch的immediate配置项', 'watch可以监听(data和computed中)任意属性'],
number: 0,
},
methods: {
add() {
console.log('触发methods', 'add');
return this.number++;
}
},
computed: {
hehe() {
console.log('触发computed');
return ' 哈哈 ' + this.number
}
},
watch: {
number(newValue, oldVaule) {
console.log(`触发 number属性 的watch`, oldVaule, newValue);
},
hehe(newValue, oldVaule) {
console.log('触发 hehe属性 的watch', oldVaule, newValue);
}
},
})
</script>
当不需要使用deep深度监听属性,也不需要使用immediate来执行页面初始化监听;同时满足这两者条件时,可以使用watch的简写写法来实现监听属性
- 例如这里的number和hehe,同样一个是监听data中的数据,一个是监听hehe中的数据,与完整写法一样可以实现功能,只是写法更为简便了
watch和computed选择哪个?
- compuetd和watch都能完成某个功能
不需要执行异步操作时
:选择computed,因为computed是同步操作,不支持异步需要执行异步/开销较大(耗时久)的操作时
:选择watch- 这里的异步,举个例子:3秒后执行一段逻辑/执行ajax请求
参考文档:计算属性 vs 侦听属性
表单提交
点击查看代码
<div id="app" v-cloak>
<h3>{{msg}}</h3>
<form>
<!-- 使用trim修饰符配合v-model,将输入的值去除前后空格 -->
<p>用户名:<input type="text" v-model.trim="user.user_name"></p>
<p>密码:<input type="password" v-model="user.password" autocomplete="off"></p>
<!-- 使用number修饰符配合v-model,将输入的值由字符串类型强转为number类型 -->
<p>年龄:<input type="number" v-model.number="user.age"></p>
<p>
性别:
<input type="radio" id="one" value="man" v-model="user.sex" checked>
<label for="one">男</label>
<input type="radio" id="two" value="woman" v-model="user.sex">
<label for="two">女</label>
</p>
<p>
爱好:
<input type="checkbox" id="运动" value="运动" v-model="user.hobby">
<label for="运动">运动</label>
<input type="checkbox" id="旅游" value="旅游" v-model="user.hobby">
<label for="旅游">旅游</label>
<input type="checkbox" id="唱歌" value="唱歌" v-model="user.hobby">
<label for="唱歌">唱歌</label>
<br>
</p>
<p>
<span>学历: </span>
<select v-model="user.edu">
<option disabled value="">请选择</option>
<option value="zk">专科</option>
<option value="bk">本科</option>
<option value="yjs">研究生</option>
</select>
</p>
<p>
简介:
<!-- 使用lazy修饰符配合v-model,在数据改变时进行收集,而不是每次修改都收集 -->
<textarea name="introduction" id="" cols="50" rows="20" v-model.lazy="user.introduction"></textarea>
</p>
<p>
<input type="checkbox" id="checkbox" v-model="user.checked">
<label for="checkbox"> 阅读并接收协议</label>
</p>
<p>
<!-- 使用prevent事件修饰符阻止页面刷新,嵌套使用stop修饰符阻止事件往外(上)层(html结构)继续传播 -->
<button @click.prevent.stop="register">注册</button>
</p>
</form>
</div>
<script src="/js/vue2.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
'msg': '收集表单数据',
// 这里把表单信息使用user对象包裹起来ed目的是为了下面methods中方便只获取user对象中的表单信息
user: {
user_name: '',
password: '',
age: '',
sex: 'woman', //给予sex用于渲染表单初始sex字段默认值
hobby: [],
edu: '',
introduction: '',
checked: false,
},
},
methods: {
register(event) {
console.log('vue中data属性的所有值', this.$data)
console.log('user表单信息', this.$data.user)
console.log('要传给后端的user表单信息', JSON.stringify(this.$data.user))
}
}
})
</script>
这里把表单信息使用user对象包裹起来的目的,是为了methods中更方便的只获取user对象中的所有表单字段信息,避免收集到其他data数据,影响业务
参考文档:表单输入绑定
filter过滤器
点击查看代码
<body>
<div id="root" v-cloak >
<h2>{{msg}}</h2>
<b>假如content是后端返回的数据,要求当数据不是0.65时,页面渲染'-'字符串,否则正常显示</b>
<ul>
<li>第1种:通过计算属性实现</li>
<li>第2种:通过过滤器实现</li>
</ul>
<p>刷新页面查看每次不同的值 </p>
<p>
计算属性实现: {{getRes}}
</p>
<p>
过滤器实现: {{content|filterRes}}
</p>
<div>45245</div>
</div>
</body>
<script src="/js/vue2.js"></script>
<script>
const vm = new Vue({
el: '#root',
data: {
msg: '过滤器',
content: [null, 'undefined', 0.65],
methodsContent: '',
},
computed: {
getRes() {
const index = Math.floor(Math.random() * this.content.length)
console.log('计算属性实现', index, this.content[index])
const res = this.content[index]
const arr = ['', undefined, null]
return arr.indexOf(res) >= 0 ? '-' : res;
}
},
// 过滤器
filters: {
filterRes(value) {
console.log('过滤器实现', value);
const index = Math.floor(Math.random() * value.length)
const res = value[index]
const arr = ['', undefined, null]
return arr.indexOf(res) >= 0 ? '-' : res;
},
}
})
</script>
filters过滤器类似于自定义函数,可以对data数据进行处理后再渲染到页面上
- 在filters中定义一个过滤器的名称,我这里起名叫filterRes
- filterRes()接收一个参数,那就是data值,filterRes(){}中编写过滤器要实现的逻辑,用于返回渲染在页面上的data值
filters过滤器可以定义多个,并且可以一直调用
此处对content属性使用了filterRes过滤器进行数据渲染
其实还可以定义第二个过滤器,例如叫filterTwo
将原有的页面渲染从{{content|filterRes}}改写为{{content|filterRes|filterTwo}}
- 由于filters实现逻辑和computed实现逻辑类似
,所以vue3中觉得filters似乎没有存在的必要,砍掉了该功能
,推荐用户在computed/methods中自行定义实现,但基于缓存机制,所以优先推崇使用computed实现
参考文档:过滤器
生命周期
点击查看代码
<div id="root">
<div>
<p>计数器</p>
<button @click="add">点我增加</button>
<p>{{num}}</p>
<p>
<input type="text" v-model="num">
</p>
</div>
</div>
<script>
new Vue({
el: "#root",
data: {
num: 1
},
methods: {
add() {
return this.num++;
}
},
beforeCreate() {
console.log('数据代理创建前')
},
created() {
console.log('数据代理创建后')
},
beforeMount() {
console.log('vue实例对象数据挂载前')
},
mounted() {
console.log("vue实例对象数据挂载后")
},
beforeUpdate() {
console.log("vue实例对象数据更新前")
},
updated() {
console.log("vue实例对象数据更新后")
},
beforeDestroy() {
console.log("vue实例对象被销毁前")
},
destroy() {
console.log("vue实例对象被数据销毁后")
}
})
</script>