美文网首页
vue基础核心

vue基础核心

作者: 渚清与沙白 | 来源:发表于2023-10-29 14:45 被阅读0次
  1. Vue的一个实例对应一个唯一的容器,在HTML中可以设置多个实例。在创建Vue实例之前,可以先设置Vue的一些全局配置,再实例化Vue,关联容器。
  2. 创建Vue实例使用new关键字,传递一个配置对象
<html lang="en">
<head>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
</head>
<body>
    <div id="root">
        <p>{{name}}</p>
    </div>
</body>
<script type="text/javascript">
    /** 先设置全局配置 */
    // 阻止Vue在启动时生成生成提示  
    Vue.config.productionTip = false;

   
    // 创建Vue实例  el的两种写法
    // 方式一 
    new Vue({
        el:'#root',
        data: {
            name:'hello'
        },
    });
    // 方式二
    const vm = new Vue({
        data: {
            name:'hello'
        },
    });
    vm.$mount('#root');

    // 创建Vue实例  data的两种写法  
    // 方式一:对象式
    new Vue({
        el:'#root',
        data: {
            name:'hello'
        },
    });
    // 方式二:函数式   使用组件时,data必须使用函数式
    const v = new Vue({
        data() {
            // this,这里的this指向的是Vue实例,不可使用箭头函数,否则this会指向window
            return {
                name:'hello'
            }
        },
    });
    v.$mount('#root');
</script>
</html>
  1. Vue.config.productionTip = false;表示阻止Vue在启动时生成生成提示
  2. 模板语法分为 插值语法{{ }})和指令语法v-model等)
    v-model双向绑定一般用在表单类元素上,单选、多选、输入框等带有value属性的元素。
    v-model:value="name"简写v-model="name"
  3. data中的所有属性都出现在Vue实例对象上,vm实例对象上所有的属性以及Vue原型上的所有属性在Vue模板中都能直接使用。mtehods中的方法、计算属性computed也会添加到vm上
    image.png
image.png

Object.defineProperty(object, property, descriptor)

给对象添加属性,以下示例给person对象添加一个age属性,可读可写。

set和get调用时机
  1. 设置number = 50,执行person.age会先去调用get函数,get函数被调用时返回number的值
  2. 执行person.age = 100时,在修改age属性的值,set函数会立即被调用,number的值随之改变
    let number = 10;
    let person = {
        name:'渚清与沙白',
        sex:'男'
    }
    /**
      * object: person 目标对象
      * property: age  属性名
      * descriptor: {
      *               value: 指定一个初始值
      *               enumerable:是否可以枚举
      *               writable:是否可编辑
      *               configurable:是否可删除
      *               set: age属性值被修改时调用
      *               get: age属性被读取时调用
      *            }
    */
    Object.defineProperty(person,'age',{
        // value:15,// 指定age的值
        // enumerable:true,// 是否可枚举
        // writable:true,// 是否可编辑
        // configurable:true,// 是否可删除

        // 当修改age属性时,set函数会被调用
        set(value){
            console.log('set函数被调用')
            number = value;
        },
        // 当读取age的值时,get函数会被调用
        get(){
            console.log('get函数被调用')
            return number;
        }
    })
数据代理

通过一个对象代理对另一个对象属性的操作(读/写...)

    let obj = {x : 100}
    let obj2 = {y: 3};
    Object.defineProperty(obj2,'x',{
        set(value){
            obj.x = value;
        },
        get(){
            return obj.x;
        }
    })
  • Vue中的数据代理
    通过vm对象来代理data对象中属性的操作(读/写...)
    基本原理:通过Object.defineProperty()把data对象所有属性添加到vm上,为每一个添加到vm上的属性,都指定一个getter/setter。在getter/setter内部去操作(读/写...)data中对应的属性。
image.png

this指向问题

    const vm = new Vue({
        data() {
            // this,这里的this指向的是Vue示例,不可使用箭头函数,否则this会指向 window
            return {
                name:'hello',
                show:false,
            }
        },
        methods: {
            showName(){
                // 此处的this指向 vm
                console.log(this);
            },
            hideName: ()=>{
                // 箭头函数 此处的this指向 window
                console.log(this);
            }
        },
    });
    vm.$mount('#root');

事件

传参
  • 不传参,可不加括号
  • 传参,必须加括号
  • 传参并使用event,在参数后面 增加$event占位符即可,传参无先后顺序,但要与mathods中的方法保持一致。
