者:Michael Thiessen
译者:前端小智
来源:hackernoon
有时候,依赖 Vue 响应方式来更新数据是不够的,相反,我们需要手动重新渲染组件来更新数据。或者,我们可能只想抛开当前的DOM,重新开始。那么,如何让Vue以正确的方式重新呈现组件呢?
强制 Vue 重新渲染组件的最佳方法是在组件上设置:key。当我们需要重新渲染组件时,只需更 key 的值,Vue 就会重新渲染组件。
这是一个非常简单的解决方案。
当然,你可能会对其他方式会更感兴趣:
这相当于每次你想关闭应用程序时都要重新启动你的电脑。
这种方式或许有用,但这是一个非常糟糕的解决方案,不要这样做,我们来看看更好的方法。
v-if指令,该指令仅在组件为true时才渲染。如果为false,则该组件在DOM中不存在。
来看看,v-if 是怎么工作的,在template中,添加v-if指令:
<template>??
<my-component?v-if="renderComponent"?/>
</template>
在script 中,使用nextTick的方法
<script>??export?default?{????data()?{??????return?{????????renderComponent:?true,??????};????},????methods:?{??????forceRerender()?{????????//?从?DOM?中删除?my-component?组件????????this.renderComponent?=?false;????????this.$nextTick(()?=>?{??????????//?在?DOM?中添加?my-component?组件??????????this.renderComponent?=?true;????????});??????}????}??};</script>
上面的过程大致如下:
在这个过程中,有两个部分比较重要
首先,我们必须等到nextTick,否则我们不会看到任何变化。
在Vue中,一个 tick 是一个DOM更新周期。Vue将收集在同一 tick 中进行的所有更新,在 tick 结束时,它将根据这些更新来渲染 DOM 中的内容。如果我们不等到next tick,我们对renderComponent的更新就会自动取消,什么也不会改变。
其次,当我们第二次渲染时,Vue将创建一个全新的组件。Vue 将销毁第一个,并创建一个新的,这意味着我们的新my-component将像正常情况一样经历其所有生命周期-created,mounted等。
另外,nextTick 可以与 promise 一起使用:
forceRerender()?{??
//?从?DOM?中删除?my-component?组件??
this.renderComponent?=?false;??
this.$nextTick().then(()?=>?{????
this.renderComponent?=?true;??
});
}
不过,这并不是一个很好的解决方案,所以,让我们做 Vue 想让我们做的
这是解决这个问题的两种最佳方法之一,这两种方法都得到了Vue的官方支持。
通常情况下,Vue 会通过更新视图来响应依赖项中的更改。然而,当我们调用forceUpdate时,也可以强制执行更新,即使所有依赖项实际上都没有改变。
下面是大多数人使用这种方法时所犯的最大错误。
如果 Vue 在事情发生变化时自动更新,为什么我们需要强制更新呢?
原因是有时候 Vue 的响应系统会让人感到困惑,我们认为Vue会对某个属性或变量的变化做出响应,但实际上并不是这样。在某些情况下,Vue的响应系统根本检测不到任何变化。
所以就像上一个方法,如果你需要这个来重新渲染你的组件,可能有一个更好的方法。
有两种不同的方法可以在组件实例本身和全局调用forceUpdate:
//?全局
import?Vue?from?'vue';Vue.forceUpdate();
//?使用组件实例
export?default?{??
methods:?{????
methodThatForcesUpdate()?{??????
//?...??????
this.$forceUpdate();??????
//?...????
}??
}}
重要提示:这不会更新任何计算属性,调用forceUpdate仅仅强制重新渲染视图。
在许多情况下,我们需要重新渲染组件。
要正确地做到这一点,我们将提供一个key属性,以便 Vue 知道特定的组件与特定的数据片段相关联。如果key保持不变,则不会更改组件,但是如果key发生更改,Vue 就会知道应该删除旧组件并创建新组件。
正是我们需要的!
但是首先,我们需要绕一小段路来理解为什么在Vue中使用key。
一旦你理解了这一点,那么这是了解如何以正确方式强制重新渲染的很小的一步。
假设我们要渲染具有以下一项或多项内容的组件列表:
如果你对该列表进行排序或以任何其他方式对其进行更新,则需要重新渲染列表的某些部分。但是,不会希望重新渲染列表中的所有内容,而只是重新渲染已更改的内容。
为了帮助 Vue 跟踪已更改和未更改的内容,我们提供了一个key属性。在这里使用数组的索引,因为索引没有绑定到列表中的特定对象。
const?people?=?[??
{?name:?'Evan',?age:?34?},??
{?name:?'Sarah',?age:?98?},??
{?name:?'James',?age:?45?}
];
如果我们使用索引将其渲染出来,则会得到以下结果:
<ul>??
<li?v-for="(person,?index)?in?people"?:key="index">????
{{?person.name?}}?-?{{?index?}}??
</li>
</ul>
//?OutputsEvan?-?0Sarah?-?1James?-?2
如果删除Sarah,得到:
Evan?-?0James?-?1
与James关联的索引被更改,即使James仍然是James。James会被重新渲染,这并不是我们希望的。
所以这里,我们可以使用唯一的 id 来作为 key
const?people?=?[??
{?id:?'this-is-an-id',?name:?'Evan',?age:?34?},??
{?id:?'unique-id',?name:?'Sarah',?age:?98?},??
{?id:?'another-unique-id',?name:?'James',?age:?45?},
];
<ul>??
<li?v-for="person?in?people"?:key="person.id">????
{{?person.name?}}?-?{{?person.id?}}??
</li>
</ul>
在我们从列表中删除Sarah之前,Vue删除了Sarah和James的组件,然后为James创建了一个新组件。现在,Vue知道它可以为Evan和James保留这两个组件,它所要做的就是删除Sarah的。
如果我们向列表中添加一个person,Vue 还知道可以保留所有现有的组件,并且只需要创建一个新组件并将其插入正确的位置。这是非常有用的,当我们有更复杂的组件,它们有自己的状态,有初始化逻辑,或者做任何类型的DOM操作时,这对我们很有帮助。
所以接下来看看,如果使用最好的方法来重新渲染组件。
最后,这是强制Vue重新渲染组件的最佳方法(我认为)。
我们可以采用这种将key分配给子组件的策略,但是每次想重新渲染组件时,只需更新该key即可。
这是一个非常基本的方法
<template>??
<component-to-re-render?:key="componentKey"?/>
</template>
export?default?{?
data()?{????
return?{??????
componentKey:?0,????
};??
},??
methods:?{????
forceRerender()?{??????
this.componentKey?+=?1;??????
}??
}
}
每次forceRerender被调用时,我们的componentKey都会改变。当这种情况发生时,Vue将知道它必须销毁组件并创建一个新组件。我们得到的是一个子组件,它将重新初始化自身并“重置”其状态。
如果确实需要重新渲染某些内容,请选择key更改方法而不是其他方法。
原文:https://hackernoon.com/the-correct-way-to-force-vue-to-re-render-a-component-bde2caae34ad
1、开关灯示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#d1{
height: 400px;
width: 400px;
border-radius: 50%;
}
.bg_green{background-color: green}
.bg_red{background-color: red}
</style>
</head>
<body>
<div id="d1" class="c1 bg_red bg_green"></div>
<button id="d2">变色</button>
<script>
let btnEle=document.getElementById('d2')
let divEle=document.getElementById('d1')
btnEle.onclick=function () { //点击时调用事件句柄
divEle.classList.toggle('bg_red') //有则删除,无则添加
}
</script>
</body>
</html>
2、input框获取焦点示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="text" value="hello world" id="d1">
<script>
let iEle=document.getElementById('d1') // 点击了后执行的操作(获取焦点)
iEle.onfocus=function (){
iEle.value='' // 重新赋值为空
}
iEle.onblur=function (){ // 鼠标点击了其他地方执行的操作(移除焦点)
iEle.value='goodbye'
}
</script>
</body>
</html>
3、input框实时刷新时间实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="text" id="d1" style="display: block;height: 50px;width:200px">
<button id="d2">开始</button>
<button id="d3">结束</button>
<script>
//4、创建一个变量t,用来存储定时器
let t=null
//1、 把要操作的标签生成对象
let inputEle=document.getElementById('d1')
let starEle=document.getElementById('d2')
let endEle=document.getElementById('d3')
//2、定义一个函数用于展示实时时间
function showTime(){
let time=new Date(); // 生成一个时间对象
inputEle.value=time.toLocaleString() //获取当前时间,并赋值给input标签
}
//3、给页面上的按钮绑定功能,一个开始,一个结束
starEle.onclick=function (){
if (!t){
t=setInterval(showTime,1000) //每点击一下就会生成一个定时器
}
}
endEle.onclick=function (){
clearInterval(t) //只能清除一个定时器
t=null
}
</script>
</body>
</html>
4、省市联动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<select name="" id="d1" >
<option value="---请选择---" selected disabled>---请选择---</option>
</select>
<select name="" id="d2"></select>
<script>
let proEle=document.getElementById('d1')
let cityEle=document.getElementById('d2')
//1、先模拟省市数据
data={
'江西':['南昌','九江','赣州','上饶'],
'浙江':['杭州','义乌','绍兴','嘉兴'],
'江苏':['南京','苏州','泰州','南通'],
'广东':['广州','深圳','惠州','清远']
};
//2、for循获取到省
for (let key in data){
//2.1 将省信息做成一个个option标签,并添加到第一个select框中
//1 创建option框
let opEle=document.createElement('option')
//2 设置value
opEle.value=key
//3 设置文本
opEle.innerText=key
//4 添加到第一个select中
proEle.appendChild(opEle)
}
//3、设置文本域变化事件,使得再更换省的时候可以更新市
proEle.onchange=function (){
//先清空市select中所有的option
cityEle.innerHTML=''
//先获取到用户选择的省
let getPro=proEle.value
//获取对应的市信息
let getCity=data[getPro]
//添加默认选择标签
let oppEle=document.createElement('option')
oppEle.innerText='-请选择-'
oppEle.setAttribute('selected','selected')
oppEle.setAttribute('disabled','disabled')
cityEle.appendChild(oppEle)
//for循环所有的市,渲染到第二个select中
for(let i=0;i<getCity.length;i++){
//拿到市名
let city=getCity[i]
//创建标签
let opEle=document.createElement('option')
//设置文本
opEle.innerText=city
//设置值
opEle.value=city
//添加到第二个select中
cityEle.appendChild(opEle)
}
}
</script>
</body>
</html>
jQuery 是一个快速、小巧且功能强大的 JavaScript 库。它通过简化 JavaScript 编程,使开发人员能够更轻松地操作 HTML 文档、处理事件、执行动画效果以及处理 AJAX 请求等。
jQuery基本语法:
jQuery(选择器).action()
可以简写:jQuery()===$()
基本选择器:
1. 元素选择器:使用元素名称选择 HTML 元素。例如:`$("p")` 会选择所有的 `<p>` 元素。
2. ID 选择器:使用元素的 ID 属性选择指定的元素。例如:`$("#myElement")` 会选择具有 ID 属性为 "myElement" 的元素。
3. 类选择器:使用元素的类名选择指定的元素。例如:`$(".myClass")` 会选择具有类名为 "myClass" 的元素。
4.属性选择器:使用元素的属性选择指定的元素。例如:$("[name='myName']") 会选择具有 name 属性值为 "myName" 的元素。
通过jQuery选择器获取到的对象都是jQuery对象,如需使用JavaScript代码对其进行操作的话,则需要转换成标签对象。
$("#myElement")[0] //转成标签对象
同样,标签对象也可转成jQuery对象:
$(document.getElementByid('#myElement')) //转成jQuery对象
组合分组嵌套选择器:
jQuery也支持组合选择器:
$('div#d1') //选择具有 id 属性为 "d1" 的 <div> 元素。
$('div.c1') //选择具有 class 属性为 "c1" 的 <div> 元素。
... ...
$('div span') //后代选择器,用于选择 <div> 元素内的所有 <span> 元素。
$('div>span') //子元素选择器,用于选择 <div> 元素的直接子元素中的 <span> 元素。
$('div+span') //相邻元素选择器,用于选择紧接在 <div> 元素后面的第一个 <span> 元素。
$('div~span') //兄弟元素选择器,用于选择所有在 <div> 元素后出现的同级别的 <span> 元素。
基本筛选器:
在jQuery中,筛选器可以根据不同的条件来选择匹配的元素,从而实现对元素的精确控制和操作。
jQuery提供了许多基本筛选器,用于选择匹配指定条件的元素。以下是一些常用的基本筛选器:
- `:first`:选择第一个匹配的元素。
- `:last`:选择最后一个匹配的元素。
- `:even`:选择索引为偶数的元素(从0开始计数)。
- `:odd`:选择索引为奇数的元素(从0开始计数)。
- `:eq(index)`:选择索引为指定值的元素。
- `:gt(index)`:选择索引大于指定值的元素。
- `:lt(index)`:选择索引小于指定值的元素。
- `:header`:选择所有标题元素(如`<h1>`、`<h2>`等)。
- `:not(selector)`:选择不匹配给定选择器的元素。
- `:has(selector)`:选择包含匹配给定选择器的元素。
- `:contains(text)`:选择包含指定文本内容的元素。
$('ul li') #ul下面的所有li子标签
$('ul li:first') #ul下面的第一个li子标签 $('ul li').first()
$('ul li:last') #ul下面的最后一个li子标签 $('ul li').last()
$('ul li:eq(2)') #放索引,根据索引取值
$('ul li:even') #偶数索引,0包含在内
$('ul li:odd') #奇数索引
$('ul li:gt(2)') #大于索引
$('ul li:lt(2)') #小于索引
$('div:has("p")') #选举出包含一个标签,或多个标签在内的标签(父级)
$('ul li:not("#d1")') # 移除所有满足条件的标签
这些基本筛选器可以与其他选择器和方法组合使用,以便更精确地选择和操作网页中的元素。
表单筛选器:
特殊情况
筛选器方法:
知道大家有没有这样的需求,用户自定义页面布局,把布局的html标签代码保存到数据库中,预览的时候根据保存的值,把标签代码渲染到页面中,当然普通JS / jQuery肯定没问题,下面小编介绍如何用Vue来实现。
在VUE中,把标准html标签输出到网页中利用v-html可以实现,但是自定义组件的标签是无法正确输出我们想要的结果,下面我介绍一种方法来实现这种需求。
<template>
<div>
测试组件 {{ dataId }}
</div>
</template>
<script>
export default {
props: {
dataId: {
type: String,
default: ''
}
}
}
<script>
import Vue from "vue";
// 1.引入组件
var testCom=()=> import("./test-com");
export default {
props: {
html: {
type: String,
required: true
}
},
render(h) {
if (!this.html) {
return false;
}
const com=Vue.extend({
components: {
"test-com": testCom
},
template: this.html
});
return h(com);
}
};
</script>
<template>
<div>
<comrender :html="htmltemplate" />
</div>
</template>
<script>
import comrender from './render'
export default {
components: {
comrender: comrender
},
data() {
return {
htmltemplate: '<test-com dataId="001"></test-com>'
}
}
};
</script>
大功告成(可自行测试,如果想要完整源码可以私信我哦)!目前自定义组件可以通过字符串来渲染,并且可以传递简单的一个字符串参数。但是如果想要传递一个对象参数,目前是不行的,如果有此需求等待小编下次更新!
*请认真填写需求信息,我们会在24小时内与您取得联系。