前言
- 本笔记是跟随B站张天禹讲师的尚硅谷Vue2.0 + Vue3.0全套教程丨vuejs从入门到精通,附上直达车。
- 本笔记更多为笔者个人使用,笔者的要求是达到能用的水平就可以,所以专业前端的同学而慎重考虑选择该笔记。很多原理性的东西笔者不会详写。如果你也是和我一样,只要求够用就行,可以看看。
- 随缘更新,笔者很懒,不知道什么时候能够完结哈哈
- 更新日志:
- 2023年07月03日,更新到Vue核心:vue的其他内置指令—text指令
- 2023年07月08日,更新到Vue组件化编程:scope样式
- 2023年07月30日,最近事情很多很杂,很大一段时间不会更新,抱歉抱歉哈哈,短时间应该更新不完,要鸽了哈哈。
Vue核心
1.Vue简介:初识Vue
-
Vue是什么?
是一套用于构建用户界面的渐进式(Vue可以自顶向上做成的应用)JavaScript框架
-
Vue的特点
- 采用组件化的模式,提高代码的复用率、让代码更用以维护
- 声明式编码,程序员操作的是虚拟DOM,没有直接操作DOM,提高开发效率
- 使用虚拟DOM+Diff算法,尽量复用DOM节点
-
[Vue官方API网站](Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org))
-
编写一个HelloWorld 小案例
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <!-- 引入vue.js --> <script type="text/javascript" src="../js/vue.js"></script> <title></title> </head> <body> <div id="root"> <h1>HelloWorld,{{ name }}</h1> </div> <script type="text/javascript"> // 创建Vue实例 new Vue({ el: '#root', //指定vue实例为哪个容器服务 data: { //data中用于存储数据 name: 'Vue' } }) </script> </body> </html>
- 通过以上小案例:
总结1:
1.想让Vue工作,必须创建一个Vue实例(new Vue),且要传入一个配置对象
2.id为root中的div容器中的代码依然符合html规范,知识中间有一些特殊的Vue语法
3.root容器中的代码被称为【Vue模板】
4.Vue实例和容器是一一对应的
5.在真实的开发场景中,只存在一个Vue实例,配合组件一起使用
- {{ xxx }}中的内容需要写入js表达式,也就是一切可以返回值的东西
7.一旦data中的数据更新,页面中使用到该数据的地方也会自动更新
el 和 data的两种写法
<body>
<div id="root">
<h1>HelloWorld,{{ name }}</h1>
<span>插值语法:{{ name }}</span>
<br>
<h1>数据绑定</h1>
单项数据绑定:<input type="text" :value="myInput"><br>
双向数据绑定:<input type="tex" v-model="myInput"><br>
</div>
<script type="text/javascript">
// 创建Vue实例
const vm = new Vue({
el: '#root', //el第一种写法
data: { //data第一种写法
name: 'Vue',
myInput: ""
}
// data() { //data第二种写法,函数式
// return {
// name: 'Vue',
// myInput: ''
// }
// }
})
//vm.$mount('#root') //el第二种写法,通过挂载
</script>
</body>
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实例了,如果没有向上继承,则this指代的是Window对象。
2.Vue中的模板语法
- Vue中的模板语法主要有:
- 插值语法:
- 功能:用户解析标签体中的内容
- {{ xxx }} 这种样式的的双大括号表达式
- 指令语法:
- 功能:用户解析标签(包括标签属性、标签体内容、绑定事件函数)
- 写法:以v-开头的vue的指令
- 插值语法:
3.Vue中的数据绑定(v-bind、v-model)
-
Vue中有两种数据绑定方式,使用指令语法:
- 单向绑定(v-bind):数据只能从data流向页面
- 双向绑定(v-model):数据能从data流向页面,也可以从页面流向data
注意:
1.双向绑定一般只应用于表单类元素上(input等)
2.v-bind可以简写为 : ,而v-model:value一般可以简写为v-model(因为默认收集的就是value值)
4.什么是MVVM模型
- vue框架的设计模式借鉴了MVVM模型,什么是MVVM模型呢?如下图:
我们拆开来看:
1.M:模型(model):对应的是data中的数据
2.V:视图(View):对应页面或者实现页面的Vue模板
3.VM:视图模型(ViewModel):Vue实例
对应到代码中就是:
小结论:
1.data中所有的属性,最后都出现到了VM上(通过数据代理)
2.VM上的所有属性以及Vue原型上的所有属性,在Vue模板中都可以直接使用
5.Vue中的数据代理(感觉太底层了,笔者过了一遍就没管了,可以自己看看)
6.Vue的事件处理(v-on)
事件绑定
- 使用v-on指令,以 v-on:事件名 或者简写 @事件名,来绑定事件
- 事件名后面需要指定回调函数,且该回调函数需要配置在methods对象中
- methods中配置的函数,不要使用箭头函数,否者this指向的就不是Vue实例了,而是Window
- methods中配置的函数,都是被Vue管理的函数,this指向的是Vue实例对象或者组件实例对象
- 事件名后面的回调函数如,@click=“demo” 和 @click=“demo($event,…params)” 效果一样,但是后者可以传参
案例:
<div id="root"> <h1>事件的使用</h1> <!-- <button v-on:click="getClick">点击事件</button> --> <!-- <button @click="getClick">点击事件</button> --> <button @click="getClick(5,$event)">点击事件</button> </div> <script type="text/javascript"> // 创建Vue实例 const vm = new Vue({ el: '#root', data: { name: 'Vue', myInput: "" }, methods: { getClick(number, event) { console.log('点击事件绑定'); console.log(number); console.log(event); } } }) </script>
事件修饰符
- Vue中的事件修饰符
- prevent:阻止默认事件的发生
- stop:阻止事件冒泡
- once:事件只触发一次
- capture:使用事件的捕获模式
- self:只有event.target是当前操作的元素才触发事件
- passive:事件默认为立即执行,无序等待事件回调执行完毕
小案例:
<div id="root"> <!-- prevent:阻止默认事件的发生 --> <a href="https://www.baidu.com/" @click.prevent="getClick">跳转到百度</a> <!-- stop:阻止事件冒泡 --> <div style="background-color: aquamarine;" @click="getClick"> <button @click.stop="getClick">冒泡</button> </div> <!-- once:事件只触发一次 --> <button @click.once="getClick">只执行一次</button> </div> <script> // 创建Vue实例 const vm = new Vue({ el: '#root', data: { name: 'Vue' }, methods: { getClick(number, event) { alert('要跳转了哦') } } }) </script>
键盘事件
-
绑定键盘事件一般有两个
- @keydown:指在键盘按下去还没弹起来时触发事件
- @keyup(常用):等按键弹起来时再触发事件
-
绑定按键写法:@keydown.key值/keyCode || @keyup.key值/keyCode
-
Vue中带用的按键别名:
- 回车=>enter
- 删除=>delete(捕获“删除”和“退格”键)
- 退出=>esc
- 空格=>space
- 换行=>tab(特殊,必须配合keydown去使用)
- 上=>up
- 下=>down
- 左=>left
- 右=>right
小案例:
<div id="root"> <!-- 只有当敲击Enter键时,才会触发事件 --> 键盘事件:<input @keyup.enter="keyClick"> </div> <script> const vm = new Vue({ el: '#root', data: { name: 'Vue' }, methods: { keyClick(e) { console.log(e.target.value); } } }) </script>
7.Vue中的计算属性(computed)
-
什么是计算属性
- 定义:通过已有的属性计算而来
- 原理:底层借助了objcet.defineproperty方法提供的getter和setter
- get函数什么时候执行?
- 初次读取时会执行一次
- 当依赖的数据发生改变时会被再次调用
- set函数什么时候被调用?
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变
- 好处:与methods实现相比,内部有缓存机制(复用,当数据不发生改变的时候,不会重复调用),效率更高,调试方便。
-
写法:
- 在Vue实例中添加一个配置对象:computed,代表里面的属性为计算属性,里面的属性以对象的形式存在
- 一般情况下,计算属性中不需要setter去修改计算属性,所以我们可以将set方法从计算属性中去掉,使用函数式简写该计算属性
<div id="root"> 数字1: <input type="text" v-model="num1"><br> 数字2: <input type="text" v-model="num2"><br> 求和:<span>{{ sum }}</span> </div> <script> // 创建Vue实例 const vm = new Vue({ el: '#root', data: { num1: 1, num2: 2 }, //声明计算属性 computed: { sum: { get() { return parseInt(this.num1) + parseInt(this.num2) }, } //简写计算属性 // sum() { // return parseInt(this.num1) + parseInt(this.num2) // } } }) </script>
8.监视属性(watch)
- 监视属性watch:
- 1.当被监视的属性变化时,回调函数自动调用,进行相关操作
- 2.监视的属性必须存在,才能进行监视!!
- 3.监视的两种写法:
- (1) new Vue时传入watch配置
- (2) 通过vm.$watch监视
深度监视
- (1)Vue中的watch默认不监测对象内部值的改变(一层)
- (2)配置deep:true可以监测对象内部值改变(多层)。
- 备注:
- (1)Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
- (2)使用watch时根据数据的具体结构,决定是否采用深度监视
小案例:
<div id="root"> <h1>今天天气很{{ info }}</h1> <button @click="change">切换</button> <br> <h1>a的值{{ num.a }}</h1> <button @click="num.a++">点击添加</button> </div> <script> // 创建Vue实例 const vm = new Vue({ el: '#root', data: { isHot: true, num: { a: 1, b: 1 } }, methods: { change() { this.isHot = !this.isHot } }, computed: { info() { return this.isHot ? '炎热' : '寒冷' } }, // 监视属性 watch: { isHot: { immediate: true, //初始化的时候让handler被调用一下 handler(newValue, oldValue) { // 监视isHot是否被改变,两个参数 一个新值,一个旧值 console.log('监视到ishot修改了', newValue, oldValue); } }, num: { deep: true, //深度监视 handler() { console.log('监视到num内部值修改了'); } } } //第二种写法 // vm.$watch('isHot', { // immediate: true, //初始化的时候让handler被调用一下 // handler(newValue, oldValue) { // 监视isHot是否被改变,两个参数 一个新值,一个旧值 // console.log('监视到ishot修改了', newValue, oldValue); // } }) }) </script>
计算属性与监视属性的区别
区别:
1.computed能完成的功能,watch都可以完成。
2.watch能完成的功能,computed不一定能完成,例如: watch可以进行异步操作。两个重要的小原则:
1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
2.所有不被Vue所管理的函数,最好写成箭头函数(定时器的回调函数、ajax的回调函数等),这样this的指向才是vm或组件实例对象。
9.Vue中绑定样式(class和style)
- 绑定class样式
- 写法: :class=“xxx”,绑定的内容可以是变量、对象、数组
- 字符串写法适用于:类名不确定,要动态获取。
- 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
- 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
- 写法: :class=“xxx”,绑定的内容可以是变量、对象、数组
- 绑定style样式
- 写法: :style=“xxx”,绑定的内容可以是变量、对象、数组
- :style="{fontSize: xxx}"其中xxx是动态值。
- :style="[a,b]"其中a、b是样式对象。
- 写法: :style=“xxx”,绑定的内容可以是变量、对象、数组
10.Vue中的条件渲染(v-if、v-else-if、v-else、v-show)
-
条件渲染指令
- v-if、v-else-if、v-else
- v-show
-
v-if系列
-
写法:
v-if="表达式"
v-else-if="表达式"
v-else
(注意v-else是不需要写表达式的) -
适用于:切换频率比较低的场景,因为它是暴力删除**,不符合条件的节点直接从页面上移除**。
注意:v-if、v-else-if、v-else可以一起使用,但是要求结构不能被打断,必须连贯使用
-
-
v-show
- 写法:
v-show="表达式"
- 适用于:页面节点切换频率较高的场景,但是未展示的节点没有被移除,只是使用display:none样式隐藏掉了
- 写法:
小案例:
<div id="root"> <!-- 使用v-show进行条件渲染 --> <span v-show="isShow">你好</span><br><br> <button @click="changeShow">显示你好?</button> <br> <hr> <!-- 使用v-if进行条件渲染 --> <div v-if="num == 1">等于1</div> <div v-else-if="num == 2">等于2</div> <div v-else>都不是</div> <button @click="add">添加++</button> </div> <script> // 创建Vue实例 const vm = new Vue({ el: '#root', data: { isShow: true, num: 0 }, methods: { changeShow() { this.isShow = !this.isShow }, add() { this.num++ } }, }) </script>
11.Vue中的列表渲染(v-for)
-
列表渲染指令
- v-for
- 一般用于循环遍历展示列表数据
- 语法:
v-for="(value,index) in xxx" :key="index"
,其中:key用于指定改遍历的节点是唯一的 - xxx中可遍历的有数组、对象、字符串、指定数字+
关于
:key
的说明1.虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】 ,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
2.对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
a.若虚拟DOM中内容没变,直接使用之前的真实DOM
b.若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM(2).旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到到页面
使用遍历对象的索引作为key时:
使用唯一标识id作为key时:
3.使用index作为key可能会引发的问题
(1)若对数据进行:逆序添加、逆序删除等破坏顺序(原来索引对应的值)操作:
会产生没有必要的真实DOM更新==>界面效果没问题,但效率低
(2)如果结构中还包含输入类的DOM:
会产生错误DOM更新==>界面有问题如果没有特别指定key,vue会默认使用对象索引 index 作为key来唯一标识
4.开发中如何选择key? :
(1)最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表展示,使用index作为key是没有问题的 - v-for
小案例:
<div id="root"> <ul> <!-- 使用v-for进行列表渲染 --> <h1>用户信息:</h1> <li v-for="(item, index) in user" :key="item.id"> {{ item.name }} - {{ item.age }}岁 </li> </ul> </div> <script> // 创建Vue实例 const vm = new Vue({ el: '#root', data: { user: [ { id: '001', name: 'wjw', age: '18' }, { id: '002', name: '小明', age: '5' }, { id: '003', name: '小美', age: '28' } ] } }) </script>
12.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()
13.Vue收集表单中的数据(v-model中的修饰符)
-
vue收集表单中的数据
-
若是普通:
<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:失去焦点再收集数据,常用于文本域textarea中
number:输入字符串转为有效的数字,常用于输入纯数字的文本框中
trim:输入首尾空格过滤
-
小案例:
<div id="root"> <form @submi.prevent="ok"> 账号:<input type="text" v-model="userInfo.account"><br><br> 密码:<input type="password" v-model="userInfo.password"><br><br> 年龄:<input type="number" v-model.number="userInfo.age"><br><br> 性别: 男<input type="radio" v-model="userInfo.sex" value="man"> 女<input type="radio" v-model="userInfo.sex" value="female"><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="hunan">湖南</option> <option value="shanghai">上海</option> <option value="beijing">北京</option> <option value="guangdong">广东</option> </select> <br><br> 自我介绍: <textarea v-model.lazy="userInfo.other"></textarea><br><br> <input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.baidu.com">点击跳转百度</a> <br><br> <button>提交</button> </form> </div> <script> // 创建Vue实例 const vm = new Vue({ el: '#root', data: { userInfo: { account: "", password: "", sex: "man", age: "", hobby: [], city: "beijing", other: '', agree: '' } }, methods: { ok() { console.log('提交了'); } }, }) </script>
14.Vue中的过滤器(在Vue3中已经被废除,可以使用计算属性代替)
-
Vue中的过滤器
-
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理),如对日期的显示格式会进行处理
-
语法:
-
1.注册过滤器:
全局注册:再创建Vue实例之前注册
Vue.filter(name,callback回调函数)
局部注册:再创建Vue实例之中注册
new Vue{filters:{}}
-
2.使用过滤
在插值语法中:
{{ xxx|过滤器名 }}
绑定的自定义属性中:
v-bind:属性 = “xxx|过滤器名"
-
注意:
1.过滤器也可以接收额外参数、多个过滤器也可以串联
2.并没有改变原本的数据,是产生新的对应的数据,只不过是以不同的格式显示
-
15.Vue中的其他内置指令(v-text、v-html)
(1)v-text指令
- 作用:向其所在的节点中渲染文本内容,但不会识别其中的html结构。
注意:
与差值语法的区别:
v-text
会替换掉当前节点中的所有内容,{{xxx}}
只会替换括号内
(2)v-html指令
- 作用:向指定节点中渲染包含html结构的内容。
注意:
1.与差值语法的区别
(1)
v-html
会替换掉节点中所有的内容,{{xx}}
则不会。(2)
v-html
可以识别html结构。2.有安全性问题
(1)在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击
(2)一定要在可信的内容上使用v-html,永不要用在用户提交的内容上
(3)v-cloak指令
- v-cloak指令(没有值):
- 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloakh性。
- 使用css配合
v-cloak
可以解决网速慢时页面延迟解析模板内容(页面闪现)的问题。
(4)v-once指令
- v-once指令(没有值):
- v-once所在节点在初次动态渲染后,就视为静态内容了。
- 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
(5)v-pre指令
- v-pre指令(没有值):
- 可以使当前节点跳过Vue编译过程,意思是如果你这条html语句没有用到Vue语法,那么再加上这条语句过后,Vue就不会再扫描这条语句,直接跳过编译。这样可以加快编译过程。
小案例:
<div id="root"> <!-- v-text指令 --> <span v-text="isText"></span><br><br> <span v-text="isHtml"></span><br><br> <!-- v-html指令 --> <span v-html="isText"></span><br><br> <span v-html="isHtml"></span><br><br> <!-- v-once指令 --> <span v-once>这是初始值,{{ num }}</span><br><br> <span>这是改变后的值,{{ num }}</span><br><br> <button @click="num++">num加一</button> <!-- v-pre指令 --> <p v-pre>v-pre会跳过编译</p> </div> <script> const vm = new Vue({ el: '#root', data: { isText: "这是text指令", isHtml: "<h1>这是html指令</h1>", num: 1, }, }) </script>
16.Vue自定义指令
- 自定义指令操作DOM元素时
- 定义自定义指令语法:
- 局部指令
- 对象式:
new Vue({ directives:{ 指令名,配置对象 } })
- 函数式:
new Vue({ directives:{ 指令名,回调函数 } })
- 对象式:
- 全局指令
- 对象式:
Vue.directive(指令名,配置对象)
- 函数式:
Vue.directive(指令名,回调函数)
- 对象式:
- 局部指令
- 对象式中常用的3个回调:
- (1)
bind
:指令与元素成功绑定时调用。 - (2)
inserted
:指令所在元素被插入页面时调用。 - (3 )
update
:指令所在模板结构被重新解析时调用。
- (1)
注意:
1.指令定义时不加
v-
,但使用时要加v-
2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
17.Vue生命周期(重点)
-
什么是Vue的生命周期
- 又名:生命周期回调函数、生命周期函数、生命周期钩子
- 是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数,在不同的生命周期回调函数里面做不同的事情
- 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
- 生命周期函数中的this指向是vm或组件实例对象。
-
常见的生命周期钩子
beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed
- 其中:
mounted
:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等初始化操作。beforeDestroy
:清除定时器、解绑自定义事件、取消订阅消息等收尾工作。
关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了
-
生命周期图示
- 代码理解生命周期
<div id="root">
<h1>{{ n }}</h1>
<button @click="n++">点击n+1</button>
<button @click="xiaohui">销毁</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
n: 1
},
methods: {
xiaohui() {
this.$destroy()
console.log('销毁');
}
},
beforeCreate() {
console.log('beforeCreate执行了。。。');
},
created() {
console.log('create执行了。。。');
},
beforeMount() {
console.log('beforeMount执行了。。。');
},
mounted() {
console.log('mounted执行了。。。');
},
beforeUpdate() {
console.log('beforeUpdate执行了。。。');
},
updated() {
console.log('updated执行了。。。');
},
beforeDestroy() {
console.log('beforeDestroy执行了。。。');
},
destroyed() {
console.log('destroyed执行了。。。');
},
})
</script>
组件化编程(component)
传统方式编程,存在的问题:
1.依赖关系混乱,不好维护、
2.代码复用率不高,总的来说属于低内聚高耦合
所以,我们引出组件的概念
组件:实现应用中局部功能代码(html、csss、js)和资源(字体、视频、音乐等)的集合,分为非单文件组件和单文件组件
可以记住一句话:组件就是一块砖,哪里需要哪里搬
-
Vue使用组件的三大步骤
一、定义组件(创建组件)
如何定义一个组件
1、el不要写,为什么?—最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
2、data必须写成函数,为什么?——避免组件被复用时,数据存在引用关系,而对象中的数据是共用的
备注:使用template可以配置组件结构。
创建组件和注册组件时,关于组件名:
一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School
多个单词组成:
第一种写法(kebab-case命名):my-school
第二种写法(CamelCase命名):MySchool(需要Vue脚手架支持)
备注:
(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。
二、注册组件
在Vue实例中注册组件,在Vue实例中添加一个全新的配置项component
- 局部注册:靠new Vue的时候传入components选项
- 全局注册:靠Vue.component('组件名’,组件)
三、使用组件(在模板中编写组件标签)
双标签写法
<school></school>
单标签写法(在脚手架环境中使用)
<school/>
1.非单文件组件
-
一个文件中包含n个组件
-
使用非单文件组件形式使用组件
- 定义组件(创建组件)
- 注册组件
- 编写标签组件
<div id="root">
<!-- 使用组件 -->
<her></her>
<hr>
<him></him>
</div>
<div id="root2">
<!-- 使用组件 -->
<msg></msg>
</div>
<script>
//一、创建her组件
const her = Vue.extend({
template: `
<div>
<h1>她的名字:{{ name }}</h1>
<h1>她的年龄:{{ age }}</h1>
<h1>她的性别:{{ sex }}</h1>
<button @click="showName">点我提示名字</button>
</div>`,
data() {
return {
name: "ikaros",
age: 18,
sex: 'woman'
}
},
methods: {
showName() {
alert(this.name)
}
},
})
//一、创建him组件
const him = Vue.extend({
template: `
<div>
<h1>他的名字:{{ name }}</h1>
<h1>他的年龄:{{ age }}</h1>
<h1>他的性别:{{ sex }}</h1>
<h1>他的地址:{{ address }}</h1>
</div>`,
data() {
return {
name: "master",
age: 20,
sex: 'man',
address: '空美町'
}
}
})
//一、创建msg组件
const msg = Vue.extend({
template: `
<div>
<h1>{{ str }}</h1>
</div>`,
data() {
return {
str: '全局注册'
}
}
})
//注册组件(全局注册)
Vue.component('msg', msg)
const vm = new Vue({
el: '#root',
data: {
},
// 注册组件 (局部注册)
components: {
her,
him
}
})
new Vue({
el: '#root2'
})
</script>
2.组件实例(VueComponent)
-
每当我们创建一个组件的时候,
Vue.extend
都会帮我们创建一个不同的VueComponent组件实例。 -
关于VueComponent:
-
school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
-
我们只需要写
<school/>
或<school></school>
,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)。 -
特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent
-
关于this指向:
-
组件的配置对象中:
data函数、methods中的函数、watch中的函数、computed中的函数它们的this均是VueComponent实例对象。
-
Vue实例的配置对象中:
data函数、methods中的函数、watch中的函数、computed中的函数它们的this均是Vue实例对象。
-
-
-
一个重要的内置关系:
//组件实例对象的隐式原型是等于Vue的原型 VueComponent.prototype._proto_ === Vue.prototype
为什么要有这个关系?
让组件实例对象(VueComponent)可以访问到Vue原型上的属性和方法。
3.单文件组件(.Vue文件)
- 一个文件中只包含1个组件
- 为了更好地理解单文件组件,我选择先创建一个脚手架(vue-cli)项目
创建一个脚手架(vue-cli)项目
-
第一步,全局安装@vue/cli
# 通过npm npm install -g @vue/cli # OR 通过yarn 这个自行搜索 yarn global add @vue/cli
如果安装下载缓慢,可以替换为npm淘宝镜像
npm config set registy http://registy.npm.taobao.org
-
第二步,创建一个文件夹,并且听过
cd
进入到当前文件夹下,运行命令创建项目vue create xxx(项目名)
-
第三步,自定义选择如下配置
等待项目创建
-
第四步,当项目创建后,
cd
到该项目包名下运行命令npm run serve
-
第五步,浏览器访问
http://localhost:8080/
出现如下页面 ,表示创建成功。
脚手架项目结构分析
-
main.js 文件分析
/* main.js 是整个Vue项目的入口文件 */ //引入Vue import Vue from 'vue' //引入App组件,App组件所有组件的父组件 import App from './App.vue' //这条语句用于关闭Vue默认的生产提示 Vue.config.productionTip = false //创建Vue实例对象--vm new Vue({ //将App组件放入到容器中 render: h => h(App), }).$mount('#app')
-
index.html 文件分析
<!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"> <!--页签图标路径 <%= BASE_URL %>代表public包下的路径 --> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <!-- 网页标题 --> <title> <%= htmlWebpackPlugin.options.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>
main.js中的render函数
关于不同版本的Vue:
1.vue.js 与vue.runtime.xxx.js的区别:
(1).vue.js是完整版的Vue,包含:核心功能+模板解析器。
(2). vue.runtime.xxx.js是运行版的Vue,只包含:核心功能**;没有模板解析器**。
2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。
//render函数,里面接收一个创建元素的回调函数createElement
render(createElement){
return createElement(页面元素或模板,也就是组件)
}
Vue项目中默认配置文件:vue.config.js
vue.config.js
是一个可选的配置文件,如果项目的 (和package.json
同级的) 根目录中存在这个文件,那么它会被@vue/cli-service
自动加载。你也可以使用package.json
中的vue
字段,但是注意这种写法需要你严格遵照 JSON 的格式来写。
module.exports = {
pages: {
index: {
//配置入口文件
entry: 'src/main.js',
}
},
lintOnSave: false //关闭语法检查
}
4.ref属性
被用来给元素或者子组件注册引用信息(作用于id类似)
应用在html结构元素标签上的时候获取的是真实DOM元素,应用在组件标签上是组件的实例对象(VueComponent)
使用方式:
打标识:
<h1 ref="xxx">.....</h1>
或<School ref="xxx"></School>
获取:
this.$refs.xxx
小案例:
<template> <div id="app"> <h1 ref="h1">标签使用ref</h1> <Ikaros ref="ika"></Ikaros> <button @click="showRef">点击显示ref值</button> </div> </template> <script> import Ikaros from './components/Ikaros.vue' export default { name: 'App', components: { Ikaros }, methods: { showRef() { console.log(this.$refs.h1); console.log(this.$refs.ika); } } } </script> <style></style>
可以看到,一个是真实DOM元素,一个是组件实例对象
5.Vue中props配置属性
-
功能:让组件接受外部传过来的数据
-
接收数据:
-
传递数据:
<DemoComponent name="伊卡洛斯" sex="robot">
-
接收数据
-
第一种方式(只接受):
props:['name','robot']
-
第二种方式(限制类型):
props:{ name:String, sex:String }
-
第三种方式(限制类型、是否必须、默认值)
props:{ name:{ type:String, //类型 required:true // 是否必要 }, sex:{ type:String, defult:'女' //默认值 } }
-
注意:
props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告(报错),若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
-
小案例:
App.vue传过来的值
<div id="app"> <Ikaros name="伊卡洛斯" sex="robot" /> </div>
Ikaros.vue中接收值
<template> <div> <h1>name:{{ name }}</h1> <h1>sex:{{ sex }}</h1> <h1>myName:{{ myName }}</h1> </div> </template> <script> import Vue from 'vue' export default { name: 'IkarosComponent', data() { return { //复制props的内容到data中一份,然后去修改data中的数据 myName: this.name } }, props: ['name', 'sex'] } </script>
最终显示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nsJ9dssl-1688828922359)(assets/image-20230705202814974.png)]
6.Vue中混合配置属性mixin
-
功能:可以把多个组件共用的配置提取成一个混入对象使用方式:
-
第一步定义混合,新建一个
mixin.js
文件:{ data(){....}, methods:{....}... }
-
第二步使用混入:全局混入,局部混入
-
7.Vue之插件plugin
-
功能:用于增强Vue
-
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
-
定义插件:
对象.install = function (Vue,options){ //1.添加全局过滤器 Vue.filter(....) // 2.添加全局指令 Vue.directive(....) //3.配置全局混入(合) Vue.mixin(....) //4.添加实例方法 Vue.prototype.$myMethod = function () {...} Vue.prototype.$myProperty = XXXX
-
使用插件:
-
导入:
import plugin from ''./路径'
使用:
Vue.use(插件名)
-
8.Vue中的scope样式
- 问题:我们在各个组件写样式的时候,会出现样式class名或id名重复的问题,这样先引入的组建的样式会被后引入组件的相同样式名给覆盖掉
- 解决:在组件的
<style></style>
标签中添加一个关键字scoped
标记,表示作用域只在该组件内生效 - 注意:建议不要在App.vue中添加该标记