Vue 简介
Vue.js 是一套用于构建用户界面的渐进式框架 (基于 JavaScript,本质上是一个 JS 框架 )。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链 以及各种支持类库 结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。Vue 是视图层 框架,遵守 Soc(关注度分离原则)
Vue 采用 MVVM 思想:
M:model 包括数据和一些基本操作
V:view 视图,页面渲染结果
VM:View-model,模型与视图间的双向操作 (无需开发人员干涉)
视图 View 和数据 Model 通过 VM 绑定起来,Model 里有变化会自动地通过 Directives 填写到视图 View 中,视图表单中添加了内容也会自动地通过 DOM Listeners 保存到模型中。Vue 使用虚拟 DOM 技术更新 View 中的数据,而不是更新真实 DOM。
即:视图中的数据变化会自动同步到模型中的变量上;模型中变量值的改变也会自动同步到视图上
View 视图写完后就不需要再修改了,只需要发出请求访问后端获取数据修改 Model 中的数据即可动态更新视图,实现与 View 与 Model 的解耦合。
Vue 本身只关注于视图层 ,不包含异步通信、页面跳转等功能,这些功能都需要使用其他框架或组件来实现,例如借助于 Axios 实现异步通信,借助于 Vue Router 实现页面跳转。
Vue.js 就是一个 MVVM 的实现者 ,他的核心就是实现了 DOM 监听 与数据绑定 。
Element
Element 是饿了么开发的基于 Vue 的桌面端组件库 (提供了许多 Vue 组件),其提供了许多的 Vue 组件,可以直接使用。
Axios
Axios 是一个开源的可以用在浏览器端和 Node.js 的异步通信 框架,其主要作用就是实现 Ajax 请求。
由于 Vue.js 是一个视图层 框架并且作者(尤玉溪)严格遵守 Soc(关注度分离原则),所以 Vue.js 并不包含 Ajax 的通信功能,为了解决通信问题,推荐使用 Axios 框架,少用 jQuery,因为它需要频繁操控 Dom。
基础命令
导入 vue.js
:
1 <script src ="../node_modules/vue/dist/vue.js" > </script >
每一个 html 组件元素都可以绑定一个 Vue 对象,实现双向绑定。例如下面例子中 vm
对象绑定元素 div id="app"
:
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <div id ="app" > <input type ="text" v-model ="num" > v-model实现双向绑定 <button v-on:click ="num++" > 点赞</button > v-on:click绑定事件,实现自增 <button v-on:click ="cancel" > 取消</button > 回到自定义的方法 <h1 > {{name}} ,非常帅,有{{num}}个人为他点赞{{hello()}}</h1 > </div > <script src ="./node_modules/vue/dist/vue.js" > </script > <script > let vm = new Vue({ el: "#app" , data: { name: "张三" , num: 1 }, methods:{ cancel ( ) { this .num -- ; }, hello ( ) { return "1" } } }); </script > </body > </html >
v-text、v-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 32 33 34 35 36 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <div id ="app" > {{msg}} {{1+1}} {{hello()}}<br /> 用v-html取内容 <span v-html ="msg" > </span > <br /> 原样显示 <span v-text ="msg" > </span > </div > <script src ="../node_modules/vue/dist/vue.js" > </script > <script > new Vue({ el:"#app" , data:{ msg:"<h1 > Hello</h1 > ", link:"http://www.baidu.com" }, methods:{ hello ( ) { return "World" } } }) </script > </body > </html >
单向绑定 v-bind
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <div id ="app" > <a v-bind:href ="link" > gogogo</a > <span v-bind:class ="{active:isActive,'text-danger':hasError}" :style ="{color: color1,fontSize: size}" > 你好</span > </div > <script src ="../node_modules/vue/dist/vue.js" > </script > <script > let vm = new Vue({ el:"#app" , data:{ link: "http://www.baidu.com" , isActive:true , hasError:true , color1:'red' , size:'36px' } }) </script > </body > </html >
双向绑定 v-model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <div id ="app" > 精通的语言: <input type ="checkbox" v-model ="language" value ="Java" > java<br /> <input type ="checkbox" v-model ="language" value ="PHP" > PHP<br /> <input type ="checkbox" v-model ="language" value ="Python" > Python<br /> 选中了 {{language.join(",")}} </div > <script src ="../node_modules/vue/dist/vue.js" > </script > <script > let vm = new Vue({ el:"#app" , data:{ language: [] } }) </script > </body > </html >
v-on
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <div id ="app" > <button v-on:click ="num++" > 点赞</button > <button @click ="cancel" > 取消</button > <h1 > 有{{num}}个赞</h1 > <div style ="border: 1px solid red;padding: 20px;" v-on:click.once ="hello" > 大div <div style ="border: 1px solid blue;padding: 20px;" @click.stop ="hello" > 小div <br /> <a href ="http://www.baidu.com" @click.prevent.stop ="hello" > 去百度</a > </div > </div > <input type ="text" v-model ="num" v-on:keyup.up ="num+=2" @keyup.down ="num-=2" @click.ctrl ="num=10" > <br /> 提示: </div > <script src ="../node_modules/vue/dist/vue.js" > </script > <script > new Vue({ el:"#app" , data:{ num: 1 }, methods:{ cancel ( ) { this .num--; }, hello ( ) { alert("点击了" ) } } }) </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 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <div id ="app" > <ul > <li v-for ="(user,index) in users" :key ="user.name" v-if ="user.gender == '女'" > 当前索引:{{index}} ==> {{user.name}} ==> {{user.gender}} ==>{{user.age}} <br > 对象信息: <span v-for ="(v,k,i) in user" > {{k}}=={{v}}=={{i}};</span > </li > </ul > <ul > <li v-for ="(num,index) in nums" :key ="index" > </li > </ul > </div > <script src ="../node_modules/vue/dist/vue.js" > </script > <script > let app = new Vue({ el: "#app" , data: { users: [{ name : '柳岩' , gender : '女' , age : 21 }, { name : '张三' , gender : '男' , age : 18 }, { name : '范冰冰' , gender : '女' , age : 24 }, { name : '刘亦菲' , gender : '女' , age : 18 }, { name : '古力娜扎' , gender : '女' , age : 25 }], nums: [1,2,3,4,4] }, }) </script > </body > </html >
v-if 和 v-show
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <div id ="app" > <button v-on:click ="show = !show" > 点我呀</button > <h1 v-if ="show" > if=看到我....</h1 > <h1 v-show ="show" > show=看到我</h1 > </div > <script src ="../node_modules/vue/dist/vue.js" > </script > <script > let app = new Vue({ el: "#app" , data: { show: true } }) </script > </body > </html >
v-else 和 v-else-if
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <div id ="app" > <button v-on:click ="random=Math.random()" > 点我呀</button > <span > {{random}}</span > <h1 v-if ="random>=0.75" > 看到我啦?! > = 0.75 </h1 > <h1 v-else-if ="random>=0.5" > 看到我啦?! > = 0.5 </h1 > <h1 v-else-if ="random>=0.2" > 看到我啦?! > = 0.2 </h1 > <h1 v-else > 看到我啦?! < 0.2 </h1 > </div > <script src ="../node_modules/vue/dist/vue.js" > </script > <script > let app = new Vue({ el: "#app" , data: { random: 1 } }) </script > </body > </html >
计算属性和监听器
computed
:某些结果是基于之前数据实时计算出来的,我们可以利用计算属性来完成
watch
: 可以让我们监控一个值的变化。从而做出相应的反应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <div id ="app" > <ul > <li > 西游记; 价格:{{xyjPrice}},数量:<input type ="number" v-model ="xyjNum" > </li > <li > 水浒传; 价格:{{shzPrice}},数量:<input type ="number" v-model ="shzNum" > </li > <li > 总价:{{totalPrice}}</li > {{msg}} </ul > </div > <script src ="../node_modules/vue/dist/vue.js" > </script > <script > new Vue({ el: "#app" , data: { xyjPrice: 99.98, shzPrice: 98.00, xyjNum: 1, shzNum: 1, msg: "" }, computed: { totalPrice ( ) { return this .xyjPrice*this .xyjNum + this .shzPrice*this .shzNum } }, watch: { xyjNum (newVal,oldVal ) { if(newVal>=3){ this .msg = "库存超出限制" ; this .xyjNum = 3 }else { this .msg = "" ; } } }, }) </script > </body > </html >
过滤器
过滤器常用来处理文本格式化的操作。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <div id ="app" > <ul > <li v-for ="user in userList" > {{user.id}} ==> {{user.name}} ==> {{user.gender == 1?"男":"女"}} ==> {{user.gender | genderFilter}} ==> {{user.gender | gFilter}} </li > </ul > </div > <script src ="../node_modules/vue/dist/vue.js" > </script > <script > Vue.filter("gFilter" , function (val ) { if (val == 1) { return "男~~~" ; } else { return "女~~~" ; } }) let vm = new Vue({ el: "#app" , data: { userList: [ { id : 1 , name : 'jacky' , gender : 1 }, { id : 2 , name : 'peter' , gender : 0 } ] }, filters: { filters 定义局部过滤器,只可以在当前vue实例中使用 genderFilter (val ) { if (val == 1) { return "男" ; } else { return "女" ; }) </script > </body > </html >
组件化
在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分。例如可能会有相同的头部导航。但是如果每个页面都自开发,这无疑增加了我们开发的成本。所以我们会把页面的不同分拆分成立的组件,然后在不同页面就可以共享这些组件,避免重复开发。
在 Vue 里,所有的 Vue 实例都是组件 。组件其实也是一个 Vue 实例,因此它在定义时也会接收:data、methods、生命周期函等。
组件是可复用的 Vue 实例,说白了就是一组可重复使用的模板 。
和之前介绍的写法不同的是:组件不会与页面的元素绑定,否则就无法复用了,因此没有 el
属性 。但是组件渲染需要 HTML 模板,所以增加了template
属性,值就是 HTML 模板。全局组件定义完毕,任何 Vue实例都可以直接在 HTML 中通过组件名称来使用了。此时,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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <div id ="app" > <button v-on:click ="count++" > 我被点击了 {{count}} 次</button > <counter > </counter > <counter > </counter > <counter > </counter > <counter > </counter > <counter > </counter > <button-counter > </button-counter > </div > <script src ="../node_modules/vue/dist/vue.js" > </script > <script > Vue.component("counter" , { template: `<button v-on:click ="count++" > 我被点击了 {{count }} 次</button > `, data ( ) { return { count: 1 } } }); const buttonCounter = { template: `<button v-on:click ="count++" > 我被点击了 {{count }} 次~~~</button > `, data ( ) { return { count: 1 } } }; new Vue({ el: "#app" , data: { count: 1 }, components: { 'button-counter' : buttonCounter } }) </script > </body > </html >
实际项目中,通常将每个 Vue 组件单独提取成一个 xxx.vue
文件,其就代表一个 Vue 组件,里面三部分:
<template>
:组件的 HTML 模板。显示在 HTML 页面上的内容,每个 DOM 都可以使用上文介绍的基础命令(例如 v-model
)绑定 Vue 对象中的某个 data
数据
<script>
:定义 Vue 组件,包括 components
(可以导入其他 Vue 组件,例如将上传文件的提示框抽取出一个 Vue 组件,这样就可以在多个页面复用该组件)、data
(与 DOM 双向绑定的数据)、methods
(方法集合)、computed
(计算属性)、watch
(监听器)与生命周期函数。同时使用 export
导出当前组件给其他组件使用
<style>
:样式,修改当前 Vue 组件的样式
示例一:
示例二:
其中的 <el-form>
标签是 Element 提供的现成 Vue 组件,可以直接使用,本质上也是 Vue 组件
生命周期钩子函数
beforeCreate
:Vue 实例初始化之前调用,此时还没创建出 Vue 对象,无法访问当前 this 实例
created
:Vue 实例初始化之后调用,此时 Vue 实例初始化完成,可以访问当前 this 实例。data 中数据赋初值,但是还没绑定 DOM,页面无法渲染出真实数据 (例如只展示模板 {{message}}
)。常用于在该函数里添加一些初始化逻辑,例如在这里发出请求访问数据库获取商品三级分类数据并保存到 data 数据中。
beforeMount
:挂载到 DOM 树之前调用。此时页面还没完成渲染,看到的只是模板
mounted
:挂载到 DOM 树之后调用,此时才可以访问 DOM 元素,此时 HTML 页面才渲染完成 (之前看到的还不是要展示的数据,只是模板,例如展示 {{message}}
。在挂载完成后才会替换成真实数据)
beforeUpdate
:数据更新之前调用,Vue 里的模型数据更新了,但是页面还没及时更新
updated
:数据更新之后调用,Vue 里的模型数据更新了,页面也更新了
beforeDestroy
: Vue 实例销毁之前调用
destroyed
:Vue 实例销毁之后调用
示意图:
简略版:
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <div id ="app" > <span id ="num" > {{num}}</span > <button @click ="num++" > 赞!</button > <h2 > {{name}},有{{num}}个人点赞</h2 > </div > <script src ="../node_modules/vue/dist/vue.js" > </script > <script > let app = new Vue({ el: "#app" , data: { name: "张三" , num: 100 }, methods: { show ( ) { return this .name; }, add ( ) { this .num++; } }, beforeCreate ( ) { console .log("=========beforeCreate=============" ); console .log("数据模型未加载:" + this .name, this .num); console .log("方法未加载:" + this .show()); console .log("html模板未加载:" + document .getElementById("num" )); }, created: function ( ) { this .getMenus(); console .log("=========created=============" ); console .log("数据模型已加载:" + this .name, this .num); console .log("方法已加载:" + this .show()); console .log("html模板已加载:" + document .getElementById("num" )); console .log("html模板未渲染:" + document .getElementById("num" ).innerText); }, beforeMount ( ) { console .log("=========beforeMount=============" ); console .log("html模板未渲染:" + document .getElementById("num" ).innerText); }, mounted ( ) { console .log("=========mounted=============" ); console .log("html模板已渲染:" + document .getElementById("num" ).innerText); }, beforeUpdate ( ) { console .log("=========beforeUpdate=============" ); console .log("数据模型已更新:" + this .num); console .log("html模板未更新:" + document .getElementById("num" ).innerText); }, updated ( ) { console .log("=========updated=============" ); console .log("数据模型已更新:" + this .num); console .log("html模板已更新:" + document .getElementById("num" ).innerText); }, beforeDestroy ( ) {}, destroyed ( ) {}, activated ( ) {} }); </script > </body > </html >
Vue Router
Vue 本身只关注于视图层 ,不包含页面跳转功能。Vue Router 是 Vue.js 官方的路由管理器 。作用:实现前端页面的路径跳转 ,不需要借助于后端的 Controller 也能实现路径跳转。
路由配置示例:
<route-link>
指定路由到哪个组件
<route-view>
:组件展示在哪个页面什么位置
案例
具体介绍见文章 【Project】云商城
Vue 工程目录结构(npm 管理):