帶你從Vue入門到進階

系統學習前端Vue框架,筆記記錄於B站的why老師,具體視頻連接,在此感謝老師的悉心授課。個人github筆記地址javascript

歡迎訪問個人博客css

My Blog: https://coderblue.cn/
Github:https://github.com/CoderBleu
My Project:https://coderblue.cn/project/html

初始化Vue

初始Vue

<script src="../js/vue.js"></script>
  <div id="hi">Hello {{name}}</div>
  <div class="movie">
      <ul>
          <li v-for="item in movies">
              {{item}}
          </li>
      </ul>
  </div>
  <script>
      const hi = new Vue({
          el: "#hi",
          data: {
              name: 'Vue.js'
          }
      })

      let movie = new Vue({
          el: '.movie',
          data: {
              movies: ["星際穿越", '大話西遊', '海賊王之黃金城', '復仇者聯盟']
              // 注意:能夠經過movie.movies.push('盜夢空間')
          }
      })
  </script>

簡易計數器

<body>
<!-- view -->
<div id="count">
    <h2>{{counter}}</h2>
    <button v-on:click="add">+</button>
    <button v-on:click="sub">-</button>
    <!-- <button v-on:click="counter++">+</button> <button v-on:click="counter--">-</button> -->
</div>
<ol>
    <li>緣由是你的body中的div中沒有設置id,vue沒有綁定</li>
    <li>解決:body中加 div id="app" </li>
    <li>雙向綁定:view,model,ViewModel</li>
</ol>
</body>
<script> // proxy: model const obj = { counter: 0 } // ViewModel let count = new Vue({ el: "#count", data: obj, methods: { add: function() { this.counter++; }, sub: function(){ this.counter--; } } }) </script>

Vue中的MVVM

MVVM簡介
  MVVM 是Model-View-ViewModel 的縮寫,它是一種基於前端開發的架構模式,其核心是提供對View 和 ViewModel 的雙向數據綁定,這使得ViewModel 的狀態改變能夠自動傳遞給 View,即所謂的數據雙向綁定。
  Vue.js 是一個提供了 MVVM 風格的雙向數據綁定的 Javascript 庫,專一於View 層。它的核心是 MVVM 中的 VM,也就是 ViewModel。 ViewModel負責鏈接 View 和 Model,保證視圖和數據的一致性,這種輕量級的架構讓前端開發更加高效、便捷。前端

MVVM模型
vue

MVVM拆開來即爲Model-View-ViewModel,有View,ViewModel,Model三部分組成。java

  • View層:是視圖、模版,負責將數據模型轉化爲UI展示出來。
  • Model層:是模型、數據,能夠在Model層中定義數據修改和操做的業務邏輯。
  • ViewModel層:View層和Model層並無直接聯繫,而是經過ViewModel層進行交互。ViewModel層經過雙向數據綁定將View層和Model層鏈接了起來,使得View層和Model層的同步工做徹底是自動的。

Vue.js中mvvm的體現
  Vue.js的實現方式,對數據(Model)進行劫持,當數據變更時,數據會出發劫持時綁定的方法,對視圖進行更新。
node

實例分析以下:
react

Vue.js關於雙向數據綁定的一些實現細節webpack

  vue是採用Object.defineProperty的getter和setter,並結合觀察者模式來實現數據綁定的。當把一個普通的javascript對象傳給Vue實例來做爲它的data選項時,Vue將遍歷它的屬性,用Object.defineProperty將它們轉爲getter/setter。用戶看不到getter/setter,可是在內部它們讓Vue追蹤依賴。在屬性被訪問和修改時通知變化。
git

  • Observer至關於Model層觀察vue實例中的data數據,當數據發生變化時,通知Watcher訂閱者。
  • Compile指令解析器位於View層,初始化View的視圖,將數據變化與更新函數綁定,傳給Watcher訂閱者。
  • Watcher是整個模型的核心,對應ViewModel層,鏈接Observer和Compile。全部的Watchers存於Dep訂閱器中,Watcher將Observer監聽到的數據變化對應相應的回調函數,處理數據,反饋給View層更新界面視圖。
  • Dep消息訂閱器,內部維護了一個數組,用來收集訂閱者(watcher),數據變更觸發notify函數,再調用訂閱者的update方法。

基本模板語法

<!DOCTYPE html>
<html lang="zh-CN">
<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>Document</title>
  <script src="../js/vue.js"></script>
  <style> [v-cloak] { display: none !important; } </style>
</head>

<body>
  <div id="app">
    <!-- 讓未被編譯的從新編譯加載 Vue加入了延緩響應的指令v-cloak,在與css:[v-cloak] { display: none } 的配合下, 能夠隱藏未編譯 Mustache 的標籤直到實例準備完畢,v-cloak屬性纔會被自動去除,對應的標籤也纔可見了 -->
    <h4 >Hello {{count}}</h4>
  </div>

  <script> setTimeout(() => { let app = new Vue({ el: '#app', data: { count: 'v-cloak' }, methods: { }, beforeMount () { alert("有趣"); } }); }, 1000); </script>
</body>

</html>

動態綁定屬性

class的綁定

傳給 v-bind:class 一個對象,以動態地切換 class(語法糖:’:表示’)

根據isActive的true,false變化,動態綁定單個class

<div :class="{ active: isActive==true }"></div>

計算屬性的方式綁定class

<div :class="classObject"></div>
data: {
  isActive: true,
  error: null
},
computed: {
  classObject: function () {
    return {
      active: this.isActive && !this.error,	//isActive爲true,且error不爲null
      'text-danger': this.error && this.error.type === 'fatal'
        //error爲null且this.error.type === 'fatal'
    }
  }
}

數組的方式綁定class

<div v-bind:class="[activeClass, errorClass]"></div>
data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}
渲染爲:
<div class="active text-danger"></div>

三元表達式動態切換class(推薦)

<div :class="[isActive ? activeClass : '', errorClass]"></div>

style的綁定

v-bind:style 的對象語法十分直觀——看着很是像 CSS,但實際上是一個 JavaScript 對象。CSS 屬性名能夠用駝峯式 (camelCase) 或短橫線分隔 (kebab-case,記得用單引號括起來) 來命名:

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
  activeColor: 'red',
  fontSize: 30
}

直接綁定到一個樣式對象一般更好,這會讓模板更清晰:

<div :style="styleObject"></div>
data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

v-bind:style 的數組語法能夠將多個樣式對象應用到同一個元素上:

<div v-bind:style="[baseStyles, overridingStyles]"></div>

計算屬性