事件修饰符

@click.prevent 阻止默认行为
@click.stop 阻止时间冒泡
@click.once 事件只执行一次
@click.capture 使用事件的捕获阶段
@click.self 只有event.target是当前操作的元素才触发事件
@click.passive 事件的默认行为立即执行,无需等待事件回调执行完毕
@click.native 给子组件添加内置事件,否则在子组件上会被认为是自定义事件

@scroll 滚动条滚动监听
@wheel 鼠标滚轮滚动监听 (会先执行事件回调,回调执行完了才会执行默认行为)可以使用@click.passive立即执行默认行为

  • 阻止冒泡并且停止默认事件
    @click.stop.prevent 修饰符可以连着写
键盘事件

@keydown@keyup

if(e.keyCode === 13){
  // 监听到了回车键事件
}
  • 按键别名
    @keyup.enter 回车键
    @keyup.delete 删除、退格
    @keyup.esc 退出
    @keyup.space 空格
    @keydown.tab 换行 必须配合keydown使用
    @keyup.up
    @keyup.down
    @keyup.left
    @keyup.right
  1. vue未提供别名的按键,可以使用按键原始key值去绑定,但是要转为keybab-case(短横线命名)
    @keyup.caps-lock
  2. 系统修饰键 ctrl shift alt meta 特殊按键
    配合keyup使用时,按下修饰键的同时,再按下其他键,随后释放其他键,事件才会被触发
    配合keydown使用时,正常触发事件
  3. 可以通过键码keyCode去指定具体的按钮,不推荐
    @keyup.13
  4. Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名 , 不推荐
    Vue.config.keyCodes.huiche = 13;
    @keyup.huiche
  5. @keyup.ctrl.y 按下Ctrl和y键才执行

计算属性computed

  • 定义
    要用的属性不存在data中,要通过data中已有的属性计算得来
  • 原理
    底层借助了Object.defineProperty()方法提供的getter和setter。
  • get函数执行时机
    1. 初次读取会执行一次
    2. 当依赖的数据发生改变时,会再次被调用
  • 优势
    与methods相比,内部有缓存机制,可以复用, 效率高
  • 备注
  1. 计算属性最终会出现在vm上,可以直接使用{{ fullName }}错误写法 {{ fullName() }}
  2. 如果计算属性要被修改,必须使用set方法去响应修改,且set中要引起依赖的数据发生改变。修改计算属性的方式:this.fullName = ‘redColor’ 直接对计算属性赋值。
    注意:这里直接对data属性进行赋值操作并不会引起计算属性中的set函数执行。
// 完整写法
computed:{
   fullName:{
       get(){
          return this.name + ',欢迎您';
       },
      set(val){
        this.name = val;
      }
   }
}
data() {
    return {
      name: "A",
    };
},
methods: {
    modifyNmae() {
      this.fullName = "B"; // 修改计算属性的值,才会执行set函数
    },
},

// 简写  只读取,不修改
computed:{
    fullName(){
       return this.name + ',欢迎您'
    }
}
  1. 计算属性传参
// 将参数传递给计算属性
<div>{{ name('张三') }}</div>

computed: {
    name() {
        // 这里 return 的是一个有形参的函数 
        return (type) => {
            return `姓名:${type}`;
        };
    },
},

监视属性 watch

可以监视 data中的属性computed中的属性props

  • 监视属性写法
  1. new Vue时传入watch
  2. 通过vm.$watch监视
  3. 在组件中的配置项添加watch配置
    默认不监视对象内部属性的改变,需要配置deep: true才可以监测内部变化
    Vue自身可以监测对象内部值的变化,但Vue提供的watch默认不可以。
data() {
  return {
     name: "hello",
     numbers: {
       x: 0,
       y: 1,
     },
  };
},
watch: {
   name: {
     // name的值发生改变时调用
     handler(newVal, oldValue) {
       // handler函数名是固定写法
     },
     immediate: true, // 初始化时,让handler调用一次
   },
   // 监视对象中某个属性的变化
   "number.x": {
      handler() {},
   },
   // 监视多级结构中的所有属性
   numbers:{
      handler() {},
      deep:true,//深度监视
   }
},
  • watch简写形式
    当配置项中只有handler()时(没有immediatedeep),可以简写。
watch: {
   name(newVal,oldVal){ }
},
vm.$watch('name', funcation (newVal, oldVal){ })

vue管理的函数都不能写成箭头函数
watch可以开启异步任务维护数据,但是computed不可以
因为computed需要return一个返回值,在异步任务seTimeout((){},1000)中return一个值,是将返回值return给了setTimeout,并没有return给computed,此时computed得到的值是undefined,所以无法做异步任务。而watch不一样,它不需要return一个值,可以在异步任务中直接操作数据即可。

watch: {
  // ✅ 可以执行异步任务
   name(newVal){
      setTimeout(()=>{
         this.name = 'A';
      },1000);
   },
},
computed:{
   fullNmae(){
      // ❌ 不可执行异步任务
      setTimeout(()=>{
        return this.name + ' B';// 这里的return 将值返回给了settimeout,fullName并没有返回值
     },1000);
   }
}

动态绑定样式

三种写法:字符串写法对象写法数组写法

data() {
  return {
     mode:'normal',
     arr: ['top','center','bottom'],
     classObj: {
        top: true,
        bottom: false,
     },
     styleObj:{
        fontSize: '40px',// fontSize要写成驼峰
     }
  },
}
<!--class 字符串写法 -->
<p class="basic" :class="mode">hello</p> 
<p class="basic" :class="'normal'">hello</p> 

<!-- 数组写法 -->
<p class="basic" :class="arr">world</p> 
<p class="basic" :class="[ 'top', 'center', 'bottom' ]">world</p> 

<!-- 对象写法 -->
<p class="basic" :class="classObj">world</p> 
<p class="basic" :class="{ top: true, bottom: false }">world</p> 

<!--style 对象写法 -->
<p class="basic" :style="styleObj">world</p> 
<p class="basic" :style="{ fontSize: '40px' }">world</p> 

<!-- 数组写法 -->
<p class="basic" :style="[ styleObj ]">world</p> 

条件编译

v-show的原理是调整css样式display属性
v-if
v-else-if
v-else
<tenplate/>标签不会影响结构,只能配合v-if,不能配合v-show

列表渲染

  • v-for 要绑定key属性
  • 遍历使用inof都可以
  • 列表渲染的数据类型支持数组对象字符串
  • 遍历指定次数 a of 10
key

对列表进行唯一标识

  1. 虚拟DOM中key的作用
    key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,
    随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较。

  2. 对比规则
    (1). 旧虚拟DOM中找到了与新虚拟DOM相同的key
    ①. 若虚拟DOM中内容没变,直接使用之前的真实DOM
    ②. 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
    (2). 旧虚拟DOM中未找到与新虚拟DOM相同的key
    创建新的真实DOM,随后渲染到页面

  3. 使用index作为key可能会引发的问题
    (1). 若对数据进行逆序添加、逆序删除等破坏顺序操作,如arr.unshift ('A')
    会让之后的真实DOM产生没有必要的更新,UI没有问题,但是效率低

    (2). 如果结构追踪还包含输入类的DOM,如<input />
    会产生错误DOM更新,UI数据错乱问题

Vue监测数据变化的原理

Vue.set

Vue.set(target, key, value) | this.$set(target, key, value)
向响应式对象中(data中定义的数据)添加一个属性,并确保这个新 属性 同样是响应式的,且触发视图更新。只能给data对象中的对象添加响应式属性。不能给data这个对象添加属性.注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。

原理
  1. vue会监视data中所有层次的数据
  2. 如何监测对象中的数据
    通过setter实现监视,且要在new Vue时就传入要检测的数据(data对象)
    (1). 对象中后追加的数据,Vue默认不做响应式处理
    (2). 如需给后添加的属性做响应式,请使用下列API
    Vue.set(target, key, value)
    this.$set(target, key, value)
  3. 如何监测数组中的数据
    通过包裹数组更新元素的方法实现,本质就是做了两件事
    (1). 调用原生对应的方法对属性进行更新 Array.push等
    (2). 重新解析模板,更而更新页面
  4. 在Vue修改数组中的某个元素一定要用下列方法
    (1). 使用这些API:push() pop() shift() unshift() splice() sort() reverse()
    (2). Vue.set() 或 vm.$set()。不能使用这两个方法给vm的跟数据对象添加属性。如data对象

双向绑定运用技巧

