文章目录
1. 什么是vue
- vue 是一套用于构建用户界面的渐进式 JavaScript 框架
- 构建用户界面:把对应的数据渲染到页面上
- 渐进式:对于简单应用 vue 的核心库非常轻量小巧,对于复杂应用可以引入各式各样的 vue 插件
2. vue的特点
- 采用组件化模式,提高代码复用率、且让代码更好的维护
- vue 是单页面应用,使页面局部刷新
- 声明式编码( 直接在标签上写 vue 指令 ),无需操作 dom,提高开发效率
- 使用虚拟 dom 和 diff 算法,避免真实 dom 过多的创建与销毁
3. MVVM模型
- M:模型(Model),对应 data 中的数据
- V:视图(View):模板
- VM:视图模型(ViewModel):Vue 实例对象
- 无需操作 DOM,数据发生变化视图自动更新
4. 初始vue
<!-- 创建视图(vue 模板) -->
<div id="app">
<button @click="btn">点击</button>
</div>
<!-- 引入 vue 框架,使用的 CDN 引入 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 创建实例 -->
<script>
const vm = new Vue({ // 创建 vue 实例
el: "#app", // 挂载,用于指定这个实例为哪个视图服务
data: { // 用于存储数据,在视图中可以直接使用,在 vue 中可以通过 this 获取
msg: "hello Vue"
},
methods: { //
btn() {
console.log(this.msg);
}
}
})
</script>
5. 模板语法(插值表达式)
数据绑定最常见的形式就是使用“Mustache”语法(双大括号)的文本插值
<!--
可以在 *标签体* 中写一些js代码,只能是表达式,不能是js语句
一、表达式:表达式会产生一个值
1. a
2. a + b
3. true ? 1 : 0
4. (function(){return "在页面中 Vue 会显示我"})()
二、js语句
1. if(){}
2. let abc
msg 为 data 中的数据
-->
<div id="app">{{msg}}</div>
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ msg.split('').reverse().join('') }}
<!-- 这样写是不可以的,和 eval 中的内容很像 -->
{{let num}}
6. vue内置指令
6.1 v-on:事件绑定
<script>
// v-on ===> @,语法糖写法
// 1. 事件修饰符
// .stop:调用 event.stopPropagation(),阻止事件冒泡
// .prevent:调用 event.preventDefault(),阻止默认事件
// .once:事件只触发一次
// .capture:转为事件捕获
// .self:只有 event.target 是当前元素时才会触发
// .passive:事件的默认行为立即执行,无需等待事件回调执行完毕
// (比如滚轮事件,要等事件回调执行完成之后,页面才会发生滚动,加了这个事件修饰符后,可以直接滚动)
// .native:监听组件根元素的原生事件
// 2. 键盘事件
// (1). Vue 中常用的按键的别名,和事件修饰符的用法一样
// 只能触发对应的键盘事件
// 回车 => enter
// 删除 => delete
// 退出 => esc
// 空格 => space
// 换行 => tab
// 上 => up
// 下 => down
// 左 => left
// 右 => right
// (2). 其他事件
// 直接使用 event 事件对象中 key 值即可,不过需要小写,多个单词 CapsLock => caps-lock
// keyCode 也可以,但是 MDN 不推荐使用,已经从 web 标准中移除
// (3). Vue.config.keyCodes.自定义键名 = keyCode ,可以自定义键盘别名
</script>
<!-- 用法 -->
<button @click.stop="btn"></button>
<!-- 还可以连续点击 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 事件对象,这两种方法在函数中的第一个参数,都可以接收事件对象 -->
<button @click="btn"></button> <!-- 不可以传参 -->
<button @click="btn($event)"></button> <!-- 可以传参 -->
6.2 v-bind:属性绑定
<!-- v-bind ===> : ,语法糖写法 -->
<!-- imageSrc 为 Vue 中 data 的数据 -->
<img :src="imageSrc">
<!-- 也可以配合 class 使用 {} -->
<!-- isActive 为真时,div 存在 active 类名,为假时,没有类名 -->
<div :class="{ active: isActive }"></div>
<!-- 也可以使用数组,添加多个属性 -->
<!-- 注意:box、active 为 data 中的数据 -->
<div :class="[box, active]"></div>
<!-- 如果不想使用变量 ['box', 'active'] 即可 -->
<!--
关于数组/对象的写法,可以直接创建一个 arr/obj,放在 data 中
然后在 class 中写 arr/obj 也可以,这种方式更容易管理
-->
<!-- 当然也可以配合使用 -->
<div :class="[box, { active: isActive }]">张三</div>
<!-- 动态属性名,key 为 data 中的数据,如果 key: class,就代表属性为 class -->
<button :[key]="value"></button>
<!-- 配合 style 使用,数组对象嵌套也可以 -->
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div v-bind:style="[{ color: activeColor, fontSize: fontSize + 'px' }]"></div>
6.3 v-text:更新元素
<!-- msg 是 Vue 中 data 的数据 -->
<!-- text 专门展示文本的 -->
<span v-text="msg">我是span中的内容,我会被覆盖</span>
6.4 v-html:更新元素
<!-- 动态渲染任意 HTML,这种行为非常的危险,不能把用户数据通过这种形式渲染 -->
<span v-html="msg">我是span中的内容,我会被覆盖</span>
6.5 v-if:条件渲染
<!-- v-for 比 v-if 优先级高一些,不推荐一起使用 -->
<!-- 当条件为真时,显示对应的元素 -->
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
6.6 v-show:条件渲染
<!--
v-show 与 v-if:
v-if:是真正的条件渲染,会对 dom 进行创建和销毁,同时它也是懒惰的,如果最开始条件就为假时,就不会进行渲染
v-show:无论条件是真或假,元素都会被渲染,只是简单的进行了 css 样式切换(display: none;)
-->
<h1 v-show="true">Hello!</h1>
6.7 v-for:列表渲染
<!-- 需要绑定 key 为唯一标识,设计到 diff 算法,如果 key 没有写,vue 默认会把 index 当做 key -->
<!-- v-for 的优先级要比 v-if 的优先级高一些,但是不推荐写在一起,会出现很多问题 -->
<!-- list 为数组时 -->
<ul>
<!-- index 为索引,item 为值 -->
<li v-for="(item, index) in list" :key="item.id">
{{item.title}}
</li>
</ul>
<!-- list 为对象时 -->
<div v-for="(val, key, index) in list"></div>
<!-- v-for 可以遍历数组、对象、集合、映射、字符串、指定次数 -->
6.8 v-model:双向绑定
<!-- 只能用于表单类,可输入元素,v-model 默认绑定的 value 值 -->
<!-- v-model:value ===> v-model,语法糖写法 -->
<input type="text" v-model:value="msg"> {{msg}}
<!--
收集表单数据:
<input type="text">,v-model 收集的是 value 值,用户输入的就是 value 值
<input type="radio">,v-model 收集的是 value 值,需要给标签配置 value 值
<input type="checkbox">
1. 没有配置 input 的 value 属性,那么收集的就是 checkout (布尔值)
2. 配置 input 的 value 属性:
(1) v-model 的初始值不是数组,那么收集的就是 checkout (布尔值)
(2) v-model 的初始值是数组,那么收集的就是 value 组成的数组
v-model 修饰符
lazy:失去焦点在收集,和 change 事件一样,v-model 默认是通过 input 事件收集的
number:输入的字符串转为数字,因为 value 默认是字符串
trim:收集时自动过滤首尾空格
-->
6.9 v-cloak
<!-- 处理 vue 生命周期中,视图数据会出现双大括号的问题 -->
<!-- 需要 css 配合,在有双大括号的时候先隐藏,等到 vue 渲染完成后在展示 -->
<style>
[v-cloak] {
display: none;
}
</style>
<div v-cloak>
{{ message }}
</div>
6.10 v-once
<!--
v-once 所在的节点在初次动态渲染后,就视为静态内容了
以后的数据在怎么改变,该内容也不会更新,可以用于性能优化
-->
<!-- 这里 n 在怎么加,也没有用 -->
<button @click="n++" v-once>{{n}}</button>
6.11 v-pre
<!-- 不去解析 Vue 中的语法,视图写什么页面就渲染什么,可以加快编译 -->
<div v-pre>{{msg}}</div>
<!-- 浏览器直接显示 {{msg}} -->
6.12 v-slot:插槽
<!-- v-slot: ===> #,语法糖写法 -->
<!-- 插槽 -->
7. 自定义指令
7.1 函数式
<!-- html 内容 -->
<div id="app">
<!-- v-big 可以把 n 放大10倍 -->
<span v-big="n"></span>
</div>
const vm = new Vue({
el: "#app",
data: {
n: 1
},
directives: {
// big(){}:代表创建了一个 v-big 的自定义指令
// el:指令所绑定的真实 dom
// binding:对应的配置数据,其中 value 属性就是指令所绑定的值
big(el, binding){
// big 函数的执行时机:
// 1. 会在 v-big 指令与元素绑定成功时执行
// 2. 当指令所在的模板被重新解析时也会执行
el.innerText = binding.value * 10
}
}
})
7.2 对象式
<!-- html 内容 -->
<div id="app">
<h2 v-my-big="n"></h2>
</div>
const vm = new Vue({
el: "#app",
data: {
n: 1
},
directives: {
// 多个单词连接的自定义指令,不能使用小驼峰,需要用 - 相连
"my-big": {
// el:指令所绑定的真实 dom
// binding:对应的配置数据,其中 value 属性就是指令所绑定的值
// vnode:虚拟 dom
// oldVnode:更新之前的虚拟 dom
bind(el, binding, vnode) {
console.log("只调用一次,指令第一次绑定到元素时调用");
},
inserted(el, binding, vnode) {
console.log("插入父节点时调用,表示该元素已经在页面上了");
},
update(el, binding, vnode, oldVnode) {
console.log("更新时调用,可能子组件还没有更新完");
},
componentUpdated(el, binding, vnode, oldVnode) {
console.log("全部更新后调用,在 update 之后调用");
},
unbind(el, binding, vnode) {
console.log("只调用一次,指令与元素解绑时调用");
}
}
}
})
7.3 全局与局部语法
// 全局
Vue.directive(指令名, 配置对象) // 对象式
Vue.directive(指令名, 回调函数) // 函数式
// 局部
new Vue({
directives:{(指令名, 配置对象)} // 对象式
})
new Vue({
directives:{(指令名, 回调函数)} // 函数式
})
注意:
- 多个单词组成的指令,无论是使用还是创建都需要用 - 相连
- 自定义指令中所有函数的 this 都指向 window
8. 数据代理
// 在 vue 实例中,我们写在 data 中的数据可以直接通过 this 使用
// 这个是因为 Vue 使用了 Object.defineProperty() 的方法进行了数据代理
// 还有一点就是 data 在 vue 实例中变成了 _data
// 从下面这段代码就可以很明显的看出,vm._data 是等于 obj 的
// vue 把 data 中的数据直接添加到 vue 实例上了
// 无论是 _data 中的数据,还是添加到 vue 实例上的数据,有一个发生改变另一个也会变
let obj = {
name: "张三",
age: 18
}
const vm = new Vue({
el: "#app",
data: obj // 只有 data 中的数据才会做数据代理
})
console.log(vm._data === obj); // true
9. 数据检测的问题
9.1 对象
- vue 在对一个对象进行修改和获取时,vue 是响应式的
- 因为 vue 是借助 Object.defineProperty() 的方法对数据进行响应的,但是这个方法只有 get 和 set
- 这个时候就会出现问题,对象中的数据进行添加或删除时,vue 无法检测
- vue 中提供了一个全局的 set ( set 有添加或修改的意思,这里是指添加 ) 和 delete 两个方法,对数据进行添加或删除
- 这里要区分一点,vue 中全局的方法是直接可以
Vue.
使用,在实例中是this.$
使用
// 全局,target ==> 数组/对象,target 不能为 vue 实例和 vue 实例中的 data
Vue.set( target, key/index, value )
Vue.delete( target, key/index )
9.2 数组
- 在对应数组的修改时 vue 对这七个方法进行了重写,push()、pop()、shift()、unshift()、splice()、sort()、reverse()
- 所以使用这七个方法对数组进行修改时,数据也是响应式的
- 对于 ES6 新增的两个改变数组自身的方法,和手动通过下标修改的数据,都不是响应式的
10. 计算属性和侦听器
10.1 计算属性(computed)
const vm = new Vue({
data: {
firstName: "",
lastName: "",
},
computed: {
fullName: {
get: function () {
return this.firstName + " " + this.lastName;
},
set: function (newValue) {
var names = newValue.split(" ");
this.firstName = names[0];
this.lastName = names[names.length - 1];
},
},
gather2() {
return "我是访问时的值,只有 get 时的简写";
},
},
});
// 现在再运行 vm.fullName =John Doe’时,setter 会被调用vm.firstName 和 vm.lastName也会相应地被更新。
- 计算属性底层也是借助了 Object.defineProperty() 的方法,使计算属性中的值也是响应式的
- get 函数在初次读取时会执行一次,当依赖的数据发生改变的时候会被再次调用(这个指上面的案例,firstName 或 lastName 发生改变时才会再次触发 get 方法,而不是指计算属性中的 set)
- 计算属性会进行缓存,当值未发生改变时,计算属性中的函数不会再次调用,只是使用了缓存的值
- 一般计算属性只是用于得到计算的结果,也就是默认只会用到 get 方法,很少会用到 set 方法,所以可以使用简写
- 计算属性最终会出现在 vm 上,可以直接读取
10.2 侦听器(watch)
// 实例中监测
const vm = new Vue({
el: "#app",
data: {
msg: "hello Vue"
},
methods: {
btn() {
console.log(this.msg);
}
},
watch: {
msg: { // 被监测的值必须存在
immediate: true, // 初始化时,就调用一次 handler
deep: true, // 开始监视深层的数据,在 watch 中默认只监测一层数据的变化
handler(newValue, oldValue) { // 当 msg 值发生改变时会执行 handler 函数
console.log("msg 发生改变了,oldValue 为改变前的值,newValue 为改变后的值")
}
},
msg(newValue, oldValue) { // 只有函数不需要配置项时可以简写
console.log("我是简写形式")
}
}
})
// 实例监测,两种效果是一样的,对象式
let unwatch = vm.$watch("msg", {
immediate: true,
deep: true,
handler(newValue, oldValue){
console.log("msg 发生改变了,oldValue 为改变前的值,newValue 为改变后的值")
}
})
// 函数式
let unwatch = vm.$watch("msg", function (newValue, oldValue){
console.log("msg 发生改变了,oldValue 为改变前的值,newValue 为改变后的值")
})
// 取消观察,在全局使用时,会返回一个取消观察的函数
unwatch()
侦听器每次发生变化时就会触发,用于监听数据的变化
10.3 computed与watch之间的区别
- computed 能完成的功能,watch 都可以完成
- watch 能完成的功能,computed 不一定能完成,例如 watch 可以进行异步操作
- computed 主要进行计算的汇总,watch 主要用于观察数据,根据数据的变化来处理对应的程序
- computed 会进行缓存,watch 不会
- computed 会计算出一个新的值,watch 对已有的值进行监测
11. 过滤器
<!-- html 内容 -->
<div id="app">
<span>{{msg | filter1 | filter1(123)}}</span>
</div>
// 全局注册
Vue.filter("filter0", (val) => {
return "我是全局的过滤器"
})
// 局部注册
const vm = new Vue({
el: "#app",
data: {
msg: "hello Vue"
},
filters: {
// 这个 val 默认为过滤器前的值,msg
filter1(val) {
return "我被过滤了" + val
},
// num 是传入的123
filter2(val, num) {
return val + num
}
}
})
- 过滤器的定义:对要显示的数据进行特定格式化后在显示,比较适用于一些简单的逻辑的处理
- 注册过滤器有两种方式,全局的在任何位置都可以使用,局部的只能用于当前组件或实例
- 默认过滤器的第一个参数是过滤器前面的值,第二个参数是使用过滤器传入的值,依次类推
- 过滤器可以连续使用
- 过滤器并不会改变原数据,只是返回新的数据