Vue+前端核心分析
Vue 的核心库只关注视图层,方便与第三方库或既有项目整合。
HTML + CSS + JS : 视图 :
给用户看,刷新后台给的数据
网络通信 : axios
页面跳转 : vue-router
状态管理:vuex
Vue-UI : ICE , Element UI
VUE 概述
Vue (读音/vju/, 类似于view)是一套用于构建用户界面的渐进式框架,发布于2014年2月。与其它大型框架不同的是,Vue被设计为可以自底向上逐层应用。
Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库(如: vue-router: 跳转,vue-resource: 通信,vuex:管理)或既有项目整合
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r3HiAnlx-1658970670003)(\java\8.框架\VUE\vue\Vue的介绍.PNG)]
前端三要素
HTML (结构) :超文本标记语言(Hyper Text Markup Language) ,决定网页的结构和内容
CSS (表现) :层叠样式表(Cascading Style sheets) ,设定网页的表现样式
JavaScript (行为) :是一种弱类型脚本语言,其源代码不需经过编译,而是由浏览器解释运行,用于控制网页的行为
JavaScript框架
jQuery: 大家熟知的JavaScript框架,优点是简化了DOM操作,缺点是DOM操作太频繁,影响前端性能;在前端眼里使用它仅仅是为了兼容IE6、7、8;
Angular: Google收购的前端框架,由一群Java程序员开发,其特点是将后台的MVC模式搬到了前端并增加了模块化开发的理念,与微软合作,采用TypeScript语法开发;对后台程序员友好,对前端程序员不太友好;最大的缺点是版本迭代不合理(如: 1代-> 2代,除了名字,基本就是两个东西;截止发表博客时已推出了Angular6)
React: Facebook出品,一款高性能的JS前端框架;特点是提出了新概念[虚拟DOM]用于减少真实DOM操作,在内存中模拟DOM操作,有效的提升了前端渲染效率;缺点是使用复杂,因为需要额外学习一门[JSX] 语言;
Vue:一款渐进式JavaScript框架,所谓渐进式就是逐步实现新特性的意思,如实现模块化开发、路由、状态管理等新特性。其特点是综合了Angular (模块化)和React (虚拟DOM)的优点;
Axios :前端通信框架;因为Vue 的边界很明确,就是为了处理DOM,所以并不具备通信能力,此时就需要额外使用一个通信框架与服务器交互;当然也可以直接选择使用jQuery提供的AJAX通信功能;
前端三大框架:Angular、React、Vue
MVVM
什么是MVVM
MVVM (Model-View-ViewModel) 是一种软件架构设计模式,由微软WPF (用于替代WinForm,以前就是用这个技术开发桌面应用程序的)和Silverlight (类似于Java Applet,简单点说就是在浏览器上运行的WPF)的架构师Ken Cooper和Ted Peters 开发,是一种简化用户界面的事件驱动编程方式。
由John Gossman (同样也是WPF和Silverlight的架构师)于2005年在他的博客上发表。
MVVM 源自于经典的MVC (ModI-View-Controller) 模式。
MVVM的核心是ViewModel层,负责转换Model中的数据对象来让数据变得更容易管理和使用,其作用如下:
该层向上与视图层进行双向数据绑定,向下与Model层通过接口请求进行数据交互
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9G9NRmq6-1658970670005)(F:\java\8.框架\VUE\vue\访问流程.png)]
为什么要使用MVVM
MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大好处:
低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可复用:你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
可测试:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
Vue 是 MVVM 模式的实现者🧡
Model : 模型层,在这里表示JavaScript对象
View : 视图层,在这里表示DOM (HTML操作的元素)
ViewModel : 连接视图和数据的中间件,Vue.js就是MVVM中的ViewModel层的实现者在MVVM架构中,是不允许数据和视图直接通信的,只能通过ViewModel来通信,而ViewModel就是定义了一个Observer观察者
ViewModel 能够观察到数据的变化,并对视图对应的内容进行更新
ViewModel 能够监听到视图的变化,并能够通知数据发生改变
至此,我们就明白了,Vue.js 就是一个MVVM的实现者,他的核心就是实现了DOM监听与数据绑定
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YiCOjJ06-1658970670006)(F:\java\8.框架\VUE\vue\MVVM.png)]
快速入门
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qwERItv4-1658970670007)(F:\java\8.框架\VUE\vue\快速入门.PNG)]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!--View层:模板-->
<div id="app">
<p>1:{{firstName}} {{lastName}}</p>
<p>2:{{name()}}</p>
<p>3:{{names}}</p>
<p>4:{{firstName+" "+lastName}}</p>
</div>
<script src="js/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{//model:数据
firstName:"张",
lastName:"三"
},
methods:{//方法
name(){
return this.firstName+" "+this.lastName
}
},
computed:{//计算属性
names(){
return this.firstName+" "+this.lastName
}
}
})
</script>
</body>
</html>
常用指令
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8ZkKziK9-1658970670008)(\java\8.框架\VUE\vue\常用指令.PNG)]
<!--引用js的包-->
<script src="../js/vue.js"></script>
<script>
//1.创建vue核心对象
new Vue({
el:"#app",
data(){
sex:"";
username:"",
url:"https://www.baidu.com",
count:3,
address:["张三","李四","王五"]
},
methods:{
show(){
alert("我被点击了....");
}
}
});
</script>
1.v-bind
<!--为html标签绑定属性-->
<div id="app">
<a v-bind:href="url">点击一下</a>
<a :href="url">点击一下</a><!--语法糖的简化写法-->
<input v-model="url">
</div>
2.v-on
<!--为html标签绑定事件-->
<input type="button" value="按钮" v-on:click="show()">
<input type="button" value="按钮" @click="show()"><!--语法糖简写-->
3.v-if
<!--条件显示,不满足条件不渲染(创建元素)-->
<div v-if="count==3">a</div>
<div v-else-if="count==4">b</div>
<div v-else>c</div>
4.v-show
<!--条件显示,不满足条件就隐藏-->
<div v-show="count==3">show</div>
5.v-for
<!--循环渲染容器及其属性-->
<div v-for="i in address">
{{i}}<br/><!--i为内容-->
</div>
<div v-for="(a,i) in address">
{{i}}——{{a}}<br/><!--i为下标(0开始),a为内容-->
</div>
6.v-model
你可以用v-model指令在表单 <input>
、<textarea>
及<select>
元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但v-model本质上不过是语法糖。它负责监听户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
注意:v-model会忽略所有元素的value、checked、selected特性的初始值而总是将Vue实例的数据作为数据来源,你应该通过JavaScript在组件的data选项中声明初始值。
<!--将username属性与表单控件进行绑定-->
<input type="text" v-model="username"/>
<select v-model="username">
<option value="" disabled>-请选择-</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<input type="radio" name="sex" value="男" v-model="sex"/> 男
<input type="radio" name="sex" value="女" v-model="sex"/>女
组件
组件是可复用的
Vue
实例,说白了就是一组可以重复使用的模板,跟JSTL的自定义标签、Thymeleaf的th:fragment
等框架有着异曲同工之妙。通常一个应用会以一棵嵌套的组件树的形式来组织:
组件无法访问Vue的实例中的数据
组件的构成
每个Vue的组件都是由3部分构成,分成是:
- template → 组件的模板结构(必备)
- script → 组件的js行为(可选)
- style → 组件的样式(可选)
组件的template
结点
template是vue提供的容器标签,只起到包裹性质的作用,不会被渲染为真正的DOM元素
<div id="app">
<!--组件:传递给组件中的值:props-->
<my-vue v-for="i in items" v-bind:qin="i"></my-vue><!---->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script >
//定义一个Vue组件componet
Vue.component('my-vue',{
props: ['qin'],
template:'<h1>{{qin}}</h1>'// 组件的模板结构
});
new Vue({
el:"#app",
data:{
items: ['Java','Python','Php']
}
})
</script>
表格练习
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>组件实战</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
box-sizing: border-box
}
html {
font-size: 12px;
font-family: Ubuntu, simHei, sans-serif;
font-weight: 400
}
body {
font-size: 1rem
}
table,
td,
th {
border-collapse: collapse;
border-spacing: 0
}
table {
width: 100%;
margin: 20px;
}
td,
th {
border: 1px solid #bcbcbc;
padding: 5px 10px
}
th {
background: #42b983;
font-size: 1.2rem;
font-weight: 400;
color: #fff;
cursor: pointer
}
tr:nth-of-type(odd) {
background: #fff
}
tr:nth-of-type(even) {
background: #eee
}
fieldset {
border: 1px solid #BCBCBC;
padding: 15px;
}
input {
outline: none
}
input[type=text] {
border: 1px solid #ccc;
padding: .5rem .3rem;
}
input[type=text]:focus {
border-color: #42b983;
}
button {
outline: none;
padding: 5px 8px;
color: #fff;
border: 1px solid #BCBCBC;
border-radius: 3px;
background-color: #009A61;
cursor: pointer;
}
button:hover {
opacity: 0.8;
}
#app {
margin: 0 auto;
max-width: 480px;
}
#searchBar {
margin: 10px;
padding-left: 20px;
}
#searchBar input[type=text] {
width: 80%;
}
.arrow {
display: inline-block;
vertical-align: middle;
width: 0;
height: 0;
margin-left: 5px;
opacity: 0.66;
}
.arrow.asc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid #fff;
}
.arrow.dsc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #fff;
}
</style>
</head>
<body>
<div id="app">
<my-vue v-bind:data="gridData " v-bind:col="gridColumns"></my-vue>
</div>
<template id="grid-template">
<table>
<thead>
<tr>
<th v-for="i in col">{{i}}</th>
</tr>
</thead>
<tbody>
<tr v-for="entry in data">
<td v-for="c in col">{{entry[c]}}</td>
</tr>
</tbody>
</table>
</template>
</body>
<script src="js/vue.js"></script>
<script>
Vue.component('my-vue', {
template: "#grid-template",
props: {
data: Array,
col: Array
}
})
var demo = new Vue({
el: '#app',
data: {
gridColumns: ['name', 'age', 'sex'],
gridData: [{
name: '小明',
age: 20,
sex: '男'
}, {
name: '小强',
age: 21,
sex: '男'
}, {
name: '小兰',
age: 22,
sex: '女'
}, {
name: '小惠',
age: 20,
sex: '女'
}]
}
})
</script>
</html>
组件的抽离
<template id="t">
<div>
<a>{{msg}}/a>
</div>
</template>
<script>
//注册组件
Vue.component("t",{
template:"#t",//进行绑定
data(){//data方法存放组件相应的数据
return{
msg:"hello"
}
},
})
</script>
全局组件与局部组件
全局组件
Vue.component('my-cpn',{
template:
`<div>
<h2>我是标题</h2>
<p>我是内容哈哈哈</p>
</div>`
})
局部组件:局部注册的组件在其子组件中不可用。
new Vue({
el:"#app",
components:{
'my-other':{
template:`<div>
<p>局部语法糖</p>
</div>`
}
}
})
prop(父组件→ 子组件)
相当于servlet中的setAttribute
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FiI6HmzQ-1658970670009)(F:\java\8.框架\VUE\vue\父子.PNG)]
<div id="app">
<blog-post title="My journey with Vue"></blog-post> <!--静态绑定-->
<blog-post v-for="i in items" v-bind:qin="i"></blog-post><!--动态绑定-->
</div>
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
new Vue({
el:"#app",
data:{
items: ['Java','Python','Php']
}
})
prop类型
这单个 prop 都有指定的值类型,当然任何类型的值都可以传给一个 prop。
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise
}
prop验证
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
案例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<!--父组件值传递给子组件-->
<cpn :cmovies="movies" :cmessage="message" ></cpn>
</div>
<template id="cpn">
<!--组件的内容-->
<div>
<h1>{{cmessage}}</h1>
<p v-for="item in cmovies">{{item}}</p>
</div>
</template>
<script type="text/javascript" src="js/vue.js"></script>
<script>
const cpn =Vue.component('cpn',{ //抽离出来的组件
template: '#cpn',
props: {
cmovies: Array,
cmessage: String
},
})
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
movies: ['a','b','c']
},
//组件的绑定
components: {
cpn
}
})
</script>
</body>
</html>
自定义事件( 子组件→父组件)
步驟
<!--子组件内-->
<!--1.@click监听点击事件,进行传值-->
<button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
<!--2.通过自定义事件传值给父组件-->
btnClick(item){
this.$emit('item-click',item)
}
<!--父组件内-->
<!--3.监听自定义事件-->
<cpn @item-click="cpnClick"></cpn>
<!--4.获取事件的值,并进行处理-->
cpnClick(item){
console.log(item.id)
}
案例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>01-子传父</title>
</head>
<body>
<!--父组件模板-->
<div id="app">
<!--3.监听自定义事件-->
<cpn @item-click="cpnClick"></cpn>
</div>
<!--子组件模板-->
<template id="mytemp">
<div>
<!--1.@click监听点击事件,进行传值-->
<button v-for="item in categories"
style="margin:20px ;"
@click="btnClick(item)">{{item.name}}</button>
</div>
</template>
<script src="js/vue.js"></script>
<script>
//子组件
const cpn=Vue.component('cpn',{
template:'#mytemp',
data(){
return{
categories:[
{id:'a',name:'热门'},
{id:'b',name:'手机'},
{id:'c',name:'电脑'},
{id:'d',name:'相机'}
]
}
},methods:{
btnClick(item){
//2.通过自定义事件传值给父组件
this.$emit('item-click',item)
}
}
})
//父组件
const app=new Vue({
el:"#app",
data:{
message:'你好啊'
},components:{
cpn
},methods:{
//4.获取事件的值,并进行处理
cpnClick(item){
console.log(item.id)
}
}
})
</script>
</body>
</html>
插槽
抽取共性,保留不同:让组件进行扩展或者自定义
模板
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>插槽</title>
</head>
<body>
<div id="app">
<!-- 2、再去相应的组件标签中添加自己的内容-->
<my-cpn></my-cpn>
<my-cpn><a href="#" slot="left"> 链接</a></my-cpn>
<form>
<my-cpn></my-cpn>
</form>
</div>
<template id="mytemp">
<div>
<h3>这是模板标题</h3>
<p>这是组件中的段落</p>
<!-- 1、 添加一个缺口slot -->
<slot name="left"><button>按钮</button></slot>
<slot name="right"><button>按钮</button></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
new Vue({
el:"#app",
components:{
'my-cpn':{
template:"#mytemp"
}
}
})
</script>
</body>
</html>
编译的作用域
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wd6qZfgt-1658970670010)(F:\java\8.框架\VUE\vue\slot作用域.PNG)]
作用域插槽
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn>
<!--获取子组件的数据,然后自定义-->
<template slot-scope="slot">
<!--<span v-for="item in slot.data">{{item}}-</span>-->
<span>{{slot.data.join('-')}}</span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<slot :data="pLanguages">
<ul>
<li v-for="item in pLanguages">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script type="text/javascript" src="js/vue.js" ></script>
<script>
new Vue({
el:'#app',
data:{
message:'你好啊'
},
components:{
cpn:{
template:'#cpn',
data(){
return{
pLanguages:['Java','C','C#']
}
}
}
}
})
</script>
</body>
</html>
Axios异步通信
1.什么是Axios
是一个专注于网络请求的库
Axios是一个开源的可以用在浏览器端和NodeJS 的异步通信框架,她的主要作用就是实现AJAX异步通信,其功能特点如下:
- 从浏览器中创建XMLHttpRequests
- 从node.js创建http请求
- 支持Promise API [JS中链式编程]
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换JSON数据
- 客户端支持防御XSRF (跨站请求伪造)
- GitHub: https://github.com/ axios/axios
中文文档: http://www.axios-js.com/
2.为什么要使用Axios
由于Vue.js是一个视图层框架且作者(尤雨溪) 严格准守SoC (关注度分离原则),所以Vue.js并不包含Ajax的通信功能,为了解决通信问题,作者单独开发了一个名为vue-resource的插件,不过在进入2.0 版本以后停止了对该插件的维护并推荐了Axios 框架。少用jQuery,因为它操作Dom太频繁 !
3.基础语法🧡
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-22nbgaME-1658970670010)(F:\java\8.框架\VUE\vue\axios方法.PNG)]
-
axios(url[, config]): 可以只指定 url 发 get 请求
-
axios.get(url[, config]): 发 get 请求
-
axios.post(url[, data, config]): 发 post 请求
axios({
method:'请求的类型',
url:'请求的URL地址'
}).then((result)=>{//.then 用来指定请求成功之后的回调函数
//形参中的results是请求成功之后的结果
})
//例:
//axios.get("资源地址").then(成功后的操作).catch(失败后操作)
axios.get('../data.json').then(response=>(this.info=response.data))
发送请求
//获取按钮
const btns = document.querySelectorAll('button');
btns[1].onclick = function(){
//发送 AJAX 请求
axios({
//请求类型
method: 'POST',//请求类型有,get,put(修改),delete(删除)
//URL
url: 'http://localhost:3000/posts',
//设置请求体
data: {
title: "今天天气不错, 还挺风和日丽的",
author: "张三"
}
}).then(response => {
console.log(response);
});
}
方法请求
axios.get("query_stuById.do").then(re=>{
console.log(re.data.data.studentName)
})
带参请求
// 全局配置默认地址
axios.defaults.baseURL="http://10.10.11.93:8084/myweb/student_info"
// get带参
// 根据id获取学生数据
axios.get("query_stuById.do",{params:{id:1}}).then(re=>{
console.log(re.data.data.studentName)
}).catch(err=>{//失败后操作
console.log(err)
})
//post
axios.post("query_stuByName1.do",{studentName:'李四'}).then(re=>{
console.log(re.data)
}).catch(err=>{
console.log(err)
})
并发请求
// 并发请求
// 1、通过get方式获取所有学生
// 2、通过id查找相应的学生
// axios.all([请求1,请求2]).then().catch()
// 取值:分割spread
axios.all([
axios.get("query_stu.do"),
axios.get("query_stuById.do",{params:{id:1}})
]).then(
//console.log(re[1])
axios.spread((re1,re2)=>{
console.log(re1)
})
)
修改默认配置
//获取按钮
const btns = document.querySelectorAll('button');
//默认配置
axios.defaults.method = 'GET';//设置默认的请求类型为 GET
axios.defaults.baseURL = 'http://localhost:3000';//设置基础 URL
axios.defaults.params = {id:100};
axios.defaults.timeout = 3000;//
btns[0].onclick = function(){
axios({
url: '/posts'
}).then(response => {
console.log(response);
})
}
4.案例
获取后端数据
<div id="app">
<button type="button" @click="getAllStu()">显示数据</button>
<table border="1">
<tr>
<td>编号</td>
<td>姓名</td>
<td>性别</td>
<td>密码</td>
<td>地址</td>
</tr>
<tr v-for="item in info">
<td>{{item.studentNo}}</td>
<td>{{item.studentName}}</td>
<td>{{item.password}}</td>
<td>{{item.sex}}</td>
<td>{{item.address}}</td>
</tr>
</table>
</div>
<!--1.导入vue.js-->
<script src="../js/vue.js"></script>
<!--导入axios-->
<script src="../js/axios.js"></script>
<script>
axios.defaults.baseURL = "http://localhost:8080"
new Vue({
el: "#app",
data: {
info:[],
},
methods: {
getAllStu() {
axios.get("all").then(re =>{
this.info = re.data;
}).catch(err => {
console.log(err)
})
}
}
})
</script>
获取json数据
json数据
{
"name": "百度一下",
"age": "18",
"sex": "男",
"url":"https://www.baidu.com",
"address": {
"street": "文苑路",
"city": "南京",
"country": "中国"
},
"links": [
{
"name": "bilibili",
"url": "https://www.bilibili.com"
},
{
"name": "baidu",
"url": "https://www.baidu.com"
},
{
"name": "cqh video",
"url": "https://www.4399.com"
}
]
}
axios
<div id="app">
{{info.name}}
<a v-bind:href="info.url">点我进入</a>
</div>
<!--1.导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<!--导入axios-->
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js"></script>
<script>
var vm=new Vue({
el:'#app',
data:{
message:'hello,vue!'
},
data(){
return{
//请求的返回参数格式,必须和json字符串一样
info: {
name: null,
age: null,
sex: null,
url: null,
address: {
street: null,
city: null,
country: null
}
}
}
},
mounted(){//钩子函数
axios.get('../data.json').then(response=>(this.info=response.data))
}
});
</script>
计算属性
计算出来的结果缓存到属性中
特点:
- 定义的时候,要被定义为“方法”
- 在使用计算属性的时候,当普通属性使用即可
好处:
- 实现了代码的复用
- 只要计算属性中依赖的数据源变化了,则计算属性会重新求值!
结论:
调用方法时,每次都需要进行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这一点,计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
<div>currentTime1: {{currentTime1()}}</div>
<div>currentTime2: {{currentTime2}}</div>
</div>
</body>
<!--导入js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
message: "hello,world!"
},
methods: {
currentTime1: function () {
return Date.now(); // 返回一个时间戳
}
},
computed: {
//计算属性:methods,computed 方法名不能重名,重名字后,只会调用methods的方法
currentTime2: function () {
this.message;
// 返回一个时间戳
return Date.now();
}
}
})
</script>
</html>
生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oGvGuVON-1658970670011)(\java\框架\VUE\vue\生命周期.PNG)]
Vue实例有一个完整的生命周期,也就是说从开始创建、初始化数据、编译模板、挂在DOM、渲染-更新-渲染、卸载等一系列过程,我们成为Vue 实例的生命周期,钩子就是在某个阶段给你一个做某些处理的机会。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1sN6c5tY-1658970670012)(F:\java\框架\VUE\vue\生命周期的流程.PNG)]
生命周期图.jpg
生命周期文字解析.jpg
beforeCreate( 创建前 )
在实例初始化之后,数据观测和事件配置之前被调用,此时组件的选项对象还未创建,el 和 data 并未初始化,因此无法访问methods, data, computed等上的方法和数据。
created ( 创建后 )
实例已经创建完成之后被调用,在这一步,实例已完成以下配置:数据观测、属性和方法的运算,watch/event事件回调,完成了data 数据的初始化,el没有。 然而,挂在阶段还没有开始, $el属性目前不可见,这是一个常用的生命周期,因为你可以调用methods中的方法,改变data中的数据,并且修改可以通过vue的响应式绑定体现在页面上,,获取computed中的计算属性等等,通常我们可以在这里对实例进行预处理,也有一些童鞋喜欢在这里发ajax请求,值得注意的是,这个周期中是没有什么方法来对实例化过程进行拦截的,因此假如有某些数据必须获取才允许进入页面的话,并不适合在这个方法发请求,建议在组件路由钩子beforeRouteEnter中完成
beforeMount
挂在开始之前被调用,相关的render函数首次被调用(虚拟DOM),实例已完成以下的配置: 编译模板,把data里面的数据和模板生成html,完成了el和data 初始化,注意此时还没有挂在html到页面上。
mounted
挂在完成,也就是模板中的HTML渲染到HTML页面中,此时一般可以做一些ajax操作,mounted只会执行一次。
beforeUpdate
在数据更新之前被调用,发生在虚拟DOM重新渲染和打补丁之前,可以在该钩子中进一步地更改状态,不会触发附加地重渲染过程
updated(更新后)
在由于数据更改导致地虚拟DOM重新渲染和打补丁只会调用,调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作,然后在大多是情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环,该钩子在服务器端渲染期间不被调用
beforeDestroy(销毁前)
在实例销毁之前调用,实例仍然完全可用,
- 这一步还可以用this来获取实例,
- 一般在这一步做一些重置的操作,比如清除掉组件中的定时器 和 监听的dom事件
destroyed(销毁后)
在实例销毁之后调用,调用后,所以的事件监听器会被移出,所有的子实例也会被销毁,该钩子在服务器端渲染期间不被调用
第一个Vue-cli项目
定义
vue-cli 官方提供的一个脚手架,用于快速生成一个 vue 的项目模板;
预先定义好的目录结构及基础代码,就好比咱们在创建 Maven 项目时可以选择创建一个骨架项目,这个骨架项目就是脚手架,我们的开发更加的快速;
主要功能:
- 统一的目录结构
- 本地调试
- 热部署
- 单元测试
- 集成打包上线
需要的环境
-
Node.js : http://nodejs.cn/download/
安装就无脑下一步就好,安装在自己的环境目录下
-
Git : https://git-scm.com/downloads
镜像:https://npm.taobao.org/mirrors/git-for-windows/
确认nodejs安装成功:
- cmd 下输入
node -v
,查看是否能够正确打印出版本号即可! - cmd 下输入
npm-v
,查看是否能够正确打印出版本号即可!
这个npm,就是一个软件包管理工具,就和linux下的apt软件安装差不多!
npm 是 JavaScript 世界的包管理工具,并且是 Node.js 平台的默认包管理工具。通过 npm 可以安装、共享、分发代码,管理项目依赖关系。
安装 Node.js 淘宝镜像加速器(cnpm)
这样子的话,下载会快很多~
1.# -g 就是全局安装
npm install cnpm -g
2.# 若安装失败,则将源npm源换成淘宝镜像
# 因为npm安装插件是从国外服务器下载,受网络影响大
npm config set registry https://registry.npm.taobao.org
# 然后再执行
npm install cnpm -g
安装的位置:C:\Users\Administrator\AppData\Roaming\npm
安装vue-cli
#在命令台输入
cnpm install vue-cli -g
#查看是否安装成功
vue list
第一个 vue-cli 应用程序
创建一个Vue项目,我们随便建立一个空的文件夹在电脑上。
我这里在D盘下新建一个目录D:\Project\vue-study;
创建一个基于 webpack 模板的 vue 应用程序
#跳转到所在的目录下
cd xxx
# 这里的 myvue 是项目名称,可以根据自己的需求起名
vue init webpack myvue
一路都选择no即可;
初始化并运行
cd myvue
npm install
npm run dev #启动项目
执行完成后,目录多了很多依赖
Webpack
安装
WebPack 是一款模块加载器兼打包工具,它能把各种资源,如 JS、JSX、ES6、SASS、LESS、图片等都作为模块来处理和使用。
npm install webpack -g
npm install webpack-cli -g
测试安装成功: 输入以下命令有版本号输出即为安装成功
webpack -v
webpack-cli -v
什么是Webpack
本质上,webpack是一个现代JavaScript应用程序的静态模块打包器(module bundler)。当webpack处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle.
Webpack是当下最热门的前端资源模块化管理和打包工具,它可以将许多松散耦合的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分离,等到实际需要时再异步加载。通过loader转换,任何形式的资源都可以当做模块,比如CommonsJS、AMD、ES6、 CSS、JSON、CoffeeScript、LESS等;
伴随着移动互联网的大潮,当今越来越多的网站已经从网页模式进化到了WebApp模式。它们运行在现代浏览器里,使用HTML5、CSS3、ES6 等新的技术来开发丰富的功能,网页已经不仅仅是完成浏览器的基本需求; WebApp通常是一个SPA (单页面应用) ,每一个视图通过异步的方式加载,这导致页面初始化和使用过程中会加载越来越多的JS代码,这给前端的开发流程和资源组织带来了巨大挑战。
前端开发和其他开发工作的主要区别,首先是前端基于多语言、多层次的编码和组织工作,其次前端产品的交付是基于浏览器的,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,就需要一个模块化系统,这个理想中的模块化系统是前端工程师多年来一直探索的难题。
vue -Router路由
实现组件之间的跳转
使用路由
引入路由
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router@3.0.0/dist/vue-router.js"></script>
创建组件对象
定义路由修对象的规则
const router=new VueRouter({
routes:[
{path:'/',component:login},//默认首页
{path:'/login',component:login},//path:自定义路径,compoent:对应的组件
{path:'/register',component:register}
]
})
将路由对象注册到vue实例中
const app=new Vue({
el:"#app",
data:{},
router:router //设置路由对象
})
根据链接切换路由
<div id="app">
<!--<a href="#/login">登录</a>-->
<!--<a href="#/register">注册</a>-->
<!--用于替换a标签,作切换,tag:可以将渲染成指定的标签-->
<router-link to="/login" tag="button">登录</router-link>
<router-link to="/register" tag="span">注册</router-link>
<!--显示路由的组件-->
<router-view></router-view>
</div>
路由中的参数传递
传统方式
通过?拼接参数
<router-link to="/login?id=21&name=zhangsan" tag="button">登录</router-link>
组件中获取参数
const login={
template:`<h1>登录</h1>`
data(){return()},
methods{},
created(){
console.log(thi.$route.query.id+":"+this.$route.query.name);
}
}
restful风格
通过路径方式传递参数
<router-link to="/login/21/zhangsan" tag="button">登录</router-link>
const router=new VueRouter({
routes:[
{path:'/login/:id/:name',component:login},//默认首页
]
})
组件中获取参数
const login={
template:`<h1>登录</h1>`,
created(){
console.log(thi.$route.params.id+":"+this.$route.para.name);
}
}
vue项目操作🧡
创建项目
下载框架
- 进入cmd中输入:
vue list
- 跳转到保存目的地址:
cd xxx
- 下载项目:
vue init webpack myvue
myvue是自己给项目取的名字 - 全部回车
- 进入项目文件:
cd myvuedemo
- 安装axios与Element_ui依赖
- 以开发者模式使用项目:
npm run dev
/在idea 中 Teemianl 中 Local中输入npm run dev
项目结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bx9EmfYT-1658970670013)(F:\java\8.框架\VUE\vue\webpack.PNG)]
代码所在地main.js
- index.html 引入了 main.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>myvuedemo</title>
</head>
<body>
<div id="app"></div>
<script src="/dist/build.js"></script><!--main.js打包后的内容-->
</body>
</html>
- main.js 引入了 App.vue
import Vue from 'vue'
import App from './App.vue' //引入上面的app.vue组件
new Vue({
el: '#app',
render: h => h(App)//将app.vue的内容全部赋给这个Vue,让app.vue的内容全部展示到该divzhong
})
引入Vue组件
全局注册
在main.js中,通过import和Vue.component配合,来将一个.vue文件注册成为一个标签。该标签就可以在整个项目中使用。
main.js
import Vue from 'vue'
import App from './App.vue'
import header from './components/Header.vue'
import content from './components/Content'
//全局注册组件
Vue.component('myHeader', header)
Vue.component('myContent', content)
new Vue({
el: '#app',
render: h => h(App)
})
app. vue
<template>
<div id="app">
<myHeader></myHeader>
<myContent></myContent>
</div>
</template>
<script>
export default {
name: 'app',
}
</script>
<style>
</style>
本地注册
在组件的内部注册一个组件,这个组件只能在本组件内使用
app. vue
<template>
<div id="app">
<myHeader></myHeader>
<myContent></myContent>
</div>
</template>
<script>
import Header from "./components/Header";
import Content from "./components/Content";
export default {
name: 'app',
data(){},
components:{
"MyHeader":Header,//注册组件
"MyContent":Content,
}
}
</script>
<style>
</style>
组件之间参数传递
父传子
父:app.vue
- 先注册组件
- 设置参数值:msg
- 传递值::myTitle=“msg”
<template>
<div id="app">
<myContent :myTitle="msg"></myContent>
</div>
</template>
<script>
import Header from "./components/Header";
import Content from "./components/Content";
export default {
name: 'app',
data(){
return{
msg:'hello vue!!'
}
},
components:{
"MyHeader":Header,
"MyContent":Content,
}
}
</script>
<style>
</style>
子:Content.vue
设置参数:props:[‘myTitle’]
<template>
<div>
列表
{{myTitle}}
</div>
</template>
<script>
export default {
name: "Content",
// props:['myTitle']
props:{
'myTitle':{
type:String,//类型
required:true,//是否是必须的
default:'xx',//默认值
}
}
}
</script>
<style scoped>
</style>
子传父
子:Content.vue
-
定义事件
methods:{ doClick(){ this.$emit('newName','hello js') } }
-
@click调用方法
<template>
<div>
列表
{{myTitle}}
<button type="button" @click="doClick" >点击</button>
</div>
</template>
<script>
export default {
name: "Content",
// props:['myTitle']
props:{
'myTitle':{
type:String,//类型
required:true,//是否是必须的
default:'xx',//默认值
},
},
methods:{
doClick(){
this.$emit('newName','hello js')
}
}
}
</script>
<style scoped>
</style>
父:app.vue
绑定事件:@newName="msg=$event"
- @newName:是子组件定义的事件
- msg:是要传递的值
<template>
<div id="app">
<myContent :myTitle="msg" @newName="msg=$event"></myContent>
</div>
</template>
<script>
import Header from "./components/Header";
import Content from "./components/Content";
export default {
name: 'app',
data(){
return{
msg:'hello vue!!'
}
},
components:{
"MyHeader":Header,
"MyContent":Content,
}
}
</script>
<style>
</style>
Axios
安装Axios
npm install --save axios vue-axios
在main.js中引入
在项目中使用Axios模块
import axios from "axios";
Vue.prototype.$http=axios;//修改内部的$http为axios
发送axios请求
发送请求
axios.get("query_stuById.do").then(re=>{//then:返回结果
console.log(re.data.data.studentName)
})
带参请求
// 全局配置默认地址
axios.defaults.baseURL="http://10.10.11.93:8084/myweb/student_info"
// get带参
// 根据id获取学生数据
axios.get("query_stuById.do",{params:{id:1}}).then(re=>{
console.log(re.data.data.studentName)
}).catch(err=>{//失败后操作
console.log(err)
})
//post
axios.post("query_stuByName1.do",{studentName:'李四'}).then(re=>{
console.log(re.data)
}).catch(err=>{
console.log(err)
})
路由
实现组件之间的跳转
安装路由模块
npm install vue-router -s
创建组件对象
定义路由修对象的规则
const router=new VueRouter({
routes:[
{path:'/',component:login},//默认首页,compoent:对应的组件
{path:'/login',component:login},//path:自定义路径
{path:'/register',component:register}
]
})
将路由对象注册到vue实例中
const app=new Vue({
el:"#app",
data:{},
router:router //设置路由对象
})
根据链接切换路由
<div id="app">
<!--用于替换a标签,作切换,tag:可以将渲染成指定的标签-->
<router-link to="/login" tag="button">登录</router-link>
<router-link to="/register" tag="span">注册</router-link>
<!--显示路由的组件-->
<router-view></router-view>
</div>
打包部署
-
在根目录下:
vue run build
注意:vue脚手架打包的项目必须在服务器上运行
-
打包之后项目变化
打包之后出现dist目录,这是vue脚手架直接部署项目
可以直接复制这个目录放入springboot中static下
element_ui
在脚手架项目中安装elementui
npm i element-ui -S
引入elementuimain.js
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
案例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AsX9B5mJ-1658970670014)(F:\java\8.框架\VUE\vue\案例.PNG)]
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import axios from "axios";
Vue.prototype.$http=axios;//修改内部的$http为axios
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
App.vue
<template>
<div id="app">
<router-link to="/home">主页</router-link>
<router-link to="/user">用户管理</router-link>
<router-link to="/student">学生管理</router-link>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
</style>
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from "../components/Home";
import User from "../components/User";
import Student from "../components/Student";
import UserAdd from "../components/UserAdd";
import UserEdit from "../components/UserEdit";
Vue.use(Router)
export default new Router({
routes: [
{path: '/', redirect: '/home'},//redirect:重定向
{path: '/home', component: Home},
{
path: '/user', component: User,
children: [
{path: 'add', component: UserAdd},
{path: 'edit', component: UserEdit},
] //将用户的添加方法嵌套在用户路由中
},
{path: '/student', component: Student},
]
})
User.vue
<template>
<div>
<h1>用户列表</h1>
<table style="text-align: center" border="1">
<tr>
<td>编号</td>
<td>姓名</td>
<td>年龄</td>
<td>生日</td>
<td>操作</td>
</tr>
<tr v-for="user in users">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.age}}</td>
<td>{{user.bir}}</td>
<td><a href="javascript:;" @click="delRow(user.id)">删除</a> <a :href="'#/user/edit?id='+user.id">修改</a></td>
</tr>
</table>
<a href="#/user/add">添加</a>
<router-view></router-view><!--显示路由界面-->
</div>
</template>
<script>
export default {
name: "User",
data(){
return{
users:[]
}
},methods:{
findAll(){
//查询所有用户的内容
this.$http.get("http://rap2api.taobao.org/app/mock/304234/user/findAll").then((res)=>{
this.users=res.data.results;
});
},
delRow(id){//删除记录
this.$http.get("http://rap2api.taobao.org/app/mock/304234/user/del?id"+id).then(res=>{
if (res.data.success){
this.findAll();
}
})
}
},
components:{},
created() {
this.findAll();
},//生命周期
watch:{//用来监听
$route:{
handler:function (val,oldVal){
console.log(val)
if(val.path='/user'){
this.findAll()
}
},
deep:true//深度监听
}
}
}
</script>
<style scoped>
</style>
UserAdd.vue
<template>
<div>
<h1>用户添加信息</h1>
<form action="">
用户名:<input type="text" v-model="user.name"><br>
年龄:<input type="text" v-model="user.age"><br>
生日: <input type="text" v-model="user.bir"><br>
<input type="button" value="添加用户信息" @click="saveUserInfo">
</form>
</div>
</template>
<script>
export default {
name: "UserAdd",
data(){
return{
user:{},
}
},methods:{
saveUserInfo(){
console.log(this.user)
//发送axios请求
this.$http.post("http://rap2api.taobao.org/app/mock/304234/user/add",this.user).then(res=>{
console.log(res);
if (res.data.success){
this.$router.push("/user");//切换路由
}
})
}
}
}
</script>
<style scoped>
</style>
UserEdit.vue
<template>
<div>
<h1>用户修改信息</h1>
<form action="">
用户名:<input type="text" v-model="user.name"><br>
年龄:<input type="text" v-model="user.age"><br>
生日: <input type="text" v-model="user.bir"><br>
<input type="button" value="修改" @click="editUserInfo()">
</form>
</div>
</template>
<script>
export default {
name: "UserEdit",
data(){
return{
user:{},
}
},
methods:{
findOne(){//根据id查询数据
this.$http.get("url?id="+this.user.id).then(res=>{
this.user=res.data;
});
},
editUserInfo(){//修改数据
this.$http.post("url",this.user).then(res=>{
if (res.data.success){
this.$router.push("/user");//切换路由
}
});
}
},
created() {
this.user.id=this.$route.query.id
this.findOne()
}
}
</script>
<style scoped>
</style>
then(res=>{
if (res.data.success){
this.findAll();
}
})
}
},
components:{},
created() {
this.findAll();
},//生命周期
watch:{//用来监听
$route:{
handler:function (val,oldVal){
console.log(val)
if(val.path=‘/user’){
this.findAll()
}
},
deep:true//深度监听
}
}
}
### UserAdd.vue
```vue
<template>
<div>
<h1>用户添加信息</h1>
<form action="">
用户名:<input type="text" v-model="user.name"><br>
年龄:<input type="text" v-model="user.age"><br>
生日: <input type="text" v-model="user.bir"><br>
<input type="button" value="添加用户信息" @click="saveUserInfo">
</form>
</div>
</template>
<script>
export default {
name: "UserAdd",
data(){
return{
user:{},
}
},methods:{
saveUserInfo(){
console.log(this.user)
//发送axios请求
this.$http.post("http://rap2api.taobao.org/app/mock/304234/user/add",this.user).then(res=>{
console.log(res);
if (res.data.success){
this.$router.push("/user");//切换路由
}
})
}
}
}
</script>
<style scoped>
</style>
UserEdit.vue
<template>
<div>
<h1>用户修改信息</h1>
<form action="">
用户名:<input type="text" v-model="user.name"><br>
年龄:<input type="text" v-model="user.age"><br>
生日: <input type="text" v-model="user.bir"><br>
<input type="button" value="修改" @click="editUserInfo()">
</form>
</div>
</template>
<script>
export default {
name: "UserEdit",
data(){
return{
user:{},
}
},
methods:{
findOne(){//根据id查询数据
this.$http.get("url?id="+this.user.id).then(res=>{
this.user=res.data;
});
},
editUserInfo(){//修改数据
this.$http.post("url",this.user).then(res=>{
if (res.data.success){
this.$router.push("/user");//切换路由
}
});
}
},
created() {
this.user.id=this.$route.query.id
this.findOne()
}
}
</script>
<style scoped>
</style>