https://juejin.im/user/580327ee0e3dd900570cf3ab


基础一:

模板语法

1.文本

数据绑定最常见的形式就是使用 “Mustache” 语法(双大括号)的文本插值:
Mustache 标签将会被替代为对应数据对象上 world 属性的值 ,而且一直会监听world的值,一但改变会跟着改变

应用场景 : 可以说处处都用的到

<template>
    <div>
        <p>hello {{world}}</p>
        <p v-text="'hello ' + world"></p>
        <p>{{`hello ${world}`}}</p>
        <p v-text="`hello ${world}`"></p>
        <button @click="world='ziksang'">改变wrold值</button>
     </div>
</template>

<script>

export default {
     data () {
         return {
              world : "world"
         }
     }
}

</script>

以上用了四种写法
第一种“Mustache” 语法(双大括号)写法
第二种 用v-text的指今写法
第三种和第四是对es6写法的拓展写法,称模板字符串

2.v-once

通过指令我们可以对文本值进行一次性赋值操作,只进行第一次的数据渲染,如果再次改变值,文本值也不会改变

应用场景 : 一般是用在组件树中传递时,导致组件数据一层一层传递时,变改了不需要改变的场景,用v-once可以避免在组件数中只需用一次性赋值操作

<template>
    <div>
        <p v-once>hello {{world}}</p>
        <button @click="world='ziksang'">改变wrold值</button>
     </div>
</template>

<script>

export default {
     data () {
         return {
              world : "world"
         }
     }
}

</script>

我们再次点击button时,我们会发现没有任何改变,值行了所谓的一次性赋值

3.纯html

我们在解析的不是文件而是一个html格式的时候放在v-text中或者{{}}就会被当作一个文本解析,所以我们此时要用v-html指令进行解析,在1.0中支持{{{}}}这种格式,为了防止xss功击,去除了这个功能

常用场景 : 当我们在跟前后台对接口数据时,后台会返回一个html格式,一般是后台操作界面编译的样式文本,此时我们就要用v-html来进行解析

<template>
    <div>
        <p v-html='html'></p>
     </div>
</template>

<script>

export default {
     data () {
         return {
              html : `<span style='color : red;'>显示红色的字你就解析成功了</span>`
         }
     }
}
</script>

此时在界面我们就能看到显示红色的字你就解析成功了这几个字样,就是被成功解析了

4.属性

在vue中属性这个东西很关健,在组件与组件中数据传递时会很有用,但是对于属性的解析我们不能用{{}}“Mustache” 语法(双大括号)写法,我们同时还是要用指令去解析,那就是v-bind:*,同时我们可以简写用v-bind语法糖 即可
如果我们先不考虑组件传递,我们就是考虑简单的给元素加属性

应用场景 在组件中传递时需要用,其它元素上的绑定属性都需要这个功能

<template>
    <div>
        <a :href='href'>href</a>
        <p :id='id'>id</p>
        <img :src="src" alt="图片">
        <button :disabled = 'disabled'>按钮</button>
     </div>
</template>

<script>

export default {
     data () {
         return {
              id : 2,
              href : 'http://www.baidu.com',
              src : 'https://cn.vuejs.org/images/logo.png',
              disabled : true
         }
     }
}

</script>

我们在属性中支持number string boolean类型,以上显示能在界面中看出都能正常进行和原本属性所预期的,不用来绑定的属性可以直接属性赋值,如果一定要通过data数据选项中返回的值一定要加 

5.使用javascript表达式

应用场景 :
在业务场景中一些方法判断或者简单的过滤,那我们可以用JavaScript表达式,能让代码更简洁,更清晰,比方说用一个三元表达式。。等等

<template>
     <div>
        <div id="method1">
            <p>{{ count < 0 ? '+' : '-'}} {{count + 1}}</p>
            <button @click='count ++'>增加</button>
            <button @click='count --'>减少</button>
        </div>
        <div id="method2">
            <input type="text" v-model='message'>
            <h1>{{ message.split('').reverse().join('') }}</h1>
        </div>
     </div>
</template>

<script>

export default {
     data () {
         return {
              count : 0,
              message : ""
         }
     }
}

</script>

在id:method1 里,我们做了一个对count进行+1,对count进行三元表达式来进行判断做出不同的显示
在id:method2 里,我进行了v-model指令和h1里message进行了双向绑定,随着message的改变h1里的值随着表达式的的改变而改变
注意示项,在官方提出来的这几种是不可行的

这是语句,不是表达式 
{{ var a = 1 }}
流控制也不会生效,请使用三元表达式 
{{ if (ok) { return message } }}

6.修饰赋

修饰符(Modifiers)是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。

应用场景 :
对于一些特殊的指令操作时需要,比方说对组件加事件,组织元素的默认行为,组织冒泡。。等等一系列,官方文档有详细解说每一个修饰赋的具体用途

再次提示主逻辑代码都是写在.App.vue中,所有其它的组件代码都是写在componentes里

