一、简介

1.vue是一套用于构建用户界面的渐进式JavaScript框架
2.特点:

  • 采用组件化模式,提高代码复用率、且让代码更好维护
  • 声明式编码,让编码人员无需直接操作DOM,提高开发效率
  • 使用虚拟DOM+优秀的Diff算法,尽量复用DOM节点

二、安装

  • 包含完整的警告和调试模式:https://yuejs/js/vue.js
  • 删除了警告, 30.96KB min+gzip:https://vuejs/js/vue.min.js

直接用<script>引入,Vue被注册为一个全局变量

<script type="text/javascript" src="../js/vue.js"></script>

三、使用

//root容器里面的代码被称为“vue模板”
<div id="root">
    <h1>hello,{{name}}</h1>
</div>
//第一种写法
<script type="text/javascript">
    //阻止vue在启动时生成生产提示
    Vue.config.productionTip = false
        
    new Vue({
//el用于指定当前vue实例为哪个容器服务,值通常为css选择器字符串        
        el:'#root',
//data用于存储数据,数据供el所指定的容器去使用
        data:{
            name:"元元元"
        }
    })
</script>
//第二种写法
<script type="text/javascript">
    const vm = new Vue({
        data:function(){
            //这里的this是vue实例对象vm
            return{
                name:"xxx"
            }
        }
    })
    v.$mount('#root')
</script>
//第三种写法
<script type="text/javascript">
    const vm = new Vue({
        data(){
            //这里的this是vue实例对象vm
            return{
                name:"xxx"
            }
        }
    })
    v.$mount('#root')
</script>
//第四种写法
<script type="text/javascript">
    const v = new Vue({
        data:()=>{
            //这里的this是全局window对象
            return{
                name:"xxx"
            }
        }
    })
    v.$mount('#root')
</script>
  • vue实例和容器是一一对应的
  • 真实开发中只有一个vue实例,而且会配合着组件一起使用
  • {{xxx}}中写js表达式,且xxx可以自动读取到data中的所有属性,一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新

四、指令

内置指令

  • v-bind : 单向数据绑定,可简写为:xxx
  • v-model : 双向数据绑定
  • v-for : 遍历数组/对象/字符串
  • v-on : 绑定事件监听,可简写为@
  • v-if : 条件渲染(动态控制节点是否存存在)
  • v-else : 条件渲染(动态控制节点是否存存在)
  • v-show : 条件渲染(动态控制节点是否展示)
  • v-text :
    1.作用:向其所在的节点中渲染文本内容。
    2.与插值语法的区别: v-text会替换掉节点中的内容,{{xx}}则不会。
  • v-html:
    1.作用:向指定节点中渲染包含html结构的内容。
    2.与插值语法的区别
    (1).v-html会替换掉节点中所有的内容,{{xx}}则不会。
    (2).v- html可以识别html结构。
    3.严重注意: v-html有安全性问题! ! ! !
    (1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
    (2).一定要在可信的内容上使用v-html.永远不要用在用户提交的内容上!
  • v-cloak(没有值) :
    1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
    2.使用css配合v-cloak可以解决网速慢时页面展示出{{xx}}的问题。
  • v-once(没有值):
    1.v-once所在节点在初次动态渲染后,就视为静态内容了
    2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
  • v-pre:
    1.跳过其所在节点的编译过程。
    2.可利用它跳过没有使用指令语法、没有使用插值语法的节点,会加快编译。
<style>
    [v-cloak]{ 
        display:none;
    }
</style>
    
<div id="root">
    <h2 v-cloak>{{name}}</h2>
</div>
<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js”></script>

自定义指令

Vue模板语法有2大类:
1.插值语法:
功能:用于解析标签体内容。
写法: {{xxx}}, xxx是js表达式,且可以直接读取到data中的所有属性。
2. 指令语法:
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)。
举例: v-bind:href-“xxx” 或简写为 :href=“xxx”, xxx同样要写js表达式,且可以直接读取到data中的所有属性。
备注: Vue中有很多的指令,且形式都是: v-??? 此处我们只是拿v-bind举个例子。

