Vue
1、概述
1.1、简介
vue遵循SOC原则:关注点分离原则,边界明确,只关注视图层
vue只关注视图层:html+css+js 视图:给用户看,刷新后台给的数据
Vue完全解耦了View和Model层,是前后端分离方案实施的重要的一环
Vue.js是一个MVVM的实现者,核心就是实现了DOM监听与数据绑定
Vue的作者严格遵守SOC(关注度分离原则),所以Vue并不包括Ajax的通信功能,即:
网络通信:交给axios
页面跳转:vue-router
状态管理:vuex
1.2、前端三大框架
Angular:首先推出前端模块化开发:mvvm(vm是前端的控制层:view-model,数据双向绑定),就是后端的mvc模式
React:facebook出品,特点是提出新概念虚拟DOM用于减少真实DOM操作,在内存中模拟DOM操作,有效地提升前端渲染效率。
vue:一款渐进式JavaScript框架,就是逐步实现新特性的意思,如实现模块化开发、路由、状态管理等,主要是综合了Angular(模块化开发:mvvm)和React(虚拟DOM)的优点
mvvm组成
- ViewModel能够观察到数据的变化,并对视图对应的内容进行更新
- ViewModel能够监听到视图的变化,并能够通知数据进行变化
1.3、为什么使用Vue
为什么使用Vue(最重要的是使用的人多)
2、MVVM和第一个Vue程序
2.1、MVVM
什么是MVVM
- 是一种软件架构设计模式,是一种简化用户界面的事件驱动编程方式
- 源自于经典的MVC模式
- 核心是VM:ViewModel,数据视图双向绑定
为什么使用MVVM
- 低耦合:视图View可以独立于Model进行修改,一个ModelView可以绑定到不同的View上,当View变化的时候Model可以不变,Model变化的时候View也可以不变
- 可复用:可以把一些视图逻辑放在一个ViewModel中,让很多View重用这段视图逻辑
- 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员专注于页面设计
- 可测试:一般来说页面测试很难,而现在测试可以针对ViewModel来写
2.2、第一个Vue程序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- view:视图 -->
<div id="app">
{{message}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var vm=new Vue({
el: "#app",
// Model:数据
data: {
message: "hello,vue!"
}
});
</script>
</body>
</html>
3、Vue基本语法
v-bind
v-bind
:类似的称为指令,指令前带有前缀v-
,以代表它们是Vue提供的特殊特性,会在渲染的DOM上应用特殊的响应式行为,可以使用:
代替
<div id="app">
<!--类似于{{message}}-->
<span v-bind:title="message">
鼠标悬停查看信息
</span>
</div>
3.1、v-if
- 判断,属性为true直接移除组件
v-show
实现效果是一样的,但是为true是设置组件display:none
<body>
<div id="app">
<h1 v-if="type==='A'">A</h1>
<h1 v-else-if="type==='B'">B</h1>
<h1 v-else>C</h1>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var vm=new Vue({
el: "#app",
data: {
type: 'A'
}
});
</script>
</body>
3.2、v-for
- 循环遍历
<body>
<div id="app">
<li v-for="(item,index) in items">
{{item.message}}--{{index}}
</li>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
items: [
{message: '狂神说Java'},
{message: '狂神说前端'},
{message: '狂神说运维'},
{message: '狂神说运维'}
]
}
});
</script>
</body>
3.3、v-on
- 事件点击
<body>
<div id="app">
<!-- 可以使用@替换 v-on: -->
<button v-on:click="sayHi">Click Me</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
message: '狂神说Java'
},
methods: {//方法必须定义在methods中,才能绑定到vue对象中
sayHi: function () {
alert(this.message);
}
}
});
</script>
</body>
3.4、v-model
- 双向绑定事件,可以根据
输入框
<body>
<div id="app">
<!--将标签的值跟vm对象的data里面的值绑定,某个值改变了就都改变-->
输入的文本:<input type="text" v-model="message"> {{message}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
message: '123'
}
});
</script>
</body>
</html>
单选框
<body>
<div id="app">
性别:
<input type="radio" name="sex" value="男" v-model="sex">男
<input type="radio" name="sex" value="女" v-model="sex">女
<br>
{{sex}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
sex: '123'
}
});
</script>
</body>
</html>
下拉框
<body>
<div id="app">
下拉框
<select v-model="selected">
<option value="" disabled>--请选择--</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
{{selected}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
selected: ''
}
});
</script>
</body>
</html>
3.5、组件
- 组件是可复用的Vue实例,就是可以重复使用的模板
模拟组件
<body>
<div id="app">
<!-- v-bind:绑定形参it把实参传item递给组件的props,再赋值 -->
<!-- v-bind: 缩写 :-->
<lvboaa v-for="item in items" v-bind:it="item"> </lvboaa>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
//组件名称,组件模板,不要用大写,不然识别不出来
Vue.component("lvboaa",{
props: ['it'],
template: '<li>{{it}}</li>'
});
var vm = new Vue({
el: "#app",
data: {
items: ["Java","Linux","Vue"]
}
});
</script>
</body>
3.6、axios
- 异步通信
vue的生命周期
<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>
<style>
/* v-clock:解决闪烁问题 */
[v-clock] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-clock>
<div>{{info.name}}</div>
<div>{{info.address.name}}</div>
<a v-bind:href="info.url">点我</a>
</div>
<script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data:{
info:{
name: null,
url: null,
address: {
street: null,
city: null,
country: null
}
}
},
mounted(){//钩子函数,一般会做一些ajax请求获取数据,进行数据初始化,在整个实例中只执行一次
axios.get('file/data.json').then(response=>(this.info=response.data));
}
});
</script>
</body>
3.7、计算属性
- 就是一个将计算结果缓存起来的属性,可以想象成缓存
- 计算属性的主要特性就是为了把不经常变化的计算结果缓存起来,以节约我们的系统开销
<body>
<div id="app" v-clock>
<p>currentTime1:{{currentTime1()}}</p>
<p>currentTime2:{{currentTime2}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
message: "hello,world!"
},
methods: {
currentTime1: function (){
return Date.now();//返回一个时间戳
}
},
computed: {//定义计算属性:methods和computed方法名不能重名,重名之后,只会调用methods的方法
//计算属性是将返回的值赋给变量名,再转化成vue对象的属性
currentTime2: function (){
this.message;
return Date.now();//返回一个时间戳
}
}
});
</script>
</body>
3.8、slot
- 内容分发:作者称其为插槽,就是在页面留下一个口,可以改变数据,相当于组件开发
<body>
<div id="app" v-clock>
<todo>
<todo-title slot="todo-title" :title="title"></todo-title>
<todo-item slot="todo-item" v-for="item in items" :item="item"></todo-item>
</todo>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
// slot:插槽
Vue.component("todo",{
template: '<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-item"></slot>\
</ul>\
</div>'
});
Vue.component("todo-title",{
props: ['title'],
template: '<div>{{title}}</div>'
});
Vue.component("todo-item",{
props: ['item'],
template: '<li>{{item}}</li>'
});
var vm = new Vue({
el: "#app",
data: {
title: "秦老师说Java",
items: ['Java','前端','Linux']
},
});
</script>
</body>
3.9、自定义事件内容分发
- 通过调用组件插槽里面的事件去调用Vue里面的事件
- 通过前端作为中间商
- 在前端绑定自定义事件和Vue事件
<body>
<div id="app" v-clock>
<todo>
<todo-title slot="todo-title" :title="title"></todo-title>
<!-- @remove1="removeItem(index) 绑定自定义事件和Vue事件 -->
<todo-item slot="todo-item" v-for="(item,index) in items"
:item="item" :index="index" @remove1="removeItem(index)"></todo-item>
</todo>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
// slot:插槽
Vue.component("todo",{
template: '<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-item"></slot>\
</ul>\
</div>'
});
//标题组件
Vue.component("todo-title",{
props: ['title'],
template: '<div>{{title}}</div>'
});
//元素组件
Vue.component("todo-item",{
props: ['item','index'],
template: '<li>{{index}}--{{item}} <button @click="remove">删除</button></li>',
methods: {
remove: function(index){
// 调用自定义事件
this.$emit('remove1',index)
}
}
});
var vm = new Vue({
el: "#app",
data: {
title: "秦老师说Java",
items: ['Java','前端','Linux']
},
methods: {
removeItem: function(index){
console.log(this.items[index]);
this.items.splice(index,1);//删除从index开始的1个元素,就是自己
}
}
});
</script>
</body>
4、webpack
- 打包js文件,主要是将ES6语法打包成ES5语法(因为大部分浏览器都只支持ES5语法)
安装webpack和webpack-cli
# -g是全局安装
npm install webpack -g
npm install webpack-cli -g
webpack
执行打包命令,webpack --watch
实现热部署监听
5、vue-router
在项目中安装vue-router,实现前端控制路由跳转
# 在当前项目执行
npm install vue-router --save-dev
使用vue-router
import VueRouter from 'vue-router'
// vue-router使用需要显示声明
Vue.use(VueRouter);
实例
- vue项目的main.js
import Vue from 'vue'
import App from './App'
import router from './router' //自动扫描里面的路由配置(index.js)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
// 配置路由
router,
components: { App },
template: '<App/>'
})
- App.vue
<template>
<div id="app">
<hello></hello>
<!-- 相当于a链接 -->
<router-link to="/main">首页</router-link>
<router-link to="/hello">欢迎页</router-link>
<router-link to="/kuang">Kuang</router-link>
<!-- router-view:展示链接的视图 -->
<router-view></router-view>
</div>
</template>
<script>
import Hello from './components/Hello'
export default {
name: 'App',
components: {
Hello
}
}
</script>
- 一个组件:Hello.vue
<template>
<h1>{{msg}}</h1>
</template>
<script>
export default {
name: 'Hello',
data() {
return {
msg: 'hello,vue'
}
},
}
- router的主配置文件:index.js
// vue-router的主配置文件
import Vue from 'vue'
import VueRouter from 'vue-router'
import Hello from '../components/Hello'
import Main from '../components/Main'
import Kuang from '../components/Kuang'
//安装路由
Vue.use(VueRouter);
// 配置导出路由
export default new VueRouter({
routes: [
{
// 路由路径
path: '/hello',
name: 'hello',
// 跳转的组件
component: Hello
},
{
// 路由路径
path: '/main',
name: 'main',
// 跳转的组件
component: Main
},
{
path: '/kuang',
component: Kuang
}
],
// 去除路由跳转的 /#
mode:'history'
});
6、Vue+Element-UI简单实战项目
6.1、创建工程
# 新建一个名为hello-vue的vue项目
vue init webpack hello-vue
# 进入工程目录
cd hello-vue
# 安装vue-router
npm install vue-router --save-dev
# 安装element-ui
npm i element-ui -S
# 安装依赖
npm install
# 安装SASS加载器
cnpm install sass-loader node-sass --save-dev
# 启动测试
npm run dev
npm命令
6.2、实际项目
报错:Module build failed: TypeError: this.getOptions is not a function
- 原因sass-loader版本过高
路由嵌套
- 可以在一个页面中的一部分跳另一个路由,相当于slot插槽
- 不这样使用router-link会跳转到新的页面
export default new Router({
routes: [
{
path: '/main',
component: Main, //使用嵌套路由,不使用的话 router-link会展示在新的页面
children: [
{path: '/user/profile',component: Profile},
{path: '/user/list',component: List}
]
}
]
});
参数传递及重定向
- 第一种
<!--Main.vue-->
<!-- name就是组件名或路由,params传递参数 需要使用v-bind绑定对象 -->
<router-link :to="{name:'Profile',params:{id:1}}">个人信息</router-link>
<!--index.js-->
export default new Router({
routes: [
{
path: '/main',
component: Main, //使用嵌套路由,不使用的话 router-link会展示在新的页面
children: [
{
path: '/user/profile/:id', //:id:绑定对象
name: 'Profile',
component: Profile
}
]
},
{
path: '/login',
component: Login
},
]
});
<!--组件展示页面-->
<template>
<!--template需要一个根节点div,所有的元素必须在这里面 -->
<div>
<h1>个人信息</h1>
{{$route.params.id}}
</div>
</template>
- 第二种:在嵌套路由里面添加属性
props: true
<!--展示页面-->
<template>
<!--template需要一个根节点div,所有的元素必须在这里面 -->
<div>
<h1>个人信息</h1>
{{id}}
</div>
</template>
<script>
export default {
props: ["id"],
name: "Profile",
};
</script>
重定向
- 在路由里面加上
{ path: '/goHome', redirect: '/main' }
6.3、路由模式和404
404
- 丢失页面
<!--NotFound.vue-->
<template>
<div><h3>404,你的页面丢失了!</h3></div>
</template>
<script>
export default {
name: 'NotFound'
}
</script>
//index.js配置路由
{
path: '*',//只要找不到配置的路由就走NotFound
component: NotFound
}
6.4、路由钩子与异步请求
beforeRouteEnter
:进入路由前执行
beforeRouteLeave
:离开路由前执行
<script>
export default {
props: ["id"],
name: "Profile",
beforeRouteEnter: (to, from, next) => {
console.log("进入路由之前");
next();
},
beforeRouteLeave: (to, from, next) => {
console.log("进入路由之后");
next();
},
};
</script>
在钩子函数中使用异步请求
- 安装axios:
npm install --save vue-axios
- 在
main.js
中引用
// 导入axios
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios,axios);