PHP8.1.21版本已发布
vue8.1.21版本已发布
jquery8.1.21版本已发布

博客列表 > 【Vue框架学习】组件注册、组件通信、前端路由实现原理等知识点学习归纳笔记

【Vue框架学习】组件注册、组件通信、前端路由实现原理等知识点学习归纳笔记

 一纸荒凉* Armani
 一纸荒凉* Armani 原创
2021年04月19日 16:22:30 875浏览

Vue组件

组件(component):是Vue最轻大的功能之一。 组件化开发特点:组件可以扩展HTML元素,封装可重用代码。


vue组件可以理解为预先定义好的ViewModel类。一个组件可以预定义很多选项,最核心的有:

  1. 模板template:模板反映了数据和最终展现给用户的DOM之间的映射关系,模板的要求:组件的模板只能有一个根元素

  2. 初始数据data:一个组件的初始数据状态,对于可重复的组件来说,通常是私有的状态。组件中的data必须是一个函数且有返回值,因为函数具有独立作用域,组件重用时才不会相互影响内部数据。

  3. 接收的外部的参数(props):组件之间通过参数来进行数据的传递和共享,参数默认是单项绑定,但也可以显示声明为双向绑定。

  4. 方法(methods):对数据的改动操作一般都在组件内进行。可以通过v-on指令将用户输入事件和组件方法进行绑定。

  5. 生命周期函数(钩子函数):一个组件会触发多个生命周期函数,在这些钩子函数中可以封装一些自定义逻辑。可以理解为controller的逻辑被分散到了这些钩子函数中。


注册组件就是利用Vue.component()方法,先传入一个自定义组件的名字,然后传入这个组件的配置。
(组件名不要带有大写字母,多个单词使用中划线分隔my-dom)

组件分为全局和局部组件,全局组件可以在多个Vue实例中重复使用,局部组件是在当前vue实例中注册的,只能在当前实例中使用。

创建组件

基本步骤:
1、创建组件构造器(调用Vue.extend()方法创建组件构造器)
2、注册组件(调用Vue.component()方法注册组件)
3、使用组件(在实例的作用范围内使用组件)

  1. 全局组件创建

第一种方式

  1. <div id="app">
  2. <child-component></child-component>
  3. </div>
  4. <script>
  5. // 1.创建组件
  6. const childComponent = Vue.extend({
  7. template: '<p>这是组件的全局注册</p>',
  8. data(){
  9. return {
  10. msg: "hello Word!",
  11. }
  12. }
  13. });
  14. // 2.注册组件
  15. Vue.component("child-component", childComponent);
  16. // 3.挂在Vue实例中
  17. const vm = new Vue({
  18. el: "#app",
  19. data: {
  20. msg: "hello",
  21. }
  22. });

第二种方式

  1. <div id="app">
  2. <child-component></child-component>
  3. </div>
  4. <script>
  5. // 注册同时创建
  6. Vue.component("childComponent", {
  7. template: `<div>
  8. <p>这是组件的全局注册--{{message}}</p>
  9. </div>`,
  10. data() {
  11. return {
  12. message: 'hello world'
  13. }
  14. },
  15. methods: {
  16. }
  17. });
  18. // 实例化Vue
  19. const vm = new Vue({
  20. el: "#app",
  21. data: {
  22. }
  23. });
  24. </script>

组件可以嵌套

  1. <div id="app">
  2. <my-itany></my-itany>
  3. </div>
  4. <script>
  5. Vue.component('my-itany',{
  6. template:`
  7. <div>
  8. <p>第一行</p>
  9. <you-itany v-bind:message="mas"></you-itany>
  10. </div>
  11. `,
  12. data:function(){
  13. return{
  14. mas:'asfg'
  15. }
  16. }
  17. })
  18. Vue.component('you-itany',{
  19. props:['message'],
  20. template:`
  21. <div>
  22. <p>{{message}}</p>
  23. </div>
  24. `
  25. })
  26. new Vue({
  27. el:'#app'
  28. })
  29. </script>

  1. 局部组件创建

