漸進式配置webpack4單頁面和多頁面(一)

雲翳發表於2019-05-13

漸進式配置webpack4單頁面和多頁面

前言

使用包的版本

webpack ->4.3.0
babel-loader ->8.0.5
npm ->6.4.1
webpack-cli ->3.3.1
複製程式碼

每個章節對應一個demo

一、初始化專案

進入專案目錄,執行 npm init來建立專案

npm init
複製程式碼

終端輸入完成後會自動建立一個package.json的檔案。

{
  "name": "demo1",
  "version": "1.0.0",
  "description": "webpack-demo",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack app.js"
  },
  "keywords": [
    "demo"
  ],
  "author": "cmf",
  "license": "ISC",
  "devDependencies": {}
}

複製程式碼

執行命令,引入webpack和webpack-cli,安裝webpack-cli是為了在專案裡面執行webpack命令。

npm i webpack webpack-cli -D
複製程式碼

手動建立webpack.config.js。

touch webpack.config.js
複製程式碼

在終端裡面通過npx執行webpack命令無法進行復雜配置。所以建立webpack.config.js是為了後面的複雜配置。 新建app.js、index.html、view資料夾、view資料夾裡面建立dom.js。

 mkdir view
 touch app.js
 cd view 
 touch app.js
複製程式碼

程式碼內容詳見demo1 配置 webpack.config.js

漸進式配置webpack4單頁面和多頁面(一)
打包方式有兩種

  • 通過npx命令
npx webpack
複製程式碼
  • 通過配置package.json的script物件。
  "scripts": {
    "build": "webpack"
  },
複製程式碼

這兩種方式都會自動尋找專案裡面的webpack包進行打包,並且webpack會根據webpack.config.js的配置規則進行打包。


二、配置開發和生產環境

  • 開發環境特點
  1. 視覺化
  2. 本地服務
  3. 可以快速找到程式碼錯誤
  4. 熱更新
  • 生產環境特點
  1. 程式碼壓縮
  2. 程式碼拆包
  3. 快速響應

下載外掛包

npm i cross-env html-webpack-plugin webpack-dev-server -D
複製程式碼

cross-env 跨平臺的解決了環境變數和引數的命令配置。
html-webpack-plugin 打包生成HTML的外掛。
webpack-dev-server建立開發伺服器。功能強大,介面轉發、熱更新等。

檔案更改

新建index.html、help.js

touch index.html
touch help.js
複製程式碼

index.html 模板檔案

<head>
  <title>demo2</title>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
</head>
<body>
  <div id="app"></div>
</body>
複製程式碼

help.js 封裝的一些方法。

module.exports.getMode = function() {
  return process.env.NODE_ENV === 'development'?'development':'production'
};
複製程式碼

配置更改

漸進式配置webpack4單頁面和多頁面(一)

webpack-dev-server其他配置請參考官方文件。
mode模式。預設值是production。 告知 webpack 使用相應模式的內建優化。是 webpack4新增的屬性。比如mode是production,webpack會預設新增一些打包外掛比如:NoEmitOnErrorsPlugin。可以節省很多配置。

命令配置

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "cross-env NODE_ENV=production webpack",
    "dev": "cross-env NODE_ENV=development webpack-dev-server"
  },
複製程式碼

cross-env NODE_ENV=production 設定環境變數資訊

npm run dev
複製程式碼

自動開啟瀏覽器,會把app.js裡面的內容自動注入到index.html裡面。

漸進式配置webpack4單頁面和多頁面(一)

三、模板解析與外部擴充套件

程式碼內容詳見demo3

外部擴充套件(externals)

externals防止將某些 import 的包是從外部獲取依賴不是從node_modules裡面獲取的。
例如我們在app.js裡面

// app.js
import Vue from 'Vue';
複製程式碼

index.html

<head>
  <title>demo4</title>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
</head>
複製程式碼

webpack.config.js

externals:{ //外部擴充套件
    'Vue':'window.Vue'
},
複製程式碼

現在app.js 裡面引入的Vue是從cdn裡面引入的,而不是從node_modules包裡面引入的。 這樣做的好處:

  • 提高打包效率。
  • 類似於使用Vue這樣的框架,使用的版本是一般是固定不變的。所以cdn引入的時候相當於模組化拆包。使用者重新整理瀏覽器和程式碼重新打包發版都不會再次請求Vue的程式碼,提高使用者體驗。

