赞
踩
为了提升项目的可维护性和可扩展性,我们把src/components目录下的组件进行目录结构的优化,把所有可以通过路由地址访问带的页面组件放到pages文件夹下,把所有页面组成部分的组件(不通过路由地址访问的组件)放到views文件夹下。
以上所有的文件夹的名称没有强制要求交这个名字,可以自行设置
调整后的目录结构为
project项目名称
-src
-components
-pages 存放页面组件
... 其他目录可以根据需求自行设置
-views 存放页面组成部分的组件
一旦组件目录结构调整以后,在引用该组件的地方也要跟着把引入的路径地址进行改变
npm i stylus stylus-loader --save
是在页面组件中的style标签中添加一个lang属性(默认值为css),并把他的属性值明确设置为stylus
<style lang="stylus"></style>
<template> <div class="wrapper"> <div class="mask"> <div class="content"> <h1>登录页面</h1> <div class="item"> <input type="text" name="" id="" placeholder="请输入用户名"> </div> <div class="item"> <input type="password" name="" id="" placeholder="请输入密码"> </div> <div class="item"> <button>登录</button> </div> </div> </div> </div> </template> <script> export default { } </script> <style lang="stylus" scpoed> .mask width 100vw height 100vh background rgba(0,0,0,0.5) .content width 400px height 300px background #fff transform translateY(50%) margin 0 auto border-radius 20px text-align center h1 padding-top 10px .item padding 10px input height 30px width 300px line-height 30px font-size 20px button width 300px height 40px background skyblue border none color #fff font-size 20px </style>
如果有不同的页面要使用相同的样式代码,可以在stylus中封装一个函数,把需要重复使用的样式代码放到函数中,在页面组件中引入函数即可
(1)src/common/css/fn.styl
mask(){
width 100vw
height 100vh
background rgba(0,0,0,0.5)
}
(2)在页面组件中引入
<style lang="stylus" scpoed>
@import '../../common/css/fn.styl'
.mask
mask()
</style>
可以预先设置好一些初始的样式信息,包括颜色,尺寸、字体、表格、表单
/src/common/color.styl
$bgColor1 = #33ad3c
$bgColor2 = #2468a2
$bgColor3 = #1b315e
只要在页面组件中引入相关的.styl文件就可以使用预先设置好的变量信息
<style lang="stylus" scoped>
@import '../../common/css/color.styl'
.nav
width 100px
background $bgColor2
</style>
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
npm i vuex --save
state是vuex仓库中所有的状态,类似vue组件中的data。
import Vuex from 'vuex';
Vue.use(Vuex);
let store = new Vuex.Store({
state: {
num:100,
name:'vuex name'
}
})
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
在任意组件中读取仓库中的数据
{{$store.state.num}}
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于methods
mutations: {
addNum(state,txt){
state.num+=txt;
}
}
组件中
<button @click="$store.commit('addNum',5)"></button>
在页面组件中使用mutation中调用,不能执行异步操作
mutations必须是一个同步函数
Action 类似于 mutation,不同在于:
actions: {
addNumSync(context){
setTimeout({
context.commit('addNum')
},1000)
}
}
组件中
<button @click="$store.dispatch('addNumSync')"></button>
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
示例代码
let store = new Vuex.Store({
getters:{
showNum(state){
return `最新的数量是${state.num}`;
}
}
})
在组件中使用计算属性
<p>{{$store.getters.showNum}}</p>
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
namespaced:true 启用命名空间
①设置模块
export default new Vuex.Store({ .... modules:{ shop:{ namespaced:true,// 启用命名空间 state:{ num:1 }, mutations:{ addNum(state, step) { state.num += step; } } } } })
②组件中使用state和mutation
<template>
<div>
<h1>shop模块</h1>
<p>shop:{{$store.state.shop.num}}</p>
<!-- 根模块 -->
<button @click="$store.commit('addNum',5)">改变根数量</button>
<!-- shop模块 -->
<button @click="$store.commit('shop/addNum',5)">改变shop数量</button>
</div>
</template>
③使用助手函数
...mapGetters('命名空间名', ["getCartGoods"])
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗.
在页面组件中
<script>
import {mapState} from 'vuex'
export default {
computed: {
...mapState(['num'])
},
}
</script>
mapGetters
辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:
在页面组件中
<script>
import {mapState,mapGetters} from 'vuex'
export default {
computed: {
...mapState(['num']),
...mapGetters(['showNum'])
},
}
</script>
使用 mapActions
辅助函数将组件的 methods 映射为 store.dispatch
调用(需要先在根节点注入 store
在页面组件中
<button @click="addNumSync(3)">action+N</button>
<script>
import {mapState,mapGetters,mapActions} from 'vuex'
export default {
methods: {
...mapActions(['addNumSync'])
}
}
</script>
使用 mapMutations
辅助函数将组件中的 methods 映射为 store.commit
调用(需要在根节点注入 store
)
在页面组件中
<script>
import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'
export default {
methods: {
...mapActions(['addNumSync']),
...mapMutations(['addNum'])
}
}
</script>
把代码都写在main.js中使非常不明智且不合理的,因为仓库中的状态非常多时,对应的代码量也会变得非常多
(1)我们在src目录下床架一个store文件夹,zai store文件夹下再创建一个index.js
(2)然后就可以把之前写在main.js中关于vuex的代码都放到/src/store/index.js中,但是,所有的代码都放在index.js中也是不合适的,以为状态和改变状态的方法会有很多个,所以在此基础上继续进行目录结构的细分,把state、mutation、actions、getters中对应的代码分别拆分到对应的js文件中,在index.js中引入即可
优化之后的代码:
/src/main.js
import store from './store'
new Vue({
el: '#app',
router,
store:store,//一定要把仓库挂到vue实例上
components: { App },
template: '<App/>'
})
/src/store/index.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) //引入状态 import state from './state' //引入修改状态的方法 import mutations from './mutation' //引入异步操作mutation的方法 import actions from './action' //引入计算属性 import getters from './getter' //实例化vuex仓库 export default new Vuex.Store({ state:state,//key和val相同时,state:state可以简写成state mutations, actions, getters });
/src/store/state.js
export default{
num: 100
}
/src/store/mutation.js
export default{
addNumByOne(state){
state.num++
},
addNumByNum(state,num){
state.num+=num
}
}
/src/store/action.js
export default{
addNumByOneSync(context){
setTimeout(()=>{
context.commit('addNumByOne')
},1000)
},
addNumByNumSync(context,n){
context.commit('addNumByNum',n)
}
}
/src/store/getter.js
export default {
showNum(state){
//业务逻辑
return `最新的数量是:${state.num}`;
}
}
状态持久化
(1)使用本地存储结合vuex
(2)使用插件实现数据持久化
安装
npm i vuex-persistedstate --save
使用:
/src/store/index.js
import creatPersistedState from 'vuex-persistedstate'
export default new Vuex.Store({
mutations,
state,
getters,
actions,
plugins:[creatPersistedState()]
})
商品列表页->商品详情页->加入购物车
(1)商品列表页–展示商品
<template> <div> <h1>商品列表</h1> <div class="list"> <div class="item" v-for="good of goodsArr" :key="good.id" @click="toInfo(good.id)"> <div class="left"> <p>商品名称:{{good.name}}</p> <p>商品价格:{{good.price}}</p> </div> <div class="right"> <img :src="good.img" alt="good.name"> </div> </div> </div> </div> </template> <script> export default { methods: { toInfo(id){ this.$router.push('/goods/'+id); } }, data () { return { } } } </script>
(2)商品详情页–展示具体信息,加入购物车
<template> <div> <h1>商品详情</h1> <p>商品名称:{{info.name}}</p> <p>商品价格:{{info.price}}</p> <p>商品图片: <img :src="info.img" alt=""> </p> <button @click="addCart">加入购物车</button> </div> </template> <script> export default { mounted() { let id = this.$route.params.gid; this.info = this.goodsArr.find(item => (item.id = id)); }, methods: { //点击加入购物车按钮 addCart(){ //触发vuex中的action this.$store.dispatch('shop/addCartGoodsSync',this.info); this.$router.push('/cart') } }, data() { return { info: { name: "", img: "", price: "", id: "" }, }; } }; </script>
(3)初始化vue状态,并定义好改变状态的方法
/src/store/shop/index.js定义初始状态
state:{
num:1,
cartGoods:[]//定义购物车空数组
},
/src/store/shop/index.js定义直接改变状态的方法
mutations:{
addCartGoods(state,obj){
state.cartGoods.push(obj)//把指定的内容追加到初始状态中
}
},
/src/store/shop/index.js定义触发mutations的action–可以执行异步操作
actions: {
addCartGoodsSync(context,obj){
context.commit('addCartGoods',obj)
}
},
/src/store/shop/index.js定义计算属性,方便页面去获取
getters: {
getCartGoods(state){
return state.cartGoods;
}
}
(4)购物车页–通过计算属性来获取到已经加入到购物车中的商品信息
<script>
import {mapGetters} from 'vuex'
export default {
computed:{
...mapGetters('shop',['getCartGoods'])
}
}
</script>
npm i element-ui -S
①完整移入
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';//非常重要
Vue.use(ElementUI);
new Vue({
...
render: h => h(App)
});
②按需引入
需要哪个组件就引入哪个组件
import Vue from 'vue';
import { Button, Select } from 'element-ui';
import App from './App.vue';
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
参照官网使用
2.iview
3.mint-ui
在vue项目,受对象数据类型的影响,有时直接通过下标操作数组,数组内容变化了,但是页面并没有跟着进行重新渲染。
可以通过JSON序列化来实现
vuex的mutations
addCartNum(state,id){
let idx = state.cartGoods.findIndex((item)=>item.id == id);
let goodsArr = JSON.parse(JSON.stringify(state.cartGoods));
goodsArr[idx].num++;
state.cartGoods = goodsArr;
}
可以使用vue提供的$forceUpdate方法
vuex的actions
addCartNumSync(context,id){
context.commit('addCartNum',id)
}
vuex的mutations
addCartNum(state,id){
let idx = state.cartGoods.findIndex((item)=>item.id == id);
state.cartGoods[idx].num++;
}
页面组件代码
methods:{
...mapActions(['addCartNumSync']),
add(id){
this.addCartNumSync(id);
//调用完成vuex中的actions操作方法对数据进行改变后
//强制重新渲染页面,触发update生命周期钩子函数
this.$forceUpdate();
}
}
element-ui中提供了表单中常用的组件,比如输入框、选择框、单选框、开关等
<el-form label-width="80px" style="width:600px;" > <el-form-item label="菜单名称"> <el-input v-model="info.title"></el-input> </el-form-item> <el-form-item label="上级菜单"> <el-select v-model="info.pid" placeholder="请选择"> <!-- value 设置选中项的值 label 设置选中项的名称 --> <el-option value="">请选择</el-option> <el-option value="0" label="顶级菜单">顶级菜单</el-option> <el-option value="1" label="系统设置">系统设置</el-option> </el-select> </el-form-item> <el-form-item label="菜单图标"> <el-input v-model="info.icon"></el-input> </el-form-item> <el-form-item label="菜单地址"> <el-input v-model="info.address"></el-input> </el-form-item> <el-form-item label="状态"> <el-switch v-model="info.status"></el-switch> </el-form-item> <el-form-item> <el-button type="primary">提交</el-button> </el-form-item> </el-form>
其中,el-select组件中的el-option组件,如果不设置label和value属性的话,则默认把el-option中的内容当成值和label,但是实际项目中一般都不会直接把el-option中的内容进行传递,所以需要设置value属性和label属性,value属性用来控制传递的值,label属性用来控制匹配的值在option中显示的内容。
element-ui的表单组件中内置了验证功能,可以防止数据的丢失
(1)rules属性
需要给表单组件添加一个rules属性,用来告知表单具体的验证规则是什么
<el-form :rules="具体的验证规则名称">
(2)验证规则
具体的验证规则需要写在data里来进行预定义
<script>
export default {
data(){
return{
验证规则名称:{
要验证的字段名:[
{ required:true,message:'菜单名称不能为空',trigger:'blur' },
{ min:1,max:20,message:'菜单名称长度不符合要求' }
]
}
}
}
}
</script>
required 设置元素必填
messeage设置元素不符合验证规则显示的文字内容
trigger 设置元素触发规则的机制
min 设置元素内容的最小长度
max设置元素内容的最大长度
(3)prop属性
给需要进行验证的表单元素设置一个prop属性,属性值要和在验证规则中设置的名称保持一致
<el-form-item label="展示的名称" prop="要验证的字段名">
(4)model属性和ref属性
在进行表单验证时,需要给表单组件设置model属性,用来进行具体数据内容的验证
<el-form :model="要进行验证的数据对象" ref="表单自定义名称">
(5)validate
在点击提交按钮时,需要执行表单组件内置的validate方法来实现表单内容的验证
<el-form-item> <el-button type="primary" @click="自定义方法('表单的ref属性值')">提交</el-button> </el-form-item> <script> export default { ... methods:{ 自定义方法(形参) { this.$refs[形参].validate((valid) => { if (valid) { //验证规则满足时,才执行数据添加操作 } }); } } } </script>
示例代码:
<template> <div> <h1>菜单信息页</h1> <!-- el-form验证时使用的属性 rules 表单的验证规则 model 表单验证时使用的数据 --> <el-form label-width="80px" style="width:600px;" :rules="rules" :model="info" ref="menuForm" > <el-form-item label="菜单名称" prop="title"> <el-input v-model="info.title"></el-input> </el-form-item> <el-form-item label="上级菜单" prop="pid"> <el-select v-model="info.pid" placeholder="请选择"> <!-- value label 设置选中的选项名称 --> <el-option value="">请选择</el-option> <el-option value="0" label="顶级菜单">顶级菜单</el-option> <el-option value="1" label="系统设置">系统设置</el-option> </el-select> </el-form-item> <el-form-item label="菜单图标"> <el-input v-model="info.icon"></el-input> </el-form-item> <el-form-item label="菜单地址"> <el-input v-model="info.address"></el-input> </el-form-item> <el-form-item label="状态"> <el-switch v-model="info.status"></el-switch> </el-form-item> <el-form-item> <el-button type="primary" @click="submitInfo('menuForm')">提交</el-button> </el-form-item> </el-form> </div> </template> <script> export default { data(){ return{ info:{ title:'', pid:'', icon:'', address:'', status:true }, rules:{ title:[ { required:true,message:'菜单名称不能为空',trigger:'blur' }, { min:1,max:20,message:'菜单名称长度不符合要求' } ], pid:[ { required:true,message:'请选择上级菜单' } ] } } }, methods:{ submitInfo(formName) { this.$refs[formName].validate((valid) => { if (valid) { //验证规则满足时,才执行数据添加操作 } }); } } } </script> <style scoped> .el-form{ margin:20px; } </style>
显示当前页面的路径,快速返回之前的任意页面。
el-breadcrumb
el-breadcrumb-item
<el-breadcrumb separator=">">
<el-breadcrumb-item :to="{path:'/home'}">首页</el-breadcrumb-item>
<el-breadcrumb-item>
<a href="#/menu">菜单列表</a>
</el-breadcrumb-item>
<el-breadcrumb-item>菜单添加</el-breadcrumb-item>
</el-breadcrumb>
可以直接给el-breadcrumb-item组件设置to属性来进行页面的跳转,也可以在其中添加a标签/router-link标签来进行页面的跳转,如果不需要页面跳转,则直接写文字内容即可。
el-breadcrumb-item之间的分隔符默认是斜杠,可以通过separator属性来自行设置分隔符。
default-active 当前激活菜单的 index
router 是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转
启用路由模式后,el-menu-item组件的index属性就是要跳转的路由地址,不需要再使用router-link标签也可以实现路由跳转。
但是dafault-active设置为固定值的话,再次刷新页面后,左侧菜单还是选中的固定值的菜单
所以需要把default-active的设置为一个变量
(1)页面加载时
/src/components/views/Nav.vue
<script>
export default {
data(){
return{
defaultActive:''
}
},
mounted(){
//页面加载时,控住左侧菜单选中效果
//把当前路由中的meta的自定义属性赋值给默认选中变量
this.defaultActive = this.$route.meta.select;
}
}
</script>
由于在路由切换时,信息页面的路由地址并没有在左侧菜单中,可以通过路由的meta属性来自行设置选中哪个左侧菜单
/src/router/index.js
{
path:'menu',
component:()=>import('../components/pages/Menu/Index'),
meta:{select:'/menu'}
},
{
path:'menu/add',
component:()=>import('../components/pages/Menu/Info'),
meta:{select:'/menu'}
}
meta属性是路由信息中内置的一个属性,它的属性值类型为对象,在对象中自定义一个键值对用来告知左侧菜单应该选中哪个即可实现。
(2)路由地址变化时
/src/components/views/Nav.vue
<script>
export default {
...
watch:{
$route(newVal){
this.defaultActive = newVal.meta.select;
}
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。