手牵手系列之工作知识点总结

  •  el-tree关于多选框变成单选问题

// template
 <el-tree
    class="showtree"
    :data="data"
    node-key="orgId"
    :accordion="false"
    show-checkbox
    check-strictly
    :props="defaultProps"
    default-expand-all
    @check-change="handleCheckChange"
    :filter-node-method="filterNode"
     ref="tree"
  ></el-tree>

// js

// 单选
handleCheckChange(data, checked, node) {
  if (checked) {
     this.$refs.tree.setCheckedNodes([data])
     this.selectedObj = JSON.parse(JSON.stringify(data))
    }
  },
  • app.vue只渲染一次的问题

使用<keep-alive>会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在activated阶段获取数据,承担原来created钩子中获取数据的任务。

当引入keep-alive 的时候,页面第一次进入,钩子的触发顺序created-> mounted-> activated,退出时触发deactivated。当再次进入(前进或者后退)时,只触发activated。

所以想要每次都渲染数据,就要在activated钩子函数中加入获取数据的函数,这样每次访问都会向后端请求数据

  • v-if 和 v-for 互斥  

    永远不要把 v-if 和 v-for 同时用在同一个元素上。

    <!-- bad:控制台报错 -->
    <ul>
      <li
        v-for="user in users"
        v-if="shouldShowUsers"
        :key="user.id">
          {{ user.name }}
      </li>
    </ul>

一般我们在两种常见的情况下会倾向于这样做:

  • 为了过滤一个列表中的项目 (比如 v-for="user in users" v-if="user.isActive")。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。

computed: {
  activeUsers: function () {
    return this.users.filter((user) => {
      return user.isActive
    })
  }
}

<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id">
      {{ user.name }}
  </li>
</ul>
  • 为了避免渲染本应该被隐藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers")。这种情形下,请将 v-if 移动至容器元素上 (比如 ulol)。 
<!-- bad -->
<ul>
  <li
    v-for="user in users"
    v-if="shouldShowUsers"
    :key="user.id">
      {{ user.name }}
  </li>
</ul>

<!-- good -->
<ul v-if="shouldShowUsers">
  <li
    v-for="user in users"
    :key="user.id">
      {{ user.name }}
  </li>
</ul>
  • el-select与el-tree结合使用

<!--
  功能:下拉树筛选
  作者:lc
  时间:2021年07月15日 17:11:03
  版本:v2.1.3
-->
<template>
  <el-select v-model="treeData" filterable clearable ref="select" :filter-method="filterHandle" @clear="delValue" placeholder="请选择案件类型">
    <el-option :value="treeDataValue" style="height: auto">
      <el-tree ref="tree" :data="treeList" :default-expand-all="false" :filter-node-method="filterNode" node-key="id" :props="defaultProps" @node-click="handNodeClick" />
    </el-option>
  </el-select>
</template>

<script>
export default {
  name: 'selectTree',
  props: {},
  components: {},
  data() {
    return {
      treeData: '',
      treeDataValue: '',
      treeList: [],
      defaultProps: {
        children: 'children',
        label: 'name'
      }
    }
  },
  computed: {},
  watch: {},
  methods: {
    // 筛选
    filterHandle(val) {
      this.$refs.tree.filter(val)
      if (!val) {
        this.getsysLawCaseTypeList()
      }
    },
    filterNode(val, data) {
      if (!val) return true
      return data.name.indexOf(val) !== -1
    },
    // 清空
    delValue() {
      this.getsysLawCaseTypeList()
    },
    handNodeClick(data, node, nodeData) {
      this.treeDataValue = data
      this.treeData = data.name
      // 选择器执行完成后,使其失去焦点隐藏下拉框的效果
      this.$refs.select.blur()
      this.$emit('getCaseData', data)
    },
    // 获取案件类型
    async getsysLawCaseTypeList() {
      try {
        let res = await this.$ajax.workbench.getsysLawCaseTypeList()
        if (res.code == 0) {
          this.treeList = this.listToTree(res.data)
        }
      } catch (e) {}
    },
    //list转成tree
    listToTree(oldArr) {
      oldArr.forEach(element => {
        let parentId = element.parentId
        if (parentId != 0) {
          oldArr.forEach(ele => {
            if (ele.id == parentId) {
              if (!ele.children) {
                ele.children = []
              }
              ele.children.push(element)
            }
          })
        }
      })
      oldArr = oldArr.filter(ele => ele.parentId == 0) //这一步是过滤,按树展开,将多余的数组剔除;
      return oldArr
    }
  },
  mounted() {
    this.getsysLawCaseTypeList()
  }
}
</script> 

