一、计算属性

什么是计算属性

计算属性,类似于过滤器,用于处理某个或者几个属性的复杂展示逻辑,但是并不会改变源数据;

示例;

  1. {
  2. ...
  3. computed: {
  4. sum() {
  5. return this.products.filter(item => item.isSelected).reduce((prev, next) => {
  6. return prev + next.productPrice * next.productCount;
  7. }, 0)
  8. }
  9. },
  10. ...
  11. }

使用计算属性

计算属性默认和普通数据一样,直接在 HTML 中使用即可;示例

  1. <td colspan="6">
  2. 总价格:{{sum | toFixed}}
  3. </td>

计算属性的set

  • computed 中的属性都有一个 get 和 set 方法,当获取这个属性时,会执行 get 方法,属性值是 get 方法的返回值;当设置 computed 中的属性时,会触发 set 方法;
  1. {
  2. ...
  3. computed: {
  4. checkAll: {
  5. get() {
  6. return this.products.every(i => i.isSelected);
  7. },
  8. set(val) {
  9. this.products.forEach(i => i.isSelected = val);
  10. }
  11. }
  12. },
  13. ...
  14. }
  • computed 中的属性都会被 vm 所代理,最终都要放到 vm 上;
  • computed 中的属性和 data 中的属性不能同名,也不能和 methods 中的属性同名
  • computed 计算属性还可以是一个函数,相当于只设置 get 的情况,函数的返回值就是计算属性的值,但是不能设置
  • 如果一个属性依赖于其他属性计算而来,那么这个属性最好用 computed

计算属性不能用在异步处理中

  • 计算属性的处理不能写在异步处理程序中,如定时器、ajax等
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <input type="text" v-model="sth"> <br>
  10. {{msg}}
  11. </div>
  12. <script src="vue.js"></script>
  13. <script>
  14. let vm = new Vue({
  15. el: '#app',
  16. data: {
  17. sth: ''
  18. },
  19. computed: {
  20. msg () {
  21. // 计算属性的处理不能写在异步处理程序中,如定时器、ajax等
  22. setTimeout(() => {
  23. if (this.sth.length > 5) {
  24. return '太长了'
  25. }
  26. if (this.sth.length < 3) {
  27. return '太少了'
  28. }
  29. }, 0)
  30. }
  31. }
  32. })
  33. </script>
  34. </body>
  35. </html>

