基於webpack構建的angular 1.x 工程(一)webpack篇

homerious發表於2019-03-04

  現在AngularJS都已經出到4.x的版本了,可我對它的認識還是停留在1.x的版本。
  之前用它是為了搭配ionic來寫web手機天氣 應用(用來應付我大學裡一門學科的課設的︿( ̄︶ ̄)︿)。之後就因為它太難學而沒有繼續深入下去。
  現在就職的公司也有個專案是做混合式的手機app的,居然也是用AngularJS+ionic來做的,而且也是用1.x的版本。
  本來沒我什麼事的,我這段時間都在用Vuejs。然後上頭發現那個專案載入是在太慢了,問我有沒有優化的方法。我看了下專案工程結構,發現是用gulp打包的一個工程。可能剛開始做這個專案的時候沒掌握好要點,導致整個專案臃腫不堪。gulp我是不會的了,由於一直在用Vuejs,官方cli提供的模板就是用webpack打包的,而且我之前寫ReactJS用的也是webpack來打包的。因此,我就用了webpack來重構一下工程。然後寫下這篇詳細的文章,想給可能會同樣遇到的這種問題的朋友做一個參考( • ̀ω•́ )✧。 另外,本文也可以當做webpack的一篇入門文章。

首先,要先配置好工程檔案。

  我先列一下我的package.json裡的配置:

{
  "name": "angular-ionic-webpack",
  "version": "1.0.0",
  "description": "a project base on angular 1.x and webpack",
  "main": "index.js",
  "scripts": {
    "build": "webpack --config ./build/webpack.prod.config.js",
    "dev": "set NODE_ENV=dev&& webpack-dev-server --config ./build/webpack.dev.config.js"
  },
  "devDependencies": {
    "css-loader": "^0.26.4",
    "extract-text-webpack-plugin": "^3.0.1",
    "html-loader": "^0.4.4",
    "html-webpack-plugin": "^2.24.1",
    "style-loader": "^0.13.1",
    "url-loader": "^0.5.7",
    "webpack": "^3.1.0",
    "webpack-dev-server": "^2.9.2",
    "webpack-manifest-plugin": "^1.3.2",
    "webpack-merge": "^4.1.0"
  },
  "dependencies": {
    "angular": "1.4.3",
    "angular-cache": "^4.5.0",
    "angular-cookies": "1.4.12",
    "angular-ui-router": "^0.3.2",
    "jquery": "^3.2.1"
  },
  "author": "homer",
  "license": "MIT"
}複製程式碼

  第一個首先是專案直接用到的依賴,也就是dependencies裡的東西
