大聲對webpack4.0說聲你好之參見plugin與經常使用配置詳解(三)

在學會使用loader以後,咱們再來看看webpack的plugin的使用。css

導讀

本章知識點:plugin/babel/settinghtml

直通車-> webpack插件介紹vue

若是你還不會使用webpack的基本使用和loader的使用,建議您先觀看個人大聲對webpack4.0說聲你好前兩個章節。由於咱們例子就是結合上部分的繼續講,只要你跟着個人文章一直 敲代碼,你就能很輕鬆的學會webpack。node

使用插件生成html

在以前的例子中,咱們都是經過npx webpack xxx打包以後,在dist目錄下手動的新建一個xx.html,而後預覽咱們的效果,這樣的操做就比較麻煩,這個時候咱們就須要經過插件的方式來打包,讓咱們的效率增倍。webpack

刪除個人dist目錄。es6

而後安裝插件web

npm install --save-dev html-webpack-plugin
複製代碼

修改一下webpack.config.js的配置chrome

const htmlPlugin = require('html-webpack-plugin'); // 引入html打包插件

plugins: [
    new htmlPlugin()
  ],
複製代碼

引入了個人實例化對象。而後我再來進行一次打包。這個時候就直接給咱們生成了dist目錄,而且在dist目錄下主動爲咱們生成了index.html,而後打開就能夠預覽到咱們的效果。express

做用:htmlWebpackPlugin會在打包結束後,自動生成一個html文件,並把打包後生成的js自動引入到這個html中。npm

回想以前的例子,咱們若是將一個元素掛載到id=root的div下面的話,其實咱們這個index.html中是並無生成root這樣的div的,那這個時候咱們又應該怎麼作呢?

修改插件的配置以下,將咱們的本身的index.html做爲模板文件,這時就會使用咱們本身的模板文件的內容進行打包。

plugins: [
    new htmlPlugin({
      template: './index.html'
    })
  ],
複製代碼

這個時候是否是插件就跟vue的聲明週期同樣,在你打包的時候,去完成一些事情。

打包前自動刪除dist目錄

咱們先作一個小測試,就是咱們改變一下咱們輸入的js名稱。

output: {
    filename: 'dist.js',  // 以前是main.js
    path: path.resolve(__dirname, 'dist'), // 打包到dist文件夾
  }
複製代碼

dist目錄打包以後

而後進行打包操做。而後發現咱們在dist目錄下生成了一個dist.js這個的確是我想要的,可是其餘的生成內容好像並非我但願的,由於我若是之後東西多了,我根本就不知道哪些是我想要的內容,因此咱們在打包以前,我但願刪除dist目錄,這樣就能保證裏面的內容都是我所須要的。

clean-webpack-plugin
// 安裝插件
npm install clean-webpack-plugin -D

// 引入插件
const { CleanWebpackPlugin } = require('html-webpack-plugin'); 

plugins: [
    new htmlPlugin({
      template: './index.html'
    }),
    new CleanWebpackPlugin()
  ],
複製代碼

ok,繼續打包。這樣就只會有本次打包的內容。

插件小總結

插件的內容比較簡單,可是咱們每次打包的時候應該如何去找到咱們的插件呢?webpack的官網是有不少插件的,可是咱們詳細的去讀確定是有些難度,因此每次使用以前能夠百度、谷歌一些打包內容,而後搜索相應的插件進行下載。

Entry與Output的基礎配置

Entry入口配置

先回顧一下第一節的知識,知道這兩個名詞究竟是什麼?

大聲對webpack4.0說聲你好之webpack的基本使用(一)

entry: 打包的入口文件(npx wbpack 就能夠直接打包, 不聲明每次打包以前須要告訴webpack你想打包哪一個文件)

output:輸出文件名稱 (打包js以後,默認生成的文件名稱,不說明默認是main.js)

so easy.

那我若是要變動一下需求,我將index.js打包兩次,分別叫main.js/sub.js,那咱們應該怎麼作的?