首我们先对components文件夹中创建一个myButton组件

<template>
    <button>按钮</button>
</template>

<script>
export default {

}
</script>

再在app.vue中写入

<template>
     <div>
         <my-button @click.native="buttonClick"></my-button>
     </div>
</template>

<script>
import myButton from './components/myButton.vue'
export default {
     components : {
         myButton
     },
     methods : {
         buttonClick () {
             alert("原生点击")
         }
     }
}

</script>

我们可以尝试一下如果我们去掉.native的话你会发现组件根本出alert(原生点击)的弹框,这是为什么呢,正常的情况在一个单个组件内部自己使用v-on的事件,ok都不会有问题,如果在一个组件上定义一个指令事件,必须要用.native,这里大家一定要注意

####推荐很实用的demo
我们用jQuery的时候经常会遇到这种场景,当我们一个layer层弹出来的时候,内部是一个超过整个屏幕长度的滚动,当我们滑动layer的时候会导致里面的滚动也会一起滚动起来,这个是一件很操蛋的事,在网上也有着很多相关解决的办法,vue可以通过修饰符来解决这个问题
index.html文件中加一个视口度适配,更好的来查看效果

 <meta name=”viewport” content=”width=device-width, initial-scale=1, maximum-scale=1″>

app.vue文件中

<template>
     <div>
         <ul>
             <li v-for="item in list">
                 <h1>{{item}}</h1>
             </li>
         </ul>
         <div @touchmove.prevent class="layer"></div>
     </div>
</template>

<script>
import myButton from './components/myButton.vue'
export default {
     data () {
         return {
             list : [1,2,3,4,5,6,7,8,9,10]
         }
     }
}

</script>

<style>
body,html {width:100%;height:100%}
h1{margin-top:10rem;}
.layer{width:100%;height:100%;position:fixed;top:0;left:0;z-index:99;background:rgba(0,0,0,.5)}
</style>

此时我们打开chrome调试工具的时候我们会发当我们滑动layer层的,里面的滚动不会再滚动了,我们再试着把.prevent给去掉,会发现滚动再次出现,这个原理就是event.preventDefault来阻止默认事件事执行的。
实现原理,我们对layer层把它的touchmove滑动事件给干掉了,就不会触发滚动区域的滑动事件,个人认为这个场景每个项目都会用的到

再做一个表单的简单示例

<template>
     <div>
        <input type="text" v-model.trim="content">
        <input type="text" v-model = 'content'>
     </div>
</template>

<script>
export default {
     data () {
         return {
             content : ""
         }
     }
}
</script>

这个我们能发现当加入.trim的修饰符的时候给到第二个input的时候都是去掉前后空白符的,这个功能也就是去掉前后空白符,这也是很常用的场景,一些表单当用户输入的时候,有些用户会打出一个前后空白符,有一次我保用户数据给数据库,操作的时候还代码还好好的,突然一个空白字符引发的一场血案,要注意细节点

6. 过滤器

Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示:

其实本质上过滤器也就是一个或多个表达式,但是有极条件控制的表达式或者长业务逻辑的表达式写在mustache不够清楚,明了

在v-bind中也就是2.10的时候才支持这个功能,这个大家要对这个版本有一定的了解,正确的使用

应用场景 :
我说说我遇到过的应用场景,再把我应用的场景再加深一点逻辑
1.首先我们从后台拿到10个用户的手机,但是要把前7位掩码,做成*,然判断如果手机尾数是偶数的话,我们就显示已中奖,不是的话显示谢谢参与

<template>
     <div>
         <p v-for='tel in telephone'>{{tel | newtel}}</p>
     </div>
</template>

<script>
export default {
     data () {
         return {
             telephone : [
                 10000000000,
                 10000000001,
                 10000000002,
                 10000000003,
                 10000000004,
                 10000000005,
                 10000000006,
                 10000000007,
                 10000000008,
                 10000000009,
                 10000000000,
             ]
         }
     },
     filters : {
         newtel (value) {
             if(!value) return ''
             value = value.toString().substr(7,4)
             value = '*'.repeat(7)+value
             let endMember = value.substr(-1,1)
             if(endMember % 2){
                 value = value+'中奖'
             }else{
                 value = value+'谢谢参与'
             }
             return value
         }
     }
}
</script>

代码会析,在filters选项里有个newtel的函数,value则是函数中固定的一个参数,代码过滤前的值,但最后在管道符里我们不用把这个固定的参数写在调用里面,如果没有,则返回"",我们再取后四位,在前7位拼接7个*,这里我用到了es6新特性,repeat这个方法,原本我想用padStart可惜是es7的新性,babel不支持,再截取尾数,如果尾数%2是偶数则再后面加一个中奖,否则谢谢参与,最后把这个value的最终值返回出去

7.结合过滤器参数与v-bind过滤

以上过滤器原理也跟大家说了,我们进一步深化一下,如果两都结合使用

应用场景

