目录
前言:着重记录vue2相关内容
一、VueJS是什么?
它是一个轻量级MVVM框架
数据驱动+组件化的前端开发
Github 超过25K + 的star 熟,社区完善
Vue.js更轻量,gzip后大小只有26K;更易上手,学习曲线平稳
形成Vue渐进式框架的核心概念为:组件化,MVVM,响应式,和生命周期
小技巧:
无论是插值语法还是指令语法亦或者是v-for列表渲染或v-if,都可以绑定变量或方法,前提是方法返回值要符合对应绑定类型。
- 如{{fun()}}方法返回值是data中变量属性即可;
- 如v-for="(item,index) in arr()" :key="item.id",arr()返回值是可遍历的数组或其他可遍历类型数据值即可;
- 其余情况...可按相同逻辑依次类推;
二、Vue的优缺点
优点
1. 轻量级
Vue作为一款轻量级前端框架,大小只有18–21KB,工程搭建简单,只需要几行命令符。因为Vue使用的主体语言为JS,开发者可以灵活地将其他框架(如React和Angular)的项目迁移到Vue,具有很高的集成能力。Vue提供的router路由可以便捷地搭建一个多界面应用。2. 高性能
虚拟DOM和响应式避免了不必要的全局重新渲染,提升了用户体验,使用户操作更加流畅。3. 好上手
与面向对象编程性质类似,组件化更符合人类思维。打个比方,我们在设计网页时,通常会把一个界面分成一块一块的、用于某功能的特定样式模块。Vue的组件化使前端开发更加容易理解,同时MVVM可以更便捷地实现交互,对新手十分友好。4. 插件化
由于Vue框架的流行性,目前有许多基于Vue的npm扩展包和开发工具(如Vuex)。Vue可以在一个文件下统一管理所有外部插件的全局使用。5. 便于测试
组件化利于开发者对于单一组件进行测试,很少发生在整个页面下找不到是哪个地方报错的情况。缺点
1. 生态环境不够完善
虽然Vue五年以来的发展寻索,生态环境不如React和Angular规范。Vue开发的讨论社区较小。
2. 国外市场小
Vue是由中国尤大神开发的,主要有阿里巴巴、饿了么等国内大公司作为主流框架,国外大部分使用React和Angular。
三、vue2和vue3的模板区别
1.响应式的区别
2.0中vue的响应式是基于数据劫持(object.defineProperty这个方法来进行劫持的)当前这个方法是兼容主流浏览器的ie9以上都可以使用 他是可以监听数据的变化 从而让vue做出改变但是他有一个bug object.defineProperty这个方法只能够监听初始化的时候数据 如果程序运行到一半你给data中的对象或者是数组添加新属性的时候 由于他只会在初始化监听 那么你中间添加的内容就无法监听 没有监听到 就没有数据劫持 没有数据劫持 那么久没有双向绑定 没有双向绑定 那么数据变视图就不会发生改变 我们使用$set
3.0中 对2.0的bug进行了解决
3.0中的响应式是采用ES2015中最新的一个规范 Proxy来进行替代的 proxy是惰性监听(他不会在初始化的时候监听数据 而是在你使用数据的时候才回去监听)
proxy是一个比较新的规范 ie还不支持 vue为了解决ie不支持的问题 所以单独给ie进行了适配
传统主流浏览器 使用proxy来进行监听
但是在ie中还是使用2.0中的object.defineProperty
2.底层的变化
3.0底层全部都是使用ts(typescript)编写的 今后3.0可以更好的与ts结合3.属性声明方式
2.0中都是使用vue属性的方式来创建数据 方法 计算属性等内容的3.0中 改变了 变成了 api方式进行创建 也就是或vue3.0把变量 方法 计算属性等内容封装成了一个个的方法 使用方法的方式进行调用
vue2响应式原理简单案例
let person = {
name: '张三',
age: 15,
}
let p = {}
Object.defineProperty(p, 'name', {
configurable: true, //配置这个属性表示可删除的,否则delete p.name 是删除不了的 false
get() {
//有人读取name属性时调用
console.log('get拦截查看');
return person.name
},
set(value) {
//有人修改时调用
console.log(value,'set拦截修改');
person.name = value
}
})
p.name = '修改属性'
console.log(p.name,'读取拦截');
vue3响应式原理简单案例
const person = {
name: "张三",
age: "18"
};
const p = new Proxy(person, {
// 有人读取p的某个属性时调用
get(target, propName) {
console.log(`有人读取了p身上的${propName}属性`);
return Reflect.get(target,propName);
},
// 有人修改p的某个属性、或给p追加某个属性时调用
set(target, propName, value) {
console.log(`有人修改了p身上的${propName}属性,我要去更新界面了`);
return Reflect.set(target,propName,value);
},
// 有人删除p的某个属性时调用
deleteProperty(target, propName) {
console.log(`有人删除了p身上的${propName}属性,我要去更新界面了`);
return Reflect.deleteProperty(target,propName);
}
})
四、MVVM 数据双向绑定
MVVM模式(全称为Model-View-ViewModel)为Vue实现数据双向绑定。在MVVM中,View为视图层,ViewModel为业务逻辑层,Model为数据层。
什么是数据双向绑定呢?当用户使View变化时(如填写表单),变化会自动同步到ViewModel处理相应逻辑,并将变化更新到Model数据库。反之,若服务端数据变化(如股价波动),变化会自动同步到ViewModel处理相应逻辑,并将变化同步到View展现给用户。
在用Vue之前,我完成HTML和JS之间的交互需要使用大量的DOM操作来实现动态加载。MVVM的数据双向绑定减少了DOM操作,更高效地实现了视图和数据的交互。同时,MVVM使界面、交互和数据层分离,便于设计人员负责设计界面,后端开发人员提供数据接口,而前端开发人员专注于业务交互逻辑的实现。
简单理解: MVVM模型 1.M:模型(Model) : data中的数据 2. V:视图(View):模板代码 3.VM:视图模型(ViewModel):Vue实例 观察发现: 1.data中所有的属性,最后都出现在了vm身上。 2.vm身上所有的属性及Vue原型上所有属性,在Vue模板中都可以直接使用。 <div id="root"> <h1>博客名:{{name}}</h1> <h1>本块内容:{{address}}</h1> <h1>测试一下1:{{1+1}}</h1> <h1>测试一下2:{{$options}}</h1> <h1>测试一下3:{{$emit}}</h1> <h1>测试一下4:{{_c}}</h1> </div> </body> <script type="text/javascript"> Vue.config.productionTip = false;//阻止vue 在在启动时生产生成提示 const vm = new Vue({ el:'#root', data:{ name:'大脸胖柴', address:'MVVM', a:1 } }) console.log(vm); </script>
数据双向绑定数据(v-model)
Vue的双向绑定可以实现: 数据变化的时候, 页面会自动刷新, 页面变化的时候,数据也会自动变化.
v-model
的三个修饰符:
- lazy:失去焦点或按下回车后才更新
- trim:过滤首尾空格
- number:自动将用户输入转为数字类型,若先输入数字,则限制输入的只能是数字,若先输入的是字符串,则number修饰符无效
- 举例:
年龄<input type="number" v-model.number="formData.age">
注意:
- 双向绑定, 只能绑定**“文本框,单选按钮,复选框,文本域,下拉列表”**等
- 文本框/单选按钮/textarea, 绑定的数据是字符串类型
- 单个复选框, 绑定的是boolean类型
- 多个复选框, 绑定的是数组
- select单选对应字符串,多选对应也是数组
五、Vue的安装和使用
1. 直接引用script
<script src="https://unpkg.com/vue@next"></script>
2. 安装脚手架
npm install -g @vue/cli
3.创建应用实例(此处为vue3)
const app = Vue.createApp({
data() { return { count: 4 }
} })
const vm = app.mount('#app')
console.log(vm.count) // => 4
<body> <div id="root"> <h1>插值语法</h1> <h3>你好,{{name}}!</h3> <hr> <h1>指令语法</h1> <a v-bind:href="url">欢迎新同学</a><br> <a :href="url">欢迎新同学</a> </div> <script> Vue.config.productionTip = false new Vue({ el:'#root', data:{ name:'大脸胖柴', url:'https://blog.csdn.net/TKY666?type=blog' } }) </script> </body>
六、Vue的两种语法
<body> <div id="root"> <h1>插值语法</h1> <h3>你好,{{name}}!</h3> <hr> <h1>指令语法</h1> <a v-bind:href="url">欢迎新同学</a><br> <a :href="url">欢迎新同学</a> </div> <script> Vue.config.productionTip = false new Vue({ el:'#root', data:{ name:'大脸胖柴', url:'https://blog.csdn.net/TKY666?type=blog' } }) </script> </body>
七、vue常用指令
v-text <div v-text="msg"> </div> 不能识别标签(不存在差值表达式闪烁问题)
v-html <div v-html="msg"> </div> 能识别标签
- v-on 绑定事件 v-on:事件名=“方法名” 可以简写为 @事件名=“方法名”
- v-on详情:
v-model 实现的是表单元素的数据双向绑定
v-once 只接收显示首次挂载值,后续变量更改不再更新显示
v-show 根据条件显示或者隐藏元素和组件
v-show详解:
1.如果表达式为
true
,则元素会被渲染,并且通过 CSS 属性display: block;
显示;如果表达式为false
,则元素会被渲染(但不显示),并且通过 CSS 属性display: none; 隐藏
2.元素始终会被渲染,只是通过 CSS 控制其显示与隐藏
v-bind 给元素绑定属性 title="sdsd" v-bind:属性名=“变量” 简写 :属性名="值"
7.1列表渲染
v-for 列表渲染,将数据渲染到页面显示
<div v-for="(item, index) in items"></div> <div v-for="(val, key) in object"></div> <div v-for="(val, name, index) in object"></div>
注意:v-for不只是可以渲染数组、对象属性、指定次数(数字)、字符串等,v-for还可遍历方法(前提:方法return返回值是以上可渲染的数据类型)
遍历方法案例:情景描述
由于前一个下拉框选中项的crossId决定了后一个下拉框展示的下选项,且前一个下拉框的每一项数据包含后一项下拉框的所有数据选项。渲染数据是包含关系,但是两个下拉框又是并列关系,所以我们需要由前一项判断得出后一项。下面是代码:
原本写的代码:
后续大佬指点直接in function方式代码
<template v-for="(item, index) in coordCrossList.length"> <a-checkbox :value="index" class="checkboxStyle" style="margin-bottom:20px;"> <div class="checkboxStyle"> <!-- 下拉框1 匝道口--> <a-select v-model="coordCrossList[index].crossId" style="width: 200px;" @change="handleChange4"> <a-select-option :value="rampCrossings[INDEX].crossingId" v-for="(ITEM, INDEX) in rampCrossings"> {{ ITEM.name }} </a-select-option> </a-select> <!-- 下拉框2 控制模式--> <a-select v-model="coordCrossList[index].ctrSchema" style="width: 200px;margin: 0 20px 0 41px;" @change="handleChange5"> <a-select-option :value="ITEM.stageNo" v-for="(ITEM, INDEX) in getStageList(coordCrossList[index].crossId)"> {{ITEM.stageNo}}:{{ITEM.name}} </a-select-option> </a-select> <img src="../images/delete.png" alt="删除" width="14px"> </div> </a-checkbox> </template>
可以看到上方下拉二是直接渲染方法的返回值
方法代码:
methods: { //动态渲染 下拉二 控制模式的选项列表 getStageList(crossingId) { let item = this.findItem(this.rampCrossings, "crossingId", crossingId); if (!!item) { return item.stages; } return []; }, //数组中根据键值对找对象 findItem(arr, key, value) { for (let i in arr) { let item = arr[i]; if (item[key] === value) { return item; } } return null; }, }
v-for遍历对象举例:
v-for="(value, key,index) in obj"
:用于遍历对象,并渲染每个键值对<ul> <li v-for="(value,key,index) in obj" :key="key"> {{value}}---{{key}}----{{index}} </li> </ul> obj:{ name:"彭于晏", age:"18", id:'001' }
列表渲染中,以形参形式传递的item直接修改会改变原数组
代码举例:
html <p v-for="(item,index) in numArr" :key="index"> {{item.a}}{{item.b}} <button @click="itemChange(item)">点击传参,测试item修改情况</button> </p> js data中 numArr:[ {a:1,b:2,}, {a:3,b:4,} ] methods中 // 列表渲染中,以形参形式传递的item直接修改会改变原数组,不用再给data中的原数组赋值了 itemChange(item){ item.a = '修改展示'; console.log("item:",item,"this.numArr:",this.numArr); },
v-if 与v-else-if与v-else 如果表达式为
true
,则元素会被渲染;如果表达式为false
,则元素不会被渲染,并且不会占据任何空间
v-if 根据条件创建(显示)或者删除元素和组件
v-else-if 配合v-if使用
v-else 三个修饰符必须紧挨着使用(不能在指令之间插入其他元素)
7.2比较v-if 与v-show
1.性能考虑:
如果需要频繁切换 v-show 较好,不经常变化使用 v-if
2.初始渲染:
v-if 不会在初始渲染时添加元素,只有当条件满足时才会添加
v-show 会在初始渲染时添加元素,并立即通过 CSS 控制其显示与隐藏
3.过渡效果:
v-show 可以与 CSS 过渡效果配合使用,从而实现平滑的显示与隐藏效果
v-if 则不会有过渡效果,因为它在条件不成立时会直接移除元素
4.使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到
5. v-if配合template模板标签使用,不影响dom结构,v-show不能配合template标签使用
<template v-if= "true"> <span>1</span> <span>2</span> <span>3</span> <template>
v-cloak 解决差值表达式闪烁问题 用法:给含有差值变量的标签添加此指令,然后给这个指令设置样式display:none;在外链式的vue未被引入前,让含有v-cloak的标签全部隐藏,防止视图出现 " ¥{{ msg}}元 " 的未渲染情况。
<style> [v-cloak]{ display: none; } </style> <body> <div id="app" v-cloak>¥{{msg}}元</div> <script> new Vue({ el: '#app', data: { msg: '100' } }) </script> </body>
八、vue常见修饰符
表单修饰符
- lazy:失去焦点或按下回车后才更新
- trim:过滤首尾空格
- number:自动将用户输入转为数字类型,若先输入数字,则限制输入的只能是数字,若先输入的是字符串,则number修饰符无效
- 举例:
年龄<input type="number" v-model.number="formData.age">
事件修饰符
- stop:阻止事件冒泡
- prevent:阻止默认行为
- capture:当捕获存在时,先从外到里捕获,然后从里到外冒泡输出
- self:绑定事件的元素本身,才可触发该事件
- once:只触发一次回调
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕
- native: 组件上绑定原生DOM事件,使用native
Vue中常用的按键修饰符
<input type="text" placeholder="输入提示按下的键以及键值" @keydown="show"> <input type="text" placeholder="按下回车提示输入" @keydown.enter="showInfo"> show(e) { console.log(e.key, e.keyCode); }, showInfo(e) { console.log(e.target.value); },
- 回车:enter
- 删除:delete和backspace (捕获“删除”和“退格”键)
- 退出:esc
- 空格:space
- 换行:tab (特殊,必须配合keydown去使用)
- 上:up
- 下:down
- 左:left
- 右:right
- 系统修饰键(用法特殊):ctrl、alt、shift、meta(表示windows键)
配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
配合keydown使用:正常触发事件
系统修饰键+特定按键组合式写法:举例:ctrl+y组合键:@keyup.ctrl.y="showInfo"
如果键名由多个单词组成,你需要使用连字符(
-
)来分隔这些单词;例如,如果你想监听CapsLock
键,你需要使用Caps-Lock
作为键名:@keyup.caps-lock- 可以使用keyCode去指定具体的按键,比如:@keydown.13="showInfo",但不推荐这样使用
- Vue.config.keyCodes.自定义键名 = 键码,可以自定义按键别名(不推荐)
- 修饰符可以连续写,比如可以这么用:
@click.prevent.stop="showInfo"
鼠标修饰符
- left 左键点击
- right 右键点击
- middle 中键点击
九、vue监视数据原理
十、vue中数组/对象修改视图不更新问题
场景:vue(data中数据)已经完成初始化后,再通过索引修改数组或对对象属性做增删,页面不会动态渲染。
原因:Vue不能检测通过索引值修改数组元素和增删对象的属性变化,因为新增属性缺少对应的get、set方法,无法实现响应式。
解决方法1
Vue 将被侦听的数组的变更方法进行了包裹(通过下方方法操作的属性会添加对应的get、set方法,实现数据代理,完成响应式更新),所以它们也将会触发视图更新。
这些被包裹过的方法包括:
- push() 给末尾加元素
- pop() 清除末尾元素
- shift() 给数组的第一位进行删除
- unshift() 给数组首位添加元素
- splice() 删除元素/替换元素/插入元素
- sort() 以字母顺序对数组进行排序
- reverse() 翻转
举例:
this.deviceArr.unshift({name:'雷达',id:'001'}); // 数组首位添加元素
解决方法2
使用vue封装的API--Vue.set( target, propertyName/index, value )
使用该指令 修改/添加 数组或对象会生成对应的get、set方法,实现数据代理,完成响应式更新。
用法1 this.$set(this.arr,index,newVal); // 修改的数组, 索引, 新值 this.$set(this.obj,"a",'66'); // 修改/添加 的对象, 属性, 属性值 用法2 import Vue from 'vue'; Vue.set(this.arr,index,newVal); // 修改的数组, 索引, 新值 Vue.set(this.obj,"a",'66'); // 修改/添加 的对象, 属性, 属性值
同理也存在删除数组值或对象属性的API--Vue.delete( target, propertyName/index )
项目中遇到场景:在for循环遍历过程中给数组索引项添加属性并赋值会造成不渲染问题。
代码如下
this.listAll[i].deviceName = item.deviceList[0].device.name; this.listAll[i].deviceLaneId = item.deviceList[0].laneId;
解决方法:可在索引赋值前通过forEach先把属性添加上,然后在通过索引直接给属性赋值(关键是避免使用索引添加属性并直接赋值的情况)
具体原因:就是因为新增属性没有被vue做代理,缺少set、get方法。
底层原理观察
如下图,只要是数组中对象的属性存在相对应的get、set方法时(也就是vue对其完成了代理封装),通过 索引/下标 直接修改对象属性值,是可以正常完成视图更新的。
数据: personnelList: [ { name:'大脸胖柴', age:18, sex: "男", } ] 通过数组 下标/索引 修改对象属性值 this.personnelList[0].age = 16;