第七章 :JavaEE项目之“谷粒商城” ----前端基础(VUE)
一、MVVM思想
- M : 即Model,模型,包括数据和一些基本操作
- V : 即View,视图,页面渲染结果
- VM :即View-Model,模型与视图见的双向操作(无需开发人员干涉)
在 MVVM 之前,开发人员从后端获取需要的数据模型,然后通过 DOM 操作 Model 渲染到 View 中。 而后当用户操作视图,我们还需要通过 DOM 获取到 View 中的数据,然后同步到 Model 中。
而 MVVM 中的 VM 要做的事情就是把 DOM 操作完全封装起来,开发人员不用再关心 Model 和 View 之间是如何相互影响的;
- 只要我们 Model 发生了变化,View 上自然就会表现出来。
- 当用户修改了 View ,Model 中的数据也会跟着改变。
- 把开发人员从繁琐的 DOM 操作中解放出来,把关注点放在如何操作 Model 上。
二、Vue简介
Vue是一套用于构建用户界面的渐进式框架。与其他大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
三、概念
1.安装(使用npm方式安装)
初始化项目
D:\VScodeWork202104\Vue2>npm init -y
Wrote to D:\VScodeWork202104\Vue2\package.json:
{
"name": "Vue2",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
安装vue
D:\VScodeWork202104\Vue2>npm install vue
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN Vue2@1.0.0 No description
npm WARN Vue2@1.0.0 No repository field.
+ vue@2.6.12
added 1 package from 1 contributor in 1.464s
安装Vue 3 Snippets
为浏览器安装Vue-Devtools
(2)创建Vue实例
vue使用流程
1.创建vue实例,关联页面模板,将自己的数据(data)渲染到关联的模板–响应式的
2.使用指令来简化对 DOM 的一些操作
3.声明方法来做更复杂的操作.methods:里面可以封装方法
vue()方法中:
el:用于绑定元素
data:用于封装数据
methods:用于封装方法
1.vue声明式渲染
新建index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>{{name}},非常帅</h1>
</div>
<!--引入vue.js-->>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
//1.vue声明式渲染
let vm = new Vue({
el: "#app",
data: {
name: "张三"
}
});
</script>
</body>
</html>
启动在浏览器打开,可看到显示结果
当在控制台使用vm.name=“李四” ,回车运行,可看到页面动态转变
2.双向绑定
输入几,就会动态显示几个人点赞
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-model="number">
<h1>{{name}},非常帅,有{{number}}个人为他点赞</h1>
</div>
<!--引入vue.js-->>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
//1.vue 声明式渲染
let vm = new Vue({
el: "#app",
data: {
name: "张三",
number: 1
}
});
//2.双向绑定--模型变化,视图也会变化,反之亦然。<input type="text" v-model="number">
</script>
</body>
</html>
3.事件处理
每点击一次单击事件,页面都会动态变化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-model="number">
<button v-on:click="number++">点赞</button>
<h1>{{name}},非常帅,有{{number}}个人为他点赞</h1>
</div>
<!--引入vue.js-->>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
//1.vue 声明式渲染
let vm = new Vue({
el: "#app",
data: {
name: "张三",
number: 1
}
});
//2.双向绑定--模型变化,视图也会变化,反之亦然。<input type="text" v-model="number">
//3.事件处理--<button v-on:click="number++">点赞</button>
//v-xx:指令
</script>
</body>
</html>
单击事件调用方法:v-on指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-model="number">
<button v-on:click="number++">点赞</button>
<button v-on:click="cancel">取消</button>
<h1>{{name}},非常帅,有{{number}}个人为他点赞</h1>
</div>
<!--引入vue.js-->>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
//1.vue 声明式渲染
let vm = new Vue({
el: "#app",
data: {
name: "张三",
number: 1
},
methods:{
cancel(){
this.number--;
}
}
});
//2.双向绑定--模型变化,视图也会变化,反之亦然。<input type="text" v-model="number">
//3.事件处理--<button v-on:click="number++">点赞</button>
//v-xx:指令
</script>
</body>
</html>
四、指令
(1)插值表达式
1.花括号
格式:{{表达式}}
说明:
- 该表达式支持 JS 语法,可以调用js内置函数(必须有返回值)
- 表达式必须要有返回结果
- 可以直接获取 Vue 实例中定义的数据或函数
2.插值闪烁
使用 {{}} 方式在网速较慢时会出现问题。在数据加载未完成时,页面会显示出原始的{{}}
,加载完毕后才显示正确数据,我们称之为插值闪烁。
3.v-text和v-html
v-text是用于操作纯文本,它会替代显示对应的数据对象上的值。
v-html用于输出html,它与v-text区别在于v-text输出的是纯文本,浏览器不会对其再进行html解析,但v-html会将其当html标签解析后输出。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!--插值表达式(由于网速慢,在没完全加载出来时会残留一会,叫插值闪烁)-->
{{msg}}<br/>
{{hello()}}<br/>
<span v-html="msg"></span><br/>
<span v-text="msg"></span><br/>
</div>
<!--引入vue.js-->>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app",
data: {
msg: "<h1>hello</h1>",
},
methods: {
hello(){
return "world"
}
},
})
</script>
</body>
</html>
4.v-bind
v-bind:需要动态决定标签的属性的时候 可以使用到v-bind
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!--给html标签的属性绑定 *v-bind*-->
<a v-bind:href="link">gogogo</a>
<!--v-bind指令对class和style标签进行了增强-->
<span
v-bind:class="{active:isActive, 'text-danger':hasError}"
v-bind:style="{color:color1,fontSize:size}">你好</span>
</div>
<!--引入vue.js-->>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
letvm = new Vue({
el: "#app",
data: {
link: "http://www.baidu.com",
isActive: true,
hasError: true,
color1:'red',
size:'36px'
}
})
</script>
</body>
</html>
5.v-model(双向绑定)
v-model用于表单数据的双向绑定,其实它就是一个语法糖,这个背后就做了两个操作:
1. v-bind绑定一个value属性
2. v-on指令给当前元素绑定input事件
自定义组件使用v-model,应该有以下操作:
接收一个value prop
触发input事件,并传入新值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--一般用于表单项-->
<div id="app">
精通的语言:<br/>
<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/>
选中了哪些:
<span v-html="language.join(',')"></span>
</div>
<!--引入vue.js-->>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el:"#app",
data:{
language:[]
}
})
</script>
</body>
</html>
6.v-on
给元素进行事件绑定,需要通过v-on:指令实现.
<标签 v-on:click=“事件处理函数名” ></标签>
简写形式(v-on: 指令可以简写成 @)
<标签 @click=“事件处理函数名” ></标签> // 推荐使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!--事件中直接写js片段-->
<button v-on:click="num++">点赞</button>
<!--事件指定一个回调函数,必须是Vue实例中定义的函数-->
<button v-on: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>
<!--引入vue.js-->>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
num:"1"
},
methods:{
cancel(){
this.num--
},
hello(){
//会产生事件冒泡,点击小div的同时也点击了大div
alert("点击了!")
}
}
})
/**
* 1.事件修饰符
* 修饰符是由点开头的指令后缀来表示的
* .stop 阻止事件冒泡到父元素
* .prevent 阻止默认事件的发生
* .capture 使用事件捕获模式
* .self 只有元素自身触发事件才执行
* .once 只执行一次
*
*/
</script>
</body>
</html>
7.v-for
我们使用vue,在对一组数组列表进行渲染的时,会使用到 v-for 指令。
v-for 指令使用 item in items 形式语法,items是源数据数组, item 是数组元素迭代的别名。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<ul>
<li v-for="(user,index) in users" :key="user.name">
<!--1.显示user信息:v-for="item in items"-->
{{user.name}} ==> {{user.gender}} ==> {{user.age}}<br/>
<!--2.获取数组下标:v-for="(item,index) in items"-->
当前索引:{{index}} ==> {{user.name}} ==> {{user.gender}} ==> {{user.age}}<br/>
<!--3.遍历对象:
v-for="value in object"
v-for="(value,key) in object"
v-for="(value,key,index) in object"
-->
对象信息:value
<span v-for="v in user">{{v}} ==> </span><br/>
对象信息:(value,key)
<span v-for="(v,k) in user"> {{k}} ==> {{v}}</span><br/>
对象信息:(value,key,index)
<span v-for="(v,k,i) in user"> {{k}} ==> {{v}} ==>{{i}}</span><br/>
<!--4.遍历的时候加上:key来区分不同数据,提高vue的渲染效果-->
</li>
</ul>
<ul>
<li v-for="(num,index) in nums" :key="index"></li>
</ul>
</div>
<!--引入vue.js-->>
<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: 22},
{name: '刘亦菲', gender: '女', age: 23},
{name: '古丽', gender: '女', age: 26}],
nums:[1,2,3,4,5]
}
})
</script>
</body>
</html>
8.v-if&v-show
- v-if:在首次渲染的时候,如果条件为false时,什么也不操作,页面就会当作没有这些元素。而当条件为true时,开始局部编译,动态的向DOM元素里面添加元素。当条件从true变为false时,开始局部编译,卸载这些元素,也就是删除。
- v-show不管条件是true还是false,第一次渲染的时候都会编译出来,也就是标签都会添加到DOM中。之后切换的时候,通过display: none的方式来显示隐藏元素。可以说只是改变css的样式,几乎不会影响什么性能。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--
v-if 顾名思义,条件判断。当得到结果为true时,所在的元素才会被渲染。
v-show 当得到结果为true时,所在的元素才会被显示
-->
<div id="app">
<button v-on:click="show = !show">点我呀</button>
<!--1.使用v-if提示-->
<h1 v-if="show">if=看到我</h1>
<!--2.使用v-show显示-->
<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>
9.v-else&v-else-if
- v-else:不需要判断条件,但是它的前一个兄弟元素必需是v-if或者v-else-if。它为v-if或v-else-if添加了else块。
- v-else-if:的前一兄弟元素必须有 v-if 或 v-else-if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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.75
</h1>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let app = new Vue({
el:"#app",
data:{
random:1
}
})
</script>
</body>
</html>
五、计算机属性&侦听器
(1)计算属性&侦听器
- computed:计算属性----某些结果是基于之前数据实时计算出来的,我们可以利用计算属性来完成
- watch:侦听器----可以让我们监控一个值的变化,从而做出相应的反应。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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>
//watch可以让我们监控一个值的变化,从而做出相应的反应。
let app = new Vue({
el:"#app",
data:{
xyjPrice: 99.98,
shzPrice: 70.10,
xyjNum: 1,
shzNum: 1,
msg:""
},
//计算属性
computed:{
//在计算属性中定义一个计算方法,用于计算总价
totalPrice(){
return this.xyjPrice*this.xyjNum + this.shzPrice*this.shzNum
}
},
//侦听器
watch:{
xyjNum(newVal,oldVal){
//alert("newVal" + newVal +" ==> oldVal" + oldVal)
//如果数量大于等于3,则会提示库存不足
if(newVal >= 3){
this.msg = "库存超出限制",
this.xyjNum = 3
}else{
//如果小于3,则取消提示
this.msg = ""
}
}
}
})
</script>
</body>
</html>
(2)过滤器
- 局部过滤器 filters
- 全局过滤器 Vue.filter
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--过滤器常用来处理文本格式化的操作。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式-->
<div id="app">
<ul>
<li v-for="user in userList">
<!-- {{user.id}} ==> {{user.name}} ==> {{user.gender == 1?"男":"女"}} -->
{{user.id}} ==> {{user.name}} ==> {{user.gender}} ==> {{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 app = new Vue({
el: "#app",
data: {
userList: [
{ id: 1, name: 'tom', gender: 1 },
{ id: 2, name: 'peter', gender: 0 }
]
},
//局部过滤器
filters: {
genderFilter(val) {
if (val == 1) {
return "男"
} else {
return "女"
}
}
}
})
</script>
</body>
</html>
六、组件化
在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分。例如:可能会有相同的头部导航。
但是吐过每个页面都独自开发,这无疑增加了我们开发的成本。所以我们会把不同的部分拆分成独立的组件,然后在不同的页面就可以共享这些组件,能避免重复开发。
在vue里,所有的vue实例都是组件
- 组件其实也是一个 Vue 实例,因此它在定义时也会接收:data、methods、生命周期函数等。
- 不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性
- 全局组件定义完毕,任何 vue 实例都可以直接在 HTML 中通过组件名称来使用组件了
- data 必须是一个函数,不再是一个对象
1.全局组件:app.component()
注册的组件是全局组件,只要定义了,处处可以使用,性能不高,但是使用起来简单
2.局部组件–定义一个常量,是一个对象,使用时需要在父组件中components中声明,声明之后可以直接使用
---------------定义局部组件时建议首字母大写
---------------定义了要注册之后才能使用,性能比较高,使用起来有些麻烦
---------------名字建议—大写字母开头,驼峰命名法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<button v-on:click="count++">我被点击了 {{count}} 次</button>
<counter></counter>
<button-counter></button-counter>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<!--抽取--封装--复用-->
<script>
//1.全局声明注册一个组件
Vue.component("counter",{
template:`<button v-on:click="count++">我被点击了 {{count}} 次</button>`,
data() {
return {
count: 1
}
}
})
//2.局部声明一个组件
const buttonCounter = {
template:`<button v-on:click="count++">(局部)我被点击了 {{count}} 次</button>`,
data() {
return {
count: 1
}
}
}
let app = new Vue({
el:"#app",
data:{
count: 1
},
components:{
'button-counter': buttonCounter
}
})
</script>
</body>
</html>
七、生命周期和钩子函数
Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
下面创建一个实例来了解Vue的生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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(){
console.log("===================created=================");
console.log("数据模型已加载:" + this.name,this.num);
console.log("方法已加载:" + this.show());
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);
}
})
</script>
</body>
</html>
八、Vue模块化开发
(1)全局安装
npm install webpack -g
(2)安装vue脚手架
npm install --global vue-cli
(3)初始化 vue 项目
vue init webpack appname
(4)启动 vue 项目
npm run dev
由于之前测试的时候写的笔记忘了保存,无了。。。。。。。T-T。。。。暂且到这里吧,后续搭建前端项目时再记录