《vue实例之options》

1,463 阅读4分钟
new Vue(options)

这个options是Vue的参数,一般称之为选项,或构造选项。

一. 入门属性

这里我们改成使用完整版,代码都写到js里,方便看

//main.js
new Vue({
  data() {
    return { n: 0 };
  },
  template: `
  <div class="red">
    {{n}}
    <button @click="add">+1</button>
  </div>
  `,
  methods: {
    add() {
      this.n += 1;
    },
  },
}).$mount("#app");
//index.html
<div id="app"></div>

1. el

el指定这个实例的挂载点,这个节点的内容会被替换。

//main.js
new Vue({
  el: "#app", //对这个div进行MVC封装
});

如果div里有其他内容,也会被替换掉,看不见。

可以用$mount代替:

new Vue({
  render(h) {
    return h(Demo);
  },
}).$mount("#app");

2. data :内部数据

写法支持对象和函数,优先使用函数。

data:{
    n:0
}

函数要return

之前我们使用vue-loader时说过,在.vue文件(可以当作一个vue组件)里写的data必须是函数。

import Demo from './Demo.vue'
new Vue({
    render(h){
        h(Demo)
    }
})

Demo是个对象,在render函数里h(Demo)时,其实是把Demo传给了new Vue,也就是new Vue(Demo) 。在第一次传的时候没有错误。但是如果一个组件下边有两个组件,先new一个,再new一个。

这时如果data是个对象,两个组件就共享了一个实例,如果改了其中一个的数据,另一个也会变化。

如果data是函数,需要改数据时Demo发现data是个函数,就会先调用data函数然后得到数据。另一个组件也是先调用,得到值。这种情况下,每次调用data都是得到了一个全新的对象,地址不一样。避免了组件共用一个对象。

3. methods

1. 事件处理函数

把事件处理函数写在methods对象里。如果写在了methods外边,会报错,说该函数没有定义。

2. 普通函数

比如用methos代替vue的filters属性。

new Vue({
  data() {
    return {
      n: 0,
      array: [1, 2, 3, 4, 5, 6, 7, 8],
    };
  },
  template: `
  <div class="red">
    {{n}}
    <button @click="add">+1</button>
    <hr>
    {{filter()}}
  </div>
  `,
  methods: {
    add() {
      this.n += 1;
    },
    filter() {
      return this.array.filter((x) => x % 2 === 0);
    },
  },
}).$mount("#app");

在页面就会展示 [2,4,6,8]

这就是methods的第二种用法,在模板里主动调用一个函数。但是这种做法有一个问题,只要template里有一处改变了(或每次渲染),就会再次调用这个函数。每点一次+1按钮,filter函数就会被调用一次。哪怕结果和之前一样,也会被调用。

4. components:Vue组件

创建组件有三种方式:

1. 通过创建vue文件

比如说,Demo.vue就是一个组件。

<template>
  <div class="red">
    我是demo组件
  </div>
</template>  //后边还有script及style标签

怎么在main.js里使用这个组件呢?

import Demo from "./Demo.vue";    // 先导入这个组件
new Vue({
  components: {                  //在Vue实例里,通过components属性,
    Anqi: Demo,                  //给组件一个名字,值是这个组件
  },
  data() {
    return {
      n: 0,
      array: [1, 2, 3, 4, 5, 6, 7, 8],
    };
  },
  template: `
  <div class="red">
    {{n}}
    <button @click="add">+1</button>
    <Anqi/>                      //在template里写这个组件名字
    <hr>
    {{filter()}}
  </div>
  `,
  methods: {
   .....
  },
}).$mount("#app");

效果:

2. 用JS的形式创建组件

在main.js里:

Vue.component("demo2", {   //注意没有s
  template: `
  <div>我是第二个组件</div>
  `,
});

第一个参数是组件的名字,第二个参数是个对象,自己写template。(作为比较,上一种方法是在vue文件里写组件的template)

此时就不需要import了,直接在Vue实例的template里写:<demo2/>

Vue.component()的第二个参数是个对象,它和new Vue后边的对象一样。new里边可以写什么,component里就可以写什么。

所以实例和组件是差不多的,实例是直接new使用。组件是放到component里给它一个名字,让别人使用。(或者通过vue文件)

3. 上两种方法的组合

new Vue({
  components: {
    demo3: {
      template: `<div>我是第三个组件</div>`,
    },
  },
  data() {
    .....
  },
  template: `
  <div class="red">
    {{n}}
    <button @click="add">+1</button>
    <demo3/>
    <hr>
    {{filter()}}
  </div>
  `,
  methods: {
   ......
  },
}).$mount("#app");

使用方法1的components属性,给组件一个名字,但是不使用导入的组件,自己写组件的内容。使用方法2的key:value的形式。

总结

推荐使用第一种方法,import组件的形式,因为模块化。

但是像上边那样写,给Demo起另外一个名字,很奇怪。直接Demo:Demo就好了。由ES6的新语法,属性名和属性值一样,可以直接写成:

import Demo from './Demo.js'
new Vue({
    components:{Demo},
})

大小写问题:组件一般首字母大写Demo,文件名最好全小写

