2021-05-17
三阶段第一天渐进增强和优雅降级渐进增强就是先兼容低版本在逐渐地向高版本提高优雅降级就是先完成高版本浏览器的编写 在向低版本兼容Vuevue的基本概念vue就是当下最流行的前端js框架(数据处理和数据绑定的一个工具)# 作者 :尤雨溪是什么vue是一款用于构建用户界面的渐进式的自底向上增量开发的MVVM框架渐进式:就是不做职责以外的事情.(就是vue可以在一个项目中的局部使用他不会影响没有使用的位置
vue
拓展 与vue无关
防制章:快速选择—>色彩范围—>调整好确定—>ctrl+J—>关闭图层,进行后续操作
promise
就是让传统的回调函数层层嵌套(回调地狱)的写法进行一个包装 让这种写法看上去可读性更高 (代码更值钱)
状态:
1.等待状态 padding
2.成功状态 resolve -----》 then(()=>{ 对应的就是成功 })
3.失败状态 reject ----》catch( ()=>{ 对应的就是失败 } )
Promise.resolve(x) 可以看作是 new Promise(resolve => resolve(x))
promise.all() 方法
有100个异步请求如何同时发送?有多个异步请求如何同时发送?
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
如果都是成功的 那么返回的结果是一个数组 并且包含所有成功的结果
如果其中由一个或多个失败的 那么返回的结果是最先那个失败的内容 不返回成功的内容
<!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>
</head>
<body>
<script>
// let pro=new Promise((resolve)=>{
// resolve("成功")
// })
let p1=Promise.resolve("aa")
let p2=Promise.resolve("bb")
let p3=Promise.reject("cc")
// p1.then((ok)=>{
// console.log(ok)
// })
// p2.then((ok)=>{
// console.log(ok)
// })
// p3.then((ok)=>{
// console.log(ok)
// })
Promise.all([p1,p2,p3]).then((ok)=>{
console.log(ok)
})
</script>
</body>
</html>
事件循环 eventloop
js在最初设计的时候就是单线程的。但是在处理一些等待事件较长的操作时(比如有:异步请求)
所以js必须要有异步 。js通过事件循环来实现异步 这也是js的运行机制。
事件循环分类
遇到同步任务直接执行
遇到异步任务分类为
宏任务(macro-task)setTimeout,setInterval
微任务(micro-task)。 promise
总结
有微则微,无微则宏
先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。
es6中的class
constructor 构造方法
super 调用他爸的构造方法
ES6类中 子类不管写不写constroctor 在初始化这个类的时候 都会自动不上
constoroctor可以不写 但是一旦你写了 那么就必须在其中添加一个super() 那么在这个时候当前子类就有了this
Generator
generator 生成器函数 *号函数 他是es6新特性 是一个用来封装异步任务的一个容器。
生成器函数就是让普通函数交出执行拳(可以手动的设置函数的执行或停止)
// 普通函数在调用的时候内容全部执行
function fun(){
console.log("111");
console.log("222");
console.log("333");
}
fun()
yield只能在generaotr函数中使用后 是分割线
next() 就是用来执行
function *fun(){
console.log("111");
yield
console.log("222");
yield
console.log("333");
}
let demofun=fun()
demofun.next()
demofun.next()
demofun.next()
传参
function *fun(){
console.log("111");
let texta= yield
console.log(texta);
let textb=yield
console.log(textb);
}
let demofun=fun()
// 如果你要是在next中传入参数 那么第一个next不会进行传递
demofun.next("一")
demofun.next("二")
demofun.next("三")
语法:
在生命函数的时候在 function与函数名之间有个*号(用来和普通函数进行区分)
jquery 的ajax请求方式
<!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="./node_modules/vue/dist/vue.min.js"></script>
<script src="./node_modules/jquery/dist/jquery.min.js"></script>
</head>
<body>
<div id="demo">
<ul>
<li v-for="(v,i) in arr">
<span>{{v.createAt}}</span>
<span>{{v.commentTxt}}</span>
</li>
</ul>
</div>
<script>
new Vue({
el:"#demo",
data:{
arr:[],
},
mounted(){
$.ajax({
url:"http://api.artgoer.cn:8084/artgoer/api/v1/user/324380/v3/topic/topicHomeByLabel?pageIndex=1&token=b544cd63-6d42-46fe-a96c-3cf96bae3113&topicId=62187",
type:"GET",//请求方式
dataType:"json",//将请求来的字符串转转成json文件
success:(ok)=>{
console.log(ok.data.commentList)
this.arr=ok.data.commentList
}
})
},
})
</script>
</body>
</html>
bootstrap
1.下载 npm install --save bootstrap@3.3.7 (@前面表示要下载的内容,后面表示下载的版本号)
2,要和jQuery结合使用,下载好以后在页面当中引入 bootstrap的css和js,同时引入jquery便可使用 bootstrap框架完成页面内容(因为他的js功能依赖jQuery)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
</head>
<body>
<button id="btn">12</button>
<script src="node_modules/jquery/dist/jquery.js"></script>
<script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<script>
</script>
</body>
</html>
第一天
渐进增强和优雅降级
渐进增强就是先兼容低版本 在逐渐地向高版本提高
优雅降级就是先完成高版本浏览器的编写 在向低版本兼容
Vue
vue的基本概念
vue就是当下最流行的前端js框架(数据处理和数据绑定的一个工具)
# 作者 :尤雨溪
是什么
vue是一款用于构建用户界面的渐进式的自底向上增量开发的MVVM框架
渐进式:就是不做职责以外的事情.(就是vue可以在一个项目中的局部使用 他不会影响没有使用的位置 也可以整个项目都是用)
自底向上增量开发:先写好一个基础的页面 在去添加各个复杂的功能,简单到繁琐的过程
MVVM是一个框架的模式:
什么是框架
用来封装一些与业务(就是你写的项目的功能)无关的代码块 我们使用他进行拼装 即可完成指定项目功能
框架的优势
大大提高了开发效率(但是要学习新的语法对初学者来说比较麻烦)
MVC
是昨早的一种框架模式------》mvvm是mvc的变种
就是把一个项目一个功能抽象成了不同的模块 每个模块来管理自己的事情 让开发变得更加的简单
M (model)模型(今后只要说到模型大家自动转换成数据) 管理数据的
V (view) 视图 (就是用户可以看见的地方)
C (controller)控制器 用来对数据接收和展示的中间商
MVVM
是vue中使用的一种模式
M (model)模型(今后只要说到模型大家自动转换成数据) 管理数据的
V (view) 视图 (就是用户可以看见的地方)
VM (ViewModel)视图模型 模型数据与视图中的一个数据桥梁(这个桥梁更加智能他能发现模型或者是视图中的数据改变 当一方发生了改变 就会立即通知另外一方进行改变)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E5dQNY4S-1621250768995)(.\img\2.bmp)]
vue的开发目的
1.解决了数据绑定问题
2.可以开发大型单页面SPA应用
3.支持组件化
HelloWord
1.我们要使用先下载 :npm install -save vue
2.开始编写页面代码
<!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>
<!-- 1.先引用 -->
<script src="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<!-- M (model)模型(今后只要说到模型大家自动转换成数据) 管理数据的
V (view) 视图 (就是用户可以看见的地方)
VM (ViewModel)视图模型 模型数据与视图中的一个数据桥梁(这个桥梁更加智能他能发现模型或者是视图中的数据改变 当一方发生了改变 就会立即通知另外一方进行改变) -->
<!-- 2.新建V层 -->
<div id="demoDiv">
{{text}}------{{num}}
</div>
<script>
// 3.新建vm层 (就是vue实例)
new Vue({
// 4.关联模型与视图层
el:"#demoDiv",
data:{ //5模型层
text:"你好么么大",
num:18
}
})
</script>
</body>
</html>
vue的渲染方式
{{}}-----》模板语法 模板表达式 双花括号赋值法
作用:
用于在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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>模板语法</h1>
<!-- {{}}-----》模板语法 模板表达式 双花括号赋值法
作用:
用于在vue中解析表达式 (表达式 通过某种计算可以返回结果的一个公式) -->
<h1>{{text}}</h1>
<h1>{{num}}</h1>
<h1>{{bool}}</h1>
<h1>{{arr[2]}}</h1>
<h1>{{obj.age}}</h1>
<hr>
<h1>{{updata.toUpperCase()}}</h1>
<h1>{{updata.substr(1,3)}}</h1>
<h1>{{arr.length}}</h1>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
text:"我是字符串",
num:666,
bool:true,
arr:[111,2222,3333,4444],
obj:{
name:"xixi",
age:18
},
updata:"abcdefg"
}
})
</script>
</body>
</html>
声明式渲染与数据驱动
vue中核心是允许我们使用简洁的模板语法进行声明式的数据渲染
声明式渲染:我们告诉程序我想干什么程序就会自动完成
命令式渲染:原生的都是命令时 我们需要告诉程序一步一步应该怎么走 他才会按照我们的指令前进
数据驱动
vue会时时刻刻的监控着模型和视图 当一方发生了改变另外一方也会随之发生改变
总结:
vue的双大括号中不要加双引号
指令
什么式HTML的属性?
就是用来扩展HTML标签的功能
属性的语法?
写在HTML的开标签中 并且属性=“属性值”
vue的指令
指令是带有 v- 前缀的特殊属性 (就是在vue中带有v-前缀的 扩展HTML标签功能的一个技术)
vue指令的语法
写在HTML的开标签中 并且指令=“指令值” 注意:一个开始标签内可写入多个指令,多个指令间使用空格分隔
v-model(双向绑定)
v-model 指令
作用:主要是用于表单上数据的双向绑定
语法:v-model = 变量
注:v-model 指令必须绑定在表单元素上
Vue框架核心的功能就是双向的数据绑定。 双向是指:HTML标签数据 绑定到 Vue对象,另外反方向数据也是绑定的
使用 v-model 指令来实现双向数据绑定 把视图数据与模型数据相互绑定
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>v-model指令</h1>
<!-- 可以关联data数据与表单元素 -->
<!-- 双向绑定----》数据在视图中改变了vm层就会发现 他会主动修改模型层的数据
模型数据改变了 视图也反之发生改变 -->
<input type="text" v-model="inputval"/>
<h1>{{inputval}}</h1>
<hr>
<!-- 复选框也是表单元素 所以也可以使用v-model 但是会把数据在true false中切换 -->
<input type="checkbox" v-model="bool"/>
<h1>{{bool?"你勾选了":"你没有勾选"}}</h1>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
inputval:"",
bool:true
}
})
</script>
</body>
</html>
双向绑定的原理
vue双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。
数据劫持
概念:数据拦截 当我们设置或访问对像的属性的时候,都会触发Object.defineProperty()函数来拦截(劫持),然后在返回(get)或设置(set)对象的属性的值。并且当数据发生改变的时候做出反应。
发布者-订阅者模式:其定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
v-show
v-show 指令
作用:控制切换一个元素的显示和隐藏
语法:v-show = 表达式
根据表达式结果的真假,确定是否显示当前元素
true表示显示该元素;false(默认)表示隐藏该元素
元素一直存在只是被动态设置了display:none
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>v-show</h1>
<input type="checkbox" v-model="bool"/>{{bool?"显示":"取消"}}
<p v-show="bool">我要显示和隐藏</p>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
bool:true
}
})
</script>
</body>
</html>
v-on
v-on 指令
作用:为 HTML 元素绑定事件监听
语法:v-on:事件名称=‘函数名称()’
简写语法:@事件名称=‘函数名称()’
注:函数定义在 methods 配置项中
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>v-on</h1>
<button v-on:click="fun()">点我打印内容</button>
<button @click="fun()">点我简写打印内容</button>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
},
// 函数写在与data el同及位置使用methods包裹
methods:{
fun(){
console.warn("你好么么哒")
}
}
})
</script>
</body>
</html>
v-for
v-for 指令
作用:遍历 data 中的数据,并在页面进行数据展示
语法:v-for = ‘(item, index) in arr’
item 表示每次遍历得到的元素
index 表示item的索引,可选参数
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>v-for</h1>
<ul>
<li v-for="(v,i) in arr">{{v.name}}------{{v.age}}</li>
</ul>
<table border="1">
<tr v-for="(xiaoming,xiaohong) in arr">
<td>{{xiaohong}}</td>
<td>{{xiaoming.name}}</td>
<td>{{xiaoming.age}}</td>
</tr>
</table>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
arr:[
{name:"xixi1",age:118},
{name:"xixi2",age:218},
{name:"xixi3",age:183},
{name:"xixi4",age:184},
{name:"xixi5",age:185},
{name:"xixi6",age:186}
]
}
})
</script>
</body>
</html>
v-bind
v-bind 指令
作用:给html的属性绑定变量
语法:v-bind:属性名 = ‘表达式’/ 简写 :属性名=‘表达式’
绑定一个属性:
绑定多个属性(不能使用简写):
<img v-bind=‘{src:myUrl, title: msg}’ />
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demoDiv">
<h1>v-bind</h1>
<a v-bind:href="ahref">{{text}}</a>
<a :href="ahref">11{{text}}11</a>
</div>
<script>
new Vue({
el:"#demoDiv",
data:{
text:"点我去百度",
ahref:"http://www.baidu.com"
}
})
</script>
</body>
</html>
第二天
v-if 全家桶
v-if v-else v-esle-if
v-f指令
作用:判断是否加载固定的内容
语法:v-f=表达式
根据表达式结果的真假,确定是否显示当前元素
true表示加载元素,false表示不加载
元素的显示和隐藏,是对dom元素的显示和隐藏
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<h1>v-if</h1>
<input type="checkbox" v-model="bool"/>
<p v-show="bool">我是v-show的元素</p>
<p v-if="bool">我是v-if的元素</p>
</div>
<script>
new Vue({
el:"#demodiv",
data:{
bool:true
}
})
</script>
</body>
</html>
v-if与v-show的区别
首先两者在用户的角度效果一样 都是对页面进行显示和隐藏 接受布尔数值 true显示,false隐藏
但是,v-if是对元素进行删除和添加进行的显示和隐藏,而v-show是对css进行的
应用场景
v-show 在初始化的时候对电脑的消耗比较大(对安全性比较低内容进行显示和隐藏)
v-if 在切换的时候对电脑的消耗比较大(对安全行比较高的内容进行显示和隐藏)
v-else
必须配合v-if进行使用,v-if条件不成立的时候执行
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<h1>v-else</h1>
<h1 v-if="bool">我是if的内容</h1>
<p>v-if与v-else之间不能有第三者</p>
<h1 v-else>我是else的内容</h1>
</div>
<script>
new Vue({
el:"#demodiv",
data:{
bool:false
}
})
</script>
</body>
</html>+
v-else-if
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<h1>v-else-if</h1>
<select v-model="text">
<option value="吃饭">吃饭</option>
<option value="睡觉">睡觉</option>
<option value="在吃">在吃</option>
<option value="在睡">在睡</option>
<option value="上厕所">上厕所</option>
</select>
<p v-if="text=='吃饭'">用户选择了吃饭饭</p>
<p v-else-if="text=='睡觉'">用户选择了睡觉觉</p>
<p v-else-if="text=='在吃'">用户选择了在吃饭饭</p>
<p v-else-if="text=='在睡'">用户选择了在睡觉觉</p>
<p v-else-if="text=='上厕所'">用户选择了上厕所</p>
<p v-else>用户什么都没有选择</p>
</div>
<script>
new Vue({
el:"#demodiv",
data:{
text:""
}
})
</script>
</body>
</html>
v-text指令
作用:操作网页元素中的纯文本内容,{{}}是他的另外一种写法,就是向网页中插入纯文本内容。
v-text与{{}}等价,{{}}叫模板插值,v-text叫指令
有一点区别就是,在渲染的数据比较多的时候,可能会把大括号显示出来,俗称屏幕闪动
屏幕闪动
①使用v-text渲染数据
②使用{{}}语法渲染数据,但是同时使用v-cloak指令(用来保持在元素上直到关联实例结束时候进行编译),v-cloak要放在什么位置呢,v-cloak并不需要添加到每个标签,只要在el挂载的标签上添加就可以
v-html与v-once
v-html 指令
作用:双大括号会将数据解释为纯文本,而非 HTML 。为了输出真正的 HTML ,你需要使用 v-html 指令
语法:<p v-html=“text”》 《/p》
v-once 指令
作用:当数据改变时,插值处的内容不会更新(会影响到该节点上的所有属性)
语法:p> v-once>{{text}}</p》
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<h1>v-html</h1>
{{newhtml}}
<div v-html="newhtml"></div>
<h1>v-once 一次性插值</h1>
<input type="text" v-model="text">
<p>{{text}}</p>
<p v-once>{{text}}</p>
<p>{{text}}</p>
<p>{{text}}</p>
</div>
<script>
new Vue({
el:"#demodiv",
data:{
newhtml:"<h1>我是字符串标签</h1>",
text:"我是默认值"
}
})
</script>
</body>
</html>
三
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="node_modules/vue/dist/vue.min.js"></script>
<style>
.red{
background-color: red;
}
</style>
</head>
<body>
<div id="demodiv">
<h1>任务列表</h1>
<p>任务总数:{{arr.length}};还有:{{sheng()}} <button @click="del()">完成</button></p>
<ul v-if="arr.length!=0">
<li v-for="(v,i) in arr">
<input type="checkbox" v-model="v.style"/>
<span :class="v.style?'red':''" v-if="!v.edit" @click="edit(i)">{{v.title}}</span>
<input type="text" v-model="v.title" v-else @blur="edit(i)">
</li>
</ul>
<p v-else>暂无数据^_^!</p>
<input type="text" v-model="inputval"><button @click="add()" :disabled="btnbool">添加</button>
</div>
<!-- 1. 我们写完了页面 -->
<!--
2.ul的展示区域是动态生成地数据
创建模拟数据
v-for循环
-->
<!--
3.添加
(1)先要给添加按钮加点击事件调用函数
(2)在添加的函数中得到输入框的值
(3)把输入框的值push到展示数组中
(4)把输入框清空
-->
<!--
4.任务总数
{{表达式}}
-->
<!--
5勾选变色
(1)你够选的时候你要是到勾选的是谁
(2)电脑傻傻的 他需要有一个状态来保存你勾的是哪一项
(3)v-model绑定状态
(4)使用三元运算符来动态添加或减少类名
(5)添加样式
-->
<!-- 6勾选删除
(1)创建一个新的变量
(2)把原始展示的数据赋值给这个心的变量
(3)把原始数据清空
(4)便利这个新数据 在其中判断style==false
(5)把等于false的数据插入原始数据中
-->
<!-- 7.还剩数量
本质就是没有勾选的数量
(1)便利原始数据 然后判断是否勾选了
(2)如果没有勾选
(3)我让一个计数器++
(4)return 这个计数器
-->
<!-- 8.修改
1.在页面中在添加一个输入框 并且双向绑定展示数据
2.在创建一个新的状态 用来管理具体显示输入框还是展示内容
3.点击文字改变这个状态让输入框和展示文字切换显示
4.反之失去焦点事件让输入框隐藏
-->
<script>
new Vue({
el:"#demodiv",
data:{
arr:[
{title:"EZ",style:false,edit:false},
{title:"VN",style:false,edit:false},
{title:"MF",style:false,edit:false},
{title:"NOC",style:false,edit:false}
],
inputval:"",
btnbool:true,
},
methods:{
add(){
console.log(this.inputval)
this.arr.push({title:this.inputval,style:false,edit:false})
this.inputval=""
},
del(){
// (1)创建一个新的变量
// (2)把原始展示的数据赋值给这个心的变量
// (3)把原始数据清空
// (4)便利这个新数据 在其中判断style==false
// (5)把等于false的数据插入原始数据中
let newarr=[];
newarr=this.arr;
this.arr=[]
newarr.forEach(v => {
if(v.style==false){
this.arr.push(v)
}
});
},
sheng(){
let i=0
this.arr.forEach(v=>{
if(v.style==false){
i++
}
})
return i
},
edit(i){
if(this.arr[i].style){
return
}
this.arr[i].edit=!this.arr[i].edit
}
},
watch:{
inputval(news,olds){
if(news!==""){
this.btnbool=false;
}else{
this.btnbool=true;
}
}
}
})
</script>
</body>
</html>
修改内容
<!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="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<ul>
<li v-for="(v,i) in arr">
<span v-if="!v.edit" @click="edit(i)">{{v.title}}</span>
<input type="text" v-model="v.title" v-else @blur="edit(i)"/>
</li>
</ul>
</div>
<script>
new Vue({
el:"#demodiv",
data:{
arr:[
{title:"这是第一个",edit:false},
{title:"这是第二个",edit:true},
{title:"这是第三个",edit:false},
{title:"这是第四个",edit:false},
]
},
methods:{
edit(i){
this.arr[i].edit=!this.arr[i].edit;
}
}
})
</script>
</body>
</html>
侦听/监听 watch
watch是用来监听data模型数据中的内容 当数据发生改变的时候 watch就会触发
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<input type="text" v-model="text">
</div>
<script>
new Vue({
el:"#demodiv",
data:{
text:"我是默认值"
},
// 监听
watch:{
// 你要监听的数据:function(新值,旧值){
// 你要完成的逻辑
// }
text(newval,oldval){
console.log(`${newval}-----${oldval}`)
}
}
})
</script>
</body>
</html>
计算属性
概念:顾名思义,首先它是一种属性,其次它有“计算”这个特殊性质。每次取得它的值得时候,它并不像普通属性那样直接返回结果,而是经过一系列的计算之后再返回结果。同时只要在它的当中里引用了 data 中的某个属性,当这个属性发生变化时,计算属性仿佛可以嗅探到这个变化,并自动重新执行。
计算属性(computed) 是一个属性 所以与el methods等同级位置编写 它的主要作用是计算(计算data的数据 当data属性发生改变的时候他会知道 并且重新计算返回我们想要的结果 一条数据在不同位置展示出不同形态的时候)
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<h1>为什么用计算属性 在模板中写太多的逻辑 会导致后期无法维护</h1>
<p>{{text}}</p>
<p>{{text.toUpperCase()}}</p>
<p>{{text.toUpperCase().substr(1,3)}}</p>
<h1>计算属性</h1>
<p>{{newatext}}</p>
<p>{{newbtext}}</p>
</div>
<script>
new Vue({
el:"#demodiv",
data:{
text:"abcdefghijk"
},
watch:{
},
// 计算属性
computed:{
// 需要返回的数据: function () {
// return 处理操作
// }
newatext(){
// 必须return
return this.text.toUpperCase()
},
newbtext(){
return this.text.toUpperCase().substr(1,4)
}
}
})
</script>
</body>
</html>
计算属性(computed)与methods(方法)的区别
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<p>{{newtext}}</p>
<p>{{newtext}}</p>
<p>{{showtext()}}</p>
<p>{{showtext()}}</p>
大家会发现两个的结果没有区别
</div>
<script>
new Vue({
el:"#demodiv",
data:{
text:"abcdefg"
},
watch:{
},
methods:{
showtext(){
return this.text.toUpperCase()
}
},
computed:{
newtext(){
return this.text.toUpperCase()
}
}
})
</script>
</body>
</html>
如果我们把计算属性调用多次 computed 只执行了一次
但是我们把方法调用多次 methods 调用几次执行几次 有很大的资源浪费
区别:
computed计算属性页面中调用函数的时候不需要加(),methods需要加
计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。
方法绑定数据只要被调用,方法将总会再次执行函数。
计算属性相对于方法在处理特定场合下节省资源性能
计算属性与侦听器的区别:
当watch监听的值发生改变就会被调用,watch可以在数据变化时做一些异步处理或者开销大的操作
计算属性是计算依赖的值,当依赖的值发生改变才会触发。
事件对象
事件对象就是用于描述触发这个事件的元素信息
$event
修饰符
v-on指令提供了事件修饰符来处理DOM事件细节
按键修饰符
按键修饰符: .up, .down, .ctrl, .enter, .space等等
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<!-- @事件.修饰符="方法()" -->
<input type="text" @keydown.ctrl="fun()">
</div>
<script>
new Vue({
el:"#demodiv",
data:{
},
methods:{
fun(){
console.log("aaaaaaaaaa")
}
},
watch:{
}
})
</script>
</body>
</html>
事件修饰符
【扩展】事件修饰符
prevent修饰符:阻止事件的默认行为(submit提交表单)
stop修饰符:阻止事件冒泡
capture修饰符:与事件冒泡的方向相反,事件捕获由外到内
self:只会触发自己范围内的事件,不包含子元素
once:只会触发一次
注意:修饰符可以串联使用
axios
初体验
axios 是一个第三方的数据请求库 使用promise对XHR对象进行了封装 是现今最主流的一种数据请求方式
使用先下载
npm install --save axios
<!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="node_modules/vue/dist/vue.min.js"></script>
<script src="node_modules/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="demodiv">
<button @click="fun()">点我发送请求</button>
<ul>
<li v-for="(v,i) in arr">{{v.commentTxt}}</li>
</ul>
</div>
<script>
new Vue({
el:"#demodiv",
data:{
arr:[]
},
methods:{
fun(){
axios({
url:"http://api.artgoer.cn:8084/artgoer/api/v1/user/324380/v3/topic/topicHomeByLabel?pageIndex=1&token=b544cd63-6d42-46fe-a96c-3cf96bae3113&topicId=62187",
method:"GET"
}).then((ok)=>{
console.log(ok.data.data.commentList)
this.arr=ok.data.data.commentList
}).catch((err)=>{
console.log(err)
})
}
},
watch:{
}
})
</script>
</body>
</html>
四
交互
vue数据请求 Vue-resource、Axios、fetch三种方式。Vue-resource是Vue官方提供的插件,axios是第三方插件,fetch es6原生
Vue-resource在vue2.0的时候已经停止更新了
axios是对xhr使用promies进行了二次封装,简化了语法。
fetch是es原生(react中会带大家写)
axios封装
<!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="node_modules/vue/dist/vue.min.js"></script>
<script src="node_modules/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="demodiv">
<div v-if="arr.length!=0">
<h1 v-for="(v,i) in arr">{{v.commentTxt}}</h1>
</div>
<img src="./1.gif" v-else/>
</div>
<script>
// 页面加载完毕后发送一个数据请求
new Vue({
el:"#demodiv",
data:{
arr:[]
},
mounted(){
this.axioslink("http://api.artgoer.cn:8084/artgoer/api/v1/user/324380/v3/topic/topicHomeByLabel?pageIndex=1&token=b544cd63-6d42-46fe-a96c-3cf96bae3113&topicId=62187").then((ok)=>{
console.log(ok)
this.arr=ok.data.data.commentList
}).catch((err)=>{
console.log(err)
})
},
methods:{
axioslink(url){
return new Promise((resolve,reject)=>{
// 异步操作
axios({
url,
method:"GET"
}).then((ok)=>{
resolve(ok)
}).catch((err)=>{
reject(err)
})
})
}
}
})
</script>
</body>
</html>
生命周期与钩子函数
生命周期:就是钩子函数从创建到销毁的过程
钩子函数:本质是一个函数,但他是一个会被自动调用的函数
生命周期的钩子函数:
就是vue从创建到销毁的过程中自动调用的函数(可以帮助我们在vue中完成一些特定条件下的自动执行的业务)
生命周期的钩子
创建实例:beforeCreate;
创建完成:Created;
开始创建模板:boforeMount;
模板创建完成:mounted;
开始更新:beforeUpdate;
更新完成:Updated
开始销毁:beforeDestroy;
销毁完成:destroy;
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
<h1>{{text}}</h1>
<button @click="text='我被改了'">点我更新</button>
</div>
<script>
new Vue({
el:"#demodiv",
data:{
text:"我是一个字符串"
},
methods:{
},
watch:{
},
computed:{
},
// 生命周期
// **beforeCreate(创建实例)**
// **created(创建完成)**
// **beforeMount(开始创建模板)**
// **mounted(创建完成)**
// **beforeUpdate(开始更新)**
// **updated(更新完成)**
// **beforeDestroy(开始销毁)**
// **destroyed(销毁完成)**
beforeCreate(){
console.log("创建实例")
},
created(){
console.log("实例创建完成")
},
beforeMount(){
console.log("开始创建模板")
},
mounted(){
console.log("创建完成")
},
beforeUpdate(){
console.log("开始更新")
},
updated(){
console.log("更新完成")
},
beforeDestroy(){
console.log("开始销毁")
},
destroyed(){
console.log("销毁完成")
},
})
</script>
</body>
</html>
自定义过滤器
过滤器的作用:在不改变数据的情况下,输出前端需要的格式数据
2.0中已经废弃了内置过滤器,需要我们自定义过滤器来使用filter
全局过滤器
全局过滤器在定义的内容在页面的任何实例中都能使用
<!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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
{{text|xiaoming}}
</div>
<div id="demodivb">
{{text|xiaoming}}
</div>
<script>
// 如果数据在页面展示的时候和我们的续期想法不一样我们可以使用过滤器格式化展示数据
// 全局过滤器定义在实例之前
Vue.filter("xiaoming",(val)=>{
// 大于5个就保留5位 并且在后面加...
if(val.length>=5){
return val.substr(0,5)+"..."
}else{
return val
}
})
new Vue({
el:"#demodiv",
data:{
text:"我是第一个内容哦么么哒"
}
})
// =====================
new Vue({
el:"#demodivb",
data:{
text:"呵呵哒呵呵哒"
}
})
</script>
</body>
</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="node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="demodiv">
{{text|xiaohone}}
</div>
<div id="demodivb">
<!-- 因为定义的是局部过滤器 所以不能使用 -->
{{text|xiaohone}}
</div>
<script>
// 局部过滤器 写在指定实例当中 与data等同级
new Vue({
el:"#demodiv",
data:{
text:"西游记"
},
filters:{
xiaohone(val){
return "<<"+val+">>"
}
}
})
new Vue({
el:"#demodivb",
data:{
text:"红楼梦"
}
})
</script>
</body>
</html>
总结:在没有冲突的前提下,过滤器可以串联
项目环境配置 vue脚手架
前期准备
电脑上必须要有node
安装淘宝镜像 cnpm (非必装,网络慢的情况可安装)
npm install -g cnpm --registry=https://registry.npm.taobao.org
使用
1 全局安装vue-cli
npm install -g @vue/cli (除了你电脑重装系统或者是重装了node以外 就不用在安装了)
2.查看版本 vue --version
3.开始创建项目 但是需要先把你的cmd路径切换到你想创建项目的文件夹下
vue create 项目名
在弹出的选择中 选择第三项 剩下的眼睛闭上一路回车
4 cmd到项目名下
5 npm run serve启动.
单文件组件
以.vue结尾的组件就是单文件组件
在一个.vue文件中分为三部分 template是写html script 是写js的 style是写css的(执行地址:http://localhost:8080/)
我们之前学的 data methods watch computed filters 以及8个钩子 除了data的语法变了 剩下没有任何改变
五
空脚手创建以后步骤:
因为空脚手架中有很多与我们开发没有关系的内容 所以拿到脚手架在写我们的代码之前 需要删除一些默认的内容
1.删除components文件夹中的HelloWord.vue
2.进入到app.vue(是所有组件的老大所有的组件今后要显示都要关联到app.vue中 写在app中的内容在所有地方都会显示)
保留如下内容:
<template>
<div id="app">
</div>
</template>
<script>
export default {
name: 'App',
components: {
}
}
</script>
<style>
</style>
拓展小概念
组件:用来封装可以复用的ui代码块
模块:用来封装可以复用的js代码块
组件本质
组件的本质是自定义标签
作用
1,提高开发效率
2,降低测试难度
分类
全局组件
在 main.js中引用,并且使用component使用,然后可以在全局范围内直接使用,无需引用调用,直接使用即可
//全局组件只需要在main.js中引用一次即可在当前项目的全局范围内直接使用
import Qj from './components/qj.vue'
//全局组件使用component来表示
//vue.component("组件名",对应的组件)
Vue.component("Qj",Qj)//
局部组件
创建
在components文件夹下创建以 .vue结尾的文件
快捷键: v> 回车
<template>
<div>
我是一个组件
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
3.要把我们的组件和app建立关系
(1)引用
// 1.引用组件
// import 是给引用进来的内容起个名字 首字母大写 from "你引用的路径"
import Home from "./components/home.vue"
export default {
name: 'App',
components: {
}
}
(2)调用
局部组件使用components来进行表示 写在与el data methods等同级的位置
// 1.引用组件
// import 是给引用进来的内容起个名字 首字母大写 from "你引用的路径"
import Home from "./components/home.vue"
export default {
name: 'App',
components: {
// 2.调用
// 组件名:你引用的组件
Home
}
}
(3)使用
<template>
<div id="app">
<!-- 使用 -->
<Home/>
</div>
</template>
<script>
// 1.引用组件
// import 是给引用进来的内容起个名字 首字母大写 from "你引用的路径"
import Home from "./components/home.vue"
export default {
name: 'App',
components: {
// 2.调用
// 组件名:你引用的组件
Home
}
}
</script>
<style>
</style>
组件中的属性使用
data,methods,watch,computed,filters,八个钩子,组件中的属性(后期还会有)
data的使用
<template>
<div>
{{text}}
</div>
</template>
<script>
export default {
data(){
return {
text:"你好么么哒"
}
}
}
</script>
<style>
</style>
组件的样式设置
在组件的style范围内写样式
但是默认默认情况下组件之间的样式会相互影响,scoped可解决这个问题,在style后面加入scoped表示仅对当前组件有效,如下:
```html
<style scoped>
/* scoped当前样式仅对当前组件生效 */
div{
color:red;
}
</style>
父子组件
vue的组件之间的嵌套关系,使用这中嵌套关系可用页面内容的拼装(方便),减少组件的体积
父子组件的作用域
注意:父组件不能直接使用子组件的数据,子组件也不能直接使用父组件的数据
vue组件与组件之间是一个独立的、完整的个体,他们之间的数据默认情况下是不能够相互使用的
父子组件的相互传值
正向传值
父组件把数据给子组件
props: vue的一个属性,写在methods,data等同级位置
作用:接受组件外部传递进来的数据
props:[“接收参数1”,“接收参数2”,…“接收参数n”]
使用
1.在子组件中设置props的接收参数
export default {
props:["xiaoming"],//设置接收参数
data(){
return {
zitext:"我是子组件的数据"
}
}
}
2.在页面内想使用的地方把props的数据插入页面
<template>
<div>
<!-- 使用props的小明数据 -->
zizizizizizzizizizi----{{xiaoming}}
</div>
</template>
<script>
export default {
props:["xiaoming"],//设置接收参数
data(){
return {
zitext:"我是子组件的数据"
}
}
}
</script>
<style>
</style>
3.父组件开始传递
在子组件被调用的地方进行传递
<!-- 传递数据给子组件 -->
<Zi :xiaoming="futext"/>
总结下基本props的使用
props就是标签的属性
逆向传值
子组件把数据给父组件
六
props验证
就是父组件传过来的数据进行校验,(数据类型,number,buer,string,Array等,是否为空,默认值 )
语法 原来的props是在props后面对应一个数组
但是props验证是对应一个对象
props:{
你的props变量:数据类型
}
<template>
<div class="item">
<!-- 注意注意 图片路径 是相对于public下的index.html的 -->
<img :src="imgurl">
<br>
<span>{{title+666}}</span>
</div>
</template>
<script>
export default {
// props:["imgurl","title"]
// props验证
props:{
imgurl:String,
title:Number
}
}
</script>
更多的验证
验证不能为空
<script>
export default {
// props:["imgurl","title"]
// props验证
// props:{
// imgurl:String,
// title:Number
// }
// props验证不能为空
props:{
imgurl:String,
title:{
type:Number,//如果验验证多个条件 那么需要用type来设置验证的数据类型
required:true//(表示传入的数据不能为空)
}
}
}
</script>
默认值
// props验证默认值
props:{
imgurl:String,
title:{
type:String,//如果验验证多个条件 那么需要用type来设置验证的数据类型
default:"我是默认值"
}
}
}
为什么有的时候写的有问题 但是props验证没有错误提示?
生产版本也就是压缩版的文件删除了警告,所以使用非压缩版的js文件就可以看到错误
逆向传值
子组件把数据给父组件,但是默认是不被允许的,必须通过自定义事件来传递
使用
子组件中:
1,在页面中创建一个事件,调用一个函数
2,在函数中使用this.$emit进行自定义事件的创建
<template>
<div>
<!-- 逆向传值默认是不允许的 必须要有事件触发才能传值 -->
<button @click="fun()">点我进行逆向传值</button>
</div>
</template>
<script>
export default {
data(){
return {
num:"我是子组件的数据么么哒!!"
}
},
methods:{
fun(){
// 千万不要把$emit记成逆向传值了 他是自定以事件
// this.$emit("自定义事件的名字","你传递的数据")
this.$emit("xiaoming",this.num)
}
}
}
</script>
$emit
// 千万不要把 e m i t 记 成 逆 向 传 值 了 , 他 是 自 定 义 事 件 / / t h i s . emit记成逆向传值了,他是自定义事件 // this. emit记成逆向传值了,他是自定义事件//this.emit(“自定义事件的名字”,“你传递的数据”)
父组件中
1,在使用子组件的地方,使用@/v-on:自定义事件名=“当前父组件的函数”(不要圆括号)
2,创建的 父组件的函数的形参就是子组件传递过来的数据
<template>
<div>
<h1>逆向传值</h1>
<!-- 函数不加()函数不加()函数不加() -->
<Bzi @xiaoming="fun"/>
</div>
</template>
<script>
import Bzi from "./bzi.vue"
export default {
methods:{
// val就是子组件传递的数据
fun(val){
console.log(val)
}
},
components:{
Bzi
}
}
</script>
同胞传值
见15
路径别名
内置别名:@
@代表src文件,无论在那个层级里面写@,都代表src文件夹
但是呢不够用比如我们要找到components这个类路径怎么办?
配置:
在项目的根路径下 在项目的根路径下在项目的根路径下
创建一个vue.config.js(名字不能变)的文件
在其中使用 module.exports={}进行暴漏
module.exports={
configureWebpack:{
resolve:{
alias:{
// "别名":"对应的文件夹"
"com":"@/components"
}
}
}
}
上述代码代表将@/components的文件名改为com,其它的语法是固定的,照抄就行,改文件名的时候只需改 “com”:"@/components"即可(代表将后面的文件名改为前面的),改完以后必须重启项目
slot
插槽/槽口
引子:
如果把组件的调用写成双标签,并且在开标前和关标签中写入内容 会显示吗?
不会
slot使用
1.基本使用 就是你在想接受外部插入的内容组件之上放置一个slot组件
<template>
<div>
<h1>测试slot</h1>
<!-- 就是给组件设置了一个开口 外部内容就可以插入 -->
<slot></slot>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
问题:
刚才直插入了一个 多个呢?
可以插入的
但是一个开口插入多个内容 后期如何管理?
具名槽口
带有名字的槽口,slot
在定义slot的时候 使用name属性起个名字,<slot name=“XXX”》</slot》
插入的时候 给元素使用slot属性=“slot的名字”,不能在使用的组件名中直接加slot属性,需要加在里面插入的标签中,如下
<Zi>
<h1 slot="key">插入slot</h1>
</Zi>
作用:(使用场景)
很多使用slot和props有相似点
props在 一个组件被多次调用 但是内容不同数量相同的时候使用
slot 在 一个组件被多次调用 但是内容不同数量也不相同的时候使用
七
路由
概念:可以搭建spa(单页面应用)的一个技术
作用:
1,可以进行先项目页面的切换
2,根据url的不同来切换不同的组件页面以达到用户观感上的页面切换
特点与原理
利用锚点进行切换
页面不会刷新
路由创建
脚手架自动创建
流程:就是在vue create 新建项目的时候 选中Router项即可
与之前默认的脚手架项目区别
在src文件夹下多了一个router和views的文件夹
router:就是配置路由的文件夹
views:写路由页面的文件夹
创建新页面的过程
1,在views下创建你新建的路由页面组件
2,配置路由在router下的index.js中先引用再配置
import Vue from 'vue'
import VueRouter from 'vue-router'
//1.引用 你要用的所有路由页面必须先引用
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import Shop from '../views/shop.vue'
Vue.use(VueRouter)
//2配置路由规则
const routes = [
{
path: '/', //url匹配的路径
name: 'Home', //当前这个规则的命名空间(给当前这个路由起个名字)
component: Home //当匹配到的时候我要显示的组件页面是什么
},
{
path: '/about',
name: 'About',
component: About
},
{
path: '/shop',
name: 'Shop',
component: Shop
},
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
3,需要创建路由的出口(自动创建时会自动添加)
路由出口就是我们最终要显示在哪里 <router-view》
4,设置路由导航:
传统的跳转连接我们使用a标签
但是但是但是在vue中千万不要使用a标签哦!!!!!!!
routerlink:vue中进行路由页面的跳转链接
<router-link to="/你要去的url地址"》</router-link》
手工创建
1.下载vue-router库 : npm install --save vue-router
2,创建路由组件页面,在views文件夹下创建.vue文件
3,路由配置规则:
在router文件夹下先引用在配置
4,实例化路由对象并传入路由规则
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
5,把路由实例在main.js中传入vue实例中建立关系
6,设置出口:router-view
7,设置导航 router-link
路由导航
切换路由的方式(页面跳转)
原生html+css+js如何进行页面切换?
1.html方式:a标签
2.js方式:href 、replace()、 go(正数负数) 、forword()、 back()
路由导航分类
1.标签的方式(声明式导航):router-link 有一个to属性用来表述去哪里
注意:router-link这个申明式导航最终会在浏览器中被编译成a标签,但是我们不能使用a标签
注意:我们再写路由规则时候路径path不要写斜杠(/),否则路由导航中会出现问题
注意:
router-link-active:每次选中之后 会发现在路由导航中会自动添加一个类名,这个类名是干什么的?
此类名用于设置当前选中的元素的样式 我们可以用vue 动态添加的类名,用于当前选中路由样式的设置,如下
<template>
<div id="app">
<router-link to="/home">点我去home</router-link>
<router-link to="/phone">点我去phone</router-link>
<router-link to="/shop">点我去shop</router-link>
<router-link to="/user">点我去user</router-link>
<router-view/>
</div>
</template>
<style>
.router-link-active{
background-color: pink;
}
</style>
2.js的方式(编程式导航)
编程式导航就是使用js的方式进行跳转
方式1:最常用页面跳转
this.$router.push("/去哪里")
<template>
<div id="app">
<button @click="fun('/home')">点我去home</button>
<button @click="fun('/phone')">点我去phone</button>
<button @click="fun('/shop')">点我去shop</button>
<button @click="fun('/user')">点我去user</button>
<router-view/>
</div>
</template>
<script>
export default {
methods:{
fun(url){
// 编程式导航
this.$router.push(url)
}
}
}
</script>
方式2:替换当前页面(退不回去)
this.$router.replace("/要替换的页面")
<template>
<div id="app">
<!-- replace方式 -->
<button @click="funb('/home')">点我去home</button>
<button @click="funb('/phone')">点我去phone</button>
<button @click="funb('/shop')">点我去shop</button>
<button @click="funb('/user')">点我去user</button>
<router-view/>
</div>
</template>
<script>
export default {
methods:{
funb(url){
// 替换
this.$router.replace(url)
}
}
}
</script>
方式3:this.$router.go(n)这个方法的参数是一个整数,意思实在history 记录中向前或者后退多少步,类似 window.history.go(n),了解一下
push与replace跳转的区别
都可以切换页面 但是但是唯一的不同就是replace不能绘回退(因为replace是替换掉了当前的url)
路径通配符
就是使用*号在path中可以匹配所有路径 通常是完成404页面的创建
重定向路由器
通过router中的 redirect 属性完成配置
// 重定向
{
path:"/",//当路径为空的时候
redirect:"/shop"//把当前的路径切换成/shop(就是默认显示的页面)
},
404页面的构建
1,首先创建建一个404页面
2,在路由的中配置如下:
注意:由于路由配置的优先级是从上而下执行,谁先定义谁的优先级最高,所以404页面要放在路由配置的最下方,当项目中的所有页面都匹配不到的时候,就会跳转到404页面
{
path: '/*',//路径用星号,表示通配符选择器,所有的都能匹配到
name: 'No',
component: () => import( '../views/no.vue')//
},
路由规则的优先级
同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。
路由模式
hash模式中url永远带着#号,开发当中默认使用这个模式
history模式url里面没有#号
在开发app的时候有分享页面,这个分享出去的页面就是用vue做的,把这个页面分享到第三方的app里,有的app里面url是不允许带有#号的,所以要将#号去除那么就要使用history模式
上线之后:做刷新操作,会出现404错误,那么就需要和后端人配合让他配置一下apache或是nginx的url重定向,重定向到你的首页路由上。
路由模式的切换
在index.js中找到vueRouter实例,其中设置mode属性
const router = new VueRouter({
mode: 'history',//设置路由模式的
base: process.env.BASE_URL,
routes
})
路由拓展
meta
1,meta路由记录路由元信息,给每个路由添加一个自定义的meta对象,在meta对象中可以设置一些状态,来进行一些操作,供页面组件或者路由钩子函数中使用。
{
path: '/clone',
name: 'Clone',
component: () => import('../views/clone.vue'),
children:[
{
path: 'era',
name: 'Era',
component: () => import('../views/era.vue'),
meta:{title:"子元素的内容"},
to:"/clone/era",//动态路径
},
{
path: '/erb',
name: 'Erb',
component: () => import('../views/erb.vue'),
meta:{title:"子元素的erbbbbb"},
to:"/erb",
},
{
path: '/erc',
name: 'Erc',
component: () => import('../views/erc.vue'),
meta:{title:"子元素的erccccccc"},
to:"/erc",
},
]
},
页面中遍历,注意,routes[3]代表第三个二级路由,下标为几就代表第几个路由,因为this.$router.options.routes可以获取所有的路由信息,clone刚好是第三个路由,所以遍历的是它下面的children,即子路由
<router-link :to="v.to" v-for="(v,i) in this.$router.options.routes[3].children" :key="i">
<span>
{{v.meta.title}}
</span>
</router-link>
<router-view>
</router-view>
获取当前路由上的meta
this.$route.meta.xxxx
<p》图片上传—{{this.¥route.meta}}<p》
获取所有路由的路由信息
this.$router.options.routes
根据下标查找元素
<!-- 根据下标查找元素,因为获取到的元素是一个数组,
所以可以利用数组下标的特性进行查找对应的元素 -->
<!-- <h2>{{this.$router.options.routes[3]}}</h2> -->
组件的二次封装
八
为什么vue组件中的data是一个函数?
vue data在创建或注册模板的时候,传入一个data属性作为用来绑定的数据。但是在组件中,data必须是一个函数,而不能直接把一个对象赋值给它。组件中的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。
嵌套路由/多级路由/二级三级
二级以及以上的路由语法相同 都是使用children属性在路由规则中进行配置
创建
1,创建多级路由组件
2,配置二级路由的路由规则
配置二级路由的规则:首先得知道是谁的子路由,(就在谁的路由规则中写children配置规则)
坑1 子路由不加/(router-link=“to/一级路由/二级路由”)
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/home.vue'
import Phone from '../views/phone.vue'
import Shop from '../views/shop.vue'
import User from '../views/user.vue'
// 二级路由
import Era from '../views/er/era.vue'
import Erc from '../views/er/erc.vue'
import Erd from '../views/er/erd.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/home',
name: 'Home',
component: Home
},
{
path: '/phone',
name: 'Phone',
component: Phone
},
{
path: '/shop',
name: 'Shop',
component: Shop
},
{
path: '/user',
name: 'User',
component: User,
// 配置二级路由
// 坑1:在写二级路由的path时候先不加/
// 坑1:在写二级路由的path时候先不加/
// 坑1:在写二级路由的path时候先不加/
// 坑1:在写二级路由的path时候先不加/
children:[
{
path: 'era',
name: 'Era',
component: Era
},
{
path: 'erc',
name: 'Erc',
component: Erc
},
{
path: 'erd',
name: 'Erd',
component: Erd
},
]
},
{
path:"/",
redirect:"/home"
}
]
const router = new VueRouter({
mode: 'history',//设置路由模式的
base: process.env.BASE_URL,
routes
})
export default router
坑2 子路由加/(router-link=“to/二级路由”)
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/home.vue'
import Phone from '../views/phone.vue'
import Shop from '../views/shop.vue'
import User from '../views/user.vue'
// 二级路由
import Era from '../views/er/era.vue'
import Erc from '../views/er/erc.vue'
import Erd from '../views/er/erd.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/home',
name: 'Home',
component: Home
},
{
path: '/phone',
name: 'Phone',
component: Phone
},
{
path: '/shop',
name: 'Shop',
component: Shop
},
{
path: '/user',
name: 'User',
component: User,
// 配置二级路由
// 坑2:在写二级路由的path时候加/
// 坑2:在写二级路由的path时候加/
// 坑2:在写二级路由的path时候加/
// 坑2:在写二级路由的path时候加/
children:[
{
path: '/era',
name: 'Era',
component: Era
},
{
path: '/erc',
name: 'Erc',
component: Erc
},
{
path: '/erd',
name: 'Erd',
component: Erd
},
]
},
{
path:"/",
redirect:"/home"
}
]
const router = new VueRouter({
mode: 'history',//设置路由模式的
base: process.env.BASE_URL,
routes
})
export default router
3,设置二级路由的出口(谁的子路由设置在谁的里面)
默认情况下一级路由的出口被脚手架自动添加了 但是二级路由的出口必须手动添加
使用router-view 写在一级路由中
<template>
<div>
user
<!-- 二级路由出口 -->
<router-view></router-view>
<Bb/>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
4.路由导航
坑1如果刚才创建path的时候子路由(二级路由)没有加/
<!-- 二级路由的导航 -->
<!-- 如果规则的path没有加/ 那么路由导航的路径是/一级路由/二级路由 -->
<router-link to="/user/era">era</router-link>
<router-link to="/user/erc">erc</router-link>
<router-link to="/user/erd">erd</router-link>
坑2如果刚才创建path的时候子路由(二级路由)加了/
<!-- 如果规则的path加/ 那么导航的路径就是 /二级路由 -->
<router-link to="/era">era</router-link>
<router-link to="/erc">erc</router-link>
<router-link to="/erd">erd</router-link>
动态路由匹配 路由传参
分类
params方式
(1)在需要接受参数的路由规则中设置 接收参数:/XXX
{
path: '/shopall/:名字自定义', //设置接受参数
name: 'Shopall',
component: Shopall
},
(2)开始传递
声明式方式传递
<!-- <router-link :to="{name:'你要去的路由的名字',params:{你在规则上绑定的参数1:值,你在规则上绑定的参数n:值}}">params声明式传参</router-link> -->
<router-link :to="{name:'Shopall',params:{xiaoming:'我是传递过去的参数params'}}">params声明式传参</router-link>
(3)接受参数(在接收的路由页面当中写)
this.$route.params.xxx
<!-- 接受传递过来的数据 -->
{{this.$route.params.xiaoming}}
传参时的注意事项:
1,传入常量,如文字等时用单引号括起来
<router-link :to="{name:'All',params:{alls:'传入常量,如文字等时用单引号括起来'}}">
</router-link>
2,
<!-- 传入变量,如图片或遍历出来的数据时,不用单引号,直接如下
<router-link :to="{name:'要去的路由的名字,路由中配置的name是什么就是什么,注意大小写'
,params:{alls:遍历出来的数据,不用单引号}}">
-->
<router-link :to="{name:'All',params:{alls:v.imgs2}}">
<img :src="v.imgs2" alt="">
</router-link>
query方式
query方式不需要在路由规则中配置参数
1,发送数据
编程式方式
<template>
<div>
phone
<button @click="fun()">点我使用query传递参数</button>
<Bb/>
</div>
</template>
<script>
export default {
methods:{
fun(){
// 编程式方式进行参数传递 里面的写法和声明式相同
this.$router.push({name:"Phoneall",query:{xiaohong:"我是query传参哈哈"}})
}
}
}
</script>
<style>
</style>
(2)接收数据
this.$route.query.xxx
query与params区别
1.用法上的:query要用path来引入,params要用name来引入,接收参数都是类似的,分别是this.
r
o
u
t
e
.
q
u
e
r
y
.
n
a
m
e
和
t
h
i
s
.
route.query.name和this.
route.query.name和this.route.params.name。
2.url展示上
query 在url展示上 是key=val params在url展示上是只有val
因为query方式显示可key所以在数据传递上不那么安全 panams相对来说安全些
回退到上一个页面
<h1 @click="fun()"><</h1>
<script>
export default {
methods:{
fun(){
this.$router.go(-1)
}
}
}
</script>
路由守卫/路由卫士/路由钩子/路由验证
就是vue路由在跳转的时候自动触发的一些钩子函数
全局守卫(写在路由配置:router/index.js中)
对所有的路由在切换的时候都生效
全局前置守卫
进入路由之前触发
当一个导航触发时,全局前置守卫在进入组件之前按照创建顺序调用
vue-router 提供的 router-beforeEach((to,from,next)=>{})可以方便地实现全局前置导航守卫
to:即将要进入的目标,路由对象
from:当前导航正要离开的路由
next:下一步执行
// 全局守卫
// to:去哪里
// from:从哪来
// next:下一步
router.beforeEach((to,from,next)=>{
console.log(to)
console.log(from)
if(to.path=="/login"||to.path=="/zhuce"){
next()
}else{
alert("您没有登录请登录后在试一试")
next(false)
}
})
全局后置守卫
进入路由之后触发
当一个导航触发时,全局后置钩子(在进入组件之后)调用。
vue-router 提供的 router.afterEach((to, from) => {})实现全局后置守卫
to:即将要进入的目标 路由对象
from: 当前导航正要离开的路由
路由独享的守卫
只对当前这一个生效
与全局前置守卫相比路由独享守卫只是对当前路由进行单一控制参数和全局前置守卫相同
在路由配置上直接定义 beforeEnter 进行路由独享守卫定义
{
path: '/',
name: 'Home',
component: Home,
// 路由独享
beforeEnter(to,from,next){
alert("当前为vip页面 请您登录后访问")
next("/login")
}
},
九
组织内守卫
组织内守卫是对某一个自定的组件范围在切换路由的时候触发的
进入之前
beforerouteEnter(to,from,next){}来进行进入组件前的钩子
离开的时候
beforeRouteLeave(to,form,next){}来进行离开组件前的钩子
<template>
<div>
user
</div>
</template>
<script>
export default{
// 路由守卫之 组件内离开触发
beforeRouteLeave(to,from,next){
if(confirm("确定离开吗?*_!")){
// 正常离开
next()
}else{
// 不能离开
next(false)
}
}
}
</script>
<style>
</style>
路由懒加载
因为vue-Router是单页面应用(只有一个显示页面 剩下的内容都是根据url在切换)
默认情况 vue在第一次加载的时候会把我们写的所有路由页面全部都加载出来 本来是没有什么太大问题的 但是一旦页面变多 (由于第一次加载的时候需要加载很多页面 所以可能会造成浏览器白屏问题/首屏加载过慢的问题)
解决:使用路由懒加载
使用
必须掌握的方式
import 方式
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
// 懒加载的方式
{
path: '/home',
name: 'home',
component: () => import('../views/home.vue')
},
{
path: '/phone',
name: 'phone',
component: () => import('../views/phone.vue')
},
{
path: '/shop',
name: 'shop',
component: () => import('../views/shop.vue')
},
{
path: '/user',
name: 'user',
component: () => import('../views/user.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
了解的方式—异步组件方式
{
path: '/shop',
name: 'shop',
component: resolve=>(require(["引用的组件路径"],resolve))
},
扩展小知识----脚手架项目怎么启动?
每个项目的启动方式可能都不相同 所以 我们需要会查看项目是使用什么命令启动的
在项目的根路径下 查看package.json文件 找到scripts 节点即可知道怎么启动的
axios与拦截器的封装
基本写法(了解即可)
1.下载axios npm install --save axios
2.要使用先引用 import axios from “axios”
<script>
import axios from "axios"
export default {
mounted(){
axios({
url:"http://api.artgoer.cn:8084/artgoer/api/v1/user/324380/v3/topic/topicHomeByLabel?pageIndex=1&token=b544cd63-6d42-46fe-a96c-3cf96bae3113&topicId=62187",
method:"GET",
}).then((ok)=>{
console.log(ok)
}).catch((err)=>{
console.log(err)
})
}
}
</script>
拦截器
1,请求拦截器
请求拦截器的作用是在请求发送前进行一些操作,例如在每个请求体里加上token,统一做了处理如果以后要改也非常容易。
2,响应式拦截器
响应式拦截器是在接受到响应之后进行一些操作,例如在服务器返回登录状态失效,需要重新登录的时候,跳转到登录页。
创建
1,新建一个util文件夹(存放一些工具库的文件夹)再新建一个api文件夹
2,创建拦截器文件夹并且写入拦截器内容
import axios from "axios"
// 创建axios 赋值给常量service
const service = axios.create();
// 添加请求拦截器(Interceptors)
service.interceptors.request.use(function (config) {
// 发送请求之前做写什么
return config;
}, function (error) {
// 请求错误的时候做些什么
return Promise.reject(error);
});
// 添加响应拦截器
service.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
export default service
3.封装数据请求 (创建api文件夹并封装请求)
// 封装数据请求
// 引用拦截器
import service from "@/util/request.js"
// 2.开始封装请求
export function getlink(url){
return new Promise((resolve,reject)=>{
service.request({
// 原来的axios怎么写 现在就怎么写
url,
method:"GET"
}).then((ok)=>{
resolve(ok)
}).catch((err)=>{
reject(err)
})
})
}
4,在页面中使用封装好的数据请求(那个页面使用在那个页面调用)
<template>
<div>
phone
</div>
</template>
<script>
import {getlink} from "@/api/getapi.js"
export default{
mounted(){
getlink("http://api.artgoer.cn:8084/artgoer/api/v1/user/324380/v3/topic/topicHomeByLabel?pageIndex=1&token=b544cd63-6d42-46fe-a96c-3cf96bae3113&topicId=62187").then((ok)=>{
console.log(ok)
})
}
}
</script>
<style>
</style>
暴漏
注意,.js文件用export暴漏时在引入的时候需要解构,用export defult暴漏时在引入的时候不需要解构
十
拓展知识–自动开启浏览器与端口修改
1,先在根路径下创建vue.config.js,
2,在该文件中写入以下代码即可修改端口
module.exports={
devServer:{
port:8899// 修改端口
}
}
自动开启 浏览器
module.exports={
devServer:{
port:8899,// 修改端口
open:true//自动开启浏览器
}
}
动态组件
让多个组件使用同一个挂载点,并且动态切换,即为动态组件
<template>
<div class="about">
<h1>动态组件</h1>
<!-- 2.使用动态组件 -->
<!-- 4.插入 -->
<button @click="tem='Taba'">teba</button>
<button @click="tem='Tabb'">tebb</button>
<button @click="tem='Tabc'">tebc</button>
<component :is="tem"></component>
</div>
</template>
<script>
// 1.把你要设置动态组件的所有组件引进来啊才能用
import Taba from "@/components/taba.vue"
import Tabb from "@/components/tabb.vue"
import Tabc from "@/components/tabc.vue"
export default {
// 3.创建动态组件使用的变量
data(){
return {
tem:Tabc
}
},
components:{
Taba,Tabb,Tabc
}
}
</script>
keep-alive—解决状态丢失
在上一个demo中我们不停的切换两个标签页的内容时候,会发现在练习我们中选择好的内容,切换路由之后会恢复初始化。
也就是说之前的状态丢失。原因是每次切换路由的时候,Vue 都创建了一个新的 组件实例
使用
如果要使用 就在想保存组件状态的出口位置 使用keep-alice包裹
解决这个问题,我们可以用一个 元素将其路由出口包裹起来。在切换过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性
使用在动态组件之上
<keep-alive>
<component :is="tem"></component>
</keep-alive>
使用在路由上
<keep-alive>
<router-view/>
</keep-alive>
动态组件的属性
比如刚才我们动态组件举例 有三个组件 我使用包裹了动态组件 那么他就会保存所有的三个组件的状态
如果我在三个动态组件中指向保存两个的状态一个不保存怎么办?
include 需要缓存谁
<!-- 指向缓存a和c两个组件 -->
<keep-alive include="Taba,Tabc">
<component :is="tem"></component>
</keep-alive>
exclude 不想缓存谁
注意:如果两个属性都写了,那么exclude的优先级大于include
keep-alive 的钩子函数
这两个生命周期一定是要在使用keep-alive组件之上
activated 类型:func 触发时机:keep-alive组件激活时使用;
deactivated 类型:func 触发时机:keep-alive组件停用时调用;
<template>
<div>
用户基本信息填写
<input type="text">
</div>
</template>
<script>
export default {
// activated 类型:func 触发时机:keep-alive组件激活时使用;
// deactivated 类型:func 触发时机:keep-alive组件停用时调用;
activated(){
console.log("keep-alive组件激活时使用")
},
deactivated(){
console.log("keep-alive组件停用时调用")
}
}
</script>
<style>
</style>
自定义指令
除了内置指令外, 用户自定义的指令
局部指令定义:
directives:{
自定义指令的名字:{
自定义指令钩子函数(el){
操作逻辑
}
}
},
自定义指令钩子函数
bind:绑定指令到元素上,只执行一次
inserted:绑定了指令的元素插入到页面中展示时调用,基本上都是操作这个钩子函数
update:所有组件节点更新时调用
componentUpdated:指令所在组件的节点及其子节点全部更新完成后调用
unbind:解除指令和元素的绑定,只执行一次
<template>
<div>
<h1>自定义指令</h1>
<!-- 怎么使用自定义指令? -->
<!-- 指令的本质是 元素的属性 -->
<input type="text" v-xiaoming>
<h1 v-red>我是测试自定义指定的元素</h1>
</div>
</template>
<script>
export default {
directives:{
// 自定义指令的名字:{
// el绑定到那个元素之上这个el就是谁
// 自定义指令钩子函数(el){
// 操作逻辑
// }
// }
xiaoming:{
inserted(el){
// focus()自定获取焦点
el.focus()
}
},
red:{
inserted(el){
el.style.color="red";
}
}
}
}
</script>
ref
思考vue中能用js的documet.getElementByxxxx进行dom操作吗?
可以使用
mounted(){
console.log(document.getElementsByTagName("p")[0].innerHTML)
}
思考2思考vue中能用jquery进行dom操作吗?
可以使用
1.下载 npm install --save jquery\
2.,引用 import $ from “jquery”
3,.使用
methods:{
fun(){
$("#demoh").toggle()
},
为什么用ref
虽然在vue中 不建议不建议议不建议议不建议议不建议议不建议议不建议 大家使用dom操作
在有些极端情况下 不得不用 所以在vue中我们就是用ref进行vue中的dom操作
使用
ref 就是在vue中给dom元素起个名字
<em ref="demoem">我是测试vue的ref的例子</em>
r e f s 这 个 就 相 当 于 d o m 查 找 t h i s . refs 这个就相当于dom查找 this. refs这个就相当于dom查找this.refs.你给dom上起的ref名字
funb(){
this.$refs.demoem.style.color="pink";
}
ref绑定的位置
ref 绑定到dom元素之上
可以进行页面的dom操作
ref绑定到子组件之上
绑定到组件之上就可以获取到组件的所有属性和方法
十一
vuex (主要用于组件传值)
正向传值 props
逆向传值 $emit()
同胞传值 (见下面)
夸层级/夸组件传值 ---- vuex
传统跨层级传值
见十一天视频课件视频img/3.bmp(图片)
新闻组件使用 联系组件的数据 传统方式进过几个正传和逆传 后期可维护性为0
简便的方式进行类似于跨层级传值
vuex基本
vuex 状态(数据)管理工具
vuex就是一个数据仓库 把所有的组件数据 统一的放在vuex中 那么这样一来 那个组件想使用数据 那么这个组件直接就去vuex中进行读取即可 大大简化了传递数据的复杂性
vuex集中的数据存储,方便程序中的所有组件进行访问
创建vuex
自动创建方式
创建vue脚手架的时候选中vuex即可
手动创建(记住)
1.下载vuex npm install --save vuex
2.创建文件夹与文件
3.开始创建store(变量存储的就是vuex实例)
import Vue from 'vue'//引用vue
import Vuex from 'vuex' //引用vuex
Vue.use(Vuex)// 在vue中use(使用)vuex
// 暴露 vuex实例
export default new Vuex.Store({
})
4 把vuex实例引用注入到vue实例中
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'//引用vuex实例
Vue.config.productionTip = false
new Vue({
router,
store,//把vuex注入到vue实例中进行使用
render: h => h(App)
}).$mount('#app')
vuex 的5大核心API
state—数据源
state的作用没有什么复杂的 就是用来创建数据的 vuex的所有数据必须必须必须只能放在state中
创建
import Vue from 'vue'//引用vue
import Vuex from 'vuex' //引用vuex
Vue.use(Vuex)// 在vue中use(使用)vuex
// 暴露 vuex实例
export default new Vuex.Store({
state:{//存放数据的 可以存放任何数据
text:"我是一个字符串",
num:123,
arr:[1111,2222,3333,444,5555]
}
})
使用数据state
在想使用数据的组件内 this.$store.state.xxx 使用vuex的state数据
<template>
<div class="about">
<h1>This is an about page</h1>
<h1>{{this.$store.state.text}}</h1>
</div>
</template>
state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
vuex的数据是响应式的 那么既然想是响应式 我们如何进行修改呢?
mutations
mutations 修改vuex的state数据 他只有一个作用就是修改
mutations是一个属性 他里面方的就是修改state的一个一个的方法。
创建
import Vue from 'vue'//引用vue
import Vuex from 'vuex' //引用vuex
Vue.use(Vuex)// 在vue中use(使用)vuex
// 暴露 vuex实例
export default new Vuex.Store({
state:{//存放数据的 可以存放任何数据
text:"我是一个字符串",
num:123,
arr:[1111,2222,3333,444,5555]
} ,
mutations:{//修改state的数据
// 修改方法1(state 这个形参就是上面的state){},
// 修改方法2(state 这个形参就是上面的state){},
// 修改方法3(state 这个形参就是上面的state){},
// 修改方法4(state 这个形参就是上面的state){},
// 修改方法5(state 这个形参就是上面的state){},
xiaoming(state){
state.text="呵呵"
}
}
})
上面创建了一个修改的mutations
虽然有一个xiaoming的修改方法 但是我在组件页面中如何去调用它呢?
调用mutatioins中的修改方法 commit(“调用的方法名”)
<template>
<div class="about">
<h1>This is an about page</h1>
<h1>{{this.$store.state.text}}</h1>
<button @click="fun()">点我修改</button>
</div>
</template>
<script>
export default {
methods:{
fun(){
// 调用修改调用修改使用this.$store.commit("mutations的方法名")
this.$store.commit("xiaoming")
}
}
}
</script>
vuex mutations 提交载荷 payload
xiaoming(state,payload){
state.text=payload.name
}
在组件调用
this.$store.commit("xiaoming",{key:val,key:val})
扩展------必须要知道 很重要 vuex数据刷新丢失
<template>
<div class="about">
<h1>This is an about page</h1>
<h1>{{this.$store.state.text}}</h1>
<button @click="fun()">点我修改</button>
</div>
</template>
<script>
export default {
// 解决vuex数据刷新丢失的问题
created () {
//在页面加载时读取localStorage里的状态信息
if (localStorage.getItem("data") ) {
//replaceState替换数据 Object.assign合并对象
this.$store.replaceState(Object.assign({}, this.$store.state,JSON.parse(localStorage.getItem("data"))))
}
//在页面刷新时将vuex里的信息保存到localStorage里
window.addEventListener("beforeunload",()=>{
localStorage.setItem("data",JSON.stringify(this.$store.state))
})
},
// 解决vuex数据刷新丢失的问题
methods:{
fun(){
// 调用修改调用修改使用this.$store.commit("mutations的方法名")
this.$store.commit("xiaoming")
}
}
}
</script>
actions 处理异步操作
actions 在vuex中的唯一作用就是用来处理异步操作的
actions 是vuex的一个属性 里面写的是一个一个的方法方法中存的就是异步操作
创建
import Vue from 'vue'//引用vue
import Vuex from 'vuex' //引用vuex
Vue.use(Vuex)// 在vue中use(使用)vuex
// 暴露 vuex实例
export default new Vuex.Store({
state:{//存放数据的 可以存放任何数据
text:"我是一个字符串",
num:123,
arr:[1111,2222,3333,444,5555]
} ,
mutations:{//修改state的数据
// 修改方法1(state 这个形参就是上面的state){},
// 修改方法2(state 这个形参就是上面的state){},
// 修改方法3(state 这个形参就是上面的state){},
// 修改方法4(state 这个形参就是上面的state){},
// 修改方法5(state 这个形参就是上面的state){},
xiaoming(state,payload){
state.text=payload.name
}
},
actions:{//处理异步操作
// 一个一个的方法
demoa(){console.log("我是第一个异步")},
demob(){console.log("我是第2个异步")},
democ(){console.log("我是第3个异步")},
}
})
调用 actions 如果actions想被调用 必须必须必须要使用this.$store.dispatch(“actions名字”)
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<h1>{{this.$store.state.text}}</h1>
<button @click="fun()">点我设置payload</button>
<button @click="funb()">点我调用异步操作</button>
</div>
</template>
<script>
export default {
name: 'Home',
methods:{
fun(){
// 如果要传递多个 那么久传递一个对象即可
this.$store.commit("xiaoming",{name:"xixi",age:18})
},
funb(){
this.$store.dispatch("demoa")
}
},
components: {
}
}
</script>
actions进行vue异步请求数据闭环
1.既然要进行异步请求 就要走vue中使用axios的那些东西 新建util 新建api 来进行请求与拦截器的封装
2.异步操作是在vuex actions中 所以我们需要在actions中创建一个方法 在方法中调用异步请求
import Vue from 'vue'
import Vuex from 'vuex'
// 引用
import {getlink} from "@/api/getapi.js"
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
// 需要在vuex中发送异步操作
actiondemoa(){
getlink("http://api.artgoer.cn:8084/artgoer/api/v1/user/324380/v3/topic/topicHomeByLabel?pageIndex=1&token=b544cd63-6d42-46fe-a96c-3cf96bae3113&topicId=62187").then((ok)=>{
console.log(ok)
})
}
}
},
modules: {
}
})
3.在需要发送请求的组件页面中 触发actions (this.$stroe.dispatch())触发了之后这个请求就发送了
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
mounted(){
// 因为异步请求在vuex的actions中所以我需要调用actions
this.$store.dispatch("actiondemoa")
},
props: {
msg: String
}
}
</script>
</style>
4.数据已经在actions打印出来了 由于vuex的数据需要存储在state中 所以我们需要让actions请求来得数据修改state中的某个变量
在actions中要调用commit触发mutations修改state
import Vue from 'vue'
import Vuex from 'vuex'
// 引用
import {getlink} from "@/api/getapi.js"
Vue.use(Vuex)
export default new Vuex.Store({
state: {
arr:[]
},
mutations: {
dataarr(state,payload){
state.arr=payload
}
},
actions: {
// 需要在vuex中发送异步操作
actiondemoa(context){
getlink("http://api.artgoer.cn:8084/artgoer/api/v1/user/324380/v3/topic/topicHomeByLabel?pageIndex=1&token=b544cd63-6d42-46fe-a96c-3cf96bae3113&topicId=62187").then((ok)=>{
console.log(ok.data.data.commentList)
// 把请求来得数据交给mutations修改
context.commit("dataarr",ok.data.data.commentList)
})
}
},
modules: {
}
})
在页面使用
<ul》
<li v-for="(v,i) in this.$store.state.arr" :key="i"》
{{v.commentTxt}}
</li》
</ul》
此时li的内容就是请求过来的数据
注意:如果封装了模块,在遍历循环时要在arr前面加上模块名
<li v-for="(v,i) in this.$store.state.模块名.arr" :key="i"》
数据请求闭环示意图(十一天视频课件img/4.png)
十二
payload
acions也可以通过dispatch向actions中传递参数 那么这个参数就是payload
getters
getters 就是vuex的计算属性
vuex的getters与传统vue的computed计算属性有什么区别?
作用域 vue的计算属性处理出来的数据只能在当前组件内使用 但是vuex使用getters处理的数据可以在任何组件中多次使用
一条数据在一个组件内展示不同的形态的时候 使用vue的计算属性
一条数据在多个组件内想重复使用处理出来的不同形态数据的时候 使用vuex的getters
getters是一个vuex的属性
创建:
getters:{//vuex的计算属性
newtext(state){
return state.text.toUpperCase()
}
},
使用
与使用state相同在组件中的计算属性当中使用 this.$store.getters.xxx来进行调用
modules
在Vue中State使用是单一状态树结构,应该的所有的状态都放在state里面,如果项目比较复杂,那state是一个很大的对象,store对象也将对变得非常大,难于管理。
module:可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
1.在store文件夹中新建文件夹存放模块
2.编写属于组件的模块
export let aboutm={
state: {
demoa:"111",
demob:"22",
democ:"113331",
},
mutations: {
},
actions: {
},
}
3.在vuex的配置中引用使用
import Vue from 'vue'
import Vuex from 'vuex'
import {aboutm} from "./modules/aboutm.js"
import {homem} from "./modules/homem.js"
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
aboutm,homem
}
})
4注意 如果使用了模块在读取数据的时候就有区别了
this.$store.state.模块名.xxxx
模拟数据mockjs
mockjs是用来在vue 中创建模拟数据接口的 方便在后台没有给我们接口的时候让我们的项目变成动态请求
1.下载 :npm install --save mockjs
2.我们在项目中要有模拟数据 那么需要写在那个位置呢?-----创建mock文件夹来容纳模拟数据
3.在mock文件夹下创建data文件夹用来容纳模拟数据 并且创建.js文件来编写mockjs代码
import Mock from "mockjs"
// 创建第一个请求
注意大小写 get要小写
// Mock.mock("请求地址你自己写","请求方式get/post",require("你的模拟数据json的地址"))
Mock.mock("/xiaoming/xiaohong","get",require("./data/demo.json"))
4.mock还需要在配置文件中引用千万不要忘
mock还需要在配置文件中引用千万不要忘
在main.js中引用
你建立mock时文件夹的名字是什么,引用的时候就是文件夹的名字: require("./XXX")
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
require("./mock")//引用mock(你建立mock时文件夹的名字是什么,引用的时候就是文件夹的名字)
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
最后将vuex中的actions中的传参地址改成Mock.mock中的地址即可
模拟数据详细步骤:
1.下载 :npm install --save mockjs
2,创建mock文件夹来容纳模拟数据,
3,在mock文件夹下再创建一个文件夹,用来写模拟数据的(里面是一个以json文件,.json结尾),模拟几个页面的数据建几个json文件,
然后继续建一个index.js文件,用来编写mockjs代码如下:
import Mock from "mockjs"
// 创建第一个请求
注意大小写 get要小写
// Mock.mock("请求地址你自己写","请求方式get/post",require("你的模拟数据json的地址"))
Mock.mock("/xiaoming/xiaohong","get",require("./data/demo.json"))//有几个json文件就写几个,
但是注意,每次写的时候里面的地址不能相同。
4,mock还需要在配置文件中引用千万不要忘
mock还需要在配置文件中引用千万不要忘
在main.js中引用 require("./Swhmockjs")
你建立mock时文件夹的名字是什么,引用的时候就是文件夹的名字: require("./XXX");
5,然后在store(vuex)文件夹下的index.js中的actions中将地址改为Mock.mock的地址,这里需要注意:
在store文件夹下可以建立模块组件(里面是.js文件)(建立模块组件详情见笔记十二天modules),也可用来请求模拟数据,里面同index.js一样,也需引入封装好的 import {getlink} from “@/swhapi/api.js”,
然后其它不变,在页面中使用时,在循坏中使用时需要写组件名,如:
《li v-for="(v,i) in this.$store.state.XXX.arr" :key=“i”》
mock数据的写法2,可分多个模块写
{
"sdemo6":[
{"imgurl":"swhimg/r1_03.png","_p":"立白洗洁精洗洗更健康","_h4":"0.82"},
{"imgurl":"swhimg/r1_05.png","_p":"新疆大盘鸡真好吃","_h4":"81"},
{"imgurl":"swhimg/r1_08.png","_p":"洗洗更健康的海思奇瑞","_h4":"82"},
{"imgurl":"swhimg/r1_05.png","_p":"哈哈哈哈打嗝","_h4":"191"},
{"imgurl":"swhimg/r1_03.png","_p":"立白洗洁精洗洗更健康","_h4":"11"},
{"imgurl":"swhimg/r1_08.png","_p":"立白洗洁精洗洗更健康","_h4":"234"},
{"imgurl":"swhimg/r1_03.png","_p":"立白洗洁精洗洗更健康","_h4":"11"},
{"imgurl":"swhimg/r1_08.png","_p":"立白洗洁精洗洗更健康","_h4":"234"}
],
"sdemo61":[
{"title":"我是sdemo61"},
{"title":"我是sdemo61"},
{"title":"我是sdemo61"},
{"title":"我是sdemo61"}
],
"sdemo7":[
{"dls":"我是dl要用的数据66"},
{"dls":"我是dl要用的数据66"},
{"dls":"我是dl要用的数据66"},
{"dls":"我是dl要用的数据66"}
]
}
2,vuex中异步请求时,(也可写多个js模块,其它步骤一样)
import {getlink} from "@/api/getapi.js"
export let sdemo6={
state: {
arr:[],
att:[],
},
mutations: {
darrs(state,payload){
state.arr=payload
},
darrt(state,payload){
state.att=payload
},
},
actions: {
indexs(context){
getlink("/sha/wenhui6").then((ok)=>{
console.log(ok.data.sdemo6)
context.commit("darrs",ok.data.sdemo6)
}).catch((err)=>{
console.log(err)
})
},
indext(context){
getlink("/sha/wenhui6").then((ok)=>{
console.log(ok.data.sdemo7)
context.commit("darrt",ok.data.sdemo7)
}).catch((err)=>{
console.log(err)
})
},
}
}
页面中触发时
mounted(){
this.$store.dispatch("indexs")
this.$store.dispatch("indext")
},
遍历循坏时一样,只需改statex
v-for="(v,i) in this.$store.state.sdemo6.att" :key="i";
v-for="(v,i) in this.$store.state.sdemo6.arr" :key="i";
vantui
https://vant-contrib.gitee.io/vant/#/zh-CN/
使用步骤:
1.下载:npm i vant -S(详情进入官网)
2,在main.js中配置
import Vue from 'vue';(可省略)
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);
3,任何页面随便使用
十三
后台发送参数
rest api 是前后端分离的最佳实践,是开发的一套标准或者说是一套规则,不是框架
规范: 明文规定会约定俗成的一套标准。
规则:让干什么就干什么,
restapi 就是在定义接口的时候,给接口设置语义化的名称
发送参数的几种方式
GET 获取数据
POST 发送数据
DELETE 删除
更新/修改
PUT 更多的意思是全部修改或者修改一些
PATCH 更多的意思是修改一个
怎么给后台发送参数
发送参数的时候 其实百分之70的情况可以使用如下写法
GET
http://localhost:3000/ceshi/get
请求参数是 name
使用params来进行参数的传递
注意:再给后台发送参数时和vuex的异步请求非常相似,首先还是新建util文件夹用来存放拦截器,然后是api文件夹下的 .js文件,写法一样,同样需要引入拦截器。不同点是vuex发送请求的时候在store下的index.js中引入api文件夹下的getapi.js,然后在 actions中处理异步操作。而在给后台发送参数的时候,需要在api文件夹下的 .js文件中写入传参方式,同时可直接在要发送参数的页面中直接引入api文件夹下的.js文件,在mounted()中处理异步操作,代码如下
api文件夹下的.js代码,除了需要加传参方式(git发送用params),其它的都和vuex的api一样
//引入拦截器
import service from "@/util/service.js"
export function getlink(url,params){
return new Promise((resolve,reject)=>{
service.request({
url,
method:"GET",
// get发送数据的时候使用params发送
params
}).then((ok)=>{
resolve(ok)
}).catch((err)=>{
reject(err)
})
})
}
页面中引入api/.js文件(给后台发参1)
<template>
<div>
<h1>我是测试发送get的组件</h1>
</div>
</template>
<script>
import {getlink} from "@/api/getapi.js"
export default {
mounted(){
// // 在发送数据的时候 key是后台给你的(请求参数)
// getlink("后台给你的地址",{请求参数:"传递过去的数据"}).then((ok)=>{
// console.log(ok)
// })
getlink("/api/ceshi/get",{name:"我是get的数据xixi"}).then((ok)=>{
console.log(ok)
})
}
}
</script>
<style>
</style>
给后台发参2
<script>
import {getlink} from "@/api/getapi.js"
export default {
data(){
return {
input1:"",
input2:"",
}
},
methods:{
fun(){
// 在发送数据的时候 key是后台给你的
getlink("http://localhost:3000/user/zhuce",{uname:this.input1,upwd:this.input2}).then((ok)=>{
console.log(ok);
})
}
}
}
</script>
后台接收
app.get("/user/zhuce",(req,res)=>{
// 要注册第一步先得到前台输入框的值
console.log(req.query.uname+"-----"+req.query.upwd);
res.send({msg:"我是注册的接口"})
})
后台存入数据库
let express = require("express") //引入express
//引入数据库
let db = require("../db/index.js")
let router = express.Router();
router.get("/zhuce", (req, res) => {
console.log(req.query.name+"------"+req.query.pwd)
// 后台将用户名和密码存入库中
let userdata = new db({ //引入数据库时的变量名是啥,new 的就是啥
name: req.query.name, //用户名
age:req.query.pwd //密码
})
//2.开始新增
userdata.save().then((ok) => {
console.log(ok)
})
res.send({ mag: "我是注册接口"})
})
module.exports = router //用exports暴露就是文件
DELETE
http://localhost:3000/ceshi/delete
参数 delid,参数name改为delid
同get, 修改发送方式即可: method:“DELETE”
其它同get一摸一样
// delete发送数据的时候同样使用params发送
PUT
http://localhost:3000/ceshi/put
参数 putid
同get , 修改发送方式即可: method:“PUT”,
// PUT发送数据的时候同样使用params发送
POST
http://localhost:3000/ceshi/post
参数 postid
api文件夹下的.js代码
import service from "@/util/service.js"
export function postlink(url,data){
return new Promise((resolve,reject)=>{
service.request({
url,
method:"POST",
// post发送数据的时候使用data发送
data
}).then((ok)=>{
resolve(ok)
}).catch((err)=>{
reject(err)
})
})
}
post发送数据的时候使用data发送
<template>
<div>
<h1>我是测试post发送参数的</h1>
</div>
</template>
<script>
import {postlink} from "@/api/postapi.js"
export default {
mounted(){
// post发送参数的时候是没有办法直接发送的 因为默认情况下没有
// 办法解析
// 所以我们要设置post参数并且解析参数
let usp=new URLSearchParams()
// usp.append("你发送的key","你发送的val")
usp.append("postid",9999)
postlink("/api/ceshi/post",usp).then((ok)=>{
console.log(ok)
})
}
}
</script>
<style>
</style>
后台接收
let express=require("express")
let col=require("./db/db.js")
let bodyParser=require("body-parser")
let ue=bodyParser.urlencoded({extended:false})
let app=express()
app.post("/user/denglu",ue,(req,res)=>{
// node是不会解析post的请求体 所以我们要使用第三方内容来帮助解析post
// body-parser 帮助node解析请求体
// 1.下载 npm install --save body-parser
// 2.引用
// 3.开启解析功能
// 4.把解析功能注入到当前路由中
// 5.接收数据 req.body.xxx
console.log(req.body.uname+"------"+req.body.upwd)
res.send({msg:"我是登录的接口"})
})
解决跨域
原生 jsonp
vue 正向代理
必须在vue的根路径下创建一个vue.config.js文件
在其中写入如下配置
module.exports={
// 配置跨域
devServer: {
proxy: { //配置跨域
'/api': {
target: 'http://localhost:3000/', //这里后台的地址模拟的;应该填写你们真实的后台接口
changOrigin: true, //允许跨域
pathRewrite: {
'^/api': ''
}
},
}
},
}
修改请求地址为/api/xxxx 注意配置的代理之后项目必须重启
cors
十四
响应式拦截
拦截器更多配置与token的前端使用
1.拦截器更多配置—相应拦截
失败响应的status(状态码)需要在response中获得 error.response.status
// 添加响应拦截器
service.interceptors.response.use(function (response) {
// 对响应数据做点什么-----后台能后正常给我们相应数据拦截的地方
return response;
}, function (error) {
// 对响应错误做点什么-----404 500 403出现的位置
console.log(error.response.status)
switch (error.response.status) {
case 500:
// 处理的操作
alert("当前连接超时")
break;
case 404:
// 处理操作
alert("您访问的页面不存在")
// 把页面跳转到首页
break;
default:
return Promise.reject(error);
}
return Promise.reject(error);
});
十五
组件传值
正向传值 props
逆向传值 $emit() ref
同胞兄弟传值 eventBus
中央事件总线 eventBus
创建一个新的Vue实例,以后它就承担起了组件之间通信的桥梁了,也就是中央事件总线。
1.新建一个文件夹用来容纳中央事件总线这个vue实例
// 中央事件总线就是新建一个vue实例用来当兄弟组件的数据桥梁
import Vue from "vue"
export default new Vue()
2 兄弟传值 demoa需要把数据给demob
(1)现在demoa中使用$emit()把数据通过自定义事件绑定到那个vue实例上\
<template>
<div>
组件a
<button @click="fun()">点我把数据传递给我兄弟demob</button>
</div>
</template>
<script>
// 1.引用中央时间总线
import eventbus from "@/eventbus"
export default {
data(){
return {
text:"我是demoa里面的变量"
}
},
methods:{
fun(){
// 2.在事件中直接给引进来的中央时间总线绑定自定义事件
eventbus.$emit("apao",this.text)
}
}
}
</script>
<style>
</style>
(2)在demob中使用$on来监听接收实例上的自定义事件
<template>
<div>
组件b
</div>
</template>
<script>
// 1.引用中央时间总线
import eventbus from "@/eventbus"
export default {
// 2.来开始监听
mounted(){
eventbus.$on("apao",(val)=>{
console.log(val)
})
}
}
</script>
<style>
</style>
1、创建一个事件总线,例如demo中的eventBus,用它作为通信桥梁
2、在需要传值的组件中用bus.¥emit触发一个自定义事件,并传递参数(emit前加美元符)
3、在需要接收数据的组件中用bus.$on监听自定义事件,并在回调函数中处理传递过来的参数
跨组件传值 vuex
Mixins 混入
mixins是vue中组件的一种复用技术 它可以把组件中很多相同的属性与方法等属性 提取出来方便多个组件进行复用
局部混入
1.创建一个文件夹用来容纳混入内容
let mixinsdemo={
methods:{
funb(){
console.log("我是一个方法")
}
},
data(){
return {
textm:"我是混入的变量"
}
}
}
export default mixinsdemo
2.在想使用的地方引用调用使用
<template>
<div>
组件a
<button @click="funb()">点我调用混入的方法---{{textm}}</button>
</div>
</template>
<script>
// 引用混入
import mixinsdemo from "@/mixins"
export default {
// 调用mixins
mixins:[mixinsdemo],
}
</script>
<style>
</style>
全局混入
在任何组件中都可以使用
在main中引用 并且使用
import MinXin from ‘@/components/MinXins/index.js’
Vue.mixin(MinXin);
在组件中即可随意使用混入中的数据
十七
$set 使用以及原理
你在vue 中有没有遇到过data数据改变了,但是视图并没有更新??
当vue里面的data声明或者赋值已经初始化过的对象或数组,向其中插入新对象的属性的属性的时候,是不会更新试图层的。
<template>
<div class="about">
<h1>$set</h1>
<h1>{{obj.name}}</h1>
<h1>11111{{obj.age}}111111</h1>
<button @click="fun()">点我添加age</button>
</div>
</template>
<script>
export default {
// 当vue中的data里面声明或者已经初始化赋值过得**对象**或者**数组** ,
// 向其中插入新对象的属性的时候 ,是不会更新视图的
data(){
return {
obj:{
name:"xixi"
}
}
},
methods:{
fun(){
this.obj.age=666;
console.log(this.obj.age)
}
}
}
</script>
根据官方文档上面:定义如果在实例创建之后添加新的属性到实例上,是不会触发视图更新的
原理:
因为vue数据劫持(object.defineproperty())只会在vue初始化的时候对数据进行拦截监听,所以data的内容只有在vue 初始化的时候才有双向绑定
解决
$set就是对vue 中的数据添加新的属性,并触发视图更新
语法
this.$set(你要操作的数据,你要新增的数据key,你要新增的值),案例如下
<template>
<div class="about">
<h1>$set</h1>
<h1>{{obj.name}}</h1>
<h1>11111{{obj.age}}111111</h1>
<button @click="fun()">点我添加age</button>
</div>
</template>
<script>
export default {
// 当vue中的data里面声明或者已经初始化赋值过得**对象**或者**数组** ,
// 向其中插入新对象的属性的时候 ,是不会更新视图的
data(){
return {
obj:{
name:"xixi"
}
}
},
methods:{
fun(){
// this.obj.age=666;
// console.log(this.obj.age)
// this.$set( 你要操作的数据 , 你要新增的数据key , 你要新增的值 )
this.$set(this.obj,"age",6669)
}
}
}
</script>
nextTick
vue的响应式并不是数据变了视图dom立即更新,而是按照一定的策略进行dom更新
<template>
<div class="home">
<h1>nextTick</h1>
<h1 ref="demoh">{{text}}</h1>
<button @click="fun()">点我改变</button>
</div>
</template>
<script>
export default {
name: 'Home',
data(){
return {
text:"我是默认数据"
}
},
methods:{
fun(){
this.text="我变了"
// 数据变了页面的dom不是立即更新
console.log(this.$refs.demoh.innerHTML);
}
},
components: {
}
}
</script>
异步说明
简单来说,vue在数据改变之后,视图是不会立即更新的,而是等待着同一事件循坏中的所有数据变了之后,在统一进行视图更新
原理
1.当我们修改数据的时候,是一个同步任务这些任务都在主线程上执行,形成一个执行栈,但是这个时候还没有设计到dom
2.vue会开启一个异步列队,并且缓冲当前事件循环中的数据变化
3.同步的修改任务完成了 就要开始执行异步列队中的这些任务了,开始更新DOM
Vue.nextTick 是什么
为了在数据变化之后读取dom中的内容所以使用nextTick
他就是dom渲染完毕之后立即执行
<template>
<div class="home">
<h1>nextTick</h1>
<h1 ref="demoh">{{text}}</h1>
<button @click="fun()">点我改变</button>
</div>
</template>
<script>
export default {
name: 'Home',
data(){
return {
text:"我是默认数据"
}
},
methods:{
fun(){
this.text="我变了"
// 数据变了页面的dom不是立即更新
// console.log(this.$refs.demoh.innerHTML);
// 我就是想当数据变了打印出dom改变之后的数据
this.$nextTick(()=>{
console.log(this.$refs.demoh.innerHTML);
})
}
},
components: {
}
}
</script>
node
node 是什么
nodejs是基于谷歌浏览器v8引擎的js 解析器 可以在脱离浏览器的情况之下运行js
就是可以在脱离浏览器的时候运行js 可以让js在服务器端运行
node的特点
单线程 非阻塞式io
io输入输出
事件驱动
适合应用
1,对稳定性没有那么极致要求的程序
2,考试系统
交互式解释器 REPL
在cmd中输入node回车就可以进行js的编写
node怎么运行js文件?
cd到当前要运行js文件的文件夹 使用node 文件名即可运行
node使用
express 是基于node的第三方框架 可以非常方便的让我们来搭建一个后台服务
1.下载 cnpm install --save express
2,使用前引用express:
let express=require(“express”);//引用并且赋值给一个变量
3,创建express的路由功能:
let router=express.Router()
4,开始创建接口
// GET
// POST
// DELETE
// PUT
// 创建出了一个get接口
router.get("/data",(req,res)=>{
// 相应给前台数据
res.send({msg:"我是get的接口",data:{list:"我是数据"}})
})
// 5.暴露
module.exports=router
详见18太天视频课件
十八
express(自己写接口)
就是根据请求数据的地址的不同的来分发不同的路由功能
使用先在.js文件中引用 express(引用见17天node使用)
先建立一个router文件夹,里面可建多个.js文件,.js文件的代码如下(多个 .js文件代码一样,只需该改get后面的地址即可):
// 1.引用express
let express=require("express");
// 2.创造出express的路由功能
let router=express.Router()
// 3.开始创建接口
// GET
// POST
// DELETE
// PUT
// 创建出了一个get接口
router.get("/data",(req,res)=>{
// 相应给前台数据
res.send({msg:"我是get的接口",data:{list:"我是数据"}})
})
// 4.暴露
module.exports=router
在router文件夹的同级位置建一个index.js文件,用来将router文件夹中的多个.js文件关联在一起,代码如下
let express=require("express");
let app=express()
// 我要关联哪些路由文件
let denglu=require("./routers/denglu.js")
let zhuce=require("./routers/zhuce.js")
let shuju=require("./routers/shuju.js")
注意:引入接口文件时,变量名必须与接口中get后面地址名相同,如上面的接口代码,get后面为data,那么引用时就是:
let data=…….js
给前台的请求时的接口地址,前台请求时输入以下地址即可
// http://localhost:3000/demoa/denglu
// http://localhost:3000/demob/zhuce
// http://localhost:3000/democ/data
// 使用引进来的这些路由文件
// use()中间件-----就是每次程序进入的时候都会先进过中间件
app.use("/demoa",denglu)
app.use("/demob",zhuce)
app.use("/democ",shuju)
//设置端口
app.listen(3000)
关于重启
注意:写完新的接口后,后台必须要重启:node index.js,无论是往接口里面添加新的数据,还是修改端口 都需要重启;还有就是后台修改完端口以后,前台的解决跨域(vue.config.js)里面的允许跨域的端口也要修改成后台修改以后的端口,修改允许跨域的端口后,前台的项目也需要重启,否则前台输入地址后无法获取数据,后台也无法在浏览器中测试数据
mongodb
是一个数据库
为什么前端要学习数据库?
主前端辅后端
数据库的分类
myspl oracle sqlserver db2 关系型数据库
mongodb 非关系型数据库
非关系型数据库的默认效率要比关系型数据库高n倍
mysql 库 表 字段
mongodb 库 collection(集合)document(文档)
安装
详见word(十八天课件)
操作
增
db.集合名.insert( {key:val} ) 插入一条
db.集合名.insert([ {{key,val},{key,val},{key,val},{key,val}} ]) 插入多条
再插入的时候会自动给我们添加一个唯一的_id标识,我们在新增的时候可以手工定义,如下
db.xixi.insert({_id:1,name:xixi,age:26})
但是_id不能重复
允许id重复的方法
注意:insert插入的id 不能重复
db.集合名.save 插入id可以重复 但是如果重复的话就变成了修改,就会将上一个的id修改掉
查
db.集合名.find() 查询所有
db.集合名.find({查询的key:查询的val}) 按照条件查询
更多便捷的条件查找
大于:$gt, 小于:¥lt,大于等于¥gte ,小于等于¥lte 不等于:¥ne
db.集合名.find({你要查询的key:{$属性名:条件}})
db.集合名.find({key:val,第二个条件的key:val})and查询(多个条件)
limit()读取指定数量的内容
skip()跳过多少条记录数
删
db.集合名.remove({删除的key:删除的val}
改
db.集合名.update({你要修改的key:你要修改的val},{$set:{修改成什么的key:修改成什么的val}})
分页
1 0-2
2 跳过3条 读取3条
3.(页码-1)*3 跳过6条 读取3条
4 跳过9条 读取3条
.连接数据库 mongoose
一个第三方的mongodb连接操作库方便我们使用node操作mongdb,
1,下载:cnpm install --save mongoose
b.在项目下创建db
b.在db下创建index.js
#####c.在index.js中进行库的连接,代码如下
vue数据库连接代码(serve文件夹下的db文件夹下的index.js中)
// 需要把数据库的连接封装成一个模块方便使用
// 1.引用
let mongoose=require("mongoose");
// 2.设置连接参数
mongoose.connect("mongodb://localhost:27017/demo",{ useNewUrlParser: true,useUnifiedTopology: true })
//注意:27017//后面的名字一定是你数据库中的仓库名,复制时要记得修改-----——……^---
// 3.开始连接
let db=mongoose.connection;
// 4.监听连接是成功还是失败的
db.on("err",console.error.bind(console,"连接失败了!!!"))
db.on("open",()=>{
console.log("连接ok")
})
// 6 设置数据库操纵对象
let xixiSchema=new mongoose.Schema({
// key:数据类型
name:String,
age:Number
})
// 7 设置集合
// mongoose 在操纵数据库的时候 如果名字不是复数 那么他就会新创建一个带有s的集合
// 在创建的时候就加s
// mongoose.model("集合名",schema对象)//
let colxixi=mongoose.model("apples",xixiSchema)//apples以你你数据库中的实际集合名(表名为准,记得修改)
// 5暴露先测试
module.exports=colxixi
d.在想连接数据库的路由文件中引用调用(在自己写的接口中引用使用)
//let express = require("express") //引入express
//引入数据库连接(db下的index.js)
let db = require("../db/index.js")
//let router = express.Router(); //把路由用express的方式拿过来
//router.get("/zhuce", (req, res) => { //创建路由
db
// res.send({ mag: "我是注册接口" })
//})
//module.exports = router //用exports暴露就是文件
6对数据库增删改查
增
let express = require("express") //引入express
//引入数据库
let db = require("../db/index.js")
let router = express.Router(); //把路由用express的方式拿过来
router.get("/zhuce", (req, res) => { //创建路由 设置请求方式 接口名
//注册功能
//1.设置新增数据
let userdata = new db({ //引入数据库时的变量名是啥,new的对象就是啥
name: "嘻嘻", //用户名
pass: "666" //密码
})
//2.开始新增
userdata.save().then((ok) => {
console.log(ok)
})
res.send({ mag: "我是注册接口" })
})
// http://localhost:3000/democ/data:接口地址,在浏览器中测试时输入,以在接口的index.js中配置的地址为准
module.exports = router //用exports暴露就是文件
注意:在写完新增代码之后是不会直接在数据库中添加内容的,必须要在浏览器中输入接口地址测试后才能将内容添加到数据库
十九
后台接受参数
get
req.query.你要的key
post
// 接收post数据
// 1.下载body-parser的中间件 帮助express来解析post数据cnpm install --save body-parser
let express=require("express");
let router=express.Router();
// 2.引用bodyparser
let bodyParser=require("body-parser")
// 3.开启解析功能
let bp=bodyParser.urlencoded({extended:false})
// 4.把解析功能传入
router.post("/denglu",bp,(req,res)=>{
// 接收post数据
// 1.下载body-parser的中间件 帮助express来解析post数据cnpm install --save body-parser
res.send({mag:"我是登录的接口"})
})
module.exports=router
接收用req.body.你要的key
原型图设计
墨刀 墨客 蓝湖
elementui
初级样式
App里:
html,body,#app{
height: 100%;
}
页面
//#home为最外层div起的id名
#home,.el-main,.el-container{
height: 100%;
}
swiper
<template>
<div>
<div class="block">
<el-carousel trigger="click" height="450px" width="600px">
<el-carousel-item v-for="item in arr" :key="item">
<img :src="item.img" alt="">
</el-carousel-item>
</el-carousel>
</div>
</div>
</template>
<script>
export default {
data(){
return{
arr:[
{img:"/img/1.jpg"},
{img:"/img/2.jpg"},
{img:"/img/3.jpg"},
{img:"/img/4.jpg"},
{img:"/img/3.jpg"},
{img:"/img/2.jpg"}
]
}
}
}
</script>
<style scoped>
.el-carousel__item h3 {
color: #475669;
font-size: 14px;
opacity: 0.75;
line-height: 150px;
margin: 0;
}
.el-carousel__item:nth-child(2n) {
background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n+1) {
background-color: #d3dce6;
}
/* 设置轮播图的宽高,其它如需设置类同,可在检查中找到对应的类名进行设置
el-carousel__item 为轮播图的父级元素,可设置它来控制轮播图的大小
*/
.el-carousel__item img{
width: 100%;
height: 100%;
}
</style>
下载 详见官网 https://element.faas.ele.me/#/zh-CN/component/quickstart
引用
main.js中添加
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
二十
token 、用户登录验证
没有登录首页不能进入的 有个提示信息 您没有登录 首页就不能展示给用户而是把用户踢到登录页面
那么这个时候就应该有一个保存用户登录状态的东西 那么这个东西就是token(token是后台生成的内容 我们前台只需要会使用即可)
token 就是一段加密的字符串 里面保存到额就是用户的相关信息
jsonwebtoken 下载 npm install --save jsonwebtoken
后台:
1.生成token
sign(你要加密的内容,密钥(自己定义越乱越好不要出现空格特殊符号等内容))
登录成功后把token生成返回给前台
// 1.下载jsonwebtoken
// 2.引用 jsonwebtoken
// 3.开始生成
let obj={
loginid:1
}
let mi="kasduhszxcvisdfviuwerlikdfghkj"
let token=jwt.sign(obj,mi)
// 4.把token给前台
res.send({mag:"我是登录的接口",data:{dengluid:1,token}})
2.解析token
接收到之后需要解析token 判断token里面的登录信息是否正确如果正确就给前台一个提示提示用户登陆过
verify(你要解密的token,密钥)
前台:
1.接收存储token
// 在登录成功的时候把这个token存到本地存储
window.localStorage.setItem(“token”,ok.data.data.token)
2.发送token
在路由的全局前置守卫中/拦截器中 先从本地存储中获取到token 在把这个数据通过请求头发送给后台
基本使用(详细使用)
生成token
sign(你要生成秘钥的数据 , 生成的私钥(越乱越好的字符串 )
let jwt=require("jsonwebtoken")
app.get("/tokendemo",(req,res)=>{
// 1.下载jsonwebtoken
// 2.引用
// 3.开始生成
let obj={
name:"我是token的name",
age:"我是token的age"
}
let mi="oasduixcviuywertpofasdoh"
let token=jwt.sign(obj,mi)
res.send({msg:"我是测试token的接口",token})
})
解密token
verify(你要解密的token , 私钥 ,回调函数( err, data ){})
// 解密token的测试接口
app.get("/tokenb",(req,res)=>{
let token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoi5oiR5pivdG9rZW7nmoRuYW1lIiwiYWdlIjoi5oiR5pivdG9rZW7nmoRhZ2UiLCJpYXQiOjE2Mjc5NTQ0ODN9.hoPiyNN2yTfmieEfCtyqZWzUpPOKzifHZc0f8OVcB6M"
let mi="oasduixcviuywertpofasdoh"
jwt.verify(token,mi,(err,data)=>{
console.log(err);//成功的时候返回null
console.log(data);
})
res.send({msg:"我是解密token的接口"})
})
流程:
后台:
当前台发送登录请求的时候后台接收到了前台发送的用户名和密码 后台就需要拿着这个用户名和密码去数据库比对是否有这个用
如果有 后台需要告诉前台用户登录了 在有了token之后需要把用户是否登录的状态 在后台生成一段加密的字符串 (保存这用户的登录状态)一并返回给前台
前台:
先判断用户是否登陆了 如果登陆了 那么我们就有一段token的秘钥 然后就把这个秘钥保存在cookie或者是 本地存储中
今后前台在发请求的时候需要每次都把这个token携带上 后台就把这个token解密就能知道当前用户是否登录过
加密–MD5
用户的密码 是唯一的 谁也不能知道 只能使用户、
在用户登录注册的时候我们需要把用户的用户名和密码 进行加密 从而保证用户的信息资料安全
crypto 库加密
1,下载:npm install --save crypto
// 开始加密
// crypto.createHash("md5").update("你要加密的数据").digest("hex")
let newupwd= crypto.createHash("md5").update(req.body.upwd).digest("hex")
// 开始加密
图片上传
后台部分(了解掌握)
multer模块 — 图片上传模块
1,下载:npm install --save multer
// 图片上传
let storage = multer.diskStorage({
// 你上传到那个路径
// req请求来得数据
// file 当前文件
// cd 下一步干什么
destination:(req,file,cd)=>{
cd(null,"./imgup")
},
// 你上传的文件名是什么
filename:(req,file,cd)=>{
cd(null,"aa.jpg")
}
})
// 使用配置信息
let upload= multer({
storage
})
// 图片上传的接口
app.post("/home/upload",upload.single("xiaoming"),(req,res)=>{
res.send({msg:"我是图片上传的接口"})
})
// 图片上传
前台部分(必须知道)
form
ajax
<template>
<div>
<h1>图片上传</h1>
<h1>form方式图片上传</h1>
<form action="/api/home/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="xiaoming">
<input type="submit" value="点我图片上传">
</form>
<h1>axios方式图片上传</h1>
<input type="file" ref="demoimg"/>
<button @click="fun()">点我图片上传</button>
</div>
</template>
<script>
import {postlink} from "@/api/postapi.js"
export default {
methods:{
fun(){
let file = this.$refs.demoimg.files[0]
/* eslint-disable no-undef */
let param = new FormData() // 创建form对象
param.append('xiaoming', file) // 通过append向form对象添加数据
postlink("/api/home/upload",param).then((ok)=>{
console.log(ok)
})
}
}
}
</script>
<style scoped>
</style>
图片上传时api也需要改变
import service from "@/util/service.js"
export function postlink (url,data){
return new Promise((resolve,reject)=>{
service.request({
url,
method:"POST",
// post发送参数使用data
data,
catch:false,//不从缓存中读取
processData:false,//不需要转换成字符串
contentType:false //不设置发送方式
}).then((ok)=>{
resolve(ok)
}).catch((err)=>{
reject(err)
})
})
}
二十一
接口测口测试工具
POSTMAN
前后台删除修改步骤
1.去elementui官网找到对话框组件 插入页面
<el-dialog
title="提示"
:visible.sync="dialogVisible"
width="30%">
<span>这是一段信息</span>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
</span>
</el-dialog>
在data里面添加dialogVisible 变量
并且在删除按钮所调用的函数中加入修改当前变量
handleDelete(index, row) {
this.dialogVisible = true
console.log(index, row._id);
}
后台
添加删除接口
// 首页删除
router.delete("/datadelete",(req,res)=>{
// 得到前台发送过来的数据(和get方式一样)
console.log(req.query.deleteid)
// 根据这个数据 进行对应内容的删除
res.send({mag:"我是首页表格删除的接口"})
})
前台
handleDelete(index, row) {
this.dialogVisible = true
console.log(index, row._id);
// 发送请求并且创建一个与getapi相同的文件
deletelink("/api/listdata/datadelete",{deleteid:row._id}).then((ok)=>{
console.log(ok)
})
}
后台 接收这个发送的参数并且执行删除操作
//首页删除
router.delete("/datadelete",(req,res)=>{
// 得到前台发送过来的数据(和get方式一样)
console.log(req.query.deleteid)
// 根据这个数据 进行对应内容的删除
userdb.remove({_id:req.query.deleteid}).then((ok)=>{
// 在查询一遍 把查询出来的数据返回给前台
userdb.find().then((ok)=>{
res.send({mag:"我是首页表格删除的接口",data:ok})
})
})
前台测试的时候发送点击删除 在没有弹出弹框的时候就已经删除掉了
所以需要在弹框的确定按钮上绑定一个函数把请求代码放到新建的函数中
deleteok(){
this.dialogVisible = false
// 发送请求并且创建一个与getapi相同的文件
之前传递的参数是直接从rou._id中得到的 现在需要在data中穿件一个变量 在点击删除的时候提前保存 即可
deletelink("/api/listdata/datadelete",{deleteid:this.deleteid}).then((ok)=>{
console.log(ok.data.data)
this.tableData=ok.data.data
})
},
修改 在element找到相关带有输入框的弹框
<el-dialog title="收货地址" :visible.sync="dialogFormVisible">
<el-form :model="form">
<el-form-item label="用户名" :label-width="formLabelWidth">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="密码" :label-width="formLabelWidth">
<el-input v-model="form.pwd" autocomplete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="updatefun()">确 定</el-button>
</div>
</el-dialog>
在data中创建出对应的变量
在确定按钮上绑定函数并且准备发送数据
分页
-
得到一共有多少条数据
新建一个接口 这个接口只给我返回一共多少条数据 -
拿到数量了 生成emelentui的 分页效果 比如 15条
总数量 除 每页条数 得到 页数
页数上舍 -
点谁知道点的是谁 通过elementui得到了我点的是谁了
events时间名称 说明
size-change pagrSize:改变时会触发
current-change currentchange 改变时会触发
prev-click 用户点击上一页按钮改变当前页后触发
next-click 用户点击一页按钮改变当前页后触发
- 在你点的时候 把这个得到的点击内容发送给后台
打包上线
tomcat java
阿帕奇 php
nginx
IIS
过程
1 npm run build
在项目下面会生成一个dist文件夹就是打包好的内容 运行其中的html文件即可
2 设置静态资源路径 解决:修改资源路径为./
module.exports={
publicPath:"./"
}
3.router-view中的内容显示不出来
需要关闭路由的history模式,因为当前模式需要后台配合。
后期补充
虚拟DOM和diff算法(原理)(面试题)
什么是虚拟DOM(virtual DOM):
所谓的虚拟 dom,也就是我们常说的虚拟节点,它是通过JS的Object对象模拟DOM中的节点,然后再通过特定的render(渲染)方法将其渲染成真实的DOM的节点。
为什么使用虚拟DOM:
使用js操作DOM时(增删改查等等),那么DOM元素的变化自然会引起页面的回流(重排)或者重绘,页面的DOM重绘自然会导致页面性能下降,那么如何尽可能的去减少DOM的操作是框架需要考虑的一个重要问题!
https://blog.csdn.net/jiang7701037/article/details/98516468。
vue中,使用虚拟DOM 来提高性能。
真实DOM和虚拟DOM的区别:
虚拟DOM不会进行排版与重绘操作
真实DOM频繁排版与重绘的效率是相当低
虚拟DOM进行频繁修改,然后一次性比较(使用diff算法)并修改真实DOM中需要改的部分,最后并在真实DOM中进行排版与重绘,减少过多DOM节点排版与重绘损耗
虚拟DOM有效降低了重绘与排版的次数,因为,最终把虚拟dom与真实DOM比较差异,可以只渲染局部
diff算法:
虚拟DOM,是一种为了尽可能减少页面频繁操作DOM的方式,那么在虚拟DOM中,通过什么方式才能做到呢? 就是Diff算法进行对比
diff算法的原理:
逐步解析newVdom的节点,找到它在oldVdom中的位置,如果找到了就移动对应的DOM元素,如果没找到说明是新增节点,则新建一个节点插入。遍历完成之后如果oldVdom中还有没处理过的节点,则说明这些节点在newVdom中被删除了,删除它们即可。
总结:
1、产生两个虚拟DOM树:newVDom,oldVDom。
2、oldVDom和真实DOM保持一致
3、操作的newVDom
4、操作完毕后,通过diff算法对比newVDom和oldVDom的差异,并在oldVDom标注哪些节点要删除,哪些节点要增加,修改
5、根据oldVDom操作真实的DOM,让真实Dom和oldVDom保持一致
面试题:
Ajax、jQuery ajax、axios和fetch的区别
Ajax:
ajax:最早出现的前后端交互技术,是原生js,核心使用XMLHttpRequest对象,多个请求之间如果有先后关系的话,就会出现回调地狱。
Jquery Ajax:
jquery Ajax是原生ajax的封装
Fetch:
fetch是ES6新增的,Fetch是基于promise设计的。fetch不是ajax的进一步封装,而是原生js。Fetch函数就是原生js,没有使用XMLHttpRequest对象。
axios:
axios是原生ajax的封装,基于promise对象的。Axios也可以在请求和响应阶段进行拦截。它不但可以在客户端使用,也可以在nodejs端使用。
6、第三方的移动端事件库
1)、zepto.js
Zepto是一个轻量级的针对现代高级浏览器的JavaScript库, 它与jquery有着类似的api。 如果你会用jquery,那么你也会用zepto。针对移动端开发的JavaScript库(不仅仅只是事件库)
-
tap
— 元素 tap 的时候触发。(叩击) -
singleTap
anddoubleTap
— 这一对事件可以用来检测元素上的单击和双击。(如果你不需要检测单击、双击,使用tap
代替)。 -
longTap
— 当一个元素被按住超过750ms触发。 -
swipe
,swipeLeft
,swipeRight
,swipeUp
,swipeDown
— 当元素被划过时触发。(可选择给定的方向)
示例:
<div id="box">
</div>
$("#box").on("tap", function () {
$('#box').append('<li>tap:单击屏幕</li>');
});
$("#box").on("singleTap", function () {
$('#box').append('<li>singleTap:单击屏幕</li>');
});
$("#box").on("doubleTap", function () {
$('#box').append('<li>双击屏幕</li>');
});
$("#box").on("longTap", function () {
$('#box').append('<li>长安屏幕750ms</li>');
});
$("#box").on("swipe", function () {
$('#box').append('<li>划过屏幕</li>');
});
$("#box").on("swipeLeft", function () {
$('#box').append('<li>划过屏幕(左)</li>');
});
$("#box").on("swipeRight", function () {
$('#box').append('<li>划过屏幕(右)</li>');
});
$("#box").on("swipeUp", function () {
$('#box').append('<li>划过屏幕(上)</li>');
});
$("#box").on("swipeDown", function () {
$('#box').append('<li>划过屏幕(下)</li>');
});
</script>
第三方(UI)组件
使用一些别人开发好的组件、组件库、插件、ui库,来提高项目开发进度,组件库通常是某家公司开发并开源出来,获取方式可以通过npm、github查询,或者百度vue组件库,这里有一些推荐1,、推荐2、推荐3
使用方式
//安装
npm i vue-swipe --save 安装
//引入
import ‘./node_modules/vue-swipe/dist/vue-swipe.css’; 引入样式
import { Swipe, SwipeItem } from ‘vue-swipe’; 引入组件
Vue.component(‘swipe’, Swipe); //注册安装到全局
Vue.component(‘swipe-item’, SwipeItem);
//注册到选项components 私有使用
组件类别*
pc端、后台管理、客户端
- element-ui 饿了么 √
- iview 个人
- ant design 蚂蚁金服
移动端、客户端
- mint-ui 饿了么
- vant 有赞 电商 √
- vue-material
- muse-ui
- VUX
- cube-ui
- vonic
- Vue-Carbon
- YDUI
通用
- bootstrap4/3
- ameizi
elementUI
安装
npm i element-ui -S
整体引入全局使用
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
按需引入
npm install babel-plugin-component -D
//修改babel配置 baberc/babel.config.js
//添加:
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
//全局使用: 所有应用内部组件直接使用 <el-button></..>
import { Button } from 'element-ui';
Vue.component(Button.name, Button); | Vue.use(Button)
//局部使用: 只有当前组件可使用
import { Select, Option } from 'element-ui';
components:{
'bulala':Select,
[Option.name]:Option,
},
mintUI
安装
npm i mint-ui -S
整体引入
import Mint from 'mint-ui';
import 'mint-ui/lib/style.css'
Vue.use(Mint);
按需引入
//全局使用: npm install babel-plugin-component -D
import { Button } from 'mint-ui';
Vue.component(Button.name, Button);
//babel.config.js配置: 添加
"plugins": [
[
"component",
{
"libraryName": "mint-ui",
"style": true
}
]
]
//组件内部引入,只有当前组件可使用
import { Button } from 'mint-ui';
components:{
//'bulala':Button,
[Button.name]:Button,
},
下拉刷新上拉加载(Better-scroll)
介绍
better-scroll 是一款重点解决移动端(已支持 PC)各种滚动场景需求的插件。它的核心是借鉴的 iscroll 的实现,它的 API 设计基本兼容 iscroll,在 iscroll 的基础上又扩展了一些 feature 以及做了一些性能优化,不仅可以做普通的滚动列表,还可以做轮播图、picker 等等。better-scroll 是基于原生 JS 实现的,不依赖任何框架。
http://ustbhuangyi.github.io/better-scroll/doc/api.html
安装和使用
1、安装:npm install better-scroll --save
2、引入:import BScroll from ‘better-scroll’
3、使用:
let scroll = new BScroll(’.wrapper’,{
scrollY: true,
click: true
})
注意:
better-scroll 的初始化时机很重要,因为它在初始化的时候,会计算父元素和子元素的高度和宽度,来决定是否可以纵向和横向滚动。因此,我们在初始化它的时候,必须确保父元素和子元素的内容已经正确渲染了。如果子元素或者父元素 DOM 结构发生改变的时候,必须重新调用 scroll.refresh() 方法重新计算来确保滚动效果的正常
在vue脚手架里使用
假定组件文件为:./src/components/Books.vue
//1、引入:
import BScroll from ‘better-scroll’
//2、使用
<template>
<div class="wrapper">
<ul>
<div v-show="downShow">加载中…………………………</div>
<li v-for="(book,index) in books" :key="index">
<p>编号:{{book.id}}</p>
<img src="../assets/img/img1.jpg" />
<p>书名:{{book.name}}</p>
<p>价格:¥{{book.price}}</p>
</li>
<div v-show="upShow">加载中…………………………</div>
</ul>
</div>
</template>
<script>
import BScroll from "better-scroll";
export default {
name: "Books",
data: function() {
return {
downShow: false, //下拉时显示
upShow: false,//上拉时显示
books: [
{
id: "878913",
name: "红楼梦",
author: "曹雪芹",
price: 52,
type: "hot"
},
{
id: "01001",
name: "西游记",
author: "曹雪芹",
price: 52,
type: "recommend"
},
{
id: "01002",
name: "霸道总裁",
author: "王馨",
price: 50,
type: "recommend"
},
{
id: "01004",
name: "侠客行",
author: "金庸",
img: "img/img7.jpg",
price: 53,
type: "武侠"
},
{
id: "01005",
name: "绝代双骄",
author: "古龙",
img: "img/img8.jpg",
price: 58,
type: "武侠"
},
{
id: "01006",
name: "三体",
author: "王馨",
img: "img/img9.jpg",
price: 59,
type: "科幻"
},
{
id: "01007",
name: "流浪地球",
author: "魏瑞峰",
img: "img/img10.jpg",
price: 68,
type: "科幻"
}
],
scroll: null
};
},
mounted() {
//1、实例化 Better-scroll对象
this.scroll = new BScroll(".wrapper", {
scrollY: true, //开启纵向滚动。
//click: true,
pullDownRefresh: { //下拉刷新的配置
threshold: 30 // 当下拉到超过顶部 30px 时,触发 pullingDown 事件
},
pullUpLoad: { //上拉加载的配置
threshold: -50 // 在上拉到超过底部 50px 时,触发 pullingUp 事件
}
});
//2、绑定事件 pullingDown
this.scroll.on("pullingDown", () => {
this.downShow = true; //显示加载中……的文字或者图片
this.getDataUnshift(); //发送请求获取数据
});
//3、绑定事件 pullingUp
this.scroll.on("pullingUp", () => {
this.upShow = true; //显示加载中……的文字或者图片
this.getDataPush(); //发送请求获取数据
});
},
methods: {
getDataUnshift() {
setTimeout(() => {
let arr = [];
for (let i = 0; i < 5; i++) {
arr.push({
id: parseInt(Math.random() * 1000) + "",
name: "三国",
author: "罗贯中",
price: (Math.random() * 100).toFixed(2)
});
}
this.books = [...arr, ...this.books];
this.downShow = false; //隐藏加载中……的文字或者图片
this.$nextTick(() => {
this.refresh(); //渲染后要重新计算父子元素的高度
});
}, 1000);
},
getDataPush(cb) {
setTimeout(() => {
let arr = [];
for (let i = 0; i < 5; i++) {
arr.push({
id: parseInt(Math.random() * 1000) + "",
name: "三国",
author: "罗贯中",
price: (Math.random() * 100).toFixed(2)
});
}
this.books = [...this.books, ...arr];
this.upShow = false;//隐藏加载中……的文字或者图片
this.$nextTick(() => {
this.refresh(); //渲染后要重新计算父子元素的高度
});
}, 1000);
},
refresh() {
this.scroll.finishPullDown();
this.scroll.finishPullUp();
this.scroll.refresh(); //重新计算元素高度
}
}
};
</script>
<style scoped>
.wrapper {
width: 100%;
height: 800px;
}
img {
width: 100%;
}
</style>
vue脚手架项目从开发到打包完整的流程
一、git版本管理
1、建立远程仓库,邀请成员,分配权限
2、每个程序员 克隆项目(就会有本地仓库)
二、脚手架搭建和开发
1、项目负责人搭建脚手架,并且把项目上传到远端
1)、vue create 项目名 | vue init webpack 项目名
2)、项目负责人把脚手架的代码拷贝到本地仓库所在目录下。
3)、项目负责人把脚手架的代码上传到远端版本库。
git add .
git commit -m “”
git push 远端地址 main
4、各程序员下载远端的脚手架空项目
5、各程序员进行开发
1)、建立分支
2)、写代码
3)、上传:git add git commit git push
4)、 循环以上三步,直到当前功能开发完毕
5)、合并分支的代码到主分支
6)、上传主分支的代码到远端。
7)、每天重复以上六步,这就是程序员的开始。
6、注意点:
每次push的时候,应该先pull。
三、打包上线
1、项目负责人(或指派人员)打包
npm run build
结果在dist目录下。
1)、第一种情况:前后端不分离(在一台服务器上)
不存在跨域问题,所以不需要反向代理。那么,打包前需要看看baseURL要不要改。 /api
2)、第二种情况:前端后端分离
有跨域问题,但是,打包时,不需要做修改。
2、上线:
1)、第一种情况:前后端不分离(在一台服务器上)
把dist的目录的代码发给后端,后端统一传到互联网(www服务器)服务器上
2)、第二种情况:前端后端分离
跨域问题(反向代理),使用nginx解决。
前端www服务上使用nginx。把曾经在webpack中的反向代理配置,写在nginx里。使用ftp工具放在www服务器上。
后端会把自己的代码放在另外一台(www)服务器上
更多推荐
所有评论(0)