1.vue基础
1.1 特点
-
渐进式框架
-
国人开发、易学、易上手
-
Vue 的核心库只关注视图层
-
声明式渲染
-
组件系统
1.2 开发模式
比较常见的三种开发模式:MVC、MVP、MVVM
-
MVVM模式
-
M:(model)数据对象,数据层
-
V:(view)前端展示页面,显示层
-
VM:(ViewModel)vue对象,逻辑层
-
-
所谓的mvvm模式,通过vm层可以将v层和m层的数据进行双向绑定
1.3 设计模式
设计模式:开发模式、发布订阅模式、工厂模式、单例模式、代理模式、观察者模式、生产消费模式、原型模式
2.vue入门
2.1 传统DOM与vue实现对比
js和jquery都需要先获取到某个节点,再对这个节点进行操作,性能不好
区别 | 传统DOM | Vue |
数据驱动 | 手动更新DOM元素以反映数据的变化。需要编写大量的代码。 | 使用数据驱动的方式,只需要关注数据的变化,Vue.js会自动更新DOM以反映这些变化。大大减少手动操作DOM的代码量。 |
响应式 | 要实现数据的响应式,需手动编写代码来监听数据变化,并更新相关的DOM元素。 | 通过其响应式系统可以自动追踪数据的变化,并且在数据变化时更新相关的DOM元素,从而使得数据和DOM保持同步。 |
模板 | 通常需要手动拼接HTML字符串来动态生成DOM元素,代码可读性较差。 | 使用基于HTML的模板语法,使得编写动态内容更加直观和易于理解。 |
组件化 | 要实现组件化通常需要手动管理DOM元素的创建、销毁和更新。 | 提供了组件化的能力,可以将页面拆分成多个独立组件,每个组件都有自己的模板、逻辑和样式,使得代码更加模块化和可维护。 |
2.2 vue数据双向绑定原理
vue2. *版本,数据双向绑定是通过数据劫持Object.defineProperty()结合发布者-订阅者模式的方式来实现的,每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。
<div id="app">
<div id="msg"></div>
<input type="text" id="ipt" />
</div>
<script>
//使用数据劫持Object.defineProperty模拟双向绑定效果
//1.创建userInfo的对象
var userInfo = { username: "lili" };
//2.创建一个空对象, 用于数据劫持
var obj = {};
//3.数据劫持参数:1:对象 2:属性 3:配置项,如get set
Object.defineProperty(obj, 'name', {
get() {
console.log("get触发了");
return userInfo.username;
},
set(val) {
console.log("set触发了", val);
userInfo.username = val; document.querySelector('#msg').innerHTML = obj.name; }
})
// 4.将obj里面的name 显示在 msg里面
document.querySelector('#msg').innerHTML = obj.name;
// 5.给 input框绑定 输入事件
document.querySelector('input').oninput = function () {obj.name = this.value;};
Vue 主要通过以下 4 个步骤来实现数据双向绑定的
实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。
实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。
实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。
3.vue模板语法
{ {}}
, 双大括号可以解析表达式,不能解析语句
v-text
没有解析标签
v-html
可以解析标签
v-bind
绑定属性,可以简写为 :
v-model
双向绑定
v-once
只执行一次
v-cloak
去掉闪白
v-for
循环
v-if
v-else
v-else-if
判断分支<div id="app"> //1.文本插值表达式 { {msg}}<br> /* v-*** 指令 */ //2.显示文字 v-text <div v-text="user"></div> /* v-text 没有解析标签 */ <div v-text="kemu"></div> //3.v-html 可以解析标签 <div v-html="kemu"></div> //4.绑定属性 <div v-bind:title="mm">aaaaaa</div> /* 属性绑定可以简写 */ <div :id="mm">aaa</div> <img :src="mysrc" alt=""/> //5.双大括号里可以写表达式 <div>{ {num+1}}</div> /* 不可以写 if else for 这些语句 */ //6.v-html 会引起 xss攻击 <div v-html="yiyi"></div> </div> <script src="./js/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { msg: "hello", user: "大幂幂", kemu: "<h1>语文</h1>", mm: "今天很热", mysrc: "./img/001.jpg", num: 10, yiyi: `<img src="./img/1.jpg" onerror="alert('笨蛋,被攻击了')" />` } }) </script>
注意:
v-html
容易引起xss攻击,尽量少用或不用。
4.vue常用指令
4.1 v-model双向绑定
双向绑定指令
v-model
本质上不过是语法糖 (v-bind
和v-on
的语法糖结构)用在input、select等表单里
<div id="app"> 性别: <input type="radio" name="sex" id="" value="1" v-model="sex"> 男//选男会把value值传给sex <input type="radio" name="sex" id="" value="0" v-model="sex"> 女 { {sex==0?'女':'男'}} </div> <script src="./js/vue.js"></script> <script> var app = new Vue({ el: "#app", data: {sex: 0} }) </script>
4.2 v-cloak去闪白
可以解决vue屏幕闪白的问题,网络不好,网页先显示 { { message }},vue加载完毕,显示 hello vue
4.3 v-once执行一次
v-once只能渲染一次,值不能修改
4.4 v-bind属性绑定
vue中当遇到变量,boolean类型或者number类型时,需要使用绑定属性
4.5 v-on事件绑定
作用:绑定事件监听器(事件绑定)
v-on
简写 @1.绑定事件常规写法
<button v-on:click="num++"></button>
2.事件绑定简写 @
<button @click="num++"></button>
3.事件绑定函数,直接写函数名
<button @click="say"></button>
4.事件绑定函数,传递参数
<button @click="alert('123')"></button>
5.ev的使用
<button @click="myfn2(10,$event)">点击</button>
如果没有小括号,函数可以直接写 ev属性,如果写了小括号,函数不能直接写 ev属性 ,值是undefined,如果写了小括号,需要在小括号中添加 $event属性
4.6 v-if条件分支
<div id="app">
<p v-if="score < 60">不及格</p>
<p v-else-if="score >= 60 && score < 80">一般</p>
<p v-else-if="score >= 80 && score < 100">优秀</p>
<p v-else">大神</p>
</div>
4.7 v-for循环
1.循环显示数组内容
<ul> <li v-for="item in fruits">{ {item}}</li> </ul>
2.可以显示下标
<ul> <li v-for="(item,index) in fruits"> 下标{ {index}} : { {item}} </li> </ul>
3. 可以使用 of
<ul> <li v-for="(item,index) of fruits"> 下标{ {index}} : { {item}} </li> </ul>
4. 循环对象
<div v-for="(value,key,index) in obj"> { {value}}--{ {key}}--{ {index}} </div>
4.7.1 v-if和v-for的优先级
-
在vue2中,v-for的优先级高于v-if
-
在vue3中,v-if的优先级高于v-for
-
在vue中,永远不要把v-if和v-for同时用在同一个元素上,会带来性能方面的浪费(每次渲染都会先循环再进行条件判断);想要避免出现这种情况,可在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环
4.7.2 key的作用
-
提高性能,不影响显示效果( 如果没有id,可以考虑使用索引替代 )
-
因为key 可以唯一的确定一行数据,在对这行数据进行修改、删除、新增的时候,不需要在轮询查找这条数据
-
实际项目中循环时,不写key会报警告
4.8 v-show显示
<button @click="change">更改状态</button>
<p v-show="flag">春华秋月何时了</p>
<p v-show="!flag">往事知多少</p>
4.8.1 v-if 和 v-show的区别
-
v-if 直接改变 dom节点 ,性能差一些
-
v-show 改变的样式,性能好,更改的次数较多,频繁切换状态,建议使用 v-show
4.9 样式绑定
4.9.1 class样式绑定
//class 使用 v-bind绑定时可以使用对象的方式
<div :class="aa">春花秋月何时了</div>****
<div :class="{active:cc,danger:bb}">往事知</div>
<div :class="[aa,tt]">往事知多少</div>
<div :class="bb?'active':'danger'">****class的三目运算符的使用</div>
<div :class="[bb?'active':'danger',tt]">class的三目运算符的使用</div>
4.9.2 style 绑定样式属性
//style 使用 v-bind绑定时可以使用对象的方式
<div :style="mystyle">春花秋月何时了</div>****
<div :style="{color:mycolor}">往事知多少</div>
<div :style="[mystyle,font]">往事知多少</div>
<div :style="bb?mystyle:font">****style的三目运算符的使用</div>
<div :style="[bb?mystyle:font,dd]">style的三目运算符的使用</div>
4.10 修饰符
事件修饰符:
-
stop
阻止冒泡 -
prevent
阻止默认事件 -
capture
事件捕获模式 -
self
点到自己才运行 -
once
只运行一次 -
passive
滚动事件的默认行为会立即触发
v-model修饰符:
-
lazy
光标从input框移出才触发更新 -
number
变为 数值类型 -
trim
去掉空格
按键修饰符:
-
enter
.space
.delete
5.深入响应式原理
深入响应式原理的表现:
-
数组: 利用索引直接设置一个数组项时、修改数组的长度时,是没有响应式效果的
-
对象: 新增、删除对象属性时没有双向绑定效果
解决办法:
-
$set
-
二次封装的数组/对象方法
-
强制更新$forceUpdate
//数组:
methods: {add() {
this.hero[3] = "囧囧侠";
// 1. $set
this.$set(this.hero, 3, "囧囧侠");
// 2.vue二次封装的数组方法
// 可以使用 vue二次封装的变更方法:push() pop() shift() unshift() splice() sort() reverse()
// 可以使用 vue二次封装的非变更方法: filter()、concat() 和 slice()
this.hero.push('囧囧侠');
// 3.强制更新 $forceUpdate
this.hero[3] = "囧囧侠";
this.$forceUpdate();},
del() {this.hero.length = 2;},
edit() {this.hero[2] = "大神";}
}
//对象:
methods: {add() {
// 新增对象属性时没有双向绑定效果
this.hero.sex = "男";
// 1. $set
this.$set(this.hero, 'sex', '男');
// 2. 二次封装的对象方法
this.hero = Object.assign({}, this.hero, { 'sex': "男" });
// 3. 强制更新
this.hero.sex = "男";
this.$forceUpdate();},
del() {delete this.hero.age;},
edit() {//修改有双向绑定的效果
this.hero.name = "小花";}
}
6.vue常用特性
6.1 自定义指令 directive
全局自定义指令:
//全局自定义指令
Vue.directive('base', {
inserted(el) {//inserted
console.log(el);//dom
}
})
局部自定义指令, 定义在 new Vue的对象中的:
var app = new Vue({
el: "#app",
data: { },
//局部的自定义指令
directives: {
//所有的自定义指令都放在这里
mycolor(el, binding) {
//设置了该dom节点里面的内容的颜色
console.log(el, binding);
el.style.color = binding.value;}}
})
应用:搜索结果加粗标红
// 在Vue实例化之前定义自定义指令 Vue.directive('highlight', { // 当被绑定的元素插入到 DOM 中时…… inserted: function (el, binding) { // 获取传入的搜索关键词 let keyword = binding.value; // 使用正则表达式替换匹配的文本,加上标签实现加粗标红 //new RegExp(keyword, 'gi')创建一个全局、不区分大小写的正则表达式对象,用于搜索指定关键词 el.innerHTML = el.innerHTML.replace(new RegExp(keyword, 'gi'), '<span style="color: red; font-weight: bold;">$&</span>'); } });
定义了一个名为
highlight
的自定义指令,其中包含一个inserted
钩子函数。当元素被插入到DOM中时,这个钩子函数会被调用。获取了传入的搜索关键词,然后使用正则表达式替换匹配的文本,将其用<span>
标签包裹起来并添加样式,实现了加粗标红的效果。在模板中使用这个自定义指令可以这样做:
<p v-highlight="'关键词'">这是包含关键词的搜索结果</p>
6.2 计算属性 computed
-
使用方式:与使用data中的数据是一模一样的,依赖项可以是多个,返回一个结果
-
注意:计算属性的名字和前面的data中的数据、methods中的方法都不能重名
-
优点:计算属性会缓存计算的结果,当结果不发生变化时,不管调用几次,计算属性对应的函数都只执行一次
-
计算属性和普通函数的区别:
-
计算属性有缓存,当结果不变时,计算属性不管调用多少次都只执行一次;函数是调用几次就执行几次
-
计算属性不用手动调用,它对应的函数,直接写计算属性名就可以了,函数调用必须加 小括号
-
想要修改计算属性的值,需要使用 get、 set
computed: {
fullN: {
get() {
return this.firstN + '-' + this.lastN;},
set(newV) {
console.log('set触发了', newV);
var arr = newV.split('-');
this.firstN = arr[0];
this.lastN = arr[1];}}
}
6.3 监听器 watch
计算属性computed和监听器 watch的区别:
- 计算属性有缓存,但是监听器没有,计算属性性能更好
- 计算属性是可以有多个依赖值,产生一个结果;监听器每次只能监听一个值
- 计算量比较大或异步的时候,建议使用监听器
- 监听器可以深度监听
var app = new Vue({
el: "#app",
data: {
firstN: "yin",
lastN: "laura",
fullN: "yin-laura"
},
watch: {//监听器、侦听器
firstN(newV, oldV) {
console.log(newV, oldV);
this.fullN = newV + "-" + this.lastN;
},
lastN(newV, oldV) {
console.log(newV, oldV);
this.fullN = this.firstN + "-" + newV;
},
},
watch深度监听的写法:
watch: {
userInfo: {
handler(newV) {
console.log(newV);
this.userInfo.fullN = newV.firstN + '.' + newV.lastN;},
deep: true //深度监听
}}
6.4 过滤器 filters
全局过滤器
Vue.filter('setUpper', function (value) {
console.log(value);
return value.toUpperCase();
})
局部过滤器
var app = new Vue({
el: "#app",
// 局部过滤器
filters: {
sexType(value) {
return value ? "男" : "女"},
findStr(value, num) {
console.log(num);
return value.substring(num);}}
})
过滤器可以传递参数 ,过滤器调用时使用 <