第一种方式

  1. <div id="app">
  2. <hello-world :msg="msg"></hello-world>
  3. </div>
  4. <script>
  5. const vm = new Vue({
  6. el: "#app",
  7. data: {
  8. msg: "hello",
  9. },
  10. methods: {
  11. },
  12. components:{
  13. helloWorld:{
  14. template:`<p>这是组件的局部注册--{{msg}}--{{message}}</p>`,
  15. data(){
  16. return {
  17. message: "hello world!"
  18. }
  19. },
  20. props:["msg"]
  21. }
  22. }
  23. });
  24. </script>

第二种方式

  1. <div id="app">
  2. <hello-world></hello-world>
  3. </div>
  4. <script>
  5. const helloWorld = {
  6. template: `<p>这是组件的局部注册--{{msg}}</p>`,
  7. data(){
  8. return {
  9. msg: ""hello world"
  10. }
  11. }
  12. };
  13. const vm = new Vue({
  14. el: "#app",
  15. data: {
  16. msg: "hello",
  17. },
  18. methods: {
  19. },
  20. components:{
  21. // helloWorld: helloWorld,
  22. // 当属性值变量与属性名相同时,且在同一个作用域中则可以省去值变量名称
  23. helloWorld,
  24. }
  25. });
  26. </script>

第三种方式

  1. <div id="app">
  2. <hello-world></hello-world>
  3. </div>
  4. <template id="hello">
  5. <p>这是组件的局部注册--{{msg}}</p>
  6. </template>
  7. <script>
  8. const helloWorld = {
  9. template: "#hello",
  10. data(){
  11. return {
  12. msg: "hello world!"
  13. }
  14. }
  15. };
  16. const vm = new Vue({
  17. el: "#app",
  18. data: {
  19. msg: "hello",
  20. },
  21. methods: {
  22. },
  23. components:{
  24. helloWorld,
  25. }
  26. });
  27. </script>

注意:在组件中data选项必须是一个函数且有返回值

多个组件点赞案例实现

  1. <div id="app">
  2. <button-inc :name="people[0]"></button-inc><br/>
  3. <button-inc :name="people[1]"></button-inc><br/>
  4. <button-inc :name="people[2]"></button-inc><br/>
  5. <button-inc :name="people[3]"></button-inc><br/>
  6. </div>
  7. <script>
  8. Vue.component("buttonInc",{
  9. props: ["name"],
  10. template: `<div>
  11. <h1 v-text="name"></h1>
  12. <button @click="count++">点赞:{{count}}</button>
  13. </div>`,
  14. // 注意:这里data需要是函数且有返回值,否则重复渲染多个相同组件时,会影响其他相同组件中的数据。
  15. data(){
  16. return{
  17. count: 0
  18. }
  19. },
  20. });
  21. const vm = new Vue({
  22. el: "#app",
  23. data:{
  24. people: ["1号 天蓬老师", "2号 灭绝老师", "3号 欧阳老师", "4号 西门老师"]
  25. },
  26. methods:{
  27. }
  28. });
  29. </script>

排名不分先后,本点赞数量纯属虚构,如有雷同纯属胡扯……


组件传参

Vue 组件通信的关系主要有:
父子关系 兄弟关系 隔代关系

  1. 父组件往子组件 (属性传参)
    组件props是父子组件间的桥梁
    props格式为:props:[‘ 自定义属性’],
  1. <div id="app">
  2. <my-mother></my-mother>
  3. </div>
  4. <script>
  5. Vue.component('my-mother',{
  6. template:`
  7. <div>
  8. <my-tit v-bind:tit="title"></my-tit>
  9. <ul>
  10. <li v-for="(item,index) in arr" :key="index" v-text="item"></li>
  11. </ul>
  12. </div>
  13. `,
  14. data:function(){
  15. return{
  16. arr:['banana','apple','orange'],
  17. title:'水果贩卖'
  18. }
  19. }
  20. })
  21. Vue.component('my-tit',{
  22. props:['tit'],
  23. template:`
  24. <h1>{{tit}}</h1>
  25. `
  26. })
  27. new Vue({
  28. el:'#app'
  29. })
  30. </script>

父组件给子组件传递数据(利用props属性)

  1. <div id="app">
  2. <parent></parent>
  3. </div>
  4. <script>
  5. const app=new Vue({
  6. el:'#app',
  7. components:{//一个父组件里边可以包含多个子组件
  8. parent:{
  9. template:'<h1 >{{msg}},父组件<children :n="msg"></children></h1>',
  10. data:function(){//父组件的数据 传给子组件
  11. return {msg:'hello'}
  12. },
  13. components:{
  14. children:{
  15. props:['n'],//根据props拿到父组件中写的动态属性拿到数据
  16. template:'<h2>{{n}},子组件</h2>',
  17. }
  18. }
  19. }
  20. }
  21. })

父向子传值总结
v-bind:dataOfChild="dataOfParent"(父组件)====>props:['dataOfChild'](子组件)

注意:组件的传参是单向的,我们不能在子组件中直接更新父组件传递过来的参数,虽然表面上看似可以更新成功,但是数据并没有同步到父组件data中,在Vue中是不允许的,需要通过事件呼叫父组件自己修改其中的数据。


  1. 子组件往父组件 (事件传参)

子传父用事件传($emit)
子组件通过this.$emit(‘自定义事件’,参数)。

子组件中需要以某种方式例如点击事件的方法来触发一个自定义事件
将需要传的值作为$emit的第二个参数,该值将作为实参传给响应自定义事件的方法
在父组件中注册子组件并在子组件标签上绑定对自定义事件

  1. <div id="itany">
  2. <my-father></my-father>
  3. </div>
  4. <script>
  5. Vue.component('my-father',{
  6. template:`
  7. <div>
  8. <p>{{message}}</p>
  9. <my-son @send="handle"></my-son>
  10. </div>
  11. `,
  12. data:function(){
  13. return{
  14. message:''
  15. }
  16. },
  17. methods:{
  18. handle(text){
  19. this.message=text
  20. }
  21. }
  22. })
  23. Vue.component('my-son',{
  24. template:`
  25. <button @click="send">传递</button>
  26. `,
  27. data:function(){
  28. return{
  29. msg:'Who are you? is son'
  30. }
  31. },
  32. methods:{
  33. send:function(){
  34. this.$emit('send',this.msg)
  35. }
  36. }
  37. })
  38. new Vue({
  39. el:'#itany'
  40. })
  41. </>

子组件给父组件传递数据,利用事件的订阅发布模式

  1. <div id="app">
  2. <parent></parent>
  3. </div>
  4. <template id="parent">
  5. <div>
  6. <p>父组件{{msg}}</p>
  7. <!-- 点击子组件触发getData获取到子组件转来的msg数据,绑定到父组件上 -->
  8. <children @s="getData"></children>
  9. </div>
  10. </template>
  11. <script>
  12. var app=new Vue({
  13. el:'#app',
  14. components:{
  15. parent:{
  16. template:'#parent',
  17. data(){
  18. return {msg:'aaa'};
  19. },
  20. methods:{
  21. getData(data){
  22. alert(data);//子组件传过来的数据
  23. this.msg=data;
  24. }
  25. },
  26. components:{
  27. children:{
  28. template:'<p @click="changeData">子组件</p>',
  29. methods:{
  30. changeData(){
  31. this.$emit('s','lalala,传值给父组件'); //传递给父组件
  32. }
  33. }
  34. }
  35. }
  36. }
  37. }
  38. })
  39. </script>

子向父传值总结:
this.$emit(‘parentFunction’,’hello world’)(子组件)
====>@:parentFunction=”customParentFunction”(父组件)
====>customParentFunction(data) {}(父组件)

讲个故事加强印象:

