Vue2和Vue3技術整理2 – 核心篇 – 更新完畢

2、組件化開發

前言

2.1、認識組件概念

  • 對於學Java的人來說的話,這個詞所要表達的意思再熟悉不過了,所謂組件就是:面向對象中的抽象、封裝思想,而所謂的組件化就是:把功能用多組件的方式搭配起來編寫( 有一個根組件,旗下有N多微型組件 ,粗暴理解就是:SpringCloud中的main()方法可以搭配很多不同功能的註解,main()方法就是根組件,不同功能的註解就是微型組件 ),而這些功能組成的應用程式就是一個組件化應用,因此:這樣做之後,好處就是利於維護和提高程式碼的復用性了
  • 但是對於前端的人來說,這個東西就需要特別解釋一下:直接下定義就是 實現應用中局部功能程式碼和資源的集合
    • 瞄一下官網,它裡面有一個圖
      • image
    • 所以:現在就可以理解前面下的定義為什麼是局部功能程式碼和資源的集合了,局部功能就是某一個模組,是針對這個模組內部的,而這個模組內部的編寫不就是CSS、HTML片段、JS程式碼嗎,同時png、mp3等等這些就是資源咯
  • 至於為什麼要學組件化開發?
    • 一是因為做的應用頁面都是很複雜的,如果使用傳統的CSS+HTML+JS,那麼就會出現很多的js文件,不利於維護和 編寫很費力的
    • 二是因為組件化開發可以極好的復用程式碼、簡化項目程式碼、所以也就提高了運行效率

同時組件又有單文件組件( 真實開發玩的 ) 和 非單文件組件

  • 單文件組件:就是指只有一個組件組成( 即:是一個.vue的文件 )
  • 非單文件組件:就是有N多個組件組成

2.2、非單文件組件

2.2.1、使用組件
點擊查看程式碼

	<!DOCTYPE html>
	<html lang="en">
	  <head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title>玩一下組件</title>

		<script src="../../js/vue.js"></script>
	  </head>

	  <body>
		<!-- 被 vm 實例所控制的區域 -->
		<div id="app">
		  <!-- 3、使用組件 -->
		  <person></person>
		  <hr/>

		  <hobbys></hobbys>

		</div>

		<script>
		   // 去除瀏覽器控制台中的警告提示資訊
		  Vue.config.productionTip = false;

		  // 玩組件三板斧
		  // 1、創建組件
		  const person = Vue.extend({
			// 這裡在基礎篇中怎麼玩就怎麼玩,相應的也有watch、computed.....
			// 但是:切記:不可以用el和data必須是函數式
			/* 
			  不可以用el的原因是:el指向的是具體的容器,這是根組件做的事情,現在這是是小弟
			  不可以用data對象式,而必須用函數式:是因為對象是一個引用地址嘛( 玩java的人很熟悉這個對象的事情 )
								 如果用對象一是Vue直接不編譯、報錯,二是就算可以用對象式,那幾個變數都可以
								 指向同一個對象了,因此就會產生:一個變數修改了對象中的東西,那麼另一個變數指向
								 的是同一個對象,因此:這個變數指向的數據也會發生改變
								 而函數式則不會,因為:函數式就是哪個變數用的,裡面的return返回值就屬於哪個變數
			*/

			// 使用模板,這個就需要直接寫在組件裡面了( 讓當前組件的內容顯示出來嘛 ),如果:放到div容器的模板中,是會報錯的
			template: `
			  <div>
				<h2>{{name}}</h2>
				<h2>{{age}}</h2>
				<h2>{{sex}}</h2>  
			  </div>
			`,
			// 切記:這裡是使用data的另一種寫法 —— 函數式,必須用
			data(){
			  return {
				name: '紫邪情',
				age: 18,
				sex: '女'
			  }
			}
		  })

		  // 再創建一個組件
		  const hobbys = Vue.extend({
			template: `
			  <div>
				<h2>{{one}}</h2>
				<h2>{{two}}</h2>
				<h2>{{three}}</h2>  
			  </div>
			`,
			data(){
			  return {
				one: '摳腳',
				two: '玩',
				three: '酒吧'
			  }
			}
		  })

		  // 創建 vm 實例對象
		  const vm = new Vue({
			// 指定控制的區域
			el:'#app',
			// 這裡面也可以使用這個編寫data,當然也可以不寫,裡面要寫哪些和以前一樣
			data:{},

			// 2、註冊組件
			components: {
			  // 前為 正式在頁面中用的組件名( div模板中用的名字 )  後為組件所在位置
			  // person: person,           // 這種同名的就可以簡寫

			  // 簡寫
			  person,
			  hobbys
			}
		   });
		</script>
	  </body>
	</html>

image

組件小結

  • Vue中使用組件的三板斧
    • 1、創建組件
    • 2、註冊組件
    • 3、使用組件( 寫組件標籤即可 )
  • 如何定義一個組件?
    • 使用Vue.extend( { options } )創建,其中options 和 new Vue( { options } )時傳入的哪些option「幾乎一樣」,區別就是:
      • 1、el不要寫 ——— 因為最終所有的組件都要經過一個vm的管理( 根組件 ),由vm中的el決定服務哪個容器
      • 2、data必須寫成函數式 ———— 因為可以避免組件被複用時,數據存在引用關係
      • 另外:template選項可以配置組件結構
  • 如何註冊組件?
    • 1、局部註冊: 靠new Vue的時候傳入components選項
    • 2、全局註冊:靠Vue。component( ‘組件名’ , 組件 )
      image

      • 真實開發中一般都是玩的局部組件,局部變全局一般都是一樣的套路,去掉s,然後使用Vue來調,最後加入相應的名字和配置即可
      • 最後編寫組件標籤即可使用,如:<person></person>

2.2.2、使用組件的注意點

1、創建組件時的簡寫問題

  • // 1、創建局部組件( 完整寫法 )
          const person = Vue.extend({
              template: `
                <div>
                    <h2>{{name}}</h2>    
                    <h2>{{age}}</h2>
                </div>
              `,
              data(){
                  return {
                      name: '紫邪情',
                      age: '女'
                  }
              }
          })
    
          // 簡寫形式
          const person2 = {
            template: `
                <div>
                    <h2>{{name}}</h2>    
                    <h2>{{age}}</h2>
                </div>
              `,
            data(){
                  return {
                      name: '紫邪情',
                      age: '女'
                  }
              }
          }
    
    
  • 上面兩種都可以

    • image
    • image
    • 但是:簡寫形式在底層其實也調用了Vue.extend()
    • image
    • image
    • 驗證完了,記得把源碼的斷點去掉

2、組件名的問題

  • (1)、組件名為一個單詞時
    • 使用全小寫字母 / 首字母大寫都沒問題
      • image
      • image
  • (2)、組件名為多個單片語成時
    • 全部用小寫 / 使用 – 進行分割都沒問題( 此種分割需要注意在註冊時的方式,加單引號 )
      • image
      • image
    • 還有一種就是上圖中的效果:大駝峰命名
      • image
      • image
      • 但是:這種大駝峰命名需要注意,有些人就不可以( 因為:嚴格來說這種命名是後續的技術 —— 使用腳手架時的方式 ),但是:有些人又可以,而且不報錯,比如我上圖中的效果,因為這是Vue版本的問題【 最近才下的Vue的話,是新的,此種命名方式就是可以的,但是:在還沒接觸腳手架之前可以測試玩,先別正式用啊 】,要看源碼的話,從下圖這裡往後看即可
        • image
    • (3)、注意組件名別和HTML中的原生標籤名一致,會衝突報錯,HTML的限制就是上圖中看源碼中的哪些( 別想著它是小寫,你搞大寫,也是不行的^ _ ^ ),如果非要用HTML標籤名,讓人見名知意,那就在原生HTML標籤名前加一些特定的詞,如:user-input這種【 一般加的前綴都是功能點名稱 】,記得千萬別用:input、h2….此類名字來命名組件名

