一、脚手架搭建的项目中组件使用
1、定义组件
-
在components目录下新建xxx.vue文件
-
在xxx.vue文件下写入代码
Myhead.vue中
<template>
<div>
这个是myhead组件
</div>
</template>
<script>
export default {
}
</script>
2、使用组件
①引入组件,②注入到components,③页面调用
// 组件模板
<template>
<div>
我的第一个vue项目
<!-- 3:调用 -->
<Myhead></Myhead>
</div>
</template>
// js
<script>
// 1:引入
import Myhead from './components/Myhead'
export default {
components:{
//2:注入
Myhead
}
}
</script>
二、组件数据通信
父向子传值 | 子向父传值 | 非父子传值 |
---|---|---|
1.父组件调用子组件,给子组件自定义属性;并在父组件中定义要传的数据值 | 1.父组件调用子组件时,给子组件自定义事件 | 1.main.js中定义一个公共区域 (新的vue实例) |
2.子组件通过props接收父组件传来的自定义属性 | 2.在父组件中的methods中定义自定义事件对应的函数 | 2.在A组件中给公共区域自定义事件.$on(‘事件名’,function(v){})–自定义接收值 |
3.子组件视图上直接使用该自定义属性 | 3.在子组件中定义要传的数据值,并通过this.$emit(‘事件名’,参数)触发自定义事件传值 | 3.在B组件中触发公共区域的自定义事件.$emit(‘事件名’,参数)–触发传值 |
1、父向子通信
-
1.父组件调用子组件,给子组件自定义属性
vue是单向数据流,子组件不能修改props中的属性,但是在父组件中修改,传到子组件的数据也会跟着变化
//父组件
<子组件 自定义属性1="值" 自定义属性2="值"></子组件>
//绑定属性
<子组件 :自定义属性1="变量"></子组件>
- 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>
<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:"title1",
list:[
'父组件中的数据1',
'父组件中的数据11'
]
},
{
title:"title1",
list:[
'父组件中的数据2',
'父组件中的数据22'
]
}
]
}
},
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:[]
}
}
}
</script>
<style>
</style>
props验证
-
为了使组件内部使用数据的可靠性,我们对外部传递进来的数据进行校验
-
实现校验- 方式1
//方式1
props:['属性1','属性2']
- 实现校验- 方式2
//方式2 (String,Number,Boolean,Array,Object Date Function Symbol)
props:{
属性1:类型 , //只有类型检测
属性2:[类型1,类型2]
}
- 实现校验- 方式3
//方式3
props:{
属性1:{
type:类型 ,
default:默认值,
required:true/false,
validator:function(val){
//validator函数一定要有返回值,返回true表示验证通过,false验证不通过 控制台有警告
}
}
}
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、非父子通信
- 中央事件总线:eventbus
需求:非父子组件A和B,从B组件向A组件传值
- 1.main.js中定义一个公共区域 (新的vue实例)
// 1:创建公共区域(新的vue实例)
var eventbus=new Vue();
// 2:公共区域挂载到Vue的原型上面(每一个vue实例都可以访问它原型上的成员)
Vue.prototype.$bus=eventbus;
- 2.在A组件中给公共区域自定义事件–自定义接收值
<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.在B组件中触发公共区域的自定义事件–触发传值
<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>
三、ref
- ref 被用来给元素或子组件注册引用信息
ref 注册在普通dom元素上 | ref加载子组件上 | ref加载循环渲染 |
---|---|---|
获取到的是dom元素 | 可以获取或使用组件的所有数据和方法 | 利用v-for和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
外部链接 | npm |
---|---|
在index.html中,通过script标签直接引入外部链接 | 局部使用、全局使用 |
- npm
cnpm install jquery -D
//局部使用(哪个组件用,哪个组件引用)
import $ from 'jquery'
//全局使用(所有的组件都可以用)
//main.js
import $ from 'jquery'
Vue.prototype.$=$
//在每一个组件中使用
this.$("#box").hide()
五、插槽(Slot)
-
插槽用于将所携带的内容,插入到指定的某个位置
-
父组件向子组件传html标签
1、匿名插槽
- 子组件
<template>
<slot></slot>
<div>我是程序员</div>
</template>
- 父组件
//msg数据在父组件中定义
<Mychild>
<p>{
{
msg}}</p>
</Mychild>
2、具名插槽
- 子组件
<template>
<slot name="foot">
默认值
</slot>
</template>
- 父组件
//msg数据在父组件中定义
<Mychild>
<div slot="foot">
我是插槽foot要的内容
</div>
</Mychild>
3、作用域插槽(难)
子组件做循环(循环的结构从父组件传来)或者某一部分他的dom结构应该由外部传递进来的时候,使用作用域插槽
使用作用域插槽,子组件会向父组件的作用域里传递数据,父组件使用插槽,要包含在template中
v-bind:自定义prop名="数据" //子组件中传值
<template v-slot:插槽名字="props">
{
{
props.prop名}}
</template>
- 子组件
<template>
<slot name="one" :msg="msg" :tit="name"></slot>
<slot name="two"></slot>
</template>
<script>
export default {
data(){
return {
msg:"nihao",
name:"张三"
}
}
}
</script>
- 父组件
//msg数据在父组件中定义
<Mychild>
<template v-slot:one="props">
<div>{
{
props.msg}}---{
{
props.tit}}</div>
</template>
</Mychild>
<script>
import Mychild from './components/Mychild'
export default {
data(){
return {
}
}
}
</script>
六、路由
- 路由:根据不同的url地址,映射不同的组件内容
- Vue是单页面应用(SPA):single page application单页面应用,只有一个页面,不断切换内部展示的内容
1、路由(vue-router)安装
- 1.安装vue-router(进入项目cmd中安装)
npm/cnpm i vue-router --save
-
2.配置路由(在main.js中)
①引入vue-router
②给vue安装路由插件
③配置—路由映射关系
④实例化路由对象
⑤把路由对象注入到根实例中
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、路由的封装
- 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组件即可
3、路由重定向–404配置
- redirect
var routes=[
{
path:'/',
component:Index
},
{
path:'*', //*是所有的,上面没有匹配到就都渲染这里
// redirect:'/' //redirect重定向
// 还可以映射到到一个组件中404
component:Notfound
}
]
4、路由懒加载
- component:()=>import(’’)
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')
}
]
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) //前进
6、动态路由
动态路由网址如:http://www.abc.com/newsdetial/102, 那么怎么配置这样的路由呢?
- newsdetial:表示新闻详情路由
- 102 表示新闻id
1.路由规则页面
var routes=[{
path:'/detail/:newsid', // 匹配detail/5
component:()=>import('../components/Detail')
}]
2.从某组件中跳转到Detail组件中
<router-link to="/detail/1"></router-link>
3.Detail.vue组件中获取动态参数
this.$route.params.newsid
- 点击首页新闻–跳转到对应新闻的详情页
<template>
<div>index这是首页
<ul >
<li v-for="(item,index) of list" :key="index"><router-link :to="'/detail/'+(index+1)">{
{
item}} </router-link></li>
</ul>
</div>
</template>
<script>
export default {
data(){
return{
list:['新闻1','新闻2','新闻3'],
}
}
}
</script>
- 进入对应新闻的详情页–要获取动态id–确定是哪条新闻
<template>
<div>
这是新闻页{
{
obj.title }}
</div>
</template>
<script>
export default {
data() {
return {
list: [{
id: 1,
title: "新闻1详情",
},
{
id: 2,
title: "新闻2详情",
},
{
id: 3,
title: "新闻3详情",
},
],
obj: "", //展示具体的新闻内容--根据动态id
};
},
created() {
console.log(this.$route.params.newsid);
var num = this.$route.params.newsid;
var index = this.list.findIndex((item, index) => {
return item.id == num;
});
this.obj = this.list[index];
},
};
</script>
7、路由参数
- 路由中传递 参数除了上文params外,还可以使用query数据
如:http://www.xx.com/p?id=8
1.路由规则页面
var routes=[
{
path:'/detail', // 匹配 detail---直接匹配
component:()=>import('../components/Detail')
}
]
2.从某组件中跳转到Prodetail组件中
<router-link to="/detail?newsid=8">
//传入动态id
<router-link :to="'/detail?newsid='+item.id">
3.Prodetail.vue组件中获取路由参数
this.$route.query.newsid
- 点击首页新闻–跳转到对应新闻的详情页
<template>
<div>index这是首页
<ul >
<li v-for="(item,index) of list" :key="index"><router-link :to="'/detail?newsid='+(index+1)">{
{
item}} </router-link></li>
</ul>
</div>
</template>
<script>
export default {
data(){
return{
list:['新闻1','新闻2','新闻3'],
}
}
}
</script>
- 进入对应新闻的详情页–要获取动态id–确定是哪条新闻
<template>
<div>
这是新闻页{
{
obj.title }}
</div>
</template>
<script>
export default {
data() {
return {
list: [{
id: 1,
title: "新闻1详情",
},
{
id: 2,
title: "新闻2详情",
},
{
id: 3,
title: "新闻3详情",
},
],
obj: "", //展示具体的新闻内容--根据动态id
};
},
created() {
console.log(this.$route.query.newsid);
var num = this.$route.query.newsid;
var index = this.list.findIndex((item, index) => {
return item.id == num;
});
this.obj = this.list[index];
},
};
</script>
- 设置动态路由,路由参数params(detial/102)和query(detail?newsid=3)方式对比
params | query |
---|---|
路由规划: path:’/detail/:newsid’ | path:’/detai’ |
传入动态id 在router-link :to="’/detail/’+item.id" | 在router-link :to="’/detail?newsid=’+item.id" |
获取动态参数 this.$route.params.newsid | this.$route.query.newsid |
8、嵌套路由
- 路由配置
给Myb组件配置二级路由
var routes=[
{
path:'/myb',
component:()=>import('../components/Myb'),
children:[
{
path:'/myb/aa',
component:()=>import('../components/Myaa'),
},
{
path:'/myb/bb',
component:()=>import('../components/Mybb'),
}
]
},
]
- 给谁设置二级路由,就在他的组件里写router-link和router-view
(例子在 Myb组件中)
<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>
9、路由进阶
-
1.路由模式
hash模式 # 默认模式
history模式
var router=new VueRouter(
{
routes,
mode:'history' //去掉#
}
)
- 2.命名路由
var routes=[
{
path:'/mya',
name:'minea', //命名路由
component:()=>import('../components/Mya')
}
]
- 3.路由跳转
//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}})
- 4.命名视图
一个组件里有多个router-view时,我们要给router-view定义name属性
<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'),
}
]
}
]
七、路由守卫
vue-router
提供的导航守卫主要用来通过跳转或取消的方式守卫导航
全局守卫 | 路由独享守卫 | 组件内的守卫 |
---|---|---|
守卫所有的路由—进入每一个路由之前执行 | 写在哪个路由上,守卫的就是那个路由 | 在路由组件内直接定义路由导航守卫 |
1、全局守卫
- 守卫所有的路由—进入每一个路由之前执行
- 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()
})
to
:即将要进入的目标 路由对象
from
:当前导航正要离开的路由
next
:一定要调用该方法来 resolve 这个钩子 进入导航
2、路由独享守卫
- 写在哪个路由上,守卫的就是那个路由
- 可以在路由配置上直接定义
beforeEnter
守卫
var routes=[
{
path:'/mine',
component:()=>import('../components/view/Mine'),
beforeEnter(to,from,next){
// console.log(to)
next();
}
}
]
3、组件内的守卫
- 在路由组件内直接定义以下路由导航守卫
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是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据共享,它采用集中式存储管理应用的所有组件的状态
优点
能够在vuex中集中管理共享的数据,易于开发和后期维护 |
---|
能够高效的实现组件之间的数据共享,提高开发效率 |
存储在vuex中的数据是响应式的,能够实时保持数据和页面的同步 |
1、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({
})
- 4.将store对象挂载到vue实例中
import store from './store'
new Vue({
el: '#app',
store,
components: {
App },
template: '<App/>'
})
2、vuex的核心概念
state | mutation | action | getter |
---|---|---|---|
存放数据 | 变更数据 | 处理异步 | 获取数据 |
this.$store.state.data | this.$store.commit(‘add’) | this.$store.dispatch(‘addAsync’) | this.$store.getters.getcount |
…mapState([‘data’]) | …mapMutations([‘add’,‘addN’]) | …mapActions([‘addAsync’,‘addNAsync’]) | …mapGetters([‘getcount’]), |
state
- state提供唯一的公共数据源,所有共享的数据都要统一放到Store的state中进行存储
export default new Vuex.Store({
//store数据源,提供唯一公共数据
state:{
count:20
}
})
- 组件访问state中数据方式
this.$store.state.全局数据名称 | 从vuex中按需导入mapState函数 |
---|
1.方式一: this.$store.state.全局数据名称
<template>
<div>
count的值是{
{
$store.state.count}}
</div>
</template>
<script>
export default {
mounted(){
console.log(this.$store.state.count)
}
}
</script>
2.方式二:从vuex中按需导入mapState函数
// 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>
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(‘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中的方法(第二种):将指定的mutations函数,映射为当前组件的methods函数
<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’,实参)
//传递多个值用对象传递
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>
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中的函数(第二种):从vuex中按需导入mapActions函数
// 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>
getter
-
getter用于对store中的数据进行加工处理形成新的数据,类似于vue中的计算属性
-
store中数据发生变化,getter的数据也会跟着变化
-
定义getter
export default new Vuex.Store({
getters:{
getcount:state=>{
return '当前最新的数量是【'+ state.count +'】';
}
}
})
- 使用getters的第一种方式
{
{
$store.getters.getcount}}
- 使用getters的第二种方式
import {
mapGetters} from 'vuex'
computed:{
...mapGetters(['getcount']),
}
九、axios
从浏览器中创建 XMLHttpRequests |
---|
从 node.js 创建 http 请求 |
支持 Promise API |
拦截请求和响应 |
转换请求数据和响应数据 |
取消请求 |
自动转换Json数据 |
客户端支持防御XSRF |
1、axios使用
//安装
cnpm install axios -S
//每个组件里单独引入
import axios from axios
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=>{
})
post方法
axios.post('/user')
.then((res) =>{
console.log(res);})
.catch((error)=> {
console.log(error);});
axios.post('/user', {
参数1: 值,参数2: 值})
.then((res) =>{
console.log(response);})
.catch((error)=> {
console.log(error);});
all方法
- axios.all([函数1(),函数2(),函数3()]) 一个函数返回一个结果,多个函数返回多个结果
- 可以实现发送多次请求,请求成功之后再做处理
- 在项目开发中,一个页面的数据需要请求多个接口地址,那我们就执行多并发请求
//执行多并发请求
// axios.all([函数1(),函数2(),函数3()]) 一个函数返回一个结果,多个函数返回多个结果
// axios.spread((结果1,结果2,结果3)=>{}) 去获取每个函数返回的结果
// 语法:
axios.all([函数1(),函数2(),函数3()])
.then(axios.spread((结果1,结果2,结果3)=>{
}))
3、axios Api
- 可以通过向
axios
传递相关配置来创建请求
axios({
配置对象})
axios({
url:"地址",
method: 'post', //请求方式
data: {
//传递的参数
参数1: 值,
参数2: 值
}
})
.then(res=>{
//res是axios返回的请求对象 res.data才是后端返回的数据})
.catch(err=>{
console.log(err)})
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=>{
})
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);
});
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方法需要导入import axios from 'axios' 再使用 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":"张三"
}
]
}
- 执行命令行
//在文件夹中进入cmd
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=>{
})
十二、Vant
- 轻量、可靠的移动端 Vue 组件库
- element-ui框架
Vant 中文教程
//安装
cnpm i vant -S
- 引入 main.js
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);
十三、token
- token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码是否正确,并作出相应的提示,这时就有了token
- token是服务端生成的一串字符串,来作为客户端进行请求的一个令牌,当第一次登陆后服务端生成一个token然后将token返回给客户端,以后客户端只需要带着这个token去请求数据就可以了,不需要再次带上用户名和密码
- 使用token的目的:减轻服务器的压力,减少了频繁的查询数据库
- 工作原理
十四、项目打包
- vue 项目中执行命令
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}}