<body>
  <div id="app">
    <h3> {{fullName}}</h3>
  </div>

  <script> let app = new Vue({ el: '#app', data: { fisrtName: 'Lom', lastName: 'Name' }, computed: { // fullName: function(){ // return this.fisrtName + ' ' + this.lastName; // } // fullName: { // get: function () { // return this.fisrtName + ' ' + this.lastName; // } // } // 底層方法 fullName: { set: function (newValue) { console.log("---------", newValue); //split() 方法用於把一個字符串分割成字符串數組。 const value = newValue.split(' '); this.fisrtName = value[0]; this.lastName = value[1]; }, get: function () { return this.fisrtName + ' ' + this.lastName; } } } }); </script>
</body>

計算屬性和methods的對比

<body>
  <div id="app">
    <!-- 1. 經過字符串直接拼接,不美觀 -->
    <h3> {{fisrtName}} {{lastName}}</h3>
    <!-- 2. 經過方法:調用方法一次就從新執行一次 -->
    <h3> {{getFullName()}}</h3>
    <!-- 3. 經過計算屬性:執行一次後,會將此緩存起來,後面在調用會直接有結果顯示,不會頻繁調用 -->
    <h3> {{fullName}}</h3>
  </div>

  <script> const app = new Vue({ el: '#app', data: { fisrtName: 'Lom', lastName: 'Name' }, methods: { getFullName: function(){ console.log("fullName"); return this.fisrtName + ' ' + this.lastName; } }, computed: { fullName: { get: function () { console.log("fullName"); return this.fisrtName + ' ' + this.lastName; } } } }); </script>
</body>

事件監聽

v-on的基本使用

<body>
    <div id="app">
        <h3>{{counter}}</h3>
        <!-- v-on語法糖使用:@ -->
        <button @click="increment">+</button>
        <!-- 不須要傳參數,故函數的雙括號能夠省略 -->
        <button v-on:click="decrement()">-</button>
    </div>

    <script> let app = new Vue({ el: '#app', data: { counter: 0 }, methods: { increment(){ this.counter++ }, decrement(){ this.counter-- } } }); </script>
</body>

v-on的參數問題

<body>
    <div id="app">
        <!-- 不須要傳參數,故函數的雙括號能夠省略 -->
        <button @click="btn0Click">+</button>
        <!-- 不傳參數,會顯示event對象 -->
        <button @click="btn1Click()">+</button>
        <!-- 帶括號傳參數 -->
        <button @click="btn2Click(value)">+</button>
        <!-- 定義方法時,咱們既須要傳參數,又須要獲取到event對象,能夠經過$event得到event對象 -->
        <button @click="btn3Click(value, $event)">+</button>
    </div>

    <script> let app = new Vue({ el: '#app', data: { value: 13 }, methods: { btn0Click() { console.log("btn1Click"); }, btn1Click() { console.log("======", event); }, btn2Click(value) { console.log("------", value); }, btn3Click(value, event) { console.log("+++++", value, event); } }, }); </script>
</body>

v-on的修飾符

<body>
  <div id="app">
    <!-- 1. .stop修飾符的使用:阻止單擊事件繼續傳播給後續定義的函數 -->
    <div @click="divClick">
      <button @click.stop="btnClick">點擊</button>
    </div>

    <!-- 2. .prevent修飾符的使用:阻止事件的自帶默認行爲, -->
    <form action="">
      <input type="submit" value="提交" @click.prevent="submitClick"></button>
    </form>

    <!-- 3. .keyup修飾符的使用:當鍵盤被釋放時觸發事件 -->
    <input @keyup="keyup">鬆開</input>
    <br>
    <input @click.keyup="keyup">鼠標點擊鬆開</input>

    <br>
    <!-- 3. .enter修飾符的使用:當鍵盤按下回車鍵觸發事件 -->
    <input @keyup.enter="enter">回車</input>

    <br>
    <!-- 4. .once修飾符的使用: 只能觸發一次回調 -->
    <button @click.once="once">只能點擊一次</button>
  </div>
    
  <script> let app = new Vue({ el: '#app', data: { }, methods: { btnClick(){ console.log("btnClick") }, divClick(){ console.log("divClick") }, submitClick(){ console.log("submitClick") }, keyup(){ console.log("keyup") }, enter(){ console.log("enter") }, once(){ console.log("once") } } }); </script>
</body>

條件判斷

v-if的基本使用

<body>
  <div id="app">
    <!-- 從控制檯輸入 app.isShow = false就能夠將其隱藏 -->
    <h2 v-if="isShow">
        <div>abc</div>
        <div>abc</div>
        <div>abc</div>
        <div>abc</div>
        <div>abc</div>
        {{message}}
    </h2>
  </div>

  <script> let app = new Vue({ el: '#app', data: { message: '哈哈', isShow: true }, methods: { } }); </script>
</body>

v-if和v-else的使用

<body>
  <div id="app">
    <!-- 從控制檯輸入 app.isShow = false就能夠將其隱藏 -->
    <h2 v-if="isShow">
        <div>abc</div>
        <div>abc</div>
        <div>abc</div>
        <div>abc</div>
        <div>abc</div>
        {{message}}
    </h2>
    <h2 v-else>當v-if爲false的時候,我就開始顯示了,我和v-if要緊連着使用</h2>
  </div>

  <script> let app = new Vue({ el: '#app', data: { message: '哈哈', isShow: true }, methods: { } }); </script>
</body>

v-if和v-else-if和v-else的使用

<body>
  <div id="app">
    <p v-if="score >= 90">優秀</p>
    <p v-else-if="score >= 80">良好</p>
    <p v-else-if="score >= 60">及格</p>
    <p v-else>不及格</p>
  </div>

  <script> let app = new Vue({ el: '#app', data: { score: 99 } }); </script>
</body>

用戶登陸切換的案例

<body>
  <div id="app">
    <span v-if="isShowUserName">
      <label for="userName">用戶名</label>
      <input type="text" id="userName" placeholder="請輸入用戶名">
    </span>
    <span v-else>
      <label for="email">用戶郵箱</label>
      <input type="text" id="email" placeholder="請輸入用戶郵箱">
    </span>
    <button @click="isShowUser">切換類型1</button>
    <button @click="isShowUserName = !isShowUserName">切換類型2</button>
  </div>

  <script> let app = new Vue({ el: '#app', data: { isShowUserName: true }, methods: { isShowUser() { this.isShowUserName = !this.isShowUserName } } }); </script>
</body>

用戶登陸切換的案例(複用的小問題)

<body>
  <div id="app">
    <!-- 注意:Vue在進行DOM渲染時,出於性能考慮,會盡量的複用先前的input框,而不是從新建立元素 如若咱們不須要複用input框,只須要添加一個 key就好,相同的key值纔會複用 -->
    <span v-if="isShowUserName">
      <!-- label中的for指向input後,點擊label能夠將光標聚焦給input框 -->
      <label for="userName">用戶名</label>
      <input type="text" id="userName" placeholder="請輸入用戶名" key="userName">
    </span>
    <span v-else>
      <label for="email">用戶郵箱</label>
      <input type="text" id="email" placeholder="請輸入用戶郵箱" key="userName">
    </span>
    <button @click="isShowUser">切換類型1</button>
  </div>

  <script> let app = new Vue({ el: '#app', data: { isShowUserName: true }, methods: { isShowUser() { this.isShowUserName = !this.isShowUserName, //第二種方式:使用js將input框值清空 document.getElementById("userName").value = ''; document.getElementById("email").value = ''; } } }); </script>
</body>

v-show的使用

<body>
  <div id="app">
    <!-- v-if 和 v-show的區別: v-if爲false的時候,壓根不會存在dom裏面, v-show爲false的時候,只是會增長個行內樣式:display:none; 建議: 若是須要頻繁切換的時候,使用v-show, 只有一次切換時一般使用v-if -->
    <span v-if="isShow" id="isShow">V-if</span>
    <span v-show="isShow" id="VShow">V-show</span>
  </div>

  <script> const app = new Vue({ el: '#app', data: { isShow: true }, methods: { } }); </script>
</body>

循環遍歷

v-for遍歷數組

<body>
  <div id="app">
    <!-- 1.簡單遍歷數組 -->
    <ul>
      <li v-for="item in movies">{{item}}</li>
    </ul>
    <br>
    <!-- 2.帶索引遍歷數組 -->
    <ul>
      <li v-for="(item, index) in movies">{{index + 1}}.{{item}}</li>
    </ul>
    <br>
    <!-- 3.帶索引遍歷數組,且添加監聽事件 -->
    <ul>
      <li v-for="(item, index) in movies" @click="showIndex(index)">{{index + 1}}.{{item}}</li>
    </ul>
  </div>

  <script> const app = new Vue({ el: '#app', data: { movies: ['海王', '大話西遊', '星際爭霸', '三傻大鬧寶萊塢'] }, methods: { showIndex(index) { console.log('第' + (index + 1) + '個值'); } } }); </script>
</body>

v-for遍歷對象

<body>
  <div id="app">
    <!-- 1.在遍歷對象的過程當中,若是隻是得到一個值,那麼獲取到的就是整個對象的value值 -->
    <ul>
      <li v-for="item in obj">{{item}}</li>
    </ul>
    <br>
    <!-- 2.在遍歷對象的同時附帶格式:value和key (value, key) -->
    <ul>
      <li v-for="(item, key) in obj">{{item}}--{{key}}</li>
    </ul>
    <br>
    <!-- 3.在遍歷對象的同時附帶格式:value和key和index (value, key, index) -->
    <ul>
      <li v-for="(value, key, index) in obj">{{value}}--{{key}}--{{index+1}}</li>
    </ul>
    <br>
  </div>

  <script> const app = new Vue({ el: '#app', data: { obj: { id: 12, name: 'Luck', height: 1.78 } }, methods: { } }); </script>
</body>

v-for使用過程添加key

<body>
  <!-- splice() 方法向/從數組中添加/刪除項目,而後返回被刪除的項目。 1.app.obj.splice(4) 去除數組第五個值 2.app.obj.splice(3,0,'F') 在數組第四個值的時候添加F -->
  <div id="app">
    <ul>
      <!-- 不添加key屬性: 在數組中間插入數據,須要將插入位置後面的值都後移,效率慢 -->
      <!-- 添加key屬性:相似於鏈表同樣,我插入中間只須要將兩端值的指向指給我,效率高-->
      <li v-for="item in obj" :key="item">{{item}}</li>
    </ul>
  </div>

  <script> const app = new Vue({ el: '#app', data: { obj: ['A', 'B', 'C', 'D', 'E'] }, methods: { } }); </script>
</body>

哪些數組的方法是響應式的

<body>
  <div id="app">
    <ul>
      <li v-for="item in obj" :key="item">{{item}}</li>
    </ul>
    <button @click="btnClick">點擊</button>
  </div>

  <script> let myDate = new Date(); const app = new Vue({ el: '#app', data: { obj: ['R', 'C', 'B', 'D', 'E'], }, methods: { btnClick() { //1.push()方法 // this.obj.push('F') //2.pop()方法,從末尾依次刪除 // this.obj.pop() //3.shift()方法,從頭開始依次刪除 // this.obj.shift() //4.unshift(),從頭開始依次添加元素 // this.obj.unshift('G','L') //5.splice(),方法向/從數組中添加/刪除/替換元素,而後返回被刪除的項目。 //this.obj.splice(4) //去除數組第五個值 //6.sort(),對數組的元素進行排序 // this.obj.sort() //7.reverse(),對數組進行反轉 // this.obj.reverse() //注意:經過索引值修改數組中的元素方法,不可取,vue不會幫我渲染修改後的值 // this.obj[0] = 'M' //解決方法: //刪除第五個 // this.obj.splice(3, 4, 'N') //①:在第三個位置修改爲 N // this.obj.splice(2, 1, 'N') //②:建議:Vue自帶的修改方法 Vue.set(this.obj, 2, 'G') } } }); </script>
</body>

對遍歷出的列表,點擊後對應的能變紅

<body>
  <div id="app">
    <ul>
      <!-- 對遍歷出來的結果列表,我能實現點擊列表對應的值,能讓它變成紅色 -->
      <li v-for="(item, index) in obj" :key="item" :class="{active: currentIndex === index}" @click="changeColor(index)">{{item}}</li>
    </ul>
  </div>

  <script> let myDate = new Date(); const app = new Vue({ el: '#app', data: { obj: ['R', 'C', 'B', 'D', 'E'], currentIndex: 0 }, methods: { changeColor(index) { this.currentIndex = index; } } }); </script>
</body>

書籍購物車案例

此案例包含JavaScript的高階函數用法

// javascript的高階函數:
const nums = [10, 20, 30, 40, 50];

// 編程式範式:命令式編程/聲明式編程
// 編程式編程(第一公民:對象),函數式編程(第一公民:函數)
// filter/map/reduce高階函數
// ①:
// filter高階函數的使用: 它的回調函數有一個要求,必須返回一個boolean值
// true:當返回true時,函數內部會自動將此次回調的n加入到新的數組中去
// false:當返回false時,函數內部會過濾掉此次的 n
let newNum1 = nums.filter(function (n) {
  return n < 100;
})
console.log('newNum1==filter==' + newNum1);

// ②:
// map高階函數的使用
let newNum2 = newNum1.map(function (n) {
  return n * 2;
})
console.log('newNum2==map==' + newNum2);
let newNum21 = nums.map(function (n) {
  // 判斷條件無效,輸出結果與上面同樣,看來仍是須要filter來過濾
  if (n < 80) {
    return n * 2;
  } else {
    return;
  }
})
console.log('newNum21==map==' + newNum21);

// ③:
//6.reduce高階函數的使用: 對數組中的全部數據進行彙總(相加,相乘......)
let total = newNum2.reduce(function (preValue, value) {
  return preValue + value;
}, 0)
console.log('total===' + total);
// 長度爲 5
// preValue value
//第一次: 0 20
//第二次: 20 40
//第三次: 60 60
//第四次: 120 80
//第五次: 200 100
//輸出 300

//④:將上面三個函數綜合起來使用:
let sum = nums.filter(function (n) {
  return n < 50
}).map(function (n) {
  return n * 2
}).reduce(function (preValue, value) {
  return preValue + value
}, 0)
console.log('sum===' + sum);

//⑤:使用箭頭函數將上面三個函數綜合起來使用(相似lombda表達式)
let sum1 = nums.filter(n => n < 50).map(n => n * 2).reduce((pre, value) => pre + value);
console.log('sum1===' + sum1);

index.html

<!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>Document</title>
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <div id="app">
    <div v-if="isFull">
      <table>
        <thead>
          <tr>
            <th>編號</th>
            <th>書名</th>
            <th>出版日期</th>
            <th>價格</th>
            <th>數量</th>
            <th>操做</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="(item,index) in books" :class="{changeColor: number == 1}" @mouseenter="change(index)" @mouseleave="remove(index)">
            <td>{{item.id}}</td>
            <td>{{item.name}}</td>
            <td>{{item.date}}</td>
            <!-- 使用函數:<td>{{getfinalPrice(item.price)}}</td> -->
            <!-- 使用過濾器: -->
            <td>{{item.price | showPrice}}</td>
            <td>
              <button @click="subBtn(index)">-</button>
              {{item.count}}
              <button @click="addBtn(index)">+</button>
            </td>
            <td>
              <button v-if="exchange" @click="addItemBtn(index)">新增</button>
              <button v-else @click="removeBtn(index)">移除</button>
              <button @click="changeType()">切換類型</button>
            </td>
          </tr>
        </tbody>
      </table>
      <br>
      <span>總價格:{{showTotalPrice | showPrice}}</span>
    </div>
    <h2 v-else>購物車清空</h2>
  </div>
</body>
<script src="../js/vue.js"></script>
<script src="main.js"></script>

</html>

main.js

const app = new Vue({
  el: '#app',
  data: {
    books: [{
        id: 1,
        name: '算法導論',
        date: '2019-2',
        price: 87.21,
        count: 1,
        exchange: true
      },
      {
        id: 2,
        name: 'UNIX編程藝術',
        date: '2019-4',
        price: 97.21,
        count: 2,
        exchange: true
      },
      {
        id: 3,
        name: '編程珠璣',
        date: '2012-2',
        price: 77.21,
        count: 1,
        exchange: true
      },
      {
        id: 4,
        name: '大話西遊',
        date: '2019-7',
        price: 100,
        count: 1,
        exchange: true
      }
    ],
    number: 2,
    exchange: false,
    isFull: true
  },
  computed: {
    showTotalPrice() {
      let totalPrice = 0;
      //1.普通for循環
      // for (let i = 0; i < this.books.length; i++) {
      // totalPrice += this.books[i].price * this.books[i].count
      // }
      // return totalPrice

      //2.index是索引
      // for (let index in this.books) {
      // totalPrice += this.books[index].price * this.books[index].count
      // }

      //3.for of
      // for (let item of this.books) {
      // totalPrice += item.price * item.count
      // }

      //4.利用reduce函數來寫
      return this.books.reduce(function (preValue, book) {
        return preValue + book.price * book.count
      }, 0)

      return totalPrice
    }
  },
  methods: {
    // 行內按鈕操做
    subBtn(index) {
      if (this.books[index].count > 0) {
        this.books[index].count--
      }
    },
    addBtn(index) {
      this.books[index].count++
    },
    removeBtn(index) {
      this.books.splice(index, 1)
      if (this.books.length <= 0) {
        this.isFull = !this.isFull
      }
    },
    // 鼠標移動進區域,改變背景顏色
    change(index) {
      // this
      this.number = 1;
      this.active = ".changeColor{ background-color: #cae6e6}"
    },
    remove(index) {
      this.number = 2
    },
    // 改變按鈕類型
    changeType() {
      this.exchange = !this.exchange
    },
    addItemBtn() {
      const obj = [5, '數值分析', '2018-8', 96.10, 2];
      this.books.push(obj)
    },
    // 格式化價格
    getfinalPrice(price) {
      return '¥' + price.toFixed(2);
    }
  },
  filters: {
    showPrice(price) {
      //.toFixed(2):保留小數點後兩位
      return '¥' + price.toFixed(2);
    }
  }
})

style.css

table {
  border: 1px solid #cccccc;
  /* 消除表格的邊框內距 */
  border-collapse: collapse;
  border-spacing: 0;
  width: 700px;
}
table thead {
  background-color: lightskyblue;
}
/* table tr:hover { background-color: pink; } */
table tr th {
  border: 1px solid #cccccc;
}
table tr td {
  border: 1px solid #cccccc;
  text-align: center;
  padding: 20px;
}
.changeColor {
  background-color: #cae6e6
}

v-model的使用

v-model雙向綁定的基本原理

<body>
  <div id="app">
    <!-- v-model的基本使用 -->
    <input type="text" v-model="message">{{message}}
    <br>
    <!-- v-model的原理: -->
    <!-- 監聽input內容改變事件 -->
    <input type="text" v-on:input="changeInput($event)">
    <input type="text" @input="changeInput">
    <br>
    <!-- 此方法:input中會直接有message的值 , 其中target是事件源-->
    <input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
    <input type="text" :value="message" @input="message = $event.target.value">
  </div>

  <script> const app = new Vue({ el: '#app', data: { message: '你好呀' }, methods: { changeInput(event) { this.message = event.target.value; } } }); </script>
</body>

v-model結合radio單選框使用

<body>
  <div id="app">
    <!-- name要一致,否則選擇一個單選框,再選擇另外一個,以前那個仍是被選中狀態 -->
    <label for="male">
      <input type="radio" name="sex" value="" id="male" v-model="message"></label>
    <label for="female">
      <input type="radio" name="sex" value="" id="female" v-model="message"></label>
    <br>
    {{'你選中的值:' + message}}
  </div>

  <script> const app = new Vue({ el: '#app', data: { message: '' }, methods: { } }); </script>
</body>

v-model結合checkbox多選框使用

<body>
  <div id="app">
    <!-- 單個多選框: 贊成協議示範 -->
    <label for="agreeLisence">
      <input type="checkbox" v-model="isAgree">統一協議
    </label>
    <button :disabled="!isAgree">下一步</button>
    <br>
    <!-- 多個多選框:愛好 -->
    <input type="checkbox" value="" v-model="hobbies"><input type="checkbox" value="" v-model="hobbies"><input type="checkbox" value="rap" v-model="hobbies">rap
    <input type="checkbox" value="打籃球" v-model="hobbies">打籃球
    你選擇的興趣愛好是:{{hobbies}}
  </div>

  <script> const app = new Vue({ el: '#app', data: { isAgree: false, //單選框 hobbies: [] //多選框 }, methods: { } }); </script>
</body>

v-model結合select下拉框使用

<body>
  <div id="app">
    <!-- 下拉框的單個使用 -->
    <select name="demo" v-model="fruit">
      <option value="香蕉">香蕉</option>
      <option value="蘋果">蘋果</option>
      <option value="葡萄">葡萄</option>
      <option value="梨子">梨子</option>
    </select>
    <h3>你選擇的水果是:{{fruit}}</h3>
    <br>
    <!-- 下拉框的多個選中使用: 注意添加multiple,而後選擇多個須要按住ctrl鍵 -->
    <select name="demo" v-model="fruits" multiple>
      <option value="香蕉">香蕉</option>
      <option value="蘋果">蘋果</option>
      <option value="葡萄">葡萄</option>
      <option value="梨子">梨子</option>
    </select>
    <h3>你選擇的水果是:{{fruits}}</h3>
  </div>

  <script> const app = new Vue({ el: '#app', data: { fruit: '香蕉', fruits: [] }, methods: { } }); </script>
</body>

v-model結合v-for使用

<body>
  <div id="app">
    <!-- 單個多選框: 贊成協議示範 -->
    <label v-for="(item, index) in originalHobbies">
      <!-- 若是綁定originalHobbies,點擊下對應的多選框就會消失 -->
      <input type="checkbox" v-model="hobbies" :id="index+1" :value="item">{{item}}
    </label>
    你選擇的興趣愛好是:{{hobbies}}
  </div>

  <script> const app = new Vue({ el: '#app', data: { isAgree: false, //單選框 hobbies: [], //多選框 originalHobbies: ['唱', '跳', 'rap', '打籃球'] }, methods: { } }); </script>
</body>

v-model的修飾符使用

<body>
  <div id="app">
    <!-- 1.lazy修飾符: 懶加載,可讓數據被按下回車失去焦點後纔會更新 -->
    <input type="text" v-model.lazy="message">{{message}}
    <hr>
    <!-- 2.number修飾符:能夠將 只能輸入數字 的類型轉換成String -->
    <input type="number" v-model="age"> 
    <h3>{{age}}--{{typeof age}}</h3>
    <!-- 若是不想轉換成String類型,只要添加 .number -->
    <input type="number" v-model.number="height"> 
    <h3>{{height}}--{{typeof height}}</h3>
    <hr>
    <!-- 3.去掉兩端的空格 -->
    <input type="text" v-model.trim="name"> 
    <!-- 添加多個修飾符只需疊加後面就行,無前後順序要求 -->
    <input type="number" v-model.lazy.number="height"> 
    <h3>{{name}}</h3>
  </div>

  <script> const app = new Vue({ el: '#app', data: { message: '你好呀', age: 0, height: 1, name: '' }, methods: { } }); </script>
</body>

組件化開發

組件化的基本使用

<body>
  <div id="app">
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
  </div>

  <script> // 建立組件構造器 const cpnC = Vue.extend({ // ES6語法:` 號能夠支持內容裏面換行比 ''更好使用 //若是有多個標籤使用,必須有個div包裹起來,不然內容顯示不徹底 template: ` <div> <h2>組件化</h2> <h3>我是,哈哈哈哈</h3> <h3>我是,呵呵呵呵</h3> </div> ` }) // 註冊組件 Vue.component('my-cpn', cpnC) const app = new Vue({ el: '#app', data: { }, methods: { } }); </script>
</body>

全局組件和局部組件

<body>
  <div id="app">
    <cpn></cpn>
    <cpn></cpn>
    <cpn></cpn>
    <cpn></cpn>
  </div>
  <div id="app2">
    <cpn></cpn>
  </div>
  <script> const cpnC = Vue.extend({ template: ` <div> <h2>組件化</h2> <h3>我是,哈哈哈哈</h3> <h3>我是,呵呵呵呵</h3> </div> ` }) // 註冊全局組件 Vue.component("cpn", cpnC) const app = new Vue({ el: '#app', data: { }, components: { // 註冊局部組件,即只能在app裏使用cpn這個組件 cpn: cpnC } }); const app2 = new Vue({ el: '#app2', }); </script>
</body>

父組件和子組件

<body>
  <div id="app">
    <cpn2></cpn2>
  </div>
  <script> const cpnC = Vue.extend({ template: ` <div> <h2>子組件</h2> <h3>我是,哈哈哈哈</h3> <h3>我是,呵呵呵呵</h3> </div> ` }) // 父組件:root組件 const cpnC2 = Vue.extend({ template: ` <div> <h2>父組件</h2> <h3>我是,哈哈哈哈</h3> <h3>我是,呵呵呵呵</h3> // 這個子組件須要先註冊 <cpn1><cpn1/> </div> `, components: { cpn1: cpnC } }) // 註冊全局組件 Vue.component("cpn", cpnC) const app = new Vue({ el: '#app', data: { }, components: { // 註冊局部組件,即只能在app裏使用這個組件 cpn1: cpnC, cpn2: cpnC2 } }); </script>
</body>

組件的語法糖註冊方式

<body>
  <div id="app">
    <cpn1></cpn1>
    <cpn2></cpn2>
  </div>
  <script> // const cpnC = Vue.extend() // 語法糖註冊全局組件 Vue.component("cpn1", { template: ` <div> <h2>我是cpn1</h2> <h3>我是,哈哈哈哈</h3> </div> ` }) const app = new Vue({ el: '#app', data: { }, components: { // 語法糖註冊局部組件,即只能在app裏使用這個組件 'cpn2': { template: ` <div> <h2>我是cpn2</h2> <h3>我是,呵呵呵呵</h3> </div> ` } } }); </script>
</body>

組件模塊的分離寫法

<body>

  <div id="app">
    <cpn></cpn>
    <cpn1></cpn1>
  </div>

  <!-- 1.使用script標籤:注意類型須要添加:text/x-template -->
  <script type="text/x-template" id="cpn"> <div> <h2>我是cpn1</h2> <h3>我是,哈哈哈哈</h3> </div> </script>

  <!-- 2.使用template標籤(推薦) -->
  <template id="cpn1">
    <div>
      <h2>我是cpn1</h2>
      <h3>我是,哈哈哈哈</h3>
    </div>
  </template>


  <script> // const cpnC = Vue.extend() // 語法糖註冊全局組件 Vue.component("cpn", { // '#cpn'  template: '#cpn1' }) const app = new Vue({ el: '#app', data: { } }); </script>
</body>

組件中的數據存放問題

<body>

  <div id="app">
    <cpn></cpn>
    <cpn></cpn>
  </div>

  <template id="cpn1">
    <div>
      <h2>我是cpn1</h2>
      <h3>我是,哈哈哈哈</h3>
      <!-- 想要獲取title,必須在組件裏面定義一個函數,且有返回值 -->
      <h3>{{title}}</h3>
    </div>
  </template>


  <script> // const cpnC = Vue.extend() // 語法糖註冊全局組件 Vue.component("cpn", { // '#cpn'  template: '#cpn1', data() { return { title: '好好學習,每天向上' } } }) const app = new Vue({ el: '#app', data: { // 模板裏的title不能獲取到此值 title: '好好學習,每天向上' } }); </script>
</body>

組件中的data爲何必須是函數

<body>

  <div id="app">
    <cpn></cpn>
    <cpn></cpn>
    <hr>
    <cpn1></cpn1>
    <cpn1></cpn1>
  </div>

  <template id="cpn1">
    <div>
      <h3>當前計數:{{count}}</h3>
      <button @click="increment">+</button>
      <button @click="decrement">-</button>
    </div>
  </template>

  <template id="cpn2">
    <div>
      <h3>當前計數:{{count}}</h3>
      <button @click="increment">+</button>
      <button @click="decrement">-</button>
    </div>
  </template>

  <script> // 推薦:count數據不會共享 // 使用data函數:不會引發連鎖反應。即每一個都是個新對象,值地址不同, Vue.component("cpn", { // '#cpn'  template: '#cpn1', data() { return { count: 0 } }, methods: { increment() { this.count++ }, decrement() { this.count-- } } }) // count數據共享 // 都是使用的這個obj常量 const obj = { count: 0 }; Vue.component("cpn1", { // '#cpn'  template: '#cpn2', data() { return obj }, methods: { increment() { this.count++ }, decrement() { this.count-- } } }) const app = new Vue({ el: '#app', }); </script>
</body>

組件通訊-父組件向子組件傳遞數據

<body>
  <div id="app">
    {{movies.toString()}}
    <hr>
    <!-- 添加了前綴v-bind,vue會幫咱們解析movies,不會當成字符串處理 -->
    <cpn v-bind:vmoives="movies" :vmessage="message"></cpn>
    <hr>
    <!-- 當成字符串處理 -->
    <cpn vmoives="movies" vmessage="message"></cpn>
  </div>

  <template id="cpn">
    <div>
      <h2>{{vmessage}}</h2>
      <ul v-for="(item,index) in vmoives">
        <li>{{index}}.{{item}}</li>
      </ul>
    </div>
  </template>

  <script> const cpn = { template: '#cpn', props: ['vmoives', 'vmessage'], //這種實際上是表示變量名字,不能當成字符串 data() { return {} }, methods: { } } // 註冊全局組件 // Vue.component('cpn', cpn) const app = new Vue({ el: '#app', data: { movies: ['海王', '海賊王', '航空母艦'], message: '真香' }, components: { // ES6中的高階寫法,等同於 cpn: cpn cpn } }); </script>
</body>

組件通訊-props用法詳解

<body>
  <div id="app">
    {{movies.toString()}}
    <hr>
    <cpn v-bind:propF="movies" :propC="message"></cpn>
    <hr>
  </div>

  <template id="cpn">
    <div>
      <h2>{{propC}}</h2>
      <ul v-for="(item,index) in propF">
        <li>{{index}}.{{item}}</li>
      </ul>
    </div>
  </template>

  <script> const cpn = { template: '#cpn', props: { // 基礎的類型檢查('null'匹配任何類型) propA: Number, // 多個可能的類型 propB: [String, Number], // 必填的字符串 propC: { type: String, required: true, default: '你好呀' }, // 帶有默認值的數字 propD: { type: Number, default: 100 }, // 注意:類型是對象/數組時,默認值必須是一個函數 // 帶有默認值的對象 propE: { type: Object, default: function () { return { message: 'hello' } } }, // 帶有默認值的對象 propF: { type: Array, default() { return ['大話西遊', '造夢西遊'] } }, // 自定義驗證函數 propG: { validator: function (value) { // 這個值必須匹配下列字符串的一個 return ['success', 'warning', 'danger'].indexOf(value) !== -1 } } }, data() { return {} } } const app = new Vue({ el: '#app', data: { movies: ['海王', '海賊王', '航空母艦'], message: '真香' }, components: { cpn } }); </script>
</body>

組件通訊-父傳子(props不支持駝峯標識)

<body>
  <div id="app">
    <cpn v-bind:prop-f="movies" v-bind:prop-g="message"></cpn>
    <hr>
  </div>

  <template id="cpn">
    <div>
      <h2>{{propG}}</h2>
      <ul v-for="(item,index) in propF">
        <li>{{index}}.{{item}}</li>
      </ul>
    </div>
  </template>

  <script> const cpn = { template: '#cpn', props: { // 帶有默認值的對象 propE: { type: Object, default: function () { return { message: 'hello' } } }, // 帶有默認值的對象 propF: { type: Array, default() { return ['大話西遊', '造夢西遊'] } }, // 自定義驗證函數 propG: { validator: function (value) { // 這個值必須匹配下列字符串的一個:若是要檢索的字符串值沒有出現,則該方法返回 -1。 console.log(['success', 'warning', 'danger'].indexOf(value) !== -1); // 校驗失敗:Invalid prop: custom validator check failed for prop "propG". return ['success', 'warning', 'danger'].indexOf(value) !== -1 } } }, data() { return {} } } const app = new Vue({ el: '#app', data: { movies: ['海王', '海賊王', '航空母艦'], message: 'succe' }, components: { cpn } }); </script>
</body>

組件通訊-父子組件通訊的案例

<body>
  <div id="app">
    <!-- cpnClick在父組件中定義的方法 -->
    <cpn v-on:item-click="cpnClick"></cpn>
    <hr>
    <cpn @item-click="cpnClick($event)"></cpn>
  </div>

  <template id="cpn">
    <div>
      <button v-for="(item,index) in categories" @click="btnClick(item)">{{item.name}}</button>
    </div>
  </template>

  <script> /*步驟: 1.子組件:觸發監聽的事件,好比被點擊了, 2.而後發送自定義事件this.$emit('cpn中的自定義事件名', item) 3.調用Vue中的事件監聽函數,如若在html文件中,不餓能使用駝峯命名自定義函數 */ // 子組件 const cpn = { template: '#cpn', data() { return { categories: [ {id: 'a1', name: '熱門推薦'}, {id: 'a2', name: '手機數碼'}, {id: 'a3', name: '家用家電'}, {id: 'a4', name: '電腦辦公'} ] } }, methods: { btnClick(item) { //發射事件:自定義事件(父組件的cpn中接收此事件的名字) // html不區分大小寫,這裏不能使用駝峯命名 this.$emit('item-click', item) } } } // <!-- 父組件 --> const app = new Vue({ el: '#app', components: { cpn }, methods: { cpnClick(item) { console.log('cpnClick', item); } }, }); </script>
</body>

組件通訊-數字遊戲-1

<body>
  <div id="app">
    <cpn :number1="num1" :number2="num2"></cpn>
  </div>

  <template id="cpn">
    <div>
      <h2>雙向綁定的是num1:{{dnum1}}</h2>
      <h2>props:{{number1}}</h2>
      <input type="text" v-model="dnum1">
      <h2>雙向綁定的是num2:{{dnum2}}</h2>
      <h2>props:{{number2}}</h2>
      <input type="text" v-model="dnum2">
    </div>
  </template>
  <script> const app = new Vue({ el: '#app', data: { num1: 0, num2: 1 }, components: { cpn: { template: '#cpn', props: { number1: Number, number2: Number }, data() { return { /* Property or method "num2" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property 即須要添加data(){} */ dnum1: this.number1, dnum2: this.number2 } } } }, methods: { } }); </script>
</body>

組件通訊-數字遊戲-2

<body>
  <div id="app">
    <!-- number1的值來源與Vue中data的num1 可是在組件中取值要用{{number1}},也就是props對應的值-->
    <cpn :number1="num1" :number2="num2" @change1props="change1props" @change2props="change2props"></cpn>
    <!-- 3.父組件接收傳過來的自定義事件,Vue中的 "change2props"方法 -->
  </div>

  <template id="cpn">
    <div>
      <h2>雙向綁定的是num1:{{dnum1}}</h2>
      <h2>props:{{number1}}</h2>
      <!-- 這方式同 v-model: 監聽input框,調用組件中的 changeInputValue1事件 -->
      <input type="text" :value="dnum1" @input="changeInputValue1">
      <h2>雙向綁定的是num2:{{dnum2}}</h2>
      <h2>props:{{number2}}</h2>
      <input type="text" :value="dnum2" @input="changeInputValue2">
    </div>
  </template>
  <script> const app = new Vue({ el: '#app', data: { num1: 0, num2: 1 }, components: { cpn: { template: '#cpn', props: { number1: Number, number2: Number }, data() { return { dnum1: this.number1, dnum2: this.number2 } }, methods: { changeInputValue1(event) { // 1.將input的值賦值到 dnum1 中去 this.dnum1 = event.target.value; // 2.爲了讓父組件能夠修改值,發送一個事件 this.$emit('change1props', this.dnum1) // 將下面輸入框的props值: 變成1/2 this.dnum2 = this.dnum1 / 2 this.$emit('change2props', this.dnum2) }, changeInputValue2() { this.dnum2 = event.target.value; this.$emit('change2props', this.dnum2) // 將上面輸入框的props值: 變成2倍 this.dnum1 = this.dnum2 * 2 this.$emit('change1props', this.dnum1) } }, } }, methods: { change1props(value) { // 改變 prop 中的num1的值 console.log('dum1' + value); this.num1 = parseFloat(value); }, change2props(value) { console.log('dum2' + value); this.num2 = parseFloat(value); } } }); </script>
</body>

組件通訊-父訪問子-children-refs

<body>
  <div id="app">
    <cpn></cpn>
    <cpn></cpn>
    <cpn ref="refA"></cpn>
    <button @click="btnClick">按鈕</button>
  </div>

  <template id="cpn">
    <div>
      我是子組件
    </div>
  </template>

  <script> const app = new Vue({ el: '#app', data: { message: 'Lemon' }, methods: { btnClick() { // 1. $.chlidren for (let item of this.$children) { console.log(item.name); item.showMessage() } // 2. $.refs: 僅僅會調動帶有refs標示的 // 這樣有時候咱們想即便在新增數據後,依舊能操控它 console.log('refs' + this.$refs.refA.name); } }, components: { cpn: { template: '#cpn', data() { return { name: 'Lemon', id: 1, height: 1.78 } }, methods: { showMessage() { console.log(this.id); } }, } } }); </script>
</body>

組件通訊-子訪問父-parent-root

<body>
  <div id="app">
    <cpn></cpn>
    <hr>
    <ccpn></ccpn>
  </div>

  <template id="cpn">
    <div>我是cpn子組件</div>
  </template>

  <!-- cpn的子組件 -->
  <template id="ccpn">
    <div>
      <h3>我是cpn的子組件</h3>
      <button @click="btnClick">ccpn按鈕</button>
    </div>
  </template>

  <script> const ccpn = Vue.component('ccpn', { template: '#ccpn', methods: { btnClick() { // 按理這個是cpn的子組件,this應該指ccpn對象, // 調用this.$parent時,訪問的是ccpn的父組件cpn,即返回對象是vuecomponents // 調用this.$root時,訪問的才應該是cpn的父組件,即返回對象是vue // 但是我放在cpn的components裏說ccpn沒有註冊 console.log('ccpn=', this.$root.message) } } }) const app = new Vue({ el: '#app', data: { message: 'Lemon' }, methods: { }, components: { cpn: { template: '#cpn', data() { return { name: '我是cpn的name' } }, components: { ccpn } } } }); </script>
</body>

組件化高級

slot-插槽的基本使用

<body>
  <!-- 1.插槽的基本使用:<slot></slot> 2.插槽的默認值:<slot>傳的元素/值:eg 哈哈哈</slot> 3.若是有多個值,同時被放入到組件中進行替換,會一塊兒做爲替換元素 -->
  <div id="app">
    <cpn>嘻嘻嘻</cpn>
    <cpn><button>按鈕</button></cpn>
    <cpn>哈哈哈</cpn>
    <cpn></cpn>
  </div>

  <template id="cpn">
    <div>
      <h3>組件化開發</h3>
      <slot><button>按鈕</button></slot>
    </div>
  </template>

  <script> const app = new Vue({ el: '#app', data: { }, methods: { }, components: { cpn: { template: '#cpn' } } }); </script>
</body>

slot-具名插槽的使用

<body>
  <div id="app">
    <cpn>嘻嘻嘻</cpn>
    <cpn><span slot="right">哈哈哈</span></cpn>
  </div>

  <template id="cpn">
    <div>
      <slot name="left">左邊</slot>
      <slot name="center">中間</slot>
      <slot name="right">右邊</slot>
      <slot>右邊</slot>
    </div>
  </template>

  <script> const app = new Vue({ el: '#app', data: { }, methods: { }, components: { cpn: { template: '#cpn' } } }); </script>
</body>

什麼是編譯的做用域

<body>
  <!-- 總結: 父組件模板的全部東西都會在父級做用域內編譯 子組件模板的全部東西都會在子級做用域內編譯 -->
  <div id="app">
    <!-- 這個裏面的isShow會先從所在模板裏面順下去找,即從Vue裏找尋, 所以Vue中的data的isShow才能影響顯示與否 -->
    <cpn v-show="isShow"></cpn>
  </div>

  <template id="cpn">
    <div>
      <h2>我是組件</h2>
      <!-- 這個裏面的isShowCpn會先從所在模板裏面順下去找,即從cpn裏找尋, -->
      <h3 v-show="isShowCpn">我是哈哈哈</h3>
    </div>
  </template>

  <script> const app = new Vue({ el: '#app', data: { isShow: true }, methods: { }, components: { cpn: { template: '#cpn', data() { return { isShow: false, isShowCpn: true } } } } }); </script>
</body>

做用域插槽的準備

<body>
  <!-- 做用域插槽:父組件替換插槽的標籤,可是內容由子組件來提供 -->
  <div id="app">
    <cpn></cpn>
    <hr>

    <cpn>
      哈哈哈哈·
      <!-- 目標得到子組件中的pLanguages -->
      <template>
        <div slot-scope="slot">
          <span v-for="(item,index) in slot.data">- {{item}} {{item}} - </span>
          <hr>
        </div>
      </template>
    </cpn>
    <hr>
    
    <cpn>
      <!-- 目標得到子組件中的pLanguagesv-slot:todo todo指向slot中的name="todo" -->
      <template v-slot:todo="slotProps">
        <div>
          加join():<span>- {{slotProps.data.join(' - ')}}</span><br>
          <span>{{slotProps.data}}</span>
        </div>
      </template>
    </cpn>
  </div>

  <template id="cpn">
    <div>
      <!-- :名字="cpn中對應須要獲取的值" -->
      <slot :data="pLanguages" name="todo">
        <ul>
          <li v-for="(item,index) in pLanguages">
            {{item}}
          </li>
        </ul>
      </slot>
    </div>
  </template>

  <script> const app = new Vue({ el: '#app', data: { }, methods: { }, components: { cpn: { template: '#cpn', data() { return { pLanguages: ['Java', 'C', 'C++', 'Python', 'C#'] } } } } }); </script>
</body>

前端模塊化

ES模塊化的實現

aaa.js

var name = '小紅'
let age = 18
var flag = true

function sum(num1, num2) {
  return num1 + num2
}

if (flag) {
  console.log(sum(200, 300));
}

export {
  flag, sum
}

bbb.js

var name = '小紅'
var flag = false

// var name = '小明'
// let age = 18
// var flag = true

// function sum(num1, num2) {
// return num1 + num2
// }

// if (flag) {
// console.log(sum(20, 30));
// }

// export {
// flag, sum
// }

index.html

<!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>Document</title>
</head>
<body>
  <!-- 出現了跨域的問題,可是原理基本這樣 -->
  <script src="aaa.js" type="module"></script>
  <script src="bbb.js" type="module"></script>
  <script src="mmm.js" type="module"></script>
</body>
</html>

mmm.js

import {flag} from "./aaa.js";

if (flag) {
  console.log('小明是天才,哈哈哈哈');
}

webpack使用

webpack的起步

info.js

export const name = 'why'
export const age = 18
export const height = 1.78

main.js

// 1.使用commonjs的模塊化規範
const {add, mul} = require('./mathUtils.js')

console.log(add(20, 30));
console.log(mul(25, 30));

// 2.使用ES6的模塊化的規範
import {name, age, height} from "./info";

console.log(name);
console.log(age);
console.log(height);

mathUtils.js

function add(num1, num2) {
  return num1 + num2
}

function mul(num1, num2) {
  return num1 * num2
}

module.exports = {
  add,
  mul
}

index.html

<!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>Document</title>
</head>
<body>
  <!-- PS D:\Web_Study\vue.js學習> cd 12-webpack使用\1-webpack的起步 PS D:\Web_Study\vue.js學習\12-webpack使用\1-webpack的起步> webpack ./src/main.js ./dist/bundle.js -->
    <!-- 生成文件的位置 -->
  <script src="./dist/bundle.js"></script>
</body>
</html>

webpack的配置

webpack.config.js

const path = require('path')

// npm init; npm install

module.exports = {
  entry: './src/main.js',
  output: {
    // 動態獲取路徑:resolve拼接地址
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
}

package.json

{
  "name": "meetwebpack",
  "version": "1.0.0",
  "description": "",
  "main": "webpack.config.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^3.6.0"
  }
}

webpack配置loader和vue

webpack.config.js

const path = require('path')

// npm init; npm install

module.exports = {
  entry: './src/main.js',
  output: {
    // 動態獲取路徑:resolve拼接地址
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    // 能夠顯示加載後的圖片
    // publicPath: '/dist'
    publicPath: 'dist/'
  },
  module: {
    rules: [{
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      }, {
        test: /\.less$/,
        use: [{
          loader: "style-loader" // creates style nodes from JS strings
        }, {
          loader: "css-loader" // translates CSS into CommonJS
        }, {
          loader: "less-loader" // compiles Less to CSS
        }]
      }, {
        test: /\.(png|jpg|gif)$/,
        use: [{
          loader: 'url-loader',
          options: {
            // 若是limit小於文件大小 * 1024,就會報錯,Cannot find module 'file-loader'
            // 通常配置成8kb
            limit: 8192,
            name: 'img/[name].[hash:8].[ext]'
          }
        }]
      }, {
        test: /\.js$/,
        // 排除
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['es2015']
          }
        }
      }
    ],
  },
}

html

index.html

<!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>Document</title>
</head>

<body>
  <!-- webpack官網:https://www.webpackjs.com/loaders/babel-loader/ 重命名會讓配置正確的出錯,須要從新安裝 如若出現,ERROR in Entry module not found: Error: Can't resolve 'babel-loader' in 'D:\Web_Study\vue.js學習\12-webpack使用\4-webpack配置vue' 請 cnpm install babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env webpack 0. 動態獲取webpack的dist配置路徑 //入口 entry: './src/main.js', //輸出 output: { // 動態獲取路徑:resolve拼接地址 path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', }, 使用 cnpm run build 啓動方式配值: 1.安裝本地webpack 2.在package.json文件中的script添加 "build": "webpack" 3.而後輸入cnpm run build,就會在webpack.config.js中根據動態路徑建立 4.加載css文件:cnpm install --save-dev css-loader 5.解析加載進去的css文件:cnpm install --save-dev style-loader 6.在webpack.config.js中的module.exports = {}中添加 module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, ], }, 7.導入less文件,並轉換成css文件:cnpm install --save-dev less-loader less 8.在webpack.config.js中的module.exports = {}中添加 module: { rules: [{ test: /\.less$/, use: [{ loader: "style-loader" // creates style nodes from JS strings }, { loader: "css-loader" // translates CSS into CommonJS }, { loader: "less-loader" // compiles Less to CSS }] }] } 9.加載圖片:cnpm install --save-dev url-loader 10.{ test: /\.(png|jpg|gif)$/, use: [ { loader: 'url-loader', options: { // 若是limit小於文件大小 * 1024,就會報錯,Cannot find module 'file-loader' // 若是要加載的圖片小於8kb,就會將加載的圖片轉換成base64 // 通常配置成8kb limit:8192 } } ] } 11.404 會把圖片發佈在dist文件夾裏,可是咱們css文件中仍是在找img裏的圖片 GET file:///D:/Web_Study/vue.js%E5%AD%A6%E4%B9%A0/12-webpack%E4%BD%BF%E7%94%A8/4-webpack%E7%9A%84lcss%E6%A0%B7%E5%BC%8F- less%E6%96%87%E4%BB%B6/c67dcb9e8b50af7c2550f6da0c40f7e0.jpg net::ERR_FILE_NOT_FOUND 12. 解決方法:能夠顯示加載後的圖片 publicPath: '/dist' 在webpack.config.js中的output裏添加 // 能夠顯示加載後的圖片 publicPath: '/dist' 13. 讓加載後的圖片在dist裏的指定目錄: 圖片名字 img文件夾/[name]此爲變量名/hash爲32位,截取8位/ext:拓展名 注意:若只寫name就是固定值,即一直都是這個名字 name: 'img/[name].[hash:8].[ext]' 14. 顯示圖片:publicPath: 'dist/' 15. ES6語法 轉換成 ES5語法: ①:cnpm install --save-dev babel-loader@7 babel-core babel-preset-es2015 ②:配置文件中添加: module: { rules: [ { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } } ] } -->
  <script src="./dist/bundle.js"></script>
</body>

</html>

js

main.js

// 1.使用commonjs的模塊化規範
const {add, mul} = require('./js/mathUtils.js')

console.log(add(20, 30));
console.log(mul(25, 30));

// 2.使用ES6的模塊化的規範
import {name, age, height} from "./js/info";

console.log(name);
console.log(age);
console.log(height);

// 3.依賴css文件
require('./css/normal.css')

// 4.依賴less文件
require('./css/special.less')
document.writeln('<h2>你好呀,李銀河</h2>')

info.js

export const name = 'why'
export const age = 18
export const height = 1.78

mathUtils.js

function add(num1, num2) {
  return num1 + num2
}

function mul(num1, num2) {
  return num1 * num2
}

module.exports = {
  add,
  mul
}

css

normal.css

body {
  /* background: mediumaquamarine; */
  background: url("../img/timg1.jpg")
}

special.less

@fontSize: 50px;
@fontColor: orange;

body {
  font-size: @fontSize;
  color: @fontColor
}

模塊展現

vue-cli3使用

App.vue

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'app', components: { HelloWorld } } </script>

<style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>

HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
  </div>
</template>

<script> export default { name: 'HelloWorld', props: { msg: String } } </script>

<style scoped> h3 { margin: 40px 0 0; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style>

main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  // 使用的是runtime-only
  render: h => h(App),
  // render: h => {
  // return h(App)
  // }
}).$mount('#app')