什么是VUE?
前端框架。Vue的核心库只关注视图层。综合了Angular(模块化:MVVM)和 React(虚拟DOM:利用内存)的优点
前端为主的MV*时代
MVC(同步通信为主):Model 、View 、Controller
MVP(异步通信为主):Model 、View 、 Persenter
MVVM(异步通信为主):Model 、View 、ViewModel
什么是MVVM?
MVVM(Model-View-ViewModel)是一种软件架构设计模式,是一种简化用户界面的事件驱动编程方式。MVVM源自于MVC模式。
Model:模型层,在这里表示JavaScript对象
View:视图层,在这里表示Dom(Html操作的元素)
ViewModel:连接视图和数据的中间件,Vue.js就是MVVM中的ViewModel层的实现者
核心是ViewModel层。
ViewModel层作用:
①向上与视图层进行双向数据绑定 ②向下与Model层通过接口请求进行数据交互
第一个Vue程序
在html文件中编写:
<!--view层:模板-->
<div id="app">
{{message}}
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
//Model层:数据
data: {
message: "hello,vue"
}
});
</script>
显示结果:
VM(ViewModel)双向绑定:
Vue基本语法
1、v-bind
<!--view层:模板-->
<div id="app">
<span v-bind:title="message">
鼠标在这里悬停几秒钟,就可以查看title绑定的message信息
</span>
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
//Model层:数据
data: {
message: "hello,vue"
}
});
</script>
显示结果:
2、v-if v-else-if v-else
<div id="app">
<h1 v-if="type==='A'">A</h1>
<h1 v-else-if="type==='B'">B</h1>
<h1 v-else>C</h1>
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
type: "A"
}
});
</script>
3、v-for
<div id="app">
<li v-for="(item, index) in items">
{{item.name}}--{{index}}
</li>
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
items: [
{name: "张三"},
{name: "李四"},
{name: "王五"}
]
}
});
</script>
显示结果:
Vue绑定事件(v-on)
<div id="app">
<button v-on:click="sayHi">点我</button>
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
message: "丘桔"
},
methods: { //方法必须定义在vue的methods对象中
sayHi: function(event){
alert("Hi " + this.message)
}
}
});
</script>
显示结果:
Vue双向绑定
1、什么是双向绑定?
当数据变了,视图也变;视图变了,数据也变。
2、在表单中使用双向数据绑定
可以用 v-model 指令在表单 <input>、<textarea>、<select>元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。它负责监听用户的输入事件来更新数据,并对一些极端场景进行一些特殊处理
input标签:
<div id="app">
文本框中输入的数据变,后面显示的数据跟着变:<input type="text" v-model="message">{{message}}
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
message: "丘桔"
}
});
</script>
textarea标签:
<div id="app">
<textarea v-model="message"></textarea>
{{message}}
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
message: "丘桔"
}
});
</script>
单选框:
<div id="app">
性别:
<input type="radio" name="sex" value="男" v-model="who">男
<input type="radio" name="sex" value="女" v-model="who"> 女
<br>
选中了谁:{{who}}
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
who: ""
}
});
</script>
显示结果:
下拉框:
<div id="app">
<select v-model="ABC">
<option value="" disabled>--请选择--</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
选中了:{{ABC}}
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
ABC: ""
}
});
</script>
注意:如果 v-model 表达式的初始值未能匹配任何选项,<select>元素将被渲染为“未选中”状态。在ios中,这会使用户无法选择第一个选项。因为这样的情况下,ios不会触发change事件。因此,更推荐像上面这样提供一个值为空的禁用选项
Vue组件讲解
Vue组件:自定义标签
<div id="app">
<qiujie v-for="item in items" v-bind:qiu="item"></qiujie>
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
//定义一个vue组件
Vue.component("qiujie",{
props: ['qiu'],
template: '<li>{{qiu}}</li>'
});
var vm = new Vue({
el: "#app",
data: {
items: ["张三","李四","丘桔"]
}
});
</script>
显示结果:
解析:
Axios异步通信
1、什么是Axios?
Axios是一个可以用在浏览器端和NodeJS的异步通信框架
主要作用:实现Ajax异步通信
中文文档:axios中文网|axios API 中文文档 | axios
2、为什么要使用Axios?
为了解决通信问题
jQuery.ajax() 也可以通信,但是操作Dom太频繁
3、Vue的生命周期
4、使用axios
生命周期中的mounted(钩子函数),可以实现通信问题
步骤:
①编写一个data.json文件(一定要是json格式)
{
"name":"丘桔",
"url": "http://baidu.com",
"page": 1,
"isNonProfit":true,
"address": {
"street": "昌平",
"city": "北京",
"country": "中国"
},
"links": [
{
"name": "B站",
"url": "https://www.bilibili.com/"
},
{
"name": "百度",
"url": "https://www.baidu.com/"
},
{
"name": "4399",
"url": "https://www.4399.com/"
}
]
}
②导入axios的cdn,并且使用mounted函数通信
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="vue">
<div>{{info.name}}</div>
<div>{{info.address}}</div>
<a v-bind:href="info.url">点我</a>
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
var vm = new Vue({
el: "#vue",
data() {
return { //请求返回的参数格式,必须和json字符串一样
info: {
name: null,
address: {
country: null,
city: null,
street: null
},
url: null
}
}
},
mounted(){ //钩子函数 链式编程 ES6新特性
axios.get('../data.json').then(response=>(this.info=response.data))
}
})
</script>
</body>
</html>
其中 response.data 是 json字符串
Vue计算属性
什么是计算属性?
计算出来的结果,保存在属性中,内存中运行,可以想象为缓存
上代码:
<div id="app">
<div>currentTime1 {{currentTime1()}}</div> <!--调方法-->
<div>currentTime2 {{currentTime2}}</div> <!--调属性-->
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
message: "hello,丘桔"
},
methods: {
currentTime1: function(){
return Date.now(); //返回一个时间戳
}
},
computed: { //注意:methods和computed中的方法名不能重复,如果重复了,调出来的只会是methods中的方法
currentTime2: function(){
this.message;
return Date.now();
}
}
});
</script>
显示结果:
插槽slot
作用:解耦合
上代码:
<div id="app">
<todo>
<todo-tile slot="todo-title" v-bind:title="title2"></todo-tile>
<todo-items slot="todo-items" v-for="item in todoItems" v-bind:item1="item"></todo-items>
</todo>
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
Vue.component("todo",{
template: "<div>\
<slot name='todo-title'></slot>\
<ul>\
<slot name='todo-items'></slot>\
</ul>\
</div>"
});
Vue.component("todo-tile",{
props: ["title"],
template: "<p>{{title}}</p>"
});
Vue.component("todo-items",{
props: ["item1"],
template: "<li>{{item1}}</li>"
})
var vm = new Vue({
el: "#app",
data: {
title2: "书籍列表",
todoItems: ["语文","数学","英语"]
}
});
</script>
显示结果:
绑定:
自定义事件
自定义事件:this.$emit('自定义事件名',参数)
在之前的代码上增加一个删除功能:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<todo>
<todo-tile slot="todo-title" v-bind:title="title2"></todo-tile>
<todo-items slot="todo-items" v-for="(item,index) in todoItems" v-bind:item1="item"
v-bind:index="index" v-on:remove="removeItems(index)"></todo-items>
</todo>
</div>
<!--导入Vue.js-->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
Vue.component("todo",{
template: "<div>\
<slot name='todo-title'></slot>\
<ul>\
<slot name='todo-items'></slot>\
</ul>\
</div>"
});
Vue.component("todo-tile",{
props: ["title"],
template: "<p>{{title}}</p>"
});
Vue.component("todo-items",{
props: ["item1","index"],
template: "<li>{{item1}}--{{index}} <button v-on:click='remove'>删除</button></li>",
methods: {
remove: function(index){
//自定义事件分发
this.$emit("remove",index);
}
}
})
var vm = new Vue({
el: "#app",
data: {
title2: "书籍列表",
todoItems: ["语文","数学","英语"]
},
methods: {
removeItems: function(index){
this.todoItems.splice(index,1); //从下标index开始,删除一个元素
}
}
});
</script>
</body>
</html>
显示结果(点击按钮真的可以删除):
Vue-cli
什么是vue-cli?
官方提供的一个脚手架,用于快速 生成一个vue的项目模板
需要的环境:
Node.js:Download | Node.js
初始化并运行
cd myvue
npm install
npm run dev
Webpack学习和使用
什么是webpack?
是一个模块化加载器兼打包工具,它能把各种资源,如JS、JSX、ES6、SASS、LESS、图片等都作为模块来处理和使用
安装:
npm install webpack -g
npm install webpack-cli -g
使用webpack
1、创建项目(新建一个文件夹,然后用 IDEA打开)
2、创建一个名为modules的目录,用于放置JS模块等资源文件
3、在modules下创建模块文件,如hello.js 用于编写js模块相关代码
//暴露一个方法
exports.sayHi = function(){
document.write("<h1>哈喽,丘桔</h1>")
}
4、在modules下创建一个名为main.js的入口,用于打包时设置entry属性
var hello = require("./hello");
hello.sayHi();
5、在项目目录下,创建webpack.config.js(注意中间是 .config 不是 -config)配置文件,使用webpack命令打包
module.exports = {
entry: './modules/main.js',
output: {
filename: "./js/bundle.js"
}
}
6、在项目目录下,创建HTML页面,如index.html,导入webpack打包之后的js文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="dist/js/bundle.js"></script>
</body>
</html>
7、使用浏览器运行index.html页面(如果失败的话,就使用管理员权限运行webpack即可)
8、显示结果
补充:
使用 webpack --watch 可以监听(代码改变之后会重新部署)
vue-router路由
安装vue-router
npm install vue-router --save-dev
//如果后面报错:export ‘default‘ (imported as ‘VueRouter‘) was not found in ‘vue-router
//说明版本不兼容
//使用 npm uninstall vue-router 卸载原版本
//再使用 npm install vue-router@3.5.2 下载新版本即可
使用vue-router:
1、创建一个components目录,用来存放我们自己编写的组件。然后在components包下,创建content.vue和main.vue。
content.vue:
<template>
<h1>内容页</h1>
</template>
<script>
export default {
name: "content"
}
</script>
<style scoped>
</style>
main.vue:
<template>
<h1>首页</h1>
</template>
<script>
export default {
name: "main"
}
</script>
<style scoped>
</style>
2、安装路由,在src目录下,新建一个router文件夹,用来专门存放路由
在router文件夹里编写一个index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Content from '../components/content'
import Main from '../components/main'
//显示声明使用 VueRouter
Vue.use(VueRouter);
//配置导出路由
export default new VueRouter({
routes: [{
//路由路径
path: '/content',
//路由名字
name: 'content',
//跳转组件
component: Content
},
{
//路由路径
path: '/main',
//路由名字
name: 'main',
//跳转组件
component: Main
}
]
})
3、在main.js中配置路由
import Vue from 'vue'
import App from './App'
import router from './router' //本来应该是 './router/index' 但是只要是index会默认加载,所以只导router包就行
Vue.config.productionTip = false;
new Vue({
el: '#app',
//配置路由 (注意:这里的router和上面的 import router都要小写,不能写成Router,不然会报错)
router,
components: { App },
template: '<App/>'
})
4、在App.vue中使用路由
<template>
<div id="app">
<h1>vue-router</h1>
<router-link to="/main">首页</router-link>
<router-link to="/content">内容页</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
5、使用命令行运行vue
npm run dev
//报错的话,可能是因为router版本太高了
//使用 cnpm install vue-router@3.1.3 --save-dev 就行
ElementUI
官网:Element - The world's most popular Vue UI framework
使用ElementUI:
1、创建一个名为hello-vue的工程
vue init webpack hello-vue
2、安装依赖
#进入工程目录
cd hello-vue
#安装 vue-router
npm install vue-router@3.5.2 --save-dev
#安装 element-ui
npm i element-ui -S
#安装依赖
npm install
#安装 SASS 加载器
npm install --save-dev sass-loader
#启动测试
npm run dev
3、使用 IDEA 打开hello vue,把没用的初始化东西删掉,并创建如下目录:
- assets:用于存放资源文件
- components:用于存放Vue功能组件
- views:用于存放Vue视图组件
- router:用于存放vue-router配置
4、在views包下创建首页视图:Main.vue
<template>
<h1>首页</h1>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped>
</style>
5、在 views包下创建登录页:Login.vue
<template>
<div>
<el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box">
<h3 class="login-title">欢迎登录</h3>
<el-form-item label="账号" prop="username">
<el-input type="text" placeholder="请输入账号" v-model="form.username"/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" placeholder="请输入密码" v-model="form.password"/>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="onSubmit('loginForm')">登录</el-button>
</el-form-item>
</el-form>
<el-dialog title="温馨提示" :visible.sync="dialogVisiable" width="30%" :before-close="handleClose">
<span>请输入账号和密码</span>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: "Login",
data(){
return{
form:{
username:'',
password:''
},
//表单验证,需要在 el-form-item 元素中增加prop属性
rules:{
username:[
{required:true,message:"账号不可为空",trigger:"blur"}
],
password:[
{required:true,message:"密码不可为空",tigger:"blur"}
]
},
//对话框显示和隐藏
dialogVisible:false
}
},
methods:{
onSubmit(formName){
//为表单绑定验证功能
this.$refs[formName].validate((valid)=>{
if(valid){
//使用vue-router路由到指定界面,该方式称为编程式导航
this.$router.push('/main');
}else{
this.dialogVisible=true;
return false;
}
});
}
}
}
</script>
<style scoped>
.login-box{
border:1px solid #DCDFE6;
width: 350px;
margin:180px auto;
padding: 35px 35px 15px 35px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow: 0 0 25px #909399;
}
.login-title{
text-align:center;
margin: 0 auto 40px auto;
color: #303133;
}
</style>
注意:是 <style scoped lang="scss">中的 lang="scss"一定要删除,不然会报错:Error: Cannot find module 'node-sass'
6、 在router目录下,创建路由配置文件:index.js
import Vue from 'vue'
import Router from 'vue-router'
import Main from '../views/Main'
import Login from '../views/Login'
Vue.use(Router);
export default new Router({
routes: [
{
path: '/main',
component: Main
},{
path: '/login',
component: Login
}
]
})
7、App.vue
<template>
<div id="app">
<router-link to="/login">login</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
8、main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(router)
Vue.use(ElementUI)
new Vue({
el: '#app',
router,
render: h => h(App) //ElementUI
})
9、运行npm run dev
这里报错: this.getResolve is not a function
说明sass版本太高了,改成7.3.1即可
改了版本之后,需要重新安装:
npm install cnpm@7.1.0 -g --registry=https://registry.npm.taobao.org
注意:使用npm install 和 cnpm install安装都会报错
10、显示结果:
点击login之后:
输入用户名和密码,并点击登录之后:
路由嵌套
步骤:
1、在views目录下创建user目录
Profile.vue:
<template>
<h1>个人信息</h1>
</template>
<script>
export default {
name: "UserProfile.vue"
}
</script>
<style scoped>
</style>
List.vue:
<template>
<h1>用户列表</h1>
</template>
<script>
export default {
name: "UserList"
}
</script>
<style scoped>
</style>
2、Main.vue
<template>
<div>
<el-container>
<el-aside width="200px">
<el-menu :default-openeds="['1']">
<el-submenu index="1">
<template slot="title"><i class="el-icon-caret-right"></i>用户管理</template>
<el-menu-item-group>
<el-menu-item index="1-1">
<!--插入的地方-->
<router-link to="/user/profile">个人信息</router-link>
</el-menu-item>
<el-menu-item index="1-2">
<!--插入的地方-->
<router-link to="/user/list">用户列表</router-link>
</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="2">
<template slot="title"><i class="el-icon-caret-right"></i>内容管理</template>
<el-menu-item-group>
<el-menu-item index="2-1">分类管理</el-menu-item>
<el-menu-item index="2-2">内容列表</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="text-align: right; font-size: 12px">
<el-dropdown>
<i class="el-icon-setting" style="margin-right: 15px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<!--在这里展示视图-->
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped>
.el-header {
background-color: #B3C0D1;
color: #333;
line-height: 60px;
}
.el-aside {
color: #333;
}
</style>
3、修改router目录下的index.js
import Vue from 'vue'
import Router from 'vue-router'
import Main from '../views/Main'
import Login from '../views/Login'
import UserList from '../views/user/List'
import UserProfile from '../views/user/Profile'
Vue.use(Router);
export default new Router({
routes: [
{
path: '/main',
component: Main,
children: [
{path: '/user/profile',component: UserProfile},
{path: '/user/list', component: UserList}
]
},{
path: '/login',
component: Login
}
]
})
4、显示结果:
参数传递及重定向
传递参数:
1、在Main.vue中
2、在router下的index.js中
3、在Profile.vue中
4、显示结果:
重定向:
1、 在router的index.js中
2、在Main.vue中
3、显示结果
点击回到登录页之后:
路由钩子与异步请求
路由模式有2种:
1、hash:路径带#符号,如 http://localhost/#/login
2、history:路径不带#符号,如 http://localhost/login
可以修改路由配置:
处理404:
1、在views目录下新建NotFound.vue
2、在路由中配置NotFound.vue
路由钩子与异步请求:
①beforeRouteEnter:在进入路由前执行
②beforeRouteLeave:在离开路由前执行
<script>
export default {
name: "UserProfile.vue",
//可以想象为java里面的过滤器
beforeRouteEnter: (to, from, next)=>{
console.log("进入路由之前");
next();
},
beforeRouteLeave: (to, from, next)=>{
console.log("离开路由之前");
next();
}
}
</script>
参数解释:
to:路由将要跳转的路径信息
from:路径跳转前的路径信息
next:路由的控制参数
next()跳入下一个页面
next('/path')改变路由的跳转方向,使其跳到另一个路由
next(false)返回原来的页面
next((vm)=>{})仅在beforeRouterEnter中可用,vm是组件实例
在钩子函数中使用异步请求:
1、安装Axios
npm install --save axios vue-axios
2、main.js引用axios
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios,axios)
3、存放数据:static/mock/data.json
{
"name":"丘桔",
"url": "http://baidu.com",
"page": 1,
"isNonProfit":true,
"address": {
"street": "昌平",
"city": "北京",
"country": "中国"
},
"links": [
{
"name": "B站",
"url": "https://www.bilibili.com/"
},
{
"name": "百度",
"url": "https://www.baidu.com/"
},
{
"name": "4399",
"url": "https://www.4399.com/"
}
]
}
4、在beforeRouteEnter中异步请求
<script>
export default {
name: "UserProfile.vue",
//可以想象为java里面的过滤器
beforeRouteEnter: (to, from, next)=>{
console.log("进入路由之前");
//要求:在进入路由之前加载数据
next(vm => {
vm.getData();
});
},
beforeRouteLeave: (to, from, next)=>{
console.log("离开路由之前");
next();
},
methods: {
getData: function(){
this.axios({
method: 'get',
url: 'http://localhost:8080/static/mock/data.json'
}).then(function(response){
console.log(response)
})
}
}
}
</script>