导图社区 vue2详细学习导图
详细的vue2学习导图,从0到1帮你梳理知识点。本学习导图旨在从零开始,全面梳理Vue2的关键知识点,帮助初学者逐步掌握Vue2的精髓,从而能够在实际项目中灵活运用。
编辑于2024-08-08 10:19:56vue
ES6 相关语法
编程范式
分类一
命令式编程
给计算机指令,第一步需要做什么、第二步需要做什么、第三步需要做什么
声明式编程
拿到数据,把数据保存到每个位置,在对应的位置进行声明,之后会自动遍历
分类二
面向对象编程(第一公民:对象)
函数式编程(第一公民:函数)
const 和 let 关键字
变量作用域:变量在什么范围内是可用
块级作用域
概念:{ } 花括号内的作用域就叫块级作用域
可以用闭包来产生块级作用域
函数作用域
概念:函数定义中{ }就叫函数作用域
其中关键字var声明的变量就在函数作用域中
let 关键字
定义的变量是存在于块级作用域中,不会出现变量污染
const 关键字
特点:可定义常量,有块级作用域,不会出现变量污染
建议:在开发中优先使用 const ,只有需要改变某一个标识符的时候才使用 let
注意
const 定义的是常量,不可修改
const 修饰的标识符必须赋值
常量:可以改变对象内的属性值,但是不能通过直接赋值的方式改变对象的地址
例子
const obj = {
// 定义一个对象obj
name: 'itchao',
age:18,
height:1.88
}
obj.name = 'kobe'
// 可以改变对象内的属性值
console.log(obj.name)
// 输出结果:kobe
obj = {
// 这样赋值会报错,由const定义的obj对象无法直接被赋值一个新的对象,这样会直接改变obj的内存地址,但是const定义的常量是不允许改变内存地址的,只允许改变对象内属性值
name:'james',
age:19,
height:2.10
}
对象字面量的增强写法
字面量
概念
根据一个东西就能直接知道这个东西代表了什么
例子:
{ }
能直接知道代表的是对象
[ ]
能直接知道代表的是数组
对象字面量增强写法
本质:增强写法本质就是在合适的情况进行适当的简写操作
属性增强写法
用法:在对象中的属性名和属性值相同则可简写为只写一个属性名即可
例子:
// 原生代码
const obj = {
name:name,
age:age,
height:height
}
————
// 属性增强写法
const obj = {
name,
age,
height
}
函数增强写法
用法:(区别声明函数的时候)
简写前格式
函数名:function ( ) { }
例子:eat:function( ) { }
简写后格式
函数名( ) { }
例子:eat( ) { }
例子:
// 原生代码
const obj = {
eat: function ( ) {
// 注意对照下面简写后的代码
console.log( '函数增强写法的实现,很重要!' )
}
}
————
const obj = {
eat ( ){
// 区别:在这里进行了简写操作
console.log( '函数增强写法的实现,很重要!' )
}
}
JavaScript高级函数(重要!)
filter函数
作用:进行筛选过滤,类似判断过程
filter中的回调函数要求:必须返回boolean值
n代表回调函数中传入的参数名字,可根据自己的需要自行修改为其他参数名字
true:将回调的n加入到新数组
false:过滤这次的n
————
例子:
const nums = [ 10, 20, 111, 222, 666, 40, 50 ]
let newNums = nums.filter( function( n ) {
return n < 100
} )
console.log( newNums)
// 输出结果:10, 20,40, 50
map函数
作用:对数组内所有的数据进行某一次的统一变化
————
例子:
let num1 = [ 10, 20, 30, 40, 50 ]
let numA = num1.map( function ( n ) {
return n * 2
} )
console.log( numA )
// 输出结果:20, 40, 60, 80, 100
reduce函数
作用:对数组中所有的内容进行汇总
————
reduce函数格式
num.reduce(参数一, 参数二)
参数一比较特殊是一个函数,该函数有两个参数
// 参数说明:
preValue
第一次的preValue是初始化的preValue,本题第一次初始化preValue为下方的0,第二次preValue为:20,第一次的preVale:0+第一次的n:20,后面以此类推
n
n就是原数组对应的属性值,本题第一个n为20,第二个n为40,后面以此类推
例子:
function(preValue, n) {
return preValue + n
}
参数二就是初始化:参数一函数的第一个参数
let total = numA( function (preValue, n) {
return preValue + n
}, 0 )
// 这里的0就是参数二,用来初始化preValue
————
例子:
let numA = [ 20, 40, 60, 80, 100 ]
let total = numA.reduce( function ( preValue, n ) {
return preValue + n
}, 0 )
console.log( total )
// 输出结果:300
具体每次输出结果:
第一次:preValue:0,n:20
第二次:preValue:20,n:40
第三次:preValue:60,n:60
第四次:preValue:120,n:80
第五次:preValue:200,n:100
total:300
三个高阶函数综合使用
例子:
const nums = [ 10, 20, 111, 222, 666, 40, 50 ]
let total = nums.filter( function ( n ) {
// 利用filter函数,进行筛选过滤
return n < 100
} ).map( function ( n){
// 利用map函数,对筛选过滤的所有值进行统一变化处理
return n * 2
} ).reduce( function ( preValue, n ) {
// 利用reduce函数,对所有统一变化处理后的值进行求和
return preValue + n
}, 0 )
console.log( total )
// 输出结果:240
// 箭头函数简写(后续会介绍,重要!)
let total= nums.filter( n => n < 100 ).map( n => n * 2 ).reduce( (preValue, n) => ( preValue + n ) , 0 )
箭头函数
参数问题
一个参数
// 原生函数写法
const power = ( num ) => {
return num * num
}
当只有一个参数的时候,参数列表的小括号可以省略,相当于简写
例子:
cosnt power = num => {
return num * num
}
两个参数
// 原生写法,参数不止一个时,无法省略小括号,无简写
const sum = ( num1, num2) => {
return num1 + num2
}
函数代码块中的返回值问题
一行代码
参数为两个,代码块为一行
const mul = ( num1, num2) => num1 * num2
参数为一个,代码行为一行
const mul2 = num => num * num
// 参数为一个时可省略( ),返回值为一行代码时可省略{ }和 return
无参数,代码行为一行
const demo = ( ) => console.log(' 没有参数,代码块为一行 ')
多行代码
// 无简写,函数内容必须用{ }包住
cont test = ( ) => {
console.log('箭头函数,多行代码一')
console.log('箭头函数,多行代码二')
}
箭头函数this的指向问题
问题:箭头函数中的this是如何查找的?
答案:向外层作用域中,一层层查找this,直到有this的定义
结论:箭头函数中的this引用的就是最近作用域中的this
————
例子:
const app = {
id: 1,
getId( ) {
setTimeout( ( ) => console.log( this.id )
// 输出结果:1
// 因为箭头函数中的this是逐层向外查找this的定义,这里最近的this.id定义是app对象内定义的id,又因为app对象内的id值为1,所以this.id输出结果为1
, 1000 )
}
}
Promise
作用:Promise 是异步编程的一种解决方案
Promise 使用场景
一般情况下是有异步操作时,使用 Promise 对这个异步操作进行封装
注意事项
Promise 的写法是链式编程的写法
成功的时候调用 resolve
失败的时候调用 reject
.then( ) 处理成功的信息
// 发送成功相关的请求
.catch( ) 处理失败的信息
// catch捕获异常
参数中只需要 resolve 时,另一个 catch 可以不写
在执行传入的回调函数时,会传入两个参数,resolve、reject,这两个参数本身又是函数
完整异步例子:
new Promise ( ( resolve, reject ) => {
setTimeout( ( ) => {
// setTimeout(定时器函数),使用此函数时是典型的异步操作
resolve ( ' Hello Promise ')
} , 1000 )
} ).then( data => {
// then 成功响应,处理成功
console.log ( ' 使用 Promise 调用回调函数 ')
} ).catch( err => {
// catch 捕获异常,处理失败
console.log( err.msg )
} )
Promise 三种状态
pending:等待状态,比如正在进行网络请求,或者定时器没有到时间
fulfill:(成功)满足状态,当我们主动回调了 resolve 时,就处于该状态,并且会回调 .then( )
reject:(失败)拒绝状态,当我们主动回调了 reject 时,就处于该状态,并且会回调 .catch( )
Promise 另外处理形式—只写 .then 方法
new Promise ( ( resolve, reject) => {
setTimeout( ( ) => {
resolve (' 请求成功!就返回这个信息哦!')
// reject(' 请求失败!就看到我出来了!')
}, 1000 )
} ).then( res => {
// 成功时
console.log( res )
}, err => {
// 失败时
console.log( err )
} )
链式调用的不同写法
① 原生写法:
return new Promise( resolve => {
resolve( res + '111' )
} )
② 简单写法:
return Promise.resolve( res + '111' )
// 处理成功信息
或者
return Promise.reject(' 请求失败!这是错误信息!')
// 处理错误信息
注意:
手动捕获异常:
throw 'error message'
③ 最简单写法( 省略 Promise.resolve):
return res + ‘111’
Promise 的 all 方法使用
作用:可以包装多个网络请求,在多个网络请求都完成的情况下,在一个地方进行统一回调
使用场景:某个需求需要发送多个网络请求才能完成,就可以使用 Promise.all
例子:Promise.all ( [
new Promise( ( resolve, reject ) => {
setTimeout( ( ) => {
resolve( { name:'kobe', age: 18' } )
}, 2000 )
} ),
new Promise( ( resolve, reject ) => {
setTimeout( ( ) => {
resolve( { name: 'james', age: 19' } )
}, 1000 )
} )
] ).then( res => {
// res 是一个数组,包含着所有请求的结果( res 是 result的简写,result是结果的意思 )
console.log( res )
} )
项目文件夹命名
src
作用:开发时使用,开发时存放的相关文件
assets
作用:存放静态资源
img
存放图片相关资源
css
存放 css 相关资源
base.css
normalize.css
common
作用:存放公共的 js 文件
components
作用:该文件夹一般放公共组件
common
可用于其他项目的公共组件
content
只可用于现在的项目,不可复用于其他项目的公共组件
network
存放网络相关东西
router
存放路由相关东西
store
存放 Vuex 状态相关东西
views
存放主要的视图,如:首页视图、分类视图等
dist
作用:存放打包之后的文件
AJAX 网络请求
AJAX
概念:异步的 JS 和 XML,通过 AJAX 可以在浏览器中向服务器发送异步请求
优点
无刷新获取数据
允许根据用户事件来更新部分页面内容
缺点
没有浏览历史,不能回退
存在跨域问题(同源)
SEO 不友好
API 接口
概念:以 HTTP 协议形式提供,定义了输入、输出、功能描述的服务
前后端交互
前端
发送网络请求,拿到后端提供的数据(Vue框架中主要用axios框架进行发送网络请求,下面会针对axios详细介绍)
后端
提供接口,把数据放到接口中让前端拿数据
服务器
处理提交的数据
查询数据库
根据结果产生响应
网络编程基本概念
客户端(Client)
移动应用(IOS、Android、 Web 等应用),浏览器之类的
服务器(Server)
为客户端提供服务、提供数据、提供资源等机器
请求(Request)
客户端向服务器索取数据的一种行为,一般指请求数据
响应(Response)
服务器对客户端请求做出的反应,一般指返回数据给客户端
HTTP 协议
HTTP 协议概念
协议
计算机通信网络中两台计算机之间进行通信所必须共同遵守的规则
HTTP 协议(超文本传输协议)
规定了浏览器(客户端)和服务器之间通信的规则
请求报文
请求行
请求类型
GET 请求方法
POST 请求方法
......
等等
URL
http 协议版本号
请求头
例子:
Host: atguigu.com
Cookie: name=guigu
content-type: application/x-www-form-urlencoded
User-Agent: chrome 83
空行
空行是固定格式,必须有
请求体
GET 请求,请求体是空的
POST 请求,请求体可以不为空
响应报文
响应行
http 协议版本号
响应状态码
响应状态字符串
响应头
例子:
Content-Type: text/html;charset=utf-8
Content-length: 2048
Content-encoding: gzip
空行
空行是固定格式,必须有
响应体
例子:
<html>
<head>
</head>
<body>
<h1>itchao</h1>
</body>
</html>
HTTP 协议与 HTTPS 协议的区别
HTTPS 协议比 HTTP 协议更加安全
URL(统一资源定位符)
概念
互联网上资源的地址
每一个资源都有一个唯一的 URL
格式: 协议:// 主机地址 、 路径
例子:
https://www.baidu.com
Chrome 网络控制台查看通信报文
GET请求 检查步骤
F12 或者 右键检查
点击 Network
Network 下面会列出当前网页在加载过程中,所有发送的请求
点击刷新按钮,刷新页面,就可以发送 GET 请求
点击第一个请求
Headers(头部)
General
Response Headers
响应行
响应头
Request Headers
作用:观看浏览器给服务器发了什么内容
请求行
请求头
Query String Parameters
作用:对 URL 内的参数进行解析
Preview(预览)
作用:对响应体解析之后的预览
Response(响应)
响应体
服务端返回的 HTML内容
Initiator
Timing
Cookies
POST请求 检查步骤
F12 或者 右键检查
点击 Network
Network 下面会列出当前网页在加载过程中,所有发送的请求
点击登录按钮,就可以发送 POST 请求
点击第一个请求
Headers
General
Response Headers
响应行
响应头
Request Headers
请求行
请求头
Form Data
原始的请求体内容
Preview
Response
Initiator
Timing
Cookies
跨域
同源策略
概念:同源策略是浏览器的一种安全策略
同源:协议、域名、端口号,必须完全相同
违背同源策略就是跨域
如何解决跨域
JSONP
概念:JSONP 是一个非官方的跨域解决方案,只支持 get 请求
工作原理:JSONP 是利用 script 标签的跨域能力来发送请求
CORS
概念:CORS 是跨域资源共享
CORS 是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持 get、post 请求。跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源
工作原理:CORS 是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行
数据传递的格式
键值对格式
?xx=11&yy=222
// ?代表后面可以拼接参数
Json 数据
{
"属性名1":"属性值1",
"属性名2":"属性值2",
"属性名3":"属性值3",
}
常见响应状态码
状态码由三位数字组成,第一位数字定义了响应类型
格式
1xx
指示信息—表示请求已接收,继续处理
2xx
成功—表示请求已被成功接收、理解、接受
3xx
重定向—要完成请求必须进行更进一步的操作
4xx
客户端错误—请求有语法错误或请求无法实现
5xx
服务端错误—服务器未能实现合法的请求
例子:
200
请求发送成功,客户端发送的请求被服务端正确处理
302
永久重定向,请求的资源已被分配了新的url
404
客户端错误,无法找到请求的资源
500
服务端错误,服务端在请求的时发生了错误
restful 风格
概念:按照一定的规则写出的易读、易懂的 api 文档
作用:让前端、后端、测试三方在工作的时候有据可循,提升工作效率
增删查改四大功能的语法风格
增
请求方法:post
post 请求
隐式提交数据,更安全
没有数据量大小的限制
重要的数据使用 POST 请求
删
请求方法:delete
查
请求方法:get
get 请求
提交的数据显示在地址栏、不安全
提交的数据量有限制
不重要的数据使用 GET 请求
改
请求方法:put
补充知识
对象的引用赋值
对象是引用类型
对象保存的是一个地址,赋值给其他对象的也是一个地址,当改变了对象的值的时候被赋值的对象里面的值也被改变了
例子:
const info = { name: ' why ', age : 18 };
const obj = info;
// 把info对象赋值给obj对象
info.name = 'kobe';
// 改变info对象内的name值
console.log(obj.name)
// 输出为kobe,因为obj对象和info对象保存的是同一个地址,并且info对象内的name值改变为kobe
对象的浅拷贝
浅拷贝作用:只拷贝对象内的属性,不拷贝对象地址
分析
创建一个新的对象,把老对象内的属性全部复制到新对象内,老对象和新对象有两个不同的地址,单纯的直接赋值是无法达到同时改变两个对象的目的
如果老对象内嵌套了一个A对象,这个A对象是有内存地址的,在浅拷贝的过程中,也就是老对象属性复制到新对象的时候,会把A对象的内存地址一起复制到新对象中
只要改变老对象的A对象内的值,新对象中A对象对应的值也会发生变化,因为新对象和老对象中的A对象保存的是同一个地址
浅拷贝对象方法:Object.assign ( )
例子1:(获取老对象的全部值)
const info = { name: 'itchao ', age: 18' };
// 创建info对象
const obj = Object.assign( { }, info );
// 对象浅拷贝的过程,将老对象内的所有值赋值给新对象
// 属性解释(针对本案例解释,仅供参考,实际情况按需求自行改进)
{ }
创建一个空对象来接受老对象传来的数据
info
传递数据的的老对象的名字
第三个参数没写
代表了把老对象的所有值传递给新对象
例子2:(获取老对象的特定值)
const itchao = { name: 'chao', age: '19', height: '1.85' };
// 创建itchao对象
const obj = Object.assign( { }, itchao, { name, age } )
// 对象浅拷贝的过程,将老对象的特定值赋值给新对象
// 属性解释(针对本案例解释,仅供参考,实际情况按需求自行改进)
{ }
创建一个空对象来接受老对象传来的数据
itchao
传递数据的老对象的名字
{ name, age }
这里指新对象传递给老对象的数据为name、age
对象浅拷贝过程的示例图
对象的深拷贝
深拷贝作用:创建一个新的对象,跟老对象没有任何关系,有新的内存地址,改变其中一个对象的属性,不会影响另一个对象的属性,因为内存地址不一样(新老对象内如果又包含了对象,被包含的对象地址也不一样)
JSON.stringify( info )
作用:将对象转换成字符串
JSON.parse( JSON.stringify( info ))
作用:把字符串又转成对象
例子:
const info = { name: 'itchao', age: 19 , friend: { name: ' kobe '}}
const obj = JSON.parse( JSON.stringify( info) )
info.friend.name = 'james'
console.log( obj.friend.name )
// 输出依然为 kobe,因为属于深拷贝,两个对象的内存地址完全不一样
第三方库—lodash
浅拷贝
例子:
const info = { name:' itchao', age: 19, friend: { name: 'kobe'} };
const obj = _.clone( info );
info.friend.name = 'james';
console.log( obj.friedn.name );
// 输出结果:james,浅拷贝是把老对象的属性赋值给新对象,如果老对象内保存了另 一个对象,那么被包含的对象在传递给新对象的时候是传递的内存地址,因此会互相影响
深拷贝
例子:
const info = { name:' itchao', age: 19, friend: { name: 'kobe'} };
const obj = _.cloneDeep( info );
info.friend.name = 'james';
console.log( obj.friend.name )
// 输出结果:kobe,深拷贝中新老对象完全不一样,内存地址都不想关,即使是老对象中包含了一个对象,这个被包含的对象在深拷贝过程中传递的也不是一个内存地址,因此无法相互影响
框架基本操作
前端开发规范
缩进2个空格
(待完成!其他规范还没去了解!)
插值操作
mustache语法
可以直接写变量
也可写简单表达式
插值操作(data中的值放入标签相关)— 不常用指令
v-once
作用:在第一次展示数据,不会跟着数据改变而改变
直接使用,不需要跟表达式,是个简答指令,只需把指令放在对应位置上
v-html
作用:解析html相关的标签字符串
例子:
<h2 v-html = ' url ' ></h2>
url:' <a href = 'http://www.baidu.com'>百度一下</a> '
v-text
作用:替换标签全部内容
例子:
<h2 v-text = 'message'></h2>
message : ' 我一定能成为很厉害的前端开发工程师!'
v-pre
作用:将要显示的数据直接展示出来,不做解析
例子:
<h2 v-pre>{{ message }}</h2>
message:‘ 我太棒了!我真的很厉害了!太厉害了!真的成功了!’
v-cloak
作用:通过Vue来控制相关的div
关键
在Vue解析之前,div中有一个属性v-cloak
在Vue解析之后,div中就清除这个属性v-cloak
计算属性
作用:对数据进行转换后再显示
计算属性基本使用
例子:
computed: {
fullName: function (){
// 创建计算属性fullName
return this.firstName + ' ' + this.lastName
}
}
计算属性复杂操作
例子:
computed: {
totalPrice: function ( ) {
// 创建计算属性totalPrice
let result = 0
for ( let i = 0; i < this.books.length; i++ ) {
result += this.books[i].price
}
return result
}
}
计算属性setter和getter
计算属性一般只有get方法(只读属性),一般没有set方法
注意:
计算属性是属性不是函数,不加( )
目前写的计算属性是语法糖,相当于简写
————
原生写法:
fullName: {
set: function ( ) {
},
get: function ( ) {
return 'abc'
}
}
————
简写:
fullName: function ( ) {
return 'abc'
}
计算属性和methods的对比
计算属性
性能更高,不管使用几次,都只调用一次函数
methods
性能更低,使用几次,就调用几次函数
侦听器 watch
概念
在 data 返回对象中定义数据,数据通过插值语法等方式绑定到 template
当数据变化时,template 会自动进行更新来显示最新数据
如果要在代码逻辑中监听某个数据变化,就要用侦听器 watch
watch 案例一:
属性名解释:(针对本例)
question
侦听的 data 中的属性名称
oldValue
变化前的旧值
newValue
变化后的新值
目的:侦听 question 变化,进行一些逻辑处理(如:JavaScript, 网络请求等)
data( ) {
return {
question:' 这是关于侦听器使用的案例!'
}
},
watch:{
question( newValue, oldValue ) {
console.log("新值:", newValue, "旧值:", oldValue);
this.queryAnswer( );
// 可能是一个函数处理事件
axios( {
// 发送相关网络请求
baseURL: " ",
method: get
} )
}
}
watch 案例二:
目的:侦听 info 对象在发生点击事件前后的变化
<button @click=" changeInfo ">改变info</button>
————
data( ){
return {
info: { name: 'itchao', age: 20 }
}
},
watch: {
info( newInfo, oldInfo ) {
console.log( "新值:", newInfo, "旧值:" , oldInfo );
// 输出结果:
新值:name: kobe8-24, 旧值: name: "itchao", age: 20
}
},
methods: {
changeInfo( ) {
this.Info = { name: 'kobe8-24' };
}
}
侦听器 watch 的配置选项
默认情况下侦听器只侦听数据本身改变(内部发生改变不能被侦听)
换句话说:默认情况下,watch 只侦听 info 引用变化,对于内部属性变化不会做出响应
深度侦听与立即执行
此处侦听属性是对象,想侦听对象内部属性时就要用到相关配置选项
————
info: {
handler( newInfo, oldInfo ) {
console.log( "newValue:", newInfo, "oldValue:", oldInfo );
},
deep: true
// 进行深度侦听
immediate: true
// 立即执行(一开始就立即执行一次)
// 无论后面数据是否变化,侦听函数都会优先执行一次
}
$watch 的 API
可以在 created 生命周期中,使用 this.$watch 侦听
参数说明:
第一个参数是侦听的源( 就是被侦听对象 )
本例是 info 对象
第二个参数是侦听的回调函数 callback
本例是 function( newInfo, oldInfo ){ }
第三个参数是额外其他选项,比如 deep、immediate等等
本例是deep、immediate
例子:
created( ) {
const unwatch = this.$watch( ' info ',
function( newInfo, oldInfo ) {
console.log( newInfo, oldInfo );
},{
deep: true
// 深度侦听
immediate: true
// 立即执行
} )
// unwatch
// 该方法设置的侦听器有一个返回值,调用返回值可取消侦听
}
生命周期函数
生命周期概念
事物从诞生到消亡的整个过程
Vue 的生命周期
new Vue( )
做每一系列事的过程中,它会告诉你,如果写了对应函数,会进行回调,反馈进度
概念
Vue的生命周期是 vue 实例在某一个时间点会自动执行的函数,可直接在 vue 实例中执行,不需要在 methods 中定义
Vue 的生命周期函数有哪些
beforeCreate
创建前(创建期间)
Vue或者组件刚实例化,data、methods都还没有被创建
created(重要)
创建完毕(创建期间)
(创建组件时开始回调)此时data和methods已经被创建,可以使用。模板还没有被编译
可做操作:
从服务器获取初始化数据
通过ajax向服务器发送数据
beforeMount
挂载前(创建期间)
created的下一阶段。此时模板已经被编译了,但是并没有被挂在到网页中
mounted(重要)
挂载结束(创建期间)
(模板挂载到 DOM 上开始回调)模板代码已被加载到网页中。此时创建期间所有事情都已准备好,网页开始运行
可做操作:操作DOM节点
befoteUpdate
更新前(运行期间)
网页运行期间,data中的数据可能会更新。在这个阶段,数据只是在data中更新了,但并没在模板中更新,因此网页中显示的还是之前的
updated
更新完成(运行期间)
(页面刷新时回调)数据在data中更新,也在网页中更新
beforeDestroy
销毁前(销毁期间)
Vue实例或者是组件在被销毁之前执行的函数。在这一个函数中Vue或者组件中所有的属性都是可以使用的
可做操作:
移除定时器
事件绑定
destroyed
销毁完成(销毁期间)
Vue实例或者是组件被销毁后执行的函数。此时Vue实例上所有东西都会解绑,所有事件都会被移除,所有子元素都会被销毁
自定义指令
作用:自定义指令解决的问题是对普通 DOM 元素进行底层操作
注册全局指令
通过 Vue.directive( ) 函数注册全局指令
创建全局指令
分析:
需传入指令名称
一个包含指令钩子函数的对象
该对象的键即钩子函数的函数名,值即函数体
钩子函数可以有多个
————
例子:
Vue.directive( 'focus', {
// 注册一个全局自定义指令 `v-focus`
inserted( el ) {
// 当被绑定的元素插入到 DOM 中时
el.focus( )
// 聚焦元素
//do something
}
} )
注册局部指令
通过组件的 directives 属性,对该组件添加一个局部指令
创建局部指令
分析:
向创建的 Vue 实例的 directives 字典属性添加键值对
键值对即需要添加的自定义指令及对应钩子函数字典对象
键值对可以有多个,对应多个自定义指令
例子:
new Vue( {
el:'#app',
directives: {
focus: {
// 指令的定义
inserted( el ) {
el.focus( )
}
}
}
} )
实用的 Vue 自定义指令
权限校验指令
v-premission
复制粘贴指令
v-copy
长按指令
v-longpress
输入框防抖指令
v-debounce
禁止表情及特殊字符
v-emoji
图片懒加载
v-LazyLoad
实现页面水印
v-waterMarker
拖拽指令
v-draggable
指令定义函数提供了几个钩子函数
bind
只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
inserted
被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)
update
被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新(详细的钩子函数参数见下)
componentUpdated
被绑定元素所在模板完成一次更新周期时调用
unbind
只调用一次, 指令与元素解绑时调用
钩子函数参数
el: 指令所绑定的元素,可以用来直接操作 DOM
binding: 一个对象,包含以下属性
name: 指令名,不包括 v- 前缀
value: 指令的绑定值
例子:
v-my-directive="1 + 1", value 的值是 2
oldValue: 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用
expression: 绑定值的表达式或变量名
例子:
v-my-directive="1 + 1" , expression 的值是 "1 + 1"
arg: 传给指令的参数
例子:
v-my-directive: foo, arg 的值是 " foo "
modifiers: 一个包含修饰符的对象
例子:
v-my-directive.foo.bar, 修饰符对象 modifiers 的值是 { foo: true, bar: true }
vnode: Vue 编译生成的虚拟节点
oldVnode: 上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用
自定义指令—函数简写
如果想在 bind 和 update 时触发相同行为,而不关心其它的钩子
————
例子:
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value
} )
自定义指令—对象字面量
注意:
如果指令需要多个值,可以传入一个 JavaScript 对象字面量
指令函数能够接受所有合法的 JavaScript 表达式
例子:
———— html 代码
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
———— Vue 代码
Vue.directive( 'demo', function ( el, binding ) {
console.log( binding.value.color )
// 输出结果:white
console.log( binding.value.text )
// 输出结果:hello
})
相关指令
v-bind(重要!)
作用:动态绑定属性
v-bind 直接绑定属性
作用:可以直接获取属性内所有信息
——
例子:
<div v-bind=" info ">v-bind 直接绑定属性!</div>
v-bind 的基本用法
语法糖(简写) :
——
例子:
<a v-bind:href=" imgURL ">百度一下</a>
// 语法糖模式
<a :href=" imgURL ">百度一下</a>
——
data:{
imgURL:' www.baidu.com '
}
v-bind 动态绑定 class(对象语法)
使用场景
动态使用 class,部分 class 可能被删除,就是用 v-bind 来动态绑定 class
固定使用 class,就直接绑定 class,不使用 v-bind 来动态绑定 class
格式:
<h2 :class="{ 类名1:boolean,类名2:boolean }">{ { message } }</h2>
例子1:
<h2 :class={ active: isActive, line: isLine }利用 v-bind 动态绑定 class</h2>
——
data:{
isActive:true,
isLine: false
}
————
例子2:
<h2 :class={ active:isActive, line:isLine }利用 v-bind 动态绑定 class</h2>
<button @click="btnClick">我可以动态的变化哦!</button>
——
const app = new Vue( {
data:{
el:"#app",
isActive: true,
isLine: true
},
methods:{
btnClick( ){
this.isActive = !this.isActive
}
}
} )
v-bind 动态绑定 class(数组语法)
用的比较少,作用不大,这样写还不如直接用直接绑定 class
v-bind 动态绑定 style( 对象语法 )
Style后面跟的是一个对象类型
对象的 key 是CSS 属性名字
对象的 value 是具体赋的值,值可以来自于 data 中的属性
格式:
<h2 :style="{ key( 属性名 ):value( 属性值 ) }">{ { message } }</h2>
例子:
<h2 :style="{ fontSize: '50px' }">{ { message } }</h2>
'50px'必须加上单引号,否则是当做一个变量去解析
————
拼接字符串,加法,隐式转换成字符串
<h1 :style="{ fontSize: fontSize + 'px' }">{ { message} }</h1>
注意:加单引号成字符串,不加单引号就是变量!
v-bind 动态绑定 style(数组语法)
style 后面跟一个数组类型
多个值以 , 分割即可
例子:
<div :style="[ baseStyles, backgroundColor ]"></div>
v-on
作用:绑定事件监听器
语法糖(简写) @
v-on 的基本用法
原生代码:
<button v-on:click="btnClick">原生代码按钮</button>
语法糖代码:
<button @click="btnClick">语法糖代码按钮</button>
v-on 参数问题
当通过 method 定义方法,供 @click 调用时,需要注意参数问题
情况一(无需额外参数)
不需要额外参数,方法后 ( )可不加
注意:如果方法本身中有一个参数,那么会默认将原生事件 event 参数传递进去
——
例子:
<button @click="btn1">+1</button>
——
methods:{
btn1( event ){
console.log( event );
this.counter++
}
}
情况二(参数,event)
如果需要同时传入某个参数,同时需要 event 时,可以通过 $enent 传入事件
——
例子:
<buttom @click="btn2(10, $event )">+5<button>
——
methods:{
btn2( count, event ){
console.log( event );
this.counter =+ 5
}
}
v-on 的修饰符使用
.stop 修饰符
作用:停止冒泡
例子:
<button @click.stop="btnClick">停止冒泡的修饰符<button>
.prevent 修饰符
作用:阻止默认事件
例子:
<from action="baidu">
<input type="submit" value="提交" @click.prenent="submitClick">
</from>
——
methods:{
submitClick( ){
console.log(' submitClick ')
}
}
keyup.enter 监听空格修饰符(监听其他键盘修饰符操作类似)
作用:监听键盘事件
例子:
<input type="text" @keyup.enter="keyUp">
——
methods:{
keyUp( ){
console.log( 'keyup' )
}
}
.native 修饰符
作用:监听组件根元素的原生事件
例子:
<cpn @click.native="cpnClick:></cpn>
// 监听组件点击
.once 修饰符
作用:只触发一次回调
例子:
<button @click.once=" btn">我只会被允许点击一次哦!珍惜我!</button>
——
methods:{
btn( ){
console.log ( '已经被点击一次!无法再次点击!' )
}
}
v-if 和 v-else和 v-else-if(v-show)
v-if
v-if 后面跟的条件是布尔值
例子:<h2 v-if = ' isShow '>{ { message } } </h2>
——
isShow: true
v-if 和 v-else
例子:<h2 v-if = ' isShow '>{ { message } }</h2>
<h1 v-else>当isShow是false的时候,你就可以看到我啦,哈哈哈哈<h1>
isShow: true
v-if 和 v-else 和 v-else-if
例子:
<h2 v-if = 'score > 90'>优秀</h2>
<h2 v-else-if = 'score >= 80'>良好</h2>
<h2 v-else-if = 'score >= 60'>及格</h2>
<h2 v-else>不及格</h2>
v-show
作用:决定一个元素是否渲染
v-if 和 v-show 的区别
原理
v-if 当条件为 false 时,压根不会有对应的元素在 DOM 中
v-show 当条件为 false 时,仅仅是将元素的 display 属性设置为 none 而已
使用场景
使用 v-show,适用于需要在显示和隐藏之间来回切换很频繁的开发
使用 v-if,适用于只有一次切换的开发
v-for
数组相关方法
常用数组方法:
概念:Vue 将被侦听数组的变更方法进行包裹,它们也将触发视图更新,会改变原来的数组
push( )
在数组尾部增加一个元素
pop( )
在数组尾部移除一个元素
shift( )
在数组头部移除第一个元素
unshift( )
在数组头部增加第一个元素
sort( )
数组排序
splice( )
添加、插入、删除、更新
reverse( )
数组完成翻转
替换数组的方法,这些数组不会替换原来的数组,而是会生成新的数组
filter( )
concat( )
slice( )
v-for 添加 key
作用:使用 key,效率会非常高
使用 v-for 进行列表渲染,通常会给元素或者组件绑定一个 key 属性
例子:
<li v-for= "item in games" :key= "item.id">{ { item } }</li>
diff 算法
真实开发中,如果某一项有 id ,一般都是采用 li 进行绑定 key,因为利用 id 在进行 diff 算法时效率更高
Vue 在进行 diff 算法时,尽量利用 key 来进行优化,在没有 key 时是非常低效的,在执行插入或重置顺序时,保持相同的 key 可以让 diff 算法更加高效
key
key 的作用主要是高效更新虚拟 DOM
key 绑定的元素不能随便绑定(不能用 index )
// 不能用index当key的原因是key的值必须是唯一的,但是index会随着数据的数量多少变化而变化,这样就不能保证key的唯一性
要保证 key 绑定的一定是唯一值
VNode
虚拟节点
本质:是一个 JavaScript 的对象
template-VNode-真实DOM
好处:可以做多平台的渲染
虚拟 DOM
template—虚拟 DOM—真实 DOM
不只是一个简单的 div,而是有一大堆的元素,它们应该会形成一个 VNode Tree
① v-for 遍历数组
① 没使用索引值(下标值)
<li v-for ='item in names'> { { item } } </li>
② 要获取索引值(下标值)
<li v-for = '( item, index) in names'>{ { index } }-{ { item } }</li>
② v-for 遍历对象
① 如果只获取一个值,那么获取到的是 value
<li v-for = 'item in info'>{ { item } }</li>
② 如果要获取 key 和 value 格式:(value,key)
<li v-for = '(value, key) in info'>{{ value}}-{{key}}</li>
③ 如果想要获取 key 和 value 、 index 格式:( value, key, index )
<li v-for = '( value, key, index ) in info'>{ { value } }-{ { key } }-{ { index } }</li>
③ v-for 遍历数字
例子:(遍历从1到10的数字!!!)
<li v-for="( number, index ) in 10>{ { number } }-{ { index } }</li>
v-model
作用:实现表单元素和数据的双向绑定
双向绑定概念
① 后台数据改变能反馈到页面显示
② 页面数据更改能直接改变后台数据
v-model 基本使用
例子:
<div id="app">
<input type="text" v-model="message">
{ { message } }
</div>
——
const app = new Vue( {
el:"#app",
data:{
message:"运用 v-model 实现双向绑定的例子"
}
} )
v-model 原理
实现原理的两个指令
v-bind 绑定一个 value 属性
v-on 给当前元素绑定 input 事件
v-model 本质是一个语法糖
————
例子:
<input type="text" v-model="message">
——等同于
<input type="text" :value="message" @input="message=$event.target.value">
v-model 结合 radio 类型使用
作用:利用v-model 实现 radio 的数据双向绑定
————
例子:
<label for="male">
<input type="radio" id="male" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" id="female" value="女" v-model="sex">女
</label>
——
const app = new Vue( {
el:"#app",
data:{
sex:"男"
}
} )
v-model 结合 checkbox 类型使用
checkbox 单选框
例子:
<div id="app">
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议
</label>
<button :disabled="!isAgree">下一步</button>
</div>
——
const app = new Vue({
el:"#app",
data:{
isAgree: false
// 单选框(布尔值)
}
} )
checkbox 多选框
注意:多选框这里,input 里面必须给出对应的 value,只有这样才能正确加入新的多选框信息展示内
例子:
<div id="app">
<input type="checkbox" value="篮球” v-model="hobbies">篮球
<input type="checkbox" value="足球” v-model="hobbies">足球
<input type="checkbox" value="乒乓球” v-model="hobbies">乒乓球
<input type="checkbox" value="羽毛球” v-model="hobbies">羽毛球
</div>
——
const app = new Vue( {
el:"#app",
data:{
hobbies: [ ]
// 多选框(数组类型)
}
} )
v-model 结合 textarea 使用
例子:
<label for="intro">
<textarea name="intro" id="intro" cols="30" rows="10" v-model="intro"></textarea>
</label>
<h2>intro:{ { intro } }</h2>
v-model 结合 select 类型使用
select 单选下拉框
例子:
<div id="app">
<select name="fruit" v-model="fruit">
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
</select>
</div>
——
const app = new Vue( {
el:"#app",
data: {
fruit:'西瓜'
}
} )
select 多选下拉框
例子:
<div id="app">
<select name="fruit" v-model="fruits" multiple>
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
</select>
</div>
——
const app = new Vue( {
el:"#app",
data:{
fruits: [ ]
}
} )
v-model 中input 的值绑定
作用:通过 v-bind 动态的给 value 赋值
例子:
<div id="app">
<label v-for="item in originHobbies" :for="item">
<input type="checkbox" :value="item" :id="item" v-model="hobbies">{ {item} }
</label>
</div>
——
const app =new Vue({
el:"#app",
data:{
hobbies: [ ],
originHobbies: ['篮球', '足球', '乒乓球', '羽毛球']
}
} )
v-model 中修饰符的使用
lazy 修饰符
作用:让数据在失去焦点或者回车时才会更新
——
例子:
<input type="text" v-model.lazy="message">
number 修饰符
作用:让输入框中输入的内容自动转成数字类型
——
例子:
<input type="number" v-model.number="age">
trim 修饰符
作用:过滤内容左右两边的空格
例子:
<input type="text" v-model.trim="name">
Vue 组件化操作
Vue 组件化思想
组件化是 Vue.js 的重要思想
它提供了一种抽象,可以开发出一个个独立可复用的小组件来构造应用
任何的应用都会被抽象成一颗组件树
组件化思想应用
作用:使代码更加方便组织和管理,并且扩展性更强
尽可能将页面拆分成一个个小的、可复用的组件
所有的组件都继承自Vue类的原型
Vue.prototype.test = function ( ) { }
组件使用步骤
① 创建组件构造器对象
Vue.extend( )
调用 Vue.extend ( ) 创建一个组件构造器
在创建组件构造器时,传入 template 代表自定义组件模板
该模板就是在使用到组件的地方,要显示的 HTML 代码
事实上,这种写法在 Vue2.x 的文档中几乎已经看不到了,他会直接使用。。。
——
例子:
const cpnC = Vue.extend( {
// 创建一个 cpnC 组件
template: `
<div>创建组件很简单!</div>
`
} )
② 注册组件
Vue.component( )
调用 Vue.component ( ) 将刚才的组件构造器注册为一个组件,并且起一个组件的标签名称
传递两个参数
注册组件的标签名
组件构造器
——
例子:
Vue.component( 'cpn', cpnC )
全局注册
直接注册的是全局组件
局部注册
在某个组件内部注册的组件就是局部组件
③ 使用组件
组件必须挂载到某个 Vue 实例下,否则它不会生效
使用组件
——
例子:
<div id="#app">
<cpn></cpn>
<cpn></cpn>
</div>
全局组件和局部组件
全局组件
直接运用 Vue.component( )创建的就是全局组件
可以在多个 Vue 实例下使用
——
例子:
Vue.component( 'cpn', cpnC )
局部组件(开发用的最多)
在 Vue 实例 里用 components 注册的组件就是局部组件
——
例子:
components:{
cpn: cpnC
// cpn 是使用组件时的标签名,cpnC 是注册的组件
}
父组件和子组件
组件和组件之间存在层级关系,非常重要的关系是父子组件关系
在一个组件内部使用 components 注册的组件是子组件
注意:在很多情况下是直接让 Vue 实例的注册的组件成为父组件
——
例子:
const cpnC1 = Vue.extend ( {
template:`
<div>
<h1>我是子组件</h1>
</div>
` } )
——
const cpnC2 = Vue.extend ( {
template:`
<div>
<h1>我是父组件</h1>
<cpn1></cpn1>
</div>
`
components: {
cpn1:cpnC1
// cpn1 是注册的子组件的标签名,cpnC1 是注册的子组件
// cpnC2 是 cpnC1 的父组件
}
} )
注册组件的语法糖
语法糖的原理
主要是省去调用 Vue.extend ( )步骤,而是直接使用一个对象来代替
注册组件的原生代码写法
// 创建组件构造器
const cpnC1 = Vue.extend( {
template:`
<div>
<h1>我是原生代码写的全局组件</h1>
</div>
`
} )
——
// 注册全局组件
Vue.component( 'cpn1', cpnC1 )
注册组件的语法糖写法
// 语法糖注册全局组件
Vue.component( 'cpn1',{
template:`
<div>
<h1>我是用语法糖注册的全局组件</h1>
</div>
`
} )
————————
// 语法糖注册局部组件
const app = new Vue( {
el:'#app',
data: {
message:'我这里在用语法糖注册一个局部组件'
},
components:{
cpn2:{
template:`
<div>
<h1>我这里是语法糖注册的局部组件</h1>
</div>
`
}
}
} )
组件模板抽离的写法
script 标签的写法(类型必须是 text/x-template )
例子:
<script type="text/x-template" id="cpn">
<div>
<h1>利用 script 标签的组件模板抽离写法</h1>
</div>
</script>
——
const app = new Vue ( {
el:"#app",
data: {
message:"利用 script 标签的组件模板抽离写法"
},
components: {
cpn: {
template:"#cpn"
}
}
} )
template 标签的写法(和普通标签使用方法一样,但是需要绑定 id)
注意:抽离出来的 <template></template>组件,必须包含一个根 <div></div>
——
例子:
<template id="cpn">
<div>
<h1>利用 template 标签的组件模板抽离写法</h1>
</div>
</template>
——
const app = new Vue ( {
el:"#app",
data: {
message:"利用 template 标签的组件模板抽离写法"
},
components: {
cpn: {
template:"#cpn"
}
}
} )
为什么组件data必须是函数
组件数据的存放
组件对象也有 data 属性(也可以有 methods 等属性)
data 属性必须是函数
// 示例:data ( ) { }
函数必须返回一个对象,对象内部保存着数据
// 示例:return { }
作用:避免变量之间相互影响,保证了每个组件实例都有独立空间和状态,组件不能相互影响
原因:函数运行每一次都会创造新空间(函数存在函数作用域,内存地址都不一样,函数有独立的作用域空间),函数之间变量互不影响
例子:
Vue.component ( 'cpn', {
template: '#cpn',
data ( ) {
// data 必须是函数
return {
// return 一个对象
message: '组件内的data必须是函数,作用是为了避免变量之间的相互影响'
}
}
} )
内存地址相同的例子:
const obj = {
name: 'itchao',
age: 18,
height: ' 1.85'
}
function data ( ) {
return obj
// 返回的是一个对象,内存地址是一样的,会存在变量互相影响的问题
}
父子组件通信(非常重要!)
父组件向子组件传递数据—props
概念:父子组件通信,主要是在两者之间进行传递数据或者传递事件,完整的展示全部数据
父子通信的思想:
在开发中,一些数据需要从上层传递到下层
比如:
在一个页面,从服务器请求到很多数据
其中一部分数据,并非是整个页面的大组件来展示,而需要下面的子组件进行展示
此时,不会让子组件再次发送一个网络请求,而是让大组件(父组件)将数据传递给小组件(子组件)
方式一(开发经常用!)对象
对象可以设置传递时的类型和默认值等等
1.类型限制
例子:
props: {
cmovies: Array,
cmessage: String
}
2.类型限制和提供一些默认值,以及必传值
例子:
props: {
cmessage: {
type: String,
default: 'aaaa',
required:true
// required 是否是必填值,后面属性值是布尔值
// true 就是必填,false 就是可以不填
}
}
类型是对象或者数组时,默认值必须是一个函数
方式二(开发很少用)字符串数组
数组中的字符串就是传递时的名称
例子:
<div id='app'>
// Vue 实例的代码
<cpn :cmovies='movies' :cmessage='message'></cpn>
</div>
<template id='cpn'>
// 组件模板的代码
<div>
<p> { { cmovies } }</p>
<p> { { cmessage } }</p>
</div>
</template>
——
const cpn = {
// 注册全局组件
template: '#cpn',
props: [ 'cmovies', 'cmessage' ],
// props以数组的形式传递数据
data ( ) {
return { }
},
methods: {
}
}
——
const app = new Vue ( {
// 注册 Vue 实例
el: '#app',
data: {
message: '利用props进行父传子的组件通信' ,
movies: '[ '海王','海贼王','火影忍者']
},
components: {
cpn
}
} )
props驼峰标识
父组件中使用 - 连接( 父组件不识别大小写 )
<cpn :child-message="message"></cpn>
// message 是父组件内定义的变量
// child-message 是子组件内定义的变量
// 这行代码相当于,从父组件中把数据传给子组件
子组件中使用驼峰标识
<h2>{ { childMessage} }</h2>
子传父(自定义事件)
利用 $emit 注册自定义事件
什么时候需要自定义事件呢
当子组件需要向父组件传递数据时,就用自定义事件,v-on 也可以用于组件间的自定义事件
自定义事件的流程
在父组件中,通过 v-on 来监听子组件事件
在子组件中,通过 $emit( ) 来触发事件
——
例子:
<div id = ' app '>
// 父组件模板
<cpn @item-click = 'cpnClick></cpn>
// @item-click,用来监听事件
</div>
——
<template id = ' cpn '>
// 子组件模板
<div>
<button
@click = 'btnClick( item )'
v-for = ' item in categories'
>{ { item.name } }</button>
<div>
</template>
——
const cpn = {
// 子组件
template: '#cpn',
data ( ) {
return ( ) {
categories : [
{ id: ' a ', name: ' 卡莎 '},
{ id:' b ',name::' 皮城女警 '},
{ id:' c ', name: ' 青钢影 '}
]
}
}
}
methods:{
btnClick(item) {
// 可以知道子组件点击了谁
this.$emit('item-click', item)
// 子组件如果有东西想发给父组件,那么就可以通过自定义事件来发射事件,事件的类型需要自定义,就是这里的 item-click 需要自定义
}
}
————————
// 父组件
const app = new Vue ( {
el:'#app',
data:{
message: ' 子传父通过自定义事件! '
},
methods: {
cpnClick ( item ) {
console.log ( ' 父组件中定义的方法 ', item)
}
}
} )
结合双向绑定
通过双向绑定东西,不要绑定到 props 里面(最好通过父组件进行数据传输)
要修改数据,必须要绑定到 data 里面,而且组件内的 data 必须是函数,有 return 返回值
父组件访问子组件—$refs( 非常重要!)
$children
开发中用的少,不建议使用,除非访问所有子组件
$refs(非常重要!)
作用:父组件访问子组件的一个非常重要的方式,其中ref 相当于 key,可以准确的拿到自己需要的数据
使用的频率很高,非常重要
$refs
对象类型
默认是空对象
想拿到其他数据,就要用到子组件定义的 ref
例子:
ref = 'itchao'
——
this.$refs.itchao.name
作用:拿到绑定好的 ref,准确的进行父组件访问子组件,$refs是一个拿到数据,使用数据的过程
ref
开发中用的非常多,作用是类似给子组件绑定一个 key,保证父组件访问子组件的准确性
————
例子:
<div id='app'>
// Vue实例
<cpn ref='itchao'></cpn>
// ref='itchao' 中 itchao 相当于一个key,保证父组件访问子组件的准确性
</div>
console.log( this.$ref.itchao.name)
// 通过ref 和 $refs 进行父组件访问子组件的通信,保证通讯的准确性
// 这个例子中itchao是子组件,这行代码是拿到 itchao 对应的子组件的名字
子组件访问父组件
$parent
开发中不建议这样使用,使用后果是子组件不够独立,开发用非常少
例子:
console.log(this.$parent.name)
$root
可以直接访问 Vue 实例,用的也比较少
例子:
console.log(this.$root.message)
slot 插槽
作用:让封装的组件更具扩展性,让使用者决定组件内部的一些内容到底展示什么
将共性抽取成组件,不同点使用插槽
slot插槽的基本使用
插槽可以预留默认值
<slot><button>我是插槽的默认值做的按钮</button></slot>
————
例子:
<div id="app">
<cpn><button>预留插槽的空间做的按钮</button></cpn>
<div>
————
<template id="Cpn">
<div>
<h1>我是一个模板,组件模板!<h1>
<slot></slot>
// 插槽放置的位置,在用组件的时候可以在这个位置放置其他元素,如上面使用组件所示
</div>
</template>
具名插槽的使用
作用:特定替换指定插槽的信息(单独替换)
例子:
<div id="app">
<cpn><button slot="center">我来了,替换插槽!!!</button></cpn>
</div>
————
<template id="cpn">
<div>
<slot name="left"><span>左边插槽!</span></slot>
<slot name="center"><span>中间插槽!</span></slot>
<slot name="right"><span>右边插槽!</span></slot>
</div>
</template>
编译作用域的概念
就近原则查找对应变量
使用的变量会查找最近的模板(优先在自己的作用域去查找对应的变量)
如果是在 Vue 模板下定义的变量,那么会首先在 Vue 实例里去查找是否有对应的变量
作用域插槽的使用
作用:父组件替换插槽的标签,但是内容由子组件提供
————
例子:
————Vue实例
<div id="app">
<cpn>
<template slot-scope='slot'>
<span v-for="item in slot.data">{ { item } }</span>
</templte>
</cpn>
</div>
—————子组件
<template id="cpn">
<div>
<slot :data="PLanguages">
<ul>
<li v-for="item in PLanguages">
{ { item } }
</li>
</ul>
</slot>
</div>
</template>
Vuex
Vuex 核心概念
State
作用:存储数据,Vuex内的数据都放在State
单一状态树
Getters
作用:当数据需要处理再用,可以通过Getters处理
Mutation
作用:Vuex的store状态的更新唯一方式:提交Mutation
Mutation 携带参数
Mutation 提交风格
Mutation 的类型常量
Action
作用:处理异步操作
action 本身可以返回一个Promise,可以在其它地方拿到这个Promise
Module
作用:让Vuex 代码结构清晰
Vuex 数据响应式原理
Vuex 的 store 是响应式,当 state 中的数据发生改变时,Vue 组件会自动更新
必须遵守一些 Vuex 对应规则:
提前在 store 中初始化好所需属性
state 中对象添加新属性时,使用下面方式
方式一:使用 Vue.set(obj,'newProp',123)
例子:
Vue.set(state.info, 'address', '成都')
方式二:用新对象给旧对象重新赋值
当给 state 中的对象删除属性时,使用下面的方式
使用 Vue.delete(obj,'oldProp')
例子:
Vue.delete(state.info, 'age')
Vuex-stored 文件夹目录组织
modules(模块化管理)
这个文件夹里面放一些 module 相关代码
index.js
组装模块并导出 store 的地方
actions.js
根级别 action
mutations.js
根级别 mutation
getters.js
根级别 getter
Vue-router
前端渲染和后端渲染
后端路由阶段
概念:服务器直接生产渲染好对应的HTML页面,返回给客户端进行展示
前后端分离阶段
概念:后端只负责提供数据,不负责任何阶段内容
前端路由阶段(SPA页面—单页面富应用)
概念:前端路由管理目前映射
SPA最主要特点:在前后端分离的基础上加了一层前端路由,让前端来维护一套路由规则
核心:改变URL,但是页面不进行整体刷新
url的hash和HTML5的history(改变url )
url的hash
location.hash = 'foo'
HTML5的history
history.pushState({ }, ' ', 'home' )
有返回按钮,可以支持返回操作
history.replaceState({ }, ' ', 'me' )
无返回按钮,不支持返回操作
history.forward( )
前进
history.back( )
后退
history.go( )
history.go( 1 )
等价于 history.forward( )
history.go( -1 )
等价于 history.back( )
Vue-Router 配置方式
router 文件夹下创建 index.js(路由相关配置)
例子:
// 配置路由相关信息
import VueRouter from 'vue-router'
import Vue from 'vue'
// 1.通过 Vue.user ( 插件 ),安装插件
Vue.use ( VueRouter )
// 2.创建 VueRouter 对象( 创建路由实例 ),并传入路由映射配置
const routes = [
]
const router = new Router ( {
// 配置路由和组件之间的应用关系
routes
} )
// 3.将 router 对象传入到 vue 实例
export default router
import App from './App'
main.js
例子:
import Vue from 'vue'
import router from './router'
Vue.config.productionTip = false
new Vue ( {
el:'#app',
// 挂载路由 router
router,
render:h => h ( App )
// render 是渲染相关的东西
} )
router 文件夹内 index.js 完整例子(路由相关配置)
例子:
// 配置路由相关信息
import VueRouter from 'vue-router'
import Vue from 'vue'
import Home from '../components/Home'
import About from '../components/About'
// 1.通过 Vue.user ( 插件 ),安装插件
Vue.use ( VueRouter )
// 2.创建 VueRouter 对象( 创建路由实例 ),并传入路由映射配置
const routes = [
{
path:'/home',
// 这里的 path 是路径,不能写成 url,因为 url 是完整网址,这里路径只是一部分相对路径
component: Home
},
{
path:'/about',
component:About
}
]
const router = new Router ( {
// 配置路由和组件之间的应用关系
routes,
mode:'history'
// 将 hash 模式修改为 history 模式
} )
// 3.将 router 对象传入到 vue 实例
export default router
路由的默认值和修改为history模式
路由的默认值
path配置根路径是: /
redirect: '/home' (重定向)
多配置一个映射代码:
{
path:'/',
redirect:'/home'
}
修改为history模式
默认情况下路径的改变使用的是URL下的hash模式
将hash模式修改为history模式(在创建router实例的花括号内): mode:'history'
router-link 组件属性
概念:该组件是在 vue-router 中内置好的组件,它会被渲染成一个 <a> 标签
router-link 例子:
<template>
<div id = 'app'>
<router-link to='/home'>首页</router-link>
<router-link to='/about'>关于</router-link>
</div>
</template>
相关属性
to 属性
作用:用于指定跳转路径
tag 属性
作用:指定<router-link>之后渲染成什么组件
例子:
<router-link to='/home' tag='li'></router-link>
replace 属性
作用:使用该属性,不会留下history记录,返回键失效,不需要赋值
例子:
<router-link to='/home' tag='button' replace></router-link>
active-class 属性
作用:<router-link>路由正确匹配后,自动给当前元素设置一个 router-link-active的class,设置active-class可以修改默认的 router-link-active 名称
例子:
根据路由匹配正确后增加的 router-link-active 的 class 类做一些相应操作,比如点击某个按钮之后,被点击的按钮会增加一个 router-link-active 的 class 类,用这个特性让实现别点击的按钮文字字体变红等操作
index.js 中 router 有 linkActiveClass 属性,可以直接改变这个属性来更改后面路由发现变化时正确响应的路由的 router-link-active 的值
例子:
linkActiveClass:'active'
$router 路由代码跳转
概念:$router 是vue-router提供在所有组件中都存在的属性
作用: 可以调用一些方法,比如 push 方法和 replace 方法等,进行相关路由跳转
$router 是创建出来的 router 路由对象
this.$router 可以直接拿到$router属性
路由跳转方法一: push 方法
this.$router.push( '/home')
this.$router.push('/about')
路由跳转方法二: replace 方法
特点:没有返回值,不能返回
this.$router.replace('/about')
this.$router.replace('./home')
动态路由
概念:在某些情况下,一个页面 path 路径可能不确定, 这种 path 和 Component 的匹配关系,称之为动态路由(也是路由传递数据的一种方式)
$route
概念:当前那个路由处于活跃状态就拿到那个路由
params 参数
例子:
computed: {
userId ( ) {
return this.$route.params.userId
// 可以直接拿到 userId(这里写 .userId 是因为,配置路由参数的时候在 path 路径里面写的就是 useId,可以根据自己情况进行修改)
}
}
router-view 组件
概念:该组件会根据当前路径,动态渲染出不同组件
网页的其他内容,比如标题/导航或一些版权信息等,和 <router-view> 处于同一等级
在路由切换时,切换的是 <router-view> 挂载的组件,其他内容不会发生改变
作用:决定了被渲染的东西是位于之后显示的具体位置(相当于先占位置,然后再进行对应替换)
例子:
<template>
<div id = 'app'>
<router-link to='/home'>首页</router-link>
<router-link to='/about'>关于</router-link>
<router-view></router-view>
// 以后这个位置组件可能会被替换成其他组件
</div>
</template>
路由懒加载
作用:将路由对应组件打包成单独的js代码块(只有在这个路由被访问时,才加载对应组件)
建议路由都是用懒加载的方式,这样才能让打包出来的 . js 文件更小,让用户在请求的时候效率更高
懒加载方式
方式一:结合Vue的异步组件和Webpack的代码分析
方式二:AMD写法
方式三:在ES6中,可以组织Vue异步组件和Webpack的代码分割(重要!)
例子:
const Home = ( ) => import ( '../components/Home.vue' )
路由嵌套使用
实现嵌套路由的两个步骤
① 创建对应的子组件,并且在路由映射中配置对应的子路由
② 在组件内部使用<router-view>标签
使用方法:在需要使用的路由内写一个 children 属性,实现嵌套使用路由
传递参数方式
Params类型
配置路由格式:/router/:id
传递的方式:在path后面跟上对应的值
传递后形成的路径:/router/123
query类型
配置路由格式:/router(就是普通配置)
传递的方式:对象中使用 query 的 key 作为传递方式
传递后形成的路径:/router?id = 123
导航守卫
作用:监听从一个地方跳到另一个地方
参数解析
to
将进入的目标路由对象
from
当前导航将离开路由对象
next
调用该方法后,才能进入下一个钩子
下面的代码是前置守卫(guard)
// 回调函数
router.beforeEach ( ( to, from, next) =>
//从from跳转到to
{
document.title = to.matched[0].meta.title
// 跳转之前就把标题改了
next( )
})
补充
如果是 afterEach 后置钩子(hook),不需要主动调用 next( )函数
// 跳转之后回调
前面两个使用的导航守卫,被称为全局守卫
beforeEach ( )
afterEach ( )
路由独享守卫
概念:只有进到某个路由内才能进行回调函数
组件内守卫
概念:进入到某个组件内才能进行回调函数
keep-alive 与 router-view
keep-alive 概念
keep-alive 是 Vue 内置的组件,可以使被包含的组件保留状态或避免重新渲染
keep-alive 作用
保持活跃状态,避免频繁被创建和销毁
只有是 keep-alive 的情况才能使用
activated
deactivated
例子:
<keep-alive>
<router-view/>
</keep-alive>
keep-alive两个重要属性
include
字符串或正则表达式,只有匹配的组件会被缓存
作用:包含,才可以缓存
exclude
字符串或正则表达式,任何匹配的组件都不会被缓存
作用:排除,就可以不缓存
例子:
<keep-alive exclude = "Profile,User">
<router-view/>
</keep-alive>
router-view
如果直接被包在keep-alive里面,所有路径匹配到的视图组件都会被缓存
axios
选择什么网络模块?
选择一:传统 Ajax 基于 XMLHttpRequest(XHR)
为什么不用它?
配置和调用方式等非常混乱
编码看起来非常不好
真实开发中很少使用它,而是选择使用 jQuery-Ajax
选择二:使用 jQuery-Ajax(相较于传统的 Ajax 非常好用)
为什么不选择它?
在 Vue 的整个开发中都不需要使用 jQuery
意味着为了方便进行一个网络请求,需要特意引用一个 jQuery,这种做法不合理
选择三:官方在 Vue1.x 的时候,推出了 Vue-resource
Vue-resource 体积相对于 jQuery 小很多
Vue-resource 是官方推出
为什么不选择它?
在 Vue2.0 推出后,Vue 作者就在 GitHub 的 Issues 中说明了去掉 vue-resource,并且不再更新
意味着以后 vue-resource 不再支持新版本时,也不再继续更新和维护
对以后的项目开发和维护都存在很大隐患
选择四:尤雨溪推荐了 axios 框架 (重要!)
axios 优点
支持 Promise API
在 node.js 中发送 http 请求
拦截请求和响应
转换请求和响应数据
在浏览器中发送 XMLHttpRequest 请求
等等
axios 请求方式
axios(config)
axios.request(config)
axios.get( url[,config])
axios.delete( url[,config])
axios.head( url[,config])
axios.post( url[,data[,config]])
axios.put( url[,data[,config]])
axios.patch( url[,data[,config]])
get 请求参数拼接 (params)
作用:get 请求中,拼接 url 参数
params : {
type: 'pop',
page: 1
}
axios 并发请求
作用:使用 axios.all 可以放入多个请求的数组
axios.all([ ])返回结果是一个数组,使用 axios.spread 可将数组 [res1, res2] 展开为 res1, res2
例子:
axios.all ( [
axios ( {
// 请求一
url:'www.baidu.com'
} ),
axios( {
// 请求二
url:'www.jd.com',
params:{
type:'sell',
page:5
}
} ) ] ).then(res => {
console.log(res )
})
常见配置选项
全局配置
开发中很多参数都是固定的,利用 axios 全局配置,进行一定的抽取
例子:
全局配置默认根路径:
axios.defaults.baseURL = 'www.baidu.com'
请求地址 url
url: ' /user '
请求类型 method
method: ' get '
默认情况下,不传入 method 方法时,请求方式为 get 请求
在 get 请求方法时,可以传入 params 进行 url 参数拼接
根路径 baseURL
baseURL: ' http://www.mt.com/api '
自定义请求头 header
header:{ 'x-Requested-With':'XMLHttpRequest'}
请求前的数据处理
transformRequest:[ function( data ) { } ]
响应后的数据处理
transformResponse:[ function( data ) { } ]
URL 查询对象 params
作用:在 get 请求方式中,利用传入 params 进行 url 参数拼接
例子:
params:{ page:1,type:'pop' }
查询对象序列化函数
paramsSerializer: function ( params ) { }
请求体 request body(对应 post 请求方式)
data:{ key: ' aa ' }
超时设置 timeout
timeout: 1000
跨域是否带 Token
withCredentials:false
自定义请求处理
adapter: function(resolve, reject, config){ }
身份验证信息
auth:{ uname: '',pwd:'12'}
响应的数据格式 json /blob/ document/ arraybuffer/ text/ stream=
responseType: ' json '
axios 实例和模块封装
创建对应 axios 实例( 不用全局实例)
作用:创建独立的新实例,实现特定实例单独配置,避免配置重复
例子:
const instance1 = axios.create({
// 创建实例1
baseURL:'http://123.203.31.52.8000',
timeout:500
})
instance1({
url: '/home/multidata'
}).then(res => {
console.log(res);
})
instance1({
url:'/home/data',
params:{
type:'pop',
page:1
}
}).then(res => {
console.log(res);
})
const instance2 = axios.create({
// 创建实例2
baseURL:'http://125.322.32.52:8000',
timeout:10000
})
instance2({
url:'/home/work'
}).then(res => {
console.log(res)
})
模块封装
作用:避免对第三方框架依赖过多,避免当第三方框架出现问题时严重影响自己项目,模块封装可以只更改 request.js 相关内容,方便很多
封装请求实例 (比如:request.js)
注意:这个要和下面的封装 request.js 一同对照看,是对应关系
例子:
****方式一(回调函数方式)****
import axios from 'axios'
export function request( config, success, failure ){
// 1.创建 axios 实例
const instance = axios.create( {
baseURL:'http://123.23.15.84:8000',
timeout:8000
} )
// 发送真正网络请求
instance(config).then( res => {
// 发送请求成功
success(res)
// 注意:利用 success 回调函数把 res 回调出去
}).catch(err => {
// 发送请求失败
failure(err)
// 注意:利用 failure 回调函数把 res 回调出去
})
}
***************方式二(回调函数方式)**************
import axios from 'axios'
export function request( config ){
// 1.创建 axios 的实例
const instance = axios.creat({
baseURL:'http:123.32.32.63:8000',
timeout:5000
})
// 发送真正的网络请求
instance( config.baseConfig ).then( res => {
// 发送请求成功
config.success(res)
// 关键点一
}).catch(err => {
// 发送请求失败
config.failure(res)
// 关键点二
})
}
***************方式三( Promise 方式 )***************
import axios form 'axios'
export fuction request (config) {
return new Promise( ( resolve, reject ) => {
// 1.创建 axios 的实例
const instance = axios.create( {
baseURL:'http://123.207.32.32:8000',
timeout:5000
} )
// 发送真正的网络请求
instance (config)
.then(res => {
// 发送请求成功来到这里
resolve (res)
} )
.catch (err => {
// 发送请求失败来到这里
reject (err)
} )
} )
}
****(重要!)方式四 (不写Promise,原因是instance实例的返回值,本身就是一个Promise)****
import axios from 'axios'
export function request (config) {
// 1.创建 axios 的实例
const instance = axios.create ( {
baseURL:'http://123.207.32.32:8000',
timeout:5000
} )
// 2.发送真正的网络请求
return instance(config)
}
封装 request 模块 (比如:main.js)
****方式一(回调函数方式)****
import { request } from './network/request'
request ( {
url:' /home/multidata '
}.res => {
// 发送请求成功来到这里
console.log(res)
},err => {
// 发送请求失败来到这里
console.log(err)
} )
****方式二(回调函数方式)****
import { request } from './network/request'
requsest( {
baseConfig:{
// 这里传入一些基本配置
url: ' ',
// 这下面的配置根据自己需求自己填写
methods: ' ',
params: ' '
},
success: function (res){
console.log( res )
},
failure: function (err){
console.log( err )
}
} )
****方式三( Promise 方式 )****
import { request } from './network/request'
request ( {
url:' /home/multidata '
} ).then(res => {
console.log(res);
} ).catch( err => {
console.log(err);
} )
****(重要!)方式四(不写Promise,原因是instance实例的返回值,本身就是一个Promise)****
// 这里与上面的方式三是一模一样的
import { request } from './network/request'
request ( {
url:' /home/multidata '
} ).then(res => {
console.log(res);
} ).catch( err => {
console.log(err);
} )
axios 拦截器 (写在 request.js )
作用:用于在发送每次请求或者得到响应后,进行对应处理
全局拦截器,axios.interceptors
实例拦截器
请求拦截
作用(常见场景)
如:config 中的一些信息不符合服务器的要求
如:某些网络请求( 比如登录 [ token ] ),必须携带一些特殊信息
请求拦截器
例子:
instance.interceptors.request
// 前面的instance是实例名字,自行根据自己实例名字进行更改
使用方法
instance.interceptors.request.use (参数一, 参数二)
// 使用时传两个函数参数,一个是请求发送成功的函数参数,另一个是请求发送失败的函数参数,具体使用方法看下面例子
例子:
instance.interceptors.request.use( config => {
// config 是传回来的参数,可以自己命名,config 请求相关配置
console.log( config );
// 请求成功拦截
return config
// 拦截 config 之后必须要返回出去,不然会导致内部发送真正请求时拿不到 config
},err => {
// err 是传回来的参数,可以自己命名
console.log( err);
// 请求失败拦截(一般来这里比较少,只有请求发不出去时才会来这里,比如网络断开等等)
return err
// 同样把拦截的 err 返回出去,不然会让其他地方拿不到数据
})
响应拦截
作用:对一些从服务器响应的东西进行拦截
响应拦截器
例子:
instance.interceptors.response
// 前面的instance是实例名字,自行根据自己的实例名字进行更改
使用方法
instance.interceptors.response.use(参数一,参数二 )
// 使用的时候需要传两个参数,一个是请求发送成功参数,另一个是请求发送失败参数,具体使用方法可以看下面例子
例子:
instance.interceptors.response.use(res => {
// res是传回来的参数,可以自己命名,res 是响应相关结果
console.log( res);
// 响应成功拦截
return res
// 这里返回最多的是 res.data ,因为 data 里面存放了需要的数据
},err => {
// err 是传回来的参数,可以自己命名,err 是响应失败的信息
console.log( err );
// 响应失败拦截
})
项目搭建相关问题
别名配置
*默认别名*
'@': 'src'
例子:module.exports = {
configureWebpack: {
resolve: {
alias: {
'assets': '@/assets',
'common': '@/common',
'components': '@/components',
'network': '@/network',
'views': '@/views',
}
}
}
}
.editorconfig 文件
作用:对代码风格进行统一管理,如:缩进的统一、缩进的大小等
建议每个项目都有这个文件,可以对整个组开发前端项目风格进行统一
*如果有一天成为项目组长,一定要加上这个文件,这个文件很重要!*
项目名称不要有中文,因为中文可能会出错