课堂练习(四)

最后更新于:2022-04-01 23:32:08

## ESLint ### 实验目的 1. 学会使用 ESLint 进行代码检查。 ### 操作步骤 (1)进入`demos/eslint-demo`目录,安装 ESLint。 ~~~ $ cd demos/eslint-demo $ npm install eslint --save-dev ~~~ (2)通常,我们会使用别人已经写好的代码检查规则,这里使用的是 Airbnb 公司的规则。所以,还要安装 ESLint 这个规则模块。 ~~~ $ npm install eslint-plugin-import eslint-config-airbnb-base --save-dev ~~~ 上面代码中,`eslint-plugin-import`是运行这个规则集必须的,所以也要一起安装。 (3)ESLint 的配置文件是`.eslintrc.json`,放置在项目的根目录下面。新建这个文件,在里面指定使用 Airbnb 的规则。 ~~~ { "extends": "airbnb-base" } ~~~ (4)打开项目的`package.json`,在`scripts`字段里面添加三个脚本。 ~~~ { // ... "scripts" : { "test": "echo \"Error: no test specified\" && exit 1", "lint": "eslint **/*.js", "lint-html": "eslint **/*.js -f html -o ./reports/lint-results.html", "lint-fix": "eslint --fix **/*.js" }, // ... } ~~~ 除了原有的`test`脚本,上面代码新定义了三个脚本,它们的作用如下。 * `lint`:检查所有`js`文件的代码 * `lint-html`:将检查结果写入一个网页文件`./reports/lint-results.html` * `lint-fix`:自动修正某些不规范的代码 (5)运行静态检查命令。 ~~~ $ npm run lint 1:5 error Unexpected var, use let or const instead no-var 2:5 warning Unexpected console statement no-console ✖ 2 problems (1 error, 1 warning) ~~~ 正常情况下,该命令会从`index.js`脚本里面,检查出来两个错误:一个是不应该使用`var`命令,另一个是不应该在生产环境使用`console.log`方法。 (6)修正错误。 ~~~ $ npm run lint-fix ~~~ 运行上面的命令以后,再查看`index.js`,可以看到`var x = 1;`被自动改成了`const x = 1;`。这样就消除了一个错误,但是还留下一个错误。 (7)修改规则。 由于我们想要允许使用`console.log`方法,因此可以修改`.eslintrc.json`,改变`no-console`规则。请将`.eslintrc.json`改成下面的样子。 ~~~ { "extends": "airbnb-base", "rules": { "no-console": "off" } } ~~~ 再运行`npm run lint`,就不会报错了。 ~~~ $ npm run lint ~~~ ## Mocha ### 实验目的 1. 学会使用 Mocha 进行单元测试。 ### 操作步骤 (1) 进入`demos/mocha-demo`目录,安装 Mocha 和 Chai。 ~~~ $ cd demos/mocha-demo $ npm install -D mocha $ npm install -D chai ~~~ (2)打开`add.js`文件,查看源码,我们要测试的就是这个脚本。 ~~~ function add(x, y) { return x + y; } module.exports = add; ~~~ (3)编写一个测试脚本`add.test.js`。 ~~~ var add = require('./add.js'); var expect = require('chai').expect; describe('加法函数的测试', function() { it('1 加 1 应该等于 2', function() { expect(add(1, 1)).to.be.equal(2); }); }); ~~~ 测试脚本与所要测试的源码脚本同名,但是后缀名为`.test.js`(表示测试)或者`.spec.js`(表示规格)。比如,`add.js`的测试脚本名字就是`add.test.js`。 测试脚本里面应该包括一个或多个`describe`块,每个`describe`块应该包括一个或多个`it`块。 `describe`块称为"测试套件"(test suite),表示一组相关的测试。它是一个函数,第一个参数是测试套件的名称("加法函数的测试"),第二个参数是一个实际执行的函数。 `it`块称为"测试用例"(test case),表示一个单独的测试,是测试的最小单位。它也是一个函数,第一个参数是测试用例的名称("1 加 1 应该等于 2"),第二个参数是一个实际执行的函数。 上面的测试脚本里面,有一句断言。 ~~~ expect(add(1, 1)).to.be.equal(2); ~~~ 所谓"断言",就是判断源码的实际执行结果与预期结果是否一致,如果不一致就抛出一个错误。上面这句断言的意思是,调用`add(1, 1)`,结果应该等于`2`。 所有的测试用例(`it`块)都应该含有一句或多句的断言。它是编写测试用例的关键。断言功能由断言库来实现,Mocha本身不带断言库,所以必须先引入断言库。 ~~~ var expect = require('chai').expect; ~~~ 断言库有很多种,Mocha并不限制使用哪一种。上面代码引入的断言库是`chai`,并且指定使用它的`expect`断言风格。 (4)打开`package.json`文件,改写`scripts`字段的`test`脚本。 ~~~ "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, // 改成 "scripts": { "test": "mocha *.test.js" }, ~~~ (5)命令行下,执行下面的命令,运行测试用例。 ~~~ $ npm test ~~~ 正常情况下,命令行会有提示,表示测试用例已经通过了。 ### 练习 1. 请在`add.test.js`里面添加一个测试用例,测试`3`加上`-3`,`add`函数应该返回`0`。 ## Nightmare ### 实验目的 1. 学会使用 Nightmare 完成功能测试。 ### 操作步骤 (1)进入`./demos/nightmare-demo`目录,安装依赖。 ~~~ $ cd demos/nightmare-demo # Linux & Mac $ env ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/ npm install # Windows $ set ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/ $ npm install ~~~ 注意,Nightmare 会先安装 Electron,而 Electron 的安装需要下载境外的包,有时会连不上,导致安装失败。所以,这里先设置了环境变量,指定使用国内的 Electron 源,然后才执行安装命令。 (2)查看一下浏览器自动化脚本`taobao.test.js`。 ~~~ var Nightmare = require('nightmare'); var nightmare = Nightmare({ show: true }); ~~~ 上面代码表示新建一个 Nightmare 实例,并且运行功能中,自动打开浏览器窗口。 ~~~ nightmare .goto('https://www.taobao.com/') .type('#q', '电视机') .click('form[action*="/search"] [type=submit]') .wait('#spulist-grid') .evaluate(function () { return document.querySelector('#spulist-grid .grid-item .info-cont') .textContent.trim(); }) .end() ~~~ 上面代码表示,打开淘宝首页,在搜索框键入`电视机`,点击”搜索“按钮,等待`#spulist-grid`元素出现,在页面内注入(`evaluate`)代码,将执行结果返回。 ~~~ .then(function (result) { console.log(result); }) .catch(function (error) { console.error('Search failed:', error); }); ~~~ Nightmare 会返回一个 Promise 对象,`then`方法指定操作成功的回调函数,`catch`方法指定操作失败的回调函数。 (3)命令行下运行这个示例脚本。 ~~~ $ node taobao.test.js ~~~ 正常情况下,运行结束后,命令行会显示淘宝”电视机“搜索结果的第一项。 (4)浏览器打开`index.html`文件,这是 React 练习时做过的一个例子,点击`Hello World`,标题会变成`Hello Clicked`。我们就要编写测试脚本,测试这个功能。 (5)打开测试脚本`test.js`。 ~~~ var Nightmare = require('nightmare'); var expect = require('chai').expect; var fork = require('child_process').fork; describe('test index.html', function() { var child; before(function (done) { child = fork('./server.js'); child.on('message', function (msg) { if (msg === 'listening') { done(); } }); }); after(function () { child.kill(); }); ~~~ 上面代码中,`before`和`after`是 Mocha 提供的两个钩子方法,分别在所有测试开始前和结束后运行。这里,我们在`before`方法里面,新建一个子进程,用来启动 HTTP 服务器;测试结束后,再杀掉这个子进程。 注意,`before`方法的参数是一个函数,它接受`done`作为参数。`done`是 Mocha 提供的一个函数,用来表示异步操作完成。如果不调用`done`,Mocha 就会认为异步操作没有结束,一直停在这一步,不往下执行,从而导致超时错误。 子进程脚本`server.js`的代码非常简单,只有四行。 ~~~ var httpServer = require('http-server'); var server = httpServer.createServer(); server.listen(8080); process.send('listening'); ~~~ 上面代码中,我们在`8080`端口启动 HTTP 服务器,然后向父进程发消息,表示启动完成。 (6)真正的自动化测试脚本如下。 ~~~ it('点击后标题改变', function(done) { var nightmare = Nightmare({ show: true }); nightmare .goto('http://127.0.0.1:8080/index.html') .click('h1') .wait(1000) .evaluate(function () { return document.querySelector('h1').textContent; }) .end() .then(function(text) { expect(text).to.equal('Hello Clicked'); done(); }) }); ~~~ 上面代码中,首先打开网页,点击`h1`元素,然后等待 1 秒钟,注入脚本获取`h1`元素的文本内容。接着,在`then`方法里面,做一个断言,判断获取的文本是否正确。 (7)运行这个测试脚本。 ~~~ $ npm test ~~~ 如果一切正常,命令行下会显示测试通过。 ### 练习 1. 请写一个测试用例,验证`

