vue基础
vue是什么
渐进式的JavaScript框架,拥有一套自己规则的语法
官网地址(作者:尤雨溪)
-
渐进式概念
-
生活里的渐进式
逐渐进步,想什么就用什么,不必全都使用
-
Vue渐进式
Vue从基础开始, 会循序渐进向前学习, 如下知识点可能你现在不明白, 但是学完整个vue回过头来看, 会很有帮助
-
什么是库和框架
库:封装的属性和方法(例如jquery.js)
框架:拥有自己的规则元素,比库强大的多(例vue.js)
@vue/cli脚手架
1. @vue/cli脚手架介绍
webpack自己配置环境很麻烦,下载@vue/cli包,用vue命令创建脚手架搭建项目
-
@vue/cli是vue官方提供的一个全局模块包(得到vue命令),此包创建于脚手架
脚手架是为了保证各施工过程顺利进行而搭建的工作平台
@vue/cli的好处
- 开箱即用
- 0配置webpack
- babel支持
- css,less支持
- 开发服务器支持
2.@vue/cli安装
把@vue/cli模块包安装到全局,电脑拥有vue名利,才能创建脚手架工程
-
全局安装命令
npm install -g @vue/cli
注意:如果半天没有动静(大多数都是网速问题),可以ctrl+c
- 停止重来
- 换一个网络继续重来
-
查看
vue
脚手架版本vue -V //如果出现版本号说明已经安装成功了
3.@vue/cli创建项目启动服务器
使用vue命令,创建脚手架项目,启动服务器
注意:项目名不能带大写字母,中文和特殊符号
-
创建项目
vue create 项目名
-
选择模板
可以上下箭头选择vue的版本,弄错了ctrl+c重来
- 回车等待生成项目文件夹+文件+下载必须的第三方包们
-
进入脚手架项目下,启动内置的热更新本地服务器
cd 项目名 npm run serve
打开浏览器输入以上地址
4.@vue/cli目录和代码分析
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 -依赖包列表文件
5.@vue/cli项目架构了解
6.@vue/cli只定义配置
src并列处新建vue.config.js
module.exports = {
//自定义服务配置
devServer:{
open:false, //是否自动打开浏览器
port:8080 //端口号
}
}
7.eslint了解
eslint是一个代码检查工具
例子:现在main.js随便声明变量,但不使用
终端和页面都报错了
注意:这种错误,证明代码不够严谨
解决办法:
- 手动解决错误
- 在代码上方添加以下代码注释
/* eslint-disable */
-
暂时关闭eslint检查,在vue.config.js中配置后重启服务器
module.exports = { //...其他配置 lintOnSave:false //关闭eslint检查 }
8.@vue/cli单vue文件讲解
单vue文件好处,独立作用域互不影响
Vue推荐采用.vue文件来开发项目
template里只能有一个根标签
vue文件-独立模块作用域互不影响
style配合scoped属性,保证样式只针对当前template内标签生效
vue文件配合webpack,把他们打包起来插入到index.html
<!-- template必须, 只能有一个根标签, 影响渲染到页面的标签结构 -->
<template>
<div>欢迎使用vue</div>
</template>
<!-- js相关 -->
<script>
export default {
name: 'App'
}
</script>
<!-- 当前组件的样式, 设置scoped, 可以保证样式只对当前页面有效 -->
<style scoped>
</style>
vue指令
插值表达式
在dom标签中直接插入内容
又叫:声明式渲染/文本插值
语法:{{表达式}}
<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>
<style>
</style>
注意:插值表达式不能写语句,函数
mvvm设计模式
用数据驱动视图改变, 操作dom的事, vue源码内干了
设计模式:是一套被反复使用,多人知晓,经过分类编目,代码设计经验分总结。
- mvvm,一种软件架构模式,决定了写代码的思想和层次
- M:model数据模型 (data里定义)
- V:view视图 (html页面)
- VM:ViewModel视图模型(vue.js源码)
- MVVM通过数据双向绑定让数据自动的双向同步 不需要操作DOM
-
V(修改视图)->M(数据自动同步)
-
M(修改数据)-> V(视图自动同步)
-
在vue中,不推荐直手动操作DOM
在vue中,通过数据驱动视图,不要想着怎么操作DOM,而是想着如何操作数据
v-bind
给标签属性设置vue变量值
vue指令,实质就是特殊的HTML标签属性,特点:v-开头
-
语法:
v-bind:属性名='vue变量'
-
简写:
:属性名='vue变量'
-
代码示例
<!-- vue指令 v-bind属性动态赋值--> <a v-bind:href='url'>我是a标签</a> // url是在data返回对象里的变量 <img :src='imgSrc'> //data里返回对象里的数据
v-on
给标签绑定事件
- 语法
v-on:事件名="要执行的少量代码"
v-on:事件名='methods中的函数'
v-on:事件名='methods中的函数(实参)'
- 简写:
@事件名='methods中的函数'
<template>
<div>
<a @click="one" href="http://www.baidu.com">阻止百度</a>
<hr>
<a @click="two(10, $event)" href="http://www.baidu.com">阻止去百度</a>
</div>
</template>
<script>
export default {
methods: {
one(e){
e.preventDefault()
},
two(num, e){
e.preventDefault()
}
}
}
</script>
v-on事件对象
vue事件处理函数中,拿到事件对象
-
语法
- 无传参,通过形参直接接收
- 传参,通过$event指代事件对象传给事件处理函数
<template> <div> <a @click="one" href="http://www.baidu.com">阻止百度</a> <hr> <a @click="two(10, $event)" href="http://www.baidu.com">阻止去百度</a> </div> </template> <script> export default { methods: { one(e){ e.preventDefault() }, two(num, e){ e.preventDefault() } } } </script>
v-on修饰符
在事件后面,修饰符名-给事件带来更加强大的功能
-
语法:
@事件名.修饰符='methods里函数'
- .stop -阻止事件冒泡
- .prevent -阻止默认行为
- .once -程序运行期间,只触发一次事件处理函数
<template> <div @click="fatherFn"> <!-- vue对事件进行了修饰符设置, 在事件后面.修饰符名即可使用更多的功能 --> <button @click.stop="btn">.stop阻止事件冒泡</button> <a href="http://www.baidu.com" @click.prevent="btn">.prevent阻止默认行为</a> <button @click.once="btn">.once程序运行期间, 只触发一次事件处理函数</button> </div> </template> <script> export default { methods: { fatherFn(){ console.log("father被触发"); }, btn(){ console.log(1); } } } </script>
总结:修饰符给事件扩展额外功能
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的变量值,非数组 - 关联的是复选框的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触发而非input时
<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-model修饰符,可以对值进行预处理
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-text会把值当成普通字符串显示,v-html把值当做html解析
v-show和v-if
控制标签的显示或隐藏
-
语法:
v-show='vue变量'
v-if='vue变量'
-
原理:
v-show
用的是标签的样式属性display:none隐藏
(频繁切换使用)v-if
直接从DOM树上移除
-
高级
v-else='vue数据变量'
必须配合v-if
使用v-else-if='vue变量'
必须配合v-if
使用,有需要是可以以v-else
结尾
<template> <div> <h1 v-show="isOk">v-show的盒子</h1> <h1 v-if="isOk">v-if的盒子</h1> <div> <p v-if="age < 18">还得多吃饭</p> <p v-else-if='age>18 && age<60'>坚持锻炼</p> <p v-else='age>60'>送你脑白金</p> </div> </div> </template> <script> export default { data() { return { isOk: true, age: 15 } } } </script>
v-for
列表渲染,所在标签结构,按照数据数量,循环生成
-
语法
v-for='(值,索引) in 目标结构'
v-for='值 in 目标结构'
-
目标结构:
- 可以遍历数组、对象、数字、字符串(可遍历结构)
-
注意:
v-for
的临时变量名不能用到v-for
范围外<template> <div id="app"> <div id="app"> <!-- v-for 把一组数据, 渲染成一组DOM --> <!-- 口诀: 让谁循环生成, v-for就写谁身上 --> <p>学生姓名</p> <ul> <li v-for="(item, index) in arr" :key="item"> {{ index }} - {{ item }} </li> </ul> <p>学生详细信息</p> <ul> <li v-for="obj in stuArr" :key="obj.id"> <span>{{ obj.name }}</span> <span>{{ obj.sex }}</span> <span>{{ obj.hobby }}</span> </li> </ul> <!-- v-for遍历对象(了解) --> <p>老师信息</p> <div v-for="(value, key) in tObj" :key="value"> {{ key }} -- {{ value }} </div> <!-- v-for遍历整数(了解) - 从1开始 --> <p>序号</p> <div v-for="i in count" :key="i">{{ i }}</div> </div> </div> </template> <script> export default { data() { return { arr: ["小明", "小欢欢", "大黄"], stuArr: [ { id: 1001, name: "孙悟空", sex: "男", hobby: "吃桃子", }, { id: 1002, name: "猪八戒", sex: "男", hobby: "背媳妇", }, ], tObj: { name: "小黑", age: 18, class: "1期", }, count: 10, }; }, }; </script>
v-for更新检测
当v-for遍历的目标结构改变,vue触发v-for更新
情况一:数组翻转
<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>
<style>
</style>
情况二:数组截取
<template>
<div>
<ul>
<li v-for="(val, index) in arr" :key="index">
{{ val }}
</li>
</ul>
<button @click="sliceBtn">截取数组</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更新 - 覆盖原始数组
let newArr = this.arr.slice(0, 3)
this.arr = newArr
},
}
}
</script>
<style>
</style>
情况三:更新值
<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(){
//更新某个值得时候,v-for是检测不到的
//this.arr[0] = 10
//解决 --this.$set()
//参数1:更新目标结构
//参数2:更新位置
//参数3:更新值
this.$set(this.arr,0,10)
}
}
}
</script>
<style>
</style>
口诀:
数组变更方法,就会导致v-for更新,页面更新
数值非变更方法,返回新数组,就不会导致v-for更新可以采用覆盖数组或者this.$set()
常见的会触发数组改变,v-for会检测到并更新页面的数组方法有:
push()
添加数组pop()
删除数组中最后一个元素shift()
删除数组第一个元素,并返回这个元素的值unshift
添加一个或者多个元素到数组开头,并返回该数组的新长度splice()
删除指定位置的数组或者添加新元素到指定位置sort()
方法用原地算法对数组的元素进行排序revese()
倒转数组
不会触发v-for更新
slice()
截取数组filter()
过滤数组concat()
合并两个数组或者多个数组
注意:vue不能检测到数组里赋值的动作而更新,如果需要就使用Vue.set()或者this.$set(),或者覆盖整个数组
v-for就地更新
v-for
的默认行为会尝试原地修改元素而不是移动他们
这种虚拟DOM对比方式,可以提升性能-但是还不够高
虚拟dom
.vue文件中的
template
里面写标签,模板都要被vue处理成虚拟DOM对象,才能被渲染到真实的DOM页面上
-
内存中生成一样的虚拟DOM结构(本质是一个JS对象)
真实的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保存在内存中,只记录dom关键信息,提高DOM更新的性能
在内存中比较差异,如然后给真实的DOM打补丁更新上
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> <script> export default { data(){ return { arr: ["老大", "新来的", "老二", "老三"] } }, methods: { addFn(){ this.arr.splice(1, 0, '新来的') } } }; </script>
新旧DOM的对比过程
-
有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> <script> export default { data(){ return { arr: ["老大", "新来的", "老二", "老三"] } }, methods: { addFn(){ this.arr.splice(1, 0, '新来的') } } }; </script>
图解过程:
-
v-for先循环产生新的DOM结构,key是连续的和数据对应
-
然后比较新旧DOM结构,找到区别,打补丁到页面上,最后一个补li然后从第二个开始往后,都要更新内容
-
有key - 值为id
key的值只能是唯一不重复,字符串或数值
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>
图文详解: