首页 > 代码库 > Vue2.0基础学习(3)--- 一个简单的实例学习
Vue2.0基础学习(3)--- 一个简单的实例学习
看完vue 的官方文档,再做一个简单的实例是最好不过了,既能巩固我们所学的知识,又能学以致用。infoq上推荐了一篇文章,面向重度 jQuery 开发者的 Vue.js 介绍, 它是老外写的,用vue做了一个简单的实例,非常适合学完vue文档来练练手,我这里并没有翻译文档,而是做了几次后,自已的思路。
首先看一下这个实例长什么样子,有什么功能
上面是一个文本框,用于输入内容,但最多只能输入140个字,所以右下角会有字数提示。当用户进行输入的时候,右下角的数字不断变化,提示用户还剩多少字可以输入。当剩余字数小于20 的时候,它会示浅红色,当剩余字数小于10 的时候,它会显示深红色,通过颜色的变化来做出提醒。当字数小于0的时候,提示超出限制,同时右侧的按钮也会进行相应的变化,当文本框中没有内容时和超出字数时都会禁用。
当点击下面的照相机图标,会弹出选择图片对话框,选择图片后,文本框的下面就会显示我们选中的图片,同时图片上面有一个×号,用于删除图片, 应用会变成以下显示
由示这个例子比较简单,直接用一个html文件来写就可以了,样式的话,我们用Bootstrap, 首先在文件中引入Bootstrap的css 样式,再引入 vue.min.js, 自己写的样式放到style标签中,js 文件放到script 标签中。大体的框架如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue 学习实例</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> <script src="https://unpkg.com/vue/dist/vue.js"></script> <style> body { padding-top: 100px; } .row { margin-top: 20px; } </style> </head> <body> <div class="container"> <h2 class="text-center">创建你的微博</h2> <textarea class="form-control" rows="3"></textarea> <div class="row"> <div class="col-md-10"> <label class="glyphicon glyphicon-camera" for="xFile"></label> <input type="file" id="xFile" style="position:absolute;clip:rect(0 0 0 0);"> </div> <div class="col-md-2"> <span>30</span> <button type="button" class="btn btn-primary">转发</button> </div> </div> </div> <!--所有js都放在以下script标签中--> <script> </script>
页面中显示内容如下,不是太好看
vue 应用都是通过vue根实例启动的,就是vue的构造函数,它里面有一个el 属性,表示挂载元素,那首先要定义一个挂载元素,一般都是最外层元素,给最外层class=‘container’的最外层元素加一个id, 为weibo.
我们先来实现第一个功能,就是输入框中输入内容时,右下角的字数和按钮会发生相应的变化, 由于所有的变化都是基于输入框中的内容,首先我们要获取到输入框中的内容, vue 提供了v-model指令,可以很轻松地实现。获取到内容后,怎么实现更新字数,vue 提供了 computed 属性,可以基于一个变化得到另外的变化。给textarea 加一个v-model=“content” 指令,将用户输入的内容绑定到content变量,再建一个变量 letterRemaining 显示剩余的字数,这时<span>30</span> 就要变成{{letterRemaining}}来动态显示剩于字数,由于剩余字符是基于最大字数限制,所以还要声明一个常量MAX_LETTER_LENGTH =140来表示最大字数。
html 内容更改如下:
<div class="container" id="weibo"> <!--添加一个id 提供挂载--> <h2 class="text-center">创建你的微博</h2> <textarea class="form-control" rows="3" v-model="content"></textarea> <!--v-model数据绑定--> <div class="row"> <div class="col-md-10"> <label class="glyphicon glyphicon-camera" for="xFile"></label> <input type="file" id="xFile" style="position:absolute;clip:rect(0 0 0 0);"> </div> <div class="col-md-2"> <span>{{letterRemaining}}</span> <!--动态显示剩余字数--> <button type="button" class="btn btn-primary">转发</button> </div> </div> </div>
script 添加以下内容
<script> const MAX_LETTER_LENGTH = 140; //最大输入字数 140 new Vue({ el:"#weibo", data: { content:‘‘ // 获得用户输入的内容 }, computed: { // 计算剩余字数 letterRemaining:function() { return MAX_LETTER_LENGTH - this.content.length; } } }) </script>
最基本的输入和字数变化已经实现了,但还有当剩余字数小于20, 和小于10的时候,给用户以颜色提示。Bootstrap 中有两个样式类,text-warning, text-danger 可以提供相应的样式,也就是说,当字数变化时要动态改变样式,这时想到v-bind:class指令,可以动态绑定。这个指令可以接受一个对象 {text-warning: isWaring}, text-waring 类取决于后面的变量,如果isWaring 取值为ture, 元素就有text-waring类,否则没有。因此我们这里还要新两个变量,表示小于20和小于10, 由于这两个变量也是基于输入的内容,所以它们也是计算属性,变量名分别为under20, under10. html中的span 也要加上v-bind:class指令
html中的span 加上v-bind:class指令
<!--v-bind指令,动态改变样式--> <span v-bind:class="{ ‘text-warning‘:under20, ‘text-danger‘: under10}">{{letterRemaining}}</span>
script 中的computed属性添加两个新属性
computed: { // 计算剩余字数 letterRemaining:function() { return MAX_LETTER_LENGTH - this.content.length; }, // 小于20个字符 under20: function(){ return this.letterRemaining <= 20 && this.letterRemaining > 10; }, // 小于10个字符 under10: function() { return this.letterRemaining <= 10 && this.letterRemaining > 0; } }
这时你输入字符的时候,当小于10和小于20时,它会出现颜色的变化,但是当我们一直输入下去的时候,它会显示负数,且可以一直输入下去,和我们的字数限制不符,原来是想用js 解决这个问题的,但一直没有想到更好的办法,突然想到textarea 有一个属性是maxlength ,它就是规定最多输入多少字符,当超过它的限制,不能输入,也就是说,我们的剩余字数提示不会是负数,最小是是0,只不过,当输入到140个字符的时候,要给用户一个提示,这里就用alert 提示一个。
现在要做两个修改,一个是textarea 增加一个maxlength属性,
<textarea class="form-control" rows="3" v-model="content" maxlength="140"></textarea> <!--增加maxlength属性,最大字数限制-->
再一个是提示用户 alert 框, 我这里是把 letterRemaining 增加了一个if 条件判断
letterRemaining:function() { var remain = MAX_LETTER_LENGTH - this.content.length; if(remain == 0){ alert(‘最多输入140个字符‘) } return remain; }
最后就是按钮的处理,当输入框中没有内容的时候,它是禁用的,只有输入内容后才能使用,它也是跟随输入框中的内容来回切换,正好button 有个disabled属性,取值true 或false, 我们可以把这个disabled属性绑定到一个变量上,而变量的取值也是true or false, 那这个变量还得是计算属性,因为它基于输入框的内容,而true or false 的取值也非常简单,因为只要输入框中有内容就是true, 没有内容就是false .现在再增加一个计算属性 contentIsEmpty, 同时让button 的disabled属性绑定到它上面
<button type="button" class="btn btn-primary" :disabled="contentIsEmpty">转发</button>
computed: { ......// 输入框中没有没内容 contentIsEmpty:function(){ return this.content.length == 0 } }
应用的第一个功能已经做完了,相对简单
现在开始第二个功能,当有图片上传时,文本框中下面显示图片,没有图片时,不显示,很明显,这要用v-if指令控制组件的显示和隐藏。其次,我们应用中应该有一个变量来存放图片,从而可以在html模版中进行引用来显示图片。假设可以上传多张图片, 所以我们在data中定义一个变量photos 为一个数组,默认为空数组。数组中的内容怎么才能显示到页面中,这要用到v-for指令。当有图片时,也就是photos.length 大于0 的时候,所以这时还需要一个计算属性来控制显示和隐藏 photoUploaded。根据描述,大体就是能写出这个组件了。
首先是一个div包括着图片和删除按钮,它有一个v-if指令,里面是figure 标签拥有v-for 指令进行循环,在text area下面增加 以下内容,这里还增加了删除功能,remove 方法后面写
<!--图片显示区域--> <div class="row" v-if="photoUploaded"> <div class="col-md-12"> <figure v-for="(photo,index) in photos"> <img :src="photo" class="img-rounded"> <button type="button" class="close" @click=‘remove(index)‘>×</button> </figure> </div> </div>
现在剩下怎么获得用户选图片,首先,input = file 还可增加一个 multiple 属性才能上传多个图片,其次,上传图片会change事件,我们监听change 事件就可以了, 上传成功后,它会存在input dom 对象中的flies属性中,最后, 我们把这个文件用base64 来进行读取,它们分别是html5 file API 和fileReader API . 在html中input= file 修改如下:
<input type="file" multiple @change="upload" id="xFile" style="position:absolute;clip:rect(0 0 0 0);">
js 中增加upload方法,还有上面还说到remove 方法
methods:{ upload:function(e){ var that = this; var files = Array.from( e.target.files); files.forEach(function(file) { var fileReader = new FileReader(); fileReader.onload = function(evt){ that.photos.push(evt.target.result) } fileReader.readAsDataURL(file) }); }, remove: function(index){ this.photos.splice(index,1) } }
忘记了,data中还要增加一个photos 变量来存放我们上传的图片
data: { content: ‘‘, // 获得用户输入的内容 photos: [] // 存取用户上传的图片 },
这时点击照相机按钮,弹出对话框,选择几张图片,点确定,可以看到输入框下面有图片显示,不过它是竖着显示,不是横向显示,样式有点问题,我就不调整了,点击右上角的关闭按钮,可以删除图片,功能完成了。
整个文件内容如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue 学习实例</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> <script src="https://unpkg.com/vue/dist/vue.js"></script> <style> body { padding-top: 100px; } .row { margin-top: 20px; } </style> </head> <body> <div class="container" id="weibo"> <!--添加一个id 提供挂载--> <h2 class="text-center">创建你的微博</h2> <!--输入框区域--> <textarea class="form-control" rows="3" v-model="content" maxlength="10"></textarea> <!--增加maxlength属性,最大字数限制 v-model数据绑定--> <!--图片显示区域--> <div class="row" v-if="photoUploaded"> <div class="col-md-12"> <figure v-for="(photo,index) in photos"> <img :src="photo" class="img-rounded"> <button type="button" class="close" @click=‘remove(index)‘>×</button> </figure> </div> </div> <!--照相机按钮和字符数提示--> <div class="row"> <div class="col-md-10"> <label class="glyphicon glyphicon-camera" for="xFile" ></label> <input type="file" multiple @change="upload" id="xFile" style="position:absolute;clip:rect(0 0 0 0);"> </div> <div class="col-md-2"> <!--v-bind指令,动态改变样式--> <span v-bind:class="{ ‘text-warning‘:under20, ‘text-danger‘: under10}">{{letterRemaining}}</span> <button type="button" class="btn btn-primary" :disabled="contentIsEmpty">转发</button> </div> </div> </div> <!--所有js都放在以下script标签中--> <script> const MAX_LETTER_LENGTH = 10; //最大输入字数 140 new Vue({ el:"#weibo", data: { content: ‘‘, // 获得用户输入的内容 photos: [] // 获取用户上传的图片 }, computed: { // 计算剩余字数 letterRemaining:function() { var remain = MAX_LETTER_LENGTH-this.content.length; if(remain == 0){ alert(‘最多输入140个字符‘) } return remain; }, // 小于20个字符 under20: function(){ return this.letterRemaining <= 20 && this.letterRemaining > 10; }, // 小于10个字符 under10: function() { return this.letterRemaining <= 10 && this.letterRemaining > 0; }, // 输入框中有没有内容 contentIsEmpty:function(){ return this.content.length == 0 }, //有没有上传图片 photoUploaded:function(){ return this.photos.length > 0 } }, methods:{
// 图片上传处理函数, 用到了h5 File 和FileReader API upload:function(e){ var that = this; var files = Array.from( e.target.files); files.forEach(function(file) { var fileReader = new FileReader(); fileReader.onload = function(evt){ that.photos.push(evt.target.result) } fileReader.readAsDataURL(file) }); },
// 删除图片处理函数 remove: function(index){ this.photos.splice(index,1) } } }) </script> </body> </html>
Vue2.0基础学习(3)--- 一个简单的实例学习