vue知识回顾总结

1.数据代理(2022.5.1)

1.1数据代理 通过一个对象代理对另一个对象中属性的操作(读写)**

   <script>
       
        let obj = {
            x: 100
        };
        let obj2 = {
            y: 200
        }
        Object.defineProperty(obj2, 'x', {
            //当有人访问obj2.x属性,调用get() getter函数,且返回obj.x的值
            get() {
                console.log('我跟随obj.x 的修改');
                return obj.x
            },
            //当有人修改obj2.x属性,调用set() setter函数,且返回value的值
            set(value) {
                console.log('我修改了obj2.x的值,并返回给obj.x');
                //obj.x = value;
            }
        })

1.2 Object.defineProperty()

  • Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
  • 参数 Object.defineProperty(obj, prop, descriptor)
    1.obj 要定义属性的对象
    2.prop 要定义或修改的属性的名称
    3.descriptor 要定义或修改的属性描述符(配置项)
  • 返回值 传递给方法的对象
<script>
        let number = 18;
        let person = {
                name: '尚硅谷',
                sex: '男',
                //age: 18
            }
            //Object.defineProperty 向对象添加属性
            // object 属性名 {value}
        Object.defineProperty(person, 'age', {
                //value: 18,
                //enumerable: true, //控制属性是否可以被枚举(遍历),默认是false
                //writable: true, //控制属性是否可以被修改,默认是false
                //configurable: true, //控制属性是否可以被删除,默认是false

                //当有人访问person.age属性,调用get() getter函数,且返回age的值
                get: function() {
                    console.log('有人读取age属性了');
                    return number
                },

                //当有人修改person.age属性,调用set() setter函数,且返回value的值
                set(value) {
                    console.log('有人要修改age的值,且值是' + value);
                    number = value;

                }
            })
            /*    for (let k in person) {
                    console.log(person[k]);
                }*/
    </script>

1.3vue中的数据代理

Vue实例化的时候vm_data = option.data
数据代理时 name adderss 代理 vm_data
1.Vue中的数据代理:

    通过vm对象来代理data对象中属性的操作(读/写)

2.Vue中数据代理的好处:

    更加方便的操作data中的数据

3.基本原理:

    通过Object.defineProperty()把data对象中所有属性添加到vm上。

    为每一个添加到vm上的属性,都指定一个getter/setter。

vm_data=option.data

  <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                name: '尚硅谷',
                adderss: '北京科技园'
            },
        });
    </script>

2022.05.03-08点34分

2.事件处理

  • 使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名
  • 事件的回调需要配置在methods对象中,最终会在vm上
  • methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象
  • 不写参数,默认传递的是event; showInfo2($event,66),此时第一个参数是事件对象,第二个用户自定义
<body>
    <div class="root">

        <!-- <button v-on:click="showInfo">点击我提示信息(不传参)</button>-->
        <!-- 简写 -->
        <button @click="showInfo">点我提示信息</button>
        <!-- <button v-on:click="showInfo2($event,66)">点击我提示信息2</button> -->
        <button @click="showInfo2($event,66)">点击我提示信息2(传参)</button>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                name: '',
            },
            //回调函数
            methods: {
                showInfo(event) {
                    alert('你好,vue');
                },
                showInfo2(event, number) {
                    console.log(number);
                    alert('你好,vue22');
                },

            }

        });
    </script>

Vue中的事件修饰符

prevent:阻止默认事件(常用)
stop:阻止事件冒泡(常用)
once:事件只触发一次(常用)

<body>
    <div class="root">
        <!-- 事件修饰符 -->
        <!-- 1.阻止事件的默认行为 -->
        <a href="www.baidu.com" @click.prevent="showInfo">点击我</a>
        <!-- 2.阻止事件冒泡 -->

        <div class="demo" @click="showInfo2">
            <div class="demo1" @click.stop="showInfo2"></div>
        </div>
        <!-- 事件可以连写 -->
        <!-- <div class="demo1" @click.prevent.stop="showInfo2"></div> -->
        <!-- 3.事件只触发一次 -->
        <button @click.once="showInfo3">点击我</button>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                name: '',
            },
            methods: {
                showInfo() {
                    alert('阻止事件的默认行为');
                },
                showInfo2() {
                    alert('阻止事件冒泡');
                },
                showInfo3() {
                    alert('事件值触发一次');
                }
            }
        });
    </script>
</body>

键盘事件

