Vue

MVVM

  • M Model:数据模型层 (数据)
  • V View :视图层 (页面 DOM)
  • VM ViewModel:负责监控视图层和数据模型层的数据,并对应的通知另一侧进行修改(监控者)

Vue

Vue 是一套用于构建用户界面的渐进式框架,使用 vue 必须先引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="box">
<h1>
{{title}}
</h1>
<p>{{age}}</p>
</div>

<script>
var firstVue = new Vue({
el: ".box",
data: {
title: "Vue",
age: 5
}
});
</script>

Vue 的模板语法

v-bind

将 data 中的数据绑定到 html 元素的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<p class="pic">
<!-- 用v-bind将data中的数据绑定到html元素的属性上 -->
<img v-bind:src="src" v-bind:alt="title" v-bind:width="width" v-bind:title="title">
<!-- 可以简写成以下形式 -->
<img :src="src" :alt="title" :width="width" :title="title">
</p>

<script>
var imgVm = new Vue({
el: ".pic",
data: {
src: "https://cn.vuejs.org/images/logo.png",
title: "Vuelogo",
width: 200
}
});
</script>

v-if

v-if 用于控制 html 元素是否创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<div id="app">
<ul v-if="list.length"> <!--当满足这个条件的时候,创建ul-->
<li>{{list[0]}}</li>
</ul>
<p v-else> <!--否则,创建p-->
{{title}}
</p>

<p>
<span v-if="salary<6000">当salary小于6000的时候我才创建</span>
<span v-else-if="salary>10000">当salary大于1000的时候我才创建</span>
<span v-else>当上面的都不满足时创建我</span>
</p>
</div>

<script>
var vm = new Vue({
el: "#app",
data: {
list: ["hello"],
title: "v-if的用法",
salary: 10000
}
});
</script>

v-show

v-show 用于控制 html 元素是隐藏还是显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="app">
<!-- v-show决定html元素是隐藏还是显示 -->
<div v-show="isShow" style="width:300px;height:100px;border:1px solid #ccc"></div>
<input type="button" value="显示/隐藏div" @click="isShow = !isShow"/>
</div>
<script>
// v-if 用于控制html元素是否创建
// v-show 用于控制html元素显示与隐藏
var vm = new Vue({
el:'#app',
data:{
isShow:true,
}
})

v-for

用于遍历数组或者对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<div id="app">
<ul>
<!-- 遍历数组 item接收的是数组元素 index接收的下标 -->
<li v-for="(item,index) in directives">
<span>{{index+1}}、</span> <span>{{item.name}}</span>
<p>{{item.desc}}</p>
</li>
</ul>
<hr />
<p v-for="(item,index) in directives">
<!-- 遍历普通对象 -->
<span v-for="(val,key) in item">{{keyDescs[key]}}:{{val}}</span>
</p>
<hr />
<p>
<!-- v-for用户控制循环次数 -->
<span v-for="i in count">*</span>
</p>

<hr />
<div>
<!-- template标签可以使它来包裹多个标签,并不影响html的原结构 -->
<template v-for="(item,index) in directives">
<h2>指令名称</h2>
<p>指令描述</p>
<hr />
</template>
</div>
</div>
<script>
// # v-for常用于:
// 遍历数组
// 遍历{}
// 控制循环次数
// template标签
var vm = new Vue({
el: "#app",
data: {
count: 4,
keyDescs: {
id: "ID",
name: "指令名称",
desc: "指令作用"
},
directives: [
{
id: 1,
name: "v-bind",
desc: "绑定html元素的属性"
},
{
id: 2,
name: "v-show",
desc: "控制元素显示或者隐藏"
},
{
id: 3,
name: "v-for",
desc: "遍历数组或者对象"
}
]
}
});
</script>

v-on

