vue.js 核心知识点六

目录

- 6.1 父组件异步获取动态数据传递给子组件

- 6.2 vue-cli中自定义指令的使用

- 6.3 vue弹窗后如何禁止滚动条滚动?

- 6.4 vue提供的几种脚手架模板


- 6.1 父组件异步获取动态数据传递给子组件

模拟异步

父组件


 


子组件


 


// 首先传过来的是空,然后在异步刷新值,也开始时候childObject.items[0]等同于''.item[0]这样的操作,
所以就会报下面的错

vue.esm.js?8910:434 [Vue warn]: Error in render function: 
"TypeError: Cannot read property '0' of undefined"
问题描述

父组件获取异步数据,并传递给子组件,直接显示没有问题,若对数据进行处理,则拿到的数据都是父组件初始值。

原因

父组件 获取异步数据 还没等到数据返回 子组件 created已经执行完毕

父子组件的生命周期


image
解决
  • 方法一 使用v-if可以解决报错问题,和created为空问题
    父组件
  

当asyncData有值得时候,在加载子组件

  • 方法二 子组件使用watch来监听父组件改变的prop,使用methods来代替created
    子组件
watch:{
  childData(val){
      this.flGoods = val;
      console.log('子组件 数据处理',val) 
      this.updata()
  }
},
methods: {
   updata () { // 既然created只会执行一次,但是又想监听改变的值做其他事情的话,只能搬到这里咯
    console.log(this.test)// 1
   }
  }
  • 方法三:在父组件里用Promise方法异步执行数据的赋值:
new Promise((resolve,reject) => {
          if (res.status === 200){
            resolve(res);
          }
        }).then((res) => {
          this.category = res.data.data.category;
          this.adBar = res.data.data.advertesPicture.PICTURE_ADDRESS;
          this.bannerSwipePics = res.data.data.slides
          this.recommendGoods = res.data.data.recommend;
          // 也可异步获取再传给子组件 Promise
          this.floorSeafood = res.data.data.floor1;
          this.floorBeverage = res.data.data.floor2;
          this.floorFruits = res.data.data.floor3;
          console.log(this.floorFruits);
          this._initScroll();
        })
      }).catch(err => {
        console.log(err);
      });

这样也是可以的,异步获取数据导致的报错的情况会在各个场景出现,比如根据数据渲染dom,而对dom有js操作的时候,会因为还没渲染出来而找不到响应的dom元素报错,这里可以用vue提供的$nextTick()函数,或者手动开个setTimeout定时器,延迟获取;使用better-scroll的时候因为dom没有渲染出来而无法获取滚动元素的高度,导致无法滚动,同样可以用vue提供的这个函数,等dom渲染完了后再初始化滚动。

  • 方法四 :子组件watch computed data 相结合
    parent.vue

 

child.vue


 

  • 方法五 :使用emit,on,bus相结合
    parent.vue

 

child.vue


 

这里使用了bus这个库,parent.vue和child.vue必须公用一个事件总线(也就是要引入同一个js,这个js定义了一个类似let bus = new Vue()的东西供这两个组件连接),才能相互触发

  • 方法六:使用prop default来解决{{childObject.items[0]}}
    parent.vue

 

child.vue


 


  • 其他方法
    将数据存到store,子组件监听数据变化(watch/computed)
大概逻辑:使用vuex全局状态管理,其实简单,
利用vuex的辅助函数(mapState,mapMutations)
mapState是将state里面的数据映射到计算中(computed),
mapMutations也是类似,把vuex中mutations的方法映射到组件里面,
就可以在组件里面直接使用方法了,
在vuex中使用异步(actions)去掉用接口,
然后在接口成功的函数里面取触发同步(mutations)里面的方法,
把得到数据传给mutations里面的方法里并且给state里面的属性赋值,
然后就可以在子组件中使用computed计算中去获取数据并且渲染到页面上,
其实说的有点绕( -_-"),但是看代码就明白了 。

vuex / index.js

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex) 
export default new Vuex.Store({  
    //定义初始数据
    state: {  
        title: '',
        list: [],
        isShow: false
    },
    //同步的方法
    mutations: {
        //向state 里面设置数据
        changeListMutation(state, list) {
            state.list = list
        },
        //在list.vue里面点击下拉选项的时候触发 给state.title赋值
        changeTitleMutation(state, title) {
            state.title = title
        },
        //selectinput.vue里面点击input的时候触发 给state.isShow赋值
        toggleShow(state, isShow) {
            state.isShow = isShow 
        }
    },
    //异步的方法
    actions: {
        //在list.vue里面created生命周期里面触发
        getListAction({ commit }) {
            axios.get('/mock/5afd9dc0c088691e06a6ab45/example/dataList')
                .then((res) => {
                    commit('changeListMutation', res.data) //调用mutations下面的changeListMutation方法并且传值过去
                })
                .catch((error) => {
                    console.log(error)
                })

        }
    }
})
// 触发异步里面的方法是用 this.$store.dispatch('这里是方法名')
// 触发同步里面的方法是用 this.$store.commit('这里是方法名')

父组件 select.vue







子组件 list.vue