当后台传给很多链接的地址的时候,每个链接要带上不同的参数,比如https//www.baidu.com?user=ziksang&age=20,针对于这种场景我给大家实战一下

<template>
     <div>
         <a v-for='(url,index) in urlList' :href='url.url | getquery(url.name,url.age)'>{{url.url}}</a>
     </div>
</template>

<script>
export default {
     data () {
         return {
             urlList : [
                 {url : 'http://www.baidu.com',name :'ziksang',age : 20},
                 {url : 'http://www.google.com',name :'ziksang2',age : 30}
             ]
         }
     },
     filters : {
         getquery (value,name,age) {
             if(!value) return ""
             return `${value}?name=${name}&age=${age}`
         }
     }
}
</script>

在v-bind管道符中的getquery(第一个参,第二个参)
第一个参就是传给fitler里getquery里的第二个参数,就是name
第二个参就是传给fitler里getquery里的第三个参数,就是age
因为第一个参数是默认的
这里我大量用了v-for来进行循环,如果有看不懂的话,等讲到第基础二或者三的时候再回头看看这里的v-for用法你就一目了然了

8.缩写

<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>
<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>

这没有什么可以多讲的,就是一个语法糖,方便大家书写

写到这里,只是简单的讲一下模板语法,但是我个人认为,还展拓了很多其它知识点,刚好如果那些知识点你不会的,可以去查一查做一个预热,在接下来几天中,我们把上出第二篇基础(v-for 列表渲染)


基础二:

v-for 列表渲染

  1. v-for array 数组渲染

我们用v-for指令根据一组数组的选项列表进行渲染。 v-for指令需要以item in items形式的特殊语法, items 是源数据数组并且 item 是数组元素迭代的别名。

应用场景:
通常情况下,进入一个活动要展示出所有参加活动的人员,里面有姓名,年龄,报名时间等等,就先举这三个列子,那后台肯定会返回一个arraylist数组集合,集合中每个元素肯定是一个对象,那我们如何去把这个数组集合高效率,合理的渲染到页面上,,再拿到用户id去到下一个页面查询memberDetail祥情,用过jq的同学肯定知识很烦繁,一般肯定是用arttemplate这种模板引擎

<template>
     <div>
         <ul>
             <li v-for="(item,index) in memberList" @click="memberDetail(index)">
                 <span>{{item.custName}}</span>
                 <span>{{item.age}}</span>
                 <span>{{item.joinTime}}</span>
             </li>
         </ul>
     </div>
</template>

<script>
export default {
     created () {
         //就当看作是ajax在初始化进入的时候从后台获取的用户列表数据
         this.memberList = [
            {custName : "ziksang1",age:20,joinTime : "2014-01-02",custId:1},
            {custName : "ziksang2",age:21,joinTime : "2014-01-03",custId:2},
            {custName : "ziksang3",age:22,joinTime : "2014-01-04",custId:3},
            {custName : "ziksang4",age:23,joinTime : "2014-01-05",custId:4},
         ]
     },
     data () {
         return {
             memberList : []
         }
     },
     methods : {
         memberDetail (index) {
             sessionStorage.custId = this.memberList[index].custId
         }
     }
}
</script>

我觉得这个demo太常用了,逐行分析
1.如果在template模板里面用到了数据必须先在data选项里先声明赋值,我们先给memberList给予一个[]数组
2.在created函数里我们就当作模拟从后台拿到的数据,然后赋值给data选项里memberList,此时memberList就有后台拿到的值了
3.(item,index) in memberList是vue自己封装的一个语法结构
一.item代表集合中的每一个元素,此时每一个元素就是一个对象
二.index代表集合每一个元素的下标
三.memberList是所要循环的数组
4.为什么我们要把 (item,index) in memberList放在每一个循环dom上,那就是li标签 只有在li循环体节点上和循环体所有子节点上拿到item这个对象里面的所有属性
5.@click="memberDetail(index)" 这里用了一个点击方法,我们把index作为了方法的参数,目的是什么,这个index代表每一个循环出来dom的下标,通过点击,我们可以拿到对应的用户id可以说拿到每一个用户的任意值,然后在methods我们进行操作,我们可以能过sessonStorage来存放,用来过度到下一个用户祥情页,来获取所有用户详情,我们可以打开谷歌浏览器,当我们用鼠标点击的时候,可以发现sessionStorage里的变化

整个流程是无论是开发任意中型项目必备的

2.template v-for

如同 v-if 模板,你也可以用带有 v-for 的 <template> 标签来渲染多个元素块,循环块区域

应用场景 :
当我们循环渲染时,我们如果有多个欠套不合理的关系下,一般情况下肯定是在最外层套一个div元素,这个我认为显的不是很清楚,我们用template来代替渲染,不但能省去很dom节点,而且也能解决欠套不合理的情况

<template>
     <div>
         <template v-for="item in list">
              <p>{{item.content}}</p>
              <img :src="item.img" alt="">
              <p class="divider"></p>
         </template>
     </div>
