vue开发记录小技巧
2019.12.18
vue页面结构 生命周期
export default {
name: 'layout',
//引用的组件
components: {
Navbar,
Sidebar,
AppMain
},
//props传值
props: {
isActive: {
type: Boolean,
default: false
},
toggleClick: {
type: Function,
default: null
}
}
data: {
message: 'Foo',
},
//混入可复用功能
mixins: [ResizeMixin],
//计算属性 只读/设置
computed: {
get: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
},
set:function(newValue){
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
},
//侦听属性 当值发生变化时,这个函数就会运行
watch:{
$route() {
this.getBreadcrumb()
}
},
methods:{
getBreadcrumb() {
console.log('watch change')
}
}
/*----[生命周期钩子](https://cn.vuejs.org/v2/guide/instance.html#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%9B%BE%E7%A4%BA)S---*/
beforeCreate:function(){
//在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
},
created: function () {
//在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。
this.getBreadcrumb()
this.getData(); //获取接口数据
},
/*----生命周期钩子E---*/
,
}
- 混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。
- 计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。
vue写法规范
和react写法差别很大,所有需要记录一下,要不然之后不做长期开发的话就又忘记了
v-bind绑定,缩写:src、:class、@click、
class的绑定
<div class="app-wrapper" :class="classObj"></div>
classObj={
hideSidebar: !this.sidebar.opened,
mobile: this.device === 'mobile'
}
<div :class="{'is-active':isActive}" ></div>
1、直接绑定class样式
<div :class="className">1、绑定classA</div>
2、绑定classA并进行判断,在isOK为true时显示样式,在isOk为false时不显示样式。
<div :class="{classA:isOk}">2、绑定class中的判断</div>
3、绑定class中的数组
<div :class="[classA,classB]">3、绑定class中的数组</div>
4、绑定class中使用三元表达式判断
<div :class="isOk?classA:classB">4、绑定class中的三元表达式判断</div>
4、Vue中动态添加多个class
<div class='fixed_button' :class="`${setFixed?'fixed':'absolute'} ${active?'active':''}`" @click="handleBtnConfirm">
5、绑定style
<div :style="{color:red,fontSize:font}">5、绑定style</div>
<div class="right_text" :style="{justifyContent:item.urls.length>1&&item.content==''?'flex-end':'space-between'}">
<span class='tag_span'v-for="(item,index) in tags" :key='index' :style="{background:colorsData[index]}">{{item}}</span>
<div class="h5_img" :style="{marginTop: item.content!=''?'0.375rem':''}">
6、用对象绑定style样式
<div :style="styleObject">6、用对象绑定style样式</div>
var app=new Vue({
el:'#app',
data:{
styleObject:{
fontSize:'24px',
color:'green'
}
}
})
style的绑定
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";
.app-wrapper {
@include clearfix;
position: relative;
height: 100%;
width: 100%;
}
</style>
事件绑定
<div @click="toggleClick"></div>
<ham-burger :toggleClick="toggleSideBar" :isActive="sidebar.opened"></ham-burger>
数据渲染
- 遍历
<div v-for="item in routes" v-if="!child.checked">1</div> - 条件
<div v-else :index="item.meta.url" :key="item.meta.url">2</div> - data
this.menus = response.data;(直接获取,直接赋值,区别与setState,setData)
引入组件
<template>
<product-detail :is-edit='true'></product-detail>
</template>
<script>
import ProductDetail from './components/ProductDetail'
export default {
name: 'updateProduct',
components: { ProductDetail }
}
</script>
指令
-
v-show
-
v-mode:在表单控件或者组件上创建双向绑定
<product-info-detail
v-show="showStatus[0]"
v-model="productParam"
:is-edit="isEdit"
@nextStep="nextStep">
</product-info-detail>
props: {
value: Object,
isEdit: {
type: Boolean,
default: false
}
},
this.$emit('nextStep'); 子用父的方法
Prop Types
<blog-post post-title="hello!"></blog-post>
Vue.component('blog-post', {
// camelCase in JavaScript
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
filters过滤器
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
<el-table-column label="订单状态" width="120" align="center">
<template slot-scope="scope">{{scope.row.status | formatStatus}}</template>
</el-table-column>
formatStatus(value) {
if (value === 2) {
return "待发货";
} else if (value === 3) {
return "待收货";
} else if (value === 4) {
return "待评价";
}
},
Vue Router
this.$router.push
this.$router.push({path:'/pms/addProduct'});
this.$router.push({path:'/pms/updateProduct',query:{id:row.id}});
this.$route.query.id
Router 构建选项
routes的配置参数:
{path: '/404', component: () => import('@/views/404'), hidden: true}
{
path: '/', //路径
component: Layout, // 命名视图组件 父组件
redirect: '/home', //重定向
children: [{ // 嵌套路由
path: 'home', //路径
name: 'home', // 命名路由
component: () => import('@/views/home/index'), // 命名视图组件
props: { newsletterPopup: false } , //路由组件传值
//props: (route) => ({ query: route.query.q })
meta: {title: '首页', icon: 'home'} //[路由元信息](https://router.vuejs.org/zh/guide/advanced/meta.html)
}]
},
router.beforeEach
router.beforeEach((to, from, next) => {})
引入所有router的组件
<router-view></router-view>
Vuex
每一个 Vuex 应用的核心就是 store(仓库)
<div class="app-wrapper" :class="classObj"></div>
computed: {//计算属性 读取或设置
sidebar() {
return this.$store.state.app.sidebar
},
classObj() {
return {
hideSidebar: !this.sidebar.opened
}
}
}
import { mapGetters } from 'vuex'
computed: {
...mapGetters([
'sidebar',
'avatar'
])
},
methods: {
toggleSideBar() {
this.$store.dispatch('ToggleSideBar')
},
}
import Cookies from 'js-cookie'
const app = {
state: {
sidebar: {
opened: !+Cookies.get('sidebarStatus'),
},
},
mutations: {
TOGGLE_SIDEBAR: state => {
if (state.sidebar.opened) {
Cookies.set('sidebarStatus', 1)
} else {
Cookies.set('sidebarStatus', 0)
}
state.sidebar.opened = !state.sidebar.opened
}
},
actions: {
ToggleSideBar: ({ commit }) => {
commit('TOGGLE_SIDEBAR')
}
}
}
export default app
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import getters from './getters'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
app
},
getters
})
export default store
const getters = {
sidebar: state => state.app.sidebar
}
export default getters
location.reload()方法用来刷新当前页面
引入插件
-
tinymce.min.js:富文本编辑器 -
nprogress:每次页面载入的时候,浏览器顶部有类似进度条的载入动画import router from './router' import store from './store' import NProgress from 'nprogress' // Progress 进度条 import 'nprogress/nprogress.css'// Progress 进度条样式 import { Message } from 'element-ui' import { getToken } from '@/utils/auth' // 验权 const whiteList = ['/login','/acceptStore'] // 不重定向白名单 router.beforeEach((to, from, next) => { NProgress.start() if (getToken()) { if (to.path === '/login') { next({ path: '/' }) NProgress.done() } else { if (store.getters.roles.length === 0) { store.dispatch('GetInfo').then(res => { // 拉取用户信息 next() }).catch((err) => { store.dispatch('FedLogOut').then(() => { Message.error(err || 'Verification failed, please login again') next({ path: '/' }) }) }) } else { next() } } } else { if (whiteList.indexOf(to.path) !== -1) { next() } else { next('/login') NProgress.done() } } }) router.afterEach(() => { NProgress.done() // 结束Progress }) -
v-charts:视图插件
vue组件库
- iView
- element-ui 应用
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI, { locale })
this.$message({
message: '暂无审核信息',
type: 'warning',
duration: 1000
});
this.$confirm('是否要进行审核', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(()=>{
//确定按钮的操作 请求接口,数据处理等
})
import { Message, MessageBox } from 'element-ui'
Message({
message: res.msg,
type: 'error',
duration: 3 * 1000
})
MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
store.dispatch('FedLogOut').then(() => {
location.reload()// 为了重新实例化vue-router对象 避免bug
})
})
table
<el-table-column
label="日期"
width="120">
<template slot-scope="scope">{{ scope.row.date }}</template>
</el-table-column>
<el-switch
@change="handlePublishStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.publishStatus">
</el-switch>
el-cascader 级联选择器 最后一级数据为空显示暂无数据问题
解决办法: 使用递归的方式,将最底层中的 children设为undefined
// 递归判断列表,把最后的children设为undefined
getTreeData(data){
for(var i=0;i<data.length;i++){
if(data[i].subcat.length<1){
// children若为空数组,则将children设为undefined
data[i].subcat=undefined;
}else {
// children若不为空数组,则继续 递归调用 本方法
this.getTreeData(data[i].subcat);
}
}
return data;
}
fetchListWithAttr().then(response => {
let list = response.data;
for (let i = 0; i < list.length; i++) {
let productAttrCate = list[i];
let children = [];
if (productAttrCate.productAttributeList != null && productAttrCate.productAttributeList.length > 0) {
for (let j = 0; j < productAttrCate.productAttributeList.length; j++) {
children.push({
label: productAttrCate.productAttributeList[j].name,
value: productAttrCate.productAttributeList[j].id
})
}
}else{
children=undefined
}
this.filterAttrs.push({label: productAttrCate.name, value: productAttrCate.id, children: children});
}
});
el-select clear 清空内容时触发事件
<el-input
placeholder="按sku编号搜索"
v-model="editSkuInfo.keyword"
size="small"
style="width: 50%;margin-left: 20px"
clearable
@clear='handleSearchEditSku'
>
el-upload fileList 数据不同步,手动 push 闪动问题
before-upload导致拿到的url不是http的格式。
handleBeforeUpload (file) {
return new Promise((resolve, reject) => {
if (this.accept && this.accept.toLowerCase().indexOf(file.name.replace(/.+(\.)/g, "$1").toLowerCase()) === -1) {
this.$message.warning(`请上传${this.accept}格式的文件`);
reject();
}
})
},
upload限制图片大小
该判断在
:before-upload中判断,但是数据请求没了
element ui 表格的checkbox初始化不可勾选
<el-table-column :selectable="checkboxT" type="selection" disabled='true' width="60" align="center"></el-table-column>
methods: {
checkboxT(row,index){
if(row.status== 1){ //未查看 2 已查看
return 1; //返回1 不可点击
}else{
return 0;//返回0 可点击
}
},
}
自定义验证el-input框只能输入数字及两位小数
<el-form :model="coupon"
:rules="rules"
ref="couponFrom"
label-width="150px"
size="small">
<el-form-item label="面额:" prop="amount">
<el-input v-model="coupon.amount" placeholder="面值只能是数值,限2位小数" class="input-width">
<template slot="append">元</template>
</el-input>
</el-form-item>
</<el-form>
data() {
const check_amount = (rule, value, callback) => {
let amount = /^(([1-9][0-9]*)|(([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2})))$/.test(value)
if(!amount){
return callback(new Error('面值只能是数值,0.01-10000,限2位小数'));
}else{
callback()
}
};
return{
rules: {
amount: [
{required: true,validator: check_amount,message: '面值只能是数值,0.01-10000,限2位小数',trigger: 'blur'}
]
},
}
}