entry: {
    main: './src/index.js',
    sub: './src/index.js'
  },
複製代碼

entry配置項詳解:官網entry詳解直通車

output

打包不出意外的失敗了,由於咱們定義了兩個文件,可是打包的輸入內容只有一個dist.js,因此咱們能夠修改一下輸入的配置。還記得第二節中講到的佔位符嗎?

大聲對webpack4.0說聲你好之loader基礎篇資源打包講解(二)

output: {
    filename: '[name].js',  //  name 就是至關於entry文件對應的key,既main sub
    path: path.resolve(__dirname, 'dist'), // 打包到dist文件夾
  }
複製代碼

而後神奇的發現,咱們的index中生成了sub與main兩個js文件,這個是由於咱們以前使用了htmlwebpackplugin插件,他幫咱們自動引入了。

<!doctype html><html><head><meta charset="utf-8"><title>Webpack App</title><meta name="viewport" content="width=device-width,initial-scale=1"></head><body><script src="main.js"></script><script src="sub.js"></script></body></html>
複製代碼

咱們繼續昇華一下咱們的此次打包操做,好比我打包以後,咱們文件以後是要放在第三方的cdn上面的,這樣的話就會在打包文件的前面自動加上一個cdn的域名,這個時候又該怎麼辦呢?固然手動改是不可能的~!

output: {
    publicPath: 'http://cdn.com/',
    filename: '[name].js',  // 打包後生成的main.js
    path: path.resolve(__dirname, 'dist'), // 打包到dist文件夾
  }
複製代碼

再次打包(npm rub build -- 我本身修改的scripts,詳見系列一)

<script src="http://cdn.com/main.js"></script>
  <script src="http://cdn.com/sub.js"></script>
複製代碼

It's perfect.

output配置項詳解:官網output詳解直通車

SourceMap

在介紹這個配置以前,咱們仍是先進入一個例子,他會讓你很清楚的知道SourceMap能幫你幹什麼。

新建一個source.js,而後修改一下咱們以前修改的配置

// source.js
consele.log('hello, axin !') // console故意寫錯

// webpack.config.js

// 咱們如今的模式是開發者模式,默認是配置了SourceMap的,因此咱們須要本身關閉
mode: 'development',
devtool: 'none',
  
  
entry: {
    main: './src/source.js'
  },
  
output: {
    filename: 'dist.js',  // 打包後生成的main.js
    path: path.resolve(__dirname, 'dist'), // 打包到dist文件夾
  }
複製代碼

即便咱們寫錯了,咱們的打包依然能夠繼續完成,可是在頁面中說咱們的consele未定義,而後點擊進去發現是打包以後的文件報錯,可是我並不知道我原文件中是哪一行報錯,否則我都不知道去哪裏修改。

SourceMap:映射關係,它知道dist目錄下打包文件的報錯信息具體是你src目錄下的哪一行

devtool: 'source-map'
複製代碼

這個時候打包,他就會告訴咱們src下面的source文件的第一行報錯了。並且咱們會在dist目錄下生成一個dist.js.map的文件,他就是咱們文件的對應關係,若是使用inline-source-map,就不會出現map文件,可是會有一個base64的字符串做爲映射聯繫。自覺上車->官方直通車

推薦形式: 開發環境中,cheap-module-eval-source-map,打包速度快,提示也比較全。

那若是開發的代碼放到線上,出現的問題,咱們應該怎麼配置呢?cheap-module-source-map

webpackDevServer

目前的操做中,咱們是修改了咱們的代碼,而後使用npm run build,最後在dist文件下,找到咱們的index.html,在瀏覽器中打開。那麼如此麻煩的操做咱們應該如何優化從而提升開發效率呢?

"scripts": {
    "build": "webpack",
    "watch": "webpack --watch"
  },
複製代碼

build是我本身新增的打包代碼命令,詳解在系列一。

watch能夠監聽咱們的源代碼發生變化,他就會自動幫咱們從新打包。

因此咱們直接使用命令,就能夠實現咱們的該操做。

npm run watch
複製代碼

監聽源文件變化從新打包,而後渲染到咱們頁面,而後刷新就能夠用了,可是這樣操做還不夠好,由於我但願我在第一次使用這個命令的時候,自動幫我打包,而後打開瀏覽器,還能模擬一些服務器上的特性。

這個時候咱們就能夠藉助webpackdevserver來達到更加酷炫的效果。

先安裝webpack-dev-server

npm install webpack-dev-server- d
複製代碼

修改配置項

// webpack.config.js
devServer: {
    contentBase: './dist', // 藉助webpack啓動服務器,根目錄就是打包以後的dist文件夾
    open: true // 啓動npm run start的時候自動打開瀏覽器
  },
  
// package.json
"scripts": {
    "build": "webpack",
    "watch": "webpack --watch",
    "start": "webpack-dev-server"
  },
複製代碼

而後咱們在執行npm run watch,修改源代碼,就不須要從新打開瀏覽器進行手動刷新,webpack-dev-server已經幫咱們作了這些操做,因此咱們只關注咱們的業務代碼就ok。

繼續擴展,gogogo!

在vue中,咱們常用proxy來代理咱們的跨域服務,由於在webpack中就是支持這種操做的。

// 跨域轉發代碼
proxy: {
  '/api': 'http://localhost:3000'  
}

// 配置端口號
port: 8080
複製代碼

官網直通車:www.webpackjs.com/configurati…

接下來咱們看一個比較高逼格的操做。

手寫本身的server

新建一個scripts命令

"scripts": {
    ...
    "server": "node server.js"
  },
複製代碼

由於用到了node,因此我使用expres和一個webpack-dev-middleware

npm install express webpack-dev-middleware -D
複製代碼

修改一個webpack配置確保個人文件打包到根目錄下

output: {
    publicPath: '/',
    filename: 'dist.js',  // 打包後生成的main.js
    path: path.resolve(__dirname, 'dist'), // 打包到dist文件夾
  }
複製代碼

咱們就會在根目錄下新建一個server.js,運行npm run server的時候就會進入咱們本身的腳本

// 引入express webapck 中間件
var express = require('express')
var webpack = require('webpack')
var webpackDevMiddleware = require('webpack-dev-middleware')
// 引入webpack配置文件
var config = require('./webpack.config.js')
var complier = webpack(config) // config配置傳入 complier主要作編譯

const app = express()
app.use(webpackDevMiddleware(complier, {
  publicPath: config.output.publicPath // 聲明本身的publicPath
}))

app.listen('3000', () => { // 監聽3000端口啓動成功 
  console.log('server is running')
})
複製代碼

在使用 npm run server 就能夠啓動本身的服務命令了。

代碼比較多,我打了一些註釋,可能不少人仍是看不懂,我再來梳理一下這個流程。

server使咱們手寫的一個相似於wbpack-dev-server的功能,因此咱們在運行npm run server的時候,其實是運行了一個js文件,利用express咱們建立了一個server應用,端口是3000,webpackDevMiddleware是webpack的中間件,complier返回的是一個編譯器,只要文件改變了就會從新編譯。

因此webpack不止在命令行中運行,還能夠在nodejs中運行。

一行代碼聲明入口文件與出口文件

webpack index.js -o dist.js
複製代碼

這句話就是打包index.js後打包文件是dist.js

Hot Module Replace

devserver的好處

熱模塊替換,簡寫HMR

咱們先在配置中修改一下配置

// 刪除打包的dist目錄

// 以前的package.json 刪除build,server,
"build": "webpack",
"watch": "webpack --watch",
"start": "webpack-dev-server",
"server": "node server.js"

// 只保留一個start
"scripts": { 
    "start": "webpack-dev-server"
  },
  
// 啓動服務
npm run start

複製代碼

這個時候咱們會發現,webpack-dev-server並無在咱們的根目錄下生成一個dist目錄,可是咱們的代碼還能夠正常運行,這個過程當中webpack其實也是對咱們的代碼進行的代碼,不過文件在咱們的內存中,因此打包的效率更高。

