为什么学习vue
1可能你的公司正要将原有的项目使用Vue进行重构。
2也可能是你的公司新项目决定使用Vue的技术栈。
3当然,如果你现在正在换工作,你会发现招聘前端的需求中,10个有8个都对Vue有或多或少的要求。
4当然,作为学习者我们知道Vuejs目前非常火,可以说是前端必备的一个技能。
Vue (读音 /vjuː/,类似于 view),不要读错。
渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,一点一点来开发,带来更丰富的交互体验。
或者如果你希望将更多的业务逻辑使用Vue实现,那么Vue的核心库以及其生态系统,比如【Vue核心+Vue-router+Vuex】,也可以满足你各种各样的需求。
解耦视图和数据
可复用的组件
前端路由技术
状态管理
虚拟DOM
在我们前端开发中,通常就是DOM层。
主要的作用是给用户展示各种信息。
数据可能是我们固定的死数据,更多的是来自我们服务器,从网络上请求下来的数据。
视图模型层是View和Model沟通的桥梁。
一方面它实现了Data Binding,也就是数据绑定,将Model的改变实时的反应到View中
另一方面它实现了DOM Listener,也就是DOM监听,当DOM发生一些事件(点击、滚动、touch等)时,可以监听到,并在需要的情况下改变对应的Data。
1-直接CDN引入
<!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- 生产环境版本,优化了尺寸和速度 --> <script src="https://cdn.jsdelivr.net/npm/vue"></script>
2-下载和引入
开发环境 https://vuejs.org/js/vue.js 生产环境 https://vuejs.org/js/vue.min.js
3-NPM安装
<div id="app"> <h2>{{message}}</h2> <h1 >{{name}}</h1> </div> <div>{{message}}</div> <script src="../js/vue.js"></script> <script> // let(变量)/const(常量) // 编程范式: 声明式编程 const app = new Vue({ el: '#app', // 用于挂载要管理的元素 data: { // 定义数据 message: '你好啊,!', name: 'zy' } }) // 原生js的做法(编程范式: 命令式编程) // 1.创建div元素,设置id属性 // 2.定义一个变量叫message // 3.将message变量放在前面的div元素中显示 // 4.修改message的数据: 今天天气不错! // 5.将修改后的数据再次替换到div元素 </script>
类型:string | HTMLElement
作用:决定之后Vue实例会管理哪一个DOM。
类型:Object | Function (组件当中data必须是一个函数)
作用:Vue实例对应的数据对象。
类型:{ [key: string]: Function }
作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用。
vue实例从创建到销毁的过程中有很多个阶段,在每个阶段都有一些函数会被自动执行,可以方便的帮助我们完成自动被执行调用的逻辑。
<body> <!-- 生命周期 --> <div id="demodiv"> <h1>{{text}}</h1> <button v-on:click="text='我改了'">点我修改</button> </div> <script> new Vue({ el:"#demodiv", data:{ text:"生命周期" }, methods:{}, watch:{}, computed:{}, filters:{}, beforeCreate(){ console.log("实例创建之前") }, created(){ console.log("实例创建之后") }, beforeMount(){ console.log("开始创建模板")挂载前 }, mounted(){ console.log("创建模板ok")挂载后 }, beforeUpdate(){ console.log("开始更新") }, updated(){ console.log("更新完成") }, beforeDestroy(){ console.log("开始销毁") }, destroyed(){ console.log("销毁完成") }, }) </script> </body>
Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易完成指定逻辑。 U�
它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后
第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
DOM 渲染在 mounted 中就已经完成了。
<!--mustache语法中,不仅仅可以直接写变量,也可以写简单的表达式--> <!--Mustache: 胡子/胡须--> <h2>{{firstName + lastName}}</h2> <h2>{{firstName + ' ' + lastName}}</h2> <h2>{{firstName}} {{lastName}}</h2> <h2>{{counter * 2}}</h2>
在某些情况下,我们可能不希望界面随意的跟随改变
这个时候,我们就可以使用一个Vue的指令
v-once:
该指令后面不需要跟任何表达式(比如之前的v-for后面是由跟表达式的)
该指令表示元素和组件(组件后面才会学习)只渲染一次,不会随着数据的改变而改变。
<h2 v-once>{{message}}</h2>
某些情况下,我们从服务器请求到的数据本身就是一个HTML代码
如果我们直接通过{{}}来输出,会将HTML代码也一起输出。
但是我们可能希望的是按照HTML格式进行解析,并且显示对应的内容。
如果我们希望解析出HTML展示
可以使用v-html指令
该指令后面往往会跟上一个string类型
会将string的html解析出来并且进行渲染
<div id="app"> <h2>{{url}}</h2> <!-- v-html指令把数据按照html标签来解析--> <h2 v-html="url"></h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', url: '<a href="http://www.baidu.com">百度一下</a>' } }) </script>
v-text作用和Mustache比较相似:都是用于将数据显示在界面中
v-text通常情况下,接受一个string类型
<h2>{{message}}, 李银河!</h2> <h2 v-text="message">, 李银河!</h2>
v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法。
比如下面的代码:
<div id="app"> <h2>{{message}}</h2> <h2 v-pre>{{message}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊' } }) </script>
第一个h2元素中的内容会被编译解析出来对应的内容
第二个h2元素中会直接显示{{message}}
在某些情况下,我们浏览器可能会直接显然出未编译的Mustache标签。 ncloak: 斗篷。
用v-cloak先不让它显示出来,加载完成后再显示出来
<div id="app" v-cloak> <h2>{{message}}</h2> </div> <script src="../js/vue.js"></script> <script> // 在vue解析之前, div中有一个属性v-cloak // 在vue解析之后, div中没有一个属性v-cloak setTimeout(function () { const app = new Vue({ el: '#app', data: { message: '你好啊' } }) }, 1000) </script>
除了内容需要动态来决定外,某些属性我们也希望动态来绑定。
比如动态绑定a元素的href属性
比如动态绑定img元素的src属性
这个时候,我们可以使用v-bind指令:
作用:动态绑定属性
缩写::
预期:any (with argument) | Object (without argument)
参数:attrOrProp (optional)
<body> <div id="app"> <!-- 错误的做法: 这里不可以使用mustache语法--> <!--<img src="{{imgURL}}" alt="">--> <!-- 正确的做法: 使用v-bind指令 --> <img v-bind:src="imgURL" alt=""> <a v-bind:href="aHref">百度一下</a> <!--<h2>{{}}</h2>--> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', imgURL: 'https://img11.360buyimg.com/mobilecms/s350x250_jfs/t1/20559/1/1424/73138/5c125595E3cbaa3c8/74fc2f84e53a9c23.jpg!q90!cc_350x250.webp', aHref: 'http://www.baidu.com' } }) </script> </body>
就是冒号 :
<div id="app"> <!--语法糖的写法--> <img :src="imgURL" alt=""> <a :href="aHref">百度一下</a> </div
很多时候,我们希望动态的来切换class,比如:
当数据为某个状态时,字体显示红色。
当数据另一个状态时,字体显示黑色。
绑定方式:对象语法
对象语法的含义是:class后面跟的是一个对象。
用法一:直接通过{}绑定一个类 <h2 :class="{'active': isActive}">Hello World</h2> 用法二:也可以通过判断,传入多个值 <h2 :class="{'active': isActive, 'line': isLine}">Hello World</h2> 用法三:和普通的类同时存在,并不冲突 注:如果isActive和isLine都为true,那么会有title/active/line三个类 <h2 class="title" :class="{'active': isActive, 'line': isLine}">Hello World</h2> 用法四:如果过于复杂,可以放在一个methods或者computed中 注:classes是一个计算属性 <h2 class="title" :class="classes">Hello World</h2>
如果对象语法过与复杂,可以将这个放到函数里或者计算属性里
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .active { color: red; } </style> </head> <body> <div id="app"> <h2 class="title" v-bind:class="{active: isActive, line: isLine}">{{message}}</h2> <h2 class="title" v-bind:class="getClasses()">{{message}}</h2> <button v-on: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 }, getClasses: function () { return {active: this.isActive, line: this.isLine} } } }) </script> </body> </html>
绑定方式:数组语法
数组语法的含义是:class后面跟的是一个数组。数组里面如果是常量要加单引号,否则常量会被当成变量来解析,是变量不用加
用法一:直接通过{}绑定一个类 <h2 :class="['active']">Hello World</h2> 用法二:也可以传入多个值 <h2 :class=“[‘active’, 'line']">Hello World</h2> 用法三:和普通的类同时存在,并不冲突 注:会有title/active/line三个类 <h2 class="title" :class=“[‘active’, 'line']">Hello World</h2> 用法四:如果过于复杂,可以放在一个methods或者computed中 注:classes是一个计算属性 <h2 class="title" :class="classes">Hello World</h2>
<body> <div id="app"> <h2 class="title" :class="[active, line]">{{message}}</h2> <h2 class="title" :class="getClasses()">{{message}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', active: 'aaaaaa', line: 'bbbbbbb' }, methods: { getClasses: function () { return [this.active, this.line] } } }) </script> </body>
:style="{color: currentColor, fontSize: fontSize + ‘px’}"
style后面跟的是一个对象类型
对象的key是CSS属性名称
对象的value是具体赋的值,值可以来自于data中的属性
value如果是具体的值,必须加上单引号,否则会被当成变量来解析
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .title { font-size: 50px; color: red; } </style> </head> <body> <div id="app"> <!--<h2 :style="{key(属性名): value(属性值)}">{{message}}</h2>--> <!--'50px'必须加上单引号, 否则是当做一个变量去解析--> <!--<h2 :style="{fontSize: '50px'}">{{message}}</h2>--> <!--finalSize当成一个变量使用--> <!--<h2 :style="{fontSize: finalSize}">{{message}}</h2>--> <h2 :style="{fontSize: finalSize + 'px', backgroundColor: finalColor}">{{message}}</h2> <h2 :style="getStyles()">{{message}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', finalSize: 100, finalColor: 'red', }, methods: { getStyles: function () { return {fontSize: this.finalSize + 'px', backgroundColor: this.finalColor} } } }) </script> </body> </html>
<div v-bind:style="[baseStyles, overridingStyles]"></div>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h2 :style="[baseStyle, baseStyle1]">{{message}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', baseStyle: {backgroundColor: 'red'}, baseStyle1: {fontSize: '100px'}, } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 普通变化 --> <h2>{{firstName + ' ' + lastName}}</h2> <h2>{{firstName}} {{lastName}}</h2> <!-- 调用方法 --> <h2>{{getFullName()}}</h2> <!-- 计算属性返回的 --> <h2>{{fullName}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { firstName: 'Lebron', lastName: 'James' }, // computed: 计算属性() computed: { fullName: function () { return this.firstName + ' ' + this.lastName } }, methods: { getFullName() { return this.firstName + ' ' + this.lastName } } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h2>总价格: {{totalPrice}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { books: [ {id: 110, name: 'Unix编程艺术', price: 119}, {id: 111, name: '代码大全', price: 105}, {id: 112, name: '深入理解计算机原理', price: 98}, {id: 113, name: '现代操作系统', price: 87}, ] }, methods: { getTotalPrice: function () { let result = 0 for (let i=0; i < this.books.length; i++) { result += this.books[i].price } return result } }, computed: { totalPrice: function () { let result = 0 for (let i=0; i < this.books.length; i++) { result += this.books[i].price } return result //es6用法 // for (let i in this.books) { // this.books[i] // } // // for (let book of this.books) { // // } } } }) </script> </body> </html>
computed: { fullName: function () { return this.firstName + ' ' + this.lastName } }
computed: { fullName: { set: function(newValue) { }, get: function () { } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h2>{{fullName}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { firstName: 'Kobe', lastName: 'Bryant' }, computed: { // fullName: function () { // return this.firstName + ' ' + this.lastName // } // name: 'coderwhy' // 计算属性一般是没有set方法, 只读属性. 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 } }, // fullName: function () { // return this.firstName + ' ' + this.lastName // } } }) </script> </body> </html>
methods和computed看起来都可以实现我们的功能,那么为什么还要多一个计算属性这个东西呢?
原因:
在前端开发中,我们需要经常和用于交互。
这个时候,我们就必须监听用户发生的时间,比如点击、拖拽、键盘事件等等
在Vue中如何监听事件呢?使用v-on指令
v-on介绍
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h2>{{counter}}</h2> <!--<button v-on:click="counter++">+</button>--> <!--<button v-on:click="counter--;">-</button>--> <!--<button v-on:click="increment">+</button>--> <!--<button v-on:click="decrement">-</button>--> <!-- 语法糖--> <button @click="increment">+</button> <button @click="decrement">-</button> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { counter: 0 }, methods: { increment() { this.counter++ }, decrement() { this.counter-- } } }) </script> </body> </html>
当通过methods中定义方法,以供@click调用时,需要注意参数问题:
如果该方法不需要额外参数,那么方法后的()可以不添加,如果方法本身中有一个参数,那么会默认将原生事件event参数传递进去
如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件。
在事件定义时, 写方法时省略了小括号, 但是方法本身是需要一个参数的, 这个时候, Vue会默认将浏览器生产的event事件对象作为参数传入到方法
如果函数需要参数,但是没有传入, 那么函数的形参为undefined
方法定义时, 我们需要event对象, 同时又需要其他参数,在调用方式, 就需要手动的获取到浏览器参数的事件对象,给绑定的函数传入 $event参数就行。
<button @click="btn3Click("abc",$event)">按钮3</button>
Vue提供了修饰符来帮助我们方便的处理一些事件:
<!--1. .stop修饰符的使用--> <div @click="divClick"> aaaaaaa <button @click.stop="btnClick">按钮</button> </div> <!--2. .prevent修饰符的使用--> <br> <form action="baidu"> <input type="submit" value="提交" @click.prevent="submitClick"> </form> <!--3. .监听某个键盘的键帽--> <input type="text" @keyup.enter="keyUp"> <!--4. .once修饰符的使用--> <button @click.once="btn2Click">按钮2</button>
v-if的原理:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h2 v-if="isShow"> <div>abc</div> <div>abc</div> <div>abc</div> <div>abc</div> <div>abc</div> {{message}} </h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', isShow: true } }) </script> </body> </html>
<div id="app"> <h2 v-if="isShow"> {{message}} </h2> <h1 v-else>isShow为false时, 显示我</h1> </div>
<div id="app"> <h2 v-if="score>=90">优秀</h2> <h2 v-else-if="score>=80">良好</h2> <h2 v-else-if="score>=60">及格</h2> <h2 v-else>不及格</h2> <h1>{{result}}</h1> </div>
v-show的用法和v-if非常相似,也用于决定一个元素是否渲染:
<h2 v-show="isShow" id="bbb">{{message}}</h2>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!--1.在遍历的过程中,没有使用索引值(下标值)--> <ul> <li v-for="item in names">{{item}}</li> </ul> <!--2.在遍历的过程中, 获取索引值--> <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: ['why', 'kobe', 'james', 'curry'] } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!--1.在遍历对象的过程中, 如果只是获取一个值, 那么获取到的是value--> <ul> <li v-for="item in info">{{item}}</li> </ul> <!--2.获取key和value 格式: (value, key) --> <ul> <li v-for="(value, key) in info">{{value}}-{{key}}</li> </ul> <!--3.获取key和value和index 格式: (value, key, index) --> <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: 'why', age: 18, height: 1.88 } } }) </script> </body> </html
官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性。这个其实和Vue的虚拟DOM的Diff算法有关系。当某一层有很多相同的节点时,也就是列表节点时,比如ABCDE,我们希望插入一个新的节点,我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的。即把C更新成F,D更新成C,E更新成D,最后再插入E,很没有效率所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。所以一句话,key的作用主要是为了高效的更新虚拟DOM。
<div id="app"> <ul> <li v-for="item in letters" :key="item">{{item}}</li> </ul> </div>
data:{ letters: ['a', 'b', 'c', 'd'] }
this.letters.push('aaa') this.letters.push('aaaa', 'bbbb', 'cccc')
this.letters.pop();
this.letters.shift();
this.letters.unshift() this.letters.unshift('aaa', 'bbb', 'ccc')
// 删除元素: 第二个参数传入你要删除几个元素(如果没有传,就删除后面所有的元素) // 替换元素: 第二个参数, 表示我们要替换几个元素, 后面是用于替换前面的元素 // 插入元素: 第二个参数, 传入0, 并且后面跟上要插入的元素
this.letters.sort()
this.letters.reverse()
this.letters[0] = 'bbbbbb';//通过下标不能响应式修改 this.letters.splice(0, 1, 'bbbbbb')//可以响应式 //set(要修改的对象, 索引值, 修改后的值) Vue.set(this.letters, 0, 'bbbbbb')//可以响应式
function sum(...num) { console.log(num); } sum(20, 30, 40, 50, 601, 111, 122, 33)
格式化展示数据,把展示的数据快速的展示成我们想要的内容
vue2x的时候,vue取消了内置过滤器,如果想使用过滤器就必须自定义过滤器
在所有vue实例都可以使用的叫全局过滤器,必须定义在实例之前
//全局过滤器的定义方法 位置:创建实例之前 Vue.filter(‘sum’, function(val){ return val + 4; });
<body> <!-- 全局过滤器 写在实例之前 Vue.filter("过滤器的名字随便写的",(你要过滤的数据会自动传入)=>{ return 逻辑 }) --> <div id="demodiv"> aaaaaaaaa <h1>{{text|xiaoming}}</h1> </div> <div id="demodivb"> bbbbbbbbbbb {{text|xiaoming}} <h1>{{num|xiaohong}}</h1> </div> <script> Vue.filter("xiaoming",(val)=>{ return "《《"+val+"》》" }) Vue.filter("xiaohong",(val)=>{ return "¥"+val }) new Vue({ el:"#demodiv", data:{ text:"demodiv" } }) new Vue({ el:"#demodivb", data:{ text:"demodivb", num:19.9 } }) </script> </body>
只能在一个指定vue实例中使用的叫局部过滤器,定义在指定vue实例中,与el同级。
只能在当前vue注册内容中使用 在vue实例中与el属性data属性同级定义 filters:{ 过滤器名字:function(val){ return 输出内容 } }
<body> <div id="demodiv"> aaaaaaaaa <h1>{{text|xiaoming}}</h1> <h1>{{textb|xiaoming}}</h1> </div> <div id="demodivb"> bbbbbbbbbbb {{text}} </div> <script> new Vue({ el:"#demodiv", data:{ text:"demodiv", textb:"abcdefgbhjfhf" }, filters:{ xiaoming:(val)=>{ return val.substr(0,4)+"..." } } }) new Vue({ el:"#demodivb", data:{ text:"demodivb" } }) </script> </body>
<body> <div id="app"> <input type="text" v-model="message"> {{message}} </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊' } }) </script> </body>
v-model其实是一个语法糖,它的背后本质上是包含两个操作
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 1-可以这样做 --> <input type="text" v-model="message"> <input type="text" :value="message" @input="valueChange"> <!-- 2-也可以这样写,不用写方法,写表达式 --> <input type="text" :value="message" @input="message = $event.target.value"> <h2>{{message}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊' }, methods: { valueChange(event) { this.message = event.target.value; } } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <label for="male"> <input type="radio" id="male" value="男" v-model="sex">男 </label> <label for="female"> <input type="radio" id="female" value="女" v-model="sex">女 </label> <h2>您选择的性别是: {{sex}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', sex: '女' } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <label for="agree"> <input type="checkbox" id="agree" v-model="isAgree">同意协议 </label> <h2>您选择的是: {{isAgree}}</h2> <button :disabled="!isAgree">下一步</button> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', isAgree: false, } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <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">羽毛球 <h2>您的爱好是: {{hobbies}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', hobbies: [], // 多选框, } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!--1.选择一个--> <select name="abc" v-model="fruit"> <option value="苹果">苹果</option> <option value="香蕉">香蕉</option> <option value="榴莲">榴莲</option> <option value="葡萄">葡萄</option> </select> <h2>您选择的水果是: {{fruit}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', fruit: '香蕉', } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!--2.选择多个--> <select name="abc" v-model="fruits" multiple> <option value="苹果">苹果</option> <option value="香蕉">香蕉</option> <option value="榴莲">榴莲</option> <option value="葡萄">葡萄</option> </select> <h2>您选择的水果是: {{fruits}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', fruits: [] } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!--1.修饰符: lazy--> <input type="text" v-model.lazy="message"> <h2>{{message}}</h2> <!--2.修饰符: number--> <input type="number" v-model.number="age"> <h2>{{age}}-{{typeof age}}</h2> <!--3.修饰符: trim--> <input type="text" v-model.trim="name"> <h2>您输入的名字:{{name}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', age: 0, name: '' } }) var age = 0 age = '1111' age = '222' </script> </body> </html>
调用Vue.extend()创建的是一个组件构造器。 通常在创建组件构造器时,传入template代表我们自定义组件的模板。该模板就是在使用到组件的地方,要显示的HTML代码。
调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。所以需要传递两个参数:1、注册组件的标签名 2、组件构造器
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!--3.使用组件--> <my-cpn></my-cpn> </div> <script src="../js/vue.js"></script> <script> // 1.创建组件构造器对象 const cpnC = Vue.extend({ template: ` <div> <h2>我是标题</h2> <p>我是内容, 哈哈哈哈</p> <p>我是内容, 呵呵呵呵</p> </div>` }) // 2.注册组件 Vue.component('my-cpn', cpnC) const app = new Vue({ el: '#app', data: { message: '你好啊' } }) </script> </body> </html>
通过调用Vue.component()注册组件时,组件的注册是全局的,这意味着该组件可以在任意Vue示例下使用。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn></cpn> <cpn></cpn> <cpn></cpn> </div> <script src="../js/vue.js"></script> <script> // 1.创建组件构造器 const cpnC = Vue.extend({ template: ` <div> <h2>我是标题</h2> <p>我是内容,哈哈哈哈啊</p> </div> ` }) // 2 注册局部组件 Vue.component('cpn', cpnC) const app = new Vue({ el: '#app', data: { message: '你好啊' }, }) </script> </body> </html>
如果注册的组件是挂载在某个实例中, 那么就是一个局部组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn></cpn> <cpn></cpn> <cpn></cpn> </div> <script src="../js/vue.js"></script> <script> // 1.创建组件构造器 const cpnC = Vue.extend({ template: ` <div> <h2>我是标题</h2> <p>我是内容,哈哈哈哈啊</p> </div> ` }) const app = new Vue({ el: '#app', data: { message: '你好啊' }, // 2注册局部组件 components: { // cpn使用组件时的标签名 cpn: cpnC } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn2></cpn2> <!--<cpn1></cpn1>--> </div> <script src="../js/vue.js"></script> <script> // 1.创建第一个组件构造器(子组件) const cpnC1 = Vue.extend({ template: ` <div> <h2>我是标题1</h2> <p>我是内容, 哈哈哈哈</p> </div> ` }) // 2.创建第二个组件构造器(父组件) const cpnC2 = Vue.extend({ //在父组件模板里面使用子组件,<cpn1></cpn1> template: ` <div> <h2>我是标题2</h2> <p>我是内容, 呵呵呵呵</p> <cpn1></cpn1> </div> `, //在父组件里面注册子组件 components: { cpn1: cpnC1 } }) // 根组件 const app = new Vue({ el: '#app', data: { message: '你好啊' }, components: { cpn2: cpnC2 } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn1></cpn1> <cpn2></cpn2> </div> <script src="../js/vue.js"></script> <script> // 正常创建组件构造器 // const cpn1 = Vue.extend({ template: ` <div> <h2>我是标题1</h2> <p>我是内容, 哈哈哈哈</p> </div> ` } ) //语法糖分别注册全局组件和局部组件 // 1 注册全局组件, Vue.component('cpn1', { template: ` <div> <h2>我是标题1</h2> <p>我是内容, 哈哈哈哈</p> </div> ` }) const app = new Vue({ el: '#app', data: { message: '你好啊' }, // 2.注册局部组件的语法糖 components: { 'cpn2': { template: ` <div> <h2>我是标题2</h2> <p>我是内容, 呵呵呵</p> </div> ` } } }) </script> </body> </html>
如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰。
Vue提供了两种方案来定义HTML模块内容
搞一个id,注册组件的时候放对应的id,:类型必须是text/x-template
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn></cpn> <cpn></cpn> <cpn></cpn> </div> <!--script标签, 注意:类型必须是text/x-template--> <script type="text/x-template" id="cpn"> <div> <h2>我是标题</h2> <p>我是内容,哈哈哈</p> </div> </script> <script src="../js/vue.js"></script> <script> // 1.注册一个全局组件 Vue.component('cpn', { template: '#cpn' }) const app = new Vue({ el: '#app', data: { message: '你好啊' } }) </script> </body> </html>
搞一个id,注册组件的时候放对应的id
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn></cpn> <cpn></cpn> <cpn></cpn> </div> <!--template标签--> <template id="cpn"> <div> <h2>我是标题</h2> <p>我是内容,呵呵呵</p> </div> </template> <script src="../js/vue.js"></script> <script> // 1.注册一个全局组件 Vue.component('cpn', { template: '#cpn' }) const app = new Vue({ el: '#app', data: { message: '你好啊' } }) </script> </body> </html>
首先,如果不是一个函数,Vue直接就会报错。
其次,原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。
当一个组件被定义,data
必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。
如果 data
是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!
通过提供 data
函数,每次创建一个新实例后,我们能够调用 data
函数,从而返回初始数据的一个全新副本数据对象。
函数在每次调用的时候会重新创建,所以data是函数,以后每次用组件的时候,数据都是单独绑定的,不会相互影响。
如果像下面这样写,直接定义一个对象放在return后面,那么每次调用组件,用的都是同一个数据,改一个就会全部改动
const obj = { counter: 0 }
// 注册组件
Vue.component(‘cpn’, {
template: ‘#cpn’,
// data() {
// return {
// counter: 0
// }
// },
data() {
return obj
},
methods: {
increment() {
this.counter++
},
decrement() {
this.counter–
}
}
})
const app = new Vue({
el: ‘#app’,
data: {
message: ‘你好啊’
}
})
# 父子组件的通信 子组件是不能引用父组件或者Vue实例的数据的。但是,在开发中,往往一些数据确实需要从上层传递到下层:比如在一个页面中,我们从服务器请求到了很多的数据。其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。如何进行父子组件间的通信呢?Vue官方提到 - 通过props向子组件传递数据(properties-属性的意思) - 通过事件向父组件发送消息 ## 父组件向子组件传递数据props ### props第一种用法:props直接用 1. 字符串数组,数组中的字符串就是传递时的名称 ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn v-bind:cmovies="movies"></cpn> </div> <!--子组件--> <template id="cpn"> <div> <ul> <li v-for="item in cmovies">{{item}}</li> </ul> <h2>{{cmessage}}</h2> </div> </template> <script src="../js/vue.js"></script> <script> // 父传子: 在子组件中用props定义接受 const cpn = { template: '#cpn', props: ['cmovies', 'cmessage'], }, data() { return {} }, } //父组件 const app = new Vue({ el: '#app', data: { message: '你好啊', movies: ['海王', '海贼王', '海尔兄弟'] }, components: { cpn } }) </script> </body> </html>
<body> <div id="app"> <cpn :cmessage="message" :cmovies="movies"></cpn> </div> <template id="cpn"> <div> <ul> <li v-for="item in cmovies">{{item}}</li> </ul> <h2>{{cmessage}}</h2> </div> </template> <script src="../js/vue.js"></script> <script> // 父传子: props const cpn = { template: '#cpn', // props: ['cmovies', 'cmessage'], props: { // 1.类型限制 // cmovies: Array, // cmessage: String, // 2.提供一些默认值, 以及必传值 cmessage: { type: String, default: 'aaaaaaaa', required: true }, // 类型是对象或者数组时, 默认值必须是一个函数 cmovies: { type: Array, default() { return [] } } }, data() { return {} }, methods: { } } const app = new Vue({ el: '#app', data: { message: '你好啊', movies: ['海王', '海贼王', '海尔兄弟'] }, components: { cpn } }) </script> </body>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn :c-info="info" :child-my-message="message" v-bind:class></cpn> </div> <template id="cpn"> <div> <h2>{{cInfo}}</h2> <h2>{{childMyMessage}}</h2> </div> </template> <script src="../js/vue.js"></script> <script> const cpn = { template: '#cpn', props: { cInfo: { type: Object, default() { return {} } }, childMyMessage: { type: String, default: '' } } } const app = new Vue({ el: '#app', data: { info: { name: 'why', age: 18, height: 1.88 }, message: 'aaaaaa' }, components: { cpn } }) </script> </body> </html>
当子组件需要向父组件传递数据时,就要用到自定义事件了。
v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。
2.自定义事件的流程
在子组件中,通过$emit()来触发事件。
在父组件中,通过v-on来监听子组件事件。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--父组件模板--> <div id="app"> <!--以前的事件不传参数,默认把事件对象传递进去,现在不传参数默认把子组件发送过来的数据传递进去,需要在定义的时候在参数里接受--> <cpn @item-click="cpnClick"></cpn> </div> <!--子组件模板--> <template id="cpn"> <div> <button v-for="item in categories" @click="btnClick(item)"> {{item.name}} </button> </div> </template> <script src="../js/vue.js"></script> <script> // 1.子组件 const cpn = { template: '#cpn', data() { return { categories: [ {id: 'aaa', name: '热门推荐'}, {id: 'bbb', name: '手机数码'}, {id: 'ccc', name: '家用家电'}, {id: 'ddd', name: '电脑办公'}, ] } }, methods: { btnClick(item) { // 发射事件: 自定义事件 this.$emit('item-click', item) } } } // 2.父组件 const app = new Vue({ el: '#app', data: { message: '你好啊' }, components: { cpn }, methods: { cpnClick(item) { console.log('cpnClick', item); } } }) </script> </body> </html>
有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问跟组件。 父组件访问子组件:使用$children或$refs reference(引用) 子组件访问父组件:使用$parent
数组类型,取值需要用下标取,不太方便
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn></cpn> <cpn></cpn> <my-cpn></my-cpn> <y-cpn></y-cpn> <cpn></cpn> <button @click="btnClick">按钮</button> </div> <template id="cpn"> <div>我是子组件</div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊' }, methods: { btnClick() { // $children 数组类型 console.log(this.$children); for (let c of this.$children) { console.log(c.name); c.showMessage(); } console.log(this.$children[3].name); } }, components: { cpn: { template: '#cpn', data() { return { name: '我是子组件的name' } }, methods: { showMessage() { console.log('showMessage'); } } }, } }) </script> </body> </html>
是一个对象类型,必须在组件上加上ref属性,随便等于一个值,比如aaa,通过this.$refs.aaa,就可以拿到这个对象,再通过对象打点数据的名字,就可以拿到子组件的数据。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn></cpn> <cpn></cpn> <my-cpn></my-cpn> <y-cpn></y-cpn> <cpn ref="aaa"></cpn> <button @click="btnClick">按钮</button> </div> <template id="cpn"> <div>我是子组件</div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊' }, methods: { btnClick() { // $refs => 对象类型, 默认是一个空的对象 ref='bbb' console.log(this.$refs.aaa.name); } }, components: { cpn: { template: '#cpn', data() { return { name: '我是子组件的name' } }, methods: { showMessage() { console.log('showMessage'); } } }, } }) </script> </body> </html>
如果我们想在子组件中直接访问父组件,可以通过$parent 注意事项: 尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。 子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。 如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。 另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn></cpn> </div> <template id="cpn"> <div> <h2>我是cpn组件</h2> <ccpn></ccpn> </div> </template> <template id="ccpn"> <div> <h2>我是子组件</h2> <button @click="btnClick">按钮</button> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊' }, components: { cpn: { template: '#cpn', data() { return { name: '我是cpn组件的name' } }, components: { ccpn: { template: '#ccpn', methods: { btnClick() { //访问父组件$parent console.log(this.$parent); console.log(this.$parent.name); } } } } } } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn></cpn> </div> <template id="cpn"> <div> <h2>我是cpn组件</h2> <ccpn></ccpn> </div> </template> <template id="ccpn"> <div> <h2>我是子组件</h2> <button @click="btnClick">按钮</button> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊' }, components: { cpn: { template: '#cpn', data() { return { name: '我是cpn组件的name' } }, components: { ccpn: { template: '#ccpn', methods: { btnClick() { // 访问根组件$root console.log(this.$root); console.log(this.$root.message); } } } } } } }) </script> </body> </html>
1.插槽的基本使用 <slot></slot> 2.插槽的默认值 <slot>button</slot> 3.如果有多个值, 同时放入到组件进行替换时, 一起作为替换元素
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn></cpn> <cpn><span>哈哈哈</span></cpn> <cpn><i>呵呵呵</i></cpn> <cpn> <i>呵呵呵</i> <div>我是div元素</div> <p>我是p元素</p> </cpn> <cpn></cpn> <cpn></cpn> <cpn></cpn> <cpn></cpn> </div> <template id="cpn"> <div> <h2>我是组件</h2> <p>我是组件, 哈哈哈</p> <slot><button>按钮</button></slot> <!--<button>按钮</button>--> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊' }, components: { cpn: { template: '#cpn' } } }) </script> </body> </html>
<slot name="left"><span>左边</span></slot> <cpn><button slot="left">返回</button></cpn>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn><span slot="center">标题</span></cpn> <cpn><button slot="left">返回</button></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> const app = new Vue({ el: '#app', data: { message: '你好啊' }, components: { cpn: { template: '#cpn' } } }) </script> </body> </html>
父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。
isShow这个变量在父子组件都有,不过父组件只会去父组件的作用域去找,子组件只会去子组件的作用域去找
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn v-show="isShow"></cpn> <cpn v-for="item in names"></cpn> </div> <template id="cpn"> <div> <h2>我是子组件</h2> <p>我是内容, 哈哈哈</p> <button v-show="isShow">按钮</button> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊', isShow: true }, components: { cpn: { template: '#cpn', data() { return { isShow: false } } }, } }) </script> </body> </html>
父组件替换插槽的标签,但是内容由子组件来提供。
在子组件绑定属性 <slot :data="pLanguages">
在父组件使用我们的子组件时,从子组件中拿到数据: 1-先用template标签包裹住,2.5版本后可以使用任意标签 2-我们通过<template slot-scope="slotProps">获取到slotProps属性 3-在通过slotProps.data就可以获取到刚才我们传入的data了
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn></cpn> <cpn> <!--2-目的是获取子组件中的pLanguages--> <template slot-scope="slot"> <!--<span v-for="item in slot.data"> - {{item}}</span>--> <span>{{slot.data.join(' - ')}}</span> </template> </cpn> <cpn> <!--2-目的是获取子组件中的pLanguages--> <template slot-scope="slot"> <!--<span v-for="item in slot.data">{{item}} * </span>--> <span>{{slot.data.join(' * ')}}</span> </template> </cpn> <!--<cpn></cpn>--> </div> <template id="cpn"> <div> <!--1-把子组件的pLanguages绑定到任意属性上,属性名可以随便起--> <slot :data="pLanguages"> <ul> <li v-for="item in pLanguages">{{item}}</li> </ul> </slot> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊' }, components: { cpn: { template: '#cpn', data() { return { pLanguages: ['JavaScript', 'C++', 'Java', 'C#', 'Python', 'Go', 'Swift'] } } } } }) </script> </body> </html>
通过模块化的方式开发,type属性等于这个
<script src="aaa.js" type="module"></script>
导入接受的名字和导出的要一样
//文件名是aaa.js var name = '小明' var age = 18 var flag = true function sum(num1, num2) { return num1 + num2 } if (flag) { console.log(sum(20, 30)); } // 1.导出方式一: export { flag, sum }
// 1.导入的{}中定义的变量 //导入接受的名字和导出的要一样 import {flag, sum} from "./aaa.js";
导入接受的名字和导出的要一样
// 2.导出方式二: export var num1 = 1000; export var height = 1.88
// 2.直接导入export定义的变量 import {num1, height} from "./aaa.js"; console.log(num1); console.log(height);
导入接受的名字和导出的要一样
// 3.导出函数/类 export function mul(num1, num2) { return num1 * num2 } export class Person { run() { console.log('在奔跑'); } }
// 3.导入 export的function/class import {mul, Person} from "./aaa.js"; console.log(mul(30, 50)); const p = new Person(); p.run()
export default function (argument) { console.log(argument); }
// 4.导入 export default中的内容 import addr from "./aaa.js"; addr('你好啊');
// 5.统一全部导入 // import {flag, num, num1, height, Person, mul, sum} from "./aaa.js"; import * as aaa from './aaa.js' console.log(aaa.flag); console.log(aaa.height);
什么是webpack? 这个webpack还真不是一两句话可以说清楚的。 我们先看看官方的解释: At its core, webpack is a static module bundler for modern JavaScript applications. 从本质上来讲,webpack是一个现代的JavaScript应用的静态模块打包工具。 但是它是什么呢?用概念解释概念,还是不清晰。 我们从两个点来解释上面这句话:模块 和 打包
前端模块化的一些方案:AMD、CMD、CommonJS、ES6。 在ES6之前,我们要想进行模块化开发,就必须借助于其他的工具,让我们可以进行模块化开发。 并且在通过模块化开发完成了项目后,还需要处理模块间的各种依赖,并且将其进行整合打包。 而webpack其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系。 而且不仅仅是JavaScript文件,我们的CSS、图片、json文件等等在webpack中都可以被当做模块来使用(在后续我们会看到)。 这就是webpack中模块化的概念。
打包如何理解呢? 理解了webpack可以帮助我们进行模块化,并且处理模块间的各种复杂关系后,打包的概念就非常好理解了。 就是将webpack中的各种资源模块进行打包合并成一个或多个包(Bundle)。 并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。 但是打包的操作似乎grunt/gulp也可以帮助我们完成,它们有什么不同呢?
grunt/gulp的核心是Task 我们可以配置一系列的task,并且定义task要处理的事务(例如ES6、ts转化,图片压缩,scss转成css) 之后让grunt/gulp来依次执行这些task,而且让整个流程自动化。 所以grunt/gulp也被称为前端自动化任务管理工具。 什么时候用grunt/gulp呢? 如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念。 只需要进行简单的合并、压缩,就使用grunt/gulp即可。 但是如果整个项目使用了模块化管理,而且相互依赖非常强,我们就可以使用更加强大的webpack了。 所以,grunt/gulp和webpack有什么不同呢? grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心。 webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能,是他附带的功能。
安装webpack首先需要安装Node.js,Node.js自带了软件包管理工具npm 查看自己的node版本: node -v 全局安装webpack(这里我先指定版本号3.6.0,因为vue cli2依赖该版本) npm install webpack@3.6.0 -g 局部安装webpack(后续才需要) --save-dev`是开发时依赖,项目打包后不需要继续使用的。 cd 对应目录 npm install webpack@3.6.0 -g --save-dev 为什么全局安装后,还需要局部安装呢? 在终端直接执行webpack命令,使用的全局安装的webpack 当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack
//是将每个添加的地址压入的栈里面,现进后出,前进后退网页类似于进出栈的操作 history.pushState({},"","/home") history.pushState({},"","/abt") history.pushState({},"","/demmt")
//直接替换地址,不能返回 history.replaceState ({},"","/home")
history.go(-1) //后退一个 history.go(0) //原地刷新 history.go(1) //向前走一个
history.back() 等价于 history.go(-1)
history.forward() 则等价于 history.go(1)
在router中创建index.js,在里面配置
// 配置路由相关的信息 import VueRouter from 'vue-router' import Vue from 'vue' import Home from '../components/Home' import About from '../components/About' // 1.通过Vue.use(插件), 安装插件 Vue.use(VueRouter) // 2.创建VueRouter对象 const routes = [] const router = new VueRouter({ // 配置路由和组件之间的应用关系 routes, mode: 'history', linkActiveClass: 'active' }) // 3.将router对象传入到Vue实例 export default router
在main.js中
import Vue from 'vue' import App from './App' //会自动找到index.js的文件,可以不用写后面的目录 import router from './router' Vue.config.productionTip = false new Vue({ el: '#app', router, render: h => h(App) })
// 配置路由相关的信息 import VueRouter from 'vue-router' import Vue from 'vue' import Home from '../components/Home' import About from '../components/About' // 1.通过Vue.use(插件), 安装插件 Vue.use(VueRouter) // 2.创建VueRouter对象 const routes = [ { path: '', // redirect重定向 redirect: '/home' }, { path: '/home', component: Home }, { path: '/about', component: About } ] const router = new VueRouter({ // 配置路由和组件之间的应用关系 routes, mode: 'history', linkActiveClass: 'active' }) // 3.将router对象传入到Vue实例 export default router
展示配置
<template> <div id="app"> <router-link to="/home" >首页</router-link> <router-link to="/about">关于</router-link> <router-view></router-view> </div> </template>
<router-link>: 该标签是一个vue-router中已经内置的组件, 它会被渲染成一个<a>标签. <router-view>: 该标签会根据当前的路径, 动态渲染出不同的组件. 网页的其他内容, 比如顶部的标题/导航, 或者底部的一些版权信息等会和<router-view>处于同一个等级. 在路由切换时, 切换的是<router-view>挂载的组件, 其他内容不会发生改变.
修改mode属性值就好
const router = new VueRouter({ // 配置路由和组件之间的应用关系 routes, mode: 'history', linkActiveClass: 'active' })
<router-link>还有一些其他属性: tag: tag可以指定<router-link>之后渲染成什么组件, 比如上面的代码会被渲染成一个<li>元素, 而不是<a> replace: replace不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中 active-class: 当<router-link>对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称. 在进行高亮显示的导航菜单或者底部tabbar时, 会使用到该类. 但是通常不会修改类的属性, 会直接使用默认的router-link-active即可.
<router-link to="/home" tag="button" replace active-class="active">首页</router-link>
const router = new VueRouter({ // 配置路由和组件之间的应用关系 routes, mode: 'history', linkActiveClass: 'active'//修改类名 })
<template> <div id="app"> <button @click="homeClick">首页</button> <button @click="aboutClick">关于</button> <router-view></router-view> </div> </template> <script> export default { name: 'App', methods: { homeClick() { // 通过代码的方式修改路由 vue-router // push => pushState // this.$router.push('/home') this.$router.replace('/home') console.log('homeClick'); }, aboutClick() { // this.$router.push('/about') this.$router.replace('/about') console.log('aboutClick'); } } } </script>
在某些情况下,一个页面的path路径可能是不确定的,比如我们进入用户界面时,希望是如下的路径: /user/aaaa或/user/bbbb 除了有前面的/user之外,后面还跟上了用户的ID 这种path和Component的匹配关系,我们称之为动态路由(也是路由传递数据的一种方式)。
{ path: '/user/:id', component: User, },
<router-link :to="'/user/'+userId">用户</router-link> <script> data() { return { userId: 'zhangsan', } } </script>
userId() { return this.$route.params.id }
const Home = resolve => { require.ensure(['../components/Home.vue'], () => { resolve(require('../components/Home.vue')) })};
const About = resolve => require(['../components/About.vue'], resolve);
const Home = () => import('../components/Home.vue')
{ path: '/home', component: Home, meta: { title: '首页' }, children: [ // { // path: '', // redirect: 'news' // }, { path: 'news', component: HomeNews }, { path: 'message', component: HomeMessage } ] },
<router-link to="/home/news">新闻</router-link> <router-link to="/home/message">消息</router-link> <router-view></router-view>
传递参数主要有两种类型: params和query params的类型: 配置路由格式: /router/:id 传递的方式: 在path后面跟上对应的值 传递后形成的路径: /router/123, /router/abc query的类型: 配置路由格式: /router, 也就是普通配置 传递的方式: 对象中使用query的key作为传递方式 传递后形成的路径: /router?id=123, /router?id=abc
// 配置路由相关的信息 import VueRouter from 'vue-router' import Vue from 'vue' // import Home from '../components/Home' // import About from '../components/About' // import User from '../components/User' const Home = () => import('../components/Home') const HomeNews = () => import('../components/HomeNews') const HomeMessage = () => import('../components/HomeMessage') const About = () => import('../components/About') const User = () => import('../components/User') const Profile = () => import('../components/Profile') // 1.通过Vue.use(插件), 安装插件 Vue.use(VueRouter) // 2.创建VueRouter对象 const routes = [ { path: '', // redirect重定向 redirect: '/home' }, { path: '/home', component: Home, meta: { title: '首页' }, children: [ // { // path: '', // redirect: 'news' // }, { path: 'news', component: HomeNews }, { path: 'message', component: HomeMessage } ] }, { path: '/about', component: About, meta: { title: '关于' }, beforeEnter: (to, from, next) => { // console.log('about beforeEnter'); next() } }, { path: '/user/:id', component: User, meta: { title: '用户' }, }, { path: '/profile', component: Profile, meta: { title: '档案' }, } ] const router = new VueRouter({ // 配置路由和组件之间的应用关系 routes, mode: 'history', linkActiveClass: 'active' }) // 前置守卫(guard) router.beforeEach((to, from, next) => { // 从from跳转到to document.title = to.matched[0].meta.title // console.log(to); // console.log('++++'); next() }) // 后置钩子(hook) router.afterEach((to, from) => { // console.log('----'); }) // 3.将router对象传入到Vue实例 export default router
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。 它们有两个非常重要的属性: include - 字符串或正则表达,只有匹配的组件会被缓存 exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存 router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存: 通过create声明周期函数来验证
<keep-alive exclude="Profile,User"> <router-view/> </keep-alive>