计算属性示例代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
  7. </head>
  8. <body>
  9. <div id="app">
  10. <!--caption 只能放在 table 中使用:表头-->
  11. <!--bootstrap-->
  12. <div class="container">
  13. <div class="row">
  14. <h2 class="text-center text-danger">珠峰购物车</h2>
  15. <table class="table table-bordered">
  16. <tr>
  17. <td>
  18. 全选 <input type="checkbox" v-model="checkAll"/>
  19. </td>
  20. <td>
  21. 商品
  22. </td>
  23. <td>
  24. 单价
  25. </td>
  26. <td>
  27. 数量
  28. </td>
  29. <td>
  30. 小计
  31. </td>
  32. <td>
  33. 操作
  34. </td>
  35. </tr>
  36. <tr v-for="(product, index) in products" :key="index">
  37. <!-- {
  38. "isSelected": true,
  39. "productCover": "https://img10.360buyimg.com/cms/s80x80_jfs/t6094/107/710811867/382815/4d54717/592bf165N755a88f0.jpg",
  40. "productName": "深入浅出Node.js",
  41. "productInfo": "颜色:Node.js学习",
  42. "productPrice": 57.8,
  43. "productCount": 3
  44. }-->
  45. <td><input type="checkbox" v-model="product.isSelected"></td>
  46. <td>
  47. <img :src="product.productCover" :title="product.productName" alt="">
  48. {{product.productInfo}}
  49. </td>
  50. <td>
  51. {{product.productPrice}}
  52. </td>
  53. <td>
  54. <input type="number" v-model="product.productCount" min="1"/>
  55. </td>
  56. <td>
  57. {{product.productPrice * product.productCount | toFixed(2)}}
  58. </td>
  59. <td>
  60. <button class="btn btn-danger" @click="remove(product)">删除</button>
  61. </td>
  62. </tr>
  63. <tr>
  64. <td colspan="6">
  65. 总价格:{{sum | toFixed}}
  66. </td>
  67. </tr>
  68. </table>
  69. </div>
  70. </div>
  71. </div>
  72. <script src="axios.js"></script>
  73. <script src="vue.js"></script>
  74. <script>
  75. let vm = new Vue({
  76. el: '#app',
  77. data: {
  78. products: []
  79. },
  80. filters: {
  81. toFixed(val, num = 2) {
  82. return '¥' + val.toFixed(num)
  83. }
  84. },
  85. created() {
  86. this.getData();
  87. },
  88. computed: {
  89. sum() {
  90. return this.products.filter(item => item.isSelected).reduce((prev, next) => {
  91. return prev + next.productPrice * next.productCount;
  92. }, 0)
  93. },
  94. checkAll: {
  95. get() {
  96. return this.products.every(i => i.isSelected);
  97. },
  98. set(val) {
  99. this.products.forEach(i => i.isSelected = val);
  100. }
  101. }
  102. },
  103. methods: {
  104. getData() {
  105. axios.get('carts.json').then(({data}) => {
  106. this.products = data;
  107. })
  108. },
  109. remove(val) {
  110. // val 点击时删除的 product
  111. this.products = this.products.filter(item => item !== val);
  112. },
  113. changeAll() {
  114. // 如果全选为 true,即 this.checkAll 为 true,下面的商品的 checkbox 都要选中,如果是全选是 false,那么商品的 checkbox 也是 false
  115. this.products.forEach(item => item.isSelected = this.checkAll)
  116. },
  117. changeOne() {
  118. // 点击每一个 input 的复选框时,去校验是否有是所有的 product 中的 isSelected 都为 true,如果都是 true,那么 checkAll 结果也是 true,如果有一个是 false,那么 checkAll 就是 false,使用 every 方法
  119. this.checkAll = this.products.every(item => item.isSelected);
  120. }
  121. }
  122. })
  123. </script>
  124. </body>
  125. </html>

二、侦听器 watch

什么是侦听器属性

watch: 侦听器属性,用于监听某个属性改变,如果发生改变就会触发对应的函数

示例:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <input type="text" v-model="sth"> <br>
  10. {{msg}}
  11. </div>
  12. <script src="vue.js"></script>
  13. <script>
  14. let vm = new Vue({
  15. el: '#app',
  16. data: {
  17. sth: '',
  18. msg: '',
  19. },
  20. watch: {
  21. // computed: 计算属性
  22. // watch: 侦听器属性,用于监听某个属性改变,如果发生改变就会触发对应的函数
  23. sth(newVal, oldVal) {
  24. // 监听属性 sth,当 sth 属性发生变化时就会触发这个函数
  25. // 这个函数有两个参数,第一个是 sth 的最新值,第二个是 sth 的上一个值
  26. console.log(newVal);
  27. console.log(oldVal);
  28. setTimeout(() => {
  29. if (newVal.length > 5) {
  30. this.msg = '太长了'
  31. }
  32. if (newVal.length < 3) {
  33. this.msg = '太少了'
  34. }
  35. }, 0)
  36. }
  37. }
  38. });
  39. // 在工作中能使用 computed 尽量使用 computed 而不要使用 watch;
  40. </script>
  41. </body>
  42. </html>

三、computed 和 watch

computed 计算属性

  1. 页面加载时就求值;支持缓存,如果依赖的数据发生改变,才会重新计算;
  2. 不支持异步
  3. 如果一个属性是由其他属性计算而来,这个属性依赖其他属性,依赖发生变化时自动求取最新的计算结果

watch 计算属性

  1. 页面加载时不求值,依赖值发生改变时才求值
  2. watch 支持异步
  3. watch 只能监听一个属性的变化,如果有属性依赖这个结果,那么需要手动去重新计算这个结果;