css熱更新

繼續,咱們在src新建btn.js,statics/style/btn.css

// btn.js
var btn = document.createElement('button')
btn.innerHTML = '新增'
document.body.appendChild(btn)
btn.onclick = function(){
  var div = document.createElement('div')
  div.innerHTML = 'item'
  document.body.appendChild(div)
}

// btn.css
div:nth-child(2n){
  background-color: yellow;
}
複製代碼

而後修改入口文件問btn.js,這段代碼就是在body裏面新增一個btn按鈕,點擊一次新增一個item的div。偶數的div背景會是一個黃色。

可是咱們若是去修改css中背景色,這個時候瀏覽器就會自動刷新,以前點擊新增的div已經徹底沒有了,這樣咱們就會從新點擊新增來添加節點,這樣就會很是麻煩,那麼咱們在修改css的時候,能保證只是從新加載樣式的話,就好多了。

在devServer中添加以下配置。

const webpack = require('webpack') // 引入webpack插件

devServer: {
    contentBase: './dist', // 藉助webpack啓動服務器,根目錄就是打包以後的dist文件夾
    open: true, // 啓動npm run start的時候自動打開瀏覽器
    proxy: { // 配置代理
      '/api': 'http://localhost:3000' 
    },
    port: 8080, // 配置端口號
    hot: true, // 開啓熱更新
    hotOnly: true // 就算是html文件沒生效也不刷新頁面
  },
  
plugins: [
    new htmlPlugin({
      template: './index.html'
    }),
    new cleanPlugin(['dist']),
    new webpack.HotModuleReplacementPlugin() // 引入插件 html才能生效
  ],
複製代碼

由於改了webpack配置文件內容,因此咱們重啓一下服務,這樣的話,咱們修改css文件就不會從新刷新頁面,只會重載樣式。

ok,再來昇華一個例子。在src下新建count.js與number.js

html熱更新

咱們仍是先註釋添加的hot,hotonly以及插件

// webpack.config.js


// count.js
function count(){
  var div = document.createElement('div')
  div.setAttribute('id', 'count')
  div.innerHTML = 1
  div.onclick = function () {
    div.innerHTML = parseInt(div.innerHTML, 10) + 1 // 按照十進制解析+1
  }
  document.body.appendChild(div)
}

export default count

// number.js
function number() {
  var div = document.createElement('div')
  div.setAttribute('id', 'number')
  div.innerHTML = 1000
  document.body.appendChild(div)
}

export default number

複製代碼

解釋:count方法新增了一個div。id爲count,初始文本爲1,有一個點擊事件,點擊一次內容按照十進制解析,而後+1,最後掛載到body上面,導出方法。number方法就本身悟。

而後在btn.js中引入該方法。

import count from './count'
import number from './number'

count()
number()
複製代碼

咱們點擊count,內容會發生變化,可是咱們去修改number的值,會發現頁面刷新了,這個時候就和遇到的css問題同樣。

可是咱們開啓以後,咱們就會發現,咱們修改了number中的數字以後,頁面並無刷新了,這時咱們須要手動在btn.js中加入如下代碼。

if(module.hot){
  module.hot.akccept('./number', ()=>{ // 監聽文件
    number()
  })
}
複製代碼

這樣就會監聽咱們的number文件中的代碼發生了變化,就會從新執行number函數。

新的問題就出現了,由於個人number文件發生了變化,我就從新執行了number函數,而後掛載到了body,可是我並不想這樣,我想的是把以前的number修改爲個人數便可。

// 在執行方法以前,刪除以前的節點
document.body.removeChild(document.getElementById('number'))
複製代碼

BABEL

初探

babel在全部的框架中,咱們都能見到他的身影。它能夠幫咱們更好的處理es6語法。

爲何使用babel

爲何要使用babel處理es6語法?由於咱們在大多數瀏覽器中。例如ie低版本等,代碼中寫了promise、async await等,他會不識別咱們的語法,這個時候就須要將咱們的es6語法轉到es5。方便瀏覽器閱讀執行代碼。

