目錄
- 6.1 父組件異步獲取動態(tài)數(shù)據(jù)傳遞給子組件
- 6.2 vue-cli中自定義指令的使用
- 6.3 vue彈窗后如何禁止?jié)L動條滾動赁濒?
- 6.4 vue提供的幾種腳手架模板
- 6.1 父組件異步獲取動態(tài)數(shù)據(jù)傳遞給子組件
模擬異步
父組件
<template>
<div>
父組件
<child :child-data="asyncData" ></child>
</div>
</template>
<script>
import child from './child'
export default {
data(){
return {
asyncData:''
}
},
components: {
child
},
created() {
// setTimeout模擬異步數(shù)據(jù)
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>
這里很常見的一個(gè)問題,就是{{childObject}}可以獲取且沒有報(bào)錯汇跨,
但是{{childObject.items[0]}}不行喇喉,往往有個(gè)疑問為什么前面獲取到值祖今,后面獲取不到呢?
</div>
</template>
<script>
export default {
props: ['childData'],
data: () => ({
}),
created () {
console.log(this.childData) //空值
console.log(this.childObject) // 空值
},
methods: {
}
}
</script>
// 首先傳過來的是空拣技,然后在異步刷新值千诬,也開始時(shí)候childObject.items[0]等同于''.item[0]這樣的操作,
所以就會報(bào)下面的錯
vue.esm.js?8910:434 [Vue warn]: Error in render function:
"TypeError: Cannot read property '0' of undefined"
問題描述
父組件獲取異步數(shù)據(jù)膏斤,并傳遞給子組件大渤,直接顯示沒有問題,若對數(shù)據(jù)進(jìn)行處理掸绞,則拿到的數(shù)據(jù)都是父組件初始值泵三。
原因
父組件 獲取異步數(shù)據(jù) 還沒等到數(shù)據(jù)返回 子組件 created已經(jīng)執(zhí)行完畢
父子組件的生命周期
解決
- 方法一 使用v-if可以解決報(bào)錯問題,和created為空問題
父組件
<child :child-data="asyncData" v-if="asyncData"></child>
當(dāng)asyncData有值得時(shí)候衔掸,在加載子組件
- 方法二 子組件使用watch來監(jiān)聽父組件改變的prop烫幕,使用methods來代替created
子組件
watch:{
childData(val){
this.flGoods = val;
console.log('子組件 數(shù)據(jù)處理',val)
this.updata()
}
},
methods: {
updata () { // 既然created只會執(zhí)行一次,但是又想監(jiān)聽改變的值做其他事情的話敞映,只能搬到這里咯
console.log(this.test)// 1
}
}
- 方法三:在父組件里用Promise方法異步執(zhí)行數(shù)據(jù)的賦值:
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);
});
這樣也是可以的较曼,異步獲取數(shù)據(jù)導(dǎo)致的報(bào)錯的情況會在各個(gè)場景出現(xiàn),比如根據(jù)數(shù)據(jù)渲染dom振愿,而對dom有js操作的時(shí)候捷犹,會因?yàn)檫€沒渲染出來而找不到響應(yīng)的dom元素報(bào)錯弛饭,這里可以用vue提供的$nextTick()函數(shù),或者手動開個(gè)setTimeout定時(shí)器萍歉,延遲獲嚷滤獭;使用better-scroll的時(shí)候因?yàn)閐om沒有渲染出來而無法獲取滾動元素的高度枪孩,導(dǎo)致無法滾動憔晒,同樣可以用vue提供的這個(gè)函數(shù),等dom渲染完了后再初始化滾動蔑舞。
- 方法四 :子組件watch computed data 相結(jié)合
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模擬異步數(shù)據(jù)
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相結(jié)合
parent.vue
<template>
<div>
父組件
<child></child>
</div>
</template>
<script>
import child from './child'
export default {
data: () => ({
}),
components: {
child
},
mounted () {
// setTimeout模擬異步數(shù)據(jù)
setTimeout(() => {
// 觸發(fā)子組件攻询,并且傳遞數(shù)據(jù)過去
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這個(gè)庫从撼,parent.vue和child.vue必須公用一個(gè)事件總線(也就是要引入同一個(gè)js,這個(gè)js定義了一個(gè)類似let bus = new Vue()的東西供這兩個(gè)組件連接)钧栖,才能相互觸發(fā)
- 方法六:使用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反而報(bào)0的錯
}),
components: {
child
},
created () {
},
mounted () {
// setTimeout模擬異步數(shù)據(jù)
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>
- 其他方法
將數(shù)據(jù)存到store低零,子組件監(jiān)聽數(shù)據(jù)變化(watch/computed)
大概邏輯:使用vuex全局狀態(tài)管理,其實(shí)簡單,
利用vuex的輔助函數(shù)(mapState桐经,mapMutations)
mapState是將state里面的數(shù)據(jù)映射到計(jì)算中(computed),
mapMutations也是類似浙滤,把vuex中mutations的方法映射到組件里面阴挣,
就可以在組件里面直接使用方法了,
在vuex中使用異步(actions)去掉用接口,
然后在接口成功的函數(shù)里面取觸發(fā)同步(mutations)里面的方法,
把得到數(shù)據(jù)傳給mutations里面的方法里并且給state里面的屬性賦值纺腊,
然后就可以在子組件中使用computed計(jì)算中去獲取數(shù)據(jù)并且渲染到頁面上畔咧,
其實(shí)說的有點(diǎn)繞( -_-"),但是看代碼就明白了 揖膜。
vuex / index.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
//定義初始數(shù)據(jù)
state: {
title: '',
list: [],
isShow: false
},
//同步的方法
mutations: {
//向state 里面設(shè)置數(shù)據(jù)
changeListMutation(state, list) {
state.list = list
},
//在list.vue里面點(diǎn)擊下拉選項(xiàng)的時(shí)候觸發(fā) 給state.title賦值
changeTitleMutation(state, title) {
state.title = title
},
//selectinput.vue里面點(diǎn)擊input的時(shí)候觸發(fā) 給state.isShow賦值
toggleShow(state, isShow) {
state.isShow = isShow
}
},
//異步的方法
actions: {
//在list.vue里面created生命周期里面觸發(fā)
getListAction({ commit }) {
axios.get('/mock/5afd9dc0c088691e06a6ab45/example/dataList')
.then((res) => {
commit('changeListMutation', res.data) //調(diào)用mutations下面的changeListMutation方法并且傳值過去
})
.catch((error) => {
console.log(error)
})
}
}
})
// 觸發(fā)異步里面的方法是用 this.$store.dispatch('這里是方法名')
// 觸發(fā)同步里面的方法是用 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數(shù)據(jù)和mutations中的方法映射到組件中
export default {
//vue 生命周期(created)在實(shí)例創(chuàng)建之后誓沸,在數(shù)據(jù)初始化之前被調(diào)用
created(){
this.$store.dispatch('getListAction') //調(diào)用vuex 中的 getListAction異步方法
},
//計(jì)算state數(shù)據(jù)
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數(shù)據(jù)并賦值綁定到 value上面 computed 里面的方法名其實(shí)就是相當(dāng)于 data里面的數(shù)據(jù),可以用this.getTitle 去訪問
getTitle(){
return this.$store.state.title
},
// 初始化控制下拉選項(xiàng)顯示隱藏的狀態(tài)壹粟,如果isShow是false 則不限是下拉菜單拜隧,默認(rèn)是false
initShow(){
return this.$store.state.isShow
}
},
methods:{
//點(diǎn)擊input的時(shí)候調(diào)用該方法,這個(gè)方法去觸發(fā)mutations下面的toggleShow趁仙,去改變isShow的狀態(tài)洪添,默認(rèn)是isShow等于false, 然后在點(diǎn)擊的時(shí)候去改變isShow 等于true , !this.initShow就是true,如果是true的話雀费,下拉選項(xiàng)才能出來,并將改變過后的值傳給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中除了內(nèi)置的指令(v-show,v-model)還允許我們自定義指令
想要創(chuàng)建自定義指令盏袄,就要注冊指令(以輸入框獲取焦點(diǎn)為例) 注意:autofocus 在移動版 Safari 上不工作
一忿峻、注冊全局指令:
// 注冊一個(gè)全局自定義指令 `v-focus`
Vue.directive('focus', {
// 當(dāng)被綁定的元素插入到 DOM 中時(shí)……
inserted: function (el,binding) {
// 當(dāng)前指令綁定的dom元素
//console.log(el);
// 指令傳入的參數(shù)薄啥、修飾符、值 v-指令名稱:參數(shù).修飾符=值
// console.log(binding)
// 聚焦元素
el.focus()
}
})
二逛尚、注冊局部指令: 組件中也接受一個(gè) directives 的選項(xiàng)
directives: {
focus: {
// 指令的定義
inserted: function (el) {
el.focus()
}
}
}
使用也很簡單:直接在元素上面使用v-focus即可:
<input type="text" v-focus/>
下面再舉一個(gè)自定義指令的小例子:拖拽
Vue.directive('drag', {
// 當(dāng)指令綁定到元素上的時(shí)候執(zhí)行
bind(el, binding) {
// console.log('bind');
// 當(dāng)前指令綁定的dom元素
//console.log(el);
// 指令傳入的參數(shù)垄惧、修飾符、值 v-指令名稱:參數(shù).修飾符=值
// 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彈窗后如何禁止?jié)L動條滾動赘艳?
/***滑動限制***/
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='';//出現(xiàn)滾動條
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) //在跳出彈窗的時(shí)候
toggleBody(0) //彈窗消失的時(shí)候
超長的頁面怎么辦呢
上面直接限制body固然有效,但如果一個(gè)頁面很長很長克握,超出了100vh蕾管,而我正好滾到中間時(shí)彈出彈窗。此時(shí)若直接限制body的overflow: hidden則會讓頁面一下彈到頂部菩暗,顯然不是好的做法掰曾。那么,又該怎么做呢停团?
對移動端旷坦,可以引入touch-action,限制為none佑稠,在彈窗消失時(shí)再變回auto秒梅。但ios的safari上不支持該屬性(可以去caniuse上查查,起碼2018.11的時(shí)候還不支持)舌胶。如果我們的app在ios上用的是safari內(nèi)核捆蜀,就起不到效果了。
這時(shí)候幔嫂,就需要結(jié)合event.preventDefault屬性來用了辆它。注意在綁定addEventListener的時(shí)候,需要多傳一個(gè)options履恩,強(qiáng)調(diào)這個(gè)事件不是passive的锰茉,否則谷歌等新版瀏覽器會報(bào)錯。同時(shí)最好也指定capture: true切心,這樣可以早點(diǎn)禁止該事件飒筑。
報(bào)錯是Unable to preventDefault inside passive event listener due to target being treated as passive.。這是因?yàn)楣雀鑿腸hrome51之后引入的新優(yōu)化绽昏。事實(shí)上扬霜,谷歌建議一般情況下,將 passive 標(biāo)志添加到每個(gè)沒有調(diào)用 preventDefault() 的 wheel而涉、mousewheel著瓶、touchstart 和 touchmove 事件偵聽器。但是啼县,對于這種禁止了默認(rèn)事件的eventListener材原,在這種情況下沸久,反而是要強(qiáng)調(diào)它不是消極監(jiān)聽的。因?yàn)闈L動都不能滾了余蟹,無所謂什么優(yōu)化了卷胯。
代碼如下(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 的腳手架項(xiàng)目模板有browserify 和 webpack , 現(xiàn)在自己在用的是webpack , 官網(wǎng)給出了兩個(gè)模板: webpack-simple 和 webpack 兩種。兩種的區(qū)別在于webpack-simple 沒有包括Eslint 檢查功能等等功能威酒,普通項(xiàng)目基本用webpack-simple 就足夠了.
搭建官方項(xiàng)目模板步驟:
1窑睁、npm install vue-cli (安裝vue-cli ) 有的時(shí)候有看到其它兩種寫法: --save-dev 和 --save的寫法。這兩個(gè)有一定的區(qū)別葵孤,我們都知道package.json 中有一個(gè) “dependencies” 和 “devDependencies” 的担钮。dependencies 是用在開發(fā)完上線模式的,就是有些東西你上線以后還需要依賴的尤仍,比如juqery , 我們這里的vue 和 babel-runtime(Babel 轉(zhuǎn)碼器 可以將ES6 轉(zhuǎn)為ES5 )箫津, 而devDependencies 則是在開發(fā)模式執(zhí)行的,比如我們?nèi)绻枰惭b一個(gè)node-sass 等等宰啦。有的時(shí)候看到package.json中安裝的模塊版本號前面有一個(gè)波浪線苏遥。例如: ~1.2.3 這里表示安裝1.2.x以上版本。但是不安裝1.3以上赡模。
2田炭、vue init webpack-simple yourdemoname 下載一個(gè)webpack-simple項(xiàng)目,這里的webpack-simple 是固定的漓柑,也就是官網(wǎng)的項(xiàng)目模板教硫。youdemoname 這個(gè)是你自己項(xiàng)目的名字。 執(zhí)行這個(gè)步驟以后欺缘。就會彈出詢問 “項(xiàng)目名稱..項(xiàng)目描述“等等問題 直接按照提示操作栋豫。這個(gè)時(shí)候?qū)?yīng)的項(xiàng)目目錄下就出現(xiàn)剛剛建立的項(xiàng)目了挤安。
3谚殊、我們還需要把項(xiàng)目的依賴下載下來。使用命令: cd youdemoname 然后執(zhí)行npm install 就可以了蛤铜,這個(gè)時(shí)候你的項(xiàng)目中有多了一個(gè)node_modules 目錄
4嫩絮、使用"npm - run - dev" 命令來運(yùn)行項(xiàng)目 "npm-run-bulid" 來執(zhí)行發(fā)布,會自動生成dist文件