定义语法:
(1).局部指令:

new Vue({
directives:{指令名:配置对象}
})

new Vue({
directives{指令名:回调函数}
})

(2).全局指令:
Vue.directive(指令名,配置对象)Vue .directive(指令名,回调函数)
配置对象中常用的3个回调:
(1).bind:指令与元素成功绑定时调用。
(2).inserted:指令所在元素被插入页面时调用。
(3).update:指令所在模板结构被重新解析时调用。
三、备注:
1.指令定义时不加v-,但使用时要加v-;
2.指令名如果是多个单词。要使用kebab-case命名方式,不要用camelCase命名。

<div id="root">
    <h2>当前的n值是: <span v-text="n"></span> </h2>
    <h2>放大10倍后的n值是: <span v-big="n"></span> </h2>
    <button @click="n++">点我n+1</button>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false
    Vue.directive('fbind',{
       bind(){
       },
       inserted(){
       },
       update(){
       }
    })
    Vue.directive('big',function(element,binding){
       element.innerText=binding.value*10
    })
    new Vue({
       el:'#root',
       data:{
            n :1
       },
       directives:{
           //big函数何时会被调用? 
           //1.指令与元素成功绑定时(一上来)
           //2.指令所在的模板被重新解析时。
           big(element,binding){
               //这里的this是window
               element.innerText=binding.value*10
           },
           fbind:{
               //指令与元素成功绑定时(一上来)
               bind(){
                   //这里的this是window
               },
               //指令所在元素被插入页面时
               inserted(){
                   //这里的this是window
               },
               //指令所在的模板被重新解析时
               update(){
                   //这里的this是window
               }
           }
       }
    })
</script>

五、模板语法

  • 插值语法
//功能:用于解析标签体内容。
//写法: {{xxx}}, xxx是js表达式,且可以直接读取到data中的所有属性。
<a href="">{{url}}</a>
  • 指令语法
//功能:用于解析标签(包括:标签属性、标签体内容、绑定事件.....)。
//v-bind:href="xxx"或:href="xxx", xxx同样要写js表达式,
//可以直接读取到data中的所有属性。
<a v-bind:href="url"></a>
//v-bind可以简写成:
<a :href="url"></a>

六、数据绑定

  • 单向数据绑定
//数据只能从data流向页面
<input type="text" v-bind:value="name"/>
  • 双向数据绑定
//v-model只能应用在表单类元素
<input type="text" v-model:value="name"/>
//v-model:value可以简写为v-model
<input type="text" v-model="name"/>

七、理解MVVM模型

  • M: 模型(Model) :对应data中的数据
  • V: 视图(View) :模板
  • VM: 视图模型(ViewModel) : Vue 实例对象

  • data中所有的属性,最后都出现在了vm身上。
  • vm身上所有的属性及Vue原型上所有属性。在Vue模板中都可以直接使用。

八、数据代理

Object.defineProperty(person,'age',{
    value:18,
    enumerable:true, //控制属性是否可以枚举,默认为false
    writable:true,//控制属性是否可以被修改
    configurable:true, //控制属性是否可以被删除
    //读取person的age属性时,get函数(getter)就会被调用
    get:function(){
        return 'hello'
    }
    //修改person的age属性时,set函数(setter)就会被调用
    set(value){
        return 'world'
    }
})

数据代理:通过一个对象代理对另一个对象中属性的操作

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

九、事件处理

事件的基本使用:
1.使用v-on:xxx或@xxx绑定事件。其中xxx是事件名
2.事件的回调需要配置在methods对象中,最终会在vm上;
3. methods中配置的函数,不要用箭头函数!否则this就不是vm了;
4. methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象;
5. @click=“demo” 和 @click="demo($event)"效果一致。但后者可以传参;

<div id="root">
    <button v-on:click="showInfo1">按钮</button>
    <button @click="showInfo2($event,66)">按钮</button>
 </div>
 <script type="text/javascript>
     new Vue({
         el:"#root",
         data:{
             
         },
         methods:{
             showInfo1(event){
                 //这里的this是vm
                 console.log(event.target.innerText)
             },
             /*
                 showInfo:(event)=>{
                     //这里的this是window对象
                 }
             */   
            showInfo2(event,number){
                alert(number);
            }  
         }
     })
