1. 计算属性

  1. <div id="app">
  2. <p>{{ reverseMsg }}</p>
  3. <p>{{msg}}</p>
  4. </div>
  5. <script src="vue.js"></script>
  6. <script>
  7. let vm = new Vue({
  8. el: '#app',
  9. data: {
  10. msg: 'hello'
  11. },
  12. computed: {
  13. reverseMsg() {
  14. return this.msg.split('').reverse().join('')
  15. }
  16. }
  17. });
  18. // 计算属性:处理某个或某些属性复杂展示逻辑,不会改变源数据;目的就是不要在模板中写太多的逻辑;
  19. </script>

2. computed

什么时候使用计算属性?

  1. 数据用来展示
  2. 需要展示的数据依赖其他数据,通过其他数据计算出来的;
  3. 当它依赖的数据发生变化时,会重新计算;

computed注意事项:

  1. computed 里面的属性会被 vm 所代理;
  2. computed 里面的属性和 data / methods / filters / 都不能重名;
  3. computed 的计算属性可以是一个函数还可以是一个对象;对象中有 get 和 set 方法,取值的时候执行 get,设置的时候执行 set;而函数形式的计算属性,只有 get 的情况,只能获取不能设置,如果设置会报错;
  4. 如果一个值需要依赖其他属性计算而来,这个时候最好用计算属性;
  1. <div id="app">
  2. <div class="container">
  3. <div class="row">
  4. <table class="table table-bordered">
  5. <tr>
  6. <td>
  7. 全选:<input type="checkbox"
  8. v-model="checkAll">
  9. </td>
  10. <td>
  11. 商品
  12. </td>
  13. <td>
  14. 数量
  15. </td>
  16. <td>
  17. 单价
  18. </td>
  19. <td>
  20. 小计
  21. </td>
  22. </tr>
  23. <tr v-for="(product, index) in carts" :key="index">
  24. <td>
  25. <input type="checkbox"
  26. v-model="product.isSelected"
  27. @change="changeOne">
  28. </td>
  29. <td>
  30. {{product.name}}
  31. </td>
  32. <td>
  33. <input type="number" v-model="product.count" min="1">
  34. </td>
  35. <td>
  36. {{product.price}}
  37. </td>
  38. <td>
  39. {{product.count * product.price | toRMB}}
  40. </td>
  41. </tr>
  42. <tr>
  43. <td colspan="5">
  44. 总价:{{total | toRMB}}
  45. </td>
  46. </tr>
  47. </table>
  48. <input type="text" v-model="total">
  49. </div>
  50. </div>
  51. </div>
  52. <script src="vue.js"></script>
  53. <script>
  54. // npm install 依赖包@版本号 指定版本号安装;如果不指定,就会按照最新的装;
  55. let vm = new Vue({
  56. el: '#app',
  57. data: {
  58. carts: [
  59. {
  60. isSelected: true,
  61. count: 3,
  62. price: 57.86,
  63. name: '车厘子'
  64. },
  65. {
  66. isSelected: true,
  67. count: 1,
  68. price: 6666,
  69. name: 'iPhoneX'
  70. }
  71. ]
  72. },
  73. filters: {
  74. toRMB(val) {
  75. return '¥' + val.toFixed(2)
  76. }
  77. },
  78. methods: {
  79. changeAll() {
  80. },
  81. changeOne() {
  82. }
  83. },
  84. computed: {
  85. // computed 里面的属性最终也会被 vm 代理,这些属性都会在 vm 身上也有一份;
  86. total: function () { // 计算属性的 getter 形式,这样声明的 total 只能读,不能写;这个属性不能修改,修改它会报错;
  87. // 首先把打钩的商品筛选出来
  88. let selected = this.carts.filter(i => i.isSelected);
  89. return selected.reduce((prev, next) => {
  90. // next 是数组项,现在是对象
  91. return prev + next.count * next.price;
  92. }, 0);
  93. },
  94. // 计算属性的 setter
  95. checkAll: {
  96. get() {
  97. // 当获取 checkAll 的时候就会执行 get 方法,并且取到的值是 get 方法的返回值
  98. return this.carts.every(item => item.isSelected);
  99. },
  100. set(val) {
  101. // 当修改 checkAll 属性的时候,会触发 set 方法,并且 val 会接收到 checkAll 的新值;
  102. // console.log(val); val 就是修改 checkAll 的时候传过来的值
  103. this.carts.forEach(item => item.isSelected = val)
  104. }
  105. }
  106. }
  107. });
  108. </script>

3. 小 demo