模板解析(resolve)

resolve開發者可以自定義解析規則。

  • resolve.modules 自定義依賴包的路徑。引數是array,可以是相對目錄也可以是絕對目錄。預設是 [path.resolve(process.cwd(),'node_modules')]。當前命令的目錄下面的node_modules資料夾。
    如果一個大的專案裡面有很多子專案,每個子專案沒必要都安裝依賴包,所以可以在各個子專案裡面通過配置resolve.modules指向母專案的node_modules包。

  • resolve.modules 自定義引入模組時可以不帶副檔名。引數是array。預設['.js','.json']

    extensions:['.js','.vue','.json','.css','.less'],
    複製程式碼

    程式碼裡面

    import 'view/dom'
    複製程式碼

    會自動找到dom.js

  • resolve.alias 自定義路徑別名。引數是object。

    alias:{
        '@': path.resolve(__dirname, './src')
    },
    // __dirname 當前檔案的目錄地址。
    複製程式碼

    在程式碼裡可以使用@符號指向src目錄。

    import '@/view/dom.js';
    複製程式碼

    好處:在很深的程式碼層裡面如果想引入頂層的某個js檔案。不需要寫很多'../../../'去找檔案,提高開發效率。

resolve裡面有很多的自定義解析規則。有時間都可以嘗試一下。

四、生產與開發環境分開打包

程式碼內容詳見demo4
雖然在webpack4裡面提供了mode屬性來分別打包開發和生產環境,各自提供不同的外掛。但是如果clean-webpack-plugin外掛想在生產環境使用在開發環境不使用,就需要每次手動更改配置,這樣做很不合理,容易出錯。
可以使用

  • 指定配置檔案。
    webpack --config 配置檔案
    複製程式碼
  • 合併公共的配置webpack-merge

具體操作

新增依賴包

npm i webpack-merge -D
複製程式碼

新增檔案

新建build資料夾

 mkdir build
 cd build
 touch weboack.build.conf.js、webpack.base.conf.js、webpack.dev.conf.js config.js help.js
複製程式碼

help.js 儲層公共方法

var path = require('path');
module.exports.getMode = function() {
  return process.env.NODE_ENV === 'development'?'development':'production'
};
module.exports.resolve = function(p){
  return path.resolve(process.cwd(),p);
}
複製程式碼

config.js 開發與生產的配置資訊

module.exports = {
  dev: {
    mode: 'development',
    publicPath: '/',
    devServer: {
      port: '8899',
      proxy: {
        '/test/shortRent': {
          target: 'http:"//www.baidu.com',
          changeOrigin: true,
          pathRewrite: {
            '^/test/shortRent': '/evcard-evrental'
          }
        },
      },
    },
  },
  build: {
    mode: 'production',
    publicPath: './',
    assetsRoot: 'you-app'
  }
}
複製程式碼

webpack.base.conf.js 開發與生產相同的webpack配置

var path = require('path');
var help = require('./help.js');
var config = require('./config.js');
var htmlWebpackPlugin = require('html-webpack-plugin');
var mode = help.getMode();

module.exports={
  entry:{
    app:help.resolve('./app.js')
  },
  output:{
  },
  resolve:{ // 解析
    alias:{
      '@': help.resolve('./src')
    },
    extensions:['.js','.vue','.json','.css','.less'],
    modules: ["./node_modules"]
  },
  externals:{ //外部擴充套件
    'Vue':'window.Vue'
  },
  plugins:[
    new htmlWebpackPlugin({
      filename:'index.html',
      template:'./index.html',
      inject:true,
    })
  ],
}
複製程式碼

webpack.dev.conf.js 開發環境的webpack配置

var merge = require("webpack-merge");
var webpackConfigBase = require('./webpack.base.conf');
var help = require('./help.js');
var config = require('./config.js');

module.exports=merge(webpackConfigBase,{
  mode: config.dev.mode,
  output:{
    filename:help.assetsPath('js/[name].js'),
    publicPath:config.dev.publicPath
  },
  devServer:config.dev.devServer
})
複製程式碼

webpack.build.conf.js 生產環境的webpack配置