vue 中的点击事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<div id="app">
<h3>现在人数是:{{count}}</h3>
<!-- 绑定单击事件并且调用add方法,此方法写在methods属性中的 事件处理写的是调用函数 -->
<p><input type="button" value="加1" v-on:click="add()"/></p>
<!-- 绑定单击事件并且调用decrement方法,此方法写在methods属性中的 事件处理写的是函数名 -->
<p><input type="button" value="减1" v-on:click="decrement"/></p>
<!-- 绑定单击事件,执行count-=2的js代码 -->
<p><input type="button" value="减2" v-on:click="count-=2"/></p>
<!-- 以缩写方式绑定事件 @事件名="" -->
<p><input type="button" value="加2" @click="count += 2"/></p>
<!-- 直接写方法名时是没有办法再传递其它参数 -->
<select @change="changeKey">
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
</select>
<!-- $event代表是当前事件对象,Vue中定义的内置变量 -->
<select @change="changeKey2($event,'额外的参数')">
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
</select>
<p v-if="key">{{styles[key]}}</p>
</div>
<script src="../js/vue.js"></script>
<script>
new Vue({
el: "#app",
data: {
count: 40,
key: "a",
styles: {
a: "red",
b: "blue",
c: "orange"
}
},
// methods属性中定义的都是些方法
methods: {
add() {
// this 代表的是当前vue实例
// this.count 访问到当前实例上的数据中定义的count属性
this.count = this.count + 1;
},
decrement() {
this.count--;
},
changeKey(event) {
// event代表的是当前事件对象,event.target获取事件源,dom元素
console.log(event, event.target, event.target.value);
this.key = event.target.value;
console.log("改变key的值");
},
changeKey2(msg, evn) {
console.log(msg);
this.key = event.target.value;
}
}
});
</script>

v-model

数据双向绑定 v-model

v-model.lazy:延迟视图更新,当按下回车键或者输入框失去焦点,视频是更新。不会随着输入框的内容同步更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<div class="box">
<select v-model="imgs" @change="changeImg()">
<option value="./images/1.jpg">1.jpg</option>
<option value="./images/2.jpg">2.jpg</option>
<option value="./images/3.jpg">3.jpg</option>
<option value="./images/4.jpg">4.jpg</option>
<option value="./images/5.jpg">5.jpg</option>
<option value="./images/6.jpg">6.jpg</option>
</select>
<p>
<img :src="src" alt="">
</p>
</div>
<script>
new Vue({
el: ".box",
data: {
imgs: "",
src: ""
},
methods: {
changeImg() {
this.src = this.imgs;
}
}
});
</script>

数据检测更新

动态添加一个对象的属性时并不会触发视图变化(没有被监听)

可以通过 Vue.set(对象,属性名,值)

和 this.$set(对象,属性名,值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<div id="app">
<div v-for="(item,index) in book">
<p><span>{{index}}:</span>{{item}}</p>
</div>
<select v-model="imgSrc">
<option value="1.jpg">1.jpg</option>
<option value="2.jpg">2.jpg</option>
<option value="3.jpg">3.jpg</option>
</select>
<input type="button" value="设置书的封面图" @click="setImg" />
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
imgSrc: "1.jpg",
book: {
id: 1,
name: "终身成长"
}
},
methods: {
setImg() {
// Vue.set(this.book,'src',this.imgSrc) //动态添加某对象的属性
this.$set(this.book, "src", this.imgSrc); //动态添加某对象的属性
}
}
});
</script>
PS: // 数组的方法 // 能改变原数组的方法:
push(),unshift(),shift(),pop(),splice(),reverse(),sort() ----直接触发视图更新 //
不能改变原数组的方法返回的一个的数组 :concat(),filter(),slice(),map()
----需要将返回的新数组重新赋给原数组 // 通过数组名[下标] = newValue
这样的方式是不能导致视图更新 如:
<div id="app">
<ul>
<li v-for="item in funcs">{{item}}</li>
</ul>
<input type="button" value="修改第二个元素" @click="modify()"/>
</div>
<script>
new Vue({
el:'#app',
data:{
funcs:['slice','chartAt','match','split']
},
methods:{
modify(){
// 将数组的第二个元素改变为some
#错误 this.funcs[1] = 'some'

// 解决方案:

//第一种: Vue.set(this.funcs,1,'some')

//第二种:this.$set(this.funcs,1,'some')

//第三种:this.funcs.splice(1,1,'some')

}
}
})
</script>

事件修饰符

事件修饰符可以连写

  • .stop 阻止事件冒泡
1
2
3
<div @click="console.log('div')">
<input type="button" value="点击" @click.stop="console.log('button')"/>
</div>
  • .prevent 阻止默认事件
1
2
3
<a href="" @click.prevent="show">查看头相</a>
#连写
<a href="" @click.prevent.stop="show">查看头相</a>
  • .once 事件绑定一次
1
<a href="javascript:;" @click.once.stop="show">只能查看一次头相</a>

##计算属性

计算属性放在computed里面

计算属性名是一个方法,该方法必须要返回一个值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<box id="app">
<p>{{sum}}</p>
<input type="text" v-model.number='x'>
<!-- .number 将输入的内容强制转换为数值型 转换的规则是用parseInt()
如果通过它转换结果为NaN则不转换 -->
<input type="text" v-model.number='y'>
</box>
<script>
new Vue({
el: "#app",
data: {
x: "",
y: ""
},
computed: {
sum() {
return this.x + this.y;
}
}
});
</script>

watch

监听

组件 component

在使用组件时,多个单词的名字,要用横线链接。

必须先写组件,再创建 Vue 实例

template 里面写的是 html 模板,它只能包含一个 html 根元素

在组建中的 data 必须是一个函数,并且返回一个对象,这样设计的模式是保证每一个组件之间都是各自独立的,互不影响的。

props 属性值是不能被修改的(单向数据流)

创建全局组件

1
2
3
4
5
6
7
8
9
10
11
12
13
Vue.component('组件名',组件的配置对象) 
1.创建
Vue.component('ThisIsHeader',{
props:[], template:`
<header>
<img src=""/>
<h2>
全局组件适用于整个Vue实例
</h2>
</header>
` })
2.使用
<thisis-header></thisis-header>

属性验证

1
2
3
4
5
6
props:{
属性名:{
type:String, //type(String,Number,Boolean,Object,Array,Function,Promise)
default:()=>''
}
}

通信

父传子

  • 父组件

    1
    2
    3
    4
    5
    <template id="parent">
    <div>
    <child :message="msg"></child>
    </div>
    </template>
  • 子组件

    在子组件的props中可以拿到父组件传递过来的值

    1
    2
    3
    4
    5
    6
    7
    8
    数据写法:
    props:['message']
    对象写法:
    props:{
    message:{
    type:Object
    }
    }

子传父

实现方法是父组件自定义一个事件,在子组件中$emit(‘父组件中自定义的事件名’,需要传递的值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
+ 子组件
<div>
<!-- 定义一个传值的方法 -->
<input type="button" value="点击传递" @click="send">
</div>
<script>
export default {
data () {
return {
childValue: '子组件的数据'
}
},
methods: {
send () {
// childByValue是在父组件on监听的方法
// 第二个参数this.childValue是需要传的值
this.$emit('parentEvent', this.childValue)
}
}
}
</script>

+ 父组件
<template>
<child @parentEvent="childByValue"></child>
</template>
<script>
import child from './child'
export default {
components: {
child
},
data () {
return {
name: ''
}
},
methods: {
childByValue:(data)=>{
// childValue就是子组件传过来的值
this.name = data
}
}
}
</script>

非父子组件通信

主要实现方法是定义一个公共vue实例文件bus.js作为中间媒介进行传值

1
2
3
4
5
6
7
8
9
10
11
12
13
//bus.js
import Vue from 'vue'
export default new Vue()

/*
* 在通信的两个组件中都需引入bus.js
*/
传递:
Bus.$emit('val',this.res)
接受:
Bus.on('val',data=>{
cosole.log(data)
})

Vue实例生命周期

销毁组件或者Vue实例

1
2
3
4
5
6
# 绑定事件,然后在事件上调用clear方法
methods:{
clear(){
this.$destroy(true) //手动销毁vue
}
},

8个事件钩子

  • beforeCreate( )
  • created( ) —–能拿到实例中的data数据,在这一步即可进行ajxa请求数据
  • beforeMount( ) —-可以拿到元素,但是元素里面的数据未被解析渲染
  • mounted( )—-操作DOM元素的地方,echarts应用在Vue中,Swiper
  • beforeUpdate( )
  • updated( )
  • beforeDestroy( )
  • destroyed( )

Vue组件的生命周期

  • beforeCreate( )
  • created( ) —–可以拿到props属性的值
  • beforeMount( ) —-在组件中此方法拿不到DOM元素
  • mounted( )—-与Vue实例的生命周期不同,组建中需要到这一步才能拿到Dom元素
  • beforeUpdate( )
  • updated( )—–不建议在update中修改data的数据,会导致页面又重新渲染。可以选择在beforeUpdate或者watch中修改
  • beforeDestroy( )
  • destroyed( )

axios

axios是一个ajax请求的框架

get方式:axios.get( url ).then( ).catch( )

post方式:axios.post( url , data ).then( ).catch( )

  • HTML
1
2
3
4
5
6
7
8
9
10
<div id="app">
<p><input type="text" v-model="title" /></p>
<p><input type="text" v-model="hours" /></p>
<input type="button" value="添加" @click="add()"/>
<ul>
<li v-for="(item,index) in list">
<span>{{item.title}}</span><span>{{item.hours}}</span>
</li>
</ul>
</div>
  • JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<script>
new Vue({
el: '#app',
data: {
list: [],
title: '',
hours: ''
},
methods: {
getData() {
axios.get('/task/list').then(res => {
if(res.data.error==0){
console.log(res);
this.list = res.data.data.list
}
})
},
add() {
axios({
url: "/task/add",
method: 'post',
data: {
title: this.title,
hours: this.hours
}
}).then(res => {
this.getData()
}).catch(err => {

})
}
},
created() {
this.getData()
}

})
</script>
  • axios统一处理请求或响应

统一集中处理接口传入相同参数的设置以及统计处理错误码的设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 设置请求的根路径
axios.defaults.baseURL = 'http://localhost:9000/';
// 设置ajax请求超时时间
axios.defaults.timeout = 2000
// 设置请求拦截器
axios.interceptors.request.use((config)=>{
// 在此可以设置所有接口都需要用到的参数
// console.log('interceptors.request',config)
config.headers['usertoken'] = '299429942428423191031031';
let joinStr = config.url.indexOf('?') === -1 ? '?' : '&';
config.url += joinStr + 'v1=1.0'
return config
})
// 设置响应拦截器
axios.interceptors.response.use(result => {
console.log('请求成功时得到的数据',result)
return result.data
},error=>{
if(error.message.includes('timeout')){
alert('请求超时')
return Promise.reject('timeout');
}
return Promise.reject(error);
});

ref属性

$refs–通过它可以获取到html元素或者组件,

this.$refs.html标签的ref属性值 —获取到对应的dom元素

循环出来的元素上加了ref属性时,通过this.$refs.ref值是可以全部获取的(获取出来的是个数组)

过滤器

  • 全局自定义过滤器
1
2
3
4
5
6
# 定义
Vue.filter('过滤器名称',(value)=>{

})
# 使用
<P>{{name|过滤器名称}}</P>
  • 局部过滤器
1
2
3
4
5
6
7
8
// 在组件的选项中定义
filters: {
capitalize: function (value) { //过滤器名:capitalize
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}

自定义指令

除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令,有时候需要对普通DOM元素进行底层操作,这时候就会用到自定义指令。

  • 注册全局指令
1
2
3
4
5
6
7
8
9
10
11
12
Vue.directive('指令名',{
//内置三个钩子函数bind,inserted,update
bind(el,binding,vnode){

},
inserted(el,binding,vnode){

},
update(el,binding,vnode){

}
})
  • 注册局部指令
1
2
3
4
5
6
7
8
9
10
11
12
// 在组件的选项中定义
directives: {
focus: { //指令名:focus
inserted: function (el,binding,vnode) {
// 指令所绑定的元素,可以用来直接操作 DOM 。
// binding 一个对象,所包含属性参考官网,其中有一个value属性获取到使用指令时传递的参数
el.focus()
}
}
}

// 在模板中使用方式:<input v-focus />

Vue-cli

  • 全局安装vue-cli
1
npm install @vue/cli -g
  • 新建一个项目
1
2
3
4
5
6
7
8
# 在存放项目的地方打开终端
1.vue create ProjeceName(项目名称)
2.选择Manually select features
3.勾选自己想要的包(Babel,Router,Vuex,css pre-processors,Linter/Formatter)
4.Sass/scss(with node-sass)
5.standard
6.Lint on save
7.ted config files
  • 查询npm来源以及更改成淘宝镜像
1
2
查询:npm get registry
更改成淘宝镜像: npm set registry="http://registry.npm.taobao.org"
  • proxy代理配置
1
2
3
4
5
6
7
8
9
10
11
// 在vue.config.js中添加以下代码
devServer:{
port:3000,
'/api':{
target:'http://xxxx.com/',
changeOrigin: true,
pathRewrite: { //路径重写
'/api': ''
}
}
}

动态路由

在定义路由时路径中带有冒号的即是动态路由,例如路径是 /film/:id 为动态路由,通过this.$route.params.id 获取到相应的值。

子路由/嵌套路由

在路由里在嵌套子路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export default new Router({
routes: [{
path:'/', //重定向:当访问的是根目录的时候,跳转到/films路径
redirect:'/films'
},{
path: '/films',
name: 'home',
component: films,
children:[{ //子路由
path:'',
redirect:'/films/nowplaying' //重定向:当访问/films时,直接跳到/films/nowplaying
},{
path:'nowPlaying',
component:NowPlaying
},{
path:'comingSoon',
component:ComeSoon
}]
},{
path:'/center',
component:Center
}]
})

编程式导航

1
2
3
4
5
6
7
8
9
this.$router.push('路径' )
this.$router.push({path:'路径',query:{参数名:值}})
this.$router.push({name:'路由名称',params:{参数名:值}})
// 注意:this.$router.push方法的参数是对象时,path不能与params搭配使用
// 获取动态路由中的参数使用this.$route.params
// 获取路径中?部分的参数使用this.$route.query
# 例如
// /article/11 ===> this.$route.params.xx
// /article/detail?id=11====>this.$router.query.id

路由守卫

  • 路由全局守卫 (全局前置守卫(全局拦截))
1
2
3
4
5
6
const router = new VueRouter({ ... })
router.beforeEach((to,from,next)=>{
//to 是即将转向的路由对象
//from 是从哪儿来的路由对象
//next是一个函数,继续执行,即验证通过next,否则拦截
})
  • 单个路由守卫 (单个拦截)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter((to,from,next)=>{
//to 是即将转向的路由对象
//from 是从哪儿来的路由对象
//next是一个函数,继续执行,即验证通过next,否则拦截
})
}
]
})
  • 组件内部的路由守卫 (单个拦截)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
