6-SCJ -》 CWL
认识Vue
Vue(读音似view)
Vue是一个渐进式的框架,响应式
Vue有很多特点和Web卡法中常见的高级功能:
- 解耦视图和数据
- 可复用的组件
- 前端路由技术
- 状态管理
- 虚拟DOM
引入Vue的CDN
1 2 3 4 5
| <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
|
初步使用
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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> {{message}} <h1>{{name}}</h1> </div> <div> {{message}} </div>
<script src="./js/vue.js"></script>
<script> const app = new Vue({ el: '#app', data: { message: 'Hello 黄泽彬', name: '黄泽彬' } }) </script> </body> </html>
|
循环展示列表数据 v-for
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <body> <div id="app"> <ul> <li v-for="item in movies"> {{item}} </li> </ul> </div>
<script src="./js/vue.js"></script>
<script> const app = new Vue({ el: '#app', data: { name: 'HuangZebin', movies: ['速度与激情', '西游记', '垃圾'] } }) </script> </body>
|
绑定事件 v-on
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <body> <div id="app"> 当前技术:{{counter}} <button v-on:click="++counter">+</button> <button v-on:click="--counter">-</button> </div>
<script src="./js/vue.js"></script>
<script> const app = new Vue({ el: '#app', data: { counter: 0 } }) </script> </body>
|
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
| <body> <div id="app"> 当前技术:{{counter}} <button @click="add">+</button> <button @click="sub">-</button> </div>
<script src="./js/vue.js"></script>
<script> const app = new Vue({ el: '#app', data: { counter: 0 }, methods: { add: function() { ++this.counter; }, sub: function() { --this.counter; } } }) </script> </body>
|
MVVM
MVVM (Model View View Model)
Vue实例传入的options
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| el 类型:string | HTMLElement 作用:决定之后Vue实例会管理哪一个DOM data 类型:Object | Function【组件当中data必须是一个函数】 作用:Vue实例对应的数据对象 methods 类型:{ [key: string]: Function } 作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用 filters 过滤器 components 组件 props 属性
|
Mustache语法
通过Mustache语法(双大括号),可以将data中的文本数据插入到HTML中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <body> <div id="app"> <h1>{{message}}</h1> <h1>{{firstName + ' ' + lastName}}</h1> <h1>{{firstName}} {{lastName}}</h1> <h1>{{counter * 2}}</h1> </div>
<script src="js/vue.js"></script>
<script> const app = new Vue({ el: '#app', data: { message: 'Hello', firstName: '黄', lastName: '泽彬', counter: 1 } }) </script> </body>
|
v-once语法
v-once指令不需要跟任何表达式,该指令表示元素和组件只渲染一次,不会随着数据的改变而改变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <body> <div id="app"> <h1>{{message}}</h1> <h1 v-once>{{message}}</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '黄泽彬' } }) </script> </body>
|
v-html语法
v-html可以将数据解析为htm格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <body> <div id="app"> <h1>{{url}}</h1> <h1 v-html="url"></h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { url: '<a href="http://hzebin.cn">黄泽彬个人主页</a>' } }) </script> </body>
|
v-text语法
v-text作用和Mustache比较相似:都是用于将数据显示在界面中,v-text通常情况下,接收一个string类型
(不够灵活,后面的数据会被覆盖,没有双大括号好用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <body> <div id="app"> <h1>{{message}}456</h1>
<h1 v-text="message">456</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '123' } }) </script> </body>
|
v-pre指令
v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <body> <div id="app"> <h1>{{message}}</h1> <h1 v-pre>{{message}}</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '黄泽彬' } }) </script> </body>
|
[??] 13 v-cloak
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
| <body> <div id="app"> <img v-bind:src="imageUrl" alt=""/> <a v-bind:href="aHref">百度一下</a>
<img :src="imageUrl" alt=""/> <a :href="aHref">百度一下(语法糖)</a> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { imageUrl: 'https://i0.hdslb.com/bfs/archive/a85460cd16061187d1eb88ef849008ac7e8f1f3a.jpg@336w_190h.webp', aHref: 'http://baidu.com' } }) </script> </body>
|
v-bind动态绑定class—-对象语法
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
| <head> <meta charset="UTF-8"> <title>Title</title>
<style> .active { color: red; } </style> </head> <body> <div id="app"> <h1 class="active">你好</h1>
<h1 class="t1" :class="{active: isActive, line: isLine}">{{message}}</h1> <button @click="btnClick">按钮</button> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', isActive: true, isLine: true }, methods: { btnClick: function() { this.isActive = !this.isActive; } } }) </script> </body>
|
v-bind动态绑定class—-数组语法
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
| <body> <div id="app"> <h1 class="title" :class="['aa', 'bbb']">{{message}}</h1> <h1 :class="['a', c]">{{message}}</h1> <h1 :class="getClass()">{{message}}</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: 'Hello', c: 'ccc' }, methods: { getClass: function() { return [this.c]; } } }) </script> </body>
|
v-bind动态绑定style(一)
我们可以用v-bind:style来绑定一些CSS内联样式
在写CSS属性名时,比如font-size
- 我们可以使用驼峰式(camelCase)fontSize
- 或短横线分割(Kebab-case,记得用单引号括起来)’font-size’
绑定class有两种方式:
对象语法(属性值加了单引号为字符串,否则为变量)
数组语法
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
| <body> <div id="app"> <h1 :style="">{{message}}</h1> <h1 :style="{fontSize: '50px'}">{{message}}</h1> <h1 :style="{fontSize: finalSize}">{{message}}</h1> <h1 :style="getStyle()">{{message}}123</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: 'Hello World', finalSize: '100px' }, methods: { getStyle: function() { return {fontSize: this.finalSize}; } } }) </script> </body>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <body>
<div id="app"> <h1 :style="[baseStyle1, baseStyle2]">{{message}}</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: 'hzb', baseStyle1: {fontSize: '100px'}, baseStyle2: {backgroundColor: 'red'} } }) </script> </body>
|
计算属性 computed
computed: {} 可以对需要的数据进行转化之后显示,或需要将多个数据结合起来显示。(只执行一次,有缓存)(相当于一个属性,用名词命名)
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
| <body> <div id="app"> <h1>{{firstName + ' ' + lastName}}</h1> <h1>{{firstName}} {{lastName}}</h1>
<h1>{{getFullName()}}</h1>
<h1>{{fullName}}</h1> </div>
<script src="./js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { firstName: 'Huang', lastName: 'Zebin' }, methods: { getFullName: function() { return this.firstName + ' ' + this.lastName; } }, computed: { fullName: function() { return this.firstName + ' ' + this.lastName; } } }) </script> </body>
|
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
| <body> <div id="app"> <h1>总价为{{totalPrice}}</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { books: [ {id: '101', name: '操作系统', price: 100}, {id: '102', name: 'Linux操作系统', price: 150}, {id: '103', name: '高等数学', price: 200} ] }, computed: { totalPrice: function() { let total = 0; for (let i = 0; i < this.books.length; ++i) { total += this.books[i].price; } return total; } } }) </script> </body>
|
完整写计算属性的get/set
计算属性一般没有set方法,为只读属性
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
| <body> <div id="app"> <h1>{{fullName}}</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { firstName: 'Huang', lastName: 'Zebin' }, computed: { fullName: { set: function(newValue) { console.log("==" + newValue + "=="); const names = newValue.split(' '); this.firstName = names[0]; this.lastName = names[1]; }, get: function() { return this.firstName + ' ' + this.lastName; } } } }) </script> </body>
|
计算属性和methods的对比
methods方法每次调用都会重新计算;而computed只要结果不变就只执行一次,它有缓存,推荐computed
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
| <body> <div id="app"> <h1>{{firstName}} {{lastName}}</h1> <h1>{{getFullName()}}</h1> <h1>{{fullName}}</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { firstName: 'Huang', lastName: '泽彬' }, methods: { getFullName: function() { return this.firstName + ' ' + this.lastName; } }, computed: { fullName: function() { return this.firstName + ' ' + this.lastName; } } }) </script> </body>
|
ES6补充
事实上var是JS的一个设计上的错误,let关键字是可以更完美代替var。let有块级作用域,而var在块外任然可以访问会有问题
const关键字:用const修饰的标识符不会被再次赋值,保证了数据的安全性,但是申明的时候需要初始化一个值。
1 2 3 4
| const 1. 一旦给const修饰的标识符被赋之后,不能修改 2. 在使用const定义标识符,必须进行赋值 3. 常量的含义是指向的对象不能修改(内存地址不能改),但是可以改变对象的内部属性
|
【建议】在ES6开发中,优先使用const,只有需要改变某一个标识符的时候才使用let
增强写法
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
| <body> <script>
const name = 'Huang Zebin'; const age = 20; const height = 1.75;
const obj = { name, age, height } console.log(obj);
const obj2 = { run() {
}, eat() {
} }
</script> </body>
|
1 2 3 4 5 6
| ES5 ''单引号 ""双引号 ES6 ``【可以换行】 上面三种方式都可以申明字符串
|
v-on的基本使用和语法糖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <body> <div id="app"> <h1>{{counter}}</h1> <button v-on:click="add">+</button> <button @click="--counter">-</button> </div>
<script src="./js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { counter: 0 }, methods: { add() { ++this.counter; } } }) </script> </body>
|
v-on方法含有参数
当通过methods中定义方法,以供@click调用时,需要注意参数问题:
如果该方法不需要额外参数,那么方法后的()可以不添加
但是注意:如果方法本身中有一个参数,那么会默认将原生事件event参数传递进去。
如果需要同时传入某个参数,同时需要event时,可以通过$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 30 31 32 33 34 35 36 37 38
| <body> <div id="app"> <button @click="btn1Click">按钮1</button> <button @click="btn1Click()">按钮1</button>
<button @click="btn2Click(888)">按钮2</button> <button @click="btn2Click()">按钮2</button> <button @click="btn2Click">按钮2</button>
<button @click="btn3Click($event, 1, 2, m1, 'm1')">按钮3</button> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { m1: '11111' }, methods: { btn1Click() { console.log("123", "----"); }, btn2Click(a) { console.log(a); }, btn3Click(event, a, b, c, d) { console.log(event, a, b, c, d); } } }) </script> </body>
|
v-on的修饰符
1 2 3 4 5
| .stop修饰符,可以组织冒泡现象 .prevent修饰符,阻止默认行为;例如提交表单,会阻止浏览器自动提交表单,转到自己的方法 .{keyCode | keyAlias}监听键盘的按键 .native监听组件根元素的原生事件 .once只触发一次回调
|
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
| <body>
<div id="app"> <div @click="div1Click"> aaa <button @click="btn1Click">按钮1</button> </div>
<hr/>
<div @click="div1Click"> aaa <button @click.stop="btn1Click">按钮1</button> </div>
<hr/>
<form action="baidu"> <input type="submit" value="提交" @click.prevent="subClick"/> </form>
<input type="text" @keyup="keyClick"/>
<hr/> <input type="text" @keyup.enter="enterClick"/>
<button @click.once="btn2Click">按钮2</button> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: {
}, methods: { btn1Click() { console.log("btn1Click"); }, div1Click() { console.log("div1Click"); }, subClick() { console.log("subClick"); }, keyClick() { console.log("keyClick"); }, enterClick() { console.log("回车"); }, btn2Click() { console.log("btn2Click"); } } }) </script> </body>
|
v-if和v-else-if和v-else的使用
如果条件很多,推荐使用计算属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <body> <div id="app"> <h1 v-if="score >= 90">优秀</h1> <h1 v-else-if="score >= 80">良好</h1> <h1 v-else-if="score >= 60">及格</h1> <h1 v-else>不及格</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { score: 92 } }) </script> </body>
|
v-show
v-show指令决定一个元素是否渲染
v-if与v-show的比较:
- v-if当条件为false时,不会有对应的元素在DOM
- v-show当条件为false时,仅仅是将元素的display属性设置为none而已
怎么选择:
- 当需要在显示和隐藏之间切面很频繁是,使用v-show
- 当只有一次切换时,使用v-if
v-for遍历数组和对象
遍历数组
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
| <body> <div id="app"> <ul> <li v-for="item in names">{{item}}</li> </ul>
<ul> <li v-for="(item, index) in names">{{index + 1}} {{item}}</li> </ul> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { names: ['黄泽彬', '熊君', '陈文略'] }, methods: {
} }) </script> </body>
|
遍历对象
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
| <body> <div id="app"> <ul> <li>{{info.name}}</li> <li>{{info.age}}</li> <li>{{info.height}}</li> </ul>
<ul> <li v-for="value in info">{{value}}</li> </ul>
<ul> <li v-for="(value, key) in info">{{value}}--{{key}}</li> </ul>
<ul> <li v-for="(value, key, index) in info">{{value}}--{{key}}--{{index}}</li> </ul> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { info: { name: 'HuangZebin', age: 20, height: 1.75 } } }) </script> </body>
|
理解v-for绑定和非绑定key的区别
推荐在使用v-for的时候,给对应的元素或组件添加:key属性,这个:key值需要能与值对应(唯一性),比如value,但index可能会改变
为什么要key属性:Vue的虚拟DOM有关系,加key不让它复用原来的代码
数组中的哪些方法是响应式的
因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 哪些方法是响应式的: push()在数组的尾部添加元素【可以一次添加多个元素,逗号分隔】 pop()删除数组尾部的元素 shift()删除数组头部的元素 unshift()在数组头部添加元素【可以一次添加多个元素,逗号分隔】 splice() 删除元素: splice(start, n) 从start下标开始,删除n个元素,如果省略n则后面全删 替换元素: splice(start, n, ...value[])从start删n个,之后添加元素在那个位置 插入元素: splice(start, 0, ...value[])在start位置开始插入元素 sort()排序,默认从小到大 reverse() 数组反转
Vue.set(要修改的对象, 索引值, 修改后的值) 例如Vue.set(this.arr, 0, 'bb');
============================= 通过索引修改元素不是响应式的 =============================
|
表单绑定v-model
Vue中使用v-model指令来实现表单元素和数据的双向绑定
原理:
v-bind绑定一个value属性
v-on指令给当前元素绑定input事件
等价于
1 2 3
| <input type="text" v-model="message"/> ↑↓ <input type="text" :value="message" :input="message = $event.target.value"/>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <body> <div id="app"> 双向绑定 <input type="text" v-model="message"/> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: 'Huang Zebin' } }) </script> </body>
|
v-model与radio(单选框)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <body> <div id="app"> <label for="male"> <input type="radio" id="male" name="sex" value="男" v-model="sex"/>男 </label> <label for="female"> <input type="radio" id="female" name="sex" value="女" v-model="sex"/>女 </label> <h1>你选择的性别是:{{sex}}</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { sex: '' } }) </script> </body>
|
v-model与checkbox(复选框)
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
| <body> <div id="app"> <label for="agree"> <input type="checkbox" id="agree" v-model="isAgree"/>同意协议 </label> <br/> <button :disabled="!isAgree">下一步</button>
<hr/>
<input type="checkbox" value="足球" v-model="hobbies"/>足球 <input type="checkbox" value="台球" v-model="hobbies"/>台球 <input type="checkbox" value="乒乓球" v-model="hobbies"/>乒乓球 <input type="checkbox" value="蓝球" v-model="hobbies"/>篮球 <h1>选择的爱好为:{{hobbies}}</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { isAgree: false, hobbies: [] } }) </script> </body>
|
单选框对应boolean,多选框对应数组
v-model和select结合使用
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
| <body> <div id="app"> <select v-model="fruit"> <option value="苹果">苹果</option> <option value="香蕉">香蕉</option> <option value="葡萄">葡萄</option> <option value="桃子">桃子</option> </select> <h1>选了什么水果:{{fruit}}</h1>
<select v-model="fruits" multiple> <option value="苹果">苹果</option> <option value="香蕉">香蕉</option> <option value="葡萄">葡萄</option> <option value="桃子">桃子</option> </select> <h1>选了什么水果:{{fruits}}</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { fruit: '香蕉', fruits: [] } }) </script> </body>
|
值绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <body> <div id="app"> <label v-for="item in starts" :for="item"> <input type="checkbox" :id="item" :value="item" v-model="selectStarts"/>{{item}} </label> <h1>选择的明星为:{{selectStarts}}</h1> </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { selectStarts: [], starts: ['赵丽颖', '吴亦凡', '邓超', '李现'] } }) </script> </body>
|
v-model修饰符
1 2 3 4 5 6 7 8 9 10 11
| lazy修饰符 1. 默认情况下,v-model默认是在input事件中同步输入框的数据的 2. 也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变 3. lazy修饰符可以让数据在失去焦点或者回车时才会更新 number修饰符 1. 默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当作【字符串】类型进行处理 2. 但是如果我们希望处理的数字类型,那么最好直接将内容当做数字处理 3. number修饰符可以让在输入框中输入的内容自动转成数字类型 trim修饰符 1. 如果输入的内容首尾有很多空格,通常我们希望将其去除 2. trim修饰符可以过滤内容左右边的空格
|
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
| <body> <div id="app"> <input type="text" v-model.lazy="message"/> {{message}}
<hr/>
<input type="number" v-model.number="age"/> 类型{{typeof age}}
<hr/>
<input type="text" v-model.trim="name"/> name={{name}} </div>
<script src="js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '123', age: 23, name: '' } }) </script> </body>
|
组件化初步认识
- 我们将一个完整的页面分成很多个组件
- 每个组件都用于实现页面的一个功能
- 而每一个组件又可以进行细分
- 组件化是Vue.js中的重要思想
- 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
- 任何的应用都会被抽象成一棵组件树
- 组件化思想的应用:
- 有了组件化思想,我们在之后的开发中就要充分利用它
- 进可能地将页面拆分成一个个小地、可复用地组件
- 这样让我们的代码更加方便组织和管理,并且扩展性也更强
注册组件的基本步骤:
- 创建组件构造器Vue.extend()
- 调用Vue.extend()创建的是一个组件构造器
- 通常在创建组件构造器是,传入template代表我们自定义组件的模板
- 该模板就是在使用到组件的地方,要显示的HTML代码
- 注册组件Vue.component()
- 调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称
- 需要传递两个参数:注册组件的标签名、组件构造器
- 使用组件
- 组件必须挂载到某个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
| <body> <div id="app"> <my-cpn></my-cpn> <my-cpn></my-cpn> </div>
<script src="js/vue.js"></script> <script> const cpnC = Vue.extend({ template: ` <div> <h6>我是标题</h6> <p>第一行内容</p> <p>第二行内容</p> </div>` })
Vue.component('my-cpn', cpnC);
const app = new Vue({ el: '#app' }) </script> </body>
|
【!写组件的时候,要写在内 !】
全局组件和局部组件
在开发中一般只有一个new Vue实例
全局组件:可以在多个Vue的实例下使用
局部组件:在new Vue里面定义的 components: {标签名: 组件名}
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
| <body> <div id="app"> <cpn2></cpn2> <cpn1></cpn1> </div>
<div id="app2"> <cpn1></cpn1> <cpn2></cpn2> </div>
<script src="js/vue.js"></script>
<script> const cpnC = Vue.extend({ template: ` <div> <h1>我的标题</h1> <p>哈哈哈这一行是废的</p> </div> ` })
Vue.component('cpn1', cpnC); </script> <script> const cpnD = Vue.extend({ template: ` <div> <h2>局部的组件模板</h2> </div> ` }) </script>
<script> const app = new Vue({ el: '#app' })
const app2 = new Vue({ el: '#app2', components: { cpn2: cpnD } }) </script> </body>
|
父组件和子组件的区别
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
| <body> <div id="app"> <cpn></cpn> </div>
<script src="js/vue.js"></script> <script> const cpnC1 = Vue.extend({ template: ` <div> <h1>组件1</h1> </div> ` })
const cpnC2 = Vue.extend({ template: ` <div> <h1>组件2</h1> <cpn1></cpn1> </div> `, components: { cpn1: cpnC1 } })
const app = new Vue({ el: '#app', components: { cpn: cpnC2 } }) </script> </body>
|
组件的语法糖写法【组件标签名用小写】
查找组件时,先在自己内部找components,如果没有则找全局的,所以全局的组件可以不用在new Vue实例里面的components注册
【组件的名字用小写的,因为HTML解析是不区分大小写,用全小写来查找组件标签!!】
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
| <body> <div id="app"> <my-cpn></my-cpn> <my-cpn2></my-cpn2> </div>
<script src="js/vue.js"></script> <script> Vue.component('my-cpn', { template: ` <div> <h1>语法糖-全局组件</h1> </div> ` }) </script> <script> const app = new Vue({ el: '#app', components: { 'my-cpn2': { template: ` <div> <h1>语法糖--局部组件</h1> </div> ` } } }) </script> </body>
|
组件模板抽离的写法
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
| <body> <div id="app"> <my-cpn1></my-cpn1> <my-cpn2></my-cpn2> </div>
<script type="text/x-template" id="my-cpn1"> <div> <h1>组件模板分离-全局变量 方式1</h1> </div> </script>
<template id="my-cpn2"> <div> <h1>组件模板分离-全局变量 方式2</h1> </div> </template>
<script src="js/vue.js"></script>
<script> Vue.component('my-cpn1', { template: '#my-cpn1' }) </script> <script> Vue.component('my-cpn2', { template: '#my-cpn2' }) </script>
<script> const app = new Vue({ el: '#app' }) </script> </body>
|
组件data必须是函数
组件里面访问不了实例里的data数据
组件自己的数据存放在哪里呢:
- 组件对象也有一个data属性(也有methods等属性)
- 只是这个data属性必须是一个函数
- 而且这个函数返回一个对象,对象内部保存着数据
如果组件里的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 31 32 33 34 35 36 37 38 39 40 41
| <body> <div id="app"> <my-cpn1></my-cpn1> <my-cpn1></my-cpn1> </div>
<template id="my-cpn1"> <div> <h1>当前计数为:{{counter}}</h1> <button @click="decrement">-</button> <button @click="increment">+</button> </div> </template>
<script src="js/vue.js"></script>
<script> Vue.component('my-cpn1', { template: '#my-cpn1', data() { return { counter: 0 } }, methods: { increment() { ++this.counter; }, decrement() { --this.counter; } } }) </script>
<script> const app = new Vue({ el: '#app' }) </script> </body>
|
父子组件的通信
在开发中,往往一些数据确实需要从上层(root)传递到下层(小组件)。
- 比如在一个页面中,我们从服务器请求到了很多的数据
- 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
- 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)
如何进行父子组件间的通信呢:
- 通过props向子组件传递数据
- 通过事件向父组件发送信息
props的值有两种方式:
一:字符串数组,数组中的字符串就是传递时的名称
二:对象,对象可以设置传递时的类型,也可以设置默认值等。可以对类型进行验证,验证支持的类型:String, Number, Boolean, Array, Object, Date, Function, Symbol; 如果有自定义构造函数时,验证也支持自定义的类型。还可以提供默认值,如果使用了模板标签而没有v-bind绑定属性的值的话,会使用默认值。还可以限制使用模板标签时是否必传值。
1 2 3 4 5
| 对象名: { type: 类型, default: 默认值, required: true/false 如果为true时,使用模板标签必须赋值 }
|
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
| 【父组件-》子组件 方式1】
<body> <div id="app"> <my-cpn :cmessage="message" :cstarts="starts"></my-cpn> </div>
<template id="my-cpn"> <div> <h1>子组件</h1> <ul> <li v-for="item in cstarts">{{item}}</li> </ul> <p>{{cmessage}}</p> </div> </template>
<script src="js/vue.js"></script>
<script> Vue.component('my-cpn', { template: '#my-cpn', props: ['cmessage', 'cstarts'] }) </script>
<script> const app = new Vue({ el: '#app', data: { message: '收到', starts: ['邓超', '吴亦凡', '赵丽颖'] } }) </script> </body>
|
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
| 【父组件 -》 子组件 方式二】
<body> <div id="app"> <my-cpn :cmessage="message" :cstarts="starts"></my-cpn> </div>
<template id="my-cpn"> <div> <h1>{{cmessage}}</h1> <ul> <li v-for="item in cstarts">{{item}}</li> </ul> </div> </template>
<script src="js/vue.js"></script>
<script> Vue.component('my-cpn', { template: '#my-cpn',
props: { cmessage: { type: String, default: 'aaa', required: true }, cstarts: { type: Array, default() { return []; } } } }) </script>
<script> const app = new Vue({ el: '#app', data: { message: '明星展示', starts: ['吴亦凡', '赵丽颖', '邓超', '邓紫棋'] } }) </script> </body>
|
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
| 【子组件 -》 父组件 自定义事件$smit】
<body> <div id="app"> <my-cpn @categories-btn-click="cpnClick"></my-cpn> </div>
<template id="my-cpn"> <div> <button v-for="item in categories" @click="btnClick(item)"> {{item.name}} </button> </div> </template>
<script src="js/vue.js"></script>
<script> Vue.component('my-cpn', { template: '#my-cpn', data() { return { categories: [ {id: '0', name: '热门推荐'}, {id: '1', name: '手机数码'}, {id: '2', name: '家用家电'}, {id: '3', name: '电脑办公'} ] } }, methods: { btnClick(item) { this.$emit('categories-btn-click', item, '测试'); } } }) </script>
<script> const app = new Vue({ el: '#app', methods: { cpnClick(item, a) { console.log(item, '123', a) } } }) </script> </body>
|
父访问子里面的东西–$children $refs
this.$children返回的是子组件数组
this.$refs返回对象类型,默认为空对象{};需要在使用标签那里加ref=””
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
| <body> <div id="app"> <my-cpn></my-cpn> <my-cpn></my-cpn> <my-cpn></my-cpn> <button @click="btnClick">按钮</button> </div>
<template id="my-cpn"> <div> <h1>我是子组件</h1> </div> </template>
<script src="./js/vue.js"></script>
<script> const app = new Vue({ el: '#app', methods: { btnClick() { console.log(this.$children); for (let c of this.$children) { c.showMessage(); console.log(c.name); } } }, components: { 'my-cpn': { template: '#my-cpn', methods: { showMessage() { console.log('Hello, 我是子组件里的方法') } }, data() { return { name: '我是子组件的数据name' } } } } }) </script> </body>
|
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
| <body> <div id="app"> <cpn ref="ccc"></cpn> <cpn ref="bbb"></cpn> <cpn ref="aaa"></cpn> <cpn></cpn> <button @click="btnClick">按钮</button> </div>
<template id="cpn"> <div> <h1>我是一个组件</h1> </div> </template>
<script src="js/vue.js"></script>
<script> const app = new Vue({ el: '#app', methods: { btnClick() { console.log(this.$refs.aaa.showMessage()); } }, components: { cpn: { template: '#cpn', data() { return { name: '子组件的name' } }, methods: { showMessage() { console.log("子组件的方法"); } } } } }) </script> </body>
|
子访问父里面的东西–$parent $root
不推荐子访问父组件,这样子组件与父组件之间有耦合性,子组件不能独立
this.$parent访问父组件
this.$root访问根组件【直接访问到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
| <body> <div id="app"> <cpn></cpn> </div>
<template id="cpn"> <div> <h1>我是子组件</h1> <button @click="btnClick">按钮</button> </div> </template>
<script src="js/vue.js"></script>
<script> const app = new Vue({ el: '#app', data: { message: '父组件message' }, components: { cpn: { template: '#cpn', methods: { btnClick() { console.log(this.$parent.message); } } } } }) </script> </body>
|
slot插槽的基本使用
组件的插槽:
- 组件的插槽也是为了让我们封装的组件更加具有扩展性
- 让使用者决定组件内部的一些内容到底展示什么
- 插槽的基本使用
- 插槽的默认值默认值写在这里
- 如果有多个值同时放入到一个插槽进行替换时,一起作为替换元素
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
| <body> <div id="app"> <cpn></cpn> <cpn> <span>嘻嘻嘻</span> </cpn> <cpn> <i>我去跑步了</i> <i>我去跑步了</i> </cpn> <cpn></cpn> <cpn></cpn> </div>
<template id="cpn"> <div> <h1>我是组件</h1> <p>哈哈哈哈哈哈</p> <slot><button>按钮</button></slot> </div> </template>
<script src="js/vue.js"></script>
<script> Vue.component('cpn', { template: '#cpn' }) </script>
<script> const app = new Vue({ el: '#app' }) </script> </body>
|
slot具名插槽的使用
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
| <body> <div id="app"> <cpn> <button slot="left">返回</button> <span slot="right">菜单</span> <span slot="center"> <span>aaa</span> <span>bbb</span> </span> </cpn> <hr/> <cpn><span slot="right">菜单</span></cpn> </div>
<template id="cpn"> <div> <slot name="left"><span>左边</span></slot> <slot name="center"><span>中间</span></slot> <slot name="right"><span>右边</span></slot> </div> </template>
<script src="js/vue.js"></script>
<script> Vue.component('cpn', { template: '#cpn' }) </script>
<script> const app = new Vue({ el: '#app' }) </script> </body>
|
理解编译作用域的概念
判断是哪里的变量,看在哪一个模板,而不是在标签的那个组件
父组件模板的所有东西都会在父级作用域内编译,子组件模板的所有东西都会在子级作用域内编译
父组件替换插槽的标签,但是内容有子组件来提供
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
| <body> <div id="app"> <cpn v-show="isShow"></cpn> </div>
<template id="cpn"> <div> <h1>我是子组件,有两个isShow,看看是哪一个</h1> </div> </template>
<script src="js/vue.js"></script>
<script> Vue.component('cpn', { template: '#cpn', data() { return { isShow: false } } }) </script>
<script> const app = new Vue({ el: '#app', data: { isShow: true } }) </script> </body>
|
作用域插槽的使用
如果父组件对子组件展示的内容不满意,想以另外一种方式进行展示,就可以用到作用域插槽。
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
| <body> <div id="app"> <cpn></cpn>
<cpn> <template slot-scope="s"> <span v-for="item in s.planguage">{{item}} - </span> <br/> <span>{{s.planguage.join(' # ')}}</span> <br/> <span>{{s.abc}}</span> </template> </cpn> </div>
<template id="cpn"> <div> <slot :planguage="pLanguage" :abc="abc"> <ul> <li v-for="item in pLanguage">{{item}}</li> </ul> </slot> </div> </template>
<script src="js/vue.js"></script>
<script> Vue.component('cpn', { template: '#cpn', data() { return { pLanguage: ['Java', 'C', 'C++', 'JavaScript', 'Python', 'Linux'], abc: 'abc' } } }) </script>
<script> const app = new Vue({ el: '#app' }) </script> </body>
|
ES6模块化的导入和导出
export导出
import导入
什么是Webpack
从本质上将,webpack是一个现代的JavaScript应用的静态模块打包工具。
【前端模块化】【打包】
webpack其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系,而且不仅仅是JavaScript文件,我们的CSS、图片、JSON文件等等在webpack中都可以被当做模块来使用。
grunt/gulp和we bpack有什么不同呢?
- grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心
- webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能,是它附带的功能。
webpack运行需要依赖node环境
node环境为了可以正常执行很多代码,必须包含各种依赖的包;所以管理各种包需要一种管理工具:npm工具(node packages manager)
安装webpack首先要安装Node.js,Node.js自带了软件包管理工具npm
node -v 查看node版本
全局安装webpack(这里我先指定版本号3.6.0,因为vue cli2依赖该版本)
全局安装webpack:npm install webpack@3.6.0 -g
查看版本webpack –version
局部安装webpack(后续才需要)。–save-dev是开发时依赖,项目打包后不需要继续使用的
为什么全局安装后还需要局部安装的:
- 在终端直接执行webpack命令,使用的是全局安装的webpack
- 当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack
====================================================
other:
数组.push()
数组.splice()
====
.toFixed(2) 保留两位小数
====
unfinish
44.JS高级函数和多张种方式的for
59.props的驼峰命名问题,先不用驼峰,用-分隔
65 watch
74 ES6导入导出
=====
typeof 变量名 -》获取变量的类型
========
Vue CLI3.X(构造Vue的项目)
Vue2.5.21
===================
不推荐子组件修改子组件的props数据,让父组件修改子组件的props数据
============