Vue 基础应用

本文介绍了Vue.js的基础应用,包括如何创建Vue实例,使用DOM相关属性如el和template,以及利用render函数替代模板。还详细讲解了实例属性、生命周期、数据绑定、computed属性用于计算属性、watcher用于响应数据变化,以及指令系统的作用。此外,探讨了组件扩展、插槽和render函数在Vue中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

VUE 实例

在我们使用 vue 之前我们需要事先得到 一个 vue 实例对象,我们需要传递一个对象用于描述你的 vue 实例。

const app = new Vue({});
DOM 相关
  • el

    提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例

    const app = new Vue({
      el: "#app",
      // el: document.getElementById("app"),
    });
    
  • template

    一个字符串模板作为 Vue 实例的标识使用。模板将会替换挂载的元素。你可以回忆一下之前学过的 innerHTML 的作用。

    const app = new Vue({
      el: "#app",
      template: "<span>hello world</span>",
    });
    
  • render

    字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个 createElement 方法作为第一个参数用来创建 VNode。如果实例化 Vue 中同时传递了 template 和 render 那么,template 会直接忽略【因为其实 template 也会走 render 的流程】。

    // render 函数中通过 createElement 创建虚拟节点(所谓虚拟节点就是使用js对象模拟DOM节点,然后在特定的方法中再解析成为真正的节点。这样做的好处是减少了重绘和回流),写法相对而言比较固定。
    
    //createElement 中需要传递三个参数。第一个是你需要渲染什么样的节点。第二个参数是该节点的描述,第三个是该节点的子节点数组。
    
    const app = new Vue({
      el: "#app",
      render: function(createElement) {
        return createElement(
          //  要渲染的标签名称
          "div",
          //  该标签的描述
          {
            // 可以写内置样式
            style: {
              color: "red",
              border: "1px solid black",
            },
            // 可以指定 class
            class: {
              cls1: true,
              cls2: false
            },
             // 普通的 HTML 特性
            attrs: {
              id: 'foo',
              name:"foo"
            },
            // 主要用于父子组件通讯
            props: {
            },
            // 事件监听
            on:{
              // 指定 作用域,同时传值
              click: handle.bind(window, 3),
            }
            // 仅用于组件,用于监听原生事件,对于使用 `vm.$emit` 触发的事件不敏感。
            nativeOn: {
              click: handle.bind(window, 3),
            },
            // 自定义指令
            directives: [],
            // 插槽名称
            slot:"",
            // 特殊属性
            key: '',
            ref: '',
          },
          // 该标签的子节点数组
          [
            "text", // 文本节点直接写就可以
            createElement(
              "div",
              {
                style: {
                  color: "blue",
                  border: "1px solid black",
                },
              },
              "hahah"
            ),
          ]
        );
      },
    });
    

给已有的对象数据添加新的属性

data: {
  title: "test",
  user: {},
},

// 下面两种方式都能监听数据变化
Vue.set(app.user, 'gender', '男');
// 实例.$set 是 Vue.set 的别名
app.$set(app.user, 'gender', '男');

实例属性

const app = new Vue({
  el: "#root",
  template: "<div>{{title}}</div>",
  data: {
    title: "测试数据",
  },
  // 当然如果是后面再去添加 watch,还需要手动删除监听
  /*
    const uw = app.$watch("title",(newVal,oldVal)=>{})
    uw() // 只需要执行一遍返回的函数,就不再监听 title 的变化
  */
  watch: {
    title(newVal, oldVal) {
      // ...
    },
  },
});
console.log(app.$data); // data 的部分
console.log(app.$el); // 挂载节点
console.log(app.$options); // 传入Vue的整个对象【包含默认属性】在这里面对 data 重新赋值是无效
app.$options.render = (h) => {
  // 那么当数据发生变化的时候,在下一次渲染的时候会更新视图
  return h("div", {}, "test");
};
console.log(app.$root === app); // true

// 自定义事件监听
app.$on("someEventName", (...args) => {
  // do something
});
/*
// 只监听一次
app.$once("someEventName", (...args) => {
  // do something
});
*/

// 在适当的时候触发事件【常用于父子组件通讯】
app.$emit("someEventName", 1, 2);

// 如果在 data 中没有定义属性,那么在后面新增属性的时候并不会监听变化
data: {
  obj: {
  }
}
app.obj.newKey = 123;
// 可以采取强制组件渲染的方式【会降低性能】
app.$forceUpdate();
/*
// 当然更好的方式是
app.$set(app.obj,"newKey",123);
*/
// 删除对象属性 app.$delete(app.obj, "newKey"); // 如果是采用 直接 delete 会导致内存溢出【 reactive 还存在 】