v-model 双向绑定是一个看作传递 props 和设定自定义事件的语法糖。
v-model是绑定元素中的value属性的值

  • v-model修饰符
    v-model.number 将输入框的数字转换成Number类型
    v-model.lazy 失去焦点时收集数据
    v-model.trim 去除文本前后空格
  • 收集表单数据v-model的使用
    1. <input type="text"/> v-model收集的是value的值,用户输入的就是value值
    2. <input type="radio"/> v-model收集的是value值,且要给标签配置value属性
    3. <input type="checkbox"/> v-model收集值有以下两种情况
      (1). 没有配置input的value属性,则收集的就是checked值,true or false
      (2). 配置input的value属性,如v-model的初始值非数组,收集的是checked值;初始值数组,收集的是value组成的数组
  • 高级用法
    1. 自定义组件实现双向绑定的能力
export default {
    model: {
        prop: 'value',
        event: 'input',// 双向绑定的事件 input
    },
    props: 
        value: [String, Number],// v-model支持多类型数据
    },
    emits: ['input'],// 定义事件 input
    data() {
        return { };
    },
    methods: { 
      input(e){
         this.$emit('input',e.target.value);// 输入值后,通过事件改变
      }
    },
};

2. v-model绑定不同的数据
实现方式:通过计算属性来实现

<input v-model="rate"/> // 绑定计算属性 rate
export default{
  data:{
    return {
      type:0,
      getPercent: '',
      ratePercent: '',
    };
  },
  computed:{
        rate: { // 计算属性rate
              get() {// 获取值 根据条件判断
                if (!this.type) return this.getPercent;
                if (this.type) return this.ratePercent;
             },
             set(value) { // 设置值,根据条件
                if (!this.type) this.getPercent = value;
                if (this.type) this.ratePercent = value;
             },
       },
  }
}

3. v-model双向绑定多个值
可参考:https://www.cnblogs.com/Im-Victor/p/16121210.html

<template>
  <view>
    <price-range
     label="价格范围"
     :disabled="isEdit"
     :start.sync="form.startPrice"
     :end.sync="form.endPrice" />
  </view>
</template>
export default {
    name: 'PriceRange',
    props: {
        label: String,
        start: [String, Number],
        end: [String, Number],
        disabled: {
            type: Boolean,
            default: false,
        },
    },
    emits: ['blur'],
    computed: {
        startValue: {
            get() {
                return this.start;
            },
            set(val) {
                this.$emit('update:start', val);
            },
        },
        endValue: {
            get() {
                return this.end;
            },
            set(val) {
                this.$emit('update:end', val);
            },
        },
    },
    watch: {
        startValue(newVal) {
            if (!newVal) return;
            const text = newVal.replace(/[^0-9]{0,1}(\d*(?:\.\d{0,2})?).*$/, '$1');
            this.$nextTick(() => {
                this.$emit('update:start', text);
            });
        },
        endValue(newVal) {
            if (!newVal) return;
            const text = newVal.replace(/[^0-9]{0,1}(\d*(?:\.\d{0,2})?).*$/, '$1');
            this.$nextTick(() => {
                this.$emit('update:end', text);
            });
        },
    },
    data() {
        return {};
    },
};
</script>

过滤器

可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值v-bind 表达式

  • 语法

    1. 注册过滤器:Vue.filter(name, callback)new Vue(filters:{})
    2. 使用过滤器:{{ xxx | 过滤器名 }}v-bind:属性 = “xxx | 过滤器名”
      注意使用格式: | 分隔
  • 使用方法
    (1)插值语法

<p> {{ time | timeFormatter }} </p> 

filters:{
  // 定义了形参
  timeFormatter(value){
       return 'hello';
  }
}

<p>{{time | timeFormatter}}</p> 含义:先读取time,然后将time作为参数传递给timeFormatter函数;拿到timeFormatter的返回值替换插值语法中的内容。

(2)v-bind中使用过滤器
<h2 :x="time | slice">渚清与沙白</h2>

  • 过滤器传参
    默认将time作为timeFormatter函数的第一个参数传递
// 看似没传参数,实则会传递time参数
<p>{{ time | timeFormatter }}</p> 

// 这里第一个参数会自动处理为time,第二个参数才是'YYYY/MM/DD'
<p>{{ time | timeFormatter('YYYY/MM/DD') }}

filters:{
    // 定义形参参数仍是两个
    timeFormatter(value, format = 'YYYY-MM-D'){
        return 'hello';
    }
}
  • 过滤器串连
    先将time传递给timeFormatter,再将timeFormatter的返回值继续往下传递给slice函数。
    <p> {{ time | timeFormatter('YYYY/MM/DD') | slice }} </p> 

    filters:{
        timeFormatter(value, format = 'YYYY-MM-D') {
            return 'hello';
        },
        slice(value) {
            return value.slice(0,4);
        }
    }
  • 局部过滤器注册
    在组件中配置的过滤器就是局部过滤器
  • 全局过滤器注册 Vue.filter