</template>

<script>
export default {
     data () {
         return {
             list : [
                 {content : 'ziksang',img :'https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=1301074775,1382810875&fm=80&w=179&h=119&img.JPEG'},
                 {content : 'ziksang2',img :'https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=1312092207,1376369244&fm=80&w=179&h=119&img.JPEG'}
             ]
         }
     }
}
</script>
<style>
body,html{
    width:100%;
    height:100%
}
.divider{
    width:100%;
    height:1px;
    background:black;
}
</style>

如果我们不想多创节点,又不想让p元素做为根节点,我们就可以用template来做渲染,在chorme开发者工具上,也能证实,最外层的的template会自动消失,不会创造出多余的节点。

3. v-for Object 对象渲染

你也可以用 v-for 通过一个对象的属性来迭代。

应用场景 :
就像第一个列子如果到了详情页,我们要展示一个用户详情,如果后端都给你排好序了,如何正确的方便展示出来,用最快,最便捷的方法那就是v-for 迭代对象,一般情况下展示一个用户详情,后台肯定返回一个用户对象给你

<template>
     <table>
         <template>
            <tr>
                <td v-for="(value,key,index) in memberDetail">{{key}}</td>
            </tr>
            <tr>
                <td v-for="(value,key,index) in memberDetail">{{value}}</td>
            </tr>
         </template>
     </table>
</template>

<script>
export default {
     created () {
        //比方说我们这里拿到前面的custId传给后台拿到用户数据
        this.memberDetail = {
                 name : 'ziksang',
                 age : 20,
                 address : "xxx省xxxx市",
                 tel : "15921898427"
             }
     },
     data () {
         return {
             memberDetail : {} 
         }
     }
}
</script>
<style>
body,html{
    width:100%;
    height:100%
}
.divider{
    width:100%;
    height:1px;
    background:black;
}
</style>

1.(value,key) in memberDetail
value代表属性值
key 代表属性,
index代表每个dom节点的下标索引
两者都是一一对应的,通过第一个例子讲解,我相信这个例子很简单了

4. v-for 整数迭代

这个看看demo就可以了,我几乎没用过

<div>
  <span v-for="n in 10">{{ n }}</span>
</div>

4. v-for 和 组件

在自定义组件里,你可以像任何普通元素一样用 v-for 。

应用场景 :
比方拿掘金来说,每个用户展现的文章列表中,每个用户的文章可以做成一个统一组件,通过父组件来进行数据的传递,循环出所有人的文章,就形成了文章列表,而且可以在热门,最新,或者专栏里面复用,只需要通过数据传递,是一种相当好的模式

在components文件夹里创一个myArticle文件

<template>
    <div>
        <p>{{artList.name}}</p>
        <p>{{artList.startTime}}</p>
        <p>{{artList.content}}</p>
        <p>{{artList.good}}</p>
    </div>
</template>

<script>
export default {
    props : ['artList']
}
</script>

用props选项来接收父组件传入的数据,渲染组件

App.vue文件

<template>
     <div>
     <my-article v-for='item in artList' :art-list='item'></my-article>
     </div>
</template>

<script>
import myArticle from "./components/myArticle.vue"
export default {
    components : {
        myArticle
    },
    created () {
        //比方说我们是从后台拿到的文章集合
        this.artList = [
            {name : 'ziksang1' , startTime : '1小时前' , content:'aaaaaaaaa',good : 1},
            {name : 'ziksang2' , startTime : '2小时前' , content:'bbbbbbbbb',good : 2},
            {name : 'ziksang3' , startTime : '3小时前' , content:'ccccccccc',good : 3},
            {name : 'ziksang4' , startTime : '4小时前' , content:'ddddddddd',good : 4}
        ]
     },
     data () {
         return {
             artList : [] 
         }
     }
}
</script>

这个需要逐行分析
1.首先们引入组件
2.v-for来循环组件
3.把每个文章的数据用 :art-list='item'传给myArticle组件
如我们进行下拉加载和上拉刷新的话,只要把取到的数据再次从新赋给子组件就可以了,而且我们还可以在各个页面来重复复用这个组件,减少开发工作量

4. 数组更新检测

1.数组变异

说的通俗一点,实是就是我们用Array.prototype里提供的原型方法里我们能直接改掉data选项里的数据,触发了视图更新,那就是叫数组变异方法
官方给大家列了只有那些方法能触发视图更新
1.push()
2.pop()
3.shift()
4.unshift()
5.splice()
6.sort()
7.reverse()

应用场景,我们比方说我们是绝境的后台控制者,我们无聊看那个文章不爽,我们对文章进行操作,这个只是我瞎说玩玩的场景,就是用情景去模拟用法的场景

App.vue文件

