概述
前端三要素
-
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
UI框架
Ant-Design:阿里巴巴出品,基于React的UI框架
ElementUl,iview, ice:饿了么出品,基于Vue的UI框架
Bootstrap:Twitter推出的一个用于前端开发的开源工具包
AmazeUl:又叫"妹子U1" ,一款HTML5跨屏前端框架
Vue.js
iView
iview是一个强大的基于Vue的UI库,有很多实用的基础组件比elementui的组件更丰富,主要服务于PC界面的中后台产品。使用单文件的Vue组件化开发模式基于npm + webpack +babel开发,支持ES2015高质量、功能丰富友好的API ,自由灵活地使用空间。
ElementUI
Element是饿了么前端开源维护的Vue UI组件库,组件齐全,基本涵盖后台所需的所有组件,文档讲解详细,例子也很丰富。主要用于开发PC端的页面,是一个质量比较高的Vue UI组件库。
了解前后分离的演变史
MVC时代
为了降低开发的复杂度, 以后端为出发点, 比如:Struts、Spring MVC等框架的使用, 就是后端的MVC时代;
以SpringMVC
流程为例。
发起请求到前端控制器(Dispatcher Servlet)
前端控制器请求HandlerMapping查找Handler,可以根据xml配置、注解进行查找
处理器映射器HandlerMapping向前端控制器返回Handler
前端控制器调用处理器适配器去执行Handler
处理器适配器去执行Handler
Handler执行完成给适配器返回ModelAndView
处理器适配器向前端控制器返回ModelAndView,ModelAndView是SpringMvc框架的一个底层对象,包括Model和View
前端控制器请求视图解析器去进行视图解析,根据逻辑视图名解析成真正的视图(JSP)
视图解析器向前端控制器返回View
前端控制器进行视图渲染,视图渲染将模型数据(在ModelAndView对象中)填充到request域
前端控制器向用户响应结果
基于AJAX带来的SPA时代
时间回到2005年A OAX(Asynchronous JavaScript And XML, 异步JavaScript和XML,老技术新用法)被正式提出并开始使用CDN作为静态资源存储, 于是出现了JavaScript王者归来(在这之前JS都是用来在网页上贴狗皮膏药广告的) 的SPA(Single Page Application) 单页面应用时代。
前端为主的MV*时代
MVC(同步通信为主) :Model、View、Controller
MVP(异步通信为主) :Model、View、Presenter
MVVM(异步通信为主):Model、View、View Model为了降低前端开发复杂度,涌现了大量的前端框架,比如:Angular JS、React、Vue.js、Ember JS等, 这些框架总的原则是先按类型分层, 比如Templates、Controllers、Models, 然后再在层内做切分,如下图:
第一个Vue程序
什么是MVVM
MVVM源自于经典的MVC(Model-View-Controller)模式。MVVM的核心是ViewModel层,负责转换Model中的数据对象来让数据变得更容易管理和使用。
- 该层向上与视图层进行双向数据绑定
- 向下与Model层通过接口请求进行数据交互
为什么要使用MVVM
低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可复用:你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewMode),设计人员可以专注于页面设计。
可测试:界面素来是比较难以测试的,而现在测试可以针对ViewModel来写。
View
View是视图层, 也就是用户界面。前端主要由HTH L和csS来构建, 为了更方便地展现vi eu to del或者Hodel层的数据, 已经产生了各种各样的前后端模板语言, 比如FreeMarker,Thyme leaf等等, 各大MV VM框架如Vue.js.Angular JS, EJS等也都有自己用来构建用户界面的内置模板语言。
Model
Model是指数据模型, 泛指后端进行的各种业务逻辑处理和数据操控, 主要围绕数据库系统展开。这里的难点主要在于需要和前端约定统一的接口规则
ViewModel
ViewModel是由前端开发人员组织生成和维护的视图数据层。在这一层, 前端开发者对从后端获取的Model数据进行转换处理, 做二次封装, 以生成符合View层使用预期的视图数据模型。
需要注意的是View Model所封装出来的数据模型包括视图的状态和行为两部分, 而Model层的数据模型是只包含状态的
比如页面的这一块展示什么,那一块展示什么这些都属于视图状态(展示)
页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互)
视图状态和行为都封装在了View Model里。这样的封装使得View Model可以完整地去描述View层。由于实现了双向绑定, View Model的内容会实时展现在View层, 这是激动人心的, 因为前端开发者再也不必低效又麻烦地通过操纵DOM去更新视图。
MVVM框架已经把最脏最累的一块做好了, 我们开发者只需要处理和维护View Model, 更新数据视图就会自动得到相应更新,真正实现事件驱动编程。
View层展现的不是Model层的数据, 而是ViewModel的数据, 由ViewModel负责与Model层交互, 这就完全解耦了View层和Model层, 这个解耦是至关重要的, 它是前后端分离方案实施的重要一环。
Vue
Vue(读音/vju/, 类似于view) 是一套用于构建用户界面的渐进式框架, 发布于2014年2月。与其它大型框架不同的是, Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层, 不仅易于上手, 还便于与第三方库(如:vue-router,vue-resource,vue x) 或既有项目整合。
MVVM模式的实现者
Model:模型层, 在这里表示JavaScript对象
View:视图层, 在这里表示DOM(HTML操作的元素)
ViewModel:连接视图和数据的中间件, Vue.js就是MVVM中的View Model层的实现者
在MVVM架构中, 是不允许数据和视图直接通信的, 只能通过ViewModel来通信, 而View Model就是定义了一个Observer观察者
ViewModel能够观察到数据的变化, 并对视图对应的内容进行更新
ViewModel能够监听到视图的变化, 并能够通知数据发生改变
至此, 我们就明白了, Vue.js就是一个MV VM的实现者, 他的核心就是实现了DOM监听与数据绑定
为什么要使用Vue.js
轻量级, 体积小是一个重要指标。Vue.js压缩后有只有20多kb(Angular压缩后56kb+,React压缩后44kb+)
移动优先。更适合移动端, 比如移动端的Touch事件
易上手,学习曲线平稳,文档齐全
吸取了Angular(模块化) 和React(虚拟DOM) 的长处, 并拥有自己独特的功能,如:计算属性
开源,社区活跃度高
第一个Vue程序
<!DOCTYPE HTML>
<HTML lang="en">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
</head>
<body>
<div id="app">{{message}}</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
var app = new Vue({el:'#app',data:{message:"Hello World"}});
</script>
</HTML>
基础语法指令
v-bind
<!DOCTYPE HTML>
<HTML lang="en">
<head>
<meta charset="UTF-8">
<title>v-bind</title>
</head>
<body>
<div id="app">
<span v-bind:title="message">
过来过来过来!!!!!!!!
</span>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
var app = new Vue({el:'#app',data:{message:'Hello World'+ new Date().toLocaleString()}});
</script>
</HTML>
v-if,v-else
<!DOCTYPE HTML>
<HTML lang="en">
<head>
<meta charset="UTF-8">
<title>v-if##v-else</title>
</head>
<body>
<div id="app">
<h1 v-if="ok">Yes</h1>
<h2 v-else>No</h2>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
var app = new Vue({el:'#app',data:{ok:true}});
</script>
</HTML>
v-for
<!DOCTYPE HTML>
<HTML lang="en">
<head>
<meta charset="UTF-8">
<title>v-for</title>
</head>
<body>
<div id="app">
<li v-for="(item,index) in items">
{{item.message}}----{{index}}
</li>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
var app = new Vue({el:'#app',data:{items:[{message:'白日依山尽'},{message:'黄河入海流'},{message:'欲穷千里目'},{message:'更上一层楼'}]}});
</script>
</HTML>
v-on
<!DOCTYPE HTML>
<HTML lang="en">
<head>
<meta charset="UTF-8">
<title>v-on</title>
</head>
<body>
<div id="app">
<button v-on:click="hello">按钮</button>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
var app = new Vue({el:'#app',data:{message:'hello world'},methods:{hello:function(event){alert(this.message)}}});
</script>
</HTML>
v-model
<!DOCTYPE HTML>
<HTML lang="en">
<head>
<meta charset="UTF-8">
<title>v-model</title>
</head>
<body>
<div id="app">
请输入的文本: <input type="text" v-model="message" value="hello"/>{{message}}
</div>
<div id="app1">
多行文本: <textarea v-model="message"></textarea> {{message}}
</div>
<div id="app2">
单复选框:
<input type="checkbox" id="checkbox" v-model="checked"/>
<label for="checkbox">{{checked}}</label>
</div>
<div id="app3">
多复选框:
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="join" value="Join" v-model="checkedNames">
<label for="join">Join</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<span>选中的值:{{checkedNames}}</span>
</div>
<div id="app4">
单选框按钮
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<span>选中的值:{{picked}}</span>
</div>
<div id="app5">
下拉框:
<select v-model="pan">
<option value="" disabled>---请选择---</option>
<option>A</option>
<option>B</option>
<option>C</option>
<option>D</option>
</select>
<span>value:{{pan}}</span>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
var app = new Vue({el:'#app',data:{message:""}});
new Vue({el:'#app1',data:{message:"白日依山尽,黄河入海流。欲穷千里目,更上一层楼。"}});
new Vue({el:'#app2',data:{checked:true}})
new Vue({el:'#app3',data:{checkedNames:[]}})
new Vue({el:'#app4',data:{picked:'Two'}})
new Vue({el:'#app5',data:{pan:''}})
</script>
</HTML>
component
<!DOCTYPE HTML>
<HTML lang="en">
<head>
<meta charset="UTF-8">
<title>component</title>
</head>
<body>
<!-- <div id="app">
<pan></pan>
</div> -->
<div id="app1">
<pan v-for="item in items" v-bind:panh="item"></pan>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
Vue.component("pan",{props:["panh"],template:'<li>{{panh}}</li>'});
// new Vue({el:"#app"});
new Vue({el:"#app1",data:{items:["java","c++","c#","go"]}})
</script>
</HTML>
axios
<!DOCTYPE HTML>
<HTML lang="en">
<head>
<meta charset="UTF-8">
<title>axios</title>
</head>
<body>
<style>
/* 解决闪烁问题 */
/* [v-cloak]{
display: none;
} */
</style>
<div id="app">
<div>姓名: {{info.name}}</div>
<div>地址:{{info.address.country}}--{{info.address.city}}--{{info.address.street}}</div>
<div>连接:<a v-bind:href="info.url" target="_bank">{{info.url}}</a></div>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script type="text/javascript">
new Vue({el:"#app",data(){return{info:{name:null,address:{country:null,city:null,street:null},url:null}}},mounted(){axios.get('http://172.30.3.216:3000/mock/465/get').then(response=>(this.info=response.data))}});
</script>
</HTML>
{
"name": "狂神说Java",
"url": "https://blog.kuangstudy.com",
"page": 1,
"isNonProfit": true,
"address": {
"street": "含光门",
"city": "陕西西安",
"country": "中国"
},
"links": [
{
"name": "bilibili",
"url": "https://space.bilibili.com/95256449"
},
{
"name": "狂神说Java",
"url": "https://blog.kuangstudy.com"
},
{
"name": "百度",
"url": "https://www.baidu.com/"
}
]
}
computed
<!DOCTYPE HTML>
<HTML lang="en">
<head>
<meta charset="UTF-8">
<title>computed</title>
</head>
<body>
<div id="app">
<div>time:{{time()}}</div>
<div>time1:{{time1}}</div>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script type="text/javascript">
var vm = new Vue({el:"#app",data:{message:'11'},mounted(){},methods:{time:function(){return Date.now()}},computed:{time1:function(){ this.message; return Date.now();}}});
</script>
</HTML>
slot
<!DOCTYPE HTML>
<HTML lang="en">
<head>
<meta charset="UTF-8">
<title>slot</title>
</head>
<body>
<div id="app">
<todo>
<todo-title slot="todo-title" :title="title"></todo-title>
<todo-input slot="todo-input" v-on:add="add_method"></todo-input>
<todo-items slot="todo-items" v-for="(item,index) in todoitems" :item="item" :index="index" v-on:remove="remove_method(index)" :key="index"></todo-items>
</todo>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
Vue.component("todo",{template:'<div>\
<slot name="todo-title"></slot>\
<slot name="todo-input"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'});
Vue.component('todo-title',{props:["title"],template:'<div>{{title}}</div>'});
//仿写插入
Vue.component('todo-input',{
template:'<div><form>\
请输入: <input type="text" v-model="arr" value="请输入"><br>\
<button type="button"@click="add_component">提交</button>\
</div>',
props:["arr"],
methods:{
add_component:function(){this.$emit('add',this.arr);}}
})
Vue.component('todo-items',{
props:["item","index"],
template:"<li>{{index+1}},{{item}} <button @click='remove_component'>删除</button></li>",
methods:{remove_component:function(index){this.$emit('remove',index);}}
})
var vue = new Vue({
el:"#app",
data:{title:"登鹳雀楼",todoitems:['白日依山尽,','黄河入海流.','欲穷千里目,','更上一层楼.'],arr:''},
methods:{
remove_method:function(index){
console.log("删除了"+this.todoitems[index]+"OK");
this.todoitems.splice(index,1);
},
add_method:function(arr){
console.log("添加元素"+arr);
this.todoitems.splice(this.todoitems.size,0,arr);
}
}
})
</script>
</HTML>
Webpack
npm install webpack
vue-router
npm install vue-router --save-dev
项目
创建项目
vue init webpack hello-vue
#进入工程目录
cd hello-vue
#安装vue-routern
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
sass-loader node-sass 老是安装失败,后来发现和node版本有关系, 解决办法:
PS C:\代码\vue> node -v
v14.17.1
安装版本为:
"node-sass": "^4.14.1",
"sass-loader": "^7.3.1",
目录结构
- assets:用于存放资源文件
- components:用于存放Vue功能组件
- views:用于存放Vue视图组件
- router:用于存放vue-router配置
代码
views/user/User.vue
<template>
<div>
<h1>个人信息</h1>
{{$route.params.id}}
</div>
</template>
<script>
export default {
name: "User",
beforeRouteEnter: (to, from, next) => {
console.log("准备进入个人信息页");
next(vm => {
//进入路由之前执行getData方法
vm.getData()
});
},
beforeRouteLeave: (to, from, next) => {
console.log("准备离开个人信息页");
next();
},
methods:{
getData: function(){
this.axios({
method: 'get',
url: 'http://localhost:8080/static/user.json'
}).then(function (response) {
console.log(response)
})
}
}
}
</script>
<style scoped>
</style>
views/user/UserList.vue
<template>
<div>
<h1>用户列表</h1>
{{id}}
</div>
</template>
<script>
export default {
name: "UserList",
props: ['id']
}
</script>
<style scoped>
</style>
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="dialogVisible" 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 lang="scss" 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>
views/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/user">个人信息</router-link> -->
<router-link :to="{name:'User',params:{id:1}}">个人信息</router-link>
</el-menu-item>
<el-menu-item index="1-2">
<!--插入的地方-->
<!-- <router-link to="/user/userlist">用户列表</router-link> -->
<router-link :to="{name:'UserList',params:{id:222}}">用户列表</router-link>
</el-menu-item>
<el-menu-item index="1-3">
<router-link to="/goHome">回到首页</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 />
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped lang="scss">
.el-header {
background-color: #B3C0D1;
color: #333;
line-height: 60px;
}
.el-aside {
color: #333;
}
</style>
views/NotFound.vue
<template>
<div>
<h1>404,你的页面走丢了</h1>
</div>
</template>
<script>
export default {
name: "NotFound"
}
</script>
<style scoped>
</style>
router/index.js
import Vue from'vue'
//导入路由插件
import Router from 'vue-router'
//导入上面定义的组件
import Main from "../views/Main";
import Login from "../views/Login";
import User from "../views/user/User";
import UserList from "../views/user/UserList";
import NotFound from '../views/NotFound'
//安装路由
Vue.use(Router) ;
//配置路由
export default new Router({
//路径不带 # 符号
mode: 'history',
routes:[
{
//路由路径
path:'/main',
//路由名称
name:'main',
children: [
{
// path: '/user/user/',
path: '/user/user/:id',
name: 'User',
component: User,
}, {
path: '/user/userlist/:id',
name: 'UserList',
component: UserList,
props: true
},
],
//跳转到组件
component:Main
},{
//路由路径
path:'/',
//路由名称
name:'login',
//跳转到组件
component:Login
},{
path: '/goHome',
redirect: '/main'
},{
path: '*',
component: NotFound
}
]
});
main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
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'
import axios from 'axios'
import VueAxios from 'vue-axios'
// Vue.config.productionTip = false
Vue.use(router);
Vue.use(ElementUI);
Vue.use(VueAxios, axios);
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render:h=>h(App)
})