Soc:
HTML + CSS + JS :视图(给用户看,刷新后台给的数据)
网络通信:axios
页面跳转:vue - router
状态管理:vuex
Vue-UI:ICE
M:模型 V:视图 C:控制器
View:JSP{{}}
DATA:数据
vm:数据双向绑定
虚拟Dom:利用内存
计算属性--> Vue特色
集大成者:MVVM + Dom
为什么要使用MVVM?
MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大好处
- 低耦合
- 可复用
- 独立开发
- 可测试
大前端时代:后端:分布式、微服务、大数据
初始Vue
第一个Vue程序
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
{{message}}
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
message:"hello ,vue",
}
});
</script>
</body>
</html>
v-bind
将这个元素节点的特性和Vue实例的data属性保持一致。
java
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
<span v-bind:title="message">
鼠标悬停几秒
</span>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
message:"hello ,vue",
}
});
</script>
</body>
</html>
判断 循环
if
java
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
<h1 v-if="flag">Yes</h1>
<h1 v-else>No</h1>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
flag:true
}
});
</script>
</body>
</html>
v-else-if
html
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
<h1 v-if="flag==='A'">A</h1>
<h1 v-else-if="flag==='B'">B</h1>
<h1 v-else>C</h1>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
flag:'A'
}
});
</script>
</body>
</html>
for
html
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
<li v-for="(item,index) in item" :key="item.name">
{{index}}-->{{item.name}}
</li>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
item:[
{name:"zs"},
{anme:"ls"},
{name:"wangwu"}
]
}
});
</script>
</body>
</html>
事件(v-on)
html
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
<button v-on:click="hello">点我</button>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
msg:"hello youyuan"
},
methods:{
hello:function(){//方法必须定义在Vue的Methods对象中
alert(this.msg);
}
}
});
</script>
</body>
</html>
双向绑定
Vue.js是一个MVVM框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。
单选框
html
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
性别:
<input type="radio" name="sex" value="男" v-model="youyuan">男
<input type="radio" name="sex" value="女" v-model="youyuan">女
<p>
选中了:{{youyuan}}
</p>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
youyuan:"",
},
});
</script>
</body>
</html>
下拉框
html
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
下拉框:
<select name="za" id="za" v-model="select">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
选中的值:{{select}}
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
select:"",
},
});
</script>
</body>
</html>
Vue组件
自定义组件
html
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
<!--组件:传递给组件中的值:props-->
<youyuan v-for="item in items" v-bind:item="item"></youyuan>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
//定义一个Vue组件Component
Vue.component("youyuan",{
props: ['item'],
template: '<li>{{item}}</li>'
});
var vm = new Vue({
el:"#app",
data:{
items:["zs","ls","ww"]
},
});
</script>
</body>
</html>
Axios
JSON
json
{
"employees": [
{
"firstName": "Bill",
"lastName": "Gates"
},
{
"firstName": "George",
"lastName": "Bush"
},
{
"firstName": "Thomas",
"lastName": "Carter"
}
]
}
html
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
<li v-for="(employee,index) in data.employees">
{{employee.firstName}}-->{{employee.lastName}}
</li>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
var vm = new Vue({
el:"#app",
//data: 属性:vm
data(){
return{
//请求的返回参数合适,必须和JSON字符串一样
data:{
employees:[
{firstName:'',lastName:''},
{firstName:'',lastName:''},
{firstName:'',lastName:''},
]
},
}
},
mounted(){//钩子函数 链式编程 ES6新特性
const _this = this;
axios.get('data.json').then(data=>{
_this.data = data.data;
});
}
});
</script>
</body>
</html>
计算属性
计算出来的结果,保存在内存里面。调用方法时,每次都需要进行计算,既然有计算过程则必定产生开销,如果这个结果是不经常变化的,此时就可以考虑将这个结果进行缓存,以节约我们的系统开销。
html
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
<p>{{currentTime1()}}</p>
<p>{{currentTime2}}</p>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
message:"hello ,vue",
},
methods: {
currentTime1:function () {
return Date.now();//返回一个时间戳
}
},
computed:{ //计算机属性:methods ,computed 方法名不能重名,重名之后,只会调用methods的方法
currentTime2:function () {
return Date.now();
}
}
});
</script>
</body>
</html>
插槽
一个组件可以放在另一个组件里面。
html
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
<todo>
<todo-title slot="todo-title" :title="title"></todo-title>
<todo-title slot="todo-title" :title="title"></todo-title>
<todo-items slot="todo-items" v-for="item in todoItems" :item="item"></todo-items>
</todo>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
//slot:插槽
Vue.component("todo",{
template: '<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
Vue.component("todo-items",{
props:['item'],
template: '<div>{{item}}</div>'
});
Vue.component("todo-title",{
props:['title'],
template: '<div>{{title}}</div>'
});
var vm = new Vue({
el:"#app",
data:{
title:'youyuan',
todoItems:['youyuan1','youyuan2','youyuan3']
}
});
</script>
</body>
</html>
自定义事件分发
数据项在Vue实例中,但删除操作要在组件中完成,那么组件如何才能删除Vue实例中的数据,此时就涉及到了参数传递与事件分发了,Vue为我们提供了自定义事件的功能很好的解决了这个问题:使用this.$emit(''自定义事件名",参数)。
html
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层模板-->
<div id="app">
<todo>
<todo-title slot="todo-title" :item="title"></todo-title>
<todo-items slot="todo-items" v-for="(item,index) in todoItems"
:index="index" :item="item" v-on:remove="remove(index)" :key="index"></todo-items>
</todo>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
//slot:插槽
Vue.component("todo",{
template: '<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
Vue.component("todo-title",{
props:['item'],
template: '<div>{{item}}</div>'
});
Vue.component("todo-items",{
props:['title','index'],
template: '<div>{{index}}--->{{title}}<button @click="remove">删除</button></div>',
methods:{
remove(index){
//this.$emit 自定义事件分发
this.$emit('remove',index);
}
}
});
var vm = new Vue({
el:"#app",
data:{
title:'youyuan',
todoItems:['youyuan1','youyuan2','youyuan3']
},
methods: {
remove(index){
this.todoItems.splice(index,1);
}
}
});
</script>
</body>
</html>
Vue 入门小结
核心:数据驱动,组件化。
优点:借鉴了AngulaJS的模块化开发 和 React的虚拟Dom,虚拟Dom就是把Dom操作放到内存中执行。
常用属性:
- v-if
- v-else-if
- v-else
- v-for
- v-on绑定事件,简写@
- v-model 数据双向绑定
- v-bind 给组件绑定参数:简写:
- 组件化
- 组合组件slot插槽
- 组件内部绑定事件需要使用到this.$emit("事件名",参数);
- 计算属性的特色,缓存计算数据
遵顼SoC关注度分离原则,Vue是纯粹的视图框架。
说明:Vue的开发都是要基于Node.js,实际开发采用vue-cli脚手架开发,vue-router路由,vuex做状态管理,Vue UI界面一般使用Element来快速搭建前端项目。
Vue-cli
1、安装node.js环境
2、安装Node.js淘宝镜像加速器
3、安装vue -cli
创建Vue项目
1、vue ui
2、
3、
4、
5、
6、不保存模板。
Webpack
Webpack是一款模块加载器兼打包工具,它能把各种资源,如JS、JSX、ES、SASS图片等都作为模块来处理和使用。
安装
shell
npm install webpack -g
npm install webpack-cli -g
测试
shell
webpack -v
webpack -cli -v
配置
创建webpack.config.js配置文件
- entry :入口文件,指定Webpack 用哪个文件作为项目的入口
- output:输出,指定WebPack把处理完成的文件放置到指定路径
- module:模块,用于处理各种类型的文件
- plugins:插件,如:热更新、代码重用等。
- resolve:设置路径指向
- watch:监听,用于设置文件改动后直接打包。
使用Webpack
1、创建项目
2、创建一个名为modules的目录,用于放置JS模块等资源文件。
3、在modules下创建模块文件,如hello.js,用于编写JS模块相关代码。
javascript
//暴露一个方法
exports.sayHello = function () {
document.write("<h1>youyuan</h1>");
}
4、在modules下创建一个名为mian.js的入口文件,用于打包时设置entrty属性。
javascript
var hello = require("./hello");
hello.sayHello();
5、在项目目录下创建webpack.config.js配置文件,使用webpack命令打包。
javascript
module.expotrs = {
entry:'./modules/main.js',
output:{
filename:'./js/bundle.js'
}
};
ES6模板
ES6模块的设计思想,是尽量静态化,使编译就能确定模块的依赖关系,以及输入和输出的变量。
Vue Router
路由导航
当前router的名称
javascript
this.$router.currentRoute.name
element UI
看文档
参数传递与重定向
参数传递
name传组件名, params传递参数,需要对象,v-bind
vue
<router-link :to="'/blog/'+ blog.id">
<h2 v-rainbos>{{blog.title | touppercase}}</h2>
</router-link>
路由:
javascript
{
path:'/blog/:id',
name:'SingleBlog',
component:SingleBlog
}
接收:
javascript
data(){
return{
id:this.$route.params.id,
}
},
重定向
javascript
{
path:'/goHome',
redirect:'/mian'
}
404和路由钩子
vue
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'Home',
props:['id'],
beforeRouteEnter:(to,from,next)=>{
},
beforeRouteLeave:(to,from,next)=>{
},
components: {
HelloWorld
}
}
</script>
- to:路由将要跳转的路径信息
- from:路径跳转前的路径信息
- next:路径的控制参数
- next() 跳入下一个页面
- next('/path') 改变路由的跳转方向,使其跳转到另一个路由
- next(false) 返回原来的页面
- next((vm)=>{}) 仅在beforeRouteEnter中可用,vm提供组件实例
自定义指令
<div v-theme:column="'narrow'" id="show-blogs">
</div>
javascript
//自定义指令
Vue.directive('theme',{
bind(el,binding,vNode){
if (binding.value == 'wide'){
el.style.maxWidth = '1260px';
}else if (binding.value == 'narrow'){
el.style.maxWidth = '560px';
}
if (binding.arg == 'column'){
el.style.background = "#6677cc";
el.style.padding = '10px';
}
}
})
过滤器
vue
{{blog.context | snippet}}
javascript
Vue.filter('snippet',function (value) {
return value.slice(0,50)+'......';
})
局部过滤器与指令
javascript
filters:{
touppercase(value){
return value.toUpperCase();
}
},
//局部自定义组件
directives:{
'rainbos':{
vbind(el,binding,vNode){
el.style.color = '#'+Math.random().toString(16).slice(2,8);
}
}
},
分页
vue
<el-pagination
background
small
layout="prev, pager, next"
:page-size="pageSize"
:total="total"
@current-change="page" >
</el-pagination>
javascript
created() {
const _this = this ;
_this.$axios.get("/blogs/1/3").then(function (data) {
_this.blogs = data.data.tableData;
_this.total = data.data.total;
_this.pageSize = 3;
});
},
methods:{
page(currentPage){
const _this = this;
_this.$axios.get('/blogs/'+(currentPage)+'/3').then(function (data) {
console.log(data);
_this.blogs = data.data.tableData;
_this.total = data.data.total;
_this.pageSize = 3;
});
}
},
选择dom元素
起别名:ref = " 表单的名字 "
使用:
vue
this.$refs["loginForm"].validate((valid) => {
}
this.
Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
在整个所有vue文件都能访问,相互调用,数据共享,安全。
创建vuex:index.js
javascript
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
/***
* 声明变量*/
state: {
//存放菜单项
routes:[],
},
mutations: {
//将查询出来的菜单项赋值给state
initRoutes(state,data){
state.routes = data;
}
},
// actions: {
// },
modules: {
},
})
在main.js中导入vuex
javascript
import store from './store'
new Vue({
router,
store, //导入store
render: h => h(App)
}).$mount('#app')
请求菜单数据
javascript
import {getRequest} from "./api";
/*菜单初始化的方法*/
export const initMenu = (router, store) => {
/**
* 判断是否有菜单数据
* 如果有 直接返回
* */
if (store.state.routes.length > 0) {
return;
}
/*获取请求*/
getRequest("/system/config/menu").then(data => {
if (data) {
/*将数据中Component字符串转换为对象*/
let fmtRoutes = formatRoutes(data);
//放入router路由中
router.addRoutes(fmtRoutes);
//vuex初始化数据
store.commit('initRoutes',fmtRoutes);
//store.dispatch('connect');
}
})
}
export const formatRoutes = (routes) => {
/*返回的数据*/
let fmRoutes = [];
/**
* 遍历
* p量的定义变量(赋值)*/
routes.forEach(router => {
let {
path,
component,
name,
meta,
iconcls,
children
} = router;
//如果有children子节点
if (children && children instanceof Array) {
/*递归调用*/
children = formatRoutes(children);
}
let fmRouter = {
path: path,
name: name,
iconcls: iconcls,
meta: meta,
children: children,
/**
* 动态导入Component组件
* */
component:resolve => {
if (component.startsWith("Home")) {
require(['../views/' + component + '.vue'], resolve);
} else if (component.startsWith("Emp")) {
require(['../views/emp/' + component + '.vue'], resolve);
} else if (component.startsWith("Per")) {
require(['../views/per/' + component + '.vue'], resolve);
} else if (component.startsWith("Sal")) {
require(['../views/sal/' + component + '.vue'], resolve);
} else if (component.startsWith("Sta")) {
require(['../views/sta/' + component + '.vue'], resolve);
} else if (component.startsWith("Sys")) {
require(['../views/sys/' + component + '.vue'], resolve);
}
}
}
fmRoutes.push(fmRouter);
});
return fmRoutes;
}
状态管理模式
- state,驱动应用的数据源;
- view ,以声明方式将 state 映射到视图;
- actions ,响应在 view 上的用户输入导致的状态变化。
但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
- 多个视图依赖于同一状态。
- 来自不同视图的行为需要变更同一状态。
导航守卫
main.js
javascript
router.beforeEach((to, from, next) => {
/**
* to:从哪来
* from:去哪里
* next:方法名,继续往下走
* 菜单页面的初始化
* **/
if (to.path == '/') {
next();
} else {
if (window.sessionStorage.getItem("user")) {
//加载菜单项
initMenu(router, store);
next();
} else {
next('/?redirect=' + to.path);
}
}
})
在页面中引用组件
引用:
javascript
import DepMana from '../../components/sys/basic/DepMana'
import PosMana from '../../components/sys/basic/PosMana'
import JobLevelMana from '../../components/sys/basic/JobLevelMana'
import EcMana from '../../components/sys/basic/EcMana'
import PermissMana from '../../components/sys/basic/PermissMana'
export default {
name: "SysBasic",
data(){
return{
activeName: 'depmana'
}
},
//注册组件,key/value形式,若相同写一个即可。
components:{
DepMana,
PosMana,
JobLevelMana,
EcMana,
PermissMana
}
}
使用:
vue
<template>
<div>
<el-tabs v-model="activeName" type="card">
<el-tab-pane label="部门管理" name="depmana"><DepMana></DepMana></el-tab-pane>
<el-tab-pane label="职位管理" name="posmana"><PosMana></PosMana></el-tab-pane>
<el-tab-pane label="职称管理" name="joblevelmana"><JobLevelMana></JobLevelMana></el-tab-pane>
<el-tab-pane label="奖惩规则" name="ecmana"><EcMana></EcMana></el-tab-pane>
<el-tab-pane label="权限组" name="permissmana"><PermissMana></PermissMana></el-tab-pane>
</el-tabs>
</div>
</template>