当 input 中输入的内容长度大于5的时候提示“太长了”,当输入的内容小于3的时候提示“太短了”

  1. <div id="app">
  2. <input type="text" v-model="text"> <br>
  3. {{msg}}
  4. </div>
  5. <script src="vue.js"></script>
  6. <script>
  7. let vm = new Vue({
  8. el: '#app',
  9. data: {
  10. text: ''
  11. },
  12. computed: {
  13. msg () {
  14. if (this.text.length > 5) {
  15. return '太长了'
  16. }
  17. if (this.text.length < 3) {
  18. return '太短了'
  19. }
  20. // 计算属性不能写在异步处理程序:ajax、定时器、Promise 的 then
  21. }
  22. }
  23. })
  24. </script>

4. 侦听器属性 watch

侦听器属性:watch 当我们需要监听一个属性的改变,当它改变的时候我们要做某些事,此时我们就需要使用侦听器属性

  1. let vm = new Vue({
  2. el: '#app',
  3. data: {
  4. text: '',
  5. msg: ''
  6. },
  7. watch: {
  8. // watch 就是侦听器属性;写在这个对象中的属性都是要被监控的;
  9. // 当被监控的属性的值发生变化的时候,就会触发对应的函数;
  10. // 属性名:被监控的属性名,例如 text;属性值是一个函数
  11. text(newVal, oldVal) {
  12. // console.log(newVal, oldVal);
  13. // newVal 是被监控属性的新值
  14. // oldVal 是被监控的属性的旧值
  15. // 侦听器属性可以使用异步;
  16. setTimeout(() => {
  17. if (newVal.length > 5) {
  18. this.msg = '太长了';
  19. } else if (newVal.length < 3) {
  20. this.msg = '太短了';
  21. } else {
  22. this.msg = '';
  23. }
  24. }, 0)
  25. }
  26. // 能用表单的事件就用事件或者使用计算属性;这两种都不行的时候再用 watch;
  27. },
  28. methods: {
  29. fn() {
  30. console.log(this.text);
  31. }
  32. }
  33. })

5. watch 和 computed 区别

computed:

  1. 页面加载时就求值,当依赖的数据发生改变时会重新求值;
  2. 不支持异步
  3. computed 可以依赖多个属性,如果被依赖的属性有一个发生变化,就会重新求值;(相当于监控多个属性)

watch:

  1. 页面加载时,watch 不执行,只有被监控的数据发生变化时才会执行对应的函数
  2. 支持异步
  3. 一个函数对应一个被监控的属性,只有当这个属性的值发生变化时才会执行这个函数

6. v-bind-class

v-bind 绑定动态属性
v-bind:class 动态绑定类名,实现动态操作样式;
v-bind:class 常见的形式:

  1. 对象,{className1: 属性值1, className2: 属性值2,…} 当属性值为 true 的时候,对应的 className 生效;
  2. 三元运算符,三元运算符的返回值是生效的类名;如果条件为 true,返回条件成立的值,否则返回条件不成立的值;
  3. 还可以是一个方法调用,最终生效的类名是方法的返回值;
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <style>
  7. .x {
  8. background: red;
  9. height: 20px;
  10. }
  11. .y {
  12. float: left;
  13. }
  14. .z {
  15. font-size: 30px;
  16. }
  17. .a {
  18. color: green;
  19. }
  20. </style>
  21. </head>
  22. <body>
  23. <div id="app">
  24. <div :ok="flag">{{notFlag}}</div>
  25. <div :class="{x: flag, z: !flag, a: flag}">
  26. <!--元素浮动以后会脱离文档流,父级元素无法识别子元素的宽高;解决方案:清浮动、给父元素一个高度-->
  27. <div class="y">A</div>
  28. </div>
  29. <div :class="0 ? 'x' : z">B</div> <!--如果要使用的类名就叫做 x,要用引号包裹 'x',因为 v-bind:class 以后,等号右侧变成了 js 表达式,不带引号的都是变量-->
  30. <div :class="getClass()">C</div>
  31. </div>
  32. <script src="vue.js"></script>
  33. <script>
  34. let vm = new Vue({
  35. el: '#app',
  36. data: {
  37. flag: true,
  38. z: 'zig-zag'
  39. },
  40. computed: {
  41. notFlag () {
  42. return !this.flag;
  43. }
  44. },
  45. methods: {
  46. getClass() {
  47. return 'abcdefg';
  48. }
  49. }
  50. });
  51. </script>
  52. </body>
  53. </html>

7. v-bind-style