3、關於組件在使用時的注意項

  • (1)、可以使用開放標籤,如:<my-info></my-info>,這種肯定沒任何問題
  • (2)、也可以使用自閉合標籤,如:<my-info/>,但是這種有坑,這種使用方式需要腳手架支援,否則渲染時會出問題
    • image
    • image

2.2.2.1、使用組件注意點總結
  • 1、關於組件名
    • 一個單片語成時
      • (1)、全小寫,如:person
      • (2)、首字母大寫,如:Person
    • 多個單片語成時
      • (1)、全小寫,如:myinfo
      • (2)、使用 – 分割,如:my-info
      • (3)、駝峰命名,如:MyInfo,但注意:目前沒用腳手架之前最好別用,是因為指不定一會好使,一會不好使
    • 注意事項:
      • (1)、組件名最好別和HTML的標籤名一致,從而造成衝突( 非要用,可以採用加詞 / 使用 – 分割 )
      • (2)、可以在創建組件時,在裡面配置name選項,從而指定組件在Vue開發者工具中呈現的名字
        • image
        • image
        • 此種形式在第三方組件時會見到
  • 2、關於組件標籤( 使用組件 )
    • (1)、使用開放標籤也行,如:<my-info></my-info>
    • (2)、使用自閉合標籤也行,如:<my-info/>
      • 但是:此種方式目前有坑,會出現後續組件不能渲染的問題,所以需要等到後續使用腳手架時才可以
  • 3、創建組件的簡寫形式
    • const person = Vue.extend( { 配置選項 } ) 可以簡寫為 const person = { 配置選項 }

2.2.3、組件的嵌套 / 子父組件
  • 很有用啊,真實開發玩的是單文件組件,逃不開這個點的,而且還會有子父組件通訊和兄弟組件通訊

	<!DOCTYPE html>
	<html lang="en">
	  <head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title>組件嵌套</title>

		<script src="../../js/vue.js"></script>
	  </head>

	  <body>
		<!-- 被 vm 實例所控制的區域 -->
		<div id="app">
			<!-- 3、使用組件 -->
			<info></info>
		</div>

		<script>
		   // 去除瀏覽器控制台中的警告提示資訊
		  Vue.config.productionTip = false;

		  // 1、定義組件
		  const person = {
			  template: `
				<div>
					<h2>{{name}}</h2>    
					<h2>{{sex}}</h2>
				</div>
			  `,
			  data(){
				  return {
					  name: '紫邪情',
					  sex: '女'
				  }
			  }
		  }

		  const info = Vue.extend({
			  template: `
				<div>
					<h2>{{address}}</h2>    
					<h2>{{job}}</h2>
					<!-- 這個組件中使用被嵌套的組件 -->
					<person></person>
				</div>
			  `,
			  data(){
				  return {
					  address: '浙江杭州',
					  job: 'java'
				  }
			  },

			  // 基礎組件嵌套 —— 這個組件中嵌套person組件
			  /* 
				注意前提:被嵌套的組件 需要比 當前嵌套組件先定義( 如:person組件是在info組件前面定義的 )
						 原因:因為Vue解析模板時,會按照程式碼順序解析,如果定義順序反了
							   就會出現:這裡用到的組件 在 解析時由於在後面還未解析從而出現找不到
			  */
			 components: {
				 person,
			 }
		  })

		  // 創建 vm 實例對象
		  const vm = new Vue({
			// 指定控制的區域
			el:'#app',
			data:{},

			// 2、註冊組件 —— 由於info組件中 嵌套了 person組件,所以在這裡只需要註冊 info組件即可
			components: {
				info,
			}
		   });
		</script>
	  </body>
	</html>

image

另一種嵌套:開發中玩的,我的快捷模板給那個div容器起的id值為app,是有用的

  • 在開發中的嵌套是一個vm管理獨一無二的app( 就是application 應用的意思 ),然後由app管理眾多小弟

    • image
  • 所以,現在來玩一下這種組件嵌套

    • 	<!DOCTYPE html>
      	<html lang="en">
      	  <head>
      		<meta charset="UTF-8">
      		<meta name="viewport" content="width=device-width, initial-scale=1.0">
      		<meta http-equiv="X-UA-Compatible" content="ie=edge">
      		<title>vm管app,app管眾多組件</title>
      
      		<script src="../../js/vue.js"></script>
      	  </head>
      
      	  <body>
      		<!-- 被 vm 實例所控制的區域 -->
      		<div id="app"></div>
      
      		<script>
      		   // 去除瀏覽器控制台中的警告提示資訊
      		  Vue.config.productionTip = false;
      
      
      		  // 1、定義組件
      		  const person = {
      			  template: `
      				<div>
      					<h2>{{name}}</h2>    
      					<h2>{{sex}}</h2>
      				</div>
      			  `,
      			  data(){
      				  return {
      					  name: '紫邪情',
      					  sex: '女'
      				  }
      			  }
      		  }
      
      		  const info = Vue.extend({
      			  template: `
      				<div>
      					<h2>{{address}}</h2>    
      					<h2>{{job}}</h2>
      					<!-- 這個組件中使用被嵌套的組件 -->
      					<person></person>
      				</div>
      			  `,
      			  data(){
      				  return {
      					  address: '浙江杭州',
      					  job: 'java'
      				  }
      			  },
      			 components: {
      				 person,
      			 }
      		  })
      
      		  // 再定義一個app組件,用來管理其他組件
      		  const app = {
      			//   這個app組件沒有其他的東西,就是註冊和使用被管理組件而已
      			  components: {
      				//   有其他組件也可以註冊在這裡面,這裡由於info管理了person,所以只註冊info即可
      				  info
      			  },
      			  template: `
      				<div>
      					<info></info>   
      				</div>
      			  `,
      		  }
      
      		  // 創建 vm 實例對象
      		  const vm = new Vue({
      			// 指定控制的區域
      			el:'#app',
      			data:{},
      
      			// 由於組件被app管理,所以:只註冊app組件即可
      			components: { app },
      
      			// 使用組件
      			template: `
      				<div>
      					<app></app> 
      				</div>
      			`,
      		   });
      		</script>
      	  </body>
      	</html>
      
      
    • image

2.2.4、認識VueComponent()函數