<style lang='less' scoped>
</style>
<style lang="less">
.el-tree-node__content:hover {
  background: #fff !important;
}
.el-select-dropdown__item.hover {
  background: #fff !important;
}
.el-select-dropdown__item {
  padding: 0;
}
.el-select-dropdown__item.selected {
  font-weight: normal;
}
</style>
 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
  // DOM 更新了
})

// 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示)
Vue.nextTick()
  .then(function () {
    // DOM 更新了
  })
参数:
{Object | Array} target
{string | number} propertyName/index
{any} value

向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = 'hi')

注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。

// 数组

var vm =new Vue({
	el:'#app',
	data:{
		items:['a', 'b', 'c']
	},
	methods:{
		btn(){
			Vue.set(this.items, 1, 'e')
			console.log(this.items)
		}
	}
})

// 对象

Vue.set(vm.userInfo,'address','beijing');
参数:
{Object | Array} target
{string | number} propertyName/index

// 示例

data : {
   namelist : {
        id : 1, 
        name : '叶落森'
   }       
}

// 删除name
delete this.namelist.name;//js方法
Vue.delete(this.namelist,'name');//vue方法
参数:

{string} id
{Function | Object} [definition]

// 注册全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

// 局部指令
directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus()
    }
  }
}

<el-input v-focus />

指令定义函数提供了几个钩子函数(可选):

bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。

inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。

update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新(详细的钩子函数参数见下)。

componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。

unbind: 只调用一次, 指令与元素解绑时调用。

 自定义指令

参数:

{string} id
{Function} [definition]

// 全局注册
Vue.filter('my-filter', function (value) {
  // 返回处理后的值
})

// 组件过滤器(局部)
filter:{
    my-filter:function(value){
        
    }
}

<!-- 在双花括号中 -->
{{ message | capitalize }}

<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>

过滤器

参数:

{string} id
{Function | Object} [definition]

用法:

注册或获取全局组件。注册还会自动使用给定的 id 设置组件的名称

// 注册组件,传入一个扩展过的构造器
Vue.component('my-component', Vue.extend({ /* ... */ }))

// 注册组件,传入一个选项对象 (自动调用 Vue.extend)
Vue.component('my-component', { /* ... */ })

// 获取注册的组件 (始终返回构造器)
var MyComponent = Vue.component('my-component')

// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

<div id="components-demo">
  <button-counter></button-counter>
</div>
参数:

{Object | Function} plugin

插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:

添加全局方法或者 property。如:vue-custom-element

添加全局资源:指令/过滤器/过渡等。如 vue-touch

通过全局混入来添加一些组件选项。如 vue-router

添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。

一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router

Vue.use(MyPlugin)

 插件

  •  data必须是一个函数

 data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。

类型:Array<string> | Object

props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。

type:可以是下列原生构造函数中的一种:String、Number、Boolean、Array、Object、Date、Function、Symbol、任何自定义构造函数、或上述内容组成的数组。会检查一个 prop 是否是给定的类型,否则抛出警告。Prop 类型的更多信息在此。
default:any
为该 prop 指定一个默认值。如果该 prop 没有被传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。
required:Boolean
定义该 prop 是否是必填项。在非生产环境中,如果这个值为 truthy 且该 prop 没有被传入的,则一个控制台警告将会被抛出。
validator:Function
自定义验证函数会将该 prop 的值作为唯一的参数代入。在非生产环境下,如果该函数返回一个 falsy 的值 (也就是验证失败),一个控制台警告将会被抛出。你可以在这里查阅更多 prop 验证的相关信息。

// 简单语法
Vue.component('props-demo-simple', {
  props: ['size', 'myMessage']
})

// 对象语法,提供验证
Vue.component('props-demo-advanced', {
  props: {
    // 检测类型
    height: Number,
    // 检测类型 + 其他验证
    age: {
      type: Number,
      default: 0,
      required: true,
      validator: function (value) {
        return value >= 0
      }
    }
  }
})

props

计算属性的结果会被缓存,除非依赖的响应式 property 变化才会重新计算。

计算属性缓存 vs 方法:两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。

注意:我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。

 注意,不应该使用箭头函数来定义 method 函数 (例如 plus: () => this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.a 将是 undefined。

 watch: {
    a: function (val, oldVal) {
      console.log('new: %s, old: %s', val, oldVal)
    },
    // 方法名
    b: 'someMethod',
    // 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
    c: {
      handler: function (val, oldVal) { /* ... */ },
      deep: true
    },
    // 该回调将会在侦听开始之后被立即调用
    d: {
      handler: 'someMethod',
      immediate: true
    },
    // 你可以传入回调数组,它们会被逐一调用
    e: [
      'handle1',
      function handle2 (val, oldVal) { /* ... */ },
      {
        handler: function handle3 (val, oldVal) { /* ... */ },
        /* ... */
      }
    ],
    // watch vm.e.f's value: {g: 5}
    'e.f': function (val, oldVal) { /* ... */ }
  }

 注意,不应该使用箭头函数来定义 watcher 函数 (例如 searchQuery: newValue => this.updateAutocomplete(newValue))。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.updateAutocomplete 将是 undefined。

生命周期钩子

注意,不能使用箭头函数来定义一个生命周期方法 。

在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。

 在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el property 目前尚不可用。

在挂载开始之前被调用:相关的 render 函数首次被调用。

该钩子在服务器端渲染期间不被调用。

 注意,dom已经挂载,可以对dom进行操作。mounted 不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在 mounted 内部使用 vm.$nextTick

mounted: function () {
  this.$nextTick(function () {
    // Code that will run only after the
    // entire view has been rendered
  })
}

该钩子在服务器端渲染期间不被调用。

数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。

该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。

 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。

当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。

注意 updated 不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在 updated 里使用 vm.$nextTick

updated: function () {
  this.$nextTick(function () {
    // Code that will run only after the
    // entire view has been re-rendered
  })
}

 该钩子在服务器端渲染期间不被调用。

被 keep-alive 缓存的组件激活时调用。

该钩子在服务器端渲染期间不被调用。

//index.js
{
   path: '/1',
   name: 'components1',
   component: Components1,
   meta: {
     keepAlive: true   //判断是否缓存
     }
   },
{
   path: '/2',
   name: 'components2',
   component: Components2,
   meta: {
      keepAlive: false
   }
},

// 然后我们的App.vue中只需要判断其keepAlive值即可
<template>
  <div id="app">
    <keep-alive>
      <router-view v-if="$route.meta.keepAlive" />
    </keep-alive>
    <router-view v-if="!$route.meta.keepAlive" />
  </div>
</template>

但如果该组件中没有使用缓存,也就是没有被<keep-alive>包裹的话,activated是不起作用的。

组件1中只执行activated钩子钩子函数,而组件2则把创建和挂载的钩子函数都执行了。
这就是缓存的原因,components其对组件进行了缓存所以并不会再一次执行创建和挂载。

简单的说activated()函数就是一个页面激活后的钩子函数,一进入页面就触发;

所以当我们运用了组件缓存时,如果想每次切换都发送一次请求的话,需要把请求函数写在activated中,而写在created或mounted中其只会在首次加载该组件的时候起作用。

被 keep-alive 缓存的组件停用时调用 

该钩子在服务器端渲染期间不被调用。

实例销毁之前调用。在这一步,实例仍然完全可用。

该钩子在服务器端渲染期间不被调用。

使用场景: 比如用户在提交form表单时,在内容填写 到 提交表单过程中,有后退或其他操作时可以在这个钩子提示内容尚未提交或保存,是否退出之类的

实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。

该钩子在服务器端渲染期间不被调用。

 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。

vue 里 this.$parent 作用

this.$parent 可以访问到父组件 上所有的 data(){ 里的数据信息和生命周期方法,methods里的方法 }!

注意,尽量避免使用 this.$parent

组件必须相互保持独立,Vue 组件也是。如果组件需要访问其父层的上下文就违反了该原则。

如果一个组件需要访问其父组件的上下文,那么该组件将不能在其它上下文中复用。

provide / inject

类型:

provide:Object | () => Object
inject:Array<string> | { [key: string]: string | Symbol | Object }

示例:

// 父级组件提供 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}

// 子组件注入 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

 provide-inject

慎用 provide/inject

既然 provide/inject 如此好用,那么,为什么 Vue 官方还要推荐我们使用 Vuex,而不是用原生的 API 呢?

我在前面提到过,Vuex 和 provide/inject 最大的区别在于,Vuex 中的全局状态的每次修改是可以追踪回溯的,而 provide/inject 中变量的修改是无法控制的,换句话说,你不知道是哪个组件修改了这个全局状态。

.sync的本质

<Component 
    :foo="val"
    @update:foo="(payload)=>{ val = payload }"
/>
<!-- 实质是 语法糖 -->
<Component :foo.sync="val" />

// 子组件使用
this.$emit('update:foo', payload)

$attrs的本质

父组件以形如:foo="xxx"或者v-bind="{age:12}"传给子组件的属性,但凡没有被子组件的props接收的,都会被扔到子组件的$attrs里去。(另外,被props指名接收的,都放入子组件的$props里。)

 $listeners的本质

父组件以 @eventName="fn" 或者 v-on:eventName="fn" 对子组件挂载事件监听。对子组件而言,父组件监听的事件都放在$listeners里。如果子组件对后代组件使用v-on="$listeners",相当于对后代组件批量挂载了父组件对自己的事件监听。因此后代组件的emit会触发父组件的事件方法。

案例

A组件代码更新如下

<template>
 <div>
   <child-dom
    :foo="foo"
    :coo="coo"
     v-on:upRocket="reciveRocket"
   >
   </child-dom>
 </div>
</template>
<script>
 import childDom from "@/components/ChildDom.vue";
 export default {
   name:'demoNo',
   data() {
     return {
       foo:"Hello, world",
        coo:"Hello,rui"
    }
  },
 components:{childDom},
 methods:{
   reciveRocket(){
      console.log("reciveRocket success")
   }
 }
}
</script>
b组件更新如下

<template>
 <div>
 <p>foo:{{foo}}</p>
 <p>attrs:{{$attrs}}</p>
 <childDomChild v-bind="$attrs" v-on="$listeners"></childDomChild>
 </div>
</template>
<script>
import childDomChild from './childDomChild';
export default {
 name:'child-dom'
 props:["foo"],
 inheritAttrs:false,
}
</script>
c组件更新如下

<template> 
 <div>
 <p>coo:{{coo}}</p>
 <button @click="startUpRocket">我要发射火箭</button>
 </div>
</template>
<script>
 export default {
 name:'childDomChild',
 props:['coo'],
 methods:{
 startUpRocket(){
 this.$emit("upRocket");
 console.log("startUpRocket")
 }
 }
 }
</script>

类数组转化成数组的方法

// 扩展运算符
function f1() {
  console.log(arguments);
  let arr = [...arguments];
  console.log(arr);
}
f1(1,32,43,4);

// Array.from()
function f1() {
  console.log(arguments);
  let arr = Array.from(arguments);
  console.log(arr);
}
f1(1,32,43,4);

// slice方法
function f1() {
  console.log(arguments);
  //console.log(arguments instanceof Array);
  //console.log(arguments instanceof Object);

  let arr = Array.prototype.slice.call(arguments);
  console.log(arr);
  let arr1 = [].slice.call(arguments);
  console.log(arr1);
}
f1(1,32,43,4);

for in 与 for of区别

for … in循环由于历史遗留问题,它遍历的实际上是对象的属性名称。一个Array数组实际上也是一个对象,它的每个元素的索引被视为一个属性。 

for … in循环将把name包括在内,但Array的length属性却不包括在内。

for … of循环则完全修复了这些问题,它只循环集合本身的元素。(更加适合遍历数组)

箭头函数基本特点

1、箭头函数this为父作用域的this,不是调用时的this

2、箭头函数的this永远指向其父作用域,任何方法都改变不了,包括call,apply,bind。
普通函数的this指向调用它的那个对象。

3、箭头函数不能作为构造函数,不能使用new

4、 箭头函数没有arguments,caller,callee

5、箭头函数没有原型属性

6、箭头函数常见错误

let a = {
  foo: 1,
  bar: () => console.log(this.foo)
}

a.bar()  //undefined
bar函数中的this指向父作用域,而a对象没有作用域,因此this不是a,打印结果为undefined

function A() {
  this.foo = 1
}

A.prototype.bar = () => console.log(this.foo)

let a = new A()
a.bar()  //undefined
原型上使用箭头函数,this指向是其父作用域,并不是对象a,因此得不到预期结果

  • 指令缩写 

用 : 表示 v-bind:

用 @ 表示 v-on:

用 # 表示 v-slot:

<input
  :value="newTodoText"
  :placeholder="newTodoInstructions">

<input
  @input="onInput"
  @focus="onFocus">

<template #header>
  <h1>Here might be a page title</h1>
</template>

<template #footer>
  <p>Here's some contact info</p>
</template>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值