/*这里直接引入官网的示例*/
// app.$nextTick
methods: {
    // ...
    example: function () {
      // 修改数据
      this.message = 'changed'
      // DOM 还没有更新
      this.$nextTick(function () {
        // DOM 现在更新了
        this.doSomethingElse()
      })
    }
}

生命周期

// 先执行了 init 【 new Vue 之后会自动调用 】先去执行了 beforeCreate
beforeCreate(){
  // 这里只是事件已经初始化好了,数据部分还没有
  // 这里不要修改数据,因为还没有 reactive
}

// 然后在 init 的注入和 reactive 时会调用 created
created(){}

beforeMount(){}
// render function 会在这之间执行
render(){}
// render 出错的时候,但是只捕获本组件的错误,子组件中的错误不会被捕获
renderError(){
  // 一般只用于开发环境
}
mounted(){}


beforeUpdate(){}

updated(){}

/*
// 一般在 keep-alive 组件的活跃状态和非活跃状态时触发
*/
activated(){}
deactivated(){}

/*
// 一般在需要销毁一个组件实例的时候使用
app.$destroy()
*/
beforeDestroy(){}
destroyed(){}

// 会向上冒泡,捕获错误,也可以用于正式环境
errorCaptured(){}

数据绑定

new Vue({
  // 可以直接使用 js 的内置对象和方法
  template: `
    <p>{{Date.now()}}</p>
    <p>{{a.join('')}}</p>
    <p>{{fn()}}</p>
  `,
  data: {
    a: [1, 2, 3],
  },
  methods: {
    fn(arr) {
      // 其实更好的是通过 computed
      return arr.join("");
    },
  },
});

computed

computed 是经过缓存处理的,针对于计算量比较大的操作而言比较合适

computed:{
  val(){
    return ...
  }
}
// or
computed:{
  val:{
    get(){
      return ...
    }
    // 当然这是不推荐的方式【computed中最好只用于处理数据之后返回,而不要在这里进行数据的变更】
    set(newVal){
      //...
    }
  }
}

watch

主要的运用场景是,监听到某个数据的变化,然后执行某个指定的操作(比如 ajax 等)

watch: {
  // 这是 data 中的数据项
  // 在一开始的时候,数据没有变化,这里也就不执行,等待数据变化才会执行
  input(newVal, oldVal) {
    console.log("handle:", newVal, oldVal);
  },
},

// 当然上面的 watch 只是一种简写形式,其实它的内部也会编译成为下面中形式
watch: {
  // 这是 data 中的数据项
  input: {
    handler(newVal, oldVal) {
      console.log("handle:", newVal, oldVal);
    },
    // 是否立即执行 handler ,如果没有此项就表明需要等待 input 数据项变化的时候才执行
    immediate: true,
  }
},

// 监听对象属性的变化
watch: {
  obj: {
    handler(newVal, oldVal) {
      console.log("handle:", newVal, oldVal);
    },
    // 当然这个配置的开销会比较大,毕竟要去监听对象中每一层每一个属性的变化
    deep: true, // 是否深度监听
  }
},

// 如果只想监听对象中某一个属性的变化
watch: {
  // 比如这里只监听 obj 对象中的 a 属性的变化,这样的性能开销比较小
  'obj.a': {
    handler(newVal, oldVal) {
      console.log("handle:", newVal, oldVal);
    },
  }
},

指令系统

