# Super-MEAN **Repository Path**: xuepx/Super-MEAN ## Basic Information - **Project Name**: Super-MEAN - **Description**: MEAN教程 - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2016-04-07 - **Last Updated**: 2024-11-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Super MEAN 全栈教程
薛鹏翔
2016-4-9
本教程[^ 参考Youtube教程(说是抄袭也不为过) [https://goo.gl/O3FqyT](https://goo.gl/O3FqyT)]介绍采用MEAN开发,以Todolist应用为例,涉及MongoDB Express AngularJS Node Webpack SASS Babel/ES6 Bootstrap AngularUI-Router ## Step1 基本环境nodeJS *** 网址:https://nodejs.org/en/ 链接:https://nodejs.org/en/download/ 安装完成,在命令行运行 `node -v` `v5.5.1` 显示具体版本号,即表示安装正确。 node安装时应将包管理工具npm也一并安装,若执行命令 `npm -v` `3.3.12` 显示具体版本号, 即包管理工具npm也安装正确。 ## Step2 建立工程目录 *** 新建工程目录todo-app `mkdir todo-app` `cd todo-app` ## Step3 建立package.json文件 *** 运行如下命令 `npm init -y` 自动生成package.json在工程目录中 Wrote to /Users/xuepx/MEAN/todo-app/package.json: { "name": "todo-app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } ## Step4 安装Express *** `npm install --save express` > `install` 安装(可用`i`代替) > `--save` 保存安装信息到package.json中(可用`-S`代替) 安装结束后package.json内容会发生变化,出现了依赖库 ```javascript { "name": "todo-app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.13.4" } } ``` ## Step5 建立Server *** 工程目录里新建server.js ```javascript var express = require('express'); var app = express(); var PORT = process.env.PORT || 3000; app.all('/*', function(request, response){ response.send('hello Express!'); }) app.listen(PORT, function(){ console.log("Server running at " + PORT); }) ``` 运行 `node server.js` 启动服务,浏览器打开[localhost:3000](http://localhost:3000)即可访问. 可以将response内容换成HTML。 ```javascript var express = require('express'); var app = express(); var PORT = process.env.PORT || 3000; app.all('/*', function(request, response){ response.send('\ \ \ 测试Express\ \ \

这是个例子

\ \ \ '); }) app.listen(PORT, function(){ console.log("Server running at " + PORT); }) ``` 重启服务,Ctrl+C关闭,重新运行`node server`, 打开浏览器即访问到新的页面. ## Step6 webpack MODULE BUNDLER *** `npm install -g webpack webpack-dev-server` > `-g` 全局安装, 即安装到node环境下, 不是在当前工程中. `npm install --save-dev webpack webpack-dev-server` > `--save-dev` 安装到开发依赖环境, 即当前工程部署环境不需要, 但开发环境需要(可用`-D`代替). 此时package.json内容会增加devDependencies段落 ```javascript { "name": "todo-app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.13.4" }, "devDependencies": { "webpack": "^1.12.14", "webpack-dev-server": "^1.14.1" } } ``` ## Step7 babel-loader babel-preset-es2015 *** `npm install --save-dev babel-loader babel-preset-es2015` 安装完后package.json文件中开发依赖包会增加该内容 ```javascript { "name": "todo-app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.13.4" }, "devDependencies": { "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.6.0", "webpack": "^1.12.14", "webpack-dev-server": "^1.14.1" } } ``` ## Step8 配置webpack 新建webpack.config.js文件 ```javascript var webpack = require('webpack'); var path = require('path'); module.exports = { devtool: 'inline-source-map', entry: [ 'webpack-dev-server/client?http://127.0.0.1:8080/', 'webpack/hot/only-dev-server', './src' ], output: { path: path.join(__dirname, 'public'), filename: 'bundle.js' }, resolve: { modulesDirectories: ['node_modules', 'src'], extension: ['', '.js'] }, module:[ { test: /\.js$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015'] } }, { test: /\.html$/, loader: 'raw' } ], plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), ], devServer: { hot: true, proxy: { '*': 'http://localhost:3000' } } } ``` 新建目录src, public, 并在目录src下新建index.js ```javascript var message = 'Hello from the entry file'; console.log(message); ``` 修改server.js增加bundle.js ```javascript var express = require('express'); var app = express(); var PORT = process.env.PORT || 3000; app.all('/*', function(request, response){ response.send('\ \ \ 测试Express\ \ \ \

这是个例子

\ \ \ '); }) app.listen(PORT, function(){ console.log("Server running at " + PORT); }) ``` 运行命令 `node server & webpack-dev-server` 即可打开浏览器访问[locahost:8080](http://localhost:8080) > 当然打开[localhost:3000](http://localhost:3000)也可访问, 与8080端口的区别时如果代码发生变化, 不需要手动重启服务, webpack-dev-server会帮我们重启该服务, 如我们现在访问8080, 打开浏览器开发者工具, 控制台可以看到我们在index.js中写的log(Hello from the entry file), 如果我们修改这个log, 那么只需要刷新浏览器即可,不必重启node ## Step9 webpack快速启动dev-server *** 修改package.json的scripts段如下 ```javascript { "name": "todo-app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "NODE_PATH=$NODE_PATH:./src node server & ", "dev": "npm start webpack-dev-server --progress --colors" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.13.4" }, "devDependencies": { "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.6.0", "webpack": "^1.12.14", "webpack-dev-server": "^1.14.1" } } ``` 运行命令 `killall -9 node` > 如果之前没有启动服务则无需此步 > windows下需要打开任务管理器关闭node进程. `npm run dev` > *之后运行dev-server只需执行上述命令即可.* ## Step10 Angular Angular-ui-router *** 执行命令 `npm install --save angular angular-ui-router` 安装结束, package.json会增加相应的依赖项. 执行命令 `npm install -D raw-loader` 安装raw loader作为开发环境, 安装结束package.json会增加dependencies内容 ```json { "name": "todo-app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "NODE_PATH=$NODE_PATH:./src node server & ", "dev": "npm start webpack-dev-server --progress --colors" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "angular": "^1.5.3", "angular-ui-router": "^0.2.18", "express": "^4.13.4" }, "devDependencies": { "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.6.0", "raw-loader": "^0.5.1", "webpack": "^1.12.14", "webpack-dev-server": "^1.14.1" } } ``` 新建src/config.js ```js import angular from 'angular'; import uiRouter from 'angular-ui-router'; const app = angular.module('app', [uiRouter]); app.config(($stateProvider, $urlRouterProvider, $locationProvider) => { $urlRouterProvider.other('/'); $stateProvider .state('todos', { url: '/', template: require('todos/todos.html') }) .state('about', { url: 'about', template: require('about/about.html') }) $locationProvider.html5Mode(true); }); export default app; ``` 修改src/index.js ```js var express = require('express'); var app = express(); var PORT = process.env.PORT || 3000; app.all('/*', function(request, response){ response.send('\ \ \ 测试Angular\ \ \ \
\ \ \ \ '); }) app.listen(PORT, function(){ console.log("Server running at " + PORT); }) ``` 新建目录src/todos和src/about, 并建立文件src/todos/todos.html如下 ```html
About

ToDo 应用

``` 建立src/about/about.html如下 ```html
ToDo

关于 Todo 应用

这是个学习应用

``` 修改webpack.config.js如下 ```javascript var webpack = require('webpack'); var path = require('path'); module.exports = { devtool: 'inline-source-map', entry: [ 'webpack-dev-server/client?http://127.0.0.1:8080/', 'webpack/hot/only-dev-server', './src' ], output: { path: path.join(__dirname, 'public'), filename: 'bundle.js' }, resolve: { modulesDirectories: ['node_modules', 'src'], extension: ['', '.js'] }, module:{ loaders:[ { test: /\.js$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015'] } }, { test: /\.html$/, loader: 'raw' } ] }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), ], devServer: { hot: true, proxy: { '*': 'http://localhost:3000' } } } ``` 执行命令 `npm run dev` 打开浏览器访问[localhost:8080](http://localhost:8080)可以看到页面,并点击about切换到Todo页面, 点击Todo切换到about. ## Step11 css-loader style-loader sass-loader node-sass autoprefixer-loader *** 执行命令 `npm install --save-dev css-loader style-loader sass-loader node-sass autoprefixer-loader` 安装开发环境. 安装完后package.json会增加开发依赖 ```javascript { "name": "todo-app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "NODE_PATH=$NODE_PATH:./src/ node server & ", "dev": "npm start webpack-dev-server --progress --colors" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "angular": "^1.5.3", "angular-ui-router": "^0.2.18", "express": "^4.13.4" }, "devDependencies": { "autoprefixer-loader": "^3.2.0", "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.6.0", "css-loader": "^0.23.1", "node-sass": "^3.4.2", "raw-loader": "^0.5.1", "sass-loader": "^3.2.0", "style-loader": "^0.13.1", "webpack": "^1.12.14", "webpack-dev-server": "^1.14.1" } } ``` 在工程中配置让css, sass等文件导入工程, 修改webpack.config.js的resolve(增加扩展名为scss)和module的loaders如下 ```javascript var webpack = require('webpack'); var path = require('path'); module.exports = { devtool: 'inline-source-map', entry: [ 'webpack-dev-server/client?http://127.0.0.1:8080/', 'webpack/hot/only-dev-server', './src' ], output: { path: path.join(__dirname, 'public'), filename: 'bundle.js' }, resolve: { modulesDirectories: ['node_modules', 'src'], extension: ['', '.js', '.scss'] }, module:{ loaders:[ { test: /\.js$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015'] } }, { test: /\.html$/, loader: 'raw' }, { test: /\.scss$/, loaders: [ 'style', 'css', 'autoprefixer?browsers=last 3 versions', 'sass?outputStyle=expanded' ] } ] }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), ], devServer: { hot: true, proxy: { '*': 'http://localhost:3000' } } } ``` ## Step12 为HTML增加style *** 修改src/about/about.html ```html
About

ToDo 应用

``` 和src/todos/todos.html ```html
Todo

关于 Todo 应用

这是个学习应用

``` 新建文件夹src/css, 并新建文件src/css/master.scss内容如下 ```sass .nav-link { float: right; margin-top: 20px; } ``` 将sass引入工程, 修改src/index.js ```javascript import angular from 'angular'; import appModule from 'config'; import 'css/master.scss'; angular.bootstrap(document, [appModule.name]); ``` 刷新页面, 添加的样式即起作用, About和todos连接显示在右侧。 ## Step13 bootstrap-sass juqery bootstrap-loader url-loader file-loader resolve-url-loader imports-loader *** 执行命令 `npm install --save bootstrap-sass jquery` 为工程引入bootstrap和query. 执行命令 `npm install --save-dev bootstrap-loader url-loader file-loader resolve-url-loader imports-loader` 为开发环境进入必要包, 两个命令执行完后package.json中依赖和开发依赖会添加相应内容 ```javascript { "name": "todo-app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "NODE_PATH=$NODE_PATH:./src/ node server & ", "dev": "npm start webpack-dev-server --progress --colors" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "angular": "^1.5.3", "angular-ui-router": "^0.2.18", "bootstrap-sass": "^3.3.6", "express": "^4.13.4", "jquery": "^2.2.3" }, "devDependencies": { "autoprefixer-loader": "^3.2.0", "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.6.0", "bootstrap-loader": "^1.0.10", "css-loader": "^0.23.1", "file-loader": "^0.8.5", "imports-loader": "^0.6.5", "node-sass": "^3.4.2", "raw-loader": "^0.5.1", "resolve-url-loader": "^1.4.3", "sass-loader": "^3.2.0", "style-loader": "^0.13.1", "url-loader": "^0.5.7", "webpack": "^1.12.14", "webpack-dev-server": "^1.14.1" } } ``` 配置bootstrap和jquery引入工程, 修改webpack.config.js增加bootstrap的entry和各类文件的module loader ```javascript var webpack = require('webpack'); var path = require('path'); module.exports = { devtool: 'inline-source-map', entry: [ 'webpack-dev-server/client?http://127.0.0.1:8080/', 'webpack/hot/only-dev-server', 'bootstrap-loader', './src' ], output: { path: path.join(__dirname, 'public'), filename: 'bundle.js' }, resolve: { modulesDirectories: ['node_modules', 'src'], extension: ['', '.js', '.scss'] }, module:{ loaders:[ { test: /\.js$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015'] } }, { test: /\.html$/, loader: 'raw' }, { test: /\.scss$/, loaders: [ 'style', 'css', 'autoprefixer?browsers=last 3 versions', 'sass?outputStyle=expanded' ] }, { test: /\.(woff2?|ttf|eot|svg)$/, loader: 'url?limit=10000' }, { test: /bootstrap-sass\/assets\/javascripts\//, loader: 'imports' } ] }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery", "window.jQuery": "jquery" }) ], devServer: { hot: true, proxy: { '*': 'http://localhost:3000' } } } ``` ## Step14 Form *** 修改src/config.js增加todos的controller如下 ```javascript import angular from 'angular'; import uiRouter from 'angular-ui-router'; import todosController from 'todos/todos'; const app = angular.module('app', [uiRouter]); app.config(($stateProvider, $urlRouterProvider, $locationProvider) => { $urlRouterProvider.otherwise('/'); $stateProvider .state('todos', { url: '/', template: require('todos/todos.html'), controller: todosController }) .state('about', { url: '/about', template: require('about/about.html') }) $locationProvider.html5Mode(true); }); export default app; ``` 实现controller, 新建文件src/todos/todos.js ```javascript export default function() { } ``` 在src/todos/todos.html增加form ```html
About

ToDo 应用

``` 为todos增加css, 新建文件src/css/todos.scss内容如下: ```sass .todos{ &__create-input { margin: 0 auto 5px; width: 300px; } &__create-button { display: block; margin: 0 auto 20px; } } /* .todos{ &__create-input { } } 等价于 .todos__create-input{ } */ ``` 在src/css/master.scss引入新添加的样式文件 ```sass @import 'todos.scss'; .nav-link { float: right; margin-top: 20px; } ``` 刷新页面即可看到bootstrap样式的页面. ## Step15 Table *** 修改src/todos/todos.html增加table ```html
About

ToDo 应用

状态 任务 操作
未完成 学习Mongodb 编辑, 删除
未完成 学习Express 编辑, 删除
未完成 学习Angular 编辑, 删除
未完成 学习Node 编辑, 删除
``` 刷新页面可以看到表格. 下面通过Controller实现刚才的表格, 修改src/todos/todos.js内容如下: ```javascript export default function($scope) { $scope.todos = [ { task: "学习Mongodb", isCompleted: false }, { task: "学习Express", isCompleted: false }, { task: "学习Angular", isCompleted: false }, { task: "学习Node", isCompleted: true }, ]; } ``` 这里是建立一个json对象todos, 并传递到todos.html中(之前配置了controller), 下面修改src/todos/todos.html ```html
About

ToDo 应用

状态 任务 操作
{{todo.task}}
``` ## Step16 check事件绑定 *** 为checkbox增加事件绑定, 修改src/todos/todos.html部分内容绑定click事件如下 ```html {{todo.task}} ``` 在src/todos/todos.js中实现函数onCompletedClick ```javascript export default function($scope) { $scope.todos = [{ task: "学习Mongodb", isCompleted: false }, { task: "学习Express", isCompleted: false }, { task: "学习Angular", isCompleted: false }, { task: "学习Node", isCompleted: true }, ]; $scope.onCompletedClick = todo => { todo.isCompleted = !todo.isCompleted; } } ``` 下面为已完成和未完成的任务添加不同的样式, 修改src/todos/todos.html的任务列 ```html {{todo.task}} ``` 在src/css/todos.scss中增加完成时的样式 ```sass .todos{ &__create-input { margin: 0 auto 5px; width: 300px; } &__create-button { display: block; margin: 0 auto 20px; } &__task{ &--completed { text-decoration: line-through; } } } ``` 刷新页面, 即可看到已完成项目出现删除线, 并且点击状态的checkbox删除线也随着消失或出现. ## Step17 Change事件绑定 *** 我们希望用户在任务框输入时任务列表同步出现添加, 点击创建任务按钮(或回车)后添加该任务, 首先修改src/todos/todos.html为输入框增加model ```
``` 修改Controller监视input的change ```javascript export default function($scope) { let params = { createHasInput: false, // 输入内容是否已经创建任务 } $scope.todos = [{ task: "学习Mongodb", isCompleted: false }, { task: "学习Express", isCompleted: false }, { task: "学习Angular", isCompleted: false }, { task: "学习Node", isCompleted: true }, ]; $scope.onCompletedClick = todo => { todo.isCompleted = !todo.isCompleted; } $scope.$watch('createTaskInput', val => { //输入框内容不为空, 且未创建任务 if (val && !params.createHasInput){ $scope.todos.push({task: val, isCompleted:false}); params.createHasInput = true; // 已创建 } // 输入框内容不为空, 且已创建任务 else if (val && params.createHasInput){ $scope.todos[$scope.todos.length - 1].task = val; } // 输入内容为空, 且未创建(即删除输入框内容) else if (!val && params.createHasInput){ $scope.todos.pop(); params.createHasInput = false; } }); } ``` ## Step18 Submit *** 为form添加submit, 修改src/todos/todos.html ```html
``` 在controller中实现createTask, 修改src/todos/todos.js ```javascript export default function($scope) { let params = { createHasInput: false, // 输入内容是否已经创建任务 } $scope.todos = [{ task: "学习Mongodb", isCompleted: false }, { task: "学习Express", isCompleted: false }, { task: "学习Angular", isCompleted: false }, { task: "学习Node", isCompleted: true }, ]; $scope.onCompletedClick = todo => { todo.isCompleted = !todo.isCompleted; } $scope.createTask = () => { params.createHasInput = false; // 置状态为未创建(避免被判断为用户删除输入内容) $scope.createTaskInput = ""; } $scope.$watch('createTaskInput', val => { //输入框内容不为空, 且未创建任务 if (val && !params.createHasInput){ $scope.todos.push({task: val, isCompleted:false}); params.createHasInput = true; // 已创建 } // 输入框内容不为空, 且已创建任务 else if (val && params.createHasInput){ $scope.todos[$scope.todos.length - 1].task = val; } // 输入内容为空, 且未创建(即删除输入框内容) else if (!val && params.createHasInput){ $scope.todos.pop(); params.createHasInput = false; } }); } ``` ## Step19 任务编辑 *** 修改src/todos/todos.html为修改按钮增加响应绑定。 ```javascript ``` 修改src/todos/todos.js增加具体实现, 为每个任务添加isEditing属性, 点击编辑时修改该属性, HTML端依据该属性判断显示输入框或span. ```javascript $scope.todos = [{ task: "学习Mongodb", isCompleted: false, isEditing: false, }, { task: "学习Express", isCompleted: false, isEditing: false, }, { task: "学习Angular", isCompleted: false, isEditing: false, }, { task: "学习Node", isCompleted: true, isEditing: false, }, ]; $scope.onEditBtnClick = todo => { todo.isEditing = true; } ``` 修改src/todos/todos.html ```html {{todo.task}}
```