2017.4.17-4.21
前言
本周继续上周的登录界面,使用less与vuex进行完善。
笔记导航
- Vue套件使用
- Vuex状态管理学习(
state
&getters
&mutations
&actions
) - 通过Vuex实现多个组件间的通信
- 使用less编辑页面样式
详细笔记
1. Vue组件嵌套使用
新建父组件,在script段落内引入子组件,并且在父组件中插入已定义的子组件
<!-- 组件嵌套实例 -->
<!-- 父组件Entry.Vue -->
<!-- 子组件Login.Vue & Register.Vue -->
<template>
<div class="entry-box">
<h1>entry</h1>
<login></login>
<register></register>
</div>
</template>
<script>
// 声明引入子组件变量
import login from './Login'
import register from './Register'
export default {
name: 'entry',
// ******* 在components参数中赋值,添加引入的子组件 *******
components: {
login,
register
}
// ******* 在components参数中赋值,添加引入的子组件 *******
}
</script>
Vue语法缩写:
@ === v-on 事件绑定监听
<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>
: === v-bind 元素绑定
<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>
2. 使用Vuex
Vuex的用处:
通过内部变量(store实例中的state)来管理整个系统的状态,提供多种系统的接口(getters、mutations、actions),使Vue组件可以通过store实例的接口获取store实例中的state变量。
Vuex的好处:
使用state: 实现Vue组件中的通信
使用getters与mutations: 不需要在每一个Vue组件中重写相同的处理函数(比如用于获取state中的参数)
使用actions: 把Vue之间的同步调用转变成异步调用,提高响应效率
Vuex学习资源:
Vuex入门视频(共5个)
Vuex视频对应的练习源码
2.1 关于Store & State
Step1: 安装Vuex
npm install --save vuex
Step2: 声明store实例,并引用到application中
// 新建store.js文件
// store.js 配置Vuex实例
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(vuex)
export const store = new Vuex.Store({
state: {
//填充用于管理状态的共享变量
}
})
//在main.js中引入Store实例
// main.js
// 使用{}引入store实例,因为store是一个const变量
import { store } from './store'
new Vue({
router,
store,
// 其他属性 ...
}).$mount('#app')
Step3: 传递Store实例中的state参数中的共享变量
在使用store之前,需要在App.vue中将公共状态传递给子组件(通过props获取)。
相当于子组件中传入的数据只是数据,而非数据地址,不可修改数据本身,因此自身操作不会影响全局,需要抛出事件给父组件。
而使用js操作变量时,子组件需要通过emit-on机制向App.vue发起事件冒泡,由app.vue执行操作并改变内部的data,进而更新子组件中的参数变化。
// App.vue
<template>
<div id="app">
// 通过绑定:users="unregisteredUsers" 向app-registration模块中传递props值
<app-registration @userRegistered="userRegistered" :users="unregisteredUsers"></app-registration>
<app-registrations @userUnregistered="userUnregistered" :registrations="registrations"></app-registrations>
</div>
</template>
<script>
import Registration from './components/Registration.vue';
import Registrations from './components/Registrations.vue';
export default {
data() { // data中存储被传到两个组件中的变量
return {
registrations: [],
users: [
{id: 1, name: 'Max', registered: false},
{id: 2, name: 'Anna', registered: false},
{id: 3, name: 'Chris', registered: false},
{id: 4, name: 'Sven', registered: false}
]
}
}
computed: {
unregisteredUsers() {
return this.users.filter((user) => {
return !user.registered;
});
}
},
// 声明引用的子组件
components: {
appRegistration: Registration,
appRegistrations: Registrations
}
}
</script>
在使用store之后,不需要再在子组件中通过props获取传入参数,可以直接通过this.$store.state.[变量名称]直接对store实例中的state参数进行有效编辑。
原本在app.vue中进行的操作,可以在子组件中直接执行。
// Registration.vue
<template>
<div id="registration">
<h3>Register here</h3>
<hr>
<div class="row" v-for="user in users">
<h4>{{ user.name }}</h4>
<button @click="registerUser(user)">Register</button>
</div>
</div>
</template>
<script>
export default {
computed: {
// 通过user() 获取store中的公共共享参数
users() {
return this.$store.state.users.filter(user => {
return !user.registered; // filter:条件过滤,只返回user.registered===false的元素
})
}
},
methods: {
registerUser(user) {
user.registered = true;
const date = new Date;
this.$store.state.registrations.push({userId: user.id, name: user.name, date: date.getMonth() + '/' + date.getDay()})
}
}
}
</script>
2.2 关于getters与mutations
概念:这一对Vuex实例属性,在一般情况下被理解作getter与setter
或accessor和mutator
getters [new store property]
getters用于简化子组件从store.js中获取公共变量的代码
使用getters优化前:
// store.js
export const store = new Vuex.Store({
state: {
registrations: [],
users: [
{id: 1, name: 'Max', registered: false},
{id: 2, name: 'Anna', registered: false},
{id: 3, name: 'Chris', registered: false},
{id: 4, name: 'Sven', registered: false}
]
}
})
// 子组件 Registration.vue
export default {
computed: {
users() {
return this.$store.state.users.filter(user => {
return !user.registered;
})
}
}
}
使用getters优化后:
// store.js
export const store = new Vuex.Store({
state: {
registrations: [],
users: [
{id: 1, name: 'Max', registered: false},
{id: 2, name: 'Anna', registered: false},
{id: 3, name: 'Chris', registered: false},
{id: 4, name: 'Sven', registered: false}
]
},
getters: {
unregisteredUsers(state) {
return state.users.filter(user => {
return !user.registered;
})
},
registerUser(state) {
return state.registrations;
}
totalRegistration(state) {
return state.registrations.length;
}
}
})
// 子组件 Registration.vue
export default {
computed: {
users() {
// 以属性的形式调用getters中的方法,不需要传入参数
return this.$store.getters.unregisteredUsers;
}
}
}
使用mapGetters的二度简化
安装mapGetters的编译工具
npm install --save-dev babel-preset-stage-2
配置mapGetters的编译参数
// .babelrc 文件
{
"presets": [
["es2015", { "modules": false }],
["stage-2"] //新增参数
]
}
// 子组件 Registrations.vue
<script>
// 引入mapGetters
import { mapGetters } from 'vuex'
export default {
computed: mapGetters({
// getters映射的语法规则:
// 'customized name' : 'name of getters'
registrations: 'registeredUser',
total: 'totalRegistrations'
}),
methods: {
unregister(registration) {
const user = this.$store.state.users.find(user => {
return user.id == registration.userId;
});
user.registered = false;
this.$store.state.registrations.splice(this.$store.state.registrations.indexOf(registration), 1);
}
}
}
</script>
mutations [new store property]
getters用于管理可复用的'获取state参数的操作
';相应地,mutations用于管理可复用的'修改state参数的js操作
'。
// mutations 的定义与声明
// store.js
export const store = new Vuex.Store({
state: { ··· },
getters: { ··· },
mutations: {
register(state, userId) {
const user = state.users.find(user => {
return user.id == userId;
});
user.registered = true;
const date = new Date;
const registration = {
userId: userId,
name: user.name,
date: date.getMonth() + '/' + date.getDay()
}
state.registrations.push(registration);
},
unregister(state, userId) {
const user = state.users.find(user => {
return user.id == userId;
});
user.registered = false;
// 使用findIndex定位目标删除元素
const registrationIndex = state.registrations.findIndex(registration => {
return registration.userId == userId;
})
state.registrations.splice(registrationIndex, 1);
// 使用find函数定位目标删除元素
// const registration = state.registrations.find(registration => {
// return registration.userId == userId;
// })
// state.registrations.splice(state.registrations.indexOf(registration), 1);
}
}
})
// mutations在组件中的调用
// Registration.vue
<script>
export default {
computed: {
users() {
return this.$store.getters.unregisteredUsers;
}
},
methods: {
registerUser(user) {
// 与getters不同,在子组件中使用commit调用mutations中的函数
this.$store.commit('register', user.id);
}
}
}
</script>
2.3 关于actions
在nodejs中,我们认识到最深刻的一点就是异步调用。但是在Vuex中,mutations属性中包含了大量函数接口,并且具有同步执行的特点。因此在必须在执行完mutations中某个被调用的函数之后,才能继续调用下一个,效率大大降低。
这个问题,就是actions属性所要解决的问题 ==> 异步调用(Async)
// store.js
// 声明actions, 可以在其中的函数中加入异步代码
export const store = new Vuex.Store({
state: { ··· },
getters: { ··· },
mutations: {
register(state, userId) {
······
},
unregister(state, userId) {
······
}
},
actions: {
// actions中的函数名可自定义,此处为了方便练习与mutations中函数同名
// 写法一:
register(context, userId) {
context.commit('register', userId);
},
// 写法二:
unregister( { commit } , userId) {
commit('unregister', userId);
}
}
})
// 在子组件中使用dispatch,调用可异步执行的actions
// Registrations.vue
<script>
import { mapGetters } from 'vuex'
export default {
computed: mapGetters({
registrations: 'registeredUser',
total: 'totalRegistrations'
}),
methods: {
unregister(registration) {
// 调用不可异步的mutations的写法,传入的第一个参数是actions中的函数名
// this.$store.commit('unregister', registration.userId);
// 调用可异步的actions的写法
this.$store.dispatch('unregister', registration.userId);
}
}
}
</script>
2.3 关于store.js的合理分装
store.js中包含了getters、mutations以及actions,随着application的功能扩展,函数将会越来越多,因此把这三个属性分装出去,是十分有必要的。
在src目录下新增store文件夹,更新后当前目录如下:
.
├── build/
├── config/
├── node_modules
├── src/
│ ├── main.js
│ ├── App.vue
│ ├── store/
│ │ ├── store.js
│ │ ├── getters.js
│ │ ├── mutations.js
│ │ └── actions.js
│ ├── components/
│ │ └── ...
│ └── assets/
│ └── ...
├── .babelrc
├── .postcssrc.js
├── .eslintrc.js
├── .editorconfig
├── index.html
└── package.json
分装方法如下:
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import mutations from './mutations'
import actions from './actions'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
registrations: [],
users: [
{id: 1, name: 'Max', registered: false},
{id: 2, name: 'Anna', registered: false},
{id: 3, name: 'Chris', registered: false},
{id: 4, name: 'Sven', registered: false}
]
},
getters,
mutations,
actions
})
// getters.js
export default {
unregisteredUsers(state) {
return state.users.filter(user => {
return !user.registered;
})
},
registeredUser(state) {
return state.registrations;
},
totalRegistrations(state) {
return state.registrations.length;
}
}
// mutations.js 与 actions.js同getters的做法
3.使用less修改Vue界面
首先:安装less依赖:
npm install less less-loader --save
然后:修改webpack.base.conf.js文件,配置loader加载依赖,让其支持外部的less。
在modules.rules属性中添加一个新的对象
{
test: /\.less$/,
loader: "style-loader!css-loader!less-loader",
}
最后:在Vue组件中使用的时候在style标签里加上lang="less"
less语法
4. Vue组件布局与路由
1. App.vue是整个application的主容器,根据不同的路由放入不同的组件。
即使当前Vue中没有html标签,也同样可以设置html、body标签的属性(width:100%
)。
2. css样式笔记
提高样式优先级
在样式的后面添加"!important"
width: 40% !important;
input标签与button标签在同一行内顶部对齐: vertical-align: baseline;