分別有:

 "dependencies": {
    "angular": "1.4.3", 
    "angular-cache": "^4.5.0",
    "angular-cookies": "1.4.12",
    "angular-ui-router": "^0.3.2",
    "jquery": "^3.2.1"
  }複製程式碼

  我這裡的angularangular-cookies都用了具體的版本(就是版本號前面沒有用符號^,直接寫數字1.4.3),因為不合版本的這兩個東西會跟ionic裡的angular-ui-router發生衝突導致渲染失敗。
  而我這裡也沒有裝ionic,是因為我直接引用的時候會報can`t resolve `ionic`的錯誤,我也不知道為什麼,所以我是直接呼叫了/app/assets/lib裡的ionic.bundle.min.js來引入的。請有找到原因的朋友麻煩告知一下我是為什麼。

  接下來是開發時用到的依賴:

"devDependencies": {
    "css-loader": "^0.26.4",
    "extract-text-webpack-plugin": "^3.0.1",
    "html-loader": "^0.4.4",
    "html-webpack-plugin": "^2.24.1",
    "style-loader": "^0.13.1",
    "url-loader": "^0.5.7",
    "webpack": "^3.1.0",
    "webpack-dev-server": "^2.9.2",
    "webpack-manifest-plugin": "^1.3.2",
    "webpack-merge": "^4.1.0"
  },複製程式碼

  各種loader是必要的,因為webpack在打包的時候會把你專案裡的非js檔案轉換出來然後打包在一起。我們常用的loader有css-loader,url-loader,這兩個分別是解析css和圖片的。然後其他的loader我們要看專案需求來按需選取,比如我這裡因為是angular 1.x的專案,裡面還有挺多的html模板,所以我這裡用到了html-loader來解析html。
  其次是webpack——說句題外話,其實webpack一般都是用最新的,因為打包的環境跟所用的框架其實沒有太多互相干擾的地方。我一開始想著用的1.x的webpack發現用起來不怎麼方便,於是又改回最新的3.x。這也算是個人的一個小小心得吧。然後我們還用了webpack-dev-server。這個是在我們開發的時候用的伺服器,可以熱替換更新程式碼。很方便,至於怎麼用我後面會詳細講。然後就是webpack-merge這個東西是用來合併webpack配置,這個是在vue專案裡看到,感覺還挺好用,就也模仿著用了。最後就是各種外掛,extract-text-webpack-plugin這個用來把css樣式獨立打包成一個css檔案的外掛,沒有它的話,樣式只會注入index.html做內聯樣式;html-webpack-plugin是用於把js注入到index.html裡;webpack-manifest-plugin是用來生成網頁的manifest檔案的。
  然後是寫啟動webpack的命令列,也就是上面的:

"scripts": {
    "build": "webpack --config ./build/webpack.prod.config.js",
    "dev": "set NODE_ENV=dev&& webpack-dev-server --config ./build/webpack.dev.config.js"
  },複製程式碼

這樣寫的意思是,當你輸入npm run + 你的命令名字就會讓npm執行你對應命令的語句。比如輸入npm run dev,相當於你執行了上面那條dev對應的set NODE_ENV=dev&& webpack-dev-server --config ./build/webpack.dev.config.js"這條語句。這裡dev命令執行的是開發版本打包並生成開發的伺服器;build命令執行的則是生產版本打包。在打包開發版本的時候,用的是webpack-dev-server,我們讓它按照./build/webpack.dev.config.js裡的配置(下文會提到)來執行。在打包生產環境,是直接執行webpack,讓它按照./build/webpack.prod.config.js裡的配置來執行。

  關於這份package.json裡其他的配置有問題的可以在評論裡提哈~~

然後來寫webpack的配置檔案

概述

  安裝了webpack,我們要配置好,讓它按照我們的期望來工作。
一般我們都會用 webpack.config.js來命名webpack的配置檔案,以免和其他配置檔案搞混。但是由於我們一般都會分開開發環境和生產環境,而對於這個兩個環境打包我們要求會有點不一樣。開發環境我們希望它可以直接模仿生產環境放上伺服器測試;
然後又想它可以一有改動就會自動打包更新顯示在頁面,不用我們手動重新整理瀏覽器;
不希望它打包花的時間太長;如果出錯會有相應的提示等等。而生產環境我們想盡量壓縮檔案大小,生成manifest檔案等等。因此,我們就需要把開發打包和生產打包的配置分開來。這裡我們就分開了 webpack.dev.config.jswebpack.prod.config.js兩個檔案。但是還是有些配置是兩個檔案都會用到的,本著複用的精神,所以我們還有一個 webapck.base.config.js來記錄公共的配置。

webpack基本結構

  webpack的配置主要分為幾個部分:

  1. webpack打包檔案的入口(entry)。
  2. webpack打包完檔案後輸出的出口(output)。
  3. webpack在打包檔案時的模組配置(module)。
  4. webpack在打包檔案時用到的外掛(plugin)。

  這四個是webpack配置的基本部分,寫好了這四個基本可以打包成功了。
還有其他要用的配置後面會說到,其他配置沒用到的可以看一下官方的文件(3.x的官網)/ (個人覺得翻譯的比較好的中文文件)。接下來我們先來分析下開發環境和生成環境共用的部分配置。首先入口檔案一般都是一樣的吧?然後打包時模組配置也是一樣的,因為你打包時的檔案都是一樣的,所以設定也是一樣的。所以我們webpack.base.config.js是這樣寫的:

var path = require(`path`);
var root = path.resolve(__dirname, `../`);

module.exports = {
    entry: {
        `main`: root + `\app\index.js`,
        jquery:[`jquery`],
        ionic:root+`\app\assets\lib\ionic\release\js\ionic.bundle.min.js`,
        datepicker:root+`\app\assets\lib\ionic-datepicker\release\ionic-datepicker.bundle.min.js`,
        calendar_pk:root+`\app\assets\lib\calendar-pk\release\js\calendar_pk.min.js`
    },
    module: {
        loaders: [
            {
                test: /.(png|jpe?g|gif|woff|svg|eot|ttf)(?.*)?$/,
                loader: `url-loader`,
                query: {
                    limit: 10000,
                }
            },
            {
                test: /.html$/,
                loader: `html-loader`
            }

        ]
    },
    resolve: {
        extensions: [`.js`, `.json`]
    }

};複製程式碼

  入口(entry)檔案其實不一定是隻有一個,我這裡就有多個。只要路徑分開寫正確就可以了。
  然後是模組(module)配置,webpack的思想是把工程所有的js都是模組,然後全部打包在一起。所以遇到一些非js會有麻煩。但是他們早就預料這種情況,做出了一些系列的loader(載入器)來把一些非js檔案做成webpack能打包的東西。
一般都會用到的是css-loader, url-loader。這兩個分別用來解析專案裡.css和圖片字型之類的檔案。上面說過,由於專案中會有較多的.html檔案要引用,所以我們還用了 html-loader。我這裡還有一個 resolve(解析)的配置,這個是用來js裡引用檔案的時候,不寫字尾的話,webpack就會自動為其加上.js或.json的字尾,可以省一些寫字尾的時間(✧◡✧)。

開發打包配置

  我們的開發打包配置是這樣的:

var baseconf = require(`./webpack.base.config`);
var merge = require(`webpack-merge`);
var HtmlWebpackPlugin = require(`html-webpack-plugin`);
var webpack = require(`webpack`);
var server = require(`./configDevServer`);
var path = require(`path`);
var root = path.resolve(__dirname, `../`);

var plugins = [
    new webpack.DefinePlugin({
        `process.env`: {
            NODE_ENV: JSON.stringify("development")
        }
    }),
    new webpack.optimize.UglifyJsPlugin({
        compress: {
            warnings: false
        }
    }),
    new webpack.ProvidePlugin({
        $: `jquery`,
        jQuery: `jquery`,
        `window.jQuery`: `jquery`,
        `window.$`: `jquery`
    }),
    new webpack.optimize.CommonsChunkPlugin({
        name: `vendor`, // 這公共程式碼的chunk名為`commons`
        filename: `[name].bundle.js`, // 生成後的檔名,雖說用了[name],但實際上就是`commons.bundle.js`了
        minChunks: 3, // 設定要有4個chunk(即4個頁面)載入的js模組才會被納入公共程式碼。這數目自己考慮吧,我認為3-5比較合適。
    }),
    new HtmlWebpackPlugin({
            filename: `index.html`,
            template: `index.html`,
            inject: true
    }),
    new webpack.HotModuleReplacementPlugin()
];
baseconf.module.loaders.push(
    {
        test: /.css$/,
        loader: [`style-loader`,`css-loader`],
    }
);
module.exports = merge(baseconf, {
    output: {
        path: root+"/dist",
        publicPath: "/",
        filename: "./js/[name].[chunkhash].js"
    },
    devtool: `cheap-module-eval-source-map`,
    devServer: server,
    plugins: plugins,
});複製程式碼

  我們首先把基本的配置引進來。然後寫外掛(plugin),畢竟我們開發配置想實現的功能有部分需要外掛來做。
webpack.DefinePlugin是用來讓webpack知道正在準備的是開發環境打包。某些框架會識別開發和生產環境,然後在我們開發的時候會給出相應的警告和提示,而在生產環境則會遮蔽這些內容。webpack.ProvidePlugin是當我們用到 jQuery之類的js庫的時候,用到的相關符號都會自動進行引用,不會導致報錯。webpack.optimize.CommonsChunkPlugin是用來提取我們程式碼裡的公共用到的部分,避免程式碼重複打包,減少程式碼體積。webpack.HotModuleReplacementPlugin是用來啟用我們的程式碼熱替換功能,在我們改了程式碼之後開發伺服器可以重新打包更新,瀏覽器自動重新整理,把我們的改動顯示在頁面。HtmlWebpackPlugin是我們自己安裝的外掛,用來把生成的js自動插入到我們的html模板裡面。
  寫完了外掛之後,我們還要寫輸出(output)。這裡指定下輸出資料夾和輸出的js名字即可。然後是是開發工具(devtool)和開發伺服器(dev-server),開發工具的意思是,webpack會根據打包的檔案做出一個標識的map檔案,如果程式碼出錯的話,它會找出來,然後提示在什麼地方。方便修改程式碼。開發伺服器是一個建立在本地的伺服器,上面就是你的專案。搭配熱替換功能,開發會很方便。這裡順帶簡單介紹下,開發伺服器配置 ./build/configDevServer.js

const server={
    contentBase:`/dist/`,
    host: `localhost`,//服務主機
    port: 8089,//埠
    inline: true, // 可以監控js變化
    hot: true, // 熱啟動
    compress: true,
    watchContentBase: true,
    proxy: {//設定代理伺服器,用於除錯介面
        `/api`:{
            target:`http://www.baidu.com`,
            pathRewrite:{"^/api": "/api"}//重寫路徑
        }
    }
};
module.exports= server;複製程式碼

  可以看上面的備註來理解對應的配置項的意思。
  上文我們裝了個 webpack-merge這時就發揮作用了。正如它的名字一樣,它會把兩個webpack配置合併起來。然後輸出。這樣我們的開發環境配置寫好了

