① Vuex
1.安装
npm install vuex --save
2. 引入Vuex并创建状态管理对象
1.state选项,定义状态(状态就是数据)
2.getters类似计算属性
3.mutations选项,定义操作状态的方法(注意:这里面只能定义同步方法)
它有两个参数,第一个参数是状态,第二个参数是新值。
4.actions选项也是定义操作状态的方法(这里的方法可以定义异步方法)
第一个参数是上下文对象(就是当前store对象),第二个参数是新值
注意:actions最好不要直接操作state,通过mutations操作state所以,actions直接操mutations。
为什么要这么设计,因为我们可能要跟value值发送请求,获取对应的值
5.modules选项用来导入模块
import Vue from 'vue'
import Vuex from 'vuex' //导入vuex插件
Vue.use(Vuex) //使用vuex插件
import phone from './phone' //导入手机模块
//创建状态管理对象
let store = new Vuex.Store({
state: {
planeName: '波音747',
planePrice: '10亿',
car: {
carName: '奔驰',
carPrice: '100w'
},
//汽车数组
cars: [
{
name: "奔驰",
price: 50,
},
{
name: "宝马",
price: 40,
},
{
name: "奥迪",
price: 30,
},
],
},
getters: {
//汽车的总价
totalCarPrice(state) {
return state.cars.reduce((p, c) => p + c.price, 0)
}
},
mutations: {
//修改飞机的名称
updatePlaneName(state, value) {
state.planeName = value
},
//修改飞机的价格
updatePlanePrice(state, value) {
state.planePrice = value
},
//修改汽车的名称和价格
updateCar(state, value) {
state.car = value
},
//添加汽车
addCar(state, value) {
state.cars.push(value)
}
},
actions: {
// 这里我们使用定时器,默认异步过程。
updatePlaneName(store, value) {
setTimeout(() => {
store.commit("updatePlaneName", value);
}, 2000);
},
//添加汽车
addCar(store, value) {
store.commit("addCar", value);
},
},
//模块
modules: {
//手机模块
phone
},
})
export default store
3.手机模块
namespaced:true是设置私有命名空间,默认情况下该属性是false。非私有命名空间的模块,只有
state是私有的,getters、mutations、actions仍然提升到全局中,私有命名空间的模块,所有成员
都是私有的(局部的)
export default {
namespaced: true,
state: {
//手机数组
phones: [
{
name: "iphone13",
price: 6999,
},
{
name: "华为",
price: 5999,
},
{
name: "小米",
price: 4999,
},
],
},
getters: {
// 计算手机总价
totalPhonePrice(state) {
return state.phones.reduce((p, c) => p + c.price, 0);
},
},
mutations: {
addPhone(state, value) {
state.phones.push(value);
},
},
actions: {
addPhone(store, value) {
setTimeout(() => {
store.commit("addPhone", value);
}, 2000);
},
}
};
4.注册给Vue
import Vue from 'vue'
import App from './App.vue'
import store from './store/index01'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
//使用全局状态管理
store,
}).$mount('#app')
5.使用
<script>
export default {
name: "Home",
computed: {
//返回汽车数组
cars() {
return this.$store.state.cars;
},
//返回汽车总价
totalCarPrice() {
return this.$store.getters.totalCarPrice;
},
//返回手机数组信息
phones() {
// 注意:手机数组数据在phone模块中
// $store.state返回的是全局状态,根据全局状态获取指定模块,再获取该模块中具体的状态。
return this.$store.state.phone.phones;
},
//返回手机总价
totalPhonePrice() {
// 注意:总价在phone模块中,获取模块中的计算机属性的方式:
// 1.return this.$store.getters.计算属性名
// return this.$store.getters.totalPhonePrice;
//使用这种方法必须是非私有命名空间的模块即模块的namespaced: false,因为getters被提升到全局了
// 2.return this.$store.getters['模块名/计算属性名']
// 使用这种方法必须是私有命名空间的模块即模块的namespaced: true,
return this.$store.getters["phone/totalPhonePrice"];
},
},
methods: {
fun1() {
//commit()方法,用于执行指定的mutations里面的方法
this.$store.commit("updatePlaneName", "轰炸机777");
this.$store.commit("updatePlanePrice", "25亿");
},
fun2() {
this.$store.commit("updateCar", {
carName: "兰博基尼",
carPrice: "1000w",
});
},
//同步方法添加car
fun3() {
this.$store.commit("addCar", {
name: "保时捷911",
price: 150,
});
},
//异步方法添加car
fun4() {
//dispatch()方法,调用的是actions里面的方法
this.$store.dispatch("addCar", {
name: "法拉利",
price: 1000,
});
},
fun5() {
this.$store.commit("addPhone", {
name: "三星",
price: 9999,
});
},
fun6() {
this.$store.dispatch("addPhone", {
name: "vivo",
price: 5999,
});
},
},
};
</script>
② 映射函数
1. 导入映射函数
// 从vuex中,导入映射函数
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
2. 使用映射函数生成计算属性
如果vuex里面state的数据名称跟页面中的计算属性名称相同,就可以使用mapState映射函数,
自动生成页面中的计算属性。
注意:如果要映射模块里面的state,函数的第一个参数设置为模块的名称
如果vuex里面getters的数据名称跟页面中的计算属性名称相同,就可以使用mapGetters映射函
数,自动生成页面中的计算属性。
注意:如果要映射模块里面的getters,函数的第一个参数设置为模块的名称。
//使用映射函数生成计算属性
computed: {
...mapState(["firstName", "lastName"]),
...mapState("phone", ["phones"]),
...mapGetters(["totalCarPrice", "totalPhonePrice1", "fullName"]),
...mapGetters("phone", ["totalPhonePrice"]),
},
3. 使用映射函数生成方法
生成的方法名跟mutations里面的方法名相同。生成的方法会带有一个参数,通过参数传递数据。
注意:如果要映射模块里面的方法,第一个参数传递模块的名称。
//使用映射函数生成方法
methods: {
...mapMutations(["updateFirstName", "updateLastName"]),
...mapActions(["updateFirstName2", "updateLastName2"]),
...mapActions('phone',['addPhone'])
},
<template>
<div>
<p>列表页</p>
<p>
{
{ firstName }}--{
{ lastName }}===={
{ fullName }}--{
{ phones }}--{
{
totalPhonePrice
}}
<button @click="updateFirstName('李')">修改姓1</button>
<button @click="updateLastName('元霸')">修改名1</button>
<button @click="updateFirstName2('王')">修改姓2</button>
<button @click="updateLastName2('羲之')">修改名2</button>
<button @click="addPhone({ name: '三星', price: 4999 })">添加手机</button>
</p>
<p>汽车总价:{
{ totalCarPrice }}W--手机总价:¥{
{ totalPhonePrice1 }}</p>
</div>
</template>
③ 自定义插件
1. 自定义指令
// 全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
局部指令,组件中也有一个directives 的选项
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
钩子函数bind(指令第一次绑定到元素时调用)、inserted(被绑定元素插入父节点时调用 )、
update(所在组件的虚节点更新时调用)。钩子函数的参数:el 、binding等 。
el:指令所绑定的元素,可以用来直接操作 DOM
binding:一个对象,包含以下 属性:
1.name-指令名,不包括 v- 前缀
2.value-指令的绑定值 ,oldValue-指令绑定的前一个值
3.expression-字符串形式的指令表达式。例如 v-my-directive=”a+b” 中,表达式为 “a+b”。
4.arg-传给指令的参数,可选。例如 v-my-directive:foo ,参数为 “foo”。
2. 自定义插件
定义一个插件,插件就是将给Vue添加的全局成员,归类到一起去,这样做利于后期维护。
插件中必须包含一个install方法,方法的第一个参数是Vue,后面的参数是可选的
let myPlugin = {
install(Vue) {
//自定义指令
Vue.directive("myHtml", (el, binding) => {
el.innerHTML = binding.value
})
Vue.directive('red', (el) => {
el.style.color = 'red'
})
Vue.directive('color', (el, binding) => {
el.style.color = binding.value
})
//全局混入,之后所有的Vue实例,都将拥有里面定义的成员
Vue.mixin({
data() {
return {
myName: '张三',
myAge: 25
}
},
methods: {
sayHi() {
alert('大家好哦')
}
},
})
//给Vue的原型对象添加成员,之后所有的Vue实例,都将共享这些成员
Vue.prototype.$bus = new Vue()
Vue.prototype.address = "北京市朝阳区1001号";
//全局过滤器
Vue.filter('toFixed2', (val) => {
return val.toFixed(2)
})
//注册全局组件
Vue.component('a-button', {
render: (h) => h('button', { style: { border: 'none', borderRadius: '10px', padding: '5px 10px', cursor: ' pointer' } }, '按钮')
})
}
}
export default myPlugin
3.$nextTick
watch: {
isShow(nval) {
//$nextTick()有一个回调函数,它里面的代码会在页面挂载完成后执行,一般在处理原生dom时会用到
this.$nextTick(() => {
if (nval) {
//显示时输入框得到焦点
document.querySelector("#input").focus()
}
})
}
},
4.过渡
<button @click="isShow = !isShow">显示/隐藏</button>
<!-- 将需要做动画或过渡效果的内容,用transition包起来 -->
<transition name="box1">
<div v-show="isShow" class="box">112</div>
</transition>
<!-- 给列表添加动画或过渡效果,用transition-group ,tag属性是设置显示标签-->
<transition-group tag="div" name="box2">
<ul v-for="(item, index) in list" :key="item.id">
<li>{
{ item.name }}</li>
<li>{
{ item.sex }}</li>
<li>{
{ item.age }}</li>
<li><button @click="del(index)">删除</button></li>
</ul>
</transition-group>
/* 进入时的样式和离开时的样式 */
.box1-enter-active,
.box1-leave-active {
transition: all 1s;
}
/* 进入前的样式和离开后的样式 */
.box1-enter,
.box1-leave-to {
opacity: 0;
transform: translateY(20px);
}
/* 进入时的样式和离开时的样式 */
.box2-enter-active,
.box2-leave-active {
transition: all 1s;
}
/* 进入前的样式和离开后的样式 */
.box2-enter,
.box2-leave-to {
opacity: 0;
transform: translateX(20px);
}
④ 代理服务器
ajax请求数据,必须要遵循同源策略,即请求地址的协议名,主机名(域名或ip地址),端口号,必
须跟当前地址相同,否则就是跨域请求。解决跨域有两种途径:
1.后端允许跨域,2.前端想办法骗过后端实现跨域(jsonp技术,代理服务器技术)
vue,config.js文件中配置代理服务器:
module.exports = {
//取消eslint语法检查
lintOnSave: false,
//devServer是脚手架中的开发服务器
devServer: {
//配置主机名
// host: 'localhost',
//配置端口号
// port: '8840',
//在开发服务器中,配置一个代理服务器地址。
//注意:在开发阶段,通过向当前开发服务器发送ajax请求,当前服务器会将请求转发给配置的代理服务器地址。
// proxy: 'http://localhost:8840',
// 配置多个代理服务器地址
proxy: {
// /api1是前缀
'/api1': {
target: 'http://localhost:8840',
//因为路径加了前缀,本身前缀是不需要的,所以要重写路径
pathRewrite: {
'^/api1': ''
}
}
}
}
}
后端服务器:
// 导入express
let express = require('express')
// 创建一个服务器对象
let app = express()
// 开启一个端口号
app.listen(8840, () => {
console.log("服务器已开启,端口号是8840");
})
//配置中间件,拦截所有的请求
app.use((req, res, next) => {
//允许跨域
// res.setHeader("Access-Control-Allow-Origin", "*");
console.log("服务器已发送请求");
next()
})
//学生数组
let stus = [
{
no: "1001",
name: "张三",
age: 20,
sex: "男",
},
{
no: "1002",
name: "李四",
age: 22,
sex: "女",
},
{
no: "1003",
name: "王五",
age: 24,
sex: "男",
},]
app.get('/list', (req, res) => {
res.send(stus)
})
组件中:
//配置多个代理服务器地址,这里要加上前缀
axios.get("http://localhost:8080/api1/list").then(({ data }) => {
this.students = data;
});