vue开发记录2019.md

448 阅读4分钟

vue开发记录小技巧

2019.12.18

vue页面结构 生命周期

API

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>

数据渲染

  1. 遍历 <div v-for="item in routes" v-if="!child.checked">1</div>
  2. 条件 <div v-else :index="item.meta.url" :key="item.meta.url">2</div>
  3. 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-XX

  • 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) => {})

beforeEach

引入所有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组件库

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的格式。

before-upload问题

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'}
              ]
        },
      }
 
 }