vue实践中的常见知识漏洞001

前言

本文主要总结了vue实际开发项目当中应该如何解决一些实际的开发问题,可能你认为很简单,但短期内也许你并没解决思路的。css

建议阅读时间:15-25minhtml


更多精彩内容请关注我掘金主页或者 达摩兵的空间博客vue

常见技术解答

for循环中针对ui样式的特征性样式或者事件

  • 针对ui有特定的数据字段进行判断(也叫数据模型方法)

这种书数据的要求比较高,且要求你可以找到比较好的对应关系,须要针对class进行特征性的组件渲染。当你须要改变时改变数据便可从新渲染达到改变样式的目的。webpack

<li v-for="item of list" :key="item.id" :class="item.status?'color':''" @click="changeColor(item.id)">{{item.name}}</li>
return {
list:[
{id:1,status:true,name:1111},
{id:2,status:true,name:222}]
}
methods:{
    changeColor(id){
        this.list.map((item)=>{
            if(item.id==id){
            item.status=!item.status;
            }
            return item;
        })
    }
}
复制代码
  • 传入对应的参数以及事件源,能够进行相应的判断改变class

特色更加灵活,也能够根据须要传入你须要传入的item属性参数进行与class的匹配判断,不用改变接口返回的数据结构。es6

<li v-for="item of list" :key="item.id"  @click="changeColor($event)">{{item.name}}</li>
return {
list:[
{id:1,name:1111},
{id:2,name:222}]
}
changeColor(e){
			let el=e.target;
            if(el.classList.contains("color")){
                el.classList.remove("color")
            }else{
                el.classList.add("color")
            }
}
复制代码

计算属性方法的使用

问题描述:若是你的计算属性依赖于data的部分,而你的data对应的字段在data里没有申明,只是在请求接口时进行申明赋值,那么当接口请求时,虽然数据发生了变化,可是计算属性的值不会发生更新。web

解决方案 :须要你在data里申明你计算属性依赖的字段,哪怕是空或者nullnpm

事件执行顺序问题

问题描述 :定义了输入框blur,再按钮点击事件问题,其中默认click的话,执行顺序是先执行blur再执行click.若是你须要场景在点击的时候不执行blur的事件数组

解决方案:bash

1 常规方案 : 须要吧点击事件变成@mousedown.prevent ,前者会让点击优于blur执行,后者会阻止blur执行babel

2 el-input并不生效,能够用计时器延迟执行 将失去焦点的事件计时器延迟执行,而后点击事件里清除定时器,也是能够只执行点击事件逻辑的

路由参数变化组件不更新

问题描述 :路由参数变化,可是组件没有对应的更新,主要是由于通常获取参数写在了created路由钩子函数中,路由参数变化的时候,这个生命周期不会从新执行。

解决方案:watch监听router

watch: {
 // 方法1
  '$route' (to, from) { //监听路由是否变化
    if(this.$route.params.articleId){// 判断条件1  判断传递值的变化
      //获取文章数据
    }
  }
  //方法2
  '$route'(to, from) {
    if (to.path == "/page") {    /// 判断条件2  监听路由名 监听你从什么路由跳转过来的
       this.message = this.$route.query.msg     
    }
  }
}
复制代码

异步函数中使用this没法指向vue实例对象

问题描述 : 在定时器或者其余异步函数中使用传统的func致使this指向不到vue实例,主要缘由是由于this指向的问题,详细的能够参考个人《神奇的this》这篇文章。

解决方案 :用箭头函数或者指定变量赋值为this(其余一些不能用箭头函数的地方本身也要注意)

定时器在组件销毁后还在执行

问题描述 :一些耗费性能的计时器或者动画在组件销毁以后仍是执行的,致使性能变低。

解决方案 :在销毁组件的生命周期中销毁定时器或者一些动画的js

//组件销毁前执行的钩子函数,跟其余生命周期钩子函数的用法相同。
beforeDestroy(){
     //我一般是把setInterval()定时器赋值给this实例,而后就能够像下面这么中止。
    clearInterval(this.intervalId);
},
复制代码

组件名与引入时大小写不一致致使报错

问题描述:

This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
Use equal casing. Compare these module identifiers:
复制代码

解决方案 :须要严格对应组件的大小写,避免低级错误

动态添加的dom没有样式

问题描述:做为常识咱们知道style中的样式都会追加scoped,这样针对模板dom中的样式就能够生效,但其生效后的最终样式并非咱们写的样式名,而是编码后的,因此咱们在js中拼接上的dom结构样式并不会生效。

解决思路: 1 当添加的部分样式不会太多,并且是动态加载的,能够将其设置为非scopred的

