如何创建实例?

每一个实例都是通过 Vue 函数创建一个新的Vue实例开始的:

  1. let vm = new Vue({
  2. //选项
  3. })

可以通过内存图来看待这个实例函数:
image.png

  • 该函数把Vue的实例命名为 vm ,vm对象封装了对视图的所有操作,包括数据读写、事件绑定、DOM更新。
  • vm 的构造函数是Vue,按照ES6的说法, vm 所属的类是Vue。
  • optioinsnew Vue 的参数,一般称之为选项或构造选项。

options里面有什么?

官方文档列出了该选项有什么:文档

注意:

  • 红色:必学、
  • 黄色:高级需要费点脑子、
  • 绿色:稍微说一下就记住、
  • 紫色:比较特殊,重点讲、
  • 蓝色:不常用,可学可不学、
  • 灰色:不需要学习,用到再看文档

options的五类属性:
数据:

  • data:内部数据,支持对象和函数,优先用函数。
  • props:外部数据,可以是数组或对象,用于接收来自父组件的数据。
  • methods:事件处理函数或者是普通函数。
  • computed:Vue组件。所有 gettersetterthis 上下文自动地绑定为Vue实例。
  • watch:一个对象,键是需要观察的表达式,值是对应回调函数。值也是可以是方法名,或者包含选项的对象。
  • propsData:创建实例时传递props。主要作用是方便测试。

DOM:

  • el:挂载点,提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以用$mount代替。
  • template:一个字符串模版作为Vue实例的标识使用。
  • renderError:只在开发环境下工作。
  • render:字符串模板的代替方案,允许你发挥JavaScript最大的编程能力。

生命周期钩子:

  • created:实例创建完成之后被立即调用,但还没挂载到DOM节点。
  • mounted:实例挂载到DOM节点后被调用。
  • updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在之后会调用该钩子。
  • destroyed:实例销毁后调用。
  • activated:被keep-alive缓存的组件激活时调用。
  • deactivated:被keep-alive缓存的组件停用时调用。
  • beforeCreate:~之前调用
  • beforeMount:同上
  • beforeUpdate:同上
  • beforeDestroy:同上
  • errorCaptured

资源:

  • components:包含Vue实例可用组件的哈希表。
  • filters:包含Vue实例可用过滤器组件的哈希表。
  • directives:指令。

组合:

  • mixins:复制,减少data等操作。
  • extends:也是复制,允许声明扩展另一个组件。
  • provide/inject:需要两个一起使用
    • provide:一个对象或者返回一个对象的函数。
    • inject:一个字符串数组或者一个对象。
    • parent

其它:

  • 先不看


入门


el

想挂载到想挂载的节点,甚至可以替换挂载的节点。

public/index.html :

  1. <div id="jeff">jeff</div>

src/main.js :

  1. new Vue({
  2. el: `#jeff`,
  3. template: `<p>Vue成功</p>`
  4. })

最后在预览链接里可以看到,最后显示的是“Vue成功”。

或者也可以代替 el 属性,效果是一样的:

  1. new Vue({
  2. template:`<p>Vue成功</p>`
  3. }).$mount('#jeff')

data

内部存放数据的,并且支持对象和函数:

  1. new Vue({
  2. data: {
  3. n:0 //存放n属性为0的data对象。
  4. }
  5. })

函数的写法:

  1. new Vue({
  2. data:function(){
  3. return {
  4. n: 0
  5. }
  6. }
  7. //上面可以缩写成:
  8. data(){
  9. return {
  10. n:0
  11. }
  12. }
  13. })

在组件(文件后缀为.vue)里必须使用函数的写法,当然也请尽量写函数的写法。

methods

可以写事件处理的函数:

  1. new vue({
  2. data(){
  3. n:0,
  4. array: [1,2,3,4,5,6,7,8]
  5. },
  6. temoplate: `
  7. <div>
  8. {{n}}
  9. <button @click="add">+1</button>
  10. <hr/>
  11. {{filter()}}
  12. </div>
  13. `,
  14. methods:{
  15. add(){
  16. this.n +=1
  17. },
  18. filter(){
  19. return this.array.filter(i = i % 2 === 0)
  20. }
  21. }
  22. })

components

使用vue组件

  1. import Demo from './Demo.vue'
  2. const vm = new Vue({
  3. components:{
  4. jeff: Demo //把Demo组件命名为jeff
  5. //如果是下面组件名和命名是一致的,那么可以直接简写成:Demo
  6. Demo:Demo
  7. //简写:
  8. Demo
  9. },
  10. template:`
  11. <div>
  12. <jeff/> 这里就使用了名为jeff的组件。
  13. </div>
  14. `
  15. })

上面使用了vue的components,还有一种方法是使用js的写法:

  1. import Demo from './Demo.vue'
  2. vue.component('jeff',{
  3. data(){
  4. return {
  5. n:'jeff'
  6. }
  7. },
  8. template:`
  9. <div>{{n}}</div>
  10. `
  11. })
  12. const vm = new Vue({
  13. template:`
  14. <div>
  15. <jeff/> 这里就使用了名为jeff的组件。
  16. </div>
  17. `
  18. })

组件:
顾名思义,就是可以和别的文件组合在一起的文件。

props

接受外部数据

src/Demo.vue:

  1. <template>
  2. <div class="red">
  3. {{message}}
  4. <button @click="fn">call fn</button>//可以接受props
  5. </div>
  6. </template>
  7. <script>
  8. export default{
  9. props:['message','fn'], //声明两个分别名为‘message’和'fn'的props。
  10. }
  11. </script>
  12. <style>
  13. .red {
  14. border: 1px solid #42b983;
  15. }
  16. </style>

src/main.js

  1. import Demo from './Demo.vue'
  2. new Vue({
  3. components:{Demo},
  4. //下面传了一个字符串“你好”
  5. template:`
  6. <div>
  7. <Demo message="你好">
  8. </div>
  9. `
  10. })

如果我想传一个data里的数据n如下:
需要注意,如果message前面没有加上”:”,就会传字符串。

  1. import Demo from './Demo.vue'
  2. new Vue({
  3. components:{Demo},
  4. data:{
  5. n:0
  6. },
  7. //下面传了一个字符串“n”,没错字符串"n"
  8. template:`
  9. <div>
  10. <Demo message="n">
  11. </div>
  12. `,
  13. //传入一个数据的写法如下:
  14. template:`
  15. <div>
  16. <Demo :message="n">
  17. </div>
  18. `,
  19. //但我又想冒号和字符串怎么办:
  20. template:`
  21. <div>
  22. <Demo :message=" 'n' ">
  23. </div>
  24. `,
  25. //当然也可以传一个函数,注意冒号:
  26. template:`
  27. <div>
  28. <Demo :message="n">
  29. <Demo :fn="add">
  30. </div>
  31. `,
  32. methods:{
  33. add(){
  34. .....
  35. }
  36. }
  37. })

深入进阶

computed

计算属性,类似与公平过滤器,对于绑定到视图的数据进行处理,并监听变化而执行对应的方法。

栗子:

  1. new Vue({
  2. data: {
  3. user: {
  4. email: "1234565@qq.com",
  5. nickname: "小明",
  6. phone: "13234567"
  7. }
  8. },
  9. // DRY don't repeat yourself
  10. // 不如用 computed 来计算 displayName
  11. template: `
  12. <div>
  13. {{user.nickname || user.email || user.phone}}
  14. <div>
  15. {{user.nickname || user.email || user.phone}}
  16. </div>
  17. {{user.nickname || user.email || user.phone}}
  18. </div>
  19. `,
  20. }).$mount("#app");

上面的栗子里,template里的{{user.nickname || user.email || user.phone}} ,如果有 user.nickname 就展示,如果没有而 user.email 有,那么展示,同理 user.phone

假如,上面的栗子里,有100个这样的 {{user.nickname || user.email || user.phone}} ,然后需求变了,要求user.nickname 先展示,后展示user.phone !难道我要把改100次这样的代码吗?