`的字体颜色是红色。(提示:可以使用`Window.getComputedStyle()`方法,获取元素的最终样式。) ## Travis CI ### 实验目的 1. 了解持续集成的做法,学会使用 Travis CI。 ### 操作步骤 (1)注册 [Github](https://github.com/) 的账户。如果你已经注册过,跳过这一步。 (2)访问这个代码库[`github.com/ruanyf/travis-ci-demo`](https://github.com/ruanyf/travis-ci-demo),点击右上角的`Fork`按钮,将它克隆到你自己的空间里面。 (3)将你`fork`的代码库,克隆到本地。注意,要将下面网址之中的`[your_username]`改成你的 Github 用户名。 ~~~ // Linux & Mac $ git clone git@github.com:[your_username]/travis-ci-demo.git // Windows $ git clone https://github.com:[your_username]/travis-ci-demo ~~~ (4)使用你的 Github 账户,登录 [Travis CI](https://travis-ci.org/auth) 的首页。然后,访问 [Profile](https://travis-ci.org/profile) 页面,选定`travis-ci-demo`代码库运行自动构建。 (5)回到命令行,进入你本地的`travis-ci-demo`目录,切换到`demo01`分支。 ~~~ $ cd travis-ci-demo $ git checkout demo01 ~~~ 项目根目录下面有一个`.travis.yml`文件,这是 Travis CI 的配置文件。如果没有这个文件,就不会触发 Travis CI 的自动构建。打开看一下。 ~~~ language: node_js node_js: - "node" ~~~ 上面代码指定,使用 Node 完成构建,版本是最新的稳定版。 指定 Node 的版本号也是可以的。 ~~~ language: node_js node_js: - "4.1" ~~~ 上面代码指定使用 Node 4.1 版。 (6)Travis CI 默认依次执行以下九个脚本。 * `before_install` * `install` * `before_script` * `script` * `after_success` 或者 `after_failure` * `after_script` * `before_deploy`(可选) * `deploy`(可选) * `after_deploy`(可选) 用户需要用到哪个脚本,就需要提供该脚本的内容。 对于 Node 项目,以下两个脚本有默认值,可以不用自己设定。 ~~~ "install": "npm install", "script": "npm test" ~~~ (7)打开当前分支的`package.json`,可以发现它的`test`脚本是一个`lint`命令。 ~~~ "scripts": { "test": "jshint hello.js" }, ~~~ (8)在项目根目录下,新建一个新文件`NewUser.txt`,内容是你的用户名。提交这个文件,就会触发 Travis CI 的自动构建。 ~~~ $ git add -A $ git commit -m 'Testing Travis CI' $ git push ~~~ (9)等到 Travis CI 完成自动构建,到页面上[检查](https://travis-ci.org/repositories)构建结果。 (10)切换到`demo02`分支,打开`package.json`,可以看到`test`脚本,现在需要完成两步操作了。 ~~~ "scripts": { "lint": "jshint hello.js hello.test.js", "test": "npm run lint && mocha hello.test.js" }, ~~~ (11)重复上面第 8 步和第 9 步。 ### 练习 1. 修改`hello.js`,让其输出`Hello Node`。并修改测试用例`hello.test.js`,使之能够通过 Travis CI 的自动构建。

';

第四讲:前端工程简介

最后更新于:2022-04-01 23:32:05

# 前端工程简介 [TOC=2,2] ## 持续集成流程 前端开发转移到后端环境,意味着可以适用标准的软件工程流程。 1. 本地开发(developing) 2. 静态代码检查(linting) 3. 单元测试(testing) 4. 合并进入主干(merging) 5. 自动构建(building) 6. 自动发布(publishing) ## 持续集成的概念 Continuous integration(简称 CI): 开发代码频繁地合并进主干,始终保持可发布状态的这个过程。 优点 * 快速发现错误 * 防止分支大幅偏离主干 * 让产品可以快速迭代,同时还能保持高质量 ## ESLint:静态代码检查工具 * 发现语法错误 * 发现风格错误 * 自动纠正错误 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/eslint.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/eslint.png) ## 课堂练习:ESLint 的用法 进入`demos/eslint-demo`目录,按照[《操作指南》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#eslint),完成练习。 ## 为什么写测试? Web 应用越来越复杂,意味着更可能出错。测试是提高代码质量、降低错误的最好方法之一。 * 测试可以确保得到预期结果。 * 加快开发速度。 * 方便维护。 * 提供用法的文档。 对于长期维护的项目,测试会大大加快开发速度,减轻维护难度。 ## 测试的类型 * 单元测试(unit testing) * 功能测试(feature testing) * 集成测试(integration testing) * 端对端测试 (End-to-End testing) ## 以测试为导向的开发模式 * TDD:测试驱动的开发(Test-Driven Development) * BDD:行为驱动的开发(Behavior-Driven Development) ## TDD vs. BDD 两者侧重点不一样 * TDD:基于开发者角度,重点测试函数的输入输出 * BDD:基于使用者角度,重点测试对用户行为的反应 比如,有一个计数器函数`counter`,TDD 测试的是输入1,输出的应该是2;BDD 测试的是用户访问以后,计数器应该增加一次。 ## Mocha Mocha 是目前最常用的测试框架。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/mocha.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/mocha.png) ## 课堂练习:Mocha 的用法 进入`demos/mocha-demo`目录,按照[《操作指南》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#mocha),练习写单元测试。 ## 功能测试 功能测试指的是,站在外部用户的角度,测试软件的某项功能。 与内部代码实现无关,只测试功能是否正常。 很多时候,单元测试都可以通过,但是整体功能会失败。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/functional-test.jpg)](https://github.com/ruanyf/jstraining/blob/master/docs/images/functional-test.jpg) ## 前端的功能测试 功能测试必须在真正浏览器做,现在有四种方法。 * 使用本机安装的浏览器 * 使用 Selenium Driver * 使用 PhantomJS * 使用 Electron ## Nightmare * 使用 Electron 模拟真实浏览器环境 * 提供大量人性化、易用的 API * 官网:nightmarejs.org ## 示例:Nightmare 打开`demos/nightmare-demo/`,按照[《操作说明》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#nightmare),完成操作。 ## 移动平台的自动化测试 目前,最常见的方案是使用 Appium。 * 基于 WebDriver * 采用 客户端/服务器架构 * 可以在模拟器运行,也可以在真机运行 ## Appium 测试流程 1. WebDriver 客户端发出测试请求和测试内容 2. Appium 服务器转发到相应的测试运行器(Driver) 3. iOS 设备是 UIAutomation,安卓设备是 UiAutomator 4. 测试运行器将运行结果,返回给 Appium ## 持续集成服务平台 代码合并进主干以后,就可以进行自动构建和发布了。 网上有很多 PaaS 平台,提供持续集成服务。 Travis CI 就是其中最著名的一个,它可以根据你提供的脚本,自动完成构建和发布。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/travis-ci.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/travis-ci.png) ## 课堂练习:Travis CI 按照[《操作说明》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#travis-ci),完成练习。
';

课堂练习(三)

最后更新于:2022-04-01 23:32:03

## Simple App ### 实验目的 1. 学会使用 Node 编写简单的前端应用。 ### 操作步骤 (1)新建一个目录 ~~~ $ mkdir simple-app-demo $ cd simple-app-demo ~~~ (2)在该目录下,新建一个`package.json`文件。 ~~~ $ npm init -y ~~~ `package.json`是项目的配置文件。 (3)安装`jquery`和`webpack`这两个模块。 ~~~ $ npm install -S jquery $ npm install -S webpack ~~~ 打开`package.json`文件,会发现`jquery`和`webpack`都加入了`dependencies`字段,并且带有版本号。 (4)在项目根目录下,新建一个网页文件`index.html`。 ~~~

Hello World

~~~ (5)在项目根目录下,新建一个脚本文件`app.js`。 ~~~ const $ = require('jquery'); $('h1').css({ color: 'red'}); ~~~ 上面代码中,`require`方法是 Node 特有的模块加载命令。 (6)打开`package.json`,在`scripts`字段里面,添加一行。 ~~~ "scripts": { "build": "webpack app.js bundle.js", "test": "...." }, ~~~ (7) 在项目根目录下,执行下面的命令,将脚本打包。 ~~~ $ npm run build ~~~ 执行完成,可以发现项目根目录下,新生成了一个文件`bundle.js`。 (8)浏览器打开`index.html`,可以发现`Hello World`变成了红色。 ### 练习 1. 修改样式,将标题变为蓝色,然后重新编译生成打包文件。 ## REST API ### 实验目的 1. 熟悉 REST API 的基本用法 ### 操作步骤 (1) 命令行进入`rest-api-demo`目录,执行下面的命令。 ~~~ $ npm install -S json-server ~~~ (2) 在项目根目录下,新建一个 JSON 文件`db.json`。 ~~~ { "posts": [ { "id": 1, "title": "json-server", "author": "typicode" } ], "comments": [ { "id": 1, "body": "some comment", "postId": 1 } ], "profile": { "name": "typicode" } } ~~~ (3) 打开`package.json`,在`scripts`字段添加一行。 ~~~ "scripts": { "server": "json-server db.json", "test": "..." }, ~~~ (4) 命令行下执行下面的命令,启动服务。 ~~~ $ npm run server ~~~ (5)打开 Chrome 浏览器的 Postman 应用。依次向`http://127.0.0.1:3000/posts`、`http://127.0.0.1:3000/posts/1`发出`GET`请求,查看结果。 (6)向`http://127.0.0.1:3000/comments`发出`POST`请求。注意,数据体`Body`要选择`x-www-form-urlencoded`编码,然后依次添加下面两个字段。 ~~~ body: "hello world" postId: 1 ~~~ 发出该请求后,再向`http://127.0.0.1:3000/comments`发出`GET`请求,查看结果。 (7) 向`http://127.0.0.1:3000/comments/2`发出`PUT`请求,数据体`Body`要选择`x-www-form-urlencoded`编码,然后添加下面的字段。 ~~~ body: "hello react" ~~~ 发出该请求后,再向`http://127.0.0.1:3000/comments`发出`GET`请求,查看结果。 (8)向`http://127.0.0.1:3000/comments/2`发出`delete`请求。 发出该请求后,再向`http://127.0.0.1:3000/comments`发出`GET`请求,查看结果。 ## Express ### 实验目的 1. 学会 Express 搭建 Web 应用的基本用法。 ### 操作步骤 (1)进入`demos/express-demo`目录,命令行执行下面的命令,安装依赖。 ~~~ $ cd demos/express-demo $ npm install ~~~ (2)打开`app1.js`,尝试看懂这个脚本。 ~~~ var express = require('express'); var app = express(); ~~~ 上面代码调用`express`,生成一个 Web 应用的实例。 ~~~ var router = express.Router(); router.get('/', function(req, res) { res.send('