5. 四个钩子

created,mounted,updated,destroyed,它们都是函数,我们关注在什么时候会调用这个钩子。

created

在实例出现在内存中之后,被调用。此时实例还未出现在页面中。

怎么证明?在created函数里先写一句debugger,设一个断点

created(){
    debugger
    console.log('实例出现在内存里,没有出现在页面中')
}

刷新页面时,进入了断点,说明created函数被调用了。但是页面中并没有n和按钮,说明created被调用的时候,实例还没有出现在页面里。

mounted

实例出现在页面中之后,被调用。也就是已经挂载到页面了,用自己创建的el节点替换了页面的el。

mounted() {
    debugger;
    console.log("实例出现在页面");
},

同样,设一个断点验证。进入断点了,说明mounted被调用了。此时页面中有n和按钮,说明mounted被调用之前,实例就已经出现在页面里了。

updated

在实例更新之后,被调用。

updated() {
    console.log("更新了");
    console.log(this.n);
},

比如我们的+1按钮。不点击按钮时,不会调用updated。当点击按钮的时候,会把n+1,数据更新了。控制台打印了“更新了”,而且接下来打印的数值是+1之后的数值。所以是在已经把数据更新了,重新渲染到页面里了,才会触发这个函数。

destroyed

在实例消亡之后,被调用。

怎么实现让一个实例消亡呢?

把之前的vue实例变成另一个实例的组件,也就是说,让n和+1按钮一起成为一个组件。

<template>
  <div class="red">
    {{n}}
    <button @click="add">+1</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      n: 0
    };
  },
  created() {
    console.log("实例出现在内存中,没有出现在页面里");
  },
  mounted() {
    console.log("实例出现在页面");
  },
  updated() {
    console.log("更新了");
    console.log(this.n);
  },
  destroyed() {
    console.log("实例消亡了");
  },
  methods: {
    add() {
      this.n += 1;
    }
  }
};
</script>

<style>
.red {
  color: red;
}
</style>

以上就是Demo.vue文件。然后在main.js里import这个组件,new Vue()一个实例,使这个Demo成为这个实例的组件。

import Demo from "./Demo.vue";
new Vue({
  data() {
    return { visible: true };
  },
  components: { Demo },
  template: `
    <div>
      <button @click='toggle'>toggle</button>
      <hr>
      <Demo v-if='visible===true'/>
    </div>
  `,
  methods: {
    toggle() {
      this.visible = !this.visible;
    },
  },
}).$mount("#app");

这个实例里边还有一个toggle按钮,点击它,就会把visible取反。通过v-if,如果visible是true,就显示Demo组件;false,就隐藏。

当Demo组件从页面消失,就是消亡了。就会看到打印出“实例消亡了”

6. props:外部数据/属性

用于接收来自父组件的数据。

Demo.vue是一个实例的组件。

<template>
  <div class="red">{{message}}</div>
</template>

<script>
export default {
  props: ["message"]
};
</script>

Demo组件的html有一个message,但是他不是data,而是外部数据,需要外部给它传。怎么声明呢?在script里,props: ["message"]。表示我这个message是一个props数据,需要外部传进来。

在main.js的实例里:

import Demo from "./Demo.vue";

new Vue({
  components: { Demo },
  template: `
    <div>
      <Demo message='我是第一个props'/>
    </div>
  `,
}).$mount("#app");

怎么传:在Demo组件后边,以属性名=属性值的形式传

传入形式:可以传字符串,也可以传变量,也可以传函数

如:

new Vue({
  data() {
    return { n: 0 };
  },
  components: { Demo },
  template: `
    <div>
      <Demo message='n'/>
    </div>
  `,
}).$mount("#app");

Vue实例有一个数据n,值是0.我想把n传给message。

如果写成 <Demo message='n'/> 传入的是字符串n,页面中出现n这个字母。

<Demo :message='n'/> 传入的就是变量n的值,就是0.在message前边加 : ,意思是后边是JS代码,在JS里n就代表变量n。不管最外层的''

<Demo :message=" 'n' "/>等价于<Demo message='n'/>,都是字符串n

传函数:

<template>
  <div class="red">
    这里是Demo的内部
    {{message}}
    <button @click="fn">call fn</button>
  </div>
</template>

<script>
export default {
  props: ["message", "fn"]
};
</script>

Demo组件里有一句话,一个{{message}} , 还有个按钮。点击按钮会调用fn函数。其中message和fn都不是自己的,是在props里定义的,需要外部给传进来。

import Demo from "./Demo.vue";

new Vue({
  data() {
    return { n: 0 };
  },
  components: { Demo },
  template: `
    <div>
    {{n}}
      <Demo :message='n' :fn='add'/>
    </div>
  `,
  methods: {
    add() {
      this.n += 1;
    },
  },
}).$mount("#app");

在实例里,自己有个{{n}},还有个Demo组件。传message是n的值,fn是add函数。实例自己没有按钮,需要点击组件的按钮,给自己的n加1.然后组件自己没有事件处理函数,是实例爸爸给它传的函数。这个函数会把实例自己的内部数据n 加1。同时组件身上的message传了爸爸的n,也会实时更新数据。