new Vue({
  template: `
    <div v-text="title"></div>
  `,

  /*
   // 给变量前面加一点其他的信息
   template: `
    <div v-text="'title:'+title"></div>
  `,
  */

  /*
  // 其实更好的运用场景是根据用户的权限动态的展示界面功能
  template: `
    <div>
      <span v-if="level==1">level-1</span>
      <span v-else-if="level==2">level-2</span>
      <span v-else >level-3</span>
    </div>
  `,
 */

  /*
  data:{
    arr:[1,2,3],
    obj:{
      a:"111",
      b:"222",
      c:"333",
    }
  }
  template: `
    <div>
      <ul>
        <!-- 这里加 :key 只是为了在下一次渲染的时候,如果在缓存中已经存在了相同的 key 值那就直接拿取缓存的数据,不需要再次渲染,减少性能开销 -->

        <!-- 而且这里最好不要用 index 下标作为 key 值,因为一旦数组排序以后可能会导致一个错位的缓存数据 -->
        <li v-for="(item,index) in arr" :key="item"></li>
      </ul>
      <ul>
        <li v-for="(val,key,index) in obj"></li>
      </ul>
    </div>
  `,
  //////////////////////////////////////////////////////////
  data: {
    arr: [1, 3, 4],
    picked: "a",
  },
  // 这样只有a 和 c 会被选中,:value 和 value 的区别是 后者是单纯的字符串,前者会解析数值。
  template:`
  <div>
    a<input type="checkbox" :value="1" v-model="arr"/>
    b<input type="checkbox" :value="2" v-model="arr"/>
    c<input type="checkbox" :value="3" v-model="arr"/>
    <hr/>
    单选:d<input type="radio" value="a" v-model="picked"/>
    e<input type="radio" value="b" v-model="picked"/>
  </div>
  `
*/
  //////////////////////修饰符
  /*
  <input v-model.number="msg"/>
  <input v-model.trim="msg"/>
  <input v-model.lazy="msg"/> 只有在失去焦点时才会触发 onchange
  */

  /*
  只会数据绑定的内容只会执行一次,再有数据变化不会更新视图
  <div v-once>{{msg}}</div>
 */
});
  • 至于为什么组件内的 data 需要是一个 function

    因为组件可能会复用,如果复用组件,那么多个组件就会使用同一个数据对象,造成污染,所以需要在组件中 data 以 function 的形式存在

let app = new Vue({
  components: {
    T: {
      // props: ["test", "str"], // 这是一种不太严谨的写法
      props: {
        test: {
          type: Boolean,
          required: true,
          default: true,
          /*
          其实 required: true 之后default也可以直接不要,如果需要default 而且如果是一个对象那么最好也是直接用函数形式返回一个对象,避免组件复用数据污染
          default(){
              return {}
          }
          */
        },
        str: String,
        other: {
          validator(value) {
            // ... 进行一波检测
            // 如果符合
            return true;
          },
        },
      },
      template: `<h1 v-show="test">{{str}}</h1>`,
    },
  },
  data: {
    arr: [1, 3, 4],
    picked: "a",
  },
  mounted() {
    // 取出名称为 t1 的组件实例
    console.log(this.$refs.t1);
  },
  template: `
  <div>
  00
    <T ref="t1" :test="true" str='一级标题'>1</T>
  </div>
  `,
}).$mount(root);

组件扩展

const com1 = {
  props: {},
  template: "",
  data() {},
};

const com2 = {
  extends: com1,
  data() {
    return {};
  },
  methods: {},
};

new Vue({
  components: {
    com2: com2,
  },
  templates: `<com2></com2>`,
});

插槽

const com1 = {
  template: `
  <div>
    <div class="header">
        <slot name="header"></slot>
    </div>
    <div class="content">
        <slot name="content">
          <slot name="msg"></slot>
        </slot>
        <slot name="test" :val="val" abc="abc"></slot>
    </div>
  </div>
  `,
  data() {
    return {
      val: "com1 Data",
    };
  },
};

new Vue({
  data: {
    val: "Vue Data",
  },
  components: {
    com1: com1,
  },
  template: `
  <div>
    <com1>
      <h1 slot="header">header</h1>
      <h1 slot="content">
        content
        <p slot="msg">{{val}}</p>
      </h1> 
      <!-- props 是可以自定义名称的 -->
      <p slot="test" slot-scope="props">{{props.val}}-{{props.abc}}</p>
    </com1>
  </div>`,
});

render function

const com1 = {
  template: `
  <div>
    <slot name="test" :val="val"></slot>
  </div>
  `,
  data() {
    return {
      val: "com1 Data",
    };
  },
};

new Vue({
  data: {
    val: "Vue Data",
  },
  components: {
    com1: com1,
  },
  // template: `
  // <div>
  //   <com1 ref="com1">
  //     <p slot="test" ref="p1" slot-scope="props">hello</p>
  //   </com1>
  // </div>`,

  // 上面的 template 会走下面的流程

  // 而这里的 createElement 其实就是在创建虚拟 DOM,存储在内存中,
  // 会跟真正的 DOM 结构进行对比,如果对比的结果是需要更新原来的DOM结构,
  // 就会将 虚拟 DOM 转换成为真正的 DOM
  render(createElement) {
    return createElement(
      "com1",
      {
        ref: "com1",
      },
      [
        createElement(
          "p",
          {
            slot: "test",
            ref: "p1",
            "slot-scope": "props",
          },
          "hello"
        ),
      ]
    );
  },
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值