示例代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <input type="text" v-model="firstName" />
  10. <input type="text" v-model="lastName" />
  11. {{fullName}}
  12. </div>
  13. <script src="vue.js"></script>
  14. <script>
  15. let vm = new Vue({
  16. el: '#app',
  17. data: {
  18. firstName: '宾',
  19. lastName: '马',
  20. fullName: ''
  21. },
  22. computed: {
  23. /*fullName () {
  24. return this.lastName + this.firstName;
  25. }*/
  26. },
  27. watch: {
  28. firstName(newVal, oldVal) {
  29. this.fullName = this.lastName + newVal;
  30. },
  31. lastName(nawVal, oldVal) {
  32. this.fullName = newVal + this.firstName;
  33. }
  34. }
  35. });
  36. // computed vs watch
  37. // computed
  38. // 1. 页面加载时就求值;支持缓存,如果依赖的数据发生改变,才会重新计算;
  39. // 2. 不支持异步
  40. // 3. 如果一个属性是由其他属性计算而来,这个属性依赖其他属性,依赖发生变化时自动求取最新的计算结果
  41. // watch
  42. // 1. 页面加载时不求值,依赖值发生改变时才求值
  43. // 2. watch 支持异步
  44. // 3. watch 只能监听一个属性的变化,如果有属性依赖这个结果,那么需要手动去重新计算这个结果;
  45. </script>
  46. </body>
  47. </html>

四、v-bind:class

什么是v-bind:class

  • v-bind: 绑定动态属性,v-bind 可以简写成 :
  • v-bind:class 动态绑定类名,属性值有以下常用情况:;
    • 如果是对象,对象的属性名是 class 名,属性值为 true 时类名生效,为 false 时不生效
    • 如果是三元运算符,三元运算符表达式的返回结果是生效的 class
  • :class 和原有的 class 并不会冲突,如果不同,最终会合并,如果相同会覆盖原有的;

代码示例:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <style>
  7. .x {
  8. font-size: 30px;
  9. }
  10. .y {
  11. color: red;
  12. }
  13. .z {
  14. background: lightgreen;
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div id="app">
  20. <div class="x" :class="{y: true, z: flag}">A</div>
  21. <div :class="1 ? 'y' : 'z'">C</div>
  22. </div>
  23. <script src="vue.js"></script>
  24. <script>
  25. // v-bind: 绑定动态属性,v-bind 可以简写成 :
  26. // v-bind:class 属性值有以下常用情况:;
  27. // 如果是对象,对象的属性名是 class 名,属性值为 true 时类名生效,为 false 时不生效
  28. // 如果是三元运算符,三元运算符表达式的返回结果是生效的 class
  29. // :class 和原有的 class 并不会冲突,如果不同,最终会合并,如果相同会覆盖原有的;
  30. let vm = new Vue({
  31. el: '#app',
  32. data: {
  33. class1: 'y',
  34. flag: true
  35. }
  36. })
  37. </script>
  38. </body>
  39. </html>

五、v-bind:style

什么是v-bind:style?

v-bind:style 用来绑定元素的动态样式;

:style的常见值

v-bind:style 属性值是一个对象或者数组,如果是对象,里面可以存储具体样式;数组中可以放多个样式对象;

代码示例:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <div :style="{color: 'red',width: '100px'}">A</div>
  10. <div :style="[style1, style2]">B</div>
  11. </div>
  12. <script src="vue.js"></script>
  13. <script>
  14. // v-bind:style 属性值是一个对象或者数组,如果是对象,里面可以存储具体样式;数组中可以放多个样式对象;
  15. let vm = new Vue({
  16. el: '#app',
  17. data: {
  18. style1: {background: 'pink'},
  19. style2: {color: '#000'}
  20. }
  21. })
  22. </script>
  23. </body>
  24. </html>

六、自定义指令

  • 自定义指令可以给元素增加一些特殊的功能;以拖拽为例;

代码示例:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <style>
  7. #app div {
  8. position: absolute;
  9. width: 100px;
  10. height: 100px;
  11. background: red;
  12. -webkit-user-select: none;
  13. }
  14. #app div:nth-child(2) {
  15. top: 200px
  16. }
  17. </style>
  18. </head>
  19. <body>
  20. <div id="app">
  21. <div v-drag>1</div>
  22. <div v-drag>2</div>
  23. </div>
  24. <script src="vue.js"></script>
  25. <script>
  26. let vm = new Vue({
  27. el: '#app',
  28. directives: {
  29. // 对象中存储的自定义指令
  30. drag: {
  31. inserted(el) {
  32. // 这个函数定义了指令的具体行为 第一个参数是使用这个元素的元素
  33. el.onmousedown = function (e) {
  34. this.startX = e.pageX - this.offsetLeft;
  35. this.startY = e.pageY - this.offsetTop;
  36. document.onmousemove = function (e) {
  37. el.style.left = e.pageX - el.startX + 'px';
  38. el.style.top = e.pageY - el.startY + 'px';
  39. };
  40. document.onmouseup = function () {
  41. document.onmousemove = document.onmouseup = null;
  42. }
  43. }
  44. }
  45. }
  46. }
  47. })
  48. </script>
  49. </body>
  50. </html>