1、來看一下組件到底是誰?

  • 基礎程式碼

    • <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <meta http-equiv="X-UA-Compatible" content="ie=edge">
          <title>認識VueComponent</title>
      
          <script src="../../js/vue.js"></script>
        </head>
      
        <body>
          <!-- 被 vm 實例所控制的區域 -->
          <div id="app"></div>
      
          <script>
             // 去除瀏覽器控制台中的警告提示資訊
            Vue.config.productionTip = false;
      
            // 1、定義組件
            const person = Vue.extend({
                template: `
                  <div>
                      <h2>{{name}}</h2>    
                      <h2>{{job}}</h2>
                      <h2>{{address}}</h2>
                  </div>
                `,
                data(){
                    return {
                        name: '紫邪情',
                        job: 'java',
                        address: '浙江杭州'
                    }
                }
            })
      
            const app = {
                components: {person},
                template: `
                  <div>
                      <person></person>    
                  </div>
                `,
      
            }
      
            // 創建 vm 實例對象
            const vm = new Vue({
              // 指定控制的區域
              el:'#app',
              data:{},
              components: {app},
              template: `
                  <div>
                      <app></app>    
                  </div>
              `,
             });
          </script>
        </body>
      </html>
      
      
    • image

  • 現在就來見一下組件的真身( 在前面玩this的時候說過,this指向的是:Vue實例 / 組件實例對象 ),因此:用this就可以知道組件的真身
    • image
    • image
  • 既然知道了組件真身是VueComponent(),那麼先去源碼中看一下它,從而獲取到一些對自己有用、能明白的資訊

    • image
  • 源碼提取出來就是下面的樣子

    •  var Sub = function VueComponent (options) {
              this._init(options);  <!--裡面的重要邏輯封裝在了_init()中了,目前不要去看-->
            };
      
            return Sub
          };
      
      
  • 經過前面的分析和查看源碼得出兩個結論:

    • 1、所有的組件指的都是VueComponent()
    • 2、每一個組件都調用了VueComponent(),但是:它們都不一樣( 源碼中有嘛,每次都是創建了一個全新的sub,sub就是VueComponent(),最後把這個sub返回去了,驗證一下嘛
      • image
      • image
      • 但是:這裡有一個有意思的東西,不了解原因的人很容易弄錯,也是第一條說的組件就是指VueComponent(),從而會出現不了解的人認為:每個組件都是調了同一個VueComponent(),來看一下
        • image
        • 這兩個長得一模一樣,所以就會讓人誤會,玩java的人看到這個肯定熟悉得不得了,這就是一個構造函數嘛,所以理解起來也更容易,構造函數,那就是Vue每次解析模板時( div容器使用的組件標籤 ),Vue就會去幫忙創建對應的組件,調用了構造函數,怎麼調用的?new出來的嘛,所以:這兩個組件對象肯定不一樣( 前面先驗證是否一樣就是為了注意這點,看起來一樣,實質不一樣 ,每個組件創建的都是全新的VueComponent(),得到的是一個全新的對象Sub )
        • 同時上面說到,Vue解析模板時,會幫忙去創建VueComponent(),那麼實質是誰去幫忙創建的?
          • 答案就是創建組件時,裡面的Vue.extend()去幫忙創建的,這不是我們程式設計師自己整出來的,看一下源碼
          • image

VueComponent()小結

  • 組件本質是一個名為VueComponent()的構造函數,且不是程式設計師自己定義的,是Vue.extend()生成的

  • 我們只需要寫組件標籤( <person></person><person/> ) ,Vue解析組件標籤時會幫我們創建組件的實例對象,即:Vue幫我們執行了new VueComponent( { options配置選項 } )

    • 注意點:每次調用Vue.extend(),返回的都是一個全新的VueComponent
  • 關於this的指向問題

    • (1)、在組件配置中:
      • data函數、methods函數、watch中的函數、computed中的函數,它們的this均是【VueComponent實例對象】
    • (2)、在new Vue()配置中:
      • data函數、methods函數、watch中的函數、computed中的函數,它們的this均是【vue實例對象,即:前面玩的vm 】
  • VueComponent實例對象,簡稱:vc( 或:組件實例對象 )

  • Vue實例對象,簡稱:vm

  • 但是:vm和vc也有一個坑

  • image

  • image

  • 觀察結構會發現:vm和vc如出一轍,什麼數據代理、數據監測等等,vm有的,vc都有,所以vm中的配置項在vc中都可以配置,但是:vm和vc不能畫等號,它們兩個不一樣

    • vm是Vue實例對象,vc是組件實例對象
    • vm中可以使用el選項,而vc中不可以( 只要用el就報錯 )
    • 在vm中,data可以用函數式和對象式,但是在vc中data只能用函數式
    • vm是大哥,vc是小弟,vc是vm的組件( 或者說:算上app,那麼vc就是vm的後代 )
    • vm和vc之間很多東西只是復用了而已- ( 這裡說的復用,裡面有大門道,vm和vc之間是有關係的,這裡需要原型對象知識 —— 就一句話:函數肯定有prototype( 顯示原型屬性 ),而實例( 如:const person = vue.extend()中的person )肯定有 _ _proto _ _ ( 隱式原型屬性 ),而這二者指向的是同一個對象:即,該實例的原型對象,而vc和vm之間就是通過這二者關聯起來的
  • vm和vc之間的內置關係是:VueComponent.prototype._ _proto _ _ === Vue.prototype【這句話的驗證自行在控制台輸出查看,答案是:true】

    • image
    • 圖中:VueComponent的原型對象通過 _ _proto _ _理論上應該直接指向object的原型對象,但是:Vue做了巧妙的事情:就是讓VueComponent的原型對象通過 _ _proto _ _指向了Vue的原型對象,這樣做的好處就是:讓組件實例對象 可以訪問到 Vue原型上的屬性、方法

以上的內容就屬於非單文件組件相關的,接下來就看單文件組件,也是開發中會做的事情

2.3、單文件組件

  • 就是只有一個文件嘛,xxxx.vue
  • 而xxxx就是前面說過的組件命名
    • 單個單詞:全小寫、首字母大寫
    • 多個單詞:用 – 進行分割、大駝峰命名
    • 而開發中最常用的就是:首字母大寫和大駝峰命名
2.3.1、疏通單文件組件的編寫流程
  • 前提:如果自己的編輯器是vscode,那麼就給編輯器安裝vetur插件,然後重啟vscode,這個插件就是為了能夠識別xxxx.vue文件的;如果自己是用的IDEA編輯器來寫的vue,那麼安裝了vue.js插件之後,不用安裝其他的插件都可以的
    • image
2.3.1.1、創建xxxx.vue文件
  • 這個創建的就是單文件組件,前面玩非單文件組件,不是有三板斧嗎,對照來看
  • 創建了xxx.vue之後,是一個空文件,裡面要寫的東西就三樣(模板template、交互script、樣式style ),裡面內容也對照非單文件組件來看
<template>
  <div class="temp">
      <!-- 這裡面就是模板 以前在非單文件組件中用的template選項是怎麼寫的,這裡面就是怎麼寫的-->
      <h2>{{name}}</h2>
  </div>
</template>


<script>
    // 這裡面就是交互( data、methods、watch、computed..... )
    // 就是非單文件組件中的定義組件
/*    const person = vue.extend({
        // 這裡就最好配置name選項了,一般都是當前創建的xxxx.vue中的xxxx名字即可
        name: 'Person',
        data() {
            return {
                name: '紫邪情'
            }
        },
        // 這裡面還可以寫什麼methods、watch.....之類的
    })
*/

    // 但是:上面是對照非單文件組件來寫的,在這個單文件中其實換了一下下
    // 1、這個組件是可以在其他地方復用的,所以:需要把這個組件暴露出去,然後在需要的地方引入即可
    /* 
        這裡需要使用到js中模組化的知識
        export暴露 import引入嘛
        但是:export暴露有三種方式
            1、分別暴露  export const person = vue.extend({ 配置選項 }),
                就是在前面加一個export而已
                可是:根據前面非單文件的知識來看,這個是可以進行簡寫的
                export person {}
            2、統一暴露 就是單獨弄一行程式碼,然後使用 export { 要進行暴露的名字 },多個使用 , 逗號隔開即可
            3、默認暴露( vue中採用的一種,因為引入時簡單 )  export default 組件名{ 配置選項 }
                但是:組件名就是當前整個文件,所以可以省略
                默認暴露引入: import 起個名字 from 它在哪裡
                而其他的暴露方式在引入時會有點麻煩
    */

   // 正宗玩法
   export default {
       name: 'Person',
       data() {
           return {
               name: '紫邪情'
           }
       },

       // 再配置其他需要的東西也行 如:methods、watch、computed.....
   }
</script>



<style>
/* 這裡面就是template中的樣式編寫, 有就寫,沒有就不寫 */
    .temp{
        color: purple;
    }
</style>

  • xxx.vue文件,如果使用的是vscode+vetur,那麼上面的模板有快捷鍵可以用 :就是輸入 <v 然後回車就可以生成了
    • image
    • image
2.3.1.2、註冊組件到app中
  • 前面玩過,vm管app,app管其他的小弟,所以需要一個app組件,創建一個app.vue
<template>
  <div>
      <!-- 3、使用app管理的組件 -->
      <person></person>
  </div>
</template>

<script>
    // 1、引入定義的person組件(要是有其他組件要引入那是一樣的套路)
    // 1Person.vue這個名字不正規啊,我只是為了排序才加了一個1
    import person from "./1Person.vue"  

    export default {
        name: 'App'
        // 2、註冊引起來的組件
        components: {person}  // 完成了引入和註冊之後,在這裡面就可以用引入的組件了
    }
</script>

<style>
/* app是為了管理其他所有的組件,所以這個style其實不寫也行( 按需要來吧 ) */
</style>

2.3.1.3、將app和vm綁定起來
  • 新建一個main.js文件,創建這個文件的目的:一是讓app和vm綁定,二是瀏覽器並不能識別.vue文件,所以根本展示不了,因此:需要將.vue文件轉成js文件,這樣瀏覽器就能解析了【 當然:解析的門道沒這麼簡單啊 】
// 1、引入app組件
import App from "./2App.vue"

// 2、把app組件和vm進行綁定
new Vue({
    // 這裡面和以前一樣寫法,當然:這裡的el值綁定的是容器id,怕誤會改成root也行
    el: '#App',
    components: {App}
})

2.3.1.4、創建容器
  • 前面app組件和vm綁定了,但是vm中指定的el值,它綁定的容器還沒有啊,因此:創建index.html
<!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>創建el容器</title>

    <!-- 
        記得要引入js,而此時就需要引入兩個js,一個是main.js,一個是vue.js 
        可是:在解析下面的容器時,可能會導致js渲染不及時出問題
        因此:引入js最好放在下面容器的後面引入
    -->
</head>
<body>

    <div id="App">
        <!-- 
            2、使用app組件,可以在這裡使用,也可以不在這裡使用
            直接在app.vue中使用template選項進行使用
        -->
        <App></App>
    </div>
    

    <!-- 
        1、引入js 
            vue.js是因為:main.js中new vue()需要它,所以:先引入vue.js【 提前準備好嘛 】
            其次再引入main.js
    -->
    <script src="../../../js/vue.js"></script>
    <script src="./3Main.js"></script>
</body>
</html>
  • 經過上面的操作之後,玩Vue單文件組件的流程也就完了,整體結構就是如下所示
    • image

  • 而整個流程按照解析的邏輯來看就是如下流程
    • 1、進入index.html,創建了div id = “App”容器,然後引入vue.js,再引入main.js
      • image
    • 2、但是:引入main.js,去main.js裡面開始解析時,發現:需要引入App.vue,所以:接著引入App.vue
      • image
    • 3、進入App.vue,又發現需要引入Person.vue
      • image
    • 4、將所有東西都引入完了之後,就可以依次進行渲染了( 邏輯就不說明了 ),而經過上面的邏輯梳理之後會發現:main.js就是入口,是從main.js開始引入,從而把其他的東西也給引入進來了

當然:以上的東西弄完之後,還啟動不了,一啟動就會報錯

  • image
  • 這是因為:瀏覽器不能解析ES6語法,這需要使用另外一個技術,腳手架來支援

2.3.2、認識腳手架 vue cli
  • cli全稱: command line interface 即:命令行介面工具,但是:一般說的都是腳手架,正規名字說起來太官方、繞口

  • 在vue官網有這個腳手架生態

    • image
2.3.2.1、使用nodejs配置腳手架
  • 前提:保證自己的電腦有nodejs,玩後端的人要是不了解nodejs的話,就把它理解為伺服器,低配版的tomcat,
  • nodejs的配置很簡單,官網進行下載、一直next
    • 有個注意點:選擇安裝目錄時,別把nodejs安裝到系統C盤了,不然很大可能出現許可權不足,無法操作的問題,特別是:如果自己的電腦沒升級,還是家庭版的而不是專業版的,這種問題更常見
      • 出現這種問題就需要切換到管理員身份運行cmd才可以進行安裝vue-cli了,甚至有時會奇葩點:需要在管理員身份下運行npm clean cache –force
      • 然後再進入到C盤的用戶目錄下的appdata/roaming下把一個叫做npm-cache的快取文件刪了,最後再用管理員身份運行npm clean cache –force清除快取【 當然:npm clean cache –force不是絕對好用,還得看自己的另外配置 不多延伸了 】,經過上面的操作,把自己整瘋了之後才可以安裝vue cli腳手架
  • nodejs官網://nodejs.org/en/download/
    image

  • LTS就是穩定版,而CURRENT就是更新版( 新特性就丟在這裡面的,可能會出現bug,所以不推薦下載 )

    • 其中下載msi和zip都可以,區別就是msi是直接幫你把環境變數配好了的,當然:也有其他的區別,而zip就是解壓之後,自己進到目錄下複製路徑,然後配置在「系統環境變數」的path目錄中即可,這些操作玩java的第一天就弄了jdk的環境變數,所以再熟悉不過了,而如果是前端人員,nodejs早學了,所以可以下滑到到後面全局安裝vue cli那裡,安裝成功之後是如下效果

      • image

      • 查看一下是否成功?

        • 進入dos窗口( win+r,輸入cmd回車 )

        • image

        • 表明成功

        • 但是:現在npm的配置和快取文件都在C盤用戶目錄/appdata/roaming/npm 和 appdata/local/npm-cache中的

        • image

        • 因此:我們需要去改動這兩個地方( 知道了這兩個目錄,不用改也可以,後面什麼事都可以不做的,對後續的操作也沒影響 ,嫌找東西時麻煩就可以改 )

          • 1、在安裝的nodejs中新建node_global和node_cache兩個文件夾( 前為全局配置路徑,後為npm快取路徑 )

            • image
          • 2、執行如下命令( 路徑記得複製成自己的 ),另外記得用管理員許可權打開命令行窗口

            • 進行設置
              C:\WINDOWS\system32>npm config set prefix "D:\install\Nodejs\node_global"
              
              C:\WINDOWS\system32>npm config set cache "D:\install\Nodejs\node_cache"
              
              檢查是否成功
              C:\WINDOWS\system32>npm config get prefix
              D:\install\Nodejs\node_global
              
              C:\WINDOWS\system32>npm config get cache
              D:\install\Nodejs\node_cache
              
              
            • 可見成功修改,但是:還需要做最後一步,去改環境變數( 使用msi安裝是默認配好了的,但現在我們改動了配置,需要去重新弄一下 )

              • 在改環境變數之前,在剛剛新建的node_global目錄下,再新建一個node_modules目錄
                • image
          • 3、修改環境變數( 後端人員再熟悉不過,如果前端人員不明白的,直接電腦右鍵,選擇屬性,選擇高級系統設置,選擇環境變數就可以了【家庭版和專業版、win10和win7不一樣,自行查找】 )

            • image
            • image
            • 最後一路點OK即可,測試是否成功,可以選擇安裝一個vue包來測試
            • image
            • 此時可能出現報一堆的ERROR,最後一行的大概意思就是讓使用 root/ admin…用戶( 也就是讓用管理員運行dos窗口,再執行命令 )

            報一堆ERROR錯誤的解決辦法:本質是你安裝時弄錯了,文件許可權不夠 / 另外一種也是最常見的一種,就是明明都是正規步驟,但是就是不行,這是因為你自己本身登錄的電腦用戶許可權不夠【 自己買電腦之後,創建登錄用戶時操作有問題 】

            • 想解決,此時:做一個操作即可,回到nodejs安裝的根目錄

              • image

              • 右鍵選擇屬性、安全、高級

              • image

              • 當然:要是自己的電腦在這個安全介面中,直接編輯許可權,然後把「寫入許可權」√上是可以的,那就直接√上【 我說的是勾上時,不報錯、直接出現一個載入過程 / 無任何報錯提示的那種 ,若選擇勾上、確認時系統有一個什麼鬼提示來著,忘球了,反正有個提示就是不允許操作 】,要是不行就接著往後看

              • image

                • 沒有用戶,點擊添加之後,選擇主體、在檢索名稱前面的框中:輸入你當前登錄用戶名、檢索即可有結果、最後確認是自己就可以了,這種情會直接看到許可權選擇,直接選完全控制 / 除這個以外的全勾上即可
              • image

              • 確認之後,再使用npm install -g xxx就發現可以了

              • 安裝之後,在剛剛新建的node_global和node_cache中是有東西的

              • image

              • 如果想要把全局配置恢復為初始化配置的話,也很簡單,系統C盤用戶目錄/.npmrc的文件,刪了就可以了

                • image

  • 配置成功了nodejs之後,就可以使用npm指令了
  • 但是:npm是國外的,我們拉取東西時就猶如隔了一道牆,很慢,因此:拉取淘寶的鏡像,從而使用cnpm來代替npm指令,拉取淘寶鏡像鏈接: npm install -g cnpm --registry=//registry.npm.taobao.orgnpm config set registry //registry.npm.taobao.org 這兩個都可以,如果因為自己電腦原因出現卡頓,進度條走得很慢、或不走了,那直接敲一下回車就可以了( 但:不是絕對好使,可能在你那邊就不得吃 )
    • 這兩個淘寶鏡像,建議用前者,另外:從拉取鏡像這裡開始就一定要保證自己的網路流暢、電腦性能稍微好一點,不然很容易導致一是淘寶鏡像拉取失敗( 看起來成功了,但是一用就報cnpm不是內部命令,出現這種一是許可權不夠,需要使用管理員身份打開dos窗口【就是前面做的修改文件許可權當白做了,但:配置沒錯啊,只是鏡像拉取失敗了】,二是cnpm沒拉完整 ),要求網路的另一個原因就是後面安裝腳手架時,要是網路和電腦不好,也很容易出現看起來成功了,但是:一用就發現vue不是內部指令,這種還是一個失敗品
    • 下圖是我重新拉取一遍的效果
      • image

  • 1、全局安裝@vue/cli,指令:npm install -g @vue/cli
    • npm 是nodejs的指令 拉取了淘寶鏡像之後,就可以使用cnpm代替了
    • install 就是安裝的意思
    • -g 是全局安裝
    • @vue/cli 是安裝的東西
    • 有個注意點:要是有人知道可以使用npm install -g vue-cli這樣安裝腳手架的話,可以用,沒錯的,但是:目前別這麼安裝( 它安裝的腳手架是舊版本的,用這種方式安裝的不能保證vue( 目前版本是1 — 3 )和vue-cli( 目前版本是1 — 4 )的版本很適合,所以後續使用一些命令時可能會出現版本不足的問題,讓把版本升級,而使用@vue/cli安裝的是最新版本
    • 對於後端人員來說,這些指令太簡單了,所以後續不說明了,下圖是我重新安裝了一次的結果
      • image
  • 2、創建一個文件夾,並進入目錄中,使用指令:vue create xxxx
    • vue create 是cli3.x的命令,要是前面安裝腳手架時是亂整的,就會發現:這個命令用不了,要是出現這樣的話,那麼執行一遍這個命令,會提示你:卸載以前的cli,然後執行什麼命令安裝cli
    • xxx 就是要創建的項目名
      • image
      • image
      • image
      • 出現如上圖就說明在開始拉取依賴了
  • 3、啟動項目
    • image
    • image
    • image
    • 想要退出啟動的程式,一是直接點窗口右上角的×,二是按兩次ctrl+c即可

2.3.2.2、分析cli構建的項目
  • 使用vscode打開剛剛編譯的項目【 ps:有些人是個人才,打開還整半天,1文件夾右鍵有一個open with code / 使用code打開,圖標就是vscode的,沒有的話,是安裝vscode時,有一個介面是open with xxxx 的兩個選擇框沒選,卸載、重裝把那兩個√打上就可以了 】

image

1、packpage-lock.json

image

2、package.json

image

  • 以上就是基礎的東西,接下來就對照前面手寫的單文件組件思路來分析接下來的東西,那時說過:main.js是入口,所以cli腳手架程式就從main.js入手( 在cli中為什麼它是入口?腳手架底層做的處理 )
    • 1、main.js
      • image
    • 2、引入了App.vue組件,那就接著分析App.vue組件
      • image
      • 注意:
        • assets目錄是專門用來放靜態資源的,如:png、mp3…( 後端的人就把這個目錄當做是SpringBoot中的那個static目錄 )
        • components目錄是專門用來放組件的( App.vue是一人之下【vm】萬人之上【其他任何組件】,所以不在這裡面 )
    • 3、上面引入了helloword組件,而那裡面就是一堆正常的組件寫法
    • 4、分析的差不多了,但是:還少了一個重要的東西,容器在哪裡?
      • 就在index.html中,而這個東西有一個專門的public目錄來放
        • image
    • 經過前面的分析之後,再結合上次寫的單文件組件,就知道對應的東西寫在哪裡了
    • 順便說一下:vscode中啟動vue程式,按ctrl + 飄字元( esc下面、tab上面的那個按鍵 )喚出控制台,後面的就知道怎麼做了 做了修改之後,按ctril+s保存時會自動重新編譯 )
      • image

2.3.2.3、認識render()函數
  • 把前面編譯好的例子改一下( 我的是把前面疏通流程的程式碼拷貝進來的 )

image

image

  • 原因就是:程式碼中的一句話
    • image
    • 那就去看一下vue到底有哪些版本?按住ctrl+滑鼠點引入的vue哪裡,點vue就進去了
      • image

      • 點開它的包說明:就發現引入的是dist/vue.runtime.esm.js

        • image
      • 隨便選擇一個右鍵在資源管理器中顯示,就可以看到文件大小( 可以和vue.js對比,就少了100kb左右而已,少的就是模板解析器,但是:少的這部分用處很大 )

        • image
      • vue為什麼要搞出這麼多版本?

        • 這是因為vue其實就是將webpack進行了封裝,然後添加了一些技術,從而搞出來的,所以webpack在進行打包時會將.vue轉成.js從而實現渲染( 後端人員不懂webpack的,就粗暴的把它當做是maven中的install打包,當然:compile編譯等等這些指令功能也在vue中有相同的效果的實現機制 )
        • 而程式編寫完了之後,webpack本身就支援將.vue轉成.js,那使用webpack打包時,模板解析器就不應該出現了( 模板解析器就是編寫時解析而已 ),所以真實打包時如果還要有模板解析器就出現一個騷氣的事情,舉個例子:去做某事
          • image
          • 整出那麼多版本的原因就知道了唄,減少程式體積、提高性能嘛
  • 回到前面報的錯,怎麼解決?控制台已經把答案給的很明確了
    • 1、使用包含模板解析器的vue就可以了
    • 2、使用render()函數實現模板解析器的效果
  • 1、使用包含模板解析器的vue最簡單
    • 引入的vue有問題,那就改一下嘛
      • image
      • image
  • 2、使用render()函數來實現解析template的功能
    • image
    • image
    • 知道了render()的結構,那就去實現解析template的功能
      • image
      • 然後蘭姆達表達式簡寫不就成原來的樣子了嗎
        • image
        • App就是h2,因為:App是一個組件,使用時就是<App/><App><App>
        • 這個render()函數也就在vm綁定容器時會用到,其他地方見都見不到的

2.3.2.4、關閉語法檢測
  • 官網中有,改cli的全局配置也是一樣的套路

image

  • 官網裡面就是說新建一個vue.config.js的文件,這個文件必須和package.json是同級目錄,然後要配置東西時,找對應的配置項,然後複製粘貼對應的內容( 或改動一點自己要的配置項即可 )
  • 關閉語法監測,就是名為lintOnSave的配置項罷了
    • 創建vue.config.js文件
      • image
      • image
  • 經過上面的操作之後,就算定義一個變數,然後後面一直沒用這個變數也不會導致項目啟動不了了,否則:使用npm run serve時,就會報錯,導致啟動不了
  • 而創建這個vue.config.js文件能夠修改cli的配置是因為:cli是在webpack的基礎上做出來的,而webpack是在nodejs的基礎上整出來的,因此:程式最後是將自己寫的vue.config.js去和webpack中的進行了合併,從而就成功了
  • 另外:注意點
    • cli中不是什麼都可以修改的,下圖這些文件夾 / 文件名最好都別改( 是名字別改啊,沒這麼傻吧^ _ ^ )
    • image
    • 想要重新改動,就在剛剛新建的vue.config.js中配置即可,但是注意:
      • vue.config.js每一次更新都要使用npm run serve重新啟動項目,否則不生效
      • vue.config.js中不可以說:不用了加個注釋,然後等後面用的時候再解開注釋,這種是不行的,要麼就配置,要麼就不配置,否則:啟動不了項目

2.3.3、認識ref屬性
  • 這個屬性是用來給「元素」或「子組件「註冊引用資訊( 也就是id屬性的替代者 ),這個小東西很重要,關係到後續組件與組件之間的通訊

我重新複製了一份src文件夾,用的時候把名字改回來就可以了( 需要哪一個就把哪一個改為src,然後執行npm run serve就行了 )

  • image

運行效果如下:

  • image

現在有一個需求:獲取下圖中的DOM結構

  • image
  • 使用傳統js來操作的話,就是document.getElementById進行獲取,但是:Vue中提供得有ref屬性來進行操作:ref屬性用來給「元素」或「子組件「註冊引用資訊( 也就是id屬性的替代者 ),所以來見識第一個「元素」註冊引用資訊( 在HTML元素中這個ref屬性就和id屬性是一樣的效果 )
    • image
  • 第二種:ref屬性是在子組件上的
  • 這種很重要,後面組件與組件之間交互的基礎
    • image
    • image
    • image
  • 但是:這種和id就不同了,id是直接獲得了子組件的DOM結構,而ref是獲得了組件本身VueComponent
    • image
    • image

ref屬性小結

  • 被用來給元素( id的替代者 )或子組件註冊引用資訊
  • 應用在HTML標籤上獲取的是真實DOM元素,應用在組件標籤上獲取的是組件實例對象( vc )
  • 使用方式:
    • 做標識: <h1 ref="xxx">......<h1><Person ref="xxx"><Person>
    • 獲取:this.$refs.xxx

2.3.4、props配置-獲取外傳數據
  • 組件中的props配置就是為了獲取從外部傳到組件中的數據
  • 這個東西很有用,後續玩子傳父、父傳子就需要這個東西,而且開發中這個東西會經常看到

image

2.3.4.1、只接收數據

Person.vue組件編寫內容

<template>
  <div>
      <h2>{{name}}</h2>
      <h2>{{sex}}</h2>
      <h2>{{age}}</h2>
      <h2>{{job}}</h2>
      <h2>{{address}}</h2>
  </div>
</template>

<script>
    export default {
        name: 'person',
        
        // 使用props配置,使這個Person組件中的數據從外部傳進來( 封裝的思想來咯 )
        // 第一種方式:只接收數據即可(數組寫法) - 此種方式:接收的數據統統都是字元串
        props: ['name','sex','age','job','address']
    }
</script>

App.vue組件編寫內容

<template>
  <div>
      <h1 ref="content">歡迎來到對抗路,對手資訊如下</h1>
      <!-- 使用組件 並 傳入數據 -->
      <Person name="紫邪情" sex="女" age="18" job="java" address="浙江杭州"/>
  </div>
</template>

<script>
    import Person from "./components/Person.vue"

    export default {
        name: 'App',
        components: {Person},
    }
</script>

ctrl+s重新編譯

  • image

效果如下

  • image

2.3.4.2、接收數據 + 數據類型限定

image

image

  • 至於限定類型有哪些? 可以是下列原生構造函數中的一種
    • StringNumberBooleanArrayObjectDateFunctionSymbol、任何自定義構造函數、或上述內容組成的數組

2.3.4.3、接收數據+限定類型+數據有無接收的必要+數據默認值
  • 這個東西,玩java的人看起來很熟悉,和ElasticSearch中的mapping映射很像

image

image

2.3.4.4、處理外部傳入數據類型問題

image

image

  • 問題就輕輕鬆鬆解決了

2.3.4.5、解決props接收數據之後,修改它的值
  • props配置中不是什麼屬性名的值都可以接收的,如:key、ref

image

image

image

  • 意思就是:key不能作為props接收的數據,原因就是因為:key這個屬性被Vue給徵用了,Vue底層需要使用diff演算法嘛,它用了這個key關鍵字,所以我們不可以用它

回到正題,props接收了數據怎麼修改它?

  • 首先要知道,props被底層監視了的,所以它的東西只可以接收,不可以修改,想要接收了數據,再修改它,我們就需要藉助data,中轉一下
  • 先來看不中轉,然後修改props中數據的下場
    • image
  • 使用data中轉,從而修改數據
    • image

2.3.4.6、props配置總結
  • 功能:讓組件接收外部傳進來的數據

  • (1)、傳遞數據

    • <Person name="xxxx">
  • (2)、接收數據

    • 1)、只接收

      • props: [‘name’]
    • 2)、接收數據 + 數據類型限定

      • 	props: {
        		name:String
        	}
        
    • 3)、接收數據 + 數據類型限定 + 必要性限制 + 數據默認值

      • 	props: {
        		type:String,
        		required:true,
        		default:'紫邪情'		註:一個數據欄位不會同時出現required和defautle
        	}
        
  • 注意:props中的數據是只讀的,Vue底層會監測對props的修改,如果進行了修改,就會發出警告

    • 如果業務需要修改,則:把props中的數據複製一份到data中,然後去修改data中的數據就可以了
    • 須知:vue中優先使用props中的數據,然後再使用data中的數據( 可以使用data中的名字和props中的名字一樣,然後去渲染,發現渲染出來的數據是props中的 )

