上一篇 使用webpack构建AngularJs项目:起步 我们使用webpack的html-loader
和file-loader
完成了一个简单的路由。现在我们继续在这基础之上加功能。
优化控制器
我们现在的写法是将控制器写在入口js文件里面,这样当控制器多了起来之后肯定不行的,我们需要分离控制器的代码为单独的文件。
首先,我们创建login.controller.js
文件:
1 | touch src/login/login.controller.js |
然后在里面定义控制器的代码:
1 | import loginTemplate from './login.lazy.html' |
可能会感到奇怪,第二行的loginController.$inject = ['$scope']
为什么可以这样写?
首先,我们可以在声明loginController
之前写这句话是利用了变量提升的特性。然后这里的$inject
是angularJs的显式注入声明。
这里需要解释下angularJs有三种依赖注入的方式:
- 推断式注入声明 如果没有明确的声明,angularJs会假定参数名称就是依赖的名称。但是需要注意的是,这种方式只适用于未经过压缩和混淆的代码,因为angularJs需要原始的参数列表来进行解析需要注入的依赖。
- 行内注入声明 当我们在声明控制器的函数定义时,可以通过行内传参的形式将依赖传入进去,它可以避免在定义过程中使用临时变量。行内声明的方式允许我们直接传入一个参数数组,而不是一个函数。数组的元素是字符串,它们代表可以被注入到对象中的依赖的名字,最后一个参数就是依赖注入的目标函数本身。
- 显式注入声明 angularJs提供了
$inject
属性来显式注入依赖。函数对象的$inject
属性是一个数组,数组元素的类型是字符串,他们的值就是需要被注入的依赖。
我们在login.controller.js
里面同时也引入了模板文件,然后通过export
暴露出去。然后我们编辑src/index.js
文件:
1 | import angular from 'angular'; |
这种方式的好处是,我们需要管理login
页面的之后直接去login的文件管理即可,用到的模板文件和js代码都在各自的区域里面进行关联。
上ES6!
到现在我们写的都是es5的语法,我们希望支持class
相关的东西。那需要用到babel,先安装babel
、preset
、babel-loader
:
1 | yarn add -D @babel/core @babel/preset-env babel-loader |
需要注意下,我们这里安装的时候直接是默认的babel7版本,所以对应的是@babel/core
,我刚开始的时候安装成了babel-core
,走了弯路。。。
@babel/preset-env
是一个智能预设,允许使用最新的JavaScript语法,而无需去关心细微之处,更简便而且包更小。
然后创建babel的配置文件:
1 | touch .babelrc |
并写入:
1 | { |
然后在webpack.config.js
中加上对js文件的解析:
1 | rules: [ |
这样webpack的部分就完成了,我们需要去写一个es6语法的服务来测试下。
我们创建文件src/services/http.services.js
:
1 | touch src/services/http.services.js |
然后用es6的class
语法创建一个服务:
1 | export default class HttpService { |
我们随便写一个方法login
,这个方法里面发出了一个get请求,请求地址是百度(暂时先不管它能不能请求成功。。。)。
然后在src/index.js
注册这个服务:
1 | import httpService from './services/http.service'; |
然后在src/login/login.controller.js
里面去使用这个服务:
1 | loginController.$inject = ['$scope', 'httpService']; |
重新启动yarn start
,然后我们访问login
路由,可以看到页面正常加载的同时,在控制台这边是有个baidu.com
的get请求的,虽然是被跨域拦截了。。。
这证明我们可以在写js文件的时候使用es6的语法了,并且转换也是成功的。
那么也可以打包下看看最后生成的dist
目录中是怎样的。
1 | yarn build |
然后找到app.xxxxxxx.js
的打包文件,可以找到HttpService
的定义:
1 | var HttpService = /*#__PURE__*/function () { |
可以看到,生成的就是目标js语法。而且这里在运行的时候$http
也是正用的推断式注入。nice~
上压缩
我们前面查看的就是打包后的文件,里面还是人可以阅读的,这代表着任何人可以访问网站后可以获取到源代码并分析,所以我们需要将打包后的文件进行压缩。
我们使用的是webpack5,所以直接支持optimization.minimize
属性进行设置是否压缩。如果设置为true的话,就使用webpack的TerserPlugin
插件或optimization.minimizer
中指定的插件进行压缩打包。
webpack5内置了TerserPlugin
包,我们无需单独安装,所以直接设置webpack.config.js
:
1 | module.exports = { |
然后再运行yarn start
,发现点击链接login
的时候,会报错:
1 | angular.js:138 Uncaught Error: [$injector:modulerr] Failed to instantiate module myApp due to: |
果然发生了注入问题。前面在了解依赖注入的时候,确实是当使用推断式注入并且压缩打包的时候,是会有问题的。那我们来解决这尴尬的问题。
要么采用显式注入,要么退回到原来的声明式写法。但这和我们的初衷相差甚远,成年人,我们当然是两者都要!
我们需要babel-plugin-angularjs-annotate
,它支持向Babel处理的ES5/ES6
代码添加angularJs的依赖注入关系,支持显式注入和典型的隐式注入。
安装babel-plugin-angularjs-annotate
:
1 | yarn add -D babel-plugin-angularjs-annotate |
然后在.babelrc
中添加:
1 | { |
然后我们需要在src/services/http.service.js
中的构造函数中加上"ngInject";
的标记:
1 | export default class HttpService { |
然后再运行,就可以看到正常了。
我们也将login.controller.js
的显式注入的写法替换为标记的方式:
1 | import loginTemplate from './login.lazy.html' |
使用babel-plugin-angularjs-annotate
后,它是可以处理一般的隐式注入的,需要特殊加标记的情况为:
- 导出类并在另一个文件/模块中使用时
- 声明为对象属性的方法的隐式注入时
- 导出为一个方法的隐式注入时
- 箭头函数声明方法赋值给变量时:
var x = /* @ngInject */ ($scope) => {};
很方便。
到目前为止,我们的webpack打包angularJs已初见模型了,接下来是处理样式文件以及资源文件,再接再厉!