2 将添加dom部分用的样式放到非scoped样式标签中

3 将添加的部分,若是有必要,能够另外写一个页面拆分的vue组件

拓展 : 项目中引入的其余ui框架的样式,若是你想覆盖修改,也是须要不加scoped的,若是你想整个项目覆盖,就能够在src/styles下定义customer-element.scss 这样的来重写覆盖样式。

vue中直接修改数据,页面视图不更新

问题描述 :在常规理解中,视图与数据是双向绑定的,可是有时候修改data的数组或者对象值,视图不会更新 。

data() { // data数据
        return {
          arr: [1,2,3],
          obj:{
              a: 1,
              b: 2
          }
        };
      },
   // 数据更新 数组视图不更新
    this.arr[0] = 'OBKoro1';
    this.arr.length = 1;
    console.log(arr);// ['OBKoro1'];
    // 数据更新 对象视图不更新
    this.obj.c = 'OBKoro1';
    delete this.obj.a;
    console.log(obj);  // {b:2,c:'OBKoro1'}

复制代码

解决方案 :因为js的限制,Vue 不能检测以上数组的变更,以及对象的添加/删除,不少人会由于像上面这样操做,出现视图没有更新的问题。

1 this.$set(你要改变的数组/对象,你要改变的位置/key,你要改为什么value)

this.$set(this.arr, 0, "OBKoro1"); // 改变数组
this.$set(this.obj, "c", "OBKoro1"); // 改变对象
复制代码

2 数组原生方法触发视图更新: splice()、 push()、pop()、shift()、unshift()、sort()、reverse() 推荐使用splice方法会比较好自定义,由于slice能够在数组的任何位置进行删除/添加操做

3 替换数组 比方说:你想遍历这个数组/对象,对每一个元素进行处理,而后触发视图更新。

// 文档中的栗子: filter遍历数组,返回一个新数组,用新数组替换旧数组
    example1.items = example1.items.filter(function (item) {
      return item.message.match(/Foo/)
    })
复制代码

须要无脑重复某内容

<div v-for="n in 5">
        <span>这里会被渲染5次,渲染模板{{n}}</span>
     </div>
复制代码

