因为需要做一个webpack+angularjs+typescript的项目,angularjs(以下都称为angular)之前看过一点,那么首先遇到的问题就是我们怎么使用webpack来搭建一个angular的开发环境。
因为不是很了解typescript,所以打算先用js做
起步
首先我们根据webpack官网的起步来初步认识一下webpack的配置和使用。
简单了解了一下之后,我遇到两个问题:
- 怎么引入angular依赖
- 怎么引入bootstrap这种全局依赖
过去使用yeoman来搭建angular开发环境时,使用的是bower下载安装依赖,并使用grunt/gulp将js和css文件注入到html当中
这时angular被作为全局依赖加入到全局作用域,我们可以在任意文件中调用angular这个全局对象
而在使用webpack的项目中,我们可以看到所有的依赖都是通过import/require加载进来,这让刚接触的我感到十分困惑
之后参考了一些其他项目,找到了解决办法
引入angular
实际上引入angular很容易,只需要在用到的地方import一下就可以了
import angular from 'angular'
打包第三方依赖
对于bootstrap这种全局依赖项,我希望能够单独打包成一个js文件,与我们的开发文件分离开来
有一种解决方案是,单独再创建一个入口文件,在这里导入我们需要的全局依赖,然后单独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: '/'
}
}
导入angular-module
有时我们需要在声明module时导入依赖
这个时候我们再在我们的入口文件中导入我们刚才导出的数组就好啦
import Dependence from './externals';
const app = angular.module('myApp', Dependence);
可以试一试material组件,发现已经可以使用了
这里不会导入material的css文件,需要自己导入,后面会讲怎么操作,可以先用cdn
引入CSS文件
注意:很多配置文件都会这么写
module.export = {
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
]
},
}
当你导入自己的css文件时会发现你的类名变成了很奇怪的样子
div{
width: 80%;
margin: auto;
}
.vZRtpaKB-zShEJMBqrZ7B{
color: red;
}
这里其实是使用了css module
如果你不需要使用css module
,或者说你是一个像我一样的小白,可以先把options
去掉,这样导入的css文件就和我们以前用的一样了
了解css module
module.export = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader','css-loader']
}
]
},
}
导入库文件的css
前面提到我们在导入material的时候是不会导入他的css文件的,如果你直接在html中link node_module中的文件,那么在你使用打包后的文件时,会报一个MIME type的错误,实际上这是对服务器静态资源访问的限制。我们需要设置一个静态资源文件夹,来存放网页中使用到的静态文件(如css)
报MIME type错误,实际上是对服务器静态资源访问的限制这个说法不一定正确
这里我们要用到一个插件,帮我们把css文件复制到打包目录下
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文件了
优化
删除相同引用
我们可能会在很多文件中引入相同的依赖,比如jQuery
这时,webpack会打包多份jQuery库到我们的bundle文件当中,这不是我们希望发生的
版本问题
webpack.optimize.CommonsChunkPlugin
已经不再被支持,需要替换为config.optimization.splitChunks
于是我们现在要使用config.optimization.splitChunks
来解决这个问题
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "externals",
chunks: "all"
}
}
}
},
打包之后我们会发现现在的app.bundle.js文件只有12KB大小,说明已经不再包含angular库文件了。控制台也不会再有WARNING: Tried to load AngularJS more than once.
的提示了。
压缩文件
当一个项目上线发布时,我们还需要考虑到文件的大小。目前我们的externals文件还有3.5MB大小,加载起来比较困难。
调整配置
这时我们就不应该使用mode:'development'
了,将其切换成mode:'production'
,同时也可以考虑关掉source-map
这时再打包,我们发现app.bundle.js文件已经只有2KB了,而externals.bundle.js文件也缩小为731KB
压缩时出现的问题
这时我们起一个服务器看看打包出来的文件,会发现报了一个错误
[$injector:unpr] Unknown provider: tProvider <- t <- MainCtrl
这是由于压缩时简写了参数名引起的,看下面这个例子
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,所以出现了错误
正确的写法是这样
app.controller('MainCtrl', ['$scope', '$mdDialog', function ($scope, $mdDialog){
...
}
这样前面的$scope, $mdDialog
和后面传入的参数已经对应起来,就不会无法识别啦
也就是说,无论后面传入的是什么名字,都会和前面数组中的参数一一对应
参考
webpack文档
generator-ts-angular
material-start