心情:上次发表了vue的笔记,感觉还是有很多东西不懂,所以这次又把vue2过了一遍,巩固一下基础
1.初始vue
1、想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象。
2、root容器里的代码依然复合HTML规范,之不贵呼入一些特殊的Vue语法。
3、root容器里的代码被称为【Vue模板】
4、Vue实例和容器是一一对应的;
5、真是开发中只有一个Vue实例,并且会配合着组件一起使用
6、{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性
7、一旦data中的数据发生改变,那么页面中共用到改数据的地方也会自动更新;
注意区分:js表达 和 js代码(语句)
1、表达式:一个表达式会产生一个值,可以防在任何一个需要的地方。
(1)、a
(2)、a+b
(3)、demo(1)
(4)、x == y ? 'a': 'b'
2、js代码(语句)
1、if(){}
2、for(){}
// 创建Vue实例 配置对象axios({} )
Vue({
el: "#root", // el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串
data: {// data中用于存储数据,数据工供el所指定的容器去使用,值我们暂时先写成一个对象。
name: "atguigu",
address:"上海"
}
})
2.Vue模板语法
Vue模板语法有2大类:
1、插值语法:
功能:用于解析标签体内容。
写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性
2、指令语法:
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件)
举例:v-bind:href="xxx" 或 :href="xxx" xxx同样是写js表达式,
且可以直接读取到data中的所有属性
备注:Vue中有很多指令,且形式都是:v-???,此处我们只是拿v-bind举个例子
3.数据绑定
Vue中有2中数据绑定的格式:
- 单向绑定(v-bind):数据只能从data流向页面
- 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。
- 备注:
1. 双向绑定一般都应用在表单类元素上(如:input、select等)
2. v-model:value 可以简写为v-model,因为v-model默认收集的就是value值。
4.el与dat的两种写法
data与el的2种写法
1、el有2种写法
(1).new Vue时候配置el属性
(2).先创建Vue实例,随后再通过vm.$mount('#root')指定el的值。
2、data有2中写法
(1).对象式
(2).函数式
如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
3、一个重要的原则:
有Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不在是Vue实例了。
5.MVVM模型
MVVM模型:
1.M:模型(Model):data中的数据
2.V:视图(View):模板代码
3.VM:视图模型(ViewModel):Vue实例
观察发现:
1.data中所有的属性,最后都出现在了vm身上
2.vm身上所有的属性,及Vue原型上所有属性,在Vue模板中都可以直接使用。
6.数据代理
-
Object.definProperty方法
Vue.config.productionTip = false let number = 18 let person = { name: '张三', sex: "男", // age: 18 } console.log(person); // 不可枚举(遍历) // 不可修改 // // 不可删除 Object.defineProperty(person, 'age', { // value: 18, // enumerable: true,//控制属性是否可以没,默认值是false // writable: true,//控制属性是否可以被修改,默认值是false // configurable: true//控制属性是否可以被删除,默认值是false // 当有人读取person的age属性是,get函数(getter)救护被调用,且返回值就是age的值 get() { console.log('有人读取age属性!'); return number }, // 当有人修改person的age属性,set函数(setter)就会被调用,且会受到修改的具体值 set(value) { console.log('有人修改了age属性,且值是:', value); number = value; } }) // console.log(Object.·keys(person)); // for (let key in person) { // console.log('@', person[key]); // }
-
数据代理的概念:通过一个对象代理另一个对象中属性的操作 (读/写)
-
Vue中的数据代理:
1. Vue中的数据代理: 通过vm对象来代理data对象属性中的操作(读/写) 2、Vue中数据代理的好处: 更加方便的操作data中的数据 3、基本原理: 通过Object.defineProperty()把data对象中所有属性添加到vm上 为每一个添加到vm上的属性,都制定一个getters/setter。 在getter/setter内部去操作(读/写)data中所对应的属性
7.事件处理
- 事件的基本使用:
- 使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名
- 事件的回调需要配置在methods对象中,最终会在vm上
- methods中配置的函数,不要用箭头函数!否则this就不是vm了;
- methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象
- @click=“demo” 和 @click=“demo($event)” 效果一直,但是后者可以传参
- Vue中的事件修饰符:
- prevent: 阻止默认事件(常用);
- stop:阻止事件冒泡(常用);
- once:事件只触发一次(常用);
- capture:使用事件的捕获模式
- self:只有event.target是当前操作的元素才触发事件
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕。
8.计算属性
计算属性
- 定义:要用的属性不存在,要通过已有的属性计算的来
- 原理:底层借助了Object.defineproperty方法提供的getters和setter。
- get函数什么时候执行?
- 除去读取时会执行一次。
- 当依赖的数据发生改变时会被再次调用
- 优势、与methods实现相比,内部有缓存机制(复用),效率更高,调试方便
- 备注:
-
计算属性最终会相互出现在vm上,直接读取使用即可。
-
如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
computed: { // 完整写法 // fullName: { // get() { // console.log('get方法被调用了'); // // console.log(this); //此处的this是vm // return this.firstName + '-' + this.lastName // }, // set(value) { // console.log('set', value); // const arr = value.split('-'); // this.firstName = arr[0]; // this.lastName = arr[1]; // } // } // 简写 fullName() { setTimeout(function () { return this.firstName + '-' + this.lastName; }, 1000) } }
-
9.监视属性
监视属性watch:
-
当被监视的属性变化时,回调函数自动调用,进行相关操作
-
监视的属性必须存在,才能进行监视!
-
监视的两种写法:
(1).new Vue()时传入watch配置 (2).通过vm.$watch监视
(1).new Vue()时传入watch配置 watch: { info: { immediate: true, //初始化是让handler调用一下 // handler什么时候调用?当isHost发生改变时。 handler(newVal, oldVal) { console.log('isHost被修改了', newVal, oldVal); } } } (2).通过vm.$watch监视 vm.$watch('abc',{ immediate:true, handler(newVal,oldVal){ console.log('info被修改了',newVal,oldVal); } })
-
深度监视:
(1). Vue中的watch默认不监测对象内部值的改变(一层)
(2). 配置deep:true可以监测对象内部值改变(多层)备注:
(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
(2).使用watch时根据数据的具体结构,决定是否采用深度监视watch: { info: { immediate: true, //初始化是让handler调用一下 // handler什么时候调用?当isHost发生改变时。 handler(newVal, oldVal) { console.log('isHost被修改了', newVal, oldVal); } }, // 监视多级结构中某个属性的变化 // "numbers.a": { // handler() { // console.log('a被改变了'); // } // } // 监视多级结构中所有属性的变化 numbers: { deep: true, handler() { console.log('numbers被监视了'); } } }
-
监视属性简写:
watch: { // 完整写法 // isHost: { // immediate: true, //初始化是让handler调用一下 // deep: true,// 监视多级结构中所有属性的变化 // handler(newVal, oldVal) { // console.log('isHost被修改了', newVal, oldVal); // } // }, // 简写 isHost(newVal, oldVal) { console.log('isHost被修改了', newVal, oldVal); } } vm.$watch('isHost', function (newVal, oldVal) { console.log('isHost被修改了', newVal, oldVal); })
-
深度监视:
computed和watch之间的区别:- computed能完成的功能,watch都可以完成。
- watch能完成的功能呢,cmputed不一定能完成,例如:watch进行异步操作。
两个重要的原则:
- 所有被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
- 所有不被Vue所管理的函数(定时器的的回调函数、ajax的回调函数等、Promise的回答函数), 最好写成回调函数,这样this的指向才是vm或组件实例对象。
10.绑定样式
绑定样式:
- class样式
写法:class=“xxx”,xxx可以是字符串、对象、数组
字符串写法适用于:类名不确定的个,要动态获取
对象写法适用于:哟啊坝顶多个样式,个数不确定,名字也不确定
数组写法适用于:要绑定多个样式,个数确定,名字确定,但不确定用不用 - style样式
:style="{fontSize:xxx}“其中xxx是动态值
:style=”[a,b]"其中a、b是样式对象
<!-- 准备好一个容器 -->
<div id='root'>
<!-- 绑定class样式 -字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<div class="basic normal" :class="mood" @click="changeMod">{{name}}</div><br><br>
<!-- 绑定class样式 -数组写法,适用于:要绑定样式的个数不确定、名字不确定 -->
<div class="basic normal" :class="classArr">{{name}}</div><br><br>
<!-- 绑定class样式 -对象写法,适用于:要绑定样式的个数确定、名字确定 但要动态决定用不用 -->
<div class="basic normal" :class="classObj">{{name}}</div><br><br>
<div class="basic normal" :style="styleObj1">{{name}}</div><br><br>
<div class="basic normal" :style="styleArr">{{name}}</div><br><br>
</div>
<script>
Vue.config.productionTip = false //阻止vue在启动时生产生产提示
var vm = new Vue({
el: '#root',
data: {
name: "尚硅谷",
mood: 'normal',
classArr: ['atguigu1', 'atguigu2', 'atguigu3'],
classObj: {
atguigu1: false,
atguigu2: true
},
styleObj1: {
backgroundColor: 'orange!important'
},
styleObj2: {
color: 'red',
fontSize: '40px'
},
styleArr: [
{
color: 'red',
fontSize: '40px'
},
{
backgroundColor: 'orange'
}
]
},
methods: {
changeMod() {
const arr = ['happy', 'sad', 'normal']
let index = Math.floor(Math.random() * 3)
this.mood = arr[index]
// document.getElementById('demo').className = 'basic happy'
}
}
});
</script>
11.条件渲染
条件渲染
-
v-if
写法: (1).v-if="表达式" (2).v-esle-if="表达式" (3).v-else="表达式" 适用于: 切换频率较低的场景 特点:不展示的DOM元素直接被移除 注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能别打断
-
v-show
写法:v-show="表达式" 适用于:切换频率较高的场景 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
-
备注:使用v-if的时,元素可能无法获取到,而是使用v-show一定可以获取到
12.列表渲染
-
基本列表:
v-for指令: 1、用于展示列表数据 2、语法:v-for="(item,index) in xxx" :key="xxx" 3、可遍历:数组、对象、字符串(用得很少)、指定次数(用的很少) <!-- 遍历数组 --> <h2>人员列表(遍历数组)</h2> <ul> <li v-for="(p,index) in persons" :key="p.id"> {{p.name}}---{{p.age}}---{{index}} </li> </ul> <!-- 遍历对象 --> <h2>汽车信息(遍历对象)</h2> <ul> <li v-for="(value,key) in car" :key="key"> {{key}}---{{value}} </li> </ul> <!-- 遍历字符串 --> <h2>测试遍历字符串(用得少)</h2> <ul> <li v-for="(char,index) in str" :key="index"> {{char}}---{{index}} </li> </ul> <!-- 遍历指定次数 --> <h2>测试遍历指定次数(用的少)</h2> <ul> <li v-for="(number,index) in 6" :key="index"> {{index}}---{{number}} </li> </ul>
-
key的原理:
面试题:react 、vue中的可以有什么作用?(key的内部原理)1、虚拟DOM中可以的作用: key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据【新数据】生成【新的虚拟DoM】 随后Vue进行【新虚拟DOM】与【就虚拟DOM】的差异比较,比较规则如下: 2、对比规则: (1).就虚拟DOM中找到与新虚拟DOM相同的key: - 若虚拟DOM中内容没变,直接使用之前的真实的DOM - 若虚拟DOM中内容变了,则生成的真实DOM,随后替换掉页面中之前的真实DOM (2). 就虚拟DOM中未找到与新虚拟DOM相同的key: - 创建新的真实DOM,随后渲染到页面 3.用index作为可以可能引发的问题: 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) in filtPersons" :key="p.id"> {{p.name}}-{{p.age}}-{{p.sex}} </li> </ul> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 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: { filtPersons() { return this.persons.filter((p) => { return p.name.indexOf(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) in filtPersons" :key="p.id"> {{p.name}}-{{p.age}}-{{p.sex}} </li> </ul> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 new Vue({ el: "#root", data: { keyword: '', sortType: 0, //0 代表原顺序,1代表降序,2代表升序 persons: [ { id: "001", name: "马冬梅", age: 30, sex: '女' }, { id: "002", name: "周冬雨", age: 31, sex: '女' }, { id: "003", name: "周杰伦", age: 18, sex: '男' }, { id: "004", name: "温兆伦", age: 19, sex: '男' }, ], }, computed: { filtPersons() { const arr = this.persons.filter((p) => { return p.name.indexOf(this.keyword) !== -1 }) if (this.sortType) { arr.sort((p1, p2) => { return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age; }) } console.log(arr); return arr; } } }) </script>
-
更新时的一个问题
<!-- 准备好一个容器 --> <div id='root'> <button @click="upateMei">点击时更新马冬梅的个人信息</button> <ul> <li v-for="(p,index) in persons" :key="p.id"> {{p.name}}-{{p.age}}-{{p.sex}} </li> </ul> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 new Vue({ el: "#root", data: { 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: '男' }, ], }, methods: { upateMei() { // 可以更新 // this.persons[0].name = "马老师" // this.persons[0].age = 50 // this.persons[0].sex = "男" // 先点按钮在点击vue的调试工具,vue数据更新,页面数据不更新 // 先点vue调试工具再点击按钮,vue数据不更新,页面数据不更新 this.persons[0] = { id: "001", name: "马老师", age: 50, sex: '男' } // 不奏效 this.persons.splice(0, 1, { id: "001", name: "马老师", age: 50, sex: '男' }) } } }) </script>
-
Vue检测数据改变的原理_对象
<!-- 准备好一个容器 --> <div id='root'> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> </div> <script> // 1、加工data(所谓的加工就是把data中的每一组的key:value 都形成了get和set写法) ,加工就能变成响应式了数据变页面也跟着变 // 一整套流程: // name一改变了,set就调用,set已调用就重新解析模板, // 模板一重新解析,生成新的虚拟DOM,然后新旧DOM对比,然后更新页面 // 2、vm._data = data Vue.config.productionTip = false //阻止vue在启动时生产生产提示 // vm.name vm.addres 这叫啥? 数据的data var vm = new Vue({ el: '#root', data: { name: "尚硅谷", address: "北京", student: { name: 'tom', age: { rAge: 40, sAge: 29 } } } }); </script>
-
模拟一个数据监测
<script> let data = { name: '尚硅谷', address: "北京", a: { b: 1 } } // let tmp = '尚硅谷' // setInterval(() => { // if (data.name !== tmp) { // tmp = data.name // console.log('name被改变了~~~'); // } // },100) // Object.defineProperty(data, 'name', { // get() { // return data.name // }, // set(val) { // data.name = val // } // }) const obs = new Observer(data); // 这个是data对象,然后交给Observer,然后data身上有什么 // 意味着obs这个实例对象身上也有相同的属性 console.log(obs); // 准备一个vm实例对象 let vm = {} vm._data = data = obs; function Observer(obj) { // 汇总对象中所有的属性形成一个数组 const keys = Object.keys(obj); // 遍历 console.log(keys); // 这个this是Observer的实例对象 console.log('this', this); keys.forEach((k) => { // console.log(k); // Object.defineProperty Object.defineProperty(this, k, { get() { return obj[k] }, set(val) { console.log(`${k}被改了1111,我要去解析模板,生成虚拟DOM,我要开始忙了`); obj[k] = val; } }) }) } </script>
-
Vue.set的使用
<div id='root'> <h1>学校信息</h1> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <h2>校长是:{{school.leader}}</h2> <hr> <h1>学生信息</h1> <button @click="addSex">添加一个性别属性,默认值是男</button> <h2>姓名: {{student.name}}</h2> <h2 v-if="student.sex">性别: {{student.sex}}</h2> <h2>年龄: 真实{{student.age.rAge}}、对外{{student.age.sAge}}</h2> <h2>朋友们</h2> <ul> <li v-for="f in student.friends" :key="index"> {{f.name}} -- {{f.age}} </li> </ul> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 var vm = new Vue({ el: '#root', data: { name: "尚硅谷", address: "北京", school: {}, student: { name: 'tom', // sex: "男", age: { rAge: 40, sAge: 29 }, firends: [ { name: "jerry", age: 35 }, { name: "tony", age: 36 }, ] } }, methods: { addSex() { // Vue.set(this.student, 'sex', '男') this.$set(this.student, 'sex', '男') } } }) </script>
-
Vue检测数据改变的原理_数组
<div id='root'> <h2>学校名称:{{ school.name}}</h2> <h2>学校地址:{{ school.address}}</h2> <h2>校长是:{{ school.leader}}</h2> <hr> <h1>学生信息</h1> <button @click="addStu">添加一个性别属性,默认值是男</button> <ul> <li>姓名:{{student.name}}</li> <li>年龄:真实{{student.age.rage}},对外{{student.age.sAge}}</li> </ul> <h2>爱好</h2> <ul> <li v-for="item in student.hobby">{{item}}</li> </ul> <h2>爱好</h2> <ul> <li v-for="f in student.friends">{{f.name}}--{{f.age}}</li> </ul> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 var vm = new Vue({ el: '#root', data: { school: { name: "尚硅谷", address: "北京", }, student: { name: 'tom', name: 'tom', hobby: [ '抽烟', '喝酒', '烫头', ], age: { rAge: 40, sAge: 29 }, friends: [ { name: 'jerry', age: 35 }, { name: 'tony', age: 36 } ] } }, methods: { addStu() { // vm._data.student.hobby.push === Array.prototype.push // vm._data.student.hobby.push vue写的一个push // 你先找到的是vue给你写的push // vue写的push做了两件事 // 1、正常的一件事,正常的调用了正常数组上(Array.prototype)的push方法 // 2、重新解析模块,生成虚拟DOM this.student.hobby[0] = '学习'; } } }); </script>
-
总结Vue数据检测
数据劫持:把vue实例中的data修改成_data,这种行为(把所有的数据都变成get和set形式),这种变化,这种行为,就叫做劫持 Vue监视数据的原理: 1.vue中监视data中所有层次的数据 2.如何监测对象中的数据? 通过setter实现监视,且要在new Vue()时就传入要检测的数据。 (1).对象中后追加的属性.Vue默认不做响应式处理 (2).如需给后添加的属性做响应式,请使用如下API: Vue.set(target,propertyeName/index,value) 或 vm. $set(target,propertyeName/index,value) 3. 如何监测数组中的数据? 通过包裹数组更新元素的方法实现。本质就是做了两件事 (1).调用原生对应的方法对数组进行更新 (2).重新解析模板,进而更新页面 4. 在Vue修改数组中的某个元素一定要用如下方法: 1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort、reverse() 2.Vue.set()或vm.$set() 特别注意:Vue.set()和vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
13.收集表单数据
收集表单数据:
若:<input type="text">. 则v-model收集的是value值,用户输入的就是value值
若:<input type="radio">. 则v-model收集的是value值,且要给标签配置value值
若:<input type="checkbox">.
1.没有配置input的value属性,name收集的就是checked(勾选 or 未勾选,是布尔值)
2.配置input的value属性:
(1)v-model的初始值是非数组,name收集的就是checked(勾选 or 未勾选,是布尔值)
(1)v-model的初始值是数组,name收集的就是value组成的数据
备注:v-model的三个修饰符
lazy:失去焦点在收集数据
number:输入字符串转为有效的数字
trim:输入首位空格过滤
14.过滤器:
定义:对要显示的数据进行特定格式化后在显示(适用于一些简单逻辑的处理)。
语法:
1、注册过滤器:Vue.filter(name,callback) 或 new Vue({filters:{}})
2、使用过滤器:{{xxx | 过滤器名 }} 或 v-bind:属性="xxx | 过滤器名称"
备注:
1.过滤器也可以接收额外参数,多个过滤器也可以串联
2.并没有该笔那原本的数据,是产生新的对应的数据
<!-- 准备好一个容器 -->
<div id='root'>
<h2>显示格式化后的时间</h2>
<h4>现在是:{{time}}</h4>
<!-- 计算属性实现 -->
<h4>现在是:{{fmtTime}}</h4>
<!-- methods实现 -->
<h3>现在是:{{getFmtTIme()}}</h3>
<!-- 过滤器的实现 -->
<h3>现在是:{{time | timeFormater}}</h3>
<h3>现在是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
<h3 :x="msg | mySlice">尚硅谷</h3>
<!-- <input type="text" v-model="ms | mySlice"> -->
</div>
<div id="root2">
<h2> {{msg | mySlice}}</h2>
</div>
<script>
Vue.config.productionTip = false //阻止vue在启动时生产生产提示
Vue.filter('mySlice', function (value) {
return value.slice(0, 4)
})
var vm = new Vue({
el: '#root',
data: {
time: 1634129928126,
msg: "你好,尚硅谷 "
},
computed: {
fmtTime() {
return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
}
},
methods: {
getFmtTIme() {
return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
}
},
filters: {
timeFormater(value, str = 'YYYY年MM日DD日 HH:mm:ss') {
// console.log('@', value);
// return dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
return dayjs(value).format(str)
},
mySlice(value) {
// console.log('@', value);
// return dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
return value.slice(0, 4)
},
}
});
new Vue({
el: "#root2",
data: {
msg: "hello!!!尚硅谷"
}
})
</script>
15.内置指令
-
v-text内置_指令
我们学过的指令: v-bind : 单向绑定解析表达式,可简写为 :xxx v-model : 双向数据绑定 v-for : 遍历数组/对象/字符串 v-on : 绑定事件加监听,可简写为@ v-if : 条件渲染(动态控制节点是否存在) v-else : 条件渲染(动态控制节点是否存在) v-show : 条件渲染(动态控制节点是否展示) v-text指令: 1.作用:向其所在节点中渲染文本内容 2.与插值语法的区别:v-text会替换掉节点中的内容,{{xxx}}则不会 `在
-
v-html_指令
v-html指令: 1.作用:向指定节点中渲染包含HTML结构的内容 2.与插值语法的区别: (1).v-html会替换节点中所有的内容,{{xx}}则不会。 (2).v-html可以识别HTML结构 3.严重注意:v-html有安全性问题!!! (1).在网站上动态渲染HTML是非常危险的,容易导致xss攻击 (2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
-
v-clock_指令
v-cloak指令(没有值): 1.本质是一个特殊属性,Vue实例创建完成并接管容器后,会删掉v-cloak属性 2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题 <style> [v-cloak] { display: none; } </style> <div id='root'> <h2 v-cloak>{{name}}</h2> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 setTimeout(function () { var vm = new Vue({ el: '#root', data: { name: '尚硅谷' } }); }, 5000) </script>
-
v-once_指令
v-once指令: 1.v-once所在节点在初次动态渲染后,被视为静态内容了。 2.以后数据的改变不会引起v-once所在结构的更新,可以优化性能。 <div id='root'> <h2 v-once>初始化的n值是:{{n}}</h2> <h2>当前的n值是:{{n}}</h2> <button @click="n++">点我n+1</button> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 var vm = new Vue({ el: '#root', data: { n: 1 } }); </script>
-
v-pre_指令
<div id='root'> <h2 v-pre>Vue其实很简单</h2> <h2 v-pre>当前的n值是:{{n}}</h2> <button @click="n++">点我n+1</button> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 var vm = new Vue({ el: '#root', data: { n: 1 } }); </script>
16.自定义指令
-
自定义指令
需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍 需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定改的input元素默认获取焦点 自定义指令总结: 一、定义语法: (1).局部指令: new Vue({ new Vue({ directives:{指令名:配置对象} 或 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-number="n+1+2"></span></h2> <button @click="n++">点我n+1</button><br><br> <input type="text" v-fbind:value="n"> </div> <div id="root2"> <input type="text" v-fbind:value="x"> </div> <script> // 定义全局指令 Vue.directive('fbind', { bind(element, binding) { element.value = binding.value }, inserted(element, binding) { element.focus(); }, update(element, binding) { element.value = binding.value }, }) // Vue.directive('big-number', function (element, binding) { // element.innerText = binding.value * 10 // }) Vue.config.productionTip = false //阻止vue在启动时生产生产提示 var vm = new Vue({ el: '#root', data: { n: 1 }, directives: { // big函数合适会被调用?1、指令与元素成功绑定时(一上来)2、指令所在的模板被重新解封时。 // 'big-number'(element, binding) { // // console.log(a); // // console.dir(a); // // console.dir(a instanceof HTMLElement); // // console.log(binding); // // console.log(binding.value); // element.innerText = binding.value * 10 // }, // fbind: { // // 1、指令与元素成功绑定时(一上来) // bind(element, binding) { // element.value = binding.value; // }, // // 2、指令所在元素被插入页面时 // inserted(element, binding) { // element.focus(); // }, // // 3、指令所在的模板被重新解封时。 // update(element, binding) { // element.value = binding.value // }, // } } }); new Vue({ el: "#root2", data: { x: 1 } }) </script>
-
回顾一个DOM操作
<style> .demo { background-color: orange; } </style <button>点击按钮添加一个输入框</button> <script> var btn = document.getElementsByTagName('button')[0] btn.onclick = function () { var input = document.createElement('input'); input.className = "demo" input.vlaue = "99" // input.parentElement.style.backgroundColor = 'skyblue' console.log(input.parentElement); input.onclick = () => { alert(1) } document.body.appendChild(input); input.focus(); console.log(input.parentElement); } </script>
17.生命周期
- 引出生命周期
生命周期: 1.又名:生命周期回调函数、声明周期函数、生命周期钩子 2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。 3.生命周期的名字不可更改,但函数的具体内容是更许愿根据需求编写的。 4.生命周期函数的this指向时vm或组件实例对象 <div id='root'> <h2></h2> <h2 v-if="a">欢迎学习Vue</h2> <h2 :style="{opacity}">欢迎学习Vue</h2> <!-- {{change()}} --> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 const vm = new Vue({ el: '#root', data: { a: false, opacity: 1 }, methods: {}, // Vue完成模板的解析并把初始的真实DOM放入页面后(挂载完毕)调用mounted mounted() { console.log('mounted', this); setInterval(() => { this.opacity -= 0.01 if (this.opacity <= 0) this.opacity = 1 }, 16) } }); </script>
- 分析生命周期
<!-- 准备好一个容器 --> <div id='root' :x="n"> <h2>当前的n值是:{{n}}</h2> <button @click='add'>点我n+1</button> <button @click='bye'>点我销毁</button> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 const vm = new Vue({ el: '#root', // template: ` // <div> // <template v-if="false"> // <h2>当前的n值是:{{n}}</h2> // <h2>当前的n值是:{{n}}</h2> // <h2>当前的n值是:{{n}}</h2> // </template> // <h2>当前的n值是:{{n}}</h2> // <button @click='add'>点我n+1</button> // </div> // `, data: { n: 1 }, watch:{ n(){ console.log('n变了~~~'); } }, methods: { add() { console.log('add'); this.n++ }, bye() { console.log('bye'); this.$destroy(); } }, beforeCreate() { console.log('beforeCreate'); // console.log(this); // debugger; }, created() { console.log('created'); // console.log(this); // debugger; }, beforeMount() { console.log('beforeMount'); // console.log(this); // debugger; // debugger; }, mounted() { console.log('mounted'); // console.log(this); // document.querySelector('h2').innerText = 123 // debugger; // console.log('mounted', this.$el instanceof HTMLElement); }, beforeUpdate() { console.log('beforeUpdate'); // console.log(this.n); // debugger; }, updated() { console.log('updated'); // console.log(this.n); // debugger; // this.n = 99; }, beforeDestroy() { console.log('beforeDestroy'); // console.log(this.n); // this.add(); this.n = 999; }, destroyed() { console.log('destroyed'); } }); </script>
- 总结生命周期
常用的生命周期钩子: 1.mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】 2.beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】 关于销毁Vue实例 1.销毁后借助Vue开发者工具看不到任何信息。 2.销毁后自定义事件失效,但原生DOM事件依然有效。 3.一般不会在beforeDestroy操作数据,因为几遍操作数据,也不会在触发更新流程了。 <div id='root'> <h2></h2> <h2 :style="{opacity}">欢迎学习Vue</h2> <button @click="opacity = 1">透明度设置为1</button> <button @click="stop">点我停止变化</button> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 const vm = new Vue({ el: '#root', data: { opacity: 1, }, methods: { stop() { this.$destroy(); } }, // Vue完成模板的解析并把初始的真实DOM放入页面后(挂载完毕)调用mounted mounted() { this.timer = setInterval(() => { this.opacity -= 0.01 console.log('setInterval'); if (this.opacity <= 0) this.opacity = 1 }, 16) }, bforeDestroy() { console.log('vm即将驾鹤西游了'); clearInterval(this.timer) } }); </script>
18.非单文件组件
- 基本使用
传统方式编写应用 存在的问题: 1.依赖关系混乱,不好维护 2.代码复用率不高 组件的定义:实现应用中局部功能代码(css,html,js)的和资源的集合。 非单文件组件: 一个文件中包含n个组件。 单文件组件: 一个文件中只包含1个组件。 重点: Vue中使用组件的三大步骤: 一、定义组件(创建组件) 二、注册组件 三、使用组件(写组件标签) 一、如何定义一个组件? 使用VUe.extend(options)创建,其中options和new Vue(options)传入的options几乎一样,但也有点区别。 区别如下: 1.el不要写,为什么? ———— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务那个容器。 1.data必须写成函数,为什么?————— 避免组件被复用时,数据存在引用关系。 二、如何注册组件? 1.局部注册:靠new Vue的时候传入components选项 2.全局注册:靠VUe.component('组件名',组件) 三、编写组件标签: <school></school> <!-- 准备好一个容器 --> <div id='root'> <hello></hello> <hr> <h1>{{msg}}</h1> <hr> <!-- 第三部:编写组件标签 --> <school></school> <hr> <!-- 第三部:编写组件标签 --> <student></student> </div> <div id="root2"> 12121 <hello></hello> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 // 创建school组件 const school = Vue.extend({ // el:"#root", // 一定不要写el配置项,一定不要写el配置型,因为最终所有的组件都要被一个vm管理,有vm决定该服务于那个容器 template: ` <div> <h2>学校名称:{{schoolName}}</h2> <h2>学校地址:{{address}}</h2> <button @click="clickHanrdel">点我提示学校名称</button> </div> `, data() { return { schoolName: '尚硅谷', address: " 北京昌平", } }, methods: { clickHanrdel() { alert(`${this.schoolName}`) } } }) // 创建student组件 const student = Vue.extend({ template: ` <div> <h2>学生姓名:{{studentName}}</h2> <h2>学生年龄:{{age}}</h2> </div> `, data() { return { studentName: '张三', age: 18 } } }) // 创建hello组件 const hello = Vue.extend({ template: "<h1>{{msg}}</h1>", data() { return { msg: "你好啊!Tom" } } }) // 全局注册组件 Vue.component('hello', hello) // 创建vm var vm = new Vue({ el: '#root', data: { msg: '你好啊!!' }, // 第二步:注册组件(局部注册) components: { school, student } }); const vm2 = new Vue({ el: "#root2", data: { }, // components: { // hello // } }) // let data = { // a: 1, // b: 2 // } // function data() { // return { // a: 1, // b: 2 // } // } // const x1 = data(); // const x2 = data(); </script>
- 几个注意点
几个注意点: 1.关于组件名: 一个单词组成: 第一种写法(首字母小写):school 第二种写法(首字母大写): school 多个单词组成: 第一种写法(kebab-case命名):my-school 第二种写法(CamelCase命名):mySchool(需要Vue脚手架支持) 备注: (1).组件名尽可能避免HTML中已有的元素名称,例如:h2,H2都不行 (2).可以使用name配置项指定组件在开发者工具中呈现的名字。 2.关于组件标签: 第一种写法:<school></school> 第二种写法:<school/> 备注:不用使用脚手架是,<school/>会导致后续组件不能渲染、 3.一个简写方式: const school = Vue.extend(options) 可简写为: const school = options; <div id='root'> <h1>{{msg}}</h1> <school></school> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 // const s = Vue.extend({ // name: "atguigu", // template: ` // <div> // <h2>学校名称:{{name}}</h2> // <h2>学校地址:{{address}}</h2> // </div> // `, // data() { // return { // name: '尚硅谷', // address: '北京' // } // } // }) const s = { name: "atguigu", template: ` <div> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> </div> `, data() { return { name: '尚硅谷', address: '北京' } } } var vm = new Vue({ el: '#root', data: { msg: '欢迎学习Vue!' }, components: { school: s } }); </script>
- 组件的嵌套
<div id='root'> <!-- <app></app> --> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 // 定义student组件 const student = Vue.extend({ template: ` <div> <h2>学生姓名:{{name}}</h2> <h2>学生年龄:{{age}}</h2> </div> `, data() { return { name: '尚硅谷', age: 18 } } }) // 定义school组件 const school = Vue.extend({ template: ` <div> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <student></student> </div> `, data() { return { name: "尚硅谷", address: "北京" } }, components: { student } }) // 定义hello组件 const hello = Vue.extend({ template: ` <div> <h2>{{msg}}</h2> </div> `, data() { return { msg: "欢迎来到尚硅谷学习", } }, components: { student } }) // 定义一个app组件 const app = Vue.extend({ template: ` <div> <hello></hello> <school></school> </div> `, data() { return { } }, components: { school, hello, } }) var vm = new Vue({ el: '#root', template: "<app></app>", data: { }, // 注册组件(局部) components: { app } }); </script>
- VueComponent
关于VueComponent: 1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的 2.我们值需要写<school></school>或<school></school>,Vue解析会帮我创建school组件的实例对象, 即Vue帮我执行的:new VueComponent(options) 3.特别注意: 每次调用Vue.extend,返回的都是一个全新的VueComponent!!! 4.关于this之指向: (1).组件配置中: data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】 (2).new Vue(options)配置中: data函数、methods中的函数、watch中的函数、computed中的函数 他们的this均是【Vue实例对象】 5.VueComponent的实例对象,以后简称VC(也可称之为:组件实例对象) Vue的实例对象,以后简称vm <div id='root'> <school></school> <hello></hello> </div> <script> /* Vue.extend = function (extendOptions) { ...... var Sub = function VueComponent (options) {l // console.log('VueComponent调用了'); this._init(options); }; ...... return Sub }; */ Vue.config.productionTip = false //阻止vue在启动时生产生产提示 // 定义school组件 const school = Vue.extend({ template: ` <div> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <button @click="showName">点我提示学校名称</button> </div> `, data() { return { name: "尚硅谷", address: "北京" } }, methods: { showName() { alert(this.name) // 这个this就是Vuecomponent实例对象 console.log(this) } } }) // 定义hello组件 const test = Vue.extend({ template:`<span>atguigu</span>` }) // 定义hello组件 const hello = Vue.extend({ template: ` <div> <h2>{{msg}}</h2> <test></test> </div> `, data() { return { msg: "你好啊", } }, components:{ test } }) school.a = 99 // console.log('@', school.a); // console.log('#', hello.a); var vm = new Vue({ el: '#root', data: { }, components: { school, hello } }); console.log(vm); </script>
- 一个重要的内置关系
1.一个重要的内置关系:VueCOmponent.prototype.__proto__ === Vue.prototype 2.为什么要有这个关系:让组件实例对象(vc)可以访问到Vue原型上的属性、方法 <!-- 准备好一个容器 --> <div id='root'> <school></school> <!-- 使用school模板,就new VueComponent() --> </div> <script> Vue.config.productionTip = false //阻止vue在启动时生产生产提示 Vue.prototype.x = 99; // 定义school组件 // school是VueComponent 构造函数 const school = Vue.extend({ template: ` <div> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <button @click="showX">点我输出x</button> </div> `, data() { return { name: "尚硅谷", address: "北京" } }, methods: { showX() { console.log(this.x); } } }) var vm = new Vue({ el: '#root', data: { }, components: { school } }); // console.log(school.prototype.__proto__ === Vue.prototype); // 定义一个构造函数 // function Demo() { // this.a = 1; // this.b = 2; // } // 创建一个Demo的实例对象 // const d = new Demo(); // console.log(Demo.prototype); //显示原型 // console.log(d.__proto__);//隐式原型 // console.log(Demo.prototype == d.__proto__); // 实例的隐式原型属性,永远指向自己缔造者的原型对象 // // 程序员通过显示原型属性操作原型对象,追加一个x属性,值为99 // Demo.prototype.x = 99; // // console.log('@', d.__proto__.x); // console.log('@', d); // function Person() { // } // function Dog() { // } // const p = new Person(); // const d2 = new Dog(); // console.log(p); // console.log(d2); </script>
19.单文件组件
// 1.创建School组件
<template>
<div class="demo">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
</div>
</template>
<script>
export default {
name: "School",
data() {
return {
name: "尚硅谷",
address: "北京.昌平",
};
},
};
</script>
<style>
.demo {
background-color: gray;
}
</style>
// 2.App.vue文件引入了School组件中
<template>
<div>
<h1 v-text="msg" ref="title"></h1>
<button ref="btn" @click="showDom">点我输出上方的DOM元素</button>
<School id="sch" />
</div>
</template>
<script>
// 引入组件
import School from "./components/School.vue";
export default {
name: "App",
data() {
return {
msg: "欢迎学习Vue!",
};
},
methods: {
showDom() {
console.log("@@", this.$refs.title);//真实DOM元素
console.log("@@", this.$refs.btn);// 真实DOM元素
console.log("@@", this.$refs.sch);//School组件的实例对象(vc)
},
},
components: {
School,
},
};
</script>
<style>
</style>
// 3.在main.js中引入App.vue文件
import App from './App.vue'
new Vue({
el: "#root",
template: `<App></App>`,
components: { App }
})