feat(性能测试): 版本对比 (#8993)
* feat(性能测试): 版本对比 --user=郭雨琦 版本对比 Co-authored-by: guoyuqi <xiaomeinvG@126.com>
This commit is contained in:
parent
9fe0c329ee
commit
74bca78188
|
@ -15,8 +15,8 @@
|
|||
<ms-table-button v-if="showRun" icon="el-icon-video-play"
|
||||
type="primary"
|
||||
:content="runTip" @click="runTest"/>
|
||||
<ms-table-button v-if="showRun" icon="el-icon-circle-plus-outline"
|
||||
content="转场景测试" @click="historicalDataUpgrade"/>
|
||||
<ms-table-button v-if="showRun" icon="el-icon-circle-plus-outline"
|
||||
content="转场景测试" @click="historicalDataUpgrade"/>
|
||||
|
||||
<slot name="button"></slot>
|
||||
<version-select v-xpack :project-id="projectId" @changeVersion="changeVersion" v-if="isShowVersion"/>
|
||||
|
@ -156,6 +156,7 @@
|
|||
projectId() {
|
||||
return getCurrentProjectID();
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
<template>
|
||||
<div class="compare-class">
|
||||
<el-card style="width: 50%;" ref="old">
|
||||
<p>1</p>
|
||||
<span>v1</span>
|
||||
<h4>1</h4>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form :inline="true">
|
||||
<el-form-item :label="$t('load_test.name') ">
|
||||
<el-input :disabled="true" :placeholder="$t('load_test.input_name')" v-model="oldData.name"
|
||||
class="input-with-select"
|
||||
size="small"
|
||||
maxlength="30" show-word-limit/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-tooltip :content="$t('commons.follow')" placement="bottom" effect="dark" v-if="!showFollow">
|
||||
<i class="el-icon-star-off" style="color: #783987; font-size: 25px; margin-right: 15px;cursor: pointer;position: relative; top: 5px; " />
|
||||
</el-tooltip>
|
||||
<el-tooltip :content="$t('commons.cancel')" placement="bottom" effect="dark" v-if="showFollow">
|
||||
<i class="el-icon-star-on" style="color: #783987; font-size: 28px; margin-right: 15px;cursor: pointer;position: relative; top: 5px; "/>
|
||||
</el-tooltip>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-tabs v-model="active" @tab-click="clickTab">
|
||||
<el-tab-pane :label="$t('load_test.basic_config')" class="advanced-config">
|
||||
<performance-basic-config :is-read-only="true" :test="oldData" @fileChange="fileChange" ref="basicConfig" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('load_test.pressure_config')" class="advanced-config">
|
||||
<performance-pressure-config :is-read-only="true" :test="oldData" :test-id="oldData.id" @fileChange="fileChange" ref="pressureConfig"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('load_test.advanced_config')" class="advanced-config">
|
||||
<performance-advanced-config :read-only="true" :test-id="oldData.id" ref="advancedConfig"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
<el-card style="width: 50%;" ref="new">
|
||||
<p>v1</p>
|
||||
<span>1</span>
|
||||
<h4>2</h4>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form :inline="true">
|
||||
<el-form-item :label="$t('load_test.name') ">
|
||||
<el-input :disabled="true" :placeholder="$t('load_test.input_name')" v-model="newData.name"
|
||||
class="input-with-select"
|
||||
size="small"
|
||||
maxlength="30" show-word-limit/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-tooltip :content="$t('commons.follow')" placement="bottom" effect="dark" v-if="!newShowFollow">
|
||||
<i class="el-icon-star-off" style="color: #783987; font-size: 25px; margin-right: 15px;cursor: pointer;position: relative; top: 5px; " />
|
||||
</el-tooltip>
|
||||
<el-tooltip :content="$t('commons.cancel')" placement="bottom" effect="dark" v-if="newShowFollow">
|
||||
<i class="el-icon-star-on" style="color: #783987; font-size: 28px; margin-right: 15px;cursor: pointer;position: relative; top: 5px; "/>
|
||||
</el-tooltip>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-tabs v-model="active" @tab-click="clickTab">
|
||||
<el-tab-pane :label="$t('load_test.basic_config')" class="advanced-config">
|
||||
<performance-basic-config :is-read-only="true" :test="newData" @fileChange="fileNewChange" ref="newBasicConfig" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('load_test.pressure_config')" class="advanced-config">
|
||||
<performance-pressure-config :is-read-only="true" :test="newData" :test-id="newData.id" @fileChange="fileNewChange" ref="newPressureConfig" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('load_test.advanced_config')" class="advanced-config">
|
||||
<performance-advanced-config :read-only="true" :test-id="newData.id" ref="newAdvancedConfig" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
<button @click="getDiff"></button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
import EditPerformanceTest from "@/business/components/performance/test/EditPerformanceTest";
|
||||
import PerformancePressureConfig from "@/business/components/performance/test/components/PerformancePressureConfig";
|
||||
import PerformanceBasicConfig from "@/business/components/performance/test/components/PerformanceBasicConfig";
|
||||
import PerformanceAdvancedConfig from "@/business/components/performance/test/components/PerformanceAdvancedConfig";
|
||||
import {patch} from "@/business/components/performance/v_node_diff";
|
||||
import Vue from "vue";
|
||||
|
||||
const {diff} = require("@/business/components/performance/v_node_diff");
|
||||
|
||||
|
||||
export default{
|
||||
name:"DiffVersion",
|
||||
components:{
|
||||
EditPerformanceTest,
|
||||
PerformancePressureConfig,
|
||||
PerformanceBasicConfig,
|
||||
PerformanceAdvancedConfig,
|
||||
},
|
||||
props:{
|
||||
oldData:{
|
||||
type:Object
|
||||
},
|
||||
newData:{
|
||||
type:Object
|
||||
},
|
||||
showFollow:{
|
||||
type:Boolean
|
||||
},
|
||||
newShowFollow:{
|
||||
type:Boolean
|
||||
}
|
||||
|
||||
},
|
||||
data(){
|
||||
return{
|
||||
active: '0',
|
||||
oldDataJson:{
|
||||
|
||||
},
|
||||
newDataJson:{
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
getDiff(){
|
||||
let oldVnode = this.$refs.old
|
||||
let vnode = this.$refs.new
|
||||
//oldVnode.style.backgroundColor = "rgb(241,200,196)";
|
||||
console.log(this.$refs.old)
|
||||
console.log(this.$refs.new)
|
||||
diff(oldVnode,vnode);
|
||||
|
||||
},
|
||||
clickTab(tab) {
|
||||
if (tab.index === '1') {
|
||||
this.$refs.pressureConfig.calculateTotalChart();
|
||||
}
|
||||
},
|
||||
fileChange(threadGroups) {
|
||||
let handler = this.$refs.pressureConfig;
|
||||
let csvSet = new Set;
|
||||
threadGroups.forEach(tg => {
|
||||
tg.threadNumber = tg.threadNumber || 10;
|
||||
tg.duration = tg.duration || 10;
|
||||
tg.durationHours = Math.floor(tg.duration / 3600);
|
||||
tg.durationMinutes = Math.floor((tg.duration / 60 % 60));
|
||||
tg.durationSeconds = Math.floor((tg.duration % 60));
|
||||
tg.rampUpTime = tg.rampUpTime || 5;
|
||||
tg.step = tg.step || 5;
|
||||
tg.rpsLimit = tg.rpsLimit || 10;
|
||||
tg.threadType = tg.threadType || 'DURATION';
|
||||
tg.iterateNum = tg.iterateNum || 1;
|
||||
tg.iterateRampUp = tg.iterateRampUp || 10;
|
||||
|
||||
if (tg.csvFiles) {
|
||||
tg.csvFiles.map(item => csvSet.add(item));
|
||||
}
|
||||
});
|
||||
let csvFiles = [];
|
||||
for (const f of csvSet) {
|
||||
csvFiles.push({name: f, csvSplit: false, csvHasHeader: true});
|
||||
}
|
||||
|
||||
this.$set(handler, "threadGroups", threadGroups);
|
||||
|
||||
this.$refs.basicConfig.threadGroups = threadGroups;
|
||||
this.$refs.pressureConfig.threadGroups = threadGroups;
|
||||
this.$refs.advancedConfig.csvFiles = csvFiles;
|
||||
|
||||
this.$refs.pressureConfig.resourcePoolChange();
|
||||
handler.calculateTotalChart();
|
||||
},
|
||||
fileNewChange(threadGroups) {
|
||||
let handler = this.$refs.newPressureConfig;
|
||||
let csvSet = new Set;
|
||||
threadGroups.forEach(tg => {
|
||||
tg.threadNumber = tg.threadNumber || 10;
|
||||
tg.duration = tg.duration || 10;
|
||||
tg.durationHours = Math.floor(tg.duration / 3600);
|
||||
tg.durationMinutes = Math.floor((tg.duration / 60 % 60));
|
||||
tg.durationSeconds = Math.floor((tg.duration % 60));
|
||||
tg.rampUpTime = tg.rampUpTime || 5;
|
||||
tg.step = tg.step || 5;
|
||||
tg.rpsLimit = tg.rpsLimit || 10;
|
||||
tg.threadType = tg.threadType || 'DURATION';
|
||||
tg.iterateNum = tg.iterateNum || 1;
|
||||
tg.iterateRampUp = tg.iterateRampUp || 10;
|
||||
|
||||
if (tg.csvFiles) {
|
||||
tg.csvFiles.map(item => csvSet.add(item));
|
||||
}
|
||||
});
|
||||
let csvFiles = [];
|
||||
for (const f of csvSet) {
|
||||
csvFiles.push({name: f, csvSplit: false, csvHasHeader: true});
|
||||
}
|
||||
|
||||
this.$set(handler, "threadGroups", threadGroups);
|
||||
|
||||
this.$refs.newBasicConfig.threadGroups = threadGroups;
|
||||
this.$refs.newPressureConfig.threadGroups = threadGroups;
|
||||
this.$refs.newAdvancedConfig.csvFiles = csvFiles;
|
||||
|
||||
this.$refs.newPressureConfig.resourcePoolChange();
|
||||
handler.calculateTotalChart();
|
||||
},
|
||||
},
|
||||
created() {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.compare-class{
|
||||
display: flex;
|
||||
justify-content:space-between;
|
||||
}
|
||||
</style>
|
|
@ -76,6 +76,18 @@
|
|||
|
||||
<ms-change-history ref="changeHistory"/>
|
||||
|
||||
<el-dialog
|
||||
:fullscreen="true"
|
||||
:visible.sync="dialogVisible"
|
||||
width="100%"
|
||||
>
|
||||
<diff-version :old-data="oldData" :show-follow="showFollow" :new-data="newData" :new-show-follow="newShowFollow" ></diff-version>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible=false">取 消</el-button>
|
||||
<el-button type="primary" >确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
</ms-main-container>
|
||||
</ms-container>
|
||||
</template>
|
||||
|
@ -91,6 +103,7 @@ import MsScheduleConfig from "../../common/components/MsScheduleConfig";
|
|||
import MsChangeHistory from "../../history/ChangeHistory";
|
||||
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
|
||||
import MsTipButton from "@/business/components/common/components/MsTipButton";
|
||||
import DiffVersion from "@/business/components/performance/test/DiffVersion";
|
||||
|
||||
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
|
||||
const versionHistory = requireComponent.keys().length > 0 ? requireComponent("./version/VersionHistory.vue") : {};
|
||||
|
@ -108,14 +121,19 @@ export default {
|
|||
MsMainContainer,
|
||||
MsChangeHistory,
|
||||
'MsVersionHistory': versionHistory.default,
|
||||
DiffVersion
|
||||
},
|
||||
inject: [
|
||||
'reload'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
dialogVisible:false,
|
||||
result: {},
|
||||
test: {schedule: {}, follows: []},
|
||||
oldData: {schedule: {}, follows: []},
|
||||
newData:{schedule: {}, follows: []},
|
||||
newShowFollow:false,
|
||||
savePath: "/performance/save",
|
||||
editPath: "/performance/edit",
|
||||
runPath: "/performance/run",
|
||||
|
@ -183,7 +201,10 @@ export default {
|
|||
},
|
||||
importAPITest() {
|
||||
let apiTest = this.$store.state.test;
|
||||
console.log("输出vuex的test")
|
||||
console.log(apiTest)
|
||||
if (apiTest && apiTest.name) {
|
||||
console.log("set test name")
|
||||
this.$set(this.test, "name", apiTest.name);
|
||||
if (apiTest.jmx.scenarioId) {
|
||||
this.$refs.basicConfig.importScenario(apiTest.jmx.scenarioId);
|
||||
|
@ -220,7 +241,10 @@ export default {
|
|||
this.$store.commit("clearTest");
|
||||
} else {
|
||||
let scenarioJmxs = this.$store.state.scenarioJmxs;
|
||||
console.log("输出vuex的scenarioJmxs")
|
||||
console.log(scenarioJmxs)
|
||||
if (scenarioJmxs && scenarioJmxs.name) {
|
||||
console.log("set scenarioJmxs name")
|
||||
this.$set(this.test, "name", scenarioJmxs.name);
|
||||
let relateApiList = [];
|
||||
if (scenarioJmxs.jmxs) {
|
||||
|
@ -444,6 +468,7 @@ export default {
|
|||
};
|
||||
},
|
||||
fileChange(threadGroups) {
|
||||
console.log("zou")
|
||||
let handler = this.$refs.pressureConfig;
|
||||
|
||||
let csvSet = new Set;
|
||||
|
@ -524,7 +549,27 @@ export default {
|
|||
});
|
||||
},
|
||||
compare(row) {
|
||||
// console.log(row);
|
||||
this.oldData = this.test;
|
||||
this.$get('/performance/get/' + row.id+"/"+this.test.refId, response => {
|
||||
this.$get('/performance/get/' + response.data.id, res => {
|
||||
if (res.data) {
|
||||
this.newData = res.data;
|
||||
this.$get('/performance/test/follow/' + response.data.id, resp => {
|
||||
if(resp.data&&resp.data.follows){
|
||||
for (let i = 0; i <resp.data.follows.length; i++) {
|
||||
if(resp.data.follows[i]===this.currentUser().id){
|
||||
this.newShowFollow = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
if(this.newData){
|
||||
this.dialogVisible = true;
|
||||
}
|
||||
},
|
||||
checkout(row) {
|
||||
//let test = this.versionData.filter(v => v.versionId === row.id)[0];
|
||||
|
@ -580,4 +625,5 @@ export default {
|
|||
margin-right: 25px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,347 @@
|
|||
|
||||
function isUndef (v) {
|
||||
return v === undefined || v === null
|
||||
}
|
||||
|
||||
function isDef (v){
|
||||
return v !== undefined && v !== null
|
||||
}
|
||||
|
||||
function isTrue (v){
|
||||
return v === true
|
||||
}
|
||||
|
||||
function makeMap (str, expectsLowerCase){
|
||||
const map = Object.create(null)
|
||||
const list= str.split(',')
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
map[list[i]] = true
|
||||
}
|
||||
return expectsLowerCase
|
||||
? val => map[val.toLowerCase()]
|
||||
: val => map[val]
|
||||
}
|
||||
|
||||
const isTextInputType = makeMap('text,number,password,search,email,tel,url')
|
||||
|
||||
/**
|
||||
* 该比较方法认为 1,2 -> 2,1 版本是有改变的
|
||||
* @param oldDom 被比较的节点
|
||||
* @param newDom 比较的节点
|
||||
*/
|
||||
export function diff (oldDom, newDom) {
|
||||
let diffNode = {
|
||||
oldNodeArray:[],
|
||||
nodeArray:[],
|
||||
}
|
||||
//两个独立的节点传过来 首先单独比较一下他们最外层的 dom的_vnode 如果 _vnode 相同 则认为最外层节点相同,否则更改样式 return
|
||||
let oldVnode = oldDom._vnode;
|
||||
let newVnode = newDom._vnode;
|
||||
|
||||
if (isUndef(newVnode)) { //如果新节点不存在
|
||||
//如果旧节点存在, 旧节点加背景颜色
|
||||
if (isDef(oldVnode)) {
|
||||
diffNode.oldNodeArray.push(oldVnode.elm)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (isUndef(oldVnode)) {//如果旧节点不存在
|
||||
//如果新节点存在, 新节点加背景颜色
|
||||
if (isDef(newVnode)) {
|
||||
diffNode.nodeArray.push(newVnode.elm)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (sameVnode(oldVnode, newVnode)) {
|
||||
//逐层比较节点 这里涉及到 children 的length 可能不同的情况
|
||||
diffDetail(oldVnode, newVnode,diffNode)
|
||||
|
||||
} else {
|
||||
diffNode.oldNodeArray.push(oldVnode.elm)
|
||||
diffNode.nodeArray.push(newVnode.elm)
|
||||
}
|
||||
|
||||
changeStyle(diffNode);
|
||||
}
|
||||
|
||||
function changeStyle(diffNode){
|
||||
console.log("查看结果");
|
||||
console.log(diffNode.oldNodeArray);
|
||||
console.log(diffNode.nodeArray);
|
||||
for (let i = 0; i < diffNode.oldNodeArray.length; i++) {
|
||||
if(diffNode.oldNodeArray[i]==='comment'||isUndef(diffNode.oldNodeArray[i].style)){
|
||||
continue
|
||||
}
|
||||
if(diffNode.oldNodeArray[i].className==='cell'){
|
||||
let rowVnodeElm = findRowVnodeElm(diffNode.oldNodeArray[i]);
|
||||
rowVnodeElm.style.setProperty("background-color","rgb(241,200,196)",'important')
|
||||
}else{
|
||||
changeStyleBySubset(diffNode.oldNodeArray[i],"rgb(241,200,196)");
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < diffNode.nodeArray.length; i++) {
|
||||
if(diffNode.nodeArray[i]==='comment'||isUndef(diffNode.nodeArray[i].style)){
|
||||
continue
|
||||
}
|
||||
if(diffNode.nodeArray[i].className==='cell'){
|
||||
let rowVnodeElm = findRowVnodeElm(diffNode.nodeArray[i]);
|
||||
rowVnodeElm.style.setProperty("background-color","rgb(215, 243, 215)",'important')
|
||||
}else{
|
||||
changeStyleBySubset(diffNode.nodeArray[i],"rgb(215, 243, 215)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function changeStyleBySubset(vnodeElm,color){
|
||||
if(isDef(vnodeElm.children)&&vnodeElm.children.length>0){
|
||||
if(isDef(vnodeElm.style)){
|
||||
vnodeElm.style.setProperty("background-color",color,'important')
|
||||
}
|
||||
for (let i = 0; i < vnodeElm.children.length; i++) {
|
||||
changeStyleBySubset(vnodeElm.children[i],color);
|
||||
}
|
||||
}else {
|
||||
if(isDef(vnodeElm.style)){
|
||||
vnodeElm.style.setProperty("background-color",color,'important')
|
||||
}else {
|
||||
vnodeElm.parentNode.style.setProperty("background-color",color,'important')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param oldChildren Array<VNode>
|
||||
* @param newChildren Array<VNode>
|
||||
* @param diffNode
|
||||
* @param isCompareChildren
|
||||
*/
|
||||
function diffChildren(oldChildren,newChildren,diffNode,isCompareChildren){
|
||||
let oldLength = oldChildren.length;
|
||||
let newLength = newChildren.length;
|
||||
//如果isCompareChildren===true,证明是需要轮巡比较table内容是否有重复的,方法是比较每个元素的最子dom,tr的td的length是相等的;
|
||||
if(isCompareChildren===true){
|
||||
let oldIndexArray = [];
|
||||
let newIndexArray = [];
|
||||
//现在oldChildren,newChildren是tbody的所有数据,
|
||||
for (let i = 0; i < oldLength; i++) {
|
||||
for (let j = 0; j < newLength; j++) {
|
||||
let sameNode = {
|
||||
nodeArray:[],
|
||||
}
|
||||
//找每行的数据,但是行是确定的,这里应该直接比较每个tr的 children
|
||||
sameDetail(oldChildren[i],newChildren[j],sameNode)
|
||||
if(sameNode.nodeArray.length>0){
|
||||
//证明oldChildren[i]与 newChildren[j] 是相同的 内容
|
||||
if(oldIndexArray.indexOf(i) === -1){
|
||||
oldIndexArray.push(i);
|
||||
}
|
||||
if(newIndexArray.indexOf(j) === -1){
|
||||
newIndexArray.push(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let j = 0; j < oldLength; j++) {
|
||||
if(oldIndexArray.indexOf(j)===-1){
|
||||
diffNode.oldNodeArray.push(oldChildren[j].elm);
|
||||
}
|
||||
}
|
||||
|
||||
for (let j = 0; j < newLength; j++) {
|
||||
if(newIndexArray.indexOf(j)===-1){
|
||||
diffNode.nodeArray.push(newChildren[j].elm);
|
||||
}
|
||||
}
|
||||
|
||||
}else {
|
||||
//取二者公共长度
|
||||
let childrenLength = Math.min(oldLength, newLength);
|
||||
for (let i = 0; i < childrenLength; i++) {
|
||||
let oldVnode = oldChildren[i]
|
||||
let newVnode = newChildren[i]
|
||||
diffDetail(oldVnode,newVnode,diffNode)
|
||||
}
|
||||
for (let i = childrenLength; i <= (oldLength - childrenLength); i++) {
|
||||
if(oldChildren[i]){
|
||||
diffNode.oldNodeArray.push(oldChildren[i].elm);
|
||||
}
|
||||
}
|
||||
for (let i = childrenLength; i <= (newLength - childrenLength); i++) {
|
||||
if(newChildren[i]){
|
||||
diffNode.nodeArray.push(newChildren[i].elm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function sameChildren(oldChildren,newChildren,sameNode){
|
||||
let oldLength = oldChildren.length;
|
||||
for (let i = 0; i < oldLength; i++) {
|
||||
let oldVnode = oldChildren[i]
|
||||
let newVnode = newChildren[i]
|
||||
sameDetail(oldVnode,newVnode,sameNode)
|
||||
if(sameNode.nodeArray.length===0){
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sameDetail(oldVnode,newVnode,sameNode){
|
||||
if(isDef(oldVnode.child)&&isDef(newVnode.child)){
|
||||
let ovnode = oldVnode.child._vnode;
|
||||
let nvnode = newVnode.child._vnode;
|
||||
sameDetail(ovnode,nvnode,sameNode)
|
||||
if(sameNode.nodeArray.length===0){
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(isDef(oldVnode.children)&&isDef(newVnode.children)){
|
||||
sameChildren(oldVnode.children,newVnode.children,sameNode)
|
||||
}
|
||||
//剩最后的子节点的时候,分类型做判断
|
||||
if(isUndef(oldVnode.child)&&isUndef(newVnode.child)&&isUndef(oldVnode.children)&&isUndef(newVnode.children)){
|
||||
if(isDef(oldVnode.text)&&isDef(newVnode.text)){
|
||||
if(oldVnode.text===newVnode.text){
|
||||
sameNode.nodeArray.push(newVnode.elm);
|
||||
}else{
|
||||
sameNode.nodeArray = [];
|
||||
}
|
||||
}else if(isDef(oldVnode.tag)&&isDef(newVnode.tag)){
|
||||
if(oldVnode.tag==='input'&&newVnode.tag==='input'){
|
||||
if(oldVnode.elm.value===newVnode.elm.value){
|
||||
if(oldVnode.elm.checked!==undefined&&newVnode.elm.checked!==undefined){
|
||||
if(oldVnode.elm.checked===newVnode.elm.checked){
|
||||
sameNode.nodeArray.push(newVnode.elm);
|
||||
}else {
|
||||
sameNode.nodeArray = [];
|
||||
}
|
||||
}else {
|
||||
sameNode.nodeArray.push(newVnode.elm);
|
||||
}
|
||||
}else{
|
||||
sameNode.nodeArray = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(sameVnode(oldVnode,newVnode)){
|
||||
sameNode.nodeArray.push(newVnode.elm);
|
||||
}else{
|
||||
sameNode.nodeArray = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function diffDetail(oldVnode,newVnode,diffNode){
|
||||
if(isDef(oldVnode.child)&&isUndef(newVnode.child)){
|
||||
diffNode.oldNodeArray.push(oldVnode.child._vnode.elm);
|
||||
}
|
||||
if(isDef(oldVnode.children)&&isUndef(newVnode.children)){
|
||||
diffNode.oldNodeArray.push(oldVnode.elm);
|
||||
}
|
||||
if(isUndef(oldVnode.child)&&isDef(newVnode.child)){
|
||||
diffNode.nodeArray.push(newVnode.child._vnode.elm);
|
||||
}
|
||||
if(isUndef(oldVnode.children)&&isDef(newVnode.children)){
|
||||
diffNode.nodeArray.push(newVnode.elm);
|
||||
}
|
||||
if(isDef(oldVnode.child)&&isDef(newVnode.child)){
|
||||
let ovnode = oldVnode.child._vnode;
|
||||
let nvnode = newVnode.child._vnode;
|
||||
diffDetail(ovnode,nvnode,diffNode)
|
||||
}
|
||||
if(isDef(oldVnode.children)&&isDef(newVnode.children)){
|
||||
//处理节点数据结构为table的情况
|
||||
let isCompareChildren = false;
|
||||
if(oldVnode.tag==='tbody'&&newVnode.tag==='tbody'){
|
||||
isCompareChildren = true;
|
||||
}else
|
||||
if(isDef(oldVnode.elm.className)&&isDef(newVnode.elm.className)){
|
||||
if(oldVnode.elm.className==='el-collapse'&&newVnode.elm.className==='el-collapse'){
|
||||
isCompareChildren = true;
|
||||
}
|
||||
}
|
||||
diffChildren(oldVnode.children,newVnode.children,diffNode,isCompareChildren)
|
||||
}
|
||||
//剩最后的子节点的时候,分类型做判断
|
||||
if(isUndef(oldVnode.child)&&isUndef(newVnode.child)&&isUndef(oldVnode.children)&&isUndef(newVnode.children)){
|
||||
|
||||
if(isDef(oldVnode.text)&&isDef(newVnode.text)){
|
||||
if(oldVnode.text!==newVnode.text){
|
||||
if(isDef(oldVnode.elm.style)){
|
||||
diffNode.oldNodeArray.push(oldVnode.elm);
|
||||
}else {
|
||||
diffNode.oldNodeArray.push(oldVnode.elm.parentNode);
|
||||
}
|
||||
if(isDef(newVnode.elm.style)){
|
||||
diffNode.nodeArray.push(newVnode.elm);
|
||||
}else {
|
||||
diffNode.nodeArray.push(newVnode.elm.parentNode);
|
||||
}
|
||||
}
|
||||
}else if(isDef(oldVnode.tag)&&isDef(newVnode.tag)){
|
||||
if(oldVnode.tag==='input'&&newVnode.tag==='input'){
|
||||
if(oldVnode.elm.value!==newVnode.elm.value){
|
||||
diffNode.oldNodeArray.push(oldVnode.elm);
|
||||
diffNode.nodeArray.push(newVnode.elm);
|
||||
}else {
|
||||
if(oldVnode.elm.checked!==undefined&&newVnode.elm.checked!==undefined){
|
||||
if(oldVnode.elm.checked!==newVnode.elm.checked){
|
||||
diffNode.oldNodeArray.push(oldVnode.elm);
|
||||
diffNode.nodeArray.push(newVnode.elm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!sameVnode(oldVnode,newVnode)){
|
||||
diffNode.oldNodeArray.push(oldVnode.elm);
|
||||
diffNode.nodeArray.push(newVnode.elm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function sameVnode (a, b) {
|
||||
return (
|
||||
a.key === b.key &&
|
||||
a.asyncFactory === b.asyncFactory && (
|
||||
(
|
||||
a.tag === b.tag &&
|
||||
a.isComment === b.isComment &&
|
||||
isDef(a.data) === isDef(b.data) &&
|
||||
sameInputType(a, b)
|
||||
) || (
|
||||
isTrue(a.isAsyncPlaceholder) &&
|
||||
isUndef(b.asyncFactory.error)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
function findRowVnodeElm(nodeElm){
|
||||
if(nodeElm.localName==="td"||nodeElm.className==="cell"){
|
||||
return findRowVnodeElm(nodeElm.parentNode)
|
||||
}else if(nodeElm.localName==="tr"){
|
||||
return nodeElm;
|
||||
}else {
|
||||
return nodeElm;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function sameInputType (a, b) {
|
||||
if (a.tag !== 'input') return true
|
||||
let i
|
||||
const typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type
|
||||
const typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type
|
||||
return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB)
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/* @flow */
|
||||
import {Component, ComponentOptions, VNodeComponentOptions, VNodeData} from "vue";
|
||||
|
||||
export default class VNode {
|
||||
tag: string | void;
|
||||
data: VNodeData | void;
|
||||
children: VNode[];
|
||||
text: string | void;
|
||||
elm: Node | void;
|
||||
ns: string | void;
|
||||
context: Component | void; // rendered in this component's scope
|
||||
key: string | number | void;
|
||||
componentOptions: VNodeComponentOptions | void;
|
||||
componentInstance: Component | void; // component instance
|
||||
parent: VNode | void; // component placeholder node
|
||||
|
||||
// strictly internal
|
||||
raw: boolean; // contains raw HTML? (server only)
|
||||
isStatic: boolean; // hoisted static node
|
||||
isRootInsert: boolean; // necessary for enter transition check
|
||||
isComment: boolean; // empty comment placeholder?
|
||||
isCloned: boolean; // is a cloned node?
|
||||
isOnce: boolean; // is a v-once node?
|
||||
asyncFactory: Function | void; // async component factory function
|
||||
asyncMeta: Object | void;
|
||||
isAsyncPlaceholder: boolean;
|
||||
ssrContext: Object | void;
|
||||
fnContext: Component | void; // real context vm for functional nodes
|
||||
fnOptions: any; // for SSR caching
|
||||
devtoolsMeta: Object; // used to store functional render context for devtools
|
||||
fnScopeId: string; // functional scope id support
|
||||
|
||||
constructor(
|
||||
tag?: string | void,
|
||||
data?: VNodeData | void,
|
||||
children?: VNode[],
|
||||
text?: string | void,
|
||||
elm?: Node | void,
|
||||
context?: Component | void,
|
||||
componentOptions?: VNodeComponentOptions | void,
|
||||
asyncFactory?: Function | void
|
||||
) {
|
||||
this.tag = tag
|
||||
this.data = data
|
||||
this.children = children
|
||||
this.text = text
|
||||
this.elm = elm
|
||||
this.ns = undefined
|
||||
this.context = context
|
||||
this.fnContext = undefined
|
||||
this.fnOptions = undefined
|
||||
this.fnScopeId = undefined
|
||||
this.key = data && data.key
|
||||
this.componentOptions = componentOptions
|
||||
this.componentInstance = undefined
|
||||
this.parent = undefined
|
||||
this.raw = false
|
||||
this.isStatic = false
|
||||
this.isRootInsert = true
|
||||
this.isComment = false
|
||||
this.isCloned = false
|
||||
this.isOnce = false
|
||||
this.asyncFactory = asyncFactory
|
||||
this.asyncMeta = undefined
|
||||
this.isAsyncPlaceholder = false
|
||||
}
|
||||
|
||||
// DEPRECATED: alias for componentInstance for backwards compat.
|
||||
/* istanbul ignore next */
|
||||
get child (): Component | void {
|
||||
return this.componentInstance
|
||||
}
|
||||
}
|
||||
|
||||
export function cloneVNode (vnode: VNode): VNode {
|
||||
const cloned = new VNode(
|
||||
vnode.tag,
|
||||
vnode.data,
|
||||
// #7975
|
||||
// clone children array to avoid mutating original in case of cloning
|
||||
// a child.
|
||||
vnode.children && vnode.children.slice(),
|
||||
vnode.text,
|
||||
vnode.elm,
|
||||
vnode.context,
|
||||
vnode.componentOptions,
|
||||
vnode.asyncFactory
|
||||
)
|
||||
cloned.ns = vnode.ns
|
||||
cloned.isStatic = vnode.isStatic
|
||||
cloned.key = vnode.key
|
||||
cloned.isComment = vnode.isComment
|
||||
cloned.fnContext = vnode.fnContext
|
||||
cloned.fnOptions = vnode.fnOptions
|
||||
cloned.fnScopeId = vnode.fnScopeId
|
||||
cloned.asyncMeta = vnode.asyncMeta
|
||||
cloned.isCloned = true
|
||||
return cloned
|
||||
}
|
Loading…
Reference in New Issue