Vue笔记(详细 *实时更新)
一. Vue基础
邂逅Vue.js
初识Vue.js
Vue.js官网: https://www.vuejs.org.
Vue是一个渐进式 的框架
渐进式意味着你可以将Vue作为你应用的一部分嵌套其中
Vue的核心库以及其生态系统
Core + Vue-router + Vuex
Vue特点和Web开发常见高级功能
- 解耦视图和数据
- 可复用的组件
- 前端路由技术
- 状态管理
- 虚拟DOM
Vue安装方式
一、直接用 <script> 引入
直接下载并用 <script> 标签引入,Vue 会被注册为一个全局变量。
【注】
开发环境版本, 包含了帮助的命令行警告
生产环境版本, 优化了尺寸和速度
二、直接CDN引入
对于制作原型或学习,你可以这样使用最新版本:
<srcipt src='https://cdn.jsdeliver.net/npm/vue/dist/vue.js'></script>
对于生产环境,我们推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏:
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
如果你使用原生 ES Modules,这里也有一个兼容 ES Module 的构建文件:
<script type="module">
import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.esm.browser.js'
</script>
你可以在 https://cdn.jsdelivr.net/npm/vue/.浏览 NPM 包的源代码。
Vue 也可以在 unpkg 和 cdnjs 上获取 (cdnjs 的版本更新可能略滞后)。
请确认了解不同构建版本并在你发布的站点中使用生产环境版本,把 vue.js 换成 vue.min.js。这是一个更小的构建,可以带来比开发环境下更快的速度体验。
三、NPM安装
在用 Vue 构建大型应用时推荐使用 NPM 安装。NPM 能很好地和诸如 webpack 或 Browserify 模块打包器配合使用。同时 Vue 也提供配套工具来开发单文件组件。
通过webpack和CLI的使用
安装脚手架npm i -g vue vue-cli
【注】需要node.js npm
二. Vue基础语法
vue基本框架搭建
<!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="../js/vue.js"></script>
</head>
<body>
<div id="app">
{{name1}}
<li>{{age}}</li>
<li>{{sex}}</li>
</div>
<script>
const pos = new Vue({
el: "#app",
data: {
name1: "小明",
age: 20,
sex: "男",
},
});
</script>
</body>
</html>
1.引入vue.js后,给我们提供了一个构造函数 Vue
2.在js中,new Vue()
3.new Vue() 后会返回一个vue实例对象,我们用变量接着它
4.const pos = new Vue()
5.传入一个配置对象{} — 如const pos = new Vue({})
el
- 类型:字符串
- 全称:element(元素)
- 作用:配置控制的元素,表示Vue要控制的区域,值为css id选择器(挂载到dom里)
<div id="app">
</div>
<script>
const pos = new Vue({
el: "#app",
)}
</script>
$mount
- 作用和el一致,都是配置控制的元素,使用哪个都可以,二选一
new Vue({
render: (h) => h(App),
}).$mount("#app");
- 问:和el有什么不同?
- 答:本质上没什么不同,$mount为手动挂载,在项目中有时要进行延迟挂载,比如有时要在挂载之前进行一些其他的操作,比如判断等等
data
-
类型:对象
-
作用:存放要用到的数据,数据为响应式的
const pos = new Vue({
el: "#app",
data: {
name1: "小明",
age: 20,
sex: "男",
},
});
插值表达式
-
使用方法: {{ }}
-
作用:可以将vue中的数据填在插值表达式中,如:
<body>
<div id="app">
{{name1}}
<li>{{age}}</li>
<li>{{sex}}</li>
</div>
<script>
const pos = new Vue({
el: "#app",
data: {
name1: "小明",
age: 20,
sex: "男",
},
});
</script>
</body>
</html>
- 除了填写data之外,还可以直接填写数据值(数字、字符串、布尔值、undefined、null、数组、对象),如:
<div id="app">
{{ 5201314 }}
{{ '婀娜多姿、亭亭玉立' }}
{{ true }}
{{ ['小红', '小刘', '小宝'] }}
{{ {name: '小红', age: 80, height: '140cm', weight: '100kg'} }}
</div>
- 注意:在插值表达式中直接书写对象类型值时,不要将三个{}连在一起,这样会报错,如:
<div id="app">
<!-- 这样可是不行滴 -->
{{{name: '小明', age: 80, height: '140cm', weight: '100kg'}}}
</div>
- 还可在插值表达式中写表达式,如:
<div id="app">
<!-- 运算表达式 -->
{{ 'you' + 'me' }}
{{ 10 - 5 }}
{{ 100 * 7 }}
{{ 1000 / 12 }}
<!-- 逻辑表达式 -->
{{ liu || li }}
{{ deng && liu }}
{{ !wang }}
<!-- 三元表达式 -->
{{ 1 + 1 === 3 ? '小明' : '人' }}
<!-- 函数调用也是表达式,也可以使用,这个以后再学哈... -->
</div>
vue的响应式
- 数据变化,页面就会重新渲染
- 问:为什么data会直接出现在vm实例对象中咧?
答:当创建vue实例时,vue会将data中的成员代理给vue实例,目的是为了实现响应式,监控数据变化,执行某个监听函数(怎么实现的?想一想,提示:Object.defineProperty,试着实现一下)
- 问:实例中除了data数据外,其他东西是啥子?
答:为了防止名称冲突。因为会将data中数据代理给vue,假如说我们自己写的data名称和vue中自带的属性冲突了,那么就会覆盖vue内部的属性,所以vue会把自己内部的属性成员名称前加上 或 , 如 果 加 上 的 是 或_,如果加上的是 或,如果加上的是,代表是我们可以使用的,如果加上的是_,是vue自己内部使用的方法或属性,我们不需要调用
- 更改的数据必须是存在的数据,否则不能重新渲染页面,因为他监听不到,如:
<!-- 即使更改了数据,也不会重新渲染页面 -->
<div id="app">
{{ message.wife }}
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
message: {
name: '邓旭明',
age: 80,
height: '140cm',
weight: '100kg'
}
}
})
vm.mrDeng.wife = 'liu';
</script>
- 更改数据后,页面会立刻重新渲染吗?
vue更新DOM的操作是异步执行的,只要侦听到数据变化,将开启一个异步队列,如果一个数据被多次变更,那么只会被推入到队列中一次,这样可以避免不必要的计算和DOM操作。
$el
- 值为被Vue控制的元素(或者说,Vue挂载的元素)
vm.$nextTick & Vue.nextTick
如何在更改数据后,看到渲染后的页面上的值?
答:利用vm. n e x t T i c k 或 V u e . n e x t T i c k , 在 页 面 重 新 渲 染 , D O M 更 新 后 , 会 立 刻 执 行 v m . nextTick或 Vue.nextTick,在页面重新渲染,DOM更新后,会立刻执行vm. nextTick或Vue.nextTick,在页面重新渲染,DOM更新后,会立刻执行vm.nextTick
<div id="app">{{ name }}</div>
<script>
const vm = new Vue({
el: '#app',
data: {
name: '小明'
}
})
vm.name= '小美';
console.log(vm.msg); // 小美,此时数据已更改
// 1. 使用vm.$nextTick
vm.$nextTick(() => {
console.log(vm.$el.innerHTML); // 小美
})
// 2. 使用Vue.nextTick
Vue.nextTick(() => {
console.log(vm.$el.innerHTML); // 小美
})
</script>
- vm.nextTick和Vue.nextTick还可以作为Promise使用
<div id="app">{{ msg }}</div>
<script>
const vm = new Vue({
el: '#app',
data: {
msg: '杉杉'
}
})
vm.msg = '杉杉超美的';
// 1. 使用vm.$nextTick
vm.$nextTick().then(() => {
console.log(vm.$el.innerHTML); // 杉杉超美的
})
// 2. 使用Vue.nextTick
Vue.nextTick().then(() => {
console.log(vm.$el.innerHTML); // 杉杉超美的
})
</script>
- vm.$nextTick 和 Vue.nextTick的区别?
Vue.nextTick内部函数的this指向window
vm.$nextTick内部函数的this指向Vue实例对象
-
好奇nextTick是怎么实现的吗?
-
异步任务分为宏任务(macro)和微任务(micro)
-
宏任务比较慢(如setTimeout等),微任务比较快(如Promise.then()等)
-
微任务在前,宏任务在后(eventloop,事件环)
// 控制台打印顺序:promise > timeout
setTimeout(() => {
console.log('timeout');
}, 0)
Promise.resolve().then(() => {
console.log('promise');
})
- 在nextTick的实现源码中,会先判断是否支持微任务,不支持后,才会执行宏任务
if(typeof Promise !== 'undefined') {
// 微任务
// 首先看一下浏览器中有没有promise
// 因为IE浏览器中不能执行Promise
const p = Promise.resolve();
} else if(typeof MutationObserver !== 'undefined') {
// 微任务
// 突变观察
// 监听文档中文字的变化,如果文字有变化,就会执行回调
// vue的具体做法是:创建一个假节点,然后让这个假节点稍微改动一下,就会执行对应的函数
} else if(typeof setImmediate !== 'undefined') {
// 宏任务
// 只在IE下有
} else {
// 宏任务
// 如果上面都不能执行,那么则会调用setTimeout
}
- 除了未被声明过和未被渲染的数据外,还有什么数据更改后不会渲染页面?
- 利用索引直接设置一个数组项时:
<!-- 即使向数组中添加了第4项,数组仍然显示3项 -->
<div id="app">{{ list }}</div>
<script>
const vm = new Vue({
el: '#app'
data: {
dengFamily: ['小明', '小刘', '小宝']
}
})
vm.dengFamily[3] = '铁锤妹妹'; // 不是响应式的
</script>
- 修改数组的长度时:
<!-- 更改了数组长度后,数组仍然显示1项 -->
<div id="app">{{ list }}</div>
<script>
const vm = new Vue({
el: '#app'
data: {
dengWife: ['小刘']
}
})
vm.dengWife.length = 0; // 不是响应式的
</script>
- 添加或删除对象:
<!-- 身高还是那个身高,媳妇也只有一个,不要痴心妄想 -->
<div id="app">{{ deng }}</div>
<script>
const vm = new Vue({
el: '#app'
data: {
deng: {
wife: '小刘',
son: '小宝',
weight: '100kg',
height: '140cm',
age: 60
}
}
})
vm.deng.secondWife = '铁锤妹妹'; // 不是响应式的
delete vm.deng.height; // 不是响应式的
</script>
- 要如何响应式的更新数组和对象?
更改数组:
利用数组变异方法:
push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度、
pop()方法可以将数组最后一位元素删除
shiftshift()方法可以将数组第一位元素删除
unshift()方法可以在数组开头添加任意数量的元素
splice() 有三个参数,第一个是想要删除的元素的下标(必选),第二个是想要删除的个数(必选),第三个是删除后原位置想要替换的值
sort() 是数组按照字符编码默认从小到大排序,成功返回排序后的数组
reverse() 将数组倒序,成功返回倒序的数组
更改对象:
1.利用vm. s e t / V u e . s e t 实 例 方 法 : v m . set/Vue.set实例方法: vm. set/Vue.set实例方法:vm.set是Vue.set的别名
使用方法:Vue.set(object, propertyName, value),也就是这个意思:Vue.set(要改谁,改它的什
么,改成啥)
2.利用vm. d e l e t e / V u e . d e l e t e t 删 除 数 组 中 的 某 一 项 : v m . delete/Vue.deletet删除数组中的某一项:vm. delete/Vue.deletet删除数组中的某一项:vm.delete是Vue.delete的别名
使用方法:Vue.delete(object, target),也就是这个意思:Vue.delete(要删除谁的值,删除哪个)
一. Vue基础
Vue相关指令
- 具有特殊含义、拥有特殊功能的特性
- 指令带有v-前缀,表示它们是Vue提供的特殊特性
- 指令可以直接使用data中的数据
v-pre
- 跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
<!-- 不会被编译 -->
<span v-pre>{{ msg }}</span>
v-cloak
- 这个指令保持在元素上直到关联实例结束编译
- v-cloak 在vue解析之前有一个属性v-cloak,解析之后消失
[v-cloak] {
display: none;
}
<div v-cloak>
{{ message }}
</div>
- 和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache(也就是{{双花括号}} ) 标签直到实例准备完毕
v-once
- 只渲染元素一次。随后的重新渲染,元素及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能
- v-once {{}}中内容一次性显示
v-text
<span v-text="msg"></span>
<!-- 和下面的一样 -->
<span>{{msg}}</span>
- v-text替换元素中所有的文本,Mustache只替换自己,不清空元素内容
<!-- 渲染为:<span>杉杉最美</span> -->
<span v-text="msg">----</span>
<!-- 渲染为:<span>----杉杉最美----</span> -->
<span>----{{msg}}----</span>
- v-text 优先级高于 {{ }}
v-html
-
更新元素的innerHTML
-
注意:内容按普通 HTML 插入,不会作为 Vue 模板进行编译
-
在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击。只在可信内容上使用 v-html,永不用在用户提交的内容上。
<button>点击</button>
<div id="app">
<div v-html="msg"></div>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
msg: "hello world",
},
});
const oButton = document.getElementsByTagName("button")[0];
let msg = "<p>这是一个p</p>";
oButton.onclick = function () {
vm.msg = msg;
};
</script>
效果图
条件渲染
v-if
- 用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true 的时候被渲染。
v-else
- 为 v-if 或者 v-else-if 添加“else 块”。
- 注意:前一兄弟元素必须有 v-if 或 v-else-if
<div v-if="Math.random() > 0.5">
小明
</div>
<div v-else>
小红
</div>
v-else-if
-
表示 v-if 的 “else if 块”。可以链式调用。
-
注意:前一兄弟元素必须有 v-if 或 v-else-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>
v-show
- 根据表达式之真假值,切换元素的 display CSS 属性。
- 也就是判断该内容是否显示
v-if与v-show的区别
v-if 是惰性的,如果在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块。v-show则不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
v-if 有更高的切换开销,v-show 有更高的初始渲染开销,如果需要非常频繁地切换,则使用 v-show 较好,如果在运行时条件很少改变,则使用 v-if 较好
v-show不支持< template>元素
v-show不支持v-else/v-else-if
v-bind 指令
- v-bind用于绑定一个或多个属性值
- 语法糖写法:属性=“值”
<!-- 绑定一个属性 -->
<img v-bind:src="imageSrc">
<!-- 动态特性名 (2.6.0+) -->
<button v-bind:[key]="value"></button>
<!-- 缩写 -->
<img :src="imageSrc">
<!-- 动态特性名缩写 (2.6.0+) -->
<button :[key]="value"></button>
<!-- 内联字符串拼接 -->
<img :src="'/path/to/images/' + fileName">
- 没有参数时,可以绑定到一个包含键值对的对象。注意此时 class 和 style 绑定不支持数组和对象。
<!-- 绑定一个有属性的对象 -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
- 绑定class
- 对象语法
<div v-bind:class="{ red: isRed }"></div>
上面的语法表示 red 这个 class 存在与否将取决于数据属性 isRed 的 真假。
- 数组语法
我们可以把一个数组传给 v-bind:class,以应用一个 class 列表
<div v-bind:class="[classA, classB]"></div>
- 在数组语法总可以使用三元表达式来切换class
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
class选取哪一个,取决与isActive 的 真假。
- v-bind:class 可以与普通 class 共存
<div v-bind:class="classA" class="red">
- 绑定style
- 使用对象语法
看着比较像CSS,但其实是一个JavaScript对象
CSS属性名可以用驼峰式(camelCase)或者短横线分隔(kebab-case)来命名
但是使用短横线分隔时,要用引号括起来
<div v-bind:style="{ fontSize: size + 'px' }"></div>
data: {
size: 30
}
也可以直接绑定一个样式对象,这样模板会更清晰:
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
fontSize: '13px'
}
}
- 使用数组语法
数组语法可以将多个样式对象应用到同一个元素
<div v-bind:style="[styleObjectA, styleObjectB]"></div>
[注]加单引号为属性值,不加解析为变量
- 修饰符:
修饰符 (modifier) 是以英文句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。 - .camel
由于绑定特性时,会将大写字母转换为小写字母
<svg :viewBox='viewBox'></svg>
<script>
const vm = new Vue({
el: "#app",
data: {
viewBox:'0 0 100 100'
}
})
</script>
<svg :view-box.camel='viewBox'></svg>
- .prop
被用于绑定 DOM 属性 (property)
<div v-bind:text-content.prop="text"></div>
<script>
const vm = new Vue({
el: "#app",
data: {
text:'智者不如爱河'
}
})
</script>
- .sync
讲解组件时再说
v-on指令
- v-on 指令可以监听 DOM 事件,并在触发时运行一些 JavaScript 代码
- 事件类型由参数指定
<div id="app">
<button v-on:click="counter += 1">点击加 1</button>
<p>按钮被点击了 {{ counter }} 次</p>
</div>
const vm = new Vue({
el: 'app',
data: {
counter: 0
}
})
- 但是很多事件处理逻辑是非常复杂的,所以直接把 JavaScript 代码写在 v-on 指令中是不可行的。所以 v-on 还可以接收一个需要调用的方法名称。
<div id="app">
<!-- `addCounter` 是在下面定义的方法名 -->
<button v-on:click="addCounter">点击加 1</button>
<p>按钮被点击了 {{ counter }} 次</p>
</div>
const vm = new Vue({
el: '#app',
data: {
counter: 0
},
// 在 methods 对象中定义方法
methods: {
addCounter(e) {
// this 在方法里指向当前 Vue 实例
this.counter += 1;
// e 是原生 DOM 事件
console.log(e.target);
}
}
})
- 可以绑定动态事件,Vue版本需要2.6.0+
<div v-on:[event]="handleClick">点击,弹出1</div>
const vm = new Vue({
el: '#app',
data: {
event: 'click'
},
methods: {
handleClick () {
alert(1);
}
}
})
- v-on指令简写:@
为什么在 HTML 中监听事件?
- 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
- 因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM - 完全解耦,更易于测试
- 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们
v-on指令的修饰符
.stop
- 调用 .stop,阻止事件冒泡
<!-- 此时只弹出button -->
<div id="app">
<div @click="alert('div')">
<button @click.stop="alert('button')">点击</button>
</div>
</div>
const vm = new Vue({
el: '#app',
methods: {
alert(str) { alert(str); }
}
})
.prevent
- 调用 event.preventDefault(),阻止默认事件
<!-- 点击提交按钮后,页面不会重载 -->
<form v-on:submit.prevent="onSubmit">
<input type="submit" />
</form>
<!-- 也可以只有修饰符 -->
<form v-on:submit.prevent>
<input type="submit" />
</form>
</div>
<!-- 会刷新页面 -->
<form submit="onSubmit">
<input type="submit" />
</form>
<script>
const vm = new Vue({
el: "#app",
methods: {
onSubmit() {
console.log("onSubmit");
},
},
});
</script>
.capture
- 事件捕获模式
<!-- 此时先弹出div再弹出button -->
<div id="app">
<div @click.capture="alert('div')">
<button @click="alert('button')">点击</button>
</div>
</div>
.self
- 只当事件是从侦听器绑定的元素本身触发时才触发回调(也就是说必须是本身触发才会回调)
<!-- 点击button时,只弹出 button -->
<div id="app">
<div id="app">
<div :style="{ backgroundColor: 'red' }"
@click.self="alert('div')">
<button @click="alert('button')">点击</button>
</div>
</div>
</div>
.once
-
只触发一次回调
-
2.1.4新增
.passive
- 设置 addEventListener 中的 passive 选项
- 能够提升移动端的性能
- 2.3.0新增
即使在触发触摸事件时,执行了一个空的函数,也会让页面卡顿。因为浏览器不知道监听器到底会不会阻止默认事件,所以浏览器要等到执行完整个函数后,才能决定是否要滚动页面。passive事件监听器,允许开发者告诉浏览器,监听器不会阻止默认行为,从而浏览器可以放心大胆的滚动页面,这样可以大幅度提升移动端页面的性能,因为据统计只有20%的触摸事件会阻止默认事件。
- .passive 会告诉浏览器你不想阻止事件的默认行为
注意
使用修饰符时,顺序很重要。相应的代码会以同样的顺序产生。因此,
v-on:click.prevent.self 会阻止所有的点击的默认事件
v-on:click.self.prevent 只会阻止对元素自身点击的默认事件
不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。