babel官方介紹webpack中使用教程:babeljs.io/setup#insta…

1.安裝loader

npm install --save-dev babel-loader @babel/core
複製代碼

babel-loader相信你們已經很熟悉了,它是幫助咱們使用webpack作打包的一個工具。babel-core是babel的一個核心庫,它能讓babel去識別js裏面的內容,而後把js轉換爲抽象語法樹,在編譯轉換成新的語法出來。

2.檢測文件若是是js文件,那麼使用babel-loader來進行語義上的分析

{ 
    test: /\.js$/, 
    exclude: /node_modules/,  // 若是你的js文件在 node_modules裏面,就不使用babel-loader
    loader: "babel-loader" 
},
複製代碼

3.安裝@babel/preset-env文件

npm install @babel/preset-env --save-dev

options: {
  "presets": ["@babel/preset-env"]
}
複製代碼

問什麼會安裝這個文件?當咱們的babel-loader處理js文件的時候,babel-loader只是babel和webpack作通信的,但實際上babel-loader並不會將你把es6語法轉成es5語法,你還須要一些其餘的模塊,才能轉換。

這樣咱們就配置好了,已經能夠將es6轉義成es5語法。

打包業務文件

新增p.js並修改配置文件

// 打包入口
entry: {
    main: './src/p.js',
  },
  
// p.js
const arr = [
  new Promise(()=>{}),
  new Promise(()=>{})
]

arr.map(item=>{
  console.log(item)
})
複製代碼

編譯以後咱們的const 變成了var 可是promise map等仍是有些瀏覽器是識別不了的。

// 編譯後
var arr = [new Promise(function () {}), new Promise(function () {})];
arr.map(function (item) {
  console.log(item);
});
複製代碼

這個時候咱們還須要藉助polyfill

npm install --save @babel/polyfill
複製代碼

安裝完成以後咱們只須要在代碼以前引入就能夠了。把它放到業務代碼的最頂部。

import "@babel/polyfill";
複製代碼

這個時候在打包,文件就會變得很是大,由於他會自動幫咱們生成promise和map等的實現。

可是在咱們目前的代碼中,只須要promse和map的實現,因此咱們還須要增長配置。

options: {
  "presets": [["@babel/preset-env",{
    useBuiltIns: "usage"
  }]]
}
複製代碼

相比於以前的430kb,如今還有81kb,代碼就精簡了不少。

更多配置項:babeljs.io/docs/en/bab…

"targets": {
  "chrome": "67" // 大於67的版本就不注入
}

...
複製代碼
打包庫文件

若是咱們平時本身作一些東西,徹底可使用polyfill,可是若是作一些比較大的ui庫等,這種就不適合了,由於他至關於一個全局使用,會污染到咱們的代碼。

npm install --save-dev @babel/plugin-transform-runtime

"plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "absoluteRuntime": false,
        "corejs": false,
        "helpers": true,
        "regenerator": true,
        "useESModules": false,
        "version": "7.0.0-beta.0"
      }
    ]
  ]
複製代碼

若是將corejs改爲2,須要額外的安裝@babel/runtime-corejs2

他會幫你以組件的方式引入,不會污染你的全局變量。

提出babel配置

這個時候咱們的代碼就已經很是多了,因此咱們能夠將options的代碼提出來,新建.babelrc文件放入其中

// .babelrc
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "targets": {
          "chrome": "67" // 大於67的版本就不注入
        }
      }
    ]
  ]
}

複製代碼

總結

  1. 使用插件,htmlWebpackPlugin/clean-webpack-plugin
  2. webpack.config.js配置講解
  3. sourcemap文件映射等
  4. webpack-dev-serser熱更新等
  5. 強大的babel

寫了這麼久,收到了很多大神的建議,寫文章的水平也比較低,第四節開始慢慢開始改變吧,加油加油奧利給~!

相關文章
相關標籤/搜索