螞蟻金服體驗技術部招人啦,前端開發專家,Node.js開發專家,歡迎投遞簡歷,長期有效。郵箱: chenglin.mcl@antfin.com,。
關于結緣的前端使用哪種框架,筆者之前有過Angular.js的開發經驗,在React.js和Vue.js之間徘徊了一陣子最終選擇了Vue,在我看來Vue和Angular有許多的共同點,在看Vue的文檔的時候有很熟悉的味道,但和React類似只關注View這一塊。Vue沒有像React那樣什么都放到js中,簡潔方便的api設計以及組件式開發是我選擇它的主要原因。
實現前端大抵有兩種思路,一個是由后端拼接頁面后返回完整的網頁,然后由瀏覽器渲染。另一種是由后端返回json之類的數據,然后由前端拼接數據進行展示,當然也可以兩者混搭。實現前后端分離是很多web開發人員的夢想,有興趣的可以看看Web系統開發構架再思考-前后端的完全分離。
結緣前端打算使用Vue.js做成SPA(Single Page Application)的形式,即一個入口頁面,后續數據由后端Nodejs實現Restful風格的API調用,然后在前端呈現。使用Webpack作為前端工程解決方案解決資源管理,按需加載,實時更新等問題。本篇我們探索使用Vue.js實現結緣的登錄界面。
代碼
代碼放在結緣的前端工程中,Vue-jieyuan,歡迎star,issue
安裝部署Vue開發環境
為了方便大型應用的開發,尤大大開發了Vue-cli腳手架,提供了一系列的工具和庫,方便我們快速的進行開發,具體功能包括單文件 Vue 組件,熱加載,保存時檢查代碼,單元測試等,本質上和Express的express-generator
是一樣的。
因為vue-cli
依賴webpack,所以首先安裝webpack這個工具:
$ npm install -g webpack
關于webpack如果沒了解過可以看基于webpack搭建前端工程解決方案探索,然后安裝vue-cli
:
$ npm install -g vue-cli
使用方法如下:
$ vue init webpack my-project
$ cd my-project
$ npm install
$ npm run dev
執行完成后在瀏覽器中localhost:8080
查看。
尤大大目前提供了4套官方模板,如下:
- browserify - A full-featured Browserify + vueify setup with hot-reload, linting & unit testing.
- browserify-simple - A simple Browserify + vueify setup for quick prototyping.
- webpack - A full-featured Webpack + vue-loader setup with hot reload, linting, testing & css extraction.
- webpack-simple - A simple Webpack + vue-loader setup for quick prototyping.
可以根據需求選擇即可。
在使用過程中遇到兩個問題,如果你也遇到了,可以在issues中查看。
學習ES6
由于vue-cli生成的文件中使用的是ES6的語法,而ES6是未來的趨勢,所以ES6必須一學,目前瀏覽器和Nodejs對ES6的支持程度不斷提高,不過要在所有的瀏覽器中使用es6代碼目前還不可行,不過babel可以幫你提前體驗新的語法而不需要等待瀏覽器支持。babel本質上是一個js的預編譯器,可以把es6程序編譯成es5,從而在支持ES5的環境中運行。
特地查了一下ES6和ES2016,ES2015的區別,實際上ES6===ES2015 < ES2016,由于ECMA委員會決定將標準每年一更,因此新推出的ES6被改名為ES2015,后面的標準將實行年制命名,如ES2016,ES2017...
學習ES6/ES2015可以參考如下資源
- Learn ES2015 A detailed overview of ECMAScript 6 features. 對ES6語法的概覽
- ECMAScript 6入門 阮一峰老師的ES6入門
學習vue目前沒有足夠的教程可以幫我們快速了解如何構建我們的應用,所以只能一點點摸索,借鑒前人的經驗,所幸尤大大有個使用Vue開發的Hacker News Clone,我們可以從這里吸收開發經驗。另外Cnode社區也有個用Vue開發的客戶端Vue-cnodejs也很不錯。
這里首先簡要介紹一些Hacker News 客戶端中使用到的ES6特性:
箭頭函數 => 和this
=>
是匿名函數的一種簡寫,即lamda表達式,格式為( 形參列表 ) => { 函數體 }
,使用箭頭函數,內部函數繼承了外圍作用域的this值,再也不用寫var that=this
這種hack代碼了。直接上代碼看:
// Expression bodies
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);
// Statement bodies
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
// Lexical this
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
};
模塊定義
在ES6之前js沒有一個統一的模塊定義方式,流行的定義方式有AMD,CommonJS等,而ES6從語言層面對定義模塊的方式進行了統一。
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
alert("2π = " + sum(pi, pi));
export default
(默認加載)和 export *
(整體加載)為:
// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
// app.js
import exp, {pi, e} from "lib/mathplusplus";
alert("2π = " + exp(pi, e));
默認加載的好處是我們不需要知道模塊所要加載的變量名或函數名,輸出時指定任意名字,且不需要大括號。更詳細的可以查看阮一峰老師的module一節
const 和 let
const
即常量,一旦定義了即不可變。let是更好的var,由于js的設計缺陷,var變量的作用域是函數體的全部,還有變量提升等怪異特性,導致詭異的錯誤,極難定位bug。而let擁有塊級作用域,聲明的全局變量不是全局對象的屬性,形如for (let x...)的循環在每次迭代時都為x創建新的綁定.能用let盡量不用var,具體請看Is there any reason to use the “var” keyword in ES6?以及深入淺出ES6(十四):let和const
function f() {
{
let x;
{
// okay, block scoped name
const x = "sneaky";
// error, const
x = "foo";
}
// okay, declared with `let`
x = "bar";
// error, already declared in block
let x = "inner";
}
}
promise
語言標準實現的異步編程解決方案:
function timeout(duration = 0) {
return new Promise((resolve, reject) => {
setTimeout(resolve, duration);
})
}
var p = timeout(1000).then(() => {
return timeout(2000);
}).then(() => {
throw new Error("hmm");
}).catch(err => {
return Promise.all([timeout(100), timeout(200)]);
})
關于Vue-cli的webpack模板的代碼風格
Vue-cli本身是一套技術選型,本身有作者自己的設計偏好在里面,例如模板默認是ES6的語法,使用ESLint進行代碼規范等。在我開始使用這個工具的過程中有兩個糾結的地方,一個是縮進,一個分號。
關于縮進
模板中默認代碼使用的2個空格進行縮進,這沒問題,問題是在ESLint的配置文件.eslintrc.js
中寫死了indent的規則,于是各種縮進必須按照規范來,不然就會出現多處如下的錯誤
error indent Expected indentation of 4 space characters but found 6
/Users/Calvin/Develop/githubs/jieyuan/Vue-jieyuan/src/App.vue:35:7
Hello,
用java習慣了,格式糟糕的話format一下就好了啊,然后各種查資料是用2個空格還是4個空格,可以看看知乎的這個回答為什么JS的規范說要用兩個空格來縮進?,恩,看來寫js代碼用2個空格更流行一些。如果你用sublime,那么可以打開你一個js文件然后Preference -> Settings More -> Syntax Specfic-User,然后寫入以下選項 :
{
"tab_size": 2,
"translate_tabs_to_spaces": true
}
同理對.vue也做一遍。不過蘿卜青菜各有所愛,只要同意規范就好,如果還是希望使用4空格,可以編輯.eslintrc.js
的indet項。
關于寫不寫分號
習慣了寫分號,至今為止一直認為寫分號會讓代碼清晰,不容易出錯。不過在js這樣不強制寫分號的語言中需要另外考慮一番,可以看看知乎的這個問題:JavaScript 語句后應該加分號么?,于是又被尤大的答案折服了,ok,咱也不寫分號了。更多的還是建議看尤大給的鏈接semicolons
單文件組件以及Vue-loader解惑
看Vue-cli中的src/componets
文件夾有個Hello.vue
的文件,這個是默認生成的單文件組件。如下:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
data () {
return {
// note: changing this line won't causes changes
// with hot-reload because the reloaded component
// preserves its current state and we are modifying
// its initial state.
msg: 'Hello World!'
}
}
}
</script>
咦?不對啊,這玩意是組件?Vue文檔中不是說組件要用如下形式聲明嗎:
var MyComponent = Vue.extend({
// options...
})
// Globally register the component with tag: my-component
Vue.component('my-component', MyComponent)
<div id="example">
<my-component></my-component>
</div>
好吧,在Building Large-Scale Apps中文檔介紹了這種單文件的組件,它的特點是單文件組合HTML 模板,CSS和JS,并且可以使用自己想用的預處理器,并且css代碼對于每個組件是隔離的,只能說Vue,就決定是你了!
,例如:
.vue
,那這種文件咋解析啊,有沒有文件解析器?于是有了vue-loader
,它的官方介紹如下:
vue-loader is a loader for Webpack that can transform Vue components written in the following format into a plain JavaScript module
恩,Vue-loader會自動幫你把這種單文件組件轉成組件使用,我們就不用操心啦。當然尤大也不強制你把代碼都放在一個文件里,可以拆開放:
<template src="./template.html"></template>
<style src="./style.css"></style>
<script src="./script.js"></script>
或者從npm模塊加載:
<!-- import a file from the installed "todomvc-app-css" npm package -->
<style src="todomvc-app-css/index.css">
更多內容可以看Vue-loader的官方文檔,跟著做一遍可以加深印象,更了解webpack和vue的思想。
開發Vue組件
好吧,說了那么多,我們來開發一個基本的Login組件吧。目前網頁設計水平還跟不上,直接使用Bootstrap的css庫,在index.html的head
標簽中加入
<link rel="stylesheet" >
在compoents
標簽中添加Login.vue
文件,輸入如下內容:
<template>
<!-- Stack the columns on mobile by making one full-width and the other half-width -->
<div class="container">
<form class="form-signin">
<h2 class="form-signin-heading">{{title}}</h2>
<label for="inputEmail" class="sr-only">郵件地址</label>
<input type="email" id="inputEmail" class="form-control" placeholder="郵件地址" required autofocus>
<label for="inputPassword" class="sr-only">密碼</label>
<input type="password" id="inputPassword" class="form-control" placeholder="密碼" required>
<div class="checkbox">
<label>
<input type="checkbox" value="remember-me"> 記住我
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">登錄</button>
</form>
</div>
</template>
<script>
export default {
data () {
return {
// note: changing this line won't causes changes
// with hot-reload because the reloaded component
// preserves its current state and we are modifying
// its initial state.
title: '登錄結緣'
}
}
}
</script>
<style >
body {
padding-top: 40px;
padding-bottom: 40px;
background-color: #eee;
}
.form-signin {
max-width: 330px;
padding: 15px;
margin: 0 auto;
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
margin-bottom: 10px;
}
.form-signin .checkbox {
font-weight: normal;
}
.form-signin .form-control {
position: relative;
height: auto;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 10px;
font-size: 16px;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
</style>
將App.vue
中的Hello組件換成Login組件即可,最終效果如下:
代碼
代碼放在結緣的前端工程中,Vue-jieyuan,歡迎star,issue