現在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"
}複製程式碼
我這裡的angular
和angular-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.js
和webpack.prod.config.js
兩個檔案。但是還是有些配置是兩個檔案都會用到的,本著複用的精神,所以我們還有一個 webapck.base.config.js
來記錄公共的配置。
webpack基本結構
webpack的配置主要分為幾個部分:
- webpack打包檔案的入口(entry)。
- webpack打包完檔案後輸出的出口(output)。
- webpack在打包檔案時的模組配置(module)。
- 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…
有什麼問題或者不對的地方歡迎指出,謝謝閱讀!
本文原創,未經授權請勿轉載。