vue技巧总结_vue 技巧 - CSDN
精华内容
参与话题
  • 本文主要讲述一些vue开发中的实用技巧。 监听组件的生命周期 比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,常规的写法可能如下: // Parent.vue <Child @...

    vue 作为目前前端三大框架之一,对于前端开发者可以说是必备技能。那么怎么系统地学习和掌握 vue 呢?为此,我做了简单的知识体系体系总结,不足之处请各位大佬多多包涵和指正,如果喜欢的可以点个小赞!本文主要讲述一些vue开发中的实用技巧。

    监听组件的生命周期

    比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,常规的写法可能如下:

     // Parent.vue
    <Child @mounted="doSomething"/>

    // Child.vue
    mounted() {
      this.$emit("mounted");
    }
     

    此外,还有一种特别简单的方式,子组件不需要任何处理,只需要在父组件引用的时候通过@hook 来监听即可,代码如下:

    <Child @hook:mounted="doSomething" /> 
    <Child @hook:updated="doSomething" />
     

    当然这里不仅仅是可以监听 mounted,其它的生命周期事件,例如:created,updated 等都可以。

    watch 的初始立即执行

    观察和响应 Vue 实例上的数据变动。类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作。

    但是当 watch 一个变量的时候,初始化时并不会执行,如下面的例子,你需要在 created 的时候手动调用一次

    created() {
      this.getList();
    },
    watch: {
      keyWord: 'getList',
    }
     

    上面这样的做法可以使用,但很麻烦,我们可以添加 immediate 属性,这样初始化的时候就会自动触发(不用再写 created 去调用了),然后上面的代码就能简化为:

    watch: {
      keyWord: {
        handler: 'getList',
        immediate: true
      }
    }

     

    watch 有三个参数

    • handler:其值是一个回调函数。即监听到变化时应该执行的函数
    • deep:其值是 true 或 false;确认是否深入监听。
    • immediate:其值是 true 或 false,确认是否以当前的初始值执行 handler 的函数


     

    路由参数变化组件不更新

    同一path的页面跳转时路由参数变化,但是组件没有对应的更新。

    原因:主要是因为获取参数写在了created或者mounted路由钩子函数中,路由参数变化的时候,这个生命周期不会重新执行。

    解决方案1:watch监听路由

    watch: {
     // 方法1 //监听路由是否变化
      '$route' (to, from) { 
       if(to.query.id !== from.query.id){
                this.id = to.query.id;
                this.init();//重新加载数据
            }
      }
    }
    //方法 2  设置路径变化时的处理函数
    watch: {
    '$route': {
        handler: 'init',
        immediate: true
      }
    }
     

     解决方案2 :为了实现这样的效果可以router-view添加一个不同的key,这样即使是公用组件,只要url变化了,就一定会重新创建这个组件。

    <router-view :key="$route.fullpath"></router-view>

    路由懒加载

    Vue 项目中实现路由按需加载(路由懒加载)的 3 中方式:

    // 1、Vue异步组件技术:
        {
            path: '/home',
            name: 'Home',
            component: resolve => reqire(['path路径'], resolve)
      }

    // 2、es6提案的import()
      const Home = () => import('path路径')

    // 3、webpack提供的require.ensure()
        {
            path: '/home',
            name: 'Home',
            component: r => require.ensure([],() =>  r(require('path路径')), 'demo')
        }
     

    require.context()

    require.context(directory,useSubdirectories,regExp)

    • directory:说明需要检索的目录
    • useSubdirectories:是否检索子目录
    • regExp: 匹配文件的正则表达式,一般是文件名

    场景:如页面需要导入多个组件,原始写法:

    import titleCom from '@/components/home/titleCom'
    import bannerCom from '@/components/home/bannerCom'
    import cellCom from '@/components/home/cellCom'
    components: {
      titleCom, bannerCom, cellCom
    }
    这样就写了大量重复的代码,

    利用 require.context 可以写成:

    const path = require('path')
    const files = require.context('@/components/home', false, /\.vue$/)
    const modules = {}
    files.keys().forEach(key => {
      const name = path.basename(key, '.vue')
      modules[name] = files(key).default || files(key)
    })
    components: modules
     

    递归组件

    • 递归组件: 组件在它的模板内可以递归的调用自己,只要给组件设置 name 组件就可以了。
    • 不过需要注意的是,必须给一个条件来限制数量,否则会抛出错误: max stack size exceeded
    • 组件递归用来开发一些具体有未知层级关系的独立组件。比如:联级选择器和树形控件

     

    <template>
      <div v-for="(item,index) in treeArr"> {{index}} <br/>
          <tree :item="item.arr" v-if="item.flag"></tree>
      </div>
    </template>
    <script>
    export default {
      // 必须定义name,组件内部才能递归调用
      name: 'tree',
      data(){
        return {}
      },
      // 接收外部传入的值
      props: {
         item: {
          type:Array,
          default: ()=>[]
        }
      }
    }
    </script>
     

    清除定时器或者事件监听

    由于项目中有些页面难免会碰到需要定时器或者事件监听。但是在离开当前页面的时候,定时器如果不及时合理地清除,会造成业务逻辑混乱甚至应用卡死的情况,这个时就需要清除定时器事件监听,即在页面卸载(关闭)的生命周期函数里,清除定时器。

    methods:{
      resizeFun () {
        this.tableHeight = window.innerHeight - document.getElementById('table').offsetTop - 128
      },
      setTimer() {
        this.timer = setInterval(() => { })
      },
      clearTimer() {//清除定时器
            clearInterval(this.timer)
        this.timer = null
        }
    },
    mounted() {
      this.setTimer()
      window.addEventListener('resize', this.resizeFun)
    },
    beforeDestroy() {
      window.removeEventListener('resize', this.resizeFun)
      this.clearTimer()
    }
     

    清除定时器的优化:

    方案1:
    data() { return { timer: null // 定时器名称 } },
    this.timer = (() => { // 某些操作 }, 1000)
    beforeDestroy() { clearInterval(this.timer); this.timer = null; }

    方案1有两点不好的地方,引用尤大的话来说就是:

    • 它需要在这个组件实例中保存这个 timer,如果可以的话最好只有生命周期钩子可以访问到它。这并不算严重的问题,但是它可以被视为杂物。
    • 我们的建立代码独立于我们的清理代码,这使得我们比较难于程序化的清理我们建立的所有东西。

        const timer = setInterval(() =>{

              // 某些定时器操作

                  }, 500);

            // 通过$once来监听定时器,在beforeDestroy钩子可以被清除。

          this.$once('hook:beforeDestroy', () => { clearInterval(timer); })

    其他程序化的事件侦听器
    通过 $on(eventName, eventHandler) 侦听一个事件
    通过 $once(eventName, eventHandler) 一次性侦听一个事件
    通过 $off(eventName, eventHandler) 停止侦听一个事件

    自定义路径别名

    我们也可以在基础配置文件中添加自己的路径别名

    resolve: {
        extensions: ['.js', '.vue', '.json'],
        alias: {
          'vue$': 'vue/dist/vue.esm.js',
          '@': resolve('src'),
          'assets': resolve('src/assets')
        }
      }

    然后我们导入组件的时候就可以这样写:
    // import YourComponent from '/src/assets/YourComponent'
    import YourComponent from 'assets/YourComponent'
    这样既解决了路径过长的麻烦,又解决了相对路径的烦恼。

    动态给修改dom的样式

    原因:因为我们在写.vue文件中的样式都会追加scoped。这样针对模板dom中的样式就可以生效,但其生效后的最终样式并不是我们写的样式名,而是编码后的。比如:

    <template>
      <div class="box">dom</div>
    </template>
    <style lang="scss" scoped>
      .box {
        background: red;
      }
    </style>
    vue 将代码转译成如下,所以我们在js中拼接上的dom结构样式并不会生效。

    .box[data-v-11c6864c]{ background:red; }
    <template>
      <div class="box" data-v-11c6864c>dom</div>
    </template>
    解决方法:将要改变的样式写在非scoped样式标签中。

     

    长列表性能优化

    我们应该都知道 vue 会通过 object.defineProperty 对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间。

    所以,我们可以通过 object.freeze 方法来冻结一个对象,这个对象一旦被冻结,vue就不会对数据进行劫持了。

    export default {
      data: () => ({
        list: []
      }),
      async created() {
        const list = await axios.get('xxxx')
        this.list = Object.freeze(list)
      },
      methods: {
        // 此处做的操作都不能改变list的值
      }
    }
    另外需要说明的是,这里只是冻结了 list 的值,引用不会被冻结,当我们需要 reactive 数据的时候,我们可以重新给 list 赋值

    内容分发(slot)

    插槽 slot,也是组件的一块 HTML 模板,这一块模板显示不显示、以及怎样显示由父组件来决定。实际上,一个 slot 最核心的两个问题在这里就点出来了,是显示不显示和怎样显示。

    默认插槽

    又名单个插槽、匿名插槽,这类插槽没有具体名字,一个组件只能有一个该类插槽

    <!-- 父组件 parent.vue -->
    <template>
      <div class="parent">
        <h1>父容器</h1>
        <child>
          <div class="tmpl">
            <span>菜单1</span>
          </div>

        </child>
      </div>
    </template>

    <!-- 子组件 child.vue -->
    <template>
      <div class="child">
        <h1>子组件</h1>
        <slot></slot>
      </div>
    </template>
     

    具名插槽

    匿名插槽没有 name 属性,所以叫匿名插槽。那么,插槽加了 name 属性,就变成了具名插槽。具名插槽可以在一个组件中出现 N 次,出现在不同的位置,只需要使用不同的 name 属性区分即可。

    <!-- 父组件 parent.vue -->
    <template>
      <div class="parent">
        <h1>父容器</h1>
        <child>
          <div class="tmpl" slot="up">
            <span>菜单up-1</span>
          </div>

          <div class="tmpl" slot="down">
            <span>菜单down-1</span>
          </div>

          <div class="tmpl">
            <span>菜单->1</span>
          </div>

        </child>
      </div>
    </template>

    <!-- 子组件 child.vue -->
    <template>
      <div class="child">
        <!-- 具名插槽 -->
        <slot name="up"></slot>
        <h3>这里是子组件</h3>
        <!-- 具名插槽 -->
        <slot name="down"></slot>
        <!-- 匿名插槽 -->
        <slot></slot>
      </div>
    </template>
     

    作用域插槽

    作用域插槽可以是默认插槽,也可以是具名插槽,不一样的地方是,作用域插槽可以为 slot 标签绑定数据,让其父组件可以获取到子组件的数据。

    <!-- parent.vue -->
    <template>
      <div class="parent">
        <h1>这是父组件</h1>
        <child
          >>
          <template slot="default" slot-scope="slotProps">
            {{ slotProps.user.name }}
          </template> </child
        >>
      </div>
    </template>

    <!-- 子组件 child.vue -->
    <template>
      <div class="child">
        <h1>这是子组件</h1>
        <slot :user="user"></slot>
      </div>
    </template>
    <script>
      export default {
        data() {
          return {
            user: {
              name: '小赵'
            }
          }
        }
      }
    </script>
     

    展开全文
  • 总结Vue的小技巧

    2020-01-17 10:17:47
    总结Vue的小技巧1.setTimeout/ setInterval2.Vue路由拦截浏览器后退实现草稿保存类似需求3.自定义组件添加click等事件不生效4.手动操控自定义组件5.深度作用选择器6.Vue数组/对象更新视图不更新7.Vue Filters过滤器...

    1.setTimeout/ setInterval

    this只想改变,无法用this访问vue实例
    
    	mouted(){
    		setTimeout(function(){
    			console.log(this)//此时this指向window对象
    		},1000)
    	}
    
    为解决这个问题,使用箭头函数或者先缓存this
    
    	mouted(){
    		setTimeout(()=>{
    			console.log(this)
    		},1000)
    	}
    	或者:
    	let self=this;
    	mouted(){
    		setTimeout(()=>{
    			console.log(self)
    		},1000)
    	}	
    
    使用setInterval的时候需要销毁,不然有可能造成程序崩溃
    
    	mouted(){
    		this.timer=setInterval(()=>{
    			console.log(self)
    		},1000)
    	},
    	beforeDestroy(){
    		clearInterval(this.timer)
    	}	
    	
    

    2.Vue路由拦截浏览器后退实现草稿保存类似需求

    为了防止用户突然离开,没有保存已输入的信息。
    
    	mounted(){
    		beforeRouteLeave (to, from, next) {
    	      if(用户已经输入信息){
    	        //出现弹窗提醒保存草稿,或者自动后台为其保存
    	      }else{
    	        next(true);//用户离开
    	      }
    	  	}
    	},
    

    3.自定义组件添加click等事件不生效

    一些自定义组件,需要额外添加一些事件来实现一些特定需求
    
    	<template>
        <el-progress type="circle" :percentage=0" @click=“stopProgress”></elprogress>
      	</template>
    	  <script>
    	    export default {
    	       methods:{
    	            stopProgress() { 
    	            console.log('停止')
    	            }
    	        }
    	    }
    	  </script>
    
    解决方法:使用.native修饰符
    
    	<template>
        <el-progress type="circle" :percentage=0" @click.native=“stopProgress”></el-progress>
      	</template>
    	  <script>
    	    export default {
    	        methods:{
    	            stopProgress() { 
    	            console.log('停止')
    	            }
    	        }
    	    }
    	  </script>复制代码
    

    4.手动操控自定义组件

    一些自定义组件,需要去获取组件对象进行一些其他的Dom操作
    解决方法 :使用ref属性暴露组件获取句柄
    
    	<template>
        <el-progress type="circle" :percentage="O" ref="progress"></el-progress>
        </template>
      <script>
        this.$refs.progress //组件对象实例, 可以手动调用组件的内置方法和属性
        this.$refs.progress.$el //组件 对象的最外层dom元素
      </script>
    

    5.深度作用选择器

    scoped的样式,希望影响到子组件的默认样式
    
    	<style scoped>
        .a >>> .b { /* ... */ }
      </style>
        //有些像Sass之类的预处理器无法正确解析>>>。这种情况下你可以使用/deep/操作符取而代之
        //- - - -这是一个>>>的别名,同样可以正常工作。
      <style scoped lang=“scss”>
        .a /deep/ .b { /* ... */ }
      </style>复制代码
    

    6.Vue数组/对象更新视图不更新

    	 data() { 
    	    return {
    	        arr: [1,2,3],
    	        obj:{
    	          a: 1,
    	          b: 2 
    	        }
    	    }; 
    	  },// 数组更新视图不更新
    	  this.arr[0] = 'OBKoro1';
    	 
    	  console.log(arr);// ['OBKoro1']; 
    	  // 数据更新,对象视图不更新    
    	  this.obj.c = 'OBKoro1';
    	  delete this.obj.a;
    	  console.log(obj);  // {b:2,c:'OBKoro1'}
    
    解决方法 :
    
    this. $set(你要改变的数组/对象,你要改变的位置/key,你要改成什么value)
    
    数组原生方法触发视图更新( vue官网可查):
    
    整体替换数组/对象
    
    	 data() { 
    	    return {
    	        arr: [1,2,3],
    	        obj:{
    	          a: 1,
    	          b: 2 
    	        }
    	    }; 
    	  },
    	  this.$set(this.arr,0,'OBKoro1')
    	  this.$set(this.obj,a,'OBKoro1')
    

    7.Vue Filters过滤器的使用

    常见的数据文本的格式化
    
      <!-- 在双花括号中 -->
      <div>{{ message | DateFormat }}</div> //展示正确时间
      <!--'v-bind'-->
      <div v-bind:id="rawId | formatId"></div>
    ​
      Demo:一个日期过滤器返回yyyy- MM-ddhh:mm:ss的样式
      引入一个提前写好日期格式化的js
      import dayjs from ‘dayjs';
      export default {
         data() {
            return {
                    //时间毫秒
                    message:18324798324789 
                }
        },
        filters: {
            //传入进行日期格式化
          DateFormat(value) {
            return dayjs(value).format(“YYYY-MM-DD HH:mm:ss")
            } 
        }
      }
    

    8.Vue深度watch与watch立即触发回调

    在watch里面监测对象里面对应的值是监测不到的,可以用如下方法。
    
    	//选项: deep
    
    	//在选项参数中指定deep:true,可以监听对象中子属性的变化。
    	
    	//选项: immediate
    	
    	//在选项参数中指定immediate:true,将立即以表达式的当前值触发回调,也就是默认触发一次。
    
    	  watch: {
    	    obj: {
    	        handler(val, oldVal) {
    	          console.log('属性变化触发这个回调',val, oldVal); 
    	        },
    	        deep: true // 监测这个对象中每一个属性的变化
    	    },
    	    step: { // 属性 //watch
    	       handler(val, oldVal) {
    	        console.log("默认触发一次", val, oldVal); 
    	       },
    	       immediate: true // 默认触发一次
    	    }
    	  }
    
    展开全文
  • 自定义属性props prop 定义了这个组件有哪些可配置的属性,组件的核心功能也都是它来确定的。写通用组件时,props 最好用对象的写法,这样可以针对每个属性设置类型、默认值或自定义校验属性的值,这点在组件开发中...

    自定义属性props

    prop 定义了这个组件有哪些可配置的属性,组件的核心功能也都是它来确定的。写通用组件时,props 最好用对象的写法,这样可以针对每个属性设置类型、默认值或自定义校验属性的值,这点在组件开发中很重要,然而很多人却忽视,直接使用 props 的数组用法,这样的组件往往是不严谨的。

    props: {
      type: {
        type: String,
        validator: (value) => {
          return ['success', 'warning', 'danger'].includes(value)
        },
        default: () => 'success'
      }
    }
    

    inheritAttrs与$attrs

    inheritAttrs

    这是2.4.0 新增的一个API,默认情况下父作用域的不被认作 props 的特性绑定将会“回退”且作为普通的 HTML 特性应用在子组件的根元素上。可通过设置 inheritAttrs 为 false,这些默认行为将会被去掉。注意:这个选项不影响 class 和 style 绑定。

    1. 父组件

      <Child1 :title="title" :message="message"></Child1>
      
    2. 子组件

      <template>
        <div>
          <div>子组件1</div>
          <Child2 v-bind="$attrs"></Child2>
        </div>
      </template>
      
      <script>
      import Child2 from './child2.vue'
      export default {
        components: {
          Child2
        },
        // inheritAttrs: false,
        props: ['title'],
        created () {
          console.log(this.$attrs)
        }
      }
      </script>
      
    3. 效果
      在这里插入图片描述

    4. 效果分析

      们看到:组件内未被注册的属性将作为普通html元素属性被渲染,如果想让属性能够向下传递,即使prop组件没有被使用,你也需要在组件上注册。这样做会使组件预期功能变得模糊不清,同时也难以维护组件的DRY。在Vue2.4.0,可以在组件定义中添加inheritAttrs:false,组件将不会把未被注册的props呈现为普通的HTML属性。但是在组件里我们可以通过其$attrs可以获取到没有使用的注册属性,如果需要,我们在这也可以往下继续传递。

    5.设置了inheritAttrs: false效果
    在这里插入图片描述
    6. 没有申明的属性,默认挂载在组件的根元素上,可以使用inheritAttrs去除掉

    $attrs

    1. 多级组件嵌套:

      例如:有一个页面由父组件,子组件,孙子组件构成

    2. 如果attrs被绑定在子组件上后,我们就可以在孙子组件里获取this.$attrs值。这个{{$attrs}}值是父组件中传递下来的props(除了子组件中props声明的)。

    3. 案例

      父组件:

      <template>
        <div>
          hello world
          <Child1 :title="title" :message="message"></Child1>
        </div>
      </template>
      

      子组件:

      <div>
        <div>子组件1</div>
        <Child2 v-bind="$attrs"></Child2>
      </div>
      

      孙子组件:

      <template>
        <div>模板2 ---- {{$attrs.message}}</div>
      </template>
      
      <script>
      export default {
        created () {
          console.log(this.$attrs)
        }
      }
      </script>
      

    单向数据流

    vue是单向数据流,子组件中不允许直接更改props传递过来的数据,如果需要更改,通过两种方式间接更改

    1. 操作data
    2. 通过计算属性
    <template>
      <div>
        <div>子组件1</div>
        <button @click="handleClick1">通过data改变传递的props值---{{currentType}}</button>
        <button>通过computed改变传递的props值---{{currentMessage}}</button>
      </div>
    </template>
    
    <script>
    export default {
      props: {
        title: {
          type: String,
          default: () => ''
        },
        message: {
          type: String,
          default: () => ''
        }
      },
      computed: { // 通过计算属性的方式间接处理props传递过来的值
        currentMessage () {
          return this.message + '...'
        }
      },
      data () {
        return {
          currentType: this.type // 通过data间接处理props传递过来的值
        }
      },
      methods: {
        handleClick1 () {
          this.currentType = '修改了标题'
        }
      }
    }
    </script>
    

    以上两种方法虽可以在子组件间接修改props的值,但如果子组件想修改数据并且同步更新到父组件,却无济于事。在一些情况下,我们可能会需要对一个 prop 进行『双向绑定』,此时就推荐以下这两种方法:

    1. 使用.sync

    2. 将父组件中的数据包装成对象传递给子组件

      父组件:

      <template>
        <div>
          父组件 --- {{title}} --- {{arr}} --- {{oldValue.value}}
          <Child1 :title.sync="title" :arr.sync="arr" :oldValue="oldValue"></Child1>
        </div>
      </template>
      
      <script>
      import Child1 from './child1.vue'
      export default {
        components: {
          Child1
        },
        data () {
          return {
            title: '标题',
            arr: ['book', 'people'],
            oldValue: {
              value: '原始值'
            }
          }
        }
      }
      </script>
      

      子组件:

      <template>
        <div>
          <div>子组件1</div>
          <button @click="$emit('update:title', '标题被更改了')">通过“双向数据绑定”的形式更改props值</button>
          <button @click="arr.push('工程师')">在子组件中改变数组</button>
          <button @click="oldValue.value = '1234'">通过包装对象(数组的)的形式更改props传递的值</button>
        </div>
      </template>
      
      <script>
      export default {
        props: {
          title: {
            type: String,
            default: () => ''
          },
          arr: {
            type: Array,
            default: () => []
          },
          oldValue: {
            type: [Array, Object, String],
            default: () => ''
          }
        },
        data () {
          return {}
        },
        methods: {}
      }
      </script>
      

    事件修饰符

    经典案例:Vue事件分为普通事件和修饰符事件,给自定义组件原生的 click 事件

    错误写法<Child1 @click="handleClick"></Child1> ,这里的 @click 是自定义事件 click,并不是原生事件 click。
    正确写法<Child1 @click.native="handleClick"></Child1>

    表单修饰符

    1. lazy
      在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 。你可以添加 lazy 修饰符,从而转变为使用 change事件进行同步。适用于输入完所有内容后,光标离开才更新视图的场景。
    2. trim
      如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:
    3. number
      自动将用户的输入值转为数值类型

    事件修饰符

    <!-- 阻止单击事件继续传播 -->
    <a v-on:click.stop="doThis"></a>
    
    <!-- 提交事件不再重载页面 -->
    <form v-on:submit.prevent="onSubmit"></form>
    
    <!-- 修饰符可以串联 -->
    <a v-on:click.stop.prevent="doThat"></a>
    

    样式穿透

    使用 >>> 或 /deep/ 解决

    <style scoped>
      .box /deep/ h2 {
        color: red;
      }
    
      .box >>> h4 {
        color: blue;
      }
    </style>
    

    watch高阶使用

    立即执行

    watch 是在监听属性改变时才会触发,有些时候,我们希望在组件创建后 watch 能够立即执行

    可能想到的的方法就是在 create 生命周期中调用一次,但这样的写法不优雅,我们可以使用这样的方法

    export default {
      data () {
        return {
          name: 'JOe'
        }
      },
      watch: {
        name: {
          handler: 'printData',
          immediate: true // 立即执行
        }
      },
      methods: {
        printData () {
          console.log(this.name)
        }
      }
    }
    

    深度监听

    在监听对象时,对象内部的属性被改变时无法触发 watch ,我们可以为其设置深度监听

    export default {
      data () {
        return {
          user: {
            name: '李四',
            age: 23
          }
        }
      },
      watch: {
        user: {
          handler: (val) => {
            console.log(val) // 使用deep才能打印出更新后的值,否则监听的值不会变化
          },
          deep: true
        }
      }
    }
    

    事件参数$event

    <template>
      <div class="box">
        <input type="text" @change="handleChange" />
        <input type="text" @change="handleChange2('hello', $event)">
      </div>
    </template>
    
    <script>
    export default {
      data () {
        return {}
      },
      methods: {
        handleChange (e) {
          console.log(e.target.value)
        },
        handleChange2 (msg, e) {
          console.log(msg, e.target.value)
        }
      }
    }
    </script>
    

    监听组件生命周期(2种方式)

    1. 通常我们监听组件生命周期会使用 $emit ,父组件接收事件来进行通知
    2. 用 @hook 即可监听组件生命周期,组件内无需做任何改变

    子组件:

    export default {
      beforeCreate () {
        this.$emit('listenChildCreate')
      }
    }
    

    父组件:

    <template>
      <div class="box">
        <button @click="show = !show">点击</button>
        <Child v-if="show" @hook:mounted="listenChildMounted" @hook:beforeDestroy="listenChildDestroy" @listenChildCreate="listenChildCreate"></Child>
      </div>
    </template>
    
    <script>
    import Child from './Child.vue'
    export default {
      components: {
        Child
      },
      data () {
        return {
          show: true
        }
      },
      methods: {
        listenChildMounted () {
          console.log('子组件挂载了')
        },
        listenChildDestroy () {
          console.log('监听到子组件销毁')
        },
        listenChildCreate () {
          console.log('监听到子组件创建')
        }
      }
    }
    </script>
    

    程序化的事件侦听器

    经典案例:在页面挂载时定义计时器,需要在页面销毁时清除定时器。这看起来没什么问题。但仔细一看 this.timer 唯一的作用只是为了能够在 beforeDestroy 内取到计时器序号,除此之外没有任何用处。

    export default {
        mounted() {
            this.timer = setInterval(() => {
                console.log(Date.now())
            }, 1000)
        },
        beforeDestroy() {
            clearInterval(this.timer)
        }
    }
    

    如果可以的话最好只有生命周期钩子可以访问到它。这并不算严重的问题,但是它可以被视为杂物。

    我们可以通过 $on 或 $once 监听页面生命周期销毁来解决这个问题:

    export default {
        mounted() {
            this.creatInterval('hello')
            this.creatInterval('world')
        },
        creatInterval(msg) {
            let timer = setInterval(() => {
                console.log(msg)
            }, 1000)
            this.$once('hook:beforeDestroy', function() {
                clearInterval(timer)
            })
        }
    }
    

    使用这个方法后,即使我们同时创建多个计时器,也不影响效果。因为它们会在页面销毁后程序化的自主清除。

    展开全文
  • img图片加载失败 场景:有些时候后台返回的图片不一定能加载成功,加载失败默认显示一张图片 <template> <div> <img :src="imgUrl" @error="handleError" alt=""> <... },

    img图片加载失败

    场景:有些时候后台返回的图片不一定能加载成功,加载失败默认显示一张图片

    <template>
      <div>
        <img :src="imgUrl" @error="handleError" alt="">
      </div>
    </template>
    
    <script>
    export default {
      data () {
        return {
          imgUrl: ''
        }
      },
      methods: {
        handleError (e) {
          e.target.src = require('@/assets/logo.png')
        }
      }
    }
    </script>
    

    组件级路由钩子

    beforeRouteEnter (to, from, next) { // 在渲染该组件的对应路由被确认前调用
      console.log(to)
      console.log(from)
      next()
    },
    beforeRouteLeave (to, from, next) { // 导航离开该组件的对应路由调用
      next()
    }
    

    递归组件

    场景:如果开发一个 tree 组件,里面层级是根据后台数据决定的,这个时候就需要用到动态组件

    • 递归组件: 组件在它的模板内可以递归的调用自己,只要给组件设置name组件就可以了。
    • 设置那么House在组件模板内就可以递归使用了,不过需要注意的是,
    • 必须给一个条件来限制数量,否则会抛出错误: max stack size exceeded
    • 组件递归用来开发一些具体有未知层级关系的独立组件。比如:联级选择器和树形控件

    父组件:

    <template>
      <div>
        <Tree :treeArr="treeArr"></Tree>
      </div>
    </template>
    
    <script>
    import Tree from './tree.vue'
    export default {
      components: {
        Tree
      },
      data () {
        return {
          imgUrl: '',
          treeArr: [
            {
              id: 1,
              expand: true,
              name: '二级节点1',
              children: [{
                id: 12,
                expand: true,
                name: '三级节点1'
              }]
            },
            {
              id: 2,
              expand: true,
              name: '二级节点2',
              children: [{
                id: 21,
                expand: true,
                name: '三级节点2'
              }]
            }
          ]
        }
      }
    }
    </script>
    

    子组件:

    <template>
      <div>
        <div v-for="(item, index) in treeArr">
          <div :key="item.id">
            {{item.name}}
            <!-- 递归调用自身,后台判断是否存在该值 -->
            <Tree :treeArr="item.children" v-if="item.expand"></Tree>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: 'Tree', // 必须定义name,组件内部递归使用
      props: {
        treeArr: {
          type: Array,
          default: () => []
        }
      }
    }
    </script>
    

    路由参数解耦

    1. 一般在组件内使用路由参数,大多数人会这样做:

      export default {
          methods: {
              getParamsId() {
                  return this.$route.params.id
              }
          }
      }
      
    2. 正确的做法是通过 props 解耦

      const router = new VueRouter({
          routes: [{
              path: '/user/:id',
              component: User,
              props: true
          }]
      })
      

    将路由的 props 属性设置为 true 后,组件内可通过 props 接收到 params 参数

    export default {
        props: ['id'],
        methods: {
            getParamsId() {
                return this.id
            }
        }
    }
    
    1. 还可以通过函数模式来返回 props

      const router = new VueRouter({
          routes: [{
              path: '/user/:id',
              component: User,
              props: (route) => ({
                  id: route.query.id
              })
          }]
      })
      

    Router key

    由于 Vue 会复用相同组件,例如 /page?id=1 => /page?id=2 这类跳转的情况,组件将不会执行 created, mounted 等钩子函数,此时常用解决方案如下。

    watch监听

    watch: {
      $route (to, from) {
        console.log(to, '....') // 可以监听到路由的变化,例如从详情1跳转到详情2
      }
    },
    

    给 router-view 绑定一个 unique key

    // $route.fullPath: 完成解析后的 URL,包含查询参数和 hash 的完整路径
    <router-view :key="$route.fullPath"></router-view>
    

    注:如果没有类似的场景不建议使用,毕竟每次渲染还是要付出一丢丢性能代价的。

    通过Vue.use()使用install创建自定义组件

    1. 首先新建loading文件夹,并在文件夹内新建loading.vue和index.js文件

      <template>
          <div>
              Loading....
          </div>
      </template>
      
    2. 编辑index.js,核心为install属性,

      import LoadingComponent from './Loading'
      
      const Loading = {
          install: (Vue) => {
              Vue.component('Loading', LoadingComponent)
          }
      }
      
      export default Loading
      
    3. main.js中引入相应的组件并用vue.use()使用组件

      import Loading from '@/components/index'
      Vue.use(Loading)
      

    统一处理方式

    index.js

    import LoadingComponent from '@/components/Loading'
    
    const customCom = (Vue) => {
        const Loading = {
            install: () => {
                Vue.component('Loading', LoadingComponent)
            }
        }
        Vue.use(Loading)
    }
    
    export default customCom
    

    main.js

    import customCom from './components/index.js'
    customCom(Vue)
    
    展开全文
  • Vue的这些小技巧

    千次阅读 2018-06-05 21:15:42
    前言用Vue开发一个网页并不难,但是也经常会遇到一些问题,其实大部分的问题都在文档中有所提及,再不然我们通过谷歌也能成功搜索到问题的答案,为了帮助小伙伴们提前踩坑,在遇到问题的时候,心里大概有个谱知道该...
  • Vue 全家桶学习资源(持续更新)

    万次阅读 多人点赞 2018-11-23 14:03:11
    下面整理了一些关于Vue以及Vue衍生的学习资源: 官网文档 官网API ECMAScript 6 入门 30分钟掌握ES6/ES2015核心内容(上) 30分钟掌握ES6/ES2015核心内容(下) vuefe Vue 中文社区 vue-router 2 Vuex ...
  • Vue进阶(一):Vue学习资料汇总

    千次阅读 多人点赞 2020-09-26 12:00:41
    Vue进阶(一):Vue学习资料汇总 Vue开发环境搭建(Windows) nodejs官网http://nodejs.cn/下载安装包; 安装好之后,在cmd命令行窗口查看相应node、npm版本信息 node -v // 显示node版本 npm -v // 显示npm包...
  • Vue.js入门环境搭建

    万次阅读 热门讨论 2017-05-18 17:14:58
    vue这个新的工具,确实能够提高效率,在经历的一段时间的摧残之后,终于能够有一个系统的认识了,下面就今天的收获做一个总结,也是vue入门的精髓: 1.要使用vue来开发前端框架,首先要有环境,这个环境要借助于...
  • vue环境搭建,vue项目创建

    万次阅读 多人点赞 2017-02-24 11:36:12
    vue这个新的工具,确实能够提高效率,在经历的一段时间的摧残之后,终于能够有一个系统的认识了,下面就今天的收获做一个总结,也是vue入门的精髓: 1.要使用vue来开发前端框架,首先要有环境,这个环境要借助于...
  • Vue.js入门之环境搭建

    千次阅读 2016-12-14 13:14:47
    vue这个新的工具,确实能够提高效率,在经历的一段时间的摧残之后,终于能够有一个系统的认识了,下面就今天的收获做一个总结,也是vue入门的精髓:  1.要使用vue来开发前端框架,首先要有环境,这个环境要借助于...
  • 5种处理Vue异常的方法

    千次阅读 多人点赞 2019-06-18 10:38:16
    原文: Handling Errors in Vue.js 译者: Fundebug 本文采用意译,版权归原作者所有 去年一整年,我都在使用最爱的—...最近,我花了不少时间研究了Vue中处理异常的各种技巧,在此想把我学到的分享给大家。 错误大全...
  • 初识 Vue.js 中的 *.Vue文件

    万次阅读 多人点赞 2018-05-23 00:23:49
    什么是Vue.js?vue.js是一套构建用户界面的渐进式框架,它采用自底向上增量开发的设计。(自底向上设计方法是根据系统功能要求,从具体的器件、逻辑部件或者相似系统开始,凭借设计者熟练的技巧和丰富的经验,通过对...
  • vue 全局键盘监听

    千次阅读 2019-04-04 13:53:52
    vue实现键盘监听,使用了控件监听组件时,需要获得控件的焦点才能起作用。 如何实现全局监听时,尝试了有效的方法如下: mounted() 在mounted()中编写监听的快捷键和方法即可实现: 例如监听ctrl+s的组合键, ...
  • Vue.js系列之vue-resource(6)

    万次阅读 2017-01-08 22:13:12
    说明:我们项目现在用的是:vue2.0 + vue-cli + webpack + vue-router2.0 + vue-resource1.0.3 如果大家在实践的过程中与本文所说的内容有较大区别的话看看是不是版本问题。 本文是一系列文章,在我对Vue有了更...
  • 策划列表相关的vue.js可畏的事 资源 官方资源外部资源工作门户社区会议播客官方的例子教程实例书 项目采用vue.js 开源商业产品应用/网站互动体验企业使用 组件&图书馆 用户界面组件 表通知...
  • Vue.js 2.0 快速上手精华梳理

    万次阅读 多人点赞 2017-09-03 19:48:27
    自从Vue2.0发布后,Vue就成了前端领域的热门话题,github也突破了三万的star,那么对于新手来说,如何高效快速的学习Vue2.0呢。http://www.tuicool.com/articles/raaeeiU Vue基础 对于没有接触过es6和webpack...
  • 轻松搭建基于 SpringBoot + Vue 的 Web 商城应用

    万次阅读 多人点赞 2020-01-03 12:40:08
    首先介绍下在本文出现的几个比较重要的概念: 函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性...
  • 【框架】记录 Vue +Ts 开发

    千次阅读 2019-04-29 22:26:45
    vscode 装 vue 插件:vetur main.ts 是入口文件。 可参考这个官方的 github 仓库写:https://github.com/vuejs/vue-class-component 在声明每一个对象的时候,都要告诉ts,对象是什么类型。 技巧: 用alert...
  • 一 昨天分享了《5分钟看懂,2019年web前端新趋势》 这篇文章。 反响很不错,但我写完翻译完,发完才知道,有其他号也发了,我很佛了。 他们一看就是机翻,而我就不一样了,每段、每个知识点我都写了自己的评语和...
  • Apicloud开发新闻类App实战...1:vue实现apicloud开发脚手架--超级实用通用 2:阿里巴巴矢量图标引入使用 3:apicloud的下拉刷新,上拉加载数据操作讲解 4:引用apicloud的模块库操作使用 5:窗
1 2 3 4 5 ... 20
收藏数 4,789
精华内容 1,915
关键字:

vue技巧总结