一、什么是大前端
简单来说是所有前端的统称。特点是一次开发,兼容所有平台。如果我们要做web端、客户端就是,一套代码适用所有平台。正是大前端时代的出现,才孕育出了MVVM架构模式
二、什么是MVC
MVC:架构模式的一种,Model、View、Controller(控制器),目的是将M和V的代码分离。数据传输方向是单向的,视图需要通过控制器,控制器改变数据,数据直接在视图上反馈出来
原理:Controller它是指页面业务逻辑,将V和M的代码分离。V和M需要通过C来承上启下
C可以直接引用M、V,但反过来不可以。M和V直接不可以相互引用(说白点就是,C中可以写M、V的代码,但是M、V只可以写自己的代码)
三、MVVM的诞生
随着数据的复杂,数据解析花费的时间就越长,如果放在c中,c就会越来越臃肿,而且c的定义没有解析这一项。所以,根据面对对象的思想,出现了新类:VIewModel。也因此诞生了一个新的架构模式:MVVM。
MVVM:架构模式的一种,Model、View、VIewModel,数据传输方向是双向的,视图通过DOM事件改变数据,数据通过数据绑定显示视图。(还是有C的,但是C的存在感被降低了)
原理:ViewModel中有一个observe观察者,当数据变化时,VIewModel可以监听到数据的变化,通知视图更新;当用户操作视图时,VIewModel能监听到视图的变化,然后通知Model改变。VIew和VIewModel可以直接相互改变,但是Model和VIewModel则需要observe进行通信
四、MVC和MVVM的区别
1、MVC数据单向通信、MVVM是双向通信
2、MVVM中VIewModel没有取代Controller,他是抽离了C中的业务逻辑,其他还是放在C中,这样就实现了业务逻辑组件的复用
五、vue中如何实现的双向绑定
监听-订阅模式
1、model直接反馈到view上: object.defineProperty给每个属性添加set、get方法,数据更新时触发observe,这时通知Dep(订阅器,管理所有的订阅者),dep去更新并告知是哪个watcher(订阅者),watcher知道是自己改变时,去触发update(),更新视图
2、视图去改变model,两种情况:指令解析、dom事件触发
指令解析:compile循环遍历解析每个指令,解析一次去创建一个新的watcher,并绑定update方法,watcher添加到dep中,同时更新初始化视图
dom事件:去触发属性的set的方法,observe监听到去触发dep.notify(),之后和第一种一样
六、用js实现v-model、v-if和v-show
1.用vue的方式先把html写完
<!DOCTYPE html>
<html lang="en">
<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>
</head>
<body>
<div id="app">
<input type="text" name="input" v-model="{{name}}"><br/>
<p>{{name}}</p>
</div>
<script src="vm.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
name: '李鑫'
}
})
</script>
</body>
</html>
2.定义vue类
class Vue{
constructor(options){
this.el = document.querySelector(options.el)
this.data = options.data
}
}
3.为data中的每个数据添加数据劫持
class Vue{
constructor(options){
this.initData()
}
initData() {
var _data = {}
var _this = this
for(let key in this.data){
Object.defineProperty(_data, key, {
set(newValue) {
_this.data[key] = newValue
},
get(){
return _this.data[key]
}
})
}
}
}
4.遍历节点,找到v-model及{{name}}
class Vue{
constructor(options){
this.initDom()
}
initDom() {
let childNodes = this.el.childNodes
const _this = this
for(let item of childNodes){
console.log('child', item)
if(item.nodeType === 1){
const value = item.getAttribute('v-model')
if(value){
const modelName = value.match(/{{(.+?)}}/)[1]
item.value = this.data[modelName]
item.addEventListener('input', function (e) {
_this.data[modelName] = e.target.value
})
}
}
if(item.childNodes.length != 0){
const value = item.childNodes[0].data.trim()
const modelName = value.match(/{{(.+?)}}/)
if(modelName){
item.innerHTML = this.data[modelName[1]]
}
}
}
}
到这里就实现了,v-model和{{name}}显示
5.添加订阅器,获取到{{name}}时,存入key对应的item,input不用,因为value等于的值是响应式的
if(item.childNodes.length != 0){
if(modelName){
this.dep[modelName[1]] = item
}
}
set(newValue) {
_this._data[key] = newValue
console.log('setProperty', _this.dep[key])
if(_this.dep[key]) {
_this.dep[key].innerHTML = newValue
}
}
6.所有的代码
class Vue {
constructor(options) {
this.el = document.querySelector(options.el)
this._data = options.data
this.dep = {}
this.initData()
this.initDom()
}
initDom() {
let childNodes = this.el.childNodes
const _this = this
for(let item of childNodes){
console.log('child', item)
if(item.nodeType === 1){
const value = item.getAttribute('v-model')
if(value){
const modelName = value.match(/{{(.+?)}}/)[1]
item.value = _this.data[modelName]
item.addEventListener('input', function (e) {
_this.data[modelName] = e.target.value
console.log('aaaa', _this.data[modelName])
})
}
}
if(item.childNodes.length != 0){
const value = item.childNodes[0].data.trim()
const modelName = value.match(/{{(.+?)}}/)
if(modelName){
console.log('sssss', modelName)
item.innerHTML = this.data[modelName[1]]
this.dep[modelName[1]] = item
}
}
}
}
initData() {
this.data = {}
let _this = this
for(let key in _this._data){
Object.defineProperty(_this.data, key, {
set(newValue) {
_this._data[key] = newValue
console.log('setProperty', _this.dep[key])
if(_this.dep[key]) {
_this.dep[key].innerHTML = newValue
}
},
get(){
return _this._data[key]
}
})
}
this.data['name'] = '11'
}
}
七、实现v-show\v-if
1.用vue的方式先把html写完
<!DOCTYPE html>
<html lang="en">
<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>
</head>
<body>
<div id="app">
<button @click="change">切换</button>
<div v-if='isIf'>v-if</div>
<div v-show='isShow'>v-show</div>
</div>
<script src="vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
isShow: false,
isIf: false
},
methods:{
change() {
this.isIf = !this.isIf
this.isShow = !this.isShow
}
}
})
</script>
</body>
</html>
2.创建vue类+初始化data
class Vue {
constructor(options) {
this.el = document.querySelector(options.el)
this.data = options.data
this.methodName = options.methods
this.initData()
}
initData() {
this._data = {}
let _this = this
for(let key in this.data) {
Object.defineProperty(_this._data, key, {
set(newValue) {
_this.data[key] = newValue
},
get() {
return _this.data[key]
}
})
}
}
}
3.循环遍历v-show\v-if v-show时判断是否显示还是隐藏,v-if建立comment节点
initDom() {
let _this = this
let childNodes = this.el.childNodes
for(let item of childNodes) {
if(item.nodeType === 1) {
if(item.getAttribute('v-show')) {
const modelName = item.getAttribute('v-show')
if(!_this.data[modelName]){
item.style.display = 'none'
}
}
if(item.getAttribute('v-if')) {
var newDiv = document.createComment('v-if')
const modelName = item.getAttribute('v-if')
if(!_this.data[modelName]){
item.parentNode.replaceChild(newDiv, item)
}
}
if(item.getAttribute('@click')) {
const modelName = item.getAttribute('@click')
item.addEventListener('click', function(){
})
}
}
}
}
5.为item添加click事件,改变this指向
item.addEventListener('click', function(){
console.log('+--+')
_this.methodName[modelName].call(_this._data)
})
6.添加订阅器
set(newValue) {
_this.data[key] = newValue
if(_this.dep[key].item) { //说明是v-if
if(newValue){
_this.dep[key].parent.replaceChild(_this.dep[key].item, _this.dep[key].value)
}else {
_this.dep[key].parent.replaceChild(_this.dep[key].value, _this.dep[key].item)
}
} else{
console.log('这是v-show', key, _this.dep[key])
if(newValue){
_this.dep[key].style.display = 'block'
}else {
_this.dep[key].style.display = 'none'
}
}
}
7.所有
<!DOCTYPE html>
<html lang="en">
<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>
</head>
<body>
<div id="app">
<button @click="change">切换</button>
<div v-if='isIf'>v-if</div>
<div v-show='isShow'>v-show</div>
</div>
<script src="vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
isShow: false,
isIf: false
},
methods:{
change() {
console.log(this, this.isShow, '+++++')
this.isIf = !this.isIf
this.isShow = !this.isShow
}
}
})
</script>
</body>
</html>
<script>
class Vue {
constructor(options) {
this.el = document.querySelector(options.el)
this.data = options.data
this.methodName = options.methods
this.dep = {}
this.initData()
this.initDom()
}
initDom() {
let _this = this
let childNodes = this.el.childNodes
for(let item of childNodes) {
if(item.nodeType === 1) {
if(item.getAttribute('v-show')) {
const modelName = item.getAttribute('v-show')
_this.dep[modelName] = item
if(!_this.data[modelName]){
item.style.display = 'none'
}
}
if(item.getAttribute('v-if')) {
var newDiv = document.createComment('v-if')
const modelName = item.getAttribute('v-if')
_this.dep[modelName] = {item, value: newDiv, parent: item.parentNode}
if(!_this.data[modelName]){
item.parentNode.replaceChild(newDiv, item)
}
console.log(_this.dep[modelName])
}
if(item.getAttribute('@click')) {
const modelName = item.getAttribute('@click')
item.addEventListener('click', function(){
console.log('+--+')
_this.methodName[modelName].call(_this._data)
})
}
}
}
}
initData() {
this._data = {}
let _this = this
for(let key in this.data) {
Object.defineProperty(_this._data, key, {
set(newValue) {
_this.data[key] = newValue
if(_this.dep[key].item) { //说明是v-if
console.log('这是v-if', key, newValue)
if(newValue){
_this.dep[key].parent.replaceChild(_this.dep[key].item, _this.dep[key].value)
}else {
_this.dep[key].parent.replaceChild(_this.dep[key].value, _this.dep[key].item)
}
} else{
console.log('这是v-show', key, _this.dep[key])
if(newValue){
_this.dep[key].style.display = 'block'
}else {
_this.dep[key].style.display = 'none'
}
}
},
get() {
return _this.data[key]
}
})
}
}
}
</script>