情景一:
话说,在遥远的大山那边,有一对父子,父亲叫老王,儿子叫小明。父亲由于岁数大了,平常就在家干点农活,小明为了养家,外出打工。
有一天,小明想爸爸了,拿起手机给爸爸发短信,子组件主动向父组件传值,小明拿起手机,调用自己的sendDataToParent方法,在通讯录中找到了爸爸的手机号,this.$emit的第一个参数,函数名,然后拿起手机,扣了一堆字:爸爸我想你了,最近怎么样?通过this.$emit的第二个参数内容,然后发送~,短信传到了信号塔,child-component相对应基站,基站对所有短信进行监听,正好,基站的列表里有小明父亲的名单==》v-on:parentFunction,然后,短信又由基站传到了老王那边,老王的手机响了,响铃相当于调用了customParentFunction方法,然后,值就传递过来了。

情景二:
但是呢,小明在外打工,有时工作比较忙,忘了给爸爸发短信,所以老王想儿子了,但老王比较保守,又没太上过学,也不会打字,所以写了封新,去了邮局。

老王用笔在纸上写了好多内容,把纸纸相当于dataOfParent,也就是数据放进了信封信封相当于属性,也就是v-bind:dataOfChild里,然后给了邮局相当于child-component,相当于一个中介,快递员近了派送,小明来到了邮箱相当于props,看到里边有封信相当于父组件的值,拿了出来。

  1. 兄弟组件传参
    兄弟组件之间传递数据 (事件车—-原理也是事件的订阅发布模式)
  • 每一个vue的实例都是独立的;相互之间不能直接进行改变数据;
  • 给两个不同的组件找一个载体;把共同的方法放在这个载体上;
  • 这个载体就是 let eventBus = new Vue; // 创建一个新的vue实例;
  • 在这个新的实例上,有 $on: 订阅 $emit: 发布;
  • $on的绑定要基于钩子函数,一般放在created或者mounted上

这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。

  1. const EventBus = new Vue();
  2. EventBus.$on(事件名,data=>{}); // 订阅
  3. EventBus.#emit(事件名,数据); // 发布

假设兄弟组件有三个,分别是A、B、C组件,C组件如何获取A或者B组件的数据

  1. <div id="itany">
  2. <my-a></my-a>
  3. <my-b></my-b>
  4. <my-c></my-c>
  5. </div>
  6. <template id="a">
  7. <div>
  8. <h3>A组件:{{name}}</h3>
  9. <button @click="send">将数据发送给C组件</button>
  10. </div>
  11. </template>
  12. <template id="b">
  13. <div>
  14. <h3>B组件:{{age}}</h3>
  15. <button @click="send">将数据发送给C组件</button>
  16. </div>
  17. </template>
  18. <template id="c">
  19. <div>
  20. <h3>C组件:{{name}}--{{age}}</h3>
  21. </div>
  22. </template>
  23. <script>
  24. const EventBus = new Vue(); // 定义一个空的Vue实例
  25. const myA = {
  26. template: "#a",
  27. data(){
  28. return {
  29. name: 'zhang'
  30. }
  31. },
  32. methods: {
  33. send(){
  34. EventBus.$emit("myData-a",this.name)
  35. }
  36. }
  37. }
  38. const myB = {
  39. template: "#b",
  40. data(){
  41. return {
  42. age: 18
  43. }
  44. },
  45. methods: {
  46. send(){
  47. EventBus.$emit("myData-b",this.age)
  48. }
  49. }
  50. }
  51. const myC = {
  52. template: "#c",
  53. data(){
  54. return {
  55. name: '',
  56. age: '',
  57. }
  58. },
  59. methods: {
  60. },
  61. mounted(){
  62. EventBus.$on("myData-a",name=>{
  63. this.name = name;
  64. })
  65. EventBus.$on("myData-b",age=>{
  66. this.age = age;
  67. })
  68. }
  69. }
  70. const vm = new Vue({
  71. el: "#itany",
  72. data: {},
  73. methods: {},
  74. components: {
  75. myA,
  76. myB,
  77. myC
  78. }
  79. })
  80. </script>


