① 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",});},//同步方法添加carfun3() {this.$store.commit("addCar", {name: "保时捷911",price: 150,});},//异步方法添加carfun4() {//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;});