v-if/v-show基本使用
本文将会讲解v-if/v-show之间的使用以及简单实现。
v-if
- 是条件渲染,条件为真,元素才会被渲染,为假,则不会渲染
- 首次不会渲染,开销小
- 元素切换高开销
- 具有v-if、v-else-if v-else
v-show
- 通过style的display来达到显示隐藏
- 首次会渲染,开销大
- 元素切换低开销
基本使用
var App = {
data() {
return {
isShowImg1: false,
isShowImg2: false
}
},
template: `
<div>
<div>
<img width="200" src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1111%2F0Q91Q50307%2F1PQ9150307-8.jpg&refer=http%3A%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1646723639&t=d56432506160e9523b3129375d285d7a" v-if="isShowImg1" />
<img width="200" src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F911%2F0R415123342%2F150R4123342-6-1200.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1646723639&t=ad8ee9e66ef1b282b78dcf00298425d0" v-show="isShowImg2" />
</div>
<button @click="handleShowImg1">显示图片1</button>
<button @click="handleShowImg2">显示图片2</button>
</div>
`,
methods: {
handleShowImg1() {
this.isShowImg1 = !this.isShowImg1
},
handleShowImg2() {
this.isShowImg2 = !this.isShowImg2
}
}
}
var vm = Vue.createApp(App).mount('#app')
console.log(vm)
简单实现v-if/v-show
app.js
import Vue from "./modules/Vue"
var vm = new Vue({
el: '#app',
data() {
return {
isShowImg1: false,
isShowImg2: false
}
},
template: `
<div>
<div>
<img width="200" src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1111%2F0Q91Q50307%2F1PQ9150307-8.jpg&refer=http%3A%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1646723639&t=d56432506160e9523b3129375d285d7a" v-if="isShowImg1" />
<img width="200" src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F911%2F0R415123342%2F150R4123342-6-1200.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1646723639&t=ad8ee9e66ef1b282b78dcf00298425d0" v-show="isShowImg2" />
</div>
<button @click="handleShowImg1">显示图片1</button>
<button @click="handleShowImg2">显示图片2</button>
</div>
`,
methods: {
handleShowImg1() {
this.isShowImg1 = !this.isShowImg1
},
handleShowImg2() {
this.isShowImg2 = !this.isShowImg2
}
}
})
console.log(vm)
Vue.js
var Vue = (function() {
function Vue(options) {
// 将$el、$data暴露在实例中
this.$el = document.querySelector(options.el)
this.$data = options.data()
this._init(this, options.template, options.methods)
}
Vue.prototype._init = function(vm, template, methods) {
var container = document.createElement('div')
container.innerHTML = template
var showPool = new Map(),
eventPool = new Map();
// 数据劫持 监听数据
initData(vm, showPool)
// 存储属性、事件
initPool(container, methods, showPool, eventPool)
// 绑定事件
bindEvent(vm, eventPool)
// 渲染
render(vm, showPool, container)
}
function initData(vm, showPool) {
var _data = vm.$data
for(var key in _data) {
(function(key) {
// 对数据进行监听 vm.key -> vm.$data.key
Object.defineProperty(vm, key, {
get: function() {
return _data[key]
},
set: function(newValue) {
_data[key] = newValue
// 数据改变时触发update 更新视图
update(vm, key, showPool)
}
})
})(key)
}
}
function initPool(container, methods, showPool, eventPool) {
var allNodes = container.getElementsByTagName('*'),
dom = null;
for(var i = 0; i < allNodes.length; i++) {
dom = allNodes[i]
// 获取dom元素上的属性 v-if v-show click
var vIfData = dom.getAttribute('v-if'),
vShowData = dom.getAttribute('v-show'),
vEventData = dom.getAttribute('@click');
if(vIfData) { // v-if存在时
showPool.set(
dom,
{
type: 'v-if',
prop: vIfData
}
)
dom.removeAttribute('v-if')
} else if(vShowData) { // v-show存在时
showPool.set(
dom,
{
type: 'v-show',
prop: vShowData
}
)
dom.removeAttribute('v-show')
}
if(vEventData) { // click事件存在时
eventPool.set(
dom,
methods[vEventData]
)
// 每次在最后需要把元素上绑定的属性删除
dom.removeAttribute('@click')
}
}
}
function bindEvent(vm, eventPool) {
for(var [ dom, handle ] of eventPool) {
// 把methos里面的方法暴露在vm/this里面
vm[handle.name] = handle
// 给dom绑定对应的方法 改变this指向
dom.addEventListener('click', handle.bind(vm), false)
}
}
function render(vm, showPool, container) {
var _data = vm.$data,
_el = vm.$el;
// Map数据可以迭代 使用for of
for(var [ dom, info ] of showPool) {
switch(info.type) {
case 'v-if':
// 创建注释节点 prop为false时 把dom替换成注释节点 replaceChild
info.commont = document.createComment(['v-if'])
!_data[info.prop] && dom.parentNode.replaceChild(info.commont, dom)
break;
case 'v-show':
// prop为false时 设置dom的display为none
!_data[info.prop] && (dom.style.display = 'none')
break;
default:
break;
}
}
// 将container插入到el中
_el.appendChild(container)
}
function update(vm, key, showPool) {
var _data = vm.$data
for(var [ dom, info ] of showPool) {
if(info.prop === key) {
switch(info.type) {
case 'v-if':
!_data[info.prop] ? dom.parentNode.replaceChild(info.commont, dom)
: info.commont.parentNode.replaceChild(dom, info.commont)
break;
case 'v-show':
// 当prop存在时 不建议直接removeAttribute style属性 因为可能其他影响其他地方
!_data[info.prop] ? dom.style.display = 'none'
: dom.style.display = 'block'
break;
default:
break;
}
}
}
}
return Vue
})()
export default Vue