因此计算属性就来了:

  1. new Vue({
  2. data: {
  3. user: {
  4. email: "1234565@qq.com",
  5. nickname: "小明",
  6. phone: "13234567"
  7. }
  8. },
  9. computed: {
  10. displayName(){
  11. const user = this.user;
  12. return user.nickname || user.email || user.phone;
  13. }
  14. },
  15. // DRY don't repeat yourself
  16. // 不如用 computed 来计算 displayName
  17. template: `
  18. <div>
  19. {{displayName}}
  20. <div>
  21. {{displayName}}
  22. <button @click="add">set</button>
  23. </div>
  24. </div>
  25. `
  26. }).$mount("#app");

添加100个 {{displayName}} 就可以了,当然可以配合v-for。

也可以使用函数来改变里面的计算属性(使用getter和setter):

  1. new Vue({
  2. data: {
  3. user: {
  4. email: "1234567@qq.com",
  5. nickname: "小明",
  6. phone: "12345678"
  7. }
  8. },
  9. computed: {
  10. displayName: {
  11. get() {
  12. const user = this.user;
  13. return user.nickname || user.email || user.phone;
  14. },
  15. set(value) {
  16. console.log(value);
  17. this.user.nickname = value;
  18. }
  19. }
  20. },
  21. // DRY don't repeat yourself
  22. // 不如用 computed 来计算 displayName
  23. template: `
  24. <div>
  25. {{displayName}}
  26. <div>
  27. {{displayName}}
  28. <button @click="add">set</button>
  29. </div>
  30. </div>
  31. `,
  32. methods: {
  33. add() {
  34. console.log("add");
  35. this.displayName = "圆圆";
  36. }
  37. }
  38. }).$mount("#app");

小结:计算属性是基于它们的依赖进行缓存的。只在相关依赖发生改变时它们才会重新求值。注意,“displayName”不能在组件的 propsdata 定义,否则会报错。

watch 侦听

官方文档

用途:watch是一个侦听的动作,用来观察和响应Vue数据的变化实例上的数据变动。当data发生变化的时候,就会发生一个回调,它有两个参数,一个newValue(修改后的data的数据),一个oldValue(原来的data数据)

简单例子:

  1. new Vue({
  2. el: '#app',
  3. data:{
  4. cityName: 'shanghai'
  5. },
  6. watch: {
  7. cityName(){
  8. console.log('cityName改变了:'+ this.cityName)
  9. }
  10. },
  11. template:`
  12. <div>
  13. <div id="app">
  14. <input type='text' v-model='cityName'/>
  15. </div>
  16. </div>
  17. `
  18. })

运行后发现,每改一次 cityName ,watch就执行一次。

注意:不应该使用箭头函数定义watcher函数。因为箭头函数没有this,它的this会继承它的父级函数,但它的父级函数是window,导致箭头函数的this指向window,而不是Vue实例。

可以写一个撤销功能的代码,如下:

  1. new Vue({
  2. data: {
  3. n: 0,
  4. history: [],
  5. inUndoMode: false
  6. },
  7. watch: {
  8. n: function(newValue, oldValue) {
  9. console.log(this.inUndoMode);
  10. if (!this.inUndoMode) {
  11. this.history.push({ from: oldValue, to: newValue });
  12. }
  13. }
  14. },
  15. template: `
  16. <div>
  17. {{n}}
  18. <hr />
  19. <button @click="add1">+1</button>
  20. <button @click="add2">+2</button>
  21. <button @click="minus1">-1</button>
  22. <button @click="minus2">-2</button>
  23. <hr/>
  24. <button @click="undo">撤销</button>
  25. <hr/>
  26. {{history}}
  27. </div>
  28. `,
  29. methods: {
  30. add1() {
  31. this.n += 1;
  32. },
  33. add2() {
  34. this.n += 2;
  35. },
  36. minus1() {
  37. this.n -= 1;
  38. },
  39. minus2() {
  40. this.n -= 2;
  41. },
  42. undo() {
  43. const last = this.history.pop();
  44. this.inUndoMode = true;
  45. console.log("ha" + this.inUndoMode);
  46. const old = last.from;
  47. this.n = old; // watch n 的函数会异步调用
  48. this.$nextTick(() => {
  49. this.inUndoMode = false;
  50. });
  51. }
  52. }
  53. }).$mount("#app");
  • 模拟 computed ,但没有 computed 好用,建议能用 computed 的场景用 computed

