本教程[^ 参考Youtube教程(说是抄袭也不为过) https://goo.gl/O3FqyT]介绍采用MEAN开发,以Todolist应用为例,涉及MongoDB Express AngularJS Node Webpack SASS Babel/ES6 Bootstrap AngularUI-Router
网址:https://nodejs.org/en/
链接:https://nodejs.org/en/download/
安装完成,在命令行运行
node -v
v5.5.1
显示具体版本号,即表示安装正确。
node安装时应将包管理工具npm也一并安装,若执行命令
npm -v
3.3.12
显示具体版本号, 即包管理工具npm也安装正确。
新建工程目录todo-app
mkdir todo-app
cd todo-app
运行如下命令
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"
}
npm install --save express
install
安装(可用i
代替)
--save
保存安装信息到package.json中(可用-S
代替)
安装结束后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",
"dependencies": {
"express": "^4.13.4"
}
}
工程目录里新建server.js
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即可访问.
可以将response内容换成HTML。
var express = require('express');
var app = express();
var PORT = process.env.PORT || 3000;
app.all('/*', function(request, response){
response.send('\
<html>\
<head>\
<title>测试Express</title>\
</head>\
<body>\
<h1>这是个例子</h1>\
</body>\
</html>\
');
})
app.listen(PORT, function(){
console.log("Server running at " + PORT);
})
重启服务,Ctrl+C关闭,重新运行node server
, 打开浏览器即访问到新的页面.
npm install -g webpack webpack-dev-server
-g
全局安装, 即安装到node环境下, 不是在当前工程中.
npm install --save-dev webpack webpack-dev-server
--save-dev
安装到开发依赖环境, 即当前工程部署环境不需要, 但开发环境需要(可用-D
代替).
此时package.json内容会增加devDependencies段落
{
"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"
}
}
npm install --save-dev babel-loader babel-preset-es2015
安装完后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",
"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"
}
}
新建webpack.config.js文件
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
var message = 'Hello from the entry file';
console.log(message);
修改server.js增加bundle.js
var express = require('express');
var app = express();
var PORT = process.env.PORT || 3000;
app.all('/*', function(request, response){
response.send('\
<html>\
<head>\
<title>测试Express</title>\
<script src="bundle.js"></script>\
</head>\
<body>\
<h1>这是个例子</h1>\
</body>\
</html>\
');
})
app.listen(PORT, function(){
console.log("Server running at " + PORT);
})
运行命令
node server & webpack-dev-server
即可打开浏览器访问locahost:8080
当然打开localhost:3000也可访问, 与8080端口的区别时如果代码发生变化, 不需要手动重启服务, webpack-dev-server会帮我们重启该服务, 如我们现在访问8080, 打开浏览器开发者工具, 控制台可以看到我们在index.js中写的log(Hello from the entry file), 如果我们修改这个log, 那么只需要刷新浏览器即可,不必重启node
修改package.json的scripts段如下
{
"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只需执行上述命令即可.
执行命令
npm install --save angular angular-ui-router
安装结束, package.json会增加相应的依赖项.
执行命令
npm install -D raw-loader
安装raw loader作为开发环境, 安装结束package.json会增加dependencies内容
{
"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
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
var express = require('express');
var app = express();
var PORT = process.env.PORT || 3000;
app.all('/*', function(request, response){
response.send('\
<html>\
<head>\
<title>测试Angular</title>\
<base href="/">\
</head>\
<body>\
<div ui-view></div> \
<script src="bundle.js"></script>\
</body>\
</html>\
');
})
app.listen(PORT, function(){
console.log("Server running at " + PORT);
})
新建目录src/todos和src/about, 并建立文件src/todos/todos.html如下
<div>
<a ui-sref="about">About</a>
<h2> ToDo 应用</h2>
</div>
建立src/about/about.html如下
<div>
<a ui-sref="todos">ToDo</a>
<h2> 关于 Todo 应用</h2>
<p>这是个学习应用</p>
</div>
修改webpack.config.js如下
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可以看到页面,并点击about切换到Todo页面, 点击Todo切换到about.
执行命令
npm install --save-dev css-loader style-loader sass-loader node-sass autoprefixer-loader
安装开发环境. 安装完后package.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": {
"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如下
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'
}
}
}
修改src/about/about.html
<div>
<a ui-sref="about" class="nav-link">About</a>
<h2> ToDo 应用</h2>
</div>
和src/todos/todos.html
<div>
<a ui-sref="todos" class="nav-link">Todo</a>
<h2> 关于 Todo 应用</h2>
<p>这是个学习应用</p>
</div>
新建文件夹src/css, 并新建文件src/css/master.scss内容如下
.nav-link {
float: right;
margin-top: 20px;
}
将sass引入工程, 修改src/index.js
import angular from 'angular';
import appModule from 'config';
import 'css/master.scss';
angular.bootstrap(document, [appModule.name]);
刷新页面, 添加的样式即起作用, About和todos连接显示在右侧。
执行命令
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中依赖和开发依赖会添加相应内容
{
"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
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'
}
}
}
修改src/config.js增加todos的controller如下
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
export default function() {
}
在src/todos/todos.html增加form
<div class="container">
<a ui-sref="about" class="nav-link">About</a>
<h2> ToDo 应用</h2>
<form class="" action="index.html" method="post">
<input class="form-control todos__create-input" placeholder="你要做什么?">
<button type="button" class="btn btn-success todos__create-button">创建任务</button>
</form>
</div>
为todos增加css, 新建文件src/css/todos.scss内容如下:
.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引入新添加的样式文件
@import 'todos.scss';
.nav-link {
float: right;
margin-top: 20px;
}
刷新页面即可看到bootstrap样式的页面.
修改src/todos/todos.html增加table
<div class="container">
<a ui-sref="about" class="nav-link">About</a>
<h2> ToDo 应用</h2>
<form class="" action="index.html" method="post">
<input class="form-control todos__create-input" placeholder="你要做什么?">
<button type="button" class="btn btn-success todos__create-button">创建任务</button>
</form>
<table class='table table-striped'>
<tr>
<th>
状态
</th>
<th>
任务
</th>
<th>
操作
</th>
</tr>
<tr>
<td>
未完成
</td>
<td>
学习Mongodb
</td>
<td>
编辑, 删除
</td>
</tr>
<tr>
<td>
未完成
</td>
<td>
学习Express
</td>
<td>
编辑, 删除
</td>
</tr>
<tr>
<td>
未完成
</td>
<td>
学习Angular
</td>
<td>
编辑, 删除
</td>
</tr>
<tr>
<td>
未完成
</td>
<td>
学习Node
</td>
<td>
编辑, 删除
</td>
</tr>
</table>
</div>
刷新页面可以看到表格. 下面通过Controller实现刚才的表格, 修改src/todos/todos.js内容如下:
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
<div class="container">
<a ui-sref="about" class="nav-link">About</a>
<h2> ToDo 应用</h2>
<form>
<input class="form-control todos__create-input" placeholder="你要做什么?">
<button type="button" class="btn btn-success todos__create-button">创建任务</button>
</form>
<table class='table table-striped'>
<tr>
<th>
状态
</th>
<th>
任务
</th>
<th>
操作
</th>
</tr>
<tr ng-repeat="todo in todos">
<td>
<input type="checkbox" class="todo__checkbox" ng-checked="todo.isCompleted" />
</td>
<td>
<span class="todos__task">{{todo.task}}</span>
</td>
<td>
<button class="btn btn-info">编辑</button>
<button class="btn btn-danger">删除</button>
</td>
</tr>
</table>
</div>
为checkbox增加事件绑定, 修改src/todos/todos.html部分内容绑定click事件如下
<tr ng-repeat="todo in todos">
<td>
<input type="checkbox" class="todo__checkbox"
ng-checked="todo.isCompleted"
ng-click="onCompletedClick(todo)"/>
</td>
<td>
<span class="todos__task">{{todo.task}}</span>
</td>
<td>
<button class="btn btn-info">编辑</button>
<button class="btn btn-danger">删除</button>
</td>
</tr>
在src/todos/todos.js中实现函数onCompletedClick
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的任务列
<td>
<span class="todos__task" ng-class="{'todos__task--completed':todo.isCompleted}">
{{todo.task}}
</span>
</td>
在src/css/todos.scss中增加完成时的样式
.todos{
&__create-input {
margin: 0 auto 5px;
width: 300px;
}
&__create-button {
display: block;
margin: 0 auto 20px;
}
&__task{
&--completed {
text-decoration: line-through;
}
}
}
刷新页面, 即可看到已完成项目出现删除线, 并且点击状态的checkbox删除线也随着消失或出现.
我们希望用户在任务框输入时任务列表同步出现添加, 点击创建任务按钮(或回车)后添加该任务, 首先修改src/todos/todos.html为输入框增加model
<form>
<input class="form-control todos__create-input" placeholder="你要做什么?" ng-model="createTaskInput">
<button type="button" class="btn btn-success todos__create-button">创建任务</button>
</form>
修改Controller监视input的change
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;
}
});
}
为form添加submit, 修改src/todos/todos.html
<form ng-submit="createTask()">
<input class="form-control todos__create-input" placeholder="你要做什么?" ng-model="createTaskInput">
<button type="submit" class="btn btn-success todos__create-button">创建任务</button>
</form>
在controller中实现createTask, 修改src/todos/todos.js
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;
}
});
}
修改src/todos/todos.html为修改按钮增加响应绑定。
<td>
<button class="btn btn-info" ng-click="onEditBtnClick(todo);">编辑</button>
<button class="btn btn-danger">删除</button>
</td>
修改src/todos/todos.js增加具体实现, 为每个任务添加isEditing属性, 点击编辑时修改该属性, HTML端依据该属性判断显示输入框或span.
$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
<tr ng-repeat="todo in todos">
<td>
<input type="checkbox" class="todo__checkbox" ng-checked="todo.isCompleted" ng-click="onCompletedClick(todo)" />
</td>
<td>
<span ng-if="!todo.isEditing" class="todos__task" ng-class="{'todos__task--completed':todo.isCompleted}">
{{todo.task}}
</span>
<form>
<input ng-if="todo.isEditing" class="form-control todos__update-input" ng-value="todo.task" />
</form>
</td>
<td>
<button class="btn btn-info" ng-click="onEditBtnClick(todo)">编辑</button>
<button class="btn btn-danger">删除</button>
</td>
</tr>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。