2.3.5、mixin混入 / 混合
  • 這個東西就是把多個組件中共同的配置給抽取出來,然後單獨弄成一個js文件,使用一個對象把相同配置給封裝起來,然後在需要的組件中引入、使用mixins配置進行使用即可
  • 這種思想後端的人再熟悉不過了,工具類的編寫不就是這麼回事嗎

2.3.5.1、使用

基礎程式碼

  • image
  • image

在上面的程式碼中,methods中的程式碼是相同的,因此:使用mixin混入來進行簡化,也是三板斧而已

  • 1、新建js文件( 名字根據需要取即可 )
    • image
  • 2、在需要的組件中引入抽取的程式碼和利用mixins配置進行使用
    • image
  • 3、運行效果
    • image

但是:mixin混入有一些注意點

  • 1、除了生命周期,如果其他配置是在mixin中定義了,同時在組件中也定義了,那麼:優先使用的是組件中自己定義的( 無論二者相同與否都一樣 )
    • image
    • image
  • 2、如果在mixin中定義了生命周期鉤子函數,那麼:優先使用mixin中的鉤子函數
    • image
    • 如果二者不同,那麼就會造成二者中定義的都會被調用
      • image
      • image

另外:mixin混入是支援全局配置的,不過這種操作不當會出現問題,因此:目前不建議用,需要時再玩吧,思路如下:

  • 1、一樣的創建js文件
  • 2、在App.vue中引入
  • 3、在App.vue中使用vue.mixin( 暴露對象1 ) 、vue.mixin( 暴露對象2 )…….
  • 使用了這三板斧之後,就可以實現全局配置了
