VueJs(1)---操作指南
【VueJs入门】
一、上手步骤
vue.js和jquery一样,只要引入vue.js就可以了。
1、创建一个Vue实例: 先引入Vue.js文件,然后new一个Vue的实例即可。如下面的代码,通过<script src="./vue.js"></script>引入,然后在<script>标签中创建实例
2、挂载 在Vue的实例里,通过传递的el属性
3、单向/双向绑定 data属性里的变量 = html标签里的{{}} = input标签里的v-model
<!DOCTYPE html> <html lang="en"> <script src="./vue.js"></script> <body> <div id="app"> <input v-model="test"/> {{test}} </div> <script> new Vue({ //创建一个Vue的实例 el: "#app", //挂载点是id="app"的地方 data: { //数据 test: "abc" //变量test } }) </script> </body> </html>
二、案例(DEMO控制)
案例说明:
1. 单个dom的添加/删除/显示/隐藏——变量控 2. 多个在显示上互斥的dom(如登录时的提示)—— 用1个变量去控制 3. 多个同类型dom(用v-for来动态生成)
代码:
1 <html lang="en"> 2 <script src="./vue.js"></script> 3 <body> 4 <div id="app"> 5 <p> 6 点击按钮切换显示的图标 7 </p> 8 <input type="button" v-on:click="set1" v-if="data1" value="【一个图标】"/> 9 <input type="button" v-on:click="set1" v-if="!data1" value="另外一个图标"/> 10 <p>————————————————————————————</p> 11 <p>点击不同按钮决定显示什么</p> 12 <input type="button" @click="set2('baidu')" value="百度"> 13 <input type="button" @click="set2('qq')" value="腾讯"> 14 <input type="button" @click="set2('taobao')" value="淘宝"> 15 <button @click="set2">什么都不显示</button> 16 <p> 17 <a href="http://www.baidu.com" target="_blank" v-if="data2=='baidu'">百度</a> 18 <a href="http://www.qq.com" target="_blank" v-if="data2=='qq'">腾讯</a> 19 <a href="http://www.taobao.com" target="_blank" v-if="data2=='taobao'">淘宝</a> 20 </p> 21 <p>————————————————————————————</p> 22 <p> 23 同类型dom,典型的是表格,类似的有li。<br> 24 <button @click="set3">点击添加内容</button> 25 </p> 26 <ul> 27 <li v-for="i in data3">{{i}}</li> 28 </ul> 29 </div> 30 <script> 31 new Vue({ //创建一个Vue的实例 32 el: "#app", //挂载点是id="app"的地方 33 data: { 34 data1: true, 35 data2: "", 36 data3: [1] 37 }, 38 methods: { 39 set1: function () { 40 this.data1 = !this.data1; 41 }, 42 set2: function (arg) { 43 this.data2 = arg; 44 }, 45 set3: function (arg) { 46 this.data3.push(this.data3.length + 1); 47 } 48 } 49 }) 50 </script> 51 </body>
三、案例(input相关)
微博的注册页面如下图
代码:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
<!DOCTYPE html> <html lang="en"> <script src="./vue.js"></script> <body> <div id="app"> <p> 邮箱:<input v-model="mail"/> </p> <p> 设置密码:<input type="password" v-model="pw"/> </p> <p> 官方注册微博名:<input v-model="name" placeholder="请参考组织/企业/品牌名称"/> </p> <p> 所在地: <select v-model="province"> <option value="zhejiang">浙江</option> <option value="shanghai">上海</option> </select> <select v-model="city"> <option v-for="(val,key) in citys" v-bind:value="key">{{val}}</option> </select> </p> <p> 验证码: <input v-model="verificationCode"> 请输入:1234 </p> <p> <input type="button" v-on:click="check" value="提交"/> <input type="button" v-on:click="inputDefault" value="默认值"/> </p> <p style="color:green" v-if="error=='success'">提交成功</p> <p style="color:red" v-if="error=='less'">缺少内容</p> <p style="color:red" v-if="error=='VerificationCode'">验证码错误</p> </div> <script> new Vue({ //创建一个Vue的实例 el: "#app", //挂载点是id="app"的地方 created: function () { this.changeProvince(); }, data: { //数据 province: "zhejiang", mail: "", pw: "", name: "", city: "", citys: {}, provinceWithCity: { zhejiang: { hangzhou: "杭州", shaoxing: "绍兴" }, shanghai: { pudong: "浦东区", jingan: "静安区" } }, verificationCode: "", error: "" }, methods: { changeProvince: function () { this.citys = this.provinceWithCity['zhejiang']; this.$watch('province', function (newVal, oldVal) { this.citys = this.provinceWithCity[newVal]; }) }, check: function () { //提交内容检查 if (this.mail && this.pw && this.name && this.province && this.city) { if (this.verificationCode === '1234') { this.error = 'success'; console.log([this.mail, this.pw, this.name, this.province, this.city]); } else { this.error = 'VerificationCode' } } else { this.error = 'less'; } }, inputDefault: function () { this.mail = '123@qq.com'; this.pw = '123'; this.name = 'abc'; this.province = 'zhejiang'; this.city = 'hangzhou'; this.verificationCode = '1234'; } } }) </script> </body> </html>
四、案例(表格相关)
1.表格的核心特点是:类型重复的大量内容。 2.Vue非常擅长对表格的处理,只需要已知数据,预先设置好格式,即可自动生成数据。 3.常见表格需求是选择性显示(比如只显示符合条件的项),这点Vue也十分擅长,你只需要设置好条件,Vue在渲染的时候会自动帮你完成
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
<!DOCTYPE html> <html lang="en"> <script src="./vue.js"></script> <style> table { border-collapse: collapse; } th, td { border: 1px solid #000; } </style> <body> <div id="app"> <input type="button" v-on:click="randomData" value="随机生成数据"/> <input type="button" v-on:click="yearAbove20" v-if="yearLimit!==20" value="只显示年龄大于20的人"/> <input type="button" v-on:click="all" v-if="yearLimit>0" value="显示全部"/> <p></p> <table> <tr> <td>序号</td> <td>姓名</td> <td>年龄</td> </tr> <tr v-for="(val,key) in datas" v-if="val.year > yearLimit"> <td>{{val.index}}</td> <td>{{val.name}}</td> <td v-if="val.year<30">{{val.year}}</td> <td v-else>年龄太大了,不告诉你喔</td> </tr> </table> </div> <script> new Vue({ //创建一个Vue的实例 el: "#app", //挂载点是id="app"的地方 data: { datas: [], yearLimit: 0 }, methods: { getName: function () { var familyNames = new Array( "赵", "钱", "孙", "李", "周", "吴", "郑", "王", "冯", "陈", "褚", "卫", "蒋", "沈", "韩", "杨", "朱", "秦", "尤", "许", "何", "吕", "施", "张", "孔", "曹", "严", "华", "金", "魏", "陶", "姜", "戚", "谢", "邹", "喻", "柏", "水", "窦", "章", "云", "苏", "潘", "葛", "奚", "范", "彭", "郎", "鲁", "韦", "昌", "马", "苗", "凤", "花", "方", "俞", "任", "袁", "柳", "酆", "鲍", "史", "唐", "费", "廉", "岑", "薛", "雷", "贺", "倪", "汤", "滕", "殷", "罗", "毕", "郝", "邬", "安", "常", "乐", "于", "时", "傅", "皮", "卞", "齐", "康", "伍", "余", "元", "卜", "顾", "孟", "平", "黄", "和", "穆", "萧", "尹" ); var givenNames = new Array( "子璇", "淼", "国栋", "夫子", "瑞堂", "甜", "敏", "尚", "国贤", "贺祥", "晨涛", "昊轩", "易轩", "益辰", "益帆", "益冉", "瑾春", "瑾昆", "春齐", "杨", "文昊", "东东", "雄霖", "浩晨", "熙涵", "溶溶", "冰枫", "欣欣", "宜豪", "欣慧", "建政", "美欣", "淑慧", "文轩", "文杰", "欣源", "忠林", "榕润", "欣汝", "慧嘉", "新建", "建林", "亦菲", "林", "冰洁", "佳欣", "涵涵", "禹辰", "淳美", "泽惠", "伟洋", "涵越", "润丽", "翔", "淑华", "晶莹", "凌晶", "苒溪", "雨涵", "嘉怡", "佳毅", "子辰", "佳琪", "紫轩", "瑞辰", "昕蕊", "萌", "明远", "欣宜", "泽远", "欣怡", "佳怡", "佳惠", "晨茜", "晨璐", "运昊", "汝鑫", "淑君", "晶滢", "润莎", "榕汕", "佳钰", "佳玉", "晓庆", "一鸣", "语晨", "添池", "添昊", "雨泽", "雅晗", "雅涵", "清妍", "诗悦", "嘉乐", "晨涵", "天赫", "玥傲", "佳昊", "天昊", "萌萌", "若萌" ); var ran1 = parseInt(100 * Math.random()); var ran2 = parseInt(100 * Math.random()); return familyNames[ran1] + givenNames[ran2]; }, randomData: function () { var data = []; for (var i = 1; i < (5 + 10 * Math.random()); i++) { var obj = { index: i, name: this.getName(), year: parseInt(1 + Math.random() * 50) } data.push(obj) } this.datas = data; console.log(this.datas); }, yearAbove20: function () { this.yearLimit = 20; }, all: function () { this.yearLimit = -1; } } }) </script> </body> </html>
五、其它Vue常见功能
1、过滤器功能:
1. 主要用于文本转换; 2. 例如获得一个日期对象后,通过过滤器命令自动转为我们要求的日期格式。 3. {{ message | capitalize }} message变量被过滤器函数capitalize所处理
2、计算属性:
1. 更加高级的功能,可以视为过滤器功能的进阶版,适用的方向更多(不仅仅是文本) 2. 获取一个变量(输入内容)→通过计算函数转换→显示转换结果(输出内容) 3. 当输入内容变更时,输出内容也会自动随之变更 4. 利用ES5的getter和setter特性来实现,有缓存特点
3、$watch方法:
1. 监控变量,当变量改变时触发回调函数; 2. 例如之前的微博注册demo中,通过检测表示省份的变量的变化,来动态设置表示市的dom
4、class/style绑定:
1. 通过改变变量,来设置dom的样式的类,或者直接设置样式的属性 2. <div v-bind:class="{ active: isActive }"></div> 3. isActive值为true时,dom获得active这个类
5、事件监听:
1. 通过$emit触发事件和$on响应事件,只在当前Vue实例内有效,因此不会带来干扰; 2. 用起来非常舒服,适用于一对多和多对一的场景; 3. 跨组件响应(父子组件通信)时,可以使用global event bus来实现,或者使用插件实现
6、路由功能:
1. 简单来说,按需加载,而不是一次性全部加载; 2. 有官方推荐支持使用的的vue-router库;
VueJs开发环境的搭建和讲解初始框架
有关如何搭建vue.js框架我这看了一篇文章,自己也根据它进行搭建环境。
文章地址:vue.js2.0实战(1):搭建开发环境及构建项目
接下来对初始的框架进行讲解,只讲index.html是如何被渲染出来的。
一、启动项目
第一步:cmd进入项目文件里,运行npm run dev 启动项目 这里说明启动端口号是8080
第二步:往页面输入:localhost:8080
二、解析渲染步骤
先看整体框架样式和index.html:
从上面我们可以看出,index的body中只有一个id为app的div,那是如何被渲染的呢。一步一步寻找
第一步:main.js
main.js是我们的入口文件,主要作用是初始化vue实例并使用需要的插件。
这里new Vue代表新建vue对象
el官方解释:为实例提供挂载元素。值可以是 CSS 选择符,或实际 HTML 元素,或返回 HTML 元素的函数。
这里就通过index.html中的<div id="app"><div>中的id=“app”和这里的“#app”进行挂载。
components:代表组件。这里是'App',这说明。首先页面肯定有<app></app>这样的标签,同时有个组建为‘App.Vue‘文件
这个地方我思考好久,才明白,首先App.vue是有的,因为上面已经import导入了,但index.html中并没有<app></App>标签
template:代表模板。官方解释:模板将会替换挂载的元素。挂载元素的内容都将被忽略。
也就是说:template: '<App/>'
表示用<app></app>
替换index.html
里面的<div id="app"></div>
那到底是不是这样,我们先把main中components这行注释掉:
再看页面:发现里面就有一个<app></app>标签。这样那么逻辑就通了。
这样mian.js就通了,那通过components: { App },我们来看App.vue
第二步:App.vue
首先一个正常的vue结尾的文件里,一般包含三部分:<template>,<script>,<style>
这里面的<img>标签,就代表页面的vue的logo,它下面又有一个组件<HellWord>
我们只要在进入到HellWord.vue中明白了。
第三步:HellWord.vue
这样一来,页面所渲染的东西都找到了,其实并不复杂,只是在main.js稍微绕了个弯。
自己也是一边学一边写,有错的地方或者有更好的解释也希望大家予以指点。
VueJs(3)---V-指令(1)
一、语法
v- 指令是带有v-的特殊属性
- v-if 条件渲染
- v-show
- v-else (必须在v-if/v-else-if/v-show指令后)
- v-else-if (v-if/v-else-if后)
- v-for (遍历)
- v-html (绑定HTML属性中的值) (本篇先讲这6个)
- v-bind (响应更新HTML特性,绑定自定义属性,如绑定某个class元素或style)
- v-on (监听指定元素的dom事件)
- v-model (内置的双向数据绑定,用在表单控件,绑定的value通常是静态字符串)
- v-cloak 关于vuejs页面闪烁{{message}}
- v-once 只渲染元素和组件一次,随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过
v-text和v-html
v-test
<span v-text="msg"></span> <!-- 和下面的一样 --> <span>{{msg}}</span>
v-html :如果你的数据是“<h1>标题文字<h1>"那么它会解析成下面的,v-test永远是按文本输出。
v-show、v-if、v-else、v-else-if
v-show
根据表达式之真假值,切换元素的 display
CSS 属性。
<h1 v-show="ok">Hello!</h1>
不同的是带有 v-show
的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display
注意,v-show
不支持 <template>
元素,也不支持 v-else
v-if
根据表达式的值的真假条件渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。如果元素是 <template>
,将提出它的内容作为条件块。
<h1 v-if="ok">Yes</h1> <!--也可以和v-else一起用--> <h1 v-if="ok">Yes</h1> <h1 v-else>No</h1>
在 <template> 元素上使用 v-if
条件渲染分组
因为 v-if
是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 <template>
元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。
<template v-if="ok"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template>
v-else
你可以使用 v-else
指令来表示 v-if 的“else 块
<div v-if="Math.random() > 0.5"> Now you see me </div> <div v-else> Now you don't </div>
注意:v-else 元素必须紧跟在带 v-if 或者 v-else-if
的元素的后面,否则它将不会被识别。
v-else-if
v-else-if,顾名思义,充当 v-if 的“else-if 块”,可以连续使用
<div v-if="type === 'A'"> A </div> <div v-else-if="type === 'B'"> B </div> <div v-else> Not A/B/C </div>
v-if
vs v-show
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if 有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
v-for
v-for需要用特定语法:alias in expression 为当前遍历的元素提供别名
<!--常见的四种用法--> <div v-for="item in items"> {{ item.text }}</div> <div v-for="(item, index) in items"></div> <div v-for="(val, key) in object"></div> <div v-for="(val, key, index) in object"></div>
v-for
默认行为试着不改变整体,而是替换元素。迫使其重新排序的元素,你需要提供一个 key
的特殊属性
<div v-for="item in items" :key="item.id"> {{ item.text }} </div>
案例官网:v-for列表渲染
key
当 Vue.js 用 v-for
正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x 的 track-by="$index"
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key
属性。理想的 key
值是每项都有的且唯一的 id。这个特殊的属性相当于 Vue 1.x 的 track-by
,但它的工作方式类似于一个属性,所以你需要用 v-bind
来绑定动态值 (在这里使用简写)
<div v-for="item in items" :key="item.id"> <!-- 内容 --> </div>
注意事项
由于 JavaScript 的限制,Vue 不能检测以下变动的数组:
- 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如:vm.items.length = newLength
举个例子:
var vm = new Vue({ data: { items: ['a', 'b', 'c'] } }) vm.items[1] = 'x' <!--不是响应性的--> vm.items.length = 2 <!-- 不是响应性的-->
为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue
相同的效果,同时也将触发状态更新:
<!-- Vue.set--> Vue.set(vm.items, indexOfItem, newValue) <!-- Array.prototype.splice--> vm.items.splice(indexOfItem, 1, newValue)
你也可以使用 vm.$set
实例方法,该方法是全局方法 Vue.set
的一个别名
vm.$set(vm.items, indexOfItem, newValue)
为了解决第二类问题,你可以使用 splice:
vm.items.splice(newLength)
对象更改检测注意事项
还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除
var vm = new Vue({ data: { a: 1 } }) <!-- `vm.a` 现在是响应式的--> vm.b = 2 <!--vm.b` 不是响应式的-->
对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于:
var vm = new Vue({ data: { userProfile: { name: 'Anika' }}}) <!--你可以添加一个新的 age 属性到嵌套的 userProfile 对象--> Vue.set(vm.userProfile, 'age', 27)
你还可以使用 vm.$set
实例方法,它只是全局 Vue.set
的别名
vm.$set(vm.userProfile, 'age', 27)
显示过滤/排序结果
v-for
with v-if
当它们处于同一节点,v-for
的优先级比 v-if
更高,这意味着 v-if
将分别重复运行于每个 v-for
循环中。当你想为仅有的_一些_项渲染节点时,这种优先级的机制会十分有用,如下
<li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo }} </li> <!--上面的代码只传递了未完成的 todos。-->
而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if
置于外层元素 (或 <template>
)上。如
<ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </li> </ul> <p v-else>No todos left!</p>
一个组件的 v-for
在自定义组件里,你可以像任何普通元素一样用 v-for
<my-component v-for="item in items" :key="item.id"></my-component>
2.2.0+ 的版本里,当在组件中使用 v-for
时,key
现在是必须的。
然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要用 props
完整例子
<div id="todo-list-example"> <input v-model="newTodoText" v-on:keyup.enter="addNewTodo" placeholder="Add a todo" > <ul> <li is="todo-item" v-for="(todo, index) in todos" v-bind:key="todo.id" v-bind:title="todo.title" v-on:remove="todos.splice(index, 1)" <!--删除一个元素--> ></li> </ul> </div>
组件
Vue.component('todo-item', { template: '\ <li>\ {{ title }}\ <!--z这里有两个地方不太明白1:"/"是什么意思。 2:这里的remove是什么意思,是调用上面的remove吗?求解。--> <button v-on:click="$emit(\'remove\')">X</button>\ </li>\ ', props: ['title'] }) new Vue({ el: '#todo-list-example', data: { newTodoText: '', todos: [ { id: 1, title: 'Do the dishes', }, { id: 2, title: 'Take out the trash', }, { id: 3, title: 'Mow the lawn' } ], nextTodoId: 4 }, methods: { addNewTodo: function () { this.todos.push({ <!--push代表末尾添加--> id: this.nextTodoId++, title: this.newTodoText }) this.newTodoText = '' } } })
注意这里的 is="todo-item"
属性。这种做法在使用 DOM 模板时是十分必要的,因为在 <ul>
元素内只有 <li>
元素会被看作有效内容。
动态效果看官网:一个组件的v-for
V-model指令
摘要
限制:
v-model只能用在:<input> <select> <textarea> <components>
修饰符
基础用法
v-model 会忽略所有表单元素的 value
、checked
、selected
特性的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data
选项中声明初始值。
文本
<input v-model="message" placeholder="edit me"> <p>Message is: {{ message }}</p>
多行文本
<span>Multiline message is:</span> <p style="white-space: pre-line;">{{ message }}</p> <br> <textarea v-model="message" placeholder="add multiple lines"></textarea>
复选框
单个复选框,绑定到布尔值
<input type="checkbox" id="checkbox" v-model="checked"> <label for="checkbox">{{ checked }}</label>
多个复选框,绑定到同一个数组
<div id='example-3'> <input type="checkbox" id="jack" value="Jack" v-model="checkedNames"> <label for="jack">Jack</label> <input type="checkbox" id="john" value="John" v-model="checkedNames"> <label for="john">John</label> <input type="checkbox" id="mike" value="Mike" v-model="checkedNames"> <label for="mike">Mike</label> <br> <span>Checked names: {{ checkedNames }}</span> </div> new Vue({ el: '#example-3', data: { checkedNames: [] } })
单选按钮
<div id="example-4"> <input type="radio" id="one" value="One" v-model="picked"> <label for="one">One</label> <br> <input type="radio" id="two" value="Two" v-model="picked"> <label for="two">Two</label> <br> <span>Picked: {{ picked }}</span> </div>
选择框
单选时
<div id="example-5"> <select v-model="selected"> <option disabled value="">请选择</option> <option>A</option> <option>B</option> <option>C</option> </select> <span>Selected: {{ selected }}</span> </div> new Vue({ el: '#example-5', data: { selected: '' } })
注意:如果 v-model
表达式的初始值未能匹配任何选项,<select>
元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项。
多选时 (绑定到一个数组)
<div id="example-6"> <select v-model="selected" multiple style="width: 50px;"> <option>A</option> <option>B</option> <option>C</option> </select> <br> <span>Selected: {{ selected }}</span> </div> new Vue({ el: '#example-6', data: { selected: [] } })
用 v-for
渲染的动态选项
<select v-model="selected"> <option v-for="option in options" v-bind:value="option.value"> {{ option.text }} </option> </select> <span>Selected: {{ selected }}</span> new Vue({ el: '...', data: { selected: 'A', options: [ { text: 'One', value: 'A' }, { text: 'Two', value: 'B' }, { text: 'Three', value: 'C' } ] } })
值绑定
对于单选按钮,复选框及选择框的选项,v-model
绑定的值通常是静态字符串 (对于复选框也可以是布尔值):
<!--看到这里上面的你都应该明白了-->
<!-- 当选中时,`picked` 为字符串 "a" --> <input type="radio" v-model="picked" value="a"> <!-- `toggle` 为 true 或 false --> <input type="checkbox" v-model="toggle"> <!-- 当选中第一个选项时,`selected` 为字符串 "abc" --> <select v-model="selected"> <option value="abc">ABC</option> </select>
思考:有时我们可能想把值绑定到 Vue 实例的一个动态属性上,这时可以用 v-bind
实现,并且这个属性的值可以不是字符串。
修饰符
.lazy
在默认情况下,v-model
在每次 input
事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy
修饰符,从而转变为使用 change
事件进行同步
<!-- 在“change”时而非“input”时更新 --> <input v-model.lazy="msg" >
.number
如果想自动将用户的输入值转为数值类型,可以给 v-model
添加 number
修饰符
<input v-model.number="age" type="number"> <!--这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串--> <!--我想它主要是用来限制用户输入的时候只能是数字-->
.trim
<!--如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符-> <input v-model.trim="msg">
在组件上使用 v-model
用自定义事件的表单输入组件
讲这个前,首先我们要明白的是:
<input v-model="something"> <!--它其实是个语法糖,而真正的其实如下:--> <input v-bind:value="something" v-on:input="something = $event.target.value"> <!--所以在组件中使用时,它相当于下面的简写--> <custom-input v-bind:value="something" v-on:input="something = arguments[0]"> </custom-input>
看了上面我们就明白,父主键是无法直接向子主键传值的,它其实绑定了父主键的click事件。
所以要让组件的 v-model
生效,它应该 (从 2.2.0 起是可配置的):
- 接受一个
value
prop - 在有新的值时触发
input
事件并将新值作为参数
案例:
货币输入的自定义控件,限制最多小数点保留两位
<currency-input v-model="price"></currency-input>
Vue.component('currency-input', { template: '\ <span>\ $\ <input\ ref="input"\ v-bind:value="value"\ v-on:input="updateValue($event.target.value)"\ >\ </span>\ ', props: ['value'], methods: { // 不是直接更新值,而是使用此方法来对输入值进行格式化和位数限制 updateValue: function (value) { var formattedValue = value // 删除两侧的空格符 .trim() // 保留 2 位小数 .slice( 0, value.indexOf('.') === -1 ? value.length : value.indexOf('.') + 3 ) // 如果值尚不合规,则手动覆盖为合规的值 if (formattedValue !== value) { this.$refs.input.value = formattedValue } // 通过 input 事件带出数值 this.$emit('input', Number(formattedValue)) } } })
最后结果,就你可以没有小数,但如果有小数后面最后只能有两位小数
上面案例我理解的:
slice方法
ref ($refs)用法
ref 有三种用法
1.ref 加在普通的元素上,用this.ref.name 获取到的是dom元素
2.ref 加在子组件上,用this.ref.name 获取到的是组件实例,可以使用组件的所有方法。
3.如何利用v-for 和ref 获取一组数组或者dom 节点
一、ref使用在外面的组件上
HTML 部分
<div id="ref-outside-component" v-on:click="consoleRef"> <component-father ref="outsideComponentRef"> </component-father> <p>ref在外面的组件上</p> </div>
js部分
var refoutsidecomponentTem={ template:"<div class='childComp'><h5>我是子组件</h5></div>" }; var refoutsidecomponent=new Vue({ el:"#ref-outside-component", components:{ "component-father":refoutsidecomponentTem }, methods:{ consoleRef:function () { console.log(this); // #ref-outside-component vue实例 console.log(this.$refs.outsideComponentRef); // div.childComp vue实例 } } });
二、ref使用在外面的元素上
HTML部分
<!--ref在外面的元素上--> <div id="ref-outside-dom" v-on:click="consoleRef" > <component-father> </component-father> <p ref="outsideDomRef">ref在外面的元素上</p> </div>
JS部分
var refoutsidedomTem={ template:"<div class='childComp'><h5>我是子组件</h5></div>" }; var refoutsidedom=new Vue({ el:"#ref-outside-dom", components:{ "component-father":refoutsidedomTem }, methods:{ consoleRef:function () { console.log(this); // #ref-outside-dom vue实例 console.log(this.$refs.outsideDomRef); // <p> ref在外面的元素上</p> } } });
三、ref使用在里面的元素上---局部注册组件
HTML
<!--ref在里面的元素上--> <div id="ref-inside-dom"> <component-father> </component-father> <p>ref在里面的元素上</p> </div>
JS部分
var refinsidedomTem={ template:"<div class='childComp' v-on:click='consoleRef'>" + "<h5 ref='insideDomRef'>我是子组件</h5>" + "</div>", methods:{ consoleRef:function () { console.log(this); // div.childComp vue实例 console.log(this.$refs.insideDomRef); // <h5 >我是子组件</h5> } } }; var refinsidedom=new Vue({ el:"#ref-inside-dom", components:{ "component-father":refinsidedomTem } });
四、ref使用在里面的元素上---全局注册组件
HTML
<!--ref在里面的元素上--全局注册--> <div id="ref-inside-dom-all"> <ref-inside-dom-quanjv></ref-inside-dom-quanjv> </div>
JS部分
Vue.component("ref-inside-dom-quanjv",{ template:"<div class='insideFather'> " + "<input type='text' ref='insideDomRefAll' v-on:input='showinsideDomRef'>" + " <p>ref在里面的元素上--全局注册 </p> " + "</div>", methods:{ showinsideDomRef:function () { console.log(this); //这里的this其实还是div.insideFather console.log(this.$refs.insideDomRefAll); // <input type="text"> } } }); var refinsidedomall=new Vue({ el:"#ref-inside-dom-all" });
$emit理解
关于$emit的用法
1、父组件可以使用 props 把数据传给子组件。
2、子组件可以使用 $emit 触发父组件的自定义事件。
子组件:
<template> <div class="train-city"> <span @click='select(`大连`)'>大连</span> </div> </template> <script> export default { name:'trainCity', methods:{ select(val) { let data = { cityname: val }; this.$emit('showCityName',data);//select事件触发后,自动触发showCityName事件 } } } </script>
父组件
<template> <trainCity @showCityName="updateCity" :index="goOrtoCity"></trainCity> //监听子组件的showCityName事件。 <template> <script> export default { name:'index', data () { return { toCity:"北京" } } methods:{ updateCity(data){//触发子组件城市选择-选择城市的事件 this.toCity = data.cityname;//改变了父组件的值 console.log('toCity:'+this.toCity) } } } </script>
结果为:toCity: 大连
在找个例子:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="counter-event-example"> <p>{{ total }}</p> <button-counter v-on:increment1="incrementTotal"></button-counter> <button-counter v-on:increment2="incrementTotal"></button-counter> </div> </body> <script src="vue/vue.min.js"></script> <script> Vue.component('button-counter',{ template:'<button v-on:click="increment">{{ counter }}</button>', data:function(){ return {counter: 0}<!--组件数据就是这样的,函数式的,请注意--> }, methods:{ increment:function(){ this.counter += 1; this.$emit('increment1',[12,'kkk']);<!--$emit--> } } }); new Vue({ el:'#counter-event-example', data:{ total:0 }, methods:{ incrementTotal:function(e){ this.total += 1; console.log(e); } } }); </script> </html>
先看组件 button-counter
绑定了事件click————>increment
然后 this.counter += 1; this.$emit('increment1',[12,'kkk']);
这边就是触发事件 increment1,参考文献里面有点乱,这边是不是清晰多了
然后 <button-counter v-on:increment1="incrementTotal"></button-counter>
v-on相当于监听吧,就触发 incrementTotal
输出// [12, "kkk"]
VueJs(5)---V-bind指令
V-bind指令
一、概述
v-bind 主要用于属性绑定,比方你的class属性,style属性,value属性,href属性等等,只要是属性,就可以用v-bind指令进行绑定。
示例:
<!-- 绑定一个属性 --> <img v-bind:src="imageSrc"> <!-- 缩写 --> <img :src="imageSrc"> <!-- 内联字符串拼接 --> <img :src="'/path/to/images/' + fileName"> <!-- class 绑定 --> <div :class="{ red: isRed }"></div> <div :class="[classA, classB]"></div> <div :class="[classA, { classB: isB, classC: isC }]"> <!-- style 绑定 --> <div :style="{ fontSize: size + 'px' }"></div> <div :style="[styleObjectA, styleObjectB]"></div> <!-- 绑定一个有属性的对象 --> <div v-bind="{ id: someProp, 'other-attr': otherProp }"></div> <!-- 通过 prop 修饰符绑定 DOM 属性 --> <div v-bind:text-content.prop="text"></div> <!-- prop 绑定。“prop”必须在 my-component 中声明。--> <my-component :prop="someThing"></my-component> <!-- 通过 $props 将父组件的 props 一起传给子组件 --> <child-component v-bind="$props"></child-component> <!-- XLink --> <svg><a :xlink:special="foo"></a></svg>
二、绑定 HTML Class
对象语法
我们可以传给 v-bind:class
一个对象,以动态地切换 class
<div v-bind:class="{ active: isActive }"></div>
上面的语法表示 active
这个 class 存在与否将取决于数据属性 isActive
的 truthiness
你可以在对象中传入更多属性来动态切换多个 class。此外,v-bind:class 指令也可以与普通的 class 属性共存。当有如下模板:
<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"> </div>
和如下 data
data: { isActive: true, hasError: false }
结果渲染为:
<div class="static active"></div>
当 isActive
或者 hasError
变化时,class 列表将相应地更新。例如,如果 hasError
的值为 true,class 列表将变为 "static active text-danger"
绑定的数据对象不必内联定义在模板里
<div v-bind:class="classObject"></div>
data: { classObject: { active: true, 'text-danger': false } }
渲染的结果和上面一样。我们也可以在这里绑定一个返回对象的计算属性。这是一个常用且强大的模式:
<div v-bind:class="classObject"></div>
data: { isActive: true, error: null }, computed: { classObject: function () { return { active: this.isActive && !this.error, 'text-danger': this.error && this.error.type === 'fatal' } } }
数组语法
我们可以把一个数组传给 v-bind:class
,以应用一个 class 列表
<div v-bind:class="[activeClass, errorClass]"></div>
data: { activeClass: 'active', errorClass: 'text-danger' }
渲染为:
<div class="active text-danger"></div>
如果你也想根据条件切换列表中的 class,可以用三元表达式
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
这样写将始终添加 errorClass,但是只有在 isActive
是 truthy 时才添加 activeClass
。
不过,当有多个条件 class 时这样写有些繁琐。所以在数组语法中也可以使用对象语法
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
三、用在组件上
当在一个自定义组件上使用 class
属性时,这些类将被添加到该组件的根元素上面。这个元素上已经存在的类不会被覆盖。
例如,如果你声明了这个组件:
Vue.component('my-component', { template: '<p class="foo bar">Hi</p>' })
然后在使用它的时候添加一些 class
<my-component class="baz boo"></my-component>
HTML 将被渲染为:
<p class="foo bar baz boo">Hi</p>
对于带数据绑定 class 也同样适用
<my-component v-bind:class="{ active: isActive }"></my-component>
当 isActive
为 truthy时,HTML 将被渲染成为
<p class="foo bar active">Hi</p>
四、绑定内联样式
对象语法
v-bind:style
的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用单引号括起来) 来命名:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: { activeColor: 'red', fontSize: 30 }
直接绑定到一个样式对象通常更好,这会让模板更清晰
<div v-bind:style="styleObject"></div>
data: { styleObject: { color: 'red', fontSize: '13px' } }
同样的,对象语法常常结合返回对象的计算属性使用
数组语法
v-bind:style
的数组语法可以将多个样式对象应用到同一个元素上
<div v-bind:style="[baseStyles, overridingStyles]"></div>
VueJs(6)---V-on指令
V-on指令
一、概述
v-on是用来绑定事件监听器,用在普通元素上时,只能监听原生 DOM 事件。用在自定义元素组件上时,也可以监听子组件触发的自定义事件。
在监听原生 DOM 事件时,方法以事件为唯一的参数。如果使用内联语句,语句可以访问一个 $event
属性:v-on:click="handle('ok', $event)"
。
示例
<!-- 方法处理器 --> <button v-on:click="doThis"></button> <!-- 内联语句 --> <button v-on:click="doThat('hello', $event)"></button> <!-- 缩写 --> <button @click="doThis"></button> <!-- 停止冒泡 --> <button @click.stop="doThis"></button> <!-- 阻止默认行为 --> <button @click.prevent="doThis"></button> <!-- 阻止默认行为,没有表达式 --> <form @submit.prevent></form> <!-- 串联修饰符 --> <button @click.stop.prevent="doThis"></button> <!-- 键修饰符,键别名 --> <input @keyup.enter="onEnter"> <!-- 键修饰符,键代码 --> <input @keyup.13="onEnter"> <!-- 点击回调只会触发一次 --> <button v-on:click.once="doThis"></button> <!-- 对象语法 (2.4.0+) --> <button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
在子组件上监听自定义事件 (当子组件触发“my-event”时将调用事件处理器)
<my-component @my-event="handleThis"></my-component> <!-- 内联语句 --> <my-component @my-event="handleThis(123, $event)"></my-component> <!-- 组件中的原生事件 --> <my-component @click.native="onClick"></my-component>
二、时间处理
1、 监听事件
可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码
示例
<div id="example-1"> <button v-on:click="counter += 1">Add 1</button> <p>The button above has been clicked {{ counter }} times.</p> </div>
var example1 = new Vue({ el: '#example-1', data: { counter: 0 } })
结果:每点击一次按钮,p标签的counter就加1
2、事件处理方法
然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on
指令中是不可行的。因此 v-on
还可以接收一个需要调用的方法名称。
示例
<div id="example-2"> <!-- `greet` 是在下面定义的方法名 --> <button v-on:click="greet">Greet</button> </div>
var example2 = new Vue({ el: '#example-2', data: { name: 'Vue.js' }, // 在 `methods` 对象中定义方法 methods: { greet: function (event) { // `this` 在方法里指向当前 Vue 实例 alert('Hello ' + this.name + '!') // `event` 是原生 DOM 事件 if (event) { alert(event.target.tagName) } } } }) // 也可以用 JavaScript 直接调用方法 example2.greet() // => 'Hello Vue.js!'
结果:当点击Green按钮,会先后弹出两个提示框
3、内联处理器中的方法
除了直接绑定到一个方法,也可以在内联 JavaScript 语句中调用方法
<div id="example-3"> <button v-on:click="say('hi')">Say hi</button> <button v-on:click="say('what')">Say what</button> </div>
new Vue({ el: '#example-3', methods: { say: function (message) { alert(message) } } })
结果:当我点击Say hi 按钮会弹如下提示框。
有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event
把它传入方法
<button v-on:click="warn('Form cannot be submitted yet.', $event)"> Submit </button>
// ... methods: { warn: function (message, event) { // 现在我们可以访问原生事件对象 if (event) event.preventDefault() alert(message) } }
4、事件修饰符
在事件处理程序中调用 event.preventDefault()
或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。
<!-- 阻止单击事件继续传播 --> <a v-on:click.stop="doThis"></a> <!-- 提交事件不再重载页面 --> <form v-on:submit.prevent="onSubmit"></form> <!-- 修饰符可以串联 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只有修饰符 --> <form v-on:submit.prevent></form> <!-- 添加事件监听器时使用事件捕获模式 --> <!-- 即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理 --> <div v-on:click.capture="doThis">...</div> <!-- 只当在 event.target 是当前元素自身时触发处理函数 --> <!-- 即事件不是从内部元素触发的 --> <div v-on:click.self="doThat">...</div>
注意:使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self
会阻止所有的点击,而 v-on:click.self.prevent
只会阻止对元素自身的点击。
5、按键修饰符
在监听键盘事件时,我们经常需要检查常见的键值。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:
<!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` --> <input v-on:keyup.13="submit">
记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名
<!-- 同上 --> <input v-on:keyup.enter="submit"> <!-- 缩写语法 --> <input @keyup.enter="submit">
全部的按键别名:
- .enter
- .tab
.delete
(捕获“删除”和“退格”键)- .esc
- .space
- .up
- .down
- .left
- .right
计算属性和侦听器
一、 概述
计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
<div id="example"> {{ message.split('').reverse().join('') }} </div>
在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message
的翻转字符串。当你想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。
所以,对于任何复杂逻辑,你都应当使用计算属性。
基础例子
<div id="example"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ reversedMessage }}"</p> </div>
var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { // 计算属性的 getter reversedMessage: function () { // `this` 指向 vm 实例 return this.message.split('').reverse().join('') } } })
结果:
这里我们声明了一个计算属性 reversedMessage。我们提供的函数将用作属性 vm.reversedMessage
的 getter 函数
console.log(vm.reversedMessage) // => 'olleH' vm.message = 'Goodbye' console.log(vm.reversedMessage) // => 'eybdooG'
你可以像绑定普通属性一样在模板中绑定计算属性。Vue 知道 vm.reversedMessage
依赖于 vm.message
,因此当 vm.message
发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。而且最妙的是我们已经以声明的方式创建了这种依赖关系:计算属性的 getter 函数是没有副作用 (side effect) 的,这使它更易于测试和理解。
二、计算属性缓存 vs 方法
你可能已经注意到我们可以通过在表达式中调用方法来达到同样的效果
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中 methods: { reversedMessage: function () { return this.message.split('').reverse().join('') } }
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message
还没有发生改变,多次访问 reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。
这也同样意味着下面的计算属性将不再更新,因为 Date.now()
不是响应式依赖
computed: { now: function () { return Date.now() } }
相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
我们为什么需要缓存?
假设我们有一个性能开销比较大的的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A 。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
三、计算属性 vs 侦听属性
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch
——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch
回调。
细想一下这个例子
<div id="demo">{{ fullName }}</div>
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { firstName: function (val) { this.fullName = val + ' ' + this.lastName }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } } })
上面代码是命令式且重复的。将它与计算属性的版本进行比较:
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName } } })
是不是好多了。我的理解监听一般用于单个对象比较合适,如果同时要监听多个数据的变化这个时候用监听显得非常麻烦,而用计算方式显的轻松很多。
四、计算属性的 setter
计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter
// ... computed: { fullName: { // getter get: function () { return this.firstName + ' ' + this.lastName }, // setter set: function (newValue) { var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } } // ...
现在再运行 vm.fullName = 'John Doe'
时,setter 会被调用,vm.firstName 和 vm.lastName
也会相应地被更新。
五、侦听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch
选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
例如:
<div id="watch-example"> <p> Ask a yes/no question: <input v-model="question"> </p> <p>{{ answer }}</p> </div>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 --> <!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 --> <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script> <script> var watchExampleVM = new Vue({ el: '#watch-example', data: { question: '', answer: 'I cannot give you an answer until you ask a question!' }, watch: { // 如果 `question` 发生改变,这个函数就会运行 question: function (newQuestion, oldQuestion) { this.answer = 'Waiting for you to stop typing...' this.getAnswer() } }, methods: { // `_.debounce` 是一个通过 Lodash 限制操作频率的函数。 // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率 // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于 // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识, // 请参考:https://lodash.com/docs#debounce getAnswer: _.debounce( function () { if (this.question.indexOf('?') === -1) { this.answer = 'Questions usually contain a question mark. ;-)' return } this.answer = 'Thinking...' var vm = this axios.get('https://yesno.wtf/api') .then(function (response) { vm.answer = _.capitalize(response.data.answer) }) .catch(function (error) { vm.answer = 'Error! Could not reach the API. ' + error }) }, // 这是我们为判定用户停止输入等待的毫秒数 500 ) } }) </script>
结果:当没有输入?号,那么显示如下:
当有?时候会输出“yes”或者“no”
具体案例效果地址:侦听器
在这个示例中,使用 watch
选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
VueJs(8)---组件(注册组件)
组件(注册组件)
一、介绍
组件系统是Vue.js其中一个重要的概念,它提供了一种抽象,让我们可以使用独立可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树
那么什么是组件呢?
组件可以扩展HTML元素,封装可重用的HTML代码,我们可以将组件看作自定义的HTML元素。
二、如何注册组件
Vue.js的组件的使用有3个步骤:创建组件构造器、注册组件和使用组件。
下面用代码演示这三步
<!DOCTYPE html> <html> <body> <div id="app"> <!-- 注意: #app是Vue实例挂载的元素,应该在挂载元素范围内使用组件--> <my-component></my-component> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> <!-- 1.创建一个组件构造器 --> var myComponent = Vue.extend({ template: '<div>This is my first component!</div>' }) <!-- 2.注册组件,并指定组件的标签,组件的HTML标签为<my-component> --> Vue.component('my-component', myComponent) <!-- 3.通过id=app进行挂载 --> new Vue({ el: '#app' }); </script> </html>
运行结果如下:
一、 全局注册和局部注册
调用Vue.component()
注册组件时,组件的注册是全局的,这意味着该组件可以在任意Vue示例下使用。
如果不需要全局注册,或者是让组件使用在其它组件内,可以用选项对象的components属性实现局部注册。
我自己的理解只要是component就代表全局组件,components代表局部组件
上面的示例可以改为局部注册的方式:
<!DOCTYPE html> <html> <body> <div id="app"> <!-- 3. my-component只能在#app下使用--> <my-component></my-component> </div> </body> <script src="js/vue.js"></script> <script> // 1.创建一个组件构造器 var myComponent = Vue.extend({ template: '<div>This is my first component!</div>' }) new Vue({ el: '#app', components: { // 2. 将myComponent组件注册到Vue实例下 'my-component' : myComponent } }); </script> </html>
由于my-component组件是注册在#app元素对应的Vue实例下的,所以它不能在其它Vue实例下使用。
<div id="app2"> <!-- 不能使用my-component组件,因为my-component是一个局部组件,它属于#app--> <my-component></my-component> </div> <script> new Vue({ el: '#app2' }); </script>
二、组件注册语法糖
以上组件注册的方式有些繁琐,Vue.js为了简化这个过程,提供了注册语法糖
// 全局注册,my-component1是标签名称 Vue.component('my-component1',{ template: '<div>This is the first component!</div>' }) var vm1 = new Vue({ el: '#app1' })
Vue.component()的第1个参数是标签名称,第2个参数是一个选项对象,使用选项对象的template属性定义组件模板。
使用这种方式,Vue在背后会自动地调用Vue.extend()。
components实现局部注册
var vm2 = new Vue({ el: '#app2', components: { // 局部注册,my-component2是标签名称 'my-component2': { template: '<div>This is the second component!</div>' }, // 局部注册,my-component3是标签名称 'my-component3': { template: '<div>This is the third component!</div>' } } })
三、父组件和子组件
我们可以在组件中定义并使用其他组件,这就构成了父子组件的关系。
<!DOCTYPE html> <html> <body> <div id="app"> <parent-component> </parent-component> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> var Child = Vue.extend({ template: '<p>This is a child component!</p>' }) var Parent = Vue.extend({ // 在Parent组件内使用<child-component>标签 template :'<p>This is a Parent component</p><child-component></child-component>', components: { // 局部注册Child组件,该组件只能在Parent组件内使用 'child-component': Child } }) // 全局注册Parent组件 Vue.component('parent-component', Parent) new Vue({ el: '#app' }) </script> </html>
这段代码的运行结果如下
四、使用script或template标签
尽管语法糖简化了组件注册,但在template选项中拼接HTML元素比较麻烦,这也导致了HTML和JavaScript的高耦合性。
庆幸的是,Vue.js提供了两种方式将定义在JavaScript中的HTML模板分离出来。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue组件</title> <script src="js/vue.js"></script> </head> <body> <div id="app1"> <my-com></my-com> <my-com1></my-com1> </div> <template id="myCom"> <div>这是template标签构建的组件</div> </template> <script type="text/x-template" id="myCom1"> <div>这是script标签构建的组件</div> </script> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> Vue.component('my-com1', { template: '#myCom1' }); var app1 = new Vue({ el: '#app1', components: { 'my-com': { template: '#myCom' } } }); </script> </body> </html>
运行结果:
注意:使用<script>标签时,type指定为text/x-template,意在告诉浏览器这不是一段js脚本,浏览器在解析HTML文档时会忽略<script>标签内定义的内容。
在理解了组件的创建和注册过程后,我建议使用<script>或<template>标签来定义组件的HTML模板。
这使得HTML代码和JavaScript代码是分离的,便于阅读和维护。
五、模板的注意事项
1. 以子标签的形式在父组件中使用
<div id="app"> <parent-component> <child-component></child-component> </parent-component> </div>
上面是错误的。为什么这种方式无效呢?因为当子组件注册到父组件时,Vue.js会编译好父组件的模板,模板的内容已经决定了父组件将要渲染的HTML。
<parent-component>…</parent-component>相当于运行时,它的一些子标签只会被当作普通的HTML来执行,<child-component></child-component>不是标准的HTML标签,会被浏览器直接忽视掉
2.组件的模板只能有一个根元素。下面的情况是不允许的。
template: `<div>这是一个局部的自定义组件,只能在当前Vue实例中使用</div> <button>hello</button>`
3.组件中的data必须是函数
注册组件时传入的配置和创建Vue实例差不多,但也有不同,其中一个就是data
属性必须是一个函数。
这是因为如果像Vue实例那样,传入一个对象,由于JS中对象类型的变量实际上保存的是对象的引用
,所以当存在多个这样的组件时,会共享数据,导致一个组件中数据的改变会引起其他组件数据的改变。
而使用一个返回对象的函数,每次使用组件都会创建一个新的对象,这样就不会出现共享数据的问题来了。
4.关于DOM模板的解析
当使用 DOM 作为模版时 (例如,将 el 选项挂载到一个已存在的元素上), 你会受到 HTML 的一些限制,因为 Vue 只有在浏览器解析和标准化 HTML 后才能获取模板内容。尤其像这些元素 <ul>
,<ol>
,<table>
,<select>
限制了能被它包裹的元素,而一些像 <option>
这样的元素只能出现在某些其它元素内部
在自定义组件中使用这些受限制的元素时会导致一些问题,例如
<table> <my-row>...</my-row> </table>
自定义组件 <my-row>
被认为是无效的内容,因此在渲染的时候会导致错误。这时应使用特殊的 is 属性:
<table> <tr is="my-row"></tr> </table>
也就是说,标准HTML中,一些元素中只能放置特定的子元素,另一些元素只能存在于特定的父元素中。比如table中不能放置div
,tr
的父元素不能div等。所以,当使用自定义标签时,标签名还是那些标签的名字,但是可以在标签的is
属性中填写自定义组件的名字。
三、动态组件
有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里
简单点说:就是几个组件放在一个挂载点下,然后根据父组件的某个变量来决定显示哪个,或者都不显示。
要点:在挂载点使用component标签,然后使用v-bind:is=”组件名”,会自动去找匹配的组件名,如果没有,则不显示
动态组件,先看案例效果:
代码演示:css代码就不复制了,上面案例效果里有。
<script src="https://unpkg.com/vue"></script> <div id="dynamic-component-demo" class="demo"> <button v-for="tab in tabs"
v-bind:key="tab"
v-bind:class="['tab-button', { active: currentTab === tab }]"
v-on:click="currentTab = tab">{{ tab }}</button> <component v-bind:is="currentTabComponent" class="tab"></component> </div>
这里v-bind:key其实可有可无,具体key介绍可以看官网。
这里v-bind:class和v-on:click都是用来为了改变样式用的。
关键是component组件标签。
<script> //显示定义了三个组件 Vue.component('tab-科长', { template: '<div>一共有100个科长</div>' }) Vue.component('tab-处长', { template: '<div>一种有50个处长</div>' }) Vue.component('tab-局长', { template: '<div>一共有10个局长</div>' }) new Vue({ el: '#dynamic-component-demo', data: { currentTab: '局长', tabs: ['科长', '处长', '局长'] }, //计算属性,根据currentTab的改变来判断选择哪个组件 computed: { currentTabComponent: function() { return 'tab-' + this.currentTab } } }) </script>