vue基础

78 阅读8分钟

只是学习过程中的一些小笔记,不喜勿喷

选项式/配置api

<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.js" type="application/javascript"></script>
<script>
  //配置api
  const app = new Vue({
    el:'#app'//挂载css元素或者节点,让vue在节点app工作
    // el:document.getElementById('app')
      data:{//data:状态,里面的数据都是响应式的,带有个体与set,同理react中的state
      a:1,
      b:2
    },
  })
</script>

data: 状态( vue对于这里声明的所有属性进行递归处理 给每个属性都加上getter和setter vue2: Object.defineProperty来处理 vue3: new Proxy 来处理 在setter当中可以劫持对于状态的修改的行为,从而主动的去进行视图的更新

vue2:

  <script>
    var data={a:1,b:2}
    const $data={};
    for(const key in data){
      Object.defineProperty($data,key,{
        get(){
          return data[key]
        },
        set(val){
          data[key]=val;
          document.querySelector('.text').innerHTML=val
        }
      })
    }
    console.log($data);
    $data.c=999;
    console.log($data);
    document.getElementById('btn1').onclick=(){
      $data.c=$data.c+1
    }
  </script>

vue3:

<script>
    var data={a:1,b:2,_c:3};
    const $data =new Proxy(data,{
      get(obj,key){
        console.log(obj,key);
        return obj[key]
      },
      set(obj,key,val){
​
      }
    })
    $data.a
    console.log($data.a);
  </script>

模板指令

1.v-text    插入文本内容
    <div v-text="c"></div>==<div>{{c}}</div>
    
2.v-html    插入html内容,使用v-html一定要确保插入内容的安全性,如果内容有xss攻击时,需要进行正则验证
    
3.v-ifnull,0,undefined,false时不渲染,且两个需要连用
    <div v-if="b">v-if</div>
    <div v-else>else</div>
​
4.v-show 通过布尔值来控制CSSdisplay进行显示
    3,4区别:v-if一定会重排重绘,v-show不一定,所以在切换次数少的时候用v-if,反之v-show
    
5.v-bind    简写成‘:’ 绑定对象时会根据值来判断是否绑定
    <div :class="'a b c'">class</div>a b c
    <div :class="['a','b','c']">class</div>a b c
    <div :class="['a','b','c'].join(' ')">class</div>a b c
    <div :class="{1:33,2:0,3:11,4:22,f:''}">class</div>1 3 4
    <div :style="{color:f,fontSize:''}">样式</div> f:'red'
​
6.v-for 需要绑定key唯一值,可以绑定数组,数字(循环个数),对象
    <div v-for="item in g" :key="item.id">{{item.title}}</div>
    <div v-for="(item,index) in g" >{{index}}-{{item.title}}</div>
    <div v-for="item of 'abc'">{{item}}</div>循环字符串个数次
    <div v-for="item in 11">{{item}}</div>
    <div v-for="(val,key) in {a:1,c:2}">val:{{val}}</div>
    g:[{id:1,title:'标题1'},{id:2,title:'标题2'}{id:3,title:'标题3'}]7.v-on  @   
  @click="函数,对象(通过对象绑定多个事件),函数(传参,如果需要获得合成事件对象需要输入$event对象),简单语句(@click='a=a+2')"
  <button @click="fn1">按钮1</button>
  <button @click="fn1(1)">按钮2</button>
  <button @click="fn2(3,$event)">按钮3</button>
  <button @click.once="fn1">单次绑定</button>
  <button @click.capture="fn1">按钮1</button>
  .capture将事件传递由冒泡改变;.stop阻止冒泡;.once执行一次;.prevent阻止默认行为
  <a href="http://baidu.com" @click.prevent="fn1">111</a> 
  <input type="text" placeholder="提示内容" @keyup.13="fn2" @keydown="fn1"/>
    methods: {//配置的是一些自定义函数
      fn1(arg1){//绑定的函数不加(),那么第一个参数就是一个合成事件对象
        console.log(arg1);
      },
      fn2(arg1,arg2){
        console.log(arg1);
        console.log(arg2);
      }
    },
​
8.v-pre  阻止编译,即当成文本进行展示;v-once  编译一次 这两个都不需要表达式<button v-once>+</button>
​
9.v-model   表单元素或自定义组件的双向绑定,语法糖。实际上都是监听某个事件并绑定。与vue双向绑定不一样(Object.definePropertyProxy)
    <input type="text" :value="val" @input="val=$event.target.value"/>==<input type="text" v-model="val">
    texttextarea 元素使用 value propertyinput 事件;
    checkboxradio 使用 checked propertychange 事件;
    select 字段将 value 作为 prop 并将 change 作为事件。
​
    修饰符:.lazy 取代input监听change事件;.number输入字符串转为有效的数字;.trim输入首尾空格过滤

watch 监听事件;监听的是内存地址的变化,如果监听对象仅仅只改变属性值是不会被监听到的,如果想监听属性的话可以进行深度监听或者直接监听该属性。

watch:{ // 侦听器
            ct(nv,ov){ //  第一个参数 是变化之后的新的值 第二个参数是变化之前旧的值
              console.log('ct is change',nv,ov)
            },
            // 默认情况下 watch监听的是 一个值的内存地址
            // handsome(nv,ov){ 
            //   console.log('handsome is change',nv,ov)
            // }
            handsome:{
              handler(nv,ov){//hander处理函数
                console.log('handsome is change')
              },
              deep: true, // 深度监听 监听对象的所有层的属性的变化
              immediate: true, // 默认值是false 是否立即执行一次
            },
            "handsome.money"(nv,ov){//"对象.属性"()  监听某个属性的变化
              console.log('属性变化',nv);
            }
          },

computed 计算属性,他的值是有缓存的,仅仅在相关依赖发生变化时才会重新计算。所以在一些根据其他状态计算出结果的情况下更优选。如购物车总价等。

computed:{
      count(){
      console.log('1111');
      return this.num*this.price
     }
    }

methods 自定义函数

全选案例

<div id="app">
  <div><input type="checkbox" v-model="options[0]"/>选项1</div>
  <div><input type="checkbox" v-model="options[1]"/>选项2</div>
  <div><input type="checkbox" v-model="options[2]"/>选项3</div>
  <div><input type="checkbox" v-model="qx"/>全选</div>
</div>
<script>
  const app = new Vue({
    el:'#app',
    data:{
      // num:1,price:10,ct:100,
      options:[false,false,false]
​
    },
    methods: {},
    computed:{
      qx:{
       get(){
        const qxall = this.options.every(item=>item);
        // const qxall = this.options.some(item=>!item);
        if(qxall) return true
        return false
       },
       set(val){
        console.log('修改全选');
        if(val){
          this.options=[true,true,true]
        }else{
          this.options=[false,false,false]
        }
       }
      }
    }
  })
  console.log(app);
</script>

filters 过滤器,实际上就是一个普通的函数,只是改变了函数的调用方式,在vue3中已移除!

发货状态案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.js" type="application/javascript"></script>
</head>
<body>
    <div id="app">
        <div> 订单状态:  {{odtxt(orderState) }} {{orderState | newodtxt }}
        </div>
    </div>  
    </div>
    <script>
        const app = new Vue({
            el: '#app',
            data:{
                orderState: 5, // 1 已下单未支付 2支付 3发货 4签收 5退款
            },
            methods:{
                odtxt(val){
                    if(val==1) return '已下单未支付'
                    if(val==2) return '支付'
                    if(val==3) return '发货'
                    if(val==4) return '签收'
                    if(val==5) return '退款'
                }
            },
            filters:{//过滤器 
                newodtxt(val){
                    if(val==1) return '已下单未支付'
                    if(val==2) return '支付'
                    if(val==3) return '发货'
                    if(val==4) return '签收'
                    if(val==5) return '退款'
                }
            }
        })
        console.log(app)
    </script>
</body>
</html>

过滤时间格式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/dayjs/1.10.8/dayjs.min.js" type="application/javascript"></script>
    <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.js" type="application/javascript"></script>
</head>
<body>
  <div id="app">
    <div> 
      {{ Date.now() | format('MM:DD hh:mm') }}
    </div>  
  </div>
    <script>
        const app = new Vue({
            el: '#app',
            data:{},
            filters:{
                format(time,str){
                    const tpl = str || 'YYYY-MM-DD'
                    return dayjs(time).format(tpl)
                }
            }
      
        })
        console.log(app)
    </script>
</body>
</html>

全局组件component

vue2 Vue.com

  <div id="app">
    <hd></hd>
  </div>
  <script>
    Vue.component('hd',{
      template:`<div>我来组成头部</div>`
    })
    
    const app = new Vue({
      el: '#app',
    })
    console.log(app)
  </script>

props:组件通信,父传子

  <div id="app">
    <!-- a传递字符串,:a传递数字 -->
    <hd a="123" :c="123" d="999" :g="2"></hd>
  </div>
  <script>
    Vue.component('hd', {
      template: `<div>我来组成头部
            <div>{{a}},{{c}},{{d}},{{e}},{{f}},{{g}}</div>
        </div>`,
      // props:['a','b'],//组件通信,父传子
      props: {//对象写法需要相对应数据类型
        a: String,
        c: Number,
        d: { type: String, required: true },//type:类型,required:是否必填
        e: { default: 123 },//默认值,普通数据类型
        f: {
          default() {
            return [1, 2, 3]
          }
        },//数组与对象需要使用函数返回值来确认唯一,原理如a=[1,2],b=a时会浅拷贝地址
        g: {//验证器 对自定义值自定义校验
          validator(val) {
            if (val > 5) return false
            return true
          }
        }
      }
    })
    const app = new Vue({
      el: '#app',
    })
    console.log(app)
  </script>

$attrs 接收使用未被props接收的属性,推荐尽量使用props进行传参

$el 组件挂载的最终元素节点

$data 指向组件声明的状态

生命周期

beforeCreate() 组件创建前 created() 组件实例创建完成,data完成初始化,可以获取数据和进行数据处理 beforeMount() 挂载完成前 mounted() 挂载完成后,完成了节点挂载可以操作节点,如地图创建、图标创建都是要节点操作,可以在这个时间去初始化 beforeUpdate() updated() beforeDestroy() /unmounted组件卸载前,进行销毁前的一些移除事件监听以及清除定时器的最好时机 destroyed()/unmounted

activated激活 被缓存的组件在created之后会触发一次,之后的每次切换回来都会触发一次激活,所以被缓存

deactivated 失活

局部注册组件components

Vue.component('hd', {
      template: `<div>我来组成头部<cp2></cp2></div>`,
      components:{//局部注册
        cp2:{
          template:`<div>我来组成身体</div>`
        }
      }
      })

混入mixin 可以把公共配置混入到当前组件中使用

 Vue.mixin({//全局混入  所有组件可以直接使用
      data(){
        return{
          name:'版本'
        }
      }
    })
    
const mix2 = {//局部混入
      methods: {
        fn1() {
          console.log('fn1执行')
        }
      }
    }
Vue.component('cp1', {
      template: `<div> 我是cp1组件 {{ version}} <button @click="fn1">我是cp1的按钮</button>  </div>`,
      //mixins: [mix1, mix2]    局部混入,可以混入多个
      extends:mix1  继承,只能继承一个
    })

provide/inject 依赖注入

父组件提供 provide: {供应的数据 } provide(){return{供应的数据}}

子组件注入inject: ['c','数据'] inject:{别名1:'数据源1',别名2:{from:'数据源名称'}}

 Vue.component('cp1', {
      template: `<div>我是cp1组件
                        <div>  inject c in cp1 is :{{ c }}</div>
                     </div>`,
      inject: ['c']
    })
    Vue.component('cp2', {
      template: `<div>我是cp2组件 <cp1></cp1></div>`,
    })
    Vue.component('cp3', {
      template: `<div>我是cp3组件 <cp2></cp2> </div>`,
      provide: {
        a: 1,
        b: 2,
        c: 3
      }
    })
  <div id="app">
    <cp3></cp3>
    <cp1></cp1>
  </div>
  </div>
  <script>
    Vue.component('cp1', {
      template: `<div>
                        我是cp1组件
                        <div>  inject c in cp1 is :{{ c }}</div>
                        <div>  a in cp1 :{{ a }}</div>
                        <div>  newa in cp1 :{{ newa }}</div>
                        <div>  newb in cp1 :{{ newb }}</div>
                        <div>  newd in cp1 :{{ newd }}</div>
                     </div>`,
      data() {
        return {
          a: 888
        }
      },
      // inject: [ 'c' ,'a']
      inject: {
        c: 'c',
        newa: 'a',
        newb: {
          from: 'b'
        },
        newd: {
          from: 'd',
          default: 7777
        },
      }
    })
    Vue.component('cp2', {
      template: `<div>我是cp2组件 <cp1></cp1></div>`,
​
    })
    Vue.component('cp3', {
      template: `<div>我是cp3组件 <cp2></cp2> </div>`,
      provide: {
        a: 1,
        b: 2,
        c: 3,
        d: 99999999
      }
    })
​
    const app = new Vue({
      el: '#app',
    })
    console.log(app)
  </script>

keep-alive

2创建,1销毁,2挂载

  • include - 指定被缓存的组件,数组、字符串或正则表达式。只有名称匹配的组件会被缓存。

    <!-- <keep-alive include="cp1,cp2"> -->
    <!-- <keep-alive :include="['cp1','cp2']"> -->
    <keep-aliveinclude="aliceCp">
    
  • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。

  • max - 数字。最多可以缓存多少组件实例,先进先销毁。

slot

简单使用:自定义组件内有东西想要使用时可以使用slot,被slot包裹的区域会进行展示。同时想东西展示在固定的位置时,可以slot=“aaa”取名,将想展示的东西包裹并slot="aaa"找到位置,如果有传参时使用slot-scope=“zzz”接收,{{zzz}}使用。而还有template时,寻找方式变为#aaa,如果有传参直接为#aaa="zzz",{{zzz}}使用。接收的参数是对象形式,所以可以通过zzz.调用。

refs

  <div id="app">
   <cp2 @abc="zzz"></cp2>
  </div>
  <script>
Vue.component('cp2',{
  template:`<div><div ref='ccc'></div></div>`,
    mounted() {//this.$refs可以访问到添加了ref命名的组件
      this.$refs.ccc.innerText = Date.now()
    },
})
    new Vue({
      el: '#app',  
    })
  </script>
</body>

$emit(事件名,传参.子传父)

触发一个事件,并传递一个参数

<body>
  <div id="app">
   <cp2 @abc="zzz"></cp2>
  </div>
  <script>
Vue.component('cp2',{
  template:`<div><h1>我是cp2</h1>{{ct}}
    <button @click="fn1">我是cp2中的按钮</button></div>`,
    methods: {
      fn1(){
        console.log('我是fn1');
        this.$emit('abc', 9999)
      }
    },//点击按钮触发组件中fn1,再触发$emit监听的abc并传参9999,寻找abc绑定的函数并执行
    data(){
      return{
        ct:100
      }
    },
})
    new Vue({
      el: '#app',
      methods:{
        zzz(res){
          console.log('我是根节点中zzz');
        }
      }
    })
  </script>
</body>

$nextTick

如果有些操作需要在状态变化并且导致视图更新之后再操作的,就需要使用,因为vue的状态改变导致视图更新是异步的

$set

原理:使用object.defineproperty来给某个对象的执行添加getter与setter达到响应式。 vue3劫持整个对象,所以不需要$set了。数组只改写了几个方法,因为不确定数组有多大。

为什么数组直接通过索引值修改成员不会引起视图更新??

vue为了性能考虑并不会给每个数组都添加getter与setter,但是vue改写了pop/push/unshift/

image-20240515152907082

    <div id="app"   >
            {{ 1+ 1}}
            <button @click="fn1">我是一个按钮</button>
    </div>  
    </div>
    <script>
            const Qfvue = Vue.extend({//继承
                methods:{
                    fn1(){
                        console.log('fn1执行')
                    }
                }  
            })
            new Qfvue({
                el:'#app'
            })
    </script>

$mount 执行挂载节点

Vue.use(插件)

插件实际上就是一个对象,通过引入可以使用插件中的方法

$watch

监听器,在mounted方法中使用:

 <div id="app">
    数量:{{num}}  x 价格:{{price}} ={{num*price}}元<br>
    <button @click="num++">数量++</button>
    <button @click="price++">价格++</button>
    <button @click="fn1">获取金额</button><br>
    {{zzzz.size}}
    <button @click="zzzz.size++">大小++</button>
  </div>
  <script>
    new Vue({
      el:'#app',
      data() {
        return {
          num:100,price:30,money:1,
          zzzz:{
            size:18
          }
        }
      },
      mounted() {
        this.$watch('num',(nv,ov)=>{
          console.log(nv);
        }),
        this.$watch('price',(nv,ov)=>{
          console.log(nv);
        }),
        this.$watch('zzzz',(nv,ov)=>{
          console.log(nv.size);
        },{deep:true})
        //深度监听,只监听zzzz时对象下size改变监听不到,使用deep:true
      },
    })
  </script>

组件通信 不建议

<body>
    <div id="app">
        <cp1></cp1>
        <cp2></cp2>
    </div>  
    <script>
        const bus = new Vue({})
        Vue.component('cp1',{
            template:`<div>我是cp1</div>`,
            mounted(){
                bus.$on('kfc',(val)=>{
                    console.log('kfc 得到val',val)
                })
            }
        })
        Vue.component('cp2',{
            template:`<div>
                            我是一个cp2组件
                            <button @click="fn1">我是cp2的按钮</button>
                       </div>`,
            methods:{
                fn1(){
                    bus.$emit('kfc',6666)
                }
            }
        })
        const app = new Vue({
            el: '#app',
        })
    </script>
</body>

虚拟dom render

render(h){return hh('div,'我是一个div')}
​
 render(h) {
        // vnode  虚拟dom
        // h可以帮我们创建一个虚拟dom  组件最终渲染的就是这个虚拟dom
        return h('div',{
          class:'a',
          attrs:{
            id:'kkkk'
          }
        },
        [
          h('h1','我是一个h1'),
          h('h1','我是一个p')
        ])
      }
​
render(h){//使用   
                return h('div',[
                    h('div','我是cp1组件'),
                    // h('div', this.$slots.default)
                    this.$slots.default,
                    this.$slots.kfc,
                ])

directives 自定义指令

指针改变

    <div id="app">
        <div v-abc="999" class="ffff"> </div>
        <button v-auth:add>添加</button>
        <button v-auth:del>删除</button>
        <button v-auth:update>编辑</button>
        <div v-copy>111111</div>
        <div v-happy> sad!!! </div>
    </div>
    <script>
        Vue.directive('happy',{
            inserted(el){
                el.innerText = ' so happy!!! '
            }
        })
​
        const app = new Vue({
            el: '#app', 
            data:{
                roles: ['add','update'],
                weltext: '我是一段得到1212312312被复制的内容'
            },            
            directives:{
                abc:{
                    inserted(el,binding,vnode){
                        console.log('this in  abc inserted', this)
                        console.log('el', el)
                        console.log('binding', binding)
                        console.log('vnode', vnode)
                    } 
                },
                auth:{
                    inserted(el,binding,vnode){
                        // binding.arg 
                        // vnode.context 
                        // el ??
                            if(!vnode.context.roles.includes(binding.arg)){
                                el.remove()
                            }
                    }
                },
                copy:{
                    inserted(el,_,vnode){
                        // el.onclick = ()=>{
                        //     navigator.clipboard.writeText(el.innerText);
                        //     alert('复制成功')
                        // }
                        el.onclick = (function(){
                            navigator.clipboard.writeText(this.weltext);
                        }).bind(vnode.context)
                    }
                }
            }
        })
    </script>

Vue3 cn.vuejs.org/

使用const x=()=>{}来写方法,@click="x"来调用 使用const x=ref(100)或const x=reactive({num:100})来赋值 使用const x=computed(()=>{return a.value+})来写计算属性,使用.value修改值的方式修改ref对象

简写箭头函数 function() ()=> function变为箭头

const{ref,reactive,computed}=Vue //所有用到的需要导入
    const app = Vue.createApp({//创建vue实例
      setup() {
        const a=ref(10)
        const b = reactive({
          name:'刚满十八岁',
          money:100
        })
        //使用const x=()=>{}来写方法,@click="x"来调用
        //使用const x=ref(100)或const x=reactive({num:100})来赋值
        //使用const x=computed(()=>{return a.value+})来写计算属性,使用.value修改值的方式修改ref对象
        const fn1=function(){
          b.money++
        }
        const fn2=()=>console.log('fn2');
        //简写箭头函数 function()  ()=>,function变为箭头
        const dbA = computed(()=>{
          return a.value*2
        })
        return{//如果要在页面使用都需要返回
          a,b,fn1,fn2,dbA
        }
      },
     
    })
    app.mount('#app')//挂载

组合式api

ref、reactive

ref 创建一个响应式的ref对象(状态) 读取修改值是通过value的值

reactive 创建一个被代理的响应式数据,代理的是对象或者数组等引用数据类型的数据

什么时候使用ref?

当reactive创建引用类型时,在后期重新赋值时会导致内存地址改变,响应式失效,而ref只是修改value,所以不会导致响应式失效。实际上ref就是特殊的reactive,

shallowRef、shallowReactive

由shallowRef创建的对象属性,只有在对象整体修改时才会触发响应式,而如修改对象下单个属性时不会触发响应式。

由shallowReactive创建的对象属性,在多层嵌套时只会在修改第一层的时候才会触发响应式,第二层开始就不会了。

unref

在setup中使用unref可以创建一个不具有响应式的数据(在setup中定义的const a=1;无法使用)

toref解构 将reactive解构为ref

setup中this指向问题

在setup中this指向windows,并且不能修改,那要这样得到和this.$refs(找到被ref标记的节点,获取到后可以进行相应的节点操作)对等的功能呢?首先需要const a = ref(),其中a为ref绑定的变量名(需要名字一模一样),然后就可以通过a.value获取到dom节点