`

子组件selectInput.vue







参考 https://www.jb51.net/article/117447.htm

- 6.2 vue-cli中自定义指令的使用

vue中除了内置的指令(v-show,v-model)还允许我们自定义指令

想要创建自定义指令,就要注册指令(以输入框获取焦点为例) 注意:autofocus 在移动版 Safari 上不工作

一、注册全局指令:

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el,binding) {
                // 当前指令绑定的dom元素
                //console.log(el);
                // 指令传入的参数、修饰符、值  v-指令名称:参数.修饰符=值
                // console.log(binding)
    // 聚焦元素
    el.focus()
  }
})

二、注册局部指令: 组件中也接受一个 directives 的选项

directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus()
    }
  }
}

使用也很简单:直接在元素上面使用v-focus即可:


下面再举一个自定义指令的小例子:拖拽

       Vue.directive('drag', {
           // 当指令绑定到元素上的时候执行
           bind(el, binding) {
               // console.log('bind');
               // 当前指令绑定的dom元素
               //console.log(el);
               // 指令传入的参数、修饰符、值  v-指令名称:参数.修饰符=值
               // console.log(binding)
               el.onmousedown = function(e) {
                   var e = e||event;
                   let disX = e.clientX - el.offsetLeft;
                   let disY = e.clientY - el.offsetTop;

                   document.onmousemove = function(e) {
                       var e = e||event;
                       let L = e.clientX - disX;
                       let T =  e.clientY - disY;

                       if (binding.modifiers.limit) {
                           if (L < 0) {
                               L = 0;
                           }
                       }

                       el.style.left = L + 'px';
                       el.style.top = T + 'px';
                   };

                   document.onmouseup = function() {
                       document.onmousemove = null;
                   };

                   return false;
               }
           }
       });

使用也很简单,只用在元素上添加v-drag或者v-drag.limit

        

- 6.3vue弹窗后如何禁止滚动条滚动?

 /***滑动限制***/
      stop(){
        var mo=function(e){e.preventDefault();};
        document.body.style.overflow='hidden';
        document.addEventListener("touchmove",mo,false);//禁止页面滑动
      },
      /***取消滑动限制***/
      move(){
        var mo=function(e){e.preventDefault();};
        document.body.style.overflow='';//出现滚动条
        document.removeEventListener("touchmove",mo,false);
      }

function toggleBody(isPin){

    if(isPin){

        document.body.style.height = '100vh'

        document.body.style['overflow-y'] = 'hidden'
    }

    else{

        document.body.style.height = 'unset'

        document.body.style['overflow-y'] = 'auto'

    }
}

toggleBody(1)  //在跳出弹窗的时候
toggleBody(0)  //弹窗消失的时候

超长的页面怎么办呢
上面直接限制body固然有效,但如果一个页面很长很长,超出了100vh,而我正好滚到中间时弹出弹窗。此时若直接限制body的overflow: hidden则会让页面一下弹到顶部,显然不是好的做法。那么,又该怎么做呢?

对移动端,可以引入touch-action,限制为none,在弹窗消失时再变回auto。但ios的safari上不支持该属性(可以去caniuse上查查,起码2018.11的时候还不支持)。如果我们的app在ios上用的是safari内核,就起不到效果了。

这时候,就需要结合event.preventDefault属性来用了。注意在绑定addEventListener的时候,需要多传一个options,强调这个事件不是passive的,否则谷歌等新版浏览器会报错。同时最好也指定capture: true,这样可以早点禁止该事件。

报错是Unable to preventDefault inside passive event listener due to target being treated as passive.。这是因为谷歌从chrome51之后引入的新优化。事实上,谷歌建议一般情况下,将 passive 标志添加到每个没有调用 preventDefault() 的 wheel、mousewheel、touchstart 和 touchmove 事件侦听器。但是,对于这种禁止了默认事件的eventListener,在这种情况下,反而是要强调它不是消极监听的。因为滚动都不能滚了,无所谓什么优化了。

代码如下(vue版本的):

watch: {
    show(v) {
      this.toggleContainerTouchAction(v)
      if (v) {
        document.body.addEventListener('touchmove', this.stopTouch, { passive: false, capture: true })
      } else {
        document.body.removeEventListener('touchmove', this.stopTouch, { capture: true })
      }
    },
  },
  methods: {
    toggleContainerTouchAction(v) {
      const container = document.querySelector('.container')
      if (!container) {
        return
      }
      container.style['touch-action'] = v ? 'none' : 'auto'
    },
    stopTouch(e) {
      e.preventDefault()
    },


- 6.4 vue提供的几种脚手架模板

vue-cli 的脚手架项目模板有browserify 和 webpack , 现在自己在用的是webpack , 官网给出了两个模板: webpack-simple 和 webpack 两种。两种的区别在于webpack-simple 没有包括Eslint 检查功能等等功能,普通项目基本用webpack-simple 就足够了.
搭建官方项目模板步骤:
1、npm install vue-cli (安装vue-cli ) 有的时候有看到其它两种写法: --save-dev 和 --save的写法。这两个有一定的区别,我们都知道package.json 中有一个 “dependencies” 和 “devDependencies” 的。dependencies 是用在开发完上线模式的,就是有些东西你上线以后还需要依赖的,比如juqery , 我们这里的vue 和 babel-runtime(Babel 转码器 可以将ES6 转为ES5 ), 而devDependencies 则是在开发模式执行的,比如我们如果需要安装一个node-sass 等等。有的时候看到package.json中安装的模块版本号前面有一个波浪线。例如: ~1.2.3 这里表示安装1.2.x以上版本。但是不安装1.3以上。

2、vue init webpack-simple yourdemoname 下载一个webpack-simple项目,这里的webpack-simple 是固定的,也就是官网的项目模板。youdemoname 这个是你自己项目的名字。 执行这个步骤以后。就会弹出询问 “项目名称..项目描述“等等问题 直接按照提示操作。这个时候对应的项目目录下就出现刚刚建立的项目了。

3、我们还需要把项目的依赖下载下来。使用命令: cd youdemoname 然后执行npm install 就可以了,这个时候你的项目中有多了一个node_modules 目录

4、使用"npm - run - dev" 命令来运行项目 "npm-run-bulid" 来执行发布,会自动生成dist文件

你可能感兴趣的:(vue.js 核心知识点六)