var cleanWebpackPlugin = require('clean-webpack-plugin');
var merge = require("webpack-merge");
var help = require('./help.js');
var webpackConfigBase = require('./webpack.base.conf.js');
var config = require('./config.js');

module.exports=merge(webpackConfigBase,{
  mode: config.build.mode,
  output:{
    filename:'assets/js/[name].[hash].js',
    publicPath:config.build.publicPath,
    path:help.resolve(config.build.assetsRoot),
  },
  plugins:[
    new cleanWebpackPlugin()
  ]
})
複製程式碼

命令修改

  "scripts": {
    "build": "cross-env NODE_ENV=production webpack --config build/weboack.build.conf.js",
    "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.dev.conf.js"
  },
複製程式碼

五、解析轉譯JS

程式碼內容詳見demo5

loaders

loader 用來解析檔案轉譯成瀏覽器可以識別的檔案。如.less、.vue、.jsx等這些檔案瀏覽器是不能正常轉譯的,loaders的作用就是充當著'翻譯'的作用。

babel-loader

我們在開發的時候都使用es6的語法去編寫程式碼,但是有些瀏覽器不支援es6的程式碼就需要將es6轉譯成瀏覽器可以讀懂的es5的程式碼。babel-loader的作用就是'翻譯'es6程式碼。

基礎配置

安裝babel-loader。參照官方的安裝方式,開啟官網選擇webpack的安裝方式。
安裝依賴

npm install --save-dev babel-loader @babel/core
npm install @babel/preset-env --save-dev
複製程式碼

babel-loader @babel/core 是核心外掛
preset-env 編譯方式

配置規則

module: {
  rules: [
        {
            test: /\.js$/,  // 正則匹配,所有的.js檔案都使用這個規則進行編譯
            exclude: /node_modules/, // 排除的資料夾。這個資料夾裡面的的檔案不進行轉譯
            loader: "babel-loader", // 轉譯的外掛
            options: {  // 轉譯的規則
                presets: [ //轉譯器配置
                    [
                        "@babel/preset-env"
                    ]
                ],
                plugins: [] // 轉譯外掛配置
            }
        },
  ]
}
複製程式碼

plugins(轉譯外掛)。轉譯外掛是用來轉譯單一功能的外掛,比如transform-es2015-arrow-functions,這個外掛只負責轉譯es2015新增的箭頭函式。

presets(轉譯器)。轉譯器是一系列轉譯外掛的集合。比如babel-preset-es2015就包含了es2015新增語法的所有轉譯外掛,比如包含transform-es2015-arrow-functions(es2015箭頭函式轉譯外掛)、transform-es2015-classes(es2015 class類轉譯外掛)等。轉譯器分為語法轉譯器和補丁轉譯器。 詳解

在app.js裡面寫es6的程式碼

const s = new Set([1, 2, 3, 4, 5, 3, 2, 16, 7, 83, 21, 2, 1]);
var w = Object.assign({}, { w: 1, e: 4 })
console.log(w);
console.log([...s]);
function pro(v) {
  return new Promise((resolve) => {
    if (v) {
      resolve('真11')
    } else {
      resolve('假22')
    }
  })
}
pro(true).then(res=>{
  console.log(res)
})
複製程式碼

執行命令

npm run build
複製程式碼

漸進式配置webpack4單頁面和多頁面(一)
開啟打包後的app.js只有1.7kb。

漸進式配置webpack4單頁面和多頁面(一)
promise裡面的箭頭函式已經被轉譯成了普通函式。
似乎看起來已經已經成功了。

npm run dev
複製程式碼

漸進式配置webpack4單頁面和多頁面(一)
在打包後的js全域性搜尋promose,程式碼裡面只有一處。沒有其他的程式碼來'翻譯'promise,這在低版本的瀏覽器裡面是不行的,所以需要繼續'翻譯'。

墊片配置

Babel 預設只轉換新的 JavaScript 句法(syntax),而不轉換新的 API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全域性物件,以及一些定義在全域性物件上的方法(比如 Object.assign)都不會轉碼。

所以還需要配置。 在babel@7.4以前使用@babel/polyfill為當前環境提供一個墊片。所謂墊片也就是墊平不同瀏覽器或者不同環境下的差異。
但是babel@7.4及以後這個墊片被廢棄了。

漸進式配置webpack4單頁面和多頁面(一)
babel-polyfill 等同於 regenerator runtime + core-js
官方建議使用