$on 监听了自定义事件 data-a和data-b,因为有时不确定何时会触发事件,一般会在 mounted 或 created 钩子中来监听。

  1. <div id="app">
  2. <tmp1></tmp1>
  3. <tmp2></tmp2>
  4. </div>
  5. <script>
  6. const eventBus=new Vue();
  7. const app = new Vue({
  8. el: '#app',
  9. data: {},
  10. components: {
  11. tmp1: {
  12. template: "<h1>组件1{{msg}}</h1>",
  13. data(){
  14. return {msg:""}
  15. },
  16. mounted(){ //钩子函数,页面一加载进来的时候就已经绑定好了
  17. eventBus.$on('aaa',(data)=>{
  18. this.msg=data;
  19. })
  20. }
  21. },
  22. tmp2: {
  23. template: "<h1 @click='sendData'>组件2{{msg}}</h1>",
  24. data(){
  25. return {msg: 'hello! My name is tmp2'}
  26. },
  27. methods:{
  28. sendData(){
  29. //发布数据
  30. eventBus.$emit('aaa',this.msg)
  31. }
  32. }
  33. }
  34. }
  35. })
  36. </>

组件切换

is可绑定的值可以是组件名称,也是是一个组件对象。通过动态切换绑定的值,就可以实现tab页式的效果。

  1. <div id="app">
  2. <div class="container">
  3. <a href="#" @click="comp='zujian1'">显示组件1</a>
  4. <a href="#" @click=" comp='zujian2'">显示组件2</a>
  5. <a href="#" @click=" comp='zujian3'">显示组件3</a>
  6. </div>
  7. <!--aaa标签是核心,必须得写,通过is决定显示那个组件-->
  8. <aaa :is="comp"></aaa>
  9. </div>
  10. <script>
  11. var app=new Vue({
  12. el:'#app',
  13. data:{
  14. comp:'zujian2',
  15. },
  16. components:{
  17. zujian1:{
  18. template:'<h1>大家好~我是组件1</h1>'
  19. },
  20. zujian2:{
  21. template:'<h1>大家好~我是组件2</h1>'
  22. },
  23. zujian2:{
  24. template:'<h1>大家好~我是组件3</h1>'
  25. }
  26. }
  27. })
  28. </script>

前端路由

基于瞄点实现的路由模式

在学习vue路由实现之前先看下浏览器对象默认的对象属性location

先来看一段代码

  1. <a href="#red">改变背景色</a>
  2. <!-- 当然页面中如果有id为red的标签,点击后会立即跳转到该标签所在的位置 -->
  3. <!-- .../index.html---》.../index.html#red -->
  4. <script>
  5. window.onhashchange = function(event){
  6. console.log(event.oldURL, event.newURL);
  7. let hash = location.hash.slice(1); // red
  8. document.body.style.color = hash;
  9. }
  10. </script>

Location对象的属性

