原文:https://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art074

-------------------------------------------------------

Angular CLI behind the scenes, part one

Google's Angular is a fairly popular web application development framework - although it has some competition from Facebook's React, there still seems to be quite a bit of support for it out there. Angular is on its 6th iteration now; one complaint that earlier versions got was around the amount of "boilerplate" code that you had to write to get even a simple application up and running. As of today, the preferred solution to this problem is a command-line application generator (rather than, say, a GUI wizard-based approach) called the Angular CLI. This is fine in the sense that it works, but it does quite a bit behind the scenes and expends a lot of effort hiding the details of what it did from you, the developer. On the one hand, this is helpful for beginners because everything "just works". On the other hand, this is a problem once you leave the beginner stage because any automation, by its nature, has to make a fair number of assumptions on your behalf. If you're not aware of what those assumptions are, you can end up tripping headlong over one or more of them with a completely inexplicable error. In this post, I'm going to walk through the Tour of Heroes tutorial and peel back a bit of the mystery behind the "magical" commands.

Step 1: Install the Angular CLI

Although, strictly speaking, Angular doesn't actually require Node.js, the recommended Angular workflow leans pretty heavily on Node and NPM. Node is a Javascript environment that can be run from the command line and NPM is its package manager. The first thing that the Angular Tour of Heroes tutorial asks you to do is to use Node's NPM to install the Angular CLI "globally":

npm install -g @angular/cli

If you run this without the -g (global) flag, it will do the following:

  1. Connect to (by default) the central NodeJS registry at https://registry.npmjs.org, look for the path @angular/cli and download the descriptor. If you click through, you can see that it's a very long JSON file that describes what Javascript files make up the Angular CLI and the other packages that the Angular CLI depends on. There are quite a few.
  2. Parse and process the package descriptor. One of the more important entries in the descriptor is the entry dist/tarball, which is where NPM finds the actual contents of the package.
  3. Create a directory named node_modules underneath the current directory. The Javascript code that makes up the Angular CLI, along with all of its dependencies, and their dependencies, is downloaded into it. There are quite a few dependencies: 193 packages, containing 6,515 files, at the time of this writing.

However, the tutorial instructs you to run it with the -g flag instead. If you do as it instructs, you see that a lot happens, but nothing changes in the current directory. Even stranger, although nothing has changed, the next thing that the tutorial instructs you to do is to run the command:

ng new my-app

which, if you run it, does work. If you have a useful terminal that supports a command like which, you can see where this mysterious new ng command came from:

$ which ng
/home/jdavies/.nvm/versions/node/v10.9.0/bin/ng

If you have a less useful terminal, though, you can still piece together what happened; when you install an NPM package "globally", it adds the binary contents of the package, as described by the package descriptors bin entry, underneath the bin directory of your NPM prefix:

C:\test\angular> npm config get prefix
C:\Users\jodavies.NA\AppData\Roaming\npm

If you take a peek in there, you see that everything that would have happened had you installed the Angular CLI locally was done in this centralized directory. You can see that it's not structured quite the same as a "local" install; the Angular CLI binary is installed under prefix/lib/node_modules, but it's dependencies are installed under prefix/lib/node_modules/@angular/cli/node_modules. The differences, though, are minimal, and the command-line tools keep track of them.

If you paid attention to the output of the original install invocation, it reported the location of this directory there, as well.

If for some reason, you don't want to install ng globally and have it appear in your system path, the local installation adds the executable script under node_modules/.bin — you can effectively run it from there, but you'll have to either add that directory to your path or give the relative path from the command line each time you invoke it.

Step 2: Generate the base application

So, ng new is a new command-line tool, now globally available (npm prefix/bin is in your $PATH environment variable). ng is either a shell script whose first line is:

#!/usr/bin/env node

Or a .cmd that invokes node with the CLI Javascript input. It's worth noting that you should only have to npm install @angular/cli one time — in fact, subsequent invocations might actually upgrade the global copy, which could conceivably cause a build that used to work to inexplicably break.

ng new, then, creates a new Angular app. Unsurprisingly, this goes into a new directory of the same name as the third parameter; my-app in the case of the Angular tutorial. You can see from the output that it's following a script — so, where does the Angular CLI go to find this script to figure out what to create? If you poke around the source code a bit, you can find these instructions in node_modules/@angular-devkit/schematics, which was downloaded when you installed the CLI. As you can see, this does quite a bit - it creates some boilerplate code and downloads 792 (!) packages consisting of 31,728 (!!) files.

Step 3: Run the application

Now, the Angular tutorial instructs you to run ng serve --open. If you leave off the --open option for a moment and just run ng serve, the Angular CLI utility ng will start up a web server on port 4200. If you navigate to it in a browser, you'll see the "Welcome to my-app" hello world template. If you go back and take a look at the generated code, you can see that there's an index.html file and, if you compare the source of the landing page against src/index.html, they're close to one another, but not identical: what shows up in the browser has a few additional script tags toward the bottom. Where did these come from?