babel-plugin-transform-runtime使用

  • 出现问题:这个插件能够兼容并转化大部分的es6语法,可是部分语法也是不能转化或者存在具体问题的。
  1. 异步加载组件时,会产生 polyfill 代码冗余
  2. 不支持对全局函数与实例方法的 polyfill。不支持全局函数(如:Promise、Set、Map),Set 跟 Map 这两种数据结构应该你们用的也很少,影响较小。可是 Promise 影响可能就比较大了。不支持实例方法(如:'abc'.include('b')、['1', '2', '3'].find((n) => n 等等),这个限制几乎废掉了大部分字符串和一半左右数组的新特性。 而两个问题的缘由均归因于 babel-plugin-transform-runtime 采用了沙箱机制来编译咱们的代码(即:不修改宿主环境的内置对象)。因为异步组件最终会被编译为一个单独的文件,因此即便多个组件中使用了同一个新特性(例如:Object.keys()),那么在每一个编译后的文件中都会有一份该新特性的 polyfill 拷贝。若是项目较小能够考虑不使用异步加载,可是首屏的压力会比较大。
  • 解决方案:通常状况下 babel-plugin-transform-runtime 能知足大部分的需求,当不知足需求时,推荐使用完整的 babel-polyfill。
    • 首先,从项目中移除 babel-plugin-transform-runtime,卸载该依赖: npm un babel-plugin-transform-runtime -D
    • 接着修改 babel 配置文件
      // .babelrc
      {
        //...
        "plugins": [
          // - "transform-runtime"
        ]
        //...
      }
      复制代码
    • 而后,安装 babel-polyfill 依赖: npm i babel-polyfill -D
    • 最后,在入口文件中导入
      // src/main.js
      import 'babel-polyfill'
      复制代码

ES6 import 引用问题

在 ES6 中,模块系统的导入与导出采用的是引用导出与导入(非简单数据类型),也就是说,若是在一个模块中定义了一个对象并导出,在其余模块中导入使用时,导入的实际上是一个变量引用(指针),若是修改了对象中的属性,会影响到其余模块的使用。

一般状况下,系统体量不大时,咱们可使用 JSON.parse(JSON.stringify(str)) 简单粗暴地来生成一个全新的深度拷贝的 数据对象。不过当组件较多、数据对象复用程度较高时,很明显会产生性能问题,这时咱们能够考虑使用 Immutable.js。

鉴于这个缘由,进行复杂数据类型的导出时,须要注意多个组件导入同一个数据对象时修改数据后可能产生的问题。

此外,模块定义变量或函数时即使使用 let 而不是 const,在导入使用时都会变成只读,不能从新赋值,效果等同于用 const 声明。

动态懒加载组件

背景:在webpack的新特性中支持组件的懒加载,也就是说咱们能够在加载到该路由的时候再把这部分脚本进行加载,同时这个在项目进行打包的时候,对应的文件也会被单独打包,对于首屏优化以及其余页面的资源加载优化都是很是好的。这也要求咱们在每一个页面组件使用组件的时候尽可能按需引入,提高体验。

问题场景:那么咱们须要解决的问题是: 0 webpack是静态解析路径的,直接传入变量并不可行 1 每次都写一串加载组件的代码很不方便,是否能够支持写成一个加载组件的方法 2 是否支持区分生产和开发环境,由于开发环境使用懒加载会致使热更新,致使更新变慢,因此开发环境使用全量默认加载,生产环境使用懒加载

解决方案以下 : 1 webpack的路径使用变量拼接,必须预先给出一个相对路径,而后把具体的组件路径在传入

2 用一个箭头函数,将须要传入的组件名或者相对路径传入

3 用process.env.NODE_ENV肯定使用哪一种加载方式

代码以下: 在原来的router/index.js中,定义一个加载组件的_import方法。

// router/index.js 
const _import = require('./_import_' + process.env.NODE_ENV)

//使用时
 {
      path: '/',
      name: 'HelloWorld',
      component: _import('HelloWorld')
    },
    
// router/_import_development.js
module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+

// router/_import_production.js 若是你加载的vue不是这个路径 请自定义哦
module.exports = file => () => import('@/views/' + file + '.vue')
复制代码

vue中的data必须为函数

场景 :vue入门的人可能在页面单独引入vue的时候,直接使用data为对象类型的,并无问题,可是在spa应用中,若是组件中的data为对象类型就会报错。

解决方案 :data换为函数,返回对象类型的键值对。

拓展 :你可能知道要这样作,这里稍微科普下缘由,主要是由于根组件只会用一次,因此能够用对象,而子组件可能在一个应用中被屡次使用,为了不多个组件使用同一数据互相影响,因此讲data约定为了返回函数类型,返回须要的对象,以此保证子组件在数据渲染的时候不会互相影响。

有父子标签关系的自定义组件渲染失败

场景 :在自定义组件的时候,不少时候须要将ul下的li标签,table下的tr\td标签进行封装为自定义组件,但直接使用自定义组件会致使其最终生成的位置不是咱们想要的。其标签会渲染到tbody标签之外。

Vue.component("row",{
  template:'<tr><td>{{content}}</td></tr>',
  data(){
    return {
      content:'this is a row'
    }
  },
})
复制代码

解决方案 :缘由是由于html会进行标签解析,tbody下的标签必须为tr,其余的同理。那么咱们能够将其子标签设置为原来的标签类型,而后用is="selfComponent" 来解决这个问题。

<tr is="row"></tr>

拓展:

  • 不要将渲染vue的容器元素定位到html或者body上,不然提示:Do not mount Vue to <html> or <body> - mount to normal elements instead.
  • 确保有在vue新建实例的时候将el属性绑定到一个html模板的标签上

ref使用

场景 :虽然vue不建议直接操做dom,可是在复杂的场景中,咱们须要进行dom的操做,这时候就能够借助ref实现。好比下面咱们举一个简单的例子,经过ref获取dom节点,拿到其内容。

解决方案

<div @click="handleClick" ref="hello">hello world
  </div>
   handleClick(){
      console.log(this.$refs.hello)
    }
复制代码
  • 拓展案例 :实现计数器加和 场景 :假设咱们有两个计数器组件的实例,如今须要用ref的方案获得两个计数器的加和。

代码以下:

<counter ref="one" @change="handleChange"></counter>
  <counter ref="two" @change="handleChange"></counter>
  <span>{{total}}</span>
  Vue.component("counter",{
  template:"<div @click='change'>{{number}}</div>",
  data(){
  return {
   number:0}  
	},
   methods:{
       change(){
  		this.number++;
         this.$emit("change")
		}       
      }
})
//app父组件方法
  handleChange(){
      this.total=this.$refs.one.number+this.$refs.two.number
    },
复制代码
  • 拓展认知 : this.$refs.name中若是是原生标签,拿到的是原生标签的节点,若是是组件,拿到的是组件的引用。

  • vueRefDemo使用

参考文档

相关文章
相关标签/搜索