Vue模板
模板语法
- Vue->template中的HTML字符串->Vue的特性:表达式、指令、属性等
- Vue的模板都是基于HTML的
Vue编译过程
- template
- 分析 HTML字符串
- 转成AST树
- 变成表达式、指令、属性
- 虚拟DOM
- render 真实节点
为什么需要虚拟DOM
为什么需要虚拟DOM?
当存在一个<span>Vue2</span>
使用span.innerText = 'Vue2' 更改文本内容时
其实更新的是同一个值 这时候就没必要进行重新渲染
转成虚拟DOM对象 就可以去对比新值和旧值 如一致则不进行操作,减少少做DOM次数
插值表达式
{{ }} -> Mustache
html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script type="module" src="./app.js"></script>
</body>
</html>
js文件
/**
* {{ }}
* 插值表达式
* Mustache
* npm install mustache --save
*/
import { h } from '@vue/runtime-core';
import Mustache from 'mustache';
function MustacheTest() {
const data = {
title: 'This is my MUSTACHE'
}
const html = Mustache.render(
`<div>
<h1>{{ title }}</h1>
</div>`,
data
)
document.getElementById('app').innerHTML = html
}
function Test() {
const App = {
data() {
return {
title: 'This is my TITLE'
}
},
template: `
<div>
<p>{{ title }}</p>
</div>
`
}
Vue.createApp(App).mount('#app')
}
/**
* 也可以使用render + h 函数来渲染
*/
function Test2() {
const App = {
data() {
return {
title: 'This is my TITLE',
author: 'aPeng',
dateTime: Date.now(),
content: 'This is my CONTENT'
}
},
render() {
return h(
'div',
{},
[
h(
'h1',
{
class: 'title'
},
this.title
),
h(
'span',
{
class: 'author'
},
this.author
),
`- ${ this.dateTime }`,
h(
'p',
{
title: this.content
},
this.content
)
]
)
}
}
Vue.createApp(App).mount('#app')
}
export {
Test,
Test2,
MustacheTest
}
Vue指令
directive指令
模板按照特定的逻辑进行渲染或者绑定的行为,template中属性以v-*都是指令
Vue内置的指令:v-if v-else-if v-else v-show v-if v-for v-once v-html
v-once
只会插值一次,后续改变值也不会重新渲染;v-once指令下的所有子元素也会受影响,不建议使用。
function DirectiveTest() {
// 可以单独在实例外定义一个变量
let title = 'This is my TITLE'
const App = {
data() {
return {
// title: 'This is my TITLE',
author: 'aPeng'
}
},
// 要想视图上的数据变量是响应式的 那么变量就必须声明在实例上
// 这样就可以达到v-once只插值一次 后续永不更新的效果
template: `
<div>
<p class="title" v-once>
${title} -----
<span>{{ author }}</span>
</p>
<button @click="changeTitle">CHANGE TITLE</button>
</div>
`,
methods: {
changeTitle() {
// this.title = 'This is your TITLE'
title = 'This is your TITLE'
this.author = '张三'
}
}
}
Vue.createApp(App).mount('#app')
}
export {
DirectiveTest
}
v-html
当内容是普通的HTML文本时 直接使用{{}}插值表达式是不会进行渲染的,因为v-html中的内容不会经过Vue的模板编译 无法使用Vue的指令 属性 方法等...,所有不要把v-html里面的内容作为子模板。
不要随便使用v-html来渲染任意HTML内容,容易遭受XSS攻击;防止在html中利用某些标签进行脚本注入 img onerror。
function DirectiveTest2() {
const show = false
const App = {
data() {
return {
content: '<h1 v-if="show">This is my CONTENT</h1><img src="sas" onerror="alert(1)" />'
}
},
// 这样会原封不动的直接渲染这个字符串 不会渲染成HTML
// template: `
// <div>
// {{ content }}
// </div>
// `
template: `
<div v-html="content">
</div>
`
}
Vue.createApp(App).mount('#app')
}
export {
DirectiveTest,
DirectiveTest2
}
v-if v-else-if v-else v-show
v-if删除,只会渲染满足条件的元素;每次切换都会在切换的地方插入一个注释节点,
每次需要移除、插入和删除都通过这个注释节点来操作。
v-show隐藏节点,元素会全部渲染出来,通过控制元素的display:none来控制元素的隐藏和显示
const App = {
data() {
return {
linkIndex: 0,
urls: [
'https://www.baidu.com',
'https://www.tmall.com',
'https://www.jd.com'
]
}
},
template: `
<!-- 为了方便演示v-if v-show 才这个例子 其实不推荐这么写 有更好的方式 比如对象属性obj[key]-->
<div>
<div>
<!--
<p v-if="linkIndex === 0">
<a :href="urls[linkIndex]" target="_blank">淘宝商城</a>
</p>
<p v-else-if="linkIndex === 1">
<a :href="urls[linkIndex]" target="_blank">天猫商城</a>
</p>
<p v-else>
<a :href="urls[linkIndex]" target="_blank">京东商城</a>
</p>
-->
<p v-show="linkIndex === 0">
<a :href="urls[linkIndex]" target="_blank">淘宝商城</a>
</p>
<p v-show="linkIndex === 1">
<a :href="urls[linkIndex]" target="_blank">天猫商城</a>
</p>
<p v-show="linkIndex === 2">
<a :href="urls[linkIndex]" target="_blank">京东商城</a>
</p>
</div>
<div>
<button @click="changeIndex(0)">淘宝</button>
<button @click="changeIndex(1)">天猫</button>
<button @click="changeIndex(2)">京东</button>
</div>
</div>
`,
methods: {
changeIndex(index) {
this.linkIndex = index
}
}
}
Vue.createApp(App).mount('#app')
v-bind v-on
v-bind绑定属性,插入表达式,v-bind:href="url",简写:href;支持动态绑定属性,通过在[]里面填写动态参数的变量;属性无法绑定null,可以通过将动态属性设置为null,以达到移除属性效果。
v-on事件绑定,绑定事件处理函数,v-on:click="handleClick", 简写@click;支持动态绑定事件,通过在[]里面填写动态事件的变量
const App = {
data() {
return {
attrName: 'data-name',
eventName: 'click'
}
},
template: `
<div>
<p :data-name="'p'">v-bind</p>
<p :[attrName]="'attrName'">attrName</p>
<!-- 属性绑定 是无法做拼接的 因为在HTML元素属性里面无法出现'' "" 空格等符号 -->
<!-- <p :[attrName + 's']="'attrName'">attrName</p> -->
<p>绑定的事件:{{ eventName }}</p>
<button @click="changeAttr('title')">CHANGE ATTR</button>
<button @click="deleteAttr(null)">DELETE ATTR</button>
<button @[eventName]="changeEvent('change')">CHANGE EVENT</button>
</div>
`,
methods: {
changeAttr(attrName) {
this.attrName = attrName
},
deleteAttr(attrName) {
this.attrName = attrName
},
changeEvent(fn) {
this.eventName = fn
}
}
}
Vue.createApp(App).mount('#app')
最后,简单写了一个思维导读以便于回顾和记忆: