css模块
1.实现已知或未知宽度的垂直水平居中
(1)margin:auto法
css:
div{
width: 400px;
height: 400px;
position: relative;
border: 1px solid #465468;
}
img{
position: absolute;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
html:
<div>
<img src="mm.jpg">
</div>
(2)margin负值法
.container{
width: 500px;
height: 400px;
border: 2px solid #379;
position: relative;
}
.inner{
width: 480px;
height: 380px;
background-color: #746;
position: absolute;
top: 50%;
left: 50%;
margin-top: -190px; /*height的一半*/
margin-left: -240px; /*width的一半*/
}
补充:其实这里也可以将marin-top和margin-left负值替换成, transform:translateX(-50%)和transform:translateY(-50%)
(3)table-cell(未脱离文档流的)
设置父元素的display:table-cell,并且vertical-align:middle,这样子元素可以实现垂直居中。
css:
div{
width: 300px;
height: 300px;
border: 3px solid #555;
display: table-cell;
vertical-align: middle;
text-align: center;
}
img{
vertical-align: middle;
}
(4)利用flex
将父元素设置为display:flex,并且设置align-items:center;justify-content:center;
css:
.container{
width: 300px;
height: 200px;
border: 3px solid #546461;
display: -webkit-flex;
display: flex;
-webkit-align-items: center;
align-items: center;
-webkit-justify-content: center;
justify-content: center;
}
.inner{
border: 3px solid #458761;
padding: 20px;
}
(5)
.container {
position:relative;
}
.inner {
position:absolute;
top:50%;
transform:translateY(-50%);
}
2.CSS渲染原理
3.CSS3 动画卡顿解决方案
4.positon都有什么属性
- static : 可以认为静态的,默认元素都是静态的定位,对象遵循常规流。此时4个定位偏移属性不会被应用,也就是使用left,right,bottom,top将不会生效。
- relative: 相对定位,对象遵循常规流,并且参照自身在常规流中的位置通过top,right,bottom,left这4个定位偏移属性进行偏移时不会影响常规流中的任何元素。
- absolute:
a、绝对定位,对象脱离常规流,此时偏移属性参照的是离自身最近的定位祖先元素,如果没有定位的祖先元素,则一直回溯到body元素。盒子的偏移位置不影响常规流中的任何元素,其margin不与其他任何margin折叠。
b、元素定位参考的是离自身最近的定位祖先元素,要满足两个条件,第一个是自己的祖先元素,可以是父元素也可以是父元素的父元素,一直找,如果没有则选择body为对照对象。第二个条件是要求祖先元素必须定位,通俗说就是position的属性值为非static都行。
- fixed 固定定位,与absolute一致,但偏移定位是以窗口为参考。当出现滚动条时,对象不会随着滚动。
5. BFC模型
其全英文拼写为 Block Formatting Context 直译为“块级格式化上下文”
BFC的原理
- 内部的box会在垂直方向,一个接一个的放置
- 每个元素的margin box的左边,与包含块border
- box的左边相接触(对于从做往右的格式化,否则相反)
- box垂直方向的距离由margin决定,属于同一个bfc的两个相邻box的margin会发生重叠
- bfc的区域不会与浮动区域的box重叠
- bfc是一个页面上的独立的容器,外面的元素不会影响bfc里的元素,反过来,里面的也不会影响外面的
- 计算bfc高度的时候,浮动元素也会参与计算
怎么取创建bfc
- float属性不为none(脱离文档流)
- position为absolute或fixed
- display为inline-block,table-cell,table-caption,flex,inine-flex
- overflow不为visible
- 根元素
应用场景
- 自适应两栏布局
- 清除内部浮动
- 防止垂直margin重叠
6. 盒子模型
盒模型的组成大家肯定都懂,由里向外content,padding,border,margin.
盒模型是有两种标准的,一个是标准模型,一个是IE模型。
盒模型的宽高只是内容(content)的宽高,
而在IE模型中盒模型的宽高是内容(content)+填充(padding)+边框(border)的总宽高。
css如何设置两种模型
/* 标准模型 */
box-sizing:content-box;
/*IE模型*/
box-sizing:border-box;
7. 盒子塌陷(清除浮动)
1.盒子塌陷是什么?
本应该在父盒子内部的元素跑到了外部。
2.为什么会出现盒子塌陷
当父元素没设置足够大小的时候,而子元素设置了浮动的属性,子元素就会跳出父元素的边界(脱离文档流),尤其是当父元素的高度为auto时,而父元素中又没有其它非浮动的可见元素时,父盒子的高度就会直接塌陷为零, 我们称这是CSS高度塌陷。
3,关于盒子塌陷的几种解决方法
- 最简单,直接,粗暴的方法就是盒子大小写死,给每个盒子设定固定的width和height,直到合适为止,这样的好处是简单方便,兼容性好,适合只改动少量内容不涉及盒子排布的版面,缺点是非自适应,浏览器的窗口大小直接影响用户体验。
- 给外部的父盒子也添加浮动,让其也脱离标准文档流,这种方法方便,但是对页面的布局不是很友好,不易维护。
- 给父盒子添加overflow属性。 overflow:auto; 有可能出现滚动条,影响美观。 overflow:hidden; 可能会带来内容不可见的问题。
- 父盒子里最下方引入清除浮动块。最简单的有:
有很多人是这么解决的,但是我们并不推荐,因为其引入了不必要的冗余元素 。 - after伪类清除浮动。 外部盒子的after伪元素设置clear属性
#parent:after{
clear: both;
content: "";
width: 0;
height: 0;
display: block;
visibility: hidden;
}
8.常见的块级元素、行内元素都有什么
块级元素 div、p、h1~h6、ul、ol、dl、li、dd、table、hr、blockquote、address、table、menu、pre,HTML5新增的header、section、aside、footer 行内元素 span、img、a、lable、input、abbr(缩写)、em(强调)、big、cite(引用)、i(斜体)、q(短引用)、textarea、select、small、sub、sup,strong、u(下划线)、button(默认display:inline-block)
9.介绍一下rem、em
px
Pixel像素,相对长度单位。像素是相对于显示器分辨率而言。
em
相对长度单位,相对于当前对象内文本的字体尺寸,如当前对行内文本的字体尺寸未被设置,则相对于浏览器的默认字体尺寸。 任意浏览器的默认字体高都是16px。所有未经调整的浏览器都符合:1em= 16px。有时为简化font-size的换算,需要在css中的body选择器中声明font-size=62.5%,此时1em=16px*62.5%=10px,这样12px=1.2em,10px=1em,也就是说换算时只需将原有px数值除以10,然后换上em作为单位即可。
特点:
- 1.em 的值并不是固定的。
- 2.em的值会继承父级元素的字体大小。
<html>
<div>
<p> 你猜我字体是多大?</p>
</div>
</html>
/******css样式*********/
html{
font-size:62.5% // font-size:10px 1em:10px
}
div{
font-size:1.2em //font-size: 10 *1.2 =12px
}
p{
font-size:1.2em //font-size= 12*1.2=14.4px
}
rem
rem是CSS3新增的一个相对单位(root em,根em),这个单位与em有什么区别呢?区别在于使用rem为元素设定字体大小时,虽然仍是相对大小,但相对的只是HTML根元素。通过rem,既可以做到只修改根元素就成比例地调整所有字体大小,又可以避免字体大小逐层复合的连锁反应。 目前除IE8及更早版本外,所有浏览器均已支持rem。对于不支持的浏览器,可以多写一个绝对单位的声明。如此浏览器会忽略rem设定的字体大小。
p{
font-size:14px;
font-size:2rem;
}
注意: 1 若没有在根元素(html字体)指定参照值,那浏览器默认1 rem就是16px,若指定值,则1rem就是指定值 2 html设置为62.5%或者10px时会失效,是因为 小于12px或者75%的字体大小 不支持换算。这可能与有些浏览器不支持12px以下的大小有关。所以,使用rem单位,html的字体默认字体大小必须设置为12px或以上。若小于12px则浏览器换算时自动默认字体为12px。
10.实现多行文本换行,并且超出文字显示"..."
知识点介绍:
overflow:hidden; //超出的文本隐藏
text-overflow:ellipsis; //溢出用省略号显示
white-space:nowrap; //溢出不换行
display:-webkit-box; //将对象作为弹性伸缩盒子模型显示。
-webkit-box-orient:vertical; //从上到下垂直排列子元素(设置伸缩盒子的子元素排列方式)
-webkit-line-clamp:2; //这个属性不是css的规范属性,需要组合上面两个属性,表示显示的行数。
方法一
h2{
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}
方法二
<template>
<span :class="['moreText', textClassName]">
{{showContent}}
<span v-if="more"
:class="['more-toggle-button',buttonClass||'']"
style="cursor:pointer;"
@click="toggle">{{toggleButtonText}}</span>
</span>
</template>
<script>
export default {
name: 'MoreText',
props: {
content: {
type: String,
default: ''
},
length: {
type: Number,
default: 80,
description: '超过该长度即开启省略功能'
},
shortClass: {
type: String,
default: 'more-text-short'
},
fullClass: {
type: String,
default: 'more-text-full'
},
buttonClass: {
type: String,
default: 'pointer el-button--text'
}
},
data () {
return {
isFull: false
}
},
computed: {
textClassName () {
return this.isFull === true ? this.fullClass : this.shortClass;
},
more () {
return this.content.length > this.length;
},
showContent () {
return this.more && !this.isFull ? this.content.substring(0, this.length) + '...' : this.content;
},
toggleButtonText () {
return this.isFull ? this.$t("btn.收起") : this.$t("btn.展开");
}
},
methods: {
toggle () {
this.isFull = !this.isFull;
}
}
}
</script>
<MoreText
:length="120"
:content="profile"
buttonClass="pointer el-button--text"
></MoreText>
10-1.单行文本超出显示"..."
h1{
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
11.画出等腰三角形
.triangle {
width: 0;
height: 0;
border-left: 10px solid transparent;
/*border-top: 10px solid #5851c3;*/
border-right: 10px solid transparent;
border-bottom: 10px solid #4ed621;
box-sizing: border-box;
}
11.左边宽度固定200px,右边占满剩余的空间
法一:
<div>
<div class="left"></div>
<div class="right"></div>
</div>
<style>
.left {
width : 200px;
}
.right {
width : calc( 100% - 200px );
}
</style>
法二:
<!-- 左侧固定列 -->
<div class="fixedColumn"></div>
<!-- 右侧自适应宽度列 -->
<div class="flexibleColumn"></div>
<style>
/*左固定列*/
.fixedColumn{
width: 40px;
height: 100%;
background-color: red;
float: left;
/*position: absolute;
left: 0;*/
}
/*右自适应列*/
.flexibleColumn{
height: 100%;
background-color: blue;
margin-left: 40px;
}
</style>
注:
1)fixedColumn 里注释的方法即绝对定位的实现方式,取消注释后把float那句注释掉,可以实现相同的效果
2)使用float需要注意清除浮动造成父元素塌陷的问题(这里不用清除,因为自适应列和固定列一样高,在标准流中可以撑起父元素)
扩展
如果把上面的问题稍微改变一下,要求展示一个左中右布局,而且左右固定,中间自适应,这要如何实现呢?
<!-- 左侧固定列 -->
<div class="fixedColumn"></div>
<!-- 右侧固定列 -->
<div class="fixedColumn" style="float: right;"></div>
<!-- 中间自适应宽度列 -->
<div class="flexibleColumn"></div>
12.flex布局
developer.mozilla.org/zh-CN/docs/…
javascript模块
1.js有哪些类型
最新的 ECMAScript 标准定义了 8 种数据类型:
- 7 种原始类型: Boolean Null Undefined Number BigInt String Symbol
- 和 Object
2.谈谈你对闭包的理解
一句话可以概括:闭包就是能够读取其他函数内部变量的函数,或者子函数在外调用,子函数所在的父函数的作用域不会被释放。
2-1.都有哪些实现闭包的方式
3.jascript中如何检测一个变量是对象
Object.prototype.toString.call()方法可以精准判断变量类型,它返回[object constructorName]的字符串格式,这里的constructorName就是call参数的函数名
var a = NaN;
var b= '222';
var c = null;
var d = false;
var e = undefined;
var f = Symbol();
var arr = ['aa','bb','cc'];
var obj = {
'a': 'aa',
'b': 'bb',
'c': 'cc'
};
var res = Object.prototype.toString.call(arr);
console.log(res); //[object Array]
var res2 = Object.prototype.toString.call(obj);
console.log(res2); //[object Object]
var res3 = Object.prototype.toString.call(a);
console.log(res3); //[object Number]
var res4 = Object.prototype.toString.call(b);
console.log(res4); //[object String]
var res4 = Object.prototype.toString.call(c);
console.log(res4); //[object Null]
var res5 = Object.prototype.toString.call(d);
console.log(res5); //[object Boolean]
var res6 = Object.prototype.toString.call(e);
console.log(res6); //[object Undefined]
var res7 = Object.prototype.toString.call(f);
console.log(res7); //[object Symbol]
4.实现一个函数,判断输入是不是回文字符串
5-1.如何理解跨域
5.常用的跨域解决方案有哪些
一、哪种跨域常用?
淘宝、百度、网易云音乐,之类会配置公共的API,会是JSONP
公司内的话会选择window.name或者nginx反向代理
我们任务中会使用nginx.conf进行反向代理
二、从安全性而言选择那种跨域方式最好,为什么?
一般最安全的是WINDOW.NAME,因为iframe会销毁
三、JSONP的缺点 jsonp有个缺陷就是只能get 而且会把请求的内容发送到url中导致安全性极低
- JSONP是一种非正式传输协议,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
- JSONP原理:和是一致的
- 实现例子: www.cnblogs.com/tapt/p/6524…
客户端
$.ajax({
type: "get",
async:false,
url: "http://192.168.1.102:8080/carop/jsonp",
dataType: "jsonp",
jsonpCallback:"jsonpCallback",
success: function(data){
alert(data.name+"\n "+data.tel);
}
});
其他的ajax方法比如getjson亦可,写法上有区别,这里仅采用一种方法。
说明:jsonpCallback:"jsonpCallback",前一个ajax参数表示要执行的函数,后面的”jsonpCallback“,这个是服务器返回jsonp的javascript函数名。(网上有相关资料这个参数写的是jsonp而不是jsonpCallback,经实际测试要写成jsonpCallback,jquery版本1.8,所测试浏览器为火狐和edge)
服务端
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
String callback=request.getParameter("callback");
//测试用的json数据
Map<String,String> map=new HashMap<String, String>();
map.put("id", "id1");
map.put("name", "name1");
JSONObject jsonObject = JSONObject.fromObject(map);
String returnstr=callback+"("+jsonObject.toString()+")";
PrintWriter out = response.getWriter();
out.println(returnstr);
}
6.cookie、loacalstroage和sessionstorage的区别
共同点:都是保存在浏览器端,并且是同源的
- Cookie:cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下,存储的大小很小只有4K左右。 (key:可以在浏览器和服务器端来回传递,存储容量小,只有大约4K左右)
- sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持,localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。(key:本身就是一个回话过程,关闭浏览器后消失,session为一个回话,当页面不同即使是同一页面打开两次,也被视为同一次回话)
- localStorage:localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。(key:同源窗口都会共享,并且不会失效,不管窗口或者浏览器关闭与否都会始终生效)
补充说明一下cookie的作用:
- 保存用户登录状态。例如将用户id存储于一个cookie内,这样当用户下次访问该页面时就不需要重新登录了,现在很多论坛和社区都提供这样的功能。 cookie还可以设置过期时间,当超过时间期限后,cookie就会自动消失。因此,系统往往可以提示用户保持登录状态的时间:常见选项有一个月、三个 月、一年等。
- 跟踪用户行为。例如一个天气预报网站,能够根据用户选择的地区显示当地的天气情况。如果每次都需要选择所在地是烦琐的,当利用了 cookie后就会显得很人性化了,系统能够记住上一次访问的地区,当下次再打开该页面时,它就会自动显示上次用户所在地区的天气情况。因为一切都是在后 台完成,所以这样的页面就像为某个用户所定制的一样,使用起来非常方便
- 定制页面。如果网站提供了换肤或更换布局的功能,那么可以使用cookie来记录用户的选项,例如:背景色、分辨率等。当用户下次访问时,仍然可以保存上一次访问的界面风格。
7.浏览器的缓存机制
segmentfault.com/a/119000001… www.cnblogs.com/engeng/arti…
8.http no-cache作用
9.两种方式实现对象的深拷贝
10.new一个函数和直接调用函数的区别
不实用new,也就是普通的函数调用而已,所以若是函数本身没有返回值,普通的函数调用没有什么意义
如:
var person=new Person();//person是一个对象
var person = Person();//这只是一次普通的函数调用并赋值而已。
function Person(name,age){
this.name=name;
this.age=age;
this.sayName=function(){
alert(this.name);
};
}
//var person=new Person("张三",20); //此处为 构造对象,构造对象的话,返回的新对象是由解析器自己生成的。
var person=Person("张三",20); //假设我在Person函数里面加了return "你好"; 这时的person就不会报undefined,而是一个字符串你好
person.sayName();//报错 person undefined 此处为普通函数调用,又没有给定返回值,出错。
//因为此时this指向window对象,
window.sayName();//此时不会报错
接下来就问,为什么我赋值给person,可以用window来引用呢?
因为如果不用new就相当于普通函数调用,而 Person()根本没有返回值,
所以Person根本就没赋值给person,此时的person只是一个undefined,
但是Person却执行了一次,成为了window的对象,this指向了window,所以window可以直接使用Person的方法,
Person("张三",20);
person.sayName();
如果函数返回值为常规意义上的数值类型(Number、String、Boolean)时,new函数将会返回一个该函数的实例对象, 而如果函数返回一个引用类型(Object、Array、Function)时,则new函数与直接调用函数产生的结果相同。
javascript中new对象的过程:如var myObj = newPerson(“aty”,25);
- 1.创建一个空的Object对象.var obj = new Object();
- 2.将构造函数Person中this指向刚创建的obj对象
- 3.将创建的obj的__proto__指向构造函数Person的prototype。这一步是建立对象和原型直接的对应关系。firefox下通过
对象的__proto__属性能够访问到原型,IE下则没有暴露出相应的属性。
- 4.执行构造函数Person()中的代码
11.var、let、const区别
区别
- var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。声明会提升
- let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
- const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
12.从输入url到页面加载完成dom都发生了什么
13.服务器如何加载静态文件
14.常见的http状态码都有什么,分别代表什么
301,302,304
15.localStorage和cookie的跨域解决方案
16.angular,react,vue区别
17.svg,canvas区别,如何实现转换
18.继承有哪些实现方式,以及区别、缺点
19.ES6的箭头函数如何理解
20.ES6的拓展符号,去重
21.bind,call,apply区别
22.事件委托,好处
23.promise的本质是什么
23-1.promise有哪些状态
23-2.如何自己实现一个promise
24.介绍一下async,aswait.如何始终执行一个方法,不管报不报错
25.数组的常用操作方法,以及区别
26.数组去重的方法,复杂度是多少?如何实现高效的去重
26-1.如何找到数组中不重复的数字
27.自己定义一个方法,实现数组的排序
27-1.自己定义一个方法,实现数组的indexOf
28.判断两个对象是否完全相等
var obj = {a : 1};
JSON.stringify(obj) === JSON.stringify(obj);
29.怎么实现定义块级变量
利用闭包
30.如何实现读取CSV文件,并且解析为一个表格
31.将字符串中的一个字符全部删掉
(正则表达式法)
vue模块
源码解析 caibaojian.com/vue-analysi…
数据驱动
Vue.js 一个核心思想是数据驱动。所谓数据驱动,是指视图是由数据驱动生成的,我们对视图的修改,不会直接操作 DOM,而是通过修改数据。它相比我们传统的前端开发,如使用 jQuery 等前端库直接修改 DOM,大大简化了代码量。特别是当交互复杂的时候,只关心数据的修改会让代码的逻辑变的非常清晰,因为 DOM 变成了数据的映射,我们所有的逻辑都是对数据的修改,而不用碰触 DOM,这样的代码非常利于维护。
new Vue 发生了什么 合并配置,初始化生命周期,初始化事件中心,初始化渲染,初始化 data、props、computed、watcher 等等
1.什么是virturl Dom,有什么作用
- Virtual DOM 在牺牲部分性能的前提下,增加了可维护性,这也是很多框架的通性
- 实现了对DOM的集中化操作,在数据改变时先对虚拟DOM进行修改,再反映到真实的DOM中,用最小的代价来更新DOM,提高效率
- 打开了函数式UI编程的大门
- 可以渲染到DOM以外的端,比如ReactNative
总结: 其实 VNode 是对真实 DOM 的一种抽象描述,它的核心定义无非就几个关键属性,标签名、数据、子节点、键值等,其它属性都是都是用来扩展 VNode 的灵活性以及实现一些特殊 feature 的。由于 VNode 只是用来映射到真实 DOM 的渲染,不需要包含操作 DOM 的方法,因此它是非常轻量和简单的。
2.v-model原理
双向绑定除了数据驱动 DOM 外, DOM 的变化反过来影响数据,是一个双向关系,在 Vue 中,我们可以通过 v-model 来实现双向绑定。
v-model 即可以作用在普通表单元素上,又可以作用在组件上,它其实是一个语法糖
<input
v-bind:value="message"
v-on:input="message=$event.target.value">
其实就是动态绑定了 input 的 value 指向了 messgae 变量,并且在触发 input 事件的时候去动态把 message 设置为目标值,这样实际上就完成了数据双向绑定了,所以说 v-model 实际上就是语法糖。它即可以支持原生表单元素,也可以支持自定义组件。在组件的实现中,我们是可以配置子组件接收的 prop 名称,以及派发的事件名称。
3.v-model实现方式
let Child = {
template: '<div>'
+ '<input :value="value" @input="updateValue" placeholder="edit me">' +
'</div>',
props: ['value'],
methods: {
updateValue(e) {
this.$emit('input', e.target.value)
}
}
}
let vm = new Vue({
el: '#app',
template: '<div>' +
'<child v-model="message"></child>' +
'<p>Message is: {{ message }}</p>' +
'</div>',
data() {
return {
message: ''
}
},
components: {
Child
}
})
可以看到,父组件引用 child 子组件的地方使用了 v-model 关联了数据 message;而子组件定义了一个 value 的 prop,并且在 input 事件的回调函数中,通过 this.$emit('input', e.target.value) 派发了一个事件,为了让 v-model 生效,这两点是必须的。
子组件传递的 value 绑定到当前父组件的 message,同时监听自定义 input 事件,当子组件派发 input 事件的时候,父组件会在事件回调函数中修改 message 的值,同时 value 也会发生变化,子组件的 input 值被更新。
这就是典型的 Vue 的父子组件通讯模式,父组件通过 prop 把数据传递到子组件,子组件修改了数据后把改变通过 $emit 事件的方式通知父组件,所以说组件上的 v-model 也是一种语法糖。
3.vue生命周期
4.如何实现动态路由
5.如何实现动态store
6.vue3.0有什么变动
ElementUi
1.怎么动态绑定表单验证
2.表单验证原理
设计一个Axios
react
webpack
mvc
mvvm
MVVM 的核心是 ViewModel 层,它就像是一个中转站(value converter),负责转换 Model 中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用。
View 层
View 是视图层,也就是用户界面。前端主要由 HTML 和 CSS 来构建,为了更方便地展现 ViewModel 或者 Model 层的数据,已经产生了各种各样的前后端模板语言,比如 FreeMarker、Marko、Pug、Jinja2等等,各大 MVVM 框架如 KnockoutJS,Vue,Angular 等也都有自己用来构建用户界面的内置模板语言。
Model 层
Model 是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,主要围绕数据库系统展开。后端的处理通常会非常复杂
ViewModel 层
ViewModel 是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。需要注意的是 ViewModel 所封装出来的数据模型包括视图的状态和行为两部分,而 Model 层的数据模型是只包含状态的,比如页面的这一块展示什么,那一块展示什么这些都属于视图状态(展示),而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。由于实现了双向绑定,ViewModel 的内容会实时展现在 View 层,这是激动人心的,因为前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图,MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新,真正实现数据驱动开发。看到了吧,View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。