2.3.5.2、mixin混入總結
  • 功能:把多個組件共同的配置提取成一個混入對象

  • 使用方法:

    • 1、定義混入,如:

      • 	暴露方式 const 對象名 {
          		data(){......},
          		methods:{........},
          		........
            }
        
    • 2、在需要的組件中引入混入

    • 3、使用混入,如:

      • 1)、局部混入:mixins: [ xxxxxx , ……… ] , 注意:這裡必須用數組
      • 2)、全局混入:Vue.mixin( xxxxx )

2.3.6、插件
  • 這個東西的作用很大,它可以合理的增強Vue

使用,也簡單,還是三板斧

  • 1、創建js文件( 包含install()方法的一個對象 )
  • 2、引入插件
  • 3、使用插件
2.3.6.1、玩一下插件

基礎程式碼效果

  • image

1、創建一個包含install()方法的對象的js文件

  • image

2、在main.js中引入、使用插件

  • image

3、效果如下

  • image
  • 可以得知:創建的插件中傳遞的那個參數Vue就是vm( Vue實例對象 )的締造者 —— vue構造函數
  • 得到上面哪那個結果就很重要了,試想:我們使用這個構造函數做過什麼事?
    • 自定義全局指令 Vue.directive
    • 定義全局過濾器 Vue.filter
    • 定義全局混入 Vue.mixin
    • ……..
  • 把這些東西放到創建插件的install()中可行?它接受的參數就是Vue嘛