Well, ng serve isn't just a static web server, but an Angular-aware web server that also builds the code for you in memory, invisibly, before serving it up. If you want to get a better idea of what ng serve is doing behind the scenes, you can invoke ng build to see the interim product. (In fact, it's not called out specifically by the tutorial itself, but if you want to run this code on any server except the test ng serve server, you have to do this and deploy the results). Doing so creates a dist directory that includes all of the source code that is required to run the app. The only difference between the auto-generated index.html source and the built target is the inclusion of five new Javascript files:

  • runtime.js
  • polyfills.js
  • styles.js
  • vendor.js
  • main.js

So, what are these, and how did ng build (or ng serve) know where to find them/generate them and insert them?

As it turns out, ng build is a wrapper over the webpack library/utility. Webpack, itself, is a modularization/ build framework for Javascript applications. "Build framework for Javascript" feels like a misnomer to older Javascript developers — Javascript is executable as-is (in fact, that's the only way it's executable), so the need to "build" anything seems out of place. But in reality, any moderately complex web application will consist of enough interleaved Javascript files that, at the very least, bundling all of them together and minifying them into a single download will provide significant runtime performance benefits. Beyond that, though, it's common to rely on third-party Javascript libraries (maybe a little too common), so the deployment process typically involves bundling additional libraries not under the developer's control along with the application's code. If those dependencies have their own dependencies, you run into dependency resolution issues (i.e. I'm using jQuery 1.7 and you're using 1.8 — which one do we include at runtime?) Webpack follows a configuration file named webpack.config.js and uses that do resolve modules, handle CJS/AMD/ES6 conflicts, and bundle up the whole thing into something that a browser can run.

There's more to webpack, in fact — asynchronous or just-in-time module loading, tree-shaking (removing unused functions), and hot module reloading, for example.

So, webpack is responsible for encapsulating, compressing, bundling, etc. the source code files, but still — how does ng build tell it where and what to process? There's no webpack.config.js file to be found, after all, and it's obviously not following the defaults. Instead, the file angular.json (which used to be a "hidden" file named .angular-cli.json in earlier Angular releases) includes this instruction along with quite a few others. In earlier versions of Angular, you could run ng eject to see the implied webpack.config.js and, if necessary, tweak it — you can't do this any more, but this functionality was of dubious value since the resulting configuration file ran into the hundreds of lines.

Although the configuration options of the angular.json file are pretty sparsely documented, it's not hard to figure out what it's doing. The relevant section appears toward the beginning of the file:

"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/my-app",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},

You can mentally map most of that back to the scripts that were inserted to index.html (itself identified by the index entry) — polyfills transpiled into polyfills.js and main transpiled into main.ts. The styles entry instructs the Webpack CSS loader to generate the styles.js javascript file that compresses and encapsulates CSS — you may notice that there are no CSS imports in the generated application!

