webpack深入解析

webpack

一. 認(rèn)識webpack

1.1. webpack介紹

什么是webpack娘赴?

  • 這個(gè)webpack還真不是一兩句話可以說清楚的锄禽。
  • 我們先看看官方的解釋:
    • At its core, webpack is a static module bundler for modern JavaScript applications.
  • 別和我說英文倒槐,OK?
    • 從本質(zhì)上來講拍皮,webpack是一個(gè)現(xiàn)代的JavaScript應(yīng)用的靜態(tài)模塊打包工具奠宜。
  • 但是它是什么呢?用概念解釋概念迈窟,還是不清晰私植。
    • 我們從兩個(gè)點(diǎn)來解釋上面這句話忌栅。
    • 模塊打包
      img

前端模塊化:

  • 在學(xué)習(xí)ES6語法的時(shí)候车酣,我已經(jīng)用了大量的篇幅解釋了為什么前端需要模塊化。
  • 而且我也提到了目前使用前端模塊化的一些方案:AMD索绪、CMD湖员、CommonJS、ES6瑞驱。
  • 在ES6之前娘摔,我們要想進(jìn)行模塊化開發(fā),就必須借助于其他的工具唤反,讓我們可以進(jìn)行模塊化開發(fā)凳寺。
  • 并且在通過模塊化開發(fā)完成了項(xiàng)目后,還需要處理模塊間的各種依賴彤侍,并且將其進(jìn)行整合打包肠缨。
  • 而webpack其中一個(gè)核心就是讓我們可能進(jìn)行模塊化開發(fā),并且會(huì)幫助我們處理模塊間的依賴關(guān)系盏阶。
  • 而且不僅僅是JavaScript文件晒奕,我們的CSS、圖片、json文件等等在webpack中都可以被當(dāng)做模塊來使用(在后續(xù)我們會(huì)看到)脑慧。
  • 這就是webpack中模塊化的概念魄眉。

打包如何理解呢?

  • 理解了webpack可以幫助我們進(jìn)行模塊化闷袒,并且處理模塊間的各種復(fù)雜關(guān)系后坑律,打包的概念就非常好理解了。
  • 就是將webpack中的各種資源模塊進(jìn)行打包合并成一個(gè)或多個(gè)包(Bundle)囊骤。
  • 并且在打包的過程中脾歇,還可以對資源進(jìn)行處理,比如壓縮圖片淘捡,將scss轉(zhuǎn)成css藕各,將ES6語法轉(zhuǎn)成ES5語法,將TypeScript轉(zhuǎn)成JavaScript等等操作焦除。
  • 但是打包的操作似乎grunt/gulp也可以幫助我們完成激况,它們有什么不同呢?
  • 我們稍后就會(huì)介紹到膘魄,不要著急乌逐。O(∩_∩)O~~

1.2. 和grunt/gulp的對比

grunt/gulp的核心是Task

  • 我們可以配置一系列的task,并且定義task要處理的事務(wù)(例如ES6创葡、ts轉(zhuǎn)化浙踢,圖片壓縮,scss轉(zhuǎn)成css)
  • 之后讓grunt/gulp來依次執(zhí)行這些task灿渴,而且讓整個(gè)流程自動(dòng)化洛波。
  • 所以grunt/gulp也被稱為前端自動(dòng)化任務(wù)管理工具。

我們來看一個(gè)gulp的task

  • 下面的task就是將src下面的所有js文件轉(zhuǎn)成ES5的語法骚露。

  • 并且最終輸出到dist文件夾中蹬挤。

    const gulp = require('gulp');
    const babel = require('gulp-babel');
    
    gulp.task('js', () =>
        gulp.src('src/*.js')
            .pipe(babel({
                presets: ['es2015']
            }))
            .pipe(gulp.dest('dist'))
    );
    

什么時(shí)候用grunt/gulp呢?

  • 如果你的工程模塊依賴非常簡單棘幸,甚至是沒有用到模塊化的概念焰扳。
  • 只需要進(jìn)行簡單的合并、壓縮误续,就使用grunt/gulp即可吨悍。
  • 但是如果整個(gè)項(xiàng)目使用了模塊化管理,而且相互依賴非常強(qiáng)蹋嵌,我們就可以使用更加強(qiáng)大的webpack了育瓜。

所以,grunt/gulp和webpack有什么不同呢欣尼?

  • grunt/gulp更加強(qiáng)調(diào)的是前端流程的自動(dòng)化爆雹,模塊化不是它的核心停蕉。
  • webpack更加強(qiáng)調(diào)模塊化開發(fā)管理,而文件壓縮合并钙态、預(yù)處理等功能慧起,是他附帶的功能。

1.3. webpack安裝

安裝webpack首先需要安裝Node.js册倒,Node.js自帶了軟件包管理工具npm

查看自己的node版本:

node -v

全局安裝webpack(這里我先指定版本號3.6.0蚓挤,因?yàn)関ue cli依賴該版本)

npm install webpack@3.6.0 -g

后續(xù)才需要,在開發(fā)的目錄中驻子,局部安裝webpack

  • --save-dev`是開發(fā)時(shí)依賴灿意,項(xiàng)目打包后不需要繼續(xù)使用的。
cd 對應(yīng)目錄
npm install webpack@3.6.0 --save-dev

為什么全局安裝后崇呵,還需要局部安裝呢缤剧?

  • 在終端直接執(zhí)行webpack命令,使用的全局安裝的webpack
  • 當(dāng)在package.json中定義了scripts時(shí)域慷,其中包含了webpack命令荒辕,那么使用的是局部webpack

二. webpack起步

2.1. 準(zhǔn)備工作

我們創(chuàng)建如下文件和文件夾:

image

文件和文件夾解析:

  • dist文件夾:用于存放之后打包的文件
  • src文件夾:用于存放我們寫的源文件
    • main.js:項(xiàng)目的入口文件。具體內(nèi)容查看下面詳情犹褒。
    • mathUtils.js:定義了一些數(shù)學(xué)工具函數(shù)抵窒,可以在其他地方引用,并且使用叠骑。具體內(nèi)容查看下面的詳情李皇。
  • index.html:瀏覽器打開展示的首頁html
  • package.json:通過npm init生成的,npm包管理的文件(暫時(shí)沒有用上宙枷,后面才會(huì)用上)

mathUtils.js文件中的代碼:

  • 定義兩個(gè)函數(shù)掉房,并且通過module.exports導(dǎo)出
function add(num1, num2) {
  return num1 + num2
}

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

module.exports = {
  add,
  mul
}

main.js文件中的代碼:

const math = require('./mathUtils')

console.log('Hello Webpack');
console.log(math.add(10, 20));
console.log(math.mul(10, 20));

2.2. js文件的打包

現(xiàn)在的js文件中使用了模塊化的方式進(jìn)行開發(fā),他們可以直接使用嗎朦拖?不可以圃阳。

  • 因?yàn)槿绻苯釉趇ndex.html引入這兩個(gè)js文件厌衔,瀏覽器并不識別其中的模塊化代碼璧帝。
  • 另外,在真實(shí)項(xiàng)目中當(dāng)有許多這樣的js文件時(shí)富寿,我們一個(gè)個(gè)引用非常麻煩睬隶,并且后期非常不方便對它們進(jìn)行管理。

我們應(yīng)該怎么做呢页徐?使用webpack工具苏潜,對多個(gè)js文件進(jìn)行打包。

  • 我們知道变勇,webpack就是一個(gè)模塊化的打包工具恤左,所以它支持我們代碼中寫模塊化贴唇,可以對模塊化的代碼進(jìn)行處理。(如何處理的飞袋,待會(huì)兒在原理中戳气,我會(huì)講解)
  • 另外,如果在處理完所有模塊之間的關(guān)系后巧鸭,將多個(gè)js打包到一個(gè)js文件中瓶您,引入時(shí)就變得非常方便了。

OK纲仍,如何打包呢呀袱?使用webpack的指令即可

webpack src/main.js dist/bundle.js
img

打包后會(huì)在dist文件下,生成一個(gè)bundle.js文件

  • 文件內(nèi)容有些復(fù)雜郑叠,這里暫時(shí)先不看夜赵,后續(xù)再進(jìn)行分析。

bundle.js文件乡革,是webpack處理了項(xiàng)目直接文件依賴后生成的一個(gè)js文件油吭,我們只需要將這個(gè)js文件在index.html中引入即可

img

打開HTML,那么就會(huì)看到我們js文件執(zhí)行的結(jié)果:

img

三. webpack配置

3.1. 入口和出口

我們考慮一下署拟,如果每次使用webpack的命令都需要寫上入口和出口作為參數(shù)婉宰,就非常麻煩,有沒有一種方法可以將這兩個(gè)參數(shù)寫到配置中推穷,在運(yùn)行時(shí)心包,直接讀取呢?

  • 當(dāng)然可以馒铃,就是創(chuàng)建一個(gè)webpack.config.js文件
const path = require('path')

module.exports = {
  // 入口: 可以是字符串/數(shù)組/對象, 這里我們?nèi)肟谥挥幸粋€(gè),所以寫一個(gè)字符串即可
  entry: './src/main.js',
  // 出口: 通常是一個(gè)對象, 里面至少包含兩個(gè)重要屬性, path 和 filename
  output: {
    path: path.resolve(__dirname, 'dist'), // 注意: path通常是一個(gè)絕對路徑
    filename: 'bundle.js'
  }
}

3.2. watch

我現(xiàn)在有一個(gè)更加大膽的想法蟹腾,希望src文件中的js代碼發(fā)生變化時(shí),webpack可以檢測到区宇,并且自動(dòng)重新打包

  • 如果有重新打包娃殖,那么我只需要刷新一下瀏覽器就可以知道我修改代碼后的效果了。
  • 這個(gè)時(shí)候议谷,我可以再添加一個(gè)配置watch
img

重新通過webpack終端執(zhí)行一次命令炉爆,會(huì)看到如下結(jié)果:

  • 你會(huì)發(fā)現(xiàn)這次命令執(zhí)行之后沒有直接結(jié)束,而是出于監(jiān)聽狀態(tài)卧晓。
  • 當(dāng)我們修改了前面的代碼后芬首,會(huì)自動(dòng)重新打包。
  • 可以通過ctrl+c停止監(jiān)聽逼裆。
img

當(dāng)然郁稍,后面我們有更加方便的方式來監(jiān)聽代碼的變化,并且可以實(shí)時(shí)在瀏覽器中查看胜宇。

3.3. 啟動(dòng)webpack方式

目前耀怜,我們使用的webpack是全局的webpack恢着,如果我們想使用局部來打包呢?

  • 因?yàn)橐粋€(gè)項(xiàng)目往往依賴特定的webpack版本财破,全局的版本可能很這個(gè)項(xiàng)目的webpack版本不一致然评,導(dǎo)出打包出現(xiàn)問題。
  • 所以通常一個(gè)項(xiàng)目狈究,都有自己局部的webpack碗淌。

第一步,項(xiàng)目中需要安裝自己局部的webpack

  • 這里我們讓局部安裝webpack3.6.0
  • 因?yàn)榉奖阄覀兒罄m(xù)查看Vue CLI2中的webpack3.6.0配置
  • Vue CLI3中已經(jīng)升級到webpack4抖锥,但是它將配置文件隱藏了起來亿眠,所以查看起來不是很方便。
  • 我們在學(xué)習(xí)階段磅废,先使用webpack3.x即可纳像。
  • 后續(xù)開發(fā)中,我們會(huì)直接使用腳手架拯勉,CLI2和CLI3都會(huì)使用竟趾。
npm install webpack@3.6.0 --save-dev

第二步,通過node_modules/.bin/webpack啟動(dòng)webpack打包

node_modules/.bin/webpack
img

但是宫峦,每次執(zhí)行都敲這么一長串有沒有覺得不方便呢岔帽?

  • OK,我們可以在package.json的scripts中定義自己的執(zhí)行腳本导绷。
img

package.json中的scripts的腳本在執(zhí)行時(shí)犀勒,會(huì)按照一定的順序?qū)ふ颐顚?yīng)的位置。

  • 首先妥曲,會(huì)尋找本地的node_modules/.bin路徑中對應(yīng)的命令贾费。
  • 如果沒有找到,會(huì)去全局的環(huán)境變量中尋找檐盟。

如何執(zhí)行我們的build指令呢褂萧?

npm run build
img

四. loader的使用

目前,webpack幫助我們處理的都是js文件葵萎。

但我們前面提過导犹,webpack可以將js、圖片陌宿、css都當(dāng)成模塊來進(jìn)行處理锡足,下面我們就來學(xué)習(xí)一下如何處理它們。

4.1. css文件處理

4.1.1. css準(zhǔn)備的階段

項(xiàng)目開發(fā)過程中壳坪,我們必然需要添加很多的樣式,而樣式我們往往寫到一個(gè)單獨(dú)的文件中掰烟。

  • 在src目錄中爽蝴,創(chuàng)建一個(gè)css文件沐批,其中創(chuàng)建一個(gè)normal.css文件。
  • 我們也可以重新組織文件的目錄結(jié)構(gòu)蝎亚,將零散的js文件放在一個(gè)js文件夾中九孩。
img

normal.css中的代碼非常簡單,就是將body設(shè)置為red

body {
  background-color: red;
}

但是发框,這個(gè)時(shí)候normal.css中的樣式會(huì)生效嗎躺彬?

  • 當(dāng)然不會(huì),因?yàn)槲覀儔焊蜎]有引用它梅惯。
  • webpack也不可能找到它宪拥,因?yàn)槲覀冎挥幸粋€(gè)入口,webpack會(huì)從入口開始查找其他依賴的文件铣减。

在入口文件中引用:

img

重新打包她君,會(huì)出現(xiàn)如下錯(cuò)誤:

img

這個(gè)錯(cuò)誤,告訴我們要想加載css文件葫哗,必須使用對應(yīng)的loader

4.1.2. 認(rèn)識loader

loader是webpack中一個(gè)非常核心的概念缔刹。

webpack用來做什么呢?

  • 在我們之前的實(shí)例中劣针,我們主要是用webpack來處理我們寫的js代碼校镐,并且webpack會(huì)自動(dòng)處理js之間相關(guān)的依賴。
  • 但是捺典,在開發(fā)中我們不僅僅有基本的js代碼處理灭翔,我們也需要加載css、圖片辣苏,也包括一些高級的將ES6轉(zhuǎn)成ES5代碼肝箱,將TypeScript轉(zhuǎn)成ES5代碼,將scss稀蟋、less轉(zhuǎn)成css煌张,將.jsx、.vue文件轉(zhuǎn)成js文件等等退客。
  • 對于webpack本身的能力來說骏融,對于這些轉(zhuǎn)化是不支持的。
  • 那怎么辦呢萌狂?給webpack擴(kuò)展對應(yīng)的loader就可以啦档玻。

loader使用過程:

  • 步驟一:通過npm安裝需要使用的loader
  • 步驟二:在webpack.config.js中的modules關(guān)鍵字下進(jìn)行配置

目前,我們希望加載和使用.css文件茫藏,就需要使用對應(yīng)的loader

  • 大部分loader我們都可以在webpack的官網(wǎng)中找到误趴,并且學(xué)習(xí)對應(yīng)的用法。
4.1.3. css處理的loader

在webpack的官方中务傲,我們可以找到如下關(guān)于樣式的loader使用方法:

img

OK凉当,按照教程枣申,我們需要先安裝css-loader

npm install --save-dev css-loader

按照官方配置webpack.config.js文件

  • 注意:配置中有一個(gè)style-loader,我們并不知道它是什么看杭,所以可以暫時(shí)不進(jìn)行配置忠藤。

重新打包項(xiàng)目:

img

但是,運(yùn)行index.html楼雹,你會(huì)發(fā)現(xiàn)樣式并沒有生效模孩。

  • 原因是css-loader只負(fù)責(zé)加載css文件,但是并不負(fù)責(zé)將css具體樣式嵌入到文檔中贮缅。
  • 這個(gè)時(shí)候榨咐,我們還需要一個(gè)style-loader幫助我們處理。

我們來安裝style-loader

npm install --save-dev style-loader

將對應(yīng)的style-loader携悯,配置在webpack.config.js中

  • 注意:style-loader需要放在css-loader的前面祭芦。
  • 疑惑:不對吧?按照我們的邏輯憔鬼,在處理css文件過程中龟劲,應(yīng)該是css-loader先加載css文件,再由style-loader來進(jìn)行進(jìn)一步的處理轴或,為什么會(huì)將style-loader放在前面呢昌跌?
  • 答案:這次因?yàn)閣ebpack在讀取使用的loader的過程中,是按照從右向左的順序讀取的照雁。

目前蚕愤,webpack.config.js的配置如下:

const path = require('path')

module.exports = {
  // 入口: 可以是字符串/數(shù)組/對象, 這里我們?nèi)肟谥挥幸粋€(gè),所以寫一個(gè)字符串即可
  entry: './src/main.js',
  // 出口: 通常是一個(gè)對象, 里面至少包含兩個(gè)重要屬性, path 和 filename
  output: {
    path: path.resolve(__dirname, 'dist'), // 注意: path通常是一個(gè)絕對路徑
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [ 'style-loader', 'css-loader' ]
      }
    ]
  }
}

并且,現(xiàn)在再次運(yùn)行index.html就會(huì)發(fā)現(xiàn)我們的樣式生效了饺蚊。

4.2. less文件處理

如果我們希望在項(xiàng)目中使用less萍诱、scss、stylus來寫樣式污呼,webpack是否可以幫助我們處理呢裕坊?

我們這里以less為例孤里,其他也是一樣的愉择。

4.1.2. less準(zhǔn)備的階段

我們還是先創(chuàng)建一個(gè)less文件,依然放在css文件夾中

special.css文件的內(nèi)容:

@fontSize: 50px;
@fontColor: white;

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

為了讓special.css生效黄琼,我們需要在main.js中引入

  • 另外苗缩,這里為了看到樣式有生效饵蒂,還在頁面中寫入了一個(gè)div元素
img

這個(gè)時(shí)候,打包程序酱讶,會(huì)報(bào)如下錯(cuò)誤:

img
4.1.3. less處理的loader

繼續(xù)在官方中查找退盯,我們會(huì)找到less-loader相關(guān)的使用說明

首先,還是需要安裝對應(yīng)的loader

  • 注意:我們這里還安裝了less,因?yàn)閣ebpack會(huì)使用less對less文件進(jìn)行編譯
npm install --save-dev less-loader less

其次得问,修改對應(yīng)的配置文件

  • 添加一個(gè)rules選項(xiàng)囤攀,用于處理.less文件
        {
            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
            }]
        }

4.3. 資源文件處理

4.3.1. 資源準(zhǔn)備階段

首先软免,我們在項(xiàng)目中加入兩張圖片:

  • 一張較小的圖片test01.jpg(小于8kb)宫纬,一張較大的圖片test02.jpeg(大于8kb)
  • 待會(huì)兒我們會(huì)針對這兩張圖片進(jìn)行不同的處理

我們先考慮在css樣式中引用圖片的情況,所以我更改了normal.css中的樣式:

body {
  background-color: red;
  background: url(../imgs/test01.jpeg)
}

如果我們現(xiàn)在直接打包膏萧,會(huì)出現(xiàn)如下問題

img

很簡單漓骚,還是沒有對應(yīng)的加載器

4.3.2. url-loader

圖片處理,我們使用url-loader來處理

依然先安裝url-loader

npm install --save-dev url-loader

修改webpack.config.js配置文件:

     {
        test: /\.(png|jpg|gif|jpeg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192
            }
          }
        ]
      }

這里有一個(gè)limit選項(xiàng)榛泛,我們先寫在這里蝌蹂,待會(huì)兒再來解釋它的作用

再次打包,運(yùn)行index.html曹锨,就會(huì)發(fā)現(xiàn)我們的背景圖片選出了出來孤个。

而仔細(xì)觀察,你會(huì)發(fā)現(xiàn)背景圖是通過base64顯示出來的

img

OK沛简,這也是limit屬性的作用齐鲤,當(dāng)圖片小于8kb時(shí),對圖片進(jìn)行base64編碼

那么問題來了椒楣,如果大于8kb呢给郊?

  • 我們將background的圖片改成test02.jpg

繼續(xù)打包我們的程序,報(bào)錯(cuò)了

img

這次因?yàn)榇笥?kb的圖片捧灰,會(huì)通過file-loader進(jìn)行處理淆九,但是我們的項(xiàng)目中并沒有file-loader

4.3.3. file-loader

當(dāng)圖片大于8kb時(shí),會(huì)使用file-loader進(jìn)行加載毛俏,所以我們需要先安裝file-loader

  • 注:file-loader可以不進(jìn)行rules的配置
npm install --save-dev file-loader

再次打包炭庙,就會(huì)發(fā)現(xiàn)dist文件夾下多了一個(gè)圖片文件

img

我們發(fā)現(xiàn)webpack自動(dòng)幫助我們生成一個(gè)非常長的名字

  • 這是一個(gè)32位hash值,目的是防止名字重復(fù)
  • 但是煌寇,真實(shí)開發(fā)中焕蹄,我們可能對打包的圖片名字有一定的要求
  • 比如,將所有的圖片放在一個(gè)文件夾中唧席,跟上圖片原來的名稱擦盾,同時(shí)也要防止重復(fù)

所以,我們可以在options中添加上如下選項(xiàng):

  • img:文件要打包到的文件夾
  • name:獲取圖片原來的名字淌哟,放在該位置
  • hash:8:為了防止圖片名稱沖突迹卢,依然使用hash,但是我們只保留8位
  • ext:使用圖片原來的擴(kuò)展名
options: {
    limit: 8192,
    name: 'img/[name].[hash:8].[ext]'
}

但是徒仓,我們發(fā)現(xiàn)圖片并沒有顯示出來腐碱,這是因?yàn)閳D片使用的路徑不正確

  • 默認(rèn)情況下,webpack會(huì)將生成的路徑直接返回給使用者
  • 但是,我們整個(gè)程序是打包在dist文件夾下的症见,所以這里我們需要在路徑下再添加一個(gè)dist/

在webpack.config.js中添加如下配置:

  • 這個(gè)配置待會(huì)兒就不再需要了喂走,我們后續(xù)再說,在這里我們必須添加上去谋作。
img

再次打包運(yùn)行程序芋肠,一切正常了。

4.4. babel-loader

如果你仔細(xì)閱讀webpack打包的js文件遵蚜,發(fā)現(xiàn)寫的ES6語法并沒有轉(zhuǎn)成ES5帖池,那么就意味著可能一些對ES6還不支持的瀏覽器沒有辦法很好的運(yùn)行我們的代碼。

在前面我們說過吭净,如果希望將ES6的語法轉(zhuǎn)成ES5睡汹,那么就需要使用babel。

  • 而在webpack中寂殉,我們直接使用babel對應(yīng)的loader就可以了囚巴。

安裝對應(yīng)的babel-loader

npm install --save-dev babel-loader@7 babel-core babel-preset-es2015

配置webpack.config.js文件

      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['es2015']
          }
        }
      }

重新打包,查看bundle.js文件友扰,發(fā)現(xiàn)其中的內(nèi)容變成了ES5的語法

五. 集成Vuejs

后續(xù)項(xiàng)目中彤叉,我們會(huì)使用Vuejs進(jìn)行開發(fā),而且會(huì)以特殊的文件來組織vue的組件焕檬。

所以姆坚,下面我們來學(xué)習(xí)一下如何在我們的webpack環(huán)境中集成Vuejs

5.1. 簡單使用Vuejs

現(xiàn)在,我們希望在項(xiàng)目中使用Vuejs实愚,那么必然需要對其有依賴兼呵,所以需要先進(jìn)行安裝

  • 注:因?yàn)槲覀兒罄m(xù)是在實(shí)際項(xiàng)目中也會(huì)使用vue的,所以并不是開發(fā)時(shí)依賴
npm install vue --save

那么腊敲,接下來就可以按照我們之前學(xué)習(xí)的方式來使用Vue了

我們創(chuàng)建一個(gè)Vue實(shí)例:

import Vue from 'vue'

new Vue({
   el: '#app',
   data: {
     name: 'coderwhy'
   }
 })

上面的Vue實(shí)例中击喂,我們要掛載一個(gè)#app對應(yīng)的元素,所以也要修改index.html

img

修改完成后碰辅,重新打包懂昂,運(yùn)行程序:

  • 打包過程沒有任何錯(cuò)誤(因?yàn)橹皇嵌啻虬艘粋€(gè)vue的js文件而已)
  • 但是運(yùn)行程序,沒有出現(xiàn)想要的效果没宾,而且瀏覽器中有報(bào)錯(cuò)
img

這個(gè)錯(cuò)誤說的是我們使用的是runtime-only版本的Vue凌彬,什么意思呢?

  • 這個(gè)在后續(xù)的課程中我具體說明runtime-compiler和runtime-only的區(qū)別循衰。
  • 這里我只說解決方案:Vue不同版本構(gòu)建
  • 所以我們修改webpack的配置铲敛,添加如下內(nèi)容即可
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js' 
    }
  },

重新打包,運(yùn)行程序:

img

5.2. el和template

正常運(yùn)行之后会钝,我們來考慮另外一個(gè)問題:

  • 如果我們希望將data中的數(shù)據(jù)顯示在界面中伐蒋,就必須是修改index.html
  • 如果我們后面自定義了組件,也必須修改index.html來使用組件
  • 但是html模板在之后的開發(fā)中,我并不希望手動(dòng)的來頻繁修改先鱼,是否可以做到呢俭正?

定義template屬性:

  • 在前面的Vue實(shí)例中,我們已經(jīng)定義了el屬性焙畔,用于和index.html中的#app進(jìn)行綁定掸读,讓Vue實(shí)例之后可以管理它其中的內(nèi)容
  • 這里,我們可以將div元素中的{{message}}內(nèi)容刪掉闹蒜,只保留一個(gè)基本的id為div的元素
  • 但是如果我依然希望在其中顯示{{message}}的內(nèi)容寺枉,應(yīng)該怎么處理呢抑淫?
  • 我們可以再定義一個(gè)template屬性绷落,代碼如下:
new Vue({
  el: '#app',
  template: '<div id="app">{{message}}</div>',
  data: {
    message: 'coderwhy'
  }
})

重新打包,運(yùn)行程序始苇,顯示一樣的結(jié)果和HTML代碼結(jié)構(gòu)

那么砌烁,el和template模板的關(guān)系是什么呢?

  • 在我們之前的學(xué)習(xí)中催式,我們知道el用于指定Vue要管理的DOM函喉,可以幫助解析其中的指令、事件監(jiān)聽等等荣月。
  • 而如果Vue實(shí)例中同時(shí)指定了template管呵,那么template模板的內(nèi)容會(huì)替換掉掛載的對應(yīng)el的模板。

這樣做有什么好處呢哺窄?

  • 這樣做之后我們就不需要在以后的開發(fā)中再次操作index.html捐下,只需要在template中寫入對應(yīng)的標(biāo)簽即可

但是,書寫template模塊非常麻煩怎么辦呢萌业?

  • 沒有關(guān)系坷襟,稍后我們會(huì)將template模板中的內(nèi)容進(jìn)行抽離。
  • 會(huì)分成三部分書寫:template生年、script婴程、style,結(jié)構(gòu)變得非常清晰抱婉。

5.3. Vue組件化開發(fā)

在學(xué)習(xí)組件化開發(fā)的時(shí)候档叔,我說過以后的Vue開發(fā)過程中,我們都會(huì)采用組件化開發(fā)的思想蒸绩。

那么衙四,在當(dāng)前項(xiàng)目中,如果我也想采用組件化的形式進(jìn)行開發(fā)侵贵,應(yīng)該怎么做呢届搁?

查看下面的代碼:

  • 1.定義一個(gè)組件對象
  • 2.在Vue實(shí)例的components中進(jìn)行注冊
  • 3.在Vue實(shí)例的template模板中使用
const App = {
  template: '<h2>{{name}}</h2>',
  data() {
    return {
      name: '我是APP組件'
    }
  }
}

new Vue({
  el: '#app',
  template: `
    <div id="app">
      {{message}}
      <App/>
    </div>`,
  data: {
    message: 'coderwhy'
  },
  components: {
    App
  }
})

重新打包,運(yùn)行程序,組件內(nèi)容正常的顯示出來了卡睦。

img

為了方便維護(hù)我們的App組件宴胧,我們可以將對應(yīng)的代碼單獨(dú)抽離取出

img

代碼依然可以正常運(yùn)行,沒有問題表锻。

5.4. .vue文件封裝處理

但是一個(gè)組件以一個(gè)js對象的形式進(jìn)行組織和使用的時(shí)候是非常不方便的

  • 一方面編寫template模塊非常的麻煩
  • 另外一方面如果有樣式的話恕齐,我們寫在哪里比較合適呢?

現(xiàn)在瞬逊,我們以一種全新的方式來組織一個(gè)vue的組件

<template>
  <h2 class="title">{{name}}</h2>
</template>
<script>
export default {
  name: "App",
  data () {
    return {
      name: '我是.vue的App組件'
    };
  }
}
</script>
<style scoped>
  .title {
    color: blue;
  }
</style>

但是显歧,這個(gè)時(shí)候這個(gè)文件可以被正確的加載嗎?

  • 必然不可以确镊,這種特殊的文件以及特殊的格式士骤,必須有人幫助我們處理。
  • 誰來處理呢蕾域?vue-loader以及vue-template-compiler拷肌。

安裝vue-loader和vue-template-compiler

npm install vue-loader vue-template-compiler --save-dev

修改webpack.config.js的配置文件:

{
    test: /\.vue$/,
    use: ['vue-loader']
}

重新打包,運(yùn)行程序旨巷,你會(huì)發(fā)現(xiàn).vue文件已經(jīng)可以被識別巨缘,并且正確的顯示了出來。

六. plugin的使用

6.1. 認(rèn)識plugin

plugin是什么采呐?

  • plugin是插件的意思若锁,通常是用于對某個(gè)現(xiàn)有的架構(gòu)進(jìn)行擴(kuò)展。
  • webpack中的插件斧吐,就是對webpack現(xiàn)有功能的各種擴(kuò)展又固,比如打包優(yōu)化,文件壓縮等等会通。

loader和plugin區(qū)別

  • loader主要用于轉(zhuǎn)換某些類型的模塊口予,它是一個(gè)轉(zhuǎn)換器。
  • plugin是插件涕侈,它是對webpack本身的擴(kuò)展沪停,是一個(gè)擴(kuò)展器。

plugin的使用過程:

  • 步驟一:通過npm安裝需要使用的plugins(某些內(nèi)置插件不需要安裝)
  • 步驟二:在webpack.config.js中的plugins中配置插件木张。

下面舷礼,我們就來看看可以通過哪些插件對現(xiàn)有的webpack打包過程進(jìn)行擴(kuò)容妻献,讓我們的webpack變得更加好用谨履。

6.2. 添加版權(quán)的Plugin

我們先來使用一個(gè)最簡單的插件笋粟,為打包的文件添加版權(quán)聲明

  • 該插件名字叫BannerPlugin害捕,屬于webpack自帶的插件。

按照下面的方式來修改webpack.config.js的文件:

const path = require('path')
const webpack = require('webpack')

module.exports = {
  ...
  plugins: [
    new webpack.BannerPlugin('最終版權(quán)歸coderwhy所有')
  ]
}

重新打包程序:查看bundle.js文件的頭部东涡,看到如下信息

img

6.3. 打包html的plugin

目前组贺,我們的index.html文件是存放在項(xiàng)目的根目錄下的失尖。

我們知道菇夸,在真實(shí)發(fā)布項(xiàng)目時(shí)庄新,發(fā)布的是dist文件夾中的內(nèi)容,但是dist文件夾中如果沒有index.html文件羞芍,那么打包的js等文件也就沒有意義了荷科。

所以副渴,我們需要將index.html文件打包到dist文件夾中,這個(gè)時(shí)候就可以使用HtmlWebpackPlugin插件

HtmlWebpackPlugin插件可以為我們做這些事情:

  • 自動(dòng)生成一個(gè)index.html文件(可以指定模板來生成)
  • 將打包的js文件将鸵,自動(dòng)通過script標(biāo)簽插入到body中

安裝HtmlWebpackPlugin插件

npm install html-webpack-plugin --save-dev

使用插件草娜,修改webpack.config.js文件中plugins部分的內(nèi)容如下:

  plugins: [
    new webpack.BannerPlugin('最終版權(quán)歸coderwhy所有'),
    new htmlWebpackPlugin({
      template: 'index.html'
    }),
  ]

注意:

  • 這里的template表示根據(jù)什么模板來生成index.html
  • 另外,我們需要?jiǎng)h除之前在output中添加的publicPath屬性移袍,否則插入的script標(biāo)簽中的src可能會(huì)有問題

6.3. js壓縮的plugin

在項(xiàng)目發(fā)布之前葡盗,我們必然需要對js等文件進(jìn)行壓縮處理

這里觅够,我們就對打包的js文件進(jìn)行壓縮

  • 我們使用一個(gè)第三方的插件uglifyjs-webpack-plugin,并且版本號指定1.1.1窘拯,和CLI2保持一致

安裝uglifyjs-webpack-plugin插件:

npm install uglifyjs-webpack-plugin@1.1.1 --save-dev

修改webpack.config.js文件树枫,使用插件:

const path = require('path')
const webpack = require('webpack')
const uglifyJsPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
  ...
  plugins: [
    new webpack.BannerPlugin('最終版權(quán)歸coderwhy所有')
    new uglifyJsPlugin()
  ]
}   

查看打包后的bunlde.js文件,是已經(jīng)被壓縮過了厨喂。

七. 搭建本地服務(wù)

現(xiàn)在我們的環(huán)境有一個(gè)很大的弊端蜕煌,每次修改了代碼都需要手動(dòng)來編譯(當(dāng)然可以通過watch),另外編譯后要刷新頁面才能看到對應(yīng)的效果盒刚。

如果代碼修改后,可以自動(dòng)刷新瀏覽器看到修改后的效果涡上,會(huì)大大提升我們的開發(fā)效率吩愧。

7.1. 搭建本地服務(wù)

webpack提供了一個(gè)可選的本地開發(fā)服務(wù)器,這個(gè)本地服務(wù)器基于node.js搭建,內(nèi)部使用express框架梢杭,可以實(shí)現(xiàn)我們想要的讓瀏覽器自動(dòng)刷新顯示我們修改后的結(jié)果武契。

不過它是一個(gè)單獨(dú)的模塊,在webpack中使用之前需要先安裝它

npm install --save-dev webpack-dev-server@2.9.1

devserver也是作為webpack中的一個(gè)選項(xiàng)全释,選項(xiàng)本身可以設(shè)置如下屬性:

  • contentBase:為哪一個(gè)文件夾提供本地服務(wù)妄迁,默認(rèn)是根文件夾,我們這里要填寫./dist
  • port:端口號
  • inline:頁面實(shí)時(shí)刷新
  • historyApiFallback:在SPA頁面中黔州,依賴HTML5的history模式

webpack.config.js文件配置修改如下:

  devServer: {
    contentBase: './dist',
    inline: true
  },

另外仿耽,我們再針對開發(fā)環(huán)境在package.json中配置一個(gè)scripts

  • --open參數(shù)表示直接打開瀏覽器
"dev": "webpack-dev-server --open"

通過npm run dev啟動(dòng)環(huán)境君躺,就會(huì)發(fā)現(xiàn)我們已經(jīng)有了一個(gè)可以實(shí)時(shí)刷新頁面的本地服務(wù)了。

7.2. 模塊熱更新

搭建完本地服務(wù)后固然非常好用,但是它還是存在一個(gè)弊端:當(dāng)只有一部分代碼發(fā)生變化時(shí)伏钠,我們?nèi)ニ⑿铝苏麄€(gè)頁面。

很明顯谨设,如果我們能在修改完代碼后只刷新修改的部分熟掂,那么界面的更新效率會(huì)更高。

  • 如何能做到呢扎拣?使用HotModuleReplacementPlugin插件即可赴肚。
  • 該拆件是一個(gè)webpack內(nèi)置的插件,并不需要安裝二蓝,直接使用即可

在webpack.config.js文件中修改插件如下:

module.exports = {
  ...
  devServer: {
    contentBase: './dist',
    inline: true,
    hot: true
  },
  ...
  plugins: [
    new webpack.BannerPlugin('最終版權(quán)歸coderwhy所有'),
    new uglifyJsPlugin(),
    new htmlWebpackPlugin({
      template: 'index.html'
    }),
    new webpack.HotModuleReplacementPlugin()
  ]
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市琴锭,隨后出現(xiàn)的幾起案子地回,更是在濱河造成了極大的恐慌谷羞,老刑警劉巖图贸,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件净神,死亡現(xiàn)場離奇詭異藻三,居然都是意外死亡忘衍,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來镰绎,“玉大人恋捆,你說我怎么就攤上這事‰誓” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵栓袖,是天一觀的道長违霞。 經(jīng)常有香客問我瞬场,道長泌类,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任古戴,我火速辦了婚禮疼蛾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘艺配。我一直安慰自己察郁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布转唉。 她就那樣靜靜地躺著皮钠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赠法。 梳的紋絲不亂的頭發(fā)上麦轰,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼款侵。 笑死末荐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的新锈。 我是一名探鬼主播甲脏,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼妹笆!你這毒婦竟也來了块请?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤拳缠,失蹤者是張志新(化名)和其女友劉穎墩新,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脊凰,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抖棘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狸涌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片切省。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖帕胆,靈堂內(nèi)的尸體忽然破棺而出朝捆,到底是詐尸還是另有隱情,我是刑警寧澤懒豹,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布芙盘,位于F島的核電站,受9級特大地震影響脸秽,放射性物質(zhì)發(fā)生泄漏儒老。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一记餐、第九天 我趴在偏房一處隱蔽的房頂上張望驮樊。 院中可真熱鬧,春花似錦片酝、人聲如沸囚衔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽练湿。三九已至,卻和暖如春审轮,著一層夾襖步出監(jiān)牢的瞬間肥哎,已是汗流浹背辽俗。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贤姆,地道東北人榆苞。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像霞捡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子薄疚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容