正宗玩法

  • 1、創建包含install()方法的對象的js文件
    • image
  • 2、在main.js中引入插件、應用組件
    • image
  • 3、使用插件中的東西
    • image
  • 4、效果如下:
    • image

當然:我們也可以給插件中傳東西進去

  • image
  • image
  • image
2.3.6.2、插件總結
  • 功能:用於增強Vue

  • 本質:是包含install()方法的一個對象的js文件,install的第一個參數是Vue,第二個以後的參數是插件使用者傳遞的數據

  • 插件的玩法:

    • 1、定義插件:

      • // 1、創建插件  export default 是自己選擇的暴露方式
        export default {
            install(Vue,[ other params ]){
                
                // 定義全局過濾器
                Vue.filter( ..... ),
                    
                Vue.directive( ...... ),
                           
                Vue.mixin( ....... )
                .........
            }
        }
        
        
    • 2、在main.js中引入插件

    • 3、在main.js中向Vue應用插件 Vue.use( 插件名 )

    • [ 4、使用插件中定義的東西 ] ———— 可有可沒有,看自己的程式碼情況

    • 這裡注意一個東西:定義插件中的install()第一個參數是Vue,即:vm的締造者,Vue構造函數( 這裡可以聯想原型對象,也就是前面說的vm和vc的內置關係:VueComponent.prototype._ _proto _ _ === Vue.prototype,這也就是說在Vue身上加點東西,那麼:vm和vc都可以拿到,如:

      •   	Vue.prototype.$myMethod = function(){ ...... }
          	Vue.prototype.$myProperty = xxxx
        
          	prototype路線是加東西
          	_ _proto_ _路線是取東西
        

2.3.7、scoped局部樣式
  • 作用:讓樣式在局部生效,防止衝突
  • 寫法:<style scoped>

假如有兩個組件,裡面都用了同一個class類名,但是做的style卻是不一樣的

  • image
  • image

此時如果把兩個class名字改成一樣呢?開發中樣式多了這種事情是在所難免的

  • image

憑什麼就是Person2.vue組件中的樣式優先?

  • 這和App.vue中引入組件的順序有關
  • image
  • 不然:把組件引入的順序換一下就發現變了
    • image

那上述樣式衝突了怎麼解決?

  • 答案就是使用scoped限制作用域( 後端人員,這個東西熟悉得很,maven中依賴的作用域,是一樣的效果 )
  • image
  • image
  • 這個小技巧在開發中會經常見到

當然:style標籤不止支援scoped屬性,還可以用其他的

  • image

  • 另外:less需要less-loader支援,所以需要安裝less-loader,但是:有坑( 版本的問題 )

    • image
  • cli中的webpack版本

    • image
  • 安裝適合cli的webpack的less-loader版本

    • image

最後還有一個問題:scoped使用在App.vue中就會發生很詭異的事情

  • App.vue是大哥,所以這裡面的style會全局生效( 也就是多組件都在使用的樣式,就可以提到App.vue中寫,然後在其他需要的組件中使用即可 )
  • 但是:如果App.vue中style使用了scoped,那麼就會導致:樣式只在App.vue中有效,那麼:其他組件中想要用,那對不起,管不了,最後頁面就會出現詭異的事情 —— 不是自己寫的樣式的樣子
  • 所以:App.vue中最好別用scoped屬性,而其他組件中最好都加上scoped屬性

2.3.8、組件化應用應該怎麼去寫?

實例:實現如下的效果( 就是一個人名錄入,然後可以對名字做一點操作罷了 )

  • image
  • 組件編寫流程( 基本上都是一樣的套路 ),接下來按照套路去弄就可以了
2.3.8.1、分析結構 / 拆分組件

根據頁面結構來看,可以拆分成如下的結構:

  • 1、外面大的框就是一個組件 —— 也就是App.vue父組件,而App組件中又包含的如下組件:
    • 1.1、輸入框是一個組件( 子 )
    • 1.2、人名的展示框是一個組件( 子 ,卻是下面兩個的「父」 )
      • 1.2.1、然後發現展示框裡面又有每一個人名組件( 子 )
      • 1.2.2、還有一個全選 / 顯示已選人數的組件( 子 )

2.3.8.2、創建對應組件並編寫內容

1、創建並編寫組件對應內容( 統稱編寫靜態組件 )

  • 1)、App組件

    • (1)、HTML結構

      • <template>
          <div id="root">
              <div class="name-container">
                <div class="name-wrap">
        
                  <NameHeader></NameHeader>
                  <NameList></NameList>
                  <NameFooter></NameFooter>
        
                </div>
              </div>
          </div>
        </template>
        
        <script>
        import NameHeader from "./components/NameHeader.vue"
        import NameList from "./components/NameList.vue"
        import NameFooter from "./components/NameFooter.vue"
        
            export default {
                name: 'App',
                components: {NameHeader,NameList,NameFooter}
            }
        </script>
        
        
    • (2)、CSS樣式 + 後續需要的通用樣式

      • body{  
          background: #fff;
        } 
        
        .btn {
          display: inline-block;
          padding: 4px 12px;
          margin-bottom: 0;
          font-size: 14px;
          line-height: 20px;
          text-align: center;
          vertical-align: middle;
          cursor: pointer;
          box-shadow: inset 0 1px rgba(255,255,255,0.2),0 1px 2px rgba(0, 0, 0, 0.05);
          border-radius: 4px;
        }
        
        .btn-danger {
          color: #fff;
          background-color: #da4f49;
          border: 1px solid #bd362f;
        }
        
        .btn-danger:hover {
          color: #fff;
          background-color: #bd362f;
        }
        
        .btn:focus {
          outline: none;
        }
        
        .name-container {
          width: 600px;
          margin: 0 auto;
        }
        
        .name-container .todo-wrap {
          padding: 10px;
          border: 1px solid #ddd;
          border-radius: 5px;
        }
        
        
  • 2)、輸入框組件

    • (1)、HTML結構

      • <div class="name-footer">
                <label>
                    <input type="checkbox">
                </label>
                <span>
                    <span>已選擇0</span> / 共計2
                </span>
                <button class="btn btn-danger">清除已選人員</button>
            </div>
        
        
  • 3)、人名展示框

    • (1)、HTML結構

      • <template>
            <NameObj></NameObj>
        </template>
        
        <script>
        import NameObj from "./NameObj.vue"
        
            export default {
                name: 'NameList',
                components: {NameObj}
            }
        </script>
        
        
    • (2)、CSS樣式

      • /* #region list */
          .name-main {
              margin-left: 0px;
              border: 1px solid s#ddd;
              border-radius: 2px;
              padding: 0px;
          }
        
          .name-empty {
              height: 40px;
              line-height: 40px;
              border: 1px solid #ddd;
              border-radius: 2px;
              padding-left: 5px;
              margin-top: 10px;
          }
        
        /* #endregion */
        
        
  • 4、每個人名展示

    • (1)、HTML結構

      • <template>
            <ul class="name-main">
                <li>
                    <label>
                        <input type="checkbox"/>
                        <span>xxxxx</span>
                    </label>
                    <button class="btn btn-danger" style="display:none">刪除</button>
                </li>
            </ul>
        </template>
        
        <script>
            export default {
                name: 'NameObj',
            }
        </script>
        
        
    • (2)、CSS樣式

      • /* #region item */
          li {
            list-style: none;
            height: 36px;
            line-height: 36px;
            padding: 0 5px;
            border-bottom: 1px solid #ddd;
          }
        
          li label {
            float: left;
            cursor: pointer;
          }
        
          li label li input {
            vertical-align: middle;
            margin-right: 6px;
            position: relative;
            top: 1px;
          }
        
          li button {
            float: right;
            display: none;
            margin-top: 3px;
          }
        
          li:before {
            content: initial;
          }
        
          li:last-child {
            border-bottom: none;
          }
        
        /* #endregion */
        
        

