目录
一.vue3介绍
1. 官网初识
2.环境搭建
2-1 线上尝试
2-2 CDN使用
2-3 Vue CLI
2-4 Vite
二.vue3基础
1.模板语法
1-1 我的第一个vue应用
1-2 应用背后的真相
1-3 模板语法-新的皮肤来了
1-4 Todolist-来个案例
1-5 点击变心案例 - 是变色
1-6 v-html- 模板的陷阱
2.class与style
2-1 class的绑定-方法太多了
2-2 style的绑定-同上
3.条件渲染
3-1 条件渲染-生或死的选择
4.列表渲染
4-1 v-for列表渲染 - 影分身术
4-2 key设置 - 性能的保障
4-3 数组变动侦测
4-4 模糊搜索案例
一.vue3介绍
1. 官网初识
Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还 是复杂的界面,Vue 都可以胜任。
https://cn.vuejs.org/
2.环境搭建
2-1 线上尝试
2-2 CDN使用
通过 CDN 使用 Vue 时,不涉及“构建步骤”。这使得设置更加简单,并且可以用于增强静态的 HTML 或与后端框架 集成。但是,你将无法使用单文件组件 (SFC) 语法。
2-3 Vue CLI
Vue CLI 是官方提供的基于 Webpack 的 Vue 工具链,它现在处于维护模式。我们建议使用 Vite 开始新的项目,除 非你依赖特定的 Webpack 的特性。在大多数情况下,Vite 将提供更优秀的开发体验。
2-4 Vite
Vite 是一个轻量级的、速度极快的构建工具,对 Vue SFC 提供第一优先级支持。作者是尤雨溪,同时也是 Vue 的 作者!
要使用 Vite 来创建一个 Vue 项目,非常简单:
$ npm init vue@latest
这个命令会安装和执行 create-vue,它是 Vue 提供的官方脚手架工具。跟随命令行的提示继续操作即可。
二.vue3基础
1.模板语法
1-1 我的第一个vue应用
<body>
<div id="box">
{{10+20}}
{{name}}
</div>
</body>
<script>
var app = Vue.createApp({
data() {
return {
name: "贾公子"
}
}
}).mount("#box")
</script>
推荐使用的 IDE 是 VSCode,配合 Vue 语言特性 (Volar) 插件。该插件提供了语法高亮、TypeScript 支持,以 及模板内表达式与组件 props 的智能提示。
Volar 取代了我们之前为 Vue 2 提供的官方 VSCode 扩展 Vetur。如果你之前已经安装了 Vetur,请确保在 Vue 3 的项目中禁用它。
1-2 应用背后的真相
(1)Object.deneProperty
无法监听数组的改变, 无法监听class改变, 无法监听Map Set结构。
var obj = {}
var obox = document.getElementById("box")
Object.defineProperty(obj, "myname", {
get() {
console.log("有人访问了")
return obox.innerHTML
},
set(value) {
console.log("有人改变我了", value)
obox.innerHTML = value
}
})
obj.myname = '贾公子'
console.log(obj.myname);
proxy 代理
访问代理对象进行拦截 支持数组的拦截
var obj = {
}
var obox = document.getElementById("box")
var vm = new Proxy(obj, {
get(target, key) {
console.log("get")
return target[key]
},
set(target, key, value) {
console.log("set")
target[key] = value
obox.innerHTML = value
}
})
obj.a = 100
vm.a = 200
console.log(obj.a);
console.log(vm.a);
1-3 模板语法-新的皮肤来了
(1)最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号):
<span>Message: {{ msg }}</span>
双大括号标签会被替换为相应组件实例中 msg 属性的值。同时每次 msg 属性更改时它也会同步更新。
(2)双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind 指令: template
注意disbale不为false都是禁用
<div v-bind:id="dynamicId"></div>
v-bind 指令指示 Vue 将元素的 id attribute 与组件的 dynamicId 属性保持一致。如果绑定的值是 null 或者 undened ,那么该 attribute 将会从渲染的元素上移除。
(3)表达式的支持
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
(4)指令
<a v-on:click="doSomething"> ... </a>
<!-- 简写 -->
<a @click="doSomething"> ... </a>
1-4 Todolist-来个案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="box">
<input type="text" v-model="mytext">
<button @click="handleAdd">add</button>
<ul>
<li v-for="data,index in datalist">
{{data}}
<button @click="handleDel(index)">del</button>
</li>
</ul>
</div>
<script>
var obj = {
data() {
return {
mytext: "",
datalist: ["11", "22", "33"],
}
},
methods: {
handleAdd(ev) {
this.datalist.push(this.mytext)
},
handleDel(index) {
this.datalist.splice(index, 1)
}
}
}
var app = Vue.createApp(obj).mount("#box")
</script>
</body>
</html>
1-5 点击变心案例 - 是变色
<!--
* @作者: kerwin
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
.active {
background-color: red;
}
</style>
</head>
<body>
<div id="box">
<ul>
<li v-for="data,index in datalist" @click="handleclick(index)" :class="current===index?'active':''">
{{data}}
</li>
</ul>
</div>
<script>
var obj = {
data() {
return {
datalist: ["11", "22", "33"],
current: 0
}
},
methods: {
handleclick(index) {
this.current = index
}
}
}
var app = Vue.createApp(obj).mount("#box")
</script>
</body>
</html>
1-6 v-html- 模板的陷阱
双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html 指令:
<p>Using text interpolation: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
2.class与style
2-1 class的绑定-方法太多了
对象写法
data() {
return {
classObject: {
active: true,
'text-danger': false
}
}
}
<div :class="classObject"></div>
数组写法
data() {
return {
activeClass: 'active',
errorClass: 'text-danger'
}
}
<div :class="[activeClass, errorClass]"></div>
2-2 style的绑定-同上
对象写法
data() {
return {
styleObject: {
color: 'red',
fontSize: '130px'
}
}
}
<div :style="styleObject">11</div>
数组写法
<body>
<div id="box">
<div :style="arr">11</div>
<div @click="handleAjax">请求</div>
</div>
</body>
<script>
var obj = {
data() {
return {
arr: [{
width: "200px",
height: "200px",
backgroundSize: "cover"
}],
}
},
methods: {
handleAjax() {
this.arr.push({
backgroundImage: "url(https://pic.maizuo.com/usr/movie/862ab6736237acd11599e5eecbbc83d7.jpg?x-ossprocess=image/quality,Q_70)"
})
},
},
}
var app = Vue.createApp(obj).mount("#box")
</script>
3.条件渲染
3-1 条件渲染-生或死的选择
v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被 渲染。
相比之下, v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display 属性会被切换。
总的来说, v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 vshow 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。
template包装 不会多数据结构
<body>
<div id="box">
<ul>
<li v-for="item,index in datalist">
{{item.title}}
<div v-if="item.state===0">
未付款
</div>
<div v-else-if="item.state===1">
未发货
</div>
<div v-else-if="item.state===2">
已发货
</div>
<div v-else>
已完成
</div>
</li>
</ul>
</div>
</body>
<script>
var obj = {
data() {
return {
datalist: [
{
state: 0,
title: "111"
},
{
state: 1,
title: "222"
},
{
state: 2,
title: "333"
}
]
}
},
}
var app = Vue.createApp(obj).mount("#box")
</script>
4.列表渲染
4-1 v-for列表渲染 - 影分身术
v-for与对象
小括号
解构赋值
in===of(js迭代器)
<body>
<div id="box">
<li v-for="(value,key,index) of myObject">
{{key}}-- {{ value }}--{{index}}
</li>
</div>
</body>
<script>
var obj = {
data() {
return {
myObject: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
}
}
var app = Vue.createApp(obj).mount("#box")
</script>
v-for和v-if的优先级
永远不要把 v-if 和 v-for 同时用在一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
如果避免出现这种情况,则在外层嵌套 template (页面渲染不生成dom节点),再这一层进行 v-if 判断,然后再内部进行 v-for 循环
<template v-for="(item) in textValue">
<div :key="item.text" v-if="item.show" >{{item.text}}</div>
</template>
4-2 key设置 - 性能的保障
Vue 默认按照“就地更新”的策略来更新通过 v-for 渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。
为了给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对 应的块提供一个唯一的 key attribute:
key就是一个标识,被使用在Vue中。再详细一点,key被使用在Vue中的虚拟DOM中,并不会出现在真实DOM中。
1.以列表的形式展示一组人员信息。
<body>
<div id="box">
<h2>人员列表</h2>
<ul>
<li v-for="(p,index) in persons">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
</body>
<script>
var obj = {
data() {
return {
persons: [
{ 'id': '001', 'name': '张三', 'age': '18' },
{ 'id': '002', 'name': '李四', 'age': '19' },
{ 'id': '003', 'name': '王五', 'age': '20' }
],
msg: '11'
}
}
}
var app = Vue.createApp(obj).mount("#box")
</script>
这个html文件在浏览器中打开如下图所示。
而上述示例html文件中并没有使用到key,似乎也没有问题。当然,单纯地展示数据,不写key是不会存在问题的。
现在我们为上述示例加上key,这里以每条数据的id为key
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
加上key的展示结果和上图结果一模一样。
而如果我们在浏览器上查看元素,不会看到key的存在。
截至目前,我们可以得到两个结论:
1. 只做数据展示用,不写key是没有任何影响的;
2.key不会出现在真实DOM中
实际上,即使不写key,Vue在生成真实DOM时,也用到了key,默认是数据索引(index)
我们把key替换为index,展示的数据不会产生任何改变。
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}
</li>
2 修改一下上述示例
在展示人员信息的基础上显示索引,并且添加一个按钮,功能是在头部添加人员信息
对上述html文件稍加修改。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="box">
<h2>人员列表</h2>
<button @click="add">添加一个老刘</button>
<ul>
<li v-for="(p,index) in persons">
{{p.name}}-{{p.age}}-{{index}}
</li>
</ul>
</div>
</body>
<script>
var obj = {
data() {
return {
persons: [
{ 'id': '001', 'name': '张三', 'age': '18' },
{ 'id': '002', 'name': '李四', 'age': '19' },
{ 'id': '003', 'name': '王五', 'age': '20' }
],
}
},
methods: {
add() {
const p = { 'id': '004', 'name': '老刘', 'age': '40' }
this.persons.unshift(p)
}
}
}
var app = Vue.createApp(obj).mount("#box")
</script>
</html>
我们可以看到,张三、李四、王五的索引分为别0,1,2
点击按钮,添加一个新人物,这个时候索引发生了变化,新添加的人物“老刘”变为了索引0,似乎对,也似乎不对
当然,单纯地讨论索引,这里一点问题也没有,下面举一个新例子,来说说“不对“在哪里
3 再修改一下示例
不展示索引了,改为输入框,在每个人物后面的输入框内写上人物的姓,观察新插入数据后原始数据的变化
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}
<input type="text">
</li>
实际效果就是下图这样
到这里,似乎没有什么不对,接下来就是见证奇迹的时刻
添加老刘,出现了问题,和我们预想的不一样。
这是key为index的情况,如果修改为数据的唯一标识,则不会产生这样的问题。
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}
<input type="text">
</li>
诶,这就是我们想要的。
列表内有输入内容,后续操作破坏了原始顺序,如果以index作为key,就会产生错误DOM
4,key的实现原理
要解释key的实现原理,就要引入Vue一个十分重要的概念——【虚拟DOM】。
给出一组数据,Vue要把这些数据渲染到页面上,首先要生成【虚拟DOM】,然后根据【虚拟DOM】去生成【真实的DOM】。如果数据发生了改变,Vue会生成【新的虚拟DOM】,注意,这个【新的虚拟DOM】并不会直接生成【新的真实DOM】,否则虚拟DOM一点用处也没有了。
Vue的操作是,拿根据新的数据生成的【新的虚拟DOM】与之前的【真实的DOM】去做比较,如果相同,直接延用即可(“拿来主义”);如果不同,则生成新的DOM对象。
在这个过程中key扮演了很重要的角色。
根据最后一个示例进行剖析。
key为index的情况。
根据数据生成【真实DOM】的流程如下:(注意,下图的真实DOM中输入框里的内容为生成页面后手动添加)
然后,添加人物“老刘”,获取到一组新数据
Vue拿新数据生成【新的虚拟DOM】
在生成真实DOM,就需要用新生成的虚拟DOM和原来的真实DOM作比较(一条一条分析)
对比第一条,key为0,找到旧DOM中key为0的数据,发现“老刘-40”和“张三-18”不同,渲染新的数据“老刘-40”到页面上;再往后,发现同为输入框,不必重新渲染,直接使用原来真实DOM的内容。第一条内容就出现了,而这个输入框还携带有张三的姓。
对比第二条,key为1,找到旧DOM中key为1的数据,发现“张三-18”和“李四-19”不同,渲染新的数据“张三-18”到页面上;再往后,发现同为输入框,不必重新渲染,直接使用原来真实DOM的内容。第二条内容就出现了,而这个输入框还携带有李四的姓
之后同理。
回顾这个过程,key是作为虚拟DOM中对象的唯一标识,标识出了数据的“身份信息”,Vue在虚拟DOM中会根据这个“身份标识”去对比内容,设计的初衷是为了节省资源开支,不必渲染重复的部分。在本示例中,不但带来了效率问题,还渲染出了错误的DOM,后果非常严重。
2. key为id的情况。
直接进入添加“老刘”后的新旧DOM对比。
对比第一条,key为‘004’,发现在旧DOM中并不存在,直接生成“老刘-40”和新的输入框。
对比第二条,key为‘001’,发现旧DOM中key为‘001’的数据相同,直接将“张三-18”和输入框拿过来使用。
……
最后生成正确的DOM,节省了资源开支。
总结
推荐使用数据的唯一标识作为key,比如id,身份证号,手机号等等,通常这些数据由后端提供。
后续操作不破坏原来数据顺序的话,使用index作为key也没有任何问题。
4-3 数组变动侦测
vue3
Vue 能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。这些变更方法包括:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
对于一些不可变 (immutable) 方法,例如 filter() , concat() 和 slice() ,这些都不会更改原数组,而总是返回一个 新数组。当遇到的是非变更方法时,我们需要将旧的数组替换为新的
vue2
一、数组
1. 不能监听的情况
直接通过下标赋值 arr[i] = value ,直接修改数组长度 arr.length = newLen
2. 替代做法
(1)修改值
- 1. Vue.set(arr, index, newvalue)
- 2. vm.$set(arr, index, newvalue)
- 3. arr.splice(index, 1, newvalue)
(2) 修改数组长度, arr.splice(newLen)
调用数组的pop、push、shift、unshift、splice、sort、reverse等方法时是可以监听到数组的变化的vue内部相当于重写了数组的原型,劫持了这七个方法
二、对象
不能监听的情况 属性的新增和删除
obj.newkey=newvalue
delete obj.key
2. 替代做法
新增
1. Vue.set(obj, newkey, newvalue)
2. vm.$set(obj, newkey, newvalue)
3. obj = Object.assign({}, obj, {newkey1: newvalue1, newkey2: newvalue2})
删除
1. Vue.delete(obj, key)
2. vm.$delete(obj, key)
三、分析 vue 2 无法监听数组和对象的这些变化的原因
首先,vue2是通过Object.defineProperty(obj, key, value)这种方式监听数据的
1. 对于数组
Object.defineProperty()是可以对数组实现监听操作的,但是vue并没有实现这个功能,因为数组长度不定而且数据可能会很多,如果对每一个数据都实现监听,性能代价太大
但是注意:数组中的元素是引用类型时是会被监听的
2. 对象
Object.defineProperty()针对的是对象的某个属性,而且这个操作在vue的初始化阶段就完成了,所以新增的属性无法监听,通过set方法新增对象就相当于初始化阶段的数据响应式处理
vue 3是通过proxy直接代理整个对象来实现的,而不是像Object.defineProperty针对某个属性。所以,只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性
4-4 模糊搜索案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="box">
<input type="text" v-model="mytext" />
<ul>
<li v-for="data in filterList()" :key="data">
{{data}}
</li>
</ul>
</div>
<script>
var obj = {
data() {
return {
mytext: "",
datalist: ["aaa", "abb", "aab", "bcc", "abc", "bcd", "add", "acd"]
}
},
methods: {
filterList(evt) {
return this.datalist.filter(item => item.includes(this.mytext))
}
}
}
Vue.createApp(obj).mount("#box")
</script>
</body>
</html>