Hello World

'); }); app.use('/home', router); ~~~ 上面代码新建了一个路由对象,该对象指定访问根路由(`/`)时,返回`Hello World`。然后,将该路由加载在`/home`路径,也就是说,访问`/home`会返回`Hello World`。 `router.get`方法的第二个参数是一个回调函数,当符合指定路由的请求进来,会被这个函数处理。该函数的两个参数,`req`和`res`都是Express 内置的对象,分别表示用户的请求和 Web 服务器的回应。`res.send`方法就表示服务器回应所送出的内容。 ~~~ var port = process.env.PORT || 8080; app.listen(port); console.log('Magic happens on port ' + port); ~~~ 上面代码指定了外部访问的端口,如果环境变量没有指定,则端口默认为`8080`。最后两行是启动应用,并输出一行提示文字。 (3)在命令行下,启动这个应用。 ~~~ $ node app1.js ~~~ 浏览器访问`localhost:8080/home`,看看是否输出`Hello World`。 然后,命令行下按 Ctrl + C,退出这个进程。 (4)通过环境变量,自定义启动端口。 假定我们指定必须启动在`7070`端口,命令行可以这样操作。 ~~~ # Linux & Mac $ PORT=7070 node app1.js # windows $ set PORT=7070 $ node app1.js ~~~ 浏览器就可以访问`localhost:7070/home`了。 然后,命令行下按 Ctrl + C,退出这个进程。 思考题:Node 应用能否直接在`80`端口启动? (5)打开`app2.js`,查看新增的那个路由。 ~~~ router.get('/:name', function(req, res) { res.send('

Hello ' + req.params.name + '

'); }); ~~~ 上面代码新增了一个路由,这个路由的路径是一个命名参数`:name`,可以从`req.params.name`拿到这个传入的参数。 在命令行下,启动这个应用。 ~~~ $ node app2.js ~~~ 浏览器访问`localhost:8080/home/张三`,看看是否输出`Hello 张三`。 然后,命令行下按 Ctrl + C,退出这个进程。 (6)打开`app3.js`,先查看页面头部新增的两行代码。 ~~~ var express = require('express'); var app = express(); // 新增代码... var bodyParser = require('body-parser'); app.use(bodyParser.urlencoded({ extended: true })); // ... ~~~ 上面代码中,`body-parser`模块的作用,是对`POST`、`PUT`、`DELETE`等 HTTP 方法的数据体进行解析。`app.use`用来将这个模块加载到当前应用。有了这两句,就可以处理`POST`、`PUT`、`DELETE`等请求了。 下面查看新增的那个路由。 ~~~ router.post('/', function (req, res) { var name = req.body.name; res.json({message: 'Hello ' + name}); }); ~~~ 上面代码表示,如果收到了`/`路径(实际上是`/home`路径)的`POST`请求,先从数据体拿到`name`字段,然后返回一段 JSON 信息。 在命令行下,启动这个应用。 ~~~ $ node app3.js ~~~ 然后,在 Chrome 浏览器的 Postman 插件里面,向`http://127.0.0.1:8080/home`发出一个`POST`请求。数据体的编码方法设为`x-www-form-urlencoded`,里面设置一个`name`字段,值可以随便取,假定设为`Alice`。也就是说,发出这样一个请求。 ~~~ POST /home HTTP/1.1 Host: 127.0.0.1:8080 Content-Type: application/x-www-form-urlencoded name=Alice ~~~ 如果一切正常,服务器会返回一段 JSON 信息。 ~~~ { "message": "Hello Alice" } ~~~ (7)打开`app4.js`,查看在所有路由之前新增的那个函数。 ~~~ var router = express.Router(); // 新增的代码 router.use(function(req, res, next) { console.log('Thers is a requesting.'); next(); }); router.get('/', function(req, res) { // ... ~~~ `router.use`的作用是加载一个函数。这个函数被称为中间件,作用是在请求被路由匹配之前,先进行一些处理。上面这个中间件起到 logging 的作用,每收到一个请求,就在命令行输出一条记录。请特别注意,这个函数内部的`next()`,它代表下一个中间件,表示将处理过的请求传递给下一个中间件。这个例子只有一个中间件,就进入路由匹配处理(实际上,`bodyparser`、`router`本质都是中间件,整个 Express 的设计哲学就是不断对 HTTP 请求加工,然后返回一个 HTTP 回应)。 ### 练习 1. 请增加一个中间件,服务器每次收到用户请求,会在服务器的控制台打印出收到请求的时间。 2. URL 的查询字符串,比如`localhost:8080?name=Alice`里面的`name`,可以用`req.query.name`拿到。请修改一个路由,使之可以收到查询字符串,然后输出`'Hello ' + req.query.name`。
';

第三讲:Node 应用开发

最后更新于:2022-04-01 23:32:01

# Node 应用开发 [TOC=2,2] ## Node 简介 Node 是服务器的 JavaScript 运行环境,提供 API 与操作系统互动。 主要用途: * 开发前端应用 * 快速搭建服务 * 架设网站 ## npm 安装 Node 的时候,会同时安装 npm。 ~~~ $ npm -v ~~~ 它是 Node 的模块管理器,开发 Node 项目的必备工具。 * * * ## 课堂练习:Node 的简单应用 进入`demos/simple-app-demo`目录,参考[《操作指南》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#simple-app),自己动手在 Node 里面,编写并编译一个前端脚本。 * * * ## Node 开发前端脚本的好处 1. 模块机制 2. 版本管理 3. 对外发布 4. 持续集成的标准开发流程 * * * ## REST API REST 是浏览器与服务器通信方式的一种设计风格。 它的全称是“REpresentational State Transfer”,中文意为”表现层状态转换“。 * Resource:资源 * Representation:表现层 * State:状态 * Transfer:转换 * * * ## REST 的核心概念 1. 互联网上所有可以访问的内容,都是资源。 2. 服务器保存资源,客户端请求资源。 3. 同一个资源,有多种表现形式。 4. 协议本身不带有状态,通信时客户端必须通过参数,表示请求不同状态的资源。 5. 状态转换通过HTTP动词表示。 * * * ## URL 设计 URL 是资源的唯一识别符。 * /store/1 * /store/2 * /store/1/employee/2 * * * ## 查询字符串 查询字符串表示对所请求资源的约束条件。 * GET /zoos/1/animals?limit=10 * GET /zoos/1/animals?limit=10&offset=10 * GET /animals?zoo_id=1 * * * ## HTTP 动词 | 操作 | SQL方法 | HTTP动词 | | --- | --- | --- | | CREATE | INSERT | POST | | READ | SELECT | GET | | UPDATE | UPDATE | PUT/PATCH | | DELETE | DELETE | DELETE | ~~~ GET /v1/stores/1234 PUT /v1/stores/1234 POST /v1/stores DELETE /v1/stores/1234 ~~~ * * * ## 课堂练习:REST API 打开`demos/rest-api-demo`,按照[《操作指南》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#rest-api),熟悉 REST API 的基本用法。 * * * ## Express Express 是最常用的 Node 框架,用来搭建 Web 应用。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/express.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/express.png) * * * ## 课堂练习:Express 搭建 Web 应用 进入`demos/express-demo`目录,按照[《操作指南》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#express),学习使用 Express 搭建 Web 应用。 * * * 定义一个 Web 应用实例,并且启动它。 ~~~ var express = require('express'); var app = express(); var port = process.env.PORT || 8080; app.listen(port); console.log('Magic happens on port ' + port); ~~~ * * * 定义一个路由 ~~~ var router = express.Router(); router.get('/', function(req, res) { res.send('

Hello World

'); }); app.use('/home', router); ~~~ * * * 中间件:对 HTTP 请求进行加工。 ~~~ router.use(function(req, res, next) { console.log('Thers is a requesting.'); next(); }); ~~~
';

课堂练习(二)

最后更新于:2022-04-01 23:31:58

## JSX ### 实验目的 1. 掌握 JSX 基本语法 ### 操作步骤 1. 浏览器打开`demos/jsx-demo/index.html`,仔细查看源码。 ### 注意事项 1. `ReactDOM.render`方法接受两个参数:一个虚拟 DOM 节点和一个真实 DOM 节点,作用是将虚拟 DOM 挂载到真实 DOM。 ### 练习 1. 修改源码,将显示文字变为 ”Hello React!“。 ## React 组件语法 ### 实验目的 1. 掌握 React 组件的基本写法 ### 操作步骤 1. 浏览器打开`demos/react-component-demo/index1.html`,仔细查看源码。 ### 注意事项 1. `class MyTitle extends React.Component`是 ES6 语法,表示自定义一个`MyTitle`类,该类继承了基类`React.Component`的所有属性和方法。 2. React 规定,自定义组件的第一个字母必须大写,比如`MyTitle`不能写成`myTitle`,以便与内置的原生类相区分。 3. 每个组件都必须有`render`方法,定义输出的样式。 4. ``表示生成一个组件类的实例,每个实例一定要有闭合标签,写成``也可。 ## React 组件的参数 ### 实验目的 1. 学会向 React 组件传参数 ### 操作步骤 1. 浏览器打开`demos/react-component-demo/index2.html`,仔细查看源码。 ### 注意事项 1. 组件内部通过`this.props`对象获取参数。 ### 练习 1. 将组件的颜色,从红色(`red`)换成黄色(`yellow`)。 ## React 组件的状态 ### 实验目的 1. 学会通过状态变动,引发组件的重新渲染。 ### 操作步骤 1. 浏览器打开`demos/react-component-demo/index3.html`,仔细查看源码。 ### 注意事项 ~~~ class MyTitle extends React.Component { constructor(...args) { super(...args); this.state = { name: '访问者' }; } // ... ~~~ `constructor`是组件的构造函数,会在创建实例时自动调用。`...args`表示组件参数,`super(...args)`是 ES6 规定的写法。`this.state`对象用来存放内部状态,这里是定义初始状态。 ~~~

你好,{this.state.name}

; ~~~ `this.state.name`表示读取`this.state`的`name`属性。每当输入框有变动,就会自动调用`onChange`指定的监听函数,这里是`this.handleChange`,`.bind(this)`表示该方法内部的`this`,绑定当前组件。 ~~~ handleChange(e) { let name = e.target.value; this.setState({ name: name }); } ~~~ `this.setState`方法用来重置`this.state`,每次调用这个方法,就会引发组件的重新渲染。 ## React 组件实战 ### 实验目的 1. 学会自己写简单的 React 组件。 ### 操作步骤 1. 浏览器打开`demos/react-component-demo/index4.html`。 2. 点击`Hello World`,看看会发生什么。 ### 练习 1. 修改源码,使得点击`Hello World`后,会显示当前的日期,比如`Hello 2016年1月1日`。 2. 请在上一步练习的基础上,进一步修改。现在`Hello World`点击一次,会改变内容,再点击就不会有反应了。请将其改成,再点击一次变回原样。 ### 提示 练习一、下面的代码可以得到当前日期。 ~~~ var d = new Date(); d.getFullYear() // 当前年份 d.getMonth() + 1 // 当前月份 d.getDate() // 当前是每个月的几号 ~~~ 练习二、可以在`this.state`里面设置一个开关变量`isClicked`。 ~~~ this.state = { text: 'World', isClicked: false }; ~~~ 然后,在`this.handleClick`方法里面,做一个`toggle`效果。 ~~~ let isClicked = !this.state.isClicked; this.setState({ isClicked: isClicked, text: isClicked ? 'Clicked' : 'World' }); ~~~ ## React 组件的生命周期 ### 实验目的 1. 掌握钩子方法的基本用法 2. 掌握组件如何通过 Ajax 请求获取数据,并对数据进行处理 ### 操作步骤 1. 打开`demos/react-lifecycle-demo/index.html`,仔细查看源码。 ### 注意事项 ~~~ componentDidMount() { const url = '...'; $.getJSON(url) .done() .fail(); } ~~~ * `componentDidMount`方法在组件加载后执行,只执行一次。本例在这个方法里向服务器请求数据,操作结束前,组件都显示`Loading`。 * `$.getJSON`方法用于向服务器请求 JSON 数据。本例的数据从 Github API 获取,可以打开源码里面的链接,看看原始的数据结构。 ### 练习 1. 本例的 JSON 数据是 Github 上面最受欢迎的 JavaScript 项目。请在网页上显示一个列表,列出这些项目。 ### 提示 (1) `this.state.loading`记录数据加载是否结束。只要数据请求没有结束,`this.state.loading`就一直是`true`,网页上显示`loading`。 (2) `this.state.error`保存数据请求失败时的错误信息。如果请求失败,`this.state.error`就是返回的错误对象,网页上显示报错信息。 (3) `this.state.data`保存从服务器获取的数据。如果请求成功,可以先用`console.log`方法,将它在控制台里打印出来,看看数据结构。 ~~~ render() { // 加一行打印命令,看看数据结构 console.log(this.state.data); return { // ... ~~~ (4) `this.state.data`里面的`this.state.data.items`应该是一个数组,保存着每个项目的具体信息。可以使用`forEach`方法进行遍历处理。 ~~~ var projects = this.state.data.items; var results = []; projects.forEach(p => { var item =
  • {p.name}
  • ; results.push(item); }); ~~~ (5)然后,将上一步的`results`插入网页即可。 ~~~
      {results}
    ~~~ ## ReCharts ### 实验目的 1. 了解如何使用第三方组件库。 ### 操作步骤 1. 浏览器打开`demos/recharts-demo/index.html`,查看效果。 ## MobX ### 实验目的 1. 理解 MobX 框架 ### 操作步骤 (1) 命令行进入`demos/mobx-demo/`目录,执行如下的命令。 ~~~ $ npm install $ npm start ~~~ (2) 打开浏览器,访问 http://localhost:8080,查看结果,并仔细研究代码。 ### 注意事项 ~~~ @observer class App extends React.Component { render() { // ... } } ~~~ `@observer`是一种新的语法,表示对整个类执行指定的函数。 数据保存在`Store`里面。`Store`的属性分成两种:被观察的属性(`@observable`),和自动计算得到的属性`@computed`。 ~~~ class Store { @observable name = 'Bartek'; @computed get decorated() { return `${this.name} is awesome!`; } } ~~~ `Store`的变化由用户引发。组件观察到`Store`的变化,自动重新渲染。 ~~~

    {this.props.store.decorated}

    this.props.store.name = event.currentTarget.value } /> ~~~ ## Redux ### 实验目的 1. 理解 Redux 架构 ### 操作步骤 (1) 命令行下进入`demos/redux-demo`目录,执行如下的命令。 ~~~ $ npm install $ npm start ~~~ (2)打开浏览器,访问 http://localhost:8080,查看结果,并仔细研究代码。 ### 注意事项 (1) Redux 要求 UI 的渲染组件都是纯组件,即不包含任何状态(`this.state`)的组件。 ~~~

    {this.props.text}

    ~~~ (2) 进行数据处理、并包含状态的组件,称为”容器组件“。Redux 使用`connect`方法,自动生成 UI 组件对应的”容器组件“。 ~~~ // MyComponent 是纯的 UI 组件 const App = connect( mapStateToProps, mapDispatchToProps )(MyComponent); ~~~ (3) `mapStateToProps`函数返回一个对象,表示一种映射关系,将 UI 组件的参数映射到`state`。 ~~~ function mapStateToProps(state) { return { text: state.text, name: state.name }; } ~~~ (4) `mapDispatchToProps`函数也是返回一个对象,表示一种映射关系,但定义的是哪些用户的操作应该当作`Action`,传给`Store`。 ~~~ function mapDispatchToProps(dispatch) { return { onChange: (e) => dispatch({ type: 'change', payload: e.target.value }) } } ~~~ (5) `reducer`函数用来接收`action`,算出新的`state`。 ~~~ function reducer(state = { text: '你好,访问者', name: '访问者' }, action) { switch (action.type) { case 'change': return { name: action.payload, text: '你好,' + action.payload }; } } ~~~ `Store`由 Redux 提供的`createStore`方法生成,该方法接受`reducer`作为参数。 ~~~ const store = createStore(reducer); ReactDOM.render( , document.body.appendChild(document.createElement('div')) ); ~~~ 为了把`Store`传入组件,必须使用 Redux 提供的`Provider`组件在应用的最外面,包裹一层。
    ';

    第二讲:React 技术栈

    最后更新于:2022-04-01 23:31:56

    # React 技术栈 React 是目前最热门的前端框架。 * Facebook 公司2013年推出 * 现在最好的社区支持和生态圈 * 大量的第三方工具 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/react-logo.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/react-logo.png) [TOC=2,2] ## React 的优点 * 组件模式:代码复用和团队分工 * 虚拟 DOM:性能优势 * 移动端支持:跨终端 ## React 的缺点 * 学习曲线较陡峭 * 全新的一套概念,与其他所有框架截然不同 * 只有采用它的整个技术栈,才能发挥最大威力 总结:React 非常先进和强大,但是学习和实现成本都不低 ## JSX 语法 React 使用 JSX 语法,JavaScript 代码中可以写 HTML 代码。 ~~~ let myTitle =

    Hello, world!

    ; ~~~ ## JSX 语法解释 (1)JSX 语法的最外层,只能有一个节点。 ~~~ // 错误 let myTitle =

    Hello

    World

    ; ~~~ (2)JSX 语法中可以插入 JavaScript 代码,使用大括号。 ~~~ let myTitle =

    {'Hello ' + 'World'}

    ~~~ ## Babel 转码器 JavaScript 引擎(包括浏览器和 Node)都不认识 JSX,需要首先使用 Babel 转码,然后才能运行。 ~~~ ~~~ React 需要加载两个库:React 和 React-DOM,前者是 React 的核心库,后者是 React 的 DOM 适配库。 Babel 用来在浏览器转换 JSX 语法,如果服务器已经转好了,浏览器就不需要加载这个库。 ## 课堂练习:JSX 语法 浏览器打开`demos/jsx-demo/index.html`,按照[《操作说明》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#jsx),完成练习。 ~~~ ReactDOM.render( Hello World!, document.getElementById('example') ); ~~~ ## 示例:React 组件 React 允许用户定义自己的组件,插入网页。 浏览器打开`demos/react-component-demo/index1.html`,按照[《操作说明》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#react-%E7%BB%84%E4%BB%B6%E8%AF%AD%E6%B3%95),仔细查看源码。 ~~~ class MyTitle extends React.Component { render() { return

    Hello World

    ; } }; ReactDOM.render( , document.getElementById('example') ); ~~~ ## 课堂练习:组件的参数 组件可以从外部传入参数,内部使用`this.props`获取参数。 打开`demos/react-component-demo/index2.html`,按照[《操作说明》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#react-%E7%BB%84%E4%BB%B6%E7%9A%84%E5%8F%82%E6%95%B0),完成练习。 ~~~ class MyTitle extends React.Component { render() { return

    Hello World

    ; } }; , ~~~ ## 示例:组件的状态 组件往往会有内部状态,使用`this.state`表示。 浏览器打开`demos/react-component-demo/index3.html`,按照[《操作说明》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#react-%E7%BB%84%E4%BB%B6%E7%9A%84%E7%8A%B6%E6%80%81),仔细查看源码。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/react-component-state.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/react-component-state.png) ## 课堂练习:React 组件实战 浏览器打开`demos/react-component-demo/index4.html`,按照[《操作说明》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#react-%E7%BB%84%E4%BB%B6%E5%AE%9E%E6%88%98),完成练习。 ## 组件的生命周期 React 为组件的不同生命阶段,提供了近十个钩子方法。 * `componentWillMount()`:组件加载前调用 * `componentDidMount()`:组件加载后调用 * `componentWillUpdate()`: 组件更新前调用 * `componentDidUpdate()`: 组件更新后调用 * `componentWillUnmount()`:组件卸载前调用 我们可以利用这些钩子,自动完成一些操作。 ## 课堂练习:组件的生命周期 组件可以通过 Ajax 请求,从服务器获取数据。Ajax 请求一般在`componentDidMount`方法里面发出。 ~~~ componentDidMount() { const url = '...'; $.getJSON(url) .done() .fail(); } ~~~ 打开`demos/react-lifecycle-demo/index.html`,按照[《操作说明》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#react-%E7%BB%84%E4%BB%B6%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F),完成练习。 ## React 组件库 React 的一大优势,就是网上有很多已经写好的组件库,可以使用。 React-Bootstrap:[https://react-bootstrap.github.io/](https://react-bootstrap.github.io/) [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/react-bootstrap.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/react-bootstrap.png) ## 示例:ReCharts ReCharts 是一个 React 图表组件库。[http://recharts.org/](http://recharts.org/) 浏览器打开`demos/recharts-demo/index.html`,按照[《操作说明》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#recharts),仔细查看源码。 ~~~ ~~~ ## React 应用的架构 React 只是一个 DOM 的抽象层,并没有解决应用程序的架构问题:大型应用程序应该如何组织代码? Facebook 提出 Flux 架构的概念。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/flow.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/flow.png) 最大特点:数据单向流动 ## 目前最流行的两个 React 框架 * MobX:采用观察者模式,自动响应数据变化 * Redux:Flux 的函数式实现 ## MobX 架构 MobX 的核心概念,就是组件是观察者,一旦`Store`有变化,会立刻被组件观察到,从而引发重新渲染。 ~~~ @observer class App extends React.Component { render() { // ... } } ~~~ ## 示例:MobX 进入`demos/mobx-demo`目录,按照[《操作说明》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#mobx),理解 MobX 框架。 UI 层是观察者,Store 是被观察者。 Store 所有的属性,分成两大类:直接被观察的属性和自动计算出来的属性。 ~~~ class Store { @observable name = 'Bartek'; @computed get decorated() { return `${this.name} is awesome!`; } } ~~~ UI 会观察到 Store 的变化,自动重新渲染。 ## Redux 架构 Redux 的核心概念 * 所有的状态存放在`Store`。组件每次重新渲染,都必须由状态变化引起。 * 用户在 UI 上发出`action`。 * `reducer`函数接收`action`,然后根据当前的`state`,计算出新的`state`。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/redux-architecture.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/redux-architecture.png) ## 示例:Redux 进入`demos/redux-demo`目录,按照[《操作说明》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#redux),理解 Redux 框架。 * Redux 将组件分成 UI 组件和容器组件两类。 * UI 组件是纯组件,需要用户自己写。 ~~~

    {this.props.text}

    ~~~ 容器组件在用户给出配置以后,由 Redux 生成。 ~~~ // MyComponent 是纯的 UI 组件 const App = connect( mapStateToProps, mapDispatchToProps )(MyComponent); ~~~ * mapStateToProps: 定义 UI 组件参数与 State 之间的映射 * mapDispatchToProps:定义 UI 组件与 Action 之间的映射 `reducer`是一个纯函数,用来接收`action`,算出新的`state`。 ~~~ function reducer(state = { text: '你好,访问者', name: '访问者' }, action) { switch (action.type) { case 'change': return { name: action.payload, text: '你好,' + action.payload }; } } ~~~ * `Store`由 Redux 提供的`createStore`方法生成,该方法接受`reducer`作为参数。 * 为了把`Store`传入组件,必须使用 Redux 提供的`Provider`组件在应用的最外面,包裹一层。 ~~~ const store = createStore(reducer); ReactDOM.render( , document.body.appendChild(document.createElement('div')) ); ~~~
    ';

    课堂练习(一)

    最后更新于:2022-04-01 23:31:54

    ## Backbone ### 实验目的 1. 理解前端框架的路由组件(`router`)的作用 ### 操作步骤 1. 浏览器打开[`demos/backbone-demo/index.html`](https://github.com/ruanyf/jstraining/blob/master/demos/backbone-demo/index.html) 2. 点击页面上的链接,注意浏览器 URL 的变化 3. 仔细查看[`js/main.js`](https://github.com/ruanyf/jstraining/blob/master/demos/backbone-demo/js/main.js)的源码,看懂 Router 组件的使用方式 ## Angular ### 实验目的 1. 理解 Angular 的双向绑定机制 ### 操作步骤 1. 浏览器打开[`demos/angular-demo/index.html`](https://github.com/ruanyf/jstraining/blob/master/demos/angular-demo/index.html) 2. 在输入框填入内容,注意页面变化 3. 查看[`index.html`](https://github.com/ruanyf/jstraining/blob/master/demos/angular-demo/index.html)的源码,理解 Angular 对 HTML 标签的增强 ## Vue ### 实验目的 1. 理解 Vue 的模板与数据的双向绑定 ### 操作步骤 1. 浏览器打开[`demos/vue-demo/index1.html`](https://github.com/ruanyf/jstraining/blob/master/demos/vue-demo/index1.html) 2. 在输入框填入内容,注意页面变化 3. 查看[`app1.js`](https://github.com/ruanyf/jstraining/blob/master/demos/vue-demo/app1.js),理解 Vue 组件的基本写法 ### 注意事项 1. [`index2.html`](https://github.com/ruanyf/jstraining/blob/master/demos/vue-demo/index2.html)是一个稍微复杂的例子,模板如何绑定数据对象的一个字段。 2. [`index3.html`](https://github.com/ruanyf/jstraining/blob/master/demos/vue-demo/index3.html)是事件绑定模板的例子。
    ';

    第一讲:前端开发的历史和趋势

    最后更新于:2022-04-01 23:31:52

    # 前端开发的历史和趋势 [TOC=2,2] ## 什么是前端 * 前端:针对浏览器的开发,代码在浏览器运行 * 后端:针对服务器的开发,代码在服务器运行 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/frontend.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/frontend.png) ## 前后端不分的时代 互联网发展的早期,前后端开发是一体的,前端代码是后端代码的一部分。 1. 后端收到浏览器的请求 2. 生成静态页面 3. 发送到浏览器 ## 后端 MVC 的开发模式 那时的网站开发,采用的是后端 MVC 模式。 * Model(模型层):提供/保存数据 * Controller(控制层):数据处理,实现业务逻辑 * View(视图层):展示数据,提供用户界面 前端只是后端 MVC 的 V。 以 PHP 框架 Laravel 为例。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/laravel-mvc.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/laravel-mvc.png) ## 前端工程师的角色 那时的前端工程师,实际上是模板工程师,负责编写页面模板。 后端代码读取模板,替换变量,渲染出页面。 ## 典型的 PHP 模板 ~~~ Car {{ $car->id }}

    Car {{ $car->id }}

    • Make: {{ $car->make }}
    • Model: {{ $car->model }}
    • Produced on: {{ $car->produced_on }}
    ~~~ ## Ajax Ajax 技术诞生,改变了一切。 * 2004年:Gmail * 2005年:Google 地图 前端不再是后端的模板,可以独立得到各种数据。 ## Web 2.0 Ajax 技术促成了 Web 2.0 的诞生。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/web20.gif)](https://github.com/ruanyf/jstraining/blob/master/docs/images/web20.gif) * Web 1.0:静态网页,纯内容展示 * Web 2.0:动态网页,富交互,前端数据处理 从那时起,前端变得复杂了,对前端工程师的要求越来越高。 ## 前端 MVC 框架 前端通过 Ajax 得到数据,因此也有了处理数据的需求。 前端代码变得也需要保存数据、处理数据、生成视图,这导致了前端 MVC 框架的诞生。 * 2010年,Backbone.js [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/backbone.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/backbone.png) ## Backbone.js Backbone 将前端代码分成两个基本部分。 * Model:管理数据 * View:数据的展现 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/backbone-model-view.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/backbone-model-view.png) ## 前端 Controller Backbone 只有 M 和 V,没有 C。因为,前端 Controller 与后端不同。 * 不需要,也不应该处理业务逻辑 * 只需要处理 UI 逻辑,响应用户的一举一动 所以,前端 Controller 相对比较简单。Backbone 没有 C,只用事件来处理 UI 逻辑。 ~~~ var AppView = Backbone.View.extend({ // ... events: { "keypress #new-todo": "createOnEnter", "click #clear-completed": "clearCompleted", "click #toggle-all": "toggleAllComplete" }, } ~~~ ## MVVM 模式 另一些框架提出 MVVM 模式,用 View Model 代替 Controller。 * Model * View * View-Model:简化的 Controller,唯一作用就是为 View 提供处理好的数据,不含其他逻辑。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/mvvm.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/mvvm.png) ## Router 前端还有一种天然的方法,可以切换视图,那就是 URL。 通过 URL 切换视图,这就是 Router(路由)的作用。以 Backbone 为例。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/backbone-routing.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/backbone-routing.png) ~~~ App.Router = Backbone.Router.extend({ routes: { '': 'index', 'show': 'show' }, index: function () { $(document.body).append("调用了 Index 路由"); }, show: function () { $(document.body).append("调用了 Show 路由"); }, }); ~~~ ## 示例:Backbone Router 打开`demos/backbone-demo/index.html`,按照[《操作说明》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#backbone),查看示例。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/backbone-demo.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/backbone-demo.png) ## SPA 前端可以做到: > * 读写数据 > * 切换视图 > * 用户交互 这意味着,网页其实是一个应用程序。 > SPA = Single-page application 2010年后,前端工程师从开发页面,变成了开发”前端应用“(跑在浏览器里面的应用程序)。 ## Angular Google 公司推出的 Angular 是最流行的 MVC 前端框架。 它的风格属于 HTML 语言的增强,核心概念是双向绑定。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/angular.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/angular.png) ## 示例:Angular 的双向绑定 浏览器打开`demos/angular-demo/index.html`,可以看到一个输入框。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/angular-demo.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/angular-demo.png) ~~~

    姓名 :

    你好,{{name}}

    ~~~ ## Vue Vue.js 是现在很热门的一种前端 MVC 框架。 它的基本思想与 Angular 类似,但是用法更简单,而且引入了响应式编程的概念。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/vue-logo.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/vue-logo.png) ## 示例:Vue 的双向绑定 Vue 的模板与数据,是双向绑定的。 打开`demos/vue-demo/index1.html`,按照[《操作说明》](https://github.com/ruanyf/jstraining/blob/master/demos/README.md#vue),查看示例。 [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/vue-demo.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/vue-demo.png) HTML 代码 ~~~
    {{message}}
    ~~~ JS 代码 ~~~ var journal = new Vue({ el: '#journal', data: { message: 'Your first entry' } }); ~~~ ## 前后端分离 * Ajax -> 前端应用兴起 * 智能手机 -> 多终端支持 这两个原因,导致前端开发方式发生根本的变化。 前端不再是后端 MVC 中的 V,而是单独的一层。 ## REST 接口 前后端分离以后,它们之间通过接口通信。 后端暴露出接口,前端消费后端提供的数据。 后端接口一般是 REST 形式,前后端的通信协议一般是 HTTP。 ## Node 2009年,Node 项目诞生,它是服务器上的 JavaScript 运行环境。 Node = JavaScript + 操作系统 API [![](https://github.com/ruanyf/jstraining/raw/master/docs/images/node-logo.png)](https://github.com/ruanyf/jstraining/blob/master/docs/images/node-logo.png) ## Node 的意义 * JavaScript 成为服务器脚本语言,与 Python 和 Ruby 一样 * JavaScript 成为唯一的浏览器和服务器都支持的语言 * 前端工程师可以编写后端程序了 ## 前端开发模式的根本改变 * Node 环境下开发 * 大量使用服务器端工具 * 引入持续集成等软件工程的标准流程 * 开发完成后,编译成浏览器可以运行的脚本,放上 CDN ## 全栈工程师 前端工程师正在转变为全栈工程师 * 一个人负责开发前端和后端 * 从数据库到 UI 的所有开发 ## 全栈技能 怎样才能称为全栈工程师? * 传统前端技能:HTML、JavaScript、CSS * 一门后端语言 * 移动端开发:iOS / Android / HTML5 * 其他技能:数据库、HTTP 等等 ## 软件行业的发展动力 历史演变:前后端不分 -> 前后端分离 -> 全栈工程师 动力:更加产业化、大规模地生产软件 * 效率更高 * 成本更低 通用性好、能够快速产出的技术最终会赢,单个程序员的生产力要求越来越高。 ## 未来软件的特点 * 联网 * 高并发 * 分布式 * 跨终端 现在基于 Web 的前端技术,将演变为未来所有软件的通用的 GUI 解决方案。 ## 未来只有两种软件工程师 * 端工程师 * 手机端 * PC 端 * TV 端 * VR 端 * …… * 云工程师
    ';

    培训准备

    最后更新于:2022-04-01 23:31:49

    > 原文出处:https://github.com/ruanyf/jstraining > 作者:阮一峰 全栈工程师培训材料,帮助学习者掌握全栈开发的基本知识,承担简单 Web 应用的前后端开发。 一共四讲,适合两天的训练营,请先阅读《培训准备》。培训时,需要完成课堂练习。 # 培训准备 参加培训的学员,事先应该做好以下准备工作。 ## 知识准备 * 掌握 HTML、CSS、JS 的基本用法 * 掌握命令行的基本用法 ## 安装 Git 请到官网 [git-scm.com](https://git-scm.com/) 或国内的下载站,下载安装包。 ## 安装 Node 请到 Node 官网[nodejs.org](https://nodejs.org/),或者国内镜像[npm.taobao.org/mirrors/node](https://npm.taobao.org/mirrors/node),下载安装包。推荐安装最新的稳定版,目前是v6.x。 安装完成后,命令行执行下面的命令,确认是否安装成功。 ~~~ $ node -v v6.9.1 ~~~ Node 的模块管理器 npm 会一起安装好。由于 Node 的官方模块仓库网速太慢,模块仓库需要切换到阿里的源。 ~~~ $ npm config set registry https://registry.npm.taobao.org/ ~~~ 执行下面的命令,确认是否切换成功。 ~~~ $ npm config get registry ~~~ ## 安装 Postman Postman 是一个 HTTP 通信测试工具,REST API 的练习会用到它。 请到官网 [GetPostman.com](https://www.getpostman.com/) 下载独立安装包;也可以参考这篇文章[www.cnblogs.com/mafly/p/postman.html](http://www.cnblogs.com/mafly/p/postman.html),下载 Chrome 浏览器的插件,它们的效果一样。 ## 安装示例库 所有的讲义和练习源码,都是开源的,网址是 [github.com/ruanyf/jstraining](https://github.com/ruanyf/jstraining)。执行下面的命令,将这个库拷贝到你的硬盘上。 ~~~ # Linux & Mac $ git clone git@github.com:ruanyf/jstraining.git # Windows $ git clone https://github.com/ruanyf/jstraining.git ~~~ 如果因为种种原因,Git 命令行无法使用,也可以直接下载压缩包,地址是[https://github.com/ruanyf/jstraining/archive/master.zip](https://github.com/ruanyf/jstraining/archive/master.zip) 。
    ';