You probably know that polyfills is a standard add-in that allows you to run Angular apps in older browsers (and if you didn't know that, you know it now), which you'll probably never touch except to uncomment some lines if you want IE support, and that main.ts is the actual startup file for Angular (which you also probably won't spend much time with - you'll start with app.module.ts instead). That leaves runtime.js and vendor.js unaccounted for.

To make more sense of these (as well as the contents of the other three), you need a firm grounding in Javascript modules or rather, the lack thereof. Since Javascript was originally conceived as a scripting add-on for web pages, and web pages themselves were originally pretty small and limited in functionality, there wasn't much attention paid to larger software engineering concepts like modularity in its early stages. The original Javascript had two effective variable scopes: global and function-level. This was OK early on — a globally-scoped variable was only scoped globally to the page it was contained in, and early web applications consisted of dozens of individual pages. In fact, sharing data across pages was a bigger problem in early Javascript applications than modular information hiding — 90's Javascript developers spent a lot of time juggling cookies to create consistency across multiple pages.

With the rise and popularity of Ajax-driven singe-page applications, though, the necessity of an intermediate "module" scope — somewhere in between global and local — became apparent. Since Javascript didn't support it, though, clever developers started faking it by wrapping their conceptual modules inside functions. Although ES6 does have a syntax for declaring modules, there's a lot of this modules-in-functions workaround-ery still to be found, and webpack still treats it as a first-class citizen. So, if you look at the first Javascript file that's loaded at runtime (appropriately named runtime.js), it's a single Immediately Invoked Functional Expression (IIFE) that accepts a parameter named modules. This configures the webpack modular system which the remaining files rely on.

Back to the original ng serve --open, though: if you do include the instructed --open parameter, ng goes ahead and opens a browser for you and navigates to the landing page of the newly minted application. It doesn't just open up a browser, though — if you modify the source code of the application, it will automatically reload the application in the browser. How is this magic accomplished? I'll go into that, along with adding new Angular components, in the next part of this series.

Angular CLI behind the scenes, part one的更多相关文章

  1. Configure a proxy for your API calls with Angular CLI

    Table of contents Local development setup with Angular Issue: Dev server plus backend API Configurin ...

  2. Angular环境准备和Angular cli

    Angular4.0来了,更小,更快,改动少 接下来为Angular4.0准备环境和学会使用Angular cli项目 1.环境准备: 1)在开始工作之前我们必须设置好开发环境 如果你的机器上还没有安 ...

  3. 迈向angularjs2系列(8):angular cli和angular2种子项目

    文章目录 1.angular cli快速搭建项目 2.angular2-seed 3.手动配置 题外话:如何更好的阅读本篇文章 一: angular cli的安装 Angular-cli(命令行界面, ...

  4. Visual Studio Code作为Angular开发工具常用插件安装、json-server安装与使用、angular/cli安装失败问题

    前提准备: 搭建好Angular开发环境 1 安装Visual Studio Code 教程简单,不会的去问度娘 2 安装Chrome浏览器 教程简单,不会的趣闻度娘 3 Visual Studio ...

  5. angular4.0 安装最新版本的nodejs、npm、@angular/cli的方法

    在使用ng项目的ui框架时,比如ng-zorro.angular Material,需要安装最新版本的@angular/cli: 配置ng-zorro框架 ng-zorro官网:https://ng. ...

  6. 使用Angular CLI生成 Angular 5项目

    如果您正在使用angular, 但是没有好好利用angular cli的话, 那么可以看看本文. Angular CLI 官网: https://github.com/angular/angular- ...

  7. Angular4---起步----环境配置安装@angular/cli

    学习angular,首先要搭建起angular的手脚架@angular/cli.首先需要NodeJS环境. 1.安装NodeJS 首先检查电脑是否安装了NodeJS环境,打开cmd命令行,运行node ...

  8. 使用Angular CLI进行单元测试和E2E测试

    第一篇文章是: "使用angular cli生成angular5项目" : http://www.cnblogs.com/cgzl/p/8594571.html 第二篇文章是: & ...

  9. 使用Angular CLI从蓝本生成代码

    第一篇文章是: "使用angular cli生成angular5项目" : http://www.cnblogs.com/cgzl/p/8594571.html 这篇文章主要是讲生 ...

随机推荐

  1. python+lego ev3的心得总结 随时更新

    一.连接方面 1.试了蓝牙连接,被电脑防火墙拒绝了很多次,很奇怪,明明都pin码都对上了,然后瞬间被踢开. 2.数据线直连,在一台win7上怎么试也不行,在另一台上自动上windows update上 ...

  2. php_mvc实现步骤三,四

    3.match_mvc MVC 以ecshop的前台为例: 功能一: 首页 购物车数据,商品分类数据,其他的首页需要的数据 功能二: 拍卖活动 购物车数据,商品分类数据,拍卖相关数据 功能三: 团购商 ...

  3. LeetCode 150. 逆波兰表达式求值(Evaluate Reverse Polish Notation) 24

    150. 逆波兰表达式求值 150. Evaluate Reverse Polish Notation 题目描述 根据逆波兰表示法,求表达式的值. 有效的运算符包括 +, -, *, /.每个运算对象 ...

  4. 【Linux】守护进程的定义,作用,创建流程

    本文内容: 1.守护进程的定义 2.守护进程的作用 3.守护进程的创建过程 一.守护进程的定义 1.守护进程是脱离于终端并且在后台运行的进程 2.守护进程脱离终端是为了避免在执行过程中的信息在任何终端 ...

  5. Jackson 动态Bean

    为了解决json字符串有很多属性, 但是不必每个属性都映射到pojo的属性. @JsonProperty : 标记一个方法是一个属性的getter或setter方法, 也即把java属性和json域关 ...

  6. springboot2.1.8使用poi导出数据生成excel(.xlsx)文件

    前言:在实际开发中经常需要将数据库的数据导出成excel文件,poi方式则是其中一种较为常用的导出框架.简单读取excel文件在之前的一篇有说明 本项目实现需求:user发出一个导出student信息 ...

  7. 为什么Redis单线程却能支撑高并发?

    作者:Draveness 原文链接:draveness.me/redis-io-multiplexing 最近在看 UNIX 网络编程并研究了一下 Redis 的实现,感觉 Redis 的源代码十分适 ...

  8. C++Primer 5th Chap4 Expressions

    左值和右值:左值:用的是对象的身份(内存中的位置),右值:用的是对象的值(内容) 解引用与递增(递减)运算符连用: *ivec++:取ivec当前值并向后移动一个元素,等价于*(ivec++),本来+ ...

  9. Python属性的查找顺序

    属性查找顺序 关于属性描述符请看上文>属性描述符   在梳理属性查找相关知识时,查看了很多的书籍和他人的博客,发现很多讲的过于抽象,并没有一个清晰的流程呈现.特此写下我对于此方面的理解和总结. ...

  10. 【转】用chrome滚动截屏

    用开发者常用的网站chrome,打开需要截屏的网页 使用快捷键组合:Alt + Command + I (Mac) || Ctrl + Shift + I (Windows) 使用快捷键组合来打开命令 ...