Vue常见问题FAQ

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_23876873/article/details/83064111

目录

 

FAQ

资源

初始化工程

工程目录

ie兼容包

webpack打包img

定义组件过滤器

全局过滤器和加载

深度监听/对象以及内部属性的监听

改变数组的值

常见事件监听

原生事件监听

路由器简单使用

嵌套路由的使用/二级页问题

组件定义.vue文件

组件注册

全局注册

局部注册

import路径中包含的 "@"

组件prop双向绑定

第一种方式/自定义事件

第二种方式/.sync修饰符

绑定Style中带有图片/图片访问不到

属性有多个属性值

绑定style

访问根实例vue/$root

自定义事件在程序中监听/组件间的通行

vue打包路径问题/build不在服务器中打开空白/cordova打开为空白

vue中使用scss、sass

屏幕适配问题/设计稿宽计算

阿里图标库

vscodeTab键不生效/emmet

操作dom/获取到的数据不正确/$nextTick()

返回上一页

各种数据url链接/路由跳转路径path/全局访问

局域网访问工程/非本机访问

缓存/keep-alive/对router使用keep-alive

缓存滚动的位置/scrollTop

深度/穿透/子组件样式/deep/>>>

组件缓存/keep-alive/路由下的状态保存

组件全局的自动注册/自动加载

返回上一页/回退

全局https网络请求/this.$https调用

子组件插槽问题?

方式1:个人

方式2:官网

路由守卫

1.路由器守卫

2.路由全局守卫

3.组件内守卫

4.守卫共享

跨域

异步路由/路由懒加载

父组件主动获取子组件的数据和方法

子组件主动获取父组件的数据和方法在子组件里面通过

子组件无法获取到父传入的props

this.$el.offsetHeight获取为0

动态加载图片/static

为路由器加入动画/animate/transition

基本操作

注意:没有进入动画

加上position:absolute后,动画执行过程中抖动。

keep-alive没必要添加include

vue计算属性get和set的作用和使用场景

遮罩处理


 

FAQ

资源

vueApi https://cn.vuejs.org/v2/api/

vue指导 https://cn.vuejs.org/v2/guide/ 

devTools调试 https://github.com/vuejs/vue-devtools

router路由 https://router.vuejs.org/zh/

vuex https://vuex.vuejs.org/zh/

srr服务器渲染 https://ssr.vuejs.org/zh/

初始化工程

//vue-cli是2.X版本
npm install -g vue-cli

vue init webpack

工程目录

目录"store"为Vuex相关的文件

ie兼容包

npm i -s 'babel-polyfill'
import "babel-polyfill";------>注意import位置尽量靠前,必须vuex前面

webpack打包img

//把图片包含进来
import png1 from '@/assets/666320.png';
import png2 from '@/assets/6pro320-220.png';
import png3 from '@/assets/pc-320-220-mi8se.png';
import png4 from '@/assets/ds50.png';
import png5 from '@/assets/ds320_220.png';


//使用
{   
          shopname: "手机",
          list:[
               png4,
               png5,
         ]
},

定义组件过滤器

filters: {

  numFilter(value) {

  // 截取当前数据到小数点后三位

    let transformVal = Number(value).toFixed(3)

    let realVal = transformVal.substring(0, transformVal.length - 1)

    // num.toFixed(3)获取的是字符串

    return Number(realVal)

  }

}

全局过滤器和加载

过滤器定义"./filter/custom.js"

function numFilter(value) {

      // 截取当前数据到小数点后三位

        let transformVal = Number(value).toFixed(3)

        let realVal = transformVal.substring(0, transformVal.length - 1)

        // num.toFixed(3)获取的是字符串

        return Number(realVal)

      }
/**
 * 
 * @param {*} Vue 
 * @param {*} custom 
 */
function init(Vue){
        Object.keys(this).forEach(key => {
            if(key!=='init') {
                Vue.filter(key, this[key]);
                // console.log(key,custom[key])
            }
          
        })
    }
export {numFilter ,init}

加载、使用"main.js"

import * as custom from './filters/custom'
custom.init(Vue);//加载过滤器

深度监听/对象以及内部属性的监听

深度监听
watch: {
  obj: {
    handler(newName, oldName) {
      console.log('obj.a changed');
    },
    immediate: true,
    deep: true
  }
}
第一个handler:其值是一个回调函数。即监听到变化时应该执行的函数。
第二个是deep:其值是true或false;确认是否深入监听。(一般监听时是不能监听到对象属性值的变化的,数组的值变化可以听到。)
第三个是immediate:其值是true或false;确认是否以当前的初始值执行handler的函数。

改变数组的值


var target  = {};
Object.assign(target,item,{checked:val})                 
this.$set(this.list, index, target);

常见事件监听

@click  |  @change  当前元素失去焦点并且元素的内容发生改变而触发此事件  |  @blur  |  @focus 

原生事件监听

@click.native  |  @mouseenter.native  |  @dbclick.native 。。。mousedown mouseup mouseover mouseout 

路由器简单使用

./router/index.js

import Vue from 'vue'
import Router from 'vue-router'
// import HelloWorld from '@/components/HelloWorld'
import index from '@/views/index'
// import Code404 from '@/components/Code404'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      // name: 'index',
      component: index
    },
    {
      path: '*',
      // name: 'index',
      redirect: '/'
      // component: Code404
    }
  ]
})

main.js

import router from './router'
//放入根vue
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

嵌套路由的使用/二级页问题

// router/index.js
import Index from '@/views/index'
import MainPage from '@/views/MainPage'
import My from '@/views/my'
import Order from '@/views/order'
import ClasstifyAll from '@/views/ClasstifyAll'
import Search from '@/views/search'
import url from '../global/url'
let nav = url.navigator;
export default new Router({
  routes: [
    {
      path: '/',
      // name: 'HelloWorld',
      redirect:'/mainpage/index'
    },
    /**
     * mainpage页,主页
     */
    {
      path: nav.mainPage.selfpath,
      name: 'MainPage',
      component: MainPage,
      children:[
        {
          path: nav.mainPage.children.indexPath,
          name: 'index',
          component: Index
        },
        {
          path: nav.mainPage.children.myPath,
          name: 'my',
          component: My
        },
        {
          path: nav.mainPage.children.orderPath,
          name: 'order',
          component: Order
        }
      ]
    },
    /**
     * classtifyAll页,全部分类页
     */
    {
      path: nav.classtifyAll,
      name: 'classtifyAll',
      component: ClasstifyAll,
    },
    {
      path: nav.searchPath,
      name: 'Search',
      component: Search,
    }
    
  ]
})

主页中的子分页(index、my、order等)需要以子页形式存在,二级页是跟主页mainpage同级的,这样在主页子分页中点击了二级页链接,才能使得二级页全屏。

组件定义.vue文件

<template>
    <div class="root">
        
    </div>
    
</template>

<script>

export default {
  props:['itemObj'],
  data() {
    return {
      maxNum:99,
    };
  },
  methods:{
      del(){
          this.list.splice(this.index,1);
      },
  
  },
  computed: {
    totalPrice() {
      return this.itemObj.num * this.itemObj.price;
    }
  },
    watch:{
    //   checked:function(val){

    //   }
  },
  filters: {

}

};
</script>

<style scoped>
.root{
  display: flex;
  width: 1200px;
  /* background-color: #234566; */
  color: #000;
  font-size: 12px;
  padding: 10px;
  border: 1px solid #ddd;
}
</style>

组件注册

全局注册

Vue.component('my-component-name', {
  // ... 选项 ...
})

局部注册

import ComponentA from '@/components/ComponentA.js'
//或者
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }


new Vue({
  el: '#app',
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

import路径中包含的 "@"

例如:"@/components/xxx.js" @在webpack中有定义,表示src目录

组件prop双向绑定

第一种方式/自定义事件

父组件中

<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>

子组件中

this.$emit('update:title', newTitle)

第二种方式/.sync修饰符

父组件中

<XXX v-bind:showFlag.sync="showFlag" >

子组件中

this.$emit('update:showFlag', false);

关闭eslint代码检测

在config/index.js目录下

// useEslint: true,
   useEslint: false,

绑定Style中带有图片/图片访问不到

//使用require或者import导入,只有这样,webpack打包才会导入图片,不然会出现图片访问不到
return {
                backgroundImage:'url(' + require('../../assets/2.jpg') + ')',
                backgroundPosition:this.bigDivx+'%' +' '+this.bigDivy+'%'
}

属性有多个属性值

// 空格隔开
backgroundPosition:this.bigDivx+'%' +' '+this.bigDivy+'%'

绑定style

<div class="zhezhao" :style="jisuanzuobiao" v-show="zhezhaoShow" ></div>

computed:{
        jisuanzuobiao(){
            let min = 0;
            let max = 500;
            let zhezhaoY =  this.bianjie(this.zhezhaoY);
            let zhezhaoX =  this.bianjie(this.zhezhaoX);
            return {top:zhezhaoY+'px', left:zhezhaoX+'px'}
        }
}

访问根实例vue/$root

通过this.$root

1.可以作为全局的store使用,放入共享的数据

2.全局bus,自定义事件监听和注册,达到各个组件间的通信

自定义事件在程序中监听/组件间的通行

$emit ("eventName",params) 发送一个事件

$on(eventName, eventHandler) 侦听一个事件

$once(eventName, eventHandler) 一次性侦听一个事件

$off(eventName, eventHandler) 停止侦听一个事件

通过根vue完成通信操作(this.$root),

vue打包路径问题/build不在服务器中打开空白/cordova打开为空白

config/index.js文件修改一下位置

vue中使用scss、sass

npm install node-sass --save-dev   //只需要这句
npm install sass-loader --save-dev
<style lang="scss" scoped>

屏幕适配问题/设计稿宽计算

//在需要使用的文件import 'vw_base.js'
//375为设计稿宽,一般宽为750,可以改成实际设计稿宽
$vm_base: 375; 
@function vw($px) {
    @return ($px / 375) * 100vw;
}

阿里图标库

在main.js中引入iconfont.css样式

import 'xxx/xxx/xxx/iconfont.css'

vscodeTab键不生效/emmet

文件-首选项-设置
v1.5版本后配置如下 

"emmet.triggerExpansionOnTab": true,
"emmet.includeLanguages": {
"vue-html": "html",
"vue": "html"
}

 

设置代码

"emmet.syntaxProfiles": {
  "vue-html": "html",
  "vue": "html"
}

操作dom/获取到的数据不正确/$nextTick()

修改data中的数据,再到dom中获取。这时候可能获取到数据为上一次的数据,dom还没有完成刷新。使用this.$nextTick(funtion(){})

返回上一页

$router.go(-1)

各种数据url链接/路由跳转路径path/全局访问

1写入一个文件中统一管理

2写入在根vue中,统一'this.$url'调用,

3或者在main.js中   "var url = 导入的url"  变为全局

//global/url.js

var url = {
  index: '/data/index',
  indexSwiper: '/data/indexSwiper',
  indexClass1: '/data/indexClass1',
  indexClass2: '/data/indexClass2',

  listing: '/data/listing',

  /**
   * 导航路径
   */
  navigator: {
    mainPage: {
      selfpath: '/mainpage',
      children:{
        indexPath: 'index',
        indexFullPath:'/mainpage/index',
        orderPath: 'order',
        orderFullPath:'/mainpage/order',
        myPath: 'my',
        myFullPath:'/mainpage/my',
      }
      
    },
    classtifyAll:'/classtifyall',
    searchPath:'/search'
  }

}
export default url;

局域网访问工程/非本机访问

缓存/keep-alive/对router使用keep-alive

<keep-alive>
      <router-view></router-view>
</keep-alive>

缓存滚动的位置/scrollTop

对组件A先使用keep-alive后,在组件A内缓存div滚动位置

<template>
    <div class="index-root"  id="mainContent">
        ........
    </div>
</template>
data: function() {
    return {
      offsetTop:0,
    };
  },
activated() {
  // keep-alive组件 页面进入的时候设置滚动高度
    document.getElementById("mainContent").scrollTop = this.offsetTop;
  },
beforeRouteLeave(to, from, next) {
   //组件离开的时候,获取页面滚动高度 
   this.offsetTop = document.getElementById('mainContent').scrollTop;
   next() 
},

深度/穿透/子组件样式/deep/>>>

.header{
    
    position: relative;
    .mint-header{
        height: 16vw;
    /deep/ .mintui.mintui-back{ ////完成对子组件中类.mintui.mintui-back穿透
                font-size: 6vw;
            }
    }

组件缓存/keep-alive/路由下的状态保存

注意:对组件A使用时,包含A组件的父B不能保存状态,B被路由替换,则A也是无效的。

app.vue中使用keep-alive包裹router-view

<keep-alive include="mainpage" > //<----------在组件中取名字name='mainpage',这可以激活状态保    
                                    存。相对于的不包含属性 exclude=''
      <router-view></router-view>
</keep-alive>

router/index.js路由表中

/**
     * mainpage页,主页
     */
    {
      path: nav.mainPage.selfpath,
      name: 'mainpage',//  注意<---------App.vue中include='mainpage'不是对应这个name
      component: MainPage,
      children:[
        {
          path: nav.mainPage.children.indexPath,
          name: 'index',
          component: Index
        },
        {
          path: nav.mainPage.children.myPath,
          name: 'my',
          component: My
        },
        {
          path: nav.mainPage.children.orderPath,
          name: 'order',
          component: Order
        }
      ]
    },

在Mainpage.vue中定义name

export default {
  name: 'mainpage',// <-------定义名字与<keep-alive>中include保持一致
  data: function() {
    return {
      nameArray: ["首页", "附近", "发现", "订单", "我的"],
      selected: "首页",
    //   offsetTop:0,
    };
  },
......

组件全局的自动注册/自动加载

下面代码会自动全局注册'/components'下的组件,

注意:下列代码依赖'lodash'

/**
 * 自动全局注册componentS下的组件
 */
import {camelCase,upperFirst} from 'lodash'
import Vue from 'vue';
const requireComponent = require.context(
  '@/components', //当前基础组件相对与main.js的相对位置
  false, //是否查询子目录
  /\.(vue|js)$/ 
)
requireComponent.keys().forEach(fileName => {
  const componentConfig = requireComponent(fileName) //获取组建配置
  const componentName = 
      fileName.replace(/^\.\/(.*)\.\w+$/, '$1') // 剥去文件名开头的 `'./` 和结尾的扩展名
  
  // const componentName = upperFirst( //获取组件的 PascalCase 命名
  //   camelCase(
  //     fileName.replace(/^\.\/(.*)\.\w+$/, '$1') // 剥去文件名开头的 `'./` 和结尾的扩展名
  //   )
  // )
      console.log(componentName)
  // 全局注册组件
  Vue.component(
    componentName,
    componentConfig.default || componentConfig
  )
})

返回上一页/回退

<div @click="$router.go(-1)">
       <slot></slot>
</div>

全局https网络请求/this.$https调用

import axios from 'axios';
import url from '../global/url'
var https={
    listing:{
        request(){
            return axios.get(url.listing).then((res)=>res.data)
        }
    },
    registerInVue(Vue){
        Vue.prototype.$https = https
    }
}
export default https;

main.js

/**https访问数据,为index.js文件
 * 可以在子vue中通过 this.$url调用
 */
import https from  './https/index.js'
https.registerInVue(Vue);

子组件插槽问题?

父中传入插槽所需内容,而内容中,有多个tag需要处理点击事件,但是传入的dom结果作用域是父的,然而需要传入的dom响应子组件内的方法

注意:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。--官网

方式1:个人

mount()方法中,查找(querySelectAll(),)到需要点击事件的node,并对node添加事件

方式2:官网

作用域插槽

提示:将子组件作用域方法值数组传出给

路由守卫

1.路由器守卫

2.路由全局守卫

3.组件内守卫

4.守卫共享

router/guard.js

import store from '../store/index'
import {Toast} from 'mint-ui'
var guard = {
  /**
   * user守卫
   */
  userGuard:(to, from, next) => {
    if(store.getters['user/loginState']){
      next();
    }else{
      Toast({
        message: '请先登录',
        position: 'bottom',
        duration: 3000
    });
      // next({ path: '/login' })
      next(false)
    }
  }
}

  export default guard;

router/index.js

import guard from './guard.js'//------->导入守卫处理函数

export default new Router({
  mode:'history',
  routes: [
.......
    /**
     * setting 个人设置页面
     */
    {
      path:nav.settingPath,
      name:"setting",
      component:Setting,
      beforeEnter: guard.userGuard//------>引用共享函数guard.userGuard
                                  //------>路由器守卫
    },
     /**
     * my-collection 我的收藏页面
     */
    {
      path:nav.myCollectionPath,
      name:"my-collection",
      component:MyCollection,
      //路由守卫
      beforeEnter: guard.userGuard//------>引用共享函数guard.userGuard
    },

.......
  ]
})

跨域

1.找到 vue项目目录 > config > index.js

dev: {
    env: require('./dev.env'),
    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {
     '/api': {  // 这里的 /api 是固定的 
          target: 'http://xxxx.cn', //你的服务器地址
          changeOrigin: true, //改变源 
          pathRewrite: { 
                '^/api': 'http://xxxx.cn'   // 意为 用 /api 这个字符代替 后面的地址 在访问接口时直接写 /api ...... 就好了,这个/api可以是任意的字符。
           } 
      } 
    },
……
}

2.在开发过程中请求数据时直接用 /api (与上面pathRewrite定义的字符保持一致)开头 +后面的接口。

axios.get('/api/xxx.json', function (res) { 
   console.log(res) 
}

异步路由/路由懒加载

以函数返回值形式导入,函数未执行

const Foo = () => import('./Foo.vue')

将导入函数放进router对象,按需求,异步导入执行导入函数

const router = new VueRouter({
  routes: [
    { path: '/foo', component: Foo }
  ]
})

把组件按组分块(可不做)

const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')

注意

如果您使用的是 Babel,你将需要添加 syntax-dynamic-import (当前新建工程已经存在改插件)插件,才能使 Babel 可以正确地解析语法。

父组件主动获取子组件的数据和方法

1.调用子组件的时候 定义一个ref

<t-tab ref="xxx"></headerchild>//定义的名字xxx,父可以通过xxx调用t-tab
1
2.在父组件里面通过

this.$refs.xxx.属性----------->xxx同上
this.$refs.xxx.方法----------->xxx同上


子组件主动获取父组件的数据和方法在子组件里面通过

this.$parent.属性
this.$parent.方法

 

子组件无法获取到父传入的props

1.父在mounted()中获取的数据,修改需改绑定在props上的值,

2.错误的原因:子不可以在mounted中去获取,不然报错,因为此时子已经mounted,父的mounted后修改数据在传入晚了。

3.子应该在watch中完成获取数据和操作。

this.$el.offsetHeight获取为0

原因1:当前vue被隐藏v-show=false;,获取为0。所以不能再初始化中运行获取函数。

原因2:v-show=true;需要在$nextTick(function(){})中获取,并且必须重新运行获取函数。

动态加载图片/static

将图片放入static中就行,所有动态图片都可以加载。如果放在assets中,就需要import或者require加载

为路由器加入动画/animate/transition

需要安装animate.css动画库

基本操作

1.html结构

<template>
  ...
      <transition
          name="custom-classes-transition"   ------------>表示自定义动画
          :enter-active-class="transitionInName"------------->表示进入时的动画
          :leave-active-class="transitionOutName"------------>表示出去时的动画
            >
    <keep-alive include="mainpage">
    <!-- <keep-alive > -->
        <router-view></router-view>
    </keep-alive>
      </transition>
  ...
</template>

2.

data() {
    return {
    transitionInName:'animated slideInRight',
    transitionOutName:'animated slideOutLeft'
    }
  },

。。。。。。。

watch: {
  '$route' (to, from) {
    if(this.$global.isRouterForward){------------->关键点
        //前进动画
      this.transitionInName =  'animated slideInRight'
      this.transitionOutName = 'animated animated-delay slideOutLeft' 
    }else{
        //回退动画
      this.transitionInName = 'animated  slideInLeft' 
      this.transitionOutName = 'animated animated-delay slideOutRight'
    }
    // 默认为true
    this.$global.isRouterForward  = true;------------->关键点
  }
}

global.js

isRouterForward : true,

back的监听,需要修改标志位isRouterForward =false;表示执行回退动画

this.$global.isRouterForward = false;
this.$router.back()

注意:没有进入动画

因为:进入的组件出去组件下方,对于手机界面,必然看不见。

.animated{//在
  animation-duration: 0.5s;
  position:absolute----------->动画不执行、不显示,<transition>加上absolute
}

或者官方解决办法

给<transition>加属性 mode='in-out'。mode='out-in'执行效果难受,先完全出去,在执行进入动画

或者(。。。。。)只要前进时:右侧进入动画,后退时:右侧出去动画

加上position:absolute后,动画执行过程中抖动。

因为在执行动画过程中,父的宽出现的变化。解决:给每一个route(每一页)的根加上定宽。

keep-alive没必要添加include

我以为会出现Android和react那种情况,每次路由push一个页A,就会是一个新的页A,导致栈中出现好多个A页。事实上,栈中只会有一个页A被重复弹出到栈顶。
 

vue计算属性get和set的作用和使用场景

<input v-model="sosoInput" type="text" :placeholder="tips">
。。。。。。。
computed: {
    sosoInput: {
        // getter
        get: function () {
            return this.$store.state.common.searchKeyword;------->getter从store获取数据
        },
        // setter
        set: function (newValue) {
            this.sosoInputValue = newValue;------->set将数据放入data的sosoInputValue中
        }
    }
},
。。。。。。。

例子中,没有将数据返回到store,因为需要最后‘确认’点击确定的按钮,才会通过store.commit()提交出去

遮罩处理

处理遮罩问题,当点击遮罩div的子,也会触发closePopup()函数,

办法1.在vue中加上'.self'修饰符

<div class="zhezhao" @click.self="closePopup">

办法2.事件目标对象===.zhezhao对象,即点击触发在div.zhezhao上,而非div.zhezhao的子类

if($event.target===this.$el.querySelector('.zhezhao')){

猜你喜欢

转载自blog.csdn.net/qq_23876873/article/details/83064111