Vue
Vue 的基本概念
为什么要学 vue
开发更加的效率和简洁,易于维护,快!快!快!就是快。
什么是 vue
Vue 是一个 渐进式 的 javascript 框架,拥有一套属于自己规则的语法。
官网地址:vue官网
补充知识点:
什么是渐进式
渐进式:逐步进步,想用什么就用什么,不必全部使用。
什么是库和框架
- 库:封装的属性或方法 (例jquery.js)
- 框架:拥有自己的规则和元素, 比库强大的多 (例vue.js)拥有自己的规则和元素, 比库强大的多 (例vue.js)
开发方式
- 传统开发方式:基于
html/css/js文件开发vue - 工程化开发方式:在
webpack环境中开发vue(推荐这种方式)
脚手架 @vue/cli
脚手架介绍
自己配置 webpack 的环境十分麻烦, Vue 官方提供了一个全局模块包,这个包用于创建脚手架项目。
脚手架的好处是无需我们配置 webpack 环境,下载即可使用,支持 css 、 less 、 babel 、开发服务器等。
脚手架安装
需要把脚手架
@vue-cli模块包安装到全局,让电脑拥有vue命令,才能创建脚手架工程。
- 全局安装命令
yarn global add @vue/cli # or npm i -g @vue/cli注意:
如果下载失败查看一下
npm的镜像源,命令nrm ls。 切换成淘宝镜像源可减少连接超时的错误。 - 查看版本
vue -V
创建项目启动服务
通过
vue命令创建一个脚手架的项目。注意:项目名不能带大写字母、中文和特殊符号。
-
创建项目
vue create 文件夹名称 -
选择模板
可以选择默认模板,也可选择自己手动配置一个新的模板。
开启成功后会出现两条代码指示,第一条让我们进入目标文件内,第二条是开启命令。
进入文件内并成功开启命令后就有相关路径。
目录和代码分析
vuecil-demo # 项目目录
├── node_modules # 项目依赖的第三方包
├── public # 静态文件目录
├── favicon.ico# 浏览器小图标
└── index.html # 单页面的html文件(网页浏览的是它)
├── src # 业务文件夹
├── assets # 静态资源
└── logo.png # vue的logo图片
├── components # 组件目录
└── HelloWorld.vue # 欢迎页面vue代码文件
├── App.vue # 整个应用的根组件
└── main.js # 入口js文件
├── .gitignore # git提交忽略配置
├── babel.config.js # babel配置
├── package.json # 依赖包列表
├── README.md # 项目说明
└── yarn.lock # 项目包版本锁定和缓存地址
主要文件及含义
node_modules下都是下载的第三方包
public/index.html – 浏览器运行的网页
src/main.js – webpack打包的入口文件
src/App.vue – vue项目入口页面,根主键
package.json – 依赖包列表文件
项目架构了解
首先去 main.js 入口文件,去到 App.vue 根组件模块,获取到样式、标签、逻辑,返回 main.js 文件。 new 实例化一个 Vue 对象,把属性插入到 index.html 页面中。
自定义配置
在
webpack中我们通过webpack.config.js文件进行自定义配置,如端口号、出入口文件等,vue中则是vue.config.js文件进行自定义配置。
在 src 文件夹的同级目录下新建一个 vue.config.js 文件。自定义端口号命令和 webpack 的一样。
module.exports = {
devServer: {
port : 3000
}
}
修改完毕后重新启动即可。
eslint 的作用
eslint是一个代码检查工具,检测代码的严谨程度
模拟情景:声明一个变量,但不使用,保存运行,结果报错。
解决方法:
- 手动解决掉错误, 以后会讲如何自动解决
- 暂时关闭
eslint检查(因为现在主要精力在学习Vue语法上), 在vue.config.js中配置后重启服务。module.exports = { // 暂时关闭eslint配置 lintOnSave: false }
单文件 vue 讲解
单文件最大的好处,就是独立作用域,互不影响。
Vue中更推荐采用.vue组件来开发项目。
-
template里只能有一个根标签
<template> <div id="app"> </div> <div id="box"></div> </template>结果会报错。
-
vue文件-独立模块-作用域互不影响。 -
style配合scoped属性, 保证样式只针对当前template内标签生效。<style scoped> </style> -
vue文件配合webpack, 把他们打包起来插入到index.html。
Vue 指令
插值表达式
可以直接在
dom元素中插入内容。
语法:
{{ 表达式 }}
在插值表达式中,可以添加文本内容,可以用三元表达式进行判断处理,可以进行简单的运算,还可以字符串内置api进行处理。但是不能写语句,如if判断和for循环。
<template>
<div>
<h1>{{ msg }}</h1>
<h2>{{ obj.name }}</h2>
<h3>{{ obj.age > 18 ? '成年' : '未成年' }}</h3>
</div>
</template>
<script>
export default {
data() { // 格式固定, 定义vue数据之处
return { // key相当于变量名
msg: "hello, vue",
obj: {
name: "小vue",
age: 5
}
}
}
}
</script>
总结:
dom中插值表达式赋值,vue的变量必须在data里声明。
MVVM 设计模式
转变思维, 用数据驱动视图改变, 操作dom的事, vue源码内干了。
设计模式: 是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。
-
M:model数据模型 (data里定义) -
V:view视图 (html页面) -
VM:ViewModel视图模型 (vue.js源码) -
MVVM通过
数据双向绑定让数据自动地双向同步 不再需要操作DOM- V(修改视图) -> M(数据自动同步)
- M(修改数据) -> V(视图自动同步)
指令 v-bind
作用:给标签属性设置
vue变量的值。
前面的插值表达式只能用在元素内容上,不能用在元素属性上。如: src="" 、value="" 等。这个时候就用到了 v-bind 指令。
- 语法:
v-bind:属性名="vue变量" - 简写:
:属性名="vue变量"
在使用 v-bind 属性绑定期间,如果绑定内容需要进行动态拼接,字符串的外面需要包裹一层单引号。如果不包裹单引号,则默认为是一个变量,会去 data 内寻找获取。
<a v-bind:href="url">百度</a>
<a :href="url">百度</a>
<script>
export default {
data() {
return {
url: "http://www.baidu.com",
};
},
};
</script>
总结:
vue指令, 实质上就是特殊的
html标签属性, 特点: v- 开头。v-bind把vue变量的值, 赋予给dom属性上, 影响标签显示效果。
指令 v-on
作用:给标签绑定事件。
-
语法
-
v-on:事件名="要执行的少量代码"<template> <div id="app"> <h1>{{ count }}</h1> <button v-on:click="count = count + 1">增加1</button> </div> </template> <script> export default { data() { return { count: 1, }; }, }; </script> -
v-on:事件名="methods中的函数"v-on等号内是函数的名字,函数写在methods对象内。事件函数想要用到
data内的数据,就要通过this.的形式获取。因为数据都是放在Vue对象内,对象调用了这些方法,this指向这个vue实例对象。<template> <div id="app"> <h1>{{ count }}</h1> <button @click="add">增加1</button> </div> </template> <script> export default { data() { return { count: 1, }; }, methods: { add() { this.count += 1; }, }, }; </script> -
v-on:事件名="methods中的函数(实参)"这里加括号不是立即执行函数,而是为函数传递一个参数。必须要有括号才能传参!
<template> <div id="app"> <h1>{{ count }}</h1> <button @click="addFn(5)">增加多个</button> </div> </template> <script> export default { data() { return { count: 1, }; }, methods: { addFn(num) { this.count += num; }, }, }; </script>
-
-
简写:
@事件名="methods中的函数"
** 总结:**
常用
@事件名,给dom标签绑定事件, 以及=右侧事件处理函数。
指令 v-on 修饰符
作用:在事件后面加上
.修饰符名给事件带来更大的功能。
-
@事件名.修饰符="methods里函数".stop- 阻止事件冒泡.prevent- 阻止默认行为.once- 程序运行期间, 只触发一次事件处理函数
<template>
<div id="app" @click="father">
<button @click.stop="son">阻止冒泡</button>
<a :href="url" @click.prevent>阻止默认行为</a>
<button @click.once="one">只出发一次</button>
</div>
</template>
<script>
export default {
data() {
return {
url: "http://www.baidu.com",
};
},
methods: {
father() {
console.log("父元素触发了");
},
son() {
console.log("我不冒泡!");
},
one() {
console.log("我只出发一次!");
},
},
};
</script>
<style scoped>
</style>
指令 v-on 按键修饰符
作用:给键盘事件, 添加修饰符, 增强能力。
- 语法:
@keyup.enter- 监测回车按键@keyup.esc- 监测返回按键
<template>
<div>
<input type="text" @keydown.enter="enterFn">
<hr>
<input type="text" @keydown.esc="escFn">
</div>
</template>
<script>
export default {
methods: {
enterFn(){
console.log("enter回车按键了");
},
escFn(){
console.log("esc按键了");
}
}
}
</script>
指令 v-model
作用:把
value属性和vue数据变量, 双向绑定到一起。 双向数据绑定的含义:
- 数据变化 -> 视图自动同步
- 视图变化 -> 数据自动同步
语法: v-model="vue数据变量"
指令 v-model 不能用于 div 这个无法获取内容的标签,一般用于 input 、select 、textarea 等。
select: 下拉菜单的v-model指令写在第一行的select中,获取到的是每条option的value值。checkbox: 复选框表单中的Vue变量是一个字符串时,关联的是checked属性;是一个数组时,关联的是value的值。
<template>
<div>
<!--
v-model:是实现vuejs变量和表单标签value属性, 双向绑定的指令
-->
<div>
<span>用户名:</span>
<input type="text" v-model="username" />
</div>
<div>
<span>密码:</span>
<input type="password" v-model="pass" />
</div>
<div>
<span>来自于: </span>
<!-- 下拉菜单要绑定在select上 -->
<select v-model="from">
<option value="北京市">北京</option>
<option value="南京市">南京</option>
<option value="天津市">天津</option>
</select>
</div>
<div>
<!-- (重要)
遇到复选框, v-model的变量值
非数组 - 关联的是复选框的checked属性
数组 - 关联的是复选框的value属性
-->
<span>爱好: </span>
<input type="checkbox" v-model="hobby" value="抽烟">抽烟
<input type="checkbox" v-model="hobby" value="喝酒">喝酒
<input type="checkbox" v-model="hobby" value="写代码">写代码
</div>
<div>
<span>性别: </span>
<input type="radio" value="男" name="sex" v-model="gender">男
<input type="radio" value="女" name="sex" v-model="gender">女
</div>
<div>
<span>自我介绍</span>
<textarea v-model="intro"></textarea>
</div>
</div>
</template>
<script>
export default {
data() {
return {
username: "",
pass: "",
from: "",
hobby: [],
sex: "",
intro: "",
};
// 总结:
// 特别注意: v-model, 在input[checkbox]的多选框状态
// 变量为非数组, 则绑定的是checked的属性(true/false) - 常用于: 单个绑定使用
// 变量为数组, 则绑定的是他们的value属性里的值 - 常用于: 收集勾选了哪些值
}
};
</script>
指令 v-model 修饰符
作用:让
v-model拥有更强大的功能。
语法:
- v-model.修饰符="vue数据变量"
.number以parseFloat转成数字类型。.trim去除首尾空白字符。.lazy在change时触发而非inupt时,有利于减少请求优化性能。
<template>
<div>
<div>
<span>年龄:</span>
<input type="text" v-model.number="age">
</div>
<div>
<span>人生格言:</span>
<input type="text" v-model.trim="motto">
</div>
<div>
<span>自我介绍:</span>
<textarea v-model.lazy="intro"></textarea>
</div>
</div>
</template>
<script>
export default {
data() {
return {
age: "",
motto: "",
intro: ""
}
}
}
</script>
指令 v-text和 v-html
作用:更新
DOM对象的innerText/innerHTML。
-
语法:
v-text="vue数据变量"v-html="vue数据变量"
-
注意: 会覆盖插值表达式
<template>
<div>
<p v-text="str"></p>
<p v-html="str"></p>
</div>
</template>
<script>
export default {
data() {
return {
str: "<span>我是一个span标签</span>"
}
}
}
</script>
指令 v-show 和 v-if
作用:控制标签的隐藏或出现
-
语法:
v-show="vue变量"v-if="vue变量"
-
原理
v-show动态为元素添加或移除display:none样式 (频繁切换使用v-show性能更好)。v-if动态从DOM树上创建或移除元素(对于刚初始化就不需要被展示的时候使用v-if性能更好,不需要额外创建这个元素)。
<template> <div id="app"> <div v-show="isok">我是v-show</div> <div v-if="isok">我是v-if</div> <button @click="change">点我改变</button> </div> </template> <script> export default { data() { return { isok: false, }; }, }; </script> -
高级
v-else使用,需要结合v-if才能生效。 ```html<script> export default { data() { return { age: 1, able: false, }; }, methods: { change() { this.isok = !this.isok; // this.age = 20; this.able = true; }, }, }; </script> ```
补充拓展:
在
html页面中,代码是从上往下执行的,因此会出现先创建v-for控制的元素节点,再判断是否删除。但是在
.vue组件中,会把整体当作js代码编译,因此会去判断是否要加载再去加载。
指令 v-for
作用:列表渲染, 所在标签结构, 按照数据数量, 循环生成。
-
语法
-
v-for="值 in 目标结构" -
v-for="(值, 索引) in 目标结构"索引必须要写在第二位上,可以自己命名,不一定是要
index。
-
-
目标结构:
可以遍历数组 / 对象 / 数字 / 字符串 (可遍历结构)
-
注意:
v-for的临时变量名不能用到v-for范围外
指令 v-for 的更新监测
作用:当
v-for遍历的目标结构改变,Vue触发v-for的更新。
-
情景一:数组翻转 数组翻转用到了数组的内置
API方法:reserve()。这个方法会修改原数组。<template> <div> <ul> <li v-for="(val, index) in arr" :key="index"> {{ val }} </li> </ul> <button @click="revBtn">数组翻转</button> </div> </template> <script> export default { data(){ return { arr: [5, 3, 9, 2, 1] } }, methods: { revBtn(){ // 1. 数组翻转可以让v-for更新 this.arr.reverse() }, } } </script> -
情景二:数组截取 数组截取用到了数组内置方法
slice,需要传递两个参数,第一个参数为起始值(从哪里开始),第二个参数为终止值(在哪里结束)。但是这个方法不会改变原数组,会产生返回值给新的数组接收,如下图所示。
所以我们要用原数组来接收这个返回值。
<template> <div> <ul> <li v-for="(val, index) in arr" :key="index"> {{ val }} </li> </ul> <button @click="sliceBtn">截取前3个</button> </div> </template> <script> export default { data(){ return { arr: [5, 3, 9, 2, 1] } }, methods: { sliceBtn(){ // 2. 数组slice方法不会造成v-for更新 // slice不会改变原始数组 // this.arr.slice(0, 3) // 解决v-for更新 - 覆盖原始数组 this.arr = this.arr.slice(0, 3) }, } } </script> -
情景三:更新值 更新值可以用到
vue的内置属性方法:$set(),需要传递三个参数:- 第一个参数:需要被更新值的数组或对象
- 第二个参数:更新的位置(必须是字符串的形式)
- 第三个参数:更新后的值
<template> <div> <ul> <li v-for="(val, index) in arr" :key="index"> {{ val }} </li> </ul> <button @click="updateBtn">更新第一个元素值</button> </div> </template> <script> export default { data(){ return { arr: [5, 3, 9, 2, 1] } }, methods: { updateBtn(){ // 3. 更新某个值的时候, v-for是监测不到的 // this.arr[0] = 1000; // 解决-this.$set() // 参数1: 更新目标结构 // 参数2: 更新位置 // 参数3: 更新值 this.$set(this.arr, 0, 1000) } } } </script>
这些方法会触发数组改变, v-for会监测到并更新页面
push()pop()shift()unshift()splice()sort()reverse()
这些方法不会触发v-for更新
slice()filter()concat()
注意:
vue不能监测到数组里赋值的动作而更新, 如果需要请使用Vue.set()或者this.$set(), 或者覆盖整个数组。即直接采用数组[索引]的方式修改值是不会被v-for监测到的。关于这点下图为vue创始人和发现这个问题并在issue上提问的程序员的对话截图。知道即可。
指令 v-for 的就地更新与虚拟 dom
.vue文件中的template里写的标签, 都是模板, 都要被vue处理成虚拟DOM对象, 才会渲染显示到真实DOM页面上
-
内存中生成一样的虚拟DOM结构(本质是个JS对象)
因为真实的DOM属性好几百个, 没办法快速的知道哪个属性改变了,如下图所示,真实
dom的属性。比如template里标签结构
<template> <div id="box"> <p class="my_p">123</p> </div> </template>对应的虚拟DOM结构
const dom = { type: 'div', attributes: [{id: 'box'}], children: { type: 'p', attributes: [{class: 'my_p'}], text: '123' } } -
以后vue数据更新
- 生成新的虚拟DOM结构
- 和旧的虚拟DOM结构对比
- 找不不同, 只更新变化的部分(重绘/回流)到页面 - 也叫打补丁
好处1: 提高了更新DOM的性能(不用把页面全删除重新渲染)
好处2: 虚拟DOM只包含必要的属性(没有真实DOM上百个属性)
在内存中比较差异, 然后给真实DOM打补丁更新上,v-for 的默认行为会尝试原地修改元素而不是移动它们。
总结: 虚拟DOM保存在内存中, 只记录dom关键信息, 提高DOM更新的性能
指令 v-for 的 key 的作用
官方建议:只要使用到了 v-for ,那么一定要绑定一个 :key 属性,而且,尽量把 id 具有唯一性的数值作为 key 的值。
而且,官方对 key 的值类型是有要求的,只能是字符串或者数值。
key 的值不允许重复,否则终端会报错。
无key - 就地更新
v-for 不会移动 DOM , 而是尝试复用, 就地更新,如果需要 v-for 移动 DOM , 你需要用特殊 attribute key 来提供一个排序提示。
<ul id="myUL">
<li v-for="str in arr">
{{ str }}
<input type="text">
</li>
</ul>
<button @click="addFn">下标为1的位置新增一个</button>
export default {
data(){
return {
arr: ["老大", "新来的", "老二", "老三"]
}
},
methods: {
addFn(){
this.arr.splice(1, 0, '新来的')
}
}
};
性能不高, 从第二个 li 往后都更新了,打开控制台,可以发现第二个 li 的文本和第三个 li 的文本被修改了;创建了新的第四个 li 。
有key - 值为索引
还是就地更新
因为新旧虚拟DOM对比, key存在就复用此标签更新内容, 如果不存在就直接建立一个新的
<ul id="myUL">
<li v-for="(str, index) in arr" :key="index">
{{ str }}
<input type="text">
</li>
</ul>
<button @click="addFn">下标为1的位置新增一个</button>
export default {
data(){
return {
arr: ["老大", "新来的", "老二", "老三"]
}
},
methods: {
addFn(){
this.arr.splice(1, 0, '新来的')
}
}
};
-
v-for先循环产生新的DOM结构,key是连续的, 和数据对应 -
然后比较新旧
DOM结构, 找到区别, 打补丁到页面上
最后补一个 li, 然后从第二个往后, 都要更新内容,效果实现和无 key 是一样的。
有key - 值为id
v-for 不会移动 DOM , 而是尝试复用, 就地更新,如果需要 v-for 移动 DOM , 你需要用特殊 attribute key 来提供一个排序提示
新DOM里数据的key存在, 去旧的虚拟DOM结构里找到key标记的标签, 复用标签。 新DOM里数据的key存在, 去旧的虚拟DOM结构里没有找到key标签的标签, 创建。
旧DOM结构的key, 在新的DOM结构里没有了, 则移除key所在的标签
<template>
<div>
<ul>
<li v-for="obj in arr" :key="obj.id">
{{ obj.name }}
<input type="text">
</li>
</ul>
<button @click="btn">下标1位置插入新来的</button>
</div>
</template>
<script>
export default {
data() {
return {
arr: [
{
name: '老大',
id: 50
},
{
name: '老二',
id: 31
},
{
name: '老三',
id: 10
}
],
};
},
methods: {
btn(){
this.arr.splice(1, 0, {
id: 19,
name: '新来的'
})
}
}
};
</script>
<style>
</style>
图解效果:
总结: 不用key也不影响功能(就地更新), 添加key可以提高更新的性能
总结
-
Vue是什么是一个渐进式的
javascript框架。 -
Vue-cli作用以及简单使用- 作用:无需我们配置自动帮我们搭建好
webpack环境,开箱即用。 - 使用:
vue create 项目名创建项目目录,进入目录后npm run serve运行。
- 作用:无需我们配置自动帮我们搭建好
-
插值表达式 语法:
{{vue变量}}。vue变量在script标签内的data函数内return返回。 -
MVVM设计模式双向数据绑定与数据驱动视图。通过数据驱动视图,不要在想着怎么操作DOM,而是想着如何操作数据!!
-
v-bind作用把
vue变量的值, 赋予给dom属性上, 影响标签显示效果 -
v-on作用和事件对象以及修饰符使用- 作用:给dom标签绑定事件, 等号右侧为事件处理函数,简写形式为
@ - 事件对象
- 无传参, 通过形参直接接收
- 传参, 通过$event指代事件对象传给事件处理函数
- 修饰符
.stop- 阻止事件冒泡.prevent- 阻止默认行为.once- 程序运行期间, 只触发一次事件处理函数
- 作用:给dom标签绑定事件, 等号右侧为事件处理函数,简写形式为
-
v-model的作用把
value属性和vue数据变量, 双向绑定到一起 -
v-if和v-show的区别和本质v-if:直接把节点从dom树上删除v-show:原理是通过样式display:none隐藏节点。
-
v-for的作用和使用- 作用:列表渲染, 所在标签结构, 按照数据数量, 循环生成
- 使用语法:
v-for="(值, 索引) in 目标结构"v-for="值 in 目标结构"
-
vue的特点- 渐进式
- 声明式渲染
- 数据驱动视图 (响应式)
- 极少的去写DOM操作相关代码
- 双向绑定
- 组件系统
- 不兼容IE8及以下浏览器
-
v-for什么时候会更新页面呢?- 数组采用更新方法, 才导致
v-for更新页面
- 数组采用更新方法, 才导致
-
vue是如何提高更新性能的?- 采用虚拟
DOM+key提高更新性能
- 采用虚拟
-
虚拟
DOM是什么?- 本质是保存
dom关键信息的JS对象
- 本质是保存
-
如何比较新旧虚拟
DOM?- 根元素改变 – 删除当前DOM树重新建
- 根元素未变, 属性改变 – 更新属性
- 根元素未变, 子元素/内容改变
- 无key – 就地更新 / 有key – 按key比较
思维导图
拓展
为什么在 script 逻辑模块中, data 是函数返回的形式,而 methods 方法是对象的形式?
这就要从基本数据类型和复杂数据类型讲起。我们都知道,基本数据类型存储的是值,当赋值给一个新的变量时,内存会开辟一个新的空间存储值,彼此互不干扰,各自独立。
let a = 10
let b = a
b = 20
console.log(a ,b) // 10 20
复杂数据类型存储的是地址,赋给新的变量时你修改我也会受到影响。
let obj = {s:2}
let obj1 = obj
obj1.s = 666
sonsole.log(obj) // {s:666}
返回 vue ,我们希望各个组件操作的值相互独立,互不干扰,因此如果使用对象的形式来存储,会造成修改前一个组件后一个组件的值也被改变。用函数返回的形式每个组件都有自己的返回值,互不干扰。
而函数我们希望能够复用,因此对象是最好的选择。