16、vue之分页组件(含勾选、过滤、ES6写法),Vue框架源码概览,Vue之Observer、Dep和Watcher,Vue.set应用实例,基于ElementUI的vue自定义组件
程序员文章站
2022-04-22 10:51:07
...
16、vue之分页组件(含勾选、过滤、ES6写法),Vue框架源码概览,Vue之Observer、Dep和Watcher,Vue.set应用实例,基于ElementUI的vue自定义组件、el-dialog三层弹窗、各弹窗的区别
一、vue之分页组件(含勾选、过滤,单页面)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>勾选和分页组件之vue2.6.10版</title>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<style>
table{
border-collapse: collapse;
border: 1px solid #cbcbcb;
width:1000px;
}
table td,table th {
padding: 5px;
border: 1px solid #cbcbcb;
}
table thead {
background-color: #e0e0e0;
color: #000;
text-align: left;
}
.filter{
width:998px;
border:1px solid gray;
padding:10px 0px;
}
.line{
display:flex
}
.group{
width:330px;
}
.label{
display: inline-block;
width:120px;
height: 24px;
line-height: 24px;
text-align: right;
}
.input{
display: inline-block;
width:180px;
height: 24px;
line-height: 24px;
border-radius: 3px;
}
.select{
display: inline-block;
width:188px;
height: 26px;
line-height: 26x;
border-radius: 3px;
}
</style>
</head>
<body>
<div id="app">
<div style="padding-bottom:5px;color:red">
<button style="color:red" @click="checkDatasOne.getResultOfCheckAndFilter(divideDatasOne.isShowFilter,divideDatasOne.filterOptions)">获取勾选和过滤结果</button>
<span>{{checkDatasOne.toServerDatas}}</span>
</div>
<div style="padding-bottom:5px">
<img :src="checkDatasOne.stateAllPages&&checkDatasOne.allExcludedIds.length===0?checkImg.yes:checkImg.no" @click="checkDatasOne.clickAllPages(divideDatasOne.tableDatas)"/>
<span>{{checkDatasOne.textAllPages}}</span>
</div>
<div style="padding-bottom:5px">
<button @click="divideDatasOne.toggleShowFilter()">{{divideDatasOne.isShowFilter?'关闭过滤':'使用过滤'}}</button>
<button @click="divideDatasOne.emptyFilterOptions({value5:10})">清空过滤</button>
<button @click="divideDatasOne.request(1)">刷新</button>
</div>
<div style="margin-bottom:5px" class="filter" v-show="divideDatasOne.isShowFilter">
<div class="line">
<div class="group">
<label class="label">标签</label>
<input class="input" type="text" v-model="divideDatasOne.filterOptions.value1" />
</div>
<div class="group">
<label class="label">这就是长标签</label>
<input class="input" type="text" v-model="divideDatasOne.filterOptions.value2" />
</div>
<div class="group">
<label class="label">标签</label>
<input class="input" type="text" v-model="divideDatasOne.filterOptions.value3" />
</div>
</div>
<div class="line" style="padding-top: 10px;">
<div class="group">
<label class="label">这就是长标签</label>
<input class="input" type="text" v-model="divideDatasOne.filterOptions.value4" />
</div>
<div class="group">
<label class="label">下拉框</label>
<select class="select" v-model="divideDatasOne.filterOptions.value5">
<option v-for="item in selectOptions" :value="item.back">{{item.front}}</option>
</select>
</div>
<div class="group">
<label class="label"></label>
<button style="width:188px;height:28px" @click="divideDatasOne.request(1)">过滤</button>
</div>
</div>
</div>
<div style="height:353px;width:1000px">
<table>
<thead>
<tr>
<th><img :src="checkDatasOne.stateThisPage?checkImg.yes:checkImg.no"
@click="checkDatasOne.clickThisPage(divideDatasOne.tableDatas,divideDatasOne.allItemsNum)"/></th>
<th>序号</th>
<th>数据1</th>
<th>数据2</th>
<th>数据3</th>
<th>数据4</th>
<th>数据5</th>
<th>数据6</th>
</tr>
</thead>
<tbody>
<tr v-for="(data,index) in divideDatasOne.tableDatas">
<td><img :src="data.state?checkImg.yes:checkImg.no" @click="checkDatasOne.clickSingleItem(data,divideDatasOne.tableDatas,divideDatasOne.allItemsNum)"/></td>
<td>{{(divideDatasOne.nowPageNum-1)*10 + (index+1)}} </td>
<td>{{ data.key1 }}</td>
<td>{{ data.key2 }}</td>
<td>{{ data.key3 }}</td>
<td>{{ data.key4 }}</td>
<td>{{ data.key5 }}</td>
<td>{{ data.key6 }}</td>
</tr>
</tbody>
</table>
</div>
<divide-page :divide-datas="divideDatasOne" :check-datas="checkDatasOne" :fixed-datas="fixedDatas"></divide-page>
</div>
</body>
<script>
new Vue({
el: '#app',
data(){
return {
divideDatasOne:{
nowPageNum:0,
allPagesNum:0,
allItemsNum:0,
tableDatas:[],
filterOptions:{value5:10},
isShowFilter:false,
otherDatas:{}
},
checkDatasOne:{
idKey: 'id',//每条数据的唯一标志
stateThisPage: false,//当前页所有项是否全选
allIncludedIds: [],//所有被选中数据的ID构成的数组
allExcludedIds: [],//所有没被选中数据的ID构成的数组
textAllPages: '全选未启用,没有选择任何项!',//复选框被点击后的提示文字。
stateAllPages: false,//复选框被点击后的提示文字。
toServerDatas: null,
},
}
},
methods: {
},
created(){
this.fixedDatas = {
};
this.selectOptions = [
{ back: 10, front: '来' },
{ back: 20, front: '来自于' },
{ back: 30, front: '来自于国内' },
{ back: 40, front: '来自于国内攻击' },
{ back: 50, front: '来自于国内攻击-2' }
];
this.checkImg = {
yes: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAA+UlEQVQoFZWSMU4DMRBF/584G7QSRcIxuAZKEykNEiUVHVTQRaKh4AIcgAvQpkukVDlBOAYNSGSlXXuwpViyYYFdS9aMZ/6bsezh5HZ3T2KhqkfosEhWqnjkyd1u3xWKdQMsfaEAB0Zilf8swfdU0w0klmpGpz1BvpbHcklbPf8Okts0CfJtWBTz/Yc++Jc8S3PZVQfKGwiuvMD6XYsMzm1dT/1jXKdQ8E0asHRrAzOzbC6UGINWHPQp1UQ/6wjF2LpmJSKfhti4Bi8+lhWP4I+gAqV1uqSi8j9WRuF3m3eMWVUJBeKxzUoYn7bEX7HDyPmB7QEHbRjyL+/+VnuXDUFOAAAAAElFTkSuQmCC',
no: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAAbklEQVQoFWM8c+ZMLQMDQxUQcwAxMeAHUFEbC5CoYmNj02ZmZn5FjK6/f/+K/fr16ypIIwdIk7a29hdiNF69ehWkjIOJGMXY1IxqxBYqULEhFDiglPMDlIygKQKPryBSILUgPSCNbaC0B6RJSuQAbowizhJuOsAAAAAASUVORK5CYII=',
}
},
components: {
dividePage: {
props: {
divideDatas: {
type: Object,
default: {}
},
checkDatas: {
type: Object,
default: {}
},
fixedDatas: {
type: Object,
default: {}
}
},
template: `
<div v-show="divideDatas.allPagesNum>=1" style="display:flex;width:1000px;margin-top:20px;">
<div>
<button
v-show="divideDatas.allPagesNum>10"
@click="clickDividePage('front') "
:disabled="divideDatas.nowPageNum===1"
>上一页</button>
<button
:disabled="number==='...'"
v-for="number in divideArray"
@click="clickDividePage(number)"
:style="{marginRight:'5px',color:number===divideDatas.nowPageNum?'red':'gray'}"
>{{ number }}</button>
<button
v-show="divideDatas.allPagesNum>10"
@click="clickDividePage('back')"
:disabled="divideDatas.nowPageNum===divideDatas.allPagesNum"
>下一页</button>
</div>
<div style="display:flex; flex:1; justify-content:flex-end;">
<div style="margin-right:20px;">
<span>转到第</span>
<input type="text" v-model="customString" @keydown="clickDividePage('leap',$event)" style="width:30px;">
<span>页</span>
<button @click="clickDividePage('leap',{which:13})">Go</button>
</div>
<div>
<span>每页显示</span>
<select v-model="divideDatas.eachPageItemsNum">
<option v-for="item in numOptions" :value="item.back">{{item.front}}</option>
</select>
<span>条,</span>
</div>
<div>
<span>{{frontMoreText}}</span>
<span>{{totalText}}</span>
<span>{{divideDatas.allItemsNum}}</span>
<span>{{totalUnit}}</span>
<span>{{backMoreText}}</span>
</div>
</div>
</div
`,
data() {
return {
customString:''
}
},
created(){
var that = this;
//1、请求配置
this.url = this.fixedDatas.url || '';
this.method = this.fixedDatas.method || 'post';
this.isShowParams = this.fixedDatas.isShowParams || false;//显式还是隐式传参。有时需要在请求发出前手动改变。
//2、响应配置(前端通过这个配置,获取后台的数据)
this.nowPageNumKey = this.fixedDatas.nowPageNumKey || 'nowPageNumKey';//来自服务器的当前页码key
this.allPagesNumKey = this.fixedDatas.allPagesNumKey || 'allPagesNumKey';//来自服务器的所有页页数key
this.allItemsNumKey = this.fixedDatas.allItemsNumKey || 'allItemsNumKey';//来自服务器的所有页数据数key
this.eachPageItemsNumKey = this.fixedDatas.eachPageItemsNumKey || 'eachPageItemsNumKey';//来自服务器的每页最多数据数key
this.tableDatasKey = this.fixedDatas.tableDatasKey || 'tableDatasKey';//来自服务器的表格数据key
//3、以下配置使用哪种转圈方式(前端根据需要决定,不受后台影响)
this.partCircle = this.fixedDatas.partCircle;//局部是否转圈。this.fixedDatas.partCircle=$scope.partCircle={isShow =false}。
this.isUsePartCircle = this.fixedDatas.isUsePartCircle;//局部是否转圈,由当前页的一个变量控制
this.isUseWholeCircle = this.fixedDatas.isUseWholeCircle;//全局是否转圈,由本项目的一个服务控制
//4、初始化以下数据,供页面使用(前端根据需要决定,不受后台影响)
this.frontMoreText = this.fixedDatas.frontMoreText || "";//('文字 ')或者("文字 "+result.numOne+" 文字 ")
this.totalText = this.fixedDatas.totalText || "";//'共'
this.totalUnit = this.fixedDatas.totalUnit || '条';//总数据的单位
this.backMoreText = this.fixedDatas.backMoreText || "";//(' 文字')或者("文字 "+result.numThree+" 文字")
this.numOptions = [
{ back: 10, front: 10 },
{ back: 20, front: 20 },
{ back: 30, front: 30 },
{ back: 40, front: 40 },
{ back: 50, front: 50 }
];
var data=[];
for(var i=1;i<=144;i++){
var obj={
id:'id'+i,
key1:'数据'+(i+0),
key2:'数据'+(i+1),
key3:'数据'+(i+2),
key4:'数据'+(i+3),
key5:'数据'+(i+4),
key6:'数据'+(i+5),
key7:'数据'+(i+6),
};
data.push(obj)
}
var result={
tableDatasKey:[],
nowPageNumKey:5,
allPagesNumKey:15,
allItemsNumKey:144,
eachPageItemsNum:10,
};
this.request = this.divideDatas.request = function (pageNum) {
//此处向后台发送请求,
//1、返回正确结果result
if(that.divideDatas.trueCb){
that.divideDatas.trueCb(result)
}else{
result.tableDatasKey = data.slice((pageNum-1)*10,(pageNum)*10);
that.customString = pageNum||result.nowPageNumKey;
that.divideDatas.tableDatas = result.tableDatasKey;
that.divideDatas.nowPageNum = pageNum||result.nowPageNumKey;
that.divideDatas.allPagesNum = result.allPagesNumKey;
that.divideDatas.allItemsNum = result.allItemsNumKey;
that.divideDatas.eachPageItemsNum = result.eachPageItemsNum;
if(that.checkDatas && that.checkDatas.signCheckbox){
that.checkDatas.signCheckbox(that.divideDatas.tableDatas)
}
}
that.createDividePage();
//2、返回错误结果result
if(that.divideDatas.errorCb){
that.divideDatas.errorCb(result)
}
};
if (!this.divideDatas.isNoInit) {
this.request(1);
};
this.divideDatas.toggleShowFilter = function () {
this.isShowFilter = !this.isShowFilter;
if (!this.isShowFilter) {
this.request(1);
}
};
this.divideDatas.emptyFilterOptions = function (extraObject) {
//清空选项时,所有值恢复成默认
for(var key in this.filterOptions){
this.filterOptions[key] = undefined;
};
if (extraObject) {
//小部分选项的默认值不是undefined
for(var key in extraObject){
this.filterOptions[key] = extraObject[key];
};
};
this.request(1);
};
this.checkDatas.init=function(){//点击“刷新”、“过滤”、“清除过滤”时执行
this.idKey = idKey ? idKey : 'id';
this.allIncludedIds = [];
this.allExcludedIds = [];
this.textAllPages = '全选未启用,没有选择任何项!';
this.stateAllPages = false;
this.stateThisPage = false;
};
this.checkDatas.clickAllPages = function (itemArray) {//所有页所有条目全选复选框被点击时执行的函数
if(this.stateAllPages){
if(this.allExcludedIds.length>0){
this.stateAllPages = true;
this.stateThisPage = true;
this.textAllPages= '全选已启用,没有排除任何项!';
itemArray.forEach(function (item) {
item.state = true;
});
}else if(this.allExcludedIds.length==0){
this.stateAllPages = false;
this.stateThisPage = false;
this.textAllPages= '全选未启用,没有选择任何项!';
itemArray.forEach(function (item) {
item.state = false;
});
}
}else{
this.stateAllPages = true;
this.stateThisPage = true;
this.textAllPages= '全选已启用,没有排除任何项!';
itemArray.forEach(function (item) {
item.state = true;
});
}
this.allExcludedIds = [];
this.allIncludedIds = [];
};
this.checkDatas.clickThisPage = function (itemsArray,allItemsNum) {//当前页所有条目全选复选框被点击时执行的函数
var that = this;
this.stateThisPage = !this.stateThisPage
itemsArray.forEach(function (item) {
item.state = that.stateThisPage;
if (item.state) {
that.delID(item[that.idKey], that.allExcludedIds);
that.addID(item[that.idKey], that.allIncludedIds);
} else {
that.delID(item[that.idKey], that.allIncludedIds);
that.addID(item[that.idKey], that.allExcludedIds);
}
});
if(this.stateAllPages){
if(this.stateThisPage && this.allExcludedIds.length === 0){
this.textAllPages = '全选已启用,没有排除任何项!';
}else{
this.textAllPages = '全选已启用,已排除'+ this.allExcludedIds.length + '项!排除项的ID为:' + this.allExcludedIds;
}
}else{
if(!this.stateThisPage && this.allIncludedIds.length === 0){
this.textAllPages='全选未启用,没有选择任何项!';
}else{
this.textAllPages = '全选未启用,已选择' + this.allIncludedIds.length + '项!选择项的ID为:' + this.allIncludedIds;
}
}
};
this.checkDatas.clickSingleItem = function (item, itemsArray, allItemsNum) {//当前页单个条目复选框被点击时执行的函数
var that = this;
item.state = !item.state;
if (item.state) {
this.stateThisPage = true;
this.addID(item[this.idKey], this.allIncludedIds);
this.delID(item[this.idKey], this.allExcludedIds);
itemsArray.forEach( function (item) {
if (!item.state) {
that.stateThisPage = false;
}
});
} else {
this.stateThisPage = false;
this.addID(item[this.idKey], this.allExcludedIds);
this.delID(item[this.idKey], this.allIncludedIds);
}
if(this.stateAllPages){
if(this.stateThisPage && this.allExcludedIds.length === 0){
this.textAllPages = '全选已启用,没有排除任何项!';
}else{
this.textAllPages = '全选已启用,已排除'+ this.allExcludedIds.length + '项!排除项的ID为:' + this.allExcludedIds;
}
}else{
if(!this.stateThisPage && this.allIncludedIds.length === 0){
this.textAllPages='全选未启用,没有选择任何项!';
}else{
this.textAllPages = '全选未启用,已选择' + this.allIncludedIds.length + '项!选择项的ID为:' + this.allIncludedIds;
}
}
};
this.checkDatas.signCheckbox = function (itemsArray) {//标注当前页被选中的条目,在翻页成功后执行。
var that = this;
if(this.stateAllPages){
this.stateThisPage = true;
itemsArray.forEach(function (item) {
var thisID = item[that.idKey];
var index = that.allExcludedIds.indexOf(thisID);
if (index > -1) {
item.state = false;
that.stateThisPage = false;
} else {
item.state = true;
}
});
}else{
this.stateThisPage = true;
itemsArray.forEach( function (item) {
var thisID = item[that.idKey];
var index = that.allIncludedIds.indexOf(thisID);
if (index === -1) {
item.state = false;
that.stateThisPage = false;
}
});
}
};
this.checkDatas.addID = function (id, idArray) {
var index = idArray.indexOf(id);
if (index === -1) {
idArray.push(id);//如果当前页的单项既有勾选又有非勾选,这时勾选当前页全选,需要这个判断,以免重复添加
}
};
this.checkDatas.delID = function (id, idArray) {
var index = idArray.indexOf(id);
if (index > -1) {
idArray.splice(index, 1)
}
};
this.checkDatas.getResultOfCheckAndFilter = function (isShowFilter,filterOptions) {//获取发送给后台的所有参数。
var toServerDatas;
var allIncludedIds = that.deepClone(this.allIncludedIds);
var allExcludedIds = that.deepClone(this.allExcludedIds);
if (!this.stateAllPages) {
if (allIncludedIds.length === 0) {
//return 弹窗告知:没有勾选项
}
toServerDatas = {
isSelectAll: false,
allIncludedIds: allIncludedIds,
}
}else {
toServerDatas = { //exclude
isSelectAll: true,
allExcludedIds: allExcludedIds,
};
}
if (isShowFilter) {
for(var key in filterOptions){
toServerDatas[key]=filterOptions[key]
}
}
this.toServerDatas=toServerDatas;//这行代码在实际项目中不需要
return toServerDatas;
}
},
methods: {
deepClone : function (arrayOrObject) {
function isArray(value) { return {}.toString.call(value) === "[object Array]"; }
function isObject(value) { return {}.toString.call(value) === "[object Object]"; }
var target = null;
if (isArray(arrayOrObject)) target = [];
if (isObject(arrayOrObject)) target = {};
for (var key in arrayOrObject) {
var value = arrayOrObject[key];
if (isArray(value) || isObject(value)) {
target[key] = deepClone(value);
} else {
target[key] = value;
}
}
return target;
},
createDividePage : function () {
var divideArray = [];
var allPagesNum = this.divideDatas.allPagesNum;
var nowPageNum = this.divideDatas.nowPageNum;
if (allPagesNum >= 1 && allPagesNum <= 10) {
for (var i = 1; i <= allPagesNum; i++) {
divideArray.push(i);
}
} else if (allPagesNum >= 11) {
if (nowPageNum > 6) {
divideArray.push(1);
divideArray.push(2);
divideArray.push(3);
divideArray.push('...');
divideArray.push(nowPageNum - 1);
divideArray.push(nowPageNum);
} else {
for (i = 1; i <= nowPageNum; i++) {
divideArray.push(i);
}
}
// 以上当前页的左边,以下当前页的右边
if (allPagesNum - nowPageNum >= 6) {
divideArray.push(nowPageNum + 1);
divideArray.push(nowPageNum + 2);
divideArray.push('...');
divideArray.push(allPagesNum - 2);
divideArray.push(allPagesNum - 1);
divideArray.push(allPagesNum);
} else {
for (var i = nowPageNum + 1; i <= allPagesNum; i++) {
divideArray.push(i);
}
}
}
this.divideArray = divideArray;
},
clickDividePage : function (stringOfNum, event) {
var allPagesNum = this.divideDatas.allPagesNum;
var nowPageNum = this.divideDatas.nowPageNum;
if (stringOfNum === 'front' && nowPageNum != 1) {
nowPageNum--;
} else if (stringOfNum === 'back' && nowPageNum != allPagesNum) {
nowPageNum++;
} else if (stringOfNum === 'leap') {
if (event.which != 13) return;//不拦截情形:(1)聚焦输入框、按“Enter”键时;(2)点击“GO”时
var customNum = Math.ceil(parseFloat(this.customString));
if (customNum < 1 || customNum == 'NaN') {
nowPageNum = 1;//不给提示
} else if(customNum > allPagesNum) {
nowPageNum = allPagesNum;//不给提示
} else {
nowPageNum = customNum;
}
} else {
nowPageNum = Math.ceil(parseFloat(stringOfNum));
}
this.request(nowPageNum);
},
}
}
},
})
</script>
</html>
附:vue之分页组件(含勾选、过滤、ES6写法)
<template>
<div v-show="divideDatas.allPagesNum>=1" style="display:flex;width:1000px;margin-top:20px;">
</div>
</template>
<script>
import comTab from '@/components/ComTab'
export default {
name: 'dividePage',
components: {
comTab
},
props: {
},
data() {
return {
}
},
created(){
},
methods: {
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
</style>
二、Vue框架源码概览
1、自动执行
(1)initMixin(Vue)
=> Vue.prototype._init
2、手动执行new Vue(options)
new Vue(options)
=> this._init(options)
=> initState(vm)
=> initData();initProps();initComputed();initMethods();initWatch();vm.$mount(vm.$options.el)
(1)initData(vm)
=> data=getData(data, vm);observe(data, true);
(2)initProps(vm, opts.props)
=> defineReactive$$1(props, key, value); proxy(vm, "_props", key)
(3)initComputed(vm, opts.computed)
=> vm._computedWatchers[key] = new Watcher(vm,getter)
=> defineComputed(vm, key, userDef)
=> Object.defineProperty(target, key, sharedPropertyDefinition)
(4)initMethods(vm, opts.methods)
=> vm[key] = Function.prototype.bind(methods[key], vm);
(5)initWatch(vm, opts.watch)
=> createWatcher(vm, key, handler)
=> vm.$watch(expOrFn, handler, options)
=> new Watcher(vm, expOrFn, cb, options)
=> vm._watchers.push(this);popTarget()
(6)vm.$mount(vm.$options.el)
=> mountComponent(this, el, hydrating)
=> updateComponent
=> new Watcher(vm,updateComponent);
=> Watcher 2个作用,(1)初始化的时候会执行回调函数,(2)实例中,监测的数据发生变化的时候执行回调函数。
三、Vue之Observer、Dep和Watcher
1、Observer实例化时,给data和props绑定响应式
(1)vue数据赋值给Observer实例的value属性:this.value = value;
(2)Observer实例赋值给vue数据的__ob__属性:def(value, '__ob__', this);
(3)Dep实例赋值给Observer实例的dep属性:this.dep = new Dep();
(4)给数据绑定getter和setter
2、Dep实例化时,
(1)获取id属性
(2)初始化订阅属性
(3)给监听函数提供操作方法
3、Watcher实例化时,给vue实例绑定Watcher实例
(1)vue实例赋值给Watcher实例的vm属性:this.vm = vm;
(2)Watcher实例赋值到vue实例的_watchers属性里:vm._watchers.push(this);
(3)Watcher实例赋值给Dep.target:Dep.target = this;
(4)计算旧值:this.value = this.get();
(5)给依赖提供操作方法
4、实例化过程
(1)实现响应式:初始化过程中initData(vm)、initProps(vm,opts.props)执行时,Observer将数据定义为响应式并建立依赖池。
(2)产生依赖:初始化过程中,initComputed(vm,opts.computed)除了将opts.computed定义为响应式,还和initWatch(vm,opts.watch)一样,执行new Watcher,产生并存放依赖项,用this.get()计算旧值。
(3)注入依赖:初始化过程最后,除了执行new Watcher,还通过render函数对“用到的变量”进行getter操作、通过dep.depend向watcher注入依赖。https://www.cnblogs.com/Qooo/p/13711967.html
(4)使用依赖:数据变化进行setter操作、通过dep.notify执行watcher里的依赖,执行update方法,更新页面。
总之,(1)页面初次渲染时,用初始值和由初始值获取的计算属性值渲染页面,当某个初始值改变时,执行watch监听函数,改变别的初始值,最后再次获取计算属性值并渲染页面。(2)["push","pop","shift","unshift","splice","sort","reverse"];
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);//{}.__proto__ = arrayProto;
[].__proto__ = arrayMethods;
四、Vue.set应用实例
Vue框架只对数组方法中的'push','pop','shift','unshift','splice','sort','reverse'实现了响应式。通过索引改变数组,没有执行发布函数,没法执行订阅函数,需要通过Vue.set来执行发布函数,实现响应式。
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app2">
<p v-for="item in items" :key="item.id">
{{item.message}}
</p>
<button class="btn" @click="btn2Click()">动态赋值</button><br />
<button class="btn" @click="btn3Click()">为data新增属性</button>
</div>
</body>
</html>
<script>
var vm2 = new Vue({
el: "#app2",
data: {
items: [
{ message: "Test one", id: "1" },
{ message: "Test two", id: "2" },
{ message: "Test three", id: "3" }
]
},
methods: {
btn2Click: function () {
Vue.set(this.items, 0, { message: "Change Test", id: '10' })
},
btn3Click: function () {
var itemLen = this.items.length;
Vue.set(this.items, itemLen, { message: "Test add attr", id: itemLen });
}
}
});
</script>
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8">
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
</head>
<body>
<div id="box">{{msg}}||{{reMsg}}</div>
<script type="text/javascript">
var vm = new Vue({
el:'#box',
data:{
msg:'12345'
},
computed:{
reMsg:function(instance){
console.log(instance===this);//true
return this.msg.split('').reverse().join('')
}
}
});
</script>
</body>
</html>
五、基于ElementUI的vue自定义组件子改父
1、通过属性传函数参数来实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>子改父:通过属性传参</title>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script src="https://cdn.bootcss.com/element-ui/2.10.1/index.js"></script>
<link href="https://cdn.bootcss.com/element-ui/2.10.1/theme-chalk/index.css" rel="stylesheet">
<style>
#app{
display: flex;
justify-content: space-between;
}
.parent, .child{
width: 45%;
}
.el-card{
height: 100%;
}
</style>
</head>
<body>
<div style="margin: 30px 0;">本案例改编自https://www.bbsmax.com/A/kjdwmRaOJN/</div>
<div style="margin-bottom: 30px;">
<div>总逻辑 </div>
<div>父组件通过属性传参,给子组件传值 </div>
<div>父组件通过属性传参,给子组件传属性函数 </div>
<div>触发子组件的某个事件,执行属性函数,改变父组件的值 </div>
</div>
<div id="app">
<div class="parent">
<el-card>
<div slot="header">
<span>父组件</span>
</div>
<el-input v-model="ParentMsg"></el-input>
<el-button type="primary" @click="changeChild" style="margin-top: 44px">父组件改变子组件</el-button>
</el-card>
</div>
<div class="child">
<el-card>
<div slot="header">
<span>子组件</span>
</div>
<child :self-msg="childMsg" :fn="changeParent"></child>
</el-card>
</div>
</div>
</body>
<script>
new Vue({
el: '#app',
data(){
return {
ParentMsg:'父组件的内容',
childMsg:'父组件传给子组件的内容'
}
},
methods: {
changeParent(data){
this.ParentMsg = data,
this.childMsg = '子-组件传给子组件的内容'
},
changeChild(){
this.ParentMsg = '父-组件传给父组件的内容',
this.childMsg = '父-组件传给子组件的内容'
}
},
components: {
child:{
props: {
selfMsg: {
type: String,
default: ''
},
fn: {
type: Function,
default: function(){}
}
},
template: `
<div>
<p>{{selfMsg}}</p>
<el-button type='primary' @click='fromChild' style='margin-top: 30px'>子组件改变父组件</el-button>
</div>
`,
data () {
return {
}
},
methods:{
fromChild () {
this.fn('子-组件传给父组件的内容')
}
}
}
},
})
</script>
</html>
2、通过属性传自定义事件来实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Title</title>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script src="https://cdn.bootcss.com/element-ui/2.10.1/index.js"></script>
<link href="https://cdn.bootcss.com/element-ui/2.10.1/theme-chalk/index.css" rel="stylesheet">
<style>
#app{
display: flex;
justify-content: space-between;
}
.parent, .child{
width: 45%;
}
.el-card{
height: 100%;
}
</style>
</head>
<body>
<div style="margin: 30px 0;">本案例改编自https://www.bbsmax.com/A/kjdwmRaOJN/</div>
<div style="margin-bottom: 30px;">
<div>总逻辑 </div>
<div>父组件通过属性传参,给子组件传值 </div>
<div>父组件通过属性传参,给子组件传自定义事件及执行函数 </div>
<div>触发子组件的某个事件,发射自定义事件,并给执行函数传参,改变父组件的值 </div>
</div>
<div id="app">
<div class="parent">
<el-card>
<div slot="header">
<span>父组件</span>
</div>
<el-input v-model="ParentMsg"></el-input>
<el-button type="primary" @click="changeChild" style="margin-top: 44px">父组件改变子组件</el-button>
</el-card>
</div>
<div class="child">
<el-card>
<div slot="header">
<span>子组件</span>
</div>
<child :self-msg="childMsg" @from-child="changeParent"></child>
</el-card>
</div>
</div>
</body>
<script>
new Vue({
el: '#app',
data(){
return {
ParentMsg:'父组件的内容',
childMsg:'父组件传给子组件的内容'
}
},
methods: {
changeParent(data){
this.ParentMsg = data,
this.childMsg = '子-组件传给子组件的内容'
},
changeChild(){
this.ParentMsg = '父-组件传给父组件的内容',
this.childMsg = '父-组件传给子组件的内容'
}
},
components: {
child:{
props: {
selfMsg: {
type: String,
default: ''
}
},
template: `
<div>
<p>{{selfMsg}}</p>
<el-button type='primary' @click='fromChild' style='margin-top: 30px'>子组件改变父组件</el-button>
</div>
`,
data () {
return {
}
},
methods:{
fromChild () {
this.$emit('from-child', '子-组件传给父组件的内容')
}
}
}
},
})
</script>
</html>
四、el-dialog三层弹窗
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>vue2.6.10组件el-dialog之三层弹窗</title>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script src="https://cdn.bootcss.com/element-ui/2.10.1/index.js"></script>
<link href="https://cdn.bootcss.com/element-ui/2.10.1/theme-chalk/index.css" rel="stylesheet">
<style>
#app{
display: flex;
justify-content: space-between;
}
.parent, .child{
width: 45%;
}
.el-card{
height: 100%;
}
</style>
</head>
<body>
<div id="app">
<el-button type="text" @click="outerVisible = true">点击打开外层弹窗</el-button>
<!-- 以下是外层 -->
<el-dialog
width="70%"
title="外层"
:visible.sync="outerVisible">
这是外层
<!-- 以下是中层 -->
<el-dialog
width="50%"
title="中层"
:visible.sync="middleVisible"
append-to-body>
这是中层
<!-- 以下是内层 -->
<el-dialog
width="30%"
title="内层"
:visible.sync="innerVisible"
append-to-body>
这是内层
<div slot="footer" class="dialog-footer">
<el-button @click="innerVisible = false">关闭内层</el-button>
<el-button type="primary" @click="innerVisible = false">关闭内层</el-button>
</div>
</el-dialog>
<!-- 以上是内层 -->
<div slot="footer" class="dialog-footer">
<el-button @click="middleVisible = false">关闭中层</el-button>
<el-button type="primary" @click="innerVisible = true">打开内层</el-button>
</div>
</el-dialog>
<!-- 以上是中层 -->
<div slot="footer" class="dialog-footer">
<el-button @click="outerVisible = false">关闭外层</el-button>
<el-button type="primary" @click="middleVisible = true">打开中层</el-button>
</div>
</el-dialog>
<!-- 以上是外层 -->
</div>
</body>
<script>
new Vue({
el: '#app',
data() {
return {
outerVisible: false,
middleVisible: false,
innerVisible: false
};
},
methods: {
},
components: {
},
})
</script>
</html>
五、elementUI各弹窗的区别
1、第1组(3秒钟后自动消失)
(1)Message 消息提示,常用于主动操作后的反馈提示。
(2)Notification 通知,常用于系统级通知的被动提醒。
2、第2组(点击确认后消失)
(1)MessageBox 弹窗,模拟系统的消息提示框alert、confirm和prompt而实现的一套模态对话框组件,用于消息提示、确认消息和提交内容。
(2)Dialog 对话框,弹出较为复杂的内容.
3、第3组(非悬停时消失)
(1)Tooltip 文字提示,常用于展示鼠标hover时的提示信息。
(2)Popover 弹出框,与Tooltip类似。
上一篇: LINUX ENVIRONMENT
下一篇: 基于Vue的Qabler组件库