<template>
     <div>
     <my-article v-for='item in artList' :art-list='item'></my-article>
     <button @click='push'>添加文章</button>
     <button @click='pop'>尾部去除一篇文章</button>
     <button @click='unshift'>在头部加入一条数据</button>
     <button @click='shift'>在头部去除一条数据</button>
     <button @click='reverse'>把所有数据进行反转</button>
     <button @click='clear'>清除所有数据</button>
     </div>
</template>

<script>
import myArticle from "./components/myArticle.vue"
export default {
    components : {
        myArticle
    },
    created () {
        //比方说我们这里拿到前面的custId传给后台拿到用户数据
        this.artList = [
            {name : 'ziksang1' , startTime : '1小时前' , content:'aaaaaaaaa',good : 1},
            {name : 'ziksang2' , startTime : '2小时前' , content:'bbbbbbbbb',good : 2},
            {name : 'ziksang3' , startTime : '3小时前' , content:'ccccccccc',good : 3},
            {name : 'ziksang4' , startTime : '4小时前' , content:'ddddddddd',good : 4}
        ]
     },
     data () {
         return {
             artList : [] 
         }
     },
     methods : {
          push () {
            this.artList.push({
                name : 'ziksang5' ,
                 startTime : '5小时前' ,
                  content:'eeeeeeeeee',
                  good : 5})
        },
        pop () {
            this.artList.pop()
        },
        shift () {
            this.artList.shift()
        },
        unshift () {
            this.artList.unshift({
                name : 'ziksang3',
                age : 40
            })
        },
        reverse () {
            this.artList.reverse()
        },
        clear () {
            this.artList = []
        }
     }
}
</script>

我们发现我们点这些按钮的时候,myArticle的视图同时跟着改变,这就是我们需要的效果就是变异方法根本的意思,能通过Array.prototype里的原形方法改变data选项artList数组触发视图改变的方法就是变异方法

2.数组非变异

no-mutation methods(非变异方法),不能通过Array.prototype里的原形方法改变data选项artList数组触发视图改变的方法就是非变异方法,其余的方法都是操作后,形成一个返回值,所有操作只是返回了一个新数组,而不会触发视图更新
1.filter(), 2.concat(), 3.slice(), 4.map()

<template>
  <div>
      <ul>
         <li v-for = " (item,index) in list" v-text='`${item} - ${index} `'></li>
      </ul>
      <button @click="filter">数组进行取余过滤</button>
      <button @click="concat">数组进行合并</button>
      <button @click="map">数组印射</button>
      <button @click="slice">返回截取后的数组</button>
  </div>
</template>

<script>

export default {
    data () {    
        return {
            list : [ 1, 2, 3, 4],
            list2 : [ 7, 8, 9, 0 ]
        }
    },
    methods : {
        filter () {
            this.list = this.list.filter((item)=>{
                return item % 2
            })
        },
        concat () {
            //以上两种方法都可以
            //1.第一种是es6的解构操作符
            //2.第二种是传统的数组合并
            this.list = [...this.list,...this.list2]
            //this.list = this.list.concat(this.list2)
        },
        map () {
            this.list = this.list.map(item =>{
                return `${item} map`
            })
        },
        slice () {
            this.list = this.list.slice(2)
        }
    }
}

</script>

那我们最终目的还是要通过方法来改变视图更新,怎么办,很简单,只要把形成的返回值再从新赋值给data选项里的数组就可以了,看api文档说起来很高深的样子,但根本原理就是Array.prototye原型上方法导的鬼,我相信大家肯定很清楚了,上面我还在代码中对...解构新特性给大家演示了一个例子

4. 注意示项

官方提示:
由于 JavaScript 的限制, Vue 不能检测以下变动的数组:
当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength

<template>
  <div>
      <ul>
         <li v-for = " (item,index) in list" v-text='`${item} - ${index} `'></li>
      </ul>
      <button @click="change3">改变数组第2个值,改成0</button>
      <button @click="change4">改变数组第2个值,改成5</button>
  </div>
</template>

<script>
import Vue from 'vue'
export default {
    data () {    
        return {
            list : [ 1, 2, 3, 4],
            list2 : [ 7, 8, 9, 0 ]
        }
    },
    methods : {
        //通过下标来改变整个数组里的值也是行不通的
        changeList () {
            this.list[2] = 3
        },
        //通过数组长度改变改个数组里的值是行不通的
        changeList2 () {
            this.length = 1
        },
        //第一我们可以通过,vue.set实列方法来改变,但我们要在开头再引一入下vue包
        // 1 第一个值代表需要改变的数组
        // 2 第二个代表改变那一项
        // 3 第三个代表改成什么值
        //样式语法 Vue.set(example1.items, indexOfItem, newValue)
        change3 () {
            Vue.set(this.list,1,0)
        },
        //通过 Array.prototype.splice 数组原型上的方法来改变整个数组的长度或者内容
        //这个方法大家肯定常用,我就不细说了
        change4 () {
            this.list.splice(1,1,5)
        }
    }
}

</script>

这里因为操作问题我就在代码中直接写了注释,更加能让大家清楚的了解,如何通过那些方法改变数组的长度,改变下标的某一个元素

