1.Vue方法、计算属性及监听器

在vue中处理复杂的逻辑的时候,我们经常使用计算属性、方法及监听器。

  1. methods:方法:它们是挂载在Vue对象上的函数,通常用于做事件处理函数,或自己封装的自定义函数。
  2. computed:计算属性:在Vue中,我们可以定义一个计算属性,这个计算属性的值,可以依赖于某个data中的数据。或者说:计算属性是对数据的再加工处理。
  3. watch:监听器:如果我们想要在数据发生改变时做一些业务处理,或者响应某个特定的变化,我们就可以通过监听器,监听数据的变化,从而做出相应的反应。

    1.1.computed 计算属性

    计算属性是根据依赖关系进行缓存的计算,并且只在需要的时候进行更新。

    1. <div id="app">
    2. <p>原数据:{{msg}}</p>
    3. <p>新数据:{{reversedMsg}}</p>
    4. </div>
    5. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    6. <script>
    7. let vm = new Vue({
    8. el: '#app',
    9. data: {
    10. msg:'hello world!'
    11. },
    12. computed:{
    13. reversedMsg(){
    14. return this.msg.split('').reverse().join('');
    15. }
    16. }
    17. });
    18. </script>

    一个案例:根据商品数量修改总价

    1. <div id="app">
    2. <table width="100%" style="text-align: center;">
    3. <tr>
    4. <th>商品编号</th>
    5. <th>商品名称</th>
    6. <th>商品单价</th>
    7. <th>商品数量</th>
    8. <th>合计</th>
    9. </tr>
    10. <tr>
    11. <td>1</td>
    12. <td>小米10</td>
    13. <td>{{price}}</td>
    14. <td>
    15. <button @click="subtract">-</button>
    16. {{quantity}}
    17. <button @click="add">+</button>
    18. </td>
    19. <td>{{totalPrice}}</td>
    20. </tr>
    21. </table>
    22. </div>
    23. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    24. <script>
    25. let vm = new Vue({
    26. el: '#app',
    27. data: {
    28. price:2999,
    29. quantity:1
    30. },
    31. computed:{
    32. totalPrice(){
    33. return this.price*this.quantity;
    34. }
    35. },
    36. methods:{
    37. add(){
    38. this.quantity++;
    39. },
    40. subtract(){
    41. this.quantity--;
    42. }
    43. }
    44. });
    45. </script>

    1.2.methods 方法

    在使用vue的时候,可能会用到很多的方法,它们可以将功能连接到事件的指令,甚至只是创建一个小的逻辑就像其他函数一样被重用。接下来我们用方法实现上面的字符串反转。

    1. <div id="app" >
    2. <p>原数据:{{msg}}</p>
    3. <p>新数据:{{reversedMsg()}}</p>
    4. </div>
    5. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    6. <script>
    7. let vm = new Vue({
    8. el: '#app',
    9. data: {
    10. msg:'hello world!'
    11. },
    12. methods:{
    13. reversedMsg(){
    14. return this.msg.split('').reverse().join('');
    15. }
    16. }
    17. });
    18. </script>

    虽然使用computed和methods方法来实现反转,两种方法得到的结果是相同的,但本质是不一样的。
    计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变的时候才会重新求值,这就意味着只要message还没有发生改变,多次访问reversedMessage计算属性立即返回的是之前计算的结果,而不会再次执行计算函数。
    而对于methods方法,只要发生重新渲染,methods调用总会执行该函数。
    如果某个computed需要的遍历一个极大的数组和做大量的计算,可以减小性能开销,如果不希望有缓存,则用methods。

    1.3.watch 监听器

    watch能够监听数据的改变。监听之后会调用一个回调函数。 此回调函数的参数有两个:

  4. 更新后的值(新值)

  5. 更新前的值(旧值)

    1.3.1.监听基本数据类型

    下面使用watch来监听商品数量的变化。如果商品数量小于1,就重置成上一个值。

    1. <div id="app">
    2. <table width="100%" style="text-align: center;">
    3. <tr>
    4. <th>商品编号</th>
    5. <th>商品名称</th>
    6. <th>商品单价</th>
    7. <th>商品数量</th>
    8. <th>合计</th>
    9. </tr>
    10. <tr>
    11. <td>1</td>
    12. <td>小米10</td>
    13. <td>{{price}}</td>
    14. <td>
    15. <button @click="subtract">-</button>
    16. {{quantity}}
    17. <button @click="add">+</button>
    18. </td>
    19. <td>{{totalPrice}}</td>
    20. </tr>
    21. </table>
    22. </div>
    23. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    24. <script>
    25. let vm = new Vue({
    26. el: '#app',
    27. data: {
    28. price: 2999,
    29. quantity: 1
    30. },
    31. computed: {
    32. totalPrice() {
    33. return this.price * this.quantity;
    34. }
    35. },
    36. methods: {
    37. add() {
    38. this.quantity++;
    39. },
    40. subtract() {
    41. this.quantity--;
    42. }
    43. },
    44. watch:{
    45. quantity(newVal,oldVal){
    46. console.log(newVal,oldVal);
    47. this.quantity = newVal<=0?oldVal:newVal
    48. }
    49. }
    50. });
    51. </script>

    1.3.2.深度监听

    在上面的例子中,监听的简单的数据类型,数据改变很容易观察,但是当需要监听的数据变为对象类型的时候,上面的监听方法就失效了,因为上面的简单数据类型属于浅度监听,对应的对象类型就需要用到深度监听,只需要在上面的基础上加上deep: true就可以了。

    1. <div id="app">
    2. <table width="100%" style="text-align: center;">
    3. <tr>
    4. <th>商品编号</th>
    5. <th>商品名称</th>
    6. <th>商品单价</th>
    7. <th>商品数量</th>
    8. <th>合计</th>
    9. </tr>
    10. <tr>
    11. <td>1</td>
    12. <td>小米10</td>
    13. <td>{{goods.price}}</td>
    14. <td>
    15. <button @click="subtract">-</button>
    16. {{goods.quantity}}
    17. <button @click="add">+</button>
    18. </td>
    19. <td>{{totalPrice}}</td>
    20. </tr>
    21. </table>
    22. </div>
    23. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    24. <script>
    25. let vm = new Vue({
    26. el: '#app',
    27. data: {
    28. goods:{
    29. price: 2999,
    30. quantity: 1
    31. }
    32. },
    33. computed: {
    34. totalPrice() {
    35. return this.goods.price * this.goods.quantity;
    36. }
    37. },
    38. methods: {
    39. add() {
    40. this.goods.quantity++;
    41. },
    42. subtract() {
    43. this.goods.quantity--;
    44. }
    45. },
    46. watch:{
    47. goods:{
    48. handler(newVal,oldVal){
    49. /**
    50. * 注意:虽然使用深度监听,可以监听到对象的改变。
    51. * 但是,由于是对象类型,所以newVal与oldVal都指向同一个对象。
    52. * 所以,newVal与oldVal中的quantity都是改变后的新值。
    53. */
    54. console.log(newVal,oldVal);
    55. this.goods.quantity = newVal.quantity<=0?oldVal.quantity:newVal.quantity;
    56. },
    57. deep:true
    58. }
    59. }
    60. });
    61. </script>

    2.Vue操作数组时的注意事项

    由于 JavaScript 语言本身的限制,当我们对数组进行某些操作时,Vue 可能不会检测到数组元素的变化,那么进而视图也不会响应。比如:

    1. <div id="app">
    2. <p v-for="(item,index) in userArr">
    3. {{item.userId}},{{item.userName}},{{item.userSex}}
    4. </p>
    5. <button @click="clear">清空</button>
    6. </div>
    7. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    8. <script>
    9. let vm = new Vue({
    10. el: '#app',
    11. data: {
    12. userArr: [{
    13. userId: 1,
    14. userName: '张三',
    15. userSex: '男'
    16. }, {
    17. userId: 2,
    18. userName: '李四',
    19. userSex: '女'
    20. }, {
    21. userId: 3,
    22. userName: '王五',
    23. userSex: '男'
    24. }]
    25. },
    26. methods: {
    27. clear() {
    28. this.userArr.length = 0;
    29. console.log(this.userArr.length); //0
    30. }
    31. }
    32. });
    33. </script>

    上面实例中,将数组的长度设置为0后,数组确实被清空了。但是Vue却不能检测到数组的变化,所以页面视图也不会响应。
    为了解决这个问题,Vue给我们提供了如下解决方案: Vue 提供一组观察数组的变异方法(就是这些方法会改变原始数组,所以才会被Vue检测到),使用它们就可以触发视图更新。 这些方法有7个:push()、pop()、shift()、unshift()、splice()、sort()、reverse()

    1. <div id="app">
    2. <p v-for="(item,index) in userArr">
    3. {{item.userId}},{{item.userName}},{{item.userSex}}
    4. </p>
    5. <button @click="clear">清空</button>
    6. </div>
    7. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    8. <script>
    9. let vm = new Vue({
    10. el: '#app',
    11. data: {
    12. userArr: [{
    13. userId: 1,
    14. userName: '张三',
    15. userSex: '男'
    16. }, {
    17. userId: 2,
    18. userName: '李四',
    19. userSex: '女'
    20. }, {
    21. userId: 3,
    22. userName: '王五',
    23. userSex: '男'
    24. }]
    25. },
    26. methods: {
    27. clear() {
    28. //使用Vue中提供的变异方法
    29. this.userArr.splice(0,this.userArr.length);
    30. }
    31. }
    32. });
    33. </script>

    3.Vue的表单绑定

    v-bind实现了数据的单向绑定,将vue实例中的数据同元素属性值进行绑定,接下来看一下vue中的数据双向绑定v-model。

    3.1.v-mode实现表单绑定

    1. <div id="app">
    2. <h3>注册</h3>
    3. <form>
    4. 用户名:<input type="text" v-model="myForm.username"/><br>
    5. 密码:<input type="password" v-model="myForm.password" /><br>
    6. 确认密码:<input type="password" v-model="myForm.beginpassword" /><br>
    7. 性别:<input type="radio" v-model="myForm.sex" value="0" />
    8. <input type="radio" v-model="myForm.sex" value="1" /><br>
    9. 爱好:<input type="checkbox" v-model="myForm.like" value="0" />读书
    10. <input type="checkbox" v-model="myForm.like" value="1" />体育
    11. <input type="checkbox" v-model="myForm.like" value="2" />旅游<br>
    12. 国籍:<select v-model="myForm.nationality">
    13. <option value="0">中国</option>
    14. <option value="1">美国</option>
    15. <option value="2">英国</option>
    16. </select><br>
    17. 个人简介:<textarea v-model="myForm.brief" rows="5" cols="30"></textarea><br>
    18. <input type="button" value="提交" @click="handler" />
    19. </form>
    20. </div>
    21. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    22. <script>
    23. let vm = new Vue({
    24. el: '#app',
    25. data: {
    26. myForm: {
    27. username: '',
    28. password: '',
    29. beginpassword: '',
    30. sex: 0,
    31. like: [0, 1, 2],
    32. nationality: 0,
    33. brief: '',
    34. level: 0
    35. }
    36. },
    37. methods: {
    38. handler: function() {
    39. console.log(this.myForm.username);
    40. console.log(this.myForm.password);
    41. console.log(this.myForm.beginpassword);
    42. console.log(this.myForm.sex);
    43. console.log(this.myForm.like);
    44. console.log(this.myForm.nationality);
    45. console.log(this.myForm.brief);
    46. }
    47. }
    48. });
    49. </script>

    3.2.v-model修饰符

    v-model中还可以使用一些修饰符来实现某些功能:

  6. v-model.lazy 只有在input输入框发生一个blur时才触发,也就是延迟同步到失去焦点时。

  7. v-model.trim 将用户输入的前后的空格去掉。
  8. v-model.number 将用户输入的字符串转换成number。
    1. <div id="app">
    2. <!-- 输入数据会延迟到失去焦点时才绑定 -->
    3. {{lazyStr}}<input type="text" v-model.lazy="lazyStr" /><br>
    4. <!-- 输入数据会自动转换为number,所以可以直接进行运算 -->
    5. {{numberStr+1}}<input type="text" v-model.number="numberStr" /><br>
    6. <!-- 输入数据会自动去除前后空格 -->
    7. {{trimStr}}<input type="text" v-model.trim="trimStr" /><br>
    8. </div>
    9. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    10. <script>
    11. let vm = new Vue({
    12. el: '#app',
    13. data: {
    14. lazyStr: '',
    15. numberStr:'',
    16. trimStr:''
    17. }
    18. });
    19. </script>

    4.过滤器

    过滤器是对即将显示的数据做进一步的筛选处理,然后进行显示,值得注意的是过滤器并没有改变原来的数据,只是在原数据的基础上产生新的数据。
    过滤器分全局过滤器和本地过滤器(局部过滤器)。

    4.1.全局过滤器

    下面定义一个全局过滤器,用于在数据前加上大写的VUE。需要注意的是,全局过滤器定义必须始终位于Vue实例之上,否则会报错。
    1. <div id="app">
    2. {{message | toAdd}}
    3. </div>
    4. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    5. <script>
    6. Vue.filter("toAdd", function(value) {
    7. return 'VUE' + value
    8. })
    9. var demo = new Vue({
    10. el: '#app',
    11. data: {
    12. message: '过滤器',
    13. }
    14. })
    15. </script>

    注意:

    1. Vue.filter() 后有两个参数:过滤器名,过滤器处理函数。
    2. 过滤器处理函数也有一个参数:要过滤的数据。