注意:watch是异步操作!

怎么样才算变化?

watch 只要数据变化就会执行,那么,怎样才算变化?

例子:

  1. new Vue({
  2. data: {
  3. n: 0,
  4. obj: {
  5. a: "a"
  6. }
  7. },
  8. template: `
  9. <div>
  10. <button @click="n += 1">n+1</button>
  11. //属性的值改变
  12. <button @click="obj.a += 'hi'">obj.a + 'hi'</button>
  13. //对象的属性的值改变
  14. <button @click="obj = {a:'a'}">obj = 新对象</button>
  15. //对象的地址改变
  16. </div>
  17. `,
  18. watch: {
  19. n() {
  20. console.log("n 变了");
  21. },
  22. obj:{
  23. handler: function (val, oldVal) {
  24. console.log("obj 变了")
  25. },
  26. },
  27. "obj.a":{
  28. handler: function (val, oldVal) {
  29. console.log("obj.a 变了")
  30. },
  31. }
  32. }
  33. }).$mount("#app");

只要以下之一:

  • 属性的值改变
  • 对象的属性的值改变
  • 对象的地址改变

就算是变化,简单类型看值,复杂类型看对象(地址),和 === 规则差不多。

handler方法和immediate属性

watch有个特点,最初绑定时是不会执行的,要等到 firstName 改变才执行监听计算,那么要是我想要一开始就可以执行监听,需要怎么做?

可以这样,如下代码:

  1. watch: {
  2. firstName:{
  3. handler(newName, oldName){
  4. this.fulName = newName + ' ' this.lastName;
  5. },
  6. immediate: true
  7. }
  8. }

可以选择给 firstName 绑定一个 handler 方法,之前写的 watch 方法默认写这个,Vue自动去处理这个逻辑,最终编译出来其实就是这个 handler 方法。

immediate: true 代表如果在watch里面声明了 firstName 后,就会立刻先去执行里面的 handler 方法,相之在绑定时就不会执行!

watchdeep 有什么用?

按刚刚 obj.a 变了,那么 obj 不会变,但一个新的需求:要求 obj.a 变了,那么 obj 也要变!这时就可以使用 deep:true 了(默认:false),如下:

  1. new Vue({
  2. data: {
  3. n: 0,
  4. obj: {
  5. a: "a"
  6. }
  7. },
  8. template: `
  9. <div>
  10. <button @click="obj.a += 'hi'">obj.a + 'hi'</button>
  11. //对象的属性的值改变
  12. </div>
  13. `,
  14. watch: {
  15. "obj.a":{
  16. handler: function (val, oldVal) {
  17. console.log("obj.a 变了")
  18. },
  19. deep: true // 该属性设定在任何被侦听的对象的 property 改变时都要执行 handler 的回调,不论其被嵌套多深
  20. },
  21. }
  22. }).$mount("#app");

watch与computed总结

这两个区别是什么?
答:

  • computed 是计算属性的意思,watch是监听的意思;
  • 其次computed调用时不需要加括号,可以当属性用,并根据依赖进行缓存,如果依赖没变就不需要重新计算;
  • watchimmdiate 表示在第一次渲染的时候是否执行这个函数, 另一个是deep ,如果监听的对象是否监听里面的属性的变化。

watchcomputed 都是数据变化的时候去执行一个函数,面对不同的场景:

  • 如果一个数据需要经过复杂的计算就用 computed
  • 如果一个数据需要被监听并且对数据做一些操作就用 watch