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

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

前言

使用包的版本css

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

每一個章節對應一個demohtml

1、初始化項目

進入項目目錄,運行 npm init來建立項目vue

npm init
複製代碼

終端輸入完成後會自動建立一個package.json的文件。node

{
  "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命令。webpack

npm i webpack webpack-cli -D
複製代碼

手動建立webpack.config.js。css3

touch webpack.config.js
複製代碼

在終端裏面經過npx運行webpack命令沒法進行復雜配置。因此建立webpack.config.js是爲了後面的複雜配置。 新建app.js、index.html、view文件夾、view文件夾裏面建立dom.js。git

mkdir view
 touch app.js
 cd view 
 touch app.js
複製代碼

代碼內容詳見demo1 配置 webpack.config.jses6

打包方式有兩種

  • 經過npx命令
npx webpack
複製代碼
  • 經過配置package.json的script對象。
"scripts": {
    "build": "webpack"
  },
複製代碼

這兩種方式都會自動尋找項目裏面的webpack包進行打包,而且webpack會根據webpack.config.js的配置規則進行打包。github


2、配置開發和生產環境

  • 開發環境特色
  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建立開發服務器。功能強大,接口轉發、熱更新等。web

文件更改

新建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'
};
複製代碼

配置更改

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裏面。

3、模板解析與外部擴展

代碼內容詳見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裏面有不少的自定義解析規則。有時間均可以嘗試一下。

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

代碼內容詳見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"
  },
複製代碼

5、解析轉譯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
複製代碼

打開打包後的app.js只有1.7kb。

promise裏面的箭頭函數已經被轉譯成了普通函數。
彷佛看起來已經已經成功了。

npm run dev
複製代碼

在打包後的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及之後這個墊片被廢棄了。

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
複製代碼

打包後的app.js有113kb。多出來了100多kb的代碼。
打開打包後的js,搜索 promise

一共有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
複製代碼

打包報錯了

根據 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
複製代碼

只有33kb。

打包的文件中有21個promise。
這樣作的好處就是使用哪一種es語法就引入哪一種轉譯器,避免代碼過大。爲何還須要繼續引入regenerator-runtime/runtime呢?由於它的代碼太少了,@babel/preset-env沒有像corejs同樣進行配置。若是不引入async、await就不能使用了。

更優秀的解決方案

動態 polyfill

polyfill.io是動態polyfill解決方案最好的方法。
用最新的Chrome打開polyfill.io/v3/polyfill…

它會根據你的瀏覽器 UA 頭,判斷你是否支持某些特性,從而返回給你一個合適的 polyfill。對於最新的 Chrome 瀏覽器來講,不須要任何polyfill,因此返回的內容爲空。

實驗

去除useBuiltIns配置

app.js去除core-js/stable,保留"regenerator-runtime/runtime" 由於async、await須要。
頁面引入polyfill.io

<script src="https://cdn.polyfill.io/v3/polyfill.min.js"></script>
複製代碼

模擬ie7瀏覽器。

頁面加載正常。polyfill.min.js 引入了不少的js代碼。裏面有不少的es5轉譯方法。
模擬safari@12.1

頁面加載正常。沒有一行es5轉譯方法。

後面我又用vivo Y13手機, 14年的安卓4.2.2手機測試了一下。是能夠用的,es七、es6均可以正常運行。

總結

對於最新的 safari 瀏覽器來講,不須要任何 polyfill,因此返回的內容爲空。對於 iie7瀏覽器 來講,須要 URL 對象的 polyfill,因此返回了對應的資源。

babel-loader還有不少配置不少坑,遇到就查文檔或者google吧。

6、樣式loader與樣式HMR

代碼內容詳見demo6

樣式loader

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

  • 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
複製代碼

按照官方的配置進行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
複製代碼

自動添加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'
                        ]
                    })
                ]
            }
        },
複製代碼

browsers配置表示按照 大於ie8,Safari6,最後10個Chrome版本的規則進行編譯。

npm run dev
複製代碼

css3的代碼都進行了前綴編碼。

importLoaders

home.less裏面的代碼並無進行css3的轉化。這時咱們須要配置在 css-loader 中使用 importLoaders 屬性。

將importLoaders設置爲2由於,css-loader前面須要執行postcss-loader和less-loader。

生產環境配置

以前一直運行的是開發環境,咱們運行一下生產打包命令

npm run build
複製代碼

打包後並無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
複製代碼

按照官方的語法進行配置

npm run build
複製代碼

css的代碼已經被抽離了而且壓縮了。

樣式HMR

查看官網有關於HMR解釋。

模塊熱替換(HMR - Hot Module Replacement)功能會在應用程序運行過程當中替換、添加或刪除模塊,而無需從新加載整個頁面。

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

由於設置模塊熱加載是開發環境須要的。因此更改webpack.dev.conf.js的配置。

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

因爲mini-css-extract-plugin不支持HMR,因此在開發環境繼續使用style-loader。 如今css的HMR已經配置好了。

文件解析。

代碼內容詳見demo7
文件解析須要用到的loaders

使用 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")
	}
},
複製代碼
相關文章
相關標籤/搜索