5. 显示过滤/排序效果

对于我的理解我就定义他叫副本过滤,在不改动data选项原数组的同时,对新数组进行改变,同时也不创造出多余的数据值,那我们这里就要用到一个基础3所要讲的计算属性
应用场景 :
我在这里做了一个小demo当原数组里面随机改变值的同时,副本基于原数组的创建出一个副本数组跟着过滤的不同而改变
有两个视图:
1.第一个视图,我们点击按纽改变1-10的随机数
2.第二个视图,随着随机数的改变来判断是否为偶数是偶数的则过滤出来

<template>
  <div>
      <ul>
         <li v-for = " (item,index) in list" v-text='`${item} - ${index} `'></li>
      </ul>
      <ul>
         <li v-for = " (item,index) in filter" v-text='`${item} - ${index} `'></li>
      </ul>
      <button @click="randomList">随机重置数组的值</button>
  </div>
</template>

<script>
export default {
    data () {    
        return {
            list : [ 1, 2, 3, 4]
        }
    },
    //检测计算 随着原数组用随机数改变的同时,来筛选出不同的过滤结果,筛选结果是原数组的里面为偶数的值拼成一个新数组
    //1.我们还可以用methods方法,但是不能一直监控
    computed : {
        filter () {
            return this.list.filter((item)=>{
                 return item % 2
            })
        }
    },
    methods : {
        //这里就是随着改变data原数组里的值
        randomList () {
            this.list = this.list.map(item => {
                return item+Math.round(Math.random()*9+1)
            })
        }
    }
}

</script>

随着我们点击按钮的同时,两个视图同时更新,本质上就是在data选项里过滤出一个新数组,同时这个新数组不会影响到data选项里的数组,这个就叫做副本过滤,通过自己的想像可以做出很多不同的demo例子


基础三:

计算属性

在计算属性中,在我们前面基础1和基础2分享中有两个地方可以计算属性,我们认为所有属性的计算都是变向的在过滤值,通过数据的不断变化计算出不同的值和操作不同的方法
1.模板内的表达式
2.属性v-bind:里的可以进行表达式
3.指令中我们也可以进行表达式
以上三者优势在那里,简洁,省代码量,如果只是一个小操作,比方说一些简单的数值++,字符拼接,三元表达式,那使用相当好
以上三者的劣势,一但要处理的逻辑复杂,如果用到if()流程控制语句,变量赋值,代码量大了就很难于维护,我们可能就会想到用filter,那filter的场景适用于那里后面再说,先看一下简单的demo

App.vue

<template>
   <div>
        <p @click="count++">{{count+'分'}}</p>
        <input v-model='message'>
        <p>{{message.split('').reverse().join('') }}</p>
   </div>
</template>
<script>
    export default {
        data () {
            return {
                count : 0,
                message : ''
            }
        }
    }
</script>

从上面不难看出
1.模板内使用字符拼接,ok代码很清楚目的的所在
2.通过指令点击,也能少一个methods方法,也很简洁
3.通过输入框输入文字,在p标签里面进行计算转换的时候,那我觉得语意化就不是很强烈了,那用什么办法呢,用filter

用filter的优势
filter给我们用于计算和过滤一些模板表达式和v-bind:属性表达式一些弊端的地方进行计算,他会返回当前计算的值,可以进行传参在多地方共用这个过滤方法,
劣势
如果我们要计算多个数据不同变化结合而成的地方那filter就能难过到了,本质上filter我认为就是1v1,对单个数据进行过滤,可以进行传参,同方法,但不同参,非常适用

<template>
   <div>
        <input v-model='message'>
        <p>{{message | reverseString}}</p>
   </div>
</template>
<script>
    export default {
        data () {
            return {
                message : ''
            }
        },
        filters : {
            reverseString (value) {
                if(!value) return ''
                value = value.split('').reverse().join('') 
                return value
            }
        }
    }
</script>

我们把上个例子中第二个例子反转字符串这个方法用filter来实现,很明显示代码量多了那么一点点,但是整体的语意化相当明显了,让人一看这里就要进行一些过滤计算,能过reverseString,就能知道是反转字符串

1. computed

以上说了这么多前面的用法,因为我们模板语法和filter过滤来对computed的应用场景做一个铺垫,划分更加明确
computed可以做那些呢,适用于什么场景呢?
他规避了模板语法和filter两个所有的劣势,他的优势在于通过计算所有依赖的数据进行计算,然后返回一个值,记住可以依赖方法里所有的数据,只要一个数据发生变化,则会从新计算,来更新视图的改变,verygood,好东西看看怎么玩

应用场景 :
这是一个简单实用的应用场景,后面再做一个好玩的应用场景
我们填写备注的时候我们会有一个字数的限制和字数显示的限制,通过输入字符,我们要提醒用户还有输入多少字

<template>
   <div>
        <textarea v-model="content" :maxlength="totalcount"></textarea>
        <p>你还可以输入{{reduceCount}}字</p>
   </div>