</script>     

十、事件修饰符

Vue中的事件修饰符:
1.prevent:阻止默认事件
2.stop: 阻止事件冒泡
3.once:事件只触发一次
4.capture:使用事件的捕获模式:
5.self:只有event.target是当前操作的元素时才触发事件
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;

<div id="root">
    <button @click.prevent="showInfo">按钮</button>
</div>
<script type="text/javascript">
    new Vue({
        el:"#root",
        data:{
            
        },
        methods:{
            showInfo(){
                
            }
        }
    })    

十一、键盘事件

<div id="root">
    <input type="text"  placeholder="按下回车提示输入" @keyup.enter="showInfo"/>
</div>
<script type="text/javascript"/>
    Vue.config.productionTip=false;
    new Vue({
        el:"#root",
        data:{
        },
        methods:{
            showInfo(e){
                //if(e.keyCode !== 13) return
                console.log(e.target.value)
            }
        }
    })    
</script>    
  1. Vue中常用的按键别名:
    回车=>enter
    删除=>delete (捕获“删除”和“退格”键)
    退出=>esc
    空格=>space
    换行=>tab (必须配合keydown使用)
    上=>up
    下=>down
    左=>left
    右=>right

2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)(CapsLock=>caps-lock)
3.系统修饰键(用法特殊) : ctrl、alt、shift、 meta
(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(2).配合keydown使用:正常触发事件。
4.也可以使用keyCode去指定具体的按键(不推荐),该特性已经从Web标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持
5. Vue.config.keyCodes.自定义键名=键码,可以去定制按键别名

十二、计算属性

1.定义:要用的属性不存在,要通过已有属性计算得来。
2.原理:底层借助了0bjcet.defineProperty方法提供的getter和setter.
3. get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
4.优势:与methods 实现相比,内部有缓存机制(复用),效率更高,调试方便。
5.备注:
(1).计算属性最终会出现在vm上,直接读取使用即可。
(2).如果计算属性要被修改。那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生变化

<div id="root">
    姓:<input type="text" v-model="firstName"/>
    名:<input type="text" v-model="lastName"/>
    全名:<span>{{fullName}}</span>
</div>
<script type="text/javascript>
    new Vue({
        el:"#root",
        data:{
            firstName:"张",
            lastName:"三"
        },
        computed:{
            fullName:{
                //get什么时候调用?
                //1.初次读取fullName时(计算属性会被缓存)
                //2.所依赖的数据发生变化时
                get(){
                    //这里的this是vm
                    return this.firstName+this.lastName;
                },
                //set
                set(value){
                    const arr=value.split('-')
                    this.firstName=value[0]
                    this.lastName=value[1]
                }
            }
        }
    })
 </script>
 
 //简写方式 (只读不写)  
 computed:{
     fullName(){
         return this.firstName+this.lastName;
     }
 }

十三、监视属性

监视属性watch:
1.当被监视的属性变化时,回调函数自动调用,进行相关操作
2.监视的属性必须存在才能进行监视! !
3.监视的两种写法:
(1) new Vue时传入watch配置
(2) 通过vm.$watch监视

<div id="root">
    <span>今天天气很{{info}}</span>
    <button @click="weatherChanged">切换天气</button>
</div>
    <script type="text/javascript">
        Vue.config.productionTip = false
        new Vue({
            el:"#root",
            data:{
                isHot:true
            },
            methods:{
                weatherChanged(){
                    this.isHot=!this.isHot
                }
            },
            computed:{
                info(){
                    return this.isHot?'炎热':'凉爽'
                }
            },
            //方法一
            watch:{
                isHot:{
                    immediate:true,//初始化时让handler调用一次
                    //handler在isHot改变时被调用
                    handler(newValue,oldValue){
                        xxx
                    }
                }
                //简写(只有handler时)
                /*
                *   isHot(newValue,oldValue){
                *       xxx
                *   }
                */
            } 
        })

</script>

//方法二
vm.$watch('isHot',{
    immediate:true,
    handler(newValue,oldValue){
        xxx
    }
})
/*
*   简写:
*   vm.$watch('isHot',function(newValue,oldValue){
*       xxx
*   })
*/

//深度监视
//(1)Vue中的watch默认不监测对象内部值的改变(一层)
//(2)配置deep :true可以监测对象内部值改变(多层)。
//备注:
//(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
//(2).使用watch时根据数据的具体结构,决定是否采用深度监视。

<div id="root">
    <button @click="numbers.a++">加1</button>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false
    new Vue({
        el:"#root",
        data:{
            numbers:{
                a:1,
                b:2
            }
        },
        watch:{
            //监视多级结构中全部属性的变化
            numbers:{
                deep:true,
                handler(newValue,oldValue){
                    xxx
                }
            },
            //监视多级结构中某个属性的变化
            "numbers.a":{
                immediate:true,//初始化时让handler调用一次
                //handler在isHot改变时被调用
                handler(newValue,oldValue){
                    xxx
                }
            }
        } 
    })
</script>

十四、computed和watch之间的区别

  1. computed能完成的功能,watch都可以完成。
  2. watch能完成的功能,Computed不一定能完成,例如: watch可以进行异步操作。

两个重要的小原则:
1.所有被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数、promise的回调函数等),最好写成箭头函数,这样this的指向才是vm或组件实例对象。

