前提,建议先学会前端几大基础:HTML、CSS、JS、Ajax,不然不好懂
这一专栏知识将一次性将vue、vue2、vue3全部讲明白
这一篇内容蛮多,我本人也打算加快学习速度,于是下面大量解释将出自于黑马程序员的视频课堂截图,本人就不在拿自己写的案例做解释。
一、了解一下项目文件的结构
上一篇经历了痛苦折磨地创建vue脚手架之后,我们就可以开始工程化开发写代码了,写代码之前先了解一下vue脚手架的各个文件结构,下面是个简单的介绍图:
重点看一下:
1、【src】里的index.html
我们不会再在HTML里写vue的模板语法,而是在App.vue里提供渲染结构,而index.html就是一个容器,相当于你要涂鸦,index.html就为你提供一面可涂鸦的墙
2、接下来是【src】里的main.js文件
我们打开Vscode的终端,然后在这也可以打开vue项目脚手架的服务器
这里补充一下,上一篇我讲得比较模糊,如果你创建的vue项目的key是yarn,那就只能【yarn serve】启动;如果是npm,那就只能【npm run serve】启动
打开浏览器之后我们就可以边改代码边看浏览器的变化
那么现在我把main.js这些初始代码的解释通过下图解释一下:
然后下面那个vue实例化跟我们以前学的不太一样,我稍微改一下各位看看,做个对比
那还有人看不懂render函数是什么玩意,这里我把render函数完整写法写出来解释一下
以前我们是在HTML里用{{ }}来创建vue渲染的结构
而现在是用App.vue,而App.vue就需要搭配main.js里的render函数来创建渲染结构,所以要写一个render函数,里面通过一个基于App的函数创建渲染结构
3、然后是App.vue
当我们点开这个文件,代码可能会是灰色的,可能会跳出提示“是否安装Vue-Official”,安装它就完事,然后回到文件,就变成彩色了
或者还有个插件可以帮助把.vue文件代码变成高亮模式
然后App.vue文件就是用来渲染index.html的,当然他自己也有自己的组件
总结:那么大致看完应该就有个思路了
首先我们开启服务器,然后就进入【main.js】这个入口文件
然后【main.js】导入【App.vue文件】创建渲染结构,就相当于一个“时尚服装品牌公司”找到了一个优秀的“服装设计师”
然后【App.vue】这个“服装设计师”设计好了几套精美绝伦的衣服,那现在给谁穿呢?
通过【main.js】里的实例Vue对“#app”的绑定,相当于“时尚服装品牌公司”找到了“模特公司”——index.html,在这个“模特公司”里选定了给id名字叫“app”的这个“模特”穿上【App.vue】设计好的服装
二、Vue的组件化开发:什么是组件
先直接拿黑马程序员视频截图看一下解释
以前我们布局一个页面,全都是在HTML里,然后一个大页面就是一个<div>,然后有头部、主体、导航......,可是一个复杂的页面每个部分都还有很多很多细节,比如一个头部有logo、搜索框、登录注册按钮......一乱的话整个页面div可能都得改,那就得用组件化开发
简单来说,以前HTML的标签代码就还是HTML的DOM标签元素
然后像这种可以我们直接自定义名字,然后单独在一个【.vue文件】里设置好它的样式,然后通过一些“声明”让它们可以在任何文件里用的,就叫组件
组件又分:根组件、普通组件
根组件:App.vue文件就是根组件!它包括了整个页面的各大块
普通组件:【整个页面的各大块(头部、侧边、底部、中间......)】 以及 【各大快里的各小块(头部里的小按钮、搜索框、小图标.......)】
然后既然每一个【.vue】文件就是一个组件,那么来看一下一个【.vue】文件的三大部分主要是以下:
提示:
这里将一个快捷写法直接生成【.vue】文件这三大部分:直接"<vue"就出来了
三、怎么用组件
1、分析【.vue】文件怎么写,怎么用.vue文件创建一个组件
(1)、首先想要有一个结构必须得先有<template></template>
它提供一个结构,然后可以在里面写div容器、自定义组件,你可以暂时按照以前HTML的逻辑把它当成<body>体
注意了,在vue2里面,<template></template>里只能有一个父容器,也就是说你设了一个div,你就只能在这个div里面去添加别的容器,而不能写一个跟它同级别的div
不过在vue3里面没有这个规则,在vue2需要注意
(2)、接着你就像HTML那样写一个<style></style>标签
在里面写样式即可
(3)、我们还可以在<script></script>标签里写行为
不过这里会自动生成这么一堆代码,我们都不要
然后我们要自己先写上【export default{ }】,意思是导出当前组件的配置项,有了它就可以往里面配置我们之前学vue里的【data、methods、computed、watch、生命周期8大钩子函数】
总结:
2、怎么去创建一个App.vue根组件里的【各个小组件】,并使用他们?
1、根组件
这没什么好讲的,就跟写HTML一样,在【App.vue】代码里开启一个<templat></template>结构,接着往里面设置一个大容器<div>,然后往里直接添加普通小组件即可
2、普通组件
【首先】:凡是普通组件都是在【components】文件夹里创建的【.vue】文件
(1)、局部组件
局部组件简单来说:自定义好后,你在哪个【.vue】文件里手动“引入”了,就能在那个【.vue】文件里使用,然后各个局部组件之间设置好了自己的样式,互相之间不会影响
(这里以后【引入】我们称为【注册】;【.vue文件】称为【组件】)
大概语法模板:
(一)先到【components】文件夹下创建【.vue】文件
注意名字一定要用“小驼峰”命名(大写字母...大写字母...),否则会强制报错,哪怕你定义的名字至少一个单词,反正你就是要么一个单词里全是小写字母,要么你就至少两个单子拼起来,这两个单词首字母都大写
(二)到要使用它的其他组件里写下面这些【注册】语法
具体例子:
解释一下【注册】这块
(2)、全局注册
简单来说就是:在【main.js】里去【注册】(引入),然后就不需要再去某个组件里分别去注册了,想在哪用在哪用
比如现在我需要在每一个普通组件里设置一个【通用按钮】这么个普通小组件,那我总不可能到每一个组件的【.vue】文件里去声明【注册】吧?那就只需要在【main.js】里进行一次全局注册即可
(一)、还是第一步,到【components】文件夹创建【组件】并设置好样式
(二)、然后再【main.js】文件里写入以下代码:
具体例子:
搞定!
四、组件的一些注意点:
(1)先讲一下全局样式和局部样式
如果你在多个组件里用同名的选择器设置样式,那么是跟以前在HTML里一个CSS选择器对多个同名元素的影响一样,虽然你只是在某一个组件的【.vue】里设置它的样式,但是如果其他组件里又相同名字的元素一样会受影响,这就是【全局样式】
比如下面这个例子,我只想在BaseOne这里设置它有边框,于是我直接在BaseOne.vue里用了div选择器
但是我的BaseTwo里面也有div,虽然我并没有给它设置样式,但是BaseOne.vue的对div的样式也给到了BaseTwo,甚至App.vue里也因为有div而被赋予了这个样式
怎么办?1、要么你专门给它一个独立的class、id选择器
2、要么在当前组件的<style>标签加上一个【scoped】,设置为【局部样式】!
至于scoped的原理我就简单讲一下,只想快速学会怎么使用的可以跳过,原理就是加上了之后,vue会自动给他标签里加上一个“自定义属性名”——也就是【data-xxx】这种就叫自定义属性名,然后vue生成的自定义属性名具体形式是【data-v-哈希值】,然后css选择器也都会被添加上【data-v-哈希值】,最后vue再通过哈希值找到对应的标签和样式选择器
(2)script部分的原来vue里的data只能是函数了
以前写vue是这样,data是一个对象,里面配成员变量
现在的data只能是函数,写成对象就错!
不过data函数return的值是对象
然后在组件部分,我们要显示值得地方还是一样用{{ }}绑定上【data】里【return的对象值】里的【其中一个成员】就行,而且v-on触发事件那里也不需要绑定函数名,直接绑定对{{ }}绑定的这个元素的【运算操作】即可,如下图
那这样有什么用?这样的话,存在多个该组件的的时候,每个组件对应一个data函数返回的值,那么这多个组件之间的数据是相互独立、互不影响的,比如下图
五、组件之间数据的“通信”
因为组件和组件之间是相互独立的,那么他们的数据也是独立的,那要怎么在一个组件里获取别的组件的数据呢?
1、不同的组件关系
首先看一下有哪些组件关系:
2、不同组件关系的不同组件通信方案
父子组件关系:
父传子
注意:
1、在【父组件里】的子组件,用【v-bind】指令添加一个自定义的属性(【v-bind: xxx = "..."】可以省略成【: xxx = "..."】)
2、在【子组件里】的script里设置props来获取数据,这里props只是简单获取(1到多个)值的话就写成【数组】形式,里面的成员的名字必须跟父组件用【v-bind】传过去的自定义属性名一样!如果对传过来的值有要求限制,那就设置成对象
props补充
这里再给个例子,更清晰的理解props:
那么props不仅仅可以【单个字符串对应一个值】这样传递,还可以以【单个对象对应一个值】,这样写法的好处是可以像数据库那样,限制传过来的值是错误的、不符合要求的乱值
子传夫
子传夫有点绕,我这里给大伙捋一捋:
1、子传夫不能直接传,必须一定要通过触发一个事件才能传。
那么就在子组件里设置一个函数,这个函数里通过【this.$emit( )函数】把修改的值传过去,最后要设置一个DOM元素,通过对这个元素的一些“点击、输入......”这些事件来触发刚刚设置的函数执行
2、this.$emit( )函数需要传两个参数,第一个是【自定义事件名】,意思就是你自定义了一个事件,然后当父组件文件里的子组件触发了这个【自定义事件】就可以获得传过来的值;第二个参数就是要传过去的值
3、那么前面已经在子组件里设置了【自定义事件】了,那么在父组件里的子组件就要触发这个【自定义事件】,并再触发时绑定执行父组件文件的一个函数
4、那么当父组件里的子组件就要触发这个【自定义事件】时要执行的函数,就是做修改值操作的函数,在我们父组件里设置一个函数,然后设置一个形参,这个形参就能获得【this.$emit( )】传过去的值,那么最后在函数里把【要修改的值 = 形参】就修改成功了
易错点:
如果一个数据是在父组件里写好的,然后通过【父传子】给到子组件,那么子组件绝对绝对不可以直接去修改这个值,因为props不支持外部传进来的值被直接修改
那么这时候我们就可以调用一些函数,去通过this.$emit( )函数,把想要把这个值修改成什么样的这个“愿望”反传回给父组件,然后在父组件里面改
比如,在父组件设置了count,然后传给子组件,子组件通过props获得之后,想直接在button里通过@click="count++"这样去该值,不行!! 只能在子组件里配置methods,然后设置一个可以调用this.$emit( )的函数,然后通过@click来触发执行这个函数,而this.$emit( )的函数传过去的值就是【this.count+1】这个“愿望”给到父亲,父亲接受后执行修改操作即可
记住:谁的数据谁负责!父亲的数据还回给父亲改,儿子的数据儿子自己改
非父子关系数据通信:
简单捋一捋:
【1、创建一个空vue】相当于一个媒婆、话事人
【2、在接收方组件里调用这个“空Vue”的$on监听事件】,男方叫媒婆去“打听”一下女方对于“相亲”(自定义事件)什么意愿呢?
【3、在发送方组件里调用“空Vue”的$emit函数发送数据】,女方叫媒婆“传话”,针对“相亲”(自定义事件)这件事自己的想法是什么(数据值)
具体例子:
首先要去utils创建一个js文件,在js文件里做三个常规操作:【导入vue】、【创一个空Vue实例】、【用export default导出这个空Vue】
拓展:怎样跨过儿子,直接将父组件的数据一直往子孙后代传下去
我们知道了父传子的通信方式,那么如果我想一直把父亲组件的数据继续往下传,传给孙子、曾孙子......我总不能每次都按照父传子的方式一级一级往下传吧,这就用到了provide和inject配置项
具体例子:
另外注意,如果传的是普通类型,后期修改后新的值并不会影响到子孙组件;只有对象类数据,在后期改动过后,还剩子孙组件那边也会跟着改
那么怎么在Vue工程化项目名里写组件(可理解为DOM元素)、怎么互相传数据,就先大概讲到这