</template>
<script>
    export default {
        data () {
            return {
                totalcount : 200 , //总共只给输入200字
                content : ''
            }
        },
        computed : {
            reduceCount () {
                return this.totalcount - this.content.length
            }
        }
    }
</script>

通过一直监听文字的字符的长度来出发compunted里reduceCount这个方法,来再次进行计算,返回值给视图,让视图进行变化。这也是一个很简单的demo例子,那很好,前面我说了可以监听多个数据,只要一个数据变了,整个方法就会从新计算,反馈到视图,这个方法只是一个简单的应用,请看下个demo例子,你们就能看懂一切的一切

这个例子是我想了一个多小时才次决定的例子,那就最就萨德事件使中韩足球最热门的一个事件,做IT做DEMO就是要玩起来,政治我们先不讨论,我们用技术来感化一下我们的爱国情
demo场景分析
1.我们要声明那些数据
一.比赛时间 用time来维护
二.比赛双方的进球数 用对象来维护
三.比赛的播报情况 在90分钟内,要显示中国领先或者韩国队领先或者双方僵持,如果到了90分种我们要显示中国队赢还是韩国队赢,还是平局
第三个数据非常关键,我们用什么来维护,可以说比赛情况是多样化的,用一个数据去定死维护,不符合场景,那我先列出那通过改变这些变化,我们不但要检测双方的进球数,还要通过时间来比对,是90分钟内,还是已经结束比赛了,来显示不同的文案。所以我们要不断监听两个维护的数据,一是比赛时间,二是比赛两队进球数

  <template>
   <div>
        <h1>比赛时间{{time}}s</h1>
        <h2>直播播报{{result}}</h2>
        <div>
             <p>中国队进球数:{{team.china}}</p>
             <button @click="team.china++">点击中国队进一球</button>
             <p>韩国队进球数:{{team.korea}}</p>
             <button @click="team.korea++">点击韩国队进一球</button>
        </div>
   </div>
</template>
<script>
    export default {
        created () {
            let time =  setInterval(()=>{
                this.time++
                if(this.time == 90){
                    clearInterval(time)
                }
            },1000)
        },
        data () {
            return {
                time : 0,
                team : {
                    china : 0,
                    korea : 0
                }
            }
        },
        computed : {
            result () {
               if(this.time<90){
                   if(this.team.china>this.team.korea){
                       return '中国队领先'
                   }else if(this.team.china<this.team.korea){
                       return '韩国队领先'
                   }else{
                       return '双方僵持'
                   }
               }else{
                   if(this.team.china>this.team.korea){
                       return '中国队赢'
                   }else if(this.team.china<this.team.korea){
                       return '韩国队赢'
                   }else{
                       return '平局'
                   }
               }
            }
        }
    }
</script>

上面的我用点击事件来进行双方进球数,把上面这个demo运行一下我们可以充分的理解computed的涵意,说到底是观察一个或者多个数据,每当其中一个数据改变的时候,这个函数就会从新计算,还有就是通过观察所有数据来维护一个状态,就是所谓的返回一个状态值,从上面这个demo我们就可以很容易的知道computed到底用在什么场景,如何去维护返回一个多状态的场景

2.methods vs computed

在methods和computed可以做同样的事,但是,computed可以进行缓存,什么意思呢,就是在上个例子我们对比赛时间和两个球队的进球数进行了检测数据,如果随着时间的改变,但是球数没动,对于computed来说只会从新计算这个球数会进入缓存,不会再次计算,而从新计算的是这个时间,而且页面的dom更新也会出发methods来从新计算属性,所以如果不想让计算属性进入缓存,请求methods,但是我推荐用computed,语议化好一点麻,什么选项里就应该改做什么事,methods里面就是应该来管事件的。个人认为,同样的操作我就不演示demo了,看看官方的用法理解一下就可以了

3.computed vs watch

computed和watch都可以做同一件事,就像跑步运动员都可以跑步,但是分100米和1000米,术业有专功麻,两个选项都是对数据进行时时监听,但是两个的适用场景就不一样了

一.computed前面说了是适用于对多数据变动进行监听,然后来维护一个状态,就是返回一个状态

二.watch是对一个数据监听,在数据变化时,会返回两个值 ,一个是value(当前值),二个是oldvalue是变化前的值,我们可以通过这些变化也可以去维护一个状态,但是不符合场景,主要用于什么地方呢?主要用于监听一个数据来进行复杂的逻辑操作

  <template>
   <div>
        <h1>比赛时间{{time}}s</h1>
        <h2>直播播报{{result}}</h2>
        <div>
             <p>中国队进球数:{{team.china}}</p>
             <button @click="team.china++">点击中国队进一球</button>
             <p>韩国队进球数:{{team.korea}}</p>
             <button @click="team.korea++">点击韩国队进一球</button>
        </div>
   </div>