4.2.本地过滤器

本地过滤器存储在vue组件中,作为filters属性中的函数,我们可以注册多个过滤器存储在其中。

  1. <div id="app">
  2. <p>{{message | Reverse}}</p>
  3. <p>{{message | Length}}</p>
  4. </div>
  5. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  6. <script>
  7. var demo = new Vue({
  8. el: '#app',
  9. data: {
  10. message: '过滤器',
  11. },
  12. filters: {
  13. Reverse: function(value) {
  14. if (!value){
  15. return '';
  16. }
  17. return value.toString().split('').reverse().join('');
  18. },
  19. Length: function(value) {
  20. return value.length;
  21. },
  22. },
  23. })
  24. </script>

4.3.过滤器传参

过滤器也是可以传递参数的。

  1. <div id="app">
  2. {{msg1 | toDou(1,2)}}
  3. {{msg2 | toDou(10,20)}}
  4. </div>
  5. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  6. <script>
  7. Vue.filter('toDou', function(value, param1, param2) {
  8. if (param2 < 10) {
  9. return value + param1;
  10. } else {
  11. return value + param2;
  12. }
  13. });
  14. new Vue({
  15. el: '#app',
  16. data: {
  17. msg1: 9,
  18. msg2: 12,
  19. }
  20. });
  21. </script>

注意:过滤器处理函数的第一个参数,仍然是要过滤的数据。从第二个参数开始,才是过滤器本身要传递的参数。

4.4.串联过滤器

多个过滤器可以串联使用

  1. <div id="app">
  2. {{money | prefix | suffix}}
  3. </div>
  4. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  5. <script>
  6. Vue.filter("prefix", function(value) {
  7. return '¥' + value
  8. })
  9. Vue.filter("suffix", function(value) {
  10. return value + '元'
  11. })
  12. var demo = new Vue({
  13. el: '#app',
  14. data: {
  15. money:10
  16. }
  17. })
  18. </script>