<body>
    <!-- 键盘事件语法糖:@keydown,@keyup

        Vue中常用的按键别名:

        回车 => enter
        删除 => delete
        退出 => esc
        空格 => space
        换行 => tab (特殊,必须配合keydown去使用)
        @keydown.keycode(自定义配置按键 不推荐) 
         Vue.config.keyCodes.键名 = 键码,可以定制按键
        可以组合按键enter.x 触发事件-->
    <div class="root">
        <label for="">欢迎来到{{name}}学习</label><br>
        <input type="text" @keydown.enter="showInfo">
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                name: '尚硅谷',
            },
            methods: {
                showInfo(e) {
                    console.log(e.target.value);
                   
                }
            }
        });
    </script>
</body>

3.计算属性

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

    <div class="root">
        姓:<input type="text" v-model:value='firstName'> <br> 名:
        <input type="text" v-model="lastName"> <br> 姓名:
        <!-- 插值语法实现姓名案例 -->
        <!-- <span>{{firstName.slice(0,3)}}- {{lastName}}</span> -->

        <!-- 用回调函数实现姓名案例 -->

        <!-- 计算属性实现姓名案例 -->
        <!-- vm.fullName.get()会报错,与methods中函数不同 -->
        <span>{{fullName}}</span><br>
        <!-- <span>{{fullName}}</span><br>
        <span>{{fullName}}</span><br>
        <span>{{fullName}}</span><br>
        <span>{{fullName}}</span><br> -->



    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                firstName: '张',
                lastName: '三',
            },
            /*methods: {
                fullName() {
                    console.log('methods被调用了');
                    return this.firstName + '-' + this.lastName
                }
            }*/

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

            computed: {
                fullName: {
                    get() {
                        console.log('get被调用了');
                        //此时this 经过vue处理指向vm
                        return this.firstName + '-' + this.lastName
                    },
                    //此时的参数是修改后的新值
                    set(value) {
                        console.log('set', value)
                         //split 按照间隔符- 将字符串分成两个数组
                        const arr = value.split('-')
                        this.firstName = arr[0]
                        this.lastName = arr[1]

                    }
                }
            }
        });
    </script>
    </div>
</body>

4.监视属性

  • 用计算属性实现的监视属性也可以实现
<body>
    <div class="root">
        <span>今天的天气好{{Info}}</span><br>

        <button @click="changeWeather">点击切换天气</button>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                isHOT: true,
            },
            methods: {
                changeWeather() {
                    this.isHOT = !this.isHOT;
                    //console.log(this.isHOT);
                }
            },
            computed: {

                Info() {
                    return this.isHOT ? '炎热' : '凉快'
                }

            },
            //监视某一个属性值的变化 计算属性也可以监视
            //handler什么时候调用?当isHot发生改变时。
            watch: {
                isHOT: {
                    //初始化时执行一次handler 默认值是false
                    immediate: true,
                    handler(newvalue, oldvalue) {
                        console.log(newvalue, oldvalue);
                    }
                }
            }




        });

        //监视属性的第二种写法 开始不确定监视的属性
        /* vm.$watch('isHot', {
             immediate: true,
             handler(newvalue, oldvalue) {
                 console.log(newvalue, oldvalue);
             }
         })*/
    </script>

5.绑定class样式

<style>
        .basic {
            width: 200px;
            height: 100px;
            border: 1px solid black;
        }
        
        .happy {
            background-color: pink;
        }
        
        .sad {
            background-color: green;
        }
        
        .normal {
            background-color: blue;
        }
        
        .atguigu1 {
            background-color: pink;
        }
        
        .atguigu2 {
            border-radius: 30px;
        }
        
        .atguigu3 {
            border: 10px solid skyblue;
        }
    </style>
</head>

<body>
    <div class="root">
        <!-- 绑定class样式--字符串写法,适用于class的类名不确定,需要动态指定 -->
        <!-- <div class="basic" :class="mood" @click="changeMood">{{name}}</div><br><br> -->


        <!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定  -->
        <!-- 可以使用数组方法 arr.shift arr.push 增加或者删除样式 -->
        <div class="basic" :class="moodArr">{{name}}</div><br><br>

        <!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
        <div class="basic" :class="moodObj">{{name}}</div><br><br>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                name: '尚硅谷',
                mood: 'normal',
                moodArr: ['atguigu1', 'atguigu2', 'atguigu3'],
                moodObj: {
                    atguigu1: false,
                    atguigu2: false,
                    atguigu3: true
                }
            },
            methods: {
                changeMood() {
                    const MoodArr = ['happy', 'sad', 'normal'];
                    const index = Math.floor(Math.random() * 3);
                    console.log(index);
                    this.mood = this.MoodArr[index];

                },

            },
            watch: {}
        });
    </script>
</body>

6.条件渲染

1.1 v-if

写法:

(1).v-if=“表达式”

(2).v-else-if=“表达式”

(3).v-else=“表达式”

  • 适用于:切换频率较低的场景

  • 特点:不展示的DOM元素直接被移除

  • 注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”

  <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>

1.2 v-show

  • 写法:v-show=“表达式”
  • 适用于:切换频率较高的场景
  • 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉(display:none)
    <button @click="n++">点击让n++</button>
    <button @click="n--">点击让n--</button>
    <div class="test" v-show="n==2">欢迎来到{{name}}</div>

1.3 列表渲染

v-for指令

  • 用于展示列表数据
  • 语法:v-for=“(item, index) in xxx” :key=“yyy”
  • 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
  • v-for 的两个参数 一个是索引值 一个是索引号
<body>
    <div class="root">
     
        <!-- v-for 的两个参数 一个是索引值 一个是索引号 -->
        <!-- 遍历数组 -->
        <ul>{{name}}
            <li v-for="(p,index) in persons" :key="index">{{p.name}}--{{index}}</li>
        </ul>


        <!-- 遍历对象 第一个参数是value 第二个是键码 key -->
        <ul>

            <li v-for="(car,key) in cars">{{key}}--{{car}}</li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                name: '人员列表',
                persons: [{
                    name: '张三',
                    id: 001,
                    age: 18
                }, {
                    name: '历史',
                    id: 002,
                    age: 19
                }, {
                    name: '王五',
                    id: 003,
                    age: 20
                }],
                cars: {
                    name: '奥迪A8',
                    price: '70万',
                    color: '黑色'
                },
                str: 'hello'
            },
            methods: {},
            watch: {}
        });
    </script>
</body>

虚拟DOM中key的作用

  • key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

  • 旧虚拟DOM中找到了与新虚拟DOM相同的key:

  • ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!

  • ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

  • 旧虚拟DOM中未找到与新虚拟DOM相同的key

  • 创建新的真实DOM,随后渲染到到页面。

用index作为key可能会引发的问题:

  • 若对数据进行:逆序添加、逆序删除等破坏顺序操作:

  • 会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

- `<!-- 准备好一个容器-->
<div id="root">
    <!-- 遍历数组 -->
    <h2>人员列表(遍历数组)</h2>
    <button @click.once="add">添加一个老刘</button>
    <ul>
        <li v-for="(p,index) of persons" :key="index">
            {{p.name}}-{{p.age}}
            <input type="text">
        </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: 19 },
				{ id: '003', name: '王五', age: 20 }
			]
		},
		methods: {
			add() {
				const p = { id: '004', name: '老刘', age: 40 }
				this.persons.unshift(p)
			}
		},
	});
</script>

初始数据

persons: [
{ id: ‘001’, name: ‘张三’, age: 18 },
{ id: ‘002’, name: ‘李四’, age: 19 },
{ id: ‘003’, name: ‘王五’, age: 20 }
]

vue根据数据生成虚拟 DOM

初始虚拟 DOM

<li key='0'>张三-18<input type="text"></li>
<li key='1'>李四-19<input type="text"></li>
<li key='2'>王五-20<input type="text"></li>

将虚拟 DOM 转为 真实 DOM

this.persons.unshift({ id: ‘004’, name: ‘老刘’, age: 40 })

在 persons 数组最前面添加上 { id: ‘004’, name: ‘老刘’, age: 40 }

新数据:

persons: [
​ { id: ‘004’, name: ‘老刘’, age: 40 },
​ { id: ‘001’, name: ‘张三’, age: 18 },
​ { id: ‘002’, name: ‘李四’, age: 19 },
​ { id: ‘003’, name: ‘王五’, age: 20 }
]

vue根据数据生成虚拟 DOM

新虚拟 DOM

<li key='0'>老刘-30<input type="text"></li>
<li key='1'>张三-18<input type="text"></li>
<li key='3'>李四-19<input type="text"></li>
<li key='4'>王五-20<input type="text"></li>

将虚拟 DOM 转为 真实 DOM
因为老刘被插到第一个,重刷了 key 的值,vue Diff 算法 根据 key 的值 判断 虚拟DOM 全部发生了改变,然后全部重新生成新的 真实 DOM。实际上,张三,李四,王五并没有发生更改,是可以直接复用之前的真实 DOM,而因为 key 的错乱,导致要全部重新生成,造成了性能的浪费。
在这里插入图片描述

结论:

最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值
如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的

正确使用key
在这里插入图片描述
2022-05-04-09点18分

7.获取表单元素的技巧

1.v-model默认收集的是value
2.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
3.男女信息收集标准做法 name=“sex”
4.多组checkbox收集,data用数组
v-model的三个修饰符

  • trim 去掉首尾的空格
  • lasy 失去焦点在收集数据
  • number 收集到数据是数字类型的 一般与 < input type=“number”>搭配使用
<!-- v-model的三个修饰符
     trim 去掉首尾的空格
     lasy 失去焦点在收集数据
     number 收集到数据是数字类型的 一般与<input type="number">搭配使用 -->

<body>
    <div class="root">
        <form action="" @submit.prevent="demo">
            <!-- v-model 默认收集的value -->
            <label>账号: <input type="text" v-model.trim="account"> </label><br><br>
            <label>密码: <input type="password" v-model.trim="password"> </label><br><br>
            <label>年龄: <input type="number" v-model.number="age"> </label><br><br>
            <label>性别::<input type="radio" name="sex" checked="checked" value="male" v-model="sex">
                 :<input type="radio" name="sex" value="female" v-model="sex"></label><br><br>
            <!-- 多组checkbox收集,data用数组  -->
            <label>爱好: 吃饭:<input type="checkbox" checked="checked" value="eat" v-model="hobbies"> 
                        睡觉:<input type="checkbox" value="sleep" v-model="hobbies">
                        打豆豆:<input type="checkbox" value="beatDou" v-model="hobbies">
                    </label><br><br>
            <!-- select收集数据在v-model绑定在select 不要写在option上 -->
            <select name="" id="" v-model="schAddress">
                    <option value="" >请选择你的校区</option>
                    <option value="beijing" >北京</option>
                    <option value="shanghai" >上海</option>
                    <option value="guangzhou" >广州</option>
                    <option value="shenzhen" >深圳</option>
                    
                </select><br><br>

            <label>其他信息: <textarea name="" id="input" class="form-control" rows="3" required="required" v-model.lazy="otherInfo">
               
                    
            </textarea></label><br><br>
            <!-- 此时不需要input 的value -->
            <input type="checkbox" v-model="agree"><span>阅读并接受 <a href="">《用户协议》</a></span><br><br>
            <button>提交</button>



        </form>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                account: '',
                password: '',
                age: '',
                sex: '',
                hobbies: [],
                schAddress: '',
                otherInfo: '',
                agree: ''

            },
            methods: {
                demo() {
                    console.log(JSON.stringify(this._data));
                }
            },
            computed: {},
            watch: {}
        });
    </script>

2022-5-5-09点46分

8.内置指令

  • v-text
    v-text指令:(使用的比较少)
    1.作用:向其所在的节点中渲染文本内容。
    2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。
    3.v-text 不会解析html标签
<div class="root">
        <!-- v-text指令:(使用的比较少) -->
        <!-- 1.作用:向其所在的节点中渲染文本内容。 -->
        <!-- 2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。 -->
        <!-- 3.v-text 不会解析html标签 -->
        <div>你好,{{name}}</div>
        <div v-text="name">你好</div>

  • v-html

      1.作用:向指定节点中渲染包含html结构的内容。
      2.与插值语法的区别:
      v-html会替换掉节点中所有的内容,{{xx}}则不会。
      v-html可以识别html结构。
      3.严重注意:v-html有安全性问题!!!!
      在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
      一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
    
//此时会解析str中html标签
<div v-html="str">这是我原来的结构</div>
 <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                n: 0,
                name: '尚硅谷',
                str: '<h2>这是一个html 的h2标签</h2>'
            },
            methods: {},
            computed: {},
            watch: {}
        });
    </script>

  • v-once
    1. v-once所在节点在初次动态渲染后,就视为静态内容了。
    2. 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
		//此时div中 n的值是第一次的值
		<div v-once>{{n}}</div>
        <button @click="n++">点击我让n++---{{n}}</button>
  • v-pre
    v-pre指令:(比较没用)
    跳过其所在节点的编译过程
    可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
		<div v-pre>我没有使用vue</div>
       	<div>{{name}}</div>

9.自定义指令

配置项关键词 directives
简写形式不能进行复杂的调用

<body>
    <div class="root">
        <div v-text="n"></div>
        <!-- 自定义指令 使n数值放大十倍 -->
        <div v-big="n"></div>
        <button @click="n++">点击让n++</button><br>
        <input type="text" v-fbind:value="n">


    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                n: 1,
                name: '',
            },
            methods: {},
            computed: {},
            watch: {},
            directives: {
                //big 函数何时被调用? 1.指令和元素绑定时(一上来,此时元素在内存中,还没有渲染到页面上)
                //                    2.指令所在的模板重新解析时(值发生变化就会重新解析)
                big(element, binding) {
                //参数1 真实dom元素 参数2指令
                    //console.log(a, b);
                    element.innerText = binding.value * 10;
                },
                /*fbind(element, binding) {
                    //此时input第一次不能获得焦点
                    //点击一次input被放到html 才能获得焦点
                    element.value = binding.value;
                    element.focus();
                    // console.log(binding);
                }*/
                fbind: {
                    //1.指令和元素绑定时
                    bing(element, binding) {
                        element.value = binding.value;
                        console.log("bind");
                    },
                    //2.指令所在元素被插入页面时
                    inserted(element, binding) {
                        element.focus();
                        console.log('inserted');
                    },
                    //3.指令所在的模板被重新解析时
                    update(element, binding) {
                        element.value = binding.value;
                        console.log('update');
                    }

                }
            }
        });
    </script>
</body>
  • 定义全局指令
    Vue.directive('fbind', {
        // 指令与元素成功绑定时(一上来)
        bind(element, binding){
            element.value = binding.value
        },
        // 指令所在元素被插入页面时
        inserted(element, binding){
            element.focus()
        },
        // 指令所在的模板被重新解析时
        update(element, binding){
            element.value = binding.value
        }
    })

10.Vue的生命周期

Vue 实例有⼀个完整的⽣命周期,也就是从new Vue()、初始化事件(.once事件)和生命周期、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是Vue的⽣命周期。

引用一张尚硅谷老师的图
在这里插入图片描述

<!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>
    <script src="./js/vue.js"></script>
</head>

<body>
    <div class="root">
        <!-- <h2>{{name}}</h2> -->
        <h3>{{n}}</h3>
        <button @click="n++">点击让n++-{{n}}</button>
        <button @click="stop">点击我销毁vm</button>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                //name: '尚硅谷',
                n: 1
            },
            methods: {
                stop() {
                    vm.$destroy();
                }
            },
            computed: {},
            watch: {},
            //beforeCreate(创建前):数据监测(getter和setter)和初始化事件还未开始,
            //此时 data 的响应式追踪、event/watcher 都还没有被设置,
            //也就是说不能访问到data、computed、watch、methods上的方法和数据
            beforeCreate() {
                console.log('beforeCreate');

                console.log(this);
                //debugger;
            },

            /*created(创建后):实例创建完成,
            实例上配置的 options 包括 data、computed、watch、methods 等都配置完成,
            但是此时渲染得节点还未挂载到 DOM,所以不能访问到 $el属性。*/
            created() {
                console.log('create');

                console.log(this);
                //debugger;
            },

            /*beforeMount(挂载前):在挂载开始之前被调用,相关的render函数首次被调用。
            此阶段Vue开始解析模板,生成虚拟DOM存在内存中,还没有把虚拟DOM转换成真实DOM,
            插入页面中。所以网页不能显示解析好的内容。
            此时可以操作dom,但最终不会生效*/
            beforeMount() {
                console.log('beforeMount');
                console.log(this);
                //debugger;

            },

            /*mounted(挂载后):在el被新创建的 vm.$el(就是真实DOM的拷贝)替换,
            并挂载到实例上去之后调用(将内存中的虚拟DOM转为真实DOM,真实DOM插入页面)。
            此时页面中呈现的是经过Vue编译的DOM,这时在这个钩子函数中对DOM的操作可以有效,
            但要尽量避免。一般在这个阶段进行:开启定时器,发送网络请求,订阅消息,绑定自定义事件等等*/
            mounted() {
                console.log('mounted');
                console.log(this);
                //debugger;
            },
            /*beforeUpdate(更新前):响应式数据更新时调用,此时虽然响应式数据更新了,
            但是对应的真实 DOM 还没有被渲染(数据是新的,但页面是旧的,页面和数据不同步 */
            beforeUpdate() {
                console.log('beforeUpdate');
                console.log(this);
                //debugger;
            },

            /*updated(更新后) :在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。
            此时 DOM 已经根据响应式数据的变化更新了。
            调用时,组件 DOM已经更新,所以可以执行依赖于DOM的操作。
            然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用*/
            updated() {
                console.log('updated');
                //debugger;
                console.log(this);
            },

            /*beforeDestroy(销毁前):实例销毁之前调用。这一步,实例仍然完全可用,this 仍能获取到实例。
            在这个阶段一般进行关闭定时器,取消订阅消息,解绑自定义事件*/
            beforeDestroy() {
                console.log('beforeDestroy');
            },

            /* destroyed(销毁后):实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,
            所有的事件监听器(自定义)会被移除,原生事件不会被移除比如click,
            所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。*/
            destroyed() {
                console.log('destroyed');
            },
        });
    </script>
</body>

</html>

11.非单文件组件

什么是组件 实现局部代码和功能的集合
基本使用
Vue中使用组件的三大步骤:

  • 定义组件(创建组件)
  • 注册组件
  • 使用组件(写组件标签)

定义组件

使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;

区别如下:

el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系,引用关系错乱。


        let data = {
            x: 100,
            y: 100
        }
        const a = data;
        const b = data;
        console.log(a);
        console.log(b);
        console.log(a === b); //true 此时返回的是地址, a===b 修改a或者b的都会导致 data的值改变

        function data1() {
            return {
                x: 100,
                y: 100
            }
        }
        const c = data1();
        const d = data1();
        console.log(c === d); //false 此时返回的是对象, a===b 修改a或者b的都会不影响 data的值改变

组件的使用

<body>

    <div class="root">

        <!-- 第三步:使用组件 -->
        <school></school>

        <hr>
        <student></student>
        <hr>
        <hello></hello>
        <hello></hello>
        <!-- <hello></hello> -->

    </div>


    <script>
        // el不要写, 为什么?——— 最终所有的组件都要经过一个vm的管理, 由vm中的el决定服务哪个容器。
        // data必须写成函数, 为什么?———— 避免组件被复用时, 数据存在引用关系

        //局部组件
        //第一步:注册组件
        const school = Vue.extend({
            template: ` <div>
                            <h2>{{name}}</h2>
                            <h2>{{address}}</h2> 
                                </div> `,
            data() {
                return {
                    name: '尚硅谷',
                    address: '北京',
                }
            }
        });
        const student = Vue.extend({

            template: ` <div>
                        <h2>{{name}}</h2>
                        <h2>{{age}}</h2>
                                </div>   `,
            data() {
                return {
                    name: '张三',
                    age: 18,
                }
            }
        });
        const hello = Vue.extend({

            template: ` <div>
            <h2>{{msg}}</h2>
            
                    </div>   `,
            data() {
                return {
                    msg: 'hello',

                }
            }
        });
        //全局组件绑定 参数1组件标签名字  参数 注册的组件变量
        //所有的vue 实例 都可以使用hello 组件
        Vue.component('hello', hello);
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            /*data: {
                name: '尚硅谷',
                address: '北京',
                name: '张三',
                age: 18
            },*/
            //第二步:绑定组件
            components: {
                school,
                student
            },
            methods: {},
            computed: {},
            watch: {}
        });
    </script>
</body>

组件的几个注意点

关于组件名:
可以使用name配置项在vue 中指定标签的名字

  • 一个单词组成:
    第一种写法(首字母小写): school
    第二种写法(首字母大写): School
  • 两个单词组成:
    第一种写法(kebab - case命名): my - school
    第二种写法(CamelCase命名): MySchool(需要Vue脚手架支持)

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

  • 组件的简写
   // 可以简写为
        //此时vue 会调用一个函数 如果穿过去的是一个对象 会自动转化为 组件
        const myschool = ({
            template: `
            <h2>{{name}}</h2>
            `,
            data() {
                return {
                    name: '尚硅谷'
                }
            }

        })

组件的嵌套

<!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>
    <script src="./js/vue.js"></script>
</head>

<body>
    <div class="root">
        <app></app>
    </div>
    <script>
        const student = Vue.extend({

            template: ` <div>
        <h2>{{name}}</h2>
        <h2>{{age}}</h2>
        </div>   `,
            data() {
                return {
                    name: '张三',
                    age: 18,
                }
            }
        });
        const school = Vue.extend({
            template: `
            <div>
                <h2>{{name}}</h2>
                <h2>{{address}}</h2>
                <student></student>
            </div>
            `,
            components: {
                student
            },
            data() {
                return {
                    name: '尚硅谷',
                    address: '北京昌平'
                }
            }
        });
        const hello = Vue.extend({

            template: ` <div>