運行效果如下:

  • image
  • 自此:靜態組件編程就搞定,後續就可以做數據的動態綁定和交互這些了
  • 將純HTML + CSS + JS轉成組件化開發也是一樣的套路,流程如下:
    • 1、把整個HTML結構放到App.vue組件的template模板中
    • 2、把所有的CSS放到App.vue組件的style中
    • 3、有JS的話,那麼把js文件創好,然後引入到App.vue組件中
    • 4、開始去分析結構,然後拆分成組件,之後把對應的內容放到對應組件中,最後做後續的動態數據綁定、交互即可

2.3.8.3、動態數據綁定

按照分析,要展示的數據是一堆數據,而數據顯示的地方是NameList,所以:data數據就放到NameList中去。本實例中數據放到這裡有坑啊,但是:先這麼放,後續遇到坑了再調整,所以改一下源碼

  • image
  • 數據是弄好了,但是:真正展示數據的是NameObj組件來顯示出來的,而NameObj是NamelList的子組件( 這就是:父傳子 ),這種就需要藉助props配置項( 從外向內傳嘛 ),所以:開始操作

1、父傳子

  • image
  • image
  • image

2、獲取輸入的內容並添加到顯示的頂部 —— 和後續知識掛鉤的重點來了

  • 輸入框組件是App組件的子組件,而數據展示是NameList組件中的NameObj組件,即:現在關係就是如下的樣子
    • image
    • 上面這種就是不同組件之間的通訊情況,現在來了解原生的解決辦法

