webpack4搭一個簡易SPA應用

jkCaptain發表於2019-01-01

開篇

webpack 在如今的前端開發中,算是不可繞過的一個工具吧。特別是在開發SPA應用的時候,無論是開發環境,還是打包上線,都十分依賴webpack。

開發環境

win10

node -v: 10.15.0

npm -v: 6.4.1

Let's go。

體驗0配置

進入到工作目錄,然後建立專案

mkdir spa-webpack-demo
複製程式碼

初始化

npm init -y
複製程式碼

先來體驗下 webpack40配置:

安裝 webpack

npm i -D webpack
複製程式碼

安裝好 webpack 依賴後,建立 src 資料夾,並在 src 中新建一個 index.js

mkdir src
cd src
type nul > index.js
複製程式碼

修改 package.json,在 scripts選項中,新增兩個命令:

"dev": "webpack --mode=development",
"prod": "webpack --mode=production"
複製程式碼

好,完事了。接下來跑命令列,測試一下

npm run dev
複製程式碼

正常情況下,控制檯會有一段如下提示,因為 webpack 命令需要依賴 webpack-cli,我們安裝即可

Do you want to install 'webpack-cli' (yes/no): yes
複製程式碼

webpack-cli安裝完成之後,會自動繼續跑我們的 npm run dev 指令,即可看到專案中多了一個 dist 目錄,而且多了一個 main.js

接下來繼續嘗試 npm run prod,可以看到 dist/main.js 已被壓縮。

這就是 webpack 號稱的零配置,主要的工作就是定義了預設的entry路徑src/index.js,定義了預設的output路徑dist/main.js,然後加了一個mode引數,根據mode引數的不同幫我們新增一些預置的打包規則。

循序漸進

上述的流程裡,只是體驗了一把零配置的感覺,連html檔案都沒有,這裡開始加上。

加個index.html

在根目錄新建 index.html,隨便編寫點內容

type nul > index.html
複製程式碼

說到處理 html 檔案,肯定少不了 html-webpack-plugin, 安裝它

npm i -D html-webpack-plugin
複製程式碼

然後再專案根目錄新建一個 webpack.config.js,webpack會自動使用它

type nul > webpack.config.js
複製程式碼

webpack.config.js的內容如下

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    module: {
        rules: []
    },
    plugins: [
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: 'index.html'
        })
    ]
}
複製程式碼

執行npm run dev,即可看到 dist資料夾多了個index.html,這個index.html自動引入了打包後的dist/main.js

加個本地伺服器

index.html 是生成了,可總不能每次手動開啟它在瀏覽器裡面預覽吧, webpack 官方推薦我們用 webpack-dev-server做伺服器,安裝它

npm i -D webpack-dev-server
複製程式碼

安裝成功後, 修改webpack.config.js,新增 devServer 選項 和 webpack.HotModuleReplacementPlugin 外掛。

對於檔案中已經新增過的內容,後面我都會用註釋表示。

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

function resolve(dir) {
    return path.join(__dirname, './', dir)
}

module.exports = {
    // module - 略
    devServer: {
        contentBase: resolve('dist'), // 根目錄
        hot: true,   // 是否開啟熱替換,無須手動重新整理瀏覽器
        port: 8081,    // 埠
        open: true,     // 是否自動開啟瀏覽器
        noInfo: true   // 不提示打包資訊,錯誤和警告仍然會顯示
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        // HtmlWebpackPlugin - 略
    ]
}
複製程式碼

然後修改 package.json scriptsdev 選項

"dev": "webpack-dev-server --mode=development",
複製程式碼

注意:當 devServerhot引數為true時,記得要在外掛裡新增new webpack.HotModuleReplacementPlugin(), 或者你可以在命令列中帶上hot引數,這樣就不需要自己再往plugins中新增外掛了。

"dev": "webpack-dev-server --hot --mode=development"
複製程式碼

然後npm run dev,就可以嘗試靜態資源熱替換功能了。

處理js, css 等其他靜態資源

首先我們要清楚一點,webpack 它本身是不知道應該如何處理靜態資源的,但是它提供了loaderplugin機制。

loader 的作用,顧名思義:載入器,就是匹配到的靜態資源,都要經過loader的內部處理,再返回處理之後的結果。我覺得,loader像是一個攔截器。

說到js,我們會想到 babel-loaderbabel-loader是幹嗎的?常規操作是,將匹配到的js檔案的ES6程式碼 根據 babelrc檔案內的配置 編譯成對應的 ES5程式碼。

我們這裡先新增一個.babelrc檔案

新增.babelrc檔案

type nul > .babelrc
複製程式碼

編輯 .babelrc 內容

{
    // 預設定,告訴Babel要轉換的原始碼使用了哪些新的語法特性
    // targets, useBuiltIns 等選項用於編譯出相容目標環境的程式碼
    // 其中 useBuiltIns 如果設為 "usage"
    // Babel 會根據實際程式碼中使用的 ES6/ES7 程式碼,以及與你指定的 targets,按需引入對應的 polyfill
    // 而無需在程式碼中直接引入 import '@babel/polyfill',避免輸出的包過大,同時又可以放心使用各種新語法特性。
    "presets": [
        ["@babel/preset-env", {
            // modules 是否 將 ES6 的 import/export模組化 轉為 babel 的 CommonJs 規範模組化
            "modules": false,
            "targets": {
                // "> 1%" : 支援市場份額超過1%的瀏覽器, 
                // ""last 2 versions"": 支援每個瀏覽器最後兩個版本
                // "not ie <= 8": 在之前條件的瀏覽器中,排除 IE8 以下的瀏覽器
                "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
            },
            "useBuiltIns": "usage"
        }]
    ],
    // 所用外掛
    // transform-runtime 外掛表示不管瀏覽器是否支援ES6,只要是ES6的語法,它都會進行轉碼成ES5
    // 這個是需要優化的
    "plugins": ["@babel/plugin-transform-runtime"]
}
複製程式碼