<h2>{{msg}}</h2>

        </div>   `,
            data() {
                return {
                    msg: 'hello',

                }
            }
        });
        //创建一个app组件用来管理所有的组件
        const app = Vue.extend({
            components: {
                school,
                hello
            },
            template: ` <div>
                <school></school>
                <hello></hello>
                </div>   `,

        });
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                name: '',
            },
            methods: {},
            computed: {},
            watch: {},
            components: {
                app
            }
        });
    </script>
</body>

</html>

Vuecomponents

  • school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
  • 我们只需要写或,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)。
  • 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!(这个VueComponent可不是实例对象)
  • 关于this指向:
    组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。
    new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。
  • VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。Vue的实例对象,以后简称vm。

一个重要的内置关系

<script>
        function Demo() {
            this.a = 1;
            this.b = 2;
        }
        const d = new Demo();
        console.log(Demo.prototype); //显示原型属性
        console.log(d.__proto__); //隐式原型属性
        console.log(Demo.prototype === d.__proto__);
        Demo.prototype.x = 99;
        console.log(Demo);
        console.log(d);
        //原型对象 原型链
        //d 是Demo的实例对象 Demo{a:1,b:2} 隐式原型属性[[Prototype]]是 _ _proto_ _的标准名字, 谷歌中为_ _proto_ _
        //对原型对象的理解
        //Demo.protoType 找到的是Demo的原型对象(是一个对象,也有原型对象),d._ _proto_ _找到的是自己缔造者的原型对象
        //此时再向下查找 找到了object的原型对象 也就是原型链的尽头
        //一般通过Demo.protoType 向Demo的原型对象添加属性 通过new Demo() 创建的实例对象都可以使用这个属性


        //一个重要的内置关系:VueComponent.prototype.proto === Vue.prototype
        //vue使VueComponent的原型对象的隐式属性不指向object的原型对象,而是指向了Vue的原型对象
        //为什么要有这个关系: 让组件实例对象( vc school) 可以访问到 Vue原型上的属性、 方法。
        const school = Vue.extend({
            template: `
            <div>
                <h2>{{name}}</h2>
                <h2>{{address}}</h2>
               
            </div>
            `,
            components: {
                //student
            },
            data() {
                return {
                    name: '尚硅谷',
                    address: '北京昌平'
                }
            }
        });
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '.root',
            data: {
                name: '',
            },
            methods: {},
            computed: {},
            watch: {},
            components: {
                school
            }
        });
    </script>

2.脚手架 vue cli

使用前置:
第一步(没有安装过的执行):全局安装 @vue/cli
npm install -g @vue/cli
第二步:切换到要创建项目的目录,然后使用命令创建项目
vue create xxxxx 项目名称
第三步:启动项目
npm run serve

2.1脚手架文件结构

├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件
————————————————

2.2demo

  • 入口文件- main.js
//引入Vue组件
import Vue from 'vue'

//引入App组件 App组件是所有组件的父组件
import App from './App.vue'

//配置Vue
Vue.config.productionTip = false

//创建一个vm,并选择id=app为容器
new Vue({
    render: h => h(App),
}).$mount('#app')
  • App组件 App.vue 所有组件的父组件
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
    <MySchool></MySchool>
    <MyStudent></MyStudent>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'
import MyStudent from './components/MyStudent.vue'
import MySchool from './components/MySchool.vue'


export default {
  name: 'App',
  components: {
    HelloWorld,
    MyStudent,
    MySchool
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

  • 子组件 MySchool.vue
<template>
<div>
    <h2>{{name}}</h2>
    <h2>{{address}}</h2>

</div>
</template>
<script>
export default {
    
           name:'MySchool',
            data() {
                return {
                    name: '尚硅谷',
                    address: '北京昌平'
                }
            }
        
}
</script>

<style>

</style>
  • 脚手架的index
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
	<!-- 针对IE浏览器的一个特殊配置,含义是让IE浏览器以最高的渲染级别渲染页面 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
	<!-- 开启移动端的理想视口 -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
	<!-- 配置页签图标 -->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
	<!-- 引入第三方样式 -->
	<link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">
	<!-- 配置网页标题 -->
    <title>硅谷系统</title>
  </head>
  <body>
		<!-- 当浏览器不支持js时noscript中的元素就会被渲染 -->
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
		<!-- 容器 -->
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>


使用 import 导入第三方库的时候不需要 加 ‘./’

  • 导入我们自己写的:

import App from ‘./App.vue’

  • 导入第三方的

    import Vue from 'vue‘ //不需要写具体路径 vue已经帮我们配置好了
    不需要在 from ‘vue’ 加 ‘./’ 的原因是第三方库 node_modules 人家帮我们配置好了。
    我们通过 import 导入第三方库,在第三方库的 package.json 文件中确定了我们引入的是哪个文件
    通过 module 确定了我们要引入的文件。

详解main.js render 函数

之前的写法是这样:

import App from ‘./App.vue’

new Vue({
el:‘#root’,
template:<App></App>,
components:{App},
})

如果这样子写,运行的话会引发如下的报错
报错的意思是,是在使用运行版本的 vue ,没有模板解析器
从上面的小知识可以知道,我们引入的 vue 不是完整版的,是残缺的(为了减小vue的大小)。所以残缺的vue.js 只有通过 render 函数才能把项目给跑起来。

来解析一下render
// render最原始写的方式
// render是个函数,还能接收到参数a
// 这个 createElement 很关键,是个回调函数

new Vue({
  render(createElement) {
      console.log(typeof createElement);
      // 这个 createElement 回调函数能创建元素
      // 因为残缺的vue 不能解析 template,所以render就来帮忙解决这个问题
      // createElement 能创建具体的元素
      return createElement('h1', 'hello')
  }
}).$mount('#app')

因为 render 函数内并没有用到 this,所以可以简写成箭头函数。

new Vue({
  // render: h => h(App),
  render: (createElement) => {
    return createElement(App)
  }
}).$mount('#app')

再简写:

new Vue({
  // render: h => h(App),
  render: createElement => createElement(App)
}).$mount('#app')

最后把 createElement 换成 h 就完事了。

来个不同版本 vue 的区别

vue.js与vue.runtime.xxx.js的区别

  • vue.js是完整版的Vue,包含:核心功能+模板解析器。
  • vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
  • 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容

修改脚手架的默认配置

  • 使用vue inspect > output.js可以查看到Vue脚手架的默认配置。//此时修改不起作用
  • 使用vue.config.js可以对脚手架进行个性化定制

vue组件的一些属性

ref属性

  • 被用来给元素或子组件注册引用信息(id的替代者)
  • 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
  • 使用方式:
  • 打标识:
  • < h1 ref=“xxx”>…或 < School ref=“xxx”>
  • 获取:this.$refs.xxx //this此时是vc app的组件实例对象
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
    <MySchool></MySchool>
    <MyStudent ref="stu"></MyStudent>
    <button @click="showDom">点击点击</button>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'
import MyStudent from './components/MyStudent.vue'
import MySchool from './components/MySchool.vue'


export default {
  name: 'App',
  components: {
    HelloWorld,
    MyStudent,
    MySchool
  },
  methods: {
    showDom(){
      console.log(document.querySelector('img'));
      console.log(this.$refs.stu);// 此时获取到的是School组件的实例对象(vc)
    }
  },
}
</script>

props属性

  • 功能:让组件接收外部传过来的数据

  • 传递数据:< Demo name=“xxx”/>

  • 接收数据:

    1.第一种方式(只接收):props:[‘name’]

    2.第二种方式(限制类型):props:{name:String}

    3.第三种方式(限制类型、限制必要性、指定默认值):

props:{
	name:{
        type:String, //类型
        required:true, //必要性
        default:'老王' //默认值
	}
}

备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。

mixin属性 混入

  • 本质是一个对象 可以定义多个混入 引入方式是数组
  • 混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
mixin.js
//混合
//两个组件的某个方法属性一样,可以使用混合共同为组件添加属性方法
export default {
    methods: {
        showDom() {
            // console.log(document.querySelector('img'));
            // console.log(this);
            // console.log(this.$refs.stu);
            alert(this.name);
        }
    }
}
引入

在需要引入的组件配置 mixins:[ ] //可以是多个混入

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
    <MySchool  ></MySchool>
    <hr>
    <MyStudent ref="stu"></MyStudent>
    <button @click="showDom">点击点击</button>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'
import MyStudent from './components/MyStudent.vue'
import MySchool from './components/MySchool.vue'
import mixins from './mixin'
export default {
  name: 'App',
  components: {
    HelloWorld,
    MyStudent,
    MySchool
  },

  data() {
    return {
      name:'aPP'
    }
  },
  mixins:[mixins]//引入一个混入
 
}
</script>
选项合并

当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。

比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。

此时引入minixs 如果没有 x y ,则添加属性 x y 若有,以组件的顺序优先.

export default {
    data() {
        return {
            x: 100,
            y: 200
        }
    },
    methods: {
        showDom() {
            // console.log(document.querySelector('img'));
            // console.log(this);
            // console.log(this.$refs.stu);
            alert(this.name);
        }
    }
}

Plugins 插件

本质是一个对象 必须包括install()
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制
通过全局方法 Vue.use() 使用插件。它需要在你调用 new Vue() 启动应用之前完成:

  • plugins.js
export default {
    install(Vue) {
        //console.log('plugins');
        //console.log(vue);
        //定义一个全局过滤器
        Vue.filter('my-filter', function(value) {
            return value.slice(0, 4);
        })

        //在Vue身上定义一个方法
        Vue.prototype.hello = function() {
            alert('你好啊啊啊啊啊')
        }


    }
}
  • main.js
//引入Vue组件
import Vue from 'vue'

//引入App组件 App组件是所有组件的父组件
import App from './App.vue'

import plugins from './plugins'
Vue.use(plugins)
    //配置Vue
Vue.config.productionTip = false

//创建一个vm,并选择id=app为容器
new Vue({
    render: h => h(App),
}).$mount('#app')
scoped属性
  • 作用:让样式在局部生效,防止冲突。
  • 写法:< style scoped>
  • 为 css限定一个作用范围,因为所有的样式最后都会汇总到一起,防止命名冲突
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值