</template>
<script>
    export default {
        created () {
            let time =  setInterval(()=>{
                this.time++
                if(this.time == 90){
                    clearInterval(time)
                }
            },1000)
        },
        data () {
            return {
                time : 0,
                team : {
                    china : 0,
                    korea : 0
                },
                result : "双方僵持"
            }
        },
        watch : {
            time (value,oldval) {
               if(value<90){
                   if(this.team.china>this.team.korea){
                       this.result =  '中国队领先'
                   }else if(this.team.china<this.team.korea){
                       this.result =  '韩国队领先'
                   }else{
                       this.result =  '双方僵持'
                   }
               }else{
                   if(this.team.china>this.team.korea){
                       this.result =  '中国队赢'
                   }else if(this.team.china<this.team.korea){
                       this.result =  '韩国队赢'
                   }else{
                       this.result =  '平局'
                   }
               }
            },
            team (value,oldval){
                if(this.time<90){
                   if(value.china>value.korea){
                       this.result =  '中国队领先'
                   }else if(value.china<value.korea){
                       this.result =  '韩国队领先'
                   }else{
                       this.result =  '双方僵持'
                   }
               }else{
                   if(value.china>value.korea){
                       this.result =  '中国队赢'
                   }else if(value.china<value.korea){
                       this.result =  '韩国队赢'
                   }else{
                       this.result =  '平局'
                   }
               }
            }
        }
    }
</script>

以上代码和computed的产生的效果一模一样,但是很明显,就像我对computedwatch阐述过了应用场景,这个场景只是维护了一个比赛的状态,而不牵扯到逻辑操作,虽然也能完成,很明显,无论从代码量的比对,还是可读性,还是可维护性的比对都不胜于computed,但是说到底谁更强大呢,我还是老实的说watch更强大,虽然他有场景的局限性,但是他可以做牵扯到计算属性的一切操作,缺点watch只能一个一个监听

watch应用场景
我想信图片预加载大家肯定都有接触过,当图片量大的时候,为了保证页面图片都加载出来的时候,我们才把主页面给显示出来,再进行一些ajax请求,或者逻辑操作
那此时你用computed对这种监听一个数据然后进行一系列逻辑操作和ajax请求,那watch再适合不过了,如果用computed的话那你连实现都实现不了,只有用watch监听

  <template>
   <div v-show=show>
       <img src="https://img.alicdn.com/simba/img/TB14sYVQXXXXXc1XXXXSutbFXXX.jpg" alt="">
       <img src="//img.alicdn.com/tfs/TB1iZ6EQXXXXXcsXFXXXXXXXXXX-520-280.jpg_q90_.webp" alt="">
       <img src="https://img.alicdn.com/simba/img/TB1C0dOPXXXXXarapXXSutbFXXX.jpg" alt="">
       <img src="//img.alicdn.com/tfs/TB1iZ6EQXXXXXcsXFXXXXXXXXXX-520-280.jpg_q90_.webp" alt="">
   </div>
</template>
<script>
    export default {
        mounted () {
            var _this = this
            let imgs = document.querySelectorAll('img')
            console.log(imgs)
            Array.from(imgs).forEach((item)=>{
                let img = new Image()
                img.onload = ()=>{
                    this.count++
                }
                img.src=item.getAttribute('src')
            })
        },
        data () {
            return {
                count : 0,
                show : false
            }
        },
        watch : {
            count (val,oldval) {
                if(val == 4){
                    this.show = true
                    alert("加载完毕")
                    //然后可以对后台发送一些ajax操作
                }
            }
        }
    }
</script>

我们可以发现发四张图片都加载完毕的时候页面才显示出来

根据完方有一句话说的很重要的一句

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的 watcher 。这是为什么 Vue 提供一个更通用的方法通过 watch 选项,来响应数据的变化。当你想要在数据变化响应时,执行异步操作或开销较大的操作,这是很有用的。

基于这个官方的理解再总结我个人的整体理解。给出computed和watch的总结,记住这几点的总结,在做项目的时候想想这些总结,选择你的应用方法

computed :

监听多个数据或者一个数据来维护返回一个状态值 ,只要其中一个或多个数据发生了变化,则会从新计算整个函数体,从新返回状态值

watch :
只有一个一个监听据,只要这个数据发生变化,就会在返回两个参数,第一个是当前的值,第二个是变化前的值,每当变化的时候,则会触发函数体的里的逻辑行为,来进逻辑后续操作

其实我觉得计算属性也好,computed,watch这几个都不是有多难,如果浅层面上看很容易理解,如果从深层面上看,很多小伙伴会存在什么问题,就是会滥用,混用,这些计算属性,我想通过这些demo例子讲解和分析,我相信你又上一层楼了,ok终于可以完结这篇了,下一篇我想看看大家想学什么可以给我留言,如果那个多我就先讲那个
1.事件处理
2.表单控件
3.条件渲染
4.class 和 style 绑定





Logo

前往低代码交流专区

更多推荐