属性 描述
hash 设置或返回从(#) 开始的 URL(锚点)
host 设置或返回主机名和当前 URL 的端口号
hostname 设置或返回当前 URL 的主机名
protocol 设置或返回当前 URL 的协议
href 设置或返回完整的 URL
pathname 设置或返回当前 URL 的路径部分
port 设置或返回当前 URL 的端口号
search 设置或返回从问号 (?) 开始的 URL

Location对象的方法

方法 描述
assign() 加载新的文档
reload() 重新加载当前文档
replace() 用新的文档替换当前文档
hashchange() 监听hashchange变化

用哈希瞄点原生实现选项卡功能

  1. <style>
  2. a {
  3. text-decoration: none;
  4. color: #555;
  5. }
  6. li {
  7. list-style: none;
  8. }
  9. ul {
  10. margin: 0;
  11. padding: 1em;
  12. }
  13. .container {
  14. width: 30em;
  15. display: flex;
  16. flex-flow: column nowrap;
  17. }
  18. nav {
  19. height: 2em;
  20. line-height: 2em;
  21. padding: 0 2em;
  22. background-color: skyblue;
  23. display: flex;
  24. }
  25. nav a {
  26. padding: 0 0.5em;
  27. }
  28. nav a:hover {
  29. background-color: green;
  30. color: white;
  31. }
  32. .route-view {
  33. background-color: #efefef;
  34. }
  35. .route-view ul li {
  36. line-height: 1.5em;
  37. }
  38. .route-view ul li a:hover {
  39. color: coral;
  40. }
  41. </style>
  42. <!-- 选项卡 -->
  43. <div class="container">
  44. <!-- 导航 -->
  45. <nav>
  46. <a href="#/list1">国际新闻</a>
  47. <a href="#/list2">国内新闻</a>
  48. </nav>
  49. <!-- 导航对应的列表内容 -->
  50. <div class="route-view"></div>
  51. </div>
  52. <script>
  53. let list1 = `
  54. <ul>
  55. <li><a href="">福岛核废水与核电厂正常废水有区别</a></li>
  56. <li><a href="">福岛核废水与核电厂正常废水有区别</a></li>
  57. <li><a href="">福岛核废水与核电厂正常废水有区别</a></li>
  58. </ul>`;
  59. let list2 = `
  60. <ul>
  61. <li><a href="">新冠疫苗接种不是选择题而是必答题</a></li>
  62. <li><a href="">新冠疫苗接种不是选择题而是必答题</a></li>
  63. <li><a href="">新冠疫苗接种不是选择题而是必答题</a></li>
  64. </ul>`;
  65. const routeView = document.querySelector(".route-view");
  66. console.log(routeView);
  67. console.log(window.location.href); // 链接地址
  68. console.log(window.location.hash); // 哈希瞄点
  69. // 监听url中的瞄点的变化
  70. window.addEventListener("hashchange", show);
  71. // 推荐DOMContentLoaded代替load,因为他只要创建好DOM树就可以触发
  72. window.addEventListener("DOMContentLoaded", show);
  73. function show() {
  74. let hash = location.hash.slice(1);
  75. console.log(hash);
  76. switch (hash) {
  77. case "/list1":
  78. routeView.innerHTML = list1;
  79. return;
  80. case "/list2":
  81. routeView.innerHTML = list2;
  82. return
  83. default:
  84. routeView.innerHTML = list1;
  85. }
  86. }
  87. </script>


Vue Router路由原理与实现

Vue路由的安装

官网文档地址:https://router.vuejs.org/zh/installation.html

下载 / CDN: https://unpkg.com/vue-router@3.5.1/dist/vue-router.js

在 Vue 后面加载 vue-router,它会自动安装的:

  1. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  2. <script src="https://unpkg.com/vue-router@3.5.1/dist/vue-router.js"></script>

基于Vue Router来实现选项卡案例

  1. <!-- 选项卡 -->
  2. <div class="container">
  3. <!-- 导航 -->
  4. <nav>
  5. <!-- router-link: 就是a标签 -->
  6. <!-- to 相对应a标签中的href -->
  7. <router-link to="/list1">国际新闻</router-link>
  8. <router-link to="/list2">国内新闻</router-link>
  9. </nav>
  10. <!-- 导航对应的列表内容 -->
  11. <router-view class="route-view"></router-view>
  12. </div>
  13. <script>
  14. let list1 = {
  15. template: `
  16. <ul>
  17. <li><a href="">福岛核废水与核电厂正常废水有区别</a></li>
  18. <li><a href="">福岛核废水与核电厂正常废水有区别</a></li>
  19. <li><a href="">福岛核废水与核电厂正常废水有区别</a></li>
  20. </ul>`
  21. };
  22. let list2 = {
  23. template: `
  24. <ul>
  25. <li><a href="">新冠疫苗接种不是选择题而是必答题</a></li>
  26. <li><a href="">新冠疫苗接种不是选择题而是必答题</a></li>
  27. <li><a href="">新冠疫苗接种不是选择题而是必答题</a></li>
  28. </ul>`
  29. };
  30. // 创建路由对象
  31. const router = new VueRouter({
  32. // 路由配置
  33. routes: [
  34. { path: '/list1',component: list1},
  35. { path: '/list2',component: list2},
  36. { path: '', redirect: '/list1' }
  37. ],
  38. });
  39. // 注册路由
  40. const vm = new Vue({
  41. // el: ".container",
  42. data: {
  43. },
  44. methods: {
  45. },
  46. router
  47. }).$mount(".container");
  48. </script>
声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议