一:vue介绍
1:为什么要学习vue
-
饭碗级别技术
-
应用非常广泛 github
-
生态好(插件,ui库)
2:vue是什么
- 框架 提供了一整套东西 从一个框架切换成另一个框架不容易
- 库 jquery 提供了一些api 从一个库切换到另一个库很方便
渐进式JavaScript 框架
作者:尤雨溪
angular 谷歌 2013 指令系统
react facebook 2014 diff算法
vue 尤雨溪
4:vue的优点
- 渐进式框架
- 轻量级
- 数据双向绑定
- 指令系统 v-xx
- 组件化系统 --单页面应用
- 虚拟dom
5:vue缺点
兼容 ie9+
二:vue使用和下载
-
安装
- 生产环境 项目开发结束,部署上线阶段
- 开发环境 项目属于开发阶段(提示报错)
-
引入
开发版本
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
生产版本
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
-
使用
- 引入vue.js
- 实例化vue对象
- 作用域范围
代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <!-- 1:引入vue --> <script src="./js/vue.js"></script> </head> <body> <!-- 3:创建一个作用域范围 --视图层--> <div id="app"> {{msg}} </div> </body> <script> // console.log(Vue) // 操作数据 // var app=new Vue({配置信息}) // 2:实例化一个vue对象 var app=new Vue({ el:"#app", //vue实例的作用范围标签 data:{ //数据层 作用域范围内使用到的数据都在这里定义 msg:"中公it" } }) // 操作dom元素 // var app=document.getElementById('app'); // var arr=[11,22,33,44]; // var str="中公成都校区" // app.innerHTML=str; </script> </html>
错误集锦
[Vue warn]: Do not mount Vue to <html> or <body> - mount to normal elements instead.
//不能把vue挂载到html或者body
Vue is not defined
//vue.js文件没有引入
三:插值表达式和函数
vue核心:数据驱动(数据改变,视图层跟着改变) 声明式渲染
-
插值表达式 Mustache语法 双花括号 {{}}
-
语法:
{{变量/表达式/函数名()}} //注意:不能写if语句和for循环
-
- 代码
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
msg
{{msg}}
{{msg+'0511'}}
<hr/>
{{num}}
{{num+50}}
<hr/>
{{flag}}
{{flag?"中公it":"中公尤久业"}}
<hr/>
{{arr}}
<hr/>
{{obj.name}}{{obj.age}}
<hr/>
{{fn()}}
</div>
</body>
<script>
var app=new Vue({
el:"#app",
data:{
msg:"中公it",
flag:false,
num:25,
arr:["aaa","bbb","ccc"],
obj:{
name:"张三",
age:25
}
},
methods:{ //所有用到的方法都在这里定义
fn:function(){
alert("123")
},
fn1(){
console.log(123)
}
}
})
</script>
</html>
vue实例的配置信息
el 作用域范围标签
data 定义数据
methods 定义方法
四:vue指令
-
什么是指令:以v-开头 v-xx,给标签添加的一些新属性【指令就是标签的属性】
-
使用:
<标签名 v-指令名="变量/表达式"></标签名>
-
提供了哪些指令
-
v-html
-
用来进行数据绑定
-
v-html和{{}} 的区别: 如果还没渲染 v-html不会显示任何内容,{{}}会显示{{msg}};
v-html会解析字符串html,插值表达式不会解析;
-
-
v-text
- 用来进行数据绑定
-
v-html和v-text的区别
v-html会解析字符串html,v-text不会解析;
-
-
v-bind
-
实现属性绑定
百度
简写
百度```
- 代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>常用指令</title> <script src="./js/vue.js"></script> </head> <body> <div id="app"> <p>{{msg}}</p> <p v-html="msg"></p> <hr/> <p>{{htmlstr}}</p> <p v-html="htmlstr"></p> <hr/> <p v-text="msg"></p> <p v-text="htmlstr"></p> <hr/> <a v-bind:href="url">百度</a> <img v-bind:src="imgurl"/> <img :src="imgurl"/> <p :title="'p标记'">这是一个p标记</p> {{'p标记'}} </div> </body> <script> var app=new Vue({ el:"#app", data:{ msg:"中公it", htmlstr:"<a href='#'>百度</a>", url:"http://www.baidu.com", imgurl:"https://upload.jianshu.io/admin_banners/web_images/4995/af694827b911ab360fe44d5252422109849c5760.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540" } }) </script> </html>
-
动态样式绑定
-
class
-
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>常用指令</title> <script src="./js/vue.js"></script> <style> .col{ width: 100px; height: 100px; background-color: rebeccapurple; } .bg{ border: 2px solid red; } </style> </head> <body> <div id="app"> <div class="col bg"></div> <!-- 1:直接写一个变量赋值 --> <div :class="classN"></div> <!-- 2:class添加一个动态对象 用true或者false去控制 --> <div :class="classobj"></div> <div :class="{'col':false}"></div> <div :class="{'col':msg=='中公'}">123</div> <!-- 3:class添加[] 用三元运算符去控制 --> <div :class="[1==1?'col':'col bg']">456</div> <!-- 4:字符串拼接 --> <div :class="'col '+classN">789</div> </div> </body> <script> var app=new Vue({ el:"#app", data:{ classN:"bg", classobj:{ 'col':true, 'bg':true }, msg:"中公it", htmlstr:"<a href='#'>百度</a>", url:"http://www.baidu.com", imgurl:"https://upload.jianshu.io/admin_banners/web_images/4995/af694827b911ab360fe44d5252422109849c5760.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540" } })
-
-
-
style
动态绑定style的值是一个对象
如果属性名有"-" 需要加引号,若不想加引号要改成驼峰命名法 比如 font-size 改写成fontSize
- 代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> </head> <body> <!-- 动态绑定style的值是一个对象 如果属性名有"-" 需要加引号,若不想加引号要改成驼峰命名法 比如 font-size 改写成fontSize --> <div id="app"> <div style="width:100px;height:100px;background-color:red"></div> <div :style="{width:'100px',height:'100px','background-color':'pink'}"></div> <div :style="{width:size,height:'100px',backgroundColor:'pink'}"></div> <div :style="styleobj"></div> <div :style="[stylea,styleb]">各位帅哥美女们</div> {{msg}} </div> </body> <script> var app=new Vue({ el:"#app", data:{ size:"100px", msg:"123", styleobj:{ width:'100px', height:'100px', 'background-color':'pink' }, stylea:{ border:"5px solid red", fontSize:'330px' }, styleb:{ color:'blue' } } }) </script> </html>
-
-
v-if
-
作用:节点的插入和删除
-
注意:v-if和v-else-if还有v-else的标签要紧邻,否则报错
- 使用
<标签 v-if="变量/表达式">内容</标签> //值为true插入节点,值为false删除节点、 <标签 v-if="变量/表达式">内容</标签> <标签 v-else>内容</标签> <div v-if="变量/表达式">登录 注册</div> <div v-else-if="变量/表达式">xxx欢迎你</div> <div v-else-if="变量/表达式">xxx欢迎你</div> <div v-else>xxx欢迎你</div>
-
v-show
-
作用:元素的显示和隐藏(通过样式display控制显示隐藏)
-
什么时候使用v-if 什么时候使用v-show
频繁的切换元素 使用v-show,只有一两次切换元素使用v-if
-
使用
<标签 v-show="变量/表达式">内容</标签>
-
v-if和v-show的区别
- v-if true就插入节点,false删除节点[没有对应的html内容]
- v-show true就显示,false隐藏[css的display属性控制显示隐藏]
-
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> </head> <body> <!-- <标签 v-if="变量/表达式">内容</标签> 值为true插入节点,值为false删除节点 <标签 v-if="变量/表达式">内容</标签> <标签 v-else>内容</标签> <div v-if="变量/表达式">登录 注册</div> <div v-else-if="变量/表达式">xxx欢迎你</div> <div v-else-if="变量/表达式">xxx欢迎你</div> <div v-else>xxx欢迎你</div> --> <div id="app"> <div v-if="flag">登录 注册</div> <div v-if="!flag">xxx欢迎你</div> <div v-if="flag">登录 注册</div> <div v-else>xxx欢迎你</div> <div v-if="score>=90&&score<=100"> <p>优秀</p> </div> <div v-else-if="score<90&&score>70">良好</div> <div v-else>合格</div> <!--注意: v-if v-else-if v-else 所对应的标签必须紧邻,否则会报错 --> <div> <span v-show="msg=='国际'">国际</span> <span v-show="msg=='国内'">国内</span> </div> </div> </body> <script> var app=new Vue({ el:"#app", data:{ flag:true, score:89, msg:"国际" } }) </script> </html>
-
-
v-for
-
作用:用于对数组或对象进行遍历显示
-
使用
<标签 v-for="值 in 数组/对象"></标签> <标签 v-for="(值,键) in 数组/对象"></标签>
-
多层嵌套
- 注意:内层的键值对的标识符不能和外层的冲突
-
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> </head> <body> <!-- <标签 v-for="值 in 数组/对象"></标签> <标签 v-for="(值,键) in 数组/对象"></标签> //推荐 --> <div id="app"> <ul> <li v-for="(item,index) in arr"> {{index+1}}----{{item}} </li> </ul> <hr/> <ul> <li v-for="(val,key) in obj"> {{key}}--- {{val}} </li> </ul> <hr/> <ul> <li v-for="item in userarr"> {{item.name}}-{{item.age}}-{{item.sex}} <ol> <li v-for="val in item.scorearr"> {{val.scorename}}-{{val.scorenum}} </li> </ol> </li> </ul> </div> </body> <script> var app=new Vue({ el:"#app", data:{ arr:["aaa","bbb","ccc","ddd"], obj:{ name:"小小", age:21, sex:"男" }, userarr:[ {name:"张三",age:24,sex:"男", scorearr:[ {scorename:"china",scorenum:96}, {scorename:"english",scorenum:12}, {scorename:"math",scorenum:120} ] }, {name:"李四",age:23,sex:"男", scorearr:[ {scorename:"china",scorenum:96}, {scorename:"english",scorenum:12}, {scorename:"math",scorenum:120} ]}, {name:"狗蛋",age:25,sex:"男", scorearr:[ {scorename:"china",scorenum:96}, {scorename:"english",scorenum:12}, {scorename:"math",scorenum:120} ]}, {name:"翠花",age:21,sex:"女", scorearr:[ {scorename:"china",scorenum:96}, {scorename:"english",scorenum:12}, {scorename:"math",scorenum:120} ]} ] } }) </script> </html>
-
v-for的key值
https://cn.vuejs.org/v2/guide/list.html#%E7%BB%B4%E6%8A%A4%E7%8A%B6%E6%80%81>
”就地复用“问题 加key值, key唯一标识
当 Vue 正在更新使用
v-for
渲染的元素列表时,它默认使用“就地更新”的策略代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> </head> <body> <div id="app"> <div v-if="flag"> 姓名: <input type="text" key="a1" /> </div> <div v-else> 年龄: <input type="text" key="a2" /> </div> <ul> <li v-for="(item,index) in arr" :key="item.id"> <input type="checkbox" /> {{item.name}} {{index}} </li> </ul> </div> </body> <script> var app=new Vue({ el:"#app", data:{ flag:true, arr:[ {id:1,name:"香蕉"}, {id:2,name:"苹果"}, {id:3,name:"西瓜"}, {id:4,name:"梨"} ] } }) </script> </html>
-
-
五:总结
- vue的安装下载
- 生产环境
- 开发环境
- vue的模板
- 引入vue
- 新建vue实例
- el 挂载点 指定vue的作用域范围 body
- data 定义数据
- methods 定义方法
- 定义作用域范围
- vue的插值表达式 mustache语法 {{变量/表达式}} 详细点
- vue指令
- v-html
- v-text
- v-bind
- 动态绑定class 4种写法
- 动态绑定style
- v-if
- v-show
- v-if和v-show区别
- v-for v-for的key值
day 02
一:常用的指令
v-on 绑定事件
-
基础的事件绑定
-
使用
<标签 v-on:click="函数()"></标签> 简写 <标签 @click="函数()"></标签>
-
代码
-
-
事件传参
-
使用
<标签 v-on:click="函数(实参)"></标签>
new Vue({ el:"#app", data:{}, methods:{ 事件函数(形参){ } } })
-
-
this的使用
this指向的是vue实例
-
事件对象
-
使用
new Vue({ el:"#app", data:{}, methods:{ 事件函数(形参){ //没有传入实参,那么这个形参就是事件对象 } } })
<标签 @事件名="事件函数"></标签> //不需要传入实参
-
-
传实参还要有事件对象
-
使用
new Vue({ el:"#app", data:{}, methods:{ 事件函数(形参,事件对象形参){ } } })
<标签 @事件名="事件函数(实参,$event)"></标签> //形参和实参位置要一一对应
-
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> <style> .box{ width: 200px; height: 200px; background-color: pink; } </style> </head> <body> <div id="app"> <button v-on:click="fn()">点我</button> <button v-on:click="fn">点我</button> <button @click="fn()">点我</button> <ul> <li v-for="(item,index) in arr" :key="index"> {{item}} <button @click="del(index)"> 删除</button> </li> </ul> <div class="box" @mousemove="move"></div> <a href="http://www.baidu.com" @click="run('成都',$event)">百度</a> </div> </body> <script> var app=new Vue({ el:"#app", data:{ msg:"中公it", arr:["香蕉","葡萄","苹果"] }, methods:{ fn(){ alert("好好") }, del(ind){ //删除 // alert(ind) // this指向的是vue实例 console.log(this.msg) }, move(event){ //移动事件 // 如果函数没有传入实参,methods里面的事件函数定义的形参就是事件对象 console.log(event.offsetX) }, run(city,event){ //阻止默认行为 event.preventDefault(); alert(1111,city) } } }) // var box=document.querySelector(".box"); // box.οnmοusemοve=function(event){ // console.log(event.offsetX) // } // var a=document.querySelector("a") // a.οnclick=function(event){ // // 阻止元素的默认行为 // event.preventDefault() // alert(1111); // } </script> </html>
-
v-model
- 作用:表单元素数据绑定
架构思想:MVVM
数据双向绑定:数据层改变,视图层跟着改变; 视图层改变,数据层跟着改变;
修饰符
-
什么是修饰符: 修饰我们事件和v-model的符号,简化我们的一些操作
-
使用
<标签 @事件名.修饰符="事件函数"></标签名> <input v-model.修饰符="变量"/>
-
事件修饰符
https://cn.vuejs.org/v2/guide/events.html
-
.stop 阻止事件冒泡
-
.prevent 阻止默认行为
-
.self 只当在 event.target 是当前元素自身时触发处理函数
-
.once 一次性事件绑定
-
键盘修饰符 (键盘事件) .enter .up .down .left .right
-
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> <style> .box{ width: 300px; height: 300px; background-color: pink; } .small{ width: 150px; height: 150px; background-color: red; } </style> </head> <body> <div id="app"> <input type="text" @keydown.enter="down"/> <!-- 阻止默认行为 --> <a href="http://www.baidu.com" @click.prevent="fn">百度</a> <!-- 一次性绑定 --> <button @click.once="pay">确认支付</button> <div class="box" @click="fn1"> <!-- 阻止冒泡 --> <div class="small" @click.stop="fn2"></div> </div> <!-- 只当在 event.target 是当前元素自身时触发处理函数 --> <div class="box" @click.self="fn1"> <!-- 阻止冒泡 --> <div class="small" @click="fn2"></div> </div> <div class="box" @click="fn1"> <!-- 修饰符可以串联 --> <a href="http://www.baidu.com" @click.stop.prevent="fn">百度</a> </div> </div> </body> <script> var app=new Vue({ el:"#app", data:{ }, methods:{ fn(){ alert(1111); }, pay(){ alert("支付完成") }, fn1(){ alert("大盒子box") }, fn2(){ alert("小盒子small") }, down(){ console.log("enter"); } } }) </script> </html>
-
表单修饰符
.lazy 不会实时数据绑定,在changge事件中进行数据绑定
.number 将输入的内容数据类型转换为数值
.trim 过滤首尾空白字符
-
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> </head> <body> <div id="app"> <input type="text" v-model.lazy="username" /> {{username}} 年龄:<input type="text" v-model.number="age"/> <input type="text" v-model.trim="str"/> </div> </body> <script> var app=new Vue({ el:"#app", data:{ username:"", age:0, str:"" } }) </script> </html>
二:面试题
1:说说你对this的理解,如何改变this指向
this表示当前对象,this的指向是根据调用的上下文来决定的,默认指向window对象。
一、全局环境:
全局环境就是在里面,这里的this始终指向的是window对象。
二、局部环境:
1)在全局作用域下直接调用函数,this指向window。
2)对象函数调用,哪个对象调用就指向哪个对象。
3)使用 new 实例化对象,在构造函数中的this指向实例化对象。
4)使用call或apply改变this的指向。
day 03
一:生命周期
生命周期:一个vue实例生命的周期,也就是vue实例从开始到结束的过程,从创建到销毁的过程;
生命周期阶段
-
创建阶段
- beforeCreate 创建之前
- 作用:一般不使用,做一些需要在创建之前做的事情
- 注意:这里不能操作data里面的数据,因为data没有被初始化
- created 创建之后
- 作用:一般在这里发送数据请求,开启定时器
- 注意:这里是最早操作data里面的数据的地方
- beforeCreate 创建之前
-
挂载阶段
-
beforeMount 挂载之前
注意:在这个函数里,页面没有真正渲染,只是有了虚拟dom(js对象)
-
mounted 挂载之后
-
作用: 把虚拟dom真正的渲染到页面上,页面上有了真实dom
-
从这里开始可以使用$refs获取dom节点
-
初始化一些插件相关的配置,也可以发送数据请求
-
-
-
更新阶段
- beforeUpdate 更新之前
- 作用:做一些判断 是否需要更新
- updated 更新之后
- beforeUpdate 更新之前
-
销毁阶段
- beforeDestroy 销毁之前
- 作用 销毁之前做一些保存
- destroyed
- 作用 释放定时器相关的内容, 销毁之后,事件绑定都不存在了
- beforeDestroy 销毁之前
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
{{msg}}
<button @click="fn">点击</button>
</div>
</body>
<script>
var app=new Vue({
el:"#app",
data:{
msg:"中公it"
},
methods:{
fn(){
console.log(123)
}
},
//创建阶段
beforeCreate(){
//这里不能操作data里面的数据,因为data没有被初始化
console.log(this.msg);
console.log("创建之前");
console.log(document.querySelector("#app"));
},
created(){
// 一般在这里发送数据请求,开启定时器
console.log(this.msg);
console.log("创建之后");
console.log(document.querySelector("#app"));
},
//挂载阶段
beforeMount(){
console.log("挂载之前");
console.log(document.querySelector("#app"));
},
mounted(){
console.log("挂载之后");
console.log(document.querySelector("#app"));
},
//更新阶段
beforeUpdate(){
// 做一些判断 是否需要更新
console.log("更新之前");
console.log(document.querySelector("#app"));
},
updated(){
console.log("更新之后");
console.log(document.querySelector("#app"));
},
//销毁阶段
beforeDestroy(){
// 销毁之前做一些保存
console.log("销毁前");
},
destroyed(){
// 释放定时器相关的内容
// 销毁之后,事件绑定都不存在了
console.log("销毁后");
}
})
setTimeout(()=>{
app.$destroy(); //销毁vue实例
},10000)
</script>
</html>
二:监听器 watch
-
作用 :来监听data里面数据的变化,一旦数据变化了就会自动执行相关的函数
-
分类:
- 浅监听:监听基本数据类型
- 深监听:监听引用数据类型
-
使用
var app=new Vue({ el:"#app", data:{ a:2, b:0 }, watch:{ //监听器 data中的变量(新值,旧值){ //浅监听 //变量的值发生变化,就会执行这里的代码 }, data中变量:{ deep:true, //深监听 【监听引用类型】 handler(新值){ //代码块 } } } })
-
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> </head> <body> <div id="app"> <input type="text" v-model.number="a"/> + <input type="text" v-model.number="b"/> = <input type="text" v-model="c"/> <hr/> <p>姓名:{{user.name}}</p> <p>年龄:{{user.age}} <button @click="ageadd">年龄增长</button></p> </div> </body> <script> var app=new Vue({ el:"#app", data:{ a:0, b:0, c:0, user:{ name:"小小", age:25 } }, methods:{ ageadd(){ this.user.age++; } }, watch:{ a(newv,oldv){ // console.log(123) // console.log(newv) //变化之后的值 // console.log(oldv) //变化之前的值 this.c=newv+this.b }, b(newv,oldv){ this.c=newv+this.a }, user:{ //深度监听 deep:true, handler(newv){ console.log(1); console.log(newv) } } } }) </script> </html>
三:computed计算属性
-
含义:计算 动词 属性 名词
- 计算属性本质是属性,这个属性需要计算(函数)得到
- 使用场景 :购物车
-
使用
var app=new Vue({ el:"#app", data:{ }, //定义默认数据的地方 computed:{ //通过计算得到属性 属性名:function(){ //值一定是一个函数,而且这个函数一定要有返回值 return 值; } } } })
{{属性名}}
-
注意:
- 计算属性的值一定是一个函数,而且这个函数一定要有返回值
- 计算属性的函数里面的this同样是当前vue实例
- 页面第一次加载时,计算属性的函数会执行,给属性赋初始值
- 依赖属性变化的时候,计算属性的函数会再次执行
- 计算属性可以直接在模板里渲染,说明他也是当前vue实例对象的属性
-
面试题:watch和computed的区别?
watch: 监听data和computed中数据的变化;可以进行异步操作;
computed: 定义属性(该属性通过计算得到);不能进行一步操作;通过return 返回处理的数据;
-
computed的get和set
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> </head> <body> <div id="app"> <input type="text" v-model.number="a"/> + <input type="text" v-model.number="b"/> = <input type="text" v-model="c"/> {{c}} </div> </body> <script> var app=new Vue({ el:"#app", data:{ a:0, b:0 // ,c:0 }, computed:{ // c(){ // // 页面第一次加载时,该函数会(第一次)默认执行,给属性赋初始值 // //依赖属性变化的时候,该函数会再次执行 // console.log("computed") // return this.a+this.b //this同样是vue实例 // } c:{ get(){ //c被获取时执行这里(直接读取c的值不会执行) console.log("get"); return this.a+this.b; }, set(v){ //c被设置时执行这里 v就是设置的值 console.log("set",v); } } } }) </script> </html>
错误集锦
1:Method “allcheck” has already been defined as a data property.
data中的变量名和methods中的方法名冲突了
day 04
组件
- 组件化开发优势:复用性强 ,便于后期维护和开发
- 什么是组件:包含了html,css,js的一个整体(js对象),是一个独立的功能
- 单页面应用(SPA)
- VUE的核心:数据驱动,组件化
一:组件使用
1-1:组件的注册
1:全局注册
-
语法
Vue.component('组件名',{组件配置对象})
-
使用(调用)组件
<组件名></组件名>
-
注意:
- 全局注册组件需要放在vue实例化之前,所有的vue实例都可以使用
- template指定了组件的模板信息
-
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> <style> ul{ padding: 0; margin: 0; list-style: none; width: 500px; display: flex; height: 50px; border: 1px solid; } li{ width: 99px; height: 50px; border-right: 1px solid; line-height: 50px; text-align: center; } .active{ background-color: yellow; color:red; } </style> </head> <body> <div id="app"> <mycom></mycom> <hr/> <mycom></mycom> </div> <h3>---------分割线-------</h3> <div id="main"> <mycom></mycom> </div> </body> <script> Vue.component("mycom",{ // template指定组件的模板 template: "<div><a href='#'>全局组件</a></div>" }) // 全局注册组件需要放在vue实例化之前,所有的vue实例都可以使用 var app=new Vue({ el:"#app", data:{ } }) var main=new Vue({ el:"#main", data:{} }) </script> </html>
2:局部注册
-
语法
var app=new Vue({ el:"#app", data:{ }, components:{ 组件名:{配置对象}, 组件名:{配置对象} } })
-
使用
<组件名></组件名>
-
注意
- 局部组件只能在当前所在的实例去使用
-
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> <style> ul{ padding: 0; margin: 0; list-style: none; width: 500px; display: flex; height: 50px; border: 1px solid; } li{ width: 99px; height: 50px; border-right: 1px solid; line-height: 50px; text-align: center; } .active{ background-color: yellow; color:red; } </style> </head> <body> <div id="app"> <mycon></mycon> </div> <h3>---------分割线-------</h3> <div id="main"> <!-- <mycon></mycon> --> </div> </body> <script> var app=new Vue({ el:"#app", data:{ }, components:{ // 组件名:{配置对象} // 局部组件只能在当前所在的实例去使用 mycon:{ template:` <div> <h3>我是局部组件</h3> </div> ` } } }) var main=new Vue({ el:"#main", data:{} }) </script> </html>
1-2:组件命名:
- 1:不能是html标签名(html新增的语义标签:header,nav,main,aside,section,slider,article,footer…)
- 2:命名尽量语义化
- 3:组件名不能使用驼峰命名了,使用组件的时候需要用- 链接 比如组件名为myHead 使用
- 4:单标签只可以使用一次,后面的将不再显示
1-3:template配置项
-
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> <style> ul{ padding: 0; margin: 0; list-style: none; width: 500px; display: flex; height: 50px; border: 1px solid; } li{ width: 99px; height: 50px; border-right: 1px solid; line-height: 50px; text-align: center; } .active{ background-color: yellow; color:red; } </style> </head> <body> <div id="app"> <mycon></mycon> <mybb></mybb> </div> <template id="aa"> <div> <div> <h3>我是局部组件</h3> </div> <div>789</div> </div> </template> <template id="bb"> <div>123</div> </template> </body> <script> var app=new Vue({ el:"#app", data:{ }, components:{ // 组件名:{配置对象} // 局部组件只能在当前所在的实例去使用 mycon:{ template:"#aa" }, mybb:{ template:"#bb" } } }) </script> </html>
错误集锦
1:Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.
组件模板有且只有一个根元素
2:Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the “name” option.
不知道元素 你确定注册了这个组件了吗?
1-4:组件嵌套
-
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> <style> ul{ padding: 0; margin: 0; list-style: none; width: 500px; display: flex; height: 50px; border: 1px solid; } li{ width: 99px; height: 50px; border-right: 1px solid; line-height: 50px; text-align: center; } .active{ background-color: yellow; color:red; } </style> </head> <body> <div id="app"> <mycon></mycon> <hr/> <mybb></mybb> </ </body> <script> Vue.component("mycd",{ template:"<a href='#'>00000</a>" }) var app=new Vue({ el:"#app", data:{ }, components:{ // 组件名:{配置对象} // 局部组件只能在当前所在的实例去使用 mycon:{ template:` <div> mycon <hr/> <mycd></mycd> <aa></aa> </div> `, components:{ aa:{ template:` <div>aaa</div> ` } } }, mybb:{ template:` <div> mybb </div> ` } } }) </script> </html>
1-5:组件的配置选项(重点)
-
The “data” option should be a function that returns a per-instance value in component definitions.
data选项应该是一个函数
-
组件中的data是一个函数,返回一个对象
- 组件的data不能是一个对象,因为一旦是一个对象,所有组件都会共享一个数据源,造成了数据的污染
- 组件data必须是一个函数,函数返回一个对象,每次返回都会重新创建一个新对象
- 组件的数据应该是独立的
-
template是指定模板的
-
其他配置选项和vue实例一模一样的
-
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/vue.js"></script> <style> ul{ padding: 0; margin: 0; list-style: none; width: 500px; display: flex; height: 50px; border: 1px solid; } li{ width: 99px; height: 50px; border-right: 1px solid; line-height: 50px; text-align: center; } .active{ background-color: yellow; color:red; } </style> </head> <body> <div id="app"> <mycon></mycon> <hr/> <mybb></mybb> </div> </body> <script> Vue.component("mycd",{ template:` <div> mycd全局组件 {{msg}} <button @click="fn">更改msg</button> </div> `, data:function(){ return { msg:"成都" } }, methods:{ fn(){ this.msg="nihao" } } }) var app=new Vue({ el:"#app", data:{ }, components:{ // 组件名:{配置对象} // 局部组件只能在当前所在的实例去使用 mycon:{ template:` <div> mycon <hr/> <mycd></mycd> <aa></aa> </div> `, components:{ aa:{ template:` <div>aaa</div> ` } } }, mybb:{ template:` <div> mybb </div> ` } } }) </script> </html>
二:vue-cli vue脚手架开发
- vue-cli脚手架工具:快速搭建一个vue项目
2.1 、 安装vue-cli
确保电脑安装了node环境,输入以下命令检测是否安装了node
node -v //显示版本号说明安装了node 如果提示node不是内部命令,那就就去安装node
node的安装地址:https://nodejs.org/en/download/
npm install vue-cli -g //全局安装脚手架工具
如果电脑上安装了cnpm淘宝镜像,可以使用cnpm 安装
cnpm install vue-cli -g
如果没有淘宝镜像先安装淘宝镜像
npm install cnpm -g --registry=http://registry.npm.taobao.org
z执行完了 执行 cnpm/npm install vue-cli -g 该命令 安装脚手架工具
vue -V //显示版本号说明安装成功
2.9.6
2.2 、初始化vue项目
-
找到需要存储项目的目录
-
在该目录下执行命令
vue init webpack 项目名(英文)
-
启动项目
npm run dev or npm start
day 05
一:回顾
1-1、组件
- 组件的含义:包含了html,css,js的独立的js对象(vue实例)
- vue是单页面应用
- 组件化开发优势:复用性强,便于后期维护和开发
1-2、组件定义
-
局部注册
-
全局注册
Vue.component("组件名",{ template:`<div>有且只能有一个根元素</div>`, data(){ return { } } })
1-3、组件命名规范
1-4、组件嵌套
1-5、组件的配置项
1-6、vue-cli脚手架
错误
命令行出现:
canot found module ‘user-home’
找不到模块
发给别人一个vue项目
-
删除项目目录下的node_modules包
-
压缩项目文件夹,将文件夹发给别人即可
如何运行别人发给你的vue项目
- 把别人发给你的项目文件,解压
- 进入解压后的文件夹里 cmd
- 运行 cnpm i 或者npm i 下载项目依赖包
- 下载完成后,执行npm run dev
二:脚手架搭建的项目中组件使用
-
定义组件
-
在components目录下新建xxx.vue文件
-
在xxx.vue文件下写入代码
<template> <div> 这个是myhead组件 </div> </template> <script> export default { } </script> <style> </style>
-
-
使用组件
-
引入,注入,调用
// 组件模板 <template> <div> 我的第一个vue项目 <!-- 3:调用 --> <Myhead></Myhead> </div> </template> // js <script> // 1:引入 import Myhead from './components/Myhead' export default { components:{ //2:注入 Myhead } } </script>
-
三:组件数据通信
- 组件之间数据的相互使用
3-1、父向子通信
-
1:父组件调用子组件,给子组件自定义属性
//父组件 <子组件 属性1="值" 属性2="值"></子组件> <子组件 :属性="变量"></子组件>
-
2:子组件通过props接收父组件传来的自定义属性
<script> export default { props:['属性1','属性2'] } </script>
-
3:子组件视图上直接使用该自定义属性
{{属性1}} <li v-for="item in 属性1"></li>
-
代码
父组件
// 组件模板 <template> <div> <!-- 1 自定义属性 --> <Myhead :mycon="num" :mycom="flag" :dataarr="arr[0]"></Myhead> <Myhead :dataarr="arr[1]"></Myhead> <Myhead :dataarr="arr[2]"></Myhead> <hr/> <Myhead v-for="(item,index) in arr" :key="index" :dataarr="item"></Myhead> </div> </template> // js <script> import Myhead from './components/Myhead' export default { data(){ return { flag:true, num:25, arr:[ { title:"学院动态", list:[ '中国上市企业市值500强出炉 中公教育位居', '中国上市企业市值500强出炉 中公教育位居', '中国上市企业市值500强出炉 中公教育位居' ] }, { title:"学员活动", list:[ '中国上市企业市值500强出炉 中公教育位居1', '中国上市企业市值500强出炉 中公教育位居1', '中国上市企业市值500强出炉 中公教育位居1' ] }, { title:"常见问题", list:[ '中国上市企业市值500强出炉 中公教育位居2', '中国上市企业市值500强出炉 中公教育位居3', '中国上市企业市值500强出炉 中公教育位居4' ] } ] } }, components:{ Myhead } } </script> // css <style> </style>
子组件
<template> <div> <div class="list"> <!-- 3:直接使用属性 --> {{mycon}} ----{{mycom}} <h3>{{dataarr.title}}</h3> <ul> <li v-for="(item,index) in dataarr.list" :key="index"> {{item}} </li> </ul> </div> </div> </template> <script> export default { props:['mycon','mycom','dataarr'], //2:通过props接收父组件传来的数据 data(){ return { arr:[ "中国上市企业市值500强出炉 中公教育位居", "中国上市企业市值500强出炉 中公教育位居", "中国上市企业市值500强出炉 中公教育位居" ] } } } </script> <style> </style>
props验证
-
为了使组件内部使用数据的可靠性,我们对外部传递进来的数据进行校验
-
如何实现校验
//方式1 props:['属性1','属性2']
//方式2 (String,Number,Boolean,Array,Object Date Function Symbol) props:{ 属性1:类型 , //只有类型检测 属性2:[类型1,类型2] }
//方式3 props:{ 属性1:{ type:类型 , default:默认值, required:true/false, validator:function(val){ //validator函数一定要有返回值,返回true表示验证通过,false验证不通过 控制台有警告 } } }
代码
<template> <div> <div class="list"> <!-- 3:直接使用属性 --> {{mycon}} ----{{mycom}} <h3>{{dataarr.title}}</h3> <ul> <li v-for="(item,index) in dataarr.list" :key="index"> {{item}} </li> </ul> </div> </div> </template> <script> export default { // props:['mycon','mycom','dataarr'], //2:通过props接收父组件传来的数据 // props验证,为了使组件内部使用数据的可靠性,我们对外部传递进来的数据进行校验 props:{ // mycon:String , //类型验证 mycon:{ type:String, default:"hellworld" //默认值 }, // mycom:[String,Number], mycom:{ type:String, required:false, //是否必须传入 true必须 false可以不传入 validator:function(val){ // 验证函数 // validator函数一定要有返回值,返回true表示验证通过,false验证不通过 控制台有警告 if(val.length<5){ return true; }else{ return false; } } }, dataarr:{ type:Object //类型校验 } }, data(){ return { arr:[ "中国上市企业市值500强出炉 中公教育位居", "中国上市企业市值500强出炉 中公教育位居", "中国上市企业市值500强出炉 中公教育位居" ] } } } </script> <style> </style>
注意:vue是单向数据流,子组件不能修改props中的属性,但是在父组件中修改,传到子组件的数据也会跟着变化
代码
父组件
<template> <div> {{num}} <button @click="fn">修改num</button> <hr/> <Myfoot :n="num"></Myfoot> </div> </template> <script> import Myfoot from './components/Myfoot' export default { data(){ return { num:25 } }, components:{ Myfoot }, methods:{ fn(){ console.log(this.num) this.num++ } } } </script> // css <style> </style>
子组件
<template> <div> {{n}}--{{m}} </div> </template> <script> export default { props:['n'], data(){ return { m:6 } } } </script>
3-2、子向父通信
-
1:父组件调用子组件时,给子组件自定义事件
//父组件 <Child @自定义事件名="函数"></Child>
-
2:在父组件中的methods中定义自定义事件对应的函数
//父组件 methods:{ 函数名(){} }
-
3:在子组件中通过this.$emit(‘事件名’,参数)触发自定义事件
//子组件 this.$emit('事件名',参数)
-
代码
父组件
<template> <div> {{m}} <hr/> <!-- 1:自定义事件 @abc="函数名" --> <Child @abc="fn"></Child> </div> </template> <script> import Child from './components/Child' export default { data(){ return { m:"" } }, components:{ Child }, methods:{ fn(val){ //2:父组件中定义自定义事件对应的函数fn console.log("wo 被触发了") // val就是触发abc事件传递过来的参数 this.m=val; } } } </script>
子组件
<template> <div> {{msg}} <button @click="change">触发abc</button> </div> </template> <script> export default { data(){ return { msg:"子组件==成都" } }, created(){ // 3:触发自定义事件的 // this.$emit('事件名',参数) // console.log(this) // this.$emit('abc',this.msg) }, methods:{ change(){ this.$emit('abc',this.msg) } } } </script>
3-3、非父子通信
中央事件总线:eventbus
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6D9S7l4D-1611110597372)(F:\Web\vue\day05\参考\day05\非父子组件传值.png)]
需求:非父子组件A和B,从A组件向B组件传值
-
1:main.js中定义一个公共区域 (新的vue实例)
// 1:创建公共区域(新的vue实例) var eventbus=new Vue(); // 2:公共区域挂载到Vue的原型上面(每一个vue实例都可以访问它原型上的成员) Vue.prototype.$bus=eventbus;
-
2:在B组件中给公共区域自定义事件
<template> <div> {{m}} </div> </template> <script> export default { data(){ return { m:"123" } }, created(){ // // console.log(this,111) // var that=this; // // 公共区域自定义事件--接收值 // this.$bus.$on('abc',function(v){ // console.log("我被触发了",v) // console.log(this) // that.m=v // }) //-------------- // console.log(this,111) // 公共区域自定义事件--接收值 this.$bus.$on('abc',(v)=>{ console.log("我被触发了",v) console.log(this) this.m=v }) } } </script>
-
3:在A组件中触发公共区域的自定义事件
<template> <div> <button @click="fn">传值</button> </div> </template> <script> export default { data(){ return { msga:"a向b传值" } }, methods:{ fn(){ // 触发公共区域的自定义事件传值 this.$bus.$emit('abc',this.msga) } } } </script>
day 06
一:回顾
父向子
子向父
非父子
二:ref
ref 被用来给元素或子组件注册引用信息
2-1、ref 注册在普通dom元素上
获取到的是dom元素
2-2、ref加载子组件上
可以获取或使用组件的所有数据和方法
2-3、ref加载循环渲染
利用v-for和ref获取一组数组
注意:
1:ref需要在dom渲染完成后才会有,使用的时候确保dom已经渲染完成,比如
mounted钩子函数中可以使用
2:ref如果是循环出来的,ref的值是一个数组,要拿到单个的ref只需要循环就可以了
代码
<template>
<div>
<!--1 ref加在普通的dom元素上 -->
<h2 ref="myh2">中公成都校区在施工</h2>
<input type="text" ref="myinput" />
<button @click="fn">获取h2</button>
<!--2 ref应用在循环渲染 -->
<ul>
<li ref="list" v-for="(item,index) in arr" :key="index">
{{item}}
</li>
</ul>
<button @click="fn1">获取li元素</button>
<!-- 3ref设置在子组件上 -->
<Myref ref="myref"></Myref>
<button @click="fn2">获取子组件</button>
</div>
</template>
<script>
import Myref from './components/Myref'
export default {
data(){
return {
arr:['aaa','bbb','cccc']
}
},
methods:{
fn(){
// 所有的ref的注册信息都会注册到组件的$refs对象上
console.log(this.$refs)
console.log(this.$refs.myh2.innerHTML)
},
fn1(){
// ref注册到循环渲染的元素上,得到的是一个数组[li,li,li]
console.log(this.$refs.list[0].innerHTML)
},
fn2(){
console.log(this.$refs.myref.msg)
this.$refs.myref.msg="hello 晋老师"
}
},
components:{
Myref
}
}
</script>
三:vue中使用jquery
3-1:外部链接
在index.html中,通过script标签直接引入外部链接
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>myvue</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
3-2、npm
安装
cnpm install jquery -D
- 1:局部使用(那个组件用,那个组件引用)
import $ from 'jquery'
- 2:全局使用(所有的组件都可以用)
//main.js
import $ from 'jquery'
Vue.prototype.$=$
//在每一个组件中使用
this.$("#box").hide()
四:插槽(Slot)
插槽用于将所携带的内容,插入到指定的某个位置
父组件向子组件传html标签
4-1、匿名插槽
-
使用
-
子组件
<template> <h2>成都中公</h2> <slot></slot> <div>我是成都学员-程序员</div> </template>
-
父组件
//msg数据在父组件中定义 <Mychild> <p>{{msg}}</p> </Mychild>
-
-
代码
父组件
<template> <div> <Mychild> <ul> <li v-for="(item,index) in arr" :key="index">{{item}}</li> </ul> </Mychild> <hr/> <Mychild> <p>{{msg}}</p> </Mychild> </div> </template> <script> import Mychild from './components/Mychild' export default { data(){ return { msg:"我是p标签123", arr:['aaa','bbb','ccc'] } }, methods:{ }, components:{ Mychild } } </script>
子组件
<h2>成都中公</h2>
<slot></slot>
<div>我是成都学员-程序员</div>
</div>
}
# 4-2、具名插槽
- 代码
- 父组件
```html
<template>
<div>
<Mychild>
<div slot="foot">
我是cen要的内容
</div>
<div slot="cen">
我是子组件的内容
</div>
</Mychild>
</div>
</template>
<script>
import Mychild from './components/Mychild'
export default {
data(){
return {
msg:"我是p标签123",
arr:['aaa','bbb','ccc']
}
},
methods:{
},
components:{
Mychild
}
}
</script>
```
- 子组件
```html
<template>
<div>
<!-- slot可以有默认值,如果找不到就显示默认值 -->
<h2>成都中公</h2>
<slot name="cen"></slot>
<div>我是成都学员-程序员</div>
<slot name="foot">
默认值
</slot>
</div>
</template>
<script>
export default {
}
</script>
```
# 4-3、作用域插槽(难)
> 子组件做循环(循环的结构从父组件传来)或者某一部分他的dom结构应该由外部传递进来的时候,使用作用域插槽
>
> 使用作用域插槽,子组件会向父组件的作用域里传递数据,父组件使用插槽,要包含在template中
- 语法
v-bind:自定义prop名=“数据” //子组件中传值
<template v-slot:插槽名字=“props”>
{{props.prop名}}
- 代码
父组件
```html
<template>
<div>
<Mychild>
<!-- <template v-slot:list="props">
<li>{{props.item}}</li>
</template> -->
<template v-slot:one="props">
<div>{{props.msg}}---{{props.tit}}</div>
</template>
</Mychild>
</div>
</template>
<script>
import Mychild from './components/Mychild'
export default {
data(){
return {
msg:"我是p标签123",
arr:['aaa','bbb','ccc']
}
},
methods:{
},
components:{
Mychild
}
}
</script>
子组件
<template>
<div>
<!-- <ul>
<slot name="list" v-for="item in arr" :item="item"></slot>
</ul> -->
<slot name="one" :msg="msg" :tit="name"></slot>
<slot name="two"></slot>
</div>
</template>
<script>
export default {
data(){
return {
arr:[
'香蕉','苹果','西瓜'
],
msg:"nihao",
name:"张三"
}
}
}
</script>
五:is属性
5-1、改变默认标签的约束
ol ul table他们的内部存放的标签是有约束的
-
代码
父组件
<template> <div> <table> <tr is="Mytd"> </tr> </table> </div> </template> <script> import Mychild from './components/Mychild' import Mytd from './components/Mytd' export default { data(){ return { } }, methods:{ }, components:{ Mychild, Mytd } } </script>
子组件
<template>
<div>
<h3>123</h3>
</div>
</template>
5-2、动态组件
使用is属性渲染组件的内容
代码
<template>
<div>
<ul>
<li @click="fn('Catgory')">分类</li>
<li @click="fn('Cart')">购物车</li>
<li @click="fn('Mine')">我的</li>
</ul>
<div :is="com">
</div>
</div>
</template>
<script>
import Catgory from './components/Catgory'
import Cart from './components/Cart'
import Mine from './components/Mine'
export default {
data(){
return {
com:'Catgory'
}
},
methods:{
fn(val){
this.com=val;
}
},
components:{
Catgory,
Cart,
Mine
}
}
</script>
day 06+
一:回顾
-
ref dom元素和子组件 this.$refs
- 普通元素
- 循环遍历元素 -数组
- 子组件
-
vue中jquery的使用
-
外部链接 index.html script
-
npm
-
局部 import $ from ‘jquery’
-
全局
import $ from ‘jquery’
Vue.prototype. = = =
-
-
-
插槽 slot
- 匿名插槽
- 具名插槽
- 作用域插槽
-
is属性
- 改变标签默认约束
- is动态组件
二:路由
SPA:single page application单页面应用,只有一个页面,不断切换内部展示的内容
路由:根据不同的url地址,映射不同的组件内容
2-1、路由(vue-router)安装
1:安装vue-router
npm/cnpm i vue-router --save
2:配置路由 —main.js
import Vue from 'vue'
import App from './App.vue'
// 1:引入vue-router
import VueRouter from 'vue-router'
// 引入组件
import Index from './components/Index'
import Mya from './components/Mya'
// 2:给vue安装路由插件,让每一个组件中都可以使用router-link和router-view组件
Vue.use(VueRouter);
// /:3: 路由配置---路由映射关系
var routes=[
{
path:'/',
component:Index
},
{
path:'/mya',
component:Mya
}
]
// 4:实例化路由对象
var router=new VueRouter({routes:routes})
Vue.config.productionTip = false
new Vue({
el: '#app',
// 5:把路由对象注入到根实例中
// 在每一个组件中都可以使用this.$router和this.$route
router:router,
components: { App },
template: '<App/>'
})
3:在组件中使用router-link 和router-view组件
<router-link to="/about">关于我们</router-link>
渲染成 <a href="/about">关于我们</a>
<router-view></router-view>
路由出口 网址上访问 /about router-view的位置上映射该路由对应的组件
2-2:路由的封装
1:src目录下新建router/index.js,index.js中写入如下代码
import Vue from 'vue'
// 1:引入vue-router
import VueRouter from 'vue-router'
// 2:给vue安装路由插件,让每一个组件中都可以使用router-link和router-view组件
Vue.use(VueRouter);
// 引入组件
import Index from '../components/Index'
import Mya from '../components/Mya'
import Myb from '../components/Myb'
// /:3: 路由配置---路由映射关系
var routes=[
{
path:'/',
component:Index
},
{
path:'/mya',
component:Mya
},
{
path:'/myb',
component:Myb
}
]
// 4:实例化路由对象
var router=new VueRouter({routes:routes})
// 导出router,vuerouter实例
export default router;
2:在main.js中导入router
// 把路由实例导入
import router from './router'
new Vue({
el: '#app',
// 5:把路由对象注入到根实例中
// 在每一个组件中都可以使用this.$router和this.$route
// router:router,
router,
components: { App },
template: '<App/>'
})
3:组件中使用router-link和router-view组件即可
2-3:路由重定向–404配置
// /:3: 路由配置---路由映射关系
var routes=[
{
path:'/',
component:Index
},
{
path:'*', //*是所有的,上面没有匹配到就都渲染这里
// redirect:'/' //redirect重定向
// 还可以映射到到一个组件中404
component:Notfound
}
]
2-4:路由懒加载
需要时再加载
//上面不需要再引入组件
var routes=[
{
path:'/',
component:()=>import('../components/Index')
},
{
path:'/mya',
component:()=>import('../components/Mya')
},
{
path:'/myb',
component:()=>import('../components/Myb')
},
{
path:'*', //*是所有的,上面没有匹配到就都渲染这里
// redirect:'/' //redirect重定向
// 还可以映射到到一个组件中404
component:()=>import('../components/Notfound')
}
]
2-5、编程式导航
-
声明式导航:html中的router-link组件实现页面跳转
-
编程式导航:通过js代码实现页面跳转
-
向history中添加记录
<router-link to="/"></router-link>
this.$router.push('/mya') this.$router.push({path:'/mya'}) this.$router.push({path:'prodetail',query:{id:1}}) //只能是query数据 this.$router.push({name:'detail',params:{newsid:2}}) //对应params数据
-
不会向history中添加记录
this.$router.replace('/mya')
-
历史记录的前进及后退
this.$router.go(-1) //后退 this.$router.go(1) //前进
-
代码
index.vue
<template> <div> index <button @click="fn"> 跳转到mya </button> </div> </template> <script> export default { methods:{ fn(){ // 跳转到mya // 会添加到history中,router-link也会添加 console.log(this.$router) // this.$router.push('/mya') this.$router.push({path:'/mya'}) // 不会添加到history中 // this.$router.replace('/myb') } } } </script>
mya.vue
<template> <div> <button @click="fn">传值</button> <button @click="go">前进</button> <button @click="back">后退</button> </div> </template> <script> export default { data(){ return { msga:"a向b传值" } }, methods:{ fn(){ // 触发公共区域的自定义事件传值 this.$bus.$emit('abc',this.msga) } , go(){//前进 this.$router.go(1) } , back(){//后退 this.$router.go(-1) } } } </script>
-
2-6、动态路由
http://www.abc.com/newsdetial/102
-
newsdetial:表示新闻详情路由
-
102 表示新闻id
那么怎么配置这样的路由呢?
路由规则页面
var routes=[ { path:'/detail/:newsid', // 匹配detail/5 component:()=>import('../components/Detail') } ]
-
从某组件中跳转到Detail组件中
<router-link to="/detail/1"></router-link>
-
Detail.vue组件中获取动态参数
this.$route.params.newsid
-
代码
index.vue
<template> <div> index <ul> <li v-for="(item,index) in arr" :key="index"> <!-- <router-link to="/detail/2"> --> <router-link :to="'/detail/'+item.id"> {{item.title}} </router-link> </li> </ul> </div> </template> <script> export default { data(){ return { arr:[ {id:1,title:"张钟文考上清华了"}, {id:2,title:"涛涛明天结婚"}, {id:3,title:"成都下雨了"}, ] } } } </script>
Detail.vue
<template> <div> detail {{obj.title}} </div> </template> <script> export default { data(){ return { arr:[ {id:1,title:"张钟文考上清华了"}, {id:2,title:"涛涛明天结婚"}, {id:3,title:"成都下雨了"}, ], obj:{} } }, created(){ // console.log(this.$route.params.newsid) //{newsid: "102"} // 获取网址上的动态newsid var id=this.$route.params.newsid; // 根据获取到的id查找对应的下标 var index=this.arr.findIndex(item=>{ return item.id==id }) // 根据下标找到数组对应的值赋给obj,展示到页面上 this.obj=this.arr[index] } } </script>
2-7、路由参数
路由中传递 参数除了刚才的params外,还可以使用query数据
http://www.xx.com/p?id=8
路由规则页面
var routes=[
{
path:'/prodetail', // 匹配 prodetail
component:()=>import('../components/Prodetail')
}
]
从某组件中跳转到Prodetail组件中
<router-link to="/prodetail?proid=8">
<router-link :to="'/prodetail?id='+item.id">
Prodetail.vue组件中获取路由参数
this.$route.query.id
代码
-
index.vue
<ul> <li v-for="(item,index) in arr" :key="index"> <!-- <router-link to="/prodetail?proid=8"> --> <router-link :to="'/prodetail?id='+item.id"> {{item.title}} </router-link> </li> </ul>
-
Prodetail.vue
<template> <div> prodetail </div> </template> <script> export default { created(){ console.log(this.$route.query.id) } } </script>
-
面试题
r o u t e r 和 router和 router和route的区别?
route这里存储的是属性,比如route.query、route.params
router这里存储的是方法,比如router.push()、router.replace()、router.go(-1)
2-8、嵌套路由
-
路由配置
var routes=[ { path:'/myb', component:()=>import('../components/Myb'), children:[ { path:'/myb/aa', component:()=>import('../components/Myaa'), }, { path:'/myb/bb', component:()=>import('../components/Mybb'), } ] }, ]
-
Myb组件中
给谁设置二级路由,就在他的组件里写router-link和router-view
<template> <div> <router-link to="/myb/aa">aa</router-link> <router-link to="/myb/bb">bb</router-link> <div class="box"> <router-view></router-view> </div> </div> </template> <script> export default { data(){ return { m:"123" } }, created(){ // // console.log(this,111) // var that=this; // // 公共区域自定义事件--接收值 // this.$bus.$on('abc',function(v){ // console.log("我被触发了",v) // console.log(this) // that.m=v // }) //-------------- // console.log(this,111) // 公共区域自定义事件--接收值 this.$bus.$on('abc',(v)=>{ console.log("我被触发了",v) console.log(this) this.m=v }) } } </script> <style> .box{ width: 500px; height: 500px; background-color: pink; } </style>
2-9、路由进阶
1-路由模式
-
hash模式 # 默认模式
-
history模式
var router=new VueRouter( {routes, mode:'history' } )
2-命名路由
-
路由规则
var routes=[ { path:'/mya', name:'minea', //命名路由 component:()=>import('../components/Mya') } ]
-
路由跳转
//1种 <router-link :to="{name:'detail',params:{newsid:item.id}}"> xxxxxx </router-link> //2种 <router-link :to="{name:'minea'}"> Mya </router-link> //3种 this.$router.push({name:'detail',params:{newsid:2}})
3-别名
4-命名视图
一个组件里有多个router-view时,我们要给router-view定义name属性
router-view添加name ,Myb组件
<router-link to="/myb/aa">aa</router-link>
<router-link to="/myb/bb">bb</router-link>
<!-- 默认的 -->
<router-view></router-view>
<div class="box">
<router-view name="a"></router-view>
</div>
<router-view name="b"></router-view>
路由规则
var routes=[
{
path:'/myb',
component:()=>import('../components/Myb'),
children:[
{
path:'/myb/aa',
// component:()=>import('../components/Myaa'),
components:{
default:()=>import('../components/Myaa'),
a:()=>import('../components/Login'),
b:()=>import('../components/Mya')
}
},
{
path:'/myb/bb',
component:()=>import('../components/Mybb'),
}
]
}
]
day 07
一:路由守卫
https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#组件内的守卫
- 1:什么是路由守卫:
vue-router
提供的导航守卫主要用来通过跳转或取消的方式守卫导航
全局守卫
-
守卫的所有的路由—进入每一个路由之前执行
-
to:即将要进入的目标 路由对象
-
from:当前导航正要离开的路由
-
next 一定要调用该方法来 resolve 这个钩子 进入导航
next() next(false) next('/login')
-
-
main.js中实现(还可以把该全局守卫放到router/index.js文件中)
import router from './router' router.beforeEach((to, from, next) => { //商城项目进入购物车页面要先进行判断 ,登录成功next()登录不成功next('/login') // console.log(to) // console.log(to.path,to.name); if(to.path=='/mine'){ next('/login') }else{ next() } // next() })
路由独享守卫
你可以在路由配置上直接定义 beforeEnter
守卫:
写在那个路由上,守卫的就是那个路由
var routes=[
{
path:'/mine',
component:()=>import('../components/view/Mine'),
beforeEnter(to,from,next){
// console.log(to)
next();
}
}
]
组件内的守卫
- 在路由组件内直接定义以下路由导航守卫
beforeRouteEnter
beforeRouteUpdate
(2.2 新增)beforeRouteLeave
<script>
export default {
beforeRouteEnter (to, from, next) { //进入路由之前
console.log("进入路由之前")
console.log(this) ; //undefined
next()
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
// 你可以通过传一个回调给 next来访问组件实例
next(vm => {
// 通过 `vm` 访问组件实例
console.log(vm)
})
},
beforeRouteUpdate (to, from, next) {
console.log("我被调用了")
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
console.log("我离开了")
next()
}
}
</script>
二、vuex-状态管理
-
解决的复杂组件之间的数据通信问题
-
vuex是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据共享,它采用集中式存储管理应用的所有组件的状态,
2-1、使用vuex统一管理状态的好处
- 能够在vuex中集中管理共享的数据,易于开发和后期维护
- 能够高效的实现组件之间的数据共享,提高开发效率
- 存储在vuex中的数据是响应式的,能够实时保持数据和页面的同步
2-2、vuex的使用
-
1:安装vuex的依赖包
cnpm install vuex --save
-
2:导入vuex包(src目录下新建store目录,store目录下新建index.js文件)
-
3:创建store对象
//index.js import Vue from 'vue' //1: 导入vuex包 import Vuex from 'vuex' Vue.use(Vuex) // 2创建store对象 export default new Vuex.Store({ })
-
将store对象挂载到vue实例中
import store from './store' new Vue({ el: '#app', store, components: { App }, template: '<App/>' })
2-3、vuex的核心概念
1:state
-
state提供唯一的公共数据源,所有共享的数据都要统一放到Store的state中进行存储
export default new Vuex.Store({ //store数据源,提供唯一公共数据 state:{ count:20 } })
-
组件访问state中数据的第一种方式
this.$store.state.全局数据名称 //代码 <template> <div> count的值是{{$store.state.count}} </div> </template> <script> export default { mounted(){ console.log(this.$store.state.count) } } </script>
-
组件访问state中数据的第二种方式
// 1:从vuex中按需导入mapState函数 import {mapState} from 'vuex' computed:{ // 2将全局数据,映射为当前组件的计算属性 ...mapState(['count']) } //代码 <template> <div> {{count}} </div> </template> <script> // 1:从vuex中按需导入mapState函数 import {mapState} from 'vuex' export default { computed:{ // 2将全局数据,映射为当前组件的计算属性 ...mapState(['count']) // 等同于 // count(){ // return this.$store.state.count; // } } } </script>
2:mutation
-
mutation用于变更store中的数据
- 只能通过mutation变更store中的数据,不可以直接操作store中的数据
- 通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化
-
定义mutation
export default new Vuex.Store({ state:{ count:20 }, mutations:{ //定义mutation 变更store中的数据 add(state){ //变更状态 state.count++; } } })
-
组件中触发mutation中的方法(第一种)
//this.$store.commit('mutation中的方法') this.$store.commit('add') //代码 <template> <div> count的值是{{$store.state.count}} <button @click="add">自增1</button> </div> </template> <script> export default { mounted(){ console.log(this.$store.state.count) }, methods:{ add(){ // 触发mutation this.$store.commit('add') } } } </script>
-
组件中触发mutation中的方法(第二种)
// 1:从vuex中按需导入mapMutations函数 import {mapMutations} from 'vuex' //2:将指定的mutations函数,映射为当前组件的methods函数 methods:{ //将指定的mutations函数,映射为当前组件的methods函数 ...mapMutations(['add']) } //代码 <template> <div> {{count}} <button @click="add">自增1</button> </div> </template> <script> // 1:从vuex中按需导入mapState,mapMutations函数 import {mapState,mapMutations} from 'vuex' export default { computed:{ // 2将全局数据,映射为当前组件的计算属性 ...mapState(['count']) // 等同于 // count(){ // return this.$store.state.count; // } }, methods:{ //将指定的mutations函数,映射为当前组件的methods函数 ...mapMutations(['add']) } } </script>
-
触发mutations时传递参数
-
定义传参的mutation
export default new Vuex.Store({ state:{ count:20 }, mutations:{ //变更store中的数据 step形参 addN(state,step){ state.count +=step } } })
-
组件中调用并传参(第一种)
//传递多个值用对象传递 this.$store.commit('addN',实参) //代码 <template> <div> count的值是{{$store.state.count}} <button @click="add">自增1</button> <button @click="addN(8)">自增N</button> </div> </template> <script> export default { mounted(){ console.log(this.$store.state.count) }, methods:{ add(){ // 触发mutation this.$store.commit('add') }, addN(n){ // 触发mutation传递参数,传多个写在对象里面 this.$store.commit('addN',n) } } } </script>
-
组件中调用并传参(第二种)
//代码 <template> <div> {{count}} <button @click="add">自增1</button> <button @click="addN(9)">自增N</button> </div> </template> <script> // 1:从vuex中按需导入mapState,mapMutations函数 import {mapState,mapMutations} from 'vuex' export default { computed:{ // 2将全局数据,映射为当前组件的计算属性 ...mapState(['count']) // 等同于 // count(){ // return this.$store.state.count; // } }, methods:{ //将指定的mutations函数,映射为当前组件的methods函数 ...mapMutations(['add','addN']) } } </script>
-
3:action
-
action用于处理异步任务,和逻辑操作
-
如果通过异步操作或者执行逻辑操作变更数据,必须通过action,而不能使用mutation,但是在action中还是通过触发mutation的方式间接变更数据
-
定义action
const store=new Vuex.Store({ state:{}, mutations:{}, actions:{ addAsync(context){ setTimeout(()=>{ //在actions中,不能直接修改state中的数据 //必须通过context.commit()触发某个mutation才行 context.commit('add') },1000) } } }) //代码 import Vue from 'vue' //1: 导入vuex包 import Vuex from 'vuex' Vue.use(Vuex) // 2创建store对象 export default new Vuex.Store({ //store数据源,提供唯一公共数据 state:{ count:20 }, mutations:{ //变更store中的数据 // 只有mutation中定义的函数,才有权利修改state中的数据 add(state){ state.count++; // 不要在mutations函数中执行异步操作 // setTimeout(()=>{ // state.count++; // },1000) }, addN(state,step){ state.count +=step } } , actions:{ addAsync(context){ setTimeout(()=>{ //在actions中,不能直接修改state中的数据 //必须通过context.commit()触发某个mutation才行 context.commit('add') },1000) } } })
-
组件中访问action中的函数(第一种)
this.$store.dispatch('函数名') //代码 <template> <div> count的值是{{$store.state.count}} <button @click="add">自增1</button> <button @click="addN(8)">自增N</button> <button @click="addAsync">自增1+async</button> </div> </template> <script> export default { mounted(){ console.log(this.$store.state.count) }, methods:{ add(){ // 触发mutation this.$store.commit('add') }, addN(n){ // 触发mutation传递参数,传多个写在对象里面 this.$store.commit('addN',n) }, addAsync(){ // 触发action中的函数 this.$store.dispatch('addAsync') } } } </script>
-
组件中访问action中的函数(第二种)
// 1:从vuex中按需导入mapActions函数 import {mapState,mapMutations,mapActions} from 'vuex' //将指定的actions函数,映射为当前组件的methods函数 methods:{ ...mapActions(['addAsync']) } //代码 <template> <div> {{count}} <button @click="add">自增1</button> <button @click="addN(9)">自增N</button> <button @click="addAsync">自增1+async</button> </div> </template> <script> // 1:从vuex中按需导入mapState,mapMutations函数 import {mapState,mapMutations,mapActions} from 'vuex' export default { computed:{ // 2将全局数据,映射为当前组件的计算属性 ...mapState(['count']) // 等同于 // count(){ // return this.$store.state.count; // } }, methods:{ //将指定的mutations函数,映射为当前组件的methods函数 ...mapMutations(['add','addN']), ...mapActions(['addAsync']) } } </script>
-
触发action异步任务时携带参数
-
定义action
const store=new Vuex.Store({ state:{}, mutations:{}, addNAsync(context,step){ setTimeout(()=>{ //在actions中,不能直接修改state中的数据 //必须通过context.commit()触发某个mutation才行 context.commit('addN',step) },1000) } }) //代码 import Vue from 'vue' //1: 导入vuex包 import Vuex from 'vuex' Vue.use(Vuex) // 2创建store对象 export default new Vuex.Store({ //store数据源,提供唯一公共数据 state:{ count:20 }, mutations:{ //变更store中的数据 // 只有mutation中定义的函数,才有权利修改state中的数据 add(state){ state.count++; // 不要在mutations函数中执行异步操作 // setTimeout(()=>{ // state.count++; // },1000) }, addN(state,step){ state.count +=step } } , actions:{ addAsync(context){ setTimeout(()=>{ //在actions中,不能直接修改state中的数据 //必须通过context.commit()触发某个mutation才行 context.commit('add') },1000) }, addNAsync(context,step){ setTimeout(()=>{ //在actions中,不能直接修改state中的数据 //必须通过context.commit()触发某个mutation才行 context.commit('addN',step) },1000) } } })
-
组件中访问action中的函数(第一种)
<template> <div> count的值是{{$store.state.count}} <button @click="add">自增1</button> <button @click="addN(8)">自增N</button> <button @click="addAsync">自增1+async</button> <button @click="addNAsync(5)">自增N+async</button> </div> </template> <script> export default { mounted(){ console.log(this.$store.state.count) }, methods:{ add(){ // 触发mutation this.$store.commit('add') }, addN(n){ // 触发mutation传递参数,传多个写在对象里面 this.$store.commit('addN',n) }, addAsync(){ // 触发action中的函数 this.$store.dispatch('addAsync') }, addNAsync(n){ this.$store.dispatch('addNAsync',n) } } } </script>
-
组件中访问action中的函数(第2种)
<template> <div> {{count}} <button @click="add">自增1</button> <button @click="addN(9)">自增N</button> <button @click="addAsync">自增1+async</button> <button @click="addNAsync(3)">自增N+async</button> </div> </template> <script> // 1:从vuex中按需导入mapState,mapMutations函数 import {mapState,mapMutations,mapActions} from 'vuex' export default { computed:{ // 2将全局数据,映射为当前组件的计算属性 ...mapState(['count']) // 等同于 // count(){ // return this.$store.state.count; // } }, methods:{ //将指定的mutations函数,映射为当前组件的methods函数 ...mapMutations(['add','addN']), ...mapActions(['addAsync','addNAsync']) } } </script>
-
4:getter
-
getter用于对store中的数据进行加工处理形成新的数据,类似于vue中的计算属性
-
store中数据发生变化,getter的数据也会跟着变化
-
定义getter
export default new Vuex.Store({ getters:{ getcount:state=>{ return '当前最新的数量是【'+ state.count +'】'; } } })
-
使用getters的第一种方式
this.$store.getters.名称
代码
<template> <div> count的值是{{$store.state.count}} <hr/> {{$store.getters.getcount}} <hr/> <button @click="add">自增1</button> <button @click="addN(8)">自增N</button> <button @click="addAsync">自增1+async</button> <button @click="addNAsync(5)">自增N+async</button> </div> </template> <script> export default { mounted(){ console.log(this.$store.state.count) }, methods:{ add(){ // 触发mutation this.$store.commit('add') }, addN(n){ // 触发mutation传递参数,传多个写在对象里面 this.$store.commit('addN',n) }, addAsync(){ // 触发action中的函数 this.$store.dispatch('addAsync') }, addNAsync(n){ this.$store.dispatch('addNAsync',n) } } } </script>
-
使用getters的第二种方式
import {mapGetters} from 'vuex' computed:{ ...mapGetters(['名称']) }
代码
<template> <div> {{count}}---{{getcount}} <button @click="add">自增1</button> <button @click="addN(9)">自增N</button> <button @click="addAsync">自增1+async</button> <button @click="addNAsync(3)">自增N+async</button> </div> </template> <script> // 1:从vuex中按需导入mapState,mapMutations函数 import {mapState,mapMutations,mapActions,mapGetters} from 'vuex' export default { computed:{ // 2将全局数据,映射为当前组件的计算属性 ...mapGetters(['getcount']), ...mapState(['count']) // 等同于 // count(){ // return this.$store.state.count; // } }, methods:{ //将指定的mutations函数,映射为当前组件的methods函数 ...mapMutations(['add','addN']), ...mapActions(['addAsync','addNAsync']) } } </script>
day 08
一:axios
- 地址:http://www.axios-js.com/
- 含义:易用、简洁且高效的(基于promise)http库 (一个封装好的http插件)
- 特点:
- 支持node端和浏览器端
- 支持promise
- 丰富的配置项
1-1、使用
-
安装
cnpm install axios -S
-
引入
//每个组件里单独引入 import axios from axios
1-2、常用的方法
-
get方法
-
语法:
axios.get(请求地址,配置对象).then(res=>{ }).catch(err=>{}) //不传参数 axios.get('url') .then(res=>{ }) .catch(err=>{ }) //传参数 第一种 axios.get('url?id=5&name=张三') .then(res=>{ }) .catch(err=>{ }) //传参数 第二种 axios.get('url', { params:{ 参数1:值, 参数2:值 } }) .then(res=>{ }) .catch(err=>{ })
-
代码
<template> <div> <li v-for="item in arr" :key="item.id">{{item.title}}</li> </div> </template> <script> import axios from 'axios' export default { data(){ return { arr:[] } }, created(){ // console.log(axios); // axios.get('http://localhost:4000/arr') // .then(res=>{ // console.log(res.data); // }) // axios.get('http://localhost:4000/arr?id=2') // .then(res=>{ // console.log(res.data); // }) axios.get('http://localhost:4000/arr',{ params:{ id:2 } }) .then(res=>{ console.log(res.data); this.arr=res.data; }) } } </script>
-
-
post方法
-
语法:
axios.post(url[,数据[,配置项]]) axios.post('/user') .then((res) =>{ console.log(response); }) .catch((error)=> { console.log(error); }); axios.post('/user', { 参数1: 值, 参数2: 值 }) .then((res) =>{ console.log(response); }) .catch((error)=> { console.log(error); });
-
代码
<template> <div> <li v-for="item in arr" :key="item.id">{{item.title}}</li> <button @click="get">get请求</button> <button @click="post">post请求</button> </div> </template> <script> import axios from 'axios' export default { data(){ return { arr:[], tit:"成都it" } }, methods:{ get(){ //get请求 // console.log(axios); // 不传参数 // axios.get('http://localhost:4000/arr') // .then(res=>{ // console.log(res.data); // }) // 传参 第一种 // axios.get('http://localhost:4000/arr?id=2') // .then(res=>{ // console.log(res.data); // }) // 传参 第二种 axios.get('http://localhost:4000/arr',{ params:{ id:2 } }) .then(res=>{ console.log(res.data); this.arr=res.data; }) }, post(){ //post请求 // axios.post(url[,数据[,配置项]]) // axios.post('http://localhost:4000/arr') // .then(res=>{ // console.log(res.data); // }) // .catch(err=>{ // console.log(err); // }) axios.post('http://localhost:4000/arr',{ title:this.tit }) .then(res=>{ console.log(res.data); }) .catch(err=>{ console.log(err); }) } } } </script>
-
-
all方法
-
可以实现发送多次请求,请求成功之后再做处理
-
在项目开发中,一个页面的数据需要请求多个接口地址,那我们就执行多并发请求
-
语法
// 执行多并发请求 // axios.all([函数1(),函数2(),函数3()]) 一个函数返回一个结果,多个函数返回多个结果 // axios.spread((结果1,结果2,结果3)=>{}) 去获取每个函数返回的结果 // 语法: axios.all([函数1(),函数2(),函数3()]) .then( axios.spread((结果1,结果2,结果3)=>{ }) )
-
代码
<template> <div> <li v-for="item in arr" :key="item.id">{{item.title}}</li> <button @click="get">get请求</button> <button @click="post">post请求</button> </div> </template> <script> import axios from 'axios' export default { data(){ return { arr:[], tit:"成都it" } }, methods:{ get(){ //get请求 // console.log(axios); // 不传参数 // axios.get('http://localhost:4000/arr') // .then(res=>{ // console.log(res.data); // }) // 传参 第一种 // axios.get('http://localhost:4000/arr?id=2') // .then(res=>{ // console.log(res.data); // }) // 传参 第二种 axios.get('http://localhost:4000/arr',{ params:{ id:2 } }) .then(res=>{ console.log(res.data); this.arr=res.data; }) }, post(){ //post请求 // axios.post(url[,数据[,配置项]]) // axios.post('http://localhost:4000/arr') // .then(res=>{ // console.log(res.data); // }) // .catch(err=>{ // console.log(err); // }) axios.post('http://localhost:4000/arr',{ title:this.tit }) .then(res=>{ console.log(res.data); }) .catch(err=>{ console.log(err); }) }, getuser(){ return axios.get('http://localhost:4000/user') }, getarr(){ return axios.get('http://localhost:4000/arr') } }, created(){ // 执行多并发请求 // axios.all([函数1(),函数2(),函数3()]) 一个函数返回一个结果,多个函数返回多个结果 // axios.spread((结果1,结果2,结果3)=>{}) 去获取每个函数返回的结果 // 语法: // axios.all([函数1(),函数2(),函数3()]) // .then( // axios.spread((结果1,结果2,结果3)=>{ // }) // ) axios.all([this.getuser(),this.getarr()]) .then(axios.spread((users,arrs)=>{ console.log(users); console.log(arrs); })) } } </script>
-
1-3、axios Api
-
可以通过向
axios
传递相关配置来创建请求axios({配置对象}) axios({ url:"地址", method: 'post', //请求方式 data: { //传递的参数 参数1: 值, 参数2: 值 } }) .then(res=>{ //res是axios返回的请求对象 res.data才是后端返回的数据 }) .catch(err=>{ console.log(err) })
1-4、axios的create方法
-
create方法不是用来请求数据的,而是用来创建一个axios实例对象,定义一些初始化配置
-
语法
let $axios=axios.create({配置对象}) let $axios=axios.create({ baseURL:'http://xxxx.com/aaa', timeout:3000 //请求时长 }) $axios.get('/getuser',{ params:{ 参数1:值 } }) .then(res=>{}) .catch(err=>{})
1-5、拦截器
-
拦截器 :发送请求之前做一些事情,返回数据之前做一些事情
-
使用
// 请求拦截器 修改请求头 【在请求头上加token】 $axios.interceptors.request.use(function (config) { console.log(55555) // 请求发送之前做一些事情 return config; //一定要return config 请求无法发送 }, function (error) { // 对请求错误做些什么 return Promise.reject(error); }); // 响应拦截器 返回数据的校验 状态是否正确 信息的提取 $axios.interceptors.response.use(function (response) { // 对响应数据做点什么 return response; }, function (error) { // 对响应错误做点什么 return Promise.reject(error); });
1-6、axios的封装
-
1:src目录下面新建http目录,目录下新建index.js文件
import axios from 'axios' //初始化配置 const $axios=axios.create({ baseURL:'http://localhost:4000', timeout:3000 }) // 请求拦截器 修改请求头 【在请求头上加token】 $axios.interceptors.request.use(function (config) { // 请求发送之前做一些事情 return config; //一定要return config 请求无法发送 }, function (error) { // 对请求错误做些什么 return Promise.reject(error); }); // 响应拦截器 返回数据的校验 状态是否正确 信息的提取 $axios.interceptors.response.use(function (response) { // 对响应数据做点什么 return response; }, function (error) { // 对响应错误做点什么 return Promise.reject(error); }); export default $axios;
-
2:main.js文件中引入
import $axios from './http' Vue.prototype.$axios=$axios
-
3:每一个组件中使用
this.$axios.get(url).then(res=>{}) this.$axios.post(url).then(res=>{}) //this.$axios没有all方法 要使用all方法需要 axios.all
二、jsonserver模拟数据
安装
npm install json-server -g
新建一个文件夹myjson,在该文件夹下面新建data.json文件
{
"arr":[
{
"id":1,
"title":"111111111"
},
{
"id":2,
"title":"222222"
},
{
"id":3,
"title":"33333333"
}
],
"user":[
{
"id":1,
"name":"张三"
}
]
}
执行命令行
json-server -w data.json -p 4000
三、跨域问题
-
跨域问题:因为浏览器的同源策略,不能访问其他网站的资源
-
同源策略:协议相同,域名相同,端口号相同
-
解决方法:
- jsonp script的src属性
- iframe
- cors 服务端
- 后端允许跨域
-
vue里面如何解决跨域问题—webpack配置跨域代理 —生产环境不存在
-
config文件下面的index.js 中找 proxyTable
proxyTable: { // 【设置代理!!!】 '/api': { //使用"/地址名"来代替"请求网址" target: 'http://localhost:3000/', //源地址 changeOrigin: true, //改变源 pathRewrite: { // 路径重写 '^/api': '' // 当请求的路径开头有 /地址名 的时候就替换成请求网址 } }
-
组件中请求要修改链接
axios.get('/api/xxx/yyy').then(res=>{})
-
四:项目中接口的使用
-
1:找个地方不是vue项目目录中 ,解压umall-api.zip
-
2:解压之后,umall-api目录中打开命令窗口 cnpm install
-
3:打开Navcat软件,新建数据库 shop_db ,然后把shop_db有数据.sql导入到数据库中
-
4:打开项目目录下的config/global.js文件,修改
exports.dbConfig = { host: 'localhost', //数据库地址 user: 'root',//数据库用户名 password: '你的密码',//数据库用户密码 port: 3306, database: 'shop_db' // 数据库名字 }
-
启动项目
npm start
五、项目启动
-
网址上打开http://localhost:8080/出现空白
var routes=[ { path:'/', component:()=>import('../components/Home'), redirect:'/index', //添加这一句 重定向 children:[] } ]
六、问题
Access to XMLHttpRequest at ‘http://localhost:3000/api/getcate’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
出现跨域问题
七、vant
轻量、可靠的移动端 Vue 组件库
安装
cnpm i vant -S
引入 main.js
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);
使用
<!-- banner图 -->
<van-swipe class="my-swipe banner" :autoplay="3000" indicator-color="white">
<van-swipe-item v-for="item in bannerarr" :key="item.id">
<img :src="item.img" alt />
</van-swipe-item>
</van-swipe>
day 09 项目
一:项目
1:接口地址的封装
-
http目录下新建api.js文件
// 存放所有的接口地址 export default { baseUrl:'http://localhost:3000', //接口网址 cateUrl:'/api/getcate', //获取分类信息(首页) bannerUrl:'/api/getbanner', //获取轮播图信息(首页) seckillUrl:'/api/getseckill', //获取限时秒杀信息(首页) indexgoodUrl: '/api/getindexgoods' //获取商品信息(首页) }
-
main.js中引入,让每一个组件都可以直接使用
imprt $api from './http/api' Vue.prototype.$api=$api
-
在组件中使用
this.$api.名称
2:会员登录
-
1:已经登录成功
-
2:登录成功用户信息保存到本地
-
login.vue
methods:{ login(){ this.$axios.post(this.$api.loginUrl,{ phone:this.phone, password:this.password }) .then(res=>{ if(res.data.code===200){ // 登录成功,登录信息存储在本地的localStorage // localStorage中只能存储字符串 永久存储 除非手动删除 userInfo自定义的 localStorage.setItem('userInfo',JSON.stringify(res.data.list)) this.$router.push('/mine'); }else{ Toast.fail(res.data.msg); } this.phone=""; this.password=""; }) }
-
-
:3:在购物车和我的路由添加自定义meta字段,来记录页面是否需要身份验证
var routes=[ { path:'/cart', // 元信息 记录进入该页面是否需要身份验证 meta:{reqiresAuth:true}, component:()=>import('../components/view/Cart'), }, { path:'/mine', meta:{reqiresAuth:true}, component:()=>import('../components/view/Mine') } ]
-
4:路由拦截
// 全局守卫--守卫的是所有的路由--进入每一个路由之前执行
router.beforeEach((to, from, next) => {
// 设置路由拦截
// $route.matched 一个数组,包含当前路由的所有嵌套路径片段的路由记录
// to.matched数组中包含$route对象的检查元信息
// arr.some()判断该数组是否有元信息,返回布尔值
// 判断哪些路由有元信息
if(to.matched.some(record=>record.meta.reqiresAuth)){
//需要登录身份验证的执行这里
// 判断是否登录
if(localStorage.getItem("userInfo")){
next()
}else{
next('/login')
}
}else{
// 不需要登录身份验证的执行这里
next()
}
})
-
5:设置请求拦截和响应拦截
import axios from 'axios' import router from '../router' import {Toast} from 'Vant' // 创建了axios的实例--初始化配置---每次请求都会先执行这里 // 每次请求会把baseURL放到请求的url的前面-前提是使用$axios请求,axios请求不会执行这个文件 const $axios=axios.create({ baseURL:'/api', ///api此处的api是跨域里的替换的地址 timeout:3000 }) // 请求拦截器 修改请求头 【在请求头上加token】 $axios.interceptors.request.use(function (config) { // console.log(config,"config") // 请求发送之前做一些事情 var userInfo=JSON.parse(localStorage.getItem('userInfo')) // 修改请求头,添加token // 每一个请求都会加token config.headers.Authorization=userInfo?userInfo.token:""; return config; //一定要return config 请求无法发送 }, function (error) { // 对请求错误做些什么 return Promise.reject(error); }); // 响应拦截器 返回数据的校验 状态是否正确 信息的提取 $axios.interceptors.response.use(function (response) { // 对响应数据做点什么 // token过期我们拦截,跳转到登录页面 if(response.data.code==403){ Toast.fail(response.data.msg) // router.push('/login') } return response; }, function (error) { // 对响应错误做点什么 return Promise.reject(error); }); export default $axios;
4:购物车
-
请求数据
export default { data(){ return { cartlist:[] } }, created(){ var uid=JSON.parse(localStorage.getItem('userInfo')).uid this.$axios.get(this.$api.cartlistUrl+"?uid="+uid) .then(res=>{ // console.log(res); this.cartlist=res.data.list.map(item=>{ item.checked=false; return item; }) }) } }
-
页面展示
<van-swipe-cell> <ul> <li> <div class="circle"> <img src="../../assets/images/shopping_04.jpg" alt="" id="sure"> </div> <img src="../../assets/images/shopping_01.jpg" alt="" class="liimg01"> <div class="name"> <h5>欧莱雅面霜</h5> <span>规格:50g</span> <p>¥123.00</p> </div> <div class="number"> <div class="minus">-</div> <input type="text" value="1" class="varible"></input> <div class="add">+</div> </div> </li> </ul> <template #right> <div>删除</div> </template> </van-swipe-cell>
3:token
-
什么是token:
Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码是否正确,并作出相应的提示,这时就有了token
-
token的定义
token是服务端生成的一串字符串,来作为客户端进行请求的一个令牌,当第一次登陆后服务端生成一个token然后将token返回给客户端,以后客户端只需要带着这个token去请求数据就可以了,不需要再次带上用户名和密码
-
使用token的目的
减轻服务器的压力,减少了频繁的查询数据库
-
工作原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UD3K0kML-1611110597377)(F:\Web\vue\day09\参考\day09./token.png)]
day 10 项目
一:1问题收集
- 1:单词不一致---------复制粘贴
- 2:先缕思路,用自己的话把写出来—一步步实现,每一步都没问题了 再往下走
- 3:每天第一件事先梳理笔记
二:页面详情页
-
1:准备详情页
-
2:配置详情页的路由
-
3:首页 实现产品跳转
<li v-for="item in recommedarr" :key="item.id"> <img :src="$api.baseUrl+item.img" alt /> <div> <h5>{{item.goodsname}}</h5> <span>¥</span> <!-- 保留两位小数 --> <span>{{item.price.toFixed(2)}}</span> <p>已售800件</p> <router-link :to="'/detail/'+item.id"> 立即抢购 </router-link> </div> </li>
-
4:详情页中接收数据并展示
export default { data(){ return { proobj:null, num:1 } }, created(){ // 1获取url网址上的id值 let id=this.$route.params.id; // console.log(id); // 2 根据id去服务端请求数据 this.$axios.get(this.$api.goodsinfoUrl,{ params:{ id } }) .then(res=>{ console.log(res.data.list[0]); if(res.data.code==200){ if(res.data.list.length>0){ this.proobj=res.data.list[0]; } } }) } } </script>
-
5:加入购物车
-
1:添加点击事件
-
2:实现添加到数据库的功能
methods:{ addcart(){ // 1获取需要传递的数据 const data={ uid:JSON.parse(localStorage.getItem('userInfo')).uid , //当前登录用户的uid goodsid:this.$route.params.id, num:this.num } // console.log(data) // 2通过请求加入到数据库 this.$axios.post(this.$api.cartaddUrl,data) .then(res=>{ if(res.data.code==200){ Toast.success('加入购物车成功'); } }) } }
-
三:分类页面
- 结构 路由
四:路由搭建
- 一级路由
- login登录
- / 首页
- 二级路由
- 系统设置下的菜单管理、角色管理 管理员管理
- 商城管理下的内容
五:ElementUi
-
饿了么提供的基于 Vue 2.0 的桌面端组件库(mintui移动端)
-
安装
npm i element-ui -S
-
引入main.js
import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.use(ElementUI)
-
使用
-
登录页
day 12
一:管理员列表页面
<template>
<div>
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>管理员列表</el-breadcrumb-item>
</el-breadcrumb>
<!-- 面包屑 -->
<el-button type="primary" @click="tzadd()">添加</el-button>
<!-- data 数据 users--数组 -->
<el-table
:data="users"
style="width: 100%"
row-key="id"
border
lazy
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
>
<el-table-column prop="id" label="用户编号"></el-table-column>
<el-table-column prop="username" label="用户名"></el-table-column>
<el-table-column prop="roleid" label="所属角色"></el-table-column>
<el-table-column prop="status" label="状态">
<template slot-scope="item">
<el-tag effect="dark" v-if="item.row.status==1">启用</el-tag>
<el-tag effect="dark" v-else type="danger">禁用</el-tag>
</template>
</el-table-column>
<el-table-column prop="address" label="操作">
<template slot-scope="item">
<el-button type="primary" @click="edit(item.row.uid)">编辑</el-button>
<el-button type="danger" @click="del(item.row.uid)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- current-change当前页数改变会被触发 -->
<el-pagination background @current-change="pagechange" :page-size="size" layout="prev, pager, next" :total="total"></el-pagination>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
users: [],
size: 1, //每一页显示条数
nowpage: 1, //当前是第几页
total:0 //总数
};
},
methods: {
pagechange(n){ //当前页数改变会被触发
this.nowpage=n;
this.getusers()
.then(res=>{
if(res.data.code==200){
this.users=res.data.list
}else{
this.$message({
type: "error",
message:res.data.msg,
});
}
})
},
getusers(){
return this.$axios.get(this.$api.userlistUrl, {
params: {
size: this.size,
page: this.nowpage,
},
})
},
getusercount(){
return this.$axios.get(this.$api.usercountUrl)
},
tzadd() {
//添加按钮 跳转到添加页面
this.$router.push("/user/add");
},
edit(uid) {
//编辑
this.$router.push("/user/" + uid);
},
del(uid) {
//删除
// 1出现提示信息
this.$confirm("此操作将永久删除该文件, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
// 成功
this.$axios.post(this.$api.menudeleteUrl, { uid }).then((res) => {
if (res.data.code == 200) {
// console.log(res.data)
this.users = res.data.list; //更新页面上的数据
this.$message({
type: "success",
message: "删除成功!",
});
// this.menuAsyncAdd().then((res) => {
// console.log(res.data.list, "123");
// this.users = res.data.list;
// });
}
});
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除",
});
});
},
},
mounted() {
axios.all([this.getusers(),this.getusercount()])
.then(axios.spread((users,usercount)=>{
this.users=users.data.list
this.total=usercount.data.list[0].total;
}))
},
};
</script>
二:富文本编辑器使用
1:下载
cnpm install wangeditor --save
2:组件中使用
<div id="desc"></div>
import E from 'wangeditor'
data(){
return {
editor:null
}
}
mounted(){
this.editor = new E("#desc");
this.editor.create();
}
//添加内容
this.editor.txt.html(this.info.description);
//获取内容
this.editor.txt.html();
三:左边aside设置
1:设置路由
2:实现下面的代码
<template>
<el-col >
<el-menu
:default-active="defaultActive"
class="el-menu-vertical-demo"
background-color="#545c64"
text-color="#fff"
router
active-text-color="#ffd04b"
>
<el-menu-item index="/home">
<i class="el-icon-menu"></i>
<span slot="title">首页</span>
</el-menu-item>
<el-submenu :index="item.id.toString()" v-for="item in userinfo.menus" :key="item.id">
<template slot="title">
<i :class="item.icon"></i>
<span>{{item.title}}</span>
</template>
<el-menu-item :index="val.url" v-for="val in item.children" :key="val.id">
{{val.title}}
</el-menu-item>
</el-submenu>
</el-menu>
</el-col>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
data(){
return {
defaultActive:""
}
},
computed:{
...mapGetters(['userinfo'])
},
mounted(){
console.log(this.$route)
// 页面加载时,控制左边菜单选中效果
this.defaultActive=this.$route.meta.select;
}
}
</script>
四:分类实现
<template>
<div>
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item :to="{ path: '/menu' }">菜单列表</el-breadcrumb-item>
<el-breadcrumb-item>菜单{{tip}}</el-breadcrumb-item>
</el-breadcrumb>
<!-- 面包屑 -->
<el-form :model="info" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="分类名称" prop="catename">
<el-input v-model="info.catename"></el-input>
</el-form-item>
<el-form-item label="上级分类" prop="pid">
<el-select v-model="info.pid" placeholder="请选择">
<el-option label="顶级分类" :value="0">顶级分类</el-option>
<!-- <el-option
v-for="item in getmenulist"
:key="item.id"
:label="item.title" :value="item.id"
></el-option>-->
</el-select>
</el-form-item>
<el-form-item label="图片">
<el-upload
action="#"
list-type="picture-card"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
:auto-upload="false"
:on-change="filechange"
:file-list="fileList"
>
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt />
</el-dialog>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-switch v-model="info.status"></el-switch>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">{{tip}}</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data() {
return {
dialogImageUrl: '',
dialogVisible: false,
fileList:[],
img:"",
// 图片
tip: "添加",
info: {
catename: "",
pid: "",
status: true,
},
rules: {
catename: [
{ required: true, message: "请输入分类名称", trigger: "blur" },
{ min: 1, max: 20, message: "分类名称长度不符合", trigger: "blur" },
],
pid: [{ required: true, message: "请输入上级分类", trigger: "change" }],
},
};
},
computed: {
},
methods: {
handleRemove(file, fileList) {
// console.log(file, fileList);
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
},
filechange(file){
console.log(file)
this.img=file.raw
},
// 图片
submitForm(formName) {
//提交
this.$refs[formName].validate((valid) => {
if (valid) {
// 验证规则都满足时,会执行这里
// 不管是添加还是修改都在这里操作
// json序列化,防止数据变化,上面的数据跟着变化
let data = JSON.parse(JSON.stringify(this.info));
// 默认url为添加
let url = '/api/cateadd';
// 如果能获取到id ,url的值变为修改
if (this.$route.params.id) {
url = '/api/cateedit';
data.id = Number(this.$route.params.id);
}
data.status = data.status ? 1 : 2;
// 获取图片
//FormData将本地数据(图片,.xlsx,.docx)上传或导入数据库
//FormData可以把所有表单元素的name与value组成一个string提交到后台
let form=new FormData();
for(let i in data){
form.append(i,data[i])
}
if(this.img){
form.append('img',this.img)
}
console.log(form);
// 发送请求
this.$axios.post(url, form).then((res) => {
console.log(res);
// if (res.data.code == 200) {
// this.$router.push("/menu");
// } else {
// this.$message.error(res.data.msg);
// }
});
}
});
},
},
created() {
// 页面刷新 需要数据
let cateid = this.$route.params.id;
if (cateid) {
this.tip = "修改";
// 根据id获取id对应的该条数据 赋值给info
this.$axios.get('/api/cateinfo', {
params: {
id: cateid,
},
})
.then((res) => {
console.log(res.data.list);
this.info = res.data.list;
// 把img转化为filelist需要的格式
this.fileList=this.info.img?[{url:this.info.img}]:[]
// // 修改status的值为true或者false
this.info.status = this.info.status == 1 ? true : false;
});
}
},
};
</script>
<style>
</style>
五:商品规格和商品分类
<template>
<div>
<!-- 面包屑导航 -->
<el-breadcrumb separator=">">
<el-breadcrumb-item :to="{path:'/home'}">首页</el-breadcrumb-item>
<el-breadcrumb-item>
<a href="/category">商品分类列表</a>
</el-breadcrumb-item>
<el-breadcrumb-item>商品分类{{ tip }}</el-breadcrumb-item>
</el-breadcrumb>
<!-- 菜单 -->
<el-form :model="info" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="一级分类" prop="pid">
<el-select v-model="info.first_cateid" placeholder="请选择" @change="catechange">
<el-option value>请选择</el-option>
<el-option
v-for="item of firstarr"
:key="item.id"
:value="item.id"
:label="item.catename"
>{{ item.catename }}</el-option>
</el-select>
</el-form-item>
<el-form-item label="二级分类" prop="pid">
<el-select v-model="info.second_cateid" placeholder="请选择">
<el-option value>请选择</el-option>
<el-option
v-for="item of secondarr"
:key="item.id"
:value="item.id"
:label="item.catename"
>{{ item.catename }}</el-option>
</el-select>
</el-form-item>
<!-- 商品规格 -->
<el-form-item label="商品规格" prop="pid">
<el-select v-model="info.specsid" placeholder="请选择" @change="specschange">
<el-option value>请选择</el-option>
<el-option
v-for="item of specsarr"
:key="item.id"
:value="item.id"
:label="item.specsname"
>{{ item.specsname }}</el-option>
</el-select>
</el-form-item>
<el-form-item label="规格属性" prop="pid">
<el-select v-model="info.specsattr" multiple placeholder="请选择">
<el-option value>请选择</el-option>
<el-option
v-for="(item,index) in specsattrarr"
:key="index"
:value="item"
:label="item"
>{{ item }}</el-option>
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-switch v-model="info.status"></el-switch>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">添加</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
data() {
return {
tip: "添加",
firstarr: [],
secondarr: [],
specsarr:[],
specsattrarr:[],
info: {
first_cateid: "",
second_cateid: "",
specsid:"", //规格
specsattr:[],
catename: "",
status: true,
},
};
},
computed: {
// ...mapGetters(["roleArr"]),
},
created() {
// 获取一级分类
this.$axios
.get("/api/catelist", {
params: {
pid: 0,
},
})
.then((res) => {
console.log(res.data, "123");
this.firstarr = res.data.list;
});
// 获取商品规格
this.$axios.get('/api/specslist')
.then(res=>{
console.log(res,"规格")
this.specsarr=res.data.list
})
//获取商品下的一条
// this.roles = this.roleArr;
if (this.$route.params.id) {
this.tip = "修改";
//获取到当前路由地址编号对应的菜单信息
this.$axios
.get("/api/goodsinfo", {
params: { id: this.$route.params.id },
})
.then((res) => {
console.log(res.data, 1);
this.info = res.data.list;
// 让二级分类的列表全部展示出来
this.catechange(this.info.first_cateid);
// 规格属性去展示
this.specschange(this.info.specsid)
console.log(this.info.specsattr,"specsattr");
this.info.specsattr=this.info.specsattr?this.info.specsattr.split(','):[]
;
//处理和数据库中不一样的数据类型
// this.info.status = this.info.status == 1 ? true : false;
});
}
},
methods: {
catechange(val) {
//一级分类的内容改变
if (val == "") {
return false;
}
// val 下拉列表的value属性值
// alert(val)
// 根据一级分类的id获取二级分类的内容
this.$axios
.get("/api/catelist", {
params: {
pid: val,
},
})
.then((res) => {
// console.log(res.data, "123");
this.secondarr = res.data.list;
});
},
specschange(val){
if (val == "") {
return false;
}
// val 下拉列表的value属性值
// alert(val)
// 根据一级分类的id获取二级分类的内容
this.$axios
.get("/api/specsinfo", {
params: {
id: val,
},
})
.then((res) => {
console.log(res.data, "12345");
this.specsattrarr = res.data.list[0].attrs;
});
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
//验证规则满足时,才执行数据添加操作
//获取一下表单中的数据,用JSON序列化一下,防止数据变化后页面跟着变化
// let data = JSON.parse(JSON.stringify(this.info));
// //如果现在访问的是动态路由,则执行修改操作,否则执行添加操作
// let url = this.$api.cateaddUrl;
// if (this.$route.params.id) {
// url = this.$api.cateeditUrl;
// data.id = this.$route.params.id; //执行修改接口时的必要条件
// }
// //数据库中的status字段不是布尔值,所以要自行转换一下
// data.status = data.status ? 1 : 2;
// let form = new FormData();
// for (let i in data) {
// form.append(i, data[i]);
// }
// if (this.img) {
// form.append("img", this.img);
// }
//发起post请求,请求接口项目中的菜单添加接口,完成数据的保存
// this.$axios.post(url,form).then((res) => {
// if (res.data.code == 200) {
// this.$router.push("/category");
// } else {
// this.$message({
// showClose: true,
// message: res.data.msg,
// type: "error",
// });
// }
// });
}
});
},
},
};
</script>
day13
一:秒杀活动
<template>
<div>
<el-form :model="info" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="活动期限">
<!--1:::::-->
<el-date-picker
v-model="dateval"
value-format="timestamp"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions"
@change="timechange"
></el-date-picker>
</el-form-item>
<el-form-item label="状态">
<el-switch v-model="info.status"></el-switch>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">添加</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data() {
return {
tip: "添加",
dateval:[],
info: {
begintime: "",
endtime: "",
status: true,
},
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
};
},
created() {
if (this.$route.params.id) {
this.tip = "修改";
//获取到当前路由地址编号对应的菜单信息
this.$axios
.get("/api/seckinfo", {
params: { id: this.$route.params.id },
})
.then((res) => {
console.log(res.data, 1);
this.info = res.data.list;
//2-------
this.dateval=[this.info.begintime,this.info.endtime]
});
}
},
methods: {
//333333333333333333333333333
timechange(e){ //时间获取
// console.log(e)
this.info.begintime=e[0];
this.info.endtime=e[1]
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
console.log(this.info)
}
});
},
},
};
</script>
二:退出
- 1:退出的结构
- 2:添加事件 执行actions中的方法,同时跳转 (既要清除store中的数据还要清除sessStorage中的数据)
- 3:actions要调用mutations中的退出方法
三:项目打包
vua 项目中执行命令:
npm run build
根目录下面生成一个dist目录
把dist目录交给后端,但是这一次我们把dist目录复制到接口目录下,打开app.js文件
app.use(express.static('dist'))
浏览器打开http://localhost:3000/login就可以访问项目了
四:vue-cli脚手架升级
1:如果安装旧版本 卸载
npm uninstall vue-cli -g
安装
npm install @vue/cli -g
查看版本
vue -V
or
vue --version
2:初始化项目
vue create 项目名
启动项目
yarn serve
3:跨域配置
在vue的项目根目录下新建vue.config.js文件(一定是vue.config.js)
module.exports={
devServer:{
proxy:{
'/api': { //使用"/地址名"来代替"请求网址"
target: 'http://localhost:3000/', //源地址
changeOrigin: true, //改变源
pathRewrite: { // 路径重写
'^/api': '' // 当请求的路径开头有 /地址名 的时候就替换成请求网址
}
}
}
}
}
五:过滤器
文本格式化—内容显示
-
局部定义
new Vue({ filters:{ //局部定义过滤器 val 名字:function(val){ return val+"111" } }, showprice(val,zk){ if(!val){ return; } return val*zk/10; } }) //定义过滤器可以传其他参数
-
使用过滤器
{{ price | 过滤器的名字 }} //price 作为过滤器的第一个参数 {{ price | 过滤器的名字(第二个参数) }}
-
全局定义
main.js中定义
// Vue.filter(过滤器的名字,()=>{ // }) Vue.filter('isTrue',function(val){ if(val==1){ return true }else{ return false; } })
-
组件中使用
{{ price | 过滤器的名字 }} //price 作为过滤器的第一个参数
-
filters的封装
-
1:src目录下新建filters/index.js文件
const isNullOrEmpty=function(val){ if(val==null || val=="" ||typeof(val)==undefined){ return "hello" }else{ return "nihao" } } const isTrue=function(val){ if(val==1){ return true }else{ return false; } } export { isTrue, isNullOrEmpty }
-
2:main.js文件中导入
import * as filters from './filters' //Object.keys把有键值对的对象转换为数组 Object.keys(filters).forEach(key=>{ console.log(key) Vue.filter(key,filters[key]) })
-
3:组件中直接使用过滤器
{{name | isNullOrEmpty}}
-
day14
一:vue-cli4脚手架搭建项目配置
vue create 项目名称 //初始化项目
//1:选择手动配置还是默认配置
default 默认配置
Manually select features 手动配置
//2:我们选择手动配置
?Please pick a preset:Manually select features
?CHeck the features needed for your project
(*)Babel
( )TypeScript
( )Progressive Web App ( PWA) Support
(*)Router
(*)Vuex
(*)CSS Pre-processors
(*)Linter / Formatter
( )Unit Testing
( )E2E Testing
//3:选择vue的版本
Choose a version of Vue.js that you want to start the project with <Use arrow keys>
>2.x
3.x <Preview>
//4:让我们检查一下我们刚选的配置,然后问我们要不要使用history mode,其实呢就是我们的页面路由含不含有#;这里我们选择Y
?Please pick a preset:Manually select features
?CHeck the features needed for your project:Babel,Router,Vuex,CSS Pre-processors,Linter
?Use history mode for router?(Requires proper server setup for index fallback in production) (y/n)
//4:选择CSS预编译器,这里我们选择Less,其实都差不多选哪个都行,选你会用的,实在不行也可以直接用原生怼;
?Pick a CSS pre-processor (PostCSS Autoprefixer and CSS Modules are supported by default):
Sass/SCSS(With dart-sass)
Sass/SCSS(With dart-sass)
Less
>Stylus
//5:选择 ESLint 代码校验规则,提供一个插件化的javascript代码检测工具,这里我们选择ESLint + Prettier;
?Pick a linter /formatter config
ESLint with error prevention only
ESLint + Airbnb config
ESLint + Standard config
>ESLint +Prettier
//6选择什么时候进行代码校验,Lint on save 保存就检查,Lint and fix on commit fix 或者 commit 的时候检查,这里我们选择第一个,稳;
?Pick additional lint features:(Press <space> to select,<a> to toggle all,<i>to invert selection)
>(*) Lint on save
( ) Lint and fix on commit
//7这里呢就是选择把我们的配置保存到哪个文件中了,In dedicated config files 存放到独立文件中,In package.json 存放到 package.json 中,这里我们选择放到package.json中;
?Where do you prefer placing config for Babel ,ESLint ,etc.?
In dedicated config files
In package.json
//8\这里呢就是问我们要不要把我们的配置保存下来,为之后的项目。这里我们就选择不了,每次都手动配置一下也不花时间
?Save this as a preset for future projects?(y/N) n
结束了
启动项目
npm run serve
or
yarn serve
二:stylus
https://stylus.bootcss.com/
富于表现力、动态的、健壮的 CSS预处理器
-
使用
-
1:下载
cnpm install stylus -dev--save cnpm install stylus-loader -dev--save
-
2:使用
<style scoped lang="stylus"> </style>
-
3:语法
.box width 100px height 100px background red // 变量 fontSize=30px $bgColor=pink body font-size fontSize background-color $bgColor // 函数 fonts(){ border:2px solid font-weight bold width 100px height 100px } .small fonts() border 2px solid background blue // 选择器 // textarea // input // color blue // background-color red textarea input color blue &:hover color red // 插值 vendor(prop,args) -webkit-{prop} args -moz-{prop} args {prop} args border-radius() vendor('border-radius',arguments) border-shadow() vendor('border-shadow',arguments) .small border-radius 10px
-
三:Scss
世界上最成熟、最稳定、最强大的专业级CSS扩展语言!
-
下载
cnpm install node-sass --save-dev cnpm install sass-loader --save-dev
-
使用
<style lang="scss"></style>
-
语法
// .box{ // width: 200px; // height:200px; // background: pink; // >a{ // text-decoration: none; // } // } .box{ width: 200px; height:200px; background: pink; a{ text-decoration: none; span{ } } &:hover{ background: red; } }
四:动画
https://cn.vuejs.org/v2/guide/transitions.html
vue中动画的使用场景:
- v-if 条件渲染
- v-show 条件展示
- 动态组件 is
- 组件根节点
transition组件
transition组件有个name属性,name属性就是动画名称,name值是自定义
在进入/离开的过渡中,会有 6 个 class 切换。
【从隐藏到显示】
动画名-enter 进入前
动画名-enter-active 进入中
动画名-enter-to 进入后
【从显示到隐藏】
动画名-leave 离开前
动画名-leave-active 离开中
动画名-leave-to 离开后
<template>
<div id="app">
<button @click="state=!state">切换</button>
<transition name="move">
<div class="box" v-show="state"></div>
</transition>
</div>
</template>
<script>
export default {
data(){
return {
state:true
}
}
}
</script>
<style lang="scss">
.box{
width: 300px;
height:300px;
background: red;
}
.move-enter{
transform: translateX(100px);
}
.move-enter-active{
transition:all 10s linear;
}
.move-enter-to{
transform: translateX(-100px);
}
.move-leave{
transform: translateY(-50px);
}
.move-leave-active{
transition:all 10s linear;
}
.move-leave-to{
transform: translateY(200px);
}
</style>
transtion结合animate.css使用
https://cdn.jsdelivr.net/npm/animate.css@3.5.1 浏览器打开下载animate.css
https://animate.style/#documentation 查找有哪些动画
//引入animate.css
// main.js中引入 ---每一个组件都可以使用
// 单独的组件中引入
<transition
enter-class=""
enter-active-class="animated fadeIn"
enter-to-class=""
leave-class=""
leave-active-class="animated fadeOut"
leave-to-class=""
>
<div class="box" v-show="state"></div>
</transition>
<style>
@import url('./assets/animate.css');
</style>
transtion 只能有0个或1个子节点
transtion-group可以有0个或多个子节点
<template>
<div id="app">
<button @click="state=!state" class="animated">切换</button>
<!-- <transition name="move">
<div class="box" v-show="state"></div>
</transition> -->
<transition
enter-class=""
enter-active-class="animated fadeIn"
enter-to-class=""
leave-class=""
leave-active-class="animated fadeOut"
leave-to-class=""
>
<div class="box" v-show="state"></div>
</transition>
<ul>
<transition-group
enter-class=""
enter-active-class="animated fadeIn"
enter-to-class=""
leave-class=""
leave-active-class="animated fadeOut"
leave-to-class=""
>
<li v-for="(item,index) in arr" :key="index">
{{item}}
<button @click="del(index)">删除</button>
</li>
</transition-group >
</ul>
</div>
</template>
<script>
export default {
data(){
return {
state:true,
arr:['香蕉','juzi' ,'bbb']
}
},
methods:{
del(ind){
this.arr.splice(ind,1)
}
}
}
</script>
<style>
@import url('./assets/animate.css');
.box{
width: 300px;
height:300px;
background: red;
}
</style>
day14+(TS)
Typescript的学习
1:什么是Typescript
1)Typescript不是新的语言,是JavaScript语言的超集
2)Typescript是由微软开发的开源编程语言,增加了非常有用的编译时类型检查特性*,最终被编译成JavaScript来执行
2:为什么要学习Typescript
- 提升自身技能
2)增加了代码的可读性和可维护性,开发大型项目
3)如果不显示定义类型,能够自动做出类型推论
4)Typescript拥有活跃的社区
缺点:增加学习成本 增加了设定类型的开发时间
3:如何去使用Typescript
ts的学习,学习的是语法,好多的语法,es5,es6…已经学过了
-
ts文件可以直接引入的
<script src="index.ts" ></script>
-
环境安装
ts的很多语法浏览器是不支持,安装环境,将ts进行编译,编译成浏览器可以执行的js
安装·
npm install -g typescript
检测是否安装成功
tsc -v //可以查看到版本说明安装成功
-
编译文件
tsc xxx.ts //同级目录下生成一个同名的js文件 tsc --outDir ./js xxx.ts //把ts文件编译到指定的目录
-
自动编译 tsconfig.json
ts --init //生成tsconfig.json配置文件
“outDir”:‘出口文件名’ 出口文件设置
“rootDir”:“入口文件名” 入口文件设置修改tsconfig.json,配合tsc -w 实时热更新
4:Typescript类型介绍
ts类型是非常严格的,定义了什么类型就必须是什么类型
let b=10; //正确 规定b是数值类型
b="123" //Type '"123"' is not assignable to type 'number'.2 b="123"
- 声明变量
语法:
let 变量名:数据类型; //声明变量并指定数据类型,给变量赋值必须是指定的该数据类型
let 变量名:数据类型= 值;
代码:
let num:number;
num=58;
// num="str" 错误的
let n:number=45;
console.log(n)
在typescript中给我们提供了以下数据类型
- 字符串(string)
- 数字类型(number)
- 布尔类型(boolean)
/**
* 字符串
*/
let str:string;
str="123"
let str1:string="78";
/**
* number 数值
*/
let n:number;
n=12;
let n2:number=23;
console.log(n2)
/**
* 布尔 boolean
*/
let bool:boolean;
bool=true;
-
null和undefined
/* null和undefined是所有类型的子类型 undefined表示未定义的值 null代表空 一个对象被人为的定义为空对象 */ let tmp:undefined; tmp=undefined; // let tmp1:number=undefined; //错误的 let tmp2:null=null; // tmp2={} 报错
-
数组类型(array)
/**
*数组
const arr=[1,"25",true]
const arr1=new Array()
*/
// ts中有两种方式定义数组 ---数组中每一个数据的元素类型指定什么类型就只能是什么类型
// 1--在元素类型后面加上[]--
const arr:string[]=["1","5"];
// arr[0]="45";
// arr[1]='true';
// console.log(arr)
// 2---使用数组泛型 Array<元素类型>
const arr1:Array<number>=[25,36];
let arr2:Array<boolean>;
arr2=[true,false]
-
any类型
/** * any任意类型 可以是任何数据类型 */ let a:any; a=5; a="123"; a=true; const arr5:any[]=[45,"bugai",true]; const arr6:Array<any>=[45,"123"] console.log(arr5,arr6)
-
元组类型(tuple)
/** * 元组类型 表示一个已知元素数量和类型的数组,各元素的类型不必相同 * * 语法:const 变量名:[类型1,类型2,类型3]=[14,"1ew",true] * 注意:前面有几个类型 后面就只能有几个值 * 每一对类型和值必须一一对应 */ // let arr7:[string,boolean,number,string]=[];//错误[]没有值,数量不统一 let arr7:[string,boolean,number,string]=["hello",true,25,"58"];
-
枚举类型(enum)
/** * 枚举类型 * 我们的值只会出现在一定范围中 * 性别: 男 女 保密 * 状态: 成功 失败 * 请求方式 : get,post,delete... * * 语法: * enum 枚举名 {标记名1,标记名2,标记名3} * 默认 0 1 2 * enum 枚举名 {标记名1=值,标记名2=值,标记名3=值} * * 使用 * let 变量:枚举名=枚举名.标记名 * */ enum Sex {man,women} let s:Sex=Sex.women console.log(s) enum Status {success=200,err=400} let result:Status=Status.success console.log(result)
-
void类型
/** * * void 没有任何类型 主要指的是函数没有返回值 */ function fn():void{ console.log(1) } fn()
-
never类型
/** * never 类型 异常 */ function fn2(): never { throw new Error() }
-
类型断言
/** * 类型断言:告诉编译器你比它更了解这个类型,并且它不应该发出错误,就是告诉不要去 * 对这个值进行类型检查 * */ function fn3(x:any):any{ if(typeof x==='string'){ // return (<string>x).length return (x as string).length } }
5:ts中函数定义
1:为函数定义类型
/****为函数定义类型
* 1:参数类型定义
* 2:返回值类型定义
* 第一种:
* function 函数名(参数:数据类型):返回值数据类型{
* return 值; //值的类型必须和返回值的数据类型一致
* }
* 第二种:
* let 函数名=function(参数:数据类型):返回值数据类型{
*
* }
* void 函数没有返回值
*/
function show(num:number):void{
console.log(num)
}
show(24)
function show1(x:string,y:string):string{
return x+y;
}
console.log(show1("中公","优就业"))
let show2=function (x:number,y:string):void{
console.log(x+y)
}
show2(14,"it")
let show3=function():string{
return "web前端"
}
console.log(show3())
可选参数
/**可选参数
* js中参数是可选的 不传时候值为undefined
* ts 可选参数使用 ? 定义
* 注意:ts中没有配置可选参数,你不传值 就会报错
*/
function get(x:string,y?:number):string{
return x+y;
}
console.log(get("中公",520))
console.log(get("中公"))
默认参数
/******默认参数 末尾的参数才能设置默认参数 */
function getd(x:number,y:number=25):number{
return x+y;
}
console.log(getd(12,23))
console.log(getd(12))
剩余参数
/****剩余参数
*
* 你想同时操作多个参数,或者你并不知道会有多少参数传递进来
*/
function get1(...data:any[]){
console.log(data)
}
// get1(1,2,3,true)
// get1(1)
// 剩余参数要放在尾部
function get2(x:number,...data:any[]){
console.log(data)
}
get2(25,"123","aaa","bbb")
6:ts装饰器
装饰器是对类,函数,属性的一种装饰,可以针对其添加一些额外的行为
通俗点理解就是在原有代码的外层包装了一层处理器
装饰器其实就是一个函数
启用装饰器特性,需要在tsconfig.json
里启用experimentalDecorators
编译器选项
"experimentalDecorators": true,
装饰器就是在调用某个方法,某个属性,某个类,某个参数之前执行一段函数,
定义装饰器
function 装饰器名(target){
} //不能传参数
function 装饰器名(参数:类型){
return function(target){
}
}
类装饰器
function 装饰器名(target){
}
@装饰器名
class 类名{
} //该类被实例化的时候装饰器函数就会被调用
代码
function add(target:any){
console.log(target,"target")
target.prototype.num=12;
}
@add //后面不能加任何符号
class Stu{
constructor(){
console.log("构造函数")
}
}
let s1=new Stu()
console.log(s1)
可以传参的装饰器
function add(num:number){
return function(target:any){ //return 的函数才是真正的装饰器
console.log(target,"target")
}
}
@add(15) //后面不能加任何符号
class Stu{
constructor(){
console.log("构造函数")
}
}
let s1=new Stu()
console.log(s1)
方法装饰器
function 装饰器名(类的原型对象,方法名,方法属性描述){
}
class 类名{
@装饰器名
方法名(){
}
}
function add(target:any,name:any,desc:any){
console.log(target,"target")
console.log(name,"方法名")
console.log(desc,"属性描述")
}
class Stu{
@add //后面不能加任何符号
run(){
console.log("nihao")
}
}
let s1=new Stu()
console.log(s1)
属性装饰器
function 装饰器名(类的原型对象,属性名){
}
class 类名{
@装饰器名
public 属性名
}
function add(target:any,propertyKey:any){
console.log(target,"target")
console.log(propertyKey)
}
class Stu{
@add //后面不能加任何符号
public name:string;
constructor(n:string){
this.name=n;
}
}
let s1=new Stu("zhans")
console.log(s1)
参数装饰器
function required(target:any, propertyKey: string, parameterIndex: number){
console.log(target) //类的原型对象
console.log(propertyKey) //方法名
console.log(parameterIndex) //参数的索引位置
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet(@required name: string) {
return "Hello " + name + ", " + this.greeting;
}
}
7:vue-cli脚手架升级
1:如果安装旧版本 卸载
npm uninstall vue-cli -g
安装
npm install @vue/cli -g
查看版本
vue -V
or
vue --version
2:初始化项目
vue create 项目名
启动项目
yarn serve
3:目录结构对比
4:使用ts语法开发
vue-property-decorator 是vue社区提出的一个对vue使用class类方式开发的一个重要包
1:创建组件About.vue
<template>
<div>
about
</div>
</template>
<script lang="ts">
// lang="ts"表示使用ts语法
// 引入类装饰器
import {Component ,Vue} from 'vue-property-decorator'
@Component({})
// 使用class方式创建类
export default class About extends Vue{
}
</script>
<style lang="stylus">
</style>
2:引入组件
<template>
<div id="app">
<!-- 3---调用组件 -->
<About></About>
</div>
</template>
<script lang="ts">
import {Component ,Vue} from 'vue-property-decorator'
// 1--引入组件
import About from './components/About.vue'
// 2---使用引入的组件
@Component({
components:{
About
}
})
export default class App extends Vue{
}
</script>
<style lang="stylus">
</style>
3:新语法–定义data数据
<template>
<div>
about
{{str}}
<li v-for="(item,index) in arr" :key="index">
{{item}}
</li>
</div>
</template>
<script lang="ts">
// lang="ts"表示使用ts语法
// 引入类装饰器
import {Component ,Vue} from 'vue-property-decorator'
@Component({})
// 使用class方式创建类
export default class About extends Vue{
str:string="中公it优就业" //没有data 直接定义
private arr:any[]=["aaa","Bbb","cccc"];
}
</script>
4:新语法—生命周期
<script lang="ts">
// lang="ts"表示使用ts语法
// 引入类装饰器
import {Component ,Vue} from 'vue-property-decorator'
@Component({})
// 使用class方式创建类
export default class About extends Vue{
str:string="中公it优就业" //没有data 直接定义
private arr:any[]=["aaa","Bbb","cccc"];
created(){
console.log("生命周期created")
}
mounted(){
console.log("mounted")
}
}
</script>
新语法–没有了methods
<template>
<div>
<li v-for="(item,index) in arr" :key="index">
{{item}} <button @click="del(index)">删除</button>
</li>
<button @click="changestr">修改str</button>
</div>
</template>
<script lang="ts">
// lang="ts"表示使用ts语法
// 引入类装饰器
import {Component ,Vue} from 'vue-property-decorator'
@Component({})
// 使用class方式创建类
export default class About extends Vue{
str:string="中公it优就业" //没有data 直接定义
private arr:any[]=["aaa","Bbb","cccc"];
changestr(){//修改str
this.str="北京中公教育科技有限公司"
}
del(index){
this.arr.splice(index,1)
}
}
</script>
新语法—没有了computed 直接写前面带上get
<template>
<div>
about
{{str}}
<li v-for="(item,index) in arr" :key="index">
{{item}} <button @click="del(index)">删除</button>
</li>
{{mystr}}
<button @click="changestr">修改str</button>
</div>
</template>
<script lang="ts">
// lang="ts"表示使用ts语法
// 引入类装饰器
import {Component ,Vue} from 'vue-property-decorator'
@Component({})
// 使用class方式创建类
export default class About extends Vue{
str:string="中公it优就业" //没有data 直接定义
private arr:any[]=["aaa","Bbb","cccc"];
created(){
console.log("生命周期created")
}
mounted(){
console.log("mounted")
}
changestr(){//修改str
this.str="北京中公教育科技有限公司"
}
del(index){
this.arr.splice(index,1)
}
get mystr(){
return "我的公司是"+this.str
}
}
</script>
新语法 没有watch 在前面加上watch装饰器
// 监听
// 浅监听 @Watch('监听的变量')
@Watch('str')
changefn(newval:string,oldval:string){
console.log("新值是"+newval,"旧值是"+oldval)
}
// 深度监听
// @Watch(监听的变量,{immediate:true,deep:true})
@Watch('arr',{immediate:true,deep:true})
changearr(newval:any){
console.log("新值为",newval)
}
组件通信------父传子
父组件
<template>
<div id="app">
<!-- 3---调用组件 -->
<About :message="message"></About>
</div>
</template>
<script lang="ts">
import {Component ,Vue} from 'vue-property-decorator'
// 1--引入组件
import About from './components/About.vue'
// 2---使用引入的组件
@Component({
components:{
About
}
})
export default class App extends Vue{
message:string="欢迎来到中公教育"
num:number=25;
}
</script>
子组件
<template>
<div>
{{message}}
</div>
</template>
<script lang="ts">
// lang="ts"表示使用ts语法
// 引入类装饰器
import {Component ,Vue, Watch,Prop} from 'vue-property-decorator'
@Component({})
// 使用class方式创建类
export default class About extends Vue{
// @Prop 属性装饰器
// @Prop(String) readonly message:string |undefined
// 1:传入message属性,只读,是string类型,可以传入可以不传入
// @Prop([String,Number]) readonly message?:string |number
// 2: 传入message属性,只读 可以是number或string ?表示不传入 !表示必须传入
@Prop({
type:String, //多个就写[Number,String]
default:"hellow",
required:true,
validator:function(val){
return val.length>3;
}
})
private message:string|undefined
}
</script>
<style lang="stylus">
</style>
组件通信 子传父
@Emit装饰器
父组件
<template>
<div id="app">
{{message}}
<!-- 3---调用组件 -->
<About @to-parent-fn="changefn"></About>
</div>
</template>
<script lang="ts">
import {Component ,Vue} from 'vue-property-decorator'
// 1--引入组件
import About from './components/About.vue'
// 2---使用引入的组件
@Component({
components:{
About
}
})
export default class App extends Vue{
message:string="欢迎来到中公教育"
num:number=25;
changefn(val:string){
this.message=val
}
}
</script>
<style lang="stylus">
</style>
子组件
<template>
<div>
<!-- 直接调用@Emit -->
<!-- <button @click="toParentFn">传值1</button> -->
<button @click="go">传值1</button>
</div>
</template>
<script lang="ts">
// lang="ts"表示使用ts语法
// 引入类装饰器
import {Component ,Vue, Watch,Emit} from 'vue-property-decorator'
@Component({})
// 使用class方式创建类
export default class About extends Vue{
private msg="我是中公讲师"
// 1---不传入事件名,表示下面紧贴的函数就是自定义事件
// @Emit()
// toParentFn(){
// return this.msg; //return 的值就是传递给父组件的值
// }
// 2 传入事件名,事件名就是该自定义事件
// @Emit('to-parent-fn')
// toParentFn(){
// return this.msg+"2222"
// }
// 3--跟方法1 相同 只不过间接调用
@Emit()
toParentFn(){
return this.msg+"33333"
}
go(){
this.toParentFn()
}
}
</script>
<style lang="stylus">
</style>
配置文件
手动在根目录下创建vue.config.js文件
module.exports={
devServer:{
proxy:{
'/api':{
target:'http://xxx.com',
ws:true,
changeOrigin:true
}
}
}
}
重新启动项目
错误集锦
‘n’ was also declared here.
n被使用过了
SSR服务器端渲染
https://www.nuxtjs.cn/
整个网站先在服务器中运行,然后返回一个完整的html字符串,将这个字符串当成响应内容输出给浏览器
ssr 解决的就是搜索引擎记录页面信息的问题
1-1:ssr有什么好处?
有利于搜索引擎抓取我们的页面
build之后,会静态化page页面,所以访问速度快
1-2:什么是Nuxt.js
Nuxt.js是基于vue的服务器端的渲染框架
1-3:使用
Vue.js本身没有附带SSR,但是有很好的库可以很容易地将SSR添加到我们的Web应用程序中。最受欢迎的两个库是vue-server-renderer和Nuxt.js。
1-3-1:vue-server-renderer
-
1:安装依赖
新建一个文件夹,安装Vue与SSR依赖包vue-server-renderer
mkdir ssr-test //新建空文件夹 cd ssr-test //进入目录 npm init //初始化,生成 package.json npm install vue vue-server-renderer express --save// 安装
-
创建启动文件
在当前目录下创建一个app.js文件,并编写如下内容:
const Vue = require('vue') const server = require('express')() const fs = require('fs') //读取模板---index.html const renderer = require('vue-server-renderer').createRenderer({ template: fs.readFileSync('./index.html', 'utf-8') }) // 此参数是vue 生成Dom之外位置的数据 如vue生成的dom一般位于body中的某个元素容器中, //此数据可在header标签等位置渲染,是renderer.renderToString()的第二个参数, //第一个参数是vue实例,第三个参数是一个回调函数。 const context = { title: 'Vue-ssr-demo', meta: `<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge">` } server.get('*', (req, res) => { //创建vue实例 主要用于替换index.html中body注释地方的内容, //和index.html中 <!--vue-ssr-outlet-->的地方是对应的 const app = new Vue({ data: { url: req.url, data: ['这是一个ssr示例'], title: '学习vue-ssr服务端渲染' }, //template 中的文本最外层一定要有容器包裹, 和vue的组件中是一样的, //只能有一个父级元素,万万不能忘了! template: ` <div> <p>{{title}}</p> <p v-for='item in data'>{{item}}</p> </div> ` }) //将 Vue 实例渲染为字符串 (其他的API自己看用法是一样的) renderer.renderToString(app, context, (err, html) => { if (err) { res.status(500).end('err:' + err) return } //将模板发送给浏览器 res.end(html) }) }) server.listen(8080)
-
引入模板
在app.js的平级目录处创建一个index.html,空内容
-
启动项目
在命令行中进入项目目录,执行如下命令启动项目:
node app.js
-
然后在浏览器中打开http://localhost:8080
1-3-2:Nuxt.js
安装
-
安装create-nuxt-app工具
cnpm install -g create-nuxt-app
-
使用create-nuxt-app创建nuxtjs项目
create-nuxt-app myapp-nuxt
创建nuxt项目的过程中,会问我们选择哪种渲染方式,一定要要选择Universal(通用的,普遍的)
spa 是单页面应用,这种模式下,文件不会ssr渲染
-
nuxt项目创建完毕后,先进入到项目中,然后运行
cd myapp-nuxt npm run dev
在浏览器中运行 http://localhost:3000
修改服务端口
package.json
"config": {
"nuxt": {
"host": "0.0.0.0",
"port": "3333"
}
},
"script":{
}
重新启动项目
引入css文件
nuxtjs没有所谓的index.html入口页,这个index.html实际是由nuxt.config.js编译而成的
-
引入公共的css文件
nuxt.config.js
css: [ 'assets/css/resert.css' ],
rem文件引入
在 static 目录下,建立 js/rem.js 文件。
static 目录是存放独立的文件的。
我们应该使用 script 标签引入 rem.js 文件,但 nuxtjs 中没有 html 页面,需要写在 nuxt.config.js 中。
在 nuxt.config.js 文件中:
head: {
script:[
{ src: '/js/rem.js', async: true, defer: true }
]
}
路由
-
1:主入口文件layouts/default.vue类似于我们vue中的App.vue
<template> <div> <!-- 引入的组件 --> <Head></Head> <!-- 类似于router-view --> <Nuxt></Nuxt> <!-- 引入的组件 --> <Foot></Foot> </div> </template> <script> // ~ 别名,固定的,是根目录 import Head from '~/components/Head.vue' import Foot from '~/components/Foot.vue' export default { components:{ Head, Foot } } </script> <style> </style>
-
components 目录下新建全局公用的组件库,该目录下新建Head.vue和Foot.vue
-
Head.vue
<template> <div> head </div> </template>
-
Foot.vue
<template> <div> <ul> <li> <!--nuxt-link相当于 router-link--> <nuxt-link to="/">首页</nuxt-link> </li> <li> <nuxt-link to="/cat">分类</nuxt-link> </li> <li> <nuxt-link to="/cart">购物车</nuxt-link> </li> <li> <nuxt-link to="/mine">我的</nuxt-link> </li> </ul> </div> </template> <style scoped> div ul { display: flex; justify-content: space-around; align-items: center; } div li { text-align: center; } div a { color: white; text-decoration: none; } div .nuxt-link-exact-active { color: green !important; } </style>
-
-
pages下面创建路由组件
-
index.vue是路由 ‘/’ 对应的组件, ’路由 ‘/mine’ 对应的组件是在pages下新建mine/index.vue
-
然后就可以实现路由跳转了
获取远程数据
下载
cnpm i axios -S
引入
import axios from 'axios'
使用
ayncData方法会在组件每次加载之前被调用,它可以在服务端或路由更新之前被调用
<script>
import axios from 'axios'
export default {
data(){
return {
}
},
async asyncData(){
let {data} = await axios.get('http://jsonplaceholder.typicode.com/posts')
// console.log("data",data)
return {arr:data}; //这个return会把结果和data属性值自动合并,视图层直接调用即可
}
}
</script>
如果不喜欢es6的async/awai,那么也可以使用回调函数
asyncData({ params },callback){
axios.get('http://jsonplaceholder.typicode.com/posts')
.then(res=>{
callback(null,{
arr:res.data
})
})
}
注意:在视图层可以直接使用return 返回来的数据
{{arr}}
打包
项目都开发完毕之后,我们需要将开发环境下的碎片化的文件做合并,这个过程叫做打包(发布)
如果 mode: ‘universal’ 模式
需要到package.json 文件中添加 “generate”: “nuxt generate”,
npm run generate
运行之后就会生成dist目录
如果 mode: ‘spa’ 模式
npm run build
部署
将上一步的dist文件夹放入网站服务器下
后端服务可以是nodejs
新建文件夹 www
dist
app.js
app.js
//cnpm install express
const express =require('express');
const app=express()
app.use(express.static('dist'))
app.listen(8888)
用户打开浏览器 访问该项目了 http://localhost:8888