十五、绑定class样式

:class=“xxx” xxx可以是字符串、对象、数组。

<div id="root">
    <!--绑定class样式,字符串写法,适用于样式的类名不确定,不需要动态指定-->
    <div class="basic" :class="normal" @click="changeMood">{{name}}</div>
    <!--绑定class样式,数组法-->
    <div class="basic" :class="arr">{{name}}</div>
    <!--绑定class样式,对象法-->
    <div class="basic" :class="classObj">{{name}}</div>
</div>    
<script type="text/javascript">
    Vue.config.productionTip = false
    
    new Vue({
        el:"#root",
        data:{
            name:'xxx',
            normal:"normal",
            arr:['style_1','style_2','style_3','style_4'],
            classObj:{
                xxx:false,
                xxxx:false,
            }
        },
        methods:{
            changeMood(){
                this.normal="happy"
            }
        }
    })
 </script>   

十六、绑定style样式

:style=" {fontSize: xxx}" 其中xxx是动态值。
;style="[a,b]"其中a、b是样式对象。

<div id="root">
    <div class="basic" :style="{fontSize:40+'px'}">{{name}}</div>
    <div class="basic" :style="styleObj">{{name}}</div>
    <div class="basic" :style="[styleObj,styleObj2]">{{name}}</div>
    <div class="basic" :style="styleArr">{{name}}</div>
</div>    
<script type="text/javascript">
    Vue.config.productionTip = false
    
    new Vue({
        el:"#root",
        data:{
            name:'xxx',
            styleObj:{
                fontSize:"40px"
            },
            styleObj2:{
                fontSize:"480px"
            },
            styleArr:[
                {
                    fontSize:"40px"
                },
                {
                    fontSize:"80px"
                }
            ]
        },
    })
 </script>   

十七、条件渲染

1.v-if
(1).v-if="表达式”
(2).v-else-if="表达式”
(3) v-else="表达式”
适用于:切换频率较低的场景。
特点:不展示的D0M元素直接被移除。
注意: v-if可以和v-else-if、v-else一起使用, 但要求结构不能被“打断”

2.v-show
写法: v-show=" 表达式"
适用于:切换频率较高的场景。
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉(display:none)

3.使用v-if的时,元素可能无法获取到,而使用v-show定可以获取到。

<div id="root">
    <button v-show="true">按钮</button>
    <button v-show="showOrHidden">按钮</button>
    
    <button v-if="true">按钮</button>
    <button v-if="showOrHidden">按钮</button>
    
    <div v-if="n==1">Angular</div>
    <div v-else-if="n==2">React</div>
    <div v-else-if="n==3">Vue</div>
    <div v-else></div>
    
    <!--template只能和v-if一起使用,而不能和v-show一起使用-->
    <template v-if="n==1">
        <h2>1</h2>
        <h2>2</h2>
        <h2>3</h2>
    </template>    
</div>    
<script type="text/javascript">
    Vue.config.productionTip = false
    
    new Vue({
        el:"#root",
        data:{
            showOrHidden:true
        },
    })
 </script>   

十八、列表渲染

V-for指令
1.用于展示列表数据
2.语法: v-for="(item, index) in xxx" :key=“yyy”
3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)

<div id="root">
    <!--遍历数组-->
    <ul>
        <li v-for="p in persons" :key="p.id">{{p.name}}-{{p.age}}</li>
    </ul>  
    
    <!--遍历数组2.0-->
    <ul>
        <li v-for="(p,index) in persons" :key="index">{{p.name}}-{{p.age}}</li>
    </ul>  
    
    <!--遍历对象-->
    <ul>
        <li v-for="(value,key) of car" :key="k"></li>
    </ul>
    
    <!--遍历字符串-->
    <ul>
        <li v-for="(char,index) of str" :key="index"></li>
    </ul>
    
    <!--遍历指定次数-->
    <ul>
        <li v-for="(number,index) of 5" :key="index"></li>
    </ul>
</div>    
<script type="text/javascript">
    Vue.config.productionTip = false
    
    new Vue({
        el:"#root",
        data:{
            persons:[
                {
                    id:'001',
                    name:'张三',
                    age:18
                },
                {
                    id:'002',
                    name:'李四',
                    age:20
                },
            ],
            car:{
                name:"xxx",
                type:"xxx",
                price:"xxx"
            },
            str:"helloWorld",
        },
    })
 </script>  

面试题: react、vue中的key有什么作用? ( 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). 若对数据进行:逆序添加、逆序刷除等破环顺序操作: 会产生没有必要的真实DOM更新==>界面效果没问题,但效率低。
(2).如果结构中还包含输入类的DOM:会产生错误DOM更新==>界面有问题。

4.开发中如何选择key?: (1).最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。 (2).如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示, 使用index作为key是没有问题的

十九、列表过滤

<div id="root">
    <input type="text" placeholder="请输入关键词" v-model="keyWord">
    <ul>
        <li v-for="(p,index) of filPersons" :key="index"></li>
    </ul>    
</div>
<script type="text/javascript>
    Vue.config.productionTip=false
    new Vue({
        el:"#root",
       data:{
           keyWord:"",
           persons:[
               {id:"001",name:"马冬梅",age:19,sex:"女"},
               {id:"002",name:"周冬雨",age:20,sex:"女"},
               {id:"003",name:"周杰伦",age:21,sex:"男"},
               {id:"004",name:"温兆伦",age:22,sex:"男"},
           ],
           filPersons:[]
       } ,
       watch:{
           keyWord:{
               immediate:true,
               handler(value){
                   this.firPersons = this.persons.filter((p)=>{
                       return: p.name.index0f(value)!== -1
                   }
               }
               
           }
       }
    })
</script>


<div id="root">
    <input type="text" placeholder="请输入关键词" v-model="keyWord">
    <ul>
        <li v-for="(p,index) of persons" :key="index"></li>
    </ul>    
</div>
<script type="text/javascript>
    Vue.config.productionTip=false
    new Vue({
        el:"#root",
       data:{
           keyWord:"",
           persons:[
               {id:"001",name:"马冬梅",age:19,sex:"女"},
               {id:"002",name:"周冬雨",age:20,sex:"女"},
               {id:"003",name:"周杰伦",age:21,sex:"男"},
               {id:"004",name:"温兆伦",age:22,sex:"男"},
           ],
       } ,
       computed:{
           filPersons(){
               return this.persons.filter((p)=>{
                          return: p.name.index0f(this.keyWord)!== -1
                       }
           }
       }
    })
</script>

二十、列表排序

<div id="root">
    <input type="text" placeholder="请输入关键词" v-model="keyWord">
    <button @click="sortType=2">年龄升序</button>
    <button @click="sortType=1">年龄降序</button>
    <button @click="sortType=0">原顺序</button>
    <ul>
        <li v-for="(p,index) of filPersons" :key="index"></li>
    </ul>    
</div>
<script type="text/javascript>
    Vue.config.productionTip=false
    new Vue({
        el:"#root",
        sortType:0,
       data:{
           keyWord:"",
           persons:[
               {id:"001",name:"马冬梅",age:19,sex:"女"},
               {id:"002",name:"周冬雨",age:20,sex:"女"},
               {id:"003",name:"周杰伦",age:21,sex:"男"},
               {id:"004",name:"温兆伦",age:22,sex:"男"},
           ],
       } ,
       computed:{
           filPersons(){
               const arr = this.persons.filter((p)=>{
                          return: p.name.index0f(this.keyWord)!== -1
                       }
               if(this.sortType){
                   arr.sort((a,b)=>{
                       return this.sortType==1?(b.age-a.age):(a.age-b.age)
                   })
               }       
               return arr; 
           }
       }
    })
</script>

二十一、vue监听数据变化的底层原理

Vue监视数据的原理:
1.vue会监视data中所有层次的数据。
2.如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target, propertyName/index, value)vm.$set(target, propertyName/index,value)

3.如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定要用如下方法:
(1).使用这些API: push()、pop()、shift()、unshift()、splice()、sort()、reverse()
(2).Vue.set()或vm.$set()
(3).使用filter()、concat()、slice()生成的新数组替换原数组
特别注意: Vue.set()vm.$set()不能给vm或vm的根数据对象添加属性! ! !

//简单模拟
<script type="text/javascript>
    let data = {
        name:'元元元',
        address:'北京'
    }
    //创建一个监视的实例对象,用于监视data中属性的变化
    const obs = new Observer (data)
    
    //准备一个vm实例对象
    let vm = {}
    vm._data = data = obs
    
    function Observer(obj){
        //汇总对象中所有的属性形成个一数组
        const keys = object.keys(obj)
        //遍历
        keys.forEach((k)=>{
            object.defineProperty(this,k,{
                get(){
                    return obj[k]
                },
                set(val){
                    console.log(`${k}被改了,我要去解析模板。生成虚拟DOM.....我要开始忙了`)
                    obj[k] = val
                })
       }    
    }        
</script>


Vue.set(vm._data.student,'sex','男')
vm.$set(vm._data.student,'sex','女')
vm.$set(vm.student,'sex','女')

Vue.set(vm._ data.student.hobby,1,'打台球')
vm.$set(vm._ data.student.hobby,1,'打台球')

二十二、v-model收集表单中的数据

收集表单数据:
若: <input type="text"/>.则v-model收集的是value值,用户输入的就是value值。
若: <input type="radio"/>.则v-model收集的是value值,且要给标签配置value值。
若: <input type="checkbox"/>
1).没有配置input的value属性,那么收集的就是checked(勾选or未勾选,是布尔值)
2).配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked (勾选or未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组

备注: v-model的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤

<div id="root">
    <form @submit.prevent="demo">
        账号: <input type="text" v-model.trim="userInfo.account"><br/><br/>
        密码: <input type="password" v-model="userInfo.password"><br/><br/>
        年龄:<input type="number" v-model.number="userInfo.password"><br/><br/>
        
        性别: 
        男<input type="radio" name="sex" v-model="userInfo.sex" value="femal">
        女<input type="radio" name="sex" v-model="userInfo.sex" value="male"> <br/><br/>
        
        爱好:
        学习<input type="checkbox" v-model="userInfo.hobby" value="study">
        打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
        吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
        <br/><br/>
        
        所属校区
        <select v-model="userInfo.city">
            <option value="">请选择校区</option>
            <option value="beijing">北京</option>
            <option value="shanghai">上海</option>
            <option value="shenzhen" >深圳</option>
            <option value="wuhan" >武汉</option>
        </select>
        
        其他信息:
        <textarea v-model.lazy="userInfo.other"></textarea> <br/><br/>
        
        <input type="checkbox" v-model="userInfo.aggre">阅读并接受<a href= "#">《用户协议》</a>
        <button>提交</button>
    </form>
</div>
<script type="text/javascript">
    Vue.config.productionTip=false
    new Vue({
        el:"#root",
        data:{
            userInfo:{
                account:"",
                password:"",
                sex:"",
                hobby:[],
                city:"",
                other:"",
                aggre:""
            }
        },
        methods:{
            demo(){
                console.log(JSON.stringify(this.userInfo))
            }
        }
    })
</script>      

二十三、过滤器

  • 定义:对要显示的数据进行特定格式化后再显示(适用于些简单逻辑的处理)。
  • 语法:
    1.注册过滤器: Vue.filter(name, callback)或new Vue{filters:{}}
    2.使用过滤器: {{ xxx| 过滤器名}} 或v-bind:属性= "xx| 过滤器名”
  • 备注:
    1.过滤器也可以接收额外参数、多个过滤器也可以串联
    2.并没有改变原本的数据,是产生新的对应的数据
<div id="root">
    <!--计算属性实现-->
    <h3>{{fmtTime}}</h3>
    <!--methods实现-->
    <h3>{{getFmtTime()}}</h3>
    <!--过滤器实现-->
    <h3>{{time | timeFormater}}</h3>
    <!--过滤器传参实现-->
    <h3>{{time | timeFormater('YYY_MM_DD')}}</h3>
    <!--过滤器传参实现-->
    <h3>{{time | timeFormater('YYY_MM_DD') | mySlice}}</h3>
    
    <h3 :x="time | timeFormater('YYY_MM_DD') | mySlice"></h3>
</div>
<script type="text/javascript">
    Vue.config.productionTip=false
    //全局过滤器
    Vue.filter('mySlice',function(value){
        return value.slice(0,4)
    })
    new Vue({
        el:"#root",
        data:{
            time:1621561377603 //时问戳
        },
        computed: {
            fmtTime(){
                return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
            }    
        },
        methods: {
            getFmtTime(){
                return dayjs(this.time).format('YYY年MM月DD日 HH:mm:ss')
            },
        },
        //局部过滤器
        filters:{
            timeFormater(value,str="YYY年MM月DD日 HH:mm:ss"){
                return dayjs(this.time).format(str)
            },
            mySlice(value){
                return value.slice(0,4)
            }
        }
</script>  

二十四、vue生命周期

生命周期:
1.又名:生命周期回调函数、生命周期函数、生命周期钩子
2.是什么: Vue在关键时刻帮我们调用的一些特殊名称的函数。
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4.生命周期函数中的this指向是vm或组件实例对象。

<div id="root">
    
</div>
<script type="text/javascript">
    Vue.config.productionTip = false
    
    new Vue({
       el:'#root',
       data:{
           
       },
       //vue完成模板的解析并把真实的DOM元素放入页面后(挂载完毕)调用mounted
       mounted(){
           //这里的this是vm
       }   
    })
</script>




常用的生命周期钩子:

  1. mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等[初始化操作]。
  2. beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等[收尾工作]。

关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会在beforeDestroy操作数据,因为即便操作数据。也不会再触发更新流程了。

二十五、组件

  • 模块 向外提供特定功能的js程序,一般就是一个js文件;复用js,简化js的编写,提高js运行效率
  • 组件 用来实现局部(特定)功能效果的代码集合(html/css/js/image…) ;复用编码,简化项目编码,提高运行效率
  • 模块化 当应用中的js都以模块来编写的,那这个应用就是一一个模块化的应用。
  • 组件化 当应用中的功能都是多组件的方式来编写的,那这个应用就是一个组件化的应用

二十六、非单文件组件

一个文件中包含有n个组件

//创建school组件
const school = Vue.extend({
    template:`<div>xxx</div>`,
    data(){
        return {
            studentName:'xxx',
            age:18
        }
    }
})
//注册组件:创建vm(局部注册)
new Vue({
    el:'#root',
    components:{
        school:school,
    }
})
//使用
<div id="root">
    <school></school>
</div>    


Vueponent('hello',hello)  //全局注册组件
new Vue({
    el:'#root'
})
//使用
<div id="root">
    <hello></hello>
</div>   

一、Vue中使用组件的三大步骤:
定义组件(创建组件)
注册组件
使用组件(写组件标签 )
二、如何定义一个组件?
使用Vue.extend(options)创建,其中options和new Vue(options )时传入的那个options几乎一样,但也有点区别:
1.e1不要写,最终所有的组件都要经过一个vm的管理,由vm中的e1决定服务哪个容器。
2. data必须写成函数,避免组件被复用时,数据存在引用关系。
3. 使用template 可以配置组件结构。
三、如何注册组件?
1.局部注册:靠new Vue的时候传入components选项
2.全局注册:靠Vueponent( ‘组件名’ ,组件)
四、编写组件标签:

<school></school>

五、几个注意点:
1.关于组件名:
一个单词组成:

  • 第一种写法(首字母小写): school
  • 第二种写法(首字母大写): School

多个单词组成:

  • 第一种写法( kebab-case命名): ‘my-school’
  • 第二种写法( CamelCase命名): MySchool (需要Vue脚手架支持)

注意:
(1).组件名尽可能回避HTML中已有的元素名称,例如: h2、 H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。

2.关于组件标签:
第一种写法: <school></school>
第二种写法: <school/>
备注:不用使用脚手架时, 会导致后续组件不能渲染。
3. 一个简写方式:
const school = Vue.extend(options)可简写为: const school = options

二十七、组件的嵌套

二十八、VueComponent

  1. 组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
  2. 我们只需要写<school/><school></school>. Vue解析时会帮我们创建组件的实例对象,即Vue帮我们执行: new VueComponent(options)。
  3. 每次调用Vue.extend,返回的都是一个全新的VueComponent!!!
  4. 关于this指向:
    (1).组件配置中:
    data函数、methods中的函数、watch中的函数、computed中的函数它们的this均是[ VueComponent实例对象:VC]。
    (2).new Vue(options)配置中:
    data函数、methods中的两 数、watch中的两数、computed中 的函数它们的this均是[Vue实例对象:VM]。
  5. vm管理着vc(vm.$children),组件是可复用的vue实例,所以它们与new Vue接收相同的选项,但它们也有区别,不能同等看待。

二十九、Vue和VueComponent的关系

VueComponent.prototype._ proto_ === Vue.prototype 让组件实例对象(vc) 可以访问到Vue原型上的属性、方法。

三十、单文件组件(.vue文件)

一个文件中只包含有一个组件

//School.vue  MySchool.vue
<template>
    <!--组件的结构-->
    <div class='demo'>
        <h2>{{schoolName}}</h2>
        <button @click="showName"></button>
    </div>    
</template>
<script>
    //组件交互相关的代码(数据、方法等等)
    1. export const school = Vue.extend({
        data(){
            return{
                shoolName:'湖南工商大学'
            }
        },
        methods:{
            showName(){
                alert(this.shoolName)
            }
        }
    })
    2. export {shool}
    3. export default shool
    4. export default Vue.extend({
        data(){
            return{
                shoolName:'湖南工商大学'
            }
        },
        methods:{
            showName(){
                alert(this.shoolName)
            }
        }
    })
    5. export default {
        name:'School',
        data(){
            return{
                shoolName:'湖南工商大学'
            }
        },
        methods:{
            showName(){
                alert(this.shoolName)
            }
        }
    }
</script>
<style>
    /*组件的样式*/
    .demo{
        background-color:orange;
    }
</style>
//App.vue汇总所有的组件
<template>
    <div>
        <School></School>
    </div>
</template>
<script>
    import School from './School'
    
    export default {
        name:'App',
        components:{
            School:School
        }
    }
 </script>
 <style>
 </style>   
//main.js 
import App from './App'
new Vue({
    el:'#root',
    component:{App}
})
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="root">
        <App></App>
    </div>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript" src="./main.js"></script>
</body>
</html>

更多推荐

vue基础知识汇总