3、原生的不同組件通訊

  • App.vue是大哥,用它做文章,這樣就變成了App這個父組件和輸入框以及Namelist這兩個子組件之間的關係了
  • 第一步:將data搬到App組件中去並傳給NameList數據展示組件
    • image
    • 然後NameList數據顯示區再傳給數據顯示組件
      • image
      • image
    • 這樣父傳子、子傳孫….這樣就串通一條路了( 即:下圖右邊部分 )
      • image
  • 第二步:將輸入框組件中的數據收集起來,並傳給父組件( 這就是子傳父 )
    • image
    • image
    • 將數據傳給父組件 —— 開始子傳父
    • 子傳父的技巧就是:父組件傳給子組件一個函數( props的變樣版,傳的不是對象、key-value,而是整一個函數傳過去 )、然後子組件利用props接收到函數之後調用一下( 把傳遞數據當做參數 ),那麼父組件中的函數的參數就是要傳遞的數據了
      • image
      • image
      • 上圖id是使用random()生成的隨機數啊,這有弊端的,數字有窮盡的時候,所以:嚴格來說用uuid、身份證號、電話號碼……作為id最好
      • image

4、將子組件傳遞的數據添加到數據欄的頂部去

  • image
  • image

2.3.8.4、交互編寫

1、實現選擇和數據的改變

  • image
  • 1)、最簡單粗暴的方式 —— 但是:不建議用
    • image
    • image
    • image
    • 成功是成功了,但是:說了不建議用,因為:這種方式違背了Vue的設計( 但是:開發中又喜歡用,簡單實用嘛 )
      • 違背了Vue的設計是因為:props在底層是被檢測了的,Vue不支援去修改它的東西,但是:按上述的方式做了之後,卻會發現:並不會報錯,這是因為:修改的是值和對象的區別
      • image
  • 2)、按照邏輯老實編寫
    • 這種實現方式的思路就是:在頁面中點擊 / 改變選擇框時,拿到這條數據的id,然後利用id去所有的數據中遍歷,找到對應的這條數據,最後改變這條數據isCheck的值即可
    • 下面的操作也是一個父傳子的使用過程
      • 這裡有一句話:數據在哪裡,對數據的操作( methods )就在哪裡
      • image
      • image
      • image
      • image

2、實現每條數據的刪除功能

  • 1)、先把樣式解開,讓滑鼠懸浮時刪除按鈕可見
    • image
    • image
  • 2)、實現數據與刪除按鈕交互( 還是子傳父的套路 )
    • 實現邏輯簡單:拿到要刪除數據的id,然後去眾多數據中把要刪除數據給過濾掉不顯示即可( 邏輯刪除 )
    • image
    • image
    • image
    • image

3、實現底部的已選擇人數和總計人數功能

3.1、最原生的方式

  • 1)、父傳子 —— 傳遞persons這個數據
    • image
    • image

3.2、使用數組的reduce()這個API,這個API專做數據統計的

  • 認識一下reduce()
    • image
    • image
    • reduce()最終的返回值是:程式執行的最後一次的nextValue值
  • 使用reduce()實現功能
    • image
    • image

接下來就只剩下底部的全選和清除已選這兩個功能了

4、實現全選交互和清除已選人員功能

4.1、實現全選交互( 子傳父 + 計算屬性使用技巧 )

  • image
  • image
  • image
  • image
  • image
  • 上面這個全選使用分步利用子傳父實現每一步也是可以的,只是有點複雜罷了

4.2、清除已選人員

  • 這個的思路更簡單了,就是查看頁面中的數據哪些的isCheck為true,然後過濾掉這些數據即可( 實現方式一樣簡單,也是子傳父 )
  • image
  • image

2.3.8.5、組件化開發流程總結

組件化編寫流程

  • 1、拆分靜態組件:組件要按照功能點拆分,命名不要和HTML元素名衝突
  • 2、實現動態組件:考慮好數據的存放位置,數據是一個組件在用,還是一些組件在用
    • 一個組件在用:把數據放到組件自身即可
    • 一些組件在用:把數據放到它們共同的父組件上【 這也叫狀態提升 】
  • 3、實現交互:從綁定事件開始

props配置總結

  • 適用於以下過程
    • 1、父組件 ——> 子組件 通訊
    • 2、子組件 ——> 父組件 通訊

v-model總結

  • 使用v-model時要切記:v-model綁定的是值,但是:這個值不能是props中傳遞過來的值,因為:props底層被Vue監測了的,不允許修改
    • 註:props中傳過來的若是對象類型的值時,雖然修改”對象中的屬性”時Vue不會報錯,但是:不建議用
2.3.9、下一篇鏈接
Tags: