Vue2.x 项目踩坑笔记

设置路径别名、全局引入scss文件

给文件设置路径别名,方便在组件内引入文件,不必写太长的路径名称。
全局引入scss文件,不必在单个组件内再次引入,可以直接使用。例如:全局变量、px2rem函数等文件

const path = require('path');

function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  css: {
    loaderOptions: {
      // 给 sass-loader 传递选项
      // 默认情况下 `sass` 选项会同时对 `sass` 和 `scss` 语法同时生效
      // 因为 `scss` 语法在内部也是由 sass-loader 处理的
      // 但是在配置 `data` 选项的时候
      // `scss` 语法会要求语句结尾必须有分号,`sass` 则要求必须没有分号
      // 在这种情况下,我们可以使用 `scss` 选项,对 `scss` 语法进行单独配置
      scss: {
        additionalData: `
        @import "assets/styles/common/variables.scss";
        @import "assets/styles/common/px2rem.scss";
        @import "assets/styles/iconfont/iconfont.scss";
                        `
      },
    }
  },
  chainWebpack: config => {
    config
      .resolve.alias
      .set('@', resolve('src'))    //配置src目录别名
      .set('assets', resolve('src/assets'))    //配置src/assets目录别名
      .set('components', resolve('src/components'));    //配置src/components目录别名
  }
}

动态路由加载

为了解决项目进入时候白屏bug

const router = new VueRouter({
  routes: [
    { 
      path: '/',
      name: 'index',
      component: () => import("@/views/Index.vue")
    }
  ]
});

prop 命名方案(驼峰式和串联式)

HTML 属性名称对大小写不敏感,因此浏览器会将所有大写字符解释为小写字符。也就是说。当你在你 DOM 模板中书写 prop 时,你应当将驼峰式(camelCase)转写为等价的(连字符分割的)串联式(kebab-case):

Vue.component('blog-post', {
  // 在 JavaScript 中使用驼峰式(camelCase)
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})
<!-- 在 HTML 中使用串联式(kebab-case) -->
<!-- 向 props传递一个静态值 -->
<blog-post post-title="hello!"></blog-post>

还可以通过 v-bind 给 props 分配动态值,就像这样:

<!-- 动态分配一个变量对应的值 -->
<blog-post v-bind:title="post.title"></blog-post>

<!-- 动态分配一个复合表达式对应的值 -->
<blog-post v-bind:title="post.title + ' by ' + post.author.name"></blog-post>

再次申明,如果是在使用字符串模板的场景,则没有这些限制。

路由跳转拼接参数

<router-link :to="`/topic/${item.id}`" :title="item.id">
</router-link>

beforeRouteEnter不能通过this访问组件实例,但是可以通过 vm 访问组件实例

  // 进入路由
  beforeRouteEnter(to, from, next) {
    // beforeRouteEnter不能通过this访问组件实例,但是可以通过 vm 访问组件实例
    next((vm) => {
      console.log('beforeRouteEnter',this);
      // 先解绑,防止多次执行
      vm.eventBus.$off('aaa');
    });
  },

滚动到页面底部加载数据

在生命周期mounted中进行监听滚动:

mounted () {
  window.addEventListener('scroll', this.scrollToTop)
},

在方法中定义监听滚动执行的方法:

scrollToTop() { 
  var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
  console.log(scrollTop)
},

记得在离开当前路由解绑scroll事件

beforeRouteLeave(to, form, next){
  window.removeEventListener('scroll',this.scrollToTop);
  next();
}

渲染异步数据,浏览器报错

<img class="avatar" v-if="item.author" :src="item.author.avatar_url" />

如果异步请求的数据出现报错,可以通过v-if判断一下,注意不能使用v-show,v-show的机制是加载后,根据条件判断是否显示,当然渲染组件的时候也可以这样做

菜单切换获取对应参数并触发请求数据

参数或查询的改变并不会触发进入/离开的导航守卫,但是可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。

父组件修改子组件变量

有时候父组件触发的实际需要修改子组件内的data数据,这时候请给子组件绑定ref属性,在父组件触发时间内修改子组件的变量

<template>
  <div class="page">
    <Header 
      :page-type="getType(searchKey.tab)"
      ref="head"
      :fix-head="true"
      :need-add="true"/>

    <PostList
      :items="postList"
      ref="postlist"
      v-if="postList.length>0" />

    <GoToTop
      ref="gototop"
      :flag="flag"/>
  </div>
</template>

  <!-- 在方法中调用this.$refs即可获取到ref绑定的组件或者DOM -->
  // 收起菜单
  this.$refs.head.show = false;

子组件内调用父组件的方法

日常开发者经常会遇到多层级嵌套组件,并且需要在最里面的子组件触发最外面的父组件事件,这种情况下当然最好的做法其实还是使用Vuex进行管理,如果你的项目使用Vuex进行管理了,后面就没必要看了。Vuex的相关内容请自行百度查阅。

第一种:直接在子组件中通过this.$parent.event来调用父组件的方法
这种方式可以无限级别的向上找父级,例如:this.$parent.$parent.$parent.$parent.event,子组件也不需要props属性接收父组件的方法,但是多层级的时候容易搞乱层级,.$parent太多了

父组件

<template>
  <div>
    <child></child>
  </div>
</template>
<script>
  import child from '@/components/child';
  export default {
    components: {
      child
    },
    methods: {
      fatherMethod() {
        console.log('fatherMethod');
      }
    }
  };
</script>

子组件

<template>
  <div>
    <button @click="childMethod">点击</button>
  </div>
</template>
<script>
  export default {
    methods: {
      childMethod() {
        this.$parent.fatherMethod();
      }
    }
  };
</script>

第二种:父组件把方法传入子组件中,在子组件里直接调用这个方法
父组件内调用子组件的时候需要显示的传入方法(注意:这里是用的:传入方法),子组件还需要通过props接收父组件传来的方法,否则也不可以调用,另外就是多层级的时候需要一层一层的往下传,这时候就比较繁琐

父组件

<template>
  <div>
    <child :fatherMethod="fatherMethod"></child>
  </div>
</template>
<script>
  import child from '@/components/child';
  export default {
    components: {
      child
    },
    methods: {
      fatherMethod() {
        console.log('fatherMethod');
      }
    }
  };
</script>

子组件

<template>
  <div>
    <button @click="childMethod">点击</button>
  </div>
</template>
<script>
  export default {
    props: {
      fatherMethod: {
        type: Function,
        default: null
      }
    },
    methods: {
      childMethod() {
        if (this.fatherMethod) {
          this.fatherMethod();
        }
      }
    }
  };
</script>

第三种:子组件里用$emit向父组件触发一个事件名(注意:这里是用的@传入方法),父组件监听这个事件名就行了,子组件不需要通过props接收父组件传来的方法,否则也不可以调用,这种最好向上传递一层父级,否则也需要层层传递

父组件

<template>
  <div>
    <child @fatherMethod="fatherMethod"></child>
  </div>
</template>
<script>
  import child from '@/components/child';
  export default {
    components: {
      child
    },
    methods: {
      fatherMethod() {
        console.log('fatherMethod');
      }
    }
  };
</script>

子组件

<template>
  <div>
    <button @click="childMethod">点击</button>
  </div>
</template>
<script>
  export default {
    methods: {
      childMethod() {
        this.$emit('fatherMethod');
      }
    }
  };
</script>

第四种:使用$emit$on 配合传递事件
如果仅仅是父子一层,传递事件就使用第三种就可以了,如果多层传递或者是兄弟组件,还可以使用$emit$on 配合,其原理就是new 一个vue实例,然后在父子组件引入这个实例,这样在子组件触发的事件就会在父组件监听到了,这就是网上说的eventBus。

新建一个bus.js

import Vue from 'vue';
export default new Vue();

在父子组件分别引入这个bus.js

import eventBus from 'bus';

在子组件触发事件

  export default {
    methods: {
      childMethod() {
        eventBus.$emit('fatherMethod');
      }
    }
  };

在父组件监听事件

  export default {
    mounted() {
      //如果出现多次监听,肯定是没有解绑,可以在监听之前解绑,也可以在进入到这个路由或者渲染组件时候解绑一下就好了
      eventBus.$off('fatherMethod');
      eventBus.$on('fatherMethod');
    }
  };

当然如果你觉得建一个bus.js文件多余的话,可以在main.js里把bus加到原型上

//vue原型链挂载总线
Vue.prototype.eventBus = new Vue();

加到原型上的好处就是全局都可以使用了

this.eventBus.$emit('fatherMethod');
this.eventBus.$on('fatherMethod');

引入vant

全局引入组件

import Vue from 'vue';
import { Toast } from 'vant';

Vue.use(Toast);

引入 Toast 组件后,会自动在 Vue 的 prototype 上挂载 $toast 方法,便于在组件内调用。

export default {
  mounted() {
    this.$toast('提示文案');
  },
};

当然也可以在组件内引入

import { Toast } from 'vant';

组件内使用

export default {
  mounted() {
    Toast('第一个 Toast');
  },
};

绑定、解绑节流函数

只要紧记绑定和解绑的事件是同一个就行了,如果不是一个,就单写成一个函数

export default {
  mounted() {
    //执行绑定函数事件
    this.addScrollData();
  },
  methods: {
    // 滚动加载数据
    getScrollData: utils.throttle(function() {
      // 节流getScrollTop
        this.getScrollTop();
    },300),
    // 绑定滚动加载数据事件
    addScrollData(){
      //当然你也可以把上面的这个getScrollData写在这个函数里
      this.getScrollData = utils.throttle(function() {
        // 节流getScrollTop
          this.getScrollTop();
      },300);

      // 绑定滚动加载数据事件
      window.addEventListener('scroll', this.getScrollData);
    }
  },
  //离开路由和组件销毁选择适合项目写一种解绑即可
  // 离开路由之前
  beforeRouteLeave(to, from, next){
    // 解绑scroll事件
    window.removeEventListener('scroll', this.getScrollData);
    next();
  },
  //组件销毁之前
  beforeDestroy () {
    // 解绑scroll事件
    window.removeEventListener('scroll', this.getScrollData);
  },
};

vuex使用

http.js使用store


import Store from '@/store';
import { SHOW_LOADING, HIDE_LOADING } from "@/store/mutationTypes.js";

...
...
...

Store.commit(SHOW_LOADING);

在组件内使用

this.$store.commit(SHOW_LOADING);
原文地址:https://www.cnblogs.com/jiaoshou/p/13841723.html