import "core-js/stable";
import "regenerator-runtime/runtime";
複製程式碼

來代替@babel/polyfill。

  • regenerator:提供對 generator 支援,如果應用程式碼中用到generator、async函式的話。
  • core-js:提供 es(6-8) 新的特性。

core-js 在安裝@babel/preset-env的時候已經安裝好了。

npm i regenerator-runtime -D
複製程式碼

在app.js裡面引入

import "core-js/stable";
import "regenerator-runtime/runtime";
複製程式碼

執行命令

npm run build
複製程式碼

漸進式配置webpack4單頁面和多頁面(一)
打包後的app.js有113kb。多出來了100多kb的程式碼。
開啟打包後的js,搜尋promise

漸進式配置webpack4單頁面和多頁面(一)
一共有26處,這裡面對promise的程式碼進行了'翻譯'。
但程式碼相比以前也太大了。把所有轉譯es的程式碼都打包了。

優化

useBuiltIns 這個配置屬性可以解決這個問題。官方文件的配置其實是babel@7.4以前的寫法,這也造成了我懵逼了兩天。
屬性值 "usage" | "entry" | false, 預設是 false。

useBuiltIns: 'entry' 其實和import "core-js/stable"; import "regenerator-runtime/runtime";效果是一樣的,表示把所有的轉譯程式碼都注入到打包的程式碼裡面。但是還是需要在程式碼裡面引入這兩個外掛。

useBuiltIns: 'usage' 表示把程式碼裡面需要用到的轉譯程式碼注入到打包的程式碼裡面。就不需要引入core-js/stable了。但是regenerator-runtime/runtime還是需要繼續引入。

{
    test: /\.js$/,  // 正則匹配,所有的.js檔案都使用這個規則進行編譯
    exclude: /node_modules/, // 排除的資料夾。這個資料夾裡面的的檔案不進行轉譯
    loader: "babel-loader", // 轉譯的外掛
    options: {  // 轉譯的規則
        presets: [ //轉譯器配置
            [
                "@babel/preset-env", {
                    useBuiltIns: "usage"
                  }
            ]
        ],
        plugins: [] // 轉譯外掛配置
    }
},
複製程式碼

執行打包命令

npm run build
複製程式碼

打包報錯了

漸進式配置webpack4單頁面和多頁面(一)
根據preset-env 的文件說明還需要配置corejs。作用是代替引入core-js/stable

{
    test: /\.js$/,  // 正則匹配,所有的.js檔案都使用這個規則進行編譯
    exclude: /node_modules/, // 排除的資料夾。這個資料夾裡面的的檔案不進行轉譯
    loader: "babel-loader", // 轉譯的外掛
    options: {  // 轉譯的規則
        presets: [ //轉譯器配置
            [
                "@babel/preset-env", {
                    useBuiltIns: "usage",
                    corejs: 3
                  }
            ]
        ],
        plugins: [] // 轉譯外掛配置
    }
},
複製程式碼

執行打包命令

npm run build
複製程式碼

漸進式配置webpack4單頁面和多頁面(一)
只有33kb。

漸進式配置webpack4單頁面和多頁面(一)

打包的檔案中有21個promise。
這樣做的好處就是使用哪種es語法就引入哪種轉譯器,避免程式碼過大。為什麼還需要繼續引入regenerator-runtime/runtime呢?因為它的程式碼太少了,@babel/preset-env沒有像corejs一樣進行配置。如果不引入async、await就不能使用了。

babel-loader還有很多配置很多坑,遇到就查文件或者google吧。

六、樣式loader與樣式HMR

程式碼內容詳見demo6

樣式loader

樣式loader,這些都是官方提供的樣式loader。

漸進式配置webpack4單頁面和多頁面(一)

  • style-loader 將js裡面引入的css檔案,解析成css樣式並且新增到style標籤裡面。
  • css-loader 解析css裡面的@import引入的樣式。
  • less-loader sass-loader stylus-loader 都是解析css擴充套件語言。
  • postcss-loader 為css3的程式碼自動新增字首。

基礎配置

比如 使用less作為樣式語法。 首先安裝style-loader css-loader。

npm i style-loader css-loader -D
複製程式碼

less-loader文件裡面顯示需要安裝

