美文网首页前端优化vueVue
vue全家桶+element 项目踩坑总结

vue全家桶+element 项目踩坑总结

作者: liy_y | 来源:发表于2019-03-03 20:57 被阅读306次

    项目简介

    vue + axios + vue-router + vuex + ElementUI

    架构

    vue

    vue数据更新,视图不更新

    只有当实例被创建时 data 中存在的属性才是响应式的,Vue 不能检测对象属性的添加或删除;
    可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性

    Vue.set(vm.userProfile, 'age', 27)
    this.$set(this.transportAddData.serviceOrderList[a].serviceOrderItems[i], 'deletePoundIds', [])
    

    vue 数据与方法
    vue 对象更改检测注意事项

    Vue 不能检测以下变动的数组:

    • 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
    • 当你修改数组的长度时,例如:vm.items.length = newLength

    vue 数组更新检测

    持续触发事件的优化

    持续触发某个事件可以用函数的节流和防抖来做性能优化

    //防抖
    function(){  
        ...
        
        clearTimeout(timeoutId);
    
        timeoutId = setTimeout(function () {
          console.log('content', content);
          player(content.msgTypeId, comId)
        }, 500);
        
        ...
        
    }
    
    // 节流
    var canRun = true;
    document.getElementById("throttle").onscroll = function(){
        if(!canRun){
            // 判断是否已空闲,如果在执行中,则直接return
            return;
        }
    
        canRun = false;
        setTimeout(function(){
            console.log("函数节流");
            canRun = true;
        }, 300);
    };
    
    

    javaScript的Throttling(节流)和Debouncing(防抖)

    nextTick

    在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

    
    
    get() {
        this.$http.get('/api/article').then(function (res) {
            this.list = res.data.data.list
            // ref  list 引用了ul元素,我想把第一个li颜色变为红色
            document.querySelectorAll('li')[0].style.color = 'red'  //这里会报错-,因为还没有 li
            
            this.$nextTick( ()=> {
                document.querySelectorAll('li')[0].style.color = 'red'
            })
        })
    },
    

    Vue.nextTick 的原理和用途

    音频文件自动播放报错

    谷歌浏览器(Chrome 66)音频自动播放报错

    DOMException: play() failed because the user didn’t interact with the document first.
    

    解决方案:AudioContext

    // Chrome
    request.get('/baseConfig/messageAudio/play', {
        params: {
            "comId": Cookies.get('comId'),
            "msgTypeId": id
        },
        responseType: 'arraybuffer'   // 这里需要设置xhr response的格式为arraybuffer
        })
        .then(req => {
        
        ...
        
            let context = new (window.AudioContext || window.webkitAudioContext)();
            context.decodeAudioData(req.data, decode => play(context, decode));
                      
            function play (context, decodeBuffer) {
                sourceadio = context.createBufferSource();
                sourceadio.buffer = decodeBuffer;
                sourceadio.connect(context.destination);
                // 从0s开始播放
                sourceadio.start(0);
            }
        
        ...
        
    })
    

    Chrome 66禁止声音自动播放之后
    AudioContext

    AudioContext.decodeAudioData()

    vuex

    使用vuex修改state的方法和区别

    • 可以直接使用 this.$store.state.变量 = xxx;
    • this.store.dispatch(actionType, payload) 或者 this.store.commit(commitType, payload)

    相同点:能够修改state里的变量,并且是响应式的(能触发视图更新)
    不同点: 若将vue创建 store 的时候传入 strict: true, 开启严格模式,那么任何修改state的操作,只要不经过
    mutation的函数,vue就会报如下错

    throw error :    [vuex] Do not mutate vuex store state outside mutation handlers。
    

    使用commit提交到mutation修改state的优点:vuex能够记录每一次state的变化记录,保存状态快照,实现时间漫游/回滚之类的操作。

    dispatch 和 commit的区别在于:
    使用dispatch是异步操作, commit是同步操作,
    所以 一般情况下,推荐直接使用commit,即 this.$store.commit(commitType, payload),以防异步操作会带来的延迟问题。

    vuex strict
    vuex Mutation
    vuex actions

    vuex到底是什么

    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      },
      actions: {
        increment (context) {
          context.commit('increment')
        }
      }
    })
    

    ==vuex中的state本质上是没有template的隐藏的vue组件。==

    vuex工作原理详解

    axios

    兼容问题

    Edge 41.16299.15.0 post请求会自动转为get

    microsoft-edge/platform/issues
    vue 使用axios 在EDGE浏览器上面post请求变成了get请求

    取消请求

    场景:每次请求在拦截器中添加token,后台判断token是否过期;当进入一个页面时触发多次请求,且正好token已经过期。这个时候需要第一次请求完成之后知道token已经过期则弹出提示、页面跳转到登录、停止之后的请求;否则会因为多次请求和axios响应拦截而多次弹框提示。

    • 解决方案
      axios自带cancelToken 取消方法,然后在路由跳转后停止之前的请求
    // 请求拦截中 添加 cancelToken
    axios.interceptors.request.use(config => {
        config.cancelToken = store.source.token
        return config
    }, err => {
        return Promise.reject(err)
    })
     
    // 路由跳转中进行判断
    router.beforeEach((to, from, next) => {
        const CancelToken = axios.CancelToken
        store.source.cancel && store.source.cancel()
        store.source = CancelToken.source()
        next()
    })
    
    
    //sort文件/
    state: {
        source: {
          token: null,
          cancel: null
        }
      }
    

    axios api
    路由变化时使用axios取消所有请求
    vue项目中 axios请求拦截器与取消pending请求功能

    存在问题: 如果 token过期提示弹框为二次确认弹框,再次确认之后才会进行页面跳转,那么在点击确认之前,页面中之前的请求还是会继续进行;
    解决方案:给弹窗添加全局状态,根据状态判断是否需要弹出弹框

    预检请求

    CORS跨域
    CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,==IE浏览器不能低于IE10。==
    浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

    • 简单请求
    1. 请求方法是以下三种方法之一:
      HEAD
      GET
      POST
    2. HTTP的头信息不超出以下几种字段:
      Accept
      Accept-Language
      Content-Language
      Last-Event-ID
      Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

    简单请求不会触发 CORS 预检请求。

    • 非简单请求

    当请求满足下述任一条件时,即为非简单请求:

    1. 使用了下面任一 HTTP 方法:
      PUT
      DELETE
      CONNECT
      OPTIONS
      TRACE
      PATCH
    2. 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:
      Accept
      Accept-Language
      Content-Language
      Content-Type (but note the additional requirements below)
      DPR
      Downlink
      Save-Data
      Viewport-Width
      Width
    3. Content-Type 的值不属于下列之一:
      application/x-www-form-urlencoded
      multipart/form-data
      text/plain
    4. 请求中的XMLHttpRequestUpload 对象注册了任意多个事件监听器。
    5. 请求中使用了ReadableStream对象。

    HTTP访问控制(CORS)

    • 预检请求

    非简单请求,会在正式通信之前,增加一次HTTP OPTIONS方法发起的查询请求,称为"预检"请求(preflight)。以获知服务器是否允许该实际请求。
    "预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
    ==该方法不会对服务器资源产生影响==

    • 优化方案
    Access-Control-Max-Age: <delta-seconds> //单位是秒。
    

    表示 preflight request (预检请求)的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息) 可以被缓存多久

    Vue Router

    push、replace的区别

    push会向history添加新的记录,replace只是替换掉原来的记录,不会添加新的记录;
    这就导致了用replace方法跳转的页面是无法回到之前的页面的;(类似window.history)

    vue Router 编程式的导航

    路由懒加载

    为了提升页面加载速度,实现按需加载,也就是当路由访问时才加载对应组件,我们可以结合vue的异步组件和webpack的代码分割功能来实现路由的懒加载。

    {
        path: '/iov/login',
        name: '登录',
        component: resolve => require(['@/views/login/login'], resolve),
    },
    {
        path:'/iov/organization',
        name:'组织管理',
        component:() => import('@/views/enterprise/organization')
    }
    

    vue Router 路由懒加载
    vue 异步组件
    vue + vue-router 懒加载 import / resolve+require

    elementUI

    表单弹窗中 elementform 清除验证残留提示

    给表单添加不同的 ref (REFNAME),如果有相同的ref 会导致残留验证提示清除失败

        this.dialogTranspor = true
        //弹窗打开后 dom 没有生成,所有要用 this.$nextTick
       
        this.$nextTick(function () {
         
            this.$refs.REFNAME.resetFields();
    
          })
    

    页码数无法正常显示

    场景:列表页在跳到详情或其他页面后再返回列表页,无法正常显示对应页数(页码数放在state中),但请求的数据时正常的;

    解决方案:页码数、总页数必须要在同一个对象中,否则当前页码数不能正常显示

    data() {
        return {
            //完成查询条件
            searchComplate: {        
            "comId": Cookies.get('comId'),
            "transportOrderCode": null,
            "orderCode": null,
            "customerId": null,
            "abnormal": 2,
            "startTime": null,
            "endTime": null,
            "pageNum": 1,
            "pageSize": 20,
            total: 0,
            currentRow: '',
            dataArea: ['', ''],
            activeName: '',
            expands: []
            },
        }
    }
    

    动态多级表单验证

    <li v-for="(item,index) in transportAddData.serviceOrderList">
     
        <template v-for="(subItem,subIndex) in item.serviceOrderItems">
        
            <tr >
                        
                <td>
                  <el-form-item :prop="'serviceOrderList.'+index+'.serviceOrderItems.' + subIndex + '.addressName'"
                                :rules="{required: true, message: '卸货地不能为空', trigger: 'blur'}">
                    <el-input v-model="subItem.addressName" placeholder="地址"></el-input>
                  </el-form-item>
                </td>
                <td>
                  <el-form-item :prop="'serviceOrderList.'+index+'.serviceOrderItems.' + subIndex + '.planTonnage'"
                                :rules="[{required: true, message: '必填项', trigger: 'blur'},{pattern: /^((([1-9]+(\d+)?)(\.\d+)?)|(0\.\d+))$/, message: '必须为正数且不为0'}]">
                    <el-input v-model="subItem.planTonnage"
                              placeholder="预卸吨数"></el-input>
                  </el-form-item>
                </td>
                ...
                
            </tr>
            
        </template>
    
    </li>
    

    相关文章

      网友评论

        本文标题:vue全家桶+element 项目踩坑总结

        本文链接:https://www.haomeiwen.com/subject/jugmuqtx.html