因為需要做一個webpack+angularjs+typescript的項目,angularjs(以下都稱為angular)之前看過一點,那么首先遇到的問題就是我們怎么使用webpack來搭建一個angular的開發(fā)環(huán)境。
因為不是很了解typescript,所以打算先用js做
起步
首先我們根據(jù)webpack官網(wǎng)的起步來初步認(rèn)識一下webpack的配置和使用。
簡單了解了一下之后,我遇到兩個問題:
- 怎么引入angular依賴
- 怎么引入bootstrap這種全局依賴
過去使用yeoman來搭建angular開發(fā)環(huán)境時,使用的是bower下載安裝依賴,并使用grunt/gulp將js和css文件注入到html當(dāng)中
這時angular被作為全局依賴加入到全局作用域,我們可以在任意文件中調(diào)用angular這個全局對象
而在使用webpack的項目中,我們可以看到所有的依賴都是通過import/require加載進來,這讓剛接觸的我感到十分困惑
之后參考了一些其他項目,找到了解決辦法
引入angular
實際上引入angular很容易,只需要在用到的地方import一下就可以了
import angular from 'angular'
打包第三方依賴
對于bootstrap這種全局依賴項,我希望能夠單獨打包成一個js文件,與我們的開發(fā)文件分離開來
有一種解決方案是,單獨再創(chuàng)建一個入口文件,在這里導(dǎo)入我們需要的全局依賴,然后單獨output一個打包后的依賴庫文件
externals文件
import 'angular';
import 'angular-ui-router';
import 'angular-aria';
import 'angular-animate';
import 'angular-messages';
import 'angular-material';
export default ['ui.router','ngMaterial'];
webpack配置文件
module.export = {
entry: {
externals: resolve(__dirname, 'app', 'externals', 'index.js'),
app: resolve(__dirname, 'app', 'index.js')
},
output: {
path: resolve(__dirname, 'build'),
filename: '[name].bundle.js',
publicPath: '/'
}
}
導(dǎo)入angular-module
有時我們需要在聲明module時導(dǎo)入依賴
這個時候我們再在我們的入口文件中導(dǎo)入我們剛才導(dǎo)出的數(shù)組就好啦
import Dependence from './externals';
const app = angular.module('myApp', Dependence);
可以試一試material組件,發(fā)現(xiàn)已經(jīng)可以使用了
這里不會導(dǎo)入material的css文件,需要自己導(dǎo)入,后面會講怎么操作,可以先用cdn
引入CSS文件
注意:很多配置文件都會這么寫
module.export = {
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
]
},
}
當(dāng)你導(dǎo)入自己的css文件時會發(fā)現(xiàn)你的類名變成了很奇怪的樣子
div{
width: 80%;
margin: auto;
}
.vZRtpaKB-zShEJMBqrZ7B{
color: red;
}
這里其實是使用了css module如果你不需要使用css module,或者說你是一個像我一樣的小白,可以先把options去掉,這樣導(dǎo)入的css文件就和我們以前用的一樣了
module.export = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader','css-loader']
}
]
},
}
導(dǎo)入庫文件的css
前面提到我們在導(dǎo)入material的時候是不會導(dǎo)入他的css文件的,如果你直接在html中l(wèi)ink node_module中的文件,那么在你使用打包后的文件時,會報一個MIME type的錯誤,實際上這是對服務(wù)器靜態(tài)資源訪問的限制。我們需要設(shè)置一個靜態(tài)資源文件夾,來存放網(wǎng)頁中使用到的靜態(tài)文件(如css)
報MIME type錯誤,實際上是對服務(wù)器靜態(tài)資源訪問的限制這個說法不一定正確
這里我們要用到一個插件,幫我們把css文件復(fù)制到打包目錄下
plugins:[
new CopyWebpackPlugin([
{ from:'node_modules/angular-material/angular-material.min.css',to:'assets'},
{ from:'node_modules/bootstrap/dist/css/bootstrap.min.css',to:'assets'}
])
]
之后我們就可以在html文件中引用同目錄assets文件夾中的css文件了
優(yōu)化
刪除相同引用
我們可能會在很多文件中引入相同的依賴,比如jQuery
這時,webpack會打包多份jQuery庫到我們的bundle文件當(dāng)中,這不是我們希望發(fā)生的
版本問題
webpack.optimize.CommonsChunkPlugin已經(jīng)不再被支持,需要替換為config.optimization.splitChunks
于是我們現(xiàn)在要使用config.optimization.splitChunks來解決這個問題
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "externals",
chunks: "all"
}
}
}
},
打包之后我們會發(fā)現(xiàn)現(xiàn)在的app.bundle.js文件只有12KB大小,說明已經(jīng)不再包含angular庫文件了??刂婆_也不會再有WARNING: Tried to load AngularJS more than once.的提示了。
壓縮文件
當(dāng)一個項目上線發(fā)布時,我們還需要考慮到文件的大小。目前我們的externals文件還有3.5MB大小,加載起來比較困難。
調(diào)整配置
這時我們就不應(yīng)該使用mode:'development'了,將其切換成mode:'production',同時也可以考慮關(guān)掉source-map
這時再打包,我們發(fā)現(xiàn)app.bundle.js文件已經(jīng)只有2KB了,而externals.bundle.js文件也縮小為731KB
壓縮時出現(xiàn)的問題
這時我們起一個服務(wù)器看看打包出來的文件,會發(fā)現(xiàn)報了一個錯誤
[$injector:unpr] Unknown provider: tProvider <- t <- MainCtrl
這是由于壓縮時簡寫了參數(shù)名引起的,看下面這個例子
app.controller('MainCtrl', function ($scope, $mdDialog){
...
}
// 壓縮之后
.controller("MainCtrl",function(t,e){this.hello="hello",t.showAlert=function(t){e.show(e.alert().parent(o.a.element(document.querySelector("#root"))).clickOutsideToClose(!0).title("This
我們可以看到原本的mdDialog被替換成了t和e,而angular無法識別這里的t和e,所以出現(xiàn)了錯誤
正確的寫法是這樣
app.controller('MainCtrl', ['$scope', '$mdDialog', function ($scope, $mdDialog){
...
}
這樣前面的$scope, $mdDialog和后面?zhèn)魅氲膮?shù)已經(jīng)對應(yīng)起來,就不會無法識別啦
也就是說,無論后面?zhèn)魅氲氖鞘裁疵?,都會和前面?shù)組中的參數(shù)一一對應(yīng)