vue最核心的功能
-
数据驱动和组件化
-
使用组件化开发可以提高开发效率 带来更好的维护性
状态管理
-
state,驱动应用的数据源;
-
view,以声明方式将 state 映射到视图;
-
actions,响应在 view 上的用户输入导致的状态变化。
组件传值的三种方法
父传子
<blog-post title="My journey with Vue"></blog-post>
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
子传父
在子组件中使用 $emit 发布一个自定义事件:
<button v-on:click="$emit('enlargeText', 0.1)">
Enlarge text
</button>
在使用这个组件的时候,使用 v-on 监听这个自定义事件
<blog-post v-on:enlargeText="hFontSize += $event"></blog-post>
非父子传值(event Bus)
我们可以使用一个非常简单的 Event Bus 来解决这个问题:
eventbus.js :
export default new Vue()
然后在需要通信的两端: 使用 $on 订阅:
// 没有参数
bus.$on('自定义事件名称', () => {
// 执行操作
})
// 有参数
bus.$on('自定义事件名称', data => {
// 执行操作
})
使用 $emit 发布:
// 没有自定义传参
bus.$emit('自定义事件名称');
// 有自定义传参
bus.$emit('自定义事件名称', 数据);
父组件直接访问子组件:通过ref获取子组件(了解即可 慎用会导致数据混乱)
ref 有两个作用:
- 如果你把它作用到普通 HTML 标签上,则获取到的是 DOM
- 如果你把它作用到组件标签上,则获取到的是组件实例
创建 base-input 组件
<template>
<input ref="input">
</template>
<script>
export default {
methods: {
// 用来从父级组件聚焦输入框
focus: function () {
this.$refs.input.focus()
}
}
}
</script>
在使用子组件的时候,添加 ref 属性:
<base-input ref="usernameInput"></base-input>
然后在父组件等渲染完毕后使用 $refs 访问:
mounted () {
this.$refs.usernameInput.focus()
}
refs 。
vuex回顾
什么是 Vuex
Vuex是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件 的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调 试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调 试功能。
- Vuex 是专门为 Vue.js 设计的状态管理库
- 它采用集中式的方式存储需要共享的数据
- 从使用角度,它就是一个 JavaScript 库
- 它的作用是进行状态管理,解决复杂组件通信,数据共享
vuex的核心概念
- store 一个容器(vuex)
- state 单一状态数(响应式)
- getter 相当于计算属性
- mutation 同步
- action 异步
- module 模块
vuex的使用
state
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import products from './modules/products'
import cart from './modules/cart'
Vue.use(Vuex)
export default new Vuex.Store({
//重点
state: {
count: 0,
msg: 'Hello Vuex'
},
//类似于vue的计算属性
getters: {},
mutations: {},
actions: {},
})
about.vue
<template>
<div id="app">
<h1>Vuex - Demo</h1>
<!-- 第一种写法 -->
<!-- count:{{ $store.state.count }} <br>
msg: {{ $store.state.msg }} -->
<!-- 第二种写法 -->
<!-- count:{{ count }} <br>
msg: {{ msg }} -->
<!-- 第三种写法 避免有重复 -->
count:{{ num }} <br>
msg: {{ message }}
</div>
</template>
<script>
// 使用计算属性简化模板代码
import { mapState} from 'vuex'
export default {
computed: {
// 第二种写法 (接收一个数组作为参数[数组里就是我们要映射的属性'count', 'msg'])
// count等于state.count
// ...mapState(['count', 'msg'])
// 第三种写法 避免重复 (进行重命名)
...mapState({ num: 'count', message: 'msg' }),
},
}
</script>
Getter(类似于vue的计算属性)
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import products from './modules/products'
import cart from './modules/cart'
Vue.use(Vuex)
export default new Vuex.Store({
//重点
state: {
count: 0,
msg: 'Hello Vuex'
},
//类似于vue的计算属性
getters: {
reverseMsg (state) {
return state.msg.split('').reverse().join('')
}
},
mutations: {
},
actions: {
},
})
about.vue
<template>
<div id="app">
<h1>Vuex - Demo</h1>
count:{{ num }} <br>
msg: {{ message }}
</div>
</template>
<script>
import { mapState, mapGetters, } from 'vuex'
export default {
computed: {
// 重点开始 使用计算属性
...mapGetters(['reverseMsg']),
//重点结束
...mapState({ num: 'count', message: 'msg' }),
},
}
</script>
store.js
Mutation(不要用Mutation执行异步操作 commit是属于同步)
export default new Vuex.Store({
state: {
count: 0,
msg: 'Hello Vuex'
},
// 重点
mutations: {
// 将state.count+3
increate (state, payload) {
state.count += payload
}
},
})
about.vue
<template>
<div id="app">
<h1>Vuex - Demo</h1>
count:{{ num }} <br>
msg: {{ message }}
<h2>Mutation</h2>
<button @click="increate(3)">Mutation</button>
</div>
</template>
<script>
import { mapState, mapMutations, } from 'vuex'
export default {
computed: {
...mapState({ num: 'count', message: 'msg' }),
},
methods: {
// 触发increate方法
...mapMutations(['increate']),
}
}
</script>
store.js
Action(dispatch属于异步)
export default new Vuex.Store({
strict: process.env.NODE_ENV !== 'production',
state: {
count: 0,
msg: 'Hello Vuex'
},
//重点 (异步)
actions: {
increateAsync (context, payload) {
setTimeout(() => {
context.commit('increate', payload)
}, 2000)
}
},
})
about.vue
<template>
<div id="app">
<h1>Vuex - Demo</h1>
count:{{ num }} <br>
msg: {{ message }}
<h2>Action</h2>
<!-- <button @click="$store.dispatch('increateAsync', 5)">Action</button> -->
<button @click="increateAsync(6)">Action</button>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
computed: {
...mapState({ num: 'count', message: 'msg' }),
},
methods: {
...mapActions(['increateAsync']),
}
}
</script>
Module 将单一状态数拆分为模块
store-》modules-》cart.js
const state = {}
const getters = {}
const mutations = {}
const actions = {}
export default {
namespaced: true, // 开启命名空间(具有复用性)
state,
getters,
mutations,
actions
}
store-》modules-》cart.js
const state = {
products: [
{ id: 1, title: 'iPhone 11', price: 8000 },
{ id: 2, title: 'iPhone 12', price: 10000 }
]
}
const getters = {}
const mutations = {
setProducts (state, payload) {
state.products = payload
}
}
const actions = {}
export default {
namespaced: true,// 开启命名空间
state,
getters,
mutations,
actions
}
store-index.js
import Vue from 'vue'
import Vuex from 'vuex'
import products from './modules/products'
import cart from './modules/cart'
Vue.use(Vuex)
export default new Vuex.Store({
// 重点
modules: {
products,
cart
}
})
about.vue
<template>
<div id="app">
<h1>Vuex - Demo</h1>
<h2>Module</h2>
products: {{ products }} <br>
<button @click="setProducts([])">Mutation</button>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
export default {
computed: {
// 开启命名空间
// 命名空间名称 (在store->modules的名称)
// products 拿到的是在store->modules->products.js->state.products
...mapState('products', ['products'])
},
methods: {
// products是文件
...mapMutations('products', ['setProducts'])
}
}
</script>
vuex的严格模式
store-》index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// 重点
// 严格模式 在开发环境使用 在生产环境 不要启用严格模式 否则会影响性能
strict: process.env.NODE_ENV !== 'production',
state: {
count: 0,
msg: 'Hello Vuex'
},
getters: { },
actions: {},
})
about.vue
<template>
<div id="app">
<h2>strict</h2>
<button @click="$store.state.msg = 'Lagou'">strict</button>
</div>
</template>
购物车案例拆解(vuex)
模块1 效果图
实现效果(获取红框框数据)
在store>创建modules>products.js
import axios from 'axios'
// eslint-disable-next-line no-unused-vars
const state = {
products: [] // 记录所有商品
}
const sgetters = {}
const mutations = {
setProducts (state, payload) {
console.log(payload)
// payload 拿到当前id
state.products = payload
}
}
const actions = {
// 异步获取数据
async getProducts ({ commit }) {
// eslint-disable-next-line no-unused-vars
const { data } = await axios({
method: 'GET',
url: 'http://127.0.0.1:3000/products'
})
commit('setProducts', data)
}
}
export default {
namespaced: true, // 开启命名空间(具有复用性)
state,
sgetters,
mutations,
actions
}
在store>index.js引入
import Vue from 'vue'
import Vuex from 'vuex'
import products from './modules/products'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
products
},
})
模板
<template>
<div>
<el-breadcrumb separator="/">
<el-breadcrumb-item><a href="#/">首页</a></el-breadcrumb-item>
<el-breadcrumb-item><a href="#/">商品列表</a></el-breadcrumb-item>
</el-breadcrumb>
<!-- products 重点 -->
<el-table
:data="products"
style="width: 100%">
<el-table-column
prop="title"
label="商品">
</el-table-column>
<el-table-column
prop="price"
label="价格">
</el-table-column>
<el-table-column
prop="address"
label="操作">
<template>
<el-button>加入购物车</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
name: 'ProductList',
computed: {
// 拿到数据(红框框的数据)
...mapState('products', ['products'])
},
methods: {
// 异步请求 渲染到页面上
...mapActions('products', ['getProducts']) // (理解为加载不是点击)
},
created () {
// 调用getProducts方法
this.getProducts()
}
}
</script>
模块二
点击加入购物车
在store>创建modules>cart.js
// eslint-disable-next-line no-unused-vars
const state = {
cartProducts: [] // 记录购物车数据
}
const getters = {
}
// mutations所有方法都是更改状态的
const mutations = {
addToCart (state, product) {
// cartProducts中没有该商品 把商品添加到数组中,并增加count,isChecked,totalPrice
// cartProducts 有商品,让商品数量加1,选中,计算小计
const prod = state.cartProducts.find(item => item.id === product.id)
// 如果数组有该商品
if (prod) {
prod.count++
prod.isChecked = true
// eslint-disable-next-line no-undef
prod.totalPrice = prod + count * prod.price
} else {
state.cartProducts.push({
...product,
count: 1,
isChecked: true,
totalPrice: product.price
})
}
}
}
const actions = {}
export default {
namespaced: true,
state,
getters,
mutations,
actions
}
模板
<template>
<div>
<el-breadcrumb separator="/">
<el-breadcrumb-item><a href="#/">首页</a></el-breadcrumb-item>
<el-breadcrumb-item><a href="#/">商品列表</a></el-breadcrumb-item>
</el-breadcrumb>
<el-table
:data="products"
style="width: 100%">
<el-table-column
prop="title"
label="商品">
</el-table-column>
<el-table-column
prop="price"
label="价格">
</el-table-column>
<el-table-column
prop="address"
label="操作">
<!-- 重点 -->
<template v-slot='scope'>
<el-button @click="addToCart(scope.row)">加入购物车</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { mapActions, mapState, mapMutations } from 'vuex'
export default {
name: 'ProductList',
computed: {
...mapState('products', ['products'])
},
methods: {
...mapActions('products', ['getProducts']), // (理解为加载不是点击)
// cart(指的是文件名称)
// addToCart 指的是mutations下面的addToCart方法
...mapMutations('cart', ['addToCart']) // 加入购物车(理解点击)
},
created () {
// 调用getProducts方法
this.getProducts()
}
}
</script>
模拟veux
新建myvuex->index.js
let _Vue = null
class Store {
constructor (options) {
const {
// eslint-disable-next-line no-unused-vars
state = {},
// eslint-disable-next-line no-unused-vars
getters = {},
// eslint-disable-next-line no-unused-vars
mutations = {},
// eslint-disable-next-line no-unused-vars
actions = {}
} = options
this.state = _Vue.Observable(state)
this.getters = Object.create(null)
Object.keys(getters).forEach(key => {
Object.defineProperty(this.getters, key, {
get: () => getters[key](state)
})
})
this._mutations = mutations
this._actions = actions
}
conmit (type, payload) {
this._mutations[type](this.statem, payload)
}
dispatch (type, payload) {
this._actions[type](this, payload)
}
}
function install (Vue) {
_Vue = Vue
_Vue.mixin({
beforeCreate () {
// this 指的是vue的实例
if (this.$options.store) {
// 在vue原型上挂载store
_Vue.prototype.$store = this.$options.store
}
}
})
}
export default {
Store,
install
}
store>index.js
import Vue from 'vue'
// import Vuex from 'vuex'
import Vuex from '../myvuex/index' //重点
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
},
})