# 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
```
建立src/about/about.html如下
```html
```
修改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
```
和src/todos/todos.html
```html
```
新建文件夹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
```
为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
```
## 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}}
|
|
```