界面可能不是太好看?,考慮到容器的高度會被拉長,所以沒有用圖片作背景。css
便利貼html
sass
(css 預編譯器)webpack
(自動化構建工具,實現LESS,CSS,JS編譯和壓縮代碼)express
(基於 Node.js 平臺的 web 開發框架)html+css
Node.js
(基於 Chrome V8 引擎的 JavaScript 運行環境)jQuery
(一個快速、簡潔的JavaScript框架)sequelize
(Node的ORM框架Sequelize操做數據庫)passport
(實現第三方登陸)nrm
,在終端輸入:npm i nrm -g
而後以下操做:前端
開始!!node
1.新建一個文件夾,名字本身起,打開終端,切換到本身新建文件夾,如:jquery
cd (文件夾名稱)
2.生成 package.json
webpack
npm init -y
3.安裝 expressgit
npm i express --save
4.安裝 express生成器:github
npm install express-generator --save
5.生成 ejs 模板(相似 jsp 的寫法)web
express -f -e npm i
其中public
用來存放編譯後的js文件以及編譯好的css文件等,routes
用來存放處理 ajax 的請求文件,views就是存放視圖文件
而後新建 database 和 src:ajax
其中 src/js 裏面 app 表明不一樣頁面的入口文件,lib 就是一些經常使用的庫,mod 就是你寫的一些模塊,database 用來存放數據庫數據的
6.輸入:
npm start
若是有出現下面的錯誤:
出現這個錯誤是由於你沒有下載模塊,只需在終端輸入:
npm i (模塊名) --save
就能夠了
7.打開瀏覽器,輸入localhost:3000
出現下面這樣就說明成功了:
8.接下來安裝webpack和相關依賴
npm i webpack --save-dev npm i --save css-loader style-loader express-session express-flash node-sass passport sass sass-loader sequelize sqlite3 extract-text-webpack-plugin onchange
9.在 src 裏建一個 webpack.config.js,配置以下:
var webpack = require('webpack'); var path = require('path'); var ExtractTextPlugin = require('extract-text-webpack-plugin') var autoprefixer = require('autoprefixer'); module.exports = { entry: path.join(__dirname, "js/app/index"), output: { path: path.join(__dirname, "../public"), filename: "js/index.js" }, module: { rules: [{ test: /(\.scss)$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: ["css-loader", "sass-loader"] }) //把 css 抽離出來生成一個文件 }] }, resolve: { alias: { jquery: path.join(__dirname, "js/lib/jquery-2.0.3.min.js"), mod: path.join(__dirname, "js/mod"), sass: path.join(__dirname, "sass") } }, plugins: [ new webpack.ProvidePlugin({ $: "jquery" }), new ExtractTextPlugin("css/index.css"), new webpack.LoaderOptionsPlugin({ options: { css: [ autoprefixer(), ] } }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] }
說明
就不須要 require 了
這個是壓縮文件的
10.在 package.json 中,增長以下兩條:
寫成這樣,你在終端就能夠寫成npm run webpack
來編譯文件,npm run watch
來監控 src 裏面的 js 和 scss 的變化,只要一修改,進行編譯,提升了效率
11.測試
你能夠試試在 js 裏面的 index.js寫點東西,而後 npm run webpack,若是終端顯示是這樣:
就證實成功了
邏輯比較簡單
html,css就不講了,能夠看看個人源碼,主要講 js。
1.瀑布流的實現
思路:(前提是必須絕對定位)
- 獲取元素的寬度
- 經過窗口的寬度除以元素的寬度來獲取列數
- 初始化一個數組來獲取每列的高度,初始化每列的高度爲0
遍歷元素,獲取最小的的列數的高度和索引,對當前元素進行定位,列數高度加等於當前元素的高度
知道思路後,代碼很快就寫出來了:
var WaterFall = (function () { var $ct, $items; function render($c) { $ct = $c; $items = $ct.children(); var nodeWidth = $items.outerWidth(true), windowHeight = $(window).height(), colNum = parseInt($(window).width() / nodeWidth), //獲取列數 colSumHeight = []; //獲取每列的高度 //對每列的高度進行初始化 for (var i = 0; i < colNum; i++) { colSumHeight[i] = 0; } $items.each(function () { var $current = $(this); var index = 0, minSumHeight = colSumHeight[0]; //獲取最小的的列數的高度和索引 for (var i = 0; i < colSumHeight.length; i++) { if (minSumHeight > colSumHeight[i]) { index = i; minSumHeight = colSumHeight[i]; } } //改變窗口高度 if (windowHeight < minSumHeight) { $("body").height(minSumHeight); } else { $("body").height(windowHeight - 72); } //對當前元素進行定位 $current.animate({ left: nodeWidth * index, top: minSumHeight }, 5); colSumHeight[index] += $current.outerHeight(true); }); } //當窗口發生變化時,從新渲染 $(window).on('resize', function () { render($ct); }); return { init: render } })();
2.筆記的拖拽
咱們先看個圖
所以代碼以下:
//設置筆記的移動 $noteHead.on('mousedown', function (e) { var evtX = e.pageX - $note.offset().left, //evtX 計算事件的觸發點在 dialog內部到 dialog 的左邊緣的距離 evtY = e.pageY - $note.offset().top; $note.addClass('draggable').data('evtPos', { x: evtX, y: evtY }); //把事件到 dialog 邊緣的距離保存下來 }).on('mouseup', function () { $note.removeClass('draggable').removeData('pos'); }); $('body').on('mousemove', function (e) { $('.draggable').length && $('.draggable').offset({ top: e.pageY - $('.draggable').data('evtPos').y, // 當用戶鼠標移動時,根據鼠標的位置和前面保存的距離,計算 dialog 的絕對位置 left: e.pageX - $('.draggable').data('evtPos').x }); }); },
3.提示模塊
這個比較容易:
/* 提示模塊 參數:狀態(1表示成功,0表示失敗),消息,出現時間(不寫默認是1s) */ function toast(status, msg, time) { this.status = status; this.msg = msg; this.time = time || 1000; this.createToast(); this.showToast(); } toast.prototype = { createToast: function () { if (this.status === 1) { var html = '<div class="toast">![](../../imgs/1.png)</img><span class="toast_word">' + this.msg + '</span></div>'; this.$toast = $(html); $('body').append(this.$toast); } else { var html = '<div class="toast">![](../../imgs/0.png)</img><span class="toast_word">' + this.msg + '</span></div>'; this.$toast = $(html); $('body').append(this.$toast); } }, showToast: function () { var _this = this; this.$toast.fadeIn(300, function () { setTimeout(function () { _this.$toast.fadeOut(300, function () { _this.$toast.remove(); }); }, _this.time); }) } } function Toast(status, msg, time) { return new toast(status, msg, time); }
4.筆記模塊
思路:
- 初始化(如 id,username 等等)
- 建立節點
- 設置顏色
綁定事件
function Note(opts) { this.initOpts(opts); this.createNode(); this.setColor(); this.bind(); } Note.prototype = { colors: [ ['#ea9b35', '#efb04e'], // headColor, containerColor ['#dd598b', '#e672a2'], ['#c24226', '#d15a39'], ['#c1c341', '#d0d25c'], ['#3f78c3', '#5591d2'] ], defaultOpts: { id: '', //Note的 id $ct: $('#content').length > 0 ? $('#content') : $('body'), //默認存放 Note 的容器 context: '請輸入內容', //Note 的內容 createTime: new Date().toLocaleDateString().replace(/\//g, '-').match(/^\d{4}-\d{1,2}-\d{1,2}/), username: 'admin' }, initOpts: function (opts) { this.opts = $.extend({}, this.defaultOpts, opts || {}); if (this.opts.id) { this.id = this.opts.id; } this.createTime = this.opts.createTime ? this.opts.createTime : new Date().toLocaleDateString().replace(/\//g, '-').match(/^\d{4}-\d{1,2}-\d{1,2}/); this.username = this.opts.username ? this.opts.username : 'admin' }, createNode: function () { var tpl = '<div class="note">' + '<div class="note-head"><span class="delete">×</span></div>' + '<div class="note-ct" contenteditable="true"></div>' + '<div class="note-info"><div class="note-name">' + this.username + '</div><div class="note-time">' + this.createTime + '</div>' + '</div>'; this.$note = $(tpl); this.$note.find('.note-ct').html(this.opts.context); this.opts.$ct.append(this.$note); //if (!this.id) this.$note.css('bottom', '10px'); //新增放到右邊 Event.fire('waterfall'); }, setColor: function () { var color = this.colors[Math.floor(Math.random() * 5)]; this.$note.find(".note-head").css('background-color', color[0]); this.$note.find('.note-ct').css('background-color', color[1]); this.$note.find('.note-info').css('background-color', color[1]); }, setLayout: function () { var self = this; if (self.clock) { clearTimeout(self.clock); } self.clock = setTimeout(function () { Event.fire('waterfall'); }, 100); }, bind: function () { var _this = this, //記錄下坑,以前末尾是分號不是逗號後面都變成了全局變量結果形成了最後一個才能修改? $note = this.$note, $noteHead = $note.find('.note-head'), $noteCt = $note.find('.note-ct'), $close = $note.find('.delete'); $close.on('click', function () { _this.delete(); }); $noteCt.on('focus', function () { if ($noteCt.html() === '請輸入內容') $noteCt.html(''); $noteCt.data('before', $noteCt.html()); }).on('blur paste', function () { if ($noteCt.data('before') != $noteCt.html()) { $noteCt.data('before', $noteCt.html()); _this.setLayout(); if (_this.id) { //判斷是否有這個id,若是有就更新,若是沒有就添加 _this.edit($noteCt.html()) } else { _this.add($noteCt.html()) } } }); //設置筆記的移動 $noteHead.on('mousedown', function (e) { var evtX = e.pageX - $note.offset().left, //evtX 計算事件的觸發點在 dialog內部到 dialog 的左邊緣的距離 evtY = e.pageY - $note.offset().top; $note.addClass('draggable').data('evtPos', { x: evtX, y: evtY }); //把事件到 dialog 邊緣的距離保存下來 }).on('mouseup', function () { $note.removeClass('draggable').removeData('pos'); }); $('body').on('mousemove', function (e) { $('.draggable').length && $('.draggable').offset({ top: e.pageY - $('.draggable').data('evtPos').y, // 當用戶鼠標移動時,根據鼠標的位置和前面保存的距離,計算 dialog 的絕對位置 left: e.pageX - $('.draggable').data('evtPos').x }); }); }, /* 添加筆記到數據庫 */ add: function (msg) { var _this = this; $.post('/api/notes/add', { note: msg }).done(function (res) { if (res.status === 1) { _this.id = res.id; Toast(1, '添加成功!'); } else { _this.$note.remove(); Event.fire('waterfall'); Toast(0, res.errorMsg); } }) }, /* 編輯筆記數據庫 */ edit: function (msg) { var _this = this; $.post('/api/notes/edit', { id: this.id, note: msg }).done(function (res) { if (res.status === 1) { Toast(1, '更新成功!'); } else { Toast(0, res.errorMsg); } }); }, /* 刪除筆記 */ delete: function () { var _this = this; if (confirm("確認要刪除嗎?")) { $.post('/api/notes/delete', { id: this.id }).done(function (res) { if (res.status === 1) { Toast(1, '刪除成功!'); _this.$note.remove(); Event.fire('waterfall') } else { Toast(0, res.errorMsg); } }); } } }
5.筆記管理模塊
var NoteManager = (function () { //頁面加載 function load() { $.get('api/notes').done(function (res) { if (res.status === 1) { $.each(res.data, function (index, msg) { new Note({ id: msg.id, context: msg.text, createTime: msg.createdAt.match(/^\d{4}-\d{1,2}-\d{1,2}/), username: msg.username }); }); Event.fire('waterfall'); } else { Toast(0, res.errorMsg); } }).fail(function () { Toast(0, "網絡異常"); }); } /* 添加筆記 */ function add() { $.get('/login').then(function (res) {//判斷是否登陸 if (res.status === 1) { new Note({ username: res.username }); } else { Toast(0, res.errorMsg); } }); } return { load: load, add: add } })();
6.發佈訂閱模式
/* 發佈訂閱模式 */ var Event = (function () { var events = {}; function on(evt, handler) { events[evt] = events[evt] || []; events[evt].push({ handler: handler }); } function fire(evt, args) { if (!events[evt]) { return; } for (var i = 0; i < events[evt].length; i++) { events[evt][i].handler(args); } } function off(name) { delete events[name]; } return { on: on, fire: fire, off: off } })();
寫完模塊後,寫入口文件index.js
require('sass/index.scss'); var Toast = require('mod/toast.js').Toast; var WaterFall = require('mod/waterfall.js'); var NoteManager = require('mod/note-manager'); var Event = require('mod/event.js'); NoteManager.load(); $('.add-note').on('click', function () { NoteManager.add(); }) Event.on('waterfall', function () { WaterFall.init($("#content")); })
到這就差很少完成了70%了,接下來就建立數據庫,鏈接數據庫了
/*建立數據庫 運行 node note.js*/ var Sequelize = require('sequelize'); var path = require('path'); var sequelize = new Sequelize(undefined, undefined, undefined, { host: 'localhost', dialect: 'sqlite', // SQLite only storage: path.join(__dirname, '../database/database.sqlite') }); /* 測試鏈接是否成功 node note.js sequelize.authenticate() .then(() => { console.log('Connection has been established successfully.'); }) .catch(err => { console.error('Unable to connect to the database:', err); }); */ var Note = sequelize.define('note', { text: { type: Sequelize.STRING }, userid: { type: Sequelize.INTEGER }, username: { type: Sequelize.STRING } }); Note.sync(); /* 刪除表 Note.drop(); */ /* //建立數據庫 Note.sync().then(function(){ Note.create({text:"sdsdsdsd"}); }).then(function(){ //查詢表 Note.findAll({raw:true}).then(function(notes){ console.log(notes); }) }); */ module.exports = Note;
而後是在routes 裏處理 ajax 請求,處理登陸信息,獲取 id,用戶名等等,到這就基本完成了
通過一星期的開發,瞭解了先後端聯調,模塊化開發方式、webpack 及loader和插件的使用、npm 的使用,Express的使用、路由、中間件、sqlite三、nodejs,在開發過程當中仍是有遇到許多問題,例如在連續聲明變量的時候,不當心把逗號寫成了分號,其餘變量就變成了全局變量,因而就出錯了,查了很久?不過在這過程之中仍是學到了許多,重要的是過程,繼續往前端的路走下去?