之前使用angularJs来作项目的时候用的是gulp,现在webpack比较火热,尝试用webpack来构建angularJs项目。为啥要搞angularJs?因为工作中的项目是基于angularJs的,想升级到angular,是离不开webpack的,需要一步步过渡,所以第一步是先用webpack构建angularJs项目,以达到弃用gulp和requireJs的目的。开搞!
初始化项目文件夹
先创建项目文件夹webpack-angularjs
,然后用yarn
初始化包:
1 2 3
| $ mkdir webpack-angularjs $ cd webpack-angularjs $ yarn init
|
安装对应的包:
1 2
| $ yarn add angular $ yarn add -D webpack webpack-cli webpack-dev-server
|
然后创建项目文件夹src
,以及入口html文件和js文件:
1 2 3
| $ mkdir src $ touch src/index.js $ touch src/index.html
|
然后我们在scr/index.js
中构造angular的模块和控制器:
1 2 3 4 5
| import angular from 'angular'; const app = angular.module('myApp', ['ngRoute']); app.controller('testCtrl', function ($scope) { $scope.test = '33'; })
|
然后再在src/index.html
中写入模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>webpack angularjs</title> </head> <body ng-app="myApp"> <div ng-controller="testCtrl"> {{test}} </div> </body> </html>
|
我们预定了angular的一个模块myApp
,以及一个控制器testCtrl
,和一个变量:test
。如果正常运行,我们应该在页面上看到33
这个字符。
配置webpack
接着创建webpack的配置文件:
1
| $ touch webpack.config.js
|
我们需要安装两个插件:
- clean-webpack-plugin 清理输出区域
- html-webpack-plugin 用html模板生成html文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const path = require('path'); const {CleanWebpackPlugin} = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = { entry: { app: './src/index.js' }, mode: "development", output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') }, module: { }, devtool: "inline-source-map", devServer: { contentBase: path.resolve(__dirname, './dist'), overlay: true, hot: true, quiet: true, }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ filename: "index.html", template: "src/index.html", inject: "body", }), ], };
|
然后我们在package.json
里面预定义运行和打包的命令:
1 2 3 4 5 6
| { "scripts": { "start": "webpack server", "build": "webpack --config webpack.config.js" }, }
|
我们首先运行下build
看下生成的dist
文件里面的文件是否正确:
查看dist
文件夹:
1 2 3
| dist ├── app.bundle.js └── index.html
|
然后查看dist/index.html
:
1 2 3 4 5
| <body ng-app="myApp"> <div ng-controller="testCtrl"> {{test}} </div> <script defer src="app.bundle.js"></script></body>
|
基本正确。
我们可以放心的运行起来:
然后打开localhost:8080
可以看到页面上正常的输出了。
加一个angular路由
上面这是一个很简单的一步,我们来加个路由试试看。
首先安装angular的路由模块:
1
| $ yarn add angular-route
|
涉及到路由的话就需要谈下路由的方式了。angularJs有两种路由模式:
- 标签模式 (hashbang)标签模式是html5模式的降级模式,url路径以
#
符号开头:http://xxx.com/#!/login
- html5模式 这种模式看起来和普通的url模式一样:
http://xxx.com/login
如果需要使用标签模式的话,需要在config
里面设置:
1 2 3 4
| angular.module('app', ['ngRoute']).config(['$locationProvider', function($locationProvider){ $locationProvider.html5Mode(false); $locationProvider.hashPrefix('!'); }])
|
需要使用html5模式的话需要:
1 2 3
| angular.module('app', ['ngRoute']).config(['$locationProvider', function($locationProvider){ $locationProvider.html5Mode(true); }])
|
并且我们需要在src/index.html
中加入base url
:
1 2 3 4
| <html lang="en"> <head> <base href="/"> </head>
|
我们选用html5模式,并且加入一个login页面的路由,修改src/index.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import angular from 'angular'; import ngRoute from 'angular-route'
const app = angular.module('myApp', ['ngRoute']);
app.config(function ($httpProvider, $routeProvider, $locationProvider, $controllerProvider) { $locationProvider.html5Mode(true) $routeProvider .when('/', { controller: 'homeCtrl', template: '<p>home page</p>' }) .when('/login', { controller: 'loginCtrl', templateUrl: './login/login.html', }); });
app.controller('loginCtrl', ['$scope', function($scope) { $scope.name = 'tony'; }]) app.controller('homeCtrl', ['$scope', function($scope){ $scope.title = 'home'; }])
|
我们这里给login
路由增加一个模板src/login/login.html
:
1 2 3 4
| <div class="login"> <p>login page</p> {{name}} </div>
|
然后修改src/index.html
,加上ng-view
和两个路由的链接:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <base href="/"> <title>webpack angularjs</title> </head> <body ng-app="myApp"> <a href="/login">login</a> <a href="/">home</a> <div ng-view></div> </body> </html>
|
然后查看运行的页面,发现点击home
的时候正常输出,但是点击login
的时候,是无效的,并且控制台里面有个login.html
的请求是404。
查看下webpack.config.js
,我们发现其中devServer -> contentBase
设置的是contentBase: path.resolve(__dirname, './dist')
,因为dist是webpack的打包路径,我们暂时没有处理任何关于angular模板html的设置,所以在dist
目录是没有src/login/login.html
的。
我们尝试将contentBase
修改为contentBase: path.resolve(__dirname, './src'),
,然后重新运行,发现是可以正常运行的。
这是不行的,我们到时候还是需要的是dist目录,而不是src目录。我们需要解决这个问题,有两个思路:
- 使用
html-loader
将模板文件捆绑到js文件中,这样的优点是在运行的时候直接从js中拿取模板,而不是再发请求获取模板文件,但缺点是会使打包的js包变大。
- 使用
copy-webpack-plugin
将文件按路径复制到dist
目录,缺点就是会发出请求获取模板文件。
最佳实践是,关键模板通过html-loader
打包到js中,而需要懒加载的通过file-loader
来使用require
延迟加载。
我们需要安装html-loader
和file-loader
:
1
| $ yarn add -D html-loader file-loader
|
我们将需要懒加载的模板定义为:xxx.lazy.html
,所以重命名src/login/login.html
为src/login/login.lazy.html
。
然后修改webpack.config.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| const path = require('path'); const {CleanWebpackPlugin} = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require('html-webpack-plugin'); const packageJson = require('./package.json');
module.exports = { entry: { app: './src/index.js', vendor: Object.keys(packageJson.dependencies), }, mode: "development", output: { filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.lazy\.html$/, use: [ { loader: 'file-loader', options: { name(resourcePath, resourceQuery) { if (process.env.NODE_ENV === 'development') { return '[path][name].[ext]'; } return '[contenthash].[ext]'; }, }, }, ], }, { test: /\.html$/, exclude: /\.lazy\.html$/, use: [ { loader: "html-loader", options: { minimize: true, } }, ] } ] }, devtool: "inline-source-map", devServer: { index: 'index.html', contentBase: path.resolve(__dirname, './dist'), overlay: true, hot: true, port: 3000, historyApiFallback: true, }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ filename: "index.html", template: "src/index.html", inject: "body", }), ], optimization: { splitChunks: { chunks: 'initial', minSize: 20000, minRemainingSize: 0, minChunks: 1, maxAsyncRequests: 30, maxInitialRequests: 30, enforceSizeThreshold: 50000, cacheGroups: { defaultVendors: { test: /[\\/]node_modules[\\/]/, priority: -10, reuseExistingChunk: true, }, default: { minChunks: 2, priority: -20, reuseExistingChunk: true, }, }, }, }, };
|
这里我们不仅加上了file-loader
和html-loader
,同时也配置了optimization.splitChunks
分离公共库,这样方便我们查看生成的包里面的内容。
然后修改src/index.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import angular from 'angular'; import ngRoute from 'angular-route' import homeTemplate from './home/index.html'; const app = angular.module('myApp', ['ngRoute']);
app.config(function ($httpProvider, $routeProvider, $locationProvider, $controllerProvider) {
$locationProvider.html5Mode(true) $routeProvider .when('/', { controller: 'homeCtrl', template: homeTemplate, }) .when('/login', { controller: 'loginCtrl', templateUrl: require('./login/login.lazy.html'), }); });
app.controller('loginCtrl', ['$scope', function($scope) { $scope.name = 'tony'; }]) app.controller('homeCtrl', ['$scope', function($scope){ }])
|
然后在运行的时候,发现点击login
按钮没用,没有去加载对应模板。很蛋疼。
尝试了很久,换成了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import angular from 'angular'; import ngRoute from 'angular-route' import homeTemplate from './home/index.html'; import loginTemplate from './login/login.lazy.html';
const app = angular.module('myApp', ['ngRoute']);
app.config(function ($httpProvider, $routeProvider, $locationProvider, $controllerProvider) {
$locationProvider.html5Mode(true) $routeProvider .when('/', { controller: 'homeCtrl', template: homeTemplate, }) .when('/login', { controller: 'loginCtrl', templateUrl:loginTemplate, }); }); app.controller('loginCtrl', ['$scope', function($scope) { $scope.name = 'tony'; }]) app.controller('homeCtrl', ['$scope', function($scope){ }])
|
这样,再运行看看,可以看到当点击login
链接后,在控制台是加载了login.lazy.html
文件的。
Emm。。。实践出真知啊。。。
这只是很简单的一步,很粗糙,但是总归踏出了这一步,下一步直接上ES6!