vue.js网址:https://cn.vuejs.org/v2/guide/
Vue就是用来代替MVC中的View的。
MVVC = MVC + 双向绑定
课程简介
从 MVC 到 MVVM
预习:Vue 官方文档
代码
MVC 版代码:http://jsbin.com/dujutov/1/edit?js,output
Vue 版代码:http://jsbin.com/dujutov/2/edit?html,js,output
Vue 浮层例子:http://jsbin.com/nabugot/1/edit?js,output
Vue 轮播例子:http://jsbin.com/liqicir/2/edit?js,output
Vue tab切换例子:http://jsbin.com/hijawuv/1/edit?html,css,output
睁大眼睛看清楚我用的是 jsbin.com 不是 js.jirengu.com,用 js.jirengu.com 有 bug 我才改成 jsbin.com 的。
用 Vue 代替 View
双向绑定
引入axios
复习一下MVC
我们做一个书籍展示的页面,需要引入axios这个库(https://github.com/axios/axios)
-
axios是一个ajax的库,是一个基于Promise的Http客户端。
- axios相对于jQuery的好处是除了get和post,还有put,patch,delete等,其用法几乎是照抄jQuery,但是好处是支持更多的API,
- 除了ajax功能之外就没有其余功能了,也就是更加专注。
-
以前我们使用jQuery,发现jQuery有一个问题,就是所有功能都混在一起,即ajax相关使用。现在我们想将ajax和dom操作分开,使用ajax的时候就使用axios,谁用dom操作的时候就使用vue
所以,后面基本不适用jQuery了
-
我们做一个图书库存管理的系统,现在以简单的样式实现
-
我们的数据库最好是从数据库里面拿,axios比jQuery好的一个点在于支持自己给自己造数据,即可以模拟服务器返回响应,其中interceptor就是拦截机的意思,其作用就会在真正返回response之前使用一个函数,这个函数会对response进行修改。
上面这个操作就是mock server:为了更好的分工合作,让前端能在不依赖后端环境的情况下进行开发,其中一种手段就是为前端开发者提供一个web容器,这个本地环境就是 mock server。
-
我们可以根据不同的url来mock不同的数据,代码如下所示,当axios的请求路径不一样的时候,将得不到返回数据
axios.interceptors.response.use(function(response){ // config里面有重要的url,method,data属性,这个data是请求的data let {config: {url, method, data}} = response data = JSON.parse(data||'{}') // 这个row是响应的data let row = { id: 1, name: 'JavaScript高级程序设计', number: 2 } if(url === '/books/1' && method === 'get'){ response.data = row }else if(url === '/books/1' && method === 'put'){ response.data = Object.assign(row, data) } return response }) axios.get('/books/1') .then((response)=>{ console.log(response) })
-
ES6中,支持这种解构语法:let {config: {url, method, data}} = response,下面图片中,第三行等于第一行+第二行
-
同样,下面第二种比第一种简洁,也是ES6的解构语法
// 第一种写法 axios.get('/books/1') .then((response)=>{ let data = response.data console.log(data) }) // 第二种写法 axios.get('/books/1') .then(({data})=>{ console.log(data) })
-
我们将拿到的数据重新渲染到页面中,使用替换即可
axios.interceptors.response.use(function(response){ // config里面有重要的url,method,data属性,这个data是请求的data let {config: {url, method, data}} = response data = JSON.parse(data||'{}') // 这个row是响应的data let row = { id: 1, name: 'JavaScript高级程序设计', number: 2 } if(url === '/books/1' && method === 'get'){ response.data = row }else if(url === '/books/1' && method === 'put'){ response.data = Object.assign(row, data) } return response }) axios.get('/books/1') .then(({data})=>{ let originalHtml = $('#app').html() let newHtml = originalHtml.replace('__name__', data.name) .replace('__number__', data.number) $('#app').html(newHtml) })
上面最终我们可以看到前面这个拿到的数据会显示在网页中
-
委托:但是我们发现一个BUG,发现原先的点击事件起不了作用,这是因为我们在操作$('#app').html(newHtml)这句话的时候,原先里面的button被替换了,我们需要做一个委托,下面这个写法的意思就是在点击app里面的任何一个元素的时候,如果这个元素符合#addOne这个条件,就会执行相关代码,这样就算代码被替换页没有关系,因为app始终是没有动的,只是里面的内容换了罢了
$('#app').on('click', '#addOne', function(){ var oldNumber = $('#number').text() var newNumber = oldNumber -0 +1 $('#number').text(newNumber) })
-
整体的JS代码如下
axios.interceptors.response.use(function(response){ // config里面有重要的url,method,data属性,这个data是请求的data let {config: {url, method, data}} = response data = JSON.parse(data||'{}') // 这个row是响应的data let row = { id: 1, name: 'JavaScript高级程序设计', number: 2 } if(url === '/books/1' && method === 'get'){ response.data = row }else if(url === '/books/1' && method === 'put'){ response.data = Object.assign(row, data) } return response }) axios.get('/books/1') .then(({data})=>{ let originalHtml = $('#app').html() let newHtml = originalHtml.replace('__name__', data.name) .replace('__number__', data.number) $('#app').html(newHtml) }) $('#app').on('click', '#addOne', function(){ var oldNumber = $('#number').text() var newNumber = oldNumber -0 +1 $('#number').text(newNumber) }) $('#app').on('click', '#minusOne', function(){ var oldNumber = $('#number').text() var newNumber = oldNumber -0 -1 $('#number').text(newNumber) }) $('#app').on('click', '#reset', function(){ $('#number').text(0) })
-
到目前为止,我们先使用ajax获取一个很简单的数据,获取到数据之后,将数据替换到html中,同时通过事件委托监听app的点击事件;当我们点击的时候,我们需要发送请求,到后台去改数值,而不是表面上的加一减一。每一次改变number的时候,我们需要将新的number先put到服务器上面去,put成功就在页面中更改值,发送不成功就不要更改值。后端那边也要做一个监听,将put上去的值给服务器。Object.assign(book, data),这个API用于部分更新,只更改对应的部分,可以一次性赋值更改,可以多次覆盖性更改
-
目前为止,我们做了一个模拟后台,三个点击按钮当被点击的时候,就会发送put请求,但是这个代码很麻烦,因为每次都要重复写put请求,这种属于意大利面条式代码,没有太多组织性,总体的代码如下,每次点击的时候会出现延迟更新的状态
let book = { id: 1, name: 'JavaScript高级程序设计', number: 2 } axios.interceptors.response.use(function(response){ // config里面有重要的url,method,data属性,这个data是请求的data let {config: {url, method, data}} = response if(url === '/books/1' && method === 'get'){ // 这个data是响应的data response.data = book }else if(url === '/books/1' && method === 'put'){ Object.assign(book, data) response.data = book } return response }) axios.get('/books/1') .then(({data})=>{ let originalHtml = $('#app').html() let newHtml = originalHtml.replace('__name__', data.name) .replace('__number__', data.number) $('#app').html(newHtml) }) /* 上面是加了一个假的后台 */ $('#app').on('click', '#addOne', function(){ var oldNumber = $('#number').text() var newNumber = oldNumber -0 +1 axios.put('/books/1', { number: newNumber }).then(()=>{ $('#number').text(newNumber) }) }) $('#app').on('click', '#minusOne', function(){ var oldNumber = $('#number').text() var newNumber = oldNumber -0 -1 axios.put('/books/1', { number: newNumber }).then(()=>{ $('#number').text(newNumber) }) }) $('#app').on('click', '#reset', function(){ axios.put('/books/1', { number: 0 }).then(()=>{ $('#number').text(0) }) })
引入MVC
- 我们引入MVC,所谓MVC是将其变成三部分。我们写一个model,所有跟数据相关的操作,都用model来实现
- 所有跟html相关的操作都用view,来操作
- 所有model和View之外的都交给controller来操作。
- 最终的代码如下所示:
fakeData() let model = { data: { name: '', number: 0, id: 1 }, fetch: function(){ return axios.get('/books/1').then((response)=>{ this.data = response.data return response }) }, update: function(data){ let id= this.data.id return axios.put('/books/1', data).then((response)=>{ this.data = response.data return response }) } } let view = { el: '#app', template: `
书名:《__name__》 数量:__number__ - 上面这个代码中,有三个对象,一个对象是Model,一个View,一个是Controller,假设我们有多个页面,如果每个页面都要写这三个对象,那么就会比较麻烦。我们应该创建类,将共同的属性放入class中。或者写构造函数,将公有的东西写到原型中。
- 我们将代码进行更好,变成下面这样:
fakeData() // 下面是MVC的类 function Model(options){ this.data = options.data this.resource = options.resource } Model.prototype.fetch = function(id){ return axios.get(`/${this.resource}s/${id}`).then((response)=>{ console.log(response) this.data = response.data return response }) } Model.prototype.update = function(data){ let id= this.data.id return axios.put(`/${this.resource}s/${id}`, data).then((response)=>{ this.data = response.data return response }) } 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) } // 下面是MVC对象 let model = new Model({ data: { name: '', number: 0, id: '' }, resource: 'book' }) let view = new View({ el: '#app', template: `
书名:《__name__》 数量:__number__
引入Vue
- 引入Vue,我们先删除自己的view,将里面的let view = new View()改成let view = new Vue()。里面的标记__改成两个大括号,如name改成{{name}}。并且我们需要将model里面的data传到Vue里面来,因为vue需要根据这个data初始化这个template
-
template里面只能有一个根元素,如果有两个根元素,那么特点1:Vue只看第一个,这样原先没有显示的button就出现了
- Vue就是MVC做了升级,特点2:Vue里面不需要render事件,因为Vue里面自动有一个render机制
- 当vue的data里面的数据发生变化的时候,会自动更新,记住,直接更改vue.data = ...是没有用的,需要更改里面的值,这是因为Vue的第三个特点是会将data里面的所有属性升级到当前的view上面,不能改data去改view,应该改data里面的属性,也就是我们找data属性没用了
- 以前的更新,最重要的一句话是view.render(midel.data),现在只需要更新view.data,html更新会自动进行
-
我们可以将三个属性同时放到一个属性里面,只要这个属性变了就会更新,这个book是view的属性,而不是放到data里面的,Vue会自动将book属性提升到view层面
- Vue只更改该改的地方,不会整个去刷新页面,而之前的MVC写法,一旦app里面的任意内容发生变化,这个app都会被重新替换。
- Vue支持不适用Controller,我们看到我们的Controller最重要的语法是绑定监听,对应到Vue上面就是method,将controller里面所有的函数写到这个method里面,而且bindEvents不需要了,因为vue内置了bindEvents
- Vue是不管model的,但是我们省略了所有dom的获取更新操作,这个形成了自动化
- 我们还需要进行初始化,Vue里面有一个created属性函数,用于在元素创建成功之后调用,还有一个mount函数,用于挂载成功之后调用。
- Vue的好处就是让以前写的代码更智能,让MVC的C合并到Vue里面去
- 现在的代码如下所示:
fakeData() // 下面是MVC的类 // 使用Vue之后,M保留了。C被合并到V中了,View使用了Vue // 初始化以及事件绑定等操作都放入View中了 function Model(options){ this.data = options.data this.resource = options.resource } Model.prototype.fetch = function(id){ return axios.get(`/${this.resource}s/${id}`).then((response)=>{ this.data = response.data return response }) } Model.prototype.update = function(data){ console.log(data) let id= this.data.id return axios.put(`/${this.resource}s/${id}`, data).then((response)=>{ this.data = response.data return response }) } // 下面是MVC对象 let model = new Model({ data: { name: '', number: 0, id: '' }, resource: 'book' }) // 使用Vue创建view let view = new Vue({ el: '#app', // 放在model里面的数据可以放一份到data中,data里面的属性会自动提升给view // 访问的时候使用this.book,而不是this.view.book data: { book: { name: '未命名', number: 0, id: '' }, n: 1 }, // 变量的标识使用两个大括号包围 template: `
书名:《{{book.name}}》 数量:{{book.number}} -
使用v-model="n"是将值绑定到data里面的n去。这是一种双向绑定,我们将input的值绑定到data的n值上面去,同时又拿到这个值渲染到这个页面中来,现在我们只需要更改input里面的值,后面的渲染也会随即更改
- 只有input能实现双向绑定,span是不可以的
- 我们之前使用span的时候,span能拿到data里面的n进行渲染,当更改内存中n的时候,再次渲染将n的值给span。所有的过程都是单向的,拿到内存中的n,渲染给span
- 但是当有input的时候,我们第一次拿到n的是给input,这是一个默认值,但是当我们使用v-model="n"将input的value值与n链接起来,使得input的value变化的时候,n也变化,页面中其余引用n的部分也跟着变化,这就是双向绑定。
-
- 只从内存到页面,这是单向的,但是如果页面能映射到内存,这就是双向的。
- Vue是一种自动化的MVC,又叫MVVM
使用Vue做三个小东西
我们使用了Vue之后,jQuery就可以不使用了,因为我们已经摆脱了操作DOM。jQuery中的ajax已经用axios代替了,我们现在只需要想数据之间的逻辑就好了,不用花大量精力去处理DOM等。
-
我们使用Vue可以很容易就实现一个点击的浮动图层,其中v-if="open"的意思就是当open为true的时候,显示,为false的时候就不显示,toggle函数会更改open的值
let view = new Vue({ el: '#app', data: { open: false }, template: `
你好 -
做个轮播,使用了VUE就不用碰DOM,绑定的函数既支持加参数,也支持只写函数名;可以使用v-bind:style绑定一个style属性,
let view = new Vue({ el: '#app', data: { transformValue: '' }, template: `
如果要做图片切换,需要使用v-for进行循环
-
做一个Tab切换,v-show="值",如果值是真的,那么就展示,如果值是false,那么就不展示;v-on:click="selected = 0"这后面的字符串会被解析成公式进行执行,v-bind:class="{active:selected === 0}的意思就是使用bind绑定一个active属性,但是只有在selected === 0的时候,绑定才生效
-
最终的代码如下所示
let view = new Vue({ el: '#app', data: { selected: 'a', tabs: [ {name: 'a', content: 'aaa'}, {name: 'b', content: 'bbb'}, {name: 'c', content: 'ccc'} ] }, template: `
- {{tab.name}}
- {{tab.content}}
-
点击的时候会出现内容,以及样式会进行更改
VUE学起来真的很简单,但是一开始学VUE会不知道怎么实现
一个很好的Vue模拟网页:https://jsfiddle.net/chrisvfritz/50wL7mdz/