当前前端最火热的框架当属 VUE,在学习 VUE 之前先来看下 VUE 的内部是如何工作的。
我们从最基本的页面操作开始做起。
用 jQuery 操作页面
我们来实现一个页面,当点击按钮式,页面上的数字增加或减少
书籍:《JavaScript高级程序设计》
数量:2
用 jQuery 操作它很容易实现需求
let log = console.log.bind(console) //把console.log 替换成 log 少打点代码
$('.addOne').on('click',()=>{
let oldHtml = $('.number').text()
let newHtml = oldHtml -0 +1
$('.number').html(newHtml)
})
$('.minusOne').on('click',()=>{
let oldHtml = $('.number').text()
let newHtml = oldHtml -0 -1
$('.number').html(newHtml)
})
$('.reset').on('click',()=>{
$('.number').text('0')
})
axios 实现 ajax
我们真实的需求是,当点击按钮时,操作的时数据库里的数据,而不是直接在页面中操作。
这里引入一个库axios
,可以实现在前端模拟后台,它有一个重要的 API:interceptors
,可以实现在它上面 Mock 数据
// 我们要的数据
let book = {
name:'JavaScript高级程序设计',
number:2,
id:''
}
axios.interceptors.response.use((response)=>{
//下面这句等价于 let {url,method,data} = response.config
let {config:{url,method,data}} = response // 这里的 data 是请求体
if(url === '/book/1' && method === 'get'){
response.data = book //这里的 data 是响应体
}else if(url === '/book/1' && method === 'put'){
data = JSON.parse(data)
Object.assign(book,data) //请求体 data,assign可实现局部更新
response.data = book //响应体 data
}
return response
})
页面中的数据我们应该用占位符代替,数据获取到之后 更新到页面中
//刚进入页面后的数据加载
axios.get('/book/1').then(({data})=>{
let oldHtml = $('.app').html()
let newHtml = oldHtml.replace('__name__',data.name)
.replace('__number__',data.number) //用真实数据替换占位符
$('.app').html(newHtml)
})
$('.app').on('click','.addOne',()=>{
let oldNumber = $('.number').text()
let newNumber = oldNumber -0 +1
axios.put('/book/1',{number:newNumber}).then(({data})=>{ //请求时更新最新数据
$('.number').html(data.number)
})
})
$('.app').on('click','.minusOne',()=>{
let oldNumber = $('.number').text()
let newNumber = oldNumber -0 -1
axios.put('/book/1',{number:newNumber}).then(({data})=>{
$('.number').html(data.number)
})
})
$('.app').on('click','.reset',()=>{
axios.put('/book/1',{number:0}).then(({data})=>{
$('.number').html(data.number)
})
})
这样的意大利面条似的写法,非常不利于后期维护,我们应该用 MVC 优化下
用 MVC 优化
获取数据,更新数据的事情交个model
去做,model
里面有三个属性:data
,fetch
,updata
;分别用来:data
负责存储最新数据,fetch
负责页面加载时向服务器获取数据,并将数据存储到data
中,updata
负责实时页面操作时,更新页面数据,并将最新数据保存到data
中。
let model ={
data:{ //model 内部用来存储数据
name:'',
number:0,
id:''
},
fetch(id){
return axios.get(`/books/${id}`).then((response)=>{
this.data = response.data //加载更新向 axios 获取的数据
return response
})
},
updata(id,data){
return axios.put(`/books/${id}`,data).then((response)=>{
this.data = response.data //点击按钮向 axios 获取最新数据,请求中的 data 是最新数据
return response
})
}
}
操作页面交给view
,view
有三个属性,分别是el
、template
、render
;el
负责视图部分,也就是你需要操作的 DOM,template
是虚拟的html
,并通过render
去渲染。
let view = {
el:'.app',
template:`
书籍:《__name__》
数量:__number__
`,
render(data){
let newHtml = this.template.replace('__name__',data.name)
.replace('__number__',data.number) //把占位符替换成数据
$(this.el).html(newHtml)
}
}
事件相关的交给controller
操作,有两个重要的属性:init
、bingEvents
;初始化时需要传入两参数view
和model
,后面操作的都是在的view
、model
都是在controller
身上,而不是直接操作model
。
let controller = {
init({view,model}){
this.view = view
this.model = model
this.bindEvents()
this.model.fetch(1).then(()=>{
view.render(this.model.data)
})
},
bindEvents(){
$(this.view.el).on('click','.addOne',this.addOne.bind(this)) //这里 addOne 内部的 this 应该是点击的那个元素,所以这里要绑一下 this
$(this.view.el).on('click','.minusOne',this.minusOne.bind(this))
$(this.view.el).on('click','.reset',this.reset.bind(this))
},
addOne(){
console.log(1)
let oldNumber = $('.number').text()
console.log(2)
let newNumber = oldNumber -0 +1
console.log(3)
this.model.updata(1,{number:newNumber}).then(()=>{
$('.number').html(this.model.data.number)
})
console.log(4)
},
minusOne(){
let oldNumber = $('.number').text()
let newNumber = oldNumber -0 -1
this.model.updata(1,{number:newNumber}).then(()=>{
$('.number').html(this.model.data.number)
})
},
reset(){
this.model.updata(1,{number:0}).then(()=>{
$('.number').html(this.model.data.number)
})
}
}
controller.init({view:view,model:model})
优化 MVC
现在是一个页面,这也写没有关系,但如果有很多页面,每个页面中的view
、model
、controller
都重复了,这里把一些公用的方法写在原型上。
在页面中使用model
,只需要传递两参数
function Model({data,resouce}){
this.data = data
this.resouce = resouce
}
Model.prototype.updata = function(id,data){
return axios.put(`/${this.resouce}s/${id}`,data).then((response)=>{
this.data = response.data
return response
})
}
Model.prototype.fetch = function(id){
return axios.get(`/${this.resouce}s/${id}`).then((response)=>{
this.data = response.data
return response
})
}
let model = new Model({
data:{
name:'',
number:0,
id:''
},
resouce:'book'
})
view
也是,页面使用时,传两个参数就 ok 了
function View({el,template}){
this.el = el
this.template = template
}
View.prototype.render = function(data){
let html = this.template
for(let key in data){ //遍历传进来的参数,用循环替换页面中的占位符
html = html.replace(`__${key}__`,data[key])
}
$(this.el).html(html)
}
let view = new View({
el:'.app',
template:`
书籍:《__name__》
数量:__number__
`
})
Controller
公用的方法比较少,这里就没有优化了
VUE
理解了 MVC 之后再来看 VUE 就会很简单,VUE 简单来说就是 MVC 中的 V,但它和 MVC 有点区别,就是它需要model
中的数据
let view = new Vue({
el:'.app',
data:{
book:{
name:'我是书籍',
number:0,
id:''
},
n:1
},
template:`
书籍:《{{book.name}}》
数量:{{book.number}}
`
}
VUE 会把data
里的属性提升为 Vue
的属性,所以下面操作可以直接用Vue.name
操作,而不是写Vue.data.name
,所以我们可以在这些属性外面套一层book
,用Vue.book
就可以对这些属性进行批量操作。
Vue
没有render
方法,那你会说它怎么实现渲染页面呢?
它提供了一个叫created
的方法,在里面直接修改Vue
的data
属性,它就会自动帮你渲染页面
created(){
model.fetch(1).then(()=>{
this.book = model.data
})
当然 VUE 的野心不止于此 ,它甚至帮你省下controllor
,你都不需要进行事件绑定
它在template
,v-on:click
的一个方法,它会帮你调用methods
中的方法,你只需要将点击执行的函数写在上面即可。
methods:{
addOne(){
model.updata(1,{number:this.book.number + (this.n-0)})
.then(()=>{
this.book = model.data
})
},
minusOne(){
model.updata(1,{number:this.book.number - (this.n-0)})
.then(()=>{
this.book = model.data
})
},
reset(){
model.updata(1,{number:0})
.then(()=>{
this.book = model.data
})
}
}
学会了 MVC 之后在来看 VUE,就变的很简单
MVVM
VUE 还实现另一个双向绑定的功能,我现在点击按钮只能+1
或-1
,如果我要实现操作+n
或减n
呢?
这里用input
实现,在按钮上面添加一行
N的值是{{n}}
当然Vue
的data
中也要添加一个n
当你在input
中输入相应值时,后面N的值会相应变化
,这就是 MVVM。