谷粒商城36-44 vue前端
Vue
Vue
是一套用于构建用户界面的渐进式框架,使用的是MVVM思想
1、什么是MVVM?
- M:即
Model
模型,包含数据和一些基本操作 - V:即
View
视图,页面渲染结果 - VM:即
View-Model
,模型与视图间的双向操作(无需开发人员干涉)
在MVVM
之前,开发人员从后端获取数据模型,然后要通过DOM
操作Model
进行渲染到View
中,而后当用户操作视图,我们还需要通过DOM
获取View
中的数据,然后同步到Model
中
而NVVM
中的VM
要做的事情就是把DOM
操作完全封装起来,开发人员不用在关心Model
和View
之间是如何互相影响的
2、安装使用Vue
我们首先新建一个vue
的文件夹,使用如下命令初始化项目
1 | npm init -y |
初始化后我们就能看到目录中多了一个package.json
文件
然后我们使用npm
安装vue
1 | 最新稳定版 |
到此我们就给当前项目成功安装了vue
,只需要在项目中引入即可使用啦
1 | <script src="./node_modules/vue/dist/vue.min.js"></script> |
下面我们使用! tab
快速生成一个html文件进行测试
1 |
|
3、双向绑定v-model
我们只需要将元素跟模型中需要使用的数据进行绑定即可
当数据变了输入框会变,同样数据框变了数据也会变
即模型变化,视图变化,反之亦然
1 | <div id="app"> |
花括号格式:{{表达式}}
说明:
- 该表达式支持JS语法,可以调用js内置函数(必须有返回值)
- 表达式必须有返回结果,例如:1+1,没有结果的表达式不允许使用,如let a=1+1
- 可以直接获取
vue
实例中定义的数据或函数
4、事件处理
示例:当点击一下按钮的时候,总人数增加1
1 | <div id="app"> |
5、声明方法
我们如果处理一下比较复杂的逻辑的时候,可以将内容写到methods
中
在这里我们可以定义很多方法
1 | <div id="app"> |
6、常用指令
v-html
:对内容进行转义后显示,即h1标签
会默认识别到,只显示标签内的内容
v-text
:不进行转义进行显示文本内容,不存在插值闪烁问题,即当vue
的声明是在插值之后的时候,如果网速很慢,在数据未加载完成时,页面会出现原始的{{}}标签,加载完毕后才显示正常数据,我们称之为插值闪烁,例如如下代码:
1 | <div id="app"> |
v-bind
:给html
标签的属性进行绑定
例如我们需要动态的将跳转的地址进行更改,代码如下:
1 | <div id="app"> <a v-bind:href="link">gogogo</a></div><script> let vm = new Vue({ el: "#app", data: { link: "https://www.baidu.com" } });</script> |
v-bind
:还可以根据boolean指定显示不同的值,可动态修改class
或style
例如我们需要根据不同情况显示class的不同属性值,可以简写为:
1 | <!-- 语法:{class名1:布尔值1,class名2:布尔值2} --><div id="app"> <span v-bind:class="{active:isActive,'text-danger':hasError}">Hello</span></div><script> let vm = new Vue({ el: "#app", data: { isActive: true, hasError: false } })</script> |
v-model
:进行双向绑定,数据和页面任何一方改变,对方都会跟着改变
1 | <div id="app"> <input type="checkbox" v-model="name" value="小火龙">宝可梦A <input type="checkbox" v-model="name" value="杰尼龟">宝可梦B <input type="checkbox" v-model="name" value="皮卡丘">宝可梦C <input type="checkbox" v-model="name" value="妙蛙种子">宝可梦D <br />您选择了:{{name.join(",")}}</div><script> let vm = new Vue({ el: "#app", data: { name: [] } })</script> |
v-for
:遍历操作,可以使用index
获取到当前索引值,使用:key
指定唯一的主键,可以提高整体渲染速度
1 | <div id="app"> <ul> <li v-for="(user, index) in users" :key="user.name"> 学号:{{index+1}} 姓名:{{user.name}} 性别:{{user.gender}} </li> </ul></div><script> let vm = new Vue({ el: "#app", data: { users: [ { name: "皮卡丘", gender: "男" }, { name: "杰尼龟", gender: "男" }, { name: "波克比", gender: "女" }, { name: "可达鸭", gender: "男" }, { name: "海星星", gender: "女" } ] } })</script> |
在循环内部,也可以获取到对象的key
、value
以及index
,名字可以自定义
1 | <div id="app"> <ul> <li v-for="(user, index) in users" :key="user.name" v-if="user.gender=='女'"> <!-- 只有一个代表值 --> <p v-for="value in user">{{value}}</p> <!-- 第一个代表值,第二个代表键 --> <p v-for="(value,key) in user">{{key}}:{{value}}</p> <!-- 第一个代表值,第二个代表键,第三个代表索引 --> <p v-for="(value,key,index) in user">{{index}}:{{key}}:{{value}}</p> </li> </ul></div><script> let vm = new Vue({ el: "#app", data: { users: [ { name: "皮卡丘", gender: "男" }, { name: "杰尼龟", gender: "男" }, { name: "波克比", gender: "女" }, { name: "可达鸭", gender: "男" }, { name: "海星星", gender: "女" } ] } })</script> |
v-if
:只有当判断条件成立的时候才会被渲染,不满足条件时页面中无相关代码
1 | <!-- 可以将上文中的案例改写如下 只查询性别为女的宝可梦 --><div id="app"> <ul> <li v-for="(user, index) in users" :key="user.name" v-if="user.gender=='女'"> 学号:{{index+1}} 姓名:{{user.name}} 性别:{{user.gender}} </li> </ul></div> |
同样,也存在v-else
和v-else-if
操作
1 | <div id="app"> <div v-if="type === 'A'">A</div> <div v-else-if="type === 'B'">B</div> <div v-else-if="type === 'C'">C</div> <div v-else>Not A/B/C</div></div><script> let vm = new Vue({ el: "#app", data: { type: 'A' } })</script> |
v-show
:只有当判断条件成立的时候才会显示
不同于
v-if
,不满足条件时仅作隐藏不会在页面中消失
1 | <div id="app"> <h1 v-show="ok">Hello!</h1></div><script> let vm = new Vue({ el: "#app", data: { ok: false } })</script> |
7、事件修饰符
现有如下案例:我们点击点我去百度
的时候,会先弹出一次弹框(点击了小DIV),然后会弹出第二次弹框(点小DIV也算点击了大DIV),最后会跳转到百度,这种情况就叫做事件冒泡
v-on
:绑定事件,v-on:click
可以简写成@click
1 | <div id="app"> <div style="border: 1px solid red;padding: 20px;" v-on:click="hello"> 大DIV <div style="border: 1px solid blue;padding:20px" @click="hello"> 小DIV <a href="https://www.baidu.com">点我去百度</a> </div> </div></div><script> let vm = new Vue({ el: "#app", methods: { hello() { alert("Hello World"); } } })</script> |
下面我们要解决一下这个问题,外层div不弹窗,在点击事件后增加stop
即可
1 | <div style="border: 1px solid blue;padding:20px" @click.stop="hello"> |
这个叫事件修饰符,下面列举一些常用的事件修饰符,也可以查看官网文档
.stop
:阻止事件冒泡,不让当前元素的事件继续往外触发.prevent
:阻止事件本身行为,例如阻止超链接跳转,表单提交等.capture
:改变js
默认的事件机制,默认是冒泡,capture功能是将冒泡改为倾听模式,当元素发生冒泡时,先触发带有该修饰符的元素。若有多个该修饰符,则由外而内触发.self
:只有自己出发的自己才会执行,如果是内部冒泡事件则会忽略.once
:将事件设置为只执行一次,prevent.once
代表只阻止事件行为一次.passive
:立即执行默认方法,无需等待浏览器判断是否存在preventDefault
阻止该次事件的默认动作,需要注意的是,如果passive
和prevent
同时存在,后者无效
1 | <!-- 阻止单击事件继续传播 --><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
只会阻止对元素自身的点击。
因为点击的时候会先
prevent
,阻止默认事件,阻止了跳转;然后判断是否是self
,因为点击到的是div
标签,所以不是self
。但是a标签
是self
,阻止了alert(2)
8、按键修饰符
在监听键盘事件时,我们经常需要检查详细的按键。Vue
允许为 v-on
在监听键盘事件时添加按键修饰符:
1 | <!-- 只有在key是Enter时调用vm.submit() --><input v-on:keyup.enter="submit"> |
你可以直接将 KeyboardEvent.key
暴露的任意有效按键名转换为 kebab-case 来作为修饰符
1 | <input v-on:keyup.page-down="onPageDown"> |
在上述示例中,处理函数只会在 $event.key
等于 PageDown
时被调用
常用键盘码如下
.enter
.tab
.delete
(捕获删除
和退格
键).esc
.space
.up
.down
.left
.right
你还可以通过全局 config.keyCodes
对象自定义按键修饰符别名:
1 | // 可以使用v-on:keyup.f1Vue.config.keyCodes.f1 = 112 |
9、计算属性
任何复杂的逻辑,都应该使用计算属性,计算属性逻辑写在
computed
中,只要检测到它使用到的属性有变化,就会触发重新计算
这也是
计算属性
与直接调用函数计算的区别所在如果使用到的属性没有任何变化,
计算属性
是会直接返回缓存结果,而不会再次执行但是如果使用的是函数的话,则依旧会继续调用
1 | <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></ul></div><script> let vm = new Vue({ el: "#app", data: { xyjPrice: 12, shzPrice: 19, xyjNum: 1, shzNum: 1 }, computed: { totalPrice() { return this.xyjPrice * this.xyjNum + this.shzPrice * this.shzNum; } } })</script> |
10、侦听属性
侦听属性是一种更通用的方式来观察和响应
Vue
实例上的数据变动当你有一些数据需要随着其它数据变动而变动时,你很容易滥用
watch
,通常更好的做法是使用计算属性而不是命令式的watch
回调
还是上面的案例,如果西游记只能买3本,超过3本就提示库存已经超了,就可以使用watch
监听了,
侦听属性里可以获取到修改前以及修改后的值,这样方便我们进行一些判断操作
1 | <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>{{message}}</ul></div><script> let vm = new Vue({ el: "#app", data: { xyjPrice: 12, shzPrice: 19, xyjNum: 1, shzNum: 1, message: "" }, watch: { xyjNum(newVal, oldVal) { if (newVal >= 3) { this.message = "库存不能超过3"; this.xyjNum = 3; } else { this.message = ""; } } } })</script> |
11、过滤器
我们可以自定义局部或全局的过滤器,对一些常见的文本格式化
过滤器可以用在两个地方:双花括号插值和
v-bind
表达式过滤器应该被添加在
JavaScript
表达式的尾部,由管道 |
符号指示
1 | <!-- 在双花括号中 -->{{ message | capitalize }}<!-- 在 `v-bind` 中 --><div v-bind:id="rawId | formatId"></div> |
你可以在一个组件的选项中定义本地的过滤器:
1 | <div id="app"> <ul> <li v-for="name in names"> {{name | nameFilter}} </li> </ul></div><script> let vm = new Vue({ el: "#app", data: { names: ["可达鸭","杰尼龟","波克比","小火龙"] }, filters: { nameFilter(n) { if (n == "可达鸭") { return n + "真笨"; } else { return n; } } } })</script> |
或者在创建Vue
实例之前全局定义过滤器:
1 | Vue.filter('nameFilterG', function (n) { if (n == "可达鸭") { return n + "真蠢"; } else { return n; }})new Vue({ // ...}) |
当全局过滤器和局部过滤器重名时,会采用局部过滤器
12、组件化
在大型应用开发的时候,页面可以划分成很多部分,往往在不同的页面,也会有相同的部分
但是如果每个页面都独自开发,无疑会增加开发的成本,所以将相同的部分拆分成独立的组件,可以在不同的页面共享这些组件,避免重复开发
在
vue
里,所有的vue
实例都是组件
例如我们现在有一个点击增加次数的按钮如下
1 | <div id="app"> <button v-on:click="num++">我被点击了{{num}}次</button></div><script> var vm = new Vue({ el: "#app", data: { num: 1 } })</script> |
然后我们使用组件来改造这个方法,其中组件的名字
counter
可以随便更改,且data
必须是一个函数才可以,因此每个实例可以维护一份被返回对象的独立的拷贝,否则如果存在多个组件,修改任意一个值都会改变全部组件的值,各个值之间不是互相独立的
1 | <div id="app"> <button v-on:click="num++">我被点击了{{num}}次</button> <counter></counter> <button-counter></button-counter></div><script></script> |
13、生命周期和钩子函数
beforeCreate
(实例创建前)
实例组件刚开始创建,元素dom
和数据都还没有初始化
应用场景:可以在这加个loading
事件
created
(实例创建后)
数据data已经初始化完成,方法也已经可以调用,但是dom
为渲染,在这个周期里面如果进行请求是可以改变数据并渲染,由于dom
未挂载,请求过多或者占用时间过长会导致页面线上空白
应用场景:在这结束loading
,还做一些初始化,实现函数自执行
beforeMoute
(元素挂载前)
dom
未完成挂载,数据初始化完成,但是数据的双向绑定还是{{}},这是因为vue
采用了虚拟dom
技术
mouted
(元素挂载后)
数据和dom
都完成挂载,在上一个周期占位的数据把值渲染进去,一般请求会放在这个地方,因为这边请求改变数据之后刚好能渲染
beforeUpdate
(实例更新前)
只要是页面数据改变了都会触发,数据更新之前,页面数据还是原来的数据,当你请求赋值一个数据的时候就会执行这个周期,如果没有数据改变不执行
updated
(实例更新后)
只要是页面数据改变了都会触发,数据更新完毕,页面的数据是更新完成的,beforeUpdated
和updated
要谨慎使用,因为页面更新数据的时候都会触发,在这里操作数据很影响性能和死循环
beforeDestory
(实例销毁前)
实例销毁之前调用,在这一步,实例仍然完全可用
destory
(实例销毁后)
vue
实例销毁后调用,调用后,vue
实例指示的所有内容都会解除绑定,所有的事件监听器都会被移除,所有的子实例也会被销毁
1 | <html> <head> <meta charset="UTF-8"> <title>vue的生命周期示例</title> </head> <body> <div id="app"> {{name}} </div> <button onclick="destory()">销毁实例</button> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> const vm=new Vue({ el:'#app', data:{ name:'xx', age:18 }, beforeCreate(){ console.log('============实例创建前============='); console.log(this.$el); //undefined console.log(this.$data);//undefined }, created(){ console.log('============实例创建后============='); console.log(this.$el); console.log(JSON.stringify(this.$data)); }, beforeMount(){ console.log('============元素挂载前============='); console.log(this.$el); console.log(JSON.stringify(this.$data)); }, mounted(){ console.log('============元素挂载后============='); console.log(this.$el); console.log(JSON.stringify(this.$data)); }, beforeUpdate(){ console.log('============实例更新前============='); console.log(this.$el); console.log(JSON.stringify(this.$data)); }, updated(){ console.log('============实例更新后============='); console.log(this.$el); console.log(JSON.stringify(this.$data)); }, beforeDestroy(){ console.log('============实例销毁前============='); console.log(this.$el); console.log(JSON.stringify(this.$data)); }, destroyed(){ console.log('============实例销毁后============='); console.log(this.$el); console.log(JSON.stringify(this.$data)); } }); function destory(){ vm.$destroy(); } </script> </body></html> |
14、使用Vue脚手架模块化开发
全局安装
webpack
命令:
npm install webpack -g
全局安装
vue脚手架
命令:
npm install -g @vue/cli-init
使用脚手架初始化一个叫
vue-demo
的项目命令:
vue init webpack vue-demo
运行项目
命令:
npm run dev
需要注意的两点:
- 如果提示
vue
不是内部或外部命令,则在Nodejs
目录下全局搜索vue.cmd
,将当前文件所在目录加入到path
环境变量中即可 - 如果使用
vscode
运行则会报错,因为powershell
禁止执行脚本文件,所以将vscode
的命令方式修改为cmd
或直接使用cmd
窗口进行执行命令即可