vue.js 核心知识点六

目录

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

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

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

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


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

模拟异步

父组件

<template>
 <div>
  父组件
  <child :child-data="asyncData" ></child>
 </div>
</template>
 
<script>
 import child from './child'
 export default {
  data(){
      return {
          asyncData:''
      }
  },
  components: {
   child
  },
  created() {
   // setTimeout模拟异步数据
   setTimeout(() => {
    this.asyncData = 'async data'
    this.asyncObject = {'items': [1, 2, 3]} 第二种情况
    console.log('parent finish')
   }, 1000)
  }
 }
</script>

子组件

<template>
 <div>
  子组件{{childData}}
第二种
  子组件<p>{{childObject.items[0]}}</p>
 这里很常见的一个问题,就是{{childObject}}可以获取且没有报错,
但是{{childObject.items[0]}}不行,往往有个疑问为什么前面获取到值,后面获取不到呢?
 </div>
</template>
 
<script>
 export default {
  props: ['childData'],
  data: () => ({
  }),
  created () {
   console.log(this.childData)  //空值
 console.log(this.childObject) // 空值
  },
  methods: {
  }
 }
</script>

// 首先传过来的是空,然后在异步刷新值,也开始时候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为空问题
    父组件
  <child :child-data="asyncData" v-if="asyncData"></child>

当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
<template>
 <div>
  父组件
  <child :child-object="asyncObject"></child>
 </div>
</template>
 
<script>
 import child from './child'
 export default {
  data: () => ({
   asyncObject: undefined
  }),
  components: {
   child
  },
  created () {
  },
  mounted () {
   // setTimeout模拟异步数据
   setTimeout(() => {
    this.asyncObject = {'items': [1, 2, 3]}
    console.log('parent finish')
   }, 2000)
  }
 }
</script>

child.vue

<template>
 <div>

  <p>{{test}}</p>
 </div>
</template>
 
<script>
 export default {
  props: ['childObject'],
  data: () => ({
   test: ''
  }),
  watch: {
   'childObject.items': function (n, o) {
    this._test = n[0]
   }
  },
  computed: {
   _test: {
    set (value) {
     this.update()
     this.test = value
    },
    get () {
     return this.test
    }
   }
  },
  methods: {
   update () {
    console.log(this.childObject) // {items: [1,2,3]}
   }
  }
 }
</script>
  • 方法五 :使用emit,on,bus相结合
    parent.vue
<template>
 <div>
  父组件
  <child></child>
 </div>
</template>
 
<script>
 import child from './child'
 export default {
  data: () => ({
  }),
  components: {
   child
  },
  mounted () {
   // setTimeout模拟异步数据
   setTimeout(() => {
    // 触发子组件,并且传递数据过去
    this.$bus.emit('triggerChild', {'items': [1, 2, 3]})
    console.log('parent finish')
   }, 2000)
  }
 }
</script>

child.vue

<template>
 <div>
  子组件
  <p>{{test}}</p>
 </div>
</template>
 
<script>
 export default {
  props: ['childObject'],
  data: () => ({
   test: ''
  }),
  created () {
   // 绑定
   this.$bus.on('triggerChild', (parmas) => {
    this.test = parmas.items[0] // 1
    this.updata()
   })
  },
  methods: {
   updata () {
    console.log(this.test) // 1
   }
  }
 }
</script>

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

  • 方法六:使用prop default来解决{{childObject.items[0]}}
    parent.vue
<template>
 <div>
  父组件
  <child :child-object="asyncObject"></child>
 </div>
</template>
 
<script>
 import child from './child'
 export default {
  data: () => ({
   asyncObject: undefined // 这里使用null反而报0的错
  }),
  components: {
   child
  },
  created () {
  },
  mounted () {
   // setTimeout模拟异步数据
   setTimeout(() => {
    this.asyncObject = {'items': [1, 2, 3]}
    console.log('parent finish')
   }, 2000)
  }
 }
</script>

child.vue

<template>
 <div>
  子组件<!--1-->
  <p>{{childObject.items[0]}}</p>
 </div>
</template>
 
<script>
 export default {
  props: {
   childObject: {
    type: Object,
    default () {
     return {
      items: ''
     }
    }
   }
  },
  data: () => ({
  }),
  created () {
   console.log(this.childObject) // {item: ''}
  }
 }