v-bind:style 动态绑定行内样式
常见的情况:

  1. 一个对象,对象中都是样式例如 {color: ‘#000’, background: ‘#fff’}
  2. 一个数组,数组项是样式对象,最终会把这些对象中的样式合并到一起;
  3. 方法调用,在方法中返回一个样式对象
  1. <div id="app">
  2. <select v-model="fontColor">
  3. <option value="#ccc">灰色</option>
  4. <option value="#fff">白色</option>
  5. <option value="#00b38a">绿色</option>
  6. </select>
  7. <div :style="{color: fontColor, backgroundColor: '#000', paddingLeft: '100px'}">A</div>
  8. <div :style="ary">BCDEFG</div>
  9. <div :style="getStyle()">XYZ</div>
  10. </div>
  11. <script src="vue.js"></script>
  12. <script>
  13. let vm = new Vue({
  14. el: '#app',
  15. data: {
  16. s1: {
  17. fontSize: '20px',
  18. background: '#00b38a'
  19. },
  20. s2: {
  21. fontStyle: 'italic',
  22. fontWeight: 'bold'
  23. },
  24. ary: [
  25. {
  26. fontSize: '20px',
  27. background: '#00b38a'
  28. },
  29. {
  30. fontStyle: 'italic',
  31. fontWeight: 'bold'
  32. }
  33. ],
  34. fontColor: '#fff'
  35. },
  36. methods: {
  37. getStyle() {
  38. return {color: 'pink', background: '#000'}
  39. }
  40. }
  41. });
  42. </script>

8. 自定义指令

  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: lightgreen;
  12. -webkit-user-select: none;
  13. }
  14. #app div:nth-child(2) {
  15. top: 120px;
  16. }
  17. </style>
  18. </head>
  19. <body>
  20. <div id="app">
  21. <div v-drag>A</div>
  22. <div v-drag>B</div>
  23. </div>
  24. <script src="vue.js"></script>
  25. <script>
  26. // 指令:是以 v- 开头的行内属性,vue 赋予它特殊的功能;
  27. let vm = new Vue({
  28. el: '#app',
  29. data: {},
  30. filters: {},
  31. methods: {},
  32. computed: {},
  33. watch: {},
  34. created() {},
  35. directives: {
  36. drag: {
  37. // inserted 是指令的钩子函数
  38. inserted(el) {
  39. // el 是使用指令的元素对象
  40. // console.dir(el);
  41. el.onmousedown = function (e) {
  42. // 1. 记录初始位置
  43. this.startX = e.pageX - this.offsetLeft;
  44. this.startY = e.pageY - this.offsetTop;
  45. // 2. 把鼠标移动事件委托给 document,防止鼠标丢失
  46. document.onmousemove = function (e) {
  47. el.style.left = e.pageX - el.startX + 'px';
  48. el.style.top = e.pageY - el.startY + 'px';
  49. };
  50. // 3. 把鼠标左键抬起事件委托给 document
  51. document.onmouseup = function () {
  52. document.onmousemove = null;
  53. document.onmouseup = null;
  54. }
  55. }
  56. }
  57. }
  58. }
  59. })
  60. </script>
  61. </body>
  62. </html>

9. todolist

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <link rel="stylesheet" href="bootstrap.css">
  7. <style>
  8. .panel-footer a {
  9. margin-left: 15px;
  10. }
  11. .del {
  12. text-decoration: line-through; /*让线穿过文字,有一种删除的效果*/
  13. color: #ccc;
  14. }
  15. </style>
  16. </head>
  17. <body>
  18. <div id="app">
  19. <div class="container">
  20. <div class="row">
  21. <div class="col-md-8">
  22. <div class="panel panel-success">
  23. <div class="panel-heading bg-warning">
  24. <h2>{{undone}}</h2>
  25. <input type="text"
  26. v-focus
  27. class="form-control"
  28. @keydown.enter="add"
  29. v-model="title">
  30. </div>
  31. <div class="panel-body">
  32. <ul class="list-group">
  33. <li v-for="(item, index) in filterTodo"
  34. :key="index"
  35. class="list-group-item">
  36. <span><input type="checkbox"
  37. v-model="item.isSelected">
  38. <!--<del>{{item.title}}</del> 让文字有一种删除效果,横线划过文字-->
  39. </span>
  40. <span :class="{del: item.isSelected}">
  41. {{item.title}}
  42. </span>
  43. <button class="btn btn-danger btn-xs pull-right"
  44. @click="remove(index)">删除
  45. </button>
  46. </li>
  47. </ul>
  48. </div>
  49. <div class="panel-footer">
  50. <a v-for="(item, index) in config"
  51. :key="index"
  52. :href="item.hash">{{item.name}}</a>
  53. </div>
  54. </div>
  55. </div>
  56. </div>
  57. </div>
  58. </div>
  59. <script src="vue.js"></script>
  60. <script>
  61. let config = [
  62. {
  63. name: '全部',
  64. hash: '#all'
  65. },
  66. {
  67. name: '已完成',
  68. hash: '#finished'
  69. },
  70. {
  71. name: '未完成',
  72. hash: '#unfinished'
  73. }
  74. ];
  75. let vm = new Vue({
  76. el: '#app',
  77. data: {
  78. title: '',
  79. todoList: [
  80. {
  81. isSelected: false,
  82. title: '吃饭饭'
  83. },
  84. {
  85. isSelected: false,
  86. title: '睡觉觉'
  87. }
  88. ],
  89. config,
  90. hash: '#all'
  91. },
  92. created() {
  93. window.addEventListener('hashchange', () => {
  94. this.hash = location.hash;
  95. });
  96. },
  97. computed: {
  98. undone() {
  99. let num = this.todoList.filter(i => !i.isSelected).length;
  100. let msg = '';
  101. if (num) {
  102. msg = `亲,^_^ 你还有${num}件事没干`;
  103. } else {
  104. msg = `亲,你好棒啊!今天的事情做完了,赶紧休息一下吧~`
  105. }
  106. return msg;
  107. },
  108. filterTodo() {
  109. // 根据当前 hash 的值过滤:
  110. switch (this.hash) {
  111. case '#all':
  112. return this.todoList;
  113. case '#finished':
  114. return this.todoList.filter(item => item.isSelected);
  115. case '#unfinished':
  116. return this.todoList.filter(item => !item.isSelected);
  117. }
  118. }
  119. },
  120. methods: {
  121. add() {
  122. // 添加任务
  123. this.todoList.push({
  124. title: this.title,
  125. isSelected: false
  126. });
  127. this.title = ''; // 添加到列表后,清空 input
  128. },
  129. remove(index) {
  130. this.todoList.splice(index, 1);
  131. }
  132. },
  133. directives: {
  134. focus: {
  135. inserted(el) {
  136. el.focus();
  137. }
  138. }
  139. }
  140. })
  141. </script>
  142. </body>
  143. </html>