生產環境配置

  同樣,先上配置:

var baseconf = require(`./webpack.base.config`);
var path = require(`path`);
var root = path.resolve(__dirname, `../`);
var merge = require(`webpack-merge`);
var HtmlWebpackPlugin = require(`html-webpack-plugin`);
var webpack=require(`webpack`);
var ExtractTextPlugin = require(`extract-text-webpack-plugin`);
var ManifestPlugin = require(`webpack-manifest-plugin`);
var plugins = [
    new webpack.DefinePlugin({
        `process.env`: {
            NODE_ENV:JSON.stringify("development")
        }
    }),
    new webpack.optimize.UglifyJsPlugin({
        compress: {
            warnings: false
        }
    }),
    new HtmlWebpackPlugin({
        filename: `index.html`,
        template: `index.html`,
        inject: true
    }),
    new ExtractTextPlugin({
        filename: `./css/[name].css?[contenthash:8]`,
        allChunks: true,
    }),
    new webpack.ProvidePlugin({
        $: `jquery`,
        jQuery: `jquery`,
        `window.jQuery`: `jquery`,
        `window.$`: `jquery`,
    }),
    new webpack.optimize.CommonsChunkPlugin({
        name: `commons`, // 這公共程式碼的chunk名為`commons`
        filename: `./js/[name].bundle.js`, // 生成後的檔名
        minChunks: 3,
    }),
    new ManifestPlugin(path.join(`dist`, `manifest.json`))
];
baseconf.module.rules.push(
    { test: /.css$/,
        loader: [`style-loader`,`css-loader`] }
);
module.exports=merge(baseconf,{
    output: {
    path: root+"/dist",
        publicPath: "./",
        filename: "./js/[name].[chunkhash].js"
},
    devtool: false,
    plugins: plugins
});複製程式碼

  重複的外掛我們就不說了,我們說說幾個上面沒有的外掛。 webpack.optimize.UglifyJsPlugin是用來壓縮混淆js程式碼的。ExtractTextPlugin是我們另外安裝的,用來把打包的css獨立出來成一個css檔案。使用這個外掛的時候,css的loader要相應做一下設定,所以可以看到 css-loader我沒有放到公共配置,裡面而是分開了。ManifestPlugin也是另外安裝的,用來生成manifest快取檔案,使網站可以減少對靜態資源的重複請求。另外你可以發現這裡devtool設成了false,沒有設定devserver,因為不是生產所需要的,所以沒有設定。

來跑一遍吧!

在你的入口的地方建立一個配置裡的entry規定名字的js檔案,就可以先跑一遍webpack。
如果webpack沒有報錯,就說明你的配置基本是對的。

接下來,我會就angular 1.x 用webpack打包打包遇到的坑來說一說,請看下一篇文章:

基於webpack構建的angular 1.x工程(angular篇)

想看詳細程式碼,可以訪問我的專案地址
github.com/homerious/a…

有什麼問題或者不對的地方歡迎指出,謝謝閱讀!

本文原創,未經授權請勿轉載。

相關文章