文档
- 介绍 — Vue.js
- 风格指南 — Vue.js
- hexo中对占位符的解析 | TonyStudio
Vue是一套用于构建用户界面的渐进式JavaScript框架。
何为渐进式?
Vue可以创建自底向上逐层的应用,从简单应用到复杂应用。
如果是简单应用,只需要一个轻量的核心库。
如果是复杂应用,可以引入各式各样的vue插件。
Vue特点
- 采用组件化模式,可复用、好维护。一个
.vue
就可以作为一个组件,其中包含html、css、js - 声明式编码,无需直接操作dom。像直接操作dom的就叫做命令式编码,有点像面向对象与面向过程的区别,具体区别可以看图。
- 使用虚拟dom和diff算法,尽量复用dom。也就是数据发生变化,只进行增量更新。
![声明式与命令式区别](/blog/images/loading.svg)
一、上手
像vue下面这样的语法,就叫做模板语法,模板语法又分很多类。
1 2 3
| <div id="test"> Hello {{message}} </div>
|
1.1 占位语法
遇到了点hexo的冲突问题,hexo中的占位符敲好也是{{}}或者{%%}
所以如果想要展示原内容的话,语法需要像下面这样使用
{% raw %}文章内容{% endraw %}
{{content}}表示占位符,content内容支持js表达式。
占位语法一般用于标签体内容
一般真实开发也只会有一个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
| <!doctype html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="root"> Hello {{message.toUpperCase()}},My name is {{name}},now timestamp is {{Date.now()}}, I will calculate 1+1 for you, the result is {{1+1}}, </div> <div id="test"> Hello {{message}} </div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> Vue.config.productionTip = false; const app = new Vue({ el: '#root', data: { message: 'Vue', name: "ccc" } });
</script> </body> </html>
|
1.2 指令语法
vue有很多指令,格式都是v-xxx
。
像v-bind
这种就是指令语法,v-bind
可以简写为:
。
指令语法用于标签,作用范围包含并大于占位语法
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
| <!doctype html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <a v-bind:href="link">{{blogName}}</a> <a :href="link">{{blogName}}</a> </div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> Vue.config.productionTip = false; const app = new Vue({ el: '#app', data: { blogName: '博客', link:"https://meethigher.top" } });
</script> </body> </html>
|
1.3 数据绑定
v-bind
是单向数据绑定,也就是绑定的值,如果通过页面进行了修改,原本的值不会变化。
v-model
支持双向数据绑定,如果页面的值发生变化,绑定的值也会变化。
v-model只能用在输入类元素,也就是内容可编辑的元素。
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
| <!doctype html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="root"> 单向数据绑定:<input type="text" v-bind:value="name"> 双向数据绑定:<input type="text" v-model:value="name"> </div> <script src="js/vue.js"></script> <script> new Vue({ el:'#root', data: { name:'test' } }) </script> </body> </html>
|
1.4 el与data的两种写法
el两种写法
- new对象时配置el属性
- 通过创建的对象使用$mount指定el的值
data两种写法
- 对象式
- 函数式
使用组件时,data必须使用函数式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <script> const app = new Vue({ data: function () { console.log(this); return { name: "test" } } }); app.$mount("#root"); </script>
|
1.5 vue中的mvvm模型
M:model模型,对应data中数据
V:view视图,对应vue模板
VM:viewModel视图模板,对应vue对象
对使用者,跟后端的MVC没啥区别,只不过控制器换成了视图模板罢了。
视图模板本身和控制器作用也是一样的,调用数据model,将数据model返回给view视图使用。
1.6 数据代理
通过Object.defineProperty添加属性值,该属性值的key在遍历时,是不会被遍历到的
![额外添加的不会被遍历](/blog/images/loading.svg)
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script> let person={ name:"test", age:18 }; Object.defineProperty(person,"sex",{ value: "男" }); console.log(person); console.log(Object.keys(person)) </script>
|
当然了,我们也可以给他们配置属性,允许该操作。
1 2 3 4 5 6 7 8 9 10
| Object.defineProperty(person,"sex",{ value: "男", enumerable:true, writable:true, configurable:true });
|
如果,我们想要动态修改对象中的值,可以这么玩
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <script> let sex="男"; let person={ name:"test", age:18 };
Object.defineProperty(person,"sex",{ get() { console.log("读取sex"); return sex; }, set(value) { console.log("修改sex"); sex=value; } }); console.log(person); </script>
|
1.7 事件与事件修饰符
注意事项
- 使用v-on:xxx或者@xxx绑定事件,其中xxx是事件名,比如click
- 事件如果放在data中,会自动生成getter与setter,建议放在methods中
- methods中如果用箭头函数,this就表示window。不用箭头函数,this表示vue对象
- @click=”function”与@click=”function($event)”一样,前者是后者省略写法。
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 id="root"> <button v-on:click="showInfo <button @click="showInfo">@简 <button @click="showInfoWith </div> <script> const app=new Vue({ el:"#root", data:{ name:"meethigher", address:"china" }, methods: { // showInfo() { // alert("test" // } showInfo(a,b,c,d) { //只有第一个参数,a有值,也 console.log(a,b //此时this是vue对象, console.log(thi }, showInfoWithParam(p console.log(eve alert(param); } } }); </script>
|
对于事件修饰符,拿阻止默认事件来说,我们从js的语法写vue可以这样
1 2 3 4 5
| showInfo(e) { e.preventDefault(); alert("test"); }
|
但是vue给提供了更方便的写法
1 2 3
| <div id="root"> <a href="https://meethigher.top" @click.prevent="showInfo">test</a> </div>
|
像这种还有很多,vue中常用的事件修饰符
- prevent:阻止默认事件,常用
- stop:阻止事件冒泡,常用
- once:事件只触发一次,常用
- capture:使用事件的捕获模式
- self:只有event.target是当前操作的元素才会触发
- passive:事件的默认行为立即执行,不需要等待事件回调完成
修饰符可以连写
.prevent.stop,先阻止默认事件,再阻止冒泡
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
| <style> .test { margin: 10px auto; background-color: #42b983; } .aaa { height:250px; overflow: auto; } .aaa>li { margin: 10px auto; height: 200px; background-color: green; } </style>
<div id="root"> <a href="https://meethigher.top" @click.prevent="showInfo">test</a> <div class="test" @click="showInfo"> <button @click.stop="showInfo">测试冒泡</button> </div> <button @click.once="showInfo">事件只触发一次</button>
<div class="test" @click.capture="showMsg('div1')"> div1 <div class="test" @click="showMsg('div2')"> div2 </div> </div> <div class="test" @click.self="showTarget"> <button @click="showTarget">测试只有当前元素才能触发</button> </div>
<ul class="aaa" @wheel.passive="calculate"> <li>向晚</li> <li>贝拉</li> <li>珈乐</li> <li>嘉然</li> <li>乃琳</li> </ul> </div>
<script> const app=new Vue({ el:"#root", data:{ name:"meethigher", address:"china" }, methods: { showInfo(e) { alert("test"); }, showMsg(param) { console.log(param); }, showTarget(e) { console.log(e.target); }, calculate() { for (let i=0;i<100000;i++) { console.log("a"); } console.log("算完了"); } } }); </script>
|
1.8 键盘事件
先看一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <div id="root"> <input type="text" placeholder="按下回车提示输入" @keydown="showInfo"> </div>
<script> new Vue({ el:"#root", methods: { showInfo(e) { if(e.keyCode!==13) return; console.log(e.target.value); } } }) </script>
|
一般操作是上面这样进行的,但是在Vue中,提供了常用的按键别名
- 回车:enter
- 删除:delete
- 退出:esc
- 空格:space
- 换行:tab,必须与keydown使用
- 上:up
- 下:down
- 左:left
- 右:right
- ctrl
- alt
- shift
- meta:对应windows的win键
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <div id="root"> <input type="text" placeholder="按下ctrl+enter提示输入" @keydown.ctrl.enter="showMsg"> <input type="text" placeholder="按下ctrl+enter提示输入" @keydown.ctrl.e="showMsg"> </div>
<script> Vue.config.keyCodes.e = 69; new Vue({ el:"#root", methods: { showInfo(e) { console.log(e.target.value); } } }) </script>
|
1.9 计算属性
需求
两个输入框,一个姓,一个名,下面文本框动态展示姓名。
占位符实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <div id="root"> 姓:<input type="text" v-model="xing"><br> 名:<input type="text" v-model="ming"><br> 全名:{{xing.slice(0,3)}}{{ming}} 全名:{{xing.slice(0,3)}}{{ming}} </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#root", data: { xing: '', ming:'' } }) </script>
|
修改一次,调用两次。
方法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <div id="root"> 姓:<input type="text" v-model="xing"><br> 名:<input type="text" v-model="ming"><br> 全名:{{getFullName()}} 全名:{{getFullName()}} </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#root", data: { xing: '', ming:'' }, methods: { getFullName() { console.log("方法被调用"); return this.xing.slice(0,3)+this.ming; } } }) </script>
|
修改一次,方法调用两次。
计算属性实现
很明显,不管是占位符或是方法,都是在只要数据发生变化后,都要重新读取一下,效率不高。
Vue提供了计算属性,为了解决这个问题,计算属性里面用到了缓存,将数据缓存了起来,便于后续的复用。
何为属性,vue对象中的data里面的可以都是属性(属性名)。
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
| <div id="root"> 姓:<input type="text" v-model="xing"><br> 名:<input type="text" v-model="ming"><br> 全名:{{getFullName}}<br> 全名:{{getFullName}} </div> <script src="js/vue.js"></script> <script> const app=new Vue({ el:"#root", data: { xing: '', ming:'' }, computed: { getFullName: { get(){ console.log("被调用了"); return this.xing+this.ming; }, set(value) { console.log("set被调用"); this.xing=value; } } } }) </script>
|
修改一次,只调用一次。
计算方法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <script> const app=new Vue({ el:"#root", data: { xing: '', ming:'' }, computed: { getFullName() { console.log("被调用了"); return this.xing+this.ming; } } }) </script>
|
1.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 id="root"> <h2>今天天气很{{weather}}</h2> <button @click="changeWeather">切换天气</button> </div> <script src="js/vue.js"></script> <script> const app = new Vue({ el: "#root", data: { isHot: true }, methods: { changeWeather() { this.isHot = !this.isHot; } }, computed: { weather() { return this.isHot ? "炎热" : "凉爽"; } } }); </script>
|
监视属性实现
监视属性的两种实现
- 实例化Vue对象时,初始化配置watch
- 通过已有的Vue对象,obj.$watch监视
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
| <div id="root"> <h2>今天天气很{{weather}}</h2> <button @click="changeWeather">切换天气</button> </div> <script src="js/vue.js"></script> <script> const app = new Vue({ el: "#root", data: { isHot: true }, methods: { changeWeather() { this.isHot = !this.isHot; } }, computed: { weather() { return this.isHot ? "炎热" : "凉爽"; } }, });
app.$watch("weather",{ immediate:true, handler(newValue, oldValue) { console.log("监测weather方法",newValue,oldValue); } }) </script>
|
vue默认不监视多层级数据的变化,如果想要监视多层级监视,就要配置deep。
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 id="root"> <button @click="number.a++">a++</button> </div>
<script> const app = new Vue({ el: "#root", data: { isHot: true, number: { a:1, b:2 } }, watch: { number: { deep: true, handler() { console.log("number发生变化"); } } } }); </script>
|
1.11 绑定样式
绑定class
vue绑定样式需要使用:class
,参数内容支持字符串、数组、对象(key的值需要是boolean类型)。
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
| <div id="root"> <div id="weather" class="basic" :class="[weatherClass,weatherValid]" >今天的天气:{{weather}}</div> <button @click="changeWeather">查询当前天气</button> <button @click="addFont">添加字体样式</button> </div> <script src="js/vue.js"></script> <script> const app = new Vue({ el: "#root", data: { weatherClass: "sunny", weather: "晴天", weatherValid: { isValid: false } }, methods: { changeWeather() { const arr=["sunny","cloudy","rain"]; const arrDesc=["晴天","多云","雨天"]; const index=Math.floor(Math.random()*3); this.weatherClass=arr[index]; this.weather=arrDesc[index]; }, addFont() { let weatherClass = this.weatherClass; if(weatherClass instanceof Array) { console.log("是数组,追加"); this.weatherClass.push("font"); }else { console.log("不是数组,拼接"); this.weatherClass=this.weatherClass+" font"; } } } }) </script>
|