-
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
移动至容器元素上 (比如ul
,ol
)。
<!-- 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
函数,从而返回初始数据的一个全新副本数据对象。
-
props用法
类型: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
}
}
}
})
计算属性的结果会被缓存,除非依赖的响应式 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 缓存的组件停用时调用
该钩子在服务器端渲染期间不被调用。
- beforeDestroy (可以用于清除定时器、缓存)
实例销毁之前调用。在这一步,实例仍然完全可用。
该钩子在服务器端渲染期间不被调用。
使用场景: 比如用户在提交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 如此好用,那么,为什么 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>