npm install --save-dev less-loader less
複製程式碼

漸進式配置webpack4單頁面和多頁面(一)
按照官方的配置進行rules配置。
編寫base.less檔案

body{
  color: lawngreen;
}
.logo{
  background: #f60;
  height: 400px;
  width: 400px;
  background-repeat: no-repeat;
  transition: all 1s;
  display: flex;
}
.logo:hover{
  height: 600px;
  width: 600px;
  transform: translateY(60px);
}
複製程式碼

在app.js 裡面引入

import './src/assets/css/base.less';
複製程式碼

執行命令

npm run dev
複製程式碼

漸進式配置webpack4單頁面和多頁面(一)

自動新增css3瀏覽器字首

less檔案裡面的transition和transform都是css3的樣式,如果想自動的生成帶字首的程式碼則需要postcss-loader,也需要postcss-loader的一個外掛autoprefixer
安裝依賴

npm i autoprefixer postcss-loader -D
複製程式碼

更改配置

       {
            loader: "postcss-loader",
            options: {
                plugins: [
                    require("autoprefixer")({
                        browsers: [
                            'last 10 Chrome versions',
                            'last 5 Firefox versions',
                            'Safari >= 6',
                            'ie> 8'
                        ]
                    })
                ]
            }
        },
複製程式碼

漸進式配置webpack4單頁面和多頁面(一)
browsers配置表示按照 大於ie8,Safari6,最後10個Chrome版本的規則進行編譯。

npm run dev
複製程式碼

漸進式配置webpack4單頁面和多頁面(一)
css3的程式碼都進行了字首編碼。

importLoaders

home.less裡面的程式碼並沒有進行css3的轉化。這時我們需要配置在 css-loader 中使用 importLoaders 屬性。

漸進式配置webpack4單頁面和多頁面(一)
將importLoaders設定為2因為,css-loader前面需要執行postcss-loader和less-loader。

生產環境配置

之前一直執行的是開發環境,我們執行一下生產打包命令

npm run build
複製程式碼

漸進式配置webpack4單頁面和多頁面(一)

打包後並沒有css檔案。 把程式碼放入伺服器或者使用http-server起一個本地的伺服器。

這時我們需要引入mini-css-extract-plugin進行css程式碼的抽離,以及打包外掛optimize-css-assets-webpack-plugin在打包的時候進行css程式碼壓縮。

npm i mini-css-extract-plugin optimize-css-assets-webpack-plugin -D
複製程式碼

按照官方的語法進行配置

漸進式配置webpack4單頁面和多頁面(一)

npm run build
複製程式碼

css的程式碼已經被抽離了並且壓縮了。

漸進式配置webpack4單頁面和多頁面(一)

樣式HMR

檢視官網有關於HMR解釋。

模組熱替換(HMR - Hot Module Replacement)功能會在應用程式執行過程中替換、新增或刪除模組,而無需重新載入整個頁面。

按照官方的例項。 首先更改devServer的配置,設定hot屬性為true。

漸進式配置webpack4單頁面和多頁面(一)

因為設定模組熱載入是開發環境需要的。所以更改webpack.dev.conf.js的配置。

  plugins:[
    new webpack.HotModuleReplacementPlugin()
  ]
複製程式碼

由於mini-css-extract-plugin不支援HMR,所以在開發環境繼續使用style-loader。 現在css的HMR已經配置好了。

檔案解析。

程式碼內容詳見demo7
檔案解析需要用到的loaders

漸進式配置webpack4單頁面和多頁面(一)
使用url-loader作為檔案解析的loader。file-loader是一種'搬運工'的loader,將程式碼中需要的檔案'搬運'到合適的地方,url-loader在搬運的基礎上可以在程式碼上新增資料的作用,如新增base64的圖片。

{
	test: /\.(png|jpg|gif)$/,
	use: [{
		// 需要下載file-loader和url-loader
		loader: "url-loader",
		options: {
			limit: 5 * 1024, //小於5kb將會已base64點陣圖片打包處理 
			// 圖片檔案輸出的資料夾
			outputPath: help.assetsPath("images"),
			name: '[name].[ext]'
		}
	}]
},
{
	test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
	loader: 'url-loader',
	options: {
		limit: 10000,
		outputPath: help.assetsPath("fonts")
	}
},
複製程式碼

相關文章