一、MVVM模式
MVVM
M 是模型(model),V是视图(view),VM(ViewModel)是V和M的双向协调者。
view
View是视图层,也就是用户界面。前端主要由HTML和css来构建,为了更方便地展现viewModel或者Model层的数据,已经产生了各种各样的前后端模板语言,比如FreeMarker、Thymeleaf等等,各大MVVM框架如Vue.js, AngularJs, EJS等也都有自己用来构建用户界面的内置模板语言。
model
Model是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,主要围绕数据库系统展开。这里的难点主要在于需要和前端约定统一的接口规则
ViewModel
ViewModel是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的Model数据进行转换处理,做二次封装,以生成符合View层使用预期的视图数据模型。
需要注意的是ViewModel所封装出来的数据模型包括视图的状态和行为两部分,而Model层的数据模型是只包含状态的
- 比如页面的这一块展示什么,那一块展示什么这些都属于视图状态(展示)
- 页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互)
视图状态和行为都封装在了ViewModel里。这样的封装使得 ViewModel 可以完整地去描述View 层。由于实现了双向绑定,ViewModel的内容会实时展现在View层,这是激动人心的,因为前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图。
MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护ViewModel,更新数据视图就会自动得到相应更新,真正实现事件驱动编程。 View 层展现的不是Model层的数据,而是ViewModel的数据,由ViewModel 负责与Model 层交互,这就完全解耦View 层和Model层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。
MVC
M 是模型,V是视图,C是控制器(业务)
vue使用了MVVM架构来设计框架。架构是说逻辑分层,框架是指具体的实现。
很明显VM的实现是这个框架的核心,也是最复杂的地方。从更大范围上看,vue专注界面的实现,也就是MVC中的V层。因此,vue只是一个局部框架,传统MVC中的模型和业务不是vue的范畴,需要自行设计。
- Model:模型层,这里表示JavaScript对象
- View:视图层,这里表示DOM(HTML操作的元素)
- ViewModel:连接视图和数据的中间件
二、vue基础
1. 安装
<script>
引入
直接下载并用 <script>
标签引入,Vue
会被注册为一个全局变量。
对于制作原型或学习,你可以这样使用最新版本:
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
对于生产环境,我们推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏:
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
2. 常用操作
变量取值
标签内取值
使用双引号
<!--view层:模板-->
<divid="app"v-text="message">
</div>
<script>
varvm=newVue({
el:"#app",
//model:数据对象
data:{
message:"hello world!"
}
})
</script>
标签外取值
使用双大括号{{}}
<!--view层:模板-->
<divid="app">
{{ message }}
</div>
<script>
varvm=newVue({
el:"#app",
//model:数据
data:{
message:"hello world!"
}
})
</script>
绑定HTML属性
v-bind
<divid="app">
<pv-bind:title="title">html属性不能使用双大括号形式绑定,只能使用v-bind指令</p>
</div>
......
var vm = new Vue({
el: '#app',
data: {
title: 'title content'
}
});
这里的 html 最后会渲染成:
<divid="app">
<ptitle="title content">html属性不能使用双大括号形式绑定,只能使用v-bind指令</p>
</div>
v-bind缩写
<!-- 完整语法 -->
<av-bind:href="url">...</a>
<!-- 缩写 -->
<a:href="url">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a:[key]="url"> ... </a>
条件
if-else
<divid="app">
<pv-if="seen">seen为真</p>
<pv-else>seen为假</p>
</div>
<script>
varvm=newVue({
el:"#app",
data:{
// seen:true
seen:false
}
})
</script>
if-else-if
<divid="app">
<!-- ==只要求值相等,===要求类型和值都要相同-->
<pv-if="type==='A'">A</p>
<pv-else-if="type==='B'">B</p>
<pv-else-if="type==='C'">C</p>
<pv-else-if="type==='D'">D</p>
<pv-else>0</p>
</div>
<script>
varvm=newVue({
el:"#app",
data:{
type:'1'
}
})
</script>
循环
v-for
<div id="app" >
<ol>
<li v-for="(todo,index) in todos">
<!--index是当前遍历元素的索引-->
{{ todo.text }}--{{index}}
</li>
</ol>
</div>
<script>
var vm=new Vue({
el:"#app",
//js中对象用{},数组用[]
data: {
todos: [//数组中存放对象
{ text: '学习 JavaScript' },
{ text: '学习 Vue' },
{ text: '整个牛项目' }
]
}
})
</script>
事件处理
v-on
给按钮绑定单机事件,事件方法定义在methods
对象中
<div id="app" >
<button v-on:click="sayHello">点击我</button>
</div>
<script>
var vm=new Vue({
el:"#app",
//js中对象用{},数组用[]
data: {
message:"hello"
},
methods:{//方法必须定义在methods对象中
sayHello: function () {
alert(this.message)
}
}
})
</script>
v-on可以缩写
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
表单输入绑定
v-model
将表单与model变量进行双向绑定
- 修改变量,表单内容也会改变
- 修改表单内容,变量也会改变
文本
示例1:将输入框与变量message绑定
<div id="app" >
输入的文本:<input type="text" v-model="message"> {{message}}
</div>
<script>
var vm=new Vue({
el:"#app",
//js中对象用{},数组用[]
data: {
message:"hello"
},
methods:{//方法必须定义在methods对象中
}
})
</script>
单选框
实例2:将单选框与model变量message绑定,并且在下面展示出选择的内容
将单选框的value与message进行双向绑定
<div id="app">
<input type="radio" value="男" v-model="message">男
<input type="radio" value="女" v-model="message">女
您的选择:{{message}}
</div>
<script>
var vm=new Vue({
el:"#app",
//js中对象用{},数组用[]
data: {
message:""
},
methods:{//方法必须定义在methods对象中
}
})
</script>
下拉框
<div id="app">
<select v-model="message">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
您的选择:{{message}}
</div>
<script>
var vm=new Vue({
el:"#app",
//js中对象用{},数组用[]
data: {
message:""
},
methods:{//方法必须定义在methods对象中
}
})
</script>
3. Vue组件
组件是可复用的 Vue 实例,可以抽取公共片段,类似于thymeleaf的th:fragment
这里有一个 Vue 组件的示例:在template中编写可重用组件
<divid="app">
<button-counter></button-counter>
</div>
<script>
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
varvm=newVue({
el:"#app",
})
</script>
组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 <button-counter>
。
- 一个组件的
data
选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝
通过 Prop 向子组件传递数据
用于修改公共页面的动态部分,如网页title,用户名等
Prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。
- 自定义组件:zujian
- 自定义组件的属性:shuxing
- 调用组件时,通过v-bind将组件属性与data变量绑定
<divid="app">
<zujianv-bind:shuxing="title"></zujian>
</div>
<script>
Vue.component('zujian',{
props:['shuxing'],
template:'<h3>{{ shuxing }}</h3>'
})
varvm=newVue({
el:"#app",
data:{
title:"这是标题"
}
})
</script>
new Vue() 和 export default{}的区别
new Vue()
Vue 就是一个构造函数,生成的实例是一个巨大的对象,可以包含数据、模板、挂载元素、方法、生命周期钩子等选项。
所以渲染的时候,可以使用构造 Vue 实例的方式来渲染相应的 html 页面:
new Vue({
el: '#app'
...
})
export default {}
这是在复用组件的时候用到的。假设我们写了一个单页面组件 A 文件,而在另一个文件 B 里面需要用到它,那么就要用 ES6 的 import/export 语法 ,在文件 A 中定义输出接口 export **,在文件 B 中引入 import **,然后再生成一个 Vue 实例 new Vue (**),把引入的组件用起来,这样就可以复用组件 A 去配合文件 B 生成 html 页面了。
所以在复用组件的时候,export 和 new Vue 缺一不可。
export 用来导出模块,Vue 的单文件组件通常需要导出一个对象,这个对象是 Vue 实例的选项对象,以便于在其它地方可以使用 import 引入。而 new Vue() 相当于一个构造函数,在入口文件 main.js 构造根组件的同时,如果根组件还包含其它子组件,那么 Vue 会通过引入的选项对象构造其对应的 Vue 实例,最终形成一棵组件树。
export 和export default 的区别在于:export 可以导出多个命名模块,例如:
//demo1.js
export const str = 'hello world'
export function f(a){
return a+1
}
对应的引入方式:
//demo2.js
import { str, f } from 'demo1'
export default 只能导出一个默认模块,这个模块可以匿名,例如:
//demo1.js
export default {
a: 'hello',
b: 'world'
}
对应的引入方式:
//demo2.js
import obj from 'demo1'
引入的时候可以给这个模块取任意名字,例如 "obj",且不需要用大括号括起来。
三、Axios异步通信
cdn
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
get请求
{
"name":"狂神说java",
"url": "http://baidu.com",
"page": 1,
"isNonProfit":true,
"address": {
"street": "含光门",
"city":"陕西西安",
"country": "中国"
},
"links": [
{
"name": "B站",
"url": "https://www.bilibili.com/"
},
{
"name": 4399,
"url": "https://www.4399.com/"
},
{
"name": "百度",
"url": "https://www.baidu.com/"
}
]
}
<div id="app">
{{name}}
</div>
<script>
var vm=new Vue({
el:"#app",
data:{
name:'',
},
mounted(){//钩子函数,用来加载异步请求
axios.get("data.json").then(response=>(this.name=response.data.address.city))
}
})
</script>
四、vue-cli
nodejs淘宝镜像加速
npm config set registry https://registry.npm.taobao.org
安装vue-cli
npm install vue-cli -g
创建
在projects文件夹下初始化vue项目
myvue是名字,一路no即可
vue init webpack myvue
进入myvue文件的cmd窗口,下载所有的依赖
cd myvue
npm install
运行
npm run dev
Ctrl+C停止
配置
vue.config.js
设置proxy与端口
proxy是ES6中就存在的,用于修改某些操作的默认行为,可以理解成在目标对象前设一个拦截层,因此也叫“代理器”。
如果你的前端应用和后端 API 服务器没有运行在同一个主机上,你需要在开发环境下将 API 请求代理到 API 服务器。这个问题可以通过 vue.config.js 中的 devServer.proxy 选项来配置。
module.exports={
devServer: {
disableHostCheck: false,
port: 8181, //指定项目运行的端口号端口号
//配置代理
proxy: {
"/api": {
target: "http://localhost:8080",//代理地址,这里设置的地址会代替axios中设置的baseURL
changeOrigin: true,// 是否跨域,虚拟的站点需要更管origin
pathRewrite: {
"^/api": ""
//pathRewrite: {'^/api': '/'} 重写之后url为 http://localhost:8080/xxxx
//pathRewrite: {'^/api': '/api'} 重写之后url为 http://localhost:8080/api/xxxx
}
}
},
},
chainWebpack: config=> {
config.resolve.alias.set("@", resolve("src"));
}
}
constpath=require("path");
functionresolve(dir) {
returnpath.join(__dirname, dir);
}
axios请求
getNewsList() {
axios.get('/api/getData')
.then((res) => {
console.log(res)
})
}
axios
中baseURL
如果被配置过,如:baseURL="http://192.168.100.1/9000"
,此时proxy代理不生效
五、webpack
打包工具
安装
npm install webpack -g
npm install webpack-cli -g
查看版本
webpack -v
使用
一、定义一个模块hello.js
//暴露方法
exports.sayHello=function () {
document.write("<h1>hello,webpack</h1>")
}
二、在main.js中引入hello模块,并调用其方法
//引入hello模块
var hello=require("./hello");
//调用hello模块中的sayHello方法
hello.sayHello()
三、创建 webpack.config.js
配置文件
- entry:入口文件,指定WebPack 用哪个文件作为项目的入口
- output:输出,指定WebPack把处理完成的文件放置到指定路径
- module:模块,用于处理各种类型的文件
- plugins:插件,如:热更新、代码重用等
- resolve:设置路径指向
- watch:监听,用于设置文件改动后直接打包
// entry:入口文件,指定WebPack 用哪个文件作为项目的入口
// output:输出,指定WebPack把处理完成的文件放置到指定路径
// module:模块,用于处理各种类型的文件
// plugins:插件,如:热更新、代码重用等
// resolve:设置路径指向
// watch:监听,用于设置文件改动后直接打包
module.exports={
entry:"./modules/main.js",
output:{
filename:"./js/bundle.js"
}
}
四、在命令行中使用webpack
命令打包
webpack --watch可以实现热部署,实时监听变化
得到打包后的js文件:dist/js/bundle.js
五、在index.heml中导入打包后的js文件
<script src="dist/js/bundle.js"></script>
六、vue-router路由
npm install vue-router --save-dev
路由配置文件 src/router/index.js
类似controller的功能,将请求路径与资源进行绑定
那么每次路径的改变,其实是改变页面的组件,并不是加载一个新的页面
importVuefrom"vue";
importVueRouterfrom"vue-router";
importcontentfrom"../components/content";
importMainfrom"../components/Main";
//使用路由
Vue.use(VueRouter);
//配置导出路由
exportdefaultnewVueRouter({
routes:[
{
//路由路径
path:"/content",
//路由名字
name:"content",
//跳转的组件
component:content
},
{
path: "/login",
name: "登录",
hidden: true,
component: () =>import("../views/login/Login.vue")//vue3的新语法,将login.vue组件与路径"/login"进行绑定
}
]
});
src/main.js
importVuefrom'vue'
importAppfrom'./App'
importrouterfrom"./router"//自动加载index.js配置
Vue.config.productionTip=false
newVue({
//配置路由
router,
el: '#app',
components: { App },
template: '<App/>'
})
router-link和router.push
区别:
使用router-link方式跳转,会在页面渲染的时候就加载对应的路由比起直接写的方式的优点:不管是h5的history还是hash模式,切换的时候很方便;会默认阻止浏览器的默认事件;写路径的时候不用写基路径了。
$router.push("/myroute")跳转到对应的路径,可在页面中写对应的点击事件,然后执行对应的方法中写跳转,可在跳转前再写一些别的逻辑。
<router-link :to="...">
to里的值可以是一个字符串路径,或者一个描述地址的对象。例如:
// 字符串
<router-linkto="apple">toapple</router-link>
// 对象
<router-link :to="{path:'apple'}">toapple</router-link>
// 命名路由
<router-link :to="{name: 'applename'}">toapple</router-link>
//直接路由带查询参数query,地址栏变成 /apple?color=red
<router-link :to="{path: 'apple', query: {color: 'red' }}">toapple</router-link>
// 命名路由带查询参数query,地址栏变成/apple?color=red
<router-link :to="{name: 'applename', query: {color: 'red' }}">toapple</router-link>
//直接路由带路由参数params,params 不生效,如果提供了 path,params 会被忽略
<router-link :to="{path: 'apple', params: { color: 'red' }}">toapple</router-link>
// 命名路由带路由参数params,地址栏是/apple/red
<router-link :to="{name: 'applename', params: { color: 'red' }}">toapple</router-link>
同样的规则也适用于router.push(...)方法
// 字符串
router.push('apple')
// 对象
router.push({path:'apple'})
// 命名路由
router.push({name: 'applename'})
//直接路由带查询参数query,地址栏变成 /apple?color=red
router.push({path: 'apple', query: {color: 'red' }})
// 命名路由带查询参数query,地址栏变成/apple?color=red
router.push({name: 'applename', query: {color: 'red' }})
//直接路由带路由参数params,params 不生效,如果提供了 path,params 会被忽略
router.push({path:'applename', params:{ color: 'red' }})
// 命名路由带路由参数params,地址栏是/apple/red
router.push({name:'applename', params:{ color: 'red' }})
注意:
1、关于带参数的路由总结如下:
无论是直接路由“path" 还是命名路由“name”,带查询参数query,地址栏会变成“/url?查询参数名:查询参数值“;直接路由“path" 带路由参数params params 不生效;命名路由“name" 带路由参数params 地址栏保持是“/url/路由参数值”;
2、设置路由map里的path值:
带路由参数params时,路由map里的path应该写成: path:'/apple/:color' ;带查询参数query时,路由map里的path应该写成: path:'/apple' ;
3、获取参数方法:
在组件中: {{route.params.color}} 在js里: this.route.params.color
七、elementUI
//初始化项目
vueinitwebpackeleUI
//进入工程目录
cdeleUI
//安装vue-router
npminstallvue-router--save-dev
//安装elementUI
npmielement-ui-S
//安装依赖
npminstall
//安装SASS加载器
npminstallsass-loadernode-sass--save-dev
//启动测试
npmrundev
八、npm脚本
概念
npm 允许在package.json
文件里面,使用scripts
字段定义脚本命令。
{
// ...
"scripts": {
"build": "node build.js"
}
}
上面代码是package.json
文件的一个片段,里面的scripts
字段是一个对象。它的每一个属性,对应一段脚本。比如,build
命令对应的脚本是node build.js
。
命令行下使用npm run
命令,就可以执行这段脚本。
$ npm run build
# 等同于执行
$ node build.js
这些定义在package.json
里面的脚本,就称为 npm 脚本。它的优点很多:
- 项目的相关脚本,可以集中在一个地方。
- 不同项目的脚本命令,只要功能相同,就可以有同样的对外接口。用户不需要知道怎么测试你的项目,只要运行
npm run test
即可。 - 可以利用 npm 提供的很多辅助功能。
查看当前项目的所有 npm 脚本命令,可以使用不带任何参数的npm run
命令。
$ npm run
参数
npm install packagename --save 或 -S
--save、-S参数意思是把模块的版本信息保存到dependencies(生产环境依赖)中,即你的package.json文件的dependencies字段中;
npm install packagename --save-dev 或 -D
--save-dev 、 -D参数意思是吧模块版本信息保存到devDependencies(开发环境依赖)中,即你的package.json文件的devDependencies字段中;
九、vuex
组件之间共享数据
Vuex中的主要核心概念如下:
- State
- Mutation
- Action
- Getter
State
在Vuex中,State提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储,这里的Store相当于一个用于存储数据的公共容器。
conststore=newVue.Store({
state: {
count: 0
}
...
})
在js中访问State中的数据
this.$store.state.全局数据名称
在HTML中
<span>{{$store.state.全局数据名称}}</span>
Mutation
mutation:变化
Vuex中的Mutation是用于变更Store中的数据。
在Vuex中,只能通过mutation变更Store数据,不可以直接操作Store中的数据。虽然通过mutation的方式来操作数据,虽然繁琐了一点,但是却可以集中监控所有数据的变化。
例如需要让全局数据自增加1,则可以通过如下的方式在Mutation中定义
conststore=newVuex.Store({
state: {
count: 0
},
mutations: {
add(state) {
//变更状态
state.count++;
}
}
})
触发mutation函数
在方法中,通过$store.commit()函数来触发mutation
commit参数为mutation函数的名字
methods: {
handle1 () {
// 触发mutations的第一种方式
this.$store.commit('add')
}
}
接着就可以通过@click方法来调用handle1,从而来触发mutation函数。
另外,可以在触发mutation函数时,传入参数
conststore=newVuex.Store({
state: {
count: 0
},
mutations: {
add(state) {
//变更状态
state.count++;
},
addN(state, n) {
state.count+=n;
}
}
})
然后定义handler2
commit参数为mutation函数的名字和参数
methods: {
handler2: {
this.$store.commit('addN', 5);
}
}
Action
从vuex官网中可以了解到,Action类似于mutation,不同之处在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
可以得出一个结论就是,如果通过异步操作变更数据,必须通过Action,而不能使用Mutation,但是在Action中还是要通过触发Mutation的方式间接变更数据。
conststore=newVuex.Store({
state: {
count: 0
},
mutations: {
add(state) {
//变更状态
state.count++;
},
addN(state, n) {
state.count+=n;
}
},
actions: {
// 通过context去调用mutation
addAsync(context) {
setTimeout(() => {
context.commit('add'
}, 1000)
},
// 调用Actions是也可以传入参数
addNAsync(context, n) {
setTimeout(() => {
context.commit('addN', n);
}, 1000);
}
}
})
需要再次强调的是,只有通过mutation中定义的函数,才有权利去修改state中的数据,因此actions最终还是要调用mutation。
触发action函数
methods: {
handleAddAsync() {
this.$store.dispatch('addAsync');
},
handleAddNAsync() {
this.$store.dispatch('addNAsync', n);
}
}
Getter
在Vuex官网中,用到了派生这一词来介绍Getter,在这里可以理解为就是用于对Store中的数据进行加工处理,形成新的数据,类似Vue的计算属性。Getter的数据是基于Store中的数据的,所以当Store中数据发生变化时,Getter中的数据也会随之变化。
定义Getter
例如state中存有todos计划项,其对象有一个done状态表示完成与否。
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
// 这里通过getters定义的doneTodos方法来过滤已完成的todo项
doneTodos: state => {
return state.todos.filter(todo => todo.done);
},
// 这里还可以通过传入getters对象来获取其他方法
doneTodosCount: (state, getters) => {
return getters.doneTools.length;
},
// 传入参数
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id == id);
}
}
})
触发getter函数
this.$store.getters.doneTodos // -> [{id: 1, text: '...', done: true}]
this.$store.getters.doneTodosCount // -> 1
Module
当Store中存放了非常多非常大的共享数据对象时,应用会变的非常的复杂,Store对象也会非常臃肿,所以Vuex提供了一个Module模块来分隔Store。通过对Vuex中的Store分隔,分隔成一个一个的Module模块,每个Module模块都拥有自己的state、mutation、actions和getters。
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
对于模块中的mutations和getters,传入的第一个参数规定为state,而actions则依旧是context参数。如下:
const moduleA = {
state: {
count: 0
},
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
},
actions: {
// context对象其实包含了 state、commit、rootState。
incrementIfOddRootsum (context) {
if ((context.state.count + context.rootState.count) % 2 === 1) {
// 调用mutations
commit('increment')
}
}
}
}
总结
- Vuex主要用于管理Vue组件中共享的数据。
- Vuex中有state、mutation、action、getter等核心概念。
- 获取state可以通过this.$store.state.xx或者是通过定义mapState来获取。
- 修改state中的变量需要通过mutation函数实现,而mutation的触发由两种方式,一种是通过this.$store.commit()函数,另外一种是通过mapMutations来实现。
- mutation只能用于修改数据,而Actions可以实现异步操作。
- 通过Actions的异步操作+mutation的修改数据,可以实现异步修改数据。调用Actions有两种方式,第一种是通this.$store.dispatch来调用,另外一种方式是通过mapActions来调用。
- Getters函数用于对Store中数据进行加工,不会修改原本Store中的数据;Getters中的数据会受Store中数据进行影响。