</script>

  • 其他方法
    将数据存到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

<template>
  <div class="select">
    <div class="wrap">
        <selectInput></selectInput>
        <list></list>
    </div>
  </div>
</template>

<script>
  // 引入子组件 
  import selectInput from '@/components/selectInput'  
  import list from '@/components/list'
  export default {
    components:{   //加载子组件
      selectInput,
      list
    },
  }
</script>

<style>
  .select{
    background:#4a56fe;
    width: 400px;
    margin: 100px auto 0;
    padding: 40px;
    border-radius: 10px;
  }
  .wrap{
    background: #e3e5fe;
    border-radius: 10px;
    padding: 40px;
  }
  ul{
    list-style: none;
  }
</style>

子组件 list.vue

<template>
 <div class="list">
   <ul>
     <li v-for="(item,index) in list" :key="index" v-show="initShow" @click="changeTitle(item.title)">{{item.title}}</li>
   </ul>
 </div>
</template>

<script>
   import {mapState,mapMutations} from 'vuex'  // 将vuex中的state数据和mutations中的方法映射到组件中
   export default {
       //vue 生命周期(created)在实例创建之后,在数据初始化之前被调用
       created(){  
           this.$store.dispatch('getListAction')  //调用vuex 中的 getListAction异步方法
       },
       //计算state数据
       computed:{
           ...mapState({
             list:'list',
             initShow:'isShow'
           })
       },
       methods:{
           changeTitle(title){
             this.$store.commit('changeTitleMutation',title)
             this.$store.commit('toggleShow',!this.initShow)
           }
       }
   }
</script>

<style>
 .list{
   padding: 10px 0;
   text-align: center;
 }
 li{
   line-height: 30px;
   height: 30px;
   border-radius: 15px;
   cursor: pointer;
   color:#535353;
 }
 li:hover{
   background: #ff705b;
   color: #fff;
 }
</style>`

子组件selectInput.vue

<template>
  <div class="inputBox">
    <input type="text" readonly :value="getTitle" @click="toggleShow" placeholder="你喜欢什么">
  </div>
</template>

<script>
export default {
  computed:{
    // 获取vuex中的state数据并赋值绑定到 value上面  computed 里面的方法名其实就是相当于 data里面的数据,可以用this.getTitle 去访问
    getTitle(){ 
      return this.$store.state.title
    },
    // 初始化控制下拉选项显示隐藏的状态,如果isShow是false 则不限是下拉菜单,默认是false
    initShow(){
        return this.$store.state.isShow
    }
  },
  methods:{
    //点击input的时候调用该方法,这个方法去触发mutations下面的toggleShow,去改变isShow的状态,默认是isShow等于false, 然后在点击的时候去改变isShow 等于true ,  !this.initShow就是true,如果是true的话,下拉选项才能出来,并将改变过后的值传给toggleShow方法,去给vuex/store.js 里面的state.isShow赋值。
    toggleShow(){
      this.$store.commit('toggleShow',!this.initShow)
    }
  }
}
</script>

<style>
input{
  outline: none;
  width: 100%;
  height: 40px;
  line-height: 40px;
  border-radius: 10px;
  border: 1px solid #d3d3d3;
  text-indent: 20px;
  color: #535353;
}
</style>

参考 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即可:

<input type="text" 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

        <div id="div1" v-drag.limit></div>
        <div id="div2" v-drag></div>

- 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文件

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容

  • 一、 组件component 1. 什么是组件? 组件(Component)是 Vue.js 最强大的功能之一。组...
    饥人谷_Leonardo阅读 1,932评论 0 18
  • 基于Vue的一些资料 内容 UI组件 开发框架 实用库 服务端 辅助工具 应用实例 Demo示例 element★...
    尝了又尝阅读 1,137评论 0 1
  • vue概述 在官方文档中,有一句话对Vue的定位说的很明确:Vue.js 的核心是一个允许采用简洁的模板语法来声明...
    li4065阅读 7,176评论 0 25
  • 来源:http://www.cnblogs.com/opendigg/p/6513510.html UI组件 el...
    YU_XI阅读 2,839评论 0 26
  • UI组件 element- 饿了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的组件库 m...
    你猜_3214阅读 11,004评论 0 118