指令

v-text:向节点渲染文本内容。与插值语法的区别是会替换节点中的内容,插值语法则不会。不支持结构解析,要显示html标签文本内容,可以使用该指令。
v-html:向节点渲染包含html结构的内容。与插值语法的区别是会替换节点中的内容,插值语法则不会。
v-html存在严重的安全问题
在网站上动态渲染html是非常危险的,容易导致XSS攻击;一定要在可信的内容上使用v-html,不要用在内容提交
v-cloak:是一个特殊属性,Vue实例创建完毕开始接管容器后,会删除掉v-cloak属性。使用css配合使用,可以解决网速慢的时候页面展示插值语法的问题。如页面展示{{ name }}的问题。

// 选中带有v-cloak属性的所有标签
[v-cloak] {
  display: none;
}

<div v-cloak>
  {{ message }}
</div>

v-once:初次渲染之后,就视为静态内容了,之后的数据改变不会引起该节点的更新。
v-pre:让Vue跳过该指令所在节点的编译过程,没有使用插值语法和其他指令的节点加上该指令可让vue不再去解析,可以加快编译
自定义指令:取名不需要加v-,在使用时需要加v-

自定义指令
  • 自定义的三个钩子函数bind(),insert(),update()
  • 钩子函数调用时机
    bind():指令和元素成功绑定时调用
    insert():指令所在元素被插入页面时调用
    update():指令所在模板被重新解析时调用
  • 简写形式
    因为bind(),update()这两个函数处理逻辑差不多一致,于是提供了简写形式,将二者合并成一个函数。
  • 自定义指令定义directivesVue.directive()
  1. 全局指令
    Vue.directive(key,value)
    参数key是指令名,指令名存在多个单词,需要使用短横线连接
    参数value是回调函数或者配置对象
    #对象式
    Vue.directive('fbind',{
          // 指令和元素成功绑定时调用bind函数
          bind(el,binding){
            el.value = binding.value;
          },
          // 指令所在元素被插入页面时调用
          insert(el,binding){
              el.focus(); // 输入框获取焦点
          },
          // 指令所在模板被重新解析时调用
          update(el,binding){
            el.value = binding.value;
          }
    });
    #函数式
    Vue.directive('big',function(el,binding){
          console.log(this);// this指向window
          el.innerHTML = binding.value * 10;
    });
  1. 局部指令
    directives:{bind(),insert(),update()}
  directives:{
        // 回调函数式 简写形式,囊括了bind()和update()函数。
        // 因为这两个函数处理逻辑差不多一致,于是提供了简写形式。
        // 调用时机:指令与元素成功绑定时会被调用;指令所在模板被重新解析时也会被调用
        big(el,binding){
          console.log(this);// this指向window
          el.innerHTML = binding.value * 10;
        },
        // 配置对象式
        fbind:{
          // 指令和元素成功绑定时调用bind函数
          bind(el,binding){
            el.value = binding.value;
          },
          // 指令所在元素被插入页面时调用
          insert(el,binding){
              el.focus(); // 输入框获取焦点
          },
          // 指令所在模板被重新解析时调用
          update(el,binding){
            el.value = binding.value;
          }
        },
        // 指令名多个单词问题
        'big-number'(el,binding){
            el.value = binding.value;
        }
      }
  • this指向
    三个钩子函数中的this指向的是window

生命周期

created:指数据监测、数据代理创建完毕
mounted:Vue完成模板解析并把初始的真实DOM放入页面(挂载完毕)后调用
绑定自定义化事件、订阅消息、请求网络、开启定时器

beforeDestory:销毁Vue实例之前时调用
解除消息订阅、关闭定时器、解绑自定义事件;此阶段可以访问vue实例的数据、可以调用vue实例的方法,但是不会触发DOM更新

lifecycle.png

VueDevTools扩展图标高亮显示,但是控制台没有Vue选项卡(Open DevTools and look for the Vue panel),无法进入调试工具。可以在main.js加入Vue.config.devtools = true。最后刷新浏览器或者重启。

相关文章

网友评论

      本文标题:vue基础核心

      本文链接:https://www.haomeiwen.com/subject/mceeidtx.html