ui框架以及vuex
一、.ui框架
- element-ui:pc端【后台管理系统】推荐使用 饿了么开发的
- 布局(栅格系统,容器)
- 表格
- 表单
- js反馈
- js模块(轮播图,手风琴,选项卡)
- iview :pc端【后台管理系统】
- mint-ui:移动端【几乎不用】 饿了么开发
- vant:移动端 推荐
1、1 element-ui(pc端)
- Element,一套为开发者、设计师和产品经理准备的基于Vue2.0的桌面端组件库
- 官网:https://element.eleme.cn/#/zh-CN
- 下载安装(最新版本时间库上有bug)
npm i element-ui@2.15
- 引入使用
//全局引入elementui框架 import ElementUI from 'element-ui; //全局引入elementui样式 import 'element-ui/lib/theme-chalk/index.css'; //注册elementuiVue.use(ElementUI); //在官网找到对应的代码
1、2 vant-ui(移动端)
- 轻量、可靠的移动端Vue组件库
- 官网:https://youzan.github.io/vant/v2/#/zh-CN/
- 下载注意版本
npm i vant@latest-v2
- 引入使用
在main.js中全局引入vant样式import Vue from 'vue' import App from './App.vue' import router from './router' Vue.config.productionTip = false //全局引入vant组件库 import Vant from 'vant'; //全局引入vant样式 import 'vant/lib/index.css'; //注册vantui Vue.use(Vant); new Vue({ router, render: h => h(App) }).$mount('#app')
1、3 css 预处理器
- css预处理器:less,sass,stylus
- 在脚手架创建项目的时候,选中css预处理器
- 使用:在组件中使用 less.vue
<template> <div> <h1>less的使用</h1> <div class="box"> <p>我是一段文字</p> </div> </div> </template> <script> export default { } </script> <!-- <style lang="less"> 设置lang,就可以使用less语法 --> <style lang="less"> @import "../assets/less/index.less"; .box{ width: 300px; height: 300px; background: @bg1; p{ width: 100px; height: 100px; background: pink; color:@bg1; } &:hover{ background: orange; } } </style>
- 可以提取总共的样式值(主题背景颜色,主题字体颜色,字号)
- 目录
其中index.css文件是用来管理color.less以及size.less文件的
- color.less文件
@bg1:red; @bg2:yellow; @bg3:teal
- size.less文件
@size20:20px; @size30:30px; @size40:40p
- index.css
@import "./color.less"; @import "./size.less;
- 组件中使用
- 目录
如果一开始创建项目的时候没有选择less,可以后期安装
npm i less@4.0.0 less-loader 8.0.0 -D
"less": "^4.0.0",
"less-loader": "^8.0.0
- 安装好就和上面一样使用就可以了
二、vuex
- 组件的通讯方式
- 父传子:父组件自定义属性,子组件props接收
- 子传父:父组件自定义事件,子组件$emit触发
- 非父子:EventBus中央事件管理、本地存储、vuex
2、1 vuex介绍
- 概念:
共享数据 Vuex是一个专门为Vue.js应用程序开发的状态管理模式 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化 注意点:vuex不是永久性存储,当刷新的时候会回到初始状态。 所以它在应用同步操作的时候(没有调用接口),要结合离线存储去实现
vuex和离线存储的区别?
1、相同点:都可以去实现数据共享
2、不同点:
vuex不是永久性存储,一刷新就会回到初始状态,但是它是响应式的数据变化
离线存储它是永久性存储,但是它没有响应式的数据变化,当数据发生变化,它不刷新,页面是不会改变的
3、使用场景:一般都会结合使用,比如登录、购物车等
- 五大核心
state 状态(唯一的数据源) getters 缓存数据 mutations 变化 (需要commit提交)(修改数据的唯一方法) acctions 行动(需要dispatch派遣)(发起异步请求,交给mutations修改数据) modules 模块
- 数据操作过程
过程 - 总结:如果想要异步改变仓库中的数据,必须dispatch触发一个actions(actions中是异步),actions请求完数据后,提交一个commit去触发mutations(mutations中是同步),mutations去修改state,当state一发生变化,依赖它的getters也发生变化,视图就改变。
2、2 vuex的基础使用
-
使用场景:只有复杂的中大型项目才会考虑使用vuex去管理一些共享数据,
一般的普通项目不需要调用vuex1)手动下载使用
- 下载
npm i vuex@3 + vuex@3.6
注意这里使用的是3的版本,对应的是vue2
vue2–>vuex3
vue3–>vuex4- 在src下创建store文件夹,创建index.js文件
//1.下载 npm i vuex@3 //2.引入 // 引入vue核心库 import Vue from 'vue' // 引入vuex核心插件 import Vuex from 'vuex' // 调用vuex插件 Vue.use(Vuex) // console.log(Vuex); //是一个对象,里面包含一个Store的构造函数,用来创建实例 //3.实例化vuex仓库,并导出 export default new Vuex.Store({ //唯一的数据源 state:{ name:"李云龙", age:20 }, //充当视图层和状态层的中间层,具有缓存作用 getters:{}, //修改数据的唯一方式 mutations:{}, //异步操作--请求数据 actions:{}, modules:{} }) //4.在main.js中引入,挂载到vue实例上
- 把封装好的仓库模块,注入到vue实例上(main.js中)
至此,在所有的组件实例上,就可以见到一个$store对象,它下面就有state、getters等属性import Vue from 'vue' import App from './App.vue' import router from './router' import store from "./store" Vue.config.productionTip = false new Vue({ router, store, render: h => h(App) }).$mount('#app')
- 创建好one.vue组件,并配置好路由及路由出口
- 在组件中使用数据
<template> <div class="box"> <h1>this is one组件</h1> <p>姓名:{{$store.state.name}}</p> <p>年龄:{{$store.state.age}}</p> </div> </template> <script> export default { mounted(){ console.log(this.$store.state); } } </script> <style> </style>
2)脚手架使用vuex
- 安装时选择vuex
- 项目初始化
1、初始化vue.app 2、初始化路由配置 router/index.js 3、删除文件components、assets、views后期需要再加
- 创建组件one.vue,设置路由以及出口
- 在store->index.js的state下面创建数据
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ //唯一的数据源 state:{ name:"李云龙", age:20 }, getters:{}, mutations:{}, actions:{}, modules:{} })
- 组件中使用数据,同上面一样
<p>姓名:{{$store.state.name}}</p>
- 下载
三、vuex核心模块
3、1 state
- state(状态):唯一的数据源,用于管理共享数据
- 概念:
官方: Vuex使用单一状态树,用一个对象就包含了全部的应用层级状态。 至此它便作为一个“唯一数据源(SSOT(opens new window))”而存在 vuex本身也是响应式的数据变化
- 用法
{ state:{ //定义的数据源 } }
- 组件中取值
$store.state.属性名
3、2 getters
- 概念:
它类似于计算属性,具有缓存的效果 一般我们会将它作为state和视图的中间层所存在 当然大量的计算逻辑也可以放在getters中
- 用法
{ getters:{ 名称(state){ return state中的内容 } } }
- 例:如下这个name属性,它在state中的层级比较深,直接获取要写好几层,而我们通过getters处理之后,层级简单很多。
当然,它更重要的是对state中的数据做逻辑处理返回,整体而言,它的使用比较简单
3、3 mutations
- mutations(变化):是修改state的唯一方式,且它只能操作同步方法
- 概念:
官方概念: 更改Vuex的store中的状态的唯一方法是提交mutation
- 用法
// 组件视图中的逻辑:commit去触发mutations中的方法。commit中的type类型传入到mutations中做为函数名 { methods:{ 自定义方法名(){ this.$store.commit('type类型',参数); } } } //仓库代码:type类型就是逻辑中的type类型。参数state就是数据仓库,参数payload即commit传过来的参数 { mutations:{ type类型(state,payload){ //state即唯一的数据源 //payload代表参数 } } }
- 例
3、4 actions
- actions(行动):它和mutations一样都是方法,但是有以下不同
- 它不能直接修改state(只有mutations可以修改state,但它可以提交commit让mutations修改state)
- 它可以实现同步或异步操作
- 概念
官方概念: action类似于mutation,不同于: action提交的是mutation,而不是直接变更状态 action可以包含任意异步操作
- 用法
// 组件视图中的逻辑:actions要用dispatch触发,它里面的type类型传到actions当函数名使用。actions中触发mutations //总之:先到actions,再到mutations { methods:{ 自定义方法名(){ this.$store.dispatch('type类型',参数) } } } //仓库代码:actions中处理后,再调用 { mutations:{ mutations中的type类型(type,payload){ //修改state //payload代表的参数 } }, actions:{ type类型(context,payload){ // 此context是整个仓库对象,包含所有方法 // 异步操作得到结果后,再调用commit context.commit(mutation中的type类型, payload) } //或者利用解构 type类型({commit},payload){ // 异步操作得到结果后,再调用commit commit(mutation中的type类型, payload) } } }
- 例
- 总结:mutations只能操作同步,而actions可以异步操作。但是actions它又不能直接操作state中的数据,必须要借助mutations。
actions中的方法,必须要用dispatch调用,然后在actions中用commit方法调用mutations中的方法。
关于仓库中命名建议
如仓库中有一个列表叫list,则以此类推定义出getters、mutations、actions中的名字
state:list
getters:get_list
mutations:mutation_list
actions:action_list
3、5 modules
1、概念
因为state是唯一的数据源,所以所有的数据都在这一个大的对象树上。导致state臃肿
modules的出现就是为了解决state臃肿的问题。
它可以把仓库切割成为一个个的小模块,每一个模块中都含有state,getters,mutations,actions,
甚至当项目过于复杂的时候我们还可以再嵌套子模块
命名空间:namespaced
模块中的state会被挂载到主的state对象树中并携带模块名称防止冲突
但是其他的核心属性,比如getters、mutations、actions,都是直接未区分挂载到当前对象树中,为了
防止冲突,我们通过设置命名空间为其携带模块名称
使用了命名空间之后,我们再用映射去取时,就不能用数组了,要用对象重命名来取值
2、操作方法
将stroe->index.js中的内容拆分到一个个模块中,每个模块都有自己的state、getters、
mutations、actions
它还有 namespaced: true 启用命名空间
// store->modules->list->index.js里的内容
export default {
state: {
name: 'list'
},
getters: {
getTitle(state) {
return state.name;
}
},
mutations: {},
actions: {},
namespaced: true // 命名空间
}
3、6 banner实战
- 实战:vuex调用小U的banner接口
- 需求:本来这个banner只在首页用了。但是如果有这样的需求,banner在其他的页面也需要使用,这样就要多次发起ajax请求,就会浪费性能。我们把它放在vuex中,只在仓库中调用一次,其他的页面就都可以使用。
- 1.起后端服务
- 2.前端安装axios npm i axios@0
- 3.解决跨域 —在package.json同级的目录下创建vue.config.js,全局配置解决跨域(注意,一定要重启 前端服务)
module.exports = {
devServer: {
proxy: 'http://localhost:3000'
}
}
-
- 封装request请求模块
- request–>index.js
import axios from 'axios'; let http = axios.create({ baseURL: '/api' }); // 请求拦载 http.interceptors.request.use(req => { return req; }) // 响应拦载 http.interceptors.response.use(res => { return res.data; // 这里做了数据过滤 }) export default http;
- request–>api.js
import http from './index'; // 获取banner export function getBanner() { return http.get('/getbanner'); }
-
- 封装banner.vue组件
-
- 定义banner路由
-
- 在响应拦载哪里,做数据过滤,把一些不需要的去掉,只返回需要的数据
-
- 在banner.vue中,使用返回过来的数据并渲染页面
-
- banner.vue一加载,就只需要数据,因此,把数据放入到仓库中,并通过getters传给组件
-
- 页面一加载,就要调取接口,但是调取接口是一个异步,将调取接口放到actions中。在
banner.vue的mounted的钩子函数中调用
store->index.js
banner.vueimport Vue from "vue"; import Vuex from "vuex"; import { getbanner } from "../request/api"; Vue.use(Vuex); export default new Vuex.Store({ state: { list: [], // banner数据 }, getters: { // 数据返给前端 get_list(state) { return state.list; }, }, mutations: { mutation_list(state, payload) { state.list = payload; }, }, actions: { action_list({ commit }) { // 发起ajax请求(异步) getbanner() .then((res) => { if (res.code == 200) { // 成功了,commit发起一个mutations commit("mutation_list", res.list); } }) .catch((err) => { console.log(err); }); }, }, modules: {}, });
先看这里辅助性函数,再看上面的模块<template> <div> <h1>banner组件</h1> <ul> <li v-for="item in $store.getters.get_list" :key="item.id"> <img class="img" :src="item.img" alt="" /> </li> </ul> </div> </template> <script> export default { mounted() { // 加载完成之后,触发actions中的方法 if (!this.$store.state.list.length) { this.$store.dispatch("action_list"); } }, }; </script> <style scoped> .img { width: 300px; } </style>
- 辅助性函数的目的
辅助性函数最大的作用就是映射 如果直接在视图中取vuex中的数据或者调用方法,需要 $store.state.name 等这样的写法 而用辅助性函数,可以直接将值映身到视图的计算属性或方法中,直接调用,就省了$store.state
- 页面一加载,就要调取接口,但是调取接口是一个异步,将调取接口放到actions中。在
四、辅助性函数
4、1 mapState()
state它是vuex的唯一数据源
mapState(很少用,因为一般都把数据放在getters中,用mapGetters)
因为都是属性,所以拿回来之后,都放在computed计算属性中
<template>
当取回来了state中的数据之后,就可以直接在这里渲染了,而不用像之前要$store.state.值
{{映射仓库中state值}}
</template>
// 视图逻辑中
import { mapState } from 'vuex'; // mapState它是一个函数,调用时传入数组或对象,数组或
对象都是state中的属性名
// 计算属性中(因为它也是属性)
computed:{
// 传入数组
...mapState(['仓库中state值1', '仓库中state值2'])
// 传入对象,用于给state中的属性设置别名(改名),在这个视图中就用这个别名渲染数据
...mapState({
想要渲染的名字: 仓库中state值1,
想要渲染的名字: 仓库中state值2,
})
}
4、2 mapGetters()
getters相当于计算属性。它作为state和视图的中间层存在。它具有缓存效果
因为都是属性,所以拿回来之后,都放在computed计算属性中
import {mapGetters} from 'vuex'
computed:{
...mapGetters(['仓库中getters值1', '仓库中getters值2', ...])
...mapGetters({
想要渲染的名字:仓库中getter的值1,
想要渲染的名字:仓库中getter的值2,
})
}
<template>
<div>
<h1>one</h1>
<h2>直接使用state中的数据:{{ $store.state.name }}</h2>
<h2>使用getters转换的数据:{{ $store.getters.getName }}</h2>
<hr />
<h1>使用辅助函数</h1>
<h2>使用辅助函数取得state中的数据:{{ name }}</h2>
<h2>使用辅助函数取得getters中的数据:{{ getName }}, 今年{{ getAge }}岁</h2>
</div>
</template>
<script>
// mapState, mapGetters因为是数据属性,所以放在computed中
// mapMutations, mapActions因为是方法,所以放在methods中
import { mapState, mapGetters } from "vuex";
export default {
data() {
return {};
},
computed: {
...mapState(["name"]),
...mapGetters(["getName", "getAge"]),
}
};
</script>
<style scoped>
</style>
4、3 mapMutations()
它是修改state的唯一方式,同步的
import {mapMutations} from 'vuex'
// 因为它们都是方法,因此要放在方法里面
methods:{
...mapMutations(['mutations名字一致']),
...mapMutations({
'自定义函数名称':'mutations名字'
})
}
它的好处是,映射过来之后,定义在mutations中的方法名,可以在视图中直接调用且传参,而不用commit提交
4、4 mapActions()
它不能直接修改state,它只能commit一个mutations。它可以操作异步方法
import {mapActions} from 'vuex'
methods:{
...mapActions(['actions名字一致']),
...mapActions({
'自定义函数名称':'actions名字'
})
}
它的好处是,映射过来之后,定义在actions中的方法名,可以在视图中直接调用并传参,而不用dispatch提交
案例
<template>
<div>
<h1>one</h1>
<hr />
<h3>使用辅助性函数--state</h3>
<p>{{ uname }}</p>
<p>{{ uage }}</p>
<hr />
<h3>使用辅助性函数--getters</h3>
<p>{{ get_name }}</p>
<p>{{ get_age }}</p>
<hr />
<h3>使用辅助性函数--mutations</h3>
<p>
<button @click="mutation_name('辉哥')">修改名字</button>
</p>
<hr />
<h3>使用辅助性函数--actions</h3>
<p>
<button @click="action_age(3)">异步修改年龄</button>
</p>
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
// console.log(mapState); // 函数,调用时传入数组或对象,数组或对象都是state中的属性名
// console.log(mapState(["name", "age"]));
export default {
// 计算属性
computed: {
// ...mapState(["name", "age"]), // 用仓库中的原名
// 改名
...mapState({
uname: "name",
uage: "age",
}),
...mapGetters(["get_name", "get_age"]),
},
// 方法,
methods: {
...mapMutations(["mutation_name"]),
...mapActions(["action_age"]),
},
};
</script>
总结:
不论是映射数据,还是映射方法,最终的目的是减少在视图层的代码,映射的数据在视图直接使
用,映射的方法在视图中直接调用且可以传参。
减少代码量,这是目的