next(vm => {
// 通过 `vm` 访问组件实例
})
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}

监听路由

监听路由的变化,当路由发生变化时要执行的操作

1
2
3
4
5
6
7
8
9
watch:{
'$router':{
handler(newVal){
//重新请求调用 当前路由发生变化时将调用该方法,如/user/1切换到/user/2时
},
deep:true,
immediate:true //页面初始化完成立即监听
}
}

路由懒加载

只有访问该组件对应的路径时,才将相对应的js文件打包。按需加载

1
2
3
4
5
6
7
8
9
10
11
12
export default new Router({
routes: [
{
path:'/active',
component: () => import('./views/active.vue')
},
{
path:'/center',
component: () => import('./views/center.vue')
}
]
})

Vuex

各组件之间的数据共享(改变共享数据是需要符合预设的规则才能被修改)

在一个组件中导出了多个方法,在导入的时候要加花括号

  • state
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mapState是一个方法可以简便获取store中的state
mapGetters是一个方法可以简便获取store中的getters
import {mapState,mapGetters} from 'vuex' //用此方法在组件中渲染的时候则用{{xxx}},否则用{{$store.state.xxx}}
export default {
name: 'home',
computed:{
...mapState(['total','username']),
...mapGetters(['totalMsg'])
},
components: {

}
}
var a = [10,20]
var b = [...a,33,44]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 状态管理中心
let store = new Vuex.Store({
// 数据中心
state:{
total:10, // 水的库存(桶)
username:'admin'
},
// 同步行为(只有mutation中的方法才能修改state中的数据)
mutations:{
addTotal(state,val){
// state接收的是当前状态管理中心的数据中心
console.log('同步行为addTotal',state,val)
state.total += val
},
reduceTotal(state,val){
console.log('同步行为reduceTotal',state,val)
state.total -= val
}
},
// 异步行为(异步行为是不能直接修改state,只能提交给mutaions中的方法来执行)
actions:{
ybAddTotal(store,val){
// store变量代表是当前状态管理中心
console.log('异步行为ybAddTotal',store,val)
setTimeout(()=>{
// 成功了则提交mutations中的方法
store.commit('addTotal',val)
},3000)
},
ybJianTotal(store,val){
setTimeout(()=>{
// 成功了则提交mutations中的方法
store.commit('reduceTotal',val)
},2000)
}
},
// 一些变量,变量名作为方法,方法一定要返回一个值,根据现有state中某个变量得来的结果
getters:{
totalMsg(state){
// state接收的是当前状态管理中的state
return state.total > 20 ? '' : '库存不足'
}
}
})

export default store
0%