安裝 babel 依賴,注意:

babel 7+ 已經廢棄了presets 中 stage-x 的用法,改為在plugins中新增。並且應用了npm scope包,程式碼全部在 @babel 中,避免以前那種 babel-preset-xxx, babel-plugin-xxx 的用法

最新的 babel-loader 版本是8+,需要依賴 babel-core 版本7+,包名為 @babel/core, 版本6+的包名為 babel-core

再分析上面的 .babelrc 檔案,它用到了@babel/preset-env, @babel/plugin-transform-runtime, 這些依賴都要我們安裝好

如果使用了 @babel/preset-env,則不支援在 plugins 中 新增 stage-x

npm i -D babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime
複製程式碼

談到css,我們就應該 想到 style-loadercss-loader。先安裝它們

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

再安裝 url-loader 用於解析靜態資源,如圖片,字型等

npm i -D url-loader
複製程式碼

然後修改 webpack.config.jsrules, 新增如下程式碼

module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader', 
                include: [
                    resolve('src'),
                    resolve('node_modules/webpack-dev-server/client')
                ]
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
                loader: 'url-loader',
                exclude: [],
                options: {
                    limit: 10000,
                    name: 'img/[name].[hash:7].[ext]'
                }
            }
        ]
    },
    // devServer - 略
    // plugins - 略
}
複製程式碼
接下來準備開發了,用 vue 吧。

vue 就不用裝在 devDependencies 中了。

npm i -S vue 
// vue-loader 依賴 vue-template-compiler 和 vue-style-loader
npm i -D vue-loader vue-template-compiler vue-style-loader
複製程式碼

修改 webpack.config.js, 新增 如下程式碼

const { VueLoaderPlugin } = require('vue-loader');

module.exports = {
    resolve: {
        extensions: ['.js', '.vue', '.json'],
        alias: {
            '@': resolve('src')
        }
    },
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            
            // 其他 - 略
        ]
    },
    // devServer - 略
    plugins: [
        // 加在最前面
        new VueLoaderPlugin()
        
        // 其他 - 略
    ]
}
複製程式碼

src 下 新建一個 views 目錄 和 assets 目錄,

我在 assets 目錄下,增加了一個 logo.png 檔案

views下建立一個 myTest 元件,myTest/index.vue, 編輯 index.vue

<template>
    <div>
        <i class="logo"></i>
    </div>
</template>

<script>
export default {
    name: 'myTest'
}
</script>

<style scoped>
    .logo {
        display: block;
        margin: auto;
        width: 400px;
        height: 400px;
        background: url(../../assets/logo.png);
    }
</style>
複製程式碼

src 目錄下新建一個 App.vue, 內容如下

<template>
  <div id="app">
    <my-test></my-test>
  </div>
</template>

<script>
import myTest from "./views/myTest/index";

export default {
  name: "App",
  components: {
    myTest
  }
};
</script>
複製程式碼

編輯 src 目錄下的 index.js,內容如下:

import Vue from 'vue';

import App from './App';

new Vue({
    el: '#app',
    render: h => h(App)
})
複製程式碼

最後npm run dev,檢視效果。

錦上添花

新增 打包進度條 資訊,如下
npm i -D progress-bar-webpack-plugin
複製程式碼

修改 webpack.config.js

const ProgressBarPlugin = require('progress-bar-webpack-plugin');

// ....

plugins: [
    // 其他 - 略
    new ProgressBarPlugin()
]
複製程式碼
新增 打包結果訊息通知
npm i -D webpack-build-notifier
複製程式碼

修改 webpack.config.js

const WebpackBuildNotifierPlugin = require('webpack-build-notifier');

// ....

plugins: [
    // 其他 - 略
    new WebpackBuildNotifierPlugin()
]
複製程式碼
歸類打包資訊
npm i -D webpack-dashboard
複製程式碼

修改 webpack.config.js

const DashboardPlugin = require('webpack-dashboard/plugin');

// ....

plugins: [
    // 其他 - 略
    new DashboardPlugin()
]
複製程式碼

修改 package.json

"dev": "webpack-dashboard -- webpack-dev-server --mode=development"
複製程式碼

這個我使用了,感覺效果不是很理想啊,會新開一個視窗,而且還不能滾動檢視資訊,不清楚是不是哪裡用錯了。

效果如圖:

webpack4搭一個簡易SPA應用

整個程式碼結構如圖:

webpack4搭一個簡易SPA應用

尚未完成的功能

  1. production 環境,需要使用 mini-css-extract-pluginoptimize-css-assets-webpack-plugin 外掛,抽離並優化 css 檔案
  2. production 環境,需要使用 UglifyJsPlugin 外掛,壓縮 js 檔案,這個外掛允許多核編譯
  3. production 環境,需要使用 optimization 選項 splitChunks
  4. vue-routervuex 的引入
  5. 等等

寫在最後

希望本文的流程能幫助到有需要的讀者,另外本文的打包功能實現的比較粗糙,打包速度比較慢,如果看官有啥建議,請在評論下告知下我。

如果有錯誤的地方,還請指出。謝謝閱讀。

程式碼地址 spa-webpack-demo

參考

babel官方文件

Webpack4 +babel7 多入口配置詳細教程

相關文章