10. vue 版选项卡

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <style>
  7. * {
  8. margin: 0;
  9. padding: 0;
  10. }
  11. ul, li {
  12. list-style: none;
  13. }
  14. .wrapper {
  15. width: 800px;
  16. margin: 30px auto;
  17. border: 1px solid #000;
  18. }
  19. .header {
  20. overflow: hidden;
  21. border-bottom: 1px solid #000;
  22. }
  23. .header li {
  24. float: left;
  25. box-sizing: border-box;
  26. width: 200px;
  27. height: 35px;
  28. line-height: 35px;
  29. text-align: center;
  30. border-right: 1px solid #000;
  31. cursor: pointer;
  32. -webkit-user-select: none;
  33. }
  34. .header li:last-child {
  35. border-right: none;
  36. }
  37. .header li.active {
  38. background: yellow;
  39. }
  40. .card {
  41. display: none;
  42. height: 400px;
  43. line-height: 400px;
  44. font-size: 40px;
  45. text-align: center;
  46. text-decoration: underline;
  47. }
  48. div.card.active {
  49. display: block;
  50. }
  51. </style>
  52. </head>
  53. <body>
  54. <div id="app">
  55. <div class="wrapper">
  56. <ul class="header">
  57. <li v-for="(item, index) in headers"
  58. :key="index"
  59. :class="{active: selected == index}"
  60. @click="headerClick(index)">{{item}}</li>
  61. </ul>
  62. <div v-for="(item, index) in cards"
  63. :key="index"
  64. class="card"
  65. :class="{active: selected == index}">
  66. {{item}}
  67. </div>
  68. </div>
  69. </div>
  70. <script src="vue.js"></script>
  71. <script>
  72. let headers = ['唱', '跳', 'RAP', '篮球'];
  73. let cards = ['《鸡你太美》', '钢管', 'RAP-鸡你太美', 'NBA-鸡你太美'];
  74. let vm = new Vue({
  75. el: '#app',
  76. data: {
  77. selected: 0,
  78. headers,
  79. cards
  80. },
  81. methods: {
  82. headerClick(index) {
  83. this.selected = index;
  84. }
  85. }
  86. })
  87. </script>
  88. </body>
  89. </html>

补充

什么是 disable?
disabled 属性:为 true 时按钮会被禁用;为 false 时是可用状态
disabled 属性是特殊的属性,存在就是 true;v-bind 以后,vue 发现它绑定值是 false 的时候,会移除这个属性;

reduce 方法是专门用来求和的

  1. // 0
  2. let ary = [1, 2, 3, 4];
  3. // ary.reduce((前一个, 后一个) => {}, 初始值);
  4. let total = ary.reduce((prev, next) => {
  5. // 第一次执行的时候:prev 是初始值0;初始值不传的时候 prev 是第一项;
  6. // 后面每一次执行的时候,prev 是上一次回调函数的返回值
  7. console.log(prev, next);
  8. return prev + next;
  9. }, 0);
  10. console.log(total); // reduce 最后的返回结果是最后一次回调执行的结果