七、todoList

代码示例:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <style>
  7. .del {
  8. text-decoration: line-through;
  9. color: #ccc;
  10. }
  11. </style>
  12. </head>
  13. <body>
  14. <div id="app">
  15. <div class="container">
  16. <div class="row">
  17. <div class="col-md-8">
  18. <div class="panel panel-success">
  19. <div class="panel-heading bg-warning">
  20. <h2 class="text-danger">亲~^_^,你有2件事要完成</h2>
  21. <input type="text" class="form-control" @keyup.enter="add" v-model="title">
  22. </div>
  23. <div class="panel-body">
  24. <ul class="list-group">
  25. <li class="list-group-item" v-for="(todo, index) in filterTodo" :key="index">
  26. <span :class="{del: todo.isSelected}">
  27. <input type="checkbox" v-model="todo.isSelected"> {{todo.title}}
  28. </span>
  29. <button class="btn btn-danger pull-right btn-xs" @click="remove(todo)">删除</button>
  30. </li>
  31. </ul>
  32. </div>
  33. <div class="panel-footer">
  34. <a href="#all">全部任务</a>
  35. <a href="#finish">已完成</a>
  36. <a href="#unfinish">未完成</a>
  37. </div>
  38. </div>
  39. </div>
  40. </div>
  41. </div>
  42. </div>
  43. <script src="vue.js"></script>
  44. <script>
  45. let vm = new Vue({
  46. el: '#app',
  47. data: {
  48. todos: [
  49. {
  50. isSelected: false,
  51. title: '睡觉'
  52. },
  53. {
  54. isSelected: false,
  55. title: '吃饭'
  56. }
  57. ],
  58. title: '',
  59. hash: '#all'
  60. },
  61. directives: {
  62. focus(el) {
  63. el.focus()
  64. }
  65. },
  66. created() {
  67. window.addEventListener('hashchange', () => {
  68. // 一旦页面中的 hash 值发生变化,对 data 中的 hash 重新赋值
  69. this.hash = window.location.hash;
  70. })
  71. },
  72. methods: {
  73. add () {
  74. this.todos.push({
  75. isSelected: false,
  76. title: this.title
  77. });
  78. this.title = '';
  79. },
  80. remove(val) {
  81. this.todos = this.todos.filter(item => item !== val);
  82. }
  83. },
  84. computed: {
  85. filterTodo() {
  86. switch (this.hash) {
  87. case '#all':
  88. return this.todos;
  89. case '#finish':
  90. return this.todos.filter(item => item.isSelected);
  91. case '#unfinish':
  92. return this.todos.filter(i => !i.isSelected);
  93. }
  94. }
  95. }
  96. })
  97. </script>
  98. </body>
  99. </html>