Claude0311
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    1
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # NTUEE+ documentation <small>[回MD首頁](https://hackmd.io/CSNbja7XTYCYquYxgq4Xow)</small> ## 起源 >試著回想18歲的自己,未來的人脈往往成為促使我們選擇台大電機的原因,然而曾經我們引以為傲的人脈資產,現在卻如此凋零。 一瞥世界上知名大學,他們都擁有一個共通點:人脈網絡。哈佛大學的老爺爺願意為了甫錄取的學弟妹提點長談,史丹佛大學的前輩也不遺餘力提拔後進。相比之下,我們認為系上一直缺乏專屬平台供系友建立緊密的網路,遂使人脈日益薄弱。 近年創立的NTUEE Chain已經輔導眾多學生申請上國外一流大學,我們更希望延續EE Chain的初衷,讓這份互相傳承聯絡的心拓展到所有系友。繼承著B03~B06學長姐們的意志,我們希望這個聯絡網能成為一個整合式的社群網路,讓NTUEErs聚在一起;秉持著恢復人脈網的精神,讓NTUEE能在世界上有更大的影響力;建立一個連結電機系的共同回憶,讓系友們有專屬的家! :::info ~~這並不是一個網頁養成班,沒辦法讓所有人把寫網頁的所有技能練習套精熟,主要學習內容是依照本網站的需求進行延伸,會從最基本的寫起,並漸漸加深加廣,目標寫出一個完整、讓人驚艷的網頁\^_^~~ ::: --- ## [背景知識](https://drive.google.com/drive/folders/1F_NW1_4ykXiAwF4cIsI16QSROVeAQVYy?usp=sharing) ### 環境安裝 * 安裝列表 * [nodejs v12](https://nodejs.org/zh-tw/download/) * [git v2](https://git-scm.com/) * 如果還沒註冊git帳號的記得去註冊 * [vscode](https://code.visualstudio.com/) * vscode extensions * npm * JavaScript (ES6) code snippets * vscode extensions(recommended) * GitLens * Bracket Pair Colorizer * [Markdown Preview Github Styling](https://ithelp.ithome.com.tw/articles/10190508) :::warning 如果有漏掉的隨時回報 ::: * 程式運作 * 選擇要存放檔案的資料夾 * ctrl+\`打開terminal,輸入 ```bash $ git clone https://github.com/chinyi0523/NTUEE_Plus_website.git $ cd NTUEE_Plus_website $ npm install $ npm run install-client $ npm start ``` * 打開[http://localhost:1993](http://localhost:1993),應該會看到EE+的網頁 ### git 一樣在vscode的terminal即可 * 初始設定,在repo上才知道誰是誰 ```bash # 輸入使用者名稱 $ git config --global user.name "YourName" # 輸入帳號email $ git config --global user.email "YourMail@gmail.com" # 查看使用者名稱 $ cat ~/.gitconfig ``` * git status * 用git add \<filename>站存 * 用git commit -m 'commit message'存檔 ![](https://i.imgur.com/ea7S5JO.png) ![](https://i.imgur.com/Nxghtb0.png) * 檔案管理(local) * add:把特定檔案加到暫存區 ```bash # 暫存所有檔案(我都是用這個) $ git add . 或 git add -a 或 git add --all # 暫存特定檔案(善用*,例如*.txt) $ git add <filename> # 暫存所有modified的檔案(無視untrack),但建議用.gitignore $ git add -u ``` * .gitignore:裡面的檔名會被```git add .```無視 * commit:存檔,commit message說明這次存檔變更了甚麼,建議英文寫(我也不知道為啥) ```bash # 存檔,'commit message'換成你的簡述 $ git commit -m 'commit message' #如果覺得commit -m太長,可以在config設定縮寫cm $ git config --global alias.cm 'commit -m' #以後打git cm取代git commmit -m ``` * 刪除和改名 ```bash # 變成untrack file(建議) $ git rm <filename> --cached # 直接從電腦刪除 $ git rm <filename> # 改檔名(不用重新add) $ git mv <oldname> <newname> ``` * 檔案上傳(local&harr;remote) remote為遠端程式庫,當local的程式有更新就可以上傳到remote上,預設只有一個版本叫做origin(也就是clone的來源,github的網址),也可以自己[設定](https://git-scm.com/book/zh-tw/v2/Git-%E5%9F%BA%E7%A4%8E-%E8%88%87%E9%81%A0%E7%AB%AF%E5%8D%94%E5%90%8C%E5%B7%A5%E4%BD%9C)其他遠端版本的連結 ```bash # 從origin更新程式 $ git pull # 把local的更動上傳到origin $ git push ``` * 分支branch 善用分支讓大家的程式不打架 ![](https://i.imgur.com/rgjRaJt.png) * 分支管理 ```bash #看目前所在的branch和local所有的branch $ git branch $ git branch -a #茶和local+remote所有分支 $ git checkout <branchname> #切換到其他branch #新增branch並切換過去,注意!他會以當前branch為根源 $ git checkout -b <branchname> ``` * 與遠端互動 ```bash # 抓遠端master $ git pull origin master #把local當前的branch發到remote的branch上 $ git push -u origin <branch name> # 之後在這個branch pull、push都是對origin branch做事 ``` * merge(重要! 1. 與master merge的規則: 在自己的branch做事,確保城市沒炸掉後才可以merge。 把自己的branch發到remote,發pr。 2. 小組功能的merge: 之後可能會讓幾個人一起開發一個功能,所以可能有一個branch開了3個小branch的情況,例如: Recruitment下有Recruitment-backend、Recruitment-frontend。 方法1:git merge ![](https://i.imgur.com/7KSzKCL.png) ```bash # 如果後端完成事情要要merge $ git checkout Recruitment $ git merge Recruitment-backend # 如果前端想要拿後端merge到Recruitment的資料 $ git checkout Recruitment-frontend $ git merge Recruitment ``` 方法2:rebase ![](https://i.imgur.com/CfwR5Cn.png) 通常用在自己的branch上,特點是保持分支樹乾淨(最終會merge成一條), 例如Recruitment-backend開了個子branch <testAwait> ```bash $ git checkout Recruitment-backend $ git rebase testAwait ``` ### 網站架構 #### front-end ##### react ###### state ##### axios #### back-end ##### 檔案架構 ``` ├── backend/routes/ ├── api.js ### doc相關 ### ├── apidoc.json #生成readme的rule ├── README.MD #在/backend跑yarn doc生成 ├── docTemplate/ #README的template ### middleware相關 ### ├── error/ ├── index.js ├── ErrorHandler #throw它可以指定狀態碼(404,500,...) ├── handleError #api.js最底下呼叫,處理所有error ├── dbCatch #mongoose相關的error handling ├── middleware/ ├── mail/ #寄信 ├── validation/ #判斷req格式 ├── fileProcess.js #把formdata塞進req.body ### 主要程式碼 ### ├── Schemas #資料庫相關 ### 特殊 ### ├── db.js #與資料庫連線的基本設定 ├── preload.js #docker建好後在/跑yarn reset-db ├── query.js #把req變成mongoose的query ### collections ### ├── ... ├── srcs #api相關 ├── in/ ├── abroadinfo #留學資訊 ├── account ├── auth/ #管理員相關的帳號設定 ├── ... ├── auth #用session檢查權限 ├── isAuth.js ├── isUser.js ├── career #就是recruitment ├── column ├── profile_new ├── recommendation ├── study #留學配對 ├── out/ ├── account #註冊登入那些的 ├── dashboard ├── forget #忘記密碼 ``` ##### nodejs obj小教學: ```js const obj1 = {a:1,b:2} const {a,b} = obj1//a=1,b=2 const c = 3 const d=4 //{c:3,d:4} const obj2 = {c,d} ``` ##### [express](https://expressjs.com/zh-tw/) ###### intro Express.js是後端Node.js的開發框架之一,框架有點像是一個工具包,開發者能夠使用 npm(Node Package Manager) 安裝所需要的工具包到專案資料夾,輕鬆完成專案的使用環境設定,讓你專注在程式碼的撰寫與實際運行上。 ```js //index.js //開啟app應用程式,套用express框架 const express = require('express'); const app = express(); ... //設定前後端路由(下面介紹) app.use("/api", require("./backend/routes/api")); app.use(express.static('./client/dist')); ... //在http://localhost:1993監聽此應用程式 app.listen(process.env.PORT||1993,function(){ console.log('server connect'); console.log('port name: ',process.env.PORT||1993); }) ``` ###### [route](https://expressjs.com/zh-tw/guide/routing.html) 路由是指判斷應用程式如何回應用戶端對特定端點的要求,而這個特定端點是一個 URI(或路徑)與一個特定的 HTTP 要求方法(GET、POST 等)。 每一個路由可以有一或多個處理程式函式,當路由相符時,就會執行這些函式。 路由定義的結構如下: ```js app.METHOD(PATH, HANDLER); //METHOD 是 HTTP 要求方法。 //PATH 是伺服器上的路徑。 //HANDLER 是當路由相符時要執行的函數,或稱中介軟體 ``` 如果想要用一個path管理很多個路由,可以用express.Router,可以想成是迷你應用程式。 例如後端的程式由api.js控管,並輸出成一個router在/api下運作。 ```js //routes/api.js const express = require("express"); const router = express.Router(); router.post(url1,function1); router.post(url2,function2); ... module.exports = router; ``` ```js //index.js ... app.use("/api", require("./routes/api")); ... ``` ###### 中介軟體 中介軟體函數是寫在route中的函式,他的input固定為「要求物件 (req)」、「回應物件 (res)」 和「執行下一個中介軟體(next)」,常見的形式為: ```js router.post(url,function(req, res, next){ //do something next() },function(req,res,next){}) ``` * 要求物件[req](https://expressjs.com/zh-tw/4x/api.html#req) (Object) 它是http request,內含呼叫這個url時夾帶的參數。常用的有: * req.body (Object): 前端發post時夾帶的data ```js //client axios.post(url,{account:"b07901029",username:"huho"},config) -> //backend req.body.account //"b07901029" ``` * req.query (Object): 前端發get時夾帶的data ```js //client GET /url?account=b07901029&username=均府 -> //backend req.query.account //"b07901029" ``` * req.protocol+"://"+req.get('host')+"/someURL" 拿到client的網址,考慮到上架後的網址不再是localhost,要這樣才能拿到最正確的網址,當然網路上也一定查的到其他方法。 * 回應物件[res](https://expressjs.com/zh-tw/4x/api.html#req) (Object) 當獲得http request後,express就會發出http response。 * res.header 設定http參數 * res.status(httpCode).send(obj or string) 回傳資料給前端。 [http code](https://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81) 成功通常是用200或201 失敗通常是4xx或5xx * res.status(httpCode).end() 和send差不多,但不回傳資料。 * 下一個中介軟體(next) (function) 在route中可以執行很多個funciton,用 ```js router.METHOD(url, function(req,res,next){}, function(req,res,next){},...) ``` 預設會執行第一個函式,當第一個函式呼叫了next(),就會執行第二個函數。 一般來說如果router呼叫同一個url兩次,則只有第一個(在檔案比較前面的)會被執行,但如果呼叫next('route'),就會切出這個route執行第二個route ```js router.post('/login',function(req,res,next){ next('route'); },function(req,res,next){ console.log('我不會被執行') }) router.post('/login',function(req,res,next){ console.log('我會被執行') }) ``` 善用中介軟體可以把許多路由都用到的function寫在外面呼叫,讓程式更簡潔。 <big>**一些express常見的應用**</big> * router.use(function) 強制執行所有路由都要經過他(好像要注意priority?) 通常會用來設定參數(例如index.js中的session或urlencoded或res header),或執行一些大家統一要用的參數。 * session * urlencoded、json * Auth 驗證使用者是否有登入 ```js //api.js const Auth = require('/in/Auth'); axios.post(url,Auth,...); //Auth.js module.export = funciton(req,res,next){ if(req.session){ next() }else{ //開發階段這裡呼叫next(); res.status(403).send({description:"請登入"}); } } ``` * [multer](https://riptutorial.com/zh-TW/node-js/example/14210/%E4%BD%BF%E7%94%A8multer%E4%B8%8A%E5%82%B3%E5%96%AE%E5%80%8B%E6%96%87%E4%BB%B6) 專門處理檔案上傳,在/register、/chVisual、/getImg有應用在照片處理 不想深入研究的話就直接套用: 1. 存單張照片 ```js //client let data = new FormData(); data.append('avatar',this.state.file); //用append設定其他要post的資料,下面介紹更多關於formData的注意事項 const config = {headers: {'content-type':'multipart/form-data'} }; axios.post(url,data,config); -> //backend const parseFile = require('./middleware/multer.js'); router.post(url,parseFile('avatar'),...); //ImgGet裡輸入要讀取的檔案的key //之後的function就可以在req.file裡拿到{buffer,mimetype(img/png之類的)} ``` * formData注意事項 ```js //frontend //要傳object時請用json let data = new FormData(); const obj = {data:"b07901029",show:true} data.append("account",JSON.stringify(obj)) //要傳array key請加[] const arr = ["a","b","c"] arr.forEach(item=>{ //如果item是obj請參考上面解法 data.append("fakeArr[]",item) }) -> //backend const {data,show} = req.body["account"] //{"b07901029",true} //用forEach拿array req.body["fakeArr"].forEach(item=>{ console.log(item); //"a"\n"b"\n"c" }) ``` 2. 若要讀取多個檔案請仿照middleware/multer.js,但upload.single改成upload.array。 * 可以參考study/runMatch/parseExcel 3. 拿單張照片 ```js //backend //這裡req.file應該變成我們存放mimetype和buffer的地方 const prefix="data:"+req.file.mimetype+";base64," const img = new Buffer(req.file.buffer, 'binary').toString('base64'); const userimage = prefix+img; res.send({userimage}) -> //client //拿到的userimage用this.setState({userimage}),然後可直接設成<img>的value: <img value={this.state.userimage}> ``` * [validation](https://medium.com/%E9%BA%A5%E5%85%8B%E7%9A%84%E5%8D%8A%E8%B7%AF%E5%87%BA%E5%AE%B6%E7%AD%86%E8%A8%98/%E7%AD%86%E8%A8%98-%E6%8A%8A%E7%8E%A9-express-validator-%E5%9C%A8%E4%BC%BA%E6%9C%8D%E5%99%A8%E7%AB%AF%E5%81%9A%E8%A1%A8%E5%96%AE%E9%A9%97%E8%AD%89-797342aab2d3) 在執行主程式前用express-validator驗證資料格式,目的是不要讓使用者傳奇怪的資料到後端 ```js //基本架構: const {check} = require('express-validator') router.post(url,[check(key1),check(key2),...],authController,function,...) ``` * express-validator中有三種常用函式:body、cookie、check,分別會檢驗req.body、req.cookies、req.{body,cookies,header,param,query}下指定key對應的value有沒有符合格式。 * 若沒有符合格式,消息會被存在req中並呼叫下一個函式:authController,express-validator下還有validationResult這個函式,用validationResult(req)可以抓到錯誤並回傳res.status(400)。 * 以下是我們的版本(以login為例),require validation,然後指定要檢查的欄位 ```js const valid = require('../../../middleware/validation') const rules = ['account', 'password'] module.exports = [valid(rules), asyncHandler(login)] ``` * ##### [mongoDB and mongoose](https://developer.mozilla.org/zh-TW/docs/Learn/Server-side/Express_Nodejs/mongoose) ###### intro mongoDB是一個文件導向的資料庫(database),一個資料庫底下可以有多個集合(collections),每個集合下有多個文檔(documentions)。 以我們的專案為例,我們的database存放在mongoDB online上,名叫eeplus。當中有以下collections: ``` user_logins user_visuals columns ... ``` 而user_logins有數個documentation,格式類似json ``` {_id:ObjectID(b6e5rb48e6), account:"b07901029", userpsw:"f2hoifpew"}, {_id:ObjectID(ber561r65b), account:"b07901014", userpsw:"vewoivnro"}, ... ``` mongoose是nodejs中用來與mongoDB溝通的套件。 * 基本設定:連線與監聽 ```js //routes/Schema/db.js const mongoose = require('mongoose'); mongoose.connect(DB_URL); //form like mongodb://<user>:<psw>@host:port/dbname const db = mongoose.connection(); //背景監聽 db.on('disconnected', function(){ console.log('disconnected!'); }); ``` * 模型(model)與綱要(schema) 一個model對應到mongoDB中的一個collection,透過schema定義格式。我們可以透過model進行doc的創建、查找、更新、刪除。 ```js const mongoose = require("./db") const Schema = mongoose.Schema; //在此設定model的type、驗證、虛擬屬性(我沒用過,感覺很好玩!) const User_login_Schema = new Schema({ //透過:TYPE設定類型 username: String, //也可以規定資料的內容,例如enum:['dog','cat']限定內容選項 account: {type:String, require:True}, ..., //可以設定多層的資料,也可以設定[] img: { data: { type: Buffer }, contentType: { type: String } } }); //把schema編譯成model,之後就可以對model進行操作 //之後save的檔案會存在user_logins(好像會自動加s)這個collection中 module.exports = mongoose.model("User_login", User_login_Schema); ``` * schema tips 1. [type and some attribute](https://mongoosejs.com/docs/schematypes.html) 2. population:{_id(docID),ref(collectionName)},與其他collecton連結 3. instance method(doc's function)。針對單一doc做事,用this拿到該doc的屬性。 ```js // define a schema const animalSchema = new Schema({ name: String, type: String }); // assign a function to the "methods" object of our animalSchema animalSchema.methods.findSimilarTypes = function(cb) { return mongoose.model('Animal').find({ type: this.type }, cb); }; ``` 4. static(model's function)。自訂model相關函式,用this拿到該model的屬性(例如.find)。 ```js animalSchema.statics.findByName = function(name) { return this.find({ name: new RegExp(name, 'i') }); }; ``` 5. [virtual type](https://mongoosejs.com/docs/tutorials/virtuals.html):可以根據doc自訂回傳格式(例如doc &rarr; res.json),不會存入資料庫。有getter和setter屬性。 * [model usage](https://mongoosejs.com/docs/api/model.html) * insert ```js const doc = await new Login({ account:"b07901029" }).save().catch(dbCatch)//throw error if error occur ``` * find [query and selector rule](https://docs.mongodb.com/manual/reference/operator/query/) ```js //只找一個 const doc = await Login .findOne(query, selector) .catch(err=>{}) //找多個 const docs = await Login .find(query,selector) .catch(err=>{}) ``` * update [update rule](https://docs.mongodb.com/manual/reference/operator/update/) ```js const {updateQuery} = require(../Schemas/query) //updateQuery會把特定的object轉成mongoose看得懂的格式 //簡單說就是依據value是不是空字串,把value塞進{$set,$unset} const {title} = req.body const update = updateQuery({'title.title':title}) //update = {$set:{'title.title':title}} const {n,nModified} = await Recruitment .updateOne(query,update,option) .catch(err=>{}) /*{ n, //number of matched doc nModified //number of modified doc }*/ ``` * delete ```js loginModel.deleteMany(query,function(err){}); ``` --- ## 其他 #### res.send * [http code](https://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81) * 可以用throw ErrorHandler(404,'any msg u want')觸發error router #### api doc * 在/backend跑yarn doc根據每個檔案的註解生成README --- ## References * **Learn Coding Websites** [codecadamy(HTML, CSS, JS)](https://www.codecademy.com/learn) [freeCodeCamp youtube channel](https://www.youtube.com/channel/UC8butISFwT-Wl7EV0hUK0BQ) * 基礎能力學習 [Git 簡介](https://zlargon.gitbooks.io/git-tutorial/content/installation.html) [Git environment setup](https://www.tutorialspoint.com/git/git_environment.htm?fbclid=IwAR07byYUJfy-Ds6Y-Qc6f_mwyF4V5KLlDq0v_zc5_f2grH1Lp7qMA5HbRk8) [Command Line 簡介](https://carolhsu.gitbooks.io/django-girls-tutorial-traditional-chiness/content/intro_to_command_line/README.html) * **Ric's Course** [link](https://ric2k1.github.io/?fbclid=IwAR0pd7K5m3Dlh_riCEIRq88tXy7InQshxinUhM-bIfmEhRTkwPtpF3ljwOI) * **資料庫管理SQL** [w3school](https://www.w3schools.com/sql/) [一天速成](http://www.finereport.com/tw/knowledge/acquire/sql-3.html?fbclid=IwAR3if2FnWsZL_T03NDqQwmZVzIy2hmbxucM3r8tXZ-DVAGVfaCkZNSwOErY) * **Node.js & Express.js & Mongo.js** [w3school](https://www.w3schools.com/nodejs/default.asp) [express_Ric](https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website) [Node.js full module list](https://www.w3schools.com/nodejs/ref_modules.asp) [Express.js MDN tutorial](https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website) [Express.js & Node.js video](https://www.youtube.com/watch?v=G8uL0lFFoN0) [Ric's ppt](https://hackmd.io/@dSpEyyWWQYaN4gOKsy7saw/HJegFNM54?type=slide#/8) * **JavaScript** [w3school](https://www.w3schools.com/js) [3hr video](https://www.youtube.com/watch?v=PkZNo7MFNFg) * **HTML** [w3school](https://www.w3schools.com/html/) [2hr video](https://www.youtube.com/watch?v=pQN-pnXPaVg) * **CSS** [w3school](https://www.w3schools.com/